From 1bd2d027ec8bb3bffc8a300f3c45c182a25464f4 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:19:20 +0330 Subject: [PATCH] Import source: app/backend/src/lib/errors Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- app/backend/src/lib/errors.ts | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 app/backend/src/lib/errors.ts diff --git a/app/backend/src/lib/errors.ts b/app/backend/src/lib/errors.ts new file mode 100644 index 0000000..229f9be --- /dev/null +++ b/app/backend/src/lib/errors.ts @@ -0,0 +1,69 @@ +import type { FastifyReply } from 'fastify'; + +export class AppError extends Error { + constructor( + public readonly statusCode: number, + public readonly code: string, + message?: string, + ) { + super(message ?? code); + this.name = this.constructor.name; + } + + send(reply: FastifyReply) { + return reply.code(this.statusCode).send({ error: this.code, message: this.message }); + } +} + +export class NotFoundError extends AppError { + constructor(resource: string, identifier?: string) { + super(404, 'not_found', identifier ? `${resource} با شناسه ${identifier} یافت نشد` : `${resource} یافت نشد`); + } +} + +export class ValidationError extends AppError { + constructor(message?: string) { + super(400, 'invalid_input', message ?? 'ورودی نامعتبر'); + } +} + +export class UnauthorizedError extends AppError { + constructor() { + super(401, 'unauthenticated', 'ابتدا وارد حساب خود شوید'); + } +} + +export class ForbiddenError extends AppError { + constructor(action?: string) { + super(403, 'forbidden', action ? `شما اجازه ${action} را ندارید` : 'دسترسی غیرمجاز'); + } +} + +export class RateLimitedError extends AppError { + public readonly retryAfter?: number; + constructor(retryAfter?: number) { + super(429, 'rate_limited', 'تعداد درخواست‌ها از حد مجاز گذشت'); + this.retryAfter = retryAfter; + } + + send(reply: FastifyReply) { + if (this.retryAfter) reply.header('Retry-After', this.retryAfter); + return reply.code(this.statusCode).send({ error: this.code, message: this.message, retryAfter: this.retryAfter }); + } +} + +export class ConflictError extends AppError { + constructor(message?: string) { + super(409, 'conflict', message ?? 'تداخل داده‌ها'); + } +} + +export class PublishQualityError extends AppError { + constructor(public readonly issues: string[]) { + super(409, 'publish_quality', `موارد نیاز به اصلاح: ${issues.join('، ')}`); + } + + send(reply: FastifyReply) { + return reply.code(this.statusCode).send({ error: this.code, message: this.message, issues: this.issues }); + } +}