diff --git a/apps/server/package.json b/apps/server/package.json index d1704ce6..a8869302 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -75,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", diff --git a/apps/server/src/common/logger/pino.config.ts b/apps/server/src/common/logger/pino.config.ts index 7299a8e9..0b8cd11a 100644 --- a/apps/server/src/common/logger/pino.config.ts +++ b/apps/server/src/common/logger/pino.config.ts @@ -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, }), diff --git a/apps/server/src/common/middlewares/audit-context.middleware.ts b/apps/server/src/common/middlewares/audit-context.middleware.ts index f5066535..52956219 100644 --- a/apps/server/src/common/middlewares/audit-context.middleware.ts +++ b/apps/server/src/common/middlewares/audit-context.middleware.ts @@ -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; - } } diff --git a/apps/server/src/integrations/throttle/throttle.module.ts b/apps/server/src/integrations/throttle/throttle.module.ts index 7dab4a16..8f080e1d 100644 --- a/apps/server/src/integrations/throttle/throttle.module.ts +++ b/apps/server/src/integrations/throttle/throttle.module.ts @@ -15,6 +15,7 @@ import Redis from 'ioredis'; return { throttlers: [{ name: 'auth', ttl: 60_000, limit: 10 }], + errorMessage: 'Too many requests', storage: new ThrottlerStorageRedisService( new Redis({ host: redisConfig.host, diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 0f2a82a1..d47bf547 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -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); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8273db3..6599b07a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -586,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 @@ -7038,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==} @@ -18109,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: {}