90dc-core 1.16.11 → 1.16.12
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/lib/middlewares/ErrorMiddleware.d.ts.map +1 -1
- package/dist/lib/middlewares/ErrorMiddleware.js +30 -1
- package/dist/lib/middlewares/ErrorMiddleware.js.map +1 -1
- package/dist/lib/utils/SentryUtil.d.ts.map +1 -1
- package/dist/lib/utils/SentryUtil.js +4 -2
- package/dist/lib/utils/SentryUtil.js.map +1 -1
- package/package.json +1 -1
|
@@ -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;AAW7D,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,mBA+EvC;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,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AppError, toAppError } from "../Errors/AppError.js";
|
|
3
|
-
import { initializeSentry, isSentryEnabled, reportErrorToSentry } from "../utils/SentryUtil.js";
|
|
3
|
+
import { initializeSentry, isSentryEnabled, reportErrorToSentry, reportMessageToSentry } from "../utils/SentryUtil.js";
|
|
4
|
+
import { Log } from "../utils/Logger.js";
|
|
5
|
+
const dclogger = Log.getInstance().extend("ErrorMiddleware");
|
|
4
6
|
export function ErrorMiddleware(config = {}) {
|
|
5
7
|
const { exposeErrorDetails = false, logErrors = true, logger, formatter, disableSentry = false } = config;
|
|
6
8
|
initializeSentry();
|
|
@@ -10,8 +12,35 @@ export function ErrorMiddleware(config = {}) {
|
|
|
10
12
|
} catch (err) {
|
|
11
13
|
let appError;
|
|
12
14
|
if (err instanceof z.ZodError) {
|
|
15
|
+
const requestContext = {
|
|
16
|
+
method: ctx.method,
|
|
17
|
+
path: ctx.path,
|
|
18
|
+
body: ctx.request.body,
|
|
19
|
+
query: ctx.query,
|
|
20
|
+
headers: ctx.headers
|
|
21
|
+
};
|
|
22
|
+
// Log validation error with full request context
|
|
23
|
+
dclogger.error("Validation error", requestContext);
|
|
24
|
+
// Report to Sentry with full context
|
|
25
|
+
if (!disableSentry && isSentryEnabled()) {
|
|
26
|
+
reportMessageToSentry("Validation error", "error", {
|
|
27
|
+
request: requestContext,
|
|
28
|
+
validationErrors: formatZodErrors(err)
|
|
29
|
+
});
|
|
30
|
+
}
|
|
13
31
|
appError = new AppError("VALIDATION_ERROR", "Request validation failed", 400, formatZodErrors(err));
|
|
14
32
|
} else {
|
|
33
|
+
const requestContext = {
|
|
34
|
+
method: ctx.method,
|
|
35
|
+
path: ctx.path,
|
|
36
|
+
body: ctx.request.body,
|
|
37
|
+
query: ctx.query,
|
|
38
|
+
headers: ctx.headers,
|
|
39
|
+
error: err instanceof Error ? err.message : String(err),
|
|
40
|
+
stack: err instanceof Error ? err.stack : undefined
|
|
41
|
+
};
|
|
42
|
+
// Log error with full request context
|
|
43
|
+
dclogger.error("Error processing request", requestContext);
|
|
15
44
|
appError = toAppError(err);
|
|
16
45
|
}
|
|
17
46
|
if (!disableSentry && isSentryEnabled() && appError.statusCode !== 401) {
|
|
@@ -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} from \"../utils/SentryUtil.js\";\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 appError = new AppError(\n \"VALIDATION_ERROR\",\n \"Request validation failed\",\n 400,\n formatZodErrors(err)\n );\n } else {\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","ErrorMiddleware","config","exposeErrorDetails","logErrors","logger","formatter","disableSentry","ctx","next","err","appError","ZodError","formatZodErrors","statusCode","operational","isOperational","errorType","name","defaultErrorLogger","status","set","code","type","
|
|
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 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 };\n\n // Log validation error with full request context\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: formatZodErrors(err),\n }\n );\n }\n\n appError = new AppError(\n \"VALIDATION_ERROR\",\n \"Request validation failed\",\n 400,\n formatZodErrors(err)\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","requestContext","method","path","body","request","query","headers","error","validationErrors","formatZodErrors","Error","message","String","stack","undefined","statusCode","operational","isOperational","errorType","name","defaultErrorLogger","status","set","code","type","formatErrorResponse","isDev","process","env","NODE_ENV","prodLogData","user","uuid","state","userUuid","traceId","console","JSON","stringify","warn","exposeDetails","response","details","issues","map","issue","join"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SentryUtil.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/SentryUtil.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAInC,wBAAgB,gBAAgB,IAAI,IAAI,CAoBvC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAUD,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,SAAI,GAAG,GAAG,CAkCpD;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAejD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAMhE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,GAAG,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CASA;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAI1E;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,GAAG,CAAC,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI,CA4BN;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,IAAI,
|
|
1
|
+
{"version":3,"file":"SentryUtil.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/SentryUtil.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAInC,wBAAgB,gBAAgB,IAAI,IAAI,CAoBvC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAUD,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,SAAI,GAAG,GAAG,CAkCpD;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAejD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAMhE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,GAAG,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CASA;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAI1E;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,GAAG,CAAC,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI,CA4BN;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,IAAI,CAuBN"}
|
|
@@ -82,7 +82,7 @@ export function buildRequestContext(ctx) {
|
|
|
82
82
|
return {
|
|
83
83
|
method: ctx.method,
|
|
84
84
|
url: ctx.url,
|
|
85
|
-
query: ctx.query,
|
|
85
|
+
query: scrubObject(ctx.query),
|
|
86
86
|
headers: scrubObject(ctx.headers),
|
|
87
87
|
data: captureRequestBody(ctx.request.body),
|
|
88
88
|
ip: ctx.ip
|
|
@@ -127,7 +127,9 @@ export function reportMessageToSentry(message, level, context) {
|
|
|
127
127
|
if (key === "logger_name" || key === "source") {
|
|
128
128
|
scope.setTag(key, value);
|
|
129
129
|
} else {
|
|
130
|
-
|
|
130
|
+
// Deeply serialize objects and arrays to avoid [Object] and [Array] in Sentry
|
|
131
|
+
const serializedValue = typeof value === "object" && value !== null ? scrubObject(value) : value;
|
|
132
|
+
scope.setContext(key, serializedValue);
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
}
|
|
@@ -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 = 10 * 1024;\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: 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 scope.setContext(key,
|
|
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 = 10 * 1024;\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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"}
|