fix: Persian duplicate-value errors; admin panel favicon uses the project logo
- PrismaExceptionFilter returned "Unique constraint failed on: mobile" in English for a duplicate. It now answers in Persian, naming the field (mobile, national code, member code, order number), with a safe generic fallback. Covered by a unit test. - The admin panel still shipped the old "NZ" favicon. favicon.png/.ico are now generated from the project logo (the same one the user apps use), in both public/ and dist/, and the links carry ?v=2 so browsers drop the cached icon. - Rebuilt apps/api/release/app.js. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
0c52adcd26
commit
600ce06d15
@@ -0,0 +1,21 @@
|
||||
import { uniqueViolationMessage } from './prisma-exception.filter';
|
||||
|
||||
describe('uniqueViolationMessage', () => {
|
||||
it('names a duplicate mobile in Persian, whether Prisma sends columns or an index name', () => {
|
||||
expect(uniqueViolationMessage(['mobile'])).toBe('این شماره موبایل قبلاً ثبت شده است');
|
||||
expect(uniqueViolationMessage('Customer_mobile_key')).toBe('این شماره موبایل قبلاً ثبت شده است');
|
||||
expect(uniqueViolationMessage('Admin_mobile_key')).toBe('این شماره موبایل قبلاً ثبت شده است');
|
||||
});
|
||||
|
||||
it('names a duplicate national code', () => {
|
||||
expect(uniqueViolationMessage(['nationalCode'])).toBe('این کد ملی قبلاً ثبت شده است');
|
||||
});
|
||||
|
||||
it('never leaks the English Prisma wording, even for an unknown column or no target', () => {
|
||||
for (const t of [['somethingElse'], undefined, null, []]) {
|
||||
const msg = uniqueViolationMessage(t);
|
||||
expect(msg).toBe('این مقدار قبلاً ثبت شده است و تکراری است');
|
||||
expect(msg).not.toMatch(/[A-Za-z]/);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,27 @@ import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus, Logger } from '@nest
|
||||
import { Response } from 'express';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
/**
|
||||
* Persian, user-facing text for a unique-index violation. The panel shows
|
||||
* `message` as-is, and the raw Prisma wording ("Unique constraint failed on:
|
||||
* mobile") is meaningless to the operator.
|
||||
*
|
||||
* `target` is the list of columns on Postgres, or the index name when the
|
||||
* driver only reports that — so both are matched by substring.
|
||||
*/
|
||||
export function uniqueViolationMessage(target: unknown): string {
|
||||
const fields = (Array.isArray(target) ? target : [target])
|
||||
.map((t) => String(t ?? ''))
|
||||
.join(',')
|
||||
.toLowerCase();
|
||||
|
||||
if (fields.includes('mobile')) return 'این شماره موبایل قبلاً ثبت شده است';
|
||||
if (fields.includes('nationalcode')) return 'این کد ملی قبلاً ثبت شده است';
|
||||
if (fields.includes('membercode')) return 'این کد عضویت قبلاً ثبت شده است';
|
||||
if (fields.includes('ordernumber')) return 'این شماره سفارش قبلاً ثبت شده است';
|
||||
return 'این مقدار قبلاً ثبت شده است و تکراری است';
|
||||
}
|
||||
|
||||
/**
|
||||
* Without this, an unmigrated table (P2021), a bad foreign key (P2003), etc.
|
||||
* all surfaced as an opaque "Internal Server Error" with no indication of
|
||||
@@ -44,7 +65,7 @@ export class PrismaExceptionFilter implements ExceptionFilter {
|
||||
return response.status(HttpStatus.CONFLICT).json({
|
||||
statusCode: HttpStatus.CONFLICT,
|
||||
error: 'Conflict',
|
||||
message: `Unique constraint failed on: ${(exception.meta?.target as string[] | undefined)?.join(', ')}`,
|
||||
message: uniqueViolationMessage(exception.meta?.target),
|
||||
});
|
||||
case 'P2003': // foreign key constraint violation
|
||||
return response.status(HttpStatus.BAD_REQUEST).json({
|
||||
|
||||
Reference in New Issue
Block a user