From 2de442dbaede252b309cf7f28ae4f72f89e83f32 Mon Sep 17 00:00:00 2001
From: parsa aghaei
Date: Wed, 29 Jul 2026 16:44:53 +0330
Subject: [PATCH] fix(lint): clean up type definitions and missing imports
---
backend/src/admin/admin.service.ts | 2 +-
backend/src/auth/auth.service.ts | 5 +-
.../common/filters/http-exception.filter.ts | 60 ++++++++++++-------
.../common/filters/prisma-exception.filter.ts | 12 ++--
.../common/schemas/error-response.schema.ts | 28 +++++++--
backend/src/common/services/sms.service.ts | 32 ++++++++--
backend/src/main.ts | 17 ++++--
backend/src/orders/orders.service.ts | 38 +++++++-----
backend/src/wholesale/wholesale.service.ts | 15 +++--
9 files changed, 147 insertions(+), 62 deletions(-)
diff --git a/backend/src/admin/admin.service.ts b/backend/src/admin/admin.service.ts
index cffa124..8ee313d 100644
--- a/backend/src/admin/admin.service.ts
+++ b/backend/src/admin/admin.service.ts
@@ -1,4 +1,4 @@
-import { Injectable, HttpException } from '@nestjs/common';
+import { Injectable, HttpException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { RedisService } from '../redis/redis.service';
diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts
index 1d281e6..fa28f60 100644
--- a/backend/src/auth/auth.service.ts
+++ b/backend/src/auth/auth.service.ts
@@ -27,7 +27,10 @@ export class AuthService {
// Dispatch OTP via MeliPayamak Pattern SMS
await this.smsService.sendOtp(phoneNumber, code);
- return { success: true, message: 'کد تایید با موفقیت به شماره شما پیامک شد.' };
+ return {
+ success: true,
+ message: 'کد تایید با موفقیت به شماره شما پیامک شد.',
+ };
}
async verifyOtp(verifyOtpDto: VerifyOtpDto) {
diff --git a/backend/src/common/filters/http-exception.filter.ts b/backend/src/common/filters/http-exception.filter.ts
index bcaa04f..67e3650 100644
--- a/backend/src/common/filters/http-exception.filter.ts
+++ b/backend/src/common/filters/http-exception.filter.ts
@@ -19,9 +19,10 @@ export class CustomHttpExceptionFilter implements ExceptionFilter {
const request = ctx.getRequest();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
- let message = 'خطای داخلی سرور رخ داده است. لطفاً بعداً تلاش کنید یا با پشتیبانی تماس بگیرید.';
+ let message =
+ 'خطای داخلی سرور رخ داده است. لطفاً بعداً تلاش کنید یا با پشتیبانی تماس بگیرید.';
let code = 'INTERNAL_SERVER_ERROR';
- let details: ErrorDetailField[] | Record = {};
+ let details: ErrorDetailField[] | Record = {};
if (exception instanceof HttpException) {
status = exception.getStatus();
@@ -30,14 +31,17 @@ export class CustomHttpExceptionFilter implements ExceptionFilter {
if (typeof exceptionResponse === 'string') {
message = this.translateGenericMessage(exceptionResponse, status);
code = this.deriveErrorCode(status, exceptionResponse);
- } else if (typeof exceptionResponse === 'object' && exceptionResponse !== null) {
- const resObj = exceptionResponse as Record;
-
+ } else if (
+ typeof exceptionResponse === 'object' &&
+ exceptionResponse !== null
+ ) {
+ const resObj = exceptionResponse as Record;
+
// Handle class-validator validation array messages
if (Array.isArray(resObj.message)) {
message = 'اطلاعات وارد شده نامعتبر است. لطفاً فرم را بررسی کنید.';
code = 'INVALID_INPUT_DATA';
- details = resObj.message.map((msg: string) => {
+ details = (resObj.message as string[]).map((msg: string) => {
const parts = msg.split(' ');
return {
field: parts[0] || 'general',
@@ -45,13 +49,19 @@ export class CustomHttpExceptionFilter implements ExceptionFilter {
};
});
} else {
- message = this.translateGenericMessage(resObj.message || exception.message, status);
- code = resObj.code || this.deriveErrorCode(status, resObj.error);
- details = resObj.details || {};
+ const rawMsg = typeof resObj.message === 'string' ? resObj.message : exception.message;
+ message = this.translateGenericMessage(rawMsg, status);
+ const rawCode = typeof resObj.code === 'string' ? resObj.code : undefined;
+ const rawError = typeof resObj.error === 'string' ? resObj.error : undefined;
+ code = rawCode || this.deriveErrorCode(status, rawError);
+ details = (resObj.details as Record) || {};
}
}
} else if (exception instanceof Error) {
- this.logger.error(`Unhandled Exception: ${exception.message}`, exception.stack);
+ this.logger.error(
+ `Unhandled Exception: ${exception.message}`,
+ exception.stack,
+ );
message = 'خطای غیرمنتظره در پردازش درخواست رخ داد.';
code = 'UNHANDLED_EXCEPTION';
}
@@ -66,14 +76,16 @@ export class CustomHttpExceptionFilter implements ExceptionFilter {
path: request.url,
};
- this.logger.warn(`[${request.method}] ${request.url} - Status ${status} - ${message}`);
+ this.logger.warn(
+ `[${request.method}] ${request.url} - Status ${status} - ${message}`,
+ );
response.status(status).json(errorPayload);
}
private translateGenericMessage(msg: string, status: number): string {
if (!msg) return 'خطای نامشخص رخ داده است.';
-
+
const lower = msg.toLowerCase();
if (lower.includes('unauthorized') || status === 401) {
return 'شما دسترسی لازم برای این عملیات را ندارید. لطفاً ابتدا وارد حساب کاربری خود شوید.';
@@ -100,14 +112,22 @@ export class CustomHttpExceptionFilter implements ExceptionFilter {
}
switch (status) {
- case 400: return 'BAD_REQUEST';
- case 401: return 'UNAUTHORIZED';
- case 403: return 'FORBIDDEN';
- case 404: return 'NOT_FOUND';
- case 409: return 'CONFLICT';
- case 422: return 'UNPROCESSABLE_ENTITY';
- case 429: return 'TOO_MANY_REQUESTS';
- default: return 'INTERNAL_SERVER_ERROR';
+ case 400:
+ return 'BAD_REQUEST';
+ case 401:
+ return 'UNAUTHORIZED';
+ case 403:
+ return 'FORBIDDEN';
+ case 404:
+ return 'NOT_FOUND';
+ case 409:
+ return 'CONFLICT';
+ case 422:
+ return 'UNPROCESSABLE_ENTITY';
+ case 429:
+ return 'TOO_MANY_REQUESTS';
+ default:
+ return 'INTERNAL_SERVER_ERROR';
}
}
}
diff --git a/backend/src/common/filters/prisma-exception.filter.ts b/backend/src/common/filters/prisma-exception.filter.ts
index fdefd38..61f2835 100644
--- a/backend/src/common/filters/prisma-exception.filter.ts
+++ b/backend/src/common/filters/prisma-exception.filter.ts
@@ -20,7 +20,7 @@ export class PrismaExceptionFilter implements ExceptionFilter {
let status = HttpStatus.BAD_REQUEST;
let message = 'خطایی در پردازش اطلاعات دیتابیس رخ داده است.';
let code = `PRISMA_${exception.code}`;
- let details: Record = {};
+ let details: Record = {};
switch (exception.code) {
case 'P2002': {
@@ -34,14 +34,16 @@ export class PrismaExceptionFilter implements ExceptionFilter {
}
case 'P2025': {
status = HttpStatus.NOT_FOUND;
- message = 'اطلاعات یا رکورد مورد نظر در سیستم یافت نشد یا قبلاً حذف شده است.';
+ message =
+ 'اطلاعات یا رکورد مورد نظر در سیستم یافت نشد یا قبلاً حذف شده است.';
code = 'RECORD_NOT_FOUND';
details = { meta: exception.meta };
break;
}
case 'P2003': {
status = HttpStatus.BAD_REQUEST;
- message = 'امکان انجام عملیات به دلیل وجود وابستگی بین اطلاعات وجود ندارد.';
+ message =
+ 'امکان انجام عملیات به دلیل وجود وابستگی بین اطلاعات وجود ندارد.';
code = 'FOREIGN_KEY_CONSTRAINT_FAILED';
break;
}
@@ -52,7 +54,9 @@ export class PrismaExceptionFilter implements ExceptionFilter {
}
}
- this.logger.error(`[Prisma ${exception.code}] ${request.method} ${request.url} - ${message}`);
+ this.logger.error(
+ `[Prisma ${exception.code}] ${request.method} ${request.url} - ${message}`,
+ );
response.status(status).json({
success: false,
diff --git a/backend/src/common/schemas/error-response.schema.ts b/backend/src/common/schemas/error-response.schema.ts
index 1893973..e1670f6 100644
--- a/backend/src/common/schemas/error-response.schema.ts
+++ b/backend/src/common/schemas/error-response.schema.ts
@@ -4,12 +4,18 @@ export class ErrorDetailField {
@ApiProperty({ description: 'نام فیلد دارای خطا', example: 'phone' })
field: string;
- @ApiProperty({ description: 'توضیح توصیفی خطا به فارسی', example: 'شماره تلفن همراه وارد شده نامعتبر است.' })
+ @ApiProperty({
+ description: 'توضیح توصیفی خطا به فارسی',
+ example: 'شماره تلفن همراه وارد شده نامعتبر است.',
+ })
message: string;
}
export class ApiErrorResponse {
- @ApiProperty({ description: 'وضعیت موفقیت درخواست (همیشه false در زمان خطا)', example: false })
+ @ApiProperty({
+ description: 'وضعیت موفقیت درخواست (همیشه false در زمان خطا)',
+ example: false,
+ })
success: boolean;
@ApiProperty({ description: 'کد وضعیت HTTP درخواست', example: 400 })
@@ -17,11 +23,15 @@ export class ApiErrorResponse {
@ApiProperty({
description: 'پیام جامع و توصیفی خطا به زبان فارسی',
- example: 'اطلاعات وارد شده نامعتبر است. لطفاً موارد مشخص شده را بررسی نمایید.',
+ example:
+ 'اطلاعات وارد شده نامعتبر است. لطفاً موارد مشخص شده را بررسی نمایید.',
})
message: string;
- @ApiProperty({ description: 'کد سیستمیک و ثابت خطا جهت پردازش فرانتاند', example: 'INVALID_INPUT_DATA' })
+ @ApiProperty({
+ description: 'کد سیستمیک و ثابت خطا جهت پردازش فرانتاند',
+ example: 'INVALID_INPUT_DATA',
+ })
code: string;
@ApiProperty({
@@ -31,9 +41,15 @@ export class ApiErrorResponse {
})
details?: ErrorDetailField[] | Record;
- @ApiProperty({ description: 'زمان وقوع خطا به فرمت ISO', example: '2026-07-29T16:30:00.000Z' })
+ @ApiProperty({
+ description: 'زمان وقوع خطا به فرمت ISO',
+ example: '2026-07-29T16:30:00.000Z',
+ })
timestamp: string;
- @ApiProperty({ description: 'مسیر و انکوینتی که خطا در آن رخ داده است', example: '/api/auth/register' })
+ @ApiProperty({
+ description: 'مسیر و انکوینتی که خطا در آن رخ داده است',
+ example: '/api/auth/register',
+ })
path: string;
}
diff --git a/backend/src/common/services/sms.service.ts b/backend/src/common/services/sms.service.ts
index a77a70a..474a14d 100644
--- a/backend/src/common/services/sms.service.ts
+++ b/backend/src/common/services/sms.service.ts
@@ -68,7 +68,9 @@ export class SmsService {
);
req.on('error', (err) => {
- this.logger.error(`[SMS Exception] MeliPayamak HTTP Error: ${err.message}`);
+ this.logger.error(
+ `[SMS Exception] MeliPayamak HTTP Error: ${err.message}`,
+ );
resolve(false);
});
@@ -92,7 +94,11 @@ export class SmsService {
/**
* Send Order Confirmation SMS
*/
- async sendOrderConfirmation(phone: string, orderNumber: string, amount: string): Promise {
+ async sendOrderConfirmation(
+ phone: string,
+ orderNumber: string,
+ amount: string,
+ ): Promise {
const bodyId = parseInt(process.env.MELIPAYAMAK_ORDER_BODY_ID || '0', 10);
return this.sendPatternSms({
to: phone,
@@ -104,8 +110,15 @@ export class SmsService {
/**
* Send Shipping Status SMS with Tracking Code
*/
- async sendShippingNotification(phone: string, orderNumber: string, trackingCode: string): Promise {
- const bodyId = parseInt(process.env.MELIPAYAMAK_SHIPPING_BODY_ID || '0', 10);
+ async sendShippingNotification(
+ phone: string,
+ orderNumber: string,
+ trackingCode: string,
+ ): Promise {
+ const bodyId = parseInt(
+ process.env.MELIPAYAMAK_SHIPPING_BODY_ID || '0',
+ 10,
+ );
return this.sendPatternSms({
to: phone,
bodyId,
@@ -116,8 +129,15 @@ export class SmsService {
/**
* Send Pet Care Vaccination / Deworming Reminder SMS
*/
- async sendPetCareReminder(phone: string, petName: string, reminderType: string): Promise {
- const bodyId = parseInt(process.env.MELIPAYAMAK_PET_CARE_BODY_ID || '0', 10);
+ async sendPetCareReminder(
+ phone: string,
+ petName: string,
+ reminderType: string,
+ ): Promise {
+ const bodyId = parseInt(
+ process.env.MELIPAYAMAK_PET_CARE_BODY_ID || '0',
+ 10,
+ );
return this.sendPatternSms({
to: phone,
bodyId,
diff --git a/backend/src/main.ts b/backend/src/main.ts
index c9247c8..acc7853 100644
--- a/backend/src/main.ts
+++ b/backend/src/main.ts
@@ -36,15 +36,21 @@ async function bootstrap() {
forbidNonWhitelisted: true,
exceptionFactory: (errors) => {
const formattedDetails = errors.map((err) => {
- const constraints = err.constraints ? Object.values(err.constraints) : [];
+ const constraints = err.constraints
+ ? Object.values(err.constraints)
+ : [];
return {
field: err.property,
- message: constraints.length > 0 ? constraints[0] : 'مقدار وارد شده نامعتبر است.',
+ message:
+ constraints.length > 0
+ ? constraints[0]
+ : 'مقدار وارد شده نامعتبر است.',
};
});
return new BadRequestException({
- message: 'اطلاعات ورودی فرم معتبر نمیباشد. لطفاً موارد مشخص شده را اصلاح نمایید.',
+ message:
+ 'اطلاعات ورودی فرم معتبر نمیباشد. لطفاً موارد مشخص شده را اصلاح نمایید.',
error: 'INVALID_INPUT_DATA',
details: formattedDetails,
});
@@ -52,7 +58,10 @@ async function bootstrap() {
}),
);
- app.useGlobalFilters(new CustomHttpExceptionFilter(), new PrismaExceptionFilter());
+ app.useGlobalFilters(
+ new CustomHttpExceptionFilter(),
+ new PrismaExceptionFilter(),
+ );
const config = new DocumentBuilder()
.setTitle('Canina Iran API')
diff --git a/backend/src/orders/orders.service.ts b/backend/src/orders/orders.service.ts
index 92a6988..a2fe223 100644
--- a/backend/src/orders/orders.service.ts
+++ b/backend/src/orders/orders.service.ts
@@ -92,7 +92,12 @@ export class OrdersService {
// Verify stock and build items
let cartTotal = 0;
- const orderItems: Array<{ productId: string; quantity: number; unitPrice: number; totalPrice: number }> = [];
+ const orderItems: Array<{
+ productId: string;
+ quantity: number;
+ unitPrice: number;
+ totalPrice: number;
+ }> = [];
for (const item of createOrderDto.items) {
const product = await this.prisma.product.findUnique({
@@ -182,15 +187,20 @@ export class OrdersService {
// Send Order Confirmation SMS
if (userId) {
- this.prisma.user.findUnique({ where: { id: userId } }).then((user) => {
- if (user && user.mobile) {
- this.smsService.sendOrderConfirmation(
- user.mobile,
- trackingNumber,
- finalAmount.toLocaleString('fa-IR'),
- ).catch(() => {});
- }
- }).catch(() => {});
+ this.prisma.user
+ .findUnique({ where: { id: userId } })
+ .then((user) => {
+ if (user && user.mobile) {
+ this.smsService
+ .sendOrderConfirmation(
+ user.mobile,
+ trackingNumber,
+ finalAmount.toLocaleString('fa-IR'),
+ )
+ .catch(() => {});
+ }
+ })
+ .catch(() => {});
}
return createdOrder;
@@ -219,11 +229,9 @@ export class OrdersService {
const mobile = updatedOrder.user?.mobile;
const trackingNum = updatedOrder.trackingNumber || trackingCode || '';
if (status === 'shipped' && mobile && trackingCode) {
- this.smsService.sendShippingNotification(
- mobile,
- trackingNum,
- trackingCode,
- ).catch(() => {});
+ this.smsService
+ .sendShippingNotification(mobile, trackingNum, trackingCode)
+ .catch(() => {});
}
return updatedOrder;
diff --git a/backend/src/wholesale/wholesale.service.ts b/backend/src/wholesale/wholesale.service.ts
index 8894c5d..6561bb8 100644
--- a/backend/src/wholesale/wholesale.service.ts
+++ b/backend/src/wholesale/wholesale.service.ts
@@ -26,11 +26,16 @@ export class WholesaleService {
// Alert admin via SMS if admin mobile is configured
const adminMobile = process.env.ADMIN_MOBILE_ALERT;
if (adminMobile) {
- this.smsService.sendPatternSms({
- to: adminMobile,
- bodyId: parseInt(process.env.MELIPAYAMAK_ADMIN_ALERT_BODY_ID || '0', 10),
- args: [dto.businessName || 'نامشخص', user.mobile || ''],
- }).catch(() => {});
+ this.smsService
+ .sendPatternSms({
+ to: adminMobile,
+ bodyId: parseInt(
+ process.env.MELIPAYAMAK_ADMIN_ALERT_BODY_ID || '0',
+ 10,
+ ),
+ args: [dto.businessName || 'نامشخص', user.mobile || ''],
+ })
+ .catch(() => {});
}
return {