generated from bastian/boilerplate
106 lines
3.8 KiB
TypeScript
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('*');
|
|
}
|
|
}
|