This commit is contained in:
2025-11-16 10:37:03 +03:30
parent ad4b2e2c89
commit fae765e05a
8 changed files with 45 additions and 30 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ import { FoodModule } from './modules/foods/food.module';
FoodModule, FoodModule,
], ],
controllers: [], controllers: [],
// providers: [CacheService], providers: [],
// exports: [CacheService], // exports: [CacheService],
}) })
export class AppModule {} export class AppModule {}
+3 -1
View File
@@ -11,6 +11,7 @@ import { RestaurantsModule } from '../restaurants/restaurants.module';
import { MikroOrmModule } from '@mikro-orm/nestjs'; import { MikroOrmModule } from '@mikro-orm/nestjs';
import { User } from '../users/entities/user.entity'; import { User } from '../users/entities/user.entity';
import { AdminAuthController } from './controllers/admin-auth.controller'; import { AdminAuthController } from './controllers/admin-auth.controller';
import { AdminAuthGuard } from './guards/adminAuth.guard';
@Module({ @Module({
imports: [ imports: [
@@ -31,6 +32,7 @@ import { AdminAuthController } from './controllers/admin-auth.controller';
MikroOrmModule.forFeature([User]), MikroOrmModule.forFeature([User]),
], ],
controllers: [AuthController, AdminAuthController], controllers: [AuthController, AdminAuthController],
providers: [AuthService, TokensService], providers: [AuthService, TokensService, AdminAuthGuard],
exports: [AdminAuthGuard],
}) })
export class AuthModule {} export class AuthModule {}
+9 -7
View File
@@ -1,23 +1,25 @@
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, Inject } from '@nestjs/common';
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';
import { ITokenPayload } from '../interfaces/IToken-payload'; import { IAdminTokenPayload } from '../interfaces/IToken-payload';
export interface AuthRequest extends Request { export interface AdminAuthRequest extends Request {
userId: string; adminId: string;
restId: string; restId: string;
} }
@Injectable() @Injectable()
export class AdminAuthGuard implements CanActivate { export class AdminAuthGuard implements CanActivate {
constructor( constructor(
@Inject(JwtService)
private readonly jwtService: JwtService, private readonly jwtService: JwtService,
@Inject(ConfigService)
private readonly configService: ConfigService, private readonly configService: ConfigService,
) {} ) {}
async canActivate(context: ExecutionContext) { async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest<AuthRequest>(); const request = context.switchToHttp().getRequest<AdminAuthRequest>();
try { try {
const secret = this.configService.get<string>('JWT_SECRET'); const secret = this.configService.get<string>('JWT_SECRET');
@@ -27,10 +29,10 @@ export class AdminAuthGuard implements CanActivate {
} }
try { try {
const payload = await this.jwtService.verifyAsync<ITokenPayload>(token, { const payload = await this.jwtService.verifyAsync<IAdminTokenPayload>(token, {
secret, secret,
}); });
request['userId'] = payload.userId; request['adminId'] = payload.adminId;
request['restId'] = payload.restId; request['restId'] = payload.restId;
} catch { } catch {
throw new UnauthorizedException(); throw new UnauthorizedException();
@@ -1,4 +1,4 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common'; import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards } from '@nestjs/common';
import { FoodService } from '../providers/food.service'; import { FoodService } from '../providers/food.service';
import { CreateFoodDto } from '../dto/create-food.dto'; import { CreateFoodDto } from '../dto/create-food.dto';
import { UpdateFoodDto } from '../dto/update-food.dto'; import { UpdateFoodDto } from '../dto/update-food.dto';
@@ -14,7 +14,10 @@ import {
ApiBody, ApiBody,
ApiParam, ApiParam,
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
import { RestId } from 'src/common/decorators';
@UseGuards(AdminAuthGuard)
@ApiTags('foods') @ApiTags('foods')
@Controller('foods') @Controller('foods')
export class FoodController { export class FoodController {
@@ -24,8 +27,8 @@ export class FoodController {
@ApiOperation({ summary: 'Create a new food' }) @ApiOperation({ summary: 'Create a new food' })
@ApiCreatedResponse({ description: 'The food has been successfully created.', type: CreateFoodDto }) @ApiCreatedResponse({ description: 'The food has been successfully created.', type: CreateFoodDto })
@ApiBody({ type: CreateFoodDto }) @ApiBody({ type: CreateFoodDto })
create(@Body() createFoodDto: CreateFoodDto) { create(@Body() createFoodDto: CreateFoodDto, @RestId() restId: string) {
return this.foodsService.create(createFoodDto); return this.foodsService.create(restId, createFoodDto);
} }
@Get() @Get()
@@ -38,8 +41,8 @@ export class FoodController {
@ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] }) @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] })
@ApiQuery({ name: 'categoryId', required: false, type: String }) @ApiQuery({ name: 'categoryId', required: false, type: String })
@ApiQuery({ name: 'isActive', required: false, type: Boolean }) @ApiQuery({ name: 'isActive', required: false, type: Boolean })
findAll(@Query() dto: FindFoodsDto) { findAll(@Query() dto: FindFoodsDto,@RestId() restId: string) {
return this.foodsService.findAll(dto); return this.foodsService.findAll(restId, dto);
} }
@Get(':id') @Get(':id')
+5 -1
View File
@@ -1,9 +1,13 @@
import { Collection, Entity, ManyToMany, Property } from '@mikro-orm/core'; import { Collection, Entity, ManyToMany, OneToOne, Property } from '@mikro-orm/core';
import { Category } from './category.entity'; import { Category } from './category.entity';
import { BaseEntity } from '../../../common/entities/base.entity'; import { BaseEntity } from '../../../common/entities/base.entity';
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
@Entity({ tableName: 'foods' }) @Entity({ tableName: 'foods' })
export class Food extends BaseEntity { export class Food extends BaseEntity {
@OneToOne(() => Restaurant)
restaurant: Restaurant;
@ManyToMany(() => Category) @ManyToMany(() => Category)
categories = new Collection<Category>(this); categories = new Collection<Category>(this);
+4 -1
View File
@@ -8,9 +8,12 @@ import { CategoryRepository } from './repositories/category.repository';
import { MikroOrmModule } from '@mikro-orm/nestjs'; import { MikroOrmModule } from '@mikro-orm/nestjs';
import { Category } from './entities/category.entity'; import { Category } from './entities/category.entity';
import { Food } from './entities/food.entity'; import { Food } from './entities/food.entity';
import { RestaurantsModule } from '../restaurants/restaurants.module';
import { AuthModule } from '../auth/auth.module';
import { JwtModule } from '@nestjs/jwt';
@Module({ @Module({
imports: [MikroOrmModule.forFeature([Food, Category])], imports: [MikroOrmModule.forFeature([Food, Category]), RestaurantsModule, AuthModule, JwtModule],
controllers: [FoodController, CategoryController], controllers: [FoodController, CategoryController],
providers: [FoodService, CategoryService, FoodRepository, CategoryRepository], providers: [FoodService, CategoryService, FoodRepository, CategoryRepository],
exports: [FoodRepository, CategoryRepository], exports: [FoodRepository, CategoryRepository],
+13 -12
View File
@@ -6,24 +6,24 @@ import { CategoryRepository } from '../repositories/category.repository';
import { EntityManager } from '@mikro-orm/postgresql'; import { EntityManager } from '@mikro-orm/postgresql';
import { RequiredEntityData } from '@mikro-orm/core'; import { RequiredEntityData } from '@mikro-orm/core';
import { Food } from '../entities/food.entity'; import { Food } from '../entities/food.entity';
import { FoodMessage } from 'src/common/enums/message.enum'; import { FoodMessage, RestMessage } from 'src/common/enums/message.enum';
// import { RestRepository } from 'src/modules/restaurants/repositories/rest.repository'; import { RestRepository } from 'src/modules/restaurants/repositories/rest.repository';
@Injectable() @Injectable()
export class FoodService { export class FoodService {
constructor( constructor(
private readonly foodRepository: FoodRepository, private readonly foodRepository: FoodRepository,
private readonly categoryRepository: CategoryRepository, private readonly categoryRepository: CategoryRepository,
// private readonly restRepository: RestRepository, private readonly restRepository: RestRepository,
private readonly em: EntityManager, private readonly em: EntityManager,
) {} ) {}
async create(createFoodDto: CreateFoodDto): Promise<Food> { async create(restId: string, createFoodDto: CreateFoodDto): Promise<Food> {
const { categoryIds, ...rest } = createFoodDto; const { categoryIds, ...rest } = createFoodDto;
// const restaurant = await this.restRepository.findOne({ id: createFoodDto.restId }); const restaurant = await this.restRepository.findOne({ id: restId });
// if (!restaurant) { if (!restaurant) {
// throw new NotFoundException(RestMessage.NOT_FOUND); throw new NotFoundException(RestMessage.NOT_FOUND);
// } }
// prepare data with defaults for required fields so repository.create typing is satisfied // prepare data with defaults for required fields so repository.create typing is satisfied
const data: RequiredEntityData<Food> = { const data: RequiredEntityData<Food> = {
// boolean/day flags // boolean/day flags
@@ -48,9 +48,10 @@ export class FoodService {
points: rest.points ?? 0, points: rest.points ?? 0,
prepareTime: rest.prepareTime ?? 0, prepareTime: rest.prepareTime ?? 0,
images: rest.images ?? undefined, images: rest.images ?? undefined,
} as RequiredEntityData<Food>; restaurant: restaurant,
};
const food = this.foodRepository.create(data) as Food; const food = this.foodRepository.create(data);
if (!food) { if (!food) {
throw new Error('Failed to create food entity'); throw new Error('Failed to create food entity');
} }
@@ -65,8 +66,8 @@ export class FoodService {
return food; return food;
} }
findAll(dto: FindFoodsDto) { findAll(restId: string, dto: FindFoodsDto) {
return this.foodRepository.findAllPaginated(dto); return this.foodRepository.findAllPaginated(restId, dto);
} }
findOne(id: string) { findOne(id: string) {
@@ -23,12 +23,12 @@ export class FoodRepository extends EntityRepository<Food> {
* Find foods with pagination and optional filters. * Find foods with pagination and optional filters.
* Supports: search (title/content), categoryId, isActive, ordering. * Supports: search (title/content), categoryId, isActive, ordering.
*/ */
async findAllPaginated(opts: FindFoodsOpts = {}): Promise<PaginatedResult<Food>> { async findAllPaginated(restId: string, opts: FindFoodsOpts = {}): Promise<PaginatedResult<Food>> {
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc', categoryId, isActive } = opts; const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc', categoryId, isActive } = opts;
const offset = (page - 1) * limit; const offset = (page - 1) * limit;
const where: FilterQuery<Food> = {}; const where: FilterQuery<Food> = { restaurant: { id: restId } };
if (typeof isActive === 'boolean') { if (typeof isActive === 'boolean') {
where.isActive = isActive; where.isActive = isActive;