/** * @file * @copyright 2020 Aleksej Komarov * @license MIT */ import { sendMessage } from 'tgui/backend'; import { storage } from 'common/storage'; import { createLogger } from 'tgui/logging'; const logger = createLogger('telemetry'); const MAX_CONNECTIONS_STORED = 10; const connectionsMatch = (a, b) => ( a.ckey === b.ckey && a.address === b.address && a.computer_id === b.computer_id ); export const telemetryMiddleware = store => { let telemetry; let wasRequestedWithPayload; return next => action => { const { type, payload } = action; // Handle telemetry requests if (type === 'telemetry/request') { // Defer telemetry request until we have the actual telemetry if (!telemetry) { logger.debug('deferred'); wasRequestedWithPayload = payload; return; } logger.debug('sending'); const limits = payload?.limits || {}; // Trim connections according to the server limit const connections = telemetry.connections .slice(0, limits.connections); sendMessage({ type: 'telemetry', payload: { connections, }, }); return; } // Keep telemetry up to date if (type === 'backend/update') { next(action); (async () => { // Extract client data const client = payload?.config?.client; if (!client) { logger.error('backend/update payload is missing client data!'); return; } // Load telemetry if (!telemetry) { telemetry = await storage.get('telemetry') || {}; if (!telemetry.connections) { telemetry.connections = []; } logger.debug('retrieved telemetry from storage', telemetry); } // Append a connection record let telemetryMutated = false; const duplicateConnection = telemetry.connections .find(conn => connectionsMatch(conn, client)); if (!duplicateConnection) { telemetryMutated = true; telemetry.connections.unshift(client); if (telemetry.connections.length > MAX_CONNECTIONS_STORED) { telemetry.connections.pop(); } } // Save telemetry if (telemetryMutated) { logger.debug('saving telemetry to storage', telemetry); storage.set('telemetry', telemetry); } // Continue deferred telemetry requests if (wasRequestedWithPayload) { const payload = wasRequestedWithPayload; wasRequestedWithPayload = null; store.dispatch({ type: 'telemetry/request', payload, }); } })(); return; } return next(action); }; };