update
This commit is contained in:
+2
-1
@@ -58,7 +58,8 @@
|
|||||||
"fastify": "^5.6.1",
|
"fastify": "^5.6.1",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"ulid": "^3.0.1"
|
"ulid": "^3.0.1",
|
||||||
|
"dayjs": "^1.11.18"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ import { MikroOrmModule } from '@mikro-orm/nestjs';
|
|||||||
import { Admin } from './entities/admin.entity';
|
import { Admin } from './entities/admin.entity';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { AdminRepository } from './repositories/admin.repository';
|
import { AdminRepository } from './repositories/admin.repository';
|
||||||
|
|
||||||
import { AdminController } from './controllers/admin.controller';
|
|
||||||
import { UtilsModule } from '../utils/utils.module';
|
import { UtilsModule } from '../utils/utils.module';
|
||||||
import { BusinessModule } from '../business/business.module';
|
import { BusinessModule } from '../business/business.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [AdminService, AdminRepository, ],
|
providers: [AdminService, AdminRepository,],
|
||||||
controllers: [AdminController],
|
controllers: [],
|
||||||
imports: [
|
imports: [
|
||||||
MikroOrmModule.forFeature([Admin]),
|
MikroOrmModule.forFeature([Admin]),
|
||||||
JwtModule,
|
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);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,80 +1,16 @@
|
|||||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { wrap } from '@mikro-orm/core';
|
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { UpdateAdminDto } from '../dto/update-admin.dto';
|
|
||||||
import { AdminRepository } from '../repositories/admin.repository';
|
import { AdminRepository } from '../repositories/admin.repository';
|
||||||
import { CreateAdminDto } from '../dto/create-admin.dto';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminService {
|
export class AdminService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly adminRepository: AdminRepository,
|
private readonly adminRepository: AdminRepository,
|
||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async findByPhone(phone: string) {
|
async findByPhone(phone: string) {
|
||||||
return this.adminRepository.findOne({ phone });
|
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 {
|
export class AuthController {
|
||||||
constructor(private readonly authService: AuthService) { }
|
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' })
|
@ApiOperation({ summary: 'refresh the admin access token / refresh token' })
|
||||||
@Post('admin/auth/refresh')
|
@Post('admin/auth/refresh')
|
||||||
@@ -47,7 +17,6 @@ export class AuthController {
|
|||||||
return this.authService.refreshToken(refreshTokenDto.refreshToken);
|
return this.authService.refreshToken(refreshTokenDto.refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// //super admin routes
|
|
||||||
|
|
||||||
@UseGuards(SuperAdminAuthGuard)
|
@UseGuards(SuperAdminAuthGuard)
|
||||||
@Post('super-admin/auth/direct-login')
|
@Post('super-admin/auth/direct-login')
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
ForbiddenException,
|
ForbiddenException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Reflector } from '@nestjs/core';
|
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
@@ -27,7 +26,6 @@ export class AdminAuthGuard implements CanActivate {
|
|||||||
private readonly jwtService: JwtService,
|
private readonly jwtService: JwtService,
|
||||||
@Inject(ConfigService)
|
@Inject(ConfigService)
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
private readonly reflector: Reflector,
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext) {
|
async canActivate(context: ExecutionContext) {
|
||||||
@@ -60,17 +58,6 @@ export class AdminAuthGuard implements CanActivate {
|
|||||||
request['adminId'] = payload.adminId;
|
request['adminId'] = payload.adminId;
|
||||||
request['businessId'] = payload.businessId;
|
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;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -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) {
|
async directLogin(danakSubId: string) {
|
||||||
const business = await this.businessService.findBySubIdOrFail(danakSubId)
|
const business = await this.businessService.findBySubIdOrFail(danakSubId)
|
||||||
console.log(business)
|
|
||||||
const admin = business.admin
|
const admin = business.admin
|
||||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, business.id);
|
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, business.id);
|
||||||
return { tokens }
|
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) {
|
refreshToken(oldRefreshToken: string) {
|
||||||
return this.tokensService.refreshToken(oldRefreshToken);
|
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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user