feat(评论): 添加Markdown支持和XSS过滤功能

添加marked和xss依赖包用于评论内容的Markdown渲染和XSS过滤
修改邮件模板以支持HTML格式的评论内容显示
更新数据库存储逻辑,同时保存原始文本和渲染后的HTML内容
This commit is contained in:
anghunk
2026-01-20 13:11:44 +08:00
parent e6bc852945
commit b11abca96d
4 changed files with 1417 additions and 106 deletions

View File

@@ -19,7 +19,9 @@
},
"dependencies": {
"hono": "^4.11.3",
"marked": "^17.0.1",
"nodemailer": "^7.0.12",
"ua-parser-js": "^2.0.7"
"ua-parser-js": "^2.0.7",
"xss": "^1.0.15"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -67,9 +67,22 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
`).run();
// 3. 准备数据
const content = checkContent(rawContent);
const contentText = checkContent(rawContent);
const author = checkContent(rawAuthor);
// Markdown 渲染与 XSS 过滤
const html = await marked.parse(rawContent, { async: true });
const contentHtml = xss(html, {
whiteList: {
...xss.whiteList,
code: ['class'],
span: ['class', 'style'],
pre: ['class'],
div: ['class', 'style'],
img: ['src', 'alt', 'title', 'width', 'height', 'style']
}
});
console.log('PostComment:request', {
postSlug: post_slug,
hasParent: parentId !== null && parentId !== undefined,
@@ -99,8 +112,8 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
`${uaResult.browser.name || ""} ${uaResult.browser.version || ""}`.trim(),
uaResult.device.model || uaResult.device.type || "Desktop",
userAgent,
content,
content,
contentText,
contentHtml,
parentId || null,
"approved" // 或者从环境变量读取默认状态
).run();
@@ -140,8 +153,8 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
const isAdminReply = !!adminEmail && email === adminEmail;
const parentComment = await c.env.CWD_DB.prepare(
"SELECT author, email, content_text FROM Comment WHERE id = ?"
).bind(parentId).first<{ author: string, email: string, content_text: string }>();
"SELECT author, email, content_html FROM Comment WHERE id = ?"
).bind(parentId).first<{ author: string, email: string, content_html: string }>();
if (parentComment && parentComment.email && parentComment.email !== email) {
let canSendUserMail = true;
@@ -163,9 +176,9 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
toEmail: parentComment.email,
toName: parentComment.author,
postTitle: data.post_title,
parentComment: parentComment.content_text,
parentComment: parentComment.content_html,
replyAuthor: author,
replyContent: content,
replyContent: contentHtml,
postUrl: data.post_url,
}, notifySettings.smtp);
await c.env.CWD_DB.prepare(
@@ -187,7 +200,7 @@ export const postComment = async (c: Context<{ Bindings: Bindings }>) => {
postTitle: data.post_title,
postUrl: data.post_url,
commentAuthor: author,
commentContent: content
commentContent: contentHtml
}, notifySettings.smtp);
await c.env.CWD_DB.prepare(
"INSERT INTO EmailLog (recipient, type, ip_address, created_at) VALUES (?, ?, ?, ?)"

View File

@@ -255,11 +255,11 @@ export async function sendCommentReplyNotification(
</p>
<div style="margin:0 0 18px 0;padding:14px 16px;border-radius:10px;background:#f3f4f6;border:1px solid #e5e7eb;">
<div style="font-size:12px;color:#6b7280;margin-bottom:6px;">你之前的评论</div>
<div style="font-size:14px;color:#374151;white-space:pre-wrap;">${parentComment}</div>
<div style="font-size:14px;color:#374151;">${parentComment}</div>
</div>
<div style="margin:0 0 24px 0;padding:14px 16px;border-radius:10px;background:#eff6ff;border:1px solid #bfdbfe;">
<div style="font-size:12px;color:#1d4ed8;margin-bottom:6px;">最新回复</div>
<div style="font-size:14px;color:#1f2937;white-space:pre-wrap;">${replyContent}</div>
<div style="font-size:14px;color:#1f2937;">${replyContent}</div>
</div>
<div style="text-align:center;margin-bottom:8px;">
<a href="${postUrl}" style="display:inline-block;padding:10px 22px;border-radius:999px;background:#2563eb;color:#ffffff;font-size:14px;font-weight:500;text-decoration:none;">
@@ -327,7 +327,7 @@ export async function sendCommentNotification(
</p>
<div style="margin:0 0 18px 0;padding:14px 16px;border-radius:10px;background:#f9fafb;border:1px solid #e5e7eb;">
<div style="font-size:12px;color:#6b7280;margin-bottom:6px;">评论内容</div>
<div style="font-size:14px;color:#374151;white-space:pre-wrap;">${commentContent}</div>
<div style="font-size:14px;color:#374151;">${commentContent}</div>
</div>
<div style="margin:0 0 8px 0;">
<a href="${postUrl}" style="display:inline-block;padding:10px 22px;border-radius:999px;background:#047857;color:#ffffff;font-size:14px;font-weight:500;text-decoration:none;">