Commit Graph
11 Commits
Author SHA1 Message Date
zqlit a40a92f526 feat: SSL 证书管家 —— 后台面板 + 专属角色 + 国内机放行
集成在 api.200181.xyz(同一个 Worker),国内机 writeapi.usj.cc 同步可用。

新增第三档后台角色 'ssl':
- 只拿证书管家钥匙,看不到评论/文章/用户等模块
- 判定正着枚举放行(canManageSSL = admin|ssl),不用排除法,
  免得以后新增角色静默获得私钥权限
- 鉴权只认 Bearer 会话,绝不走 isAdminRequest —— 后者有 Artalk
  老客户端的 query 兜底,一旦进来的就是 TLS 私钥

数据(复用 RSS_KV,前缀 certkeeper:):
- 凭据一条一键,避开 KV 读-改-写无事务导致的并发丢数据
- 私钥/AK-SK 一律 AES-GCM 密文(TOKEN_SECRET 经 PBKDF2 派生)
- 列表接口只回显前 4 后 4 位,明文不进内存
- 配置读失败抛错而非返回空,避免一次保存覆盖线上配置

只读监控:
- probeTls 走 cloudflare:sockets 拿证书正文,实现到期分级
  与「库里记录 vs 线上实测」对比(match/mismatch/live-only/unreachable)
- 到期提醒邮件(HTML 已转义)

国内机 editor-api:
- identify 识别 ssl 角色;业务分支前白名单,ssl 只能碰 /health、
  /admin/session、/admin/logout 与 /api/v2/ssl*
- /api/v2/ssl* 反代放行 admin + ssl(RSS 仍是 admin-only)
- posts.mjs 越权兜底方向修正:owns() 从「非 editor 即放行」改为
  「只有 admin 不受限」,漏进来的 ssl 被当受限编辑而非管理员全放行
- createPost 显式拒绝非写作角色

验证:typecheck ✓ / selftest:ssl 117 项 ✓ / 浏览器 37 项 ✓
2026-10-06 14:19:01 +08:00
zqlit cfadba6007 fix(后台文案): 删除确认框措辞纠正
旧文案「…不进 git,也不影响版本历史,但前台会立刻少一篇」有两处不准:
- 「前台会立刻少一篇」不成立 —— DELETE 只把目录 move 到服务器回收目录
  (/srv/editor-trash),不 commit 不 push,线上要等下次「发布」才少一篇;
- 「不影响版本历史」也不严谨 —— 下次发布时这次删除会作为 D 一起进历史。

新文案:「文件会移到服务器上的回收目录(在仓库外,不进 git):
后台立刻少一篇,线上要等你下次点「发布」后才会少。确定删除?」
2026-10-05 22:53:30 +08:00
zqlit 1b2e3d46f8 fix(友链): 修复申请「通过不了/删不掉」;feat(评论): 通知按文章作者分派
## 友链自助申请(通过不了 / 删除不掉)
根因:admin.js 全局事件委托把 data-id 一律 parseInt,
而友链申请 id 是字符串(lamus5uptzcdam)→ NaN → JSON 成 {"id":null}
→ 后端 String(null||'') 为空 → 400 id and action required,查看/通过/拒绝全失效。
引入于 a74b3c71(10-04 归档 rss-robot 时;后台只有友链申请用字符串 id)。

- admin.js:纯数字才转数字,其余原样(评论/用户/站点按钮请求完全不变)
- admin.js:待审卡片加「删除」按钮
- links.ts:新增 action:'delete'(不受「已处理过」限制,处理过的也能删)
- links.ts:approve 改为先写友链再改状态 + URL 去重(重试幂等)
- links.ts:已是目标状态时返回 {ok:true,already:true},不再 409
  —— 否则网络抖动后重试会让用户以为「怎么点都通不过」

## 评论通知按文章作者分派
编辑角色(女朋友)写的文章,评论通知改发给她,不再打扰博主。
不需要「文章→作者」同步表:Hugo 模板把 author_id 注入 artalkConfig,
artalk.js 已有的 fetch 拦截器把它塞进评论提交体,Worker 直接读。

- artalk.html:artalkConfig 增 pageAuthorId(老文章 0)
- artalk.js:提交评论时带上 author_id(读 window.artalkConfig,防 PJAX 过期)
- comments.ts:createComment 解析 author_id 并传给通知
- mail.ts:收件人改为「这篇文章的主人」——作者非管理员且开了邮件则发给他,
  否则回退博主;已被回复通知过 / 作者本人评论自己的文章都不重复发
- mail.ts:增一行收件人决策日志,便于线上排查

验证:Worker tsc 通过;Hugo 构建通过;artalk.js 语法通过;
友链本地跑通(通过 200 → 再通过 already:true → 友链仅 1 条 → 删除 200 → 再删 404)。
2026-10-05 16:44:15 +08:00
zqlit d899cd793b feat(编辑角色): editor 只能写并发布自己的文章
- D1 users 加 role 列(''/editor/admin),与 is_admin 成对写入
- Worker routes/editor.ts 放行 admin+editor,反代注入 X-Editor-Uid/User/Role(昵称 encodeURIComponent)
- editor-api 引入 identify():身份取自注入头或本机会话;文章归属记 frontmatter author_id
- 编辑发布改精确 pathspec(只提交自己文章目录),管理员保持全量;空 pathspec 显式拦截
- 国内机直连登录改为转发 CF /user/access_token 校验,CF 不可达回退本机管理员
- handoff 签名覆盖身份(ts/uid/name/role),防编辑一键跳转变管理员
- admin.js:编辑只渲染「文章编辑」tab、作者框只读;用户管理加角色下拉 + 新建用户
2026-10-05 14:34:56 +08:00
zqlit bcfa229105 邮件模板微信化 + 评论老友自动填充 + 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 分钟有效、防重放),
  晚高峰丢包时直达国内机后台免重新登录
2026-10-05 07:36:26 +08:00
zqlit 18a4882577 登录页去掉订阅口令:订阅接口认后台会话 + editor-api 反代
- Worker 侧 requireAuth 新增两条通道:① Authorization 管理员会话(CF 版后台
  登录后订阅模块免口令);② X-Editor-Token 共享令牌(服务端到服务端)。
  原 ?token=/Cookie 通道保留,RSS 阅读器与友圈页不受影响
- editor-api 新增订阅反代(/api/feeds|links|link-apply):国内机后台的
  订阅源/友链由本机带共享令牌去 CF 取,前端不再持有任何口令;GET 幂等重试
  抗跨境抖动
- 登录页只留账号+密码(CF 版与国内机版统一);国内机后台侧栏新增订阅中心
  入口;RSS_TOKEN / LS_RSS 前端残留全部清理
2026-10-04 23:57:02 +08:00
zqlit 9f586d625b 后台登录改造:自建登录页 + Cookie 会话,弃用 basic auth 弹窗
- editor-api 新增 /admin/login|session|logout:HttpOnly Cookie 会话(落盘持久化)、
  登录失败限速(5 次锁 5 分钟)、鉴权改为「X-Editor-Token 或 会话」双通道
- 登录页重做:品牌标识、图标输入框、密码可见切换、柔光背景,深浅色适配
- 前端以 /admin/session 探测部署模式:国内机走本机会话,CF 版走原令牌流程
- nginx:/admin/ 与 /api/v2/editor/ 去掉 basic auth 与令牌注入,登录凭据只存 Cookie
- bootstrap.sh 同步 ADMIN_USER/ADMIN_PASS,保持一键迁移能力
2026-10-04 23:40:23 +08:00
zqlit 96751b8143 fix(editor): 删文章 EXDEV 修正 + 订阅口令不再阻断登录
1) 删除文章报 EXDEV(cross-device link not permitted)
   · 根因一:容器内 /blog 与 /app/trash 是两个独立 bind mount,
     rename(2) 跨挂载点必然 EXDEV —— 哪怕宿主机上它们同在一块盘。
   · 根因二:退化成 fs.cpSync 后,Windows 上遇到**中文目录名**会把
     Node 进程直接干崩(exit 127,不抛异常,stdout 缓冲丢失)。
     而文章目录名几乎必然含中文(<日期>-<中文标题>-<slug>)。
   · 修法:改用 readdirSync/copyFileSync 自己递归复制(宽字符路径,中文 OK);
     bootstrap.sh / compose 改成挂 BLOG_DIR 的父目录,让仓库与回收站
     落在**同一个挂载点**内,正常情况下 rename 直接成功、不再复制。

2) 登录页「订阅口令」填错会阻断整个登录
   · 它只是可选订阅模块的口令,不该 throw 掉登录流程。
   · rssApi() 无条件 searchParams.set('token', RSS_TOKEN),会用浏览器里
     记住的旧口令覆盖调用方刚传的值 → 正确口令也验不过。
   · 现在:填错只 toast 提示 + 清掉失效旧口令;rssApi 尊重调用方传的值。

3) wrangler.toml:EDITOR_API_BASE 改指国内机新域名 writeapi.usj.cc
   (原指向即将到期的境外机 post.usj.cc,且那台从未部署过 editor-api)

验证:api-e2e 44/44;front matter 往返 130/130;保存往返 130/130;
中文路径删除实测通过(目录 + 中文子目录 + 图片全部移入回收站,进程不崩)。
2026-10-04 22:11:02 +08:00
zqlit c217d20c30 feat(editor): 在线编辑文章(Worker 前端 + 轻量 docker 后端)
后端(新增 editor-api/,零 npm 依赖,只用 node 内置模块):
- 只做文章相关:列表/读取/新建/保存/删除/图片上传/git 状态·发布·同步
- front matter 往返保真:未改动的块按字节照抄,CRLF/块标量/引号写法都不动
- 列表用目录名当 id,slug 撞名不再静默改错文件(返回 409 列候选)
- 鉴权只有一条路:X-Editor-Token(只存在 Worker 侧,浏览器拿不到)
- 端口只绑 127.0.0.1,由宿主机 nginx 反代出去

部署(新增 deploy/editor-api/bootstrap.sh):
- 一条命令在新机器上完成 克隆仓库→写 .env→起容器→健康检查
- 状态全在两个目录(/srv/blog 仓库工作区 + /srv/editor-api 配置),
  迁移 = 复制目录或在新机重跑本脚本,容器本身无状态

Cloudflare Worker 侧(blog-admin):
- src/routes/editor.ts:鉴权 + 反代,浏览器只跟 Worker 说话
- 管理面板新增「文章编辑」:两栏布局 + 快捷插入面板(13 项短代码,
  与 write-server 的 ShortcutPanel 一致)+ 底部 草稿/保存/发布
- 系统设置改为 schema 驱动的表单,且**以运行时实际生效的配置为准**
  (frontend_conf/captcha/moderator/ip_region/site_default + KV human_check),
  修复「表单显示一套、评论系统跑另一套」的脱节问题

验证:tsc 0 错;front matter 往返 130/130;保存往返 130/130;
API e2e 44/44;无头 Chrome UI e2e 14/14
2026-10-04 21:16:06 +08:00
zqlit 437cd339ad fix: 管理员编辑跳过邮箱校验 + 返回顶部 rAF 丝滑滚动 + admin.js 正则笔误 + 页面标题污染
- artalk.js: beforeSubmit 在编辑态跳过昵称/邮箱校验(管理员改评论不再被拦)
- floating-tools.js: 返回顶部/去评论改 rAF + easeInOutCubic 自绘,原生 smooth 不可靠
- admin.js: 修 5 处 //s //n 正则笔误(整脚本 SyntaxError 致后台全灰),需 wrangler deploy
- artalk.html: 显式传 pageTitle(不再读 document.title,PJAX 竞态致文章页标题被记成「留言」)
- artalk.js: conf 缓存合并前剔除 pageKey/pageTitle/pageThumb,杜绝跨页串值
2026-10-04 18:30:30 +08:00
zqlit a74b3c7127 归档 artalk-cf 评论后端 + rss-robot 到 blog-admin(含技术选型/模块分布 README)
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
2026-10-04 08:45:40 +08:00