Deploy to Production / pre-check (push) Successful in 58s
Deploy to Production / build (push) Successful in 4m3s
Deploy to Production / deploy-edgeone (push) Successful in 3m48s
Deploy to Production / finalize (push) Successful in 26s
Deploy to Production / notify-failure (push) Skipped
81 lines
3.4 KiB
TypeScript
81 lines
3.4 KiB
TypeScript
/* 邮件模板自检:验证渲染不炸、转义生效、关键字段都在。
|
||
* 跑法(见 package.json selftest 同款套路,tools/ 不在 src 的 tsc 范围里):
|
||
* npx tsc tools/mailtest.ts --outDir .selftest-build --module commonjs \
|
||
* --target es2022 --moduleResolution node --skipLibCheck --esModuleInterop
|
||
* node .selftest-build/tools/mailtest.js
|
||
*/
|
||
import { replyMailHtml, replyMailSubject, adminMailHtml, adminMailSubject } from '../src/lib/mail';
|
||
import type { CommentRow } from '../src/types';
|
||
|
||
let pass = 0,
|
||
fail = 0;
|
||
const ok = (name: string, cond: boolean) => {
|
||
if (cond) {
|
||
pass++;
|
||
console.log(' ✓', name);
|
||
} else {
|
||
fail++;
|
||
console.log(' ✗', name);
|
||
}
|
||
};
|
||
|
||
// CommentRow 只要模板用到的字段;其余给空值占位
|
||
const row = (id: number, content: string, createdAt: string): CommentRow =>
|
||
({
|
||
id,
|
||
content,
|
||
created_at: createdAt,
|
||
page_key: '/20240811.html',
|
||
site_name: '优世界',
|
||
}) as unknown as CommentRow;
|
||
|
||
console.log('== 回复提醒模板(reply.html 气泡风) ==');
|
||
const rInput = {
|
||
siteName: '优世界',
|
||
siteUrl: 'https://usj.cc',
|
||
pageTitle: '迁移博客至hugo',
|
||
pageUrl: 'https://usj.cc/20240811.html',
|
||
parentComment: row(15580, '原始评论 <b>加粗</b>', '2026-10-01 10:00:00'),
|
||
parentNick: '小赵',
|
||
parentEmail: 'imql@qq.com',
|
||
comment: row(15590, '试试 <script>alert(1)</script> 和 <img src=x onerror=alert(1)>', '2026-10-02 10:00:00'),
|
||
commenterNick: '路人甲',
|
||
commenterEmail: 'walker@example.com',
|
||
toNick: '小赵',
|
||
};
|
||
const r = replyMailHtml(rInput);
|
||
ok('渲染出 HTML', r.startsWith('<!DOCTYPE html>'));
|
||
ok('包含站点名', r.includes('优世界'));
|
||
ok('包含页面标题', r.includes('迁移博客至hugo'));
|
||
ok('跳转链接带 atk_comment 锚参数', r.includes('atk_comment=15590'));
|
||
ok('script 标签被转义', !r.includes('<script>alert(1)</script>'));
|
||
ok('没有可执行的 onerror(img 白名单外的 img 一律转义成文本)', !/<img[^>]*onerror/i.test(r));
|
||
ok('被回复者昵称出现', r.includes('小赵'));
|
||
ok('回复者昵称出现', r.includes('路人甲'));
|
||
ok('两个头像 MD5 都渲染', (r.match(/weavatar\.com\/avatar\//g) || []).length >= 2);
|
||
ok('site_url 出现在落款', r.includes('https://usj.cc'));
|
||
ok('主题格式正确', replyMailSubject(rInput) === '[优世界] 您收到了来自 @路人甲 的回信');
|
||
|
||
console.log('== 管理员通知模板(notice.html 气泡风) ==');
|
||
const aInput = {
|
||
siteName: '优世界',
|
||
siteUrl: 'https://usj.cc',
|
||
pageTitle: '留言',
|
||
pageUrl: 'https://usj.cc/comment.html',
|
||
comment: row(999, '你好,<img src="https://usj.cc/emotion/douyin/19.png" atk-emoticon="19"> 哈哈', '2026-10-02 11:00:00'),
|
||
commenterNick: '路人',
|
||
commenterEmail: 'x@example.com',
|
||
adminNick: '博主',
|
||
};
|
||
const a = adminMailHtml(aInput);
|
||
ok('渲染出 HTML', a.startsWith('<!DOCTYPE html>'));
|
||
ok('标题是新评论', a.includes('有了新的评论'));
|
||
ok('含作者昵称', a.includes('路人'));
|
||
ok('表情包 img 被保留并内联宽度', /<img[^>]*width:100px[^>]*atk-emoticon/.test(a));
|
||
ok('跳转链接带锚参数', a.includes('atk_comment=999'));
|
||
ok('site_url 出现在落款', a.includes('https://usj.cc'));
|
||
ok('主题格式正确', adminMailSubject(aInput) === '[优世界] 其中 "留言" 页面有一条新的评论!');
|
||
|
||
console.log('\n通过 ' + pass + ' / 失败 ' + fail);
|
||
if (fail > 0) (globalThis as { __fail?: boolean }).__fail = true;
|