From 9996ea3b092a60942ba0f9efb26da3b3b3f4c6ab Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Mon, 24 Nov 2025 16:08:16 +0330 Subject: [PATCH] update auth --- src/modules/admin/providers/admin.service.ts | 6 +- src/modules/auth/auth.module.ts | 3 +- .../auth/controllers/admin-auth.controller.ts | 23 +- .../auth/controllers/auth.controller.ts | 20 +- .../entities/refresh-token.entity.ts | 18 +- src/modules/auth/services/auth.service.ts | 78 ++--- src/modules/auth/services/tokens.service.ts | 306 +++--------------- src/modules/users/user.module.ts | 3 +- 8 files changed, 91 insertions(+), 366 deletions(-) rename src/modules/{users => auth}/entities/refresh-token.entity.ts (51%) diff --git a/src/modules/admin/providers/admin.service.ts b/src/modules/admin/providers/admin.service.ts index ae91780..dfb9753 100644 --- a/src/modules/admin/providers/admin.service.ts +++ b/src/modules/admin/providers/admin.service.ts @@ -28,7 +28,7 @@ export class AdminService { async findById(adminId: string, restId: string): Promise { const admin = await this.adminRepository.findOne( { id: adminId, roles: { restaurant: { id: restId } } }, - { populate: ['roles', 'roles.role'], distinct: true }, + { populate: ['roles', 'roles.role'] }, ); return admin; } @@ -131,7 +131,7 @@ export class AdminService { } // Update role if roleId is provided - if (roleId) { + if (roleId ) { const role = await this.em.findOne(Role, { id: roleId }); if (!role) { throw new NotFoundException('Role not found'); @@ -147,6 +147,8 @@ export class AdminService { // Update existing role existingAdminRole.role = role; } else { + const restaurant = await this.em.findOne(Restaurant, { id: restId }); + if (!restaurant) throw new NotFoundException('Restaurant not found'); // Create new AdminRole const newAdminRole = this.em.create(AdminRole, { admin, diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 8a878b7..78b160b 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -12,6 +12,7 @@ import { MikroOrmModule } from '@mikro-orm/nestjs'; import { User } from '../users/entities/user.entity'; import { AdminAuthController } from './controllers/admin-auth.controller'; import { AdminAuthGuard } from './guards/adminAuth.guard'; +import { RefreshToken } from './entities/refresh-token.entity'; @Module({ imports: [ @@ -29,7 +30,7 @@ import { AdminAuthGuard } from './guards/adminAuth.guard'; }), forwardRef(() => AdminModule), forwardRef(() => RestaurantsModule), - MikroOrmModule.forFeature([User]), + MikroOrmModule.forFeature([User, RefreshToken]), ], controllers: [AuthController, AdminAuthController], providers: [AuthService, TokensService, AdminAuthGuard], diff --git a/src/modules/auth/controllers/admin-auth.controller.ts b/src/modules/auth/controllers/admin-auth.controller.ts index 87627c8..cff64b6 100644 --- a/src/modules/auth/controllers/admin-auth.controller.ts +++ b/src/modules/auth/controllers/admin-auth.controller.ts @@ -19,7 +19,7 @@ export class AdminAuthController { @ApiResponse({ status: 201, description: 'OTP requested successfully' }) @ApiResponse({ status: 400, description: 'Invalid mobile number' }) otpRequest(@Body() dto: RequestOtpDto) { - return this.authService.requestOtpAdmin(dto); + return this.authService.requestOtp(dto, true); } @Post('otp/verify') @@ -31,28 +31,11 @@ export class AdminAuthController { return this.authService.verifyOtpAdmin(dto.phone, dto.slug, dto.otp); // assuming dto has `otp` property } - // @Post('admin/otp/request') - // @ApiOperation({ summary: 'Request OTP for login or signup' }) - // @ApiBody({ type: RequestOtpDto, description: 'Mobile number to receive OTP' }) - // @ApiResponse({ status: 201, description: 'OTP requested successfully' }) - // @ApiResponse({ status: 400, description: 'Invalid mobile number' }) - // adminOtpRequest(@Body() dto: RequestOtpDto) { - // return this.authService.requestOtpAdmin(dto); - // } - - // @Post('admin/otp/verify') - // @ApiOperation({ summary: 'Verify OTP code' }) - // @ApiBody({ type: VerifyOtpDto, description: 'Mobile number and OTP code' }) - // @ApiResponse({ status: 200, description: 'OTP verified successfully' }) - // @ApiResponse({ status: 400, description: 'Invalid OTP or expired' }) - // adminOtpVerify(@Body() dto: VerifyOtpDto) { - // return this.authService.verifyOtpAdmin(dto.phone, dto.otp); // assuming dto has `otp` property - // } @RefreshTokenRateLimit() - @ApiOperation({ summary: 'refresh the user access token / refresh token' }) + @ApiOperation({ summary: 'refresh the admin access token / refresh token' }) @Post('refresh') refreshToken(@Body() refreshTokenDto: RefreshTokenDto) { - return this.authService.refreshTokenAdmin(refreshTokenDto.refreshToken); + return this.authService.refreshToken(refreshTokenDto.refreshToken, true); } } diff --git a/src/modules/auth/controllers/auth.controller.ts b/src/modules/auth/controllers/auth.controller.ts index b8e2ca8..8300834 100644 --- a/src/modules/auth/controllers/auth.controller.ts +++ b/src/modules/auth/controllers/auth.controller.ts @@ -19,7 +19,7 @@ export class AuthController { @ApiResponse({ status: 201, description: 'OTP requested successfully' }) @ApiResponse({ status: 400, description: 'Invalid mobile number' }) otpRequest(@Body() dto: RequestOtpDto) { - return this.authService.requestOtp(dto); + return this.authService.requestOtp(dto, false); } @Post('otp/verify') @@ -31,28 +31,12 @@ export class AuthController { return this.authService.verifyOtp(dto.phone, dto.slug, dto.otp); // assuming dto has `otp` property } - // @Post('admin/otp/request') - // @ApiOperation({ summary: 'Request OTP for login or signup' }) - // @ApiBody({ type: RequestOtpDto, description: 'Mobile number to receive OTP' }) - // @ApiResponse({ status: 201, description: 'OTP requested successfully' }) - // @ApiResponse({ status: 400, description: 'Invalid mobile number' }) - // adminOtpRequest(@Body() dto: RequestOtpDto) { - // return this.authService.requestOtpAdmin(dto); - // } - // @Post('admin/otp/verify') - // @ApiOperation({ summary: 'Verify OTP code' }) - // @ApiBody({ type: VerifyOtpDto, description: 'Mobile number and OTP code' }) - // @ApiResponse({ status: 200, description: 'OTP verified successfully' }) - // @ApiResponse({ status: 400, description: 'Invalid OTP or expired' }) - // adminOtpVerify(@Body() dto: VerifyOtpDto) { - // return this.authService.verifyOtpAdmin(dto.phone, dto.otp); // assuming dto has `otp` property - // } @RefreshTokenRateLimit() @ApiOperation({ summary: 'refresh the user access token / refresh token' }) @Post('refresh') refreshToken(@Body() refreshTokenDto: RefreshTokenDto) { - return this.authService.refreshToken(refreshTokenDto.refreshToken); + return this.authService.refreshToken(refreshTokenDto.refreshToken, false); } } diff --git a/src/modules/users/entities/refresh-token.entity.ts b/src/modules/auth/entities/refresh-token.entity.ts similarity index 51% rename from src/modules/users/entities/refresh-token.entity.ts rename to src/modules/auth/entities/refresh-token.entity.ts index fe49646..ae1fa55 100644 --- a/src/modules/users/entities/refresh-token.entity.ts +++ b/src/modules/auth/entities/refresh-token.entity.ts @@ -1,7 +1,11 @@ -import { Entity, ManyToOne, Opt, Property } from '@mikro-orm/core'; +import { Entity, Enum, Property } from '@mikro-orm/core'; import { BaseEntity } from '../../../common/entities/base.entity'; -import { Restaurant } from '../../../modules/restaurants/entities/restaurant.entity'; + +export enum RefreshTokenType { + USER = 'user', + ADMIN = 'admin', +} @Entity({ tableName: 'refreshtokens' }) export class RefreshToken extends BaseEntity { @@ -11,12 +15,12 @@ export class RefreshToken extends BaseEntity { @Property() ownerId!: string; - @ManyToOne(() => Restaurant, { deleteRule: 'cascade' }) - restaurant!: Restaurant; + @Property() + restId!: string; + + @Enum(() => RefreshTokenType) + type!: RefreshTokenType; @Property({ type: 'timestamptz' }) expiresAt!: Date; - - @Property({ default: false }) - isRevoked: boolean & Opt; } diff --git a/src/modules/auth/services/auth.service.ts b/src/modules/auth/services/auth.service.ts index 10d7f3e..f9f80ba 100644 --- a/src/modules/auth/services/auth.service.ts +++ b/src/modules/auth/services/auth.service.ts @@ -1,4 +1,4 @@ -import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common'; +import { Injectable, BadRequestException } from '@nestjs/common'; import { RequestOtpDto } from '../dto/request-otp.dto'; import { CacheService } from '../../utils/cache.service'; import { SmsService } from '../../utils/sms.service'; @@ -10,11 +10,12 @@ import { AuthMessage, RestMessage } from 'src/common/enums/message.enum'; import { AdminRepository } from 'src/modules/admin/repositories/admin.repository'; import { AdminLoginTransformer } from '../transformers/admin-login.transformer'; import { UserLoginTransformer } from '../transformers/user-login.transformer'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class AuthService { readonly ADMIN_PERMISSIONS_KEY = 'admin-perms'; - + private OTP_EXPIRATION_TIME: number; constructor( private readonly cacheService: CacheService, private readonly smsService: SmsService, @@ -22,42 +23,35 @@ export class AuthService { private readonly tokensService: TokensService, private readonly restRepository: RestRepository, private readonly adminRepository: AdminRepository, - ) {} + private readonly configService: ConfigService, + ) { + this.OTP_EXPIRATION_TIME = this.configService.getOrThrow('OTP_EXPIRATION_TIME'); + } + private userOtpKey(restaurantSlug: string, phone: string) { + return `otp:${restaurantSlug}:${phone}`; + } + private adminOtpKey(restaurantSlug: string, phone: string) { + return `otp-admin:${restaurantSlug}:${phone}`; + } - async requestOtp(dto: RequestOtpDto) { + async requestOtp(dto: RequestOtpDto, isAdmin: boolean) { const { phone, slug } = dto; const code = this.generateOtpCode(); - - await this.cacheService.set(`otp:${slug}:${phone}`, code, 160); + const key = isAdmin ? this.adminOtpKey(slug, phone) : this.userOtpKey(slug, phone); + await this.cacheService.set(key, code, this.OTP_EXPIRATION_TIME); // await this.smsService.sendOtp(phone, code); return { code }; } - async requestOtpAdmin(dto: RequestOtpDto) { - const { phone, slug } = dto; - - const admin = await this.adminRepository.findByPhoneAndRestaurantSlug(phone, slug); - if (!admin) { - throw new NotFoundException(AuthMessage.ADMIN_NOT_FOUND); - } - - const code = this.generateOtpCode(); - - await this.cacheService.set(`otp-admin:${slug}:${phone}`, code, 160); - - // await this.smsService.sendOtp(phone, code); - - return code; - } - async verifyOtp(phone: string, slug: string, code: string) { - const cachedCode = await this.cacheService.get(`otp:${slug}:${phone}`); + const key = this.userOtpKey(slug, phone); + const cachedCode = await this.cacheService.get(key); if (!cachedCode) throw new BadRequestException('OTP expired or not found'); if (cachedCode !== code) throw new BadRequestException('Invalid OTP'); - await this.cacheService.del(`otp:${slug}:${phone}`); + await this.cacheService.del(key); const user = await this.userService.findOrCreateByPhone(phone); @@ -67,7 +61,7 @@ export class AuthService { throw new BadRequestException(RestMessage.NOT_FOUND); } - const tokens = await this.tokensService.generateTokens(user.id, rest); + const tokens = await this.tokensService.generateAccessAndRefreshToken(user.id, rest.id, false); const userResponse = UserLoginTransformer.transform(user, rest); @@ -75,18 +69,18 @@ export class AuthService { } async verifyOtpAdmin(phone: string, slug: string, code: string) { - const cachedCode = await this.cacheService.get(`otp-admin:${slug}:${phone}`); + const key = this.adminOtpKey(slug, phone); + const cachedCode = await this.cacheService.get(key); if (!cachedCode) throw new BadRequestException('OTP expired or not found'); if (cachedCode !== code) throw new BadRequestException('Invalid OTP'); - - await this.cacheService.del(`otp-admin:${slug}:${phone}`); + await this.cacheService.del(key); const admin = await this.adminRepository.findByPhoneAndRestaurantSlug(phone, slug); if (!admin) { - throw new BadRequestException(AuthMessage.USER_NOT_FOUND); + throw new BadRequestException(AuthMessage.ADMIN_NOT_FOUND); } const rest = await this.restRepository.findOne({ slug }); @@ -95,7 +89,7 @@ export class AuthService { throw new BadRequestException(RestMessage.NOT_FOUND); } - const tokens = await this.tokensService.generateTokensAdmin(admin.id, rest); + const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, rest.id, true); const adminResponse = await AdminLoginTransformer.transform(admin, rest); @@ -107,25 +101,7 @@ export class AuthService { return code.toString(); } - refreshToken(oldRefreshToken: string) { - return this.tokensService.refreshToken(oldRefreshToken); - } - - refreshTokenAdmin(oldRefreshToken: string) { - return this.tokensService.refreshTokenAdmin(oldRefreshToken); - } - - // async getAdminPerms(adminId: string, restId: string) { - // const cachedPerms = await this.cacheService.get(`${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`); - // // eslint-disable-next-line @typescript-eslint/no-unsafe-return - // return cachedPerms ? JSON.parse(cachedPerms) : []; - // } - - async setAdminPerms(adminId: string, restId: string, permissions: string) { - return this.cacheService.set( - `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`, - JSON.stringify(permissions), - 3600, - ); + refreshToken(oldRefreshToken: string, isAdmin: boolean) { + return this.tokensService.refreshToken(oldRefreshToken, isAdmin); } } diff --git a/src/modules/auth/services/tokens.service.ts b/src/modules/auth/services/tokens.service.ts index 70b8755..c283e8f 100755 --- a/src/modules/auth/services/tokens.service.ts +++ b/src/modules/auth/services/tokens.service.ts @@ -1,19 +1,13 @@ import { randomBytes } from 'node:crypto'; - import { EntityManager } from '@mikro-orm/postgresql'; -import { BadRequestException, Injectable, Logger, UnauthorizedException } from '@nestjs/common'; +import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import dayjs from 'dayjs'; -import { AuthMessage, UserMessage } from '../../../common/enums/message.enum'; -import { RefreshToken } from '../../users/entities/refresh-token.entity'; -import { User } from '../../users/entities/user.entity'; +import { AuthMessage } from '../../../common/enums/message.enum'; +import { RefreshToken, RefreshTokenType } from '../entities/refresh-token.entity'; import { IAdminTokenPayload, ITokenPayload } from '../interfaces/IToken-payload'; -import { Restaurant } from '../../restaurants/entities/restaurant.entity'; -import { RestRepository } from '../../restaurants/repositories/rest.repository'; -import { Admin } from '../../admin/entities/admin.entity'; -import { CacheService } from '../../utils/cache.service'; @Injectable() export class TokensService { @@ -22,286 +16,68 @@ export class TokensService { private readonly configService: ConfigService, private readonly jwtService: JwtService, private readonly em: EntityManager, - private readonly restRepository: RestRepository, - private readonly cacheService: CacheService, ) {} - async generateTokens(ownerId: string, rest: Restaurant, em?: EntityManager) { - return this.generateAccessAndRefreshToken( - { - userId: ownerId, - restId: rest.id, - }, - em, - ); - } - - async generateTokensAdmin(adminId: string, rest: Restaurant, em?: EntityManager) { - return this.generateAccessAndRefreshTokenAdmin( - { - adminId, - restId: rest.id, - }, - em, - ); - } - - private async generateAccessAndRefreshToken(payload: ITokenPayload, em?: EntityManager) { - const accessExpire = this.configService.getOrThrow('ACCESS_TOKEN_EXPIRE'); + async generateAccessAndRefreshToken(ownerId: string, restaurantId: string, isAdmin: boolean) { const refreshExpire = this.configService.getOrThrow('REFRESH_TOKEN_EXPIRE'); + const accessExpire = this.configService.getOrThrow('ACCESS_TOKEN_EXPIRE'); - const tokenPayload: ITokenPayload = { - userId: payload.userId, - restId: payload.restId, - }; - const accessToken = await this.jwtService.signAsync(tokenPayload, { expiresIn: `${accessExpire}m` }); + const payload: ITokenPayload | IAdminTokenPayload = isAdmin + ? { adminId: ownerId, restId: restaurantId } + : { userId: ownerId, restId: restaurantId }; + + const accessToken = await this.generateAccessToken(payload, accessExpire); const refreshToken = this.generateRefreshToken(); + const type = isAdmin ? RefreshTokenType.ADMIN : RefreshTokenType.USER; - // Skip validation when called from refresh token flow (when em is provided and in transaction) - const skipValidation = !!em && em.isInTransaction(); - await this.storeRefreshToken(payload.userId, payload.restId, refreshToken, em, skipValidation); + await this.storeRefreshToken(ownerId, restaurantId, refreshToken, type); return { - accessToken: { token: accessToken, expire: dayjs().add(accessExpire, 'minute').valueOf() }, + accessToken: { token: accessToken, expire: dayjs().add(accessExpire, 'minutes').valueOf() }, refreshToken: { token: refreshToken, expire: dayjs().add(refreshExpire, 'day').valueOf() }, }; } - private async generateAccessAndRefreshTokenAdmin(payload: IAdminTokenPayload, em?: EntityManager) { - const accessExpire = this.configService.getOrThrow('ACCESS_TOKEN_EXPIRE'); + private generateAccessToken(payload: ITokenPayload | IAdminTokenPayload, expire: number) { + return this.jwtService.signAsync(payload, { expiresIn: `${expire}m` }); + } + + async storeRefreshToken(ownerId: string, restId: string, refreshToken: string, type: RefreshTokenType) { const refreshExpire = this.configService.getOrThrow('REFRESH_TOKEN_EXPIRE'); + const expiresAt = dayjs().add(refreshExpire, 'day').toDate(); - const tokenPayload: IAdminTokenPayload = { - adminId: payload.adminId, - restId: payload.restId, - }; - const accessToken = await this.jwtService.signAsync(tokenPayload, { expiresIn: `${accessExpire}m` }); - const refreshToken = this.generateRefreshToken(); + const token = this.em.create(RefreshToken, { + token: refreshToken, + ownerId, + restId, + type, + expiresAt, + }); - // Skip validation when called from refresh token flow (when em is provided and in transaction) - const skipValidation = !!em && em.isInTransaction(); - await this.storeRefreshTokenAdmin(payload.adminId, payload.restId, refreshToken, em, skipValidation); - return { - accessToken: { token: accessToken, expire: dayjs().add(accessExpire, 'minute').valueOf() }, - refreshToken: { token: refreshToken, expire: dayjs().add(refreshExpire, 'day').valueOf() }, - }; + return this.em.persistAndFlush(token); } - async storeRefreshToken( - userId: string, - restId: string, - refreshToken: string, - em?: EntityManager, - skipValidation = false, - ) { - const entityManager = em || this.em.fork(); - const isExternalTransaction = !!em && em.isInTransaction(); + async refreshToken(oldRefreshToken: string, isAdmin: boolean) { + const token = await this.em.findOne(RefreshToken, { token: oldRefreshToken }); - try { - // Only start transaction if no external EntityManager with transaction is provided - if (!isExternalTransaction && !entityManager.isInTransaction()) { - await entityManager.begin(); - } + if (!token) throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN); - const refreshExpire = this.configService.getOrThrow('REFRESH_TOKEN_EXPIRE'); - const expiresAt = dayjs().add(refreshExpire, 'day').toDate(); - - // Skip User validation when refreshing (user is already validated via refresh token) - if (!skipValidation) { - const user = await entityManager.findOne(User, { id: userId }); - if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); - } - - const restaurant = await entityManager.findOne(Restaurant, { id: restId }); - if (!restaurant) throw new BadRequestException(UserMessage.Rest_NOT_FOUND); - - const token = entityManager.create(RefreshToken, { - token: refreshToken, - restaurant, - ownerId: userId, - expiresAt, - }); - - // Use persist instead of persistAndFlush when in transaction - if (isExternalTransaction) { - entityManager.persist(token); - } else { - await entityManager.persistAndFlush(token); - if (entityManager.isInTransaction()) { - await entityManager.commit(); - } - } - } catch (error) { - this.logger.error(error); - // Only rollback if we started the transaction - if (!isExternalTransaction && entityManager.isInTransaction()) { - await entityManager.rollback(); - } - throw error; + if (dayjs(token.expiresAt).isBefore(dayjs())) { + this.em.remove(token); + await this.em.flush(); + throw new UnauthorizedException(AuthMessage.REFRESH_TOKEN_EXPIRED); } - } - async storeRefreshTokenAdmin( - adminId: string, - restId: string, - refreshToken: string, - em?: EntityManager, - skipValidation = false, - ) { - const entityManager = em || this.em.fork(); - const isExternalTransaction = !!em && em.isInTransaction(); + // Remove the old token + this.em.remove(token); - try { - // Only start transaction if no external EntityManager with transaction is provided - if (!isExternalTransaction && !entityManager.isInTransaction()) { - await entityManager.begin(); - } + // Generate new tokens within the same transaction + const tokens = await this.generateAccessAndRefreshToken(token.ownerId, token.restId, isAdmin); - const refreshExpire = this.configService.getOrThrow('REFRESH_TOKEN_EXPIRE'); - const expiresAt = dayjs().add(refreshExpire, 'day').toDate(); + // Commit the transaction + await this.em.flush(); - // Skip Admin validation when refreshing (admin is already validated via refresh token) - if (!skipValidation) { - const admin = await entityManager.findOne(Admin, { id: adminId }); - if (!admin) throw new BadRequestException(UserMessage.USER_NOT_FOUND); - } - - const restaurant = await entityManager.findOne(Restaurant, { id: restId }); - if (!restaurant) throw new BadRequestException(UserMessage.Rest_NOT_FOUND); - - const token = entityManager.create(RefreshToken, { - token: refreshToken, - restaurant, - ownerId: adminId, - expiresAt, - }); - - // Use persist instead of persistAndFlush when in transaction - if (isExternalTransaction) { - entityManager.persist(token); - } else { - await entityManager.persistAndFlush(token); - if (entityManager.isInTransaction()) { - await entityManager.commit(); - } - } - } catch (error) { - this.logger.error(error); - // Only rollback if we started the transaction - if (!isExternalTransaction && entityManager.isInTransaction()) { - await entityManager.rollback(); - } - throw error; - } - } - - async refreshToken(oldRefreshToken: string) { - const entityManager = this.em.fork(); - - try { - await entityManager.begin(); - const token = await entityManager.findOne( - RefreshToken, - { token: oldRefreshToken, isRevoked: { $ne: true } }, - { - populate: ['restaurant'], - }, - ); - - if (!token) throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN); - - // const rest = await this.restRepository.findOne({ id: token.user.id }); - // if (!rest) throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN); - - if (dayjs(token.expiresAt).isBefore(dayjs())) { - entityManager.remove(token); - await entityManager.flush(); - await entityManager.commit(); - throw new UnauthorizedException(AuthMessage.REFRESH_TOKEN_EXPIRED); - } - - // Remove the old token - entityManager.remove(token); - - // Generate new tokens within the same transaction - const tokens = await this.generateTokens(token.ownerId, token.restaurant, entityManager); - - // Commit the transaction - await entityManager.flush(); - await entityManager.commit(); - - return tokens; - } catch (error) { - this.logger.error(error); - if (entityManager.isInTransaction()) { - await entityManager.rollback(); - } - throw error; - } - } - - async refreshTokenAdmin(oldRefreshToken: string) { - const entityManager = this.em.fork(); - - try { - await entityManager.begin(); - const token = await entityManager.findOne( - RefreshToken, - { token: oldRefreshToken, isRevoked: { $ne: true } }, - { - populate: ['restaurant'], - }, - ); - - if (!token) throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN); - - // const rest = await this.restRepository.findOne({ id: token.user.id }); - // if (!rest) throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN); - - if (dayjs(token.expiresAt).isBefore(dayjs())) { - entityManager.remove(token); - await entityManager.flush(); - await entityManager.commit(); - throw new UnauthorizedException(AuthMessage.REFRESH_TOKEN_EXPIRED); - } - - // Remove the old token - entityManager.remove(token); - - // Generate new tokens within the same transaction - const tokens = await this.generateTokensAdmin(token.ownerId, token.restaurant, entityManager); - - // Commit the transaction - await entityManager.flush(); - await entityManager.commit(); - - return tokens; - } catch (error) { - this.logger.error(error); - if (entityManager.isInTransaction()) { - await entityManager.rollback(); - } - throw error; - } - } - - async invalidateRefreshToken(userId: string) { - const entityManager = this.em.fork(); - - try { - await entityManager.begin(); - - await entityManager.nativeDelete(RefreshToken, { ownerId: userId }); - this.logger.log(`Successfully deleted all refresh tokens for user ${userId}`); - - await entityManager.commit(); - } catch (error) { - this.logger.error(error); - if (entityManager.isInTransaction()) { - await entityManager.rollback(); - } - throw error; - } + return tokens; } private generateRefreshToken() { diff --git a/src/modules/users/user.module.ts b/src/modules/users/user.module.ts index d8976fd..95c4a78 100644 --- a/src/modules/users/user.module.ts +++ b/src/modules/users/user.module.ts @@ -7,12 +7,11 @@ import { User } from './entities/user.entity'; import { UserAddress } from './entities/user-address.entity'; import { JwtModule } from '@nestjs/jwt'; import { UserRepository } from './repositories/user.repository'; -import { RefreshToken } from './entities/refresh-token.entity'; @Module({ providers: [UserService, UserRepository], controllers: [UsersController, PublicUserController], - imports: [MikroOrmModule.forFeature([User, UserAddress, RefreshToken]), JwtModule], + imports: [MikroOrmModule.forFeature([User, UserAddress]), JwtModule], exports: [UserService, UserRepository], }) export class UserModule {}