chore: baseline snapshot before moderation-strictness feature

Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
This commit is contained in:
tamakyi
2026-09-10 10:56:27 +08:00
commit e3e59e7c69
296 changed files with 67458 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
# 2026-09-04
- 调研并输出 /user/profile 页面(account 面板)UI 优化评审与改造方案(等待用户批准后实施)。
- 关键结论:
- 密码强制登出:`users.password_version` + 会话 `pwd_ver` 机制已存在(internal/context/auth.go authenticatedUser),改密后旧会话下次请求即失效;但 UpdateProfile 改密后仍重定向 /user/profile,被 Toggle 以「请先登录」错误文案弹回 /login,成功 flash 被吞。改造方向:改密成功 → Session.Flush() + success flash(复用 auth.errPasswordChanged)→ 直接跳 /login。
- 头像/背景裁剪:项目无任何 crop 库;第三方库全部自托管于 R2(r2.337845818.best/tamabox/cdn)。候选方案:Cropper.js 内嵌 static/(需扩 static/fs.go embed 规则)。当前 avatarPlaceholder 文案自嘲"非1:1会扁"但无强制。
- 复用点:profile-tabs 面板、Pickr 颜色选择、easy.qrcode 预览、UIkit modal/accordion、imagecheck 像素校验、storage.UploadPictureToS3(自动转 WebP、宽>1920 缩放)。
- i18n 四语言字典 key 全等校验(i18n_test.go TestDictionaryComplete),新增词条须同步 4 个 dict 文件。
## 下午:profile account 面板改造已实施(用户已批准方案)
- 决策:Cropper.js 走自有 R2 CDN(`lib/cropperjs/1.6.2/`,用户自行上传);改密立即登出跳登录页;卡片分区+折叠(中等重构);前后端双比例校验。
- 改动清单:
- `route/user/profile.go`:avatar→storage.UploadAvatarToS3、background→storage.UploadBackgroundToS3(新增),改密成功(passwordChanged 标志)→ Session.Flush + flash(profile.flash.passwordChanged) → /login;比例不符 flash 提示(profile.err.avatarRatio / backgroundRatio)。
- `internal/imagecheck/imagecheck.go`:新增 ErrBadAspectRatio + CheckAspectRatio + DefaultAspectTolerance(0.01);`imagecheck_test.go` 新增 4 个用例。
- `internal/storage/s3.go`:抽 uploadPictureToS3 内部分管线(aspect 可选),UploadAvatarToS3=1:1、UploadBackgroundToS3=16:9,魔数后、像素总量前校验;原有 UploadPictureToS3 兼容签名(其他调用方不变)。
- `internal/i18n/dict_*.go`(4 语):新增 15 词条(card.* 4、cropHint 2、cropTitle/cropConfirm、pwdNote、pickAvatar/pickBackground、err.avatarRatio/backgroundRatio/cropLibUnavailable、flash.passwordChanged)。
- `templates/user/profile.html`:account 面板重构为 8 张卡片(基础资料/头像与背景[预览+裁剪]/二维码样式折叠/社交折叠/修改密码[随主表单]/标签/页面密码/账号操作);头像 96px 圆、背景 16:9 预览;Cropper 弹窗+写回 file input 逻辑;accordion 展开时重绘二维码预览;新增 .tb-pcard 等样式与暗色适配、移动端单列。
- 验证:go build ./...(用 dev/webpstub overlay 方式,Windows 无 libwebp)通过;go test internal/i18n + internal/imagecheck 通过;dev/checktpl 全模板语法 OK;HTML 标签开闭平衡校验通过。
- 待办:R2 上传 cropper.min.{js,css} 到 r2.337845818.best/tamabox/cdn/lib/cropperjs/1.6.2/ 后即可端到端生效;未做浏览器实测(需本地 DB/Redis)。
## 晚间:修复部署环境 `UIkit is not defined`(Rocket Loader 时序)
- 现象:VM234:42 Uncaught ReferenceError: UIkit is not defined;更换头像/背景按钮点击无效果。
- 根因:裁剪脚本(templates/user/profile.html 末尾)IIFE 解析期即执行 `var modalObj = UIkit.modal(modalEl)`;UIkit 在 base/header.html 是 defer 加载,Cloudflare Rocket Loader 把该内联脚本激活在 UIkit 执行前 → 抛错中断整个脚本 → `[data-pick-image]` 监听(其后)从未绑定。
- 修复(templates/user/profile.html,4 处):
1. `modalObj` 改为 null 懒创建(startCrop 内、依赖就绪后 `UIkit.modal(modalEl)`);
2. 新增 `notify()`:UIkit 可用→UIkit.modal.alert,否则回退原生 window.alert;
3. 新增 `whenReady(cb)`:轮询等待 window.UIkit && window.Cropper,超时 3s → notify(MSG_NO_CROPPER);文件大小校验前置(不依赖库);
4. 脚本标签加 `data-cfasync="false"`,按钮监听尽早同步绑定。
- 经验:模板内联脚本**禁止在解析期触碰 defer/外部库全局对象**(UIkit/Pickr 等),一律在用户事件回调内或依赖就绪后使用——Rocket Loader 会打乱执行序;全站内联脚本仅裁剪脚本踩中此坑。
- 验证:node --check 通过(模板占位符替换后);R2 上 cropper 文件此前 curl 仍不可达(HTTP 000),用户需先上传,否则选图后 3s 提示「裁剪组件暂不可用」——需提醒用户。
## 晚间(二):图像保存 UX + 同行居中 + console 噪音排查
- 用户反馈:图像上传/裁剪已可用;但「保存修改」按钮在「修改密码」卡片里,改图后要去那里点击才生效,不直观;且头像修改显示在左上角别扭。
- 改动(templates/user/profile.html):
1. 头像与背景卡片内新增本地保存按钮(type=submit,label 新 i18n key `profile.saveMedia`,随主表单提交,裁剪写回的文件域一并上传);CSS `.tb-media-save`(居中条)。
2. `.tb-media-row` 改 `justify-content:center`、子项 `flex:0 1 auto` + column 居中、`tb-frame-bg` 固定 320px(max-width:100%);移动端 ≤640 子项 `flex-basis:100%` 上下堆叠居中、背景图全宽、保存按钮全宽。
3. 4 语字典新增 `profile.saveMedia`(zh-CN 保存头像与背景 / zh-TW 儲存頭像與背景 / en Save avatar & background / ja アイコンと背景を保存)。
- console 噪音排查结论:`inspector.js` AbortError/Request timeout/responseType arraybuffer 报错**非项目代码**(全仓库 grep 无 inspector/fetch 包装);是浏览器 XHR 监控类扩展包装 fetch/XHR + Rocket Loader 激进异步调度(arraybuffer 脚本请求被中止)造成的误报。建议无痕窗口验证 + 或 Cloudflare 关闭 Rocket Loader。
- 验证:gofmt -w 4 个 dict;go test ./internal/i18n/ 通过;go run ./dev/checktpl 全模板语法 OK。
## 晚间(三):二维码/社交默认展开 + 头像背景组件等宽 + 每卡片独立保存按钮
- 用户要求:①二维码、社交两块默认展开;②更换头像/更换背景组件大小一致(原头像 96 圆 vs 背景 320 宽预览悬殊);③为 account 面板每块加独立保存按钮(仿头像背景卡样式)。
- 改动(templates/user/profile.html):
1. 卡片3/4 的 uk-accordion `<li>` 加 `uk-open` 默认展开(仍可折叠,multiple:true)。
2. `.tb-media-item` 固定等宽列 320px(flex:0 0 320 + max-width:100%),头像预览 96→120px、背景帧 width:100%(列内满宽 16:9);`.tb-media-actions .uk-button` min-width:150px 令两个更换按钮同宽。移动端:头像 80→96px、item 全宽堆叠、按钮全宽。
3. 保存按钮:基础资料(profile.saveBasic)/头像背景(已有 saveMedia)/二维码(profile.saveQR)/社交(profile.saveSocial)/改密(原 admin.saveProfile 改 profile.savePassword「确认修改密码」)——均为同表单 type=submit(提交整表),复用居中类 `.tb-card-save`(与 .tb-media-save 同规则)。
4. 二维码预览:默认展开后从其它 tab(?tab=question 等)切回 account 面板时不会再触发 accordion 'shown' → showPanel 里 account 时调 `window.__tbRefreshAccountPreview`(IIFE 内暴露的 debouncedUpdatePreview)重绘。
- i18n 4 语新增 saveBasic/savePassword/saveQR/saveSocial;admin.saveProfile key 保留未删(四语仍在,仅模板不再引用)。
- 验证:go test ./internal/i18n/ 通过;go run ./dev/checktpl OK。
## 晚间(四):头像/背景框等高 + 按钮统一居中 + 页面纵向压缩
- 用户反馈:①头像框偏小,希望与背景框显示高度一致;②按钮有的居中有的居左,要求统一并压缩空间。
- 改动(templates/user/profile.html,纯 CSS/结构):
1. 预览框等高:头像 120→150px 圆;背景框 267×150(16:9,与头像等高);媒体列 320→280 等宽;行间隙 20/40→14/32;移动端头像 96→120px。
2. 按钮统一居中:账号操作卡片(登出/导出/停用)`text-align:center` + 行距 12→9px;卡片6 标签、卡片7 页面密码按钮 `div.uk-margin`→`div.tb-card-save`;提问面板 updateHarassment/updateAskPassword、私聊面板 updateChat 三个按钮同改居中。
3. 压缩:`.tb-pcard` padding 16/16/10→13/14/8、margin-bottom 16→12、标题 margin 12→10;新增 `.profile-panel .uk-margin { margin-bottom: 12px }`(全面板字段间距 20→12)。
- 范围说明:global-blockwords / feature-control-form 两个 partial 模板(三面板共用)内按钮未改(避免影响其它页面),如需统一再单独处理。
- 验证:go run ./dev/checktpl OK。
## 晚间(五):账号操作按钮分组同行 + partial 内按钮居中
- 用户要求:①导出数据/导出全部数据按钮同一行;②退出登录/停用我的账号同一行;③屏蔽词设置、计数设置也居中。
- 改动:
1. `templates/user/profile.html`:账号操作卡重构为 `.tb-account-pair`(flex 居中,两按钮同行):导出组(默认按钮×2,target=_blank 表单)+ 说明行 `.tb-account-note`;登出组(danger 按钮+停用链接)+ 说明行。旧的 `.tb-account-action` 行式堆叠 CSS 删除,暗色规则改 `.tb-account-note`。
2. `.feat-quota-line` 加 text-align:center;`.feat-actions` 加 justify-content:center(保存/重置计数按钮居中)——覆盖 feature-control-form(提问/私聊/投票共用)。
3. `templates/user/global-blockwords.html`:提交按钮行 `.uk-margin` → `.tb-card-save`(居中);该 partial 仅 profile.html 引用,无外溢。
4. 说明文字合并保留:exportHint+exportJSONHint 合并一行小字,deactivateHint(含 <b>)保留在其按钮组下方(deactivate 确认页另有完整警告文案)。
- 验证:go run ./dev/checktpl OK。
## 晚间(六):修复导出按钮点击后永久「提交中」转圈
- 现象:导出数据/导出全部数据(target=_blank 下载表单)文件能下,但按钮永久转圈禁用。
- 根因:`templates/base/footer.html` 全局 submit 反馈(P0 防重复提交)把触发按钮置「转圈+提交中」并禁用;复位仅靠整页导航/bfcache pageshow。`target="_blank"` 下载表单当前页不导航 → 无复位机会 → 永久卡死。
- 修复(双保险):
1. `templates/user/profile.html`:两个导出 form 加 `data-tb-skip-submit-feedback="1"`(footer 既有跳过通道,每次 submit 读到即跳并移除)。
2. `templates/base/footer.html`:submit 监听里对 `form.getAttribute('target')==='_blank'` 一律 return 跳过反馈——防御未来任何下载/新窗口表单踩同一坑(全站现存 target=_blank 表单仅 profile 这两处,行为从“卡死 bug”变“无反馈”=纯修复)。
- 经验:全站提交反馈的复位依赖导航,凡不触发本页导航的提交(target=_blank / 下载 / iframe)都必须显式跳过。
- 验证:go run ./dev/checktpl OK。
## 晚间(七):私聊面板 chat 专属功能聚合单卡
- 用户要求:`?tab=chat` 面板里「私聊块」(功能开关/收集窗口 feature-control-form)与「私聊设置」(update-chat-settings 消息上限)聚合为一张卡;全局屏蔽词为三面板共享、保持独立。
- 改动(templates/user/profile.html):
1. chat 面板改两段结构:私聊专属 = `<section class="tb-pcard chat-zone">` 内含 FeatureChat partial + `<hr>` + update-chat-settings 表单(原外层 `div.uk-margin` 移除);全局屏蔽词维持原 `hr` + partial 独立区。
2. CSS `.chat-zone`:内部 `.feat-card` 去边框透明(padding:0; border:0; background:transparent),避免卡中卡;`hr` margin 14px;内部 `legend`(admin.chat)从 uk-legend 大标题降级为 1.05rem 小节标题。
- 提问/投票面板未动(结构同前)。
- 验证:go run ./dev/checktpl OK。
## 晚间(八):功能控制卡按钮尺寸统一为常规按钮
- 用户要求:「保存该功能设置」按钮样式/大小改成与「更新私聊设置」一致;「重置计数」同调。
- 改动(templates/user/feature-control-form.html,三面板共享 partial):feat-actions 内两按钮去掉 `uk-button-small` —— 保存改 `uk-button uk-button-primary`、重置改 `uk-button uk-button-default`(常规尺寸,与全站其它表单主按钮一致)。提问/私聊/投票同步生效。
- 验证:go run ./dev/checktpl OK。