- ThrottlerGuard runs first, so requests with wrong tokens or API keys count against the rate limit before any database lookup. - TRUST_PROXY (hop count or Express list) replaces the hard-coded 1. - Every API answer except the public lot pages is Cache-Control: no-store without ETag; helmet sends default-src 'none' / frame-ancestors 'none' (Swagger keeps the page policy when enabled) and same-site CORP. - Request log lines carry the path only - no query strings (the SSO callback's code and state no longer reach the logs). - In production the server refuses to start without an https FRONTEND_ORIGIN or with COOKIE_SECURE=false. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
34 lines
1.1 KiB
TypeScript
34 lines
1.1 KiB
TypeScript
import { Logger } from '@nestjs/common';
|
|
import type { NextFunction, Request, Response } from 'express';
|
|
|
|
const logger = new Logger('HTTP');
|
|
|
|
/**
|
|
* One line per request in stdout/kubectl logs: method, path, status, and
|
|
* timing - never the body/headers, so passwords and tokens never end up in
|
|
* logs. Without this, a normal (non-error) request leaves zero trace,
|
|
* which makes "did the request even arrive?" the hardest question to
|
|
* answer when something looks broken from outside the cluster.
|
|
*/
|
|
export function requestLoggerMiddleware(
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction,
|
|
): void {
|
|
const start = Date.now();
|
|
res.on('finish', () => {
|
|
const duration = Date.now() - start;
|
|
// Path only: query strings can carry secrets (the SSO callback's
|
|
// authorization code and state, search terms with personal data).
|
|
const line = `${req.method} ${req.baseUrl}${req.path} ${res.statusCode} ${duration}ms`;
|
|
if (res.statusCode >= 500) {
|
|
logger.error(line);
|
|
} else if (res.statusCode >= 400) {
|
|
logger.warn(line);
|
|
} else {
|
|
logger.log(line);
|
|
}
|
|
});
|
|
next();
|
|
}
|