邮件模板微信化 + 评论老友自动填充 + CF 后台国内线路一键跳

- 邮件模板重做成微信聊天式:灰底、自己评论绿色气泡靠右、对方白色气泡靠左、
  40px 圆角方头像(weavatar,邮箱 md5 不外泄)、时间居中、#07C160 主按钮;
  布局全嵌套表格(Outlook 稳),表情内联 max-height:28px(修掉「表情太大」)、
  配图限宽 100%;管理员主题从「新的评论待审」改成「您有一个新的评论」
- 老友自动填充:新增 /comments/lookup(只回昵称+网址,绝不含邮箱;IP 限速
  30 次/10 分钟)+ 主题 comment-autofill 模块(填昵称弹提示、填邮箱自动带出
  上次的昵称/网址,事件委托不怕 Artalk 重建编辑器)
- CF 后台新增「国内线路」一键跳:/editor/handoff 用共享令牌对时间戳 HMAC
  签名 → editor-api /admin/handoff 验签发会话 Cookie(2 分钟有效、防重放),
  晚高峰丢包时直达国内机后台免重新登录
This commit is contained in:
zqlit committed 2026-10-05 07:36:26 +08:00
1 parent 18a4882577
commit bcfa229105
10 files changed
+443 -87

No files matched your search

+20 -8
View File
@@ -1,26 +1,32 @@
// 渲染两封示例邮件 HTML 到 .mail-preview/,供浏览器预览(不发送)
// 渲染两封示例邮件 HTML 到 .mail-preview/,供浏览器预览与 SMTP 试寄(不经过 Worker)
// 用法(在仓库根目录):
// npx tsc tools/preview-mail.ts src/lib/mail.ts --outDir .mail-build --module commonjs --target es2022 --moduleResolution node --skipLibCheck --esModuleInterop --noEmit false
// node .mail-build/tools/preview-mail.js
import * as path from 'path';
const { replyMailHtml, adminMailHtml, replyMailSubject, adminMailSubject } = require('../src/lib/mail');
const now = Date.now();
const parent = {
const parentMsg = {
id: 15630,
content: '终于恢复更新了啊 确实有点无聊了,做些损人不利己的事 :prophet:',
content: '终于恢复更新了啊,最近也在考虑要不要再次拥抱静态 🤔',
created_at: now - 3600_000,
page_key: '/comment.html',
};
const reply = {
id: 15631,
content: '恢复了,今天有空,把服务全部迁移到 cloudflare 上了,GitHub 仓库只做文件备份了 😄[doge]',
content:
'恢复了,今天有空,把服务全部迁移到 cloudflare 上了 ' +
'<img src="https://usj.cc/emotion/douyin/1.png" atk-emoticon="1">' +
' 欢迎回来常玩!<img src="https://usj.cc/emotion/bilibili/bq_1.png" atk-emoticon="2">',
created_at: now,
page_key: '/comment.html',
};
const newMsg = {
id: 15632,
content: '测试留言:这个聊天气泡样式的邮件通知看起来真不错!',
content:
'测试留言:这个聊天气泡样式的邮件通知看起来真不错!' +
'<img src="https://usj.cc/emotion/weibo/wb1.png" atk-emoticon="33">' +
'<img src="https://usj.cc/emotion/twemoji/angry.png" atk-emoticon="1">' +
'<img src="https://pic.imgdb.cn/item/64ec38b4661c6c8e54c5acc2.jpg" alt="配图" title="配图">',
created_at: now,
page_key: '/comment.html',
};
@@ -29,7 +35,7 @@ const site = { siteName: '优世界', siteUrl: 'https://usj.cc', pageTitle: '留
const replyHtml = replyMailHtml({
...site,
parentComment: parent,
parentComment: parentMsg,
parentNick: '扳布',
parentEmail: 'banbu@example.com',
comment: reply,
@@ -49,8 +55,14 @@ const fs = require('fs');
fs.mkdirSync(__dirname + '/.mail-preview', { recursive: true });
fs.writeFileSync(__dirname + '/.mail-preview/reply.html', replyHtml);
fs.writeFileSync(__dirname + '/.mail-preview/notice.html', adminHtml);
// 试寄用的主题也落盘,发送脚本直接读
fs.writeFileSync(__dirname + '/.mail-preview/reply.subject.txt', replyMailSubject({
...site, parentComment: parentMsg, parentNick: 'a', parentEmail: 'a@a.com',
comment: reply, commenterNick: '小赵', commenterEmail: 'b@b.com', toNick: '扳布',
}));
fs.writeFileSync(__dirname + '/.mail-preview/notice.subject.txt', adminMailSubject({ ...site, comment: newMsg, commenterNick: 'c', commenterEmail: 'c@c.com', adminNick: 'x' }));
console.log('SUBJECT reply :', replyMailSubject({
...site, parentComment: parent, parentNick: 'a', parentEmail: 'a@a.com',
...site, parentComment: parentMsg, parentNick: 'a', parentEmail: 'a@a.com',
comment: reply, commenterNick: '小赵', commenterEmail: 'b@b.com', toNick: '扳布',
}));
console.log('SUBJECT notice:', adminMailSubject({ ...site, comment: newMsg, commenterNick: 'c', commenterEmail: 'c@c.com', adminNick: 'x' }));