lint
This commit is contained in:
@@ -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 {
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -7,4 +7,3 @@ export class ApplyCouponDto {
|
||||
@IsString()
|
||||
code!: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,3 @@ export class SetAddressDto {
|
||||
@IsString()
|
||||
addressId!: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,3 @@ export class SetDeliveryMethodDto {
|
||||
@IsString()
|
||||
deliveryMethodId!: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@ export class UpdateItemQuantityDto {
|
||||
@Min(1)
|
||||
quantity!: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -28,4 +28,3 @@ export function normalizePhone(phone: string): string {
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,4 +29,3 @@ export const adminsData: AdminData[] = [
|
||||
restaurantSlug: null,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -13,4 +13,3 @@ export const categoriesData: CategoryData[] = [
|
||||
{ title: 'دسر', restaurantSlug: 'boote' },
|
||||
{ title: 'نوشیدنی', restaurantSlug: 'boote' },
|
||||
];
|
||||
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -13,4 +13,3 @@ export const getPermissionsData = () => {
|
||||
group: specialPermissions.includes(name) ? 'special' : 'general',
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@ export const timeSlots: TimeSlot[] = [
|
||||
{ openTime: '12:00', closeTime: '15:00' }, // Lunch
|
||||
{ openTime: '18:00', closeTime: '23:00' }, // Dinner
|
||||
];
|
||||
|
||||
|
||||
@@ -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,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';
|
||||
|
||||
|
||||
@@ -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,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,4 +26,3 @@ export class PermissionsSeeder {
|
||||
return createdPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,4 +19,3 @@ export class RestaurantsSeeder {
|
||||
return restaurantsMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)', () => {
|
||||
|
||||
Reference in New Issue
Block a user