14 KiB
2026-10-19
部署后复测(强制严格档那批)
- 规则页返回首页已居中:线上
<div class="uk-margin-large-top uk-text-center">✔ - 验证码弹窗按钮已缩短:线上
>送信<✔(旧的「認証して送信」已消失) - 箱主面板
/user/questions:限制期条幅 + 「查看社区规则」链接都在 ✔ - 严格度锁定(admin6 当前 L2):
/user/profile出现「处置期间审核严格度已锁定为最高档,解除后自动恢复你的原设置」✔select[name=strictness]与「保存审核严格度」按钮均带disabled✔- 直接 POST
/user/moderation-strictness/update?tab=security+strictness=lax→ 302 回 profile, flash 仍是「已锁定」,且选中档仍为strict(说明未写入)✔
- 踩坑:表单 action 是
/user/moderation-strictness/update(没有/profile段);用错路径返回 302 到/, 容易误判成「被中间件拦」。另外每次 POST 都要重新取_csrf,用上一页(已跳首页)的 token 会 400。
记忆维护
MEMORY.md 一度 19KB 被注入截断,已重写压缩到约 4.9k 字符(8.1KB),保留构建/行尾/embed/审核/处置/CLI 等硬约束, 删去已在日志里的过程细节。
CLI 文档 + 全量代码审阅(同日)
新增 docs/cli.md:命令总览、reports 三种用法与口径、restrict 级别语义/副作用、unrestrict、
hide-public、ban/restore、典型处置流程、排错表、明确不做的事。
审阅中发现并修复(全部本地临时库实测):
hide-public举报闭环静默失效(真 bug):build()带is_private=false,在 Update 之后拿它当子查询恒为空集 → 隐藏成功但content_reports仍 pending。改为 Update 前Pluck("id")快照,再按 ID 更新/标记。 实测:3 条 pending → accepted,未波及其他箱主。- 整箱分享卡未拦截:
ShareCard直接走缓存,L1–L3 仍能生成整箱图。已在缓存查询前加拦截(fail-closed)。 - L2 通知邮件误导:强制私密后仍给提问者发「你的提问有回答」,链接点开 302。改为
!forcedPrivate才发。 - L2 写入顺序:
SetPrivate+AnswerByID两条语句有「公开+已作答」窗口,第二条失败还会留下「私密+无回答」。 新增Questions.AnswerByIDPrivate(单条 UPDATE 同时写 answer/is_read/is_private),并补测试TestQuestions/AnswerByIDPrivate。 ListAPI绕过 Pager:L4 停用与页面访问密码只挡 HTML,/api/v1/user/{domain}/questions仍能整页拉。 已补两道校验(均返回「用户不存在」,不暴露原因)。页面密码可被 API 绕过是升级前就存在的问题。- L1 曝光残留:
GetShowInHome的 Count 未过滤(分页总数虚高、末页空白)、热门标签查询未过滤。已补同一子查询。 - 性能:
ActiveLevel由两次查询改一次(取最高 rank);新增moderation.EffectiveStrictnessFor(level, raw), 发布路径上不再重复查库(原先一次发布最多 4 次处置查询)。 - 证据包口径:分母是「累计公开回答」不是窗口产出 → 文案加「累计」;新增
待处理 N/已处理标记, 避免把已闭合的举报误读成还挂着;补q.deleted_at IS NULL;--stats说明最长回溯 30 天;CLI 对active做 nil 守卫。
验证:go build ./... EXIT=0;db/i18n/templates 测试全绿;CLI 全流程(restrict L3 → 证据包 → unrestrict)实测通过;
临时库已 DROP。
遗留(未改,已向用户说明):L4 不覆盖 /vote/{token};restrict 没有 --days 上限;处置不发邮件给箱主(只有登录后条幅);
abuseAlertCooldowns 进程内 map 无清理(重启即失效)。
第二轮:处置通知邮件 + 超长确认 + 冷却表清理
- 新增
templates/mail/restriction-notice.html+mail.SendRestrictionNoticeMail(RestrictionNotice): 文案由调用方按箱主自己的语言(users.language,空则conf.App.DefaultLang)预翻译后传入, 模板本身单语言。不含--reason原文,只有级别名/到期/规则链接/申诉渠道。 i18n 新增 10 个键 ×4 语言:mail.restriction.*(6)+restriction.level.L1..L4。 restrict:新增--force(--days > 90必填,否则直接报错)与--no-notify;交互模式下超长处置 还要再输一次级别名(--force --yes视为两次确认)。发信失败只 warn + 终端提示,不回滚处置; 发信加了 20s 超时(gomail 无 Timeout 字段,裸 Dial 会等系统 TCP 超时)。abuseAlertCooldowns加惰性清理(>1024 条时删除已过 24h 的项)。- 顺带:
/user/questions限制期条幅改用restriction.level.*的名称(「收窄公开」)而不是裸L2。
验证:真实 SMTP sink 实收两封(ja 与 zh-CN),标题/级别名/到期/规则链接均正确、--reason 未出现、
链接未被 ZgotmplZ 转义;--days 120 无 --force 被拒;黑洞 SMTP 下 23s 内返回(未阻塞两分钟)。
go build ./...、go vet、db/i18n/templates 测试、checktpl 全绿。
遗留:unrestrict 不发通知(只解禁);restrict 对 --days 无上限(设计如此)。
第三轮:解除处置也发通知
- 复用同一封模板
mail/restriction-notice.html:appeal段改为{{if .appeal}}可选, 解除通知不传该字段就整段省略。 - 新增 i18n 键 ×4:
mail.unrestrict.heading/body/note(body 带域与级别名; note 明确写「处置记录保留用于审计、私密内容不会自动恢复」)。 unrestrict新增--no-notify;撤销前先ActiveLevel取级别(撤销后读不到), 撤销后按箱主语言发解除通知。affected==0直接返回、不发信。- 抽出公共件:
sendNotice(20s 超时)、reportNoticeResult(统一成功/失败输出)。 - 文档补充:
unrestrict的--no-notify、解除通知内容、以及到期自动解除不会发信(无调度器)。
实测(本地 SMTP sink,2 封):处置信含级别名/到期/申诉段;解除信含「已解除」+ 私密内容提示、
无申诉段;两封都不含 --reason。--no-notify 两处都不发信;无生效处置时不发信。
go build ./...、db/i18n/templates 测试、checktpl 全绿。
上线前最终验收(部署后实测)
- 公开页:
//rules/restricted/deactivated全部 200;/rules四语言标题正确。 - 箱主面板(admin6,L2):条幅显示「收窄公开」+ 规则链接;严格度面板锁定提示 1 处、下拉 disabled。
- L2 写入路径实测(关键,因为改成了
AnswerByIDPrivate单条 UPDATE): 对未回答题 q95 作答 → 302 回题目页 → flash「已保存为私密:当前处于平台处置期,回答不会公开」→ 匿名访问 302。 - 公开列表 API
/api/v1/user/{domain}/questions正常返回{code,data:{data,next_cursor,has_more}}(新增的 L4/密码校验没有打断正常路径)。 - 整箱分享卡:受处置箱主 302(拦截生效);未处置箱主 500,原因是测试站未配置字体(
分享图生成失败:字体文件未配置或无法加载),与本次改动无关。 这一点反而证明拦截位置在生成之前。 - 未能自测:CLI 发信(restrict/unrestrict 通知)需要在服务器上跑命令,只能由用户执行。
注意:admin6 现有测试题 73/88/89/90/94/95;其中 95 已被我回答并存为私密(内容带 restrict self-check)。
admin6 的 L2 于 2026-10-19 21:29 自动到期。
邮件模板统一(用户反馈:页脚与其它邮件不一致)
- 原因:
restriction-notice.html是我新写的,页脚用了自造的© 2026 TamaBox,与项目其它 11 封邮件的 「此邮件由系统自动发送,请勿直接回复。
{{ .year }} 狼的提问箱」不一致。 - 处理:整封模板按
question-reported.html的结构重写(24px 标题 +{{.time}}副行、同样的卡片/页脚/外间距),time由mail.SendRestrictionNoticeMail统一注入(调用方不用各自拼时间)。 - 实测(本地 SMTP sink):页脚文本与 question-reported 完全一致,
© ... TamaBox已消失,头部时间行存在。 - 注意:站点名在所有邮件模板里都是硬编码「狼的提问箱」,不是配置项;改站名要一起改 12 个模板。
页脚精简(移动端一行放不下)
- 去掉「盘」链接(
https://share.shiroko.one,zh 文案就叫「盘」),并删除已无引用的site.footer.drive键(四语言同步删,保证TestDictionaryComplete词条数一致)。 site.footer.rules:zh-CN「社区规则」→「规则」,zh-TW「社群規則」→「規則」(en/ja 本就短)。- 现在页脚一行:规则 | 找站长 | 公告 | 聊天 | 状态 | 主页。
- 注意:
templates/base/footer.html用|分隔,行首行尾各带一个,删整行时不会留下悬空分隔符(已核对)。
页脚改两行(用户改主意:盘恢复)
- 恢复
https://share.shiroko.one「盘」链接与site.footer.drive键(zh-CN 盘 / zh-TW 盤 / en Drive / ja ドライブ)。 site.footer.rules文案恢复:社区规则 / 社群規則(en/ja 不变)。- 布局两行(靠
<br>断行,不用额外 CSS): 第一行社区规则 | 找站长,第二行盘 | 公告板 | 聊天室 | 监控板 | 导航页。 - 键插在
site.footer.rules之后(组末),空格数按同长度键复制,gofmt 对齐不变;dict 仍 CRLF。 - 验证:build / checktpl / i18n / templates 测试全绿。
页脚改为「从少到多」四行
版权行与存活计时移到最后两行(原来在最上面),整块按文字量递增:
社区规则 | 找站长 → 盘 | 公告板 | 聊天室 | 监控板 | 导航页 → Powered by NekoBox | ICP → 提问箱已成功存活:…。
实现只调整了 <br> 的位置,仍共 4 个 <br>,所以块高与顶部空行跟原来一致(未新增占位)。
注意:box_runtime 的脚本按 id 取元素,移动位置不影响;但 <br> 数量别随意增删,否则页脚高度会变。
前端样式体检(只出结论,未改代码)
量化事实(脚本统计 templates/**/*.html):
- 全站 0 个 .css 文件,样式全部内联:
base/header.html内含 17.5KB CSS + 10.6KB JS(模板总 42.5KB), footer 3.0KB JS;全模板共 24 个<style>块,user/profile.html自身还有 11.2KB CSS + 22.8KB JS(总 62KB)。 --tb-*设计变量 只在html[data-theme="dark"]里定义,light 主题没有:root一套; 调用处全靠 fallback(#e9e9e9/#e5e5e5/#fafafa/#333/#666混用),explore 等页面还写死色值。- 深色模式 FOUC:
applyTheme(getTheme())在 body 内、<nav>之后执行(header.html:812), 深色用户刷新时顶栏会先白闪一下。修法:把 theme 初始化内联脚本提到<head>。 - 结构:
uk-container-xsmall(750px)由 header 统一输出,仅 explore/vote/chat 页面自套 small;question/list.html仍用<center>、内联style="min-height:260px",用户名/简介颜色由用户自选压在背景图上,无 scrim → 可读性无保障。 - 首页
home.html是纯 UIkit 卡片 +<hr>+style="color: red"+<pre>统计 + 首页挂 Waline(250 行 locale)。
建议分档(未实施):P0 = light/dark 双套 token + 圆角/阴影/中文字体栈 + 首页重排 + 背景图 scrim;
P1 = header CSS 抽成 static/css/*.css(注意 embed.go/static/fs.go 都是显式清单)+ FOUC 修复;
P2 = 图标统一(UIkit icons 取代 emoji/位图 png)、标签减量、viewpoint/meta 去重。
P0 前端美化(已实施)
用户拍板:卡片结构不变,只加圆角和柔阴影;先做 P0,不抽 CSS 文件。
改动文件:
templates/base/header.html- head 顶部新增主题初始化内联脚本(深色用户不再白闪;原
applyTheme在 body 里、nav 之后执行)。 顺手删掉 body 脚本里的applyTheme(getTheme()),只保留切换逻辑。 - 新增设计令牌块:
:root亮色 +html[data-theme=dark]暗色,同名 14 个(bg/surface/surface-2/surface-hover/ border/text/text-secondary/text-muted/primary/primary-weak/radius/radius-sm/radius-xs/shadow/shadow-hover/font)。 原暗色块里的 7 个变量定义已删除(现在统一在令牌块)。 - 新增基础质感层:html+body 底色与字体栈(system-ui + 苹方/冬青黑/雅黑)、卡片 12px 圆角 + 双层柔投影、
按钮/输入 8px、标签 6px、alert 10px、modal 14px、导航栏白底、
:focus-visible描边、tabular-nums、prefers-reduced-motion。 .tb-cover:封面遮罩;默认浅色(配.uk-light的黑字兜底),.tb-cover-dark为深色。.tb-img-uploader上收到 header(原来 item / new-question 各写一份内联 style,写死 #f8f8f8 → 深色下露白)。
- head 顶部新增主题初始化内联脚本(深色用户不再白闪;原
templates/home.html:整页重排为 hero(标题+一句话+探索/规则按钮)+ 3 张功能卡 + 域名迁移公告- 提醒列表 +
<details>折叠更新日志 + 3 个数字统计卡(.SiteStats数值字段,30 分钟 JS 刷新改为按字段更新)。 全部原有信息保留;页内style=归零。
- 提醒列表 +
templates/question/list.html:封面加.tb-cover(仅当有背景图),新增 12 行 JS 按用户名颜色亮度 决定遮罩方向;min-height:260px内联改为.tb-profile-cover。- 6 个页面样式块里的硬编码色值收敛到令牌(explore/question-list/profile/list/item/vote-page,共 ~84 行), CRLF 文件用字节级替换(未改动行尾)。
验证:go test ./templates/... ./internal/i18n/... 全绿;go build(overlay)通过。
新增 templates/zz_preview_test.go:TEST_PREVIEW=1 go test ./templates -run TestGenPreview 产出
outputs/preview-{home,box,explore}-{light,dark}.html,用无头 Chrome 截图核对过亮暗两套。
⚠️ 本机 Chrome 新版 headless 会挂住,必须 --headless=old,且页面外部资源要本地化
(outputs/preview-assets/,从 cdnjs.snrat.com 取 UIkit 3.3.3;cloudflare/jsdelivr 在本机不通)。