| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- import path from 'path';
- import { fileURLToPath } from 'url';
- import Cache from '../libs/cache.js';
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = path.dirname(__filename);
- const ClientsCacheFile = path.join(__dirname, '../data/clients.cache');
- const CLIENT_HEADER_FIELDS = [
- 'X-Device',
- 'X-Version',
- 'X-Data-Type',
- 'X-Market-Type',
- 'X-Group-Sequence',
- 'X-Platform',
- ];
- const CLIENT_KEY_FIELDS = [
- 'X-Data-Type',
- 'X-Market-Type',
- 'X-Group-Sequence',
- 'X-Platform',
- ];
- const CLIENT_FIELD_NAMES = {
- 'X-Data-Type': 'dataType',
- 'X-Device': 'device',
- 'X-Group-Sequence': 'groupSequence',
- 'X-Market-Type': 'marketType',
- 'X-Platform': 'platform',
- 'X-Version': 'version',
- };
- const CLIENTS = {
- Items: {},
- };
- let saveTimer = null;
- const normalizeHeaderValue = (value) => {
- if (Array.isArray(value)) {
- return value.join(',');
- }
- return value == null ? '' : String(value).trim();
- }
- const getRequestHeader = (req, field) => {
- return normalizeHeaderValue(req.get?.(field));
- }
- const getClientHeaders = (req) => {
- return CLIENT_HEADER_FIELDS.reduce((headers, field) => {
- headers[field] = getRequestHeader(req, field);
- return headers;
- }, {});
- }
- const formatClientFields = (headers) => {
- return CLIENT_HEADER_FIELDS.reduce((clientFields, field) => {
- clientFields[CLIENT_FIELD_NAMES[field]] = headers[field];
- return clientFields;
- }, {});
- }
- const getClientKey = (headers) => {
- return CLIENT_KEY_FIELDS
- .map(field => `${field}:${headers[field] ?? ''}`)
- .join('|');
- }
- const scheduleSaveClientsToCache = () => {
- if (saveTimer) {
- clearTimeout(saveTimer);
- }
- saveTimer = setTimeout(saveClientsToCache, 1000);
- }
- const recordRequest = (req) => {
- const route = req.path;
- const headers = getClientHeaders(req);
- const clientFields = formatClientFields(headers);
- const key = getClientKey(headers);
- const now = Date.now();
- const current = CLIENTS.Items[key] ?? {};
- CLIENTS.Items[key] = {
- key,
- ...clientFields,
- deviceId: current.deviceId,
- route,
- firstRequestTime: current.firstRequestTime ?? now,
- lastRequestTime: now,
- requestCount: (current.requestCount ?? 0) + 1,
- };
- scheduleSaveClientsToCache();
- return CLIENTS.Items[key];
- }
- const getClients = () => {
- return Object.values(CLIENTS.Items)
- .sort((a, b) => (b.lastRequestTime ?? 0) - (a.lastRequestTime ?? 0));
- }
- const updateClient = ({ key, deviceId } = {}) => {
- if (!key || !CLIENTS.Items[key]) {
- return Promise.reject(new Error('CLIENT_NOT_FOUND'));
- }
- if (deviceId === undefined || deviceId === null || deviceId === '') {
- delete CLIENTS.Items[key].deviceId;
- saveClientsToCache();
- return Promise.resolve(CLIENTS.Items[key]);
- }
- const parsedDeviceId = Number(deviceId);
- if (!Number.isInteger(parsedDeviceId)) {
- return Promise.reject(new Error('DEVICE_ID_INVALID'));
- }
- CLIENTS.Items[key] = {
- ...CLIENTS.Items[key],
- deviceId: parsedDeviceId,
- };
- saveClientsToCache();
- return Promise.resolve(CLIENTS.Items[key]);
- }
- const deleteClient = (key) => {
- if (!key || !CLIENTS.Items[key]) {
- return Promise.reject(new Error('CLIENT_NOT_FOUND'));
- }
- delete CLIENTS.Items[key];
- saveClientsToCache();
- return Promise.resolve();
- }
- function saveClientsToCache() {
- if (saveTimer) {
- clearTimeout(saveTimer);
- saveTimer = null;
- }
- Cache.setData(ClientsCacheFile, CLIENTS);
- }
- function loadClientsFromCache() {
- const cachedClients = Cache.getData(ClientsCacheFile, true);
- if (!cachedClients?.Items) {
- return;
- }
- CLIENTS.Items = cachedClients.Items;
- }
- loadClientsFromCache();
- process.on('exit', saveClientsToCache);
- process.on('SIGINT', () => {
- process.exit(0);
- });
- process.on('SIGTERM', () => {
- process.exit(0);
- });
- process.on('SIGUSR2', () => {
- process.exit(0);
- });
- const Clients = {
- recordRequest,
- getClients,
- updateClient,
- deleteClient,
- };
- export { recordRequest, getClients, updateClient, deleteClient };
- export default Clients;
|