This commit is contained in:
2026-03-15 16:29:31 +03:30
parent f9094fcbb3
commit 364eb55fb5
11 changed files with 8 additions and 346 deletions
+2 -1
View File
@@ -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",
+3 -5
View File
@@ -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,
@@ -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);
// }
}
+2 -66
View File
@@ -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;
}
}
@@ -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
@@ -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')
-15
View File
@@ -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;
}
-21
View File
@@ -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;
}
+1 -14
View File
@@ -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<string[]>(PERMISSIONS_KEY, [context.getHandler(), context.getClass()]) ?? [];
// if (!requiredPermissions || requiredPermissions.length === 0) {
// return true;
// }
return true;
} catch (err) {
-86
View File
@@ -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);
@@ -1,16 +0,0 @@
export interface UserLoginResponse {
id: string;
firstName: string;
lastName?: string;
phone: string;
isActive?: boolean;
restaurant: {
id: string;
name: string;
slug: string;
};
}