diff --git a/backend/README.md b/backend/README.md index 2487046..1fdfe76 100644 --- a/backend/README.md +++ b/backend/README.md @@ -96,4 +96,4 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors ## License Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). -########################################################################### \ No newline at end of file +########################################################################### diff --git a/backend/package-lock.json b/backend/package-lock.json index 1828890..4e61db5 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -2666,6 +2666,17 @@ } } }, + "node_modules/@nestjs/throttler": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.5.0.tgz", + "integrity": "sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==", + "license": "MIT", + "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" + } + }, "node_modules/@nestjs/typeorm": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.0.tgz", diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index ea92b37..03508c7 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,5 +1,8 @@ import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { ThrottlerModule } from '@nestjs/throttler'; +import { ThrottlerGuard } from './auth/throttler.guard'; +import { APP_GUARD } from '@nestjs/core'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { User } from './users/entities/user.entity'; @@ -30,6 +33,23 @@ import { Like } from './likes/entities/like.entity'; entities: [User, Post, Comment, Conversation, Message, Like], synchronize: true, }), + ThrottlerModule.forRoot([ + { + name: 'short', + ttl: 1000, + limit: 10, + }, + { + name: 'medium', + ttl: 10000, + limit: 50, + }, + { + name: 'long', + ttl: 60000, + limit: 200, + }, + ]), HealthModule, LoggingModule, CreatorsModule, @@ -39,7 +59,13 @@ import { Like } from './likes/entities/like.entity'; LikesModule, ], controllers: [AppController], - providers: [AppService], + providers: [ + AppService, + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, + ], }) export class AppModule { configure(consumer: MiddlewareConsumer) { diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts index 7d3d1ac..cc837c9 100644 --- a/backend/src/auth/auth.controller.ts +++ b/backend/src/auth/auth.controller.ts @@ -1,4 +1,5 @@ import { Controller, Post, Body } from '@nestjs/common'; +import { Throttle } from '@nestjs/throttler'; import { AuthService } from './auth.service'; @Controller('auth') @@ -6,10 +7,20 @@ export class AuthController { constructor(private authService: AuthService) {} @Post('login') + @Throttle({ auth: { limit: 5, ttl: 60000 } }) async login(@Body() body: { address: string }) { if (!this.authService.validateStellarAddress(body.address)) { return { error: 'Invalid Stellar address' }; } return this.authService.createSession(body.address); } + + @Post('register') + @Throttle({ auth: { limit: 5, ttl: 60000 } }) + async register(@Body() body: { address: string }) { + if (!this.authService.validateStellarAddress(body.address)) { + return { error: 'Invalid Stellar address' }; + } + return this.authService.createSession(body.address); + } } diff --git a/backend/src/auth/throttler.guard.ts b/backend/src/auth/throttler.guard.ts new file mode 100644 index 0000000..3dd196f --- /dev/null +++ b/backend/src/auth/throttler.guard.ts @@ -0,0 +1,17 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { ThrottlerGuard as NestThrottlerGuard } from '@nestjs/throttler'; + +@Injectable() +export class ThrottlerGuard extends NestThrottlerGuard implements CanActivate { + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest<{ url?: string }>(); + const url = request.url ?? ''; + + // Exclude health check routes from rate limiting + if (url === '/health' || url.startsWith('/health/')) { + return true; + } + + return super.canActivate(context); + } +} diff --git a/backend/src/main.ts b/backend/src/main.ts index f951358..41968b7 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -11,6 +11,10 @@ async function bootstrap() { transform: true, }), ); + + // Enable CORS + app.enableCors(); + await app.listen(process.env.PORT ?? 3000); } bootstrap();