diff --git a/src/app.module.ts b/src/app.module.ts index 16b9fb2fb..19ffc4fe2 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,4 +1,6 @@ import { Module } from '@nestjs/common'; +import { APP_INTERCEPTOR } from '@nestjs/core'; +import { RateLimiterInterceptor, RateLimiterModule } from 'nestjs-rate-limiter'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { AuthModule } from './modules/auth/auth.module'; @@ -6,8 +8,14 @@ import { PrismaModule } from './modules/prisma/prisma.module'; import { UsersModule } from './modules/user/user.module'; @Module({ - imports: [PrismaModule, UsersModule, AuthModule], + imports: [PrismaModule, UsersModule, AuthModule, RateLimiterModule], controllers: [AppController], - providers: [AppService], + providers: [ + AppService, + { + provide: APP_INTERCEPTOR, + useClass: RateLimiterInterceptor, + }, + ], }) export class AppModule {} diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index dd35ac235..75a475100 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,5 +1,6 @@ import { Body, Controller, Post } from '@nestjs/common'; import { users } from '@prisma/client'; +import { RateLimit } from 'nestjs-rate-limiter'; import { OmitSecrets } from 'src/modules/prisma/prisma.interface'; import { RegisterDto } from './auth.dto'; import { AuthService } from './auth.service'; @@ -9,6 +10,11 @@ export class AuthController { constructor(private authService: AuthService) {} @Post('register') + @RateLimit({ + points: 10, + duration: 60, + errorMessage: 'Wait for 60 seconds before trying to create an account', + }) async update(@Body() data: RegisterDto): Promise> { return this.authService.register(data); } diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 666194bda..86f37c7d6 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { PrismaModule } from '../prisma/prisma.module'; +import { UsersService } from '../user/user.service'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; @Module({ imports: [PrismaModule], controllers: [AuthController], - providers: [AuthService], + providers: [AuthService, UsersService], }) export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 423173dd3..51f109d61 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -1,14 +1,37 @@ -import { Injectable } from '@nestjs/common'; -import { users, usersCreateInput } from '@prisma/client'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { users } from '@prisma/client'; +import { OmitSecrets } from '../prisma/prisma.interface'; import { PrismaService } from '../prisma/prisma.service'; +import { UsersService } from '../user/user.service'; +import { RegisterDto } from './auth.dto'; @Injectable() export class AuthService { - constructor(private prisma: PrismaService) {} + constructor(private prisma: PrismaService, private users: UsersService) {} - async register(data: usersCreateInput): Promise { - return this.prisma.users.create({ - data, + async register(data: RegisterDto): Promise> { + const email = data.email; + const emailSafe = this.users.getSafeEmail(email); + delete data.email; + + const users = await this.users.users({ + take: 1, + where: { emails: { some: { emailSafe } } }, + }); + if (users.length) + throw new HttpException( + 'A user with this email already exists', + HttpStatus.CONFLICT, + ); + + const user = await this.prisma.users.create({ + data: { + ...data, + emails: { + create: { email: email, emailSafe }, + }, + }, }); + return this.prisma.expose(user); } } diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index e374c131e..9445bf8f6 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -66,4 +66,10 @@ export class UsersService { }); return this.prisma.expose(user); } + + getSafeEmail(email: string) { + email = email.toLowerCase(); + email = `${email.split('@', 1)[0].split('+')}[0]@${email.split('@', 1)[1]}`; + return email; + } }