90dc-core 1.16.15 → 1.16.16
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorMiddleware.d.ts","sourceRoot":"","sources":["../../../src/lib/middlewares/ErrorMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAc,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"ErrorMiddleware.d.ts","sourceRoot":"","sources":["../../../src/lib/middlewares/ErrorMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAc,MAAM,uBAAuB,CAAC;AAa7D,MAAM,WAAW,qBAAqB;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IACvD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,eAAe,CAAC,MAAM,GAAE,qBAA0B,IAWlD,KAAK,OAAO,EAAE,MAAM,IAAI,mBAmGvC;AA4DD,wBAAgB,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG;IAClD,gBAAgB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrE,CAQA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AppError, toAppError } from "../Errors/AppError.js";
|
|
3
|
-
import { initializeSentry, isSentryEnabled, reportErrorToSentry, reportMessageToSentry } from "../utils/SentryUtil.js";
|
|
3
|
+
import { initializeSentry, isSentryEnabled, reportErrorToSentry, reportMessageToSentry, scrubObject, captureRequestBody } from "../utils/SentryUtil.js";
|
|
4
4
|
import { Log } from "../utils/Logger.js";
|
|
5
5
|
const dclogger = Log.getInstance().extend("ErrorMiddleware");
|
|
6
6
|
export function ErrorMiddleware(config = {}) {
|
|
@@ -16,11 +16,13 @@ export function ErrorMiddleware(config = {}) {
|
|
|
16
16
|
const requestContext = {
|
|
17
17
|
method: ctx.method,
|
|
18
18
|
path: ctx.path,
|
|
19
|
-
body: ctx.request.body,
|
|
20
|
-
query: ctx.query,
|
|
21
|
-
headers: ctx.headers,
|
|
19
|
+
body: captureRequestBody(ctx.request.body),
|
|
20
|
+
query: scrubObject(ctx.query),
|
|
21
|
+
headers: scrubObject(ctx.headers),
|
|
22
22
|
userUuid: ctx.state.user?.userUuid,
|
|
23
|
-
validationErrors: validationErrors.validationErrors
|
|
23
|
+
validationErrors: validationErrors.validationErrors,
|
|
24
|
+
traceId: ctx.state.traceId,
|
|
25
|
+
ip: ctx.ip
|
|
24
26
|
};
|
|
25
27
|
// Log validation error with full request context and validation details
|
|
26
28
|
dclogger.error("Validation error", requestContext);
|
|
@@ -36,11 +38,14 @@ export function ErrorMiddleware(config = {}) {
|
|
|
36
38
|
const requestContext = {
|
|
37
39
|
method: ctx.method,
|
|
38
40
|
path: ctx.path,
|
|
39
|
-
body: ctx.request.body,
|
|
40
|
-
query: ctx.query,
|
|
41
|
-
headers: ctx.headers,
|
|
41
|
+
body: captureRequestBody(ctx.request.body),
|
|
42
|
+
query: scrubObject(ctx.query),
|
|
43
|
+
headers: scrubObject(ctx.headers),
|
|
44
|
+
userUuid: ctx.state.user?.userUuid,
|
|
42
45
|
error: err instanceof Error ? err.message : String(err),
|
|
43
|
-
stack: err instanceof Error ? err.stack : undefined
|
|
46
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
47
|
+
traceId: ctx.state.traceId,
|
|
48
|
+
ip: ctx.ip
|
|
44
49
|
};
|
|
45
50
|
// Log error with full request context
|
|
46
51
|
dclogger.error("Error processing request", requestContext);
|
|
@@ -63,7 +68,17 @@ export function ErrorMiddleware(config = {}) {
|
|
|
63
68
|
ctx.status = appError.statusCode;
|
|
64
69
|
ctx.set("X-Error-Code", appError.code);
|
|
65
70
|
ctx.type = "application/json";
|
|
66
|
-
|
|
71
|
+
const responseBody = formatter ? formatter(appError, ctx) : formatErrorResponse(appError, exposeErrorDetails);
|
|
72
|
+
ctx.body = responseBody;
|
|
73
|
+
// Log response body for debugging (only in non-production or for server errors)
|
|
74
|
+
if (process.env.NODE_ENV !== "production" || appError.statusCode >= 500) {
|
|
75
|
+
dclogger.error("Error response sent", {
|
|
76
|
+
statusCode: appError.statusCode,
|
|
77
|
+
errorCode: appError.code,
|
|
78
|
+
responseBody: scrubObject(responseBody),
|
|
79
|
+
traceId: ctx.state.traceId
|
|
80
|
+
});
|
|
81
|
+
}
|
|
67
82
|
}
|
|
68
83
|
};
|
|
69
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/middlewares/ErrorMiddleware.ts"],"sourcesContent":["import type { Context, Next } from \"koa\";\nimport { z } from \"zod\";\nimport { AppError, toAppError } from \"../Errors/AppError.js\";\nimport {\n initializeSentry,\n isSentryEnabled,\n reportErrorToSentry,\n reportMessageToSentry,\n} from \"../utils/SentryUtil.js\";\nimport { Log } from \"../utils/Logger.js\";\n\nconst dclogger = Log.getInstance().extend(\"ErrorMiddleware\");\n\nexport interface ErrorMiddlewareConfig {\n exposeErrorDetails?: boolean;\n logErrors?: boolean;\n logger?: (error: Error, ctx: Context) => void;\n formatter?: (error: AppError, ctx: Context) => unknown;\n disableSentry?: boolean;\n}\n\nexport function ErrorMiddleware(config: ErrorMiddlewareConfig = {}) {\n const {\n exposeErrorDetails = false,\n logErrors = true,\n logger,\n formatter,\n disableSentry = false,\n } = config;\n\n initializeSentry();\n\n return async (ctx: Context, next: Next) => {\n try {\n await next();\n } catch (err) {\n let appError: AppError;\n\n if (err instanceof z.ZodError) {\n const validationErrors = formatZodErrors(err);\n const requestContext = {\n method: ctx.method,\n path: ctx.path,\n body: (ctx.request as any).body,\n query: ctx.query,\n headers: ctx.headers,\n userUuid: ctx.state.user?.userUuid,\n validationErrors: validationErrors.validationErrors,\n };\n\n // Log validation error with full request context and validation details\n dclogger.error(\"Validation error\", requestContext);\n\n // Report to Sentry with full context\n if (!disableSentry && isSentryEnabled()) {\n reportMessageToSentry(\n \"Validation error\",\n \"error\",\n {\n request: requestContext,\n validationErrors,\n }\n );\n }\n\n appError = new AppError(\n \"VALIDATION_ERROR\",\n \"Request validation failed\",\n 400,\n validationErrors\n );\n } else {\n const requestContext = {\n method: ctx.method,\n path: ctx.path,\n body: (ctx.request as any).body,\n query: ctx.query,\n headers: ctx.headers,\n error: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n };\n\n // Log error with full request context\n dclogger.error(\"Error processing request\", requestContext);\n\n appError = toAppError(err);\n }\n\n if (!disableSentry && isSentryEnabled() && appError.statusCode !== 401) {\n reportErrorToSentry(appError, ctx, {\n operational: appError.isOperational,\n errorType: appError.constructor.name,\n statusCode: appError.statusCode,\n });\n }\n\n if (logErrors) {\n if (logger) {\n logger(appError, ctx);\n } else {\n defaultErrorLogger(appError, ctx);\n }\n }\n\n ctx.status = appError.statusCode;\n ctx.set(\"X-Error-Code\", appError.code);\n ctx.type = \"application/json\";\n\n ctx.body = formatter\n ? formatter(appError, ctx)\n : formatErrorResponse(appError, exposeErrorDetails);\n\n }\n };\n}\n\nfunction defaultErrorLogger(error: AppError, ctx: Context): void {\n const isDev = process.env.NODE_ENV === \"development\";\n\n if (error.statusCode >= 500) {\n if (!isDev) {\n const prodLogData = {\n error: {\n code: error.code,\n message: error.message,\n statusCode: error.statusCode,\n },\n request: {\n path: ctx.path,\n },\n user: {\n uuid: ctx.state.user?.userUuid,\n },\n traceId: ctx.state.traceId,\n };\n console.error(\"Server Error:\", JSON.stringify(prodLogData, null, 2));\n } else {\n console.error(\"Server Error:\", error.stack || error.message);\n }\n } else {\n console.warn(\"Client Error:\", JSON.stringify({\n code: error.code,\n message: error.message,\n statusCode: error.statusCode,\n path: ctx.path,\n }));\n }\n}\n\n\nfunction formatErrorResponse(error: AppError, exposeDetails: boolean): unknown {\n const response: {\n code: string;\n message: string;\n details?: unknown;\n stack?: string;\n } = {\n code: error.code,\n message: error.message,\n };\n\n if (exposeDetails || error.statusCode < 500) {\n if (error.details !== undefined) {\n response.details = error.details;\n }\n }\n\n if (exposeDetails && error.stack) {\n response.stack = error.stack;\n }\n\n return response;\n}\n\nexport function formatZodErrors(error: z.ZodError): {\n validationErrors: { path: string; message: string; code: string }[];\n} {\n return {\n validationErrors: error.issues.map((issue) => ({\n path: issue.path.join(\".\"),\n message: issue.message,\n code: issue.code,\n })),\n };\n}"],"names":["z","AppError","toAppError","initializeSentry","isSentryEnabled","reportErrorToSentry","reportMessageToSentry","Log","dclogger","getInstance","extend","ErrorMiddleware","config","exposeErrorDetails","logErrors","logger","formatter","disableSentry","ctx","next","err","appError","ZodError","validationErrors","formatZodErrors","requestContext","method","path","body","request","query","headers","userUuid","state","user","error","Error","message","String","stack","undefined","statusCode","operational","isOperational","errorType","name","defaultErrorLogger","status","set","code","type","formatErrorResponse","isDev","process","env","NODE_ENV","prodLogData","uuid","traceId","console","JSON","stringify","warn","exposeDetails","response","details","issues","map","issue","join"],"mappings":"AACA,SAASA,CAAC,QAAQ,MAAM;AACxB,SAASC,QAAQ,EAAEC,UAAU,QAAQ,wBAAwB;AAC7D,SACEC,gBAAgB,EAChBC,eAAe,EACfC,mBAAmB,EACnBC,qBAAqB,QAChB,yBAAyB;AAChC,SAASC,GAAG,QAAQ,qBAAqB;AAEzC,MAAMC,WAAWD,IAAIE,WAAW,GAAGC,MAAM,CAAC;AAU1C,OAAO,SAASC,gBAAgBC,SAAgC,CAAC,CAAC;IAChE,MAAM,EACJC,qBAAqB,KAAK,EAC1BC,YAAY,IAAI,EAChBC,MAAM,EACNC,SAAS,EACTC,gBAAgB,KAAK,EACtB,GAAGL;IAEJT;IAEA,OAAO,OAAOe,KAAcC;QAC1B,IAAI;YACF,MAAMA;QACR,EAAE,OAAOC,KAAK;YACZ,IAAIC;YAEJ,IAAID,eAAepB,EAAEsB,QAAQ,EAAE;gBAC7B,MAAMC,mBAAmBC,gBAAgBJ;gBACzC,MAAMK,iBAAiB;oBACrBC,QAAQR,IAAIQ,MAAM;oBAClBC,MAAMT,IAAIS,IAAI;oBACdC,MAAM,AAACV,IAAIW,OAAO,CAASD,IAAI;oBAC/BE,OAAOZ,IAAIY,KAAK;oBAChBC,SAASb,IAAIa,OAAO;oBACpBC,UAAUd,IAAIe,KAAK,CAACC,IAAI,EAAEF;oBAC1BT,kBAAkBA,iBAAiBA,gBAAgB;gBACrD;gBAEA,wEAAwE;gBACxEf,SAAS2B,KAAK,CAAC,oBAAoBV;gBAEnC,qCAAqC;gBACrC,IAAI,CAACR,iBAAiBb,mBAAmB;oBACvCE,sBACE,oBACA,SACA;wBACEuB,SAASJ;wBACTF;oBACF;gBAEJ;gBAEAF,WAAW,IAAIpB,SACX,oBACA,6BACA,KACAsB;YAEN,OAAO;gBACL,MAAME,iBAAiB;oBACrBC,QAAQR,IAAIQ,MAAM;oBAClBC,MAAMT,IAAIS,IAAI;oBACdC,MAAM,AAACV,IAAIW,OAAO,CAASD,IAAI;oBAC/BE,OAAOZ,IAAIY,KAAK;oBAChBC,SAASb,IAAIa,OAAO;oBACpBI,OAAOf,eAAegB,QAAQhB,IAAIiB,OAAO,GAAGC,OAAOlB;oBACnDmB,OAAOnB,eAAegB,QAAQhB,IAAImB,KAAK,GAAGC;gBAC5C;gBAEA,sCAAsC;gBACtChC,SAAS2B,KAAK,CAAC,4BAA4BV;gBAE3CJ,WAAWnB,WAAWkB;YACxB;YAEA,IAAI,CAACH,iBAAiBb,qBAAqBiB,SAASoB,UAAU,KAAK,KAAK;gBACtEpC,oBAAoBgB,UAAUH,KAAK;oBACjCwB,aAAarB,SAASsB,aAAa;oBACnCC,WAAWvB,SAAS,WAAW,CAACwB,IAAI;oBACpCJ,YAAYpB,SAASoB,UAAU;gBACjC;YACF;YAEA,IAAI3B,WAAW;gBACb,IAAIC,QAAQ;oBACVA,OAAOM,UAAUH;gBACnB,OAAO;oBACL4B,mBAAmBzB,UAAUH;gBAC/B;YACF;YAEAA,IAAI6B,MAAM,GAAG1B,SAASoB,UAAU;YAChCvB,IAAI8B,GAAG,CAAC,gBAAgB3B,SAAS4B,IAAI;YACrC/B,IAAIgC,IAAI,GAAG;YAEXhC,IAAIU,IAAI,GAAGZ,YACLA,UAAUK,UAAUH,OACpBiC,oBAAoB9B,UAAUR;QAEtC;IACF;AACF;AAEA,SAASiC,mBAAmBX,KAAe,EAAEjB,GAAY;IACvD,MAAMkC,QAAQC,QAAQC,GAAG,CAACC,QAAQ,KAAK;IAEvC,IAAIpB,MAAMM,UAAU,IAAI,KAAK;QAC3B,IAAI,CAACW,OAAO;YACV,MAAMI,cAAc;gBAClBrB,OAAO;oBACLc,MAAMd,MAAMc,IAAI;oBAChBZ,SAASF,MAAME,OAAO;oBACtBI,YAAYN,MAAMM,UAAU;gBAC9B;gBACAZ,SAAS;oBACPF,MAAMT,IAAIS,IAAI;gBAChB;gBACAO,MAAM;oBACJuB,MAAMvC,IAAIe,KAAK,CAACC,IAAI,EAAEF;gBACxB;gBACA0B,SAASxC,IAAIe,KAAK,CAACyB,OAAO;YAC5B;YACAC,QAAQxB,KAAK,CAAC,iBAAiByB,KAAKC,SAAS,CAACL,aAAa,MAAM;QACnE,OAAO;YACLG,QAAQxB,KAAK,CAAC,iBAAiBA,MAAMI,KAAK,IAAIJ,MAAME,OAAO;QAC7D;IACF,OAAO;QACLsB,QAAQG,IAAI,CAAC,iBAAiBF,KAAKC,SAAS,CAAC;YAC3CZ,MAAMd,MAAMc,IAAI;YAChBZ,SAASF,MAAME,OAAO;YACtBI,YAAYN,MAAMM,UAAU;YAC5Bd,MAAMT,IAAIS,IAAI;QAChB;IACF;AACF;AAGA,SAASwB,oBAAoBhB,KAAe,EAAE4B,aAAsB;IAClE,MAAMC,WAKF;QACAf,MAAMd,MAAMc,IAAI;QAChBZ,SAASF,MAAME,OAAO;IAC1B;IAEA,IAAI0B,iBAAiB5B,MAAMM,UAAU,GAAG,KAAK;QAC3C,IAAIN,MAAM8B,OAAO,KAAKzB,WAAW;YAC/BwB,SAASC,OAAO,GAAG9B,MAAM8B,OAAO;QAClC;IACF;IAEA,IAAIF,iBAAiB5B,MAAMI,KAAK,EAAE;QAChCyB,SAASzB,KAAK,GAAGJ,MAAMI,KAAK;IAC9B;IAEA,OAAOyB;AACT;AAEA,OAAO,SAASxC,gBAAgBW,KAAiB;IAG/C,OAAO;QACLZ,kBAAkBY,MAAM+B,MAAM,CAACC,GAAG,CAAC,CAACC,QAAW,CAAA;gBAC7CzC,MAAMyC,MAAMzC,IAAI,CAAC0C,IAAI,CAAC;gBACtBhC,SAAS+B,MAAM/B,OAAO;gBACtBY,MAAMmB,MAAMnB,IAAI;YAClB,CAAA;IACF;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/middlewares/ErrorMiddleware.ts"],"sourcesContent":["import type { Context, Next } from \"koa\";\nimport { z } from \"zod\";\nimport { AppError, toAppError } from \"../Errors/AppError.js\";\nimport {\n initializeSentry,\n isSentryEnabled,\n reportErrorToSentry,\n reportMessageToSentry,\n scrubObject,\n captureRequestBody,\n} from \"../utils/SentryUtil.js\";\nimport { Log } from \"../utils/Logger.js\";\n\nconst dclogger = Log.getInstance().extend(\"ErrorMiddleware\");\n\nexport interface ErrorMiddlewareConfig {\n exposeErrorDetails?: boolean;\n logErrors?: boolean;\n logger?: (error: Error, ctx: Context) => void;\n formatter?: (error: AppError, ctx: Context) => unknown;\n disableSentry?: boolean;\n}\n\nexport function ErrorMiddleware(config: ErrorMiddlewareConfig = {}) {\n const {\n exposeErrorDetails = false,\n logErrors = true,\n logger,\n formatter,\n disableSentry = false,\n } = config;\n\n initializeSentry();\n\n return async (ctx: Context, next: Next) => {\n try {\n await next();\n } catch (err) {\n let appError: AppError;\n\n if (err instanceof z.ZodError) {\n const validationErrors = formatZodErrors(err);\n const requestContext = {\n method: ctx.method,\n path: ctx.path,\n body: captureRequestBody((ctx.request as any).body),\n query: scrubObject(ctx.query),\n headers: scrubObject(ctx.headers),\n userUuid: ctx.state.user?.userUuid,\n validationErrors: validationErrors.validationErrors,\n traceId: ctx.state.traceId,\n ip: ctx.ip,\n };\n\n // Log validation error with full request context and validation details\n dclogger.error(\"Validation error\", requestContext);\n\n // Report to Sentry with full context\n if (!disableSentry && isSentryEnabled()) {\n reportMessageToSentry(\n \"Validation error\",\n \"error\",\n {\n request: requestContext,\n validationErrors,\n }\n );\n }\n\n appError = new AppError(\n \"VALIDATION_ERROR\",\n \"Request validation failed\",\n 400,\n validationErrors\n );\n } else {\n const requestContext = {\n method: ctx.method,\n path: ctx.path,\n body: captureRequestBody((ctx.request as any).body),\n query: scrubObject(ctx.query),\n headers: scrubObject(ctx.headers),\n userUuid: ctx.state.user?.userUuid,\n error: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n traceId: ctx.state.traceId,\n ip: ctx.ip,\n };\n\n // Log error with full request context\n dclogger.error(\"Error processing request\", requestContext);\n\n appError = toAppError(err);\n }\n\n if (!disableSentry && isSentryEnabled() && appError.statusCode !== 401) {\n reportErrorToSentry(appError, ctx, {\n operational: appError.isOperational,\n errorType: appError.constructor.name,\n statusCode: appError.statusCode,\n });\n }\n\n if (logErrors) {\n if (logger) {\n logger(appError, ctx);\n } else {\n defaultErrorLogger(appError, ctx);\n }\n }\n\n ctx.status = appError.statusCode;\n ctx.set(\"X-Error-Code\", appError.code);\n ctx.type = \"application/json\";\n\n const responseBody = formatter\n ? formatter(appError, ctx)\n : formatErrorResponse(appError, exposeErrorDetails);\n\n ctx.body = responseBody;\n\n // Log response body for debugging (only in non-production or for server errors)\n if (process.env.NODE_ENV !== \"production\" || appError.statusCode >= 500) {\n dclogger.error(\"Error response sent\", {\n statusCode: appError.statusCode,\n errorCode: appError.code,\n responseBody: scrubObject(responseBody),\n traceId: ctx.state.traceId,\n });\n }\n\n }\n };\n}\n\nfunction defaultErrorLogger(error: AppError, ctx: Context): void {\n const isDev = process.env.NODE_ENV === \"development\";\n\n if (error.statusCode >= 500) {\n if (!isDev) {\n const prodLogData = {\n error: {\n code: error.code,\n message: error.message,\n statusCode: error.statusCode,\n },\n request: {\n path: ctx.path,\n },\n user: {\n uuid: ctx.state.user?.userUuid,\n },\n traceId: ctx.state.traceId,\n };\n console.error(\"Server Error:\", JSON.stringify(prodLogData, null, 2));\n } else {\n console.error(\"Server Error:\", error.stack || error.message);\n }\n } else {\n console.warn(\"Client Error:\", JSON.stringify({\n code: error.code,\n message: error.message,\n statusCode: error.statusCode,\n path: ctx.path,\n }));\n }\n}\n\n\nfunction formatErrorResponse(error: AppError, exposeDetails: boolean): unknown {\n const response: {\n code: string;\n message: string;\n details?: unknown;\n stack?: string;\n } = {\n code: error.code,\n message: error.message,\n };\n\n if (exposeDetails || error.statusCode < 500) {\n if (error.details !== undefined) {\n response.details = error.details;\n }\n }\n\n if (exposeDetails && error.stack) {\n response.stack = error.stack;\n }\n\n return response;\n}\n\nexport function formatZodErrors(error: z.ZodError): {\n validationErrors: { path: string; message: string; code: string }[];\n} {\n return {\n validationErrors: error.issues.map((issue) => ({\n path: issue.path.join(\".\"),\n message: issue.message,\n code: issue.code,\n })),\n };\n}"],"names":["z","AppError","toAppError","initializeSentry","isSentryEnabled","reportErrorToSentry","reportMessageToSentry","scrubObject","captureRequestBody","Log","dclogger","getInstance","extend","ErrorMiddleware","config","exposeErrorDetails","logErrors","logger","formatter","disableSentry","ctx","next","err","appError","ZodError","validationErrors","formatZodErrors","requestContext","method","path","body","request","query","headers","userUuid","state","user","traceId","ip","error","Error","message","String","stack","undefined","statusCode","operational","isOperational","errorType","name","defaultErrorLogger","status","set","code","type","responseBody","formatErrorResponse","process","env","NODE_ENV","errorCode","isDev","prodLogData","uuid","console","JSON","stringify","warn","exposeDetails","response","details","issues","map","issue","join"],"mappings":"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"}
|
|
@@ -54,7 +54,7 @@ export function scrubObject(obj, depth = 0) {
|
|
|
54
54
|
}
|
|
55
55
|
return scrubbedObj;
|
|
56
56
|
}
|
|
57
|
-
const MAX_BODY_SIZE =
|
|
57
|
+
const MAX_BODY_SIZE = 100 * 1024; // 100KB - increased from 10KB for full payload visibility
|
|
58
58
|
export function captureRequestBody(body) {
|
|
59
59
|
if (!body) {
|
|
60
60
|
return body;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/utils/SentryUtil.ts"],"sourcesContent":["import * as Sentry from \"@sentry/node\";\nimport type { Context } from \"koa\";\n\nlet sentryInitialized = false;\n\nexport function initializeSentry(): void {\n if (sentryInitialized) {\n return;\n }\n\n const dsn = process.env.SENTRY_DSN;\n const nodeEnv = process.env.NODE_ENV;\n\n if (!dsn || nodeEnv !== \"production\") {\n return;\n }\n\n Sentry.init({\n dsn,\n environment: nodeEnv,\n enabled: true,\n tracesSampleRate: 0,\n });\n\n sentryInitialized = true;\n}\n\nexport function isSentryEnabled(): boolean {\n return sentryInitialized;\n}\n\nconst SENSITIVE_FIELDS = [\n \"password\",\n \"token\",\n \"apikey\",\n \"secret\",\n \"cookie\",\n];\n\nexport function scrubObject(obj: any, depth = 0): any {\n if (depth > 10) {\n return \"[Max Depth]\";\n }\n\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (typeof obj !== \"object\") {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => scrubObject(item, depth + 1));\n }\n\n const scrubbedObj: any = {};\n for (const [key, value] of Object.entries(obj)) {\n const keyLower = key.toLowerCase();\n const isSensitive = SENSITIVE_FIELDS.some((field) =>\n keyLower.includes(field),\n );\n\n if (isSensitive) {\n scrubbedObj[key] = \"[REDACTED]\";\n } else if (typeof value === \"object\" && value !== null) {\n scrubbedObj[key] = scrubObject(value, depth + 1);\n } else {\n scrubbedObj[key] = value;\n }\n }\n\n return scrubbedObj;\n}\n\nconst MAX_BODY_SIZE =
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils/SentryUtil.ts"],"sourcesContent":["import * as Sentry from \"@sentry/node\";\nimport type { Context } from \"koa\";\n\nlet sentryInitialized = false;\n\nexport function initializeSentry(): void {\n if (sentryInitialized) {\n return;\n }\n\n const dsn = process.env.SENTRY_DSN;\n const nodeEnv = process.env.NODE_ENV;\n\n if (!dsn || nodeEnv !== \"production\") {\n return;\n }\n\n Sentry.init({\n dsn,\n environment: nodeEnv,\n enabled: true,\n tracesSampleRate: 0,\n });\n\n sentryInitialized = true;\n}\n\nexport function isSentryEnabled(): boolean {\n return sentryInitialized;\n}\n\nconst SENSITIVE_FIELDS = [\n \"password\",\n \"token\",\n \"apikey\",\n \"secret\",\n \"cookie\",\n];\n\nexport function scrubObject(obj: any, depth = 0): any {\n if (depth > 10) {\n return \"[Max Depth]\";\n }\n\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (typeof obj !== \"object\") {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => scrubObject(item, depth + 1));\n }\n\n const scrubbedObj: any = {};\n for (const [key, value] of Object.entries(obj)) {\n const keyLower = key.toLowerCase();\n const isSensitive = SENSITIVE_FIELDS.some((field) =>\n keyLower.includes(field),\n );\n\n if (isSensitive) {\n scrubbedObj[key] = \"[REDACTED]\";\n } else if (typeof value === \"object\" && value !== null) {\n scrubbedObj[key] = scrubObject(value, depth + 1);\n } else {\n scrubbedObj[key] = value;\n }\n }\n\n return scrubbedObj;\n}\n\nconst MAX_BODY_SIZE = 100 * 1024; // 100KB - increased from 10KB for full payload visibility\n\nexport function captureRequestBody(body: any): any {\n if (!body) {\n return body;\n }\n\n const bodyString = JSON.stringify(body);\n if (bodyString.length > MAX_BODY_SIZE) {\n return {\n _truncated: true,\n _originalSize: bodyString.length,\n preview: bodyString.substring(0, MAX_BODY_SIZE),\n };\n }\n\n return scrubObject(body);\n}\n\nexport function extractUserContext(ctx: Context): { id?: string } {\n const user = (ctx.state as any)?.user;\n if (user?.userUuid) {\n return { id: user.userUuid };\n }\n return {};\n}\n\nexport function buildRequestContext(ctx: Context): {\n method: string;\n url: string;\n query: any;\n headers: any;\n data: any;\n ip?: string;\n} {\n return {\n method: ctx.method,\n url: ctx.url,\n query: scrubObject(ctx.query),\n headers: scrubObject(ctx.headers),\n data: captureRequestBody((ctx.request as any).body),\n ip: ctx.ip,\n };\n}\n\nexport function buildResponseContext(ctx: Context): { status_code: number } {\n return {\n status_code: ctx.status,\n };\n}\n\nexport interface ErrorReportOptions {\n operational?: boolean;\n errorType?: string;\n statusCode?: number;\n}\n\nexport function reportErrorToSentry(\n error: Error,\n ctx?: Context,\n options?: ErrorReportOptions,\n): void {\n if (!isSentryEnabled()) {\n return;\n }\n\n Sentry.withScope((scope) => {\n if (ctx) {\n scope.setContext(\"request\", buildRequestContext(ctx));\n scope.setContext(\"response\", buildResponseContext(ctx));\n scope.setUser(extractUserContext(ctx));\n }\n\n if (options?.operational !== undefined) {\n scope.setTag(\"error.operational\", options.operational);\n }\n if (options?.errorType) {\n scope.setTag(\"error.type\", options.errorType);\n }\n if (options?.statusCode) {\n scope.setTag(\"http.status_code\", options.statusCode);\n }\n\n scope.setTag(\"source\", \"middleware\");\n\n Sentry.captureException(error);\n });\n\n (error as any)._sentryReported = true;\n}\n\nexport type SeverityLevel = \"fatal\" | \"error\" | \"warning\" | \"info\" | \"debug\";\n\nexport function reportMessageToSentry(\n message: string,\n level: SeverityLevel,\n context?: Record<string, any>,\n): void {\n if (!isSentryEnabled()) {\n return;\n }\n\n Sentry.withScope((scope) => {\n if (context) {\n for (const [key, value] of Object.entries(context)) {\n if (key === \"logger_name\" || key === \"source\") {\n scope.setTag(key, value);\n } else {\n // Deeply serialize objects and arrays to avoid [Object] and [Array] in Sentry\n const serializedValue =\n typeof value === \"object\" && value !== null\n ? scrubObject(value)\n : value;\n scope.setContext(key, serializedValue);\n }\n }\n }\n\n Sentry.captureMessage(message, level);\n });\n}\n"],"names":["Sentry","sentryInitialized","initializeSentry","dsn","process","env","SENTRY_DSN","nodeEnv","NODE_ENV","init","environment","enabled","tracesSampleRate","isSentryEnabled","SENSITIVE_FIELDS","scrubObject","obj","depth","undefined","Array","isArray","map","item","scrubbedObj","key","value","Object","entries","keyLower","toLowerCase","isSensitive","some","field","includes","MAX_BODY_SIZE","captureRequestBody","body","bodyString","JSON","stringify","length","_truncated","_originalSize","preview","substring","extractUserContext","ctx","user","state","userUuid","id","buildRequestContext","method","url","query","headers","data","request","ip","buildResponseContext","status_code","status","reportErrorToSentry","error","options","withScope","scope","setContext","setUser","operational","setTag","errorType","statusCode","captureException","_sentryReported","reportMessageToSentry","message","level","context","serializedValue","captureMessage"],"mappings":"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"}
|