diff --git a/package.json b/package.json index 2f94b46..07dd8b5 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,8 @@ "fastify": "^5.6.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "ulid": "^3.0.1" + "ulid": "^3.0.1", + "dayjs": "^1.11.18" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/src/modules/admin/admin.module.ts b/src/modules/admin/admin.module.ts index 7672d43..a190588 100644 --- a/src/modules/admin/admin.module.ts +++ b/src/modules/admin/admin.module.ts @@ -4,14 +4,12 @@ import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Admin } from './entities/admin.entity'; import { JwtModule } from '@nestjs/jwt'; import { AdminRepository } from './repositories/admin.repository'; - -import { AdminController } from './controllers/admin.controller'; import { UtilsModule } from '../utils/utils.module'; import { BusinessModule } from '../business/business.module'; - + @Module({ - providers: [AdminService, AdminRepository, ], - controllers: [AdminController], + providers: [AdminService, AdminRepository,], + controllers: [], imports: [ MikroOrmModule.forFeature([Admin]), JwtModule, diff --git a/src/modules/admin/controllers/admin.controller.ts b/src/modules/admin/controllers/admin.controller.ts deleted file mode 100644 index 4051d25..0000000 --- a/src/modules/admin/controllers/admin.controller.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Controller, Post, Body, HttpCode, HttpStatus, Get, UseGuards, Patch, Param, Delete } from '@nestjs/common'; -import { AdminService } from '../providers/admin.service'; -import { UpdateAdminDto } from '../dto/update-admin.dto'; -import { ApiTags, ApiOperation, ApiBody, ApiBearerAuth, ApiParam } from '@nestjs/swagger'; -import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; -import { SuperAdminAuthGuard } from 'src/modules/auth/guards/superAdminAuth.guard'; -import { BusinessId } from 'src/common/decorators'; -import { AdminId } from 'src/common/decorators/admin-id.decorator'; - -@ApiBearerAuth() -@ApiTags('admin') -@Controller() -export class AdminController { - constructor(private readonly adminService: AdminService) { } - - // @Get('admin/admins') - // @UseGuards(AdminAuthGuard) - // @Permissions(Permission.MANAGE_ADMINS) - // getAll(@BusinessId() restId: string) { - // return this.adminService.findAllByRestaurantId(restId); - // } - - // @Get('admin/admins/profile') - // @UseGuards(AdminAuthGuard) - // @Permissions(Permission.MANAGE_ADMINS) - // getMe(@AdminId() adminId: string, @BusinessId() restId: string) { - // return this.adminService.finAdminById(adminId, restId); - // } - - - // @Patch('admin/admins/:adminId') - // @UseGuards(AdminAuthGuard) - // @Permissions(Permission.MANAGE_ADMINS) - // @ApiOperation({ summary: 'Update an admin' }) - // @ApiParam({ name: 'adminId', description: 'Admin ID' }) - // update(@Param('adminId') adminId: string, @Body() dto: UpdateAdminDto, - // @BusinessId() restId: string) { - // return this.adminService.update(adminId, restId, dto); - // } - - // @Get('admin/admins/:adminId') - // @UseGuards(AdminAuthGuard) - // @Permissions(Permission.MANAGE_ADMINS) - // getById(@Param('adminId') adminId: string, @BusinessId() restId: string) { - // return this.adminService.finAdminById(adminId, restId); - // } - - // @Delete('admin/admins/:adminId') - // @UseGuards(AdminAuthGuard) - // @Permissions(Permission.MANAGE_ADMINS) - // @ApiOperation({ summary: 'Delete an admin by ID' }) - // @ApiParam({ name: 'id', description: 'Admin ID' }) - // deleteById(@Param('adminId') adminId: string, @BusinessId() restId: string) { - // return this.adminService.remove(adminId, restId); - // } - - - // @UseGuards(SuperAdminAuthGuard) - // @Delete('super-admin/restaurants/:restaurantId/admins/:adminId') - // @ApiOperation({ summary: 'Revoke admin role from a restaurant (Super Admin only)' }) - // @ApiParam({ name: 'restaurantId', description: 'Restaurant ID' }) - // @ApiParam({ name: 'adminId', description: 'Admin ID' }) - // revokeAdminFromRestaurant( - // @Param('restaurantId') restaurantId: string, - // @Param('adminId') adminId: string, - // ) { - // return this.adminService.remove(adminId, restaurantId); - // } -} diff --git a/src/modules/admin/providers/admin.service.ts b/src/modules/admin/providers/admin.service.ts index 57d81e6..12b930f 100644 --- a/src/modules/admin/providers/admin.service.ts +++ b/src/modules/admin/providers/admin.service.ts @@ -1,80 +1,16 @@ import { Injectable, NotFoundException } from '@nestjs/common'; -import { wrap } from '@mikro-orm/core'; import { EntityManager } from '@mikro-orm/postgresql'; -import { UpdateAdminDto } from '../dto/update-admin.dto'; import { AdminRepository } from '../repositories/admin.repository'; -import { CreateAdminDto } from '../dto/create-admin.dto'; - + @Injectable() export class AdminService { constructor( private readonly adminRepository: AdminRepository, - private readonly em: EntityManager, + private readonly em: EntityManager, ) { } async findByPhone(phone: string) { return this.adminRepository.findOne({ phone }); } - // async create(dto:CreateAdminDto){ - // this.em.create(dto) - // } - - async finAdminById( - adminId: string, - restId: string, - ) { - const admin = await this.adminRepository.findOne( - { id: adminId }, - {}, - ); - if (!admin) { - throw new NotFoundException('Admin not found'); - } - const adminPlain = wrap(admin).toObject(); - - return { ...adminPlain }; - } - - async findAllByRestaurantId(restId: string) { - return this.adminRepository.find({}, { populate: [] }); - } - - - - async update(adminId: string, restId: string, dto: UpdateAdminDto) { - const admin = await this.adminRepository.findOne( - { id: adminId }, - { populate: [] }, - ); - - if (!admin) { - throw new NotFoundException('Admin not found'); - } - - const { ...rest } = dto; - - // Update scalar fields (firstName, lastName, phone) - if (rest.firstName !== undefined) { - admin.firstName = rest.firstName; - } - if (rest.lastName !== undefined) { - admin.lastName = rest.lastName; - } - if (rest.phone !== undefined && rest.phone !== admin.phone) { - // const exists = await this.adminRepository.findOne({ phone: normalizedPhone }); - // if (exists) { - // throw new ConflictException('This Phone Number is already taken'); - // } - // admin.phone = normalizedPhone; - } - - // Update role if roleId is provided - - - await this.em.persistAndFlush(admin); - return admin; - } - - } diff --git a/src/modules/admin/transformers/admin-detail.transformer.ts b/src/modules/admin/transformers/admin-detail.transformer.ts deleted file mode 100644 index ae694b7..0000000 --- a/src/modules/admin/transformers/admin-detail.transformer.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { Admin } from '../entities/admin.entity'; - -export interface AdminDetailResponse { - id: string; - firstName?: string; - lastName?: string; - phone: string; - role: { - id: string; - name: string; - }; - permissions: string[]; - restaurant?: { - id: string; - name: string; - slug: string; - }; -} - - - -// Extract permissions - ensure collection is loaded if needed diff --git a/src/modules/auth/controllers/auth.controller.ts b/src/modules/auth/controllers/auth.controller.ts index 9ef3140..bf985a8 100644 --- a/src/modules/auth/controllers/auth.controller.ts +++ b/src/modules/auth/controllers/auth.controller.ts @@ -10,36 +10,6 @@ import { RefreshTokenDto } from '../dto/refresh-token.dto'; export class AuthController { constructor(private readonly authService: AuthService) { } - // @Throttle({ default: { limit: 3, ttl: 180_000 } }) - // @Post('public/auth/otp/request') - // @ApiOperation({ summary: 'Request OTP for login or signup' }) - // @ApiBody({ type: RequestOtpDto, description: 'Mobile number to receive OTP' }) - // otpRequest(@Body() dto: RequestOtpDto) { - // return this.authService.requestOtp(dto, false); - // } - - - // @RefreshTokenRateLimit() - // @ApiOperation({ summary: 'refresh the user access token / refresh token' }) - // @Post('public/auth/refresh') - // refreshToken(@Body() refreshTokenDto: RefreshTokenDto) { - // return this.authService.refreshToken(refreshTokenDto.refreshToken); - // } - - // @Throttle({ default: { limit: 3, ttl: 180_000 } }) - // @Post('admin/auth/otp/request') - // @ApiOperation({ summary: 'Request OTP for login or signup' }) - // @ApiBody({ type: RequestOtpDto, description: 'Mobile number to receive OTP' }) - // otpRequestAdmin(@Body() dto: RequestOtpDto) { - // return this.authService.requestOtp(dto, true); - // } - - // @Post('admin/auth/otp/verify') - // @ApiOperation({ summary: 'Verify OTP code' }) - // @ApiBody({ type: VerifyOtpDto, description: 'Mobile number and OTP code' }) - // otpVerifyAdmin(@Body() dto: VerifyOtpDto) { - // return this.authService.verifyOtpAdmin(dto.phone, dto.slug, dto.otp); - // } @ApiOperation({ summary: 'refresh the admin access token / refresh token' }) @Post('admin/auth/refresh') @@ -47,7 +17,6 @@ export class AuthController { return this.authService.refreshToken(refreshTokenDto.refreshToken); } - // //super admin routes @UseGuards(SuperAdminAuthGuard) @Post('super-admin/auth/direct-login') diff --git a/src/modules/auth/dto/request-otp.dto.ts b/src/modules/auth/dto/request-otp.dto.ts deleted file mode 100644 index 83984af..0000000 --- a/src/modules/auth/dto/request-otp.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsNotEmpty, IsString, IsMobilePhone } from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; - -export class RequestOtpDto { - @IsNotEmpty() - @IsString() - @ApiProperty({ example: '09362532122', description: 'Mobile number' }) - @IsMobilePhone('fa-IR') - phone: string; - - @IsNotEmpty() - @IsString() - @ApiProperty({ example: 'zhivan', description: 'restaurant slug' }) - slug: string; -} diff --git a/src/modules/auth/dto/verify-otp.dto.ts b/src/modules/auth/dto/verify-otp.dto.ts deleted file mode 100644 index ec1c6f1..0000000 --- a/src/modules/auth/dto/verify-otp.dto.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { IsNotEmpty, IsString, Length, IsMobilePhone } from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; - -export class VerifyOtpDto { - @IsNotEmpty() - @IsString() - @ApiProperty({ example: '09362532122', description: 'Mobile number' }) - @IsMobilePhone('fa-IR') - phone: string; - - @IsNotEmpty() - @IsString() - @ApiProperty({ example: '', description: 'Otp' }) - @Length(5) - otp: string; - - @IsNotEmpty() - @IsString() - @ApiProperty({ example: 'zhivan', description: 'restaurant slug' }) - slug: string; -} diff --git a/src/modules/auth/guards/adminAuth.guard.ts b/src/modules/auth/guards/adminAuth.guard.ts index 3be3231..786064b 100644 --- a/src/modules/auth/guards/adminAuth.guard.ts +++ b/src/modules/auth/guards/adminAuth.guard.ts @@ -7,12 +7,11 @@ import { Logger, ForbiddenException, } from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; import { Request } from 'express'; import { JwtService } from '@nestjs/jwt'; import { ConfigService } from '@nestjs/config'; import { IAdminTokenPayload } from '../interfaces/IToken-payload'; - + export interface AdminAuthRequest extends Request { adminId: string; businessId: string; @@ -27,7 +26,6 @@ export class AdminAuthGuard implements CanActivate { private readonly jwtService: JwtService, @Inject(ConfigService) private readonly configService: ConfigService, - private readonly reflector: Reflector, ) { } async canActivate(context: ExecutionContext) { @@ -60,17 +58,6 @@ export class AdminAuthGuard implements CanActivate { request['adminId'] = payload.adminId; request['businessId'] = payload.businessId; - // check if the user has the required permissions - // const requiredPermissions = - // this.reflector.getAllAndOverride(PERMISSIONS_KEY, [context.getHandler(), context.getClass()]) ?? []; - - // if (!requiredPermissions || requiredPermissions.length === 0) { - // return true; - // } - - - - return true; } catch (err) { diff --git a/src/modules/auth/services/auth.service.ts b/src/modules/auth/services/auth.service.ts index 056f3de..ad1c5f8 100644 --- a/src/modules/auth/services/auth.service.ts +++ b/src/modules/auth/services/auth.service.ts @@ -11,99 +11,13 @@ export class AuthService { ) { } - // private userOtpKey(restaurantSlug: string, phone: string) { - // return `otp:${restaurantSlug}:${phone}`; - // } - - // private adminOtpKey(restaurantSlug: string, phone: string) { - // return `otp-admin:${restaurantSlug}:${phone}`; - // } - async directLogin(danakSubId: string) { const business = await this.businessService.findBySubIdOrFail(danakSubId) - console.log(business) const admin = business.admin const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, business.id); return { tokens } } - // async requestOtp(dto: RequestOtpDto, isAdmin: boolean) { - // const { phone, slug } = dto; - // const normalizedPhone = normalizePhone(phone); - - // if (isAdmin) { - // const admin = await this.adminRepository.findByPhoneAndRestaurantSlug(normalizedPhone, slug); - // if (!admin) { - // throw new BadRequestException(AuthMessage.ADMIN_NOT_FOUND); - // } - // } - // const code = this.generateOtpCode(); - // const key = isAdmin ? this.adminOtpKey(slug, normalizedPhone) : this.userOtpKey(slug, normalizedPhone); - // await this.cacheService.set(key, code, this.OTP_EXPIRATION_TIME); - - // await this.smsService.sendotp(normalizedPhone, code); - - // return { code: null }; - // } - - - - // async verifyOtpAdmin(phone: string, slug: string, code: string) { - // const normalizedPhone = normalizePhone(phone); - // const key = this.adminOtpKey(slug, normalizedPhone); - // 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(key); - - // // const admin = await this.adminRepository.findByPhoneAndRestaurantSlug(normalizedPhone, slug); - - // // if (!admin) { - // // throw new BadRequestException(AuthMessage.ADMIN_NOT_FOUND); - // // } - - // const rest = await this.restRepository.findOne({ slug }); - - // if (!rest) { - // throw new BadRequestException(RestMessage.NOT_FOUND); - // } - - // const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, rest.id, true, slug); - - - - // return { tokens, admin }; - // } - /** - * - to use for login directly from DSC - */ - // async loginAdminForDsc(phone: string, slug: string) { - // const normalizedPhone = normalizePhone(phone); - // const admin = await this.adminRepository.findByPhoneAndRestaurantSlug(normalizedPhone, slug); - - // if (!admin) { - // throw new BadRequestException(AuthMessage.ADMIN_NOT_FOUND); - // } - - // const rest = await this.restRepository.findOne({ slug }); - - // if (!rest) { - // throw new BadRequestException(RestMessage.NOT_FOUND); - // } - - // const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, rest.id, true, slug); - - - // return { tokens, admin }; - // } - - // private generateOtpCode(): string { - // const code = randomInt(10000, 100000); - // return code.toString(); - // } refreshToken(oldRefreshToken: string) { return this.tokensService.refreshToken(oldRefreshToken); diff --git a/src/modules/auth/transformers/user-login.transformer.ts b/src/modules/auth/transformers/user-login.transformer.ts deleted file mode 100644 index 8663bcb..0000000 --- a/src/modules/auth/transformers/user-login.transformer.ts +++ /dev/null @@ -1,16 +0,0 @@ - -export interface UserLoginResponse { - id: string; - firstName: string; - lastName?: string; - phone: string; - - isActive?: boolean; - restaurant: { - id: string; - name: string; - slug: string; - }; -} - -