diff --git a/bun.lock b/bun.lock index 4885469..35569ac 100644 --- a/bun.lock +++ b/bun.lock @@ -37,6 +37,7 @@ "@types/better-sqlite3": "7.6.13", "@types/markdown-it": "14.1.2", "@types/multer": "2.1.0", + "@types/nodemailer": "^8.0.0", "@types/pg": "8.20.0", "bun-types": "1.3.12", "drizzle-kit": "0.31.10", @@ -857,6 +858,8 @@ "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "@types/nodemailer": ["@types/nodemailer@8.0.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-fyf8jWULsCo0d0BuoQ75i6IeoHs47qcqxWc7yUdUcV0pOZGjUTTOvwdG1PRXUDqN/8A64yQdQdnA2pZgcdi+cA=="], + "@types/pg": ["@types/pg@8.20.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow=="], "@types/qs": ["@types/qs@6.15.0", "", {}, "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow=="], diff --git a/package.json b/package.json index 9af8330..b3bc43a 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@types/better-sqlite3": "7.6.13", "@types/markdown-it": "14.1.2", "@types/multer": "2.1.0", + "@types/nodemailer": "^8.0.0", "@types/pg": "8.20.0", "bun-types": "1.3.12", "drizzle-kit": "0.31.10", diff --git a/packages/drizzle-pkg/db.sqlite b/packages/drizzle-pkg/db.sqlite index df4dbd1..62e6504 100644 Binary files a/packages/drizzle-pkg/db.sqlite and b/packages/drizzle-pkg/db.sqlite differ diff --git a/server/api/config/global/comment-email-test.post.ts b/server/api/config/global/comment-email-test.post.ts index 1ae2340..6c7950c 100644 --- a/server/api/config/global/comment-email-test.post.ts +++ b/server/api/config/global/comment-email-test.post.ts @@ -1,3 +1,4 @@ +import log4js from "logger"; import { dbGlobal } from "drizzle-pkg/lib/db"; import { users } from "drizzle-pkg/lib/schema/auth"; import { eq } from "drizzle-orm"; @@ -9,6 +10,8 @@ import { sendCommentEmailTestMail, } from "#server/service/comment-email/test-mail"; +const logger = log4js.getLogger("COMMENT_EMAIL_TEST"); + export default defineWrappedResponseHandler(async (event) => { const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown"; assertUnderRateLimit(`admin-config-comment-email-test:${ip}`, 5, 60_000); @@ -21,19 +24,21 @@ export default defineWrappedResponseHandler(async (event) => { .where(eq(users.id, admin.id)) .limit(1); + const commentEmailConfig = { + enabled: await event.context.config.getGlobal("commentEmailNotifyEnabled"), + fromEmail: await event.context.config.getGlobal("commentMailFromEmail"), + smtpHost: await event.context.config.getGlobal("commentSmtpHost"), + smtpPort: await event.context.config.getGlobal("commentSmtpPort"), + smtpSecure: await event.context.config.getGlobal("commentSmtpSecure"), + smtpUser: await event.context.config.getGlobal("commentSmtpUser"), + smtpPass: await event.context.config.getGlobal("commentSmtpPass"), + }; + try { await sendCommentEmailTestMail({ toEmail: adminRow?.email ?? "", requestedBy: admin.username, - config: { - enabled: await event.context.config.getGlobal("commentEmailNotifyEnabled"), - fromEmail: await event.context.config.getGlobal("commentMailFromEmail"), - smtpHost: await event.context.config.getGlobal("commentSmtpHost"), - smtpPort: await event.context.config.getGlobal("commentSmtpPort"), - smtpSecure: await event.context.config.getGlobal("commentSmtpSecure"), - smtpUser: await event.context.config.getGlobal("commentSmtpUser"), - smtpPass: await event.context.config.getGlobal("commentSmtpPass"), - }, + config: commentEmailConfig, }); return R.success({ message: "测试邮件发送成功,请检查管理员邮箱", @@ -45,6 +50,16 @@ export default defineWrappedResponseHandler(async (event) => { statusMessage: error.message, }); } + logger.error( + "[send-test-mail-failed]", + `adminId=${admin.id}`, + `adminUsername=${admin.username}`, + `smtpHost=${commentEmailConfig.smtpHost}`, + `smtpPort=${commentEmailConfig.smtpPort}`, + `smtpSecure=${commentEmailConfig.smtpSecure}`, + error instanceof Error ? error.message : String(error), + error instanceof Error ? (error.stack ?? "") : "", + ); throw createError({ statusCode: 502, statusMessage: "测试邮件发送失败,请检查 SMTP 配置或稍后重试", diff --git a/server/service/comment-notify/index.ts b/server/service/comment-notify/index.ts index 6eb2978..daf75f1 100644 --- a/server/service/comment-notify/index.ts +++ b/server/service/comment-notify/index.ts @@ -42,6 +42,13 @@ function getReason(error: unknown): string { return "unknown"; } +function getStack(error: unknown): string { + if (error instanceof Error && hasValue(error.stack ?? "")) { + return error.stack ?? ""; + } + return ""; +} + function isValidEmail(value: string): boolean { return EMAIL_REGEX.test(value.trim()); } @@ -205,6 +212,7 @@ export async function notifyReplyCommentCreated( commentId: input.commentId, receiverUserId, reason: getReason(error), + stack: getStack(error), }); } }