Files
flat-pilot/apps/backend/src/app.module.ts
Bastian Wagner e62673ac11 mvp
2026-07-20 09:01:36 +02:00

106 lines
3.8 KiB
TypeScript

import { createHash } from 'node:crypto';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { APP_FILTER, APP_GUARD, Reflector } from '@nestjs/core';
import {
ThrottlerGuard,
ThrottlerModule,
type ThrottlerGenerateKeyFunction,
type ThrottlerGetTrackerFunction,
} from '@nestjs/throttler';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuditModule } from './audit/audit.module';
import { AuthModule } from './auth/auth.module';
import { CsrfGuard } from './auth/guards/csrf.guard';
import { PermissionsGuard } from './auth/guards/permissions.guard';
import { AppConfigModule } from './config/config.module';
import { AppConfigService } from './config/config.service';
import { ApiExceptionFilter } from './common/errors/api-exception.filter';
import { RequestIdMiddleware } from './common/request-context/request-id.middleware';
import { SENSITIVE_RATE_LIMIT_KEY } from './common/rate-limit/sensitive-rate-limit.decorator';
import { typeOrmOptionsFactory } from './database/typeorm-options';
import { DatabaseModule } from './database/database.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { HealthModule } from './health/health.module';
import { ItemsModule } from './items/items.module';
import { NotificationsModule } from './notifications/notifications.module';
import { ProjectsModule } from './projects/projects.module';
import { RolesModule } from './roles/roles.module';
import { RenovationModule } from './renovation/renovation.module';
import { SessionsModule } from './sessions/sessions.module';
import { UsersModule } from './users/users.module';
const getIpTracker: ThrottlerGetTrackerFunction = (req) => {
const ip = typeof req['ip'] === 'string' ? req['ip'] : undefined;
const socket = req['socket'] as { remoteAddress?: unknown } | undefined;
const remoteAddress =
typeof socket?.remoteAddress === 'string'
? socket.remoteAddress
: undefined;
return ip ?? remoteAddress ?? 'unknown';
};
const generateGlobalKey: ThrottlerGenerateKeyFunction = (
_context,
tracker,
throttlerName,
) => createHash('sha256').update(`${throttlerName}:${tracker}`).digest('hex');
@Module({
imports: [
AppConfigModule,
TypeOrmModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: typeOrmOptionsFactory,
}),
DatabaseModule,
DashboardModule,
ThrottlerModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService, Reflector],
useFactory: (config: AppConfigService, reflector: Reflector) => ({
getTracker: getIpTracker,
generateKey: generateGlobalKey,
throttlers: [
{
name: 'default',
ttl: config.rateLimit.global.windowSeconds * 1000,
limit: config.rateLimit.global.maxRequests,
},
{
name: 'sensitive',
ttl: config.rateLimit.sensitive.windowSeconds * 1000,
limit: config.rateLimit.sensitive.maxRequests,
skipIf: (context) =>
!reflector.getAllAndOverride<boolean>(SENSITIVE_RATE_LIMIT_KEY, [
context.getHandler(),
context.getClass(),
]),
},
],
}),
}),
AuthModule,
UsersModule,
RolesModule,
SessionsModule,
NotificationsModule,
ProjectsModule,
RenovationModule,
AuditModule,
ItemsModule,
HealthModule,
],
providers: [
{ provide: APP_FILTER, useClass: ApiExceptionFilter },
{ provide: APP_GUARD, useClass: ThrottlerGuard },
{ provide: APP_GUARD, useClass: CsrfGuard },
{ provide: APP_GUARD, useClass: PermissionsGuard },
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): void {
consumer.apply(RequestIdMiddleware).forRoutes('*');
}
}