zeti-framework-backend 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-SSL7KSRJ.js +190 -0
- package/dist/chunk-SSL7KSRJ.js.map +1 -0
- package/dist/error-handler-LBN3A7N3.js +10 -0
- package/dist/error-handler-LBN3A7N3.js.map +1 -0
- package/dist/index.js +21 -202
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// src/utils/error-handler.ts
|
|
2
|
+
import { HTTPException } from "hono/http-exception";
|
|
3
|
+
function isJsonParseError(err) {
|
|
4
|
+
if (!err) return false;
|
|
5
|
+
const message = err.message?.toLowerCase() || "";
|
|
6
|
+
const name = err.name?.toLowerCase() || "";
|
|
7
|
+
return name === "syntaxerror" || message.includes("json") || message.includes("unexpected token") || message.includes("unexpected end") || message.includes("malformed") || message.includes("parse") || message.includes("invalid character");
|
|
8
|
+
}
|
|
9
|
+
function getJsonErrorDetails(err) {
|
|
10
|
+
const message = err.message || "";
|
|
11
|
+
const positionMatch = message.match(/position\s*(\d+)/i);
|
|
12
|
+
const lineMatch = message.match(/line\s*(\d+)/i);
|
|
13
|
+
const columnMatch = message.match(/column\s*(\d+)/i);
|
|
14
|
+
let details = "JSON inv\xE1lido no corpo da requisi\xE7\xE3o";
|
|
15
|
+
if (positionMatch) {
|
|
16
|
+
details += ` (posi\xE7\xE3o ${positionMatch[1]})`;
|
|
17
|
+
}
|
|
18
|
+
if (lineMatch && columnMatch) {
|
|
19
|
+
details += ` (linha ${lineMatch[1]}, coluna ${columnMatch[1]})`;
|
|
20
|
+
}
|
|
21
|
+
const tokenMatch = message.match(/unexpected token\s*['"]?([^'"]+)['"]?/i);
|
|
22
|
+
if (tokenMatch) {
|
|
23
|
+
details += `. Caractere inesperado: "${tokenMatch[1]}"`;
|
|
24
|
+
}
|
|
25
|
+
if (message.includes("unexpected end")) {
|
|
26
|
+
details += ". Verifique se o JSON est\xE1 completo (faltando } ou ])";
|
|
27
|
+
}
|
|
28
|
+
return details;
|
|
29
|
+
}
|
|
30
|
+
function createErrorHandler(config) {
|
|
31
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
32
|
+
const includeTrace = config?.includeTrace ?? isDev;
|
|
33
|
+
const includeStack = config?.includeStack ?? false;
|
|
34
|
+
return async (c, next) => {
|
|
35
|
+
try {
|
|
36
|
+
await next();
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const tenant = c.req.header("x-tenant") || c.req.header("X-Tenant") || "not-set";
|
|
39
|
+
const trace = {
|
|
40
|
+
path: c.req.path,
|
|
41
|
+
method: c.req.method,
|
|
42
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
43
|
+
errorType: err?.constructor?.name || "Error"
|
|
44
|
+
};
|
|
45
|
+
if (includeStack && err?.stack) {
|
|
46
|
+
trace.stack = err.stack;
|
|
47
|
+
}
|
|
48
|
+
console.error(`
|
|
49
|
+
\u274C [${trace.timestamp}] Error in ${trace.method} ${trace.path}`);
|
|
50
|
+
console.error(` Tenant: ${tenant}`);
|
|
51
|
+
console.error(` Type: ${trace.errorType}`);
|
|
52
|
+
console.error(` Message: ${err?.message || "Unknown error"}`);
|
|
53
|
+
if (err?.cause) {
|
|
54
|
+
console.error(` Cause: ${err.cause?.message || err.cause}`);
|
|
55
|
+
}
|
|
56
|
+
if (isDev && err?.stack) {
|
|
57
|
+
console.error(` Stack:
|
|
58
|
+
${err.stack.split("\n").slice(0, 8).map((l) => ` ${l}`).join("\n")}`);
|
|
59
|
+
}
|
|
60
|
+
const response = handleError(err, config, includeTrace ? trace : void 0, { tenant });
|
|
61
|
+
if (config?.logError) {
|
|
62
|
+
config.logError(err, c);
|
|
63
|
+
}
|
|
64
|
+
return c.json(response, response.status);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function handleError(exception, config, trace, context) {
|
|
69
|
+
if (config?.formatError) {
|
|
70
|
+
return config.formatError(exception, {});
|
|
71
|
+
}
|
|
72
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
73
|
+
const includeDetails = config?.includeTrace ?? isDev;
|
|
74
|
+
let status = 500;
|
|
75
|
+
let message = "Internal server error";
|
|
76
|
+
let validationErrors = void 0;
|
|
77
|
+
if (isJsonParseError(exception)) {
|
|
78
|
+
status = 400;
|
|
79
|
+
message = getJsonErrorDetails(exception);
|
|
80
|
+
validationErrors = {
|
|
81
|
+
body: {
|
|
82
|
+
errors: [
|
|
83
|
+
"O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
|
|
84
|
+
"Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
} else if (exception instanceof HTTPException) {
|
|
89
|
+
status = exception.status;
|
|
90
|
+
message = exception.message || "Erro na requisi\xE7\xE3o";
|
|
91
|
+
if (exception.cause && isJsonParseError(exception.cause)) {
|
|
92
|
+
status = 400;
|
|
93
|
+
message = getJsonErrorDetails(exception.cause);
|
|
94
|
+
validationErrors = {
|
|
95
|
+
body: {
|
|
96
|
+
errors: [
|
|
97
|
+
"O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
|
|
98
|
+
"Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
} else if (status === 400 || status === 422) {
|
|
103
|
+
if (exception.cause && typeof exception.cause === "object") {
|
|
104
|
+
const cause = exception.cause;
|
|
105
|
+
if (cause.validationErrors) {
|
|
106
|
+
validationErrors = cause.validationErrors;
|
|
107
|
+
} else if (cause.issues && Array.isArray(cause.issues)) {
|
|
108
|
+
validationErrors = cause.issues.reduce((acc, issue) => {
|
|
109
|
+
const field = issue.path?.join(".") || "unknown";
|
|
110
|
+
if (!acc[field]) {
|
|
111
|
+
acc[field] = { errors: [] };
|
|
112
|
+
}
|
|
113
|
+
acc[field].errors.push(issue.message);
|
|
114
|
+
return acc;
|
|
115
|
+
}, {});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} else if (exception && typeof exception === "object" && "code" in exception && exception.code && typeof exception.code === "string" && exception.code.startsWith("P")) {
|
|
120
|
+
const prismaError = exception;
|
|
121
|
+
switch (prismaError.code) {
|
|
122
|
+
case "P2003": {
|
|
123
|
+
status = 422;
|
|
124
|
+
const fieldName = prismaError.meta?.field_name;
|
|
125
|
+
if (fieldName) {
|
|
126
|
+
validationErrors = {
|
|
127
|
+
[fieldName]: {
|
|
128
|
+
errors: ["Campo inv\xE1lido ou o registro n\xE3o existe"]
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
} else {
|
|
132
|
+
message = "Campo inv\xE1lido ou o registro n\xE3o existe";
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case "P2002": {
|
|
137
|
+
status = 409;
|
|
138
|
+
message = "Registro j\xE1 cadastrado";
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case "P2025": {
|
|
142
|
+
status = 404;
|
|
143
|
+
message = "Registro n\xE3o encontrado";
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
default: {
|
|
147
|
+
message = prismaError.message || "Erro no banco de dados";
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} else if (Array.isArray(exception)) {
|
|
152
|
+
status = 422;
|
|
153
|
+
message = "";
|
|
154
|
+
validationErrors = exception.reduce((acc, error) => {
|
|
155
|
+
acc[error.field] = {
|
|
156
|
+
errors: error.errors
|
|
157
|
+
};
|
|
158
|
+
return acc;
|
|
159
|
+
}, {});
|
|
160
|
+
} else if (exception instanceof Error) {
|
|
161
|
+
if (includeDetails) {
|
|
162
|
+
message = exception.message || "Erro interno do servidor";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const response = {
|
|
166
|
+
data: null,
|
|
167
|
+
status,
|
|
168
|
+
message
|
|
169
|
+
};
|
|
170
|
+
if ((status === 400 || status === 422) && validationErrors && Object.keys(validationErrors).length > 0) {
|
|
171
|
+
response.validationErrors = validationErrors;
|
|
172
|
+
}
|
|
173
|
+
if (trace) {
|
|
174
|
+
response.trace = trace;
|
|
175
|
+
if (includeDetails && context?.tenant) {
|
|
176
|
+
response.debug = {
|
|
177
|
+
tenant: context.tenant,
|
|
178
|
+
errorMessage: exception?.message,
|
|
179
|
+
errorCause: exception?.cause?.message || exception?.cause
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return response;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
createErrorHandler,
|
|
188
|
+
handleError
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=chunk-SSL7KSRJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/error-handler.ts"],"sourcesContent":["import type { Context } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\nimport type { Prisma } from '../types/prisma';\n\nexport interface ValidationError {\n field: string;\n errors: string[];\n}\n\nexport interface ErrorTrace {\n path: string;\n method: string;\n timestamp: string;\n errorType: string;\n stack?: string;\n}\n\nexport interface ErrorResponse {\n data: null;\n status: number;\n message: string;\n validationErrors?: Record<string, { errors: string[] }>;\n trace?: ErrorTrace;\n}\n\nexport interface ZetiErrorHandlerConfig {\n formatError?: (error: any, context: Context) => ErrorResponse;\n logError?: (error: any, context: Context) => void;\n /** Incluir trace de erro na resposta (default: true em dev, false em prod) */\n includeTrace?: boolean;\n /** Incluir stack trace na resposta (default: false) */\n includeStack?: boolean;\n}\n\n/**\n * Detecta se o erro é relacionado a JSON malformado\n */\nfunction isJsonParseError(err: any): boolean {\n if (!err) return false;\n \n const message = err.message?.toLowerCase() || '';\n const name = err.name?.toLowerCase() || '';\n \n return (\n name === 'syntaxerror' ||\n message.includes('json') ||\n message.includes('unexpected token') ||\n message.includes('unexpected end') ||\n message.includes('malformed') ||\n message.includes('parse') ||\n message.includes('invalid character')\n );\n}\n\n/**\n * Extrai informações detalhadas do erro de JSON\n */\nfunction getJsonErrorDetails(err: any): string {\n const message = err.message || '';\n \n // Tenta extrair posição do erro\n const positionMatch = message.match(/position\\s*(\\d+)/i);\n const lineMatch = message.match(/line\\s*(\\d+)/i);\n const columnMatch = message.match(/column\\s*(\\d+)/i);\n \n let details = 'JSON inválido no corpo da requisição';\n \n if (positionMatch) {\n details += ` (posição ${positionMatch[1]})`;\n }\n if (lineMatch && columnMatch) {\n details += ` (linha ${lineMatch[1]}, coluna ${columnMatch[1]})`;\n }\n \n // Adiciona dica sobre o caractere problemático\n const tokenMatch = message.match(/unexpected token\\s*['\"]?([^'\"]+)['\"]?/i);\n if (tokenMatch) {\n details += `. Caractere inesperado: \"${tokenMatch[1]}\"`;\n }\n \n // Dicas comuns\n if (message.includes('unexpected end')) {\n details += '. Verifique se o JSON está completo (faltando } ou ])';\n }\n \n return details;\n}\n\nexport function createErrorHandler(config?: ZetiErrorHandlerConfig) {\n const isDev = process.env.NODE_ENV !== 'production';\n const includeTrace = config?.includeTrace ?? isDev;\n const includeStack = config?.includeStack ?? false;\n \n return async (c: Context, next: () => Promise<void>) => {\n try {\n await next();\n } catch (err: any) {\n // Extrai informações do request para debug\n const tenant = c.req.header('x-tenant') || c.req.header('X-Tenant') || 'not-set';\n \n // Cria trace do erro\n const trace: ErrorTrace = {\n path: c.req.path,\n method: c.req.method,\n timestamp: new Date().toISOString(),\n errorType: err?.constructor?.name || 'Error',\n };\n \n if (includeStack && err?.stack) {\n trace.stack = err.stack;\n }\n \n // Log do erro no console com detalhes\n console.error(`\\n❌ [${trace.timestamp}] Error in ${trace.method} ${trace.path}`);\n console.error(` Tenant: ${tenant}`);\n console.error(` Type: ${trace.errorType}`);\n console.error(` Message: ${err?.message || 'Unknown error'}`);\n \n // Log de causa aninhada se existir\n if (err?.cause) {\n console.error(` Cause: ${err.cause?.message || err.cause}`);\n }\n \n if (isDev && err?.stack) {\n console.error(` Stack:\\n${err.stack.split('\\n').slice(0, 8).map((l: string) => ` ${l}`).join('\\n')}`);\n }\n \n const response = handleError(err, config, includeTrace ? trace : undefined, { tenant });\n \n if (config?.logError) {\n config.logError(err, c);\n }\n\n return c.json(response, response.status as any);\n }\n };\n}\n\nexport function handleError(\n exception: Error | HTTPException | Prisma.PrismaClientKnownRequestError | ValidationError[] | any,\n config?: ZetiErrorHandlerConfig,\n trace?: ErrorTrace,\n context?: { tenant?: string }\n): ErrorResponse {\n if (config?.formatError) {\n return config.formatError(exception, {} as Context);\n }\n\n const isDev = process.env.NODE_ENV !== 'production';\n const includeDetails = config?.includeTrace ?? isDev;\n \n let status = 500;\n let message = 'Internal server error';\n let validationErrors: Record<string, { errors: string[] }> | undefined = undefined;\n\n // Verifica se é erro de JSON malformado PRIMEIRO\n if (isJsonParseError(exception)) {\n status = 400;\n message = getJsonErrorDetails(exception);\n validationErrors = {\n body: {\n errors: [\n 'O corpo da requisição contém JSON inválido',\n 'Verifique: aspas duplas em strings, vírgulas entre campos, chaves/colchetes balanceados',\n ],\n },\n };\n } else if (exception instanceof HTTPException) {\n status = exception.status;\n message = exception.message || 'Erro na requisição';\n\n // Verifica se a causa é erro de JSON\n if (exception.cause && isJsonParseError(exception.cause)) {\n status = 400;\n message = getJsonErrorDetails(exception.cause);\n validationErrors = {\n body: {\n errors: [\n 'O corpo da requisição contém JSON inválido',\n 'Verifique: aspas duplas em strings, vírgulas entre campos, chaves/colchetes balanceados',\n ],\n },\n };\n } else if (status === 400 || status === 422) {\n if (exception.cause && typeof exception.cause === 'object') {\n const cause = exception.cause as any;\n if (cause.validationErrors) {\n validationErrors = cause.validationErrors;\n } else if (cause.issues && Array.isArray(cause.issues)) {\n validationErrors = cause.issues.reduce((acc: any, issue: any) => {\n const field = issue.path?.join('.') || 'unknown';\n if (!acc[field]) {\n acc[field] = { errors: [] };\n }\n acc[field].errors.push(issue.message);\n return acc;\n }, {});\n }\n }\n }\n } else if (exception && typeof exception === 'object' && 'code' in exception && exception.code && typeof exception.code === 'string' && exception.code.startsWith('P')) {\n const prismaError = exception as any as Prisma.PrismaClientKnownRequestError;\n switch (prismaError.code) {\n case 'P2003': {\n status = 422;\n const fieldName = (prismaError.meta as any)?.field_name as string;\n if (fieldName) {\n validationErrors = {\n [fieldName]: {\n errors: ['Campo inválido ou o registro não existe'],\n },\n };\n } else {\n message = 'Campo inválido ou o registro não existe';\n }\n break;\n }\n\n case 'P2002': {\n status = 409;\n message = 'Registro já cadastrado';\n break;\n }\n\n case 'P2025': {\n status = 404;\n message = 'Registro não encontrado';\n break;\n }\n\n default: {\n message = prismaError.message || 'Erro no banco de dados';\n break;\n }\n }\n } else if (Array.isArray(exception)) {\n status = 422;\n message = '';\n validationErrors = exception.reduce((acc, error: ValidationError) => {\n acc[error.field] = {\n errors: error.errors,\n };\n return acc;\n }, {} as Record<string, { errors: string[] }>);\n } else if (exception instanceof Error) {\n // Em desenvolvimento, mostra a mensagem real do erro\n if (includeDetails) {\n message = exception.message || 'Erro interno do servidor';\n }\n }\n\n const response: ErrorResponse = {\n data: null,\n status,\n message,\n };\n\n if ((status === 400 || status === 422) && validationErrors && Object.keys(validationErrors).length > 0) {\n response.validationErrors = validationErrors;\n }\n\n // Adiciona trace se disponível\n if (trace) {\n response.trace = trace;\n \n // Em desenvolvimento, adiciona informações extras\n if (includeDetails && context?.tenant) {\n (response as any).debug = {\n tenant: context.tenant,\n errorMessage: exception?.message,\n errorCause: exception?.cause?.message || exception?.cause,\n };\n }\n }\n\n return response;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
definePrismaConfig,
|
|
15
15
|
getAllDatabaseUrls
|
|
16
16
|
} from "./chunk-VW7PIVLA.js";
|
|
17
|
+
import {
|
|
18
|
+
createErrorHandler,
|
|
19
|
+
handleError
|
|
20
|
+
} from "./chunk-SSL7KSRJ.js";
|
|
17
21
|
import {
|
|
18
22
|
FILE_SCHEMA_SYMBOL,
|
|
19
23
|
generateSwagger,
|
|
@@ -10109,6 +10113,9 @@ import { createRequire } from "module";
|
|
|
10109
10113
|
import { Hono } from "hono";
|
|
10110
10114
|
import { z } from "zod";
|
|
10111
10115
|
import { zValidator } from "@hono/zod-validator";
|
|
10116
|
+
import { logger } from "hono/logger";
|
|
10117
|
+
import { cors } from "hono/cors";
|
|
10118
|
+
import { swaggerUI } from "@hono/swagger-ui";
|
|
10112
10119
|
var require2 = createRequire(import.meta.url);
|
|
10113
10120
|
var debugEnabled = false;
|
|
10114
10121
|
function debug(category, message, data) {
|
|
@@ -10183,13 +10190,11 @@ function createZetiApp(options) {
|
|
|
10183
10190
|
);
|
|
10184
10191
|
const swaggerRegistry = new SwaggerRegistry();
|
|
10185
10192
|
if (config.logger !== false) {
|
|
10186
|
-
const { logger } = require2("hono/logger");
|
|
10187
10193
|
honoApp.use("*", logger());
|
|
10188
10194
|
}
|
|
10189
10195
|
if (config.cors?.enabled !== false) {
|
|
10190
|
-
const { cors } = require2("hono/cors");
|
|
10191
10196
|
honoApp.use("*", cors({
|
|
10192
|
-
origin: config.cors?.origin
|
|
10197
|
+
origin: config.cors?.origin,
|
|
10193
10198
|
allowMethods: config.cors?.allowMethods || ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE", "OPTIONS"],
|
|
10194
10199
|
allowHeaders: config.cors?.allowHeaders || ["*"],
|
|
10195
10200
|
credentials: config.cors?.credentials !== false,
|
|
@@ -10208,8 +10213,8 @@ function createZetiApp(options) {
|
|
|
10208
10213
|
});
|
|
10209
10214
|
if (config.errorHandler !== false) {
|
|
10210
10215
|
const errorHandlerConfig = typeof config.errorHandler === "object" ? config.errorHandler : void 0;
|
|
10211
|
-
honoApp.onError((err, c) => {
|
|
10212
|
-
const { handleError: handleError2 } =
|
|
10216
|
+
honoApp.onError(async (err, c) => {
|
|
10217
|
+
const { handleError: handleError2 } = await import("./error-handler-LBN3A7N3.js");
|
|
10213
10218
|
const isDev = process.env.NODE_ENV !== "production";
|
|
10214
10219
|
const includeTrace = errorHandlerConfig?.includeTrace ?? isDev;
|
|
10215
10220
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -10232,7 +10237,6 @@ function createZetiApp(options) {
|
|
|
10232
10237
|
const docPath = config.swagger.docPath || "/swagger/doc";
|
|
10233
10238
|
let cachedSwaggerSpec = null;
|
|
10234
10239
|
let cachedSchemas = null;
|
|
10235
|
-
const { swaggerUI } = require2("@hono/swagger-ui");
|
|
10236
10240
|
honoApp.get(uiPath, swaggerUI({ url: docPath }));
|
|
10237
10241
|
honoApp.get(docPath, async (c) => {
|
|
10238
10242
|
const { generateSwagger: generateSwagger2 } = await import("./generator-KC24DE6M.js");
|
|
@@ -11239,7 +11243,7 @@ function defineZetiConfig(config, options) {
|
|
|
11239
11243
|
...config.swagger,
|
|
11240
11244
|
servers: config.swagger.servers || swaggerDefaults.servers
|
|
11241
11245
|
};
|
|
11242
|
-
const
|
|
11246
|
+
const cors2 = {
|
|
11243
11247
|
...DEFAULT_CORS,
|
|
11244
11248
|
...config.cors
|
|
11245
11249
|
};
|
|
@@ -11253,7 +11257,7 @@ function defineZetiConfig(config, options) {
|
|
|
11253
11257
|
};
|
|
11254
11258
|
const baseConfig = {
|
|
11255
11259
|
swagger,
|
|
11256
|
-
cors,
|
|
11260
|
+
cors: cors2,
|
|
11257
11261
|
prisma,
|
|
11258
11262
|
dev,
|
|
11259
11263
|
logger: config.logger ?? true,
|
|
@@ -11293,216 +11297,31 @@ function defineZetiConfig(config, options) {
|
|
|
11293
11297
|
};
|
|
11294
11298
|
}
|
|
11295
11299
|
|
|
11296
|
-
// src/utils/error-handler.ts
|
|
11297
|
-
import { HTTPException } from "hono/http-exception";
|
|
11298
|
-
function isJsonParseError(err) {
|
|
11299
|
-
if (!err) return false;
|
|
11300
|
-
const message = err.message?.toLowerCase() || "";
|
|
11301
|
-
const name = err.name?.toLowerCase() || "";
|
|
11302
|
-
return name === "syntaxerror" || message.includes("json") || message.includes("unexpected token") || message.includes("unexpected end") || message.includes("malformed") || message.includes("parse") || message.includes("invalid character");
|
|
11303
|
-
}
|
|
11304
|
-
function getJsonErrorDetails(err) {
|
|
11305
|
-
const message = err.message || "";
|
|
11306
|
-
const positionMatch = message.match(/position\s*(\d+)/i);
|
|
11307
|
-
const lineMatch = message.match(/line\s*(\d+)/i);
|
|
11308
|
-
const columnMatch = message.match(/column\s*(\d+)/i);
|
|
11309
|
-
let details = "JSON inv\xE1lido no corpo da requisi\xE7\xE3o";
|
|
11310
|
-
if (positionMatch) {
|
|
11311
|
-
details += ` (posi\xE7\xE3o ${positionMatch[1]})`;
|
|
11312
|
-
}
|
|
11313
|
-
if (lineMatch && columnMatch) {
|
|
11314
|
-
details += ` (linha ${lineMatch[1]}, coluna ${columnMatch[1]})`;
|
|
11315
|
-
}
|
|
11316
|
-
const tokenMatch = message.match(/unexpected token\s*['"]?([^'"]+)['"]?/i);
|
|
11317
|
-
if (tokenMatch) {
|
|
11318
|
-
details += `. Caractere inesperado: "${tokenMatch[1]}"`;
|
|
11319
|
-
}
|
|
11320
|
-
if (message.includes("unexpected end")) {
|
|
11321
|
-
details += ". Verifique se o JSON est\xE1 completo (faltando } ou ])";
|
|
11322
|
-
}
|
|
11323
|
-
return details;
|
|
11324
|
-
}
|
|
11325
|
-
function createErrorHandler(config) {
|
|
11326
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
11327
|
-
const includeTrace = config?.includeTrace ?? isDev;
|
|
11328
|
-
const includeStack = config?.includeStack ?? false;
|
|
11329
|
-
return async (c, next) => {
|
|
11330
|
-
try {
|
|
11331
|
-
await next();
|
|
11332
|
-
} catch (err) {
|
|
11333
|
-
const tenant = c.req.header("x-tenant") || c.req.header("X-Tenant") || "not-set";
|
|
11334
|
-
const trace = {
|
|
11335
|
-
path: c.req.path,
|
|
11336
|
-
method: c.req.method,
|
|
11337
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11338
|
-
errorType: err?.constructor?.name || "Error"
|
|
11339
|
-
};
|
|
11340
|
-
if (includeStack && err?.stack) {
|
|
11341
|
-
trace.stack = err.stack;
|
|
11342
|
-
}
|
|
11343
|
-
console.error(`
|
|
11344
|
-
\u274C [${trace.timestamp}] Error in ${trace.method} ${trace.path}`);
|
|
11345
|
-
console.error(` Tenant: ${tenant}`);
|
|
11346
|
-
console.error(` Type: ${trace.errorType}`);
|
|
11347
|
-
console.error(` Message: ${err?.message || "Unknown error"}`);
|
|
11348
|
-
if (err?.cause) {
|
|
11349
|
-
console.error(` Cause: ${err.cause?.message || err.cause}`);
|
|
11350
|
-
}
|
|
11351
|
-
if (isDev && err?.stack) {
|
|
11352
|
-
console.error(` Stack:
|
|
11353
|
-
${err.stack.split("\n").slice(0, 8).map((l) => ` ${l}`).join("\n")}`);
|
|
11354
|
-
}
|
|
11355
|
-
const response = handleError(err, config, includeTrace ? trace : void 0, { tenant });
|
|
11356
|
-
if (config?.logError) {
|
|
11357
|
-
config.logError(err, c);
|
|
11358
|
-
}
|
|
11359
|
-
return c.json(response, response.status);
|
|
11360
|
-
}
|
|
11361
|
-
};
|
|
11362
|
-
}
|
|
11363
|
-
function handleError(exception, config, trace, context) {
|
|
11364
|
-
if (config?.formatError) {
|
|
11365
|
-
return config.formatError(exception, {});
|
|
11366
|
-
}
|
|
11367
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
11368
|
-
const includeDetails = config?.includeTrace ?? isDev;
|
|
11369
|
-
let status = 500;
|
|
11370
|
-
let message = "Internal server error";
|
|
11371
|
-
let validationErrors = void 0;
|
|
11372
|
-
if (isJsonParseError(exception)) {
|
|
11373
|
-
status = 400;
|
|
11374
|
-
message = getJsonErrorDetails(exception);
|
|
11375
|
-
validationErrors = {
|
|
11376
|
-
body: {
|
|
11377
|
-
errors: [
|
|
11378
|
-
"O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
|
|
11379
|
-
"Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
|
|
11380
|
-
]
|
|
11381
|
-
}
|
|
11382
|
-
};
|
|
11383
|
-
} else if (exception instanceof HTTPException) {
|
|
11384
|
-
status = exception.status;
|
|
11385
|
-
message = exception.message || "Erro na requisi\xE7\xE3o";
|
|
11386
|
-
if (exception.cause && isJsonParseError(exception.cause)) {
|
|
11387
|
-
status = 400;
|
|
11388
|
-
message = getJsonErrorDetails(exception.cause);
|
|
11389
|
-
validationErrors = {
|
|
11390
|
-
body: {
|
|
11391
|
-
errors: [
|
|
11392
|
-
"O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
|
|
11393
|
-
"Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
|
|
11394
|
-
]
|
|
11395
|
-
}
|
|
11396
|
-
};
|
|
11397
|
-
} else if (status === 400 || status === 422) {
|
|
11398
|
-
if (exception.cause && typeof exception.cause === "object") {
|
|
11399
|
-
const cause = exception.cause;
|
|
11400
|
-
if (cause.validationErrors) {
|
|
11401
|
-
validationErrors = cause.validationErrors;
|
|
11402
|
-
} else if (cause.issues && Array.isArray(cause.issues)) {
|
|
11403
|
-
validationErrors = cause.issues.reduce((acc, issue) => {
|
|
11404
|
-
const field = issue.path?.join(".") || "unknown";
|
|
11405
|
-
if (!acc[field]) {
|
|
11406
|
-
acc[field] = { errors: [] };
|
|
11407
|
-
}
|
|
11408
|
-
acc[field].errors.push(issue.message);
|
|
11409
|
-
return acc;
|
|
11410
|
-
}, {});
|
|
11411
|
-
}
|
|
11412
|
-
}
|
|
11413
|
-
}
|
|
11414
|
-
} else if (exception && typeof exception === "object" && "code" in exception && exception.code && typeof exception.code === "string" && exception.code.startsWith("P")) {
|
|
11415
|
-
const prismaError = exception;
|
|
11416
|
-
switch (prismaError.code) {
|
|
11417
|
-
case "P2003": {
|
|
11418
|
-
status = 422;
|
|
11419
|
-
const fieldName = prismaError.meta?.field_name;
|
|
11420
|
-
if (fieldName) {
|
|
11421
|
-
validationErrors = {
|
|
11422
|
-
[fieldName]: {
|
|
11423
|
-
errors: ["Campo inv\xE1lido ou o registro n\xE3o existe"]
|
|
11424
|
-
}
|
|
11425
|
-
};
|
|
11426
|
-
} else {
|
|
11427
|
-
message = "Campo inv\xE1lido ou o registro n\xE3o existe";
|
|
11428
|
-
}
|
|
11429
|
-
break;
|
|
11430
|
-
}
|
|
11431
|
-
case "P2002": {
|
|
11432
|
-
status = 409;
|
|
11433
|
-
message = "Registro j\xE1 cadastrado";
|
|
11434
|
-
break;
|
|
11435
|
-
}
|
|
11436
|
-
case "P2025": {
|
|
11437
|
-
status = 404;
|
|
11438
|
-
message = "Registro n\xE3o encontrado";
|
|
11439
|
-
break;
|
|
11440
|
-
}
|
|
11441
|
-
default: {
|
|
11442
|
-
message = prismaError.message || "Erro no banco de dados";
|
|
11443
|
-
break;
|
|
11444
|
-
}
|
|
11445
|
-
}
|
|
11446
|
-
} else if (Array.isArray(exception)) {
|
|
11447
|
-
status = 422;
|
|
11448
|
-
message = "";
|
|
11449
|
-
validationErrors = exception.reduce((acc, error) => {
|
|
11450
|
-
acc[error.field] = {
|
|
11451
|
-
errors: error.errors
|
|
11452
|
-
};
|
|
11453
|
-
return acc;
|
|
11454
|
-
}, {});
|
|
11455
|
-
} else if (exception instanceof Error) {
|
|
11456
|
-
if (includeDetails) {
|
|
11457
|
-
message = exception.message || "Erro interno do servidor";
|
|
11458
|
-
}
|
|
11459
|
-
}
|
|
11460
|
-
const response = {
|
|
11461
|
-
data: null,
|
|
11462
|
-
status,
|
|
11463
|
-
message
|
|
11464
|
-
};
|
|
11465
|
-
if ((status === 400 || status === 422) && validationErrors && Object.keys(validationErrors).length > 0) {
|
|
11466
|
-
response.validationErrors = validationErrors;
|
|
11467
|
-
}
|
|
11468
|
-
if (trace) {
|
|
11469
|
-
response.trace = trace;
|
|
11470
|
-
if (includeDetails && context?.tenant) {
|
|
11471
|
-
response.debug = {
|
|
11472
|
-
tenant: context.tenant,
|
|
11473
|
-
errorMessage: exception?.message,
|
|
11474
|
-
errorCause: exception?.cause?.message || exception?.cause
|
|
11475
|
-
};
|
|
11476
|
-
}
|
|
11477
|
-
}
|
|
11478
|
-
return response;
|
|
11479
|
-
}
|
|
11480
|
-
|
|
11481
11300
|
// src/utils/http-exceptions.ts
|
|
11482
|
-
import { HTTPException
|
|
11301
|
+
import { HTTPException } from "hono/http-exception";
|
|
11483
11302
|
function badRequestError(message, cause) {
|
|
11484
|
-
throw new
|
|
11303
|
+
throw new HTTPException(400, { message, cause });
|
|
11485
11304
|
}
|
|
11486
11305
|
function unauthorizedError(message, cause) {
|
|
11487
|
-
throw new
|
|
11306
|
+
throw new HTTPException(401, { message, cause });
|
|
11488
11307
|
}
|
|
11489
11308
|
function forbiddenError(message, cause) {
|
|
11490
|
-
throw new
|
|
11309
|
+
throw new HTTPException(403, { message, cause });
|
|
11491
11310
|
}
|
|
11492
11311
|
function notFoundError(message, cause) {
|
|
11493
|
-
throw new
|
|
11312
|
+
throw new HTTPException(404, { message, cause });
|
|
11494
11313
|
}
|
|
11495
11314
|
function conflictError(message, cause) {
|
|
11496
|
-
throw new
|
|
11315
|
+
throw new HTTPException(409, { message, cause });
|
|
11497
11316
|
}
|
|
11498
11317
|
function goneError(message, cause) {
|
|
11499
|
-
throw new
|
|
11318
|
+
throw new HTTPException(410, { message, cause });
|
|
11500
11319
|
}
|
|
11501
11320
|
function unprocessableEntityError(message, cause) {
|
|
11502
|
-
throw new
|
|
11321
|
+
throw new HTTPException(422, { message, cause });
|
|
11503
11322
|
}
|
|
11504
11323
|
function internalServerError(message, cause) {
|
|
11505
|
-
throw new
|
|
11324
|
+
throw new HTTPException(500, { message, cause });
|
|
11506
11325
|
}
|
|
11507
11326
|
|
|
11508
11327
|
// src/utils/middleware.ts
|