This commit is contained in:
2025-12-13 11:14:18 +03:30
parent 3eb6b889a4
commit c25a5afe01
80 changed files with 440 additions and 190 deletions
+2 -2
View File
@@ -23,7 +23,7 @@ export function cacheConfig(): CacheModuleAsyncOptions {
keyvRedisStore.client.on('connect', () => {
logger.log('✅ Redis connected successfully.');
});
keyvRedisStore.client.on('error', (error) => {
keyvRedisStore.client.on('error', error => {
logger.error('❌ Redis connection error:', error);
});
@@ -40,4 +40,4 @@ export function cacheConfig(): CacheModuleAsyncOptions {
};
},
};
}
}
+2 -2
View File
@@ -29,7 +29,7 @@ const dataBaseConfig: MikroOrmModuleAsyncOptions = {
const encodedPassword = encodeURIComponent(DB_PASS);
const isProduction = configService.getOrThrow<string>('NODE_ENV') === 'production';
return ({
return {
// entities: ['dist/**/*.entity.js'],
entitiesTs: ['src/**/*.entity.ts'],
driver: PostgreSqlDriver,
@@ -102,7 +102,7 @@ const dataBaseConfig: MikroOrmModuleAsyncOptions = {
return false;
},
},
});
};
},
};
@@ -174,5 +174,4 @@ export class AdminService {
}
return this.em.removeAndFlush(adminRole);
}
}
@@ -1,6 +1,5 @@
import { SetMetadata } from '@nestjs/common';
import { SetMetadata } from '@nestjs/common';
export const PERMISSIONS_KEY = 'permissions';
export const Permissions = (...permissions: string[]) =>
SetMetadata(PERMISSIONS_KEY, permissions);
export const Permissions = (...permissions: string[]) => SetMetadata(PERMISSIONS_KEY, permissions);
@@ -1,5 +1,5 @@
import { User } from '../../users/entities/user.entity';
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
import type { User } from '../../users/entities/user.entity';
import type { Restaurant } from '../../restaurants/entities/restaurant.entity';
export interface UserLoginResponse {
id: string;
@@ -34,4 +34,3 @@ export class UserLoginTransformer {
};
}
}
+112 -14
View File
@@ -22,14 +22,28 @@ export class CartController {
@Get()
@ApiOperation({ summary: 'Get cart for current user and restaurant' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
async findOne(@UserId() userId: string, @RestId() restaurantId: string) {
return this.cartService.getOrCreateCart(userId, restaurantId);
}
@Post('items/:foodId/increment')
@ApiOperation({ summary: 'Increment item quantity in cart by 1' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'foodId', description: 'Food ID to increment in cart' })
async incrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
return this.cartService.incrementItem(userId, restaurantId, foodId);
@@ -37,7 +51,14 @@ export class CartController {
@Post('items/:foodId/decrement')
@ApiOperation({ summary: 'Decrement item quantity in cart by 1 (removes item if quantity reaches 0)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'foodId', description: 'Food ID to decrement in cart' })
async decrementItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
return this.cartService.decrementItem(userId, restaurantId, foodId);
@@ -45,7 +66,14 @@ export class CartController {
@Post('items/bulk')
@ApiOperation({ summary: 'Bulk add items to cart (increments quantity if items exist)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: BulkAddItemsToCartDto })
bulkAddItems(
@UserId() userId: string,
@@ -57,7 +85,14 @@ export class CartController {
@Patch('items/:foodId')
@ApiOperation({ summary: 'Update item quantity in cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'foodId', description: 'Food ID in the cart' })
@ApiBody({ type: UpdateItemQuantityDto })
async updateItemQuantity(
@@ -71,7 +106,14 @@ export class CartController {
@Delete('items/:foodId')
@ApiOperation({ summary: 'Remove item from cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'foodId', description: 'Food ID in the cart' })
async removeItem(@UserId() userId: string, @RestId() restaurantId: string, @Param('foodId') foodId: string) {
return this.cartService.removeItem(userId, restaurantId, foodId);
@@ -79,14 +121,28 @@ export class CartController {
@Delete()
@ApiOperation({ summary: 'Clear entire cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
async clearCart(@UserId() userId: string, @RestId() restaurantId: string) {
return this.cartService.clearCart(userId, restaurantId);
}
@Post('apply-coupon')
@ApiOperation({ summary: 'Apply coupon to cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: ApplyCouponDto })
async applyCoupon(@UserId() userId: string, @RestId() restaurantId: string, @Body() applyCouponDto: ApplyCouponDto) {
return this.cartService.applyCoupon(userId, restaurantId, applyCouponDto);
@@ -94,14 +150,28 @@ export class CartController {
@Delete('coupon')
@ApiOperation({ summary: 'Remove coupon from cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
async removeCoupon(@UserId() userId: string, @RestId() restaurantId: string) {
return this.cartService.removeCoupon(userId, restaurantId);
}
@Patch('address')
@ApiOperation({ summary: 'Set address for cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: SetAddressDto })
async setAddress(@UserId() userId: string, @RestId() restaurantId: string, @Body() setAddressDto: SetAddressDto) {
return this.cartService.setAddress(userId, restaurantId, setAddressDto);
@@ -109,7 +179,14 @@ export class CartController {
@Patch('payment-method')
@ApiOperation({ summary: 'Set payment method for cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: SetPaymentMethodDto })
async setPaymentMethod(
@UserId() userId: string,
@@ -121,7 +198,14 @@ export class CartController {
@Patch('delivery-method')
@ApiOperation({ summary: 'Set delivery method for cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: SetDeliveryMethodDto })
setDeliveryMethod(
@UserId() userId: string,
@@ -133,7 +217,14 @@ export class CartController {
@Patch('description')
@ApiOperation({ summary: 'Set description for cart' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: SetDescriptionDto })
setDescription(
@UserId() userId: string,
@@ -145,7 +236,14 @@ export class CartController {
@Patch('table-number')
@ApiOperation({ summary: 'Set table number for cart (required when delivery method is DineIn)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: SetTableNumberDto })
setTableNumber(
@UserId() userId: string,
-1
View File
@@ -7,4 +7,3 @@ export class ApplyCouponDto {
@IsString()
code!: string;
}
-1
View File
@@ -7,4 +7,3 @@ export class SetAddressDto {
@IsString()
addressId!: string;
}
@@ -7,4 +7,3 @@ export class SetDeliveryMethodDto {
@IsString()
deliveryMethodId!: string;
}
+5 -2
View File
@@ -2,9 +2,12 @@ import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, IsString } from 'class-validator';
export class SetDescriptionDto {
@ApiProperty({ description: 'Cart description or notes', required: false, example: 'Please deliver to the back door' })
@ApiProperty({
description: 'Cart description or notes',
required: false,
example: 'Please deliver to the back door',
})
@IsOptional()
@IsString()
description?: string;
}
@@ -7,4 +7,3 @@ export class SetPaymentMethodDto {
@IsString()
paymentMethodId!: string;
}
@@ -7,4 +7,3 @@ export class SetTableNumberDto {
@IsString()
tableNumber?: string;
}
-1
View File
@@ -8,4 +8,3 @@ export class UpdateItemQuantityDto {
@Min(1)
quantity!: number;
}
+1 -5
View File
@@ -613,11 +613,7 @@ export class CartService {
/**
* Set table number for cart
*/
async setTableNumber(
userId: string,
restaurantId: string,
setTableNumberDto: SetTableNumberDto,
): Promise<Cart> {
async setTableNumber(userId: string, restaurantId: string, setTableNumberDto: SetTableNumberDto): Promise<Cart> {
const cart = await this.findOneOrFail(userId, restaurantId);
// If delivery method is set, validate that table number is required for DineIn
@@ -25,7 +25,14 @@ export class ContactController {
@Post('public/contact')
@ApiOperation({ summary: 'Create a new contact (public endpoint)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
async create(@Body() createContactDto: CreateContactDto) {
return this.contactService.create(createContactDto);
}
@@ -39,4 +39,3 @@ export class FindContactsDto {
@ApiPropertyOptional({ enum: ContactStatusEnum, description: 'Filter by status' })
status?: ContactStatusEnum;
}
@@ -8,4 +8,3 @@ export class UpdateContactStatusDto {
@ApiProperty({ enum: ContactStatusEnum, description: 'New status for the contact' })
status!: ContactStatusEnum;
}
@@ -37,11 +37,7 @@ export class ContactRepository extends EntityRepository<Contact> {
if (search) {
const pattern = `%${search}%`;
where.$or = [
{ subject: { $ilike: pattern } },
{ content: { $ilike: pattern } },
{ phone: { $ilike: pattern } },
];
where.$or = [{ subject: { $ilike: pattern } }, { content: { $ilike: pattern } }, { phone: { $ilike: pattern } }];
}
const [data, total] = await this.findAndCount(where, {
@@ -63,4 +59,3 @@ export class ContactRepository extends EntityRepository<Contact> {
};
}
}
@@ -28,7 +28,14 @@ export class CouponController {
@ApiBearerAuth()
@Get('public/coupons/me')
@ApiOperation({ summary: 'Get paginated list of restaurant coupons' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({ description: 'Paginated list of restaurant coupons' })
@ApiQuery({ name: 'page', required: false, type: Number })
@ApiQuery({ name: 'limit', required: false, type: Number })
-1
View File
@@ -15,4 +15,3 @@ import { JwtModule } from '@nestjs/jwt';
exports: [CouponRepository, CouponService],
})
export class CouponModule {}
@@ -42,4 +42,3 @@ export class FindCouponsDto {
@ApiPropertyOptional({ enum: ['asc', 'desc'], example: 'desc', description: 'Sort order' })
order?: 'asc' | 'desc';
}
@@ -2,4 +2,3 @@ import { PartialType } from '@nestjs/swagger';
import { CreateCouponDto } from './create-coupon.dto';
export class UpdateCouponDto extends PartialType(CreateCouponDto) {}
@@ -19,4 +19,3 @@ export class ValidateCouponDto {
@ApiPropertyOptional({ example: 'user-id', description: 'User ID for per-user usage limits' })
userId?: string;
}
@@ -27,7 +27,14 @@ export class DeliveryController {
@ApiBearerAuth()
@Get('public/delivery-methods/restaurant')
@ApiOperation({ summary: 'Get restaurant delivery methods' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({
description: 'List of restaurant delivery methods',
type: [Delivery],
@@ -8,4 +8,3 @@ export class DeliveryRepository extends EntityRepository<Delivery> {
super(em, Delivery);
}
}
@@ -25,7 +25,14 @@ export class CategoryController {
@Get('public/categories/restaurant/:slug')
@ApiOperation({ summary: 'Get all categories by restaurant slug' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'slug', required: true, description: 'Restaurant Slug' })
@ApiOkResponse({ description: 'List of all categories for the restaurant' })
findAllByRestaurant(@Param('slug') slug: string) {
@@ -25,7 +25,14 @@ export class FoodController {
@Get('public/foods/restaurant/:slug')
@ApiOperation({ summary: 'Get all foods by restaurant slug' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'slug', required: true, description: 'Restaurant Slug' })
@ApiOkResponse({ description: 'List of all foods for the restaurant' })
findAllByRestaurant(@Param('slug') slug: string) {
@@ -21,7 +21,14 @@ export class NotificationsController {
@ApiBearerAuth()
@Get('public/notifications')
@ApiOperation({ summary: 'Get user restaurant notifications' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiQuery({ name: 'limit', required: false, type: Number })
async getUserNotifications(@UserId() userId: string, @RestId() restaurantId: string, @Query('limit') limit?: number) {
return await this.notificationService.findByUserAndRestaurant(
@@ -1,5 +1,6 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
import type { ExecutionContext } from '@nestjs/common';
import { createParamDecorator } from '@nestjs/common';
import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
/**
* Extract restaurant ID from authenticated WebSocket client
@@ -38,4 +38,3 @@ export class CreateNotificationDto {
@IsOptional()
data?: Record<string, any>;
}
@@ -28,7 +28,7 @@ export class PushNotificationService {
private initializeFirebase() {
try {
const firebaseConfig = this.configService.get<string>('FIREBASE_SERVICE_ACCOUNT');
if (!firebaseConfig) {
this.logger.warn('Firebase service account not configured. Push notifications will be disabled.');
return;
@@ -51,7 +51,10 @@ export class PushNotificationService {
async sendToToken(payload: PushNotificationPayload): Promise<PushNotificationResponse> {
if (!this.firebaseApp || !payload.token) {
throw new HttpException('Push notification service not configured or token missing', HttpStatus.SERVICE_UNAVAILABLE);
throw new HttpException(
'Push notification service not configured or token missing',
HttpStatus.SERVICE_UNAVAILABLE,
);
}
try {
@@ -115,9 +118,7 @@ export class PushNotificationService {
};
const response = await admin.messaging().sendEachForMulticast(message);
this.logger.log(
`Push notifications sent: ${response.successCount} successful, ${response.failureCount} failed`,
);
this.logger.log(`Push notifications sent: ${response.successCount} successful, ${response.failureCount} failed`);
return {
success: response.successCount > 0,
@@ -145,4 +146,3 @@ export class PushNotificationService {
return this.firebaseApp !== null;
}
}
@@ -16,7 +16,14 @@ export class OrdersController {
@ApiBearerAuth()
@Post('public/checkout')
@ApiOperation({ summary: 'Checkout : create order and payment record' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
checkout(@UserId() userId: string, @RestId() restaurantId: string) {
return this.ordersService.checkout(userId, restaurantId);
}
@@ -25,7 +32,14 @@ export class OrdersController {
@ApiBearerAuth()
@Get('public/orders')
@ApiOperation({ summary: 'Get all orders with pagination and filters' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
findAll(@RestId() restId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) {
return this.ordersService.findAllForUser(restId, dto, userId);
}
@@ -33,7 +47,14 @@ export class OrdersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get an order By id for User' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'orderId', description: 'Order ID' })
@Get('public/orders/:orderId')
findOne(@Param('orderId') orderId: string, @RestId() restId: string) {
@@ -44,7 +65,14 @@ export class OrdersController {
@ApiBearerAuth()
@Patch('public/orders/:id/cancel')
@ApiOperation({ summary: 'Cancel an order By User' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'id', description: 'Order ID' })
cancelOrder(@Param('id') id: string, @RestId() restId: string) {
return this.ordersService.cancelOrderAsUser(id, restId);
@@ -124,4 +124,3 @@ export class FindOrdersDto {
@IsIn(sortOrderOptions)
order?: SortOrder = 'desc';
}
@@ -57,7 +57,14 @@ export class PagerController {
@UseGuards(OptionalAuthGuard)
@Get('public/pager')
@ApiOperation({ summary: 'Get all pager requests for the current session' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiHeader({ name: 'Authorization', required: false, description: 'Bearer token (optional authentication)' })
async findAll(@Req() request: FastifyRequest) {
const cookieId = request.cookies['pagerId'] || undefined;
@@ -1,5 +1,6 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
import type { ExecutionContext } from '@nestjs/common';
import { createParamDecorator } from '@nestjs/common';
import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
/**
* Extract admin ID from authenticated WebSocket client
@@ -23,4 +24,3 @@ export const WsAdminId = createParamDecorator((data: unknown, ctx: ExecutionCont
return adminId;
});
@@ -1,5 +1,6 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
import type { ExecutionContext } from '@nestjs/common';
import { createParamDecorator } from '@nestjs/common';
import type { AuthenticatedSocket } from '../guards/ws-admin-auth.guard';
/**
* Extract restaurant ID from authenticated WebSocket client
@@ -23,4 +24,3 @@ export const WsRestId = createParamDecorator((data: unknown, ctx: ExecutionConte
return restId;
});
+1 -1
View File
@@ -69,7 +69,7 @@ export class PagerService {
// Populate relations before emitting
await this.em.populate(pager, ['restaurant']);
// Emit socket event for new pager
// Emit socket event for new pager
this.eventEmitter.emit(PagerCreatedEvent.name, new PagerCreatedEvent(pager.restaurant.id));
return pager;
@@ -29,7 +29,14 @@ export class PaymentsController {
@ApiBearerAuth()
@Get('public/payments/methods/restaurant')
@ApiOperation({ summary: 'Get the restaurant payment methods' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiNotFoundResponse({ description: 'Restaurant payment methods not found' })
getTheRestaurantPaymentMethods(@RestId() restId: string) {
return this.paymentMethodService.findByRestaurant(restId);
@@ -39,7 +46,14 @@ export class PaymentsController {
@ApiBearerAuth()
@Get('public/payments')
@ApiOperation({ summary: 'Get all the restaurant payments for user' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
findAllByRestaurant(@UserId() userId: string, @RestId() restId: string) {
return this.paymentsService.findAllByRestaurantId(restId, userId);
}
@@ -127,7 +141,14 @@ export class PaymentsController {
@Post('public/payments/verify')
@ApiOperation({ summary: 'Verify a payment' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: VerifyPaymentDto })
@ApiNotFoundResponse({ description: 'Payment not found' })
verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) {
@@ -2,4 +2,3 @@ import { PartialType } from '@nestjs/mapped-types';
import { CreatePaymentMethodDto } from './create-payment-method.dto';
export class UpdatePaymentMethodDto extends PartialType(CreatePaymentMethodDto) {}
@@ -42,10 +42,7 @@ export class PaymentMethodService {
}
async findByRestaurant(restaurantId: string): Promise<PaymentMethod[]> {
return this.paymentMethodRepository.find(
{ restaurant: { id: restaurantId } },
{ populate: ['restaurant'] },
);
return this.paymentMethodRepository.find({ restaurant: { id: restaurantId } }, { populate: ['restaurant'] });
}
async update(id: string, updatePaymentMethodDto: UpdatePaymentMethodDto): Promise<PaymentMethod> {
@@ -21,7 +21,14 @@ export class RestaurantsController {
@Get('public/restaurants/:slug')
@ApiOperation({ summary: 'Get restaurant specification by slug' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'slug', required: true, description: 'Restaurant slug' })
@ApiNotFoundResponse({ description: 'Restaurant not found' })
findBySlug(@Param('slug') slug: string) {
@@ -26,7 +26,14 @@ export class ScheduleController {
@Get('public/schedules/restaurant/:slug')
@ApiOperation({ summary: 'Get schedule of a restaurant by slug' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'slug', description: 'Restaurant Slug' })
@ApiOkResponse({ description: 'Today schedule of the restaurant', type: Schedule })
async getTodaySchedules(@Param('slug') slug: string): Promise<Schedule[]> {
@@ -29,5 +29,4 @@ export class CreateScheduleDto {
@IsBoolean()
@Type(() => Boolean)
isActive?: boolean;
}
@@ -26,7 +26,18 @@ export class RestaurantSpecificationDto {
longitude?: number;
@ApiPropertyOptional({
example: { type: 'Polygon', coordinates: [[[51.389, 35.6892], [51.390, 35.6892], [51.390, 35.6902], [51.389, 35.6902], [51.389, 35.6892]]] },
example: {
type: 'Polygon',
coordinates: [
[
[51.389, 35.6892],
[51.39, 35.6892],
[51.39, 35.6902],
[51.389, 35.6902],
[51.389, 35.6892],
],
],
},
description: 'Service area as GeoJSON Polygon',
})
serviceArea?: {
@@ -30,7 +30,14 @@ export class ReviewController {
@ApiBearerAuth()
@Post('public/reviews')
@ApiOperation({ summary: 'Create a new review and rating' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiCreatedResponse({ description: 'The review has been successfully created.' })
@ApiBody({ type: CreateReviewDto })
create(@Body() createReviewDto: CreateReviewDto, @UserId() userId: string) {
@@ -39,7 +46,14 @@ export class ReviewController {
@Get('public/reviews/:foodId')
@ApiOperation({ summary: 'Get all reviews of food by id(public - only approved)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({ description: 'List of approved reviews' })
@ApiParam({ name: 'foodId', required: true, type: String })
@ApiQuery({ name: 'page', required: false, type: Number })
@@ -53,7 +67,14 @@ export class ReviewController {
@Get('public/reviews/restuarant/:restuarantSlug')
@ApiOperation({ summary: 'Get all reviews of restuarant by slug(public - only approved)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({ description: 'List of approved reviews' })
@ApiParam({ name: 'restuarantSlug', required: true, type: String })
@ApiQuery({ name: 'page', required: false, type: Number })
@@ -78,7 +99,14 @@ export class ReviewController {
@ApiBearerAuth()
@Patch('public/reviews/:reviewId')
@ApiOperation({ summary: 'Update a review (own reviews only)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'reviewId', required: true })
@ApiBody({ type: UpdateReviewDto })
@ApiOkResponse({ description: 'The updated review' })
@@ -90,7 +118,14 @@ export class ReviewController {
@ApiBearerAuth()
@Delete('public/reviews/:id')
@ApiOperation({ summary: 'Delete a review (own reviews only)' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiParam({ name: 'id', required: true })
remove(@Param('id') id: string, @UserId() userId: string) {
return this.reviewService.remove(id, userId, false);
@@ -11,4 +11,3 @@ export class ChangeStatusDto {
})
status: ReviewStatus;
}
+6 -7
View File
@@ -21,20 +21,20 @@ export class UpdateReviewDto {
@IsOptional()
@IsArray()
@IsEnum(PositivePoint, { each: true })
@ApiPropertyOptional({
description: 'Positive points about the food',
@ApiPropertyOptional({
description: 'Positive points about the food',
enum: PositivePoint,
isArray: true
isArray: true,
})
positivePoints?: PositivePoint[];
@IsOptional()
@IsArray()
@IsEnum(NegativePoint, { each: true })
@ApiPropertyOptional({
description: 'Negative points about the food',
@ApiPropertyOptional({
description: 'Negative points about the food',
enum: NegativePoint,
isArray: true
isArray: true,
})
negativePoints?: NegativePoint[];
@@ -43,4 +43,3 @@ export class UpdateReviewDto {
@ApiPropertyOptional({ description: 'Review status (admin only)', enum: ReviewStatus })
status?: ReviewStatus;
}
@@ -3,4 +3,3 @@ export enum ReviewStatus {
REJECTED = 'rejected',
APPROVED = 'approved',
}
@@ -69,12 +69,9 @@ export class FoodRatingCronService {
// Flush all updates at once for better performance
await this.em.flush();
this.logger.log(
`Food rating calculation completed. Updated: ${updatedCount}, Errors: ${errorCount}`,
);
this.logger.log(`Food rating calculation completed. Updated: ${updatedCount}, Errors: ${errorCount}`);
} catch (error) {
this.logger.error(`Error in food rating cron job: ${error.message}`, error.stack);
}
}
}
@@ -53,7 +53,7 @@ export class ReviewRepository extends EntityRepository<Review> {
limit,
offset,
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
populate: ['food','user'],
populate: ['food', 'user'],
});
const totalPages = Math.ceil(total / limit);
+1 -8
View File
@@ -11,16 +11,9 @@ import { AuthModule } from '../auth/auth.module';
import { JwtModule } from '@nestjs/jwt';
@Module({
imports: [
MikroOrmModule.forFeature([Review]),
FoodModule,
UserModule,
AuthModule,
JwtModule,
],
imports: [MikroOrmModule.forFeature([Review]), FoodModule, UserModule, AuthModule, JwtModule],
controllers: [ReviewController],
providers: [ReviewService, ReviewRepository, FoodRatingCronService],
exports: [ReviewRepository],
})
export class ReviewModule {}
-1
View File
@@ -7,7 +7,6 @@ export class CreateRoleDto {
@IsString()
name!: string;
@ApiProperty({ description: 'List of permission IDs', isArray: true, required: false })
@IsOptional()
@IsArray()
@@ -10,7 +10,7 @@ export class Permission extends BaseEntity {
@Property()
title!: string;
// اضافه شد
// اضافه شد
@Property({ default: 'general' })
group!: string;
@@ -1,4 +1,4 @@
import { File } from "@nest-lab/fastify-multer";
import type { File } from '@nest-lab/fastify-multer';
export interface FileUploadResult {
file: File;
@@ -19,7 +19,7 @@ export interface FileMetadata {
language?: string;
}
export type FileType = "video" | "audio" | "image" | "document" | "text" | "unknown";
export type FileType = 'video' | 'audio' | 'image' | 'document' | 'text' | 'unknown';
export interface FileValidationOptions {
maxFileSize: number;
@@ -32,7 +32,7 @@ export interface FileUploadOptions {
}
// File processing status
export type FileProcessingStatus = "pending" | "processing" | "completed" | "failed";
export type FileProcessingStatus = 'pending' | 'processing' | 'completed' | 'failed';
export interface FileProcessingResult {
status: FileProcessingStatus;
@@ -7,7 +7,7 @@ export interface S3UploadResult {
export interface S3UploadOptions {
contentType: string;
metadata?: Record<string, string>;
acl?: "private" | "public-read" | "public-read-write";
acl?: 'private' | 'public-read' | 'public-read-write';
}
export interface S3DownloadOptions {
@@ -19,7 +19,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get the current authenticated user profile' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@Get('public/user/me')
async getUser(@UserId() userId: string) {
const user = await this.userService.findById(userId);
@@ -28,7 +35,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Update the authenticated user profile' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: UpdateUserDto })
@Patch('public/user/update')
async updateUser(
@@ -42,7 +56,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get all addresses for the authenticated user' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({ description: 'List of user addresses' })
@Get('public/user/addresses')
async getUserAddresses(@UserId() userId: string) {
@@ -53,7 +74,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Create a new address for the authenticated user' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: CreateUserAddressDto })
@ApiOkResponse({ description: 'Created user address' })
@Post('public/user/addresses')
@@ -68,7 +96,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get a single address by ID for the authenticated user' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({ description: 'User address details' })
@Get('public/user/addresses/:addressId')
async getUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
@@ -79,7 +114,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Update an address for the authenticated user' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: UpdateUserAddressDto })
@ApiOkResponse({ description: 'Updated user address' })
@Patch('public/user/addresses/:addressId')
@@ -95,7 +137,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Delete an address for the authenticated user' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({ description: 'Address deleted successfully' })
@Delete('public/user/addresses/:addressId')
async deleteUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
@@ -106,7 +155,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Set an address as default for the authenticated user' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiOkResponse({ description: 'Address set as default' })
@Patch('public/user/addresses/:addressId/default')
async setDefaultAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
@@ -130,7 +186,14 @@ export class UsersController {
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Convert user score (points) to wallet balance' })
@ApiHeader({ name: 'X-Slug', required: true, description: 'Restaurant slug identifier', example: 'zhivan' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBody({ type: ConvertScoreToWalletDto })
@Post('public/user/convert-score-to-wallet')
async convertScoreToWallet(@UserId() userId: string, @Body() dto: ConvertScoreToWalletDto, @RestId() restId: string) {
@@ -33,7 +33,7 @@ export class CreateUserAddressDto {
@Max(90)
latitude!: number;
@ApiProperty({ description: 'Longitude coordinate', example: 51.3890, minimum: -180, maximum: 180 })
@ApiProperty({ description: 'Longitude coordinate', example: 51.389, minimum: -180, maximum: 180 })
@IsNumber()
@Min(-180)
@Max(180)
@@ -49,4 +49,3 @@ export class CreateUserAddressDto {
@IsBoolean()
isDefault?: boolean;
}
@@ -37,7 +37,7 @@ export class UpdateUserAddressDto {
@Max(90)
latitude?: number;
@ApiPropertyOptional({ description: 'Longitude coordinate', example: 51.3890, minimum: -180, maximum: 180 })
@ApiPropertyOptional({ description: 'Longitude coordinate', example: 51.389, minimum: -180, maximum: 180 })
@IsOptional()
@IsNumber()
@Min(-180)
@@ -54,4 +54,3 @@ export class UpdateUserAddressDto {
@IsBoolean()
isDefault?: boolean;
}
+1 -4
View File
@@ -43,9 +43,6 @@ export class CacheService implements OnModuleInit {
}
}
// // src/modules/cache/cache.service.ts
// import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
// import { Inject, Injectable, Logger, OnModuleInit } from '@nestjs/common';
@@ -147,4 +144,4 @@ export class CacheService implements OnModuleInit {
// return undefined;
// }
// }
// }
// }
-1
View File
@@ -28,4 +28,3 @@ export function normalizePhone(phone: string): string {
return normalized;
}
+2 -2
View File
@@ -1,8 +1,8 @@
import type { EntityManager } from '@mikro-orm/core';
import { Admin } from '../modules/admin/entities/admin.entity';
import { AdminRole } from '../modules/admin/entities/adminRole.entity';
import { Role } from '../modules/roles/entities/role.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Role } from '../modules/roles/entities/role.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { adminsData } from './data/admins.data';
import { normalizePhone } from '../modules/utils/phone.util';
+2 -6
View File
@@ -1,13 +1,10 @@
import type { EntityManager } from '@mikro-orm/core';
import { Category } from '../modules/foods/entities/category.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { categoriesData } from './data/categories.data';
export class CategoriesSeeder {
async run(
em: EntityManager,
restaurantsMap: Map<string, Restaurant>,
): Promise<Map<string, Category>> {
async run(em: EntityManager, restaurantsMap: Map<string, Restaurant>): Promise<Map<string, Category>> {
const categoriesMap = new Map<string, Category>();
for (const catData of categoriesData) {
@@ -36,4 +33,3 @@ export class CategoriesSeeder {
return categoriesMap;
}
}
+1 -2
View File
@@ -1,6 +1,6 @@
import type { EntityManager } from '@mikro-orm/core';
import { Coupon } from '../modules/coupons/entities/coupon.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { couponsData } from './data/coupons.data';
export class CouponsSeeder {
@@ -40,4 +40,3 @@ export class CouponsSeeder {
await em.flush();
}
}
-1
View File
@@ -29,4 +29,3 @@ export const adminsData: AdminData[] = [
restaurantSlug: null,
},
];
-1
View File
@@ -13,4 +13,3 @@ export const categoriesData: CategoryData[] = [
{ title: 'دسر', restaurantSlug: 'boote' },
{ title: 'نوشیدنی', restaurantSlug: 'boote' },
];
-1
View File
@@ -92,4 +92,3 @@ export const couponsData: CouponData[] = [
isActive: true,
},
];
@@ -48,4 +48,3 @@ export const deliveryMethodsData: DeliveryMethodData[] = [
order: 4,
},
];
@@ -1,5 +1,5 @@
import { NotifChannelEnum, NotifTitleEnum } from '../../modules/notifications/interfaces/notification.interface';
export interface NotificationPreferenceData {
title: NotifTitleEnum;
channels: NotifChannelEnum[];
-1
View File
@@ -13,4 +13,3 @@ export const getPermissionsData = () => {
group: specialPermissions.includes(name) ? 'special' : 'general',
}));
};
+1 -4
View File
@@ -16,9 +16,7 @@ export const rolesData: RoleConfig[] = [
name: 'accountant',
isSystem: false,
permissionFilter: (name: Permission) =>
name === Permission.VIEW_REPORTS ||
name === Permission.MANAGE_FINANCES ||
name === Permission.MANAGE_ORDERS,
name === Permission.VIEW_REPORTS || name === Permission.MANAGE_FINANCES || name === Permission.MANAGE_ORDERS,
},
{
name: 'superAdmin',
@@ -29,4 +27,3 @@ export const rolesData: RoleConfig[] = [
name === Permission.UPDATE_RESTAURANT,
},
];
-1
View File
@@ -8,4 +8,3 @@ export const timeSlots: TimeSlot[] = [
{ openTime: '12:00', closeTime: '15:00' }, // Lunch
{ openTime: '18:00', closeTime: '23:00' }, // Dinner
];
+7 -8
View File
@@ -20,7 +20,7 @@ export const userAddressesData: UserAddressData[] = [
province: 'تهران',
postalCode: '1234567890',
latitude: 35.6892,
longitude: 51.3890,
longitude: 51.389,
phone: '09362532122',
isDefault: true,
},
@@ -31,8 +31,8 @@ export const userAddressesData: UserAddressData[] = [
city: 'تهران',
province: 'تهران',
postalCode: '1234567891',
latitude: 35.6900,
longitude: 51.3900,
latitude: 35.69,
longitude: 51.39,
phone: '09185290775',
isDefault: true,
},
@@ -43,8 +43,8 @@ export const userAddressesData: UserAddressData[] = [
city: 'تهران',
province: 'تهران',
postalCode: '1234567892',
latitude: 35.6910,
longitude: 51.3910,
latitude: 35.691,
longitude: 51.391,
phone: '09129283395',
isDefault: true,
},
@@ -55,10 +55,9 @@ export const userAddressesData: UserAddressData[] = [
city: 'تهران',
province: 'تهران',
postalCode: '1234567893',
latitude: 35.6920,
longitude: 51.3920,
latitude: 35.692,
longitude: 51.392,
phone: '09184317567',
isDefault: true,
},
];
+1 -1
View File
@@ -1,6 +1,6 @@
import type { EntityManager } from '@mikro-orm/core';
import { Delivery } from '../modules/delivery/entities/delivery.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { deliveryMethodsData } from './data/delivery-methods.data';
import { DeliveryFeeTypeEnum } from 'src/modules/delivery/interface/delivery';
+2 -2
View File
@@ -1,7 +1,7 @@
import type { EntityManager } from '@mikro-orm/core';
import { Food } from '../modules/foods/entities/food.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { Category } from '../modules/foods/entities/category.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Category } from '../modules/foods/entities/category.entity';
import { foodsData } from './data/foods.data';
export class FoodsSeeder {
+1 -2
View File
@@ -1,6 +1,6 @@
import type { EntityManager } from '@mikro-orm/core';
import { PaymentMethod } from '../modules/payments/entities/payment-method.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { paymentMethodsData } from './data/payment-methods.data';
export class PaymentMethodsSeeder {
@@ -53,4 +53,3 @@ export class PaymentMethodsSeeder {
await em.flush();
}
}
-1
View File
@@ -26,4 +26,3 @@ export class PermissionsSeeder {
return createdPermissions;
}
}
-1
View File
@@ -19,4 +19,3 @@ export class RestaurantsSeeder {
return restaurantsMap;
}
}
+3 -6
View File
@@ -1,7 +1,7 @@
import type { EntityManager } from '@mikro-orm/core';
import { Role } from '../modules/roles/entities/role.entity';
import { Permission } from '../common/enums/permission.enum';
import { Permission as PermissionEntity } from '../modules/roles/entities/permission.entity';
import type { Permission } from '../common/enums/permission.enum';
import type { Permission as PermissionEntity } from '../modules/roles/entities/permission.entity';
import { rolesData } from './data/roles.data';
export class RolesSeeder {
@@ -25,9 +25,7 @@ export class RolesSeeder {
generalPermissions.forEach(p => newRole.permissions.add(p));
} else {
// For other roles, use the permission filter
const filteredPermissions = permissions.filter(p =>
roleConfig.permissionFilter(p.name as Permission),
);
const filteredPermissions = permissions.filter(p => roleConfig.permissionFilter(p.name as Permission));
filteredPermissions.forEach(p => newRole.permissions.add(p));
}
@@ -41,4 +39,3 @@ export class RolesSeeder {
return rolesMap;
}
}
+1 -2
View File
@@ -1,6 +1,6 @@
import type { EntityManager } from '@mikro-orm/core';
import { Schedule } from '../modules/restaurants/entities/schedule.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { timeSlots } from './data/schedules.data';
export class SchedulesSeeder {
@@ -36,4 +36,3 @@ export class SchedulesSeeder {
await em.flush();
}
}
+4 -3
View File
@@ -1,7 +1,8 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import type { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { App } from 'supertest/types';
import type { App } from 'supertest/types';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {