Server-Timing

This commit is contained in:
2026-05-31 09:18:34 +03:30
parent 0673729a4d
commit 9eddcbd2a7
3 changed files with 58 additions and 4 deletions
+2 -2
View File
@@ -42,5 +42,5 @@ SMS_SENDER=
# SMS_TRANSPORT=soap
# SMS_DOMAIN=0098
# Minimum minutes before a booking can be scheduled (default 60)
BOOKING_MIN_LEAD_MINUTES=60
# Optional: log Server-Timing to PM2 stdout (total;dur=… per request)
# LOG_SERVER_TIMING=true
@@ -0,0 +1,42 @@
import {
CallHandler,
ExecutionContext,
Injectable,
Logger,
NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
@Injectable()
export class ServerTimingInterceptor implements NestInterceptor {
private readonly logger = new Logger(ServerTimingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
const http = context.switchToHttp();
const response = http.getResponse<{
setHeader(name: string, value: string): void;
getHeader(name: string): string | number | string[] | undefined;
}>();
const request = http.getRequest<{ method?: string; url?: string }>();
const start = performance.now();
return next.handle().pipe(
finalize(() => {
const dur = Math.max(0, Math.round(performance.now() - start));
const existing = response.getHeader('Server-Timing');
const total = `total;dur=${dur}`;
response.setHeader(
'Server-Timing',
existing ? `${String(existing)}, ${total}` : total,
);
if (process.env.LOG_SERVER_TIMING === 'true') {
const route = request.url ?? '';
this.logger.log(
`${request.method ?? '?'} ${route} total;dur=${dur}`,
);
}
}),
);
}
}
+14 -2
View File
@@ -5,17 +5,28 @@ import { NestExpressApplication } from '@nestjs/platform-express';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { join } from 'path';
import { TypeOrmExceptionFilter } from './common/filters/typeorm-exception.filter';
import { ServerTimingInterceptor } from './common/interceptors/server-timing.interceptor';
import { AppModule } from './app.module';
const CORS_EXPOSE_HEADERS = ['Server-Timing'];
function configureCors(app: Awaited<ReturnType<typeof NestFactory.create>>) {
const raw = process.env.CORS_ORIGINS?.trim();
if (raw) {
const origins = raw.split(',').map((o) => o.trim()).filter(Boolean);
app.enableCors({ origin: origins, credentials: true });
app.enableCors({
origin: origins,
credentials: true,
exposedHeaders: CORS_EXPOSE_HEADERS,
});
return;
}
if (process.env.NODE_ENV !== 'production') {
app.enableCors({ origin: true, credentials: true });
app.enableCors({
origin: true,
credentials: true,
exposedHeaders: CORS_EXPOSE_HEADERS,
});
}
}
@@ -32,6 +43,7 @@ async function bootstrap() {
}),
);
app.useGlobalFilters(new TypeOrmExceptionFilter());
app.useGlobalInterceptors(new ServerTimingInterceptor());
const swaggerConfig = new DocumentBuilder()
.setTitle('Montime API')