chore: first commit

This commit is contained in:
mahyargdz
2025-06-24 11:26:06 +03:30
commit ccbf743ecb
186 changed files with 22258 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsNotEmpty, IsNumber, IsOptional, Max, Min } from "class-validator";
export class PaginationDto {
@IsOptional()
@IsNotEmpty()
@IsNumber()
@Min(1)
@Type(() => Number)
@ApiPropertyOptional({ type: "number", required: false })
page?: number;
@IsOptional()
@IsNotEmpty()
@IsNumber()
@Min(1)
@Max(50)
@Type(() => Number)
@ApiPropertyOptional({ type: "number", required: false })
limit?: number;
}
+19
View File
@@ -0,0 +1,19 @@
import { ApiProperty, PartialType } from "@nestjs/swagger";
import { IsNotEmpty, IsUUID } from "class-validator";
import { CommonMessage } from "../enums/message.enum";
export class ParamDto {
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
@IsUUID("7", { message: CommonMessage.ID_SHOULD_BE_UUID })
@ApiProperty({ description: "Id of the entity", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" })
id: string;
}
export class OptionalParamDto extends PartialType(ParamDto) {}
export class ParamNumberIdDto {
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
@ApiProperty({ description: "Id of the entity", example: "20" })
id: string;
}
+15
View File
@@ -0,0 +1,15 @@
// export const AUTH_THROTTLE = "AUTH_THROTTLE";
export const AUTH_THROTTLE_TTL = 5 * 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 DANAK_IMAPS_SERVER = "imaps.danakcorp.com";
export const DANAK_SMTPS_SERVER = "smtps.danakcorp.com";
export const DANAK_IMAPS_PORT = 993;
export const DANAK_SMTPS_PORT = 465;
export const DANAK_IMAPS_ENCRYPTION = "TLS";
export const DANAK_SMTPS_ENCRYPTION = "TLS";
+4
View File
@@ -0,0 +1,4 @@
export const ADMIN_ROUTE = "shouldAdmin";
import { SetMetadata } from "@nestjs/common";
export const AdminRoute = (isAdminRoute: boolean = true) => SetMetadata(ADMIN_ROUTE, isAdminRoute);
+8
View File
@@ -0,0 +1,8 @@
import { UseGuards, applyDecorators } from "@nestjs/common";
import { ApiBearerAuth } from "@nestjs/swagger";
import { JwtAuthGuard } from "../../modules/auth/guards/auth.guard";
export function AuthGuards() {
return applyDecorators(UseGuards(JwtAuthGuard), ApiBearerAuth("authorization"));
}
@@ -0,0 +1,17 @@
import { ExecutionContext, createParamDecorator } from "@nestjs/common";
import { FastifyRequest } from "fastify";
/**
* Extracts the business ID from the request header
* @example
* ```typescript
* @Get()
* findAll(@BusinessIdHeader() businessId: string) {
* // Use businessId
* }
* ```
*/
export const BusinessIdHeader = createParamDecorator((headerName: string = "X-Business-Id", ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest<FastifyRequest>();
return req.headers[headerName.toLocaleLowerCase()];
});
@@ -0,0 +1,11 @@
import { ExecutionContext, createParamDecorator } from "@nestjs/common";
import { FastifyRequest } from "fastify";
import { Business } from "../../modules/businesses/entities/business.entity";
export const BusinessDec = createParamDecorator((data: keyof Business | undefined, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest<FastifyRequest>();
const business = req.business;
return data ? business?.[data] : business;
});
+35
View File
@@ -0,0 +1,35 @@
import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, registerDecorator } from "class-validator";
export function isValidIranianNationalCode(nationalCode: string): boolean {
if (!/^\d{10}$/.test(nationalCode)) return false;
const digits = nationalCode.split("").map(Number);
const controlDigit = digits[9];
const sum = digits.slice(0, 9).reduce((acc, digit, index) => acc + digit * (10 - index), 0);
const remainder = sum % 11;
return (remainder < 2 && controlDigit === remainder) || (remainder >= 2 && controlDigit === 11 - remainder);
}
@ValidatorConstraint({ async: false })
export class IsNationalCodeConstraint implements ValidatorConstraintInterface {
validate(value: unknown, _args: ValidationArguments) {
return typeof value === "string" && isValidIranianNationalCode(value);
}
defaultMessage(_args: ValidationArguments) {
return `Invalid Iranian national code :${_args.value}`;
}
}
export function IsNationalCode(validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: IsNationalCodeConstraint,
});
};
}
+9
View File
@@ -0,0 +1,9 @@
import { applyDecorators } from "@nestjs/common";
import { ApiQuery } from "@nestjs/swagger";
export function Pagination() {
return applyDecorators(
ApiQuery({ name: "page", example: 1, required: false, type: "number" }),
ApiQuery({ name: "limit", example: 10, required: false, type: "number" }),
);
}
+6
View File
@@ -0,0 +1,6 @@
// import { SetMetadata } from "@nestjs/common";
// import { PermissionEnum } from "../../modules/users/enums/permission.enum";
// export const PERMISSION_KEY = "permissions";
// export const PermissionsDec = (...permissions: PermissionEnum[]) => SetMetadata(PERMISSION_KEY, permissions);
@@ -0,0 +1,12 @@
import { UseGuards, applyDecorators } from "@nestjs/common";
import { Throttle, ThrottlerGuard } from "@nestjs/throttler";
import { AUTH__REFRESH_THROTTLE_LIMIT, AUTH__REFRESH_THROTTLE_TTL } from "../constants";
export const RateLimit = (limit: number, ttl: number) => applyDecorators(Throttle({ default: { limit, ttl } }), UseGuards(ThrottlerGuard));
// Predefined rate limits for common scenarios
export const StrictRateLimit = () => RateLimit(5, 60000); // 5 requests per minute
export const StandardRateLimit = () => RateLimit(30, 60000); // 30 requests per minute
export const MailSendRateLimit = () => RateLimit(10, 60000); // 10 emails per minute
export const RefreshTokenRateLimit = () => RateLimit(AUTH__REFRESH_THROTTLE_LIMIT, AUTH__REFRESH_THROTTLE_TTL); // 10 emails per minute
+4
View File
@@ -0,0 +1,4 @@
// import { SetMetadata } from "@nestjs/common";
// export const ROLES_KEY = "roles";
// export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
+5
View File
@@ -0,0 +1,5 @@
import { SetMetadata } from "@nestjs/common";
export const SKIP_AUTH_KEY = "skipAuth";
export const SkipAuth = () => SetMetadata(SKIP_AUTH_KEY, true);
+17
View File
@@ -0,0 +1,17 @@
import { ExecutionContext, createParamDecorator } from "@nestjs/common";
import { FastifyRequest } from "fastify";
import { ITokenPayload } from "../../modules/auth/interfaces/IToken-payload";
declare module "fastify" {
interface FastifyRequest {
user?: ITokenPayload;
}
}
export const UserDec = createParamDecorator((data: keyof ITokenPayload | undefined, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest<FastifyRequest>();
const user = req.user;
return data ? user?.[data] : user;
});
+16
View File
@@ -0,0 +1,16 @@
import { Opt, PrimaryKey, Property, sql } from "@mikro-orm/core";
import { v7 } from "uuid";
export abstract class BaseEntity {
@PrimaryKey({ type: "uuid" })
id = v7();
@Property({ default: sql.now(), type: "timestamptz" })
createdAt: Date & Opt;
@Property({ default: sql.now(), type: "timestamptz" })
updatedAt: Date & Opt;
@Property({ nullable: true })
deletedAt?: Date;
}
+222
View File
@@ -0,0 +1,222 @@
export const enum AuthMessage {
UNAUTHORIZED_ACCESS = "شما دسترسی لازم برای این عملیات را ندارید.",
PHONE_REGISTERED = "شماره ثبت شد",
INVALID_PHONE_FORMAT = "فرمت شماره تلفن صحیح نیست",
OTP_SENT = "کد یکبار مصرف به شماره شما ارسال شد.",
INVALID_PHONE = "شماره موبایل اشتباه می باشد",
INVALID_CREDENTIAL = "شماره موبایل یا رمز عبور اشتباه می باشد",
OTP_FAILED = "کد یا شماره موبایل اشتباه است",
LOGIN_SUCCESS = "ورود با موفقیت انجام شد",
PASSWORD_LOGIN_SUCCESS = "با موفقیت وارد شدید",
FORGOT_PASSWORD_SUCCESS = "درخواست فراموشی رمز عبور با موفقیت ثبت شد",
PASSWORD_SET_SUCCESS = "پسورد با موفقیت تنظیم شد",
PASSWORD_UPDATE_SUCCESS = "رمز عبور شما تغییر کرد",
INVALID_OTP = "کد صحیح نمی‌باشد یا منقضی شده است",
PASSWORD_MISMATCH = "رمز عبور یکسان نیست",
INVALID_PASSWORD = "ایمیل یا رمز عبور اشتباه است",
INVALID_REPEAT_PASSWORD = "تکرار رمز عبور اشتباه است",
EMAIL_NOT_EMPTY = "ایمیل نمیتواند خالی باشد.",
INVALID_EMAIL_FORMAT = "فرمت ایمیل صحیح نیست",
PasswordNotEmpty = "پسورد نمیتواند خالی باشد.",
USER_NOT_FOUND = "کاربری با این شماره وجود ندارد",
USER_EXISTS = "با این شماره قبلا ثبت نام شده است",
USER_REGISTER_SUCCESS = "ثبت نام موفقیت آمیز بود",
INVALID_USER = "کاربری با این مشخصات یافت نشد",
PHONE_NOT_FOUND = "کاربری با این شماره یافت نشد",
TOKEN_EXPIRED = "توکن منقضی شده است",
TOKEN_INVALID = "توکن نامعتبر است",
BANNED = "در حال حاضر شما امکان دسترسی به این سرویس را ندارید",
PHONE_EXISTS = "شماره تلفن قبلا ثبت شده است",
ADMIN_CREATED = "ادمین با موفقیت ایجاد شد",
PERM_NOT_FOUND = "دسترسی یافت نشد",
INVALID_PASS_FORMAT = "فرمت رمز عبور صحیح نیست",
PHONE_NOT_EMPTY = "شماره تلفن نمیتواند خالی باشد",
PASSWORD_FORMAT_INVALID = "رمز عبور باید به صورت رشته باشد",
OTP_FORMAT_INVALID = "کد یکبار مصرف باید عددی و ۵ رقمی باشد",
PHONE_FORMAT_INVALID = "شماره تلفن باید معتبر و به فرمت ایران باشد",
TOKEN_NOT_EMPTY = "توکن نمی‌تواند خالی باشد",
PASSWORD_NOT_EMPTY = "رمز عبور نمی‌تواند خالی باشد",
PASSWORD_CHANGED_SUCCESSFULLY = "رمز عبور با موفقیت تغییر کرد",
PASSWORD_CONFIRMATION_NOT_EMPTY = "تایید رمز عبور نمی‌تواند خالی باشد",
PASSWORD_LENGTH = "رمز عبور باید حداقل ۸ کاراکتر باشد",
OTP_NOT_EMPTY = "کد یکبار مصرف نمی‌تواند خالی باشد",
LAST_PASSWORD_NOT_EMPTY = "رمز عبور قبلی نمی‌تواند خالی باشد",
FIRST_NAME_NOT_EMPTY = "نام نمی‌تواند خالی باشد",
LAST_NAME_NOT_EMPTY = "نام خانوادگی نمی‌تواند خالی باشد",
FIRST_NAME_SHOULD_BE_BETWEEN_2_AND_50 = "نام باید بین ۲ تا ۵۰ کاراکتر باشد",
LAST_NAME_SHOULD_BE_BETWEEN_2_AND_50 = "نام خانوادگی باید بین ۲ تا ۵۰ کاراکتر باشد",
BIRTH_DATE_NOT_EMPTY = "تاریخ تولد نمی‌تواند خالی باشد",
NATIONAL_CODE_INCORRECT = "کد ملی باید ۱۰ رقمی باشد",
NATIONAL_NOT_EMPTY = "کد ملی نمی‌تواند خالی باشد",
OTP_ALREADY_SENT = "کد یکبار مصرف قبلا ارسال شده است",
TOO_MANY_REQUESTS = "تعداد درخواست های شما بیش از حد مجاز است",
NOT_ADMIN = "شما دسترسی به بخش ادمین ندارید",
PHONE_SHOULD_BE_11_DIGIT = "شماره تلفن باید ۱۱ رقم باشد",
TIMESTAMP_NOT_EMPTY = "زمان نمی‌تواند خالی باشد",
ADMIN_CAN_NOT_LOGIN = "ادمین نمی‌تواند وارد شود",
NATIONAL_CODE_INVALID = "کد ملی نامعتبر است",
INVALID_REFRESH_TOKEN = "توکن رفرش نامعتبر است",
REFRESH_TOKEN_EXPIRED = "توکن رفرش منقضی شده است",
LOGOUT_SUCCESS = "خروج با موفقیت انجام شد",
CURRENT_PASSWORD_INVALID = "رمز عبور فعلی نامعتبر است",
TOKEN_EXPIRED_OR_INVALID = "توکن منقضی شده یا نامعتبر است",
ACCESS_DENIED = "دسترسی شما محدود شده است",
USER_ACCOUNT_INACTIVE = "حساب کاربری شما غیر فعال است",
}
export const enum UserMessage {
USER_NOT_FOUND = "کاربری با این شماره وجود ندارد",
USER_EXISTS = "با این شماره قبلا ثبت نام شده است",
USER_REGISTER_SUCCESS = "ثبت نام موفقیت آمیز بود",
INVALID_USER = "کاربری با این مشخصات یافت نشد",
EMAIL_ADDRESS_ALREADY_EXISTS = "ایمیل قبلا ثبت شده است",
EMAIL_USER_NOT_FOUND = "ایمیل یافت نشد",
EMAIL_USER_DELETED_SUCCESSFULLY = "ایمیل با موفقیت حذف شد",
FAILED_TO_DELETE_EMAIL_USER = "خطا در حذف ایمیل",
EMAIL_USER_QUOTA_UPDATED_SUCCESSFULLY = "ایمیل با موفقیت به روز رسانی شد",
FAILED_TO_UPDATE_EMAIL_USER_QUOTA = "خطا در به روز رسانی حجم ایمیل",
}
export const enum CommonMessage {
VALIDITY_TYPE_REQUIRED = "نوع اعتبار سنجی مورد نیاز است",
THIS_FILED_IS_REQUIRED = "این فیلد الزامی است",
VALID_FOR_CHOOSE = "معتبر برای انتخاب",
UPDATE_SUCCESS = "با موفقیت به روز رسانی شد",
CREATED = "با موفقیت ایجاد شد",
DELETED = "با موفقیت حذف شد",
ID_REQUIRED = "شناسه مورد نیاز است",
ID_SHOULD_BE_UUID = "شناسه باید یک یو یو آی دی باشد",
PaymentTypeQueryNotEmpty = "نوع پرداخت نمی‌تواند خالی باشد",
SEARCH_QUERY_STRING = "رشته جستجو باید یک رشته باشد",
UPDATED = "با موفقیت آپدیت شد",
IS_ACTIVE_SHOULD_BE_1_0 = "وضعیت باید یکی از مقادیر ۰ و ۱ باشد",
DATE_MUST_BE_DATE = "تاریخ باید یک تاریخ معتبر به صورت رشته باشد",
}
export const enum UploaderMessage {
UPLOAD_FILE_INVALID = "فایل معتبر نیست",
}
export const enum EmailMessage {
EMAIL_VERIFICATION = "تایید ایمیل",
EMAIL_SENDING_FAILED = "ارسال ایمیل با خطا مواجه شد",
LOGIN = "ورود به سیستم",
INVOICE = "فاکتور",
WALLET = "اعلان کیف پول",
TICKET = "تیکت پشتیبانی",
ANNOUNCEMENT = "اعلان",
PAYMENT_REMINDER = "یادآوری پرداخت",
PAYMENT_CANCELLATION = "لغو پرداخت",
}
export const enum SmsMessage {
SEND_SMS_ERROR = "خطا در ارسال پیامک",
}
export const enum BusinessMessage {
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 = "توکن تایید دامنه مورد نیاز است",
}
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 = "این کسب و کار قبلا دامنه ای دارد",
}
export const enum MailServerMessage {
FAILED_TO_CREATE_ACCOUNT = "خطا در ایجاد ایمیل",
FAILED_TO_CREATE_DKIM_KEY = "FAILED_TO_CREATE_DKIM_KEY",
}
export const enum DnsRecordMessage {
DNS_RECORD_NOT_FOUND = "رکورد DNS یافت نشد",
DNS_RECORD_DELETED = "رکورد DNS با موفقیت حذف شد",
}
+9
View File
@@ -0,0 +1,9 @@
export interface ErrorResponse {
statusCode: number;
success: boolean;
error: {
message: string | string[];
timestamp?: string;
path?: string;
};
}
+19
View File
@@ -0,0 +1,19 @@
export interface IPageFormat {
page: number;
limit: number;
totalItems: number;
totalPages: number;
prevPage: string | boolean;
nextPage: string | boolean;
}
export interface PaginationQuery {
page?: string;
limit?: string;
}
export interface PaginatedResponse {
paginate?: boolean;
count?: number;
[key: string]: unknown;
}
+9
View File
@@ -0,0 +1,9 @@
import { ValueProvider } from "@nestjs/common";
import { Logger } from "@nestjs/common";
export const MIKRO_ORM_QUERY_LOGGER = "MikroOrmQueryLogger";
export const MikroOrmQueryLogger: ValueProvider = {
provide: MIKRO_ORM_QUERY_LOGGER,
useValue: new Logger("mikro-orm"),
};
+33
View File
@@ -0,0 +1,33 @@
import { OnWorkerEvent, WorkerHost } from "@nestjs/bullmq";
import { Logger } from "@nestjs/common";
import { Job } from "bullmq";
export abstract class WorkerProcessor extends WorkerHost {
protected readonly logger = new Logger(WorkerProcessor.name);
@OnWorkerEvent("completed")
onCompleted(job: Job) {
const { id, name, queueName, finishedOn, returnvalue } = job;
const completionTime = finishedOn ? new Date(finishedOn).toISOString() : "";
this.logger.log(`Job id: ${id}, name: ${name} completed in queue ${queueName} on ${completionTime}. Result: ${returnvalue}`);
}
@OnWorkerEvent("progress")
onProgress(job: Job) {
const { id, name, progress } = job;
this.logger.log(`Job id: ${id}, name: ${name} completes ${progress}%`);
}
@OnWorkerEvent("failed")
onFailed(job: Job) {
const { id, name, queueName, failedReason } = job;
this.logger.error(`Job id: ${id}, name: ${name} failed in queue ${queueName}. Failed reason: ${failedReason}`);
}
@OnWorkerEvent("active")
onActive(job: Job) {
const { id, name, queueName, timestamp } = job;
const startTime = timestamp ? new Date(timestamp).toISOString() : "";
this.logger.log(`Job id: ${id}, name: ${name} starts in queue ${queueName} on ${startTime}.`);
}
}