import { Logger } from '@nestjs/common'; import type { NextFunction, Request, Response } from 'express'; /** * Eine Protokollzeile je API-Anfrage im Docker-Log (quick-261002): * `GET /modules/nextcloud-status/instances 200 34 ms user=admin`. * * Nur Methode, Pfad OHNE Abfrageteil (dort koennten Kennungen oder Token * stehen), Status, Dauer und Benutzername — nie Koerper, Cookies oder * Kopfzeilen. `/health` wird ausgelassen, der Docker-Healthcheck fragt es * alle paar Sekunden ab. Fehler (>= 500) als `error`, Abweisungen (>= 400) * als `warn`, langsame Anfragen (>= 3 s) mit Vermerk. */ const logger = new Logger('HTTP'); export const SLOW_REQUEST_MS = 3000; export function formatRequestLine(input: { method: string; url: string; status: number; ms: number; username?: string | null; }): string { const path = input.url.split('?')[0] ?? input.url; const slow = input.ms >= SLOW_REQUEST_MS ? ' LANGSAM' : ''; return `${input.method} ${path} ${input.status} ${input.ms} ms user=${input.username ?? '-'}${slow}`; } export function requestLogMiddleware(req: Request, res: Response, next: NextFunction): void { const url = req.originalUrl ?? req.url; if (url === '/health' || url.startsWith('/health?') || url.startsWith('/health/')) { next(); return; } const started = process.hrtime.bigint(); res.on('finish', () => { const ms = Number((process.hrtime.bigint() - started) / 1_000_000n); const username = (req as Request & { user?: { username?: string } }).user?.username; const line = formatRequestLine({ method: req.method, url, status: res.statusCode, ms, username, }); if (res.statusCode >= 500) logger.error(line); else if (res.statusCode >= 400 || ms >= SLOW_REQUEST_MS) logger.warn(line); else logger.log(line); }); next(); }