diff --git a/src/common/constants/index.ts b/src/common/constants/index.ts index d2b97d8..f9119d2 100755 --- a/src/common/constants/index.ts +++ b/src/common/constants/index.ts @@ -6,15 +6,6 @@ export const AUTH_THROTTLE_TTL = 1 * 60 * 1000; export const AUTH_THROTTLE_LIMIT = 5; export const AUTH__REFRESH_THROTTLE_TTL = 10 * 60 * 1000; export const AUTH__REFRESH_THROTTLE_LIMIT = 10; -// -export const CONSOLE_JWT_STRATEGY_NAME = 'console_jwt_strategy'; -export const LOCAL_JWT_STRATEGY_NAME = 'local_jwt_strategy'; + + -export const API_HEADER_SLUG = { - name: 'X-Slug', - required: true, - schema: { - type: 'string', - default: 'zhivan', - }, -}; diff --git a/src/common/decorators/index.ts b/src/common/decorators/index.ts index 6a12fec..0d2868e 100644 --- a/src/common/decorators/index.ts +++ b/src/common/decorators/index.ts @@ -1,3 +1,2 @@ export { UserId } from './user-id.decorator'; -export { RestId } from './rest-id.decorator'; -export { RateLimit } from './rate-limit.decorator'; + export { RateLimit } from './rate-limit.decorator'; diff --git a/src/common/decorators/rest-id.decorator.ts b/src/common/decorators/rest-id.decorator.ts deleted file mode 100644 index afebf8c..0000000 --- a/src/common/decorators/rest-id.decorator.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createParamDecorator, type ExecutionContext } from '@nestjs/common'; -import type { Request } from 'express'; - -/** - * Decorator to extract restId from the authenticated request. - * Must be used after AdminAuthGuard or AuthGuard that sets request.restId. - * - * @example - * @Get('/restaurants') - * @UseGuards(AdminAuthGuard) - * getRestaurants(@RestId() restId: string) { - * return this.restaurantService.findById(restId); - * } - */ -export const RestId = createParamDecorator((data: unknown, ctx: ExecutionContext): string => { - const request = ctx.switchToHttp().getRequest(); - return request.restId || ''; -}); diff --git a/src/common/decorators/rest-slug.decorator.ts b/src/common/decorators/rest-slug.decorator.ts deleted file mode 100644 index 16296d0..0000000 --- a/src/common/decorators/rest-slug.decorator.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createParamDecorator, type ExecutionContext } from '@nestjs/common'; -import type { Request } from 'express'; - -export const RestSlug = createParamDecorator((data: unknown, ctx: ExecutionContext): string => { - const request = ctx.switchToHttp().getRequest(); - return request.slug || ''; -}); diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 95afda1..4eb1e19 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -72,7 +72,7 @@ export const enum AuthMessage { INVALID_OR_EXPIRED_TOKEN = 'توکن نامعتبر یا منقضی شده است', } export const enum productMessage { - NOT_FOUND = 'غذایی با این مشخصات یافت نشد', + NOT_FOUND = 'کالایی با این مشخصات یافت نشد', } export const enum CategoryMessage { NOT_FOUND = 'دسته‌بندی مورد نظر یافت نشد', @@ -154,154 +154,7 @@ export const enum UploaderMessage { UPLOAD_FILE_SUCCESS = 'فایل با موفقیت آپلود شد', } -export const enum EmailMessage { - EMAIL_VERIFICATION = 'تایید ایمیل', - EMAIL_SENDING_FAILED = 'ارسال ایمیل با خطا مواجه شد', - LOGIN = 'ورود به سیستم', - INVOICE = 'فاکتور', - WALLET = 'اعلان کیف پول', - TICKET = 'تیکت پشتیبانی', - ANNOUNCEMENT = 'اعلان', - PAYMENT_REMINDER = 'یادآوری پرداخت', - PAYMENT_CANCELLATION = 'لغو پرداخت', - EMAIL_SENDING_SUCCESS = 'ارسال ایمیل با موفقیت انجام شد', - EMAIL_FORWARD_SUCCESS = 'فوروارد ایمیل با موفقیت انجام شد', - EMAIL_STATUS_RETRIEVED_SUCCESSFULLY = 'وضعیت ایمیل با موفقیت دریافت شد', - MESSAGES_SEARCHED_SUCCESSFULLY = 'جستجوی ایمیل با موفقیت انجام شد', - MESSAGES_LISTED_SUCCESSFULLY = 'لیست ایمیل ها با موفقیت دریافت شد', - MESSAGE_RETRIEVED_SUCCESSFULLY = 'ایمیل با موفقیت دریافت شد', - MESSAGE_DELETED_SUCCESSFULLY = 'ایمیل با موفقیت حذف شد', - USER_ID_REQUIRED = 'شناسه کاربری مورد نیاز است', - USER_ID_MUST_BE_STRING = 'شناسه کاربری باید یک رشته باشد', - MAILBOX_ID_REQUIRED = 'شناسه میل باکس ایمیل مورد نیاز است', - MAILBOX_ID_MUST_BE_STRING = 'شناسه میل باکس ایمیل باید یک رشته باشد', - QUEUE_ID_REQUIRED = 'شناسه پیش‌نویس ایمیل مورد نیاز است', - QUEUE_ID_MUST_BE_STRING = 'شناسه پیش‌نویس ایمیل باید یک رشته باشد', - MESSAGE_ID_MUST_BE_NUMBER = 'شناسه ایمیل باید یک عدد باشد', - FROM_ADDRESS_REQUIRED = 'آدرس از مورد نیاز است', - - // Validation Messages for Email DTOs - FROM_ADDRESS_MUST_BE_EMAIL = 'آدرس فرستنده باید یک ایمیل معتبر باشد', - - // Query validation messages - SEARCH_QUERY_PARAMETER_MUST_BE_STRING = 'پارامتر جستجو باید یک رشته باشد', - MESSAGE_IDS_MUST_BE_STRING = 'شناسه‌های پیام باید یک رشته باشد', - THREAD_ID_MUST_BE_STRING = 'شناسه رشته گفتگو باید یک رشته باشد', - OR_QUERY_MUST_BE_OBJECT = 'پارامتر OR باید یک شیء باشد', - DATE_START_MUST_BE_DATE_STRING = 'تاریخ شروع باید یک رشته تاریخ معتبر باشد', - DATE_END_MUST_BE_DATE_STRING = 'تاریخ پایان باید یک رشته تاریخ معتبر باشد', - TO_ADDRESS_MUST_BE_STRING = 'آدرس گیرنده باید یک رشته باشد', - MIN_SIZE_MUST_BE_NUMBER = 'حداقل اندازه باید یک عدد باشد', - MIN_SIZE_MUST_BE_NON_NEGATIVE = 'حداقل اندازه نمی‌تواند منفی باشد', - MAX_SIZE_MUST_BE_NUMBER = 'حداکثر اندازه باید یک عدد باشد', - MAX_SIZE_MUST_BE_NON_NEGATIVE = 'حداکثر اندازه نمی‌تواند منفی باشد', - ATTACHMENTS_FILTER_MUST_BE_BOOLEAN = 'فیلتر پیوست‌ها باید بولین باشد', - FLAGGED_FILTER_MUST_BE_BOOLEAN = 'فیلتر پیام‌های علامت‌دار باید بولین باشد', - UNSEEN_FILTER_MUST_BE_BOOLEAN = 'فیلتر پیام‌های خوانده‌نشده باید بولین باشد', - INCLUDE_HEADERS_MUST_BE_STRING = 'لیست هدرها باید یک رشته باشد', - SEARCHABLE_FILTER_MUST_BE_BOOLEAN = 'فیلتر قابل جستجو باید بولین باشد', - THREAD_COUNTERS_MUST_BE_BOOLEAN = 'شمارنده رشته گفتگو باید بولین باشد', - ORDER_MUST_BE_VALID = 'ترتیب باید یکی از مقادیر asc یا desc باشد', - NEXT_CURSOR_MUST_BE_STRING = 'نشانگر صفحه بعد باید یک رشته باشد', - PREVIOUS_CURSOR_MUST_BE_STRING = 'نشانگر صفحه قبل باید یک رشته باشد', - - RECIPIENT_NAME_MUST_BE_STRING = 'نام گیرنده باید یک رشته باشد', - RECIPIENT_ADDRESS_REQUIRED = 'آدرس گیرنده مورد نیاز است', - RECIPIENT_ADDRESS_MUST_BE_EMAIL = 'آدرس گیرنده باید یک ایمیل معتبر باشد', - HEADER_KEY_REQUIRED = 'کلید هدر مورد نیاز است', - HEADER_KEY_MUST_BE_STRING = 'کلید هدر باید یک رشته باشد', - HEADER_VALUE_REQUIRED = 'مقدار هدر مورد نیاز است', - HEADER_VALUE_MUST_BE_STRING = 'مقدار هدر باید یک رشته باشد', - ATTACHMENT_FILENAME_MUST_BE_STRING = 'نام فایل پیوست باید یک رشته باشد', - ATTACHMENT_CONTENT_TYPE_MUST_BE_STRING = 'نوع محتوای پیوست باید یک رشته باشد', - ATTACHMENT_ENCODING_MUST_BE_STRING = 'کدگذاری پیوست باید یک رشته باشد', - ATTACHMENT_CONTENT_TRANSFER_ENCODING_MUST_BE_STRING = 'کدگذاری انتقال محتوای پیوست باید یک رشته باشد', - ATTACHMENT_CONTENT_DISPOSITION_INVALID = 'نحوه نمایش پیوست باید inline یا attachment باشد', - ATTACHMENT_CONTENT_REQUIRED = 'محتوای پیوست مورد نیاز است', - ATTACHMENT_CONTENT_MUST_BE_STRING = 'محتوای پیوست باید یک رشته باشد', - ATTACHMENT_CID_MUST_BE_STRING = 'شناسه محتوای پیوست باید یک رشته باشد', - DRAFT_MAILBOX_REQUIRED = 'شناسه صندوق پیش‌نویس مورد نیاز است', - DRAFT_MAILBOX_MUST_BE_STRING = 'شناسه صندوق پیش‌نویس باید یک رشته باشد', - DRAFT_ID_REQUIRED = 'شناسه پیش‌نویس مورد نیاز است', - DRAFT_ID_MUST_BE_NUMBER = 'شناسه پیش‌نویس باید یک عدد باشد', - ENVELOPE_FROM_REQUIRED = 'آدرس فرستنده در envelope مورد نیاز است', - ENVELOPE_TO_REQUIRED = 'آدرس گیرنده در envelope مورد نیاز است', - ENVELOPE_TO_MUST_BE_ARRAY = 'آدرس گیرنده در envelope باید یک آرایه باشد', - MAILBOX_MUST_BE_STRING = 'شناسه صندوق باید یک رشته باشد', - REPLY_TO_MUST_BE_VALID = 'آدرس پاسخ باید معتبر باشد', - TO_ADDRESSES_REQUIRED = 'آدرس‌های گیرنده مورد نیاز است', - TO_ADDRESSES_MUST_BE_ARRAY = 'آدرس‌های گیرنده باید یک آرایه باشد', - CC_ADDRESSES_MUST_BE_ARRAY = 'آدرس‌های کپی کربن باید یک آرایه باشد', - BCC_ADDRESSES_MUST_BE_ARRAY = 'آدرس‌های کپی مخفی باید یک آرایه باشد', - HEADERS_MUST_BE_ARRAY = 'هدرهای سفارشی باید یک آرایه باشد', - SUBJECT_MUST_BE_STRING = 'موضوع ایمیل باید یک رشته باشد', - TEXT_CONTENT_MUST_BE_STRING = 'محتوای متنی باید یک رشته باشد', - HTML_CONTENT_MUST_BE_STRING = 'محتوای HTML باید یک رشته باشد', - ATTACHMENTS_MUST_BE_ARRAY = 'پیوست‌ها باید یک آرایه باشد', - SESSION_ID_MUST_BE_STRING = 'شناسه جلسه باید یک رشته باشد', - IP_ADDRESS_MUST_BE_STRING = 'آدرس IP باید یک رشته باشد', - IS_DRAFT_MUST_BE_BOOLEAN = 'وضعیت پیش‌نویس باید بولین باشد', - SEND_TIME_MUST_BE_DATE = 'زمان ارسال باید یک تاریخ معتبر باشد', - UPLOAD_ONLY_MUST_BE_BOOLEAN = 'حالت فقط آپلود باید بولین باشد', - ENVELOPE_MUST_BE_VALID = 'envelope باید معتبر باشد', - - // Message operations - MESSAGE_MARKED_AS_SEEN_SUCCESSFULLY = 'پیام با موفقیت به عنوان خوانده شده علامت گذاری شد', - ALL_MESSAGES_MARKED_AS_READ_SUCCESSFULLY = 'تمام پیام ها با موفقیت به عنوان خوانده شده علامت گذاری شدند', - MESSAGE_MARK_AS_SEEN_FAILED = 'علامت گذاری پیام با خطا مواجه شد', - SENT_MESSAGES_RETRIEVED_SUCCESSFULLY = 'پیام های ارسالی با موفقیت دریافت شد', - SENT_MESSAGES_RETRIEVAL_FAILED = 'دریافت پیام های ارسالی با خطا مواجه شد', - TRASH_MESSAGES_RETRIEVED_SUCCESSFULLY = 'پیام های حذف شده با موفقیت دریافت شد', - TRASH_MESSAGES_RETRIEVAL_FAILED = 'دریافت پیام های حذف شده با خطا مواجه شد', - DRAFT_UPDATED_SUCCESSFULLY = 'پیش نویس با موفقیت به روز رسانی شد', - DRAFT_SENT_SUCCESSFULLY = 'پیش نویس با موفقیت ارسال شد', - DRAFT_DELETED_SUCCESSFULLY = 'پیش نویس با موفقیت حذف شد', - MESSAGE_MOVED_TO_ARCHIVE_SUCCESSFULLY = 'پیام با موفقیت به آرشیو منتقل شد', - MESSAGE_MOVED_TO_FAVORITE_SUCCESSFULLY = 'پیام با موفقیت به علاقه مندی ها منتقل شد', - MESSAGE_MOVED_TO_TRASH_SUCCESSFULLY = 'پیام با موفقیت به سطل زباله منتقل شد', - MESSAGE_MOVED_TO_JUNK_SUCCESSFULLY = 'پیام با موفقیت به جانک منتقل شد', - MESSAGE_MOVED_TO_INBOX_FROM_ARCHIVE_SUCCESSFULLY = 'پیام با موفقیت از آرشیو به صندوق ورودی منتقل شد', - MESSAGE_MOVED_TO_INBOX_FROM_FAVORITE_SUCCESSFULLY = 'پیام با موفقیت از علاقه مندی ها به صندوق ورودی منتقل شد', - MESSAGE_MOVED_TO_INBOX_FROM_TRASH_SUCCESSFULLY = 'پیام با موفقیت از سطل زباله به صندوق ورودی منتقل شد', - MESSAGE_MOVED_TO_INBOX_FROM_JUNK_SUCCESSFULLY = 'پیام با موفقیت از جانک به صندوق ورودی منتقل شد', - MESSAGE_NOT_FOUND = 'پیام یافت نشد', - - // Bulk action messages - BULK_ACTION_COMPLETED_SUCCESSFULLY = '[count] پیام با موفقیت [action] شد', - BULK_ACTION_PARTIALLY_COMPLETED = '[successful] از [total] پیام [action] شد. [failed] پیام با خطا مواجه شد', - BULK_ACTION_FAILED = 'هیچ پیامی [action] نشد. تمام [total] تلاش ناموفق بود', - - // Bulk action display names - BULK_ACTION_SEEN = 'خوانده شده علامت‌گذاری', - BULK_ACTION_DELETE = 'حذف', - BULK_ACTION_ARCHIVE = 'آرشیو', - BULK_ACTION_UNARCHIVE = 'از آرشیو خارج', - BULK_ACTION_FAVORITE = 'به علاقه مندی ها منتقل', - BULK_ACTION_UNFAVORITE = 'از علاقه مندی ها حذف', - BULK_ACTION_JUNK = 'به جانک منتقل', - BULK_ACTION_NOTJUNK = 'از جانک از حذف', - BULK_ACTION_TRASH = 'به سطل زباله منتقل', - BULK_ACTION_RESTORE = 'از سطل زباله بازیابی', - BULK_ACTION_PROCESSED = 'پردازش', - USER_NOT_FOUND = 'کاربر یافت نشد', - PRIORITY_MUST_BE_ONE_OF_HIGH_NORMAL_LOW = 'اولویت باید یکی از مقادیر high, normal, low باشد', - SUBJECT_REQUIRED = 'موضوع ایمیل مورد نیاز است', - ALL_MESSAGES_DELETED_SUCCESSFULLY = 'همه پیام ها با موفقیت حذف شدند', - MAILBOX_ID_MUST_BE_MONGO_ID = 'شناسه میل باکس ایمیل باید یک آی دی معتبر باشد', - 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', - - // Favorite/Star messages (Gmail-like behavior) - MESSAGE_MARKED_AS_FAVORITE_SUCCESSFULLY = 'پیام با موفقیت به عنوان مورد علاقه علامت گذاری شد', - MESSAGE_REMOVED_FROM_FAVORITE_SUCCESSFULLY = 'پیام با موفقیت از مورد علاقه حذف شد', - TRASH_EMPTYED_SUCCESSFULLY = 'سطل زباله با موفقیت خالی شد', - SPAM_EMPTYED_SUCCESSFULLY = 'همه پیام های اسپم با موفقیت حذف شدند', - SAVE_DRAFT_SUCCESS = 'پیش نویس با موفقیت ذخیره شد', - HTML_CONTENT_REQUIRED = 'محتوای HTML مورد نیاز است', - TEXT_CONTENT_REQUIRED = 'TEXT_CONTENT_REQUIRED', -} + export const enum WebSocketMessage { // Connection Messages @@ -398,173 +251,14 @@ export const enum SmsMessage { SEND_SMS_ERROR = 'خطا در ارسال پیامک', } -export const enum RestMessage { - NOT_FOUND = 'کسب و کار یافت نشد', - BUSINESS_ID_REQUIRED = 'شناسه کسب و کار مورد نیاز است', - SLUG_REQUIRED = 'اسلاگ مورد نیاز است', - SLUG_MUST_BE_STRING = 'اسلاگ باید یک رشته باشد', - DOMAIN_INVALID = 'دامنه وارد شده معتبر نیست', - DOMAIN_UPDATED = 'دامنه با موفقیت به‌روزرسانی شد', - DOMAIN_VERIFICATION_INITIATED = 'فرآیند تایید دامنه آغاز شد', - DOMAIN_VERIFICATION_FAILED = 'تایید دامنه با خطا مواجه شد', - DOMAIN_VERIFICATION_SUCCESS = 'دامنه با موفقیت تایید شد', - DOMAIN_VERIFICATION_METHOD_INVALID = 'روش تایید دامنه نامعتبر است', - DOMAIN_ALREADY_VERIFIED = 'این دامنه قبلا تایید شده است', - DOMAIN_REQUIRED = 'دامنه مورد نیاز است', - DOMAIN_MUST_BE_STRING = 'دامنه باید یک رشته باشد', - STAFF_NOT_FOUND = 'کاربر ادمین یافت نشد', - ZARINPAL_MERCHANT_ID_REQUIRED = 'شناسه مرچنت زرین پال مورد نیاز است', - ZARINPAL_MERCHANT_ID_STRING = 'شناسه مرچنت زرین پال باید یک رشته باشد', - LOGO_URL_REQUIRED = 'آدرس تصویر لوگو مورد نیاز است', - LOGO_URL_STRING = 'آدرس تصویر لوگو باید یک رشته باشد', - LOGO_URL_INVALID = 'آدرس تصویر لوگو معتبر نیست', - BUSINESS_SETTINGS_UPDATED = 'تنظیمات کسب و کار با موفقیت به روز رسانی شد', - NAME_REQUIRED = 'نام کسب و کار مورد نیاز است', - NAME_STRING = 'نام کسب و کار باید یک رشته باشد', - DOMAIN_CONNECTED = 'دامنه با موفقیت متصل شد', - DOMAIN_NOT_CONNECTED = 'دامنه به سرور ما متصل نشد', - DOMAIN_NOT_FOUND = 'دامنه یافت نشد', - DOMAIN_VERIFICATION_TOKEN_REQUIRED = 'توکن تایید دامنه مورد نیاز است', - INSUFFICIENT_QUOTA = 'حجم شما برای ساخت ایمیل کافی نمی باشد، لطفا حجم را افزایش دهید', - QUOTA_DOWNGRADE_NOT_ALLOWED = 'کاهش حجم ایمیل مجاز نیست، فقط می‌توانید حجم را افزایش دهید', - QUOTA_AMOUNT_REQUIRED = 'مقدار حجم مورد نیاز است', - QUOTA_AMOUNT_MUST_BE_NUMBER = 'مقدار حجم باید یک عدد باشد', - QUOTA_AMOUNT_MUST_BE_POSITIVE = 'مقدار حجم باید مثبت باشد', - CALLBACK_URL_MUST_BE_STRING = 'آدرس بازگشت باید یک رشته باشد', - CALLBACK_URL_INVALID = 'آدرس بازگشت معتبر نیست', - PURCHASE_DESCRIPTION_MUST_BE_STRING = 'توضیحات خرید باید یک رشته باشد', - QUOTA_PURCHASE_SUCCESS = 'درخواست خرید حجم با موفقیت ثبت شد', - QUOTA_PURCHASE_FAILED = 'خرید حجم با خطا مواجه شد', - PAYMENT_GATEWAY_ERROR = 'خطا در اتصال به درگاه پرداخت', - QUOTA_UPDATED = 'حجم ایمیل با موفقیت به روز شد', - USER_DOES_NOT_HAVE_ACCESS_TO_SERVICE = 'کاربر دسترسی به این سرویس را ندارد', -} - -export const enum DomainMessage { - // Domain Ownership & Verification - NOT_BELONG_TO_BUSINESS = 'دامنه به این کسب و کار تعلق ندارد', - NOT_VERIFIED = 'دامنه باید تایید شده باشد', - DOMAIN_NOT_FOUND = 'دامنه یافت نشد', - DOMAIN_ALREADY_EXISTS = 'دامنه [name] قبلا ثبت شده است', - DOMAIN_DELETED_SUCCESSFULLY = 'دامنه با موفقیت حذف شد', - VERIFICATION_NOT_FOUND = 'تایید دامنه یافت نشد', - NO_DOMAIN_FOUND = 'دامنه ای یافت نشد', - DOMAIN_CHECKED = 'دامنه با موفقیت بررسی شد', - - // Domain Creation & Setup - DOMAIN_CREATED_WITH_RECORDS = 'دامنه [name] با موفقیت ایجاد شد و تمام رکوردهای مورد نیاز تولید گردید', - ALL_REQUIRED_RECORDS_GENERATED = 'تمام رکوردهای مورد نیاز (MX, SPF, DMARC, DKIM) به طور خودکار برای شما تولید شده‌اند', - ESTIMATED_PROPAGATION_TIME = 'تغییرات DNS معمولاً ۱۵ دقیقه تا ۴۸ ساعت در سراسر جهان منتشر می‌شود', - - // DNS Records Instructions - ADD_MX_RECORD = 'یک رکورد MX با نام [name] که به [value] با اولویت [priority] اشاره کند اضافه کنید', - ADD_TXT_RECORD = 'یک رکورد TXT با نام [name] و مقدار [value] اضافه کنید', - ADD_DKIM_RECORD = 'یک رکورد TXT با نام [name] و مقدار [value] اضافه کنید (کلید عمومی DKIM)', - ADD_DMARC_RECORD = 'یک رکورد TXT با نام [name] و مقدار [value] اضافه کنید (سیاست DMARC)', - ADD_CNAME_RECORD = 'یک رکورد CNAME با نام [name] که به [value] اشاره کند اضافه کنید', - ADD_GENERIC_RECORD = 'یک رکورد [type] با نام [name] و مقدار [value] اضافه کنید', - - // Domain Setup Steps - STEP_LOGIN_DNS_PANEL = 'وارد پنل مدیریت DNS ثبت کننده دامنه خود شوید', - STEP_ADD_ALL_RECORDS = 'تمام رکوردهای DNS مورد نیاز لیست شده در بالا را به زون DNS دامنه خود اضافه کنید', - STEP_INCLUDE_ALL_TYPES = 'مطمئن شوید که رکوردهای MX، SPF، DMARC و DKIM را برای عملکرد کامل ایمیل شامل کرده‌اید', - STEP_WAIT_PROPAGATION = 'منتظر انتشار DNS بمانید (معمولاً ۱۵ دقیقه تا ۴۸ ساعت)', - STEP_USE_CHECK_ENDPOINT = "از نقطه پایانی 'بررسی DNS' برای تایید تنظیم صحیح رکوردهای خود استفاده کنید", - STEP_DOMAIN_READY = 'پس از تایید، دامنه شما برای سرویس‌های ایمیل آماده خواهد بود!', - - // Status Messages - RECORDS_PENDING_VERIFICATION = '[count] رکورد DNS هنوز در انتظار تایید هستند', - ALL_REQUIRED_RECORDS_MUST_BE_CONFIGURED = 'تمام [count] رکورد مورد نیاز باید برای کار درست ایمیل تنظیم شوند', - DOMAIN_FULLY_VERIFIED = 'دامنه شما به طور کامل تایید شده و برای ایمیل آماده است!', - EMAIL_ACCOUNTS_READY = 'اکنون می‌توانید حساب‌های ایمیل ایجاد کرده و شروع به ارسال/دریافت ایمیل کنید', - - // Verification Status - DOMAIN_VERIFICATION_INITIATED = 'فرآیند تایید دامنه آغاز شد', - DOMAIN_VERIFICATION_FAILED = 'تایید دامنه با خطا مواجه شد', - DOMAIN_VERIFICATION_SUCCESS = 'دامنه با موفقیت تایید شد', - DOMAIN_VERIFICATION_PENDING = 'تایید دامنه در انتظار است', - - // DNS Record Status - DNS_RECORD_VERIFIED = 'رکورد DNS تایید شد', - DNS_RECORD_FAILED = 'رکورد DNS تایید نشد', - DNS_RECORD_NOT_FOUND_OR_MISMATCH = 'رکورد DNS یافت نشد یا مقدار مطابقت ندارد', - - // DKIM Messages - DKIM_ENABLED_AUTOMATICALLY = 'DKIM به طور خودکار برای دامنه [name] فعال شد', - DKIM_FAILED_TO_CREATE = 'ایجاد DKIM برای دامنه [name] ناموفق بود', - DKIM_ENABLED_SUCCESSFULLY = 'DKIM برای دامنه [name] با موفقیت فعال شد', - - // Error Messages - FAILED_TO_VERIFY_DOMAIN = 'تایید دامنه [name] ناموفق بود', - FAILED_TO_SETUP_MAIL_SERVER = 'راه‌اندازی دامنه [name] در سرور ایمیل ناموفق بود', - DOMAIN_CREATION_FAILED = 'ایجاد دامنه ناموفق بود', - DNS_VERIFICATION_ATTEMPTS_EXCEEDED = 'تعداد تلاش‌های تایید DNS از حد مجاز بیشتر شد', - - // Service Messages - DOMAIN_UPDATED_SUCCESSFULLY = 'دامنه بروزرسانی شد', - VERIFICATION_TOKEN_PREFIX = 'توکن تایید: [token]', - RECORD_NEEDS_FIXING = 'رکورد [type] برای [name] نیاز به اصلاح دارد: [error]', - RECORD_PENDING_VERIFICATION_DETAIL = 'رکورد [type] برای [name] هنوز در انتظار تایید است. لطفا مطمئن شوید که انتشار DNS کامل شده است.', - RECORD_DESCRIPTION_EMAIL_FUNCTIONALITY = 'رکورد [type] برای عملکرد ایمیل', - BUSINESS_ALREADY_HAS_DOMAIN = 'این کسب و کار قبلا دامنه ای دارد', - CAN_NOT_USE_THIS_DOMAIN = 'این دامنه نمیتواند ثبت شود', -} - -export const enum MailServerMessage { - FAILED_TO_CREATE_ACCOUNT = 'خطا در ایجاد ایمیل', - FAILED_TO_CREATE_DKIM_KEY = 'خطا در ایجاد کلید DKIM', -} - -export const enum DnsRecordMessage { - DNS_RECORD_NOT_FOUND = 'رکورد DNS یافت نشد', - DNS_RECORD_DELETED = 'رکورد DNS با موفقیت حذف شد', -} + + export const enum RoleMessage { NOT_FOUND = 'نقش یافت نشد', } -export const enum SignatureMessage { - NAME_NOT_EMPTY = 'نام امضاء نمیتواند خالی باشد', - NAME_MAX_LENGTH = 'نام امضاء نمیتواند بیشتر از حداکثر طول مجاز باشد', - NAME_STRING = 'نام امضاء باید یک رشته باشد', - TEXT_CONTENT_NOT_EMPTY = 'محتوای متنی امضاء نمیتواند خالی باشد', - TEXT_CONTENT_STRING = 'محتوای متنی امضاء باید یک رشته باشد', - TEXT_CONTENT_MAX_LENGTH = 'محتوای متنی امضاء نمیتواند بیشتر از حداکثر طول مجاز باشد', - IS_ACTIVE_NOT_EMPTY = 'وضعیت فعال بودن نمیتواند خالی باشد', - IS_ACTIVE_BOOLEAN = 'وضعیت فعال بودن باید بولین باشد', - IS_DEFAULT_NOT_EMPTY = 'وضعیت پیش فرض نمیتواند خالی باشد', - IS_DEFAULT_BOOLEAN = 'وضعیت پیش فرض باید بولین باشد', - CREATED = 'امضاء با موفقیت ایجاد شد', - NOT_FOUND = 'امضاء یافت نشد', - DELETED = 'امضاء با موفقیت حذف شد', - TOGGLE_ACTIVE = 'وضعیت امضاء با موفقیت تغییر کرد', -} - -export const enum TemplateMessage { - TEMPLATE_NOT_FOUND = 'قالب یافت نشد', - TEMPLATE_CREATED_SUCCESSFULLY = 'قالب با موفقیت ایجاد شد', - TEMPLATE_UPDATED_SUCCESSFULLY = 'قالب با موفقیت به روزرسانی شد', - TEMPLATE_DELETED_SUCCESSFULLY = 'قالب با موفقیت حذف شد', - TEMPLATE_RETRIEVED_SUCCESSFULLY = 'قالب با موفقیت دریافت شد', - TEMPLATES_LISTED_SUCCESSFULLY = 'لیست قالب ها با موفقیت دریافت شد', - TEMPLATE_NAME_REQUIRED = 'نام قالب الزامی است', - TEMPLATE_NAME_STRING = 'نام قالب باید یک رشته باشد', - TEMPLATE_NAME_MAX_LENGTH = 'نام قالب نمیتواند بیشتر از ۲۵۵ کاراکتر باشد', - TEMPLATE_STRUCTURE_REQUIRED = 'ساختار قالب الزامی است', - TEMPLATE_STRUCTURE_VALID = 'ساختار قالب باید معتبر باشد', - TEMPLATE_RAW_HTML_STRING = 'HTML خام باید یک رشته باشد', - TEMPLATE_BUSINESS_ID_REQUIRED = 'شناسه کسب و کار الزامی است', - TEMPLATE_BUSINESS_ID_UUID = 'شناسه کسب و کار باید یک UUID معتبر باشد', - TEMPLATE_ID_REQUIRED = 'شناسه قالب الزامی است', - TEMPLATE_ID_UUID = 'شناسه قالب باید یک UUID معتبر باشد', - TEMPLATE_ACCESS_DENIED = 'شما دسترسی به این قالب را ندارید', - TEMPLATE_BUSINESS_NOT_FOUND = 'کسب و کار مالک قالب یافت نشد', - SEARCH_STRING = 'نام برای جستجو باید یک رشته باشد', - TEMPLATE_SELECTED_SUCCESSFULLY = 'وضعیت قالب با موفقیت تغییر کرد', - TEMPLATE_THUMBNAIL_URL_VALID = 'آدرس تصویر قالب باید یک آدرس معتبر باشد', - TEMPLATE_RAW_HTML_REQUIRED = 'HTML خام قالب الزامی است', -} + export const enum NotificationMessage { TITLE_IS_REQUIRED = 'عنوان اعلان الزامی است', @@ -597,57 +291,7 @@ export const enum SettingMessageEnum { NOTIFICATION_NOT_FOUND_OR_ACCESS_DENIED = 'اعلان یافت نشد یا دسترسی آن را ندارید', } -export const enum AiMessage { - NO_RESPONSE_FROM_AI_MODEL = 'خطا در دریافت پاسخ از مدل AI', - MESSAGE_NOT_FOUND_OR_ACCESS_DENIED = 'ایمیل یافت نشد یا دسترسی آن را ندارید', - EMAIL_CONTENT_IS_EMPTY_OR_COULD_NOT_BE_EXTRACTED = 'محتوای ایمیل خالی است یا قابل استخراج نیست', - DESCRIPTION_IS_REQUIRED = 'شرح و هدف قالب الزامی است', - DESCRIPTION_MUST_BE_A_STRING = 'شرح قالب باید یک رشته باشد', - DESCRIPTION_MUST_BE_AT_LEAST_10_CHARACTERS = 'شرح قالب باید حداقل ۱۰ کاراکتر باشد', - DESCRIPTION_MUST_BE_LESS_THAN_500_CHARACTERS = 'شرح قالب نمی‌تواند بیشتر از ۵۰۰ کاراکتر باشد', - THEME_MUST_BE_A_VALID_VALUE = 'تم قالب باید یکی از مقادیر مجاز باشد', - STYLE_MUST_BE_A_VALID_VALUE = 'سبک قالب باید یکی از مقادیر مجاز باشد', - PURPOSE_MUST_BE_A_VALID_VALUE = 'هدف قالب باید یکی از مقادیر مجاز باشد', - TEMPLATE_NAME_MUST_BE_A_STRING = 'نام قالب باید یک رشته باشد', - TEMPLATE_NAME_MUST_BE_LESS_THAN_100_CHARACTERS = 'نام قالب نمی‌تواند بیشتر از ۱۰۰ کاراکتر باشد', - PREFERRED_COLORS_MUST_BE_A_STRING = 'رنگ‌های ترجیحی باید یک رشته باشد', - PREFERRED_COLORS_MUST_BE_LESS_THAN_200_CHARACTERS = 'رنگ‌های ترجیحی نمی‌تواند بیشتر از ۲۰۰ کاراکتر باشد', - PREFERRED_COLORS_IS_REQUIRED = 'رنگ‌های ترجیحی الزامی است', -} - -export const enum TwoFactorMessage { - TWO_FACTOR_SETUP_INITIATED = 'راه‌اندازی احراز هویت دو مرحله‌ای با موفقیت آغاز شد', - TWO_FACTOR_ENABLED_SUCCESSFULLY = 'احراز هویت دو مرحله‌ای با موفقیت فعال شد', - TWO_FACTOR_DISABLED_SUCCESSFULLY = 'احراز هویت دو مرحله‌ای با موفقیت غیرفعال شد', - TWO_FACTOR_SETUP_FAILED = 'راه‌اندازی احراز هویت دو مرحله‌ای ناموفق بود', - TWO_FACTOR_ENABLE_FAILED = 'فعال‌سازی احراز هویت دو مرحله‌ای ناموفق بود', - TWO_FACTOR_TOKEN_VERIFIED_SUCCESSFULLY = 'کد احراز هویت دو مرحله‌ای با موفقیت تأیید شد', - TWO_FACTOR_TOKEN_VERIFIED_FAILED = 'کد احراز هویت دو مرحله‌ای نامعتبر می‌باشد', - TWO_FACTOR_NOT_ENABLED = 'احراز هویت دو مرحله‌ای فعال نیست', - TWO_FACTOR_BACKUP_CODES_GENERATED_SUCCESSFULLY = 'کدهای پشتیبان با موفقیت تولید شدند', - TWO_FACTOR_BACKUP_CODE_VERIFIED_SUCCESSFULLY = 'کد پشتیبان با موفقیت تأیید شد', - TWO_FACTOR_BACKUP_CODE_VERIFIED_FAILED = 'کد پشتیبان نامعتبر است', - TWO_FACTOR_TOKEN_REQUIRED = 'کد احراز هویت دو مرحله‌ای الزامی است', - TWO_FACTOR_TOKEN_INVALID = 'کد احراز هویت دو مرحله‌ای نامعتبر است', -} - -export const enum CouponMessage { - NOT_FOUND = 'کوپن یافت نشد', - CODE_ALREADY_EXISTS = 'کد کوپن قبلاً ثبت شده است', - END_DATE_MUST_BE_AFTER_START_DATE = 'تاریخ پایان باید بعد از تاریخ شروع باشد', - PERCENTAGE_MUST_BE_BETWEEN_0_AND_100 = 'درصد تخفیف باید بین ۰ تا ۱۰۰ باشد', - COUPON_INACTIVE = 'کوپن غیرفعال است', - COUPON_NOT_STARTED = 'کوپن هنوز شروع نشده است', - COUPON_EXPIRED = 'کوپن منقضی شده است', - COUPON_MAX_USES_REACHED = 'حداکثر استفاده از کوپن به پایان رسیده است', - MIN_ORDER_AMOUNT_NOT_MET = 'مبلغ سفارش کمتر از حداقل مجاز است', - COUPON_CREATED = 'کوپن با موفقیت ایجاد شد', - COUPON_UPDATED = 'کوپن با موفقیت به‌روزرسانی شد', - COUPON_DELETED = 'کوپن با موفقیت حذف شد', - COUPON_VALID = 'کوپن معتبر است', - COUPON_INVALID = 'کوپن نامعتبر است', - COUPON_RESTRICTED_TO_USER = 'کوپن فقط برای کاربر مشخصی معتبر است', -} + export const enum ReviewMessage { NOT_FOUND = 'نظر یافت نشد', @@ -660,13 +304,7 @@ export const enum ReviewMessage { COMMENT_UPDATED = 'نظر با موفقیت به‌روزرسانی شد', COMMENT_DELETED = 'نظر با موفقیت حذف شد', } - -export const enum IconMessage { - NOT_FOUND = 'آیکون یافت نشد', - NOT_CREATED = 'ایجاد آیکون با خطا مواجه شد', - NOT_UPDATED = 'به‌روزرسانی آیکون با خطا مواجه شد', - NOT_DELETED = 'حذف آیکون با خطا مواجه شد', -} + export const enum GroupMessage { NOT_FOUND = 'گروه آیکون یافت نشد', @@ -695,38 +333,7 @@ export const enum OrderMessage { product_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد', } -export const enum CartMessage { - TABLE_NUMBER_REQUIRED = 'شماره میز برای سفارش در محل الزامی است', - CAR_ADDRESS_REQUIRED = 'آدرس خودرو برای تحویل به خودرو الزامی است', - ADDRESS_REQUIRED = 'آدرس برای تحویل پیک الزامی است', - NOT_FOUND = 'سبد خرید یافت نشد', - COUPON_NOT_FOUND = 'کوپن یافت نشد', - product_NOT_FOUND = 'غذا یافت نشد', - product_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد', - product_NO_INVENTORY = 'غذا موجودی ندارد', - INSUFFICIENT_STOCK = 'موجودی کافی نیست', - USER_NOT_FOUND = 'کاربر یافت نشد', - ADDRESS_NOT_FOUND = 'آدرس یافت نشد', - RESTAURANT_NOT_FOUND = 'رستوران یافت نشد', - ADDRESS_NOT_BELONGS_TO_USER = 'آدرس به کاربر فعلی تعلق ندارد', - ADDRESS_NO_COORDINATES = 'آدرس مختصات جغرافیایی ندارد', - ADDRESS_OUTSIDE_SERVICE_AREA = 'آدرس خارج از محدوده سرویس رستوران است', - DELIVERY_METHOD_NOT_FOUND = 'روش ارسال یافت نشد', - DELIVERY_METHOD_NOT_ENABLED = 'روش ارسال برای این رستوران فعال نیست', - PAYMENT_METHOD_NOT_FOUND = 'روش پرداخت یافت نشد', - PAYMENT_METHOD_NOT_ENABLED = 'روش پرداخت برای این رستوران فعال نیست', - WALLET_NOT_FOUND = 'کیف پول کاربر یافت نشد', - WALLET_INSUFFICIENT = 'موجودی کیف پول کافی نیست', - COUPON_MAX_USES_REACHED = 'حداکثر استفاده از کوپن به پایان رسیده است', - ITEM_NOT_FOUND = 'آیتم در سبد خرید یافت نشد', - DELIVERY_METHOD_NOT_FOUND_FOR_RESTAURANT = 'روش ارسال برای این رستوران یافت نشد', - COUPON_CANNOT_BE_APPLIED = 'این کوپن قابل اعمال بر روی آیتم‌های سبد خرید شما نیست. لطفا آیتم‌هایی از دسته‌بندی‌ها یا غذاهای مشخص شده اضافه کنید', - productS_MUST_HAVE_PICKUP_SERVE_FOR_COURIER = 'تمام غذاها باید قابلیت تحویل پیک داشته باشند. غذاهای زیر این قابلیت را ندارند: [productTitles]', - product_ONLY_AVAILABLE_DURING_MEAL_TIMES = 'غذا [productTitle] فقط در ساعات وعده‌های غذایی (صبحانه، ناهار، عصرانه یا شام) در دسترس است. زمان فعلی خارج از ساعات وعده‌های غذایی است', - product_ONLY_AVAILABLE_FOR_MEAL_TYPES = 'غذا [productTitle] فقط برای [allowedMealTypes] در دسترس است. زمان فعلی [mealType] است که با این غذا سازگار نیست', - product_ONLY_AVAILABLE_ON_DAYS = 'غذا [productTitle] فقط در روزهای [allowedDays] در دسترس است. امروز [currentDay] است که این غذا در دسترس نیست', -} - + export const enum PaymentMessage { UNSUPPORTED_PAYMENT_METHOD = 'روش پرداخت پشتیبانی نمی‌شود', ORDER_NOT_FOUND = 'سفارش یافت نشد', @@ -748,33 +355,10 @@ export const enum PaymentMessage { GATEWAY_ERROR = 'خطا در اتصال به درگاه پرداخت', } -export const enum DeliveryMessage { - RESTAURANT_NOT_FOUND = 'رستوران یافت نشد', - DELIVERY_METHOD_NOT_FOUND = 'روش ارسال یافت نشد', -} - -export const enum InventoryMessage { - AVAILABLE_STOCK_EXCEEDS_TOTAL = 'موجودی موجود نمی‌تواند از موجودی کل بیشتر باشد', - product_NOT_FOUND = 'غذا یافت نشد', - product_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد', - RESTAURANT_NOT_FOUND = 'رستوران یافت نشد', - productS_NOT_FOUND = 'غذاها یافت نشدند', -} - - -export const enum PagerMessage { - CREATED_SUCCESS = 'درخواست پیج با موفقیت ایجاد شد', -} + export const enum UserSuccessMessage { ADDRESS_DELETED_SUCCESS = 'آدرس با موفقیت حذف شد', SCORE_CONVERTED_SUCCESS = 'امتیاز با موفقیت به کیف پول تبدیل شد', } - -export const enum ContactMessage { - NOT_FOUND = 'تماس یافت نشد', - CREATED = 'تماس با موفقیت ثبت شد', - UPDATED = 'تماس با موفقیت به‌روزرسانی شد', - DELETED = 'تماس با موفقیت حذف شد', -} - + diff --git a/src/modules/admin/controllers/admin.controller.ts b/src/modules/admin/controllers/admin.controller.ts index 3bea57b..7b21dc2 100644 --- a/src/modules/admin/controllers/admin.controller.ts +++ b/src/modules/admin/controllers/admin.controller.ts @@ -16,13 +16,13 @@ import { CreateMyRestaurantAdminDto } from '../dto/create-my-restaurant-admin.dt @ApiTags('admin') @Controller() export class AdminController { - constructor(private readonly adminService: AdminService) {} + constructor(private readonly adminService: AdminService) { } @Get('admin/admins') @UseGuards(AdminAuthGuard) @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'admin' }) - async getAll(@RestId() restId: string) { + async getAll() { const admin = await this.adminService.findAllByRestaurantId(restId); return admin; } @@ -31,7 +31,7 @@ export class AdminController { @UseGuards(AdminAuthGuard) @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'admin' }) - async getMe(@AdminId() adminId: string, @RestId() restId: string) { + async getMe(@AdminId() adminId: string,) { const admin = await this.adminService.findById(adminId, restId); return admin; } @@ -42,7 +42,7 @@ export class AdminController { @HttpCode(HttpStatus.CREATED) @ApiOperation({ summary: 'Create a new admin' }) @ApiBody({ type: CreateAdminDto }) - async create(@Body() dto: CreateAdminDto, @RestId() restId: string) { + async create(@Body() dto: CreateAdminDto,) { const admin = await this.adminService.createAdminForMyRestaurant(restId, dto); return admin; } @@ -53,7 +53,7 @@ export class AdminController { @ApiOperation({ summary: 'Update an admin' }) @ApiParam({ name: 'adminId', description: 'Admin ID' }) @ApiBody({ type: UpdateAdminDto }) - update(@Param('adminId') adminId: string, @Body() dto: UpdateAdminDto, @RestId() restId: string): Promise { + update(@Param('adminId') adminId: string, @Body() dto: UpdateAdminDto,): Promise { return this.adminService.update(adminId, restId, dto); } @@ -62,7 +62,7 @@ export class AdminController { @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'Get an admin by ID' }) @ApiParam({ name: 'id', description: 'Admin ID' }) - getById(@Param('adminId') adminId: string, @RestId() restId: string): Promise { + getById(@Param('adminId') adminId: string,): Promise { return this.adminService.findById(adminId, restId); } @@ -71,7 +71,7 @@ export class AdminController { @Permissions(Permission.MANAGE_ADMINS) @ApiOperation({ summary: 'Delete an admin by ID' }) @ApiParam({ name: 'id', description: 'Admin ID' }) - deleteById(@Param('adminId') adminId: string, @RestId() restId: string): Promise { + deleteById(@Param('adminId') adminId: string,): Promise { return this.adminService.remove(adminId, restId); } diff --git a/src/modules/auth/guards/adminAuth.guard.ts b/src/modules/auth/guards/adminAuth.guard.ts index 1683ea9..3103d64 100644 --- a/src/modules/auth/guards/adminAuth.guard.ts +++ b/src/modules/auth/guards/adminAuth.guard.ts @@ -17,7 +17,6 @@ import { PermissionsService } from 'src/modules/roles/providers/permissions.serv export interface AdminAuthRequest extends Request { adminId: string; - restId: string; } @Injectable() @@ -61,7 +60,7 @@ export class AdminAuthGuard implements CanActivate { } request['adminId'] = payload.adminId; - request['restId'] = payload.restId; + // check if the user has the required permissions const requiredPermissions = diff --git a/src/modules/auth/guards/auth.guard.ts b/src/modules/auth/guards/auth.guard.ts index c600f6d..15686de 100644 --- a/src/modules/auth/guards/auth.guard.ts +++ b/src/modules/auth/guards/auth.guard.ts @@ -6,7 +6,6 @@ import { ITokenPayload } from '../interfaces/IToken-payload'; export interface UserAuthRequest extends Request { userId: string; - restId: string; } @Injectable() @@ -26,10 +25,7 @@ export class AuthGuard implements CanActivate { try { const secret = this.configService.getOrThrow('JWT_SECRET'); const token = this.extractTokenFromHeader(request); - const slug = this.extractSlugFromHeader(request); - if (!slug) { - throw new UnauthorizedException('Slug is required'); - } + if (!token) { throw new UnauthorizedException(); } @@ -42,11 +38,9 @@ export class AuthGuard implements CanActivate { this.logger.error('Invalid token payload structure', payload); throw new UnauthorizedException('Invalid token payload'); } - if (payload.slug !== slug) { - throw new UnauthorizedException('Invalid slug'); - } + request['userId'] = payload.userId; - request['restId'] = payload.restId; + } catch { throw new UnauthorizedException(); } @@ -64,9 +58,5 @@ export class AuthGuard implements CanActivate { const [type, token] = request.headers.authorization?.split(' ') ?? []; return type === 'Bearer' ? token : undefined; } - private extractSlugFromHeader(request: Request): string | undefined { - // Fastify normalizes headers to lowercase, so check both cases - const slug = (request.headers['x-slug'] || request.headers['X-Slug']) as string; - return slug; - } + } diff --git a/src/modules/auth/guards/optinalAuth.guard.ts b/src/modules/auth/guards/optinalAuth.guard.ts deleted file mode 100644 index 2cb712b..0000000 --- a/src/modules/auth/guards/optinalAuth.guard.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable, Inject, Logger } from '@nestjs/common'; -import { Request } from 'express'; -import { JwtService } from '@nestjs/jwt'; -import { ConfigService } from '@nestjs/config'; -import { ITokenPayload } from '../interfaces/IToken-payload'; - -export interface UserOptionalAuthRequest extends Request { - userId?: string; - restId?: string; - slug?: string; -} - -@Injectable() -export class OptionalAuthGuard implements CanActivate { - private readonly logger = new Logger(OptionalAuthGuard.name); - - constructor( - @Inject(JwtService) - private readonly jwtService: JwtService, - @Inject(ConfigService) - private readonly configService: ConfigService, - ) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const token = this.extractTokenFromHeader(request); - const slug = this.extractSlugFromHeader(request); - - // If no token is provided, allow the request without authentication - if (!slug) { - console.log('No slug provided'); - this.logger.warn('No slug provided'); - return false; - } - if (!token) { - request['slug'] = slug; - return true; - } - - // Try to verify the token if it exists - try { - const secret = this.configService.getOrThrow('JWT_SECRET'); - const payload = await this.jwtService.verifyAsync(token, { - secret, - }); - - // Validate payload structure - if (!payload.userId || !payload.restId) { - this.logger.warn('Invalid token payload structure', payload); - // Allow request but don't set user info - request['slug'] = slug; - return true; - } - - // Validate slug if provided - if (slug && payload.slug !== slug) { - this.logger.warn('Token slug does not match header slug', { - tokenSlug: payload.slug, - headerSlug: slug, - }); - // Allow request but don't set user info - request['slug'] = slug; - return true; - } - - // Token is valid, set user info - request['userId'] = payload.userId; - request['restId'] = payload.restId; - request['slug'] = slug; - return true; - } catch (err) { - // Token is invalid or expired, but allow the request anyway - this.logger.debug('Token verification failed in OptionalAuthGuard', { - error: err instanceof Error ? err.message : 'Unknown error', - }); - // Set restId from slug - - request['slug'] = slug; - - return true; - } - } - - private extractTokenFromHeader(request: Request): string | undefined { - const [type, token] = request.headers.authorization?.split(' ') ?? []; - return type === 'Bearer' ? token : undefined; - } - - private extractSlugFromHeader(request: Request): string | undefined { - // Fastify normalizes headers to lowercase, so check both cases - const slug = (request.headers['x-slug'] || request.headers['X-Slug']) as string; - return slug; - } -} diff --git a/src/modules/auth/guards/superAdminAuth.guard.ts b/src/modules/auth/guards/superAdminAuth.guard.ts deleted file mode 100644 index 8fc4ba3..0000000 --- a/src/modules/auth/guards/superAdminAuth.guard.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, Inject, Logger } from '@nestjs/common'; -import { Request } from 'express'; -import { ConfigService } from '@nestjs/config'; - -@Injectable() -export class SuperAdminAuthGuard implements CanActivate { - private readonly logger = new Logger(SuperAdminAuthGuard.name); - - constructor( - @Inject(ConfigService) - private readonly configService: ConfigService, - ) {} - - canActivate(context: ExecutionContext): boolean { - const request = context.switchToHttp().getRequest(); - - try { - const credentials = this.extractBasicAuthCredentials(request); - if (!credentials) { - throw new UnauthorizedException('Basic authentication required'); - } - - const { username, password } = credentials; - const expectedUsername = this.configService.get('SUPER_ADMIN_USERNAME') ?? 'danak@dsc.com'; - const expectedPassword = this.configService.get('SUPER_ADMIN_PASSWORD') ?? 'DsCdAnAk?@ABC'; - if (username !== expectedUsername || password !== expectedPassword) { - this.logger.warn(`Invalid super admin credentials attempt for username: ${username}`); - throw new UnauthorizedException('Invalid credentials'); - } - - return true; - } catch (err) { - if (err instanceof UnauthorizedException) { - throw err; - } - this.logger.error('error in SuperAdminAuthGuard', err); - throw new UnauthorizedException('Authentication failed'); - } - } - - private extractBasicAuthCredentials(request: Request): { username: string; password: string } | null { - const authHeader = request.headers.authorization; - if (!authHeader) { - return null; - } - - const [type, encoded] = authHeader.split(' '); - if (type?.toLowerCase() !== 'basic' || !encoded) { - return null; - } - - try { - const decoded = Buffer.from(encoded, 'base64').toString('utf-8'); - const [username, password] = decoded.split(':'); - - if (!username || !password) { - return null; - } - - return { username, password }; - } catch (error) { - this.logger.error('Failed to decode Basic Auth credentials', error); - return null; - } - } -} diff --git a/src/modules/notification/controllers/notifications.controller.ts b/src/modules/notification/controllers/notifications.controller.ts index 71e5335..6567075 100644 --- a/src/modules/notification/controllers/notifications.controller.ts +++ b/src/modules/notification/controllers/notifications.controller.ts @@ -27,7 +27,7 @@ export class NotificationsController { @ApiBearerAuth() @Get('public/notifications') @ApiOperation({ summary: 'Get user restaurant notifications' }) - @ApiHeader(API_HEADER_SLUG) + @ApiQuery({ name: 'limit', required: false, type: Number, description: 'Number of items to return (default: 50)' }) @ApiQuery({ name: 'cursor', @@ -56,7 +56,7 @@ export class NotificationsController { @ApiBearerAuth() @Get('public/notifications/unseen-count') @ApiOperation({ summary: 'Get unseen notifications count for user' }) - @ApiHeader(API_HEADER_SLUG) + async getUserUnseenCount(@UserId() userId: string, @RestId() restaurantId: string) { const count = await this.notificationService.countUnseenByUserAndRestaurant(userId, restaurantId); return { count }; diff --git a/src/modules/order/controllers/orders.controller.ts b/src/modules/order/controllers/orders.controller.ts index 3fb2f72..0358564 100644 --- a/src/modules/order/controllers/orders.controller.ts +++ b/src/modules/order/controllers/orders.controller.ts @@ -20,7 +20,7 @@ export class OrdersController { @UseGuards(AuthGuard) @Post('public/checkout') - @ApiHeader(API_HEADER_SLUG) + @ApiOperation({ summary: 'Checkout : create order and payment record' }) checkout(@UserId() userId: string, @RestId() restaurantId: string) { return this.ordersService.checkout(userId, restaurantId); @@ -28,18 +28,18 @@ export class OrdersController { @UseGuards(AuthGuard) @Get('public/orders') - @ApiHeader(API_HEADER_SLUG) + @ApiOperation({ summary: 'Get all orders with pagination and filters' }) - findAll(@RestId() restId: string, @Query() dto: FindOrdersDto, @UserId() userId: string) { + findAll(, @Query() dto: FindOrdersDto, @UserId() userId: string) { return this.ordersService.findAllForUser(restId, dto, userId); } @UseGuards(AuthGuard) @ApiOperation({ summary: 'Get an order By id for User' }) @ApiParam({ name: 'orderId', description: 'Order ID' }) - @ApiHeader(API_HEADER_SLUG) + @Get('public/orders/:orderId') - findOne(@Param('orderId') orderId: string, @RestId() restId: string) { + findOne(@Param('orderId') orderId: string,) { return this.ordersService.findOne(orderId, restId); } @@ -50,7 +50,7 @@ export class OrdersController { description: 'Order status', enum: OrderStatus, }) - @ApiHeader(API_HEADER_SLUG) + @ApiBody({ type: UpdateOrderStatusDto }) @ApiOperation({ summary: 'Update status of an order By User' }) @ApiParam({ name: 'id', description: 'Order ID' }) @@ -58,7 +58,7 @@ export class OrdersController { @Body() dto: UpdateOrderStatusDto, @Param('id') orderId: string, @Param('status') status: OrderStatus, - @RestId() restId: string, + , ) { return this.ordersService.changeOrderStatus(orderId, restId, status, 'user', dto?.desc); } @@ -68,7 +68,7 @@ export class OrdersController { @Permissions(Permission.MANAGE_ORDERS) @Get('admin/orders') @ApiOperation({ summary: 'Get all orders with pagination and filters' }) - findAllAdmin(@RestId() restId: string, @Query() dto: FindOrdersDto) { + findAllAdmin(, @Query() dto: FindOrdersDto) { return this.ordersService.findAllForAdmin(restId, dto); } @@ -77,7 +77,7 @@ export class OrdersController { @ApiOperation({ summary: 'Get an order By id for User' }) @ApiParam({ name: 'orderId', description: 'Order ID' }) @Get('admin/orders/:orderId') - findOneAsAdmin(@Param('orderId') orderId: string, @RestId() restId: string) { + findOneAsAdmin(@Param('orderId') orderId: string,) { return this.ordersService.findOne(orderId, restId); } @@ -96,7 +96,7 @@ export class OrdersController { @Param('orderId') orderId: string, @Body() dto: UpdateOrderStatusDto, @Param('status') status: OrderStatus, - @RestId() restId: string, + , ) { return this.ordersService.changeOrderStatus(orderId, restId, status, 'admin', dto?.desc); } @@ -105,16 +105,16 @@ export class OrdersController { @Permissions(Permission.VIEW_REPORTS) @ApiOperation({ summary: 'Get Stats for report page' }) @Get('admin/orders/stats') - findStats(@RestId() restId: string) { + findStats() { return this.ordersService.getStats(restId); } @UseGuards(AdminAuthGuard) @Permissions(Permission.VIEW_REPORTS) @ApiOperation({ summary: 'Get product sales pie chart data for last month' }) - @ApiHeader(API_HEADER_SLUG) + @Get('admin/orders/product-sales-pie-chart') - getproductSalesPieChart(@RestId() restId: string) { + getproductSalesPieChart() { return this.ordersService.getproductSalesPieChart(restId); } } diff --git a/src/modules/order/entities/order-item.entity.ts b/src/modules/order/entities/order-item.entity.ts index 34ca225..a52b8c6 100644 --- a/src/modules/order/entities/order-item.entity.ts +++ b/src/modules/order/entities/order-item.entity.ts @@ -1,7 +1,7 @@ import { Entity, Enum, Index, ManyToOne, Property } from '@mikro-orm/core'; import { BaseEntity } from '../../../common/entities/base.entity'; import { Order } from './order.entity'; -import { product } from 'src/modules/product/entities/product.entity'; +import { Product } from 'src/modules/product/entities/product.entity'; import { OrderItemStatus } from '../interface/order.interface'; @Entity({ tableName: 'order_items' }) @@ -11,8 +11,8 @@ export class OrderItem extends BaseEntity { @ManyToOne(() => Order) order!: Order; - @ManyToOne(() => product) - product!: product; + @ManyToOne(() => Product) + product!: Product; @Property({ type: 'int' }) quantity!: number; diff --git a/src/modules/payment/controllers/payments.controller.ts b/src/modules/payment/controllers/payments.controller.ts index a9d208e..062cf97 100644 --- a/src/modules/payment/controllers/payments.controller.ts +++ b/src/modules/payment/controllers/payments.controller.ts @@ -33,9 +33,9 @@ export class PaymentsController { @ApiBearerAuth() @Get('public/payments/methods/restaurant') @ApiOperation({ summary: 'Get the restaurant payment methods' }) - @ApiHeader(API_HEADER_SLUG) + @ApiNotFoundResponse({ description: 'Restaurant payment methods not found' }) - getTheRestaurantPaymentMethods(@RestId() restId: string) { + getTheRestaurantPaymentMethods() { return this.paymentMethodService.findByRestaurant(restId); } @@ -43,8 +43,8 @@ export class PaymentsController { @ApiBearerAuth() @Get('public/payments') @ApiOperation({ summary: 'Get all the restaurant payments for user' }) - @ApiHeader(API_HEADER_SLUG) - findAllByRestaurant(@UserId() userId: string, @RestId() restId: string) { + + findAllByRestaurant(@UserId() userId: string,) { return this.paymentsService.findAllPaymentsByRestaurantId(restId, userId); } @@ -53,14 +53,14 @@ export class PaymentsController { @Get('public/payments/pay-order/:orderId') @ApiOperation({ summary: 'Pay for an order' }) @ApiParam({ name: 'orderId', type: 'string', description: 'Order ID' }) - @ApiHeader(API_HEADER_SLUG) + payAnOrder(@Param('orderId') orderId: string) { return this.paymentsService.payOrder(orderId); } @Post('public/payments/verify') @ApiOperation({ summary: 'Verify a payment' }) - @ApiHeader(API_HEADER_SLUG) + @ApiBody({ type: VerifyPaymentDto }) @ApiNotFoundResponse({ description: 'Payment not found' }) verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) { @@ -73,7 +73,7 @@ export class PaymentsController { @ApiBearerAuth() @Get('admin/payments/methods') @ApiOperation({ summary: 'Get restaurant all payment methods' }) - findByRestaurant(@RestId() restId: string) { + findByRestaurant() { return this.paymentMethodService.findByRestaurant(restId); } @@ -83,7 +83,7 @@ export class PaymentsController { @Post('admin/payments/methods') @ApiOperation({ summary: 'Create a new restaurant payment method' }) @ApiBody({ type: CreatePaymentMethodDto }) - createRestaurantPaymentMethod(@RestId() restId: string, @Body() createPaymentMethodDto: CreatePaymentMethodDto) { + createRestaurantPaymentMethod(, @Body() createPaymentMethodDto: CreatePaymentMethodDto) { return this.paymentMethodService.create(restId, createPaymentMethodDto); } @@ -136,8 +136,8 @@ export class PaymentsController { @ApiBearerAuth() @Get('admin/payments/chart') @ApiOperation({ summary: 'Get payment chart data with date and period filters' }) - @ApiHeader(API_HEADER_SLUG) - getPaymentChart(@Query() query: PaymentChartDto, @RestId() restId: string) { + + getPaymentChart(@Query() query: PaymentChartDto,) { return this.paymentsService.getChartData(query, restId); } } diff --git a/src/modules/product/controllers/product.controller.ts b/src/modules/product/controllers/product.controller.ts index 53a6d87..6cd447f 100644 --- a/src/modules/product/controllers/product.controller.ts +++ b/src/modules/product/controllers/product.controller.ts @@ -6,9 +6,6 @@ import { FindproductsDto } from '../dto/find-products.dto'; import { ApiTags, ApiOperation, - ApiCreatedResponse, - ApiOkResponse, - ApiNotFoundResponse, ApiQuery, ApiBody, ApiParam, @@ -16,10 +13,8 @@ import { ApiHeader, } from '@nestjs/swagger'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; -import { RestId, UserId } from 'src/common/decorators'; +import { UserId } from 'src/common/decorators'; import { AuthGuard } from 'src/modules/auth/guards/auth.guard'; -import { OptionalAuthGuard } from 'src/modules/auth/guards/optinalAuth.guard'; -import { API_HEADER_SLUG } from 'src/common/constants'; import { Permission } from 'src/common/enums/permission.enum'; import { Permissions } from 'src/common/decorators/permissions.decorator'; @@ -28,53 +23,27 @@ import { Permissions } from 'src/common/decorators/permissions.decorator'; export class productController { constructor(private readonly productsService: productService) { } - @Get('public/products/restaurant/:slug') - @ApiOperation({ summary: 'Get all products by restaurant slug' }) - @ApiHeader(API_HEADER_SLUG) - @ApiParam({ name: 'slug', required: true, description: 'Restaurant Slug' }) - findAllByRestaurant(@Param('slug') slug: string) { - return this.productsService.findByWeekDateAndMealType(slug); - } - @Get('public/products/:productId') - @UseGuards(OptionalAuthGuard) - @ApiHeader(API_HEADER_SLUG) - @ApiBearerAuth() - @ApiOperation({ summary: 'Get a product by id' }) - @ApiParam({ name: 'productId', required: true }) - findPublicproductById(@Param('productId') productId: string, @UserId() userId?: string): Promise { - const a = this.productsService.findPublicById(productId, userId); - return a; - } + // @Get('public/products/:productId') + // @UseGuards() + // @ApiBearerAuth() + // @ApiOperation({ summary: 'Get a product by id' }) + // @ApiParam({ name: 'productId', required: true }) + // findPublicproductById(@Param('productId') productId: string, @UserId() userId?: string): Promise { + // const a = this.productsService.findPublicById(productId, userId); + // return a; + // } - @Post('public/products/favorite/:productId') - @UseGuards(AuthGuard) - @ApiHeader(API_HEADER_SLUG) - @ApiBearerAuth() - @ApiOperation({ summary: 'toggle a product as favorite' }) - @ApiParam({ name: 'productId', required: true }) - setAsFavorute(@Param('productId') productId: string, @UserId() userId: string) { - return this.productsService.toggleFavorite(userId, productId); - } - - @Get('public/products/favorite') - @UseGuards(AuthGuard) - @ApiHeader(API_HEADER_SLUG) - @ApiBearerAuth() - @ApiOperation({ summary: 'get my favorites' }) - getMyFavorites(@UserId() userId: string, @RestId() restId: string) { - return this.productsService.getMyFavorites(userId, restId); - } /* ---------------------------------- Admin ---------------------------------- */ @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Permissions(Permission.MANAGE_productS) - @Post('admin/products') + @Post('admin/product') @ApiOperation({ summary: 'Create a new product' }) @ApiBody({ type: CreateproductDto }) - create(@Body() createproductDto: CreateproductDto, @RestId() restId: string) { - return this.productsService.create(restId, createproductDto); + create(@Body() createproductDto: CreateproductDto) { + return this.productsService.create(createproductDto); } @UseGuards(AdminAuthGuard) @@ -89,19 +58,19 @@ export class productController { @ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] }) @ApiQuery({ name: 'categoryId', required: false, type: String }) @ApiQuery({ name: 'isActive', required: false, type: Boolean }) - async findAll(@Query() dto: FindproductsDto, @RestId() restId: string) { - const result = await this.productsService.findAll(restId, dto); + async findAll(@Query() dto: FindproductsDto) { + const result = await this.productsService.findAll(dto); return result; } + @Get('admin/products/:id') @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Permissions(Permission.MANAGE_productS) - @Get('admin/products/:id') @ApiOperation({ summary: 'Get a product by id' }) @ApiParam({ name: 'id', required: true }) - findById(@Param('id') id: string, @RestId() restId: string) { - return this.productsService.findAdminById(restId, id); + findById(@Param('id') id: string,) { + return this.productsService.findById(id); } @UseGuards(AdminAuthGuard) @@ -111,8 +80,8 @@ export class productController { @ApiOperation({ summary: 'Update a product' }) @ApiParam({ name: 'id', required: true }) @ApiBody({ type: UpdateproductDto }) - update(@Param('id') id: string, @Body() updateproductDto: UpdateproductDto, @RestId() restId: string) { - return this.productsService.update(restId, id, updateproductDto); + update(@Param('id') id: string, @Body() updateproductDto: UpdateproductDto) { + return this.productsService.update(id, updateproductDto); } @UseGuards(AdminAuthGuard) @ApiBearerAuth() @@ -120,7 +89,7 @@ export class productController { @Delete('admin/products/:id') @ApiOperation({ summary: 'Delete (soft) a product' }) @ApiParam({ name: 'id', required: true }) - remove(@Param('id') id: string, @RestId() restId: string) { - return this.productsService.remove(restId, id); + remove(@Param('id') id: string,) { + return this.productsService.remove(id); } } diff --git a/src/modules/product/dto/create-product.dto.ts b/src/modules/product/dto/create-product.dto.ts index 3517fa3..2346323 100644 --- a/src/modules/product/dto/create-product.dto.ts +++ b/src/modules/product/dto/create-product.dto.ts @@ -1,19 +1,14 @@ import { Type } from 'class-transformer'; import { - ArrayUnique, IsArray, IsBoolean, - IsEnum, IsInt, IsNotEmpty, - IsNumber, IsOptional, IsString, - Max, Min, } from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { MealType } from '../interface/product.interface'; export class CreateproductDto { @IsNotEmpty() @@ -23,43 +18,14 @@ export class CreateproductDto { @IsOptional() @IsString() - @ApiPropertyOptional({ example: 'قرمه سبزی' }) - title?: string; + @ApiPropertyOptional({ example: 'کارت ویزیت' }) + title: string; - @IsOptional() - @IsString() - @ApiPropertyOptional({ example: 'توضیحات غذا' }) - desc?: string; - - @IsOptional() @IsArray() @IsString({ each: true }) @ApiPropertyOptional({ type: [String] }) - content?: string[]; + desc: string[]; - @IsOptional() - @IsArray() - @ArrayUnique() - @IsInt({ each: true }) - @Min(0, { each: true }) - @Max(6, { each: true }) - @Type(() => Number) - @ApiPropertyOptional({ type: [Number], example: [0, 1, 2, 3, 4, 5, 6] }) - weekDays?: number[]; - - @IsOptional() - @IsArray() - @ArrayUnique() - @IsEnum(MealType, { each: true }) - @ApiPropertyOptional({ enum: MealType, isArray: true }) - mealTypes?: MealType[]; - - @IsOptional() - @IsNumber() - @Min(0) - @Type(() => Number) - @ApiPropertyOptional({ example: 120000 }) - price?: number; @IsOptional() @IsInt() @@ -80,37 +46,6 @@ export class CreateproductDto { @ApiPropertyOptional({ type: [String] }) images?: string[]; - @IsOptional() - @IsBoolean() - @ApiPropertyOptional({ example: false }) - @Type(() => Boolean) - inPlaceServe?: boolean; - - @IsOptional() - @IsBoolean() - @ApiPropertyOptional({ example: false }) - @Type(() => Boolean) - pickupServe?: boolean; - - @IsOptional() - @IsNumber() - @Min(0) - @Type(() => Number) - @ApiPropertyOptional({ example: 0 }) - discount?: number; - - @IsOptional() - @IsNumber() - @Min(0) - @Type(() => Number) - @ApiProperty({ example: 50 }) - dailyStock: number; - - @IsOptional() - @IsBoolean() - @ApiPropertyOptional({ example: false }) - @Type(() => Boolean) - isSpecialOffer?: boolean; @IsOptional() @IsInt() diff --git a/src/modules/product/entities/attribute-value.entity.ts b/src/modules/product/entities/attribute-value.entity.ts index 99a8124..ac3ad14 100644 --- a/src/modules/product/entities/attribute-value.entity.ts +++ b/src/modules/product/entities/attribute-value.entity.ts @@ -1,6 +1,6 @@ import { Entity, Property, ManyToOne } from '@mikro-orm/core'; import { BaseEntity } from '../../../common/entities/base.entity'; -import { Attribute } from './attribute.entity'; +// import { Attribute } from './attribute.entity'; @Entity({ tableName: 'attribute_values' }) export class AttributeValue extends BaseEntity { @@ -10,13 +10,13 @@ export class AttributeValue extends BaseEntity { @Property() value: string; - @ManyToOne(() => Attribute) - attribute: Attribute; + // @ManyToOne(() => Attribute) + // attribute: Attribute; @Property({ type: 'bigint' }) attributeId: bigint; @Property({ type: 'int', nullable: true }) - sortOrder?: number; + order?: number; } diff --git a/src/modules/product/entities/attribute.entity.ts b/src/modules/product/entities/attribute.entity.ts index 094a941..2bd7431 100644 --- a/src/modules/product/entities/attribute.entity.ts +++ b/src/modules/product/entities/attribute.entity.ts @@ -4,6 +4,9 @@ import { AttributeType } from '../interface/product.interface'; @Entity({ tableName: 'attributes' }) export class Attribute extends BaseEntity { + @Property({ type: 'bigint' }) + productId: string + @Property({ primary: true }) id: bigint; diff --git a/src/modules/product/entities/category.entity.ts b/src/modules/product/entities/category.entity.ts new file mode 100644 index 0000000..2764226 --- /dev/null +++ b/src/modules/product/entities/category.entity.ts @@ -0,0 +1,30 @@ +import { Entity, Index, Property, Collection, OneToMany, ManyToOne } from '@mikro-orm/core'; +import { Product } from './product.entity'; +import { BaseEntity } from '../../../common/entities/base.entity'; + + +@Entity({ tableName: 'categories' }) +@Index({ properties: ['isActive'] }) +export class Category extends BaseEntity { + + @ManyToOne(() => Category) + parent?: Category | null + + @Property({ primary: true }) + id: bigint + + @Property() + title!: string; + + @OneToMany(() => Product, product => product.category) + Products = new Collection(this); + + @Property({ default: true }) + isActive: boolean = true; + + @Property({ nullable: true }) + avatarUrl?: string; + + @Property({ type: 'int', nullable: true }) + order?: number; +} diff --git a/src/modules/product/entities/product.entity.ts b/src/modules/product/entities/product.entity.ts index 1ba716e..ad31e2b 100644 --- a/src/modules/product/entities/product.entity.ts +++ b/src/modules/product/entities/product.entity.ts @@ -1,15 +1,29 @@ -import { Entity, Property } from '@mikro-orm/core'; +import { Collection, Entity, ManyToOne, OneToMany, Property } from '@mikro-orm/core'; import { BaseEntity } from '../../../common/entities/base.entity'; +import { Category } from './category.entity'; +import { Attribute } from './attribute.entity'; @Entity({ tableName: 'products' }) -export class product extends BaseEntity { +export class Product extends BaseEntity { @Property({ primary: true }) id: bigint + @ManyToOne(() => Category) + category: Category + + @OneToMany(() => Attribute, (attr) => attr.productId) + attributes = new Collection(this) + @Property() title: string; + @Property({ type: 'json' }) + desc: string[] + + @Property() + prepareTime: number; + @Property({ type: 'text', nullable: true }) linkUrl?: string; @@ -19,4 +33,7 @@ export class product extends BaseEntity { @Property({ type: 'json', nullable: true }) images?: string[]; + @Property({ type: 'int' }) + order?: number | null + } diff --git a/src/modules/product/product.module.ts b/src/modules/product/product.module.ts index 57adb23..5b90b46 100644 --- a/src/modules/product/product.module.ts +++ b/src/modules/product/product.module.ts @@ -1,27 +1,25 @@ import { Module } from '@nestjs/common'; import { productService } from './providers/product.service'; -import { productStockCrone } from './crone/product.crone'; import { productController } from './controllers/product.controller'; -import { productRepository } from './repositories/product.repository'; +import { ProductRepository } from './repositories/product.repository'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Category } from './entities/category.entity'; -import { product } from './entities/product.entity'; +import { Product } from './entities/product.entity'; import { Attribute } from './entities/attribute.entity'; import { AttributeValue } from './entities/attribute-value.entity'; import { AuthModule } from '../auth/auth.module'; import { JwtModule } from '@nestjs/jwt'; import { UtilsModule } from '../util/utils.module'; -import { Favorite } from './entities/favorite.entity'; @Module({ imports: [ - MikroOrmModule.forFeature([product, Category, Favorite, Attribute, AttributeValue]), + MikroOrmModule.forFeature([Product, Category, Attribute, AttributeValue]), AuthModule, JwtModule, UtilsModule, ], controllers: [productController,], - providers: [productService, productRepository, productStockCrone], - exports: [productRepository,], + providers: [productService, ProductRepository,], + exports: [ProductRepository,], }) export class productModule { } diff --git a/src/modules/product/providers/product.service.ts b/src/modules/product/providers/product.service.ts index 05142c8..a7fe14f 100644 --- a/src/modules/product/providers/product.service.ts +++ b/src/modules/product/providers/product.service.ts @@ -1,264 +1,135 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { CreateproductDto } from '../dto/create-product.dto'; import { FindproductsDto } from '../dto/find-products.dto'; -import { productRepository } from '../repositories/product.repository'; +import { ProductRepository } from '../repositories/product.repository'; import { EntityManager } from '@mikro-orm/postgresql'; -import { RequiredEntityData, FilterQuery } from '@mikro-orm/core'; -import { product } from '../entities/product.entity'; -import { CategoryMessage, productMessage, RestMessage } from 'src/common/enums/message.enum'; -import { CacheService } from '../../util/cache.service'; +import { RequiredEntityData } from '@mikro-orm/core'; +import { Product } from '../entities/product.entity'; +import { CategoryMessage, productMessage } from 'src/common/enums/message.enum'; +import { CategoryRepository } from '../repositories/category.repository'; @Injectable() export class productService { constructor( - private readonly productRepository: productRepository, + private readonly productRepository: ProductRepository, + private readonly categoryRepository: CategoryRepository, private readonly em: EntityManager, - ) { } + ) { } - async create(restId: string, createproductDto: CreateproductDto) { - const { categoryId, dailyStock = 0, ...rest } = createproductDto; - const restaurant = await this.restRepository.findOne({ id: restId }); - if (!restaurant) { - throw new NotFoundException(RestMessage.NOT_FOUND); - } - const category = await this.categoryRepository.findOne({ id: categoryId, restaurant: { id: restId } }); + async create(createproductDto: CreateproductDto) { + const { categoryId, ...rest } = createproductDto; + const category = await this.categoryRepository.findOne({ id: categoryId }); if (!category) { throw new NotFoundException(CategoryMessage.NOT_FOUND); } - const { product, inventory } = await this.em.transactional(async em => { - // prepare data with defaults for required fields so repository.create typing is satisfied - const data: RequiredEntityData = { - desc: rest.desc, - isActive: rest.isActive ?? true, - inPlaceServe: rest.inPlaceServe ?? false, - pickupServe: rest.pickupServe ?? false, - discount: rest.discount ?? 0, - isSpecialOffer: rest.isSpecialOffer ?? false, - order: rest.order ?? null, - // map single-title/content DTO to localized fields - title: rest.title, - content: rest.content, - // numeric/array fields - price: rest.price ?? 0, - prepareTime: rest.prepareTime ?? 0, - images: rest.images ?? undefined, - // new fields - weekDays: rest.weekDays ?? [0, 1, 2, 3, 4, 5, 6], - mealTypes: rest.mealTypes ?? [], - restaurant: restaurant, - category: category, - }; - - const product = em.create(product, data); - const newInventoryRecord = em.create(Inventory, { - product: product, - availableStock: dailyStock, - totalStock: dailyStock, - }); - - await em.flush(); - return { product, inventory: newInventoryRecord }; - }); - - // Re-load created entities with the primary EM to ensure they're attached - const savedproduct = product?.id - ? await this.productRepository.findOne({ id: product.id }, { populate: ['category', 'restaurant'] }) - : null; - const savedInventory = inventory?.id ? await this.em.findOne(Inventory, { id: inventory.id }) : inventory; - - return { product: savedproduct ?? product, inventory: savedInventory ?? inventory }; - } - - findAll(restId: string, dto: FindproductsDto) { - return this.productRepository.findAllPaginated(restId, dto); - } - - /** - * Public product detail (only active products are visible). - */ - async findPublicById(productId: string, userId?: string): Promise { - const product = await this.productRepository.findOne({ id: productId, isActive: true }, { populate: ['category', 'inventory'] }); - if (!product) throw new NotFoundException(productMessage.NOT_FOUND); - let isFavorite = false; - if (userId) { - isFavorite = (await this.em.count(Favorite, { user: { id: userId }, product: { id: productId } })) > 0; - } - return { - ...product, - isFavorite, + const data: RequiredEntityData = { + isActive: rest.isActive ?? true, + order: rest.order ?? null, + title: rest.title, + desc: rest.desc, + prepareTime: rest.prepareTime ?? 0, + images: rest.images ?? undefined, + category, }; + + return this.productRepository.create(data) + } - /** - * Admin product detail (scoped to the authenticated restaurant). - */ - async findAdminById(restId: string, id: string): Promise { - const product = await this.productRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['category', 'inventory'] }); + async update(productId: string, createproductDto: Partial) { + const { categoryId, ...rest } = createproductDto; + const product = await this.productRepository.findOne({ id: productId }) + if (!product) { + throw new NotFoundException(productMessage.NOT_FOUND); + } + + if (categoryId) { + const category = await this.categoryRepository.findOne({ id: categoryId }); + if (!category) { + throw new NotFoundException(CategoryMessage.NOT_FOUND); + } + this.productRepository.assign(product, { category }) + } + + this.productRepository.assign(product, rest) + //TODO : which one od these are correct + // this.productRepository.nativeUpdate(productId,product) + this.em.persistAndFlush(product) + + } + + findAll(dto: FindproductsDto) { + return this.productRepository.findAllPaginated(dto); + } + + async findById(productId: string): Promise { + const product = await this.productRepository.findOne({ id: productId }, { populate: ['category', 'attributes'] }); if (!product) throw new NotFoundException(productMessage.NOT_FOUND); return product; } - /** - * Find active products for a restaurant based on current week day and meal type in Iran timezone. - * @param slug - Restaurant slug identifier - * @returns Array of active products matching current day and meal time - */ - async findByWeekDateAndMealType(slug: string): Promise { - const restaurant = await this.restRepository.findOne({ slug }); - if (!restaurant) { - throw new NotFoundException(RestMessage.NOT_FOUND); - } + // async update( id: string, dto: Partial): Promise { + // const { categoryId, dailyStock, ...rest } = dto; + // const product = await this.productRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['restaurant'] }); + // if (!product) { + // throw new NotFoundException(productMessage.NOT_FOUND); + // } - const { iranWeekDay, mealType } = this.getCurrentIranTimeContext(); + // // attach new categories if provided (adds, does not attempt to preserve/remove existing ones) + // if (categoryId) { + // const category = await this.categoryRepository.findOne({ id: categoryId, restaurant: { id: restId } }); - const queryFilter: FilterQuery = { - restaurant: { slug }, - isActive: true, - // weekDays: { $contains: iranWeekDay }, - // ...(mealType ? { mealTypes: { $contains: mealType } } : {}), - } as unknown as FilterQuery; + // if (!category) { + // throw new NotFoundException(CategoryMessage.NOT_FOUND); + // } - return this.productRepository.find(queryFilter, { populate: ['category'] }); - } + // this.em.assign(product, { category: category }); + // } - /** - * Get current Iran timezone context (weekday and meal type). - * @returns Object containing Iran weekday (0-6, where 0=Saturday) and current meal type - */ - private getCurrentIranTimeContext(): { iranWeekDay: number; mealType: MealType | null } { - const nowInIran = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Tehran' })); - const weekDay = nowInIran.getDay(); // 0 = Sunday, 6 = Saturday - const currentHour = nowInIran.getHours(); + // // assign other fields from DTO (excluding dailyStock handled below) + // this.em.assign(product, rest); - // Convert to Iran weekday: Saturday=0, Sunday=1, ..., Friday=6 - // JavaScript: Sunday=0, Monday=1, ..., Saturday=6 - // Iran week: Saturday=0, Sunday=1, ..., Friday=6 - const iranWeekDay = (weekDay + 1) % 7; + // // Persist product and update/create related inventory atomically + // await this.em.transactional(async em => { + // await em.persistAndFlush(product); - const mealType = this.getMealTypeByHour(currentHour); + // if (typeof dailyStock !== 'undefined') { + // const inventoryRecord = await em.findOne(Inventory, { product: product }); + // if (inventoryRecord) { + // inventoryRecord.totalStock = dailyStock; + // } else { + // em.create(Inventory, { + // product: product, + // availableStock: dailyStock, + // totalStock: dailyStock, + // }); + // } + // } - return { iranWeekDay, mealType }; - } + // await em.flush(); + // }); - /** - * Determine meal type based on current hour in Iran timezone. - * @param hour - Current hour (0-23) - * @returns Meal type or null if outside meal hours - */ - private getMealTypeByHour(hour: number): MealType | null { - const MEAL_TIME_RANGES = { - BREAKFAST: { start: 6, end: 11 }, - LUNCH: { start: 11, end: 15 }, - SNACK: { start: 15, end: 19 }, - DINNER: { start: 19, end: 24 }, - } as const; + // // Re-load the product to ensure populated relations and stable instance + // const savedproduct = await this.productRepository.findOne({ id: product.id }, { populate: ['category', 'restaurant'] }); - if (hour >= MEAL_TIME_RANGES.BREAKFAST.start && hour < MEAL_TIME_RANGES.BREAKFAST.end) { - return MealType.BREAKFAST; - } - if (hour >= MEAL_TIME_RANGES.LUNCH.start && hour < MEAL_TIME_RANGES.LUNCH.end) { - return MealType.LUNCH; - } - if (hour >= MEAL_TIME_RANGES.SNACK.start && hour < MEAL_TIME_RANGES.SNACK.end) { - return MealType.SNACK; - } - if (hour >= MEAL_TIME_RANGES.DINNER.start && hour < MEAL_TIME_RANGES.DINNER.end) { - return MealType.DINNER; - } + // // Invalidate cache for the restaurant if present (optional) + // // await this.invalidateRestaurantproductsCache(savedproduct?.restaurant?.slug); - return null; - } - - async update(restId: string, id: string, dto: Partial): Promise { - const { categoryId, dailyStock, ...rest } = dto; - const product = await this.productRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['restaurant'] }); - if (!product) { - throw new NotFoundException(productMessage.NOT_FOUND); - } - - // attach new categories if provided (adds, does not attempt to preserve/remove existing ones) - if (categoryId) { - const category = await this.categoryRepository.findOne({ id: categoryId, restaurant: { id: restId } }); - - if (!category) { - throw new NotFoundException(CategoryMessage.NOT_FOUND); - } - - this.em.assign(product, { category: category }); - } - - // assign other fields from DTO (excluding dailyStock handled below) - this.em.assign(product, rest); - - // Persist product and update/create related inventory atomically - await this.em.transactional(async em => { - await em.persistAndFlush(product); - - if (typeof dailyStock !== 'undefined') { - const inventoryRecord = await em.findOne(Inventory, { product: product }); - if (inventoryRecord) { - inventoryRecord.totalStock = dailyStock; - } else { - em.create(Inventory, { - product: product, - availableStock: dailyStock, - totalStock: dailyStock, - }); - } - } - - await em.flush(); - }); - - // Re-load the product to ensure populated relations and stable instance - const savedproduct = await this.productRepository.findOne({ id: product.id }, { populate: ['category', 'restaurant'] }); - - // Invalidate cache for the restaurant if present (optional) - // await this.invalidateRestaurantproductsCache(savedproduct?.restaurant?.slug); - - return savedproduct ?? product; - } - - async remove(restId: string, id: string): Promise { - const product = await this.productRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['restaurant'] }); - if (!product) { - throw new NotFoundException(productMessage.NOT_FOUND); - } - - // const restaurantSlug = product.restaurant.slug; - product.deletedAt = new Date(); - await this.em.persistAndFlush(product); - - // Invalidate cache for the restaurant - // await this.invalidateRestaurantproductsCache(restaurantSlug); - } - - - async toggleFavorite(userId: string, productId: string) { - - const favorite = await this.em.findOne(Favorite, { user: userId, product: productId }); - if (favorite) { - return this.em.removeAndFlush(favorite); - } - const newFavorite = this.em.create(Favorite, { - user: userId, - product: productId, - }); - return this.em.persistAndFlush(newFavorite); - } - - - async getMyFavorites(userId: string, restId: string) { - return this.em.find(Favorite, { user: userId, product: { restaurant: { id: restId } } }, { populate: ['product'] }); - } - /** - * Invalidate cache for restaurant products - */ - // private async invalidateRestaurantproductsCache(slug: string): Promise { - // const cacheKey = `${this.productS_BY_RESTAURANT_CACHE_KEY_PREFIX}${slug}`; - // await this.cacheService.del(cacheKey); + // return savedproduct ?? product; // } + + async remove(id: string): Promise { + const product = await this.productRepository.findOne({ id }); + if (!product) { + throw new NotFoundException(productMessage.NOT_FOUND); + } + + product.deletedAt = new Date(); + return await this.em.persistAndFlush(product); + + } + } diff --git a/src/modules/product/repositories/category.repository.ts b/src/modules/product/repositories/category.repository.ts new file mode 100644 index 0000000..7ab8506 --- /dev/null +++ b/src/modules/product/repositories/category.repository.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; +import { FilterQuery } from '@mikro-orm/core'; +import { Product } from '../entities/product.entity'; +import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; +import { Category } from '../entities/category.entity'; + +type FindCategoriesOpts = { + page?: number; + limit?: number; + search?: string; + orderBy?: string; + order?: 'asc' | 'desc'; + isActive?: boolean; +}; + +@Injectable() +export class CategoryRepository extends EntityRepository { + constructor(readonly em: EntityManager) { + super(em, Category); + } + + async findAllPaginated(opts: FindCategoriesOpts = {}): Promise> { + const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc', isActive } = opts; + + const offset = (page - 1) * limit; + + const where: FilterQuery = {}; + + if (typeof isActive === 'boolean') { + where.isActive = isActive; + } + + + const [data, total] = await this.findAndCount(where, { + limit, + offset, + orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, + }); + + const totalPages = Math.ceil(total / limit); + + return { + data, + meta: { + total, + page, + limit, + totalPages, + }, + }; + + + } +} diff --git a/src/modules/product/repositories/product.repository.ts b/src/modules/product/repositories/product.repository.ts index ee34197..6012223 100644 --- a/src/modules/product/repositories/product.repository.ts +++ b/src/modules/product/repositories/product.repository.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; import { FilterQuery } from '@mikro-orm/core'; -import { product } from '../entities/product.entity'; +import { Product } from '../entities/product.entity'; import { PaginatedResult } from 'src/common/interfaces/pagination.interface'; type FindproductsOpts = { @@ -15,20 +15,17 @@ type FindproductsOpts = { }; @Injectable() -export class productRepository extends EntityRepository { +export class ProductRepository extends EntityRepository { constructor(readonly em: EntityManager) { - super(em, product); + super(em, Product); } - /** - * Find products with pagination and optional filters. - * Supports: search (title/content), categoryId, isActive, ordering. - */ - async findAllPaginated(restId: string, opts: FindproductsOpts = {}): Promise> { + + async findAllPaginated(opts: FindproductsOpts = {}): Promise> { const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc', categoryId, isActive } = opts; const offset = (page - 1) * limit; - const where: FilterQuery = { restaurant: { id: restId } }; + const where: FilterQuery = {}; if (typeof isActive === 'boolean') { where.isActive = isActive; @@ -36,19 +33,19 @@ export class productRepository extends EntityRepository { if (search) { const pattern = `%${search}%`; - where.$or = [{ title: { $ilike: pattern } }, { desc: { $ilike: pattern } }]; + where.$or = [{ title: { $ilike: pattern } },]; } if (categoryId) { // filter by related category (product has a single `category` relation) - Object.assign(where, { category: { id: categoryId } } as unknown as FilterQuery); + Object.assign(where, { category: { id: categoryId } } as unknown as FilterQuery); } const [data, total] = await this.findAndCount(where, { limit, offset, orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' }, - populate: ['category', 'inventory'], + populate: ['category'], }); const totalPages = Math.ceil(total / limit); diff --git a/src/modules/roles/controllers/roles.controller.ts b/src/modules/roles/controllers/roles.controller.ts index f727fc0..cfc7952 100644 --- a/src/modules/roles/controllers/roles.controller.ts +++ b/src/modules/roles/controllers/roles.controller.ts @@ -26,7 +26,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Get all through restaurant roles with pagination and filters' }) - findAll(@RestId() restId: string) { + findAll() { return this.roleService.findAllGeneralAndRestaurantRoles(restId); } @@ -35,7 +35,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Get all permissions that the admin has' }) - async findAllPermissions(@AdminId() adminId: string, @RestId() restId: string) { + async findAllPermissions(@AdminId() adminId: string,) { const adminPermissionNames = await this.permissionService.getAdminFullPermissions(adminId, restId); return adminPermissionNames; @@ -49,7 +49,7 @@ export class RolesController { @ApiBearerAuth() @ApiOperation({ summary: 'Create a new role' }) @ApiBody({ type: CreateRoleDto }) - create(@Body() dto: CreateRoleDto, @RestId() restId: string) { + create(@Body() dto: CreateRoleDto,) { return this.roleService.createRestaurantRole(dto, restId); } @@ -58,7 +58,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Get a specific role by ID' }) - findOne(@Param('id') id: string, @RestId() restId: string) { + findOne(@Param('id') id: string,) { return this.roleService.findOne(restId, id); } @@ -68,7 +68,7 @@ export class RolesController { @ApiBearerAuth() @ApiOperation({ summary: 'Update a role' }) @ApiBody({ type: UpdateRoleDto }) - update(@Param('id') id: string, @Body() dto: UpdateRoleDto, @RestId() restId: string) { + update(@Param('id') id: string, @Body() dto: UpdateRoleDto,) { return this.roleService.update(restId, id, dto); } @@ -77,7 +77,7 @@ export class RolesController { @Permissions(Permission.MANAGE_ROLES) @ApiBearerAuth() @ApiOperation({ summary: 'Delete a role' }) - remove(@Param('id') id: string, @RestId() restId: string) { + remove(@Param('id') id: string,) { return this.roleService.remove(restId, id); } diff --git a/src/modules/ticket/entities/ticket.entity.ts b/src/modules/ticket/entities/ticket.entity.ts index ebf46f7..ae82d80 100755 --- a/src/modules/ticket/entities/ticket.entity.ts +++ b/src/modules/ticket/entities/ticket.entity.ts @@ -8,11 +8,11 @@ import { Cascade, Enum, } from '@mikro-orm/core'; - + import { BaseEntity } from "../../../common/entities/base.entity"; import { User } from "../../user/entities/user.entity"; import { TicketStatus } from "../enums/ticket-status.enum"; -import { product } from 'src/modules/product/entities/product.entity'; +import { Product } from 'src/modules/product/entities/product.entity'; import { Admin } from 'src/modules/admin/entities/admin.entity'; @Entity() @@ -23,8 +23,8 @@ export class Ticket extends BaseEntity { @Enum(() => TicketStatus) status: TicketStatus; // TODO : create relation - @Property({type:'json'}) - products: product | null; + @Property({ type: 'json' }) + products: Product | null; @ManyToOne(() => Admin) admin: Admin | null; @@ -33,7 +33,7 @@ export class Ticket extends BaseEntity { user: User; @ManyToOne(() => Ticket) - parent: Ticket|null; + parent: Ticket | null; @Property({ type: 'json' }) attachments: string[] diff --git a/src/modules/user/controllers/users.controller.ts b/src/modules/user/controllers/users.controller.ts index a32a6de..38d54f9 100644 --- a/src/modules/user/controllers/users.controller.ts +++ b/src/modules/user/controllers/users.controller.ts @@ -22,12 +22,12 @@ export class UsersController { constructor( private readonly userService: UserService, private readonly walletService: WalletService, - ) {} + ) { } @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Get the current authenticated user profile' }) - @ApiHeader(API_HEADER_SLUG) + @Get('public/user/me') async getUser(@UserId() userId: string) { const user = await this.userService.findById(userId); @@ -36,10 +36,10 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Update the authenticated user profile' }) - @ApiHeader(API_HEADER_SLUG) + @ApiBody({ type: UpdateUserDto }) @Patch('public/user/update') - async updateUser(@UserId() userId: string, @RestId() restId: string, @Body() dto: UpdateUserDto) { + async updateUser(@UserId() userId: string, , @Body() dto: UpdateUserDto) { const user = await this.userService.updateUser(userId, restId, dto); return user; } @@ -47,7 +47,7 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Get all addresses for the authenticated user' }) - @ApiHeader(API_HEADER_SLUG) + @ApiOkResponse({ description: 'List of user addresses' }) @Get('public/user/addresses') async getUserAddresses(@UserId() userId: string) { @@ -58,9 +58,9 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Get User Wallet' }) - @ApiHeader(API_HEADER_SLUG) + @Get('public/user/wallet/balance') - async getUserWalletBalance(@UserId() userId: string, @RestId() restId: string) { + async getUserWalletBalance(@UserId() userId: string,) { const wallet = await this.walletService.getUserCurrentWalletBalance(userId, restId); return wallet; } @@ -68,9 +68,9 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Get User Wallet' }) - @ApiHeader(API_HEADER_SLUG) + @Get('public/user/points/balance') - async getUserWallet(@UserId() userId: string, @RestId() restId: string) { + async getUserWallet(@UserId() userId: string,) { const wallet = await this.walletService.getUserCurrentPoinrBalance(userId, restId); return wallet; } @@ -78,11 +78,11 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Get User Wallet Transactions' }) - @ApiHeader(API_HEADER_SLUG) + @Get('public/user/wallet/transactions') async getUserWalletTransactions( @UserId() userId: string, - @RestId() restId: string, + , @Query(new ValidationPipe({ transform: true, whitelist: true })) query: FindWalletTransactionsDto, ) { @@ -93,7 +93,7 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Create a new address for the authenticated user' }) - @ApiHeader(API_HEADER_SLUG) + @ApiBody({ type: CreateUserAddressDto }) @ApiOkResponse({ description: 'Created user address' }) @Post('public/user/addresses') @@ -108,7 +108,7 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Get a single address by ID for the authenticated user' }) - @ApiHeader(API_HEADER_SLUG) + @ApiOkResponse({ description: 'User address details' }) @Get('public/user/addresses/:addressId') async getUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) { @@ -119,7 +119,7 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Update an address for the authenticated user' }) - @ApiHeader(API_HEADER_SLUG) + @ApiBody({ type: UpdateUserAddressDto }) @ApiOkResponse({ description: 'Updated user address' }) @Patch('public/user/addresses/:addressId') @@ -135,7 +135,7 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Delete an address for the authenticated user' }) - @ApiHeader(API_HEADER_SLUG) + @ApiOkResponse({ description: 'Address deleted successfully' }) @Delete('public/user/addresses/:addressId') async deleteUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) { @@ -146,7 +146,7 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Set an address as default for the authenticated user' }) - @ApiHeader(API_HEADER_SLUG) + @ApiOkResponse({ description: 'Address set as default' }) @Patch('public/user/addresses/:addressId/default') async setDefaultAddress(@UserId() userId: string, @Param('addressId') addressId: string) { @@ -157,9 +157,9 @@ export class UsersController { @UseGuards(AuthGuard) @ApiBearerAuth() @ApiOperation({ summary: 'Convert user score (points) to wallet balance' }) - @ApiHeader(API_HEADER_SLUG) + @Post('public/user/convert-score-to-wallet') - async convertScoreToWallet(@UserId() userId: string, @RestId() restId: string) { + async convertScoreToWallet(@UserId() userId: string,) { await this.userService.convertScoreToWallet(userId, restId); return { message: UserSuccessMessage.SCORE_CONVERTED_SUCCESS, @@ -176,7 +176,7 @@ export class UsersController { async findAllRestaurantUsers( @Query(new ValidationPipe({ transform: true, whitelist: true })) query: FindUsersDto, - @RestId() restId: string, + , ) { return this.userService.findAll(restId, query); }