内容覆盖与反馈采纳工作流
本文档面向运维。说明站内反馈与人工修正如何回到正式发布站,而不会被每天 02:00 的自动同步覆盖。
同步链路顺序
scripts/sync-and-deploy.sh 处理优先级(数字越大越后,后写入覆盖前者):
- 拉上游
fmhy/edit增量 - 计算 diff 文件
- rsync 源文件 ->
docs/ - LLM 翻译 -> 写入
docs-zh/<file>.md缓存 4.4 刷新品牌白名单scripts/brands.json4.5 写当日上游同步日报docs-zh/posts/daily-YYYY-MM-DD.md rsync -a docs-zh/ docs/(翻译版本覆盖英文)rsync -a docs-overrides/ docs/(手工版本最终覆盖)pnpm docs:buildpost-build-i18n.sh处理 dist 默认主题硬编码英文- rsync 部署到
/var/www/ziyuan/
任何不希望被翻译流程覆盖的内容,放在 docs-overrides/ 下,路径与 docs/ 镜像。
反馈 -> 正式内容的工作流
1) 看新反馈
SSH 进 VPS:
cd ~/ziyuan-site/services/feedback
node feedback-cli.mjs stats # 看累计、最近 24h
node feedback-cli.mjs list new 50 # 列出待审阅
node feedback-cli.mjs show <id> # 看详情2) 采纳一条资源推荐
假设用户在 /ai 推荐了一个新工具:
- 验证可用性、合法性、是否已存在
- 在
docs-overrides/ai.md对应小节下手工新增 markdown 行(若 overrides 还没有此文件,从docs/ai.md复制开始) node feedback-cli.mjs mark <id> accepted "已加入 ai.md AI 写作小节"- 等下次 02:00 cron 自动重建,或手动跑
bash scripts/sync-and-deploy.sh
3) 采纳翻译修正
- 在
scripts/i18n_pipeline/glossary.json(或对应术语表) 加术语映射 - 重新跑翻译流程:
cd ~/ziyuan-site && bash scripts/sync-and-deploy.sh(术语表覆盖优先) node feedback-cli.mjs mark <id> accepted "术语表 X -> Y"
4) 处理失效链接
- 在
docs-overrides/<分类>.md修改对应 markdown 行(替换为新链接或删除) node feedback-cli.mjs mark <id> accepted "已替换为 <new-url>"
5) 拒绝/标垃圾
node feedback-cli.mjs mark <id> rejected "重复 / 不可信 / 不合规"
node feedback-cli.mjs mark <id> spam "广告"
node feedback-cli.mjs purge-spam 30 # 清除 30 天前的 spam备份与导出
- 每日 02:00 自动备份:
~/ziyuan-data/backup/feedback-YYYYMMDD.db.gz(保留 30 天) - 每日导出:
~/ziyuan-site/.logs/feedback-YYYYMMDD.json - 每日摘要:
~/ziyuan-site/.logs/feedback-summary.txt,会被当天的 daily-YYYY-MM-DD.md 顶部引用
手工导出全量:
node feedback-cli.mjs export ~/feedback-all.json不要做什么
- ❌ 不要直接改
docs/,会在下次 sync 被翻译流程覆盖 - ❌ 不要在
docs-zh/修中文页面,这里是缓存,任何手工改动可能被翻译流程重写 - ✅ 改
docs-overrides/<同名路径>.md,这是人工最高优先级
每周更新页(weekly-update)
scripts/weekly-update.sh 每周日 05:30 自动生成 docs-overrides/posts/weekly-update-YYYY-MM-DD.md,数据源:
docs-zh/posts/daily-*.md最近 7 天的「涉及页面」段(上游同步)feedback.db最近 7 天的 accepted 反馈(社区采纳)feedback.db最近 7 天的反馈热点页 top 3(任意状态)
触发条件
| P_COUNT | FB_COUNT | ANY_COUNT | 行为 |
|---|---|---|---|
| 0 | 0 | 0 | skip,不写空周报 |
| 任意 >0 | 任意 | 任意 | 生成周报 |
| 0 | 0 | >0 | 生成「安静周」段落 |
回填(--backfill)
bash scripts/weekly-update.sh --backfill[=N](默认 8 周):
- 从最近一个周日往前 N 周,逐窗口生成
- 已存在的周报 skip(不覆盖)
- 单次模式无 --backfill,行为是为「今天」生成 / 覆盖
上界语义(ts <= UNTIL)
所有 sqlite 查询都带 AND ts <= UNTIL,其中 UNTIL = 锚定日 23:59:59 (Asia/Shanghai)。
设计意图:回填历史周报时,绝不能把当前才有的反馈数据计入过去那一周。比如今天 6-21 收到 1 条反馈,跑 --backfill=4,6-14 / 6-07 / 5-31 的窗口都不会包含这条反馈,会被 skip。
反馈服务字段口径
CLI feedback-cli.mjs stats --json、HTTP /stats、/status.json.feedback 三处字段对齐:
total: 累计反馈条数by_status: 各 status 计数week.{total,new,triaged,accepted,rejected,spam}: 最近 7 天分组day.total: 最近 24 小时条数last_ts_ms: 最近一条反馈的时间戳(毫秒)
任一处字段缺失或重命名都视为接口破坏,需同步更新另外两处。
运维 Troubleshooting
Caddy reload 失败
sudo caddy validate --config /etc/caddy/Caddyfile # 先校验语法
sudo journalctl -u caddy --no-pager -n 50 # 看启动错误常见原因:
- 反向代理
header_down缩进错(必须在reverse_proxy { ... }块内,与同级指令对齐) - 端口冲突:
ss -ltnp | grep :443 - 证书目录权限:
/var/lib/caddy/.local/share/caddy/
回滚:把 /home/ubuntu/ziyuan-site/ops/caddy/Caddyfile 在 git 里 reset 到上一笔,再 sudo cp ... /etc/caddy/Caddyfile && sudo systemctl reload caddy。
/api/feedback/stats 504
systemctl is-active freeresource-feedback.service
curl -sS http://127.0.0.1:8123/health
sudo journalctl -u freeresource-feedback.service -n 80 --no-pager服务挂了直接 sudo systemctl restart freeresource-feedback.service,1 秒内重启;数据在 /home/ubuntu/ziyuan-data/feedback.db,启动只是把表结构与 prepare 重建。
feedback.db locked / busy
sqlite3 /home/ubuntu/ziyuan-data/feedback.db .timeout 5000
## 运维 Troubleshooting
### Caddy reload 失败
```bash
sudo caddy validate --config /etc/caddy/Caddyfile # 先校验语法
sudo journalctl -u caddy --no-pager -n 50 # 看启动错误常见原因:
- 反向代理
header_down缩进错(必须在reverse_proxy { ... }块内,与同级指令对齐) - 端口冲突:
ss -ltnp | grep :443 - 证书目录权限:
/var/lib/caddy/.local/share/caddy/
回滚:把 /home/ubuntu/ziyuan-site/ops/caddy/Caddyfile 在 git 里 reset 到上一笔,再 sudo cp ... /etc/caddy/Caddyfile && sudo systemctl reload caddy。
/api/feedback/stats 504
systemctl is-active freeresource-feedback.service
curl -sS http://127.0.0.1:8123/health
sudo journalctl -u freeresource-feedback.service -n 80 --no-pager服务挂了直接 sudo systemctl restart freeresource-feedback.service,1 秒内重启;数据在 /home/ubuntu/ziyuan-data/feedback.db,启动只是把表结构与 prepare 重建。
feedback.db locked / busy
sqlite3 /home/ubuntu/ziyuan-data/feedback.db ".timeout 5000; PRAGMA journal_mode;"应该看到 wal。若返回 delete,服务忘了切 WAL,重启 service 即可;若多个写者同时操作,优先让 server.mjs 处理,CLI 等服务空闲再跑。
git push 被工作树 dirty 拒绝
不会再出现了。scripts/sync-and-deploy.sh 末尾会 git restore --source=HEAD -- docs/ 并把 docs-zh/ 改动自动 commit。如果手工跑了别的脚本污染工作树,直接 cd ~/ziyuan-site && git status 确认,该 add 就 add,该 restore 就 restore。
Cloudflare 边缘缓存导致老内容不更新
部署链路里已经 scripts/indexnow.sh,但 Cloudflare 自家缓存独立。手工 purge:
- 控制台 → Caching → Configuration → Purge Cache → Custom Purge → 输入受影响 URL
/api/feedback/stats这类 30s short-cache 端点,等一会即可自动失效。
/stats schema_version
/stats 端点输出顶部带 schema_version: "1.0"。如果未来要改动字段(改名/删除/语义变化),必须 bump:
| 变更类型 | 新版本 | FeedbackStats.vue 处理 |
|---|---|---|
| 加字段 | 1.x → 1.(x+1) | 旧前端忽略新字段,无影响 |
| 改名/删字段 | 1.x → 2.0 | 前端按 schema_version 分支读取或降级到 /status.json |
CLI feedback-cli.mjs stats --json 与 status.json.feedback 三处字段必须同步,口径见上文"反馈服务字段口径"。
备份恢复演练
每 6 个月跑一次,确认 /home/ubuntu/ziyuan-data/backup/ 里的快照真的能还原。
演练步骤
# 1) 拉取最新一份备份
cd /tmp
LATEST=$(ls -1t /home/ubuntu/ziyuan-data/backup/feedback-*.db.gz | head -1)
echo "using $LATEST"
# 2) 解压到临时位置(别动生产 db)
zcat "$LATEST" > /tmp/restore-test.db
# 3) 完整性自检
sqlite3 /tmp/restore-test.db "PRAGMA integrity_check;" # 期望 ok
sqlite3 /tmp/restore-test.db "PRAGMA quick_check;" # 期望 ok
sqlite3 /tmp/restore-test.db "SELECT COUNT(*) FROM feedback;" # 期望 >= 备份时刻条数
# 4) Schema 比对
sqlite3 /tmp/restore-test.db ".schema feedback" > /tmp/restore-schema.txt
sqlite3 /home/ubuntu/ziyuan-data/feedback.db ".schema feedback" > /tmp/prod-schema.txt
diff /tmp/prod-schema.txt /tmp/restore-schema.txt # 期望无差异
# 5) 关键统计核对
sqlite3 /tmp/restore-test.db "SELECT status, COUNT(*) FROM feedback GROUP BY status;"
# 6) 演练完成 -> 清理临时文件
rm -f /tmp/restore-test.db /tmp/restore-test.db-shm /tmp/restore-test.db-wal /tmp/restore-schema.txt /tmp/prod-schema.txt真实恢复流程(只在生产数据丢失时执行)
sudo systemctl stop freeresource-feedback.service
mv /home/ubuntu/ziyuan-data/feedback.db /home/ubuntu/ziyuan-data/feedback.db.broken.$(date +%s)
zcat /home/ubuntu/ziyuan-data/backup/feedback-YYYYMMDD.db.gz > /home/ubuntu/ziyuan-data/feedback.db
chown ubuntu:ubuntu /home/ubuntu/ziyuan-data/feedback.db
sudo systemctl start freeresource-feedback.service
sleep 1
curl -sS http://127.0.0.1:8123/stats # 期望返回 schema_version 与计数恢复后视情况补一段 /feedback 的"近期数据丢失,请重新提交"公告,放到 docs-overrides/feedback.md 顶部。
ABUSE_MODE 启用清单
只在被刷或预期被刷时启用,默认保持关闭。启用步骤:
- 在 Cloudflare 控制台拿到 Turnstile site key + secret(分别给前端与 server.mjs)
sudo systemctl edit freeresource-feedback.service增加:[Service] Environment=ABUSE_MODE=1 Environment=TURNSTILE_SECRET=<secret>sudo systemctl daemon-reload && sudo systemctl restart freeresource-feedback.service- 在
Feedback.vue加 Turnstile widget,提交时把 token 写到 body.turnstile - 灰度观察
journalctl -u freeresource-feedback.service | grep captcha一段时间 - 不需要时直接
drop那两行 Environment,再 daemon-reload + restart 即可关闭