diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 7314cca..c9080f5 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -58,6 +58,10 @@ export const enum AuthMessage { NATIONAL_CODE_INVALID = 'کد ملی نامعتبر است', INVALID_REFRESH_TOKEN = 'توکن رفرش نامعتبر است', REFRESH_TOKEN_EXPIRED = 'توکن رفرش منقضی شده است', + REFRESH_TOKEN_MUST_BE_STRING = 'توکن رفرش باید یک رشته باشد', + REFRESH_TOKEN_REQUIRED = 'توکن رفرش الزامی است', + FAILED_TO_REFRESH_TOKEN = 'تمدید توکن با خطا مواجه شد', + OTP_EXPIRED_OR_NOT_FOUND = 'کد منقضی شده یا یافت نشد', LOGOUT_SUCCESS = 'خروج با موفقیت انجام شد', CURRENT_PASSWORD_INVALID = 'رمز عبور فعلی نامعتبر است', TOKEN_EXPIRED_OR_INVALID = 'توکن منقضی شده یا نامعتبر است', @@ -73,6 +77,11 @@ export const enum AuthMessage { } export const enum ProductMessage { NOT_FOUND = 'محصولی با این مشخصات یافت نشد', + ATTRIBUTE_REQUIRED_FOR_VARIANTS = 'برای محصولات با چند نوع، ویژگی الزامی است', + VARIANT_NOT_FOUND = 'نوع محصول یافت نشد', + PRODUCT_NOT_BELONGS_TO_SHOP = 'محصول به فروشگاه شما تعلق ندارد', + FAVORITE_REMOVED = 'محصول از علاقه‌مندی‌ها حذف شد', + FAVORITE_ADDED = 'محصول به علاقه‌مندی‌ها اضافه شد', } export const enum CategoryMessage { NOT_FOUND = 'دسته‌بندی مورد نظر یافت نشد', @@ -129,6 +138,11 @@ export const enum UserMessage { PUSH_TOKEN_ADDED_SUCCESSFULLY = 'توکن پوش با موفقیت اضافه شد', NO_PUSH_TOKENS_FOUND = 'توکن پوشی برای این کاربر یافت نشد', PUSH_TOKEN_REMOVED_SUCCESSFULLY = 'توکن پوش با موفقیت حذف شد', + ADDRESS_NOT_FOUND = 'آدرس یافت نشد', + POINTS_MUST_BE_GREATER_THAN_ZERO = 'امتیاز تبدیل باید بیشتر از صفر باشد', + SHOP_SCORE_NOT_FOUND = 'امتیاز فروشگاه یافت نشد', + SHOP_PURCHASE_SCORE_NOT_FOUND = 'امتیاز خرید فروشگاه یافت نشد', + SHOP_PURCHASE_AMOUNT_NOT_FOUND = 'مبلغ خرید فروشگاه یافت نشد', } export const enum CommonMessage { @@ -291,7 +305,7 @@ export const enum EmailMessage { MESSAGE_MARKED_AS_UNSEEN_SUCCESSFULLY = 'پیام با موفقیت به عنوان خوانده نشده علامت گذاری شد', MESSAGE_MARK_AS_UNSEEN_FAILED = 'علامت گذاری پیام به عنوان خوانده نشده با خطا مواجه شد', ATTACHMENT_ID_MUST_BE_STRING = 'شناسه پیوست باید یک رشته باشد', - IS_FORWARD_MUST_BE_BOOLEAN = 'IS_FORWARD_MUST_BE_BOOLEAN', + IS_FORWARD_MUST_BE_BOOLEAN = 'وضعیت فوروارد باید بولین باشد', // Favorite/Star messages (Gmail-like behavior) MESSAGE_MARKED_AS_FAVORITE_SUCCESSFULLY = 'پیام با موفقیت به عنوان مورد علاقه علامت گذاری شد', @@ -300,7 +314,7 @@ export const enum EmailMessage { SPAM_EMPTYED_SUCCESSFULLY = 'همه پیام های اسپم با موفقیت حذف شدند', SAVE_DRAFT_SUCCESS = 'پیش نویس با موفقیت ذخیره شد', HTML_CONTENT_REQUIRED = 'محتوای HTML مورد نیاز است', - TEXT_CONTENT_REQUIRED = 'TEXT_CONTENT_REQUIRED', + TEXT_CONTENT_REQUIRED = 'محتوای متنی الزامی است', } export const enum WebSocketMessage { @@ -318,6 +332,8 @@ export const enum WebSocketMessage { LEFT_ROOM_SUCCESS = 'با موفقیت از اتاق خارج شدید', ROOM_NOT_FOUND = 'اتاق یافت نشد', UNAUTHORIZED_ROOM_ACCESS = 'دسترسی غیرمجاز به اتاق', + ADMIN_LEFT_ROOM = 'ادمین با موفقیت از اتاق خارج شد', + ADMIN_JOINED_ROOM = 'ادمین با موفقیت به اتاق متصل شد', // Session Management Messages WS_SESSION_CREATED = 'جلسه وب سوکت ایجاد شد', @@ -347,6 +363,7 @@ export const enum WebSocketMessage { // Authentication errors AUTHENTICATION_REQUIRED = 'احراز هویت ضروری است', + INVALID_TOKEN_PAYLOAD = 'محتویات توکن نامعتبر است', INVALID_TOKEN = 'توکن احراز هویت نامعتبر است', TOKEN_EXPIRED = 'توکن منقضی شده است', UNAUTHORIZED_ACCESS = 'دسترسی غیرمجاز', @@ -398,8 +415,19 @@ export const enum SmsMessage { SEND_SMS_ERROR = 'خطا در ارسال پیامک', } +export const enum AdminMessage { + ROLE_NOT_FOUND = 'نقش یافت نشد', + ROLE_NOT_BELONGS_TO_SHOP = 'نقش به این فروشگاه تعلق ندارد', + SHOP_NOT_FOUND = 'فروشگاه یافت نشد', + ADMIN_NOT_FOUND = 'ادمین یافت نشد', + PHONE_ALREADY_TAKEN = 'این شماره قبلا توسط ادمین دیگری استفاده شده است', + ADMIN_ROLE_NOT_FOUND = 'نقش ادمین یافت نشد', +} + export const enum RestMessage { NOT_FOUND = 'کسب و کار یافت نشد', + DUPLICATE_SUBSCRIPTION_ID = 'شناسه اشتراک تکراری است', + SYSTEM_ROLE_NOT_FOUND = 'نقش سیستمی یافت نشد', BUSINESS_ID_REQUIRED = 'شناسه کسب و کار مورد نیاز است', SLUG_REQUIRED = 'اسلاگ مورد نیاز است', SLUG_MUST_BE_STRING = 'اسلاگ باید یک رشته باشد', @@ -522,6 +550,9 @@ export const enum DnsRecordMessage { export const enum RoleMessage { NOT_FOUND = 'نقش یافت نشد', + ROLE_ALREADY_EXISTS_FOR_SHOP = 'نقشی با این نام قبلا برای این فروشگاه ثبت شده است', + ONE_OR_MORE_PERMISSIONS_NOT_FOUND = 'یک یا چند دسترسی یافت نشد', + ROLE_HAS_ADMINS = 'این نقش دارای ادمین است و نمی‌توان حذف کرد', } export const enum SignatureMessage { @@ -584,6 +615,7 @@ export const enum NotificationMessage { CHANGE_PASSWORD_MESSAGE = 'رمز عبور شما با موفقیت تغییر کرد', CHANGE_PASSWORD = 'تغییر رمز عبور', NOT_FOUND = 'اعلان یافت نشد', + NOTIFICATION_PREFERENCE_NOT_FOUND = 'تنظیمات اعلان یافت نشد', READ_SUCCESS = 'اعلان با موفقیت خوانده شد', ORDER_CREATED = 'سفارش جدید ایجاد شد', ORDER_STATUS_CHANGED = 'وضعیت سفارش تغییر کرد', @@ -659,6 +691,9 @@ export const enum ReviewMessage { COMMENT_CREATED = 'نظر با موفقیت ثبت شد', COMMENT_UPDATED = 'نظر با موفقیت به‌روزرسانی شد', COMMENT_DELETED = 'نظر با موفقیت حذف شد', + PRODUCT_NOT_IN_ORDER = 'محصول در سفارش مشخص شده وجود ندارد', + CREATE_REVIEW_FAILED = 'ثبت نظر با خطا مواجه شد', + ONLY_ADMIN_CAN_CHANGE_STATUS = 'فقط ادمین می‌تواند وضعیت نظر را تغییر دهد', } export const enum IconMessage { @@ -677,6 +712,7 @@ export const enum GroupMessage { export const enum OrderMessage { NOT_FOUND = 'سفارش یافت نشد', + ORDER_NOT_FOUND_OR_NOT_OWNED = 'سفارش یافت نشد یا به کاربر فعلی تعلق ندارد', PAYMENT_METHOD_MISSING = 'روش پرداخت سفارش مشخص نشده است', INVALID_STATUS_TRANSITION = 'انتقال وضعیت نامعتبر است', CART_EMPTY = 'سبد خرید خالی است. لطفا قبل از ایجاد سفارش، آیتم‌هایی به سبد اضافه کنید', @@ -696,6 +732,7 @@ export const enum OrderMessage { } export const enum CartMessage { + AT_LEAST_ONE_ITEM_REQUIRED = 'حداقل یک آیتم الزامی است', TABLE_NUMBER_REQUIRED = 'شماره میز برای سفارش در محل الزامی است', CAR_ADDRESS_REQUIRED = 'آدرس خودرو برای تحویل به خودرو الزامی است', ADDRESS_REQUIRED = 'آدرس برای تحویل پیک الزامی است', @@ -744,6 +781,7 @@ export const enum PaymentMessage { PAYMENT_METHOD_NOT_CASH = 'روش پرداخت نقدی نیست', PAYMENT_ALREADY_PAID = 'پرداخت قبلا انجام شده است', EXISTING_PENDING_PAYMENT_MISMATCH = 'پرداخت در انتظار موجود با روش پرداخت سفارش مطابقت ندارد', + INVALID_TRANSACTION_TYPE = 'نوع تراکنش نامعتبر است', ZARINPAL_INVALID_RESPONSE = 'پاسخ نامعتبر از API زرین‌پال', ZARINPAL_PAYMENT_REQUEST_ERROR = 'خطا در درخواست پرداخت زرین‌پال', ZARINPAL_VERIFY_ERROR = 'خطا در تایید پرداخت زرین‌پال', diff --git a/src/core/exceptions/http.exceptions.ts b/src/core/exceptions/http.exceptions.ts index 010de9d..ce271fc 100755 --- a/src/core/exceptions/http.exceptions.ts +++ b/src/core/exceptions/http.exceptions.ts @@ -43,6 +43,6 @@ export class HttpExceptionFilter implements ExceptionFilter { return Array.isArray(response.message) ? response.message : [response.message]; } - return 'something wrong'; + return 'مشکلی پیش آمده است'; } } diff --git a/src/modules/admin/providers/admin.service.ts b/src/modules/admin/providers/admin.service.ts index a41dab9..1db6b17 100644 --- a/src/modules/admin/providers/admin.service.ts +++ b/src/modules/admin/providers/admin.service.ts @@ -11,6 +11,7 @@ import { UpdateAdminDto } from '../dto/update-admin.dto'; import { AdminRole } from '../entities/adminRole.entity'; import { normalizePhone } from '../../utils/phone.util'; import { AdminRepository } from '../repositories/admin.repository'; +import { AdminMessage, RoleMessage } from 'src/common/enums/message.enum'; @Injectable() export class AdminService { @@ -55,15 +56,15 @@ export class AdminService { await this.em.persistAndFlush(admin); } const role = await this.em.findOne(Role, { id: roleId, isSystem: false }); - if (!role) throw new NotFoundException('Role not found'); + if (!role) throw new NotFoundException(AdminMessage.ROLE_NOT_FOUND); // Validate that role belongs to the shop or is general (shop: null) if (role.shop && role.shop.id !== shopId) { - throw new BadRequestException('Role does not belong to this shop'); + throw new BadRequestException(AdminMessage.ROLE_NOT_BELONGS_TO_SHOP); } const shop = await this.em.findOne(Shop, { id: shopId }); - if (!shop) throw new NotFoundException('Shop not found'); + if (!shop) throw new NotFoundException(AdminMessage.SHOP_NOT_FOUND); let adminRole = await this.adminRoleRepository.findOne({ admin: admin, @@ -105,7 +106,7 @@ export class AdminService { const shop = await this.em.findOne(Shop, { id: shopId }); - if (!shop) throw new NotFoundException('Shop not found'); + if (!shop) throw new NotFoundException(AdminMessage.SHOP_NOT_FOUND); let adminRole = await this.adminRoleRepository.findOne({ admin: admin, @@ -128,7 +129,7 @@ export class AdminService { async getShopAdminsBySuperAdmin(shopId: string): Promise { const shop = await this.em.findOne(Shop, { id: shopId }); - if (!shop) throw new NotFoundException('Shop not found'); + if (!shop) throw new NotFoundException(AdminMessage.SHOP_NOT_FOUND); return this.adminRepository.find({ roles: { shop: shop } }, { populate: ['roles', 'roles.role'] }); } @@ -140,7 +141,7 @@ export class AdminService { ); if (!admin) { - throw new NotFoundException('Admin not found'); + throw new NotFoundException(AdminMessage.ADMIN_NOT_FOUND); } const { roleId, ...rest } = dto; @@ -159,7 +160,7 @@ export class AdminService { id: { $ne: adminId }, // Exclude current admin from duplicate check }); if (exists) { - throw new ConflictException('This Phone Number is already taken'); + throw new ConflictException(AdminMessage.PHONE_ALREADY_TAKEN); } admin.phone = normalizedPhone; } @@ -168,12 +169,12 @@ export class AdminService { if (roleId) { const role = await this.em.findOne(Role, { id: roleId, isSystem: false }, { populate: ['shop'] }); if (!role) { - throw new NotFoundException('Role not found'); + throw new NotFoundException(AdminMessage.ROLE_NOT_FOUND); } // Validate that role belongs to the shop or is general (shop: null) if (role.shop && role.shop.id !== shopId) { - throw new BadRequestException('Role does not belong to this shop'); + throw new BadRequestException(AdminMessage.ROLE_NOT_BELONGS_TO_SHOP); } // Find existing AdminRole for this admin and shop @@ -187,7 +188,7 @@ export class AdminService { existingAdminRole.role = role; } else { const shop = await this.em.findOne(Shop, { id: shopId }); - if (!shop) throw new NotFoundException('Shop not found'); + if (!shop) throw new NotFoundException(AdminMessage.SHOP_NOT_FOUND); // Create new AdminRole using repository for consistency const newAdminRole = this.adminRoleRepository.create({ admin, @@ -205,7 +206,7 @@ export class AdminService { async remove(adminId: string, shopId: string): Promise { const adminRole = await this.adminRoleRepository.findOne({ admin: { id: adminId }, shop: { id: shopId } }); if (!adminRole) { - throw new NotFoundException('Admin role not found'); + throw new NotFoundException(AdminMessage.ADMIN_ROLE_NOT_FOUND); } return this.em.removeAndFlush(adminRole); } @@ -226,7 +227,7 @@ export class AdminService { ); if (!admin) { - throw new BadRequestException('Admin not found'); + throw new BadRequestException(AdminMessage.ADMIN_NOT_FOUND); } // Ensure all roles are populated diff --git a/src/modules/auth/dto/refresh-token.dto.ts b/src/modules/auth/dto/refresh-token.dto.ts index 180bbaa..e277620 100755 --- a/src/modules/auth/dto/refresh-token.dto.ts +++ b/src/modules/auth/dto/refresh-token.dto.ts @@ -1,9 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString } from 'class-validator'; +import { AuthMessage } from 'src/common/enums/message.enum'; export class RefreshTokenDto { @ApiProperty({ description: 'Refresh token' }) - @IsString({ message: 'Refresh token must be a string' }) - @IsNotEmpty({ message: 'Refresh token is required' }) + @IsString({ message: AuthMessage.REFRESH_TOKEN_MUST_BE_STRING }) + @IsNotEmpty({ message: AuthMessage.REFRESH_TOKEN_REQUIRED }) refreshToken: string; } diff --git a/src/modules/auth/services/auth.service.ts b/src/modules/auth/services/auth.service.ts index 8b6deac..614b42d 100644 --- a/src/modules/auth/services/auth.service.ts +++ b/src/modules/auth/services/auth.service.ts @@ -55,8 +55,8 @@ export class AuthService { const normalizedPhone = normalizePhone(phone); const key = this.userOtpKey(slug, normalizedPhone); const cachedCode = await this.cacheService.get(key); - if (!cachedCode) throw new BadRequestException('OTP expired or not found'); - if (cachedCode !== code) throw new BadRequestException('Invalid OTP'); + if (!cachedCode) throw new BadRequestException(AuthMessage.OTP_EXPIRED_OR_NOT_FOUND); + if (cachedCode !== code) throw new BadRequestException(AuthMessage.INVALID_OTP); await this.cacheService.del(key); @@ -80,9 +80,9 @@ export class AuthService { const key = this.adminOtpKey(slug, normalizedPhone); const cachedCode = await this.cacheService.get(key); - if (!cachedCode) throw new BadRequestException('OTP expired or not found'); + if (!cachedCode) throw new BadRequestException(AuthMessage.OTP_EXPIRED_OR_NOT_FOUND); - if (cachedCode !== code) throw new BadRequestException('Invalid OTP'); + if (cachedCode !== code) throw new BadRequestException(AuthMessage.INVALID_OTP); await this.cacheService.del(key); const admin = await this.adminService.findByPhoneAndShopSlug(normalizedPhone, slug); diff --git a/src/modules/auth/services/tokens.service.ts b/src/modules/auth/services/tokens.service.ts index 59adbcf..61f3640 100755 --- a/src/modules/auth/services/tokens.service.ts +++ b/src/modules/auth/services/tokens.service.ts @@ -139,7 +139,7 @@ export class TokensService { } catch (error) { this.logger.error('Failed to generate new tokens after refresh', error); // Transaction will rollback automatically, preserving the old token - throw new UnauthorizedException('Failed to refresh token'); + throw new UnauthorizedException(AuthMessage.FAILED_TO_REFRESH_TOKEN); } }); } diff --git a/src/modules/cart/dto/bulk-add-items.dto.ts b/src/modules/cart/dto/bulk-add-items.dto.ts index cfdab72..4860c4f 100644 --- a/src/modules/cart/dto/bulk-add-items.dto.ts +++ b/src/modules/cart/dto/bulk-add-items.dto.ts @@ -1,6 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsArray, ValidateNested, ArrayMinSize, IsInt, Min, IsString } from 'class-validator'; import { Type } from 'class-transformer'; +import { CartMessage } from 'src/common/enums/message.enum'; class AddItemToCartDto { @ApiProperty({ description: 'Quantity of the product item', example: 1, minimum: 1 }) @@ -27,7 +28,7 @@ export class BulkAddItemsToCartDto { }) @IsNotEmpty() @IsArray() - @ArrayMinSize(1, { message: 'At least one item is required' }) + @ArrayMinSize(1, { message: CartMessage.AT_LEAST_ONE_ITEM_REQUIRED }) @ValidateNested({ each: true }) @Type(() => AddItemToCartDto) items!: AddItemToCartDto[]; diff --git a/src/modules/notifications/guards/ws-admin-auth.guard.ts b/src/modules/notifications/guards/ws-admin-auth.guard.ts index a5fb183..664d4ba 100644 --- a/src/modules/notifications/guards/ws-admin-auth.guard.ts +++ b/src/modules/notifications/guards/ws-admin-auth.guard.ts @@ -4,6 +4,7 @@ import { Socket } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; import { ConfigService } from '@nestjs/config'; import { IAdminTokenPayload } from '../../auth/interfaces/IToken-payload'; +import { WebSocketMessage } from 'src/common/enums/message.enum'; export interface AuthenticatedSocket extends Socket { adminId?: string; @@ -33,7 +34,7 @@ export class WsAdminAuthGuard implements CanActivate { auth: client.handshake.auth, query: client.handshake.query, }); - throw new WsException('Authentication required'); + throw new WsException(WebSocketMessage.AUTHENTICATION_REQUIRED); } const secret = this.configService.getOrThrow('JWT_SECRET'); @@ -43,7 +44,7 @@ export class WsAdminAuthGuard implements CanActivate { if (!payload.adminId || !payload.shopId) { this.logger.error('Invalid token payload structure', payload); - throw new WsException('Invalid token payload'); + throw new WsException(WebSocketMessage.INVALID_TOKEN_PAYLOAD); } // Attach admin info to socket @@ -61,7 +62,7 @@ export class WsAdminAuthGuard implements CanActivate { error: error instanceof Error ? error.message : 'Unknown error', socketId: client.id, }); - throw new WsException('Authentication failed'); + throw new WsException(WebSocketMessage.WS_AUTHENTICATION_FAILED); } } diff --git a/src/modules/notifications/notifications.gateway.ts b/src/modules/notifications/notifications.gateway.ts index 7443177..a78a71b 100644 --- a/src/modules/notifications/notifications.gateway.ts +++ b/src/modules/notifications/notifications.gateway.ts @@ -14,6 +14,7 @@ import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-au import { ModuleRef } from '@nestjs/core'; import { ExecutionContext } from '@nestjs/common'; import { IInAppNotificationPayload } from './interfaces/notification.interface'; +import { WebSocketMessage } from 'src/common/enums/message.enum'; @WebSocketGateway({ cors: { @@ -53,7 +54,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco this.logger.error( `Connection authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`, ); - void client.emit('error', { message: 'Authentication failed' }); + void client.emit('error', { message: WebSocketMessage.WS_AUTHENTICATION_FAILED }); client.disconnect(); } } @@ -89,14 +90,14 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco const room = this.getRoom(client.adminId!, client.shopId!); void client.leave(room); this.logger.log(`Admin ${client.adminId} (Client ${client.id}) left room: ${room}`); - void client.emit('left', { room, message: 'Successfully Admin left room' }); + void client.emit('left', { room, message: WebSocketMessage.ADMIN_LEFT_ROOM }); } handleJoinRoom(client: AuthenticatedSocket) { const room = this.getRoom(client.adminId!, client.shopId!); void client.join(room); this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`); - void client.emit('joined', { room, message: 'Successfully Admin joined room' }); + void client.emit('joined', { room, message: WebSocketMessage.ADMIN_JOINED_ROOM }); } private async authenticateConnection(client: Socket): Promise { diff --git a/src/modules/notifications/services/notification-preference.service.ts b/src/modules/notifications/services/notification-preference.service.ts index d359ae4..41fcba3 100644 --- a/src/modules/notifications/services/notification-preference.service.ts +++ b/src/modules/notifications/services/notification-preference.service.ts @@ -5,6 +5,7 @@ import { Shop } from '../../shops/entities/shop.entity'; import { CreatePreferenceDto } from '../dto/create-preference.dto'; import { UpdatePreferenceDto } from '../dto/update-preference.dto'; import { NotifTitleEnum } from '../interfaces/notification.interface'; +import { NotificationMessage, RestMessage } from 'src/common/enums/message.enum'; @Injectable() export class NotificationPreferenceService { @@ -13,7 +14,7 @@ export class NotificationPreferenceService { async create(shopId: string, dto: CreatePreferenceDto): Promise { const shop = await this.em.findOne(Shop, { id: shopId }); if (!shop) { - throw new NotFoundException('Shop not found'); + throw new NotFoundException(RestMessage.NOT_FOUND); } const preference = this.em.create(NotificationPreference, { @@ -46,7 +47,7 @@ export class NotificationPreferenceService { // ): Promise { // const preference = await this.findByRestaurantAndType(shopId, notificationType); // if (!preference) { - // throw new NotFoundException('Notification preference not found'); + // throw new NotFoundException(NotificationMessage.NOTIFICATION_PREFERENCE_NOT_FOUND); // } // preference.enabled = enabled; @@ -64,7 +65,7 @@ export class NotificationPreferenceService { shop: { id: shopId }, }); if (!preference) { - throw new NotFoundException('Notification preference not found'); + throw new NotFoundException(NotificationMessage.NOTIFICATION_PREFERENCE_NOT_FOUND); } this.em.assign(preference, dto); @@ -78,7 +79,7 @@ export class NotificationPreferenceService { id: preferenceId, }); if (!preference) { - throw new NotFoundException('Notification preference not found'); + throw new NotFoundException(NotificationMessage.NOTIFICATION_PREFERENCE_NOT_FOUND); } await this.em.removeAndFlush(preference); } diff --git a/src/modules/notifications/services/notification.service.ts b/src/modules/notifications/services/notification.service.ts index c955d8e..ed4dff9 100644 --- a/src/modules/notifications/services/notification.service.ts +++ b/src/modules/notifications/services/notification.service.ts @@ -7,6 +7,7 @@ import { NotificationsGateway } from '../notifications.gateway'; import { NotifChannelEnum, NotifRequest, NotifTitleEnum } from '../interfaces/notification.interface'; import { SmsLogRepository } from '../repositories/sms-log.repository'; import { PaginatedResult } from '../../../common/interfaces/pagination.interface'; +import { NotificationMessage } from 'src/common/enums/message.enum'; @Injectable() export class NotificationService { @@ -96,7 +97,7 @@ export class NotificationService { async findOne(id: string): Promise { const notification = await this.em.findOne(Notification, { id }, { populate: ['shop', 'user'] }); if (!notification) { - throw new NotFoundException('Notification not found'); + throw new NotFoundException(NotificationMessage.NOT_FOUND); } return notification; } @@ -188,7 +189,7 @@ export class NotificationService { shop: { id: shopId }, }); if (!notification) { - throw new NotFoundException('Notification not found'); + throw new NotFoundException(NotificationMessage.NOT_FOUND); } notification.seenAt = new Date(); await this.em.persistAndFlush(notification); @@ -200,7 +201,7 @@ export class NotificationService { shop: { id: shopId }, }); if (!notification) { - throw new NotFoundException('Notification not found'); + throw new NotFoundException(NotificationMessage.NOT_FOUND); } notification.seenAt = new Date(); await this.em.persistAndFlush(notification); diff --git a/src/modules/payments/services/payments.service.ts b/src/modules/payments/services/payments.service.ts index f8fa639..667f42b 100644 --- a/src/modules/payments/services/payments.service.ts +++ b/src/modules/payments/services/payments.service.ts @@ -113,11 +113,11 @@ export class PaymentsService { }); if (!walletTransaction) { - throw new NotFoundException('User wallet not found'); + throw new NotFoundException(PaymentMessage.WALLET_NOT_FOUND); } if (walletTransaction.balance < ctx.amount) { - throw new BadRequestException('Insufficient wallet balance'); + throw new BadRequestException(PaymentMessage.INSUFFICIENT_WALLET_BALANCE); } const newBalance = walletTransaction.balance - ctx.amount; diff --git a/src/modules/products/providers/product.service.ts b/src/modules/products/providers/product.service.ts index 3b1acf1..5017d39 100644 --- a/src/modules/products/providers/product.service.ts +++ b/src/modules/products/providers/product.service.ts @@ -6,7 +6,7 @@ import { CategoryRepository } from '../repositories/category.repository'; import { EntityManager } from '@mikro-orm/postgresql'; import { RequiredEntityData } from '@mikro-orm/core'; import { Product } from '../entities/product.entity'; -import { CategoryMessage, ProductMessage } from 'src/common/enums/message.enum'; +import { CategoryMessage, ProductMessage, UserMessage } from 'src/common/enums/message.enum'; import { Favorite } from '../entities/favorite.entity'; import { Variant } from '../entities/variant.entity'; import { ShopService } from 'src/modules/shops/providers/shops.service'; @@ -24,7 +24,6 @@ export class ProductService { private readonly favoriteRepository: FavoriteRepository, private readonly em: EntityManager, ) { } - // TODO : Messages must be in persian async create(shopId: string, dto: CreateProductDto) { const { categoryId, variants, ...rest } = dto; const shop = await this.shopService.findOrFail(shopId); @@ -32,7 +31,7 @@ export class ProductService { const category = await this.categoryService.findOrFail(categoryId) if (variants.length > 1 && !dto.attribute) { - throw new BadRequestException('Attribute is required for multiple variants'); + throw new BadRequestException(ProductMessage.ATTRIBUTE_REQUIRED_FOR_VARIANTS); } const product = await this.em.transactional(async em => { @@ -144,7 +143,7 @@ export class ProductService { // Update existing variant const existingVariant = existingVariantsMap.get(id); if (!existingVariant) { - throw new NotFoundException('Variant not found'); + throw new NotFoundException(ProductMessage.VARIANT_NOT_FOUND); } // Update the variant fields @@ -183,7 +182,7 @@ export class ProductService { const product = await this.findOrFail(id) if (product.shop.id !== shopId) { - throw new BadRequestException("Product doesnt belongs to your shop") + throw new BadRequestException(ProductMessage.PRODUCT_NOT_BELONGS_TO_SHOP); } product.deletedAt = new Date(); @@ -198,7 +197,7 @@ export class ProductService { const product = await this.em.findOne(Product, productId); if (!user) { - throw new NotFoundException('User not found'); + throw new NotFoundException(UserMessage.USER_NOT_FOUND); } if (!product) { throw new NotFoundException(ProductMessage.NOT_FOUND); @@ -211,12 +210,12 @@ export class ProductService { if (favorite) { await this.em.removeAndFlush(favorite); - return { message: 'Favorite removed successfully' }; + return { message: ProductMessage.FAVORITE_REMOVED }; } const newFavorite = this.em.create(Favorite, { user, product }); await this.em.persistAndFlush(newFavorite); - return { message: 'Favorite added successfully' }; + return { message: ProductMessage.FAVORITE_ADDED }; } @@ -245,7 +244,7 @@ export class ProductService { populate: ['product', 'product.shop', 'product.variants'], }); - if (!variant) throw new NotFoundException('Variant not found'); + if (!variant) throw new NotFoundException(ProductMessage.VARIANT_NOT_FOUND); return variant; } diff --git a/src/modules/review/providers/review.service.ts b/src/modules/review/providers/review.service.ts index 4519f5f..336a086 100644 --- a/src/modules/review/providers/review.service.ts +++ b/src/modules/review/providers/review.service.ts @@ -8,7 +8,7 @@ import { UserRepository } from '../../users/repositories/user.repository'; import { EntityManager } from '@mikro-orm/postgresql'; import { RequiredEntityData } from '@mikro-orm/core'; import { Review } from '../entities/review.entity'; -import { ProductMessage, UserMessage, ReviewMessage } from 'src/common/enums/message.enum'; +import { OrderMessage, ProductMessage, RestMessage, UserMessage, ReviewMessage } from 'src/common/enums/message.enum'; import { Order } from '../../orders/entities/order.entity'; import { OrderItem } from '../../orders/entities/order-item.entity'; import { Shop } from '../../shops/entities/shop.entity'; @@ -43,7 +43,7 @@ export class ReviewService { // Find order and verify it belongs to the user, populate shop to get shopId const order = await this.em.findOne(Order, { id: orderId, user: { id: userId } }, { populate: ['shop'] }); if (!order) { - throw new NotFoundException('Order not found or does not belong to the current user'); + throw new NotFoundException(OrderMessage.ORDER_NOT_FOUND_OR_NOT_OWNED); } // Verify the product is in the order (check through variant.product) @@ -54,7 +54,7 @@ export class ReviewService { populate: ['variant', 'variant.product'], }); if (!orderItem) { - throw new BadRequestException('Product is not in the specified order'); + throw new BadRequestException(ReviewMessage.PRODUCT_NOT_IN_ORDER); } // Check if user already commented on this product for this order @@ -81,7 +81,7 @@ export class ReviewService { const review = this.reviewRepository.create(data); if (!review) { - throw new BadRequestException('Failed to create review entity'); + throw new BadRequestException(ReviewMessage.CREATE_REVIEW_FAILED); } await this.em.persistAndFlush(review); @@ -115,7 +115,7 @@ export class ReviewService { const { shopSlug: restuarantSlug, ...restDto } = dto; const shop = await this.em.findOne(Shop, { slug: restuarantSlug }); if (!shop) { - throw new NotFoundException('RestaurantMessage.NOT_FOUND'); + throw new NotFoundException(RestMessage.NOT_FOUND); } return this.reviewRepository.findAllPaginated({ ...restDto, shopId: shop.id }); } @@ -144,7 +144,7 @@ export class ReviewService { // Users can only update rating and comment, not status if (!isAdmin && dto.status !== undefined) { - throw new BadRequestException('Only admins can change review status'); + throw new BadRequestException(ReviewMessage.ONLY_ADMIN_CAN_CHANGE_STATUS); } const oldRating = review.rating; diff --git a/src/modules/roles/providers/roles.service.ts b/src/modules/roles/providers/roles.service.ts index 78bbb29..7f34716 100644 --- a/src/modules/roles/providers/roles.service.ts +++ b/src/modules/roles/providers/roles.service.ts @@ -8,6 +8,7 @@ import { EntityManager } from '@mikro-orm/postgresql'; import { CreateRoleDto } from '../dto/create-role.dto'; import { UpdateRoleDto } from '../dto/update-role.dto'; import { RolePermission } from '../entities/rolePermission.entity'; +import { RestMessage, RoleMessage } from 'src/common/enums/message.enum'; @Injectable() export class RolesService { @@ -25,14 +26,14 @@ export class RolesService { // Check if role already exists const existing = await this.roleRepository.findOne({ name, shop: shopId ? { id: shopId } : null }); if (existing) { - throw new BadRequestException('Role with this name already exists for the shop'); + throw new BadRequestException(RoleMessage.ROLE_ALREADY_EXISTS_FOR_SHOP); } let shop: Shop | null = null; if (shopId) { shop = await this.em.findOne(Shop, { id: shopId }); if (!shop) { - throw new NotFoundException('Shop not found'); + throw new NotFoundException(RestMessage.NOT_FOUND); } } @@ -46,7 +47,7 @@ export class RolesService { if (permissionIds && permissionIds.length > 0) { const permissions = await this.permissionRepository.find({ id: { $in: permissionIds } }); if (permissions.length !== permissionIds.length) { - throw new BadRequestException('One or more permissions not found'); + throw new BadRequestException(RoleMessage.ONE_OR_MORE_PERMISSIONS_NOT_FOUND); } permissions.forEach(p => role.permissions.add(p)); } @@ -72,7 +73,7 @@ export class RolesService { { populate: ['permissions', 'shop'] }, ); if (!role) { - throw new NotFoundException('Role not found'); + throw new NotFoundException(RoleMessage.NOT_FOUND); } return role; } @@ -83,7 +84,7 @@ export class RolesService { { populate: ['permissions', 'shop'] }, ); if (!role) { - throw new NotFoundException('Role not found'); + throw new NotFoundException(RoleMessage.NOT_FOUND); } if (dto.name) { @@ -95,7 +96,7 @@ export class RolesService { role.permissions.removeAll(); const permissions = await this.permissionRepository.find({ id: { $in: dto.permissionIds } }); if (permissions.length !== dto.permissionIds.length) { - throw new BadRequestException('One or more permissions not found'); + throw new BadRequestException(RoleMessage.ONE_OR_MORE_PERMISSIONS_NOT_FOUND); } permissions.forEach(p => role.permissions.add(p)); } @@ -122,10 +123,10 @@ export class RolesService { { populate: ['permissions', 'shop'] }, ); if (!role) { - throw new NotFoundException('Role not found'); + throw new NotFoundException(RoleMessage.NOT_FOUND); } if (!role.admins.isEmpty()) { - throw new BadRequestException('Role has admins'); + throw new BadRequestException(RoleMessage.ROLE_HAS_ADMINS); } // Hard delete pivot table entries (role_permissions) before soft deleting the role diff --git a/src/modules/shops/providers/shops.service.ts b/src/modules/shops/providers/shops.service.ts index 837dbc4..5971de6 100644 --- a/src/modules/shops/providers/shops.service.ts +++ b/src/modules/shops/providers/shops.service.ts @@ -46,7 +46,7 @@ export class ShopService { const validateSubscriptionId = await em.findOne(Shop, { subscriptionId: dto.subscriptionId }) if (validateSubscriptionId) { - throw new BadRequestException("Duplicate subscriptionId: " + dto.subscriptionId) + throw new BadRequestException(RestMessage.DUPLICATE_SUBSCRIPTION_ID); } // Create shop @@ -64,7 +64,7 @@ export class ShopService { const role = await em.findOne(Role, { isSystem: true }); if (!role) { - throw new BadRequestException(`System role not found`); + throw new BadRequestException(RestMessage.SYSTEM_ROLE_NOT_FOUND); } const normalizedPhone = normalizePhone(dto.phone); @@ -116,7 +116,7 @@ export class ShopService { const shop = await this.em.findOne(Shop, { slug }); if (!shop) { - throw new NotFoundException(`Shop with slug "${slug}" not found`); + throw new NotFoundException(RestMessage.NOT_FOUND); } return shop; @@ -235,7 +235,7 @@ export class ShopService { async findOrFail(shopId: string) { const shop = await this.shopRepository.findOne({ id: shopId }, { populate: ['deliveries'] }) if (!shop) { - throw new BadRequestException("Shop not found") + throw new BadRequestException(RestMessage.NOT_FOUND); } return shop } @@ -243,7 +243,7 @@ export class ShopService { async findOrFailBySlug(shopSlug: string) { const shop = await this.shopRepository.findOne({ slug: shopSlug }, { populate: ['deliveries'] }) if (!shop) { - throw new BadRequestException("Shop not found") + throw new BadRequestException(RestMessage.NOT_FOUND); } return shop } diff --git a/src/modules/users/providers/user.service.ts b/src/modules/users/providers/user.service.ts index c4ce31f..4f19526 100644 --- a/src/modules/users/providers/user.service.ts +++ b/src/modules/users/providers/user.service.ts @@ -18,6 +18,7 @@ import { WalletTransactionRepository } from '../repositories/wallet-transaction. import { WalletService } from './wallet.service'; import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet'; import { WalletTransaction } from '../entities/wallet-transaction.entity'; +import { RestMessage, UserMessage } from 'src/common/enums/message.enum'; @Injectable() export class UserService { @@ -56,7 +57,7 @@ export class UserService { // async updateUser(userId: string, dto: UpdateUserDto): Promise { // const user = await this.em.findOneOrFail(User, userId, {}).catch(() => { - // throw new NotFoundException(`User with ID ${userId} not found.`); + // throw new NotFoundException(UserMessage.USER_NOT_FOUND); // }); // this.em.assign(user, { ...dto }); @@ -86,7 +87,7 @@ export class UserService { // get registerscore from shop const shop = await this.restaurantRepository.findOne({ id: shopId }); if (!shop) { - throw new NotFoundException(`Shop not found.`); + throw new NotFoundException(RestMessage.NOT_FOUND); } const points = Number(shop.score?.registerScore || 0); const createData = _raw as unknown as RequiredEntityData; @@ -107,7 +108,7 @@ export class UserService { async updateUser(userId: string, shopId: string, dto: UpdateUserDto): Promise { const user = await this.userRepository.findOne({ id: userId }); if (!user) { - throw new NotFoundException(`User with ID ${userId} not found.`); + throw new NotFoundException(UserMessage.USER_NOT_FOUND); } // Normalize date strings into Date objects if provided @@ -180,7 +181,7 @@ export class UserService { async getUserAddresses(userId: string): Promise { const user = await this.userRepository.findOne({ id: userId }, { populate: ['addresses'] }); if (!user) { - throw new NotFoundException(`User with ID ${userId} not found.`); + throw new NotFoundException(UserMessage.USER_NOT_FOUND); } // Load addresses if not already loaded @@ -191,7 +192,7 @@ export class UserService { async createUserAddress(userId: string, dto: CreateUserAddressDto): Promise { const user = await this.userRepository.findOne({ id: userId }); if (!user) { - throw new NotFoundException(`User with ID ${userId} not found.`); + throw new NotFoundException(UserMessage.USER_NOT_FOUND); } // If setting as default, unset other default addresses @@ -228,7 +229,7 @@ export class UserService { }); if (!address) { - throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`); + throw new NotFoundException(UserMessage.ADDRESS_NOT_FOUND); } return address; @@ -241,7 +242,7 @@ export class UserService { }); if (!address) { - throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`); + throw new NotFoundException(UserMessage.ADDRESS_NOT_FOUND); } // If setting as default, unset other default addresses @@ -271,7 +272,7 @@ export class UserService { }); if (!address) { - throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`); + throw new NotFoundException(UserMessage.ADDRESS_NOT_FOUND); } // Soft delete the address @@ -286,7 +287,7 @@ export class UserService { }); if (!address) { - throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`); + throw new NotFoundException(UserMessage.ADDRESS_NOT_FOUND); } // Unset all other default addresses @@ -310,11 +311,11 @@ export class UserService { return this.em.transactional(async em => { const user = await em.findOne(User, { id: userId }); if (!user) { - throw new NotFoundException(`User with ID ${userId} not found.`); + throw new NotFoundException(UserMessage.USER_NOT_FOUND); } const shop = await em.findOne(Shop, { id: shopId }); if (!shop) { - throw new NotFoundException(`Shop with ID ${shopId} not found.`); + throw new NotFoundException(RestMessage.NOT_FOUND); } let walletTransaction = await em.findOne(WalletTransaction, { user: { id: userId }, shop: { id: shopId } }, { orderBy: { createdAt: 'DESC' } }); @@ -336,17 +337,17 @@ export class UserService { // Validate points to convert if (pointsToConvert <= 0) { - throw new BadRequestException('Points to convert must be greater than 0.'); + throw new BadRequestException(UserMessage.POINTS_MUST_BE_GREATER_THAN_ZERO); } if (!shop.score) { - throw new BadRequestException('Shop score not found.'); + throw new BadRequestException(UserMessage.SHOP_SCORE_NOT_FOUND); } if (!shop.score.purchaseScore) { - throw new BadRequestException('Shop purchase score not found.'); + throw new BadRequestException(UserMessage.SHOP_PURCHASE_SCORE_NOT_FOUND); } if (!shop.score.purchaseAmount) { - throw new BadRequestException('Shop purchase amount not found.'); + throw new BadRequestException(UserMessage.SHOP_PURCHASE_AMOUNT_NOT_FOUND); } const walletIncreaseAmount = (Number(pointsToConvert) * Number(shop.score.purchaseAmount)) / Number(shop.score.purchaseScore); @@ -388,7 +389,7 @@ export class UserService { async findOneOrFail(userId: string): Promise { const user = await this.userRepository.findOne({ id: userId }); if (!user) { - throw new NotFoundException(`User with ID ${userId} not found.`); + throw new NotFoundException(UserMessage.USER_NOT_FOUND); } return user; } @@ -396,7 +397,7 @@ export class UserService { async getUserAddressOrFail(addressId: string): Promise { const address = await this.em.findOne(UserAddress, { id: addressId }, { populate: ['user'] }); if (!address) { - throw new NotFoundException("User address not found."); + throw new NotFoundException(UserMessage.ADDRESS_NOT_FOUND); } return address; } diff --git a/src/modules/users/providers/wallet.service.ts b/src/modules/users/providers/wallet.service.ts index 6bf185f..cee294b 100644 --- a/src/modules/users/providers/wallet.service.ts +++ b/src/modules/users/providers/wallet.service.ts @@ -8,6 +8,7 @@ import { CreateWalletTransactionDto } from '../dto/create-wallet-transaction.dto import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; import { WalletTransactionType } from '../interface/wallet'; import { PointTransactionRepository } from '../repositories/point-transaction.repository'; +import { PaymentMessage } from 'src/common/enums/message.enum'; @Injectable() export class WalletService { @@ -44,7 +45,7 @@ export class WalletService { }); if (!walletTransaction) { - throw new NotFoundException(`User wallet not found for user ${userId} and shop ${shopId}.`); + throw new NotFoundException(PaymentMessage.WALLET_NOT_FOUND); } // Build the 'where' filter query @@ -127,7 +128,7 @@ export class WalletService { // Validate amount if (amount <= 0) { - throw new BadRequestException('Transaction amount must be greater than 0.'); + throw new BadRequestException(PaymentMessage.AMOUNT_MUST_BE_GREATER_THAN_ZERO); } // Find the user's wallet for this shop @@ -137,7 +138,7 @@ export class WalletService { }); if (!walletTransaction) { - throw new NotFoundException(`User wallet not found for user ${userId} and shop ${shopId}.`); + throw new NotFoundException(PaymentMessage.WALLET_NOT_FOUND); } // Calculate new balance @@ -148,11 +149,11 @@ export class WalletService { newBalance = currentBalance + amount; } else if (type === WalletTransactionType.DEBIT) { if (currentBalance < amount) { - throw new BadRequestException(`Insufficient wallet balance. Current: ${currentBalance}, Required: ${amount}.`); + throw new BadRequestException(PaymentMessage.INSUFFICIENT_WALLET_BALANCE); } newBalance = currentBalance - amount; } else { - throw new BadRequestException(`Invalid transaction type: ${type}`); + throw new BadRequestException(PaymentMessage.INVALID_TRANSACTION_TYPE); } // Update wallet balance