Compare commits

...

2 Commits

Author SHA1 Message Date
Philipinho 61ae9703c9 ip 2026-03-30 15:32:19 +01:00
Philipinho 3ba5cff98b feat: rate limits 2026-03-30 14:25:53 +01:00
9 changed files with 104 additions and 33 deletions
+3
View File
@@ -44,6 +44,7 @@
"@langchain/core": "1.1.34",
"@langchain/textsplitters": "1.0.1",
"@modelcontextprotocol/sdk": "^1.27.1",
"@nest-lab/throttler-storage-redis": "^1.2.0",
"@nestjs-labs/nestjs-ioredis": "^11.0.4",
"@nestjs/bullmq": "^11.0.4",
"@nestjs/cache-manager": "^3.1.0",
@@ -58,6 +59,7 @@
"@nestjs/platform-socket.io": "^11.1.17",
"@nestjs/schedule": "^6.1.1",
"@nestjs/terminus": "^11.1.1",
"@nestjs/throttler": "^6.5.0",
"@nestjs/websockets": "^11.1.17",
"@node-saml/passport-saml": "^5.1.0",
"@react-email/components": "1.0.10",
@@ -73,6 +75,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.15.1",
"cookie": "^1.1.1",
"fastify-ip": "^2.0.0",
"fs-extra": "^11.3.4",
"happy-dom": "20.8.9",
"ioredis": "^5.10.1",
+2
View File
@@ -26,6 +26,7 @@ import KeyvRedis from '@keyv/redis';
import { LoggerModule } from './common/logger/logger.module';
import { ClsModule } from 'nestjs-cls';
import { NoopAuditModule } from './integrations/audit/audit.module';
import { ThrottleModule } from './integrations/throttle/throttle.module';
const enterpriseModules = [];
try {
@@ -83,6 +84,7 @@ try {
EventEmitterModule.forRoot(),
SecurityModule,
TelemetryModule,
ThrottleModule,
...enterpriseModules,
],
controllers: [AppController],
+6 -14
View File
@@ -50,20 +50,12 @@ export function createPinoConfig(): Params {
},
},
serializers: {
req: (req) => {
const forwardedFor = req.headers?.['x-forwarded-for'];
const ip =
req.headers?.['cf-connecting-ip'] ||
(typeof forwardedFor === 'string' ? forwardedFor.split(',')[0]?.trim() : undefined) ||
req.remoteAddress;
return {
method: req.method,
url: req.url,
ip,
userAgent: req.headers?.['user-agent'],
};
},
req: (req) => ({
method: req.method,
url: req.url,
ip: req.ip || req.remoteAddress,
userAgent: req.headers?.['user-agent'],
}),
res: (res) => ({
statusCode: res.statusCode,
}),
@@ -18,7 +18,8 @@ export class AuditContextMiddleware implements NestMiddleware {
use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) {
const workspaceId = (req as any).workspaceId ?? null;
const ipAddress = this.extractIpAddress(req);
const ipAddress = (req as any).ip ?? (req as any).socket?.remoteAddress ?? null;
const userAgent =
(req.headers['user-agent'] as string) ?? null;
@@ -35,21 +36,4 @@ export class AuditContextMiddleware implements NestMiddleware {
next();
}
private extractIpAddress(req: FastifyRequest['raw']): string | null {
const xForwardedFor = req.headers['x-forwarded-for'];
if (xForwardedFor) {
const ips = Array.isArray(xForwardedFor)
? xForwardedFor[0]
: xForwardedFor.split(',')[0];
return ips?.trim() ?? null;
}
const xRealIp = req.headers['x-real-ip'];
if (xRealIp) {
return Array.isArray(xRealIp) ? xRealIp[0] : xRealIp;
}
return (req as any).socket?.remoteAddress ?? null;
}
}
@@ -10,6 +10,7 @@ import {
UseGuards,
Logger,
} from '@nestjs/common';
import { SkipThrottle, ThrottlerGuard } from '@nestjs/throttler';
import { LoginDto } from './dto/login.dto';
import { AuthService } from './services/auth.service';
import { SessionService } from '../session/session.service';
@@ -33,6 +34,7 @@ import {
IAuditService,
} from '../../integrations/audit/audit.service';
@UseGuards(ThrottlerGuard)
@Controller('auth')
export class AuthController {
private readonly logger = new Logger(AuthController.name);
@@ -111,6 +113,7 @@ export class AuthController {
return workspace;
}
@SkipThrottle()
@UseGuards(JwtAuthGuard)
@HttpCode(HttpStatus.OK)
@Post('change-password')
@@ -173,6 +176,7 @@ export class AuthController {
return this.authService.verifyUserToken(verifyUserTokenDto, workspace.id);
}
@SkipThrottle()
@UseGuards(JwtAuthGuard)
@HttpCode(HttpStatus.OK)
@Post('collab-token')
@@ -183,6 +187,7 @@ export class AuthController {
return this.authService.getCollabToken(user, workspace.id);
}
@SkipThrottle()
@UseGuards(JwtAuthGuard)
@HttpCode(HttpStatus.OK)
@Post('logout')
@@ -0,0 +1,35 @@
import { Module } from '@nestjs/common';
import { ThrottlerModule } from '@nestjs/throttler';
import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis';
import { EnvironmentService } from '../environment/environment.service';
import { EnvironmentModule } from '../environment/environment.module';
import { parseRedisUrl } from '../../common/helpers';
import Redis from 'ioredis';
@Module({
imports: [
ThrottlerModule.forRootAsync({
imports: [EnvironmentModule],
useFactory: (environmentService: EnvironmentService) => {
const redisConfig = parseRedisUrl(environmentService.getRedisUrl());
return {
throttlers: [{ name: 'auth', ttl: 60_000, limit: 10 }],
errorMessage: 'Too many requests',
storage: new ThrottlerStorageRedisService(
new Redis({
host: redisConfig.host,
port: redisConfig.port,
password: redisConfig.password,
db: redisConfig.db,
family: redisConfig.family,
keyPrefix: 'throttle:',
}),
),
};
},
inject: [EnvironmentService],
}),
],
})
export class ThrottleModule {}
+2
View File
@@ -10,6 +10,7 @@ import { TransformHttpResponseInterceptor } from './common/interceptors/http-res
import { WsRedisIoAdapter } from './ws/adapter/ws-redis.adapter';
import fastifyMultipart from '@fastify/multipart';
import fastifyCookie from '@fastify/cookie';
import fastifyIp from 'fastify-ip';
import { InternalLogFilter } from './common/logger/internal-log-filter';
async function bootstrap() {
@@ -45,6 +46,7 @@ async function bootstrap() {
app.useWebSocketAdapter(redisIoAdapter);
await app.register(fastifyIp);
await app.register(fastifyMultipart);
await app.register(fastifyCookie);
+48
View File
@@ -493,6 +493,9 @@ importers:
'@modelcontextprotocol/sdk':
specifier: ^1.27.1
version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6)
'@nest-lab/throttler-storage-redis':
specifier: ^1.2.0
version: 1.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2)
'@nestjs-labs/nestjs-ioredis':
specifier: ^11.0.4
version: 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1)
@@ -535,6 +538,9 @@ importers:
'@nestjs/terminus':
specifier: ^11.1.1
version: 11.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/throttler':
specifier: ^6.5.0
version: 6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)
'@nestjs/websockets':
specifier: ^11.1.17
version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@@ -580,6 +586,9 @@ importers:
cookie:
specifier: ^1.1.1
version: 1.1.1
fastify-ip:
specifier: ^2.0.0
version: 2.0.0
fs-extra:
specifier: ^11.3.4
version: 11.3.4
@@ -2925,6 +2934,15 @@ packages:
'@napi-rs/wasm-runtime@1.1.1':
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
'@nest-lab/throttler-storage-redis@1.2.0':
resolution: {integrity: sha512-tMkUyo68NCKTR+zILk+EC35SMYBtDPZY2mCj7ZaCietWGVTnuP4zwq9ERYfvU6kJv6h8teNZrC6MJCmY6/dljw==}
peerDependencies:
'@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
'@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
'@nestjs/throttler': '>=6.0.0'
ioredis: '>=5.0.0'
reflect-metadata: ^0.2.1
'@nestjs-labs/nestjs-ioredis@11.0.4':
resolution: {integrity: sha512-4jPNOrxDiwNMIN5OLmsMWhA782kxv/ZBxkySX9l8n6sr55acHX/BciaFsOXVa/ILsm+Y7893y98/6WNhmEoiNQ==}
engines: {node: '>=16'}
@@ -3127,6 +3145,13 @@ packages:
'@nestjs/platform-express':
optional: true
'@nestjs/throttler@6.5.0':
resolution: {integrity: sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==}
peerDependencies:
'@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
'@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
reflect-metadata: ^0.1.13 || ^0.2.0
'@nestjs/websockets@11.1.17':
resolution: {integrity: sha512-YbwQ0QfVj0lxkKQhdIIgk14ZSVWDqGk1J8nNSN6SLjf36sVv58Ma5ro+dtQua8wj3l2Ub7JJCVFixEhKtYc/rQ==}
peerDependencies:
@@ -7016,6 +7041,10 @@ packages:
resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==}
hasBin: true
fastify-ip@2.0.0:
resolution: {integrity: sha512-7mQyAc7sapawpiriEFoJyQIs41nNIO42UCzgMKrjNGsIegnevj2VhOlXLLTa+q7cxXfJ5fDGmOAdQpaIgA9ObA==}
engines: {node: '>=20.x'}
fastify-plugin@5.0.1:
resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==}
@@ -13463,6 +13492,15 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
'@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2)':
dependencies:
'@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/throttler': 6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)
ioredis: 5.10.1
reflect-metadata: 0.2.2
tslib: 2.8.1
'@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1)':
dependencies:
'@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@@ -13643,6 +13681,12 @@ snapshots:
'@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)
tslib: 2.8.1
'@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)':
dependencies:
'@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)
reflect-metadata: 0.2.2
'@nestjs/websockets@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
'@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@@ -18072,6 +18116,10 @@ snapshots:
path-expression-matcher: 1.2.0
strnum: 2.2.1
fastify-ip@2.0.0:
dependencies:
fastify-plugin: 5.1.0
fastify-plugin@5.0.1: {}
fastify-plugin@5.1.0: {}