add upload rate limiter per IP to backend.
Nest throttler with nullcart-style default profile and upload override via throttleProfiles.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService, registerAs } from '@nestjs/config';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { getAltchaConfig, getAppConfig, getPostgresConfig } from './config';
|
||||
import { validate } from './config/validate';
|
||||
@@ -20,6 +22,22 @@ import { Config } from './types/Config';
|
||||
registerAs('altcha', getAltchaConfig)
|
||||
]
|
||||
}),
|
||||
ThrottlerModule.forRootAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
const { throttle } = configService.get('app') as Config['app'];
|
||||
|
||||
return {
|
||||
throttlers: [
|
||||
{
|
||||
name: 'default',
|
||||
ttl: throttle.ttlMs,
|
||||
limit: throttle.limit
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}),
|
||||
TypeOrmModule.forRootAsync({
|
||||
useFactory: (configService: ConfigService) => configService.get('postgres') as Config['postgres'],
|
||||
inject: [ConfigService]
|
||||
@@ -28,6 +46,12 @@ import { Config } from './types/Config';
|
||||
AltchaModule,
|
||||
StorageModule,
|
||||
UploadModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: ThrottlerGuard
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -41,6 +41,14 @@ export const getAppConfig = (): AppConfig => {
|
||||
corsOrigins: rawOrigins
|
||||
.split(',')
|
||||
.map(o => o.trim())
|
||||
.filter(Boolean)
|
||||
.filter(Boolean),
|
||||
throttle: {
|
||||
ttlMs: envInt('THROTTLE_TTL_MS'),
|
||||
limit: envInt('THROTTLE_LIMIT')
|
||||
},
|
||||
uploadThrottle: {
|
||||
ttlMs: envInt('THROTTLE_UPLOAD_TTL_MS'),
|
||||
limit: envInt('THROTTLE_UPLOAD_LIMIT')
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { getAppConfig } from './index';
|
||||
|
||||
const {
|
||||
uploadThrottle: { limit, ttlMs }
|
||||
} = getAppConfig();
|
||||
|
||||
export const throttleProfiles = {
|
||||
upload: { default: { limit, ttl: ttlMs } }
|
||||
} as const;
|
||||
@@ -75,6 +75,18 @@ class EnvironmentVariables {
|
||||
@Max(3650)
|
||||
NEVER_MODE_INACTIVITY_DAYS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1000)
|
||||
@Max(3_600_000)
|
||||
THROTTLE_TTL_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(1_000_000)
|
||||
THROTTLE_LIMIT: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1000)
|
||||
@@ -86,30 +98,6 @@ class EnvironmentVariables {
|
||||
@Min(1)
|
||||
@Max(10_000)
|
||||
THROTTLE_UPLOAD_LIMIT: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1000)
|
||||
@Max(3_600_000)
|
||||
THROTTLE_HOTLINK_PER_IP_TTL_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(1_000_000)
|
||||
THROTTLE_HOTLINK_PER_IP_LIMIT: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1000)
|
||||
@Max(3_600_000)
|
||||
THROTTLE_HOTLINK_PER_ID_TTL_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(1_000_000)
|
||||
THROTTLE_HOTLINK_PER_ID_LIMIT: number;
|
||||
}
|
||||
|
||||
export const validate = (config: Record<string, unknown>) => {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { NodeEnv } from './NodeEnv';
|
||||
import { ThrottleConfig } from './ThrottleConfig';
|
||||
|
||||
export type AppConfig = {
|
||||
port: number;
|
||||
nodeEnv: NodeEnv;
|
||||
corsOrigins: string[];
|
||||
throttle: ThrottleConfig;
|
||||
uploadThrottle: ThrottleConfig;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export type ThrottleConfig = {
|
||||
ttlMs: number;
|
||||
limit: number;
|
||||
};
|
||||
Reference in New Issue
Block a user