diff --git a/src/modules/admin/admin.module.ts b/src/modules/admin/admin.module.ts index 3eda9af..363c778 100644 --- a/src/modules/admin/admin.module.ts +++ b/src/modules/admin/admin.module.ts @@ -1,9 +1,15 @@ import { Module } from '@nestjs/common'; -import { TestsController } from './controllers/tests/tests.controller'; +import { AdminTestsController } from './tests/admin-tests.controller'; import { TestsModule } from '../tests/tests.module'; +import { AuthModule } from '../auth/auth.module'; +import { HashingModule } from '@/common/modules/hashing/hashing.module'; +import { UsersModule } from '../users/users.module'; +import { AdminAuthController } from './auth/admin-auth.controller'; +import { AdminAuthService } from './auth/providers/admin-auth.service'; @Module({ - imports: [TestsModule], - controllers: [TestsController], + imports: [TestsModule, AuthModule, HashingModule, UsersModule], + controllers: [AdminTestsController, AdminAuthController], + providers: [AdminAuthService], }) export class AdminModule {} diff --git a/src/modules/admin/auth/admin-auth.controller.ts b/src/modules/admin/auth/admin-auth.controller.ts new file mode 100644 index 0000000..1660610 --- /dev/null +++ b/src/modules/admin/auth/admin-auth.controller.ts @@ -0,0 +1,21 @@ +import { Body, Controller, HttpCode, Post } from '@nestjs/common'; +import { AdminAuthService } from './providers/admin-auth.service'; +import { AdminLoginDTO } from './dtos/admin-login.dto'; +import { Public } from '@/modules/auth/decorators/public.decorator'; + +@Controller('admin/auth') +export class AdminAuthController { + constructor( + /** + * Inject Admin Auth Service + */ + private readonly adminAuthService: AdminAuthService, + ) {} + + @Public() + @Post('login') + @HttpCode(200) + public async login(@Body() adminLoginDto: AdminLoginDTO) { + return await this.adminAuthService.login(adminLoginDto); + } +} diff --git a/src/modules/admin/auth/dtos/admin-login.dto.ts b/src/modules/admin/auth/dtos/admin-login.dto.ts new file mode 100644 index 0000000..0e22877 --- /dev/null +++ b/src/modules/admin/auth/dtos/admin-login.dto.ts @@ -0,0 +1,23 @@ +import { PhoneDTO } from '@/modules/auth/dtos/login.dto'; +import { IsNotEmpty, IsString } from 'class-validator'; +import { i18nValidationMessage as t } from 'nestjs-i18n'; + +export class AdminLoginDTO extends PhoneDTO { + // @ApiProperty({ + // type: 'string', + // description: 'Password for User Account', + // example: 'Password1@', + // required: true, + // }) + @IsNotEmpty({ + message: t('validation.requiredField', { + field: '$t(auth.fields.password)', + }), + }) + @IsString({ + message: t('validation.wrongFieldFormat', { + field: '$t(auth.fields.password)', + }), + }) + password: string; +} diff --git a/src/modules/admin/auth/http/admin-login.post.http b/src/modules/admin/auth/http/admin-login.post.http new file mode 100644 index 0000000..7d4ae19 --- /dev/null +++ b/src/modules/admin/auth/http/admin-login.post.http @@ -0,0 +1,7 @@ +POST http://localhost:3000/admin/auth/login +Content-Type: application/json + +{ + "phone": "0933302636", + "password": "Password1@" +} \ No newline at end of file diff --git a/src/modules/admin/auth/providers/admin-auth.service.ts b/src/modules/admin/auth/providers/admin-auth.service.ts new file mode 100644 index 0000000..1afbe72 --- /dev/null +++ b/src/modules/admin/auth/providers/admin-auth.service.ts @@ -0,0 +1,52 @@ +import { HashingProvider } from '@/common/modules/hashing/providers/hashing.provider'; +import { GenerateTokenProvider } from '@/modules/auth/providers/generate-token.provider'; +import { UsersService } from '@/modules/users/providers/users.service'; +import { + ForbiddenException, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { AdminLoginDTO } from '../dtos/admin-login.dto'; +import { Role } from '@/common/enums/roles.enum'; +import { randomUUID } from 'crypto'; + +@Injectable() +export class AdminAuthService { + constructor( + /** + * Inject Users Service + */ + private readonly usersService: UsersService, + + /** + * Inject Generate Tokens Provider + */ + private readonly generateTokenProvider: GenerateTokenProvider, + + /** + * Inject Hashing Provider + */ + private readonly hashingProvider: HashingProvider, + ) {} + + public async login(adminLoginDto: AdminLoginDTO) { + const { phone, password } = adminLoginDto; + + const user = await this.usersService.findOneByPhone(phone); + const isPasswordCorrect = await this.hashingProvider.compare( + password, + user?.password || '', + ); + + if (!user || !isPasswordCorrect) { + throw new UnauthorizedException('auth.errors.wrongPhoneOrPass'); + } + + if ([Role.ADMIN, Role.SUPERUSER].includes(user.role)) { + const jti = randomUUID(); + return await this.generateTokenProvider.generateTokens(user, jti); + } + + throw new ForbiddenException('auth.errors.accessDenied'); + } +} diff --git a/src/modules/admin/controllers/tests/tests.controller.ts b/src/modules/admin/tests/admin-tests.controller.ts similarity index 94% rename from src/modules/admin/controllers/tests/tests.controller.ts rename to src/modules/admin/tests/admin-tests.controller.ts index eb834b2..1cca2c9 100644 --- a/src/modules/admin/controllers/tests/tests.controller.ts +++ b/src/modules/admin/tests/admin-tests.controller.ts @@ -5,7 +5,7 @@ import { TestsService } from '@/modules/tests/providers/tests.service'; import { Body, Controller, Post } from '@nestjs/common'; @Controller('admin/tests') -export class TestsController { +export class AdminTestsController { constructor( /** * Inject Tests Service diff --git a/src/modules/admin/http/tests/tests.post.http b/src/modules/admin/tests/http/admin-tests.post.http similarity index 100% rename from src/modules/admin/http/tests/tests.post.http rename to src/modules/admin/tests/http/admin-tests.post.http diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 4f9e2b2..3368189 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -48,5 +48,6 @@ import { AuthRedisProvider } from './providers/auth-redis.provider'; useClass: GlobalAuthGuard, }, ], + exports: [GenerateTokenProvider], }) export class AuthModule {} diff --git a/src/modules/auth/http/login.post.endpoints.http b/src/modules/auth/http/login.post.endpoints.http index a2ae2aa..a66a9c6 100644 --- a/src/modules/auth/http/login.post.endpoints.http +++ b/src/modules/auth/http/login.post.endpoints.http @@ -3,6 +3,5 @@ Content-Type: application/json lang: fa { - "phone": "09333026363", - "password": "Password1@" + "phone": "09121111111" } \ No newline at end of file diff --git a/src/modules/auth/http/verify-otp.post.endpoints.http b/src/modules/auth/http/verify-otp.post.endpoints.http index 6493b47..d08a021 100644 --- a/src/modules/auth/http/verify-otp.post.endpoints.http +++ b/src/modules/auth/http/verify-otp.post.endpoints.http @@ -2,6 +2,6 @@ POST http://localhost:3000/auth/verify-otp Content-Type: application/json { - "phone": "09333026363", - "otp": "44875" + "phone": "09121111111", + "otp": "69210" } \ No newline at end of file diff --git a/src/modules/users/http/users.get.endpoints.http b/src/modules/users/http/users.get.endpoints.http index 706a2e1..8a40ad2 100644 --- a/src/modules/users/http/users.get.endpoints.http +++ b/src/modules/users/http/users.get.endpoints.http @@ -1,2 +1,2 @@ GET http://localhost:3000/users/profile -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEwLCJwaG9uZSI6IjA5MzMzMDI2MzYzIiwiaWF0IjoxNzgwMjIzNDExLCJleHAiOjE3ODAyMjcwMTEsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.FNTIrk6FKO8P88yKnRCybZ6E5N3KlJaCHqZt5DMVgds +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjExLCJwaG9uZSI6IjA5MTIxMTExMTExIiwiaWF0IjoxNzgwMzE3OTU0LCJleHAiOjE3ODAzMjE1NTQsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.YDOb0tXVdSPyBnjuBvby4acp6BP_NyXK0gtLdVEoMp4 diff --git a/src/modules/users/http/users.patch.endpoints.http b/src/modules/users/http/users.patch.endpoints.http index f6f36f0..7328ff5 100644 --- a/src/modules/users/http/users.patch.endpoints.http +++ b/src/modules/users/http/users.patch.endpoints.http @@ -1,6 +1,6 @@ PATCH http://localhost:3000/users/profile Content-Type: application/json -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEwLCJwaG9uZSI6IjA5MzMzMDI2MzYzIiwiaWF0IjoxNzc5OTIzMzU4LCJleHAiOjE3Nzk5MjY5NTgsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.1XFNvOFdfSc2LRybiCNlRPAdP5JNWpCu5MeJCZIE9kc +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjExLCJwaG9uZSI6IjA5MTIxMTExMTExIiwiaWF0IjoxNzgwMzE3OTU0LCJleHAiOjE3ODAzMjE1NTQsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.YDOb0tXVdSPyBnjuBvby4acp6BP_NyXK0gtLdVEoMp4 lang: fa // { @@ -9,8 +9,8 @@ lang: fa // } { - "firstName": "Radmehr", - "lastName": "Tarnas", + "firstName": "تست", + "lastName": "تستعلی", "password": "Password1@", "confirmPassword": "Password1@" } \ No newline at end of file