// programs/log.js 'use strict'; const fs = require('fs'); const path = require('path'); // --- configuration --- const LOG_DIR = path.join(__dirname, '..', 'logs'); fs.mkdirSync(LOG_DIR, { recursive: true }); function getLogFilePath(d = new Date()) { const yyyy = d.getFullYear(); const mm = String(d.getMonth() + 1).padStart(2, '0'); const dd = String(d.getDate()).padStart(2, '0'); return path.join(LOG_DIR, `${yyyy}_${mm}_${dd}.txt`); } function write(obj) { const line = JSON.stringify(obj) + '\n'; fs.appendFile(getLogFilePath(), line, (err) => { if (err) console.error('[log] write error:', err); }); } // --- common extractors --- function commonFromReq(req) { try { const xff = req?.headers?.['x-forwarded-for']; const xRealIp = req?.headers?.['x-real-ip']; const ipFromXff = xff ? xff.split(',')[0].trim() : null; const ip = ipFromXff || xRealIp || req?.ip || req?.socket?.remoteAddress || null; const tls = req?.socket?.encrypted ? { protocol: typeof req.socket.getProtocol === 'function' ? req.socket.getProtocol() : null, cipher: typeof req.socket.getCipher === 'function' ? (req.socket.getCipher() || {}).name : null, } : null; // MAC is not available across routed networks const mac = null; return { ip, ips: Array.isArray(req?.ips) ? req.ips : [], xff: xff || null, remoteAddress: req?.socket?.remoteAddress || null, remoteFamily: req?.socket?.remoteFamily || null, userAgent: req?.headers?.['user-agent'] || null, acceptLanguage: req?.headers?.['accept-language'] || null, secChUa: req?.headers?.['sec-ch-ua'] || null, secChUaPlatform: req?.headers?.['sec-ch-ua-platform'] || null, secChUaMobile: req?.headers?.['sec-ch-ua-mobile'] || null, referer: req?.headers?.['referer'] || null, tls, mac, }; } catch { return {}; } } function commonFromSocket(socket) { return { remoteAddress: socket?.remoteAddress || null, remoteFamily: socket?.remoteFamily || null, }; } // --- specific log functions --- function logHttpRequest(req) { write({ ts: new Date().toISOString(), type: 'http', method: req?.method || null, url: (req?.originalUrl ?? req?.url) || null, ...commonFromReq(req), }); } function logTcpConnection(socket) { write({ ts: new Date().toISOString(), type: 'tcp', ...commonFromSocket(socket), }); } function logHttpUpgrade(req) { write({ ts: new Date().toISOString(), type: 'http-upgrade', url: req?.url || null, ...commonFromReq(req), }); } function logWssConnected(req) { write({ ts: new Date().toISOString(), type: 'wss', url: req?.url || null, ...commonFromReq(req), }); } function logWssClosed(req, code, reason) { write({ ts: new Date().toISOString(), type: 'wss-close', url: req?.url || null, code: typeof code === 'number' ? code : null, reason: reason ? reason.toString() : null, ...commonFromReq(req), }); } function logSnapshot(python, response){ write({ ts: new Date().toISOString(), type: 'snapshot', command: python.toString(), wsResponse: response.toString() }) } // --- generic hooks you requested --- function connected(context = {}) { write({ ts: new Date().toISOString(), type: 'connected', ...context, }); } function connectionLost(context = {}) { write({ ts: new Date().toISOString(), type: 'connection-lost', ...context, }); } module.exports = { logHttpRequest, logTcpConnection, logHttpUpgrade, logWssConnected, logSnapshot, logWssClosed, connected, connectionLost, };