From fdaa8b3dfddcd9e403d981174a5fdb050dce3f21 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 18 Nov 2025 23:44:28 +0330 Subject: [PATCH] Enhance CategoryController and CategoryService to enforce admin-only access for category management; introduce restaurant-specific handling by adding RestId parameter to relevant methods; implement new endpoint for retrieving categories by restaurant for user access. --- .../admin/entities/adminRole.entity.ts | 2 +- .../foods/controllers/category.controller.ts | 41 ++++++++++++++----- .../foods/providers/category.service.ts | 29 +++++++++---- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/modules/admin/entities/adminRole.entity.ts b/src/modules/admin/entities/adminRole.entity.ts index 2b2b296..788a7b3 100644 --- a/src/modules/admin/entities/adminRole.entity.ts +++ b/src/modules/admin/entities/adminRole.entity.ts @@ -5,8 +5,8 @@ import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity'; import { Admin } from './admin.entity'; @Entity({ tableName: 'admin_roles' }) +@Unique({ properties: ['admin', 'role', 'restaurant'] }) export class AdminRole extends BaseEntity { - @Unique({ properties: ['admin.id', 'role.id', 'restaurant.id'] }) @ManyToOne(() => Admin) admin!: Admin; diff --git a/src/modules/foods/controllers/category.controller.ts b/src/modules/foods/controllers/category.controller.ts index 9d2871f..a2fdcac 100644 --- a/src/modules/foods/controllers/category.controller.ts +++ b/src/modules/foods/controllers/category.controller.ts @@ -11,13 +11,19 @@ import { ApiParam, ApiBody, ApiQuery, + ApiBearerAuth, } from '@nestjs/swagger'; +import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; +import { UseGuards } from '@nestjs/common'; +import { RestId } from 'src/common/decorators'; @ApiTags('categories') -@Controller('categories') +@Controller() export class CategoryController { constructor(private readonly categoryService: CategoryService) {} + @UseGuards(AdminAuthGuard) + @ApiBearerAuth('/admin/categories') @Post() @ApiOperation({ summary: 'Create category' }) @ApiCreatedResponse({ description: 'Category created', type: CreateCategoryDto }) @@ -26,40 +32,55 @@ export class CategoryController { return this.categoryService.create(dto); } + @UseGuards(AdminAuthGuard) + @ApiBearerAuth('/admin/categories') @Get() - @ApiOperation({ summary: 'List categories' }) + @ApiOperation({ summary: 'List categories for admin' }) @ApiOkResponse({ description: 'List of categories' }) @ApiQuery({ name: 'restId', required: false, type: String }) @ApiQuery({ name: 'isActive', required: false, type: Boolean }) - findAll(@Query('restId') restId?: string, @Query('isActive') isActive?: string) { + findAll(@RestId() restId: string, @Query('isActive') isActive?: string) { // convert isActive from query string to boolean when provided const isActiveBool: boolean | undefined = typeof isActive !== 'undefined' ? isActive === 'true' : undefined; return this.categoryService.findAll({ restId, isActive: isActiveBool }); } + @UseGuards(AdminAuthGuard) + @ApiBearerAuth('/admin/categories') @Get(':id') @ApiOperation({ summary: 'Get category' }) - @ApiParam({ name: 'id' }) @ApiOkResponse({ description: 'Category detail', type: CreateCategoryDto }) @ApiNotFoundResponse({ description: 'Category not found' }) - findOne(@Param('id') id: string) { - return this.categoryService.findOne(id); + findOne(@Param() id: string, @RestId() restId: string) { + return this.categoryService.findOne(restId, id); } + @UseGuards(AdminAuthGuard) + @ApiBearerAuth('/admin/categories') @Patch(':id') @ApiOperation({ summary: 'Update category' }) @ApiParam({ name: 'id' }) @ApiBody({ type: UpdateCategoryDto }) @ApiOkResponse({ description: 'Updated category', type: UpdateCategoryDto }) - update(@Param('id') id: string, @Body() dto: UpdateCategoryDto) { - return this.categoryService.update(id, dto); + update(@Param('id') id: string, @Body() dto: UpdateCategoryDto, @RestId() restId: string) { + return this.categoryService.update(restId, id, dto); } + @UseGuards(AdminAuthGuard) + @ApiBearerAuth('/admin/categories') @Delete(':id') @ApiOperation({ summary: 'Delete category' }) @ApiParam({ name: 'id' }) @ApiOkResponse({ description: 'Deleted' }) - remove(@Param('id') id: string) { - return this.categoryService.remove(id); + remove(@Param('id') id: string, @RestId() restId: string) { + return this.categoryService.remove(restId, id); + } + + @Get('restaurant/:restId') + @ApiOperation({ summary: 'Restaurant categories for user' }) + @ApiOkResponse({ description: 'List of categories' }) + @ApiParam({ name: 'restId', required: true, type: String }) + findAllForRestaurant(@Param('restId') restId: string) { + return this.categoryService.findRestaurantCategories(restId); } } diff --git a/src/modules/foods/providers/category.service.ts b/src/modules/foods/providers/category.service.ts index acf2a58..f55ad0e 100644 --- a/src/modules/foods/providers/category.service.ts +++ b/src/modules/foods/providers/category.service.ts @@ -47,8 +47,8 @@ export class CategoryService { })) as unknown as Category[]; } - async findOne(id: string): Promise { - const cat = await this.categoryRepository.findOne({ id }, { populate: ['foods'] }); + async findOne(restId: string, id: string): Promise { + const cat = await this.categoryRepository.findOne({ id, restId }, { populate: ['foods'] }); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); return { @@ -63,16 +63,31 @@ export class CategoryService { } as unknown as Category; } - async update(id: string, dto: UpdateCategoryDto): Promise { - const cat = await this.categoryRepository.findOne({ id }); + async findRestaurantCategories(restId: string): Promise { + const cat = await this.categoryRepository.findOne({ restId }, { populate: ['foods'] }); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); - this.em.assign(cat, dto as Partial); + + return { + id: cat.id, + title: cat.title, + isActive: cat.isActive, + restId: cat.restId, + avatarUrl: cat.avatarUrl, + createdAt: cat.createdAt, + updatedAt: cat.updatedAt, + } as unknown as Category; + } + + async update(restId: string, id: string, dto: UpdateCategoryDto): Promise { + const cat = await this.categoryRepository.findOne({ id, restId }); + if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); + this.em.assign(cat, dto); await this.em.persistAndFlush(cat); return cat; } - async remove(id: string): Promise { - const cat = await this.categoryRepository.findOne({ id }); + async remove(restId: string, id: string): Promise { + const cat = await this.categoryRepository.findOne({ id, restId }); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); cat.deletedAt = new Date(); await this.em.persistAndFlush(cat);