@nest-omni/core 4.1.3-31 → 4.1.3-32
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/package.json +1 -1
- package/setup/bootstrap.setup.js +95 -0
package/package.json
CHANGED
package/setup/bootstrap.setup.js
CHANGED
|
@@ -16,8 +16,10 @@ const profiling_node_1 = require("@sentry/profiling-node");
|
|
|
16
16
|
const dotenv = require("dotenv");
|
|
17
17
|
const fs_1 = require("fs");
|
|
18
18
|
const path_1 = require("path");
|
|
19
|
+
const pino_1 = require("pino");
|
|
19
20
|
const process = require("process");
|
|
20
21
|
const mode_setup_1 = require("./mode.setup");
|
|
22
|
+
const email_log_1 = require("../email-log");
|
|
21
23
|
function findValidRootPath() {
|
|
22
24
|
const getAppRootPath = () => {
|
|
23
25
|
// 优先使用 require.main.filename(应用入口文件)
|
|
@@ -72,6 +74,60 @@ requiredEnvVars.forEach((envVar) => {
|
|
|
72
74
|
throw new Error(`Missing required environment variable: ${envVar}`);
|
|
73
75
|
}
|
|
74
76
|
});
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// 创建用于 Sentry 错误邮件告警的 Pino Logger
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// 这个 Logger 会在 Sentry 捕获到非 HTTP 请求的错误时发送邮件告警
|
|
81
|
+
// (如 Cron Job、Bull Queue 中的错误)
|
|
82
|
+
// ============================================================================
|
|
83
|
+
let sentryEmailLogger = null;
|
|
84
|
+
function initSentryEmailLogger() {
|
|
85
|
+
const emailLogEnabled = process.env.EMAIL_LOG_ENABLED === 'true';
|
|
86
|
+
if (!emailLogEnabled) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
// 从环境变量读取邮件配置
|
|
91
|
+
const emailConfig = {
|
|
92
|
+
enabled: true,
|
|
93
|
+
to: (process.env.EMAIL_LOG_TO || '')
|
|
94
|
+
.split(',')
|
|
95
|
+
.map((e) => e.trim())
|
|
96
|
+
.filter(Boolean),
|
|
97
|
+
from: process.env.EMAIL_LOG_FROM || 'noreply@example.com',
|
|
98
|
+
level: process.env.EMAIL_LOG_LEVEL || 'error',
|
|
99
|
+
subjectPrefix: process.env.EMAIL_LOG_SUBJECT_PREFIX || '[Sentry Alert]',
|
|
100
|
+
smtpHost: process.env.EMAIL_LOG_SMTP_HOST || 'localhost',
|
|
101
|
+
smtpPort: parseInt(process.env.EMAIL_LOG_SMTP_PORT || '587', 10),
|
|
102
|
+
smtpUsername: process.env.EMAIL_LOG_SMTP_USERNAME || '',
|
|
103
|
+
smtpPassword: process.env.EMAIL_LOG_SMTP_PASSWORD || '',
|
|
104
|
+
smtpSecure: process.env.EMAIL_LOG_SMTP_SECURE === 'true',
|
|
105
|
+
smtpIgnoreTLSError: process.env.EMAIL_LOG_SMTP_IGNORE_TLS_ERROR === 'true',
|
|
106
|
+
rateLimitMaxEmails: parseInt(process.env.EMAIL_LOG_RATE_LIMIT_MAX_EMAILS || '10', 10),
|
|
107
|
+
rateLimitWindowMs: parseInt(process.env.EMAIL_LOG_RATE_LIMIT_WINDOW_MS || '60000', 10),
|
|
108
|
+
rateLimitBurstSize: parseInt(process.env.EMAIL_LOG_RATE_LIMIT_BURST_SIZE || '3', 10),
|
|
109
|
+
useHtmlFormat: process.env.EMAIL_LOG_USE_HTML_FORMAT !== 'false',
|
|
110
|
+
};
|
|
111
|
+
// 创建 Pino 邮件传输
|
|
112
|
+
const emailTransport = new email_log_1.PinoEmailTransport(emailConfig);
|
|
113
|
+
// 创建 Logger(只输出到邮件,不重复输出到控制台)
|
|
114
|
+
sentryEmailLogger = (0, pino_1.default)({
|
|
115
|
+
name: 'SentryEmailForwarder',
|
|
116
|
+
level: 'error',
|
|
117
|
+
}, pino_1.default.multistream([
|
|
118
|
+
{
|
|
119
|
+
level: emailConfig.level,
|
|
120
|
+
stream: emailTransport,
|
|
121
|
+
},
|
|
122
|
+
]));
|
|
123
|
+
console.log('[Sentry] Email logger initialized for non-HTTP errors');
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.error('[Sentry] Failed to initialize email logger:', error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// 初始化 Sentry 邮件 Logger
|
|
130
|
+
initSentryEmailLogger();
|
|
75
131
|
Sentry.init({
|
|
76
132
|
dsn: process.env.SENTRY_DSN || '',
|
|
77
133
|
release: process.env.API_VERSION || '',
|
|
@@ -83,6 +139,45 @@ Sentry.init({
|
|
|
83
139
|
profileLifecycle: 'trace',
|
|
84
140
|
sendDefaultPii: true,
|
|
85
141
|
integrations: [profiling_node_1.nodeProfilingIntegration],
|
|
142
|
+
// ============================================================================
|
|
143
|
+
// beforeSend 钩子:在 Sentry 发送错误之前,通过 Pino 发送邮件告警
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// 注意:HTTP 请求中的错误已经在 HttpExceptionFilter 中处理,这里跳过
|
|
146
|
+
// 只处理非 HTTP 请求的错误(如 Cron Job、Bull Queue 中的错误)
|
|
147
|
+
// ============================================================================
|
|
148
|
+
beforeSend(event) {
|
|
149
|
+
var _a, _b, _c, _d, _e;
|
|
150
|
+
// 如果有 request.url,说明是 HTTP 请求错误
|
|
151
|
+
// 这些错误已经在 HttpExceptionFilter 中通过 Pino Logger 处理了
|
|
152
|
+
// 这里跳过,避免重复发送邮件
|
|
153
|
+
if ((_a = event.request) === null || _a === void 0 ? void 0 : _a.url) {
|
|
154
|
+
return event;
|
|
155
|
+
}
|
|
156
|
+
// 只处理非 HTTP 请求的错误(Cron、Bull 等)
|
|
157
|
+
if (sentryEmailLogger && event.exception) {
|
|
158
|
+
const exceptionValue = (_c = (_b = event.exception) === null || _b === void 0 ? void 0 : _b.values) === null || _c === void 0 ? void 0 : _c[0];
|
|
159
|
+
sentryEmailLogger.error({
|
|
160
|
+
msg: `[Sentry] ${event.message || (exceptionValue === null || exceptionValue === void 0 ? void 0 : exceptionValue.value) || 'Error captured by Sentry'}`,
|
|
161
|
+
error: (exceptionValue === null || exceptionValue === void 0 ? void 0 : exceptionValue.value) || 'Unknown error',
|
|
162
|
+
stack: (_e = (_d = exceptionValue === null || exceptionValue === void 0 ? void 0 : exceptionValue.stacktrace) === null || _d === void 0 ? void 0 : _d.frames) === null || _e === void 0 ? void 0 : _e.map((f) => `${f.filename}:${f.lineno}`).join('\n'),
|
|
163
|
+
sentryEventId: event.event_id,
|
|
164
|
+
environment: event.environment,
|
|
165
|
+
release: event.release,
|
|
166
|
+
// Sentry 收集的额外上下文
|
|
167
|
+
request: event.request,
|
|
168
|
+
user: event.user,
|
|
169
|
+
tags: event.tags,
|
|
170
|
+
extra: event.extra,
|
|
171
|
+
// 异常类型和值
|
|
172
|
+
exceptionType: exceptionValue === null || exceptionValue === void 0 ? void 0 : exceptionValue.type,
|
|
173
|
+
exceptionValue: exceptionValue === null || exceptionValue === void 0 ? void 0 : exceptionValue.value,
|
|
174
|
+
// 错误来源(通过 transaction 判断是 Cron 还是 Bull)
|
|
175
|
+
transaction: event.transaction,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// 返回 event 让 Sentry 继续正常处理
|
|
179
|
+
return event;
|
|
180
|
+
},
|
|
86
181
|
});
|
|
87
182
|
const crud_1 = require("@dataui/crud");
|
|
88
183
|
crud_1.CrudConfigService.load({
|