From 98f973958d384a5b175dc58e50aff56a632be84d Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 23 Dec 2025 13:00:27 +0330 Subject: [PATCH] set perms --- .../decorators/permissions.decorator.ts | 0 src/common/enums/permission.enum.ts | 2 + .../admin/controllers/admin.controller.ts | 7 ++- src/modules/auth/guards/adminAuth.guard.ts | 4 +- .../contact/controllers/contact.controller.ts | 21 ++++----- .../coupons/controllers/coupon.controller.ts | 27 +++++------- .../controllers/delivery.controller.ts | 44 +++++-------------- .../foods/controllers/category.controller.ts | 28 +++++------- .../foods/controllers/food.controller.ts | 13 +++--- .../roles/controllers/roles.controller.ts | 4 +- 10 files changed, 62 insertions(+), 88 deletions(-) rename src/{modules/auth => common}/decorators/permissions.decorator.ts (100%) diff --git a/src/modules/auth/decorators/permissions.decorator.ts b/src/common/decorators/permissions.decorator.ts similarity index 100% rename from src/modules/auth/decorators/permissions.decorator.ts rename to src/common/decorators/permissions.decorator.ts diff --git a/src/common/enums/permission.enum.ts b/src/common/enums/permission.enum.ts index 36a5173..5dbb207 100644 --- a/src/common/enums/permission.enum.ts +++ b/src/common/enums/permission.enum.ts @@ -30,6 +30,7 @@ export enum Permission { MANAGE_SETTINGS = 'manage_settings', MANAGE_SCHEDULES = 'manage_schedules', MANAGE_REPORTS = 'manage_reports', + MANAGE_CONTACTS = 'manage_contacts', } /** @@ -53,4 +54,5 @@ export const PermissionTitles: Record = { [Permission.MANAGE_SETTINGS]: 'مدیریت تنظیمات', [Permission.MANAGE_SCHEDULES]: 'مدیریت برنامه‌ها', [Permission.MANAGE_REPORTS]: 'مدیریت گزارشات', + [Permission.MANAGE_CONTACTS]: 'مدیریت تماس‌های کاربران', }; diff --git a/src/modules/admin/controllers/admin.controller.ts b/src/modules/admin/controllers/admin.controller.ts index adce1db..91a9414 100644 --- a/src/modules/admin/controllers/admin.controller.ts +++ b/src/modules/admin/controllers/admin.controller.ts @@ -7,10 +7,13 @@ import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { RestId } from 'src/common/decorators'; import { AdminId } from 'src/common/decorators/admin-id.decorator'; import { Admin } from '../entities/admin.entity'; - -@UseGuards(AdminAuthGuard) +import { Permission } from 'src/common/enums/permission.enum'; +import { Permissions } from 'src/common/decorators/permissions.decorator'; + @ApiBearerAuth() @ApiTags('admin') +@UseGuards(AdminAuthGuard) +@Permissions(Permission.MANAGE_ADMINS) @Controller('admin/admins') export class AdminController { constructor(private readonly adminService: AdminService) {} diff --git a/src/modules/auth/guards/adminAuth.guard.ts b/src/modules/auth/guards/adminAuth.guard.ts index 3ab68cb..1683ea9 100644 --- a/src/modules/auth/guards/adminAuth.guard.ts +++ b/src/modules/auth/guards/adminAuth.guard.ts @@ -12,7 +12,7 @@ import { Request } from 'express'; import { JwtService } from '@nestjs/jwt'; import { ConfigService } from '@nestjs/config'; import { IAdminTokenPayload } from '../interfaces/IToken-payload'; -import { PERMISSIONS_KEY } from '../decorators/permissions.decorator'; +import { PERMISSIONS_KEY } from '../../../common/decorators/permissions.decorator'; import { PermissionsService } from 'src/modules/roles/providers/permissions.service'; export interface AdminAuthRequest extends Request { @@ -31,7 +31,7 @@ export class AdminAuthGuard implements CanActivate { private readonly configService: ConfigService, private readonly permissionsService: PermissionsService, private readonly reflector: Reflector, - ) {} + ) { } async canActivate(context: ExecutionContext) { const request = context.switchToHttp().getRequest(); diff --git a/src/modules/contact/controllers/contact.controller.ts b/src/modules/contact/controllers/contact.controller.ts index 0e249a8..22e4d31 100644 --- a/src/modules/contact/controllers/contact.controller.ts +++ b/src/modules/contact/controllers/contact.controller.ts @@ -8,8 +8,6 @@ import { Query, UseGuards, Delete, - HttpCode, - HttpStatus, } from '@nestjs/common'; import { ContactService } from '../providers/contact.service'; import { CreateContactDto } from '../dto/create-contact.dto'; @@ -18,6 +16,9 @@ import { UpdateContactStatusDto } from '../dto/update-contact-status.dto'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiQuery, ApiHeader } from '@nestjs/swagger'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { SuperAdminAuthGuard } from 'src/modules/auth/guards/superAdminAuth.guard'; +import { API_HEADER_SLUG } from 'src/common/constants'; +import { Permission } from 'src/common/enums/permission.enum'; +import { Permissions } from 'src/common/decorators/permissions.decorator'; @ApiTags('contact') @Controller() @@ -26,22 +27,15 @@ export class ContactController { @Post('public/contact') @ApiOperation({ summary: 'Create a new contact (public endpoint)' }) - @ApiHeader({ - name: 'X-Slug', - required: true, - schema: { - type: 'string', - default: 'zhivan', - }, - }) + @ApiHeader(API_HEADER_SLUG) async create(@Body() createContactDto: CreateContactDto) { return this.contactService.create(createContactDto); } - @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_CONTACTS) @Get('admin/contact') @ApiOperation({ summary: 'Get paginated list of contacts (admin only)' }) @ApiQuery({ name: 'page', required: false, type: Number }) @@ -56,6 +50,7 @@ export class ContactController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_CONTACTS) @Get('admin/contact/:id') @ApiOperation({ summary: 'Get contact details by ID (admin only)' }) @ApiParam({ name: 'id', description: 'Contact ID' }) @@ -65,6 +60,7 @@ export class ContactController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_CONTACTS) @Patch('admin/contact/:id/status') @ApiOperation({ summary: 'Update contact status (admin only)' }) @ApiParam({ name: 'id', description: 'Contact ID' }) @@ -74,14 +70,15 @@ export class ContactController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_CONTACTS) @Delete('admin/contact/:id') - @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Hard delete a contact (admin only)' }) @ApiParam({ name: 'id', description: 'Contact ID' }) async remove(@Param('id') id: string) { await this.contactService.remove(id); } + /*** Super Admin ***/ @UseGuards(SuperAdminAuthGuard) @ApiBearerAuth() @Get('super-admin/contact') diff --git a/src/modules/coupons/controllers/coupon.controller.ts b/src/modules/coupons/controllers/coupon.controller.ts index 4553030..ca54469 100644 --- a/src/modules/coupons/controllers/coupon.controller.ts +++ b/src/modules/coupons/controllers/coupon.controller.ts @@ -18,25 +18,20 @@ import { import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { RestId } from 'src/common/decorators'; import { AuthGuard } from 'src/modules/auth/guards/auth.guard'; +import { API_HEADER_SLUG } from 'src/common/constants'; +import { Permission } from 'src/common/enums/permission.enum'; +import { Permissions } from 'src/common/decorators/permissions.decorator'; @ApiTags('coupons') @Controller() export class CouponController { - constructor(private readonly couponService: CouponService) {} + constructor(private readonly couponService: CouponService) { } @UseGuards(AuthGuard) @ApiBearerAuth() @Get('public/coupons/me') @ApiOperation({ summary: 'Get paginated list of restaurant coupons' }) - @ApiHeader({ - name: 'X-Slug', - required: true, - schema: { - type: 'string', - default: 'zhivan', - }, - }) - @ApiOkResponse({ description: 'Paginated list of restaurant coupons' }) + @ApiHeader(API_HEADER_SLUG) @ApiQuery({ name: 'page', required: false, type: Number }) @ApiQuery({ name: 'limit', required: false, type: Number }) @ApiQuery({ name: 'search', required: false, type: String }) @@ -47,9 +42,10 @@ export class CouponController { getMyCoupons(@Query() dto: FindCouponsDto, @RestId() restId: string) { return this.couponService.findAll(restId, dto); } - + /*** Admin ***/ @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_COUPONS) @Post('admin/coupons') @ApiOperation({ summary: 'Create a new coupon' }) @ApiCreatedResponse({ description: 'The coupon has been successfully created.', type: CreateCouponDto }) @@ -60,9 +56,9 @@ export class CouponController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_COUPONS) @Get('admin/coupons') @ApiOperation({ summary: 'Get a paginated list of coupons' }) - @ApiOkResponse({ description: 'Paginated list of coupons' }) @ApiQuery({ name: 'page', required: false, type: Number }) @ApiQuery({ name: 'limit', required: false, type: Number }) @ApiQuery({ name: 'search', required: false, type: String }) @@ -76,28 +72,28 @@ export class CouponController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_COUPONS) @Get('admin/coupons/:id') @ApiOperation({ summary: 'Get a coupon by id' }) @ApiParam({ name: 'id', required: true }) - @ApiOkResponse({ description: 'The coupon', type: CreateCouponDto }) - @ApiNotFoundResponse({ description: 'Coupon not found' }) findById(@Param('id') id: string) { return this.couponService.findById(id); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_COUPONS) @Patch('admin/coupons/:id') @ApiOperation({ summary: 'Update a coupon' }) @ApiParam({ name: 'id', required: true }) @ApiBody({ type: UpdateCouponDto }) - @ApiOkResponse({ description: 'The updated coupon', type: UpdateCouponDto }) update(@Param('id') id: string, @Body() updateCouponDto: UpdateCouponDto) { return this.couponService.update(id, updateCouponDto); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_COUPONS) @Delete('admin/coupons/:id') @ApiOperation({ summary: 'Delete (soft) a coupon' }) @ApiParam({ name: 'id', required: true }) @@ -107,6 +103,7 @@ export class CouponController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_COUPONS) @Get('admin/coupons/generate-code') @ApiOperation({ summary: 'generate a coupon code' }) generateCouponCode(@RestId() restId: string) { diff --git a/src/modules/delivery/controllers/delivery.controller.ts b/src/modules/delivery/controllers/delivery.controller.ts index 71b72b4..c2ece7c 100644 --- a/src/modules/delivery/controllers/delivery.controller.ts +++ b/src/modules/delivery/controllers/delivery.controller.ts @@ -17,6 +17,9 @@ import { RestId } from 'src/common/decorators/rest-id.decorator'; import { AuthGuard } from 'src/modules/auth/guards/auth.guard'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { Delivery } from '../entities/delivery.entity'; +import { API_HEADER_SLUG } from 'src/common/constants'; +import { Permission } from 'src/common/enums/permission.enum'; +import { Permissions } from 'src/common/decorators/permissions.decorator'; @ApiTags('Delivery') @Controller() @@ -27,83 +30,58 @@ export class DeliveryController { @ApiBearerAuth() @Get('public/delivery-methods/restaurant') @ApiOperation({ summary: 'Get restaurant delivery methods' }) - @ApiHeader({ - name: 'X-Slug', - required: true, - schema: { - type: 'string', - default: 'zhivan', - }, - }) - @ApiOkResponse({ - description: 'List of restaurant delivery methods', - type: [Delivery], - }) - findAllDeliveryMethods(@RestId() restId: string) { + @ApiHeader(API_HEADER_SLUG) + findAllDeliveryMethods(@RestId() restId: string) { return this.deliveryService.findAllForRestaurantId(restId); } + /*** Admin ***/ @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_DELIVERY) @Post('admin/delivery-methods/restaurant') @ApiOperation({ summary: 'Create a delivery method for a restaurant' }) @ApiBody({ type: CreateDeliveryDto }) - @ApiCreatedResponse({ - description: 'Delivery method created successfully', - type: Delivery, - }) create(@Body() createDto: CreateDeliveryDto, @RestId() restId: string) { return this.deliveryService.create(restId, createDto); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_DELIVERY) @Get('admin/delivery-methods/restaurant') @ApiOperation({ summary: 'Get the restaurant delivery methods' }) - @ApiOkResponse({ - description: 'List of restaurant delivery methods', - type: [Delivery], - }) findRestaurantDeliveryMethods(@RestId() restId: string) { return this.deliveryService.findAllForRestaurantId(restId); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_DELIVERY) @Get('admin/delivery-methods/restaurant/:deliveryId') @ApiOperation({ summary: 'Get a restaurant delivery method by delivery ID' }) @ApiParam({ name: 'deliveryId', description: 'Delivery method ID' }) - @ApiOkResponse({ - description: 'Delivery method details', - type: Delivery, - }) - @ApiNotFoundResponse({ description: 'Delivery method not found' }) findOne(@Param('deliveryId') deliveryId: string, @RestId() restId: string) { return this.deliveryService.findOne(restId, deliveryId); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_DELIVERY) @Patch('admin/delivery-methods/restaurant/:deliveryId') @ApiOperation({ summary: 'Update a restaurant delivery method' }) @ApiParam({ name: 'deliveryId', description: 'Delivery method ID' }) @ApiBody({ type: UpdateDeliveryDto }) - @ApiOkResponse({ - description: 'Delivery method updated successfully', - type: Delivery, - }) - @ApiNotFoundResponse({ description: 'Delivery method not found' }) update(@Param('deliveryId') deliveryId: string, @Body() updateDto: UpdateDeliveryDto, @RestId() restId: string) { return this.deliveryService.update(restId, deliveryId, updateDto); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_DELIVERY) @Delete('admin/delivery-methods/restaurant/:deliveryId') @ApiOperation({ summary: 'Delete a restaurant delivery method' }) @ApiParam({ name: 'deliveryId', description: 'Delivery method ID' }) - @ApiOkResponse({ description: 'Delivery method deleted successfully' }) - @ApiNotFoundResponse({ description: 'Delivery method not found' }) remove(@Param('deliveryId') deliveryId: string, @RestId() restId: string) { return this.deliveryService.remove(restId, deliveryId); } diff --git a/src/modules/foods/controllers/category.controller.ts b/src/modules/foods/controllers/category.controller.ts index 2bddc4a..8f52cf8 100644 --- a/src/modules/foods/controllers/category.controller.ts +++ b/src/modules/foods/controllers/category.controller.ts @@ -16,11 +16,13 @@ import { import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { UseGuards } from '@nestjs/common'; import { RestId } from 'src/common/decorators'; +import { Permission } from 'src/common/enums/permission.enum'; +import { Permissions } from 'src/common/decorators/permissions.decorator'; @ApiTags('category') @Controller() export class CategoryController { - constructor(private readonly categoryService: CategoryService) {} + constructor(private readonly categoryService: CategoryService) { } @Get('public/categories/restaurant/:slug') @ApiOperation({ summary: 'Get all categories by restaurant slug' }) @@ -38,31 +40,31 @@ export class CategoryController { return this.categoryService.findAllByRestaurant(slug); } + /*** Admin ***/ + @ApiOperation({ summary: 'Create category' }) - @ApiCreatedResponse({ description: 'Category created', type: CreateCategoryDto }) @ApiBody({ type: CreateCategoryDto }) + @Permissions(Permission.MANAGE_CATEGORIES) @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Post('admin/categories') create(@Body() dto: CreateCategoryDto, @RestId() restId: string) { return this.categoryService.create(restId, dto); } - + @Permissions(Permission.MANAGE_CATEGORIES) @ApiOperation({ summary: 'my restaurant categories' }) - @ApiOkResponse({ description: 'List of categories' }) @UseGuards(AdminAuthGuard) @ApiBearerAuth() - @Get('admin/categories') + @Get('admin/categories') findAll(@RestId() restId: string) { return this.categoryService.findAllByRestaurantId(restId); - } + } + @Permissions(Permission.MANAGE_CATEGORIES) @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Get('admin/categories/:id') @ApiOperation({ summary: 'Get category' }) - @ApiOkResponse({ description: 'Category detail', type: CreateCategoryDto }) - @ApiNotFoundResponse({ description: 'Category not found' }) @ApiParam({ name: 'id', required: true, type: String }) findOne(@Param('id') id: string, @RestId() restId: string) { return this.categoryService.findOne(restId, id); @@ -70,17 +72,18 @@ export class CategoryController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_CATEGORIES) @Patch('admin/categories/: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, @RestId() restId: string) { return this.categoryService.update(restId, id, dto); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_CATEGORIES) @Delete('admin/categories/:id') @ApiOperation({ summary: 'Delete category' }) @ApiParam({ name: 'id' }) @@ -89,11 +92,4 @@ export class CategoryController { 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/controllers/food.controller.ts b/src/modules/foods/controllers/food.controller.ts index 1f2180e..860e5ae 100644 --- a/src/modules/foods/controllers/food.controller.ts +++ b/src/modules/foods/controllers/food.controller.ts @@ -20,6 +20,8 @@ import { RestId, UserId } from 'src/common/decorators'; import { AuthGuard } from 'src/modules/auth/guards/auth.guard'; import { OptionalAuthGuard } from 'src/modules/auth/guards/optinalAuth.guard'; import { API_HEADER_SLUG } from 'src/common/constants'; +import { Permission } from 'src/common/enums/permission.enum'; +import { Permissions } from 'src/common/decorators/permissions.decorator'; @ApiTags('foods') @Controller() @@ -30,7 +32,6 @@ export class FoodController { @ApiOperation({ summary: 'Get all foods by restaurant slug' }) @ApiHeader(API_HEADER_SLUG) @ApiParam({ name: 'slug', required: true, description: 'Restaurant Slug' }) - @ApiOkResponse({ description: 'List of all foods for the restaurant' }) findAllByRestaurant(@Param('slug') slug: string) { return this.foodsService.findByWeekDateAndMealType(slug); } @@ -68,9 +69,9 @@ export class FoodController { /* ---------------------------------- Admin ---------------------------------- */ @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_FOODS) @Post('admin/foods') @ApiOperation({ summary: 'Create a new food' }) - @ApiCreatedResponse({ description: 'The food has been successfully created.', type: CreateFoodDto }) @ApiBody({ type: CreateFoodDto }) create(@Body() createFoodDto: CreateFoodDto, @RestId() restId: string) { return this.foodsService.create(restId, createFoodDto); @@ -78,9 +79,9 @@ export class FoodController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_FOODS) @Get('admin/foods') @ApiOperation({ summary: 'Get a paginated list of foods' }) - @ApiOkResponse({ description: 'Paginated list of foods' }) @ApiQuery({ name: 'page', required: false, type: Number }) @ApiQuery({ name: 'limit', required: false, type: Number }) @ApiQuery({ name: 'search', required: false, type: String }) @@ -95,27 +96,27 @@ export class FoodController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_FOODS) @Get('admin/foods/:id') @ApiOperation({ summary: 'Get a food by id' }) @ApiParam({ name: 'id', required: true }) - @ApiOkResponse({ description: 'The food', type: CreateFoodDto }) - @ApiNotFoundResponse({ description: 'Food not found' }) findById(@Param('id') id: string, @RestId() restId: string) { return this.foodsService.findAdminById(restId, id); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_FOODS) @Patch('admin/foods/:id') @ApiOperation({ summary: 'Update a food' }) @ApiParam({ name: 'id', required: true }) @ApiBody({ type: UpdateFoodDto }) - @ApiOkResponse({ description: 'The updated food', type: UpdateFoodDto }) update(@Param('id') id: string, @Body() updateFoodDto: UpdateFoodDto, @RestId() restId: string) { return this.foodsService.update(restId, id, updateFoodDto); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() + @Permissions(Permission.MANAGE_FOODS) @Delete('admin/foods/:id') @ApiOperation({ summary: 'Delete (soft) a food' }) @ApiParam({ name: 'id', required: true }) diff --git a/src/modules/roles/controllers/roles.controller.ts b/src/modules/roles/controllers/roles.controller.ts index 49a953c..0dc6c4c 100644 --- a/src/modules/roles/controllers/roles.controller.ts +++ b/src/modules/roles/controllers/roles.controller.ts @@ -5,7 +5,7 @@ import { PermissionsService } from '../providers/permissions.service'; import { CreateRoleDto } from '../dto/create-role.dto'; import { UpdateRoleDto } from '../dto/update-role.dto'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; -import { Permissions } from 'src/modules/auth/decorators/permissions.decorator'; +import { Permissions } from 'src/common/decorators/permissions.decorator'; import { RestId } from 'src/common/decorators'; import { AdminId } from 'src/common/decorators/admin-id.decorator'; @@ -17,7 +17,7 @@ export class RolesController { constructor( private readonly roleService: RolesService, private readonly permissionService: PermissionsService, - ) {} + ) { } @Get() @ApiOperation({ summary: 'Get all through restaurant roles with pagination and filters' })