import { randomUUID } from 'node:crypto'; const startedAt = new Date().toISOString(); const telemetry = { total: 0, active: 0, errors: 0, blocked: 0, by_status: {}, by_method: {}, last_error_at: null, last_blocked_at: null, recent: [], }; function safePath(req) { return String(req.path || req.url || '').split('?')[0].slice(0, 300); } function enabled() { return !/^(0|false|no)$/i.test(String(process.env.VENDOO_JSON_LOGS || 'true')); } export function recordSecurityBlock(reason) { telemetry.blocked += 1; telemetry.last_blocked_at = new Date().toISOString(); if (enabled()) console.warn(JSON.stringify({ level: 'warn', event: 'security.blocked', reason, time: telemetry.last_blocked_at })); } export function requestContextMiddleware(req, res, next) { const requestId = String(req.get('x-vendoo-request-id') || randomUUID()).replace(/[^a-zA-Z0-9._:-]/g, '').slice(0, 100) || randomUUID(); const correlationId = String(req.get('x-correlation-id') || requestId).replace(/[^a-zA-Z0-9._:-]/g, '').slice(0, 100) || requestId; const started = process.hrtime.bigint(); req.requestId = requestId; req.correlationId = correlationId; res.setHeader('X-Vendoo-Request-ID', requestId); res.setHeader('X-Correlation-ID', correlationId); telemetry.total += 1; telemetry.active += 1; telemetry.by_method[req.method] = (telemetry.by_method[req.method] || 0) + 1; res.on('finish', () => { telemetry.active = Math.max(0, telemetry.active - 1); const durationMs = Number(process.hrtime.bigint() - started) / 1e6; const status = String(res.statusCode); telemetry.by_status[status] = (telemetry.by_status[status] || 0) + 1; if (res.statusCode >= 500) { telemetry.errors += 1; telemetry.last_error_at = new Date().toISOString(); } const entry = { time: new Date().toISOString(), request_id: requestId, correlation_id: correlationId, method: req.method, path: safePath(req), status: res.statusCode, duration_ms: Math.round(durationMs * 10) / 10, user_id: req.user?.id || null, }; telemetry.recent.unshift(entry); telemetry.recent = telemetry.recent.slice(0, 50); if (enabled()) console.log(JSON.stringify({ level: res.statusCode >= 500 ? 'error' : 'info', event: 'http.request', ...entry })); }); next(); } export function getRequestTelemetry() { return { started_at: startedAt, ...telemetry, recent: telemetry.recent.map(item => ({ ...item })) }; }