feat(comments): 添加评论组件自动获取页面标题和URL功能

refactor(email): 简化评论回复邮件通知逻辑
fix(docs): 移除过时的EMAIL_ADDRESS配置说明
style(store): 修改loading初始状态为true
This commit is contained in:
anghunk
2026-01-19 21:26:03 +08:00
parent a722788cc9
commit 5eee1ed8dc
6 changed files with 56 additions and 58 deletions

View File

@@ -13,7 +13,8 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
if (!data || typeof data !== 'object') {
return c.json({ message: '无效的请求体' }, 400);
}
const { post_slug, content: rawContent, author: rawAuthor, email, url, parent_id, post_title, post_url } = data;
const { post_slug, content: rawContent, author: rawAuthor, email, url, post_title, post_url } = data;
const parentId = (data as any).parent_id ?? (data as any).parentId ?? null;
if (!post_slug || typeof post_slug !== 'string') {
return c.json({ message: 'post_slug 必填' }, 400);
}
@@ -64,7 +65,7 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
console.log('PostComment:request', {
postSlug: post_slug,
hasParent: !!parent_id,
hasParent: parentId !== null && parentId !== undefined,
author,
email,
ip,
@@ -95,7 +96,7 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
userAgent,
content,
content,
parent_id || null,
parentId || null,
"approved" // 或者从环境变量读取默认状态
).run();
@@ -103,70 +104,60 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
console.log('PostComment:inserted', {
postSlug: post_slug,
hasParent: !!parent_id,
hasParent: parentId !== null && parentId !== undefined,
ip
});
// 5. 发送邮件通知 (后台异步执行,不阻塞用户响应)
if (c.env.SEND_EMAIL && c.env.CF_FROM_EMAIL) {
console.log('PostComment:mailDispatch:start', {
hasParent: !!data.parent_id
hasParent: parentId !== null && parentId !== undefined
});
c.executionCtx.waitUntil((async () => {
try {
if (data.parent_id) {
if (parentId !== null && parentId !== undefined) {
let adminEmail: string | null = null;
try {
adminEmail = await getAdminNotifyEmail(c.env);
} catch (e) {
console.error('PostComment:mailDispatch:userReply:getAdminEmailFailed', e);
}
const isAdminReply = !!adminEmail && email === adminEmail;
const parentComment = await c.env.CWD_DB.prepare(
"SELECT author, email, content_text FROM Comment WHERE id = ?"
).bind(data.parent_id).first<{ author: string, email: string, content_text: string }>();
).bind(parentId).first<{ author: string, email: string, content_text: string }>();
if (parentComment && parentComment.email !== data.email) {
let adminEmail: string | null = null;
try {
adminEmail = await getAdminNotifyEmail(c.env);
} catch (e) {
console.error('PostComment:mailDispatch:userReply:getAdminEmailFailed', e);
}
const isAdminReply = !!adminEmail && email === adminEmail;
const isParentThirdParty = !!adminEmail && parentComment.email !== adminEmail;
if (isAdminReply && isParentThirdParty) {
if (parentComment && parentComment.email && parentComment.email !== email) {
let canSendUserMail = true;
if (!isAdminReply) {
const recentUserMail = await c.env.CWD_DB.prepare(
"SELECT created_at FROM EmailLog WHERE recipient = ? AND type = 'user-reply' ORDER BY created_at DESC LIMIT 1"
).bind(parentComment.email).first<{ created_at: string }>();
const canSendUserMail = !recentUserMail || (Date.now() - new Date(recentUserMail.created_at).getTime() > 60 * 1000);
if (canSendUserMail && isValidEmail(parentComment.email)) {
console.log('PostComment:mailDispatch:userReply:send', {
toEmail: parentComment.email,
toName: parentComment.author
});
await sendCommentReplyNotification(c.env, {
toEmail: parentComment.email,
toName: parentComment.author,
postTitle: data.post_title,
parentComment: parentComment.content_text,
replyAuthor: author,
replyContent: content,
postUrl: data.post_url,
});
await c.env.CWD_DB.prepare(
"INSERT INTO EmailLog (recipient, type, ip_address, created_at) VALUES (?, ?, ?, ?)"
).bind(parentComment.email, 'user-reply', ip, new Date().toISOString()).run();
console.log('PostComment:mailDispatch:userReply:logInserted', {
toEmail: parentComment.email
});
}
if (!canSendUserMail) {
console.log('PostComment:mailDispatch:userReply:skippedByRateLimit', {
toEmail: parentComment.email
});
}
} else {
console.log('PostComment:mailDispatch:userReply:skipNonAdminReply', {
adminEmail,
currentEmail: email,
parentEmail: parentComment.email
canSendUserMail =
!recentUserMail ||
(Date.now() - new Date(recentUserMail.created_at).getTime() > 30 * 1000);
}
if (canSendUserMail && isValidEmail(parentComment.email)) {
console.log('PostComment:mailDispatch:userReply:send', {
toEmail: parentComment.email,
toName: parentComment.author
});
await sendCommentReplyNotification(c.env, {
toEmail: parentComment.email,
toName: parentComment.author,
postTitle: data.post_title,
parentComment: parentComment.content_text,
replyAuthor: author,
replyContent: content,
postUrl: data.post_url,
});
await c.env.CWD_DB.prepare(
"INSERT INTO EmailLog (recipient, type, ip_address, created_at) VALUES (?, ?, ?, ?)"
).bind(parentComment.email, 'user-reply', ip, new Date().toISOString()).run();
console.log('PostComment:mailDispatch:userReply:logInserted', {
toEmail: parentComment.email
});
}
}

View File

@@ -121,7 +121,7 @@ export async function sendCommentNotification(
<div style="background-color:#f4f4f5;padding:24px 0;">
<div style="max-width:640px;margin:0 auto;background:#ffffff;border-radius:12px;border:1px solid #e5e7eb;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;color:#111827;">
<div style="padding:20px 28px;border-bottom:1px solid #e5e7eb;background:linear-gradient(135deg,#0f766e,#059669);">
<h1 style="margin:0;font-size:18px;line-height:1.4;color:#f9fafb;">新评论提醒 - ${postTitle}</h1>
<h1 style="margin:0;font-size:18px;line-height:1.4;color:#f9fafb;">新评论提醒</h1>
<p style="margin:4px 0 0;font-size:12px;color:#d1fae5;">你的文章收到了新的评论</p>
</div>
<div style="padding:24px 28px;">