feat(payment): add raw logs, IP/UA capture, exponential backoff, inventory hold, IPaymentGateway interface and admin health check
All checks were successful
Deploy Canina / deploy (push) Successful in 1m53s

This commit is contained in:
parsa aghaei 2026-08-16 12:24:20 +03:30
parent 5ace474d71
commit 6e98b8779d
18 changed files with 11978 additions and 9477 deletions

View File

@ -158,9 +158,10 @@ model Product {
category Category @relation(fields: [categoryId], references: [id])
ingredientList ProductIngredient[]
symptoms ProductSymptom[]
reminders Reminder[]
orderItems OrderItem[]
advisorRules SmartAdvisorRule[]
reminders Reminder[]
orderItems OrderItem[]
advisorRules SmartAdvisorRule[]
inventoryReservations InventoryReservation[]
@@index([categorySlug])
@@index([suitableFor])
@ -326,6 +327,7 @@ model Order {
coupon Coupon? @relation(fields: [couponId], references: [id])
orderItems OrderItem[]
paymentTransactions PaymentTransaction[]
inventoryReservations InventoryReservation[]
@@map("orders")
}
@ -346,6 +348,10 @@ model PaymentTransaction {
message String? @db.Text
description String? @db.Text
type String @default("ORDER") @db.VarChar(30) // ORDER, WALLET_TOPUP
ipAddress String? @map("ip_address") @db.VarChar(50)
userAgent String? @map("user_agent") @db.Text
rawRequest Json? @map("raw_request")
rawResponse Json? @map("raw_response")
paidAt DateTime? @map("paid_at") @db.Timestamptz()
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz()
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz()
@ -359,6 +365,26 @@ model PaymentTransaction {
@@map("payment_transactions")
}
model InventoryReservation {
id String @id @default(uuid()) @db.Uuid
orderId String @map("order_id") @db.Uuid
productId String @map("product_id") @db.Uuid
quantity Int @default(1)
expiresAt DateTime @map("expires_at") @db.Timestamptz()
status String @default("ACTIVE") @db.VarChar(20) // ACTIVE, CONSUMED, RELEASED
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz()
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz()
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
@@index([orderId])
@@index([productId])
@@index([status])
@@index([expiresAt])
@@map("inventory_reservations")
}
model OrderItem {
id String @id @default(uuid()) @db.Uuid
orderId String @map("order_id") @db.Uuid

View File

@ -244,6 +244,7 @@ export class OrdersService {
const isOnline = createOrderDto.paymentMethod === 'online';
const initialStatus = isOnline ? 'pending_payment' : 'processing';
const reservationExpiry = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes hold
const createdOrder = await this.prisma.order.create({
data: {
@ -260,11 +261,20 @@ export class OrdersService {
orderItems: {
create: orderItemsData,
},
inventoryReservations: {
create: orderItemsData.map((item) => ({
productId: item.productId,
quantity: item.quantity,
expiresAt: reservationExpiry,
status: isOnline ? 'ACTIVE' : 'CONSUMED',
})),
},
},
include: {
orderItems: {
include: { product: true },
},
inventoryReservations: true,
},
});
@ -391,4 +401,25 @@ export class OrdersService {
}
}
}
/**
* Release inventory reservations that expired without successful payment
*/
async releaseExpiredInventoryReservations() {
const now = new Date();
const expiredReservations = await this.prisma.inventoryReservation.updateMany({
where: {
status: 'ACTIVE',
expiresAt: { lte: now },
},
data: {
status: 'RELEASED',
},
});
return {
releasedCount: expiredReservations.count,
executedAt: now,
};
}
}

View File

@ -0,0 +1,55 @@
export interface PaymentRequestOptions {
amountRials: number;
callbackUrl: string;
orderId?: string;
mobile?: string;
description?: string;
}
export interface PaymentRequestResult {
result: number;
trackId: string | number;
message?: string;
paymentUrl?: string;
}
export interface PaymentVerifyResult {
result: number;
refNumber?: string;
cardNumber?: string;
amount?: number;
paidAt?: string;
message?: string;
}
export interface PaymentInquiryResult {
result: number;
status?: number;
amount?: number;
refNumber?: string;
cardNumber?: string;
paidAt?: string;
message?: string;
}
export interface GatewayHealthResult {
gatewayName: string;
status: 'ONLINE' | 'DEGRADED' | 'OFFLINE';
latencyMs: number;
merchantConfigured: boolean;
activeMerchant: string;
message: string;
checkedAt: Date;
}
/**
* Standard Payment Gateway Interface (Strategy / Gateway Adapter Pattern)
*/
export interface IPaymentGateway {
getGatewayName(): string;
requestPayment(options: PaymentRequestOptions): Promise<PaymentRequestResult>;
verifyPayment(trackId: string | number): Promise<PaymentVerifyResult>;
inquiryPayment(trackId: string | number): Promise<PaymentInquiryResult>;
getStartUrl(trackId: string | number): string;
checkHealth(): Promise<GatewayHealthResult>;
}

View File

@ -9,8 +9,10 @@ import {
Req,
Res,
HttpStatus,
Headers,
Ip,
} from '@nestjs/common';
import type { Response } from 'express';
import type { Request, Response } from 'express';
import { PaymentService } from './payment.service';
import { ZibalService } from './zibal.service';
import {
@ -44,15 +46,6 @@ export class PaymentController {
@ApiOperation({ summary: 'شروع فرایند پرداخت آنلاین سفارش با درگاه زیبال' })
@ApiOkResponse({
description: 'لینک هدایت به درگاه پرداخت زیبال با موفقیت تولید شد',
schema: {
example: {
success: true,
trackId: '15966442233311',
paymentUrl: 'https://gateway.zibal.ir/start/15966442233311',
orderId: 'e1d2c3b4-1234-5678-abcd-ef1234567890',
amount: 350000,
},
},
})
@ApiBadRequestResponse({
description: 'خطا در ایجاد تراکنش یا نامعتبر بودن سفارش',
@ -60,11 +53,14 @@ export class PaymentController {
async initiateOrderPayment(
@Req() req: { user: { id: string } },
@Body() body: InitiatePaymentDto,
@Ip() ip: string,
@Headers('user-agent') userAgent?: string,
) {
return this.paymentService.initiateOrderPayment(
req.user.id,
body.orderId,
body.customCallbackUrl,
{ ipAddress: ip, userAgent },
);
}
@ -78,11 +74,14 @@ export class PaymentController {
async initiateWalletTopup(
@Req() req: { user: { id: string } },
@Body() body: InitiateWalletTopupDto,
@Ip() ip: string,
@Headers('user-agent') userAgent?: string,
) {
return this.paymentService.initiateWalletTopup(
req.user.id,
Number(body.amount),
body.customCallbackUrl,
{ ipAddress: ip, userAgent },
);
}
@ -93,6 +92,8 @@ export class PaymentController {
async handleZibalCallback(
@Query() query: ZibalCallbackQueryDto,
@Res() res: Response,
@Ip() ip: string,
@Headers('user-agent') userAgent?: string,
) {
const trackId = query.trackId || '';
const success = query.success;
@ -113,6 +114,7 @@ export class PaymentController {
success,
status,
orderId,
{ ipAddress: ip, userAgent },
);
const params = new URLSearchParams({
@ -141,6 +143,8 @@ export class PaymentController {
async handleZibalLazyCallback(
@Body() body: ZibalCallbackQueryDto,
@Res() res: Response,
@Ip() ip: string,
@Headers('user-agent') userAgent?: string,
) {
const trackId = body.trackId || '';
const success = body.success;
@ -158,6 +162,7 @@ export class PaymentController {
success,
status,
orderId,
{ ipAddress: ip, userAgent },
);
return res.status(HttpStatus.OK).json(result);
@ -169,7 +174,7 @@ export class PaymentController {
return this.paymentService.getTransaction(trackId);
}
// --- ADMIN ENDPOINTS FOR TRANSACTION LOGS & DIAGNOSTICS ---
// --- ADMIN ENDPOINTS FOR TRANSACTION LOGS, DIAGNOSTICS & HEALTH CHECK ---
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('Admin')
@ -206,4 +211,13 @@ export class PaymentController {
async liveInquiry(@Param('id') id: string) {
return this.paymentService.adminLiveInquiry(id);
}
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('Admin')
@ApiBearerAuth()
@Get('admin/health')
@ApiOperation({ summary: 'بررسی وضعیت آنلاین بودن و سلامت درگاه پرداخت (Health Check & Latency)' })
async getHealth() {
return this.paymentService.checkGatewayHealth();
}
}

View File

@ -10,6 +10,11 @@ import { SmsService } from '../common/services/sms.service';
import { Prisma } from '@prisma/client';
import { AdminTransactionFilterDto } from './dto/admin-transaction-filter.dto';
export interface ClientMetadata {
ipAddress?: string;
userAgent?: string;
}
@Injectable()
export class PaymentService {
private readonly logger = new Logger(PaymentService.name);
@ -27,6 +32,7 @@ export class PaymentService {
userId: string,
orderId: string,
customCallbackUrl?: string,
clientMeta?: ClientMetadata,
) {
const order = await this.prisma.order.findFirst({
where: { id: orderId, userId },
@ -54,6 +60,14 @@ export class PaymentService {
const callbackUrl =
customCallbackUrl || `${frontendUrl}/payment/verify?orderId=${order.id}`;
const requestPayload = {
amountRials,
callbackUrl,
orderId: order.id,
mobile: order.user?.mobile || undefined,
description: `پرداخت سفارش ${order.trackingNumber || ''} - پت‌شاپ کانینا`,
};
// Create a pending PaymentTransaction in database
const transaction = await this.prisma.paymentTransaction.create({
data: {
@ -65,17 +79,14 @@ export class PaymentService {
status: 'PENDING',
type: 'ORDER',
description: `پرداخت آنلاین سفارش ${order.trackingNumber || order.id}`,
ipAddress: clientMeta?.ipAddress,
userAgent: clientMeta?.userAgent,
rawRequest: requestPayload as unknown as Prisma.InputJsonValue,
},
});
try {
const zibalRes = await this.zibalService.requestPayment({
amountRials,
callbackUrl,
orderId: order.id,
mobile: order.user?.mobile || undefined,
description: `پرداخت سفارش ${order.trackingNumber || ''} - پت‌شاپ کانینا`,
});
const zibalRes = await this.zibalService.requestPayment(requestPayload);
if (zibalRes.result !== 100) {
const errorMsg = this.zibalService.getResultMessage(zibalRes.result);
@ -85,6 +96,7 @@ export class PaymentService {
status: 'FAILED',
resultCode: zibalRes.result,
message: errorMsg,
rawResponse: zibalRes as unknown as Prisma.InputJsonValue,
},
});
throw new BadRequestException(`خطا در ایجاد تراکنش زیبال: ${errorMsg}`);
@ -99,6 +111,7 @@ export class PaymentService {
trackId,
resultCode: zibalRes.result,
message: zibalRes.message,
rawResponse: zibalRes as unknown as Prisma.InputJsonValue,
},
});
@ -126,6 +139,7 @@ export class PaymentService {
userId: string,
amountTomans: number,
customCallbackUrl?: string,
clientMeta?: ClientMetadata,
) {
if (amountTomans < 1000) {
throw new BadRequestException('حداقل مبلغ افزایش موجودی ۱,۰۰۰ تومان است');
@ -141,6 +155,13 @@ export class PaymentService {
const callbackUrl =
customCallbackUrl || `${frontendUrl}/payment/verify?type=wallet`;
const requestPayload = {
amountRials,
callbackUrl,
mobile: user.mobile || undefined,
description: `شارژ کیف پول - پت شاپ کانینا`,
};
const transaction = await this.prisma.paymentTransaction.create({
data: {
userId,
@ -150,16 +171,14 @@ export class PaymentService {
status: 'PENDING',
type: 'WALLET_TOPUP',
description: `افزایش موجودی کیف پول کاربر ${user.mobile}`,
ipAddress: clientMeta?.ipAddress,
userAgent: clientMeta?.userAgent,
rawRequest: requestPayload as unknown as Prisma.InputJsonValue,
},
});
try {
const zibalRes = await this.zibalService.requestPayment({
amountRials,
callbackUrl,
mobile: user.mobile || undefined,
description: `شارژ کیف پول - پت شاپ کانینا`,
});
const zibalRes = await this.zibalService.requestPayment(requestPayload);
if (zibalRes.result !== 100) {
const errorMsg = this.zibalService.getResultMessage(zibalRes.result);
@ -169,6 +188,7 @@ export class PaymentService {
status: 'FAILED',
resultCode: zibalRes.result,
message: errorMsg,
rawResponse: zibalRes as unknown as Prisma.InputJsonValue,
},
});
throw new BadRequestException(`خطا در ایجاد تراکنش زیبال: ${errorMsg}`);
@ -182,6 +202,7 @@ export class PaymentService {
trackId,
resultCode: zibalRes.result,
message: zibalRes.message,
rawResponse: zibalRes as unknown as Prisma.InputJsonValue,
},
});
@ -199,13 +220,14 @@ export class PaymentService {
}
/**
* 3. Verify & Process Callback from Zibal (With Idempotency & Amount Match Check)
* 3. Verify & Process Callback from Zibal (With Idempotency, Amount Match & Inventory Hold Release/Consume)
*/
async verifyAndProcess(
trackId: string,
successParam?: string,
statusParam?: string,
orderIdParam?: string,
clientMeta?: ClientMetadata,
) {
this.logger.log(
`Processing callback for trackId=${trackId}, success=${successParam}, status=${statusParam}, orderId=${orderIdParam}`,
@ -245,8 +267,6 @@ export class PaymentService {
}
// --- IDEMPOTENCY CHECK ---
// If transaction is already marked as VERIFIED, return stored confirmation immediately
// without executing any duplicate side-effects (wallet increment, charity, status change, SMS).
if (transaction.status === 'VERIFIED') {
this.logger.log(
`Idempotency Guard: Transaction ${transaction.id} (trackId=${trackId}) is already VERIFIED. Skipping duplicate execution.`,
@ -275,9 +295,19 @@ export class PaymentService {
data: {
status: 'FAILED',
message: statusMessage,
ipAddress: clientMeta?.ipAddress || transaction.ipAddress,
userAgent: clientMeta?.userAgent || transaction.userAgent,
},
});
// Release any active inventory reservation for this order
if (transaction.orderId) {
await this.prisma.inventoryReservation.updateMany({
where: { orderId: transaction.orderId, status: 'ACTIVE' },
data: { status: 'RELEASED' },
});
}
return {
success: false,
status: 'FAILED',
@ -301,9 +331,18 @@ export class PaymentService {
resultCode: verifyRes.result,
message: errorMsg,
cardNumber: verifyRes.cardNumber,
rawResponse: verifyRes as unknown as Prisma.InputJsonValue,
},
});
// Release inventory reservations on failure
if (transaction.orderId) {
await this.prisma.inventoryReservation.updateMany({
where: { orderId: transaction.orderId, status: 'ACTIVE' },
data: { status: 'RELEASED' },
});
}
return {
success: false,
status: 'FAILED',
@ -335,6 +374,7 @@ export class PaymentService {
status: 'FAILED',
resultCode: verifyRes.result,
message: `مغایرت مالی: مبلغ تایید شده زیبال (${verifyRes.amount} ریال) با مبلغ سیستم (${expectedAmountRials} ریال) مطابقت ندارد.`,
rawResponse: verifyRes as unknown as Prisma.InputJsonValue,
},
});
@ -358,7 +398,7 @@ export class PaymentService {
// Execute atomic status update with concurrency guard
await this.prisma.$transaction(async (tx) => {
// Optimistic lock: only update if still PENDING (prevents race condition)
// Optimistic lock: only update if still PENDING
const updatedCount = await tx.paymentTransaction.updateMany({
where: { id: transaction.id, status: 'PENDING' },
data: {
@ -368,10 +408,12 @@ export class PaymentService {
cardNumber,
paidAt: paidDate,
message: 'پرداخت با موفقیت انجام و تایید شد',
rawResponse: verifyRes as unknown as Prisma.InputJsonValue,
ipAddress: clientMeta?.ipAddress || transaction.ipAddress,
userAgent: clientMeta?.userAgent || transaction.userAgent,
},
});
// If another concurrent thread verified it first, exit safely
if (updatedCount.count === 0) {
this.logger.warn(
`Concurrent execution detected for transaction ${transaction.id}. Handled safely.`,
@ -379,7 +421,7 @@ export class PaymentService {
return;
}
// 2. If it's an Order payment
// 2. If it's an Order payment -> Mark processing & Consume Inventory Reservation
if (transaction.type === 'ORDER' && transaction.orderId) {
await tx.order.update({
where: { id: transaction.orderId },
@ -389,6 +431,12 @@ export class PaymentService {
},
});
// Mark inventory reservations as CONSUMED
await tx.inventoryReservation.updateMany({
where: { orderId: transaction.orderId, status: 'ACTIVE' },
data: { status: 'CONSUMED' },
});
// If charity donation was made, update user's total charity
if (
transaction.order &&
@ -471,12 +519,11 @@ export class PaymentService {
}
/**
* 4. Auto-Reconciliation / Cron Job for Pending Transactions (استعلام تراکنشهای بلاتکلیف)
* 4. Auto-Reconciliation / Cron Job for Pending Transactions
*/
async reconcilePendingTransactions() {
this.logger.log('Starting pending transactions reconciliation job...');
// Find transactions that are PENDING and created between 5 minutes and 24 hours ago
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
@ -510,12 +557,10 @@ export class PaymentService {
`Inquiry for trackId=${tx.trackId}: status=${inquiry.status}, result=${inquiry.result}`,
);
// Status 1 (paid & verified) or 2 (paid, unverified)
if (inquiry.status === 1 || inquiry.status === 2 || inquiry.result === 100) {
await this.verifyAndProcess(tx.trackId, '1', String(inquiry.status), tx.orderId || undefined);
results.verified++;
} else if (inquiry.status === 3 || inquiry.status === -2 || inquiry.result === 202) {
// Cancelled or failed
const msg = this.zibalService.getStatusMessage(inquiry.status || 3);
await this.prisma.paymentTransaction.update({
where: { id: tx.id },
@ -523,8 +568,16 @@ export class PaymentService {
status: 'FAILED',
resultCode: inquiry.result,
message: msg,
rawResponse: inquiry as unknown as Prisma.InputJsonValue,
},
});
if (tx.orderId) {
await this.prisma.inventoryReservation.updateMany({
where: { orderId: tx.orderId, status: 'ACTIVE' },
data: { status: 'RELEASED' },
});
}
results.failed++;
} else {
results.remainedPending++;
@ -536,6 +589,13 @@ export class PaymentService {
}
}
// Also release all expired reservations
const now = new Date();
await this.prisma.inventoryReservation.updateMany({
where: { status: 'ACTIVE', expiresAt: { lte: now } },
data: { status: 'RELEASED' },
});
this.logger.log(`Reconciliation finished: ${JSON.stringify(results)}`);
return results;
}
@ -572,6 +632,10 @@ export class PaymentService {
gateway: transaction.gateway,
type: transaction.type,
cardNumber: transaction.cardNumber,
ipAddress: transaction.ipAddress,
userAgent: transaction.userAgent,
rawRequest: transaction.rawRequest,
rawResponse: transaction.rawResponse,
paidAt: transaction.paidAt,
createdAt: transaction.createdAt,
order: transaction.order,
@ -760,10 +824,9 @@ export class PaymentService {
}
const inquiryRes = await this.zibalService.inquiryPayment(transaction.trackId);
const statusMsg = this.zibalService.getStatusMessage(inquiryRes.status);
const statusMsg = this.zibalService.getStatusMessage(inquiryRes.status ?? 3);
const resultMsg = this.zibalService.getResultMessage(inquiryRes.result);
// If transaction was PENDING and inquiry confirms payment, sync automatically
if (
transaction.status === 'PENDING' &&
(inquiryRes.status === 1 || inquiryRes.status === 2 || inquiryRes.result === 100)
@ -786,4 +849,11 @@ export class PaymentService {
inquiredAt: new Date(),
};
}
/**
* 9. Gateway Health Check for Admin
*/
async checkGatewayHealth() {
return this.zibalService.checkHealth();
}
}

View File

@ -1,5 +1,13 @@
import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import {
IPaymentGateway,
PaymentRequestOptions,
PaymentRequestResult,
PaymentVerifyResult,
PaymentInquiryResult,
GatewayHealthResult,
} from './interfaces/payment-gateway.interface';
export interface ZibalRequestResponse {
trackId: number;
@ -32,12 +40,51 @@ export interface ZibalInquiryResponse {
}
@Injectable()
export class ZibalService {
export class ZibalService implements IPaymentGateway {
private readonly logger = new Logger(ZibalService.name);
private readonly baseUrl = 'https://gateway.zibal.ir';
constructor(private readonly prisma: PrismaService) {}
getGatewayName(): string {
return 'zibal';
}
/**
* Helper: Execute an HTTP operation with Exponential Backoff Retry (1s, 2s, 4s)
*/
private async callWithRetry<T>(
operation: () => Promise<T>,
operationName: string,
maxRetries = 3,
delays = [1000, 2000, 4000],
): Promise<T> {
let lastError: unknown;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (err: unknown) {
lastError = err;
const msg = err instanceof Error ? err.message : String(err);
this.logger.warn(
`[Zibal Retry] Attempt ${attempt}/${maxRetries} failed for '${operationName}': ${msg}`,
);
if (attempt < maxRetries) {
const delayMs = delays[attempt - 1] || 1000;
this.logger.log(`Waiting ${delayMs}ms before retry attempt ${attempt + 1}...`);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
}
this.logger.error(
`[Zibal Retry] All ${maxRetries} attempts failed for '${operationName}'.`,
);
throw lastError;
}
/**
* Retrieves active merchant code from settings (DB) or fallback to env / 'zibal' sandbox.
*/
@ -76,15 +123,10 @@ export class ZibalService {
}
/**
* 1. Request Payment (درخواست پرداخت)
* 1. Request Payment (درخواست پرداخت با قابلیت Retry)
* Amount must be in Rials.
*/
async requestPayment(params: {
amountRials: number;
callbackUrl: string;
description?: string;
orderId?: string;
mobile?: string;
async requestPayment(params: PaymentRequestOptions & {
nationalCode?: string;
allowedCards?: string[];
}): Promise<ZibalRequestResponse> {
@ -101,42 +143,46 @@ export class ZibalService {
allowedCards: params.allowedCards,
};
this.logger.log(
`Sending payment request to Zibal for orderId=${params.orderId}, amount=${params.amountRials} Rials, merchant=${merchant}`,
);
const response = await fetch(`${this.baseUrl}/v1/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorText = await response.text();
this.logger.error(
`Zibal request HTTP error ${response.status}: ${errorText}`,
return this.callWithRetry(async () => {
this.logger.log(
`Sending payment request to Zibal for orderId=${params.orderId}, amount=${params.amountRials} Rials, merchant=${merchant}`,
);
throw new Error(
`خطا در اتصال به درگاه زیبال: کد وضعیت ${response.status}`,
);
}
const data = (await response.json()) as ZibalRequestResponse;
this.logger.log(`Zibal request response: ${JSON.stringify(data)}`);
return data;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const response = await fetch(`${this.baseUrl}/v1/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
signal: controller.signal,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Zibal request HTTP ${response.status}: ${errorText}`);
}
const data = (await response.json()) as ZibalRequestResponse;
return data;
} finally {
clearTimeout(timeoutId);
}
}, `requestPayment(orderId=${params.orderId})`);
}
/**
* 2. Generate Start Payment URL (آدرس هدایت به درگاه)
* 2. Start URL for user redirect
*/
getStartUrl(trackId: string | number): string {
return `${this.baseUrl}/start/${trackId}`;
}
/**
* 3. Verify Payment (تایید تراکنش)
* 3. Verify Payment (تایید تراکنش با قابلیت Retry)
*/
async verifyPayment(trackId: string | number): Promise<ZibalVerifyResponse> {
const merchant = await this.getMerchant();
@ -146,39 +192,39 @@ export class ZibalService {
trackId: String(trackId),
};
this.logger.log(
`Verifying payment on Zibal for trackId=${trackId}, merchant=${merchant}`,
);
return this.callWithRetry(async () => {
this.logger.log(`Verifying payment for trackId=${trackId}, merchant=${merchant}`);
const response = await fetch(`${this.baseUrl}/v1/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
if (!response.ok) {
const errorText = await response.text();
this.logger.error(
`Zibal verify HTTP error ${response.status}: ${errorText}`,
);
throw new Error(
`خطا در تایید تراکنش درگاه زیبال: کد وضعیت ${response.status}`,
);
}
try {
const response = await fetch(`${this.baseUrl}/v1/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
signal: controller.signal,
});
const data = (await response.json()) as ZibalVerifyResponse;
this.logger.log(`Zibal verify response: ${JSON.stringify(data)}`);
return data;
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Zibal verify HTTP ${response.status}: ${errorText}`);
}
const data = (await response.json()) as ZibalVerifyResponse;
return data;
} finally {
clearTimeout(timeoutId);
}
}, `verifyPayment(trackId=${trackId})`);
}
/**
* 4. Inquiry Payment (استعلام وضعیت تراکنش)
* 4. Inquiry Payment (استعلام تراکنش با قابلیت Retry)
*/
async inquiryPayment(
trackId: string | number,
): Promise<ZibalInquiryResponse> {
async inquiryPayment(trackId: string | number): Promise<ZibalInquiryResponse> {
const merchant = await this.getMerchant();
const payload = {
@ -186,78 +232,137 @@ export class ZibalService {
trackId: String(trackId),
};
this.logger.log(`Inquiring payment on Zibal for trackId=${trackId}`);
return this.callWithRetry(async () => {
this.logger.log(`Inquiring payment for trackId=${trackId}`);
const response = await fetch(`${this.baseUrl}/v1/inquiry`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
if (!response.ok) {
const errorText = await response.text();
this.logger.error(
`Zibal inquiry HTTP error ${response.status}: ${errorText}`,
);
throw new Error(
`خطا در استعلام تراکنش زیبال: کد وضعیت ${response.status}`,
);
try {
const response = await fetch(`${this.baseUrl}/v1/inquiry`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
signal: controller.signal,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Zibal inquiry HTTP ${response.status}: ${errorText}`);
}
const data = (await response.json()) as ZibalInquiryResponse;
return data;
} finally {
clearTimeout(timeoutId);
}
}, `inquiryPayment(trackId=${trackId})`);
}
/**
* 5. Health Check & Latency Monitor for Admin
*/
async checkHealth(): Promise<GatewayHealthResult> {
const merchant = await this.getMerchant();
const startTime = Date.now();
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const res = await fetch(`${this.baseUrl}/v1/inquiry`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ merchant, trackId: '1' }),
signal: controller.signal,
});
clearTimeout(timeoutId);
const latencyMs = Date.now() - startTime;
if (res.ok || res.status === 400 || res.status === 200) {
return {
gatewayName: 'Zibal IPG',
status: latencyMs < 1500 ? 'ONLINE' : 'DEGRADED',
latencyMs,
merchantConfigured: merchant !== 'zibal',
activeMerchant: merchant === 'zibal' ? 'zibal (تستی/سندباکس)' : `${merchant.slice(0, 4)}****`,
message: 'اتصال به درگاه زیبال با موفقیت برقرار است',
checkedAt: new Date(),
};
}
return {
gatewayName: 'Zibal IPG',
status: 'DEGRADED',
latencyMs,
merchantConfigured: merchant !== 'zibal',
activeMerchant: merchant,
message: `پاسخ غیرعادی سرور زیبال (${res.status})`,
checkedAt: new Date(),
};
} catch (err: unknown) {
const latencyMs = Date.now() - startTime;
const msg = err instanceof Error ? err.message : String(err);
return {
gatewayName: 'Zibal IPG',
status: 'OFFLINE',
latencyMs,
merchantConfigured: merchant !== 'zibal',
activeMerchant: merchant,
message: `عدم دسترسی به درگاه: ${msg}`,
checkedAt: new Date(),
};
}
const data = (await response.json()) as ZibalInquiryResponse;
return data;
}
/**
* Translates status codes from Zibal to Persian human-friendly messages
* Human-friendly Persian messages for Zibal result codes
*/
getStatusMessage(status?: number | string): string {
const statusNum = Number(status);
getResultMessage(resultCode: number): string {
const messages: Record<number, string> = {
[-1]: 'در انتظار پرداخت',
[-2]: 'خطای داخلی درگاه زیبال',
1: 'پرداخت شده و تایید شده',
2: 'پرداخت شده - در انتظار تایید',
3: 'تراکنش توسط کاربر لغو شد',
4: 'شماره کارت نامعتبر است',
5: 'موجودی حساب کافی نیست',
6: 'رمز وارد شده اشتباه است',
7: 'تعداد درخواست‌ها بیش از حد مجاز است',
8: 'تعداد پرداخت اینترنتی روزانه بیش از حد مجاز است',
9: 'مبلغ پرداخت اینترنتی روزانه بیش از حد مجاز است',
10: 'صادرکننده کارت نامعتبر است',
11: 'خطای سوئیچ بانکی',
12: 'کارت قابل دسترسی نیست',
15: 'تراکنش استرداد شده است',
16: 'تراکنش در حال استرداد است',
18: 'تراکنش ریورس شده است',
21: 'پذیرنده نامعتبر است',
100: 'با موفقیت تایید شد',
102: 'merchant یافت نشد',
103: 'merchant غیرفعال است',
104: 'merchant نامعتبر است',
105: 'مبلغ باید بیشتر از ۱,۰۰۰ ریال باشد',
106: 'callbackUrl نامعتبر است',
113: 'مبلغ تراکنش بیش از سقف مجاز است',
201: 'تراکنش قبلا تایید شده است',
202: 'سفارش پرداخت نشده یا ناموفق بوده است',
203: 'trackId نامعتبر است',
};
return messages[statusNum] || 'وضعیت نامشخص';
return (
messages[resultCode] ||
`خطای ناشناخته در ارتباط با درگاه (کد ${resultCode})`
);
}
/**
* Translates verify result codes to Persian messages
* Human-friendly Persian messages for Zibal status codes
*/
getResultMessage(result?: number): string {
getStatusMessage(statusCode: number | string): string {
const code = Number(statusCode);
const messages: Record<number, string> = {
100: 'با موفقیت تایید شد.',
102: 'مرچنت یافت نشد.',
103: 'مرچنت غیرفعال است یا قرارداد درگاه امضا نشده است.',
104: 'مرچنت نامعتبر است.',
105: 'مبلغ باید بزرگتر از ۱,۰۰۰ ریال باشد.',
106: 'آدرس بازگشت (callbackUrl) نامعتبر است.',
113: 'مبلغ تراکنش از سقف مجاز بیشتر است.',
114: 'کد ملی ارسالی نامعتبر است.',
115: 'آی‌پی سرور شما در پنل کاربری زیبال ثبت نشده است.',
201: 'تراکنش قبلا تایید شده است.',
202: 'سفارش پرداخت نشده یا ناموفق بوده است.',
203: 'شناسه پیگیری (trackId) نامعتبر است.',
'-1': 'در انتظار پردخت',
'-2': 'خطای داخلی درگاه',
'1': 'پرداخت شده و تایید شده',
'2': 'پرداخت شده اما هنوز تایید نشده',
'3': 'تراکنش توسط کاربر لغو شد',
'4': 'شماره کارت نامعتبر است',
'5': 'موجودی حساب کافی نیست',
'6': 'رمز کارت اشتباه است',
'7': 'تعداد دفعات ورود رمز غلط بیش از حد مجاز است',
'8': 'کارت منقضی شده است',
'9': 'مبلغ تراکنش بیش از سقف مجاز کارت است',
'10': 'صادرکننده کارت نامعتبر است',
'11': 'خطای سوییچ بانک صادرکننده',
'12': 'کارت یا حساب مسدود است',
};
return messages[result ?? 0] || 'نتیجه نامشخص تراکنش';
return messages[code] || `وضعیت نامشخص (${statusCode})`;
}
}

View File

@ -18,7 +18,11 @@ import {
ArrowUpDown,
ExternalLink,
ShieldCheck,
RotateCcw
RotateCcw,
Activity,
Globe,
Monitor,
Code
} from 'lucide-react';
import { toast } from 'react-hot-toast';
import api from '../services/api';
@ -40,6 +44,10 @@ interface Transaction {
message?: string | null;
description?: string | null;
type: string;
ipAddress?: string | null;
userAgent?: string | null;
rawRequest?: any;
rawResponse?: any;
paidAt?: string | null;
createdAt: string;
user?: {
@ -68,9 +76,21 @@ interface Stats {
successRate: number;
}
interface GatewayHealth {
gatewayName: string;
status: 'ONLINE' | 'DEGRADED' | 'OFFLINE';
latencyMs: number;
merchantConfigured: boolean;
activeMerchant: string;
message: string;
checkedAt: string;
}
export default function Transactions() {
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [stats, setStats] = useState<Stats | null>(null);
const [health, setHealth] = useState<GatewayHealth | null>(null);
const [healthLoading, setHealthLoading] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [isReconciling, setIsReconciling] = useState(false);
const [page, setPage] = useState(1);
@ -89,6 +109,7 @@ export default function Transactions() {
const [selectedTx, setSelectedTx] = useState<Transaction | null>(null);
const [liveInquiryLoading, setLiveInquiryLoading] = useState(false);
const [liveInquiryData, setLiveInquiryData] = useState<any>(null);
const [showRawLogs, setShowRawLogs] = useState(false);
const fetchStats = async () => {
try {
@ -101,6 +122,22 @@ export default function Transactions() {
}
};
const fetchHealth = async (silent = false) => {
try {
if (!silent) setHealthLoading(true);
const res = await api.get('/payment/admin/health');
if (res.data) {
setHealth(res.data);
if (!silent) toast.success(`وضعیت درگاه زیبال: ${res.data.status} (${res.data.latencyMs}ms)`);
}
} catch (e) {
console.error('Failed to fetch gateway health', e);
if (!silent) toast.error('عدم دسترسی به سرویس Health Check درگاه');
} finally {
if (!silent) setHealthLoading(false);
}
};
const fetchTransactions = useCallback(async () => {
try {
setIsLoading(true);
@ -131,6 +168,7 @@ export default function Transactions() {
useEffect(() => {
fetchTransactions();
fetchStats();
fetchHealth(true);
}, [fetchTransactions]);
const handleCopy = (text: string, title: string) => {
@ -143,7 +181,7 @@ export default function Transactions() {
setIsReconciling(true);
const res = await api.post('/payment/admin/reconcile');
toast.success(
`انطباق تراکنش‌ها انجام شد: ${res.data.verified} تایید شده، ${res.data.failed} ناموفق`,
`انطباق انجام شد: ${res.data.verified} تایید شده، ${res.data.failed} ناموفق`,
);
fetchTransactions();
fetchStats();
@ -181,20 +219,63 @@ export default function Transactions() {
لاگها و مدیریت تراکنشهای مالی
</h2>
<p className="text-gray-500 font-medium mt-1">
رهگیری لحظهای پرداختهای آنلاین زیبال، کارت به کارت، کیف پول و عیبیابی تراکنشها
رهگیری لحظهای پرداختهای آنلاین زیبال، بررسی لاگهای خام و عیبیابی تراکنشها
</p>
</div>
<button
onClick={handleReconcile}
disabled={isReconciling}
className="bg-indigo-50 text-indigo-700 hover:bg-indigo-100 border border-indigo-200 text-xs font-bold px-4 py-2.5 rounded-xl transition-all flex items-center gap-2 shadow-sm disabled:opacity-50"
>
<RotateCcw className={`w-4 h-4 ${isReconciling ? 'animate-spin' : ''}`} />
<span>{isReconciling ? 'در حال استعلام...' : 'انطباق خودکار تراکنش‌های معلق'}</span>
</button>
<div className="flex items-center gap-2 flex-wrap">
<button
onClick={() => fetchHealth(false)}
disabled={healthLoading}
className="bg-purple-50 text-purple-700 hover:bg-purple-100 border border-purple-200 text-xs font-bold px-3.5 py-2.5 rounded-xl transition-all flex items-center gap-2 shadow-sm disabled:opacity-50"
>
<Activity className={`w-4 h-4 text-purple-600 ${healthLoading ? 'animate-spin' : ''}`} />
<span>پایش سلامت درگاه (Ping)</span>
</button>
<button
onClick={handleReconcile}
disabled={isReconciling}
className="bg-indigo-50 text-indigo-700 hover:bg-indigo-100 border border-indigo-200 text-xs font-bold px-3.5 py-2.5 rounded-xl transition-all flex items-center gap-2 shadow-sm disabled:opacity-50"
>
<RotateCcw className={`w-4 h-4 ${isReconciling ? 'animate-spin' : ''}`} />
<span>{isReconciling ? 'در حال استعلام...' : 'انطباق خودکار تراکنش‌ها'}</span>
</button>
</div>
</div>
{/* Gateway Health Monitor Banner */}
{health && (
<div className="bg-gradient-to-r from-slate-900 via-indigo-950 to-purple-950 p-4 rounded-2xl text-white shadow-md flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 border border-indigo-800/40">
<div className="flex items-center gap-3">
<div className="relative flex items-center justify-center">
<span className={`w-3.5 h-3.5 rounded-full ${health.status === 'ONLINE' ? 'bg-emerald-500 animate-ping' : health.status === 'DEGRADED' ? 'bg-amber-500' : 'bg-rose-500'}`} />
<span className={`absolute w-2.5 h-2.5 rounded-full ${health.status === 'ONLINE' ? 'bg-emerald-400' : health.status === 'DEGRADED' ? 'bg-amber-400' : 'bg-rose-400'}`} />
</div>
<div>
<div className="flex items-center gap-2">
<span className="font-black text-sm">{health.gatewayName}</span>
<span className={`text-[10px] font-black px-2 py-0.5 rounded-full ${health.status === 'ONLINE' ? 'bg-emerald-500/20 text-emerald-300 border border-emerald-500/30' : 'bg-amber-500/20 text-amber-300 border border-amber-500/30'}`}>
{health.status}
</span>
</div>
<p className="text-[11px] text-gray-300 mt-0.5">{health.message}</p>
</div>
</div>
<div className="flex items-center gap-4 text-xs font-mono text-gray-300 mr-auto sm:mr-0">
<div className="bg-white/10 px-3 py-1.5 rounded-xl border border-white/10 flex items-center gap-1.5">
<Clock className="w-3.5 h-3.5 text-purple-300" />
<span>تاخیر: <strong>{health.latencyMs} ms</strong></span>
</div>
<div className="bg-white/10 px-3 py-1.5 rounded-xl border border-white/10 flex items-center gap-1.5">
<ShieldCheck className="w-3.5 h-3.5 text-emerald-300" />
<span>مرچنت: <strong>{health.activeMerchant}</strong></span>
</div>
</div>
</div>
)}
{/* Summary Stat Cards */}
{stats && (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
@ -488,6 +569,7 @@ export default function Transactions() {
onClick={() => {
setSelectedTx(tx);
setLiveInquiryData(null);
setShowRawLogs(false);
}}
className="p-2 bg-gray-100 hover:bg-purple-50 text-gray-600 hover:text-purple-600 rounded-xl transition-all"
title="مشاهده جزئیات و عیب‌یابی"
@ -598,6 +680,24 @@ export default function Transactions() {
{selectedTx.cardNumber || 'ثبت نشده'}
</span>
</div>
{selectedTx.ipAddress && (
<div>
<span className="text-gray-400 block font-medium">IP کلاینت:</span>
<span className="font-mono font-bold text-gray-700 dir-ltr inline-block">
{selectedTx.ipAddress}
</span>
</div>
)}
{selectedTx.userAgent && (
<div className="sm:col-span-2">
<span className="text-gray-400 block font-medium">User-Agent دستگاه:</span>
<span className="text-[10px] font-mono text-gray-600 line-clamp-1 dir-ltr inline-block">
{selectedTx.userAgent}
</span>
</div>
)}
</div>
{/* Error Message / Reason */}
@ -661,12 +761,48 @@ export default function Transactions() {
)}
</div>
{/* Raw Request & Response Inspector Toggle */}
<div className="border border-gray-200 rounded-2xl overflow-hidden">
<button
type="button"
onClick={() => setShowRawLogs(!showRawLogs)}
className="w-full bg-gray-50 hover:bg-gray-100 px-4 py-3 text-xs font-bold text-gray-700 flex items-center justify-between transition-colors"
>
<span className="flex items-center gap-2">
<Code className="w-4 h-4 text-purple-600" />
مشاهده لاگ خام درخواست و پاسخ (Raw JSON Payload)
</span>
<span>{showRawLogs ? '▲ بستن' : '▼ باز کردن'}</span>
</button>
{showRawLogs && (
<div className="p-4 bg-slate-950 text-emerald-400 text-[11px] font-mono space-y-4 dir-ltr overflow-x-auto">
{selectedTx.rawRequest && (
<div>
<p className="text-purple-400 font-bold mb-1">// Raw Request:</p>
<pre className="p-2 bg-slate-900 rounded-lg">{JSON.stringify(selectedTx.rawRequest, null, 2)}</pre>
</div>
)}
{selectedTx.rawResponse && (
<div>
<p className="text-cyan-400 font-bold mb-1">// Raw Response:</p>
<pre className="p-2 bg-slate-900 rounded-lg">{JSON.stringify(selectedTx.rawResponse, null, 2)}</pre>
</div>
)}
{!selectedTx.rawRequest && !selectedTx.rawResponse && (
<p className="text-gray-500">// لاگ خامی برای این تراکنش ثبت نشده است.</p>
)}
</div>
)}
</div>
{/* Close Button */}
<div className="flex justify-end">
<button
onClick={() => {
setSelectedTx(null);
setLiveInquiryData(null);
setShowRawLogs(false);
}}
className="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold px-6 py-2.5 rounded-xl text-xs transition-colors"
>

View File

@ -12,15 +12,15 @@
"10": "SmartAdvisor.tsx",
"11": "compilerOptions",
"12": "toPersian",
"13": "SmsService",
"13": "ZibalService",
"14": "ProductsService",
"15": "lib/services/api.ts",
"16": "eslint",
"17": "useSettingsStore",
"18": "AppModule",
"18": "MetricsController",
"19": "B2B Inquiry Controller",
"20": "Category Management Controller",
"21": "auth.service.ts",
"20": "CategoriesController",
"21": "RedisService",
"22": "MediaController",
"23": "Ingredient Management Controller",
"24": "adminRoutes.tsx",
@ -32,20 +32,20 @@
"30": "src/services/api.ts",
"31": "Spinner.tsx",
"32": "useCartStore",
"33": "ZibalService",
"33": "PaymentController",
"34": "main.ts",
"35": "ContactService",
"35": "admin.ts",
"36": "Backend TypeScript Config",
"37": "App TypeScript Config",
"38": "Coupons.tsx",
"38": "Products.tsx",
"39": "dependencies",
"40": "Node TypeScript Config",
"41": "Admin Blog Controller",
"42": "WholesaleApplyDto",
"42": "WholesaleService",
"43": "devDependencies",
"44": "Generic CRUD Controller",
"45": "seo.module.ts",
"46": "OrdersController",
"46": "PaginationDto",
"47": "Project Build Scripts",
"48": "Home Data Module",
"49": "devDependencies",
@ -60,10 +60,10 @@
"58": "Pet Management API",
"59": "PrismaService",
"60": "Jest Testing Config",
"61": "PaginationDto",
"62": "SettingsService",
"61": "WikiController",
"62": "api",
"63": "FE-001",
"64": "payment.module.ts",
"64": "zibal.service.ts",
"65": "rules/graphify.md",
"66": "App Health Controller",
"67": "dependencies",
@ -76,7 +76,7 @@
"74": "BlogsController",
"75": "React Error Boundary",
"76": "Application Package Config",
"77": "WikiController",
"77": ".findAll",
"78": "Error and Not Found Pages",
"79": "VPN Utility Scripts",
"80": "NestJS CLI Config",
@ -119,7 +119,7 @@
"117": "WikiService",
"118": "TS-001",
"119": "TEST-001",
"120": "ValidateCouponDto",
"120": "AdminTransactionFilterDto",
"121": "@types/react-dom",
"122": "Admin Panel TSConfig",
"123": "About Page Component",
@ -283,5 +283,11 @@
"281": "tsconfig-paths",
"282": "@types/bcryptjs",
"283": "typescript-eslint",
"284": "globals"
"284": "wholesale.controller.ts",
"285": "InitiatePaymentDto",
"286": "GetProductsDto",
"287": "Coupons.tsx",
"288": "SmsSettingsPage.tsx",
"289": "ZibalCallbackQueryDto",
"290": "@tailwindcss/postcss"
}

File diff suppressed because one or more lines are too long

View File

@ -1,31 +1,31 @@
{
"0": "AdminService",
"1": "PetsController",
"1": "pets/pets.controller.ts",
"2": "Roles",
"3": "UsersService",
"4": "CmsController",
"5": "BannersService",
"6": "auth.service.ts",
"6": "auth.controller.ts",
"7": "app.module.ts",
"8": "CreateVideoDto",
"9": "ProductService",
"10": "SmartAdvisor.tsx",
"11": "compilerOptions",
"12": "toPersian",
"13": "pets/pets.controller.ts",
"13": "SmsService",
"14": "ProductsService",
"15": "lib/services/api.ts",
"16": "devDependencies",
"16": "eslint",
"17": "useSettingsStore",
"18": "MetricsController",
"18": "AppModule",
"19": "B2B Inquiry Controller",
"20": "Category Management Controller",
"21": "RedisService",
"21": "auth.service.ts",
"22": "MediaController",
"23": "Ingredient Management Controller",
"24": "adminRoutes.tsx",
"25": "admin.module.ts",
"26": "api",
"26": "ConfirmModal.tsx",
"27": "Prescription Review Controller",
"28": "Smart Advisor Controller",
"29": "Testimonials Management Controller",
@ -34,18 +34,18 @@
"32": "useCartStore",
"33": "ZibalService",
"34": "main.ts",
"35": "payment.controller.ts",
"35": "ContactService",
"36": "Backend TypeScript Config",
"37": "App TypeScript Config",
"38": "ConfirmModal.tsx",
"38": "Coupons.tsx",
"39": "dependencies",
"40": "Node TypeScript Config",
"41": "Admin Blog Controller",
"42": "WholesaleService",
"42": "WholesaleApplyDto",
"43": "devDependencies",
"44": "Generic CRUD Controller",
"45": "seo.module.ts",
"46": "Coupon Management UI",
"46": "OrdersController",
"47": "Project Build Scripts",
"48": "Home Data Module",
"49": "devDependencies",
@ -61,9 +61,9 @@
"59": "PrismaService",
"60": "Jest Testing Config",
"61": "PaginationDto",
"62": "WikiController",
"62": "SettingsService",
"63": "FE-001",
"64": "PetsService",
"64": "payment.module.ts",
"65": "rules/graphify.md",
"66": "App Health Controller",
"67": "dependencies",
@ -73,10 +73,10 @@
"71": "Analytics and Report Charts",
"72": "Task Orchestration Scripts",
"73": "Backend Package Config",
"74": "CreateHealthLogDto",
"74": "BlogsController",
"75": "React Error Boundary",
"76": "Application Package Config",
"77": ".findAll",
"77": "WikiController",
"78": "Error and Not Found Pages",
"79": "VPN Utility Scripts",
"80": "NestJS CLI Config",
@ -92,7 +92,7 @@
"90": "Blog Listing Page",
"91": "Blog Post Detail Page",
"92": "@types/node",
"93": "typescript",
"93": "devDependencies",
"94": "UI Text Seeding",
"95": "Wiki Terms Seeding",
"96": "Blog Management DTOs",
@ -102,7 +102,7 @@
"100": "Network Status Banner",
"101": "Docker Deployment Scripts",
"102": "DB-001",
"103": "CreateReminderDto",
"103": "BlogsService",
"104": "Database Migration Scripts",
"105": "Scientific Terms Schema",
"106": "Blog Data Seeding",
@ -116,10 +116,10 @@
"114": "Manifest Data Generation",
"115": "Honest Manifest Synchronization",
"116": "Manifest Entry Synchronization",
"117": "Videos.tsx",
"117": "WikiService",
"118": "TS-001",
"119": "TEST-001",
"120": "@tailwindcss/postcss",
"120": "ValidateCouponDto",
"121": "@types/react-dom",
"122": "Admin Panel TSConfig",
"123": "About Page Component",
@ -129,7 +129,7 @@
"127": "DEVOPS-001",
"128": "DOC-001",
"129": "What You Must Do When Invoked",
"130": "RolesGuard",
"130": "JwtAuthGuard",
"131": "راهنمای تست سیستم (Software Testing)",
"132": "Role & Core Objective",
"133": "Required Review Group Closures",
@ -140,8 +140,8 @@
"138": "Operational Rules & Boundaries",
"139": "Operational Rules & Boundaries",
"140": "Bcrypt Type Definitions",
"141": "Passport JWT Type Definitions",
"142": "Supertest Type Definitions",
"141": "bcryptjs",
"142": "helmet",
"143": "Blog Entity Model",
"144": "Home Entity Model",
"145": "Wiki Entity Model",
@ -213,7 +213,7 @@
"211": "Phase 2 Final Quality Gate Summary Report",
"212": "Task Modifications Log",
"213": "Install",
"214": "BlogsController",
"214": ".findAll",
"215": "System Discovery",
"216": "Product Requirement Document (PRD)",
"217": "Baseline Command Plan & Reconciled Command History",
@ -237,7 +237,7 @@
"235": "👁️ UX & Persona Interface Review (08_visual_qa)",
"236": "Compiler Diagnostic Dispositions",
"237": "@eslint/eslintrc",
"238": "eslint-plugin-prettier",
"238": "js-yaml",
"239": "globals",
"240": "prettier",
"241": "prisma",
@ -265,6 +265,23 @@
"263": "application/public/fonts/sahel-font-v3.4.0/CHANGELOG.md",
"264": "tailwindcss",
"265": "typescript-eslint",
"268": "wholesale.controller.ts",
"273": "AGENTS.md"
"266": "@nestjs/core",
"267": "@nestjs/jwt",
"268": "@nestjs/swagger",
"269": "@nestjs/throttler",
"270": "passport-jwt",
"271": "@prisma/client",
"272": "swagger-ui-express",
"273": "AGENTS.md",
"274": "eslint-config-prettier",
"275": "@eslint/js",
"276": "jest",
"277": "@nestjs/schematics",
"278": "@nestjs/testing",
"279": "source-map-support",
"280": "ts-jest",
"281": "tsconfig-paths",
"282": "@types/bcryptjs",
"283": "typescript-eslint",
"284": "globals"
}

View File

@ -1,47 +1,47 @@
# Graph Report - canina (2026-08-16)
## Corpus Check
- 461 files · ~675,412 words
- 463 files · ~678,911 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 3213 nodes · 5194 edges · 268 communities (173 shown, 95 thin omitted)
- Extraction: 97% EXTRACTED · 3% INFERRED · 0% AMBIGUOUS · INFERRED: 177 edges (avg confidence: 0.79)
- 3234 nodes · 5269 edges · 285 communities (172 shown, 113 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 186 edges (avg confidence: 0.79)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `d8c6aa8d`
- Built from commit: `8de13513`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation)
- AdminService
- PetsController
- pets/pets.controller.ts
- Roles
- UsersService
- CmsController
- BannersService
- auth.service.ts
- auth.controller.ts
- app.module.ts
- CreateVideoDto
- ProductService
- SmartAdvisor.tsx
- compilerOptions
- toPersian
- pets/pets.controller.ts
- SmsService
- ProductsService
- lib/services/api.ts
- devDependencies
- eslint
- useSettingsStore
- MetricsController
- AppModule
- B2B Inquiry Controller
- Category Management Controller
- RedisService
- auth.service.ts
- MediaController
- Ingredient Management Controller
- adminRoutes.tsx
- admin.module.ts
- api
- ConfirmModal.tsx
- Prescription Review Controller
- Smart Advisor Controller
- Testimonials Management Controller
@ -50,18 +50,18 @@
- useCartStore
- ZibalService
- main.ts
- payment.controller.ts
- ContactService
- Backend TypeScript Config
- App TypeScript Config
- ConfirmModal.tsx
- Coupons.tsx
- dependencies
- Node TypeScript Config
- Admin Blog Controller
- WholesaleService
- WholesaleApplyDto
- devDependencies
- Generic CRUD Controller
- seo.module.ts
- Coupon Management UI
- OrdersController
- Project Build Scripts
- Home Data Module
- devDependencies
@ -77,9 +77,9 @@
- PrismaService
- Jest Testing Config
- PaginationDto
- WikiController
- SettingsService
- FE-001
- PetsService
- payment.module.ts
- rules/graphify.md
- App Health Controller
- dependencies
@ -89,10 +89,10 @@
- Analytics and Report Charts
- Task Orchestration Scripts
- Backend Package Config
- CreateHealthLogDto
- BlogsController
- React Error Boundary
- Application Package Config
- .findAll
- WikiController
- Error and Not Found Pages
- VPN Utility Scripts
- NestJS CLI Config
@ -108,7 +108,7 @@
- Blog Listing Page
- Blog Post Detail Page
- @types/node
- typescript
- devDependencies
- UI Text Seeding
- Wiki Terms Seeding
- Blog Management DTOs
@ -118,7 +118,7 @@
- Network Status Banner
- Docker Deployment Scripts
- DB-001
- CreateReminderDto
- BlogsService
- Database Migration Scripts
- Scientific Terms Schema
- Blog Data Seeding
@ -132,10 +132,10 @@
- Manifest Data Generation
- Honest Manifest Synchronization
- Manifest Entry Synchronization
- Videos.tsx
- WikiService
- TS-001
- TEST-001
- @tailwindcss/postcss
- ValidateCouponDto
- @types/react-dom
- Admin Panel TSConfig
- About Page Component
@ -145,7 +145,7 @@
- DEVOPS-001
- DOC-001
- What You Must Do When Invoked
- RolesGuard
- JwtAuthGuard
- راهنمای تست سیستم (Software Testing)
- Role & Core Objective
- Required Review Group Closures
@ -156,8 +156,8 @@
- Operational Rules & Boundaries
- Operational Rules & Boundaries
- Bcrypt Type Definitions
- Passport JWT Type Definitions
- Supertest Type Definitions
- bcryptjs
- helmet
- Blog Entity Model
- Home Entity Model
- Wiki Entity Model
@ -222,7 +222,7 @@
- Phase 2 Final Quality Gate Summary Report
- Task Modifications Log
- Install
- BlogsController
- .findAll
- System Discovery
- Product Requirement Document (PRD)
- Baseline Command Plan & Reconciled Command History
@ -246,7 +246,7 @@
- 👁️ UX & Persona Interface Review (08_visual_qa)
- Compiler Diagnostic Dispositions
- @eslint/eslintrc
- eslint-plugin-prettier
- js-yaml
- globals
- prettier
- prisma
@ -267,15 +267,32 @@
- typescript
- vitest
- typescript-eslint
- wholesale.controller.ts
- @nestjs/core
- @nestjs/jwt
- @nestjs/swagger
- @nestjs/throttler
- passport-jwt
- @prisma/client
- swagger-ui-express
- AGENTS.md
- eslint-config-prettier
- @eslint/js
- jest
- @nestjs/schematics
- @nestjs/testing
- source-map-support
- ts-jest
- tsconfig-paths
- @types/bcryptjs
- typescript-eslint
- globals
## God Nodes (most connected - your core abstractions)
1. `PrismaService` - 74 edges
2. `Roles()` - 54 edges
2. `Roles()` - 59 edges
3. `PaginationDto` - 39 edges
4. `SmsService` - 38 edges
5. `api` - 33 edges
5. `api` - 34 edges
6. `useSettingsStore` - 33 edges
7. `AdminService` - 31 edges
8. `toPersian()` - 31 edges
@ -295,31 +312,31 @@
backend/src/orders/orders.controller.ts → frontend/admin-panel/src/types/admin.ts
## Import Cycles
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/services/authService.ts -> frontend/application/lib/services/api.ts`
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/services/authService.ts -> frontend/application/lib/services/api.ts`
- 4-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/orderService.ts -> frontend/application/lib/services/api.ts`
## Hyperedges (group relationships)
- **Rastikerdar Font Ecosystem** — frontend_application_public_fonts_shabnam_font_v5_0_1_readme, frontend_application_public_fonts_vazirmatn_v33_003_readme, vazir_font [EXTRACTED 0.95]
- **Canina Deployment Stack** — scripts_compose_prod, scripts_compose_stage [EXTRACTED 1.00]
## Communities (268 total, 95 thin omitted)
## Communities (285 total, 113 thin omitted)
### Community 0 - "AdminService"
Cohesion: 0.06
Nodes (24): AdminController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+16 more)
Cohesion: 0.07
Nodes (22): AdminController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+14 more)
### Community 1 - "PetsController"
Cohesion: 0.17
Nodes (18): PetsController, ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags (+10 more)
### Community 1 - "pets/pets.controller.ts"
Cohesion: 0.05
Nodes (45): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, CreatePetDto, ApiProperty (+37 more)
### Community 2 - "Roles"
Cohesion: 0.05
Nodes (31): Roles(), SmsService, Injectable, ContactController, Body, Controller, Get, Param (+23 more)
Cohesion: 0.19
Nodes (16): Roles(), SettingsController, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+8 more)
### Community 3 - "UsersService"
Cohesion: 0.06
Nodes (37): AuthModule, Module, JwtPayload, JwtStrategy, Injectable, AddressDto, ApiProperty, ApiPropertyOptional (+29 more)
Cohesion: 0.07
Nodes (33): JwtPayload, JwtStrategy, Injectable, AddressDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty (+25 more)
### Community 4 - "CmsController"
Cohesion: 0.09
@ -329,17 +346,17 @@ Nodes (24): CmsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controlle
Cohesion: 0.13
Nodes (15): BannersController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+7 more)
### Community 6 - "auth.service.ts"
Cohesion: 0.05
Nodes (44): AuthController, ApiBadRequestResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller, Post (+36 more)
### Community 6 - "auth.controller.ts"
Cohesion: 0.07
Nodes (39): AuthController, ApiBadRequestResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller, Post (+31 more)
### Community 7 - "app.module.ts"
Cohesion: 0.08
Nodes (30): AdminModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+22 more)
Cohesion: 0.10
Nodes (25): AuthModule, Module, B2BModule, Module, BannersModule, Module, CmsModule, Module (+17 more)
### Community 8 - "CreateVideoDto"
Cohesion: 0.08
Nodes (29): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+21 more)
Cohesion: 0.07
Nodes (31): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+23 more)
### Community 9 - "ProductService"
Cohesion: 0.07
@ -357,10 +374,6 @@ Nodes (32): compilerOptions, allowJs, esModuleInterop, incremental, isolatedModu
Cohesion: 0.08
Nodes (37): ClientLayout(), VerifyContent(), AddressModal(), AddressModalProps, AuthModal(), AuthModalProps, B2BPortal(), BackButton() (+29 more)
### Community 13 - "pets/pets.controller.ts"
Cohesion: 0.16
Nodes (13): CreatePetDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional, IsString (+5 more)
### Community 14 - "ProductsService"
Cohesion: 0.09
Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+11 more)
@ -369,18 +382,10 @@ Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, P
Cohesion: 0.08
Nodes (17): metadata, ContactFormClient(), ContactInfoItem, PrescriptionUploadModal(), PrescriptionUploadModalProps, api, ApiErrorPayload, ApiErr (+9 more)
### Community 16 - "devDependencies"
Cohesion: 0.09
Nodes (23): devDependencies, eslint, eslint-config-prettier, @eslint/js, jest, @nestjs/schematics, @nestjs/testing, source-map-support (+15 more)
### Community 17 - "useSettingsStore"
Cohesion: 0.07
Nodes (30): HomeClient(), HomeClientProps, getHomeData(), Home(), metadata, metadata, EnamadBadge(), Footer() (+22 more)
### Community 18 - "MetricsController"
Cohesion: 0.20
Nodes (5): ApiExcludeController, MetricsController, Controller, Get, Res
### Community 19 - "B2B Inquiry Controller"
Cohesion: 0.13
Nodes (15): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+7 more)
@ -389,12 +394,12 @@ Nodes (15): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controlle
Cohesion: 0.10
Nodes (16): CategoriesController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+8 more)
### Community 21 - "RedisService"
Cohesion: 0.19
Nodes (5): RedisModule, Global, Module, RedisService, Injectable
### Community 21 - "auth.service.ts"
Cohesion: 0.10
Nodes (10): AdminLoginInput, AuthService, LoginInput, RegisterInput, Injectable, RedisModule, Global, Module (+2 more)
### Community 22 - "MediaController"
Cohesion: 0.11
Cohesion: 0.10
Nodes (16): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
### Community 23 - "Ingredient Management Controller"
@ -402,16 +407,16 @@ Cohesion: 0.13
Nodes (14): IngredientsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 24 - "adminRoutes.tsx"
Cohesion: 0.10
Nodes (14): App(), ContactInfoItem, ContactSubmission, GROUP_PAGE_MAP, GROUPS, PAGE_TABS, AdminRouteConfig, ContactSubmissions (+6 more)
Cohesion: 0.08
Nodes (18): App(), ContactInfoItem, ContactSubmission, CategoryDist, DashboardData, PatternItem, SmsConfigState, SmsLogItem (+10 more)
### Community 25 - "admin.module.ts"
Cohesion: 0.06
Nodes (20): BlogQuery, BlogsService, Injectable, PetQuery, PetsService, Injectable, ReportsController, ApiBearerAuth (+12 more)
Cohesion: 0.10
Nodes (14): AdminModule, Module, PetQuery, PetsService, Injectable, ReportsController, ApiBearerAuth, ApiOperation (+6 more)
### Community 26 - "api"
Cohesion: 0.11
Nodes (12): HeroBanner, VetTestimonial, CategoryDist, DashboardData, PatternItem, SmsConfigState, SmsLogItem, SmsLogStats (+4 more)
### Community 26 - "ConfirmModal.tsx"
Cohesion: 0.10
Nodes (16): ConfirmModal(), ConfirmModalProps, HeroBanner, VetTestimonial, Pet, fetchVideosList(), Video, Videos() (+8 more)
### Community 27 - "Prescription Review Controller"
Cohesion: 0.14
@ -427,27 +432,27 @@ Nodes (14): TestimonialsController, ApiBearerAuth, ApiOperation, ApiTags, Body,
### Community 30 - "src/services/api.ts"
Cohesion: 0.10
Nodes (25): Layout(), ProtectedRoute(), menuGroups, Sidebar(), SidebarProps, SEARCHABLE_PAGES, Topbar(), TopbarProps (+17 more)
Nodes (27): Layout(), ProtectedRoute(), menuGroups, Sidebar(), SidebarProps, SEARCHABLE_PAGES, Topbar(), TopbarProps (+19 more)
### Community 31 - "Spinner.tsx"
Cohesion: 0.10
Nodes (19): Media, MediaSelector(), MediaSelectorProps, Spinner(), Category, Product, BannersManager, IngredientsManager (+11 more)
Cohesion: 0.08
Nodes (23): Media, MediaSelector(), MediaSelectorProps, Spinner(), BlogPost, Category, Category, Product (+15 more)
### Community 32 - "useCartStore"
Cohesion: 0.12
Nodes (16): metadata, ArchiveProductCard(), ProductCard(), Header(), MENU_ICONS, OrderSuccess(), OrderTracking(), PetProfile() (+8 more)
### Community 33 - "ZibalService"
Cohesion: 0.10
Nodes (19): PaymentController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+11 more)
Cohesion: 0.06
Nodes (37): AdminTransactionFilterDto, ApiPropertyOptional, IsNumber, IsOptional, IsString, Type, InitiatePaymentDto, InitiateWalletTopupDto (+29 more)
### Community 34 - "main.ts"
Cohesion: 0.11
Nodes (11): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+3 more)
Cohesion: 0.14
Nodes (9): CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable, ApiErrorResponse, ErrorDetailField (+1 more)
### Community 35 - "payment.controller.ts"
Cohesion: 0.23
Nodes (11): InitiatePaymentDto, InitiateWalletTopupDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, ApiPropertyOptional (+3 more)
### Community 35 - "ContactService"
Cohesion: 0.13
Nodes (11): ContactController, Body, Controller, Get, Param, Post, Put, Query (+3 more)
### Community 36 - "Backend TypeScript Config"
Cohesion: 0.09
@ -457,13 +462,13 @@ Nodes (22): compilerOptions, allowSyntheticDefaultImports, baseUrl, declaration,
Cohesion: 0.09
Nodes (22): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, jsx, lib, module, moduleDetection, moduleResolution (+14 more)
### Community 38 - "ConfirmModal.tsx"
Cohesion: 0.10
Nodes (17): ConfirmModal(), ConfirmModalProps, Pagination(), PaginationProps, BlogPost, Category, Media, Pet (+9 more)
### Community 38 - "Coupons.tsx"
Cohesion: 0.09
Nodes (15): Pagination(), PaginationProps, Coupon, CouponFormData, CouponModalProps, CouponTarget, Media, Stats (+7 more)
### Community 39 - "dependencies"
Cohesion: 0.05
Nodes (41): dependencies, bcrypt, bcryptjs, class-transformer, class-validator, helmet, ioredis, js-yaml (+33 more)
Cohesion: 0.10
Nodes (21): dependencies, bcrypt, class-transformer, class-validator, ioredis, @nestjs/common, @nestjs/passport, @nestjs/platform-express (+13 more)
### Community 40 - "Node TypeScript Config"
Cohesion: 0.10
@ -473,9 +478,9 @@ Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib
Cohesion: 0.13
Nodes (15): BlogsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+7 more)
### Community 42 - "WholesaleService"
Cohesion: 0.14
Nodes (14): ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param, Post (+6 more)
### Community 42 - "WholesaleApplyDto"
Cohesion: 0.10
Nodes (20): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto, ApiBearerAuth, ApiOperation (+12 more)
### Community 43 - "devDependencies"
Cohesion: 0.13
@ -489,9 +494,9 @@ Nodes (14): ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, De
Cohesion: 0.16
Nodes (10): SeoController, ApiOperation, ApiTags, Controller, Get, Param, SeoModule, Module (+2 more)
### Community 46 - "Coupon Management UI"
Cohesion: 0.25
Nodes (5): Coupon, CouponFormData, CouponModalProps, CouponTarget, Coupons
### Community 46 - "OrdersController"
Cohesion: 0.13
Nodes (16): OrdersController, ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags (+8 more)
### Community 47 - "Project Build Scripts"
Cohesion: 0.11
@ -503,7 +508,7 @@ Nodes (10): HomeController, ApiOkResponse, ApiOperation, ApiTags, Controller, Ge
### Community 49 - "devDependencies"
Cohesion: 0.11
Nodes (19): autoprefixer, devDependencies, autoprefixer, eslint, @eslint/js, globals, postcss, @types/node (+11 more)
Nodes (19): autoprefixer, devDependencies, autoprefixer, eslint, @eslint/js, postcss, @tailwindcss/postcss, @types/node (+11 more)
### Community 50 - "Database Seeding Logic"
Cohesion: 0.17
@ -542,25 +547,29 @@ Cohesion: 0.15
Nodes (11): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+3 more)
### Community 59 - "PrismaService"
Cohesion: 0.09
Nodes (15): CategoryQuery, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, SmsLogQuery, CreateContactSubmissionDto, UpdateContactInfoItemDto (+7 more)
Cohesion: 0.08
Nodes (18): ApiExcludeController, CouponTargetInput, PaginationQuery, CategoryQuery, MetricsController, Controller, Get, Res (+10 more)
### Community 60 - "Jest Testing Config"
Cohesion: 0.15
Nodes (13): jest, collectCoverageFrom, coverageDirectory, moduleFileExtensions, rootDir, testEnvironment, testRegex, transform (+5 more)
### Community 61 - "PaginationDto"
Cohesion: 0.12
Nodes (13): BlogsModule, Module, BlogsService, Injectable, PaginationDto, SortOrder, ApiPropertyOptional, IsEnum (+5 more)
Cohesion: 0.13
Nodes (13): PaginationDto, SortOrder, ApiPropertyOptional, IsEnum, IsOptional, IsString, Type, Module (+5 more)
### Community 62 - "WikiController"
Cohesion: 0.20
Nodes (7): ApiTags, Controller, WikiController, Module, WikiModule, Injectable, WikiService
### Community 62 - "SettingsService"
Cohesion: 0.11
Nodes (4): SmsLogQuery, ScientificTermData, SettingsService, Injectable
### Community 63 - "FE-001"
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Architecture Overview & Confirmed Strengths, Category, Completion Statement, Confidence (+23 more)
### Community 64 - "payment.module.ts"
Cohesion: 0.16
Nodes (10): SmsModule, Global, Module, ContactModule, Module, PaymentModule, Module, ZibalInquiryResponse (+2 more)
### Community 66 - "App Health Controller"
Cohesion: 0.29
Nodes (5): AppController, Controller, Get, AppService, Injectable
@ -570,8 +579,8 @@ Cohesion: 0.12
Nodes (17): dependencies, axios, lucide-react, motion, next, react, react-dom, sonner (+9 more)
### Community 68 - "OrdersService"
Cohesion: 0.06
Nodes (35): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+27 more)
Cohesion: 0.12
Nodes (15): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+7 more)
### Community 69 - "Integrity Validation Scripts"
Cohesion: 0.20
@ -593,9 +602,9 @@ Nodes (8): cmd_next_task(), cmd_progress(), cmd_reset(), cmd_status(), cmd_unblo
Cohesion: 0.22
Nodes (8): author, description, license, name, prisma, seed, private, version
### Community 74 - "CreateHealthLogDto"
Cohesion: 0.29
Nodes (6): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
### Community 74 - "BlogsController"
Cohesion: 0.20
Nodes (7): BlogsController, ApiTags, Controller, BlogsModule, Module, BlogsService, Injectable
### Community 75 - "React Error Boundary"
Cohesion: 0.22
@ -605,9 +614,9 @@ Nodes (3): ErrorBoundary, Props, State
Cohesion: 0.22
Nodes (8): name, private, scripts, build, dev, lint, start, version
### Community 77 - ".findAll"
Cohesion: 0.32
Nodes (6): ApiNotFoundResponse, ApiOkResponse, ApiOperation, Get, Param, Query
### Community 77 - "WikiController"
Cohesion: 0.21
Nodes (9): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param, Query (+1 more)
### Community 79 - "VPN Utility Scripts"
Cohesion: 0.62
@ -657,6 +666,10 @@ Nodes (4): Blog(), getBlogs(), metadata, BlogPage()
Cohesion: 0.60
Nodes (4): BlogPostPage(), generateMetadata(), getBlog(), revalidate
### Community 93 - "devDependencies"
Cohesion: 0.22
Nodes (9): devDependencies, eslint-plugin-prettier, @types/passport-jwt, @types/supertest, typescript, typescript, eslint-plugin-prettier, @types/passport-jwt (+1 more)
### Community 99 - "Wiki Page Routing"
Cohesion: 0.83
Nodes (3): generateMetadata(), getWikiTerm(), WikiTermPage()
@ -669,17 +682,17 @@ Nodes (3): COMPOSE_DOCKER_CLI_BUILD, DOCKER_BUILDKIT, deploy.sh script
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Database and Data Integrity Audit Report (+23 more)
### Community 103 - "CreateReminderDto"
Cohesion: 0.29
Nodes (6): CreateReminderDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
### Community 103 - "BlogsService"
Cohesion: 0.22
Nodes (3): BlogQuery, BlogsService, Injectable
### Community 109 - "Auth Architecture and Planning"
Cohesion: 0.67
Nodes (3): ADR-AUTH-001: Admin Panel Authentication Architecture & Token Management, Master Task Backlog (Phase 3.3), Phase 3 Master Execution Plan
### Community 117 - "Videos.tsx"
Cohesion: 0.50
Nodes (4): fetchVideosList(), Video, Videos(), Videos
### Community 117 - "WikiService"
Cohesion: 0.22
Nodes (3): Injectable, WikiQuery, WikiService
### Community 118 - "TS-001"
Cohesion: 0.06
@ -689,6 +702,10 @@ Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternati
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
### Community 120 - "ValidateCouponDto"
Cohesion: 0.50
Nodes (4): IsNotEmpty, IsNumber, IsString, ValidateCouponDto
### Community 126 - "Typography and Font Assets"
Cohesion: 0.67
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
@ -705,9 +722,9 @@ Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternati
Cohesion: 0.07
Nodes (26): For /graphify add and --watch, For /graphify query, For the commit hook and native CLAUDE.md integration, For --update and --cluster-only, /graphify, Honesty Rules, Interpreter guard for subcommands, Part A - Structural extraction for code files (+18 more)
### Community 130 - "RolesGuard"
Cohesion: 0.23
Nodes (6): ROLES_KEY, RequestWithUser, RolesGuard, Injectable, UserReqPayload, ScientificTermData
### Community 130 - "JwtAuthGuard"
Cohesion: 0.18
Nodes (7): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Injectable, UserReqPayload
### Community 131 - "راهنمای تست سیستم (Software Testing)"
Cohesion: 0.07
@ -857,9 +874,9 @@ Nodes (8): 1. `TASK-AUTH-001`, 2. `TASK-FIN-001`, 3. `DECISION-002`, 4. `TASK-VE
Cohesion: 0.22
Nodes (8): Arch Linux, bower, Install, npm, Shabnam Font, yarn, طریقه استفاده در صفحات وب:, نمونه متن Sample:
### Community 214 - "BlogsController"
Cohesion: 0.21
Nodes (9): BlogsController, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param (+1 more)
### Community 214 - ".findAll"
Cohesion: 0.32
Nodes (6): ApiNotFoundResponse, ApiOkResponse, ApiOperation, Get, Param, Query
### Community 215 - "System Discovery"
Cohesion: 0.25
@ -937,29 +954,25 @@ Nodes (3): Expanding the ESLint configuration, React Compiler, React + TypeScrip
Cohesion: 0.50
Nodes (3): Deploy on Vercel, Getting Started, Learn More
### Community 268 - "wholesale.controller.ts"
Cohesion: 0.17
Nodes (8): B2BModule, Module, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto
## Knowledge Gaps
- **1178 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1173 more)
- **1179 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1174 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **95 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **113 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `ApiResponse` connect `src/services/api.ts` to `PetsController`, `UsersService`, `OrdersService`, `auth.service.ts`, `ProductsService`, `Home Data Module`, `BlogsController`, `WikiController`?**
_High betweenness centrality (0.036) - this node is a cross-community bridge._
- **Why does `Roles()` connect `Roles` to `RolesGuard`, `CmsController`, `BannersService`, `WholesaleService`, `wholesale.controller.ts`, `ProductsService`, `B2B Inquiry Controller`, `Ingredient Management Controller`, `admin.module.ts`, `Prescription Review Controller`, `Smart Advisor Controller`, `Testimonials Management Controller`?**
_High betweenness centrality (0.024) - this node is a cross-community bridge._
- **Why does `JwtAuthGuard` connect `admin.module.ts` to `AdminService`, `RolesGuard`, `payment.controller.ts`, `CmsController`, `OrdersService`, `UsersService`, `CreateVideoDto`, `wholesale.controller.ts`, `pets/pets.controller.ts`, `ProductsService`?**
_High betweenness centrality (0.023) - this node is a cross-community bridge._
- **Why does `ApiResponse` connect `src/services/api.ts` to `pets/pets.controller.ts`, `UsersService`, `auth.controller.ts`, `BlogsController`, `WikiController`, `OrdersController`, `ProductsService`, `Home Data Module`?**
_High betweenness centrality (0.039) - this node is a cross-community bridge._
- **Why does `Roles()` connect `Roles` to `ZibalService`, `JwtAuthGuard`, `ContactService`, `CmsController`, `BannersService`, `WholesaleApplyDto`, `ProductsService`, `B2B Inquiry Controller`, `Ingredient Management Controller`, `Prescription Review Controller`, `Smart Advisor Controller`, `Testimonials Management Controller`, `SettingsService`?**
_High betweenness centrality (0.037) - this node is a cross-community bridge._
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `AdminService`, `pets/pets.controller.ts`, `UsersService`, `CmsController`, `OrdersService`, `CreateVideoDto`, `MediaController`, `admin.module.ts`, `SettingsService`?**
_High betweenness centrality (0.022) - this node is a cross-community bridge._
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
_1178 weakly-connected nodes found - possible documentation gaps or missing edges._
_1179 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `AdminService` be split into smaller, more focused modules?**
_Cohesion score 0.06414414414414414 - nodes in this community are weakly interconnected._
- **Should `Roles` be split into smaller, more focused modules?**
_Cohesion score 0.051446321102698506 - nodes in this community are weakly interconnected._
_Cohesion score 0.06841046277665996 - nodes in this community are weakly interconnected._
- **Should `pets/pets.controller.ts` be split into smaller, more focused modules?**
_Cohesion score 0.053923541247484906 - nodes in this community are weakly interconnected._
- **Should `UsersService` be split into smaller, more focused modules?**
_Cohesion score 0.06093189964157706 - nodes in this community are weakly interconnected._
_Cohesion score 0.06766917293233082 - nodes in this community are weakly interconnected._

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
# Graph Report - canina (2026-08-16)
## Corpus Check
- 463 files · ~678,911 words
- 464 files · ~680,159 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 3234 nodes · 5269 edges · 285 communities (172 shown, 113 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 186 edges (avg confidence: 0.79)
- 3257 nodes · 5318 edges · 291 communities (178 shown, 113 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 188 edges (avg confidence: 0.79)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `8de13513`
- Built from commit: `5ace474d`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@ -28,15 +28,15 @@
- SmartAdvisor.tsx
- compilerOptions
- toPersian
- SmsService
- ZibalService
- ProductsService
- lib/services/api.ts
- eslint
- useSettingsStore
- AppModule
- MetricsController
- B2B Inquiry Controller
- Category Management Controller
- auth.service.ts
- CategoriesController
- RedisService
- MediaController
- Ingredient Management Controller
- adminRoutes.tsx
@ -48,20 +48,20 @@
- src/services/api.ts
- Spinner.tsx
- useCartStore
- ZibalService
- PaymentController
- main.ts
- ContactService
- admin.ts
- Backend TypeScript Config
- App TypeScript Config
- Coupons.tsx
- Products.tsx
- dependencies
- Node TypeScript Config
- Admin Blog Controller
- WholesaleApplyDto
- WholesaleService
- devDependencies
- Generic CRUD Controller
- seo.module.ts
- OrdersController
- PaginationDto
- Project Build Scripts
- Home Data Module
- devDependencies
@ -76,10 +76,10 @@
- Pet Management API
- PrismaService
- Jest Testing Config
- PaginationDto
- SettingsService
- WikiController
- api
- FE-001
- payment.module.ts
- zibal.service.ts
- rules/graphify.md
- App Health Controller
- dependencies
@ -92,7 +92,7 @@
- BlogsController
- React Error Boundary
- Application Package Config
- WikiController
- .findAll
- Error and Not Found Pages
- VPN Utility Scripts
- NestJS CLI Config
@ -135,7 +135,7 @@
- WikiService
- TS-001
- TEST-001
- ValidateCouponDto
- AdminTransactionFilterDto
- @types/react-dom
- Admin Panel TSConfig
- About Page Component
@ -285,11 +285,17 @@
- tsconfig-paths
- @types/bcryptjs
- typescript-eslint
- globals
- wholesale.controller.ts
- InitiatePaymentDto
- GetProductsDto
- Coupons.tsx
- SmsSettingsPage.tsx
- ZibalCallbackQueryDto
- @tailwindcss/postcss
## God Nodes (most connected - your core abstractions)
1. `PrismaService` - 74 edges
2. `Roles()` - 59 edges
2. `Roles()` - 60 edges
3. `PaginationDto` - 39 edges
4. `SmsService` - 38 edges
5. `api` - 34 edges
@ -312,15 +318,15 @@
backend/src/orders/orders.controller.ts → frontend/admin-panel/src/types/admin.ts
## Import Cycles
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/services/authService.ts -> frontend/application/lib/services/api.ts`
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
- 4-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/orderService.ts -> frontend/application/lib/services/api.ts`
## Hyperedges (group relationships)
- **Rastikerdar Font Ecosystem** — frontend_application_public_fonts_shabnam_font_v5_0_1_readme, frontend_application_public_fonts_vazirmatn_v33_003_readme, vazir_font [EXTRACTED 0.95]
- **Canina Deployment Stack** — scripts_compose_prod, scripts_compose_stage [EXTRACTED 1.00]
## Communities (285 total, 113 thin omitted)
## Communities (291 total, 113 thin omitted)
### Community 0 - "AdminService"
Cohesion: 0.07
@ -331,12 +337,12 @@ Cohesion: 0.05
Nodes (45): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, CreatePetDto, ApiProperty (+37 more)
### Community 2 - "Roles"
Cohesion: 0.19
Nodes (16): Roles(), SettingsController, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+8 more)
Cohesion: 0.05
Nodes (31): Roles(), SmsService, Injectable, ContactController, Body, Controller, Get, Param (+23 more)
### Community 3 - "UsersService"
Cohesion: 0.07
Nodes (33): JwtPayload, JwtStrategy, Injectable, AddressDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty (+25 more)
Cohesion: 0.06
Nodes (37): AuthModule, Module, JwtPayload, JwtStrategy, Injectable, AddressDto, ApiProperty, ApiPropertyOptional (+29 more)
### Community 4 - "CmsController"
Cohesion: 0.09
@ -347,12 +353,12 @@ Cohesion: 0.13
Nodes (15): BannersController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+7 more)
### Community 6 - "auth.controller.ts"
Cohesion: 0.07
Nodes (39): AuthController, ApiBadRequestResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller, Post (+31 more)
Cohesion: 0.06
Nodes (41): AuthController, ApiBadRequestResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller, Post (+33 more)
### Community 7 - "app.module.ts"
Cohesion: 0.10
Nodes (25): AuthModule, Module, B2BModule, Module, BannersModule, Module, CmsModule, Module (+17 more)
Nodes (26): BannersModule, Module, CmsModule, Module, SmsModule, Global, Module, ContactModule (+18 more)
### Community 8 - "CreateVideoDto"
Cohesion: 0.07
@ -374,9 +380,13 @@ Nodes (32): compilerOptions, allowJs, esModuleInterop, incremental, isolatedModu
Cohesion: 0.08
Nodes (37): ClientLayout(), VerifyContent(), AddressModal(), AddressModalProps, AuthModal(), AuthModalProps, B2BPortal(), BackButton() (+29 more)
### Community 13 - "ZibalService"
Cohesion: 0.16
Nodes (4): PaymentService, Injectable, Injectable, ZibalService
### Community 14 - "ProductsService"
Cohesion: 0.09
Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+11 more)
Cohesion: 0.12
Nodes (13): ProductsController, ApiOperation, ApiTags, Body, Controller, Get, Param, Patch (+5 more)
### Community 15 - "lib/services/api.ts"
Cohesion: 0.08
@ -386,20 +396,24 @@ Nodes (17): metadata, ContactFormClient(), ContactInfoItem, PrescriptionUploadMo
Cohesion: 0.07
Nodes (30): HomeClient(), HomeClientProps, getHomeData(), Home(), metadata, metadata, EnamadBadge(), Footer() (+22 more)
### Community 18 - "MetricsController"
Cohesion: 0.20
Nodes (5): ApiExcludeController, MetricsController, Controller, Get, Res
### Community 19 - "B2B Inquiry Controller"
Cohesion: 0.13
Nodes (15): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+7 more)
### Community 20 - "Category Management Controller"
Cohesion: 0.10
Nodes (16): CategoriesController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+8 more)
### Community 20 - "CategoriesController"
Cohesion: 0.09
Nodes (17): CategoriesController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+9 more)
### Community 21 - "auth.service.ts"
Cohesion: 0.10
Nodes (10): AdminLoginInput, AuthService, LoginInput, RegisterInput, Injectable, RedisModule, Global, Module (+2 more)
### Community 21 - "RedisService"
Cohesion: 0.15
Nodes (7): CouponTargetInput, PaginationQuery, RedisModule, Global, Module, RedisService, Injectable
### Community 22 - "MediaController"
Cohesion: 0.10
Cohesion: 0.11
Nodes (16): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
### Community 23 - "Ingredient Management Controller"
@ -407,16 +421,16 @@ Cohesion: 0.13
Nodes (14): IngredientsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 24 - "adminRoutes.tsx"
Cohesion: 0.08
Nodes (18): App(), ContactInfoItem, ContactSubmission, CategoryDist, DashboardData, PatternItem, SmsConfigState, SmsLogItem (+10 more)
Cohesion: 0.14
Nodes (10): App(), ContactInfoItem, ContactSubmission, GROUP_PAGE_MAP, GROUPS, PAGE_TABS, AdminRouteConfig, ContactSubmissions (+2 more)
### Community 25 - "admin.module.ts"
Cohesion: 0.10
Nodes (14): AdminModule, Module, PetQuery, PetsService, Injectable, ReportsController, ApiBearerAuth, ApiOperation (+6 more)
Cohesion: 0.11
Nodes (13): AdminModule, Module, PetsService, Injectable, ReportsController, ApiBearerAuth, ApiOperation, ApiTags (+5 more)
### Community 26 - "ConfirmModal.tsx"
Cohesion: 0.10
Nodes (16): ConfirmModal(), ConfirmModalProps, HeroBanner, VetTestimonial, Pet, fetchVideosList(), Video, Videos() (+8 more)
Cohesion: 0.11
Nodes (12): ConfirmModal(), ConfirmModalProps, HeroBanner, VetTestimonial, Media, WholesaleRequest, ProductItem, WikiTerm (+4 more)
### Community 27 - "Prescription Review Controller"
Cohesion: 0.14
@ -431,28 +445,28 @@ Cohesion: 0.13
Nodes (14): TestimonialsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 30 - "src/services/api.ts"
Cohesion: 0.10
Nodes (27): Layout(), ProtectedRoute(), menuGroups, Sidebar(), SidebarProps, SEARCHABLE_PAGES, Topbar(), TopbarProps (+19 more)
Cohesion: 0.16
Nodes (16): Layout(), ProtectedRoute(), menuGroups, Sidebar(), SidebarProps, SEARCHABLE_PAGES, Topbar(), TopbarProps (+8 more)
### Community 31 - "Spinner.tsx"
Cohesion: 0.08
Nodes (23): Media, MediaSelector(), MediaSelectorProps, Spinner(), BlogPost, Category, Category, Product (+15 more)
Cohesion: 0.09
Nodes (20): Media, MediaSelector(), MediaSelectorProps, Spinner(), BlogPost, Category, BannersManager, Blogs (+12 more)
### Community 32 - "useCartStore"
Cohesion: 0.12
Nodes (16): metadata, ArchiveProductCard(), ProductCard(), Header(), MENU_ICONS, OrderSuccess(), OrderTracking(), PetProfile() (+8 more)
### Community 33 - "ZibalService"
Cohesion: 0.06
Nodes (37): AdminTransactionFilterDto, ApiPropertyOptional, IsNumber, IsOptional, IsString, Type, InitiatePaymentDto, InitiateWalletTopupDto (+29 more)
### Community 33 - "PaymentController"
Cohesion: 0.19
Nodes (17): PaymentController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+9 more)
### Community 34 - "main.ts"
Cohesion: 0.14
Nodes (9): CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable, ApiErrorResponse, ErrorDetailField (+1 more)
Cohesion: 0.11
Nodes (11): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+3 more)
### Community 35 - "ContactService"
Cohesion: 0.13
Nodes (11): ContactController, Body, Controller, Get, Param, Post, Put, Query (+3 more)
### Community 35 - "admin.ts"
Cohesion: 0.12
Nodes (13): B2BManager, FinancialSettingsPage, SmartAdvisorManager, SystemSettingsPage, AdminLoginPayload, B2BInquiry, FinancialSettings, PartnerAccount (+5 more)
### Community 36 - "Backend TypeScript Config"
Cohesion: 0.09
@ -462,9 +476,9 @@ Nodes (22): compilerOptions, allowSyntheticDefaultImports, baseUrl, declaration,
Cohesion: 0.09
Nodes (22): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, jsx, lib, module, moduleDetection, moduleResolution (+14 more)
### Community 38 - "Coupons.tsx"
Cohesion: 0.09
Nodes (15): Pagination(), PaginationProps, Coupon, CouponFormData, CouponModalProps, CouponTarget, Media, Stats (+7 more)
### Community 38 - "Products.tsx"
Cohesion: 0.13
Nodes (11): Pagination(), PaginationProps, Pet, Category, Product, GatewayHealth, Stats, Transaction (+3 more)
### Community 39 - "dependencies"
Cohesion: 0.10
@ -478,9 +492,9 @@ Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib
Cohesion: 0.13
Nodes (15): BlogsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+7 more)
### Community 42 - "WholesaleApplyDto"
Cohesion: 0.10
Nodes (20): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto, ApiBearerAuth, ApiOperation (+12 more)
### Community 42 - "WholesaleService"
Cohesion: 0.14
Nodes (14): ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param, Post (+6 more)
### Community 43 - "devDependencies"
Cohesion: 0.13
@ -494,9 +508,9 @@ Nodes (14): ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, De
Cohesion: 0.16
Nodes (10): SeoController, ApiOperation, ApiTags, Controller, Get, Param, SeoModule, Module (+2 more)
### Community 46 - "OrdersController"
Cohesion: 0.13
Nodes (16): OrdersController, ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags (+8 more)
### Community 46 - "PaginationDto"
Cohesion: 0.20
Nodes (9): PaginationDto, SortOrder, ApiPropertyOptional, IsEnum, IsOptional, IsString, Type, IsInt (+1 more)
### Community 47 - "Project Build Scripts"
Cohesion: 0.11
@ -508,7 +522,7 @@ Nodes (10): HomeController, ApiOkResponse, ApiOperation, ApiTags, Controller, Ge
### Community 49 - "devDependencies"
Cohesion: 0.11
Nodes (19): autoprefixer, devDependencies, autoprefixer, eslint, @eslint/js, postcss, @tailwindcss/postcss, @types/node (+11 more)
Nodes (19): autoprefixer, devDependencies, autoprefixer, eslint, @eslint/js, globals, postcss, @types/node (+11 more)
### Community 50 - "Database Seeding Logic"
Cohesion: 0.17
@ -548,27 +562,27 @@ Nodes (11): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Cont
### Community 59 - "PrismaService"
Cohesion: 0.08
Nodes (18): ApiExcludeController, CouponTargetInput, PaginationQuery, CategoryQuery, MetricsController, Controller, Get, Res (+10 more)
Nodes (19): BlogQuery, PetQuery, WikiQuery, AdminLoginInput, LoginInput, RegisterInput, MeliPayamakPattern, MeliPayamakResponse (+11 more)
### Community 60 - "Jest Testing Config"
Cohesion: 0.15
Nodes (13): jest, collectCoverageFrom, coverageDirectory, moduleFileExtensions, rootDir, testEnvironment, testRegex, transform (+5 more)
### Community 61 - "PaginationDto"
Cohesion: 0.13
Nodes (13): PaginationDto, SortOrder, ApiPropertyOptional, IsEnum, IsOptional, IsString, Type, Module (+5 more)
### Community 61 - "WikiController"
Cohesion: 0.20
Nodes (7): ApiTags, Controller, WikiController, Module, WikiModule, Injectable, WikiService
### Community 62 - "SettingsService"
Cohesion: 0.11
Nodes (4): SmsLogQuery, ScientificTermData, SettingsService, Injectable
### Community 62 - "api"
Cohesion: 0.20
Nodes (8): CategoryDist, DashboardData, fetchVideosList(), Video, Videos(), Dashboard, Videos, api
### Community 63 - "FE-001"
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Architecture Overview & Confirmed Strengths, Category, Completion Statement, Confidence (+23 more)
### Community 64 - "payment.module.ts"
### Community 64 - "zibal.service.ts"
Cohesion: 0.16
Nodes (10): SmsModule, Global, Module, ContactModule, Module, PaymentModule, Module, ZibalInquiryResponse (+2 more)
Nodes (9): GatewayHealthResult, IPaymentGateway, PaymentInquiryResult, PaymentRequestOptions, PaymentRequestResult, PaymentVerifyResult, ZibalInquiryResponse, ZibalRequestResponse (+1 more)
### Community 66 - "App Health Controller"
Cohesion: 0.29
@ -579,8 +593,8 @@ Cohesion: 0.12
Nodes (17): dependencies, axios, lucide-react, motion, next, react, react-dom, sonner (+9 more)
### Community 68 - "OrdersService"
Cohesion: 0.12
Nodes (15): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+7 more)
Cohesion: 0.06
Nodes (35): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+27 more)
### Community 69 - "Integrity Validation Scripts"
Cohesion: 0.20
@ -614,9 +628,9 @@ Nodes (3): ErrorBoundary, Props, State
Cohesion: 0.22
Nodes (8): name, private, scripts, build, dev, lint, start, version
### Community 77 - "WikiController"
Cohesion: 0.21
Nodes (9): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param, Query (+1 more)
### Community 77 - ".findAll"
Cohesion: 0.32
Nodes (6): ApiNotFoundResponse, ApiOkResponse, ApiOperation, Get, Param, Query
### Community 79 - "VPN Utility Scripts"
Cohesion: 0.62
@ -682,18 +696,10 @@ Nodes (3): COMPOSE_DOCKER_CLI_BUILD, DOCKER_BUILDKIT, deploy.sh script
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Database and Data Integrity Audit Report (+23 more)
### Community 103 - "BlogsService"
Cohesion: 0.22
Nodes (3): BlogQuery, BlogsService, Injectable
### Community 109 - "Auth Architecture and Planning"
Cohesion: 0.67
Nodes (3): ADR-AUTH-001: Admin Panel Authentication Architecture & Token Management, Master Task Backlog (Phase 3.3), Phase 3 Master Execution Plan
### Community 117 - "WikiService"
Cohesion: 0.22
Nodes (3): Injectable, WikiQuery, WikiService
### Community 118 - "TS-001"
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
@ -702,9 +708,9 @@ Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternati
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
### Community 120 - "ValidateCouponDto"
Cohesion: 0.50
Nodes (4): IsNotEmpty, IsNumber, IsString, ValidateCouponDto
### Community 120 - "AdminTransactionFilterDto"
Cohesion: 0.22
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsNumber, IsOptional, IsString, Type, IsIn
### Community 126 - "Typography and Font Assets"
Cohesion: 0.67
@ -723,8 +729,8 @@ Cohesion: 0.07
Nodes (26): For /graphify add and --watch, For /graphify query, For the commit hook and native CLAUDE.md integration, For --update and --cluster-only, /graphify, Honesty Rules, Interpreter guard for subcommands, Part A - Structural extraction for code files (+18 more)
### Community 130 - "JwtAuthGuard"
Cohesion: 0.18
Nodes (7): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Injectable, UserReqPayload
Cohesion: 0.15
Nodes (9): JwtAuthGuard, Injectable, B2BModule, Module, ROLES_KEY, RequestWithUser, RolesGuard, Injectable (+1 more)
### Community 131 - "راهنمای تست سیستم (Software Testing)"
Cohesion: 0.07
@ -954,25 +960,49 @@ Nodes (3): Expanding the ESLint configuration, React Compiler, React + TypeScrip
Cohesion: 0.50
Nodes (3): Deploy on Vercel, Getting Started, Learn More
### Community 284 - "wholesale.controller.ts"
Cohesion: 0.31
Nodes (6): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto
### Community 285 - "InitiatePaymentDto"
Cohesion: 0.43
Nodes (7): InitiatePaymentDto, InitiateWalletTopupDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
### Community 286 - "GetProductsDto"
Cohesion: 0.29
Nodes (6): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, Query
### Community 287 - "Coupons.tsx"
Cohesion: 0.25
Nodes (5): Coupon, CouponFormData, CouponModalProps, CouponTarget, Coupons
### Community 288 - "SmsSettingsPage.tsx"
Cohesion: 0.29
Nodes (5): PatternItem, SmsConfigState, SmsLogItem, SmsLogStats, SmsSettingsPage
### Community 289 - "ZibalCallbackQueryDto"
Cohesion: 0.40
Nodes (4): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto
## Knowledge Gaps
- **1179 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1174 more)
- **1181 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1176 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **113 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `ApiResponse` connect `src/services/api.ts` to `pets/pets.controller.ts`, `UsersService`, `auth.controller.ts`, `BlogsController`, `WikiController`, `OrdersController`, `ProductsService`, `Home Data Module`?**
- **Why does `ApiResponse` connect `src/services/api.ts` to `pets/pets.controller.ts`, `UsersService`, `OrdersService`, `admin.ts`, `auth.controller.ts`, `BlogsController`, `ProductsService`, `Home Data Module`, `WikiController`?**
_High betweenness centrality (0.039) - this node is a cross-community bridge._
- **Why does `Roles()` connect `Roles` to `ZibalService`, `JwtAuthGuard`, `ContactService`, `CmsController`, `BannersService`, `WholesaleApplyDto`, `ProductsService`, `B2B Inquiry Controller`, `Ingredient Management Controller`, `Prescription Review Controller`, `Smart Advisor Controller`, `Testimonials Management Controller`, `SettingsService`?**
_High betweenness centrality (0.037) - this node is a cross-community bridge._
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `AdminService`, `pets/pets.controller.ts`, `UsersService`, `CmsController`, `OrdersService`, `CreateVideoDto`, `MediaController`, `admin.module.ts`, `SettingsService`?**
_High betweenness centrality (0.022) - this node is a cross-community bridge._
- **Why does `Roles()` connect `Roles` to `PaymentController`, `JwtAuthGuard`, `CmsController`, `BannersService`, `WholesaleService`, `ProductsService`, `B2B Inquiry Controller`, `wholesale.controller.ts`, `Ingredient Management Controller`, `Prescription Review Controller`, `Smart Advisor Controller`, `Testimonials Management Controller`?**
_High betweenness centrality (0.029) - this node is a cross-community bridge._
- **Why does `PrismaService` connect `PrismaService` to `pets/pets.controller.ts`, `JwtAuthGuard`, `Roles`, `CmsController`, `BannersService`, `UsersService`, `app.module.ts`, `CreateVideoDto`, `ZibalService`, `ProductsService`, `MetricsController`, `B2B Inquiry Controller`, `CategoriesController`, `RedisService`, `MediaController`, `Ingredient Management Controller`, `admin.module.ts`, `Prescription Review Controller`, `Smart Advisor Controller`, `Testimonials Management Controller`, `wholesale.controller.ts`, `WholesaleService`, `seo.module.ts`, `Home Data Module`, `WikiController`, `zibal.service.ts`, `OrdersService`, `BlogsController`, `BlogsService`, `WikiService`?**
_High betweenness centrality (0.025) - this node is a cross-community bridge._
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
_1179 weakly-connected nodes found - possible documentation gaps or missing edges._
_1181 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `AdminService` be split into smaller, more focused modules?**
_Cohesion score 0.06841046277665996 - nodes in this community are weakly interconnected._
_Cohesion score 0.06690140845070422 - nodes in this community are weakly interconnected._
- **Should `pets/pets.controller.ts` be split into smaller, more focused modules?**
_Cohesion score 0.053923541247484906 - nodes in this community are weakly interconnected._
- **Should `UsersService` be split into smaller, more focused modules?**
_Cohesion score 0.06766917293233082 - nodes in this community are weakly interconnected._
_Cohesion score 0.0528169014084507 - nodes in this community are weakly interconnected._
- **Should `Roles` be split into smaller, more focused modules?**
_Cohesion score 0.051446321102698506 - nodes in this community are weakly interconnected._

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff