import { CallHandler, ExecutionContext, Injectable, NestInterceptor, } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { Request } from 'express'; /** Clés à masquer dans les logs (corps de requête) */ const SENSITIVE_KEYS = [ 'password', 'smtp_password', 'token', 'accessToken', 'refreshToken', 'secret', ]; function maskBody(body: unknown): unknown { if (body === null || body === undefined) return body; if (typeof body !== 'object') return body; const out: Record = {}; for (const [key, value] of Object.entries(body)) { const lower = key.toLowerCase(); const isSensitive = SENSITIVE_KEYS.some((s) => lower.includes(s)); out[key] = isSensitive ? '***' : value; } return out; } @Injectable() export class LogRequestInterceptor implements NestInterceptor { private readonly enabled: boolean; constructor() { this.enabled = process.env.LOG_API_REQUESTS === 'true' || process.env.LOG_API_REQUESTS === '1'; } intercept(context: ExecutionContext, next: CallHandler): Observable { if (!this.enabled) return next.handle(); const http = context.switchToHttp(); const req = http.getRequest(); const { method, url, body, query } = req; const hasBody = body && Object.keys(body).length > 0; const logLine = [ `[API] ${method} ${url}`, Object.keys(query || {}).length ? `query=${JSON.stringify(query)}` : '', hasBody ? `body=${JSON.stringify(maskBody(body))}` : '', ] .filter(Boolean) .join(' '); console.log(logLine); return next.handle().pipe( tap({ next: () => { // Optionnel: log du statut en fin de requête (si besoin plus tard) }, }), ); } }