# TamaBox-v4 长期笔记 ## 协作 - 结论分级 P0/P1/P2;给建议前先根因取证 + 实测复现,别只静态推断。 - 改代码前先出变更清单获批准;用户常授权自主推进,但重大问题仍要显式指出。 - 用户是运营者也是圈内人:处置功能必须可解释、可留痕、可申诉,不做自动级联封号。 ## 构建与测试 - **无 libwebp/GCC**,cgo 的 `internal/storage/s3.go` 挡编译 → overlay 换桩 `dev/s3stub/s3_stub.go`: `-overlay="C:/Users/Tamakyi Gekka/AppData/Local/Temp/tb-webpcheck/overlay.json"`。 但 overlay 把 s3.go 当桩,**要检查 s3.go 本身得 tar 复制项目 + `go mod edit -replace`**(overlay 换不了 GOMODCACHE 文件)。 - **「编译卡死」= `proxy.golang.org` 不可达**。每条 go 命令前: `export GOPROXY=https://goproxy.cn,direct GOSUMDB=off GOFLAGS=-mod=mod`(`go env -w` 在本沙箱失败)。 1C1G 是正式生产环境,**别用「沙箱慢」解释超时**。 - DB 测试:`DB_TYPE=postgres DB_USER=postgres DB_PASSWORD=15207786347hXPm DB_HOST=127.0.0.1 DB_PORT=5432` (DSN 不带库名,`NewTestDB` 自建临时库后 DROP)。带 DB 的 `go test ./...` 目前全绿。 - store 方法内用 `NewXStore(db.DB)`,别引用包级单例(测试下 nil panic;`FeatureStateOf` 例外,有守卫)。 - ⚠️ **本沙箱起不了 `TamaBox web`**(session 固定走 redis,6379 未开,启动即 panic)→ HTTP 端到端只能部署后做。 探测端口 200 可能是别的进程,必须看日志确认 `Starting web server` 且无 panic。 - ⚠️ **本机 python urllib 不稳定**(代理下偶发读超时,curl 正常),曾误判「接口卡死」。线上验证优先 curl; python 别用 `/tmp`(Windows 解析成 `C:\tmp`,与 MSYS `/tmp` 不通)。 ## 行尾 / 模板 - 行尾混合不可推断(`internal/db/*`、`mail.go`、`route/user/profile.go` 是 LF;`internal/route/route.go`、 `users.go`、`i18n/dict_*.go`、`route/question/*.go`、`question-list.html` 是 CRLF)。改前用 Python 数 `b'\r\n'`; `gofmt -l` 会把 CRLF 误报为未格式化。格式化走 `dev/gofmt_keep_crlf.py`。 - 往 gofmt 对齐组中间插键会污染 diff,新键插组末;注释别用 `''`(gofmt 会改成 `”`)。 - ⚠️ **`templates/embed.go` 是显式清单**:新增**顶层**模板必须加进 `//go:embed` 那行,否则运行时 `html/template: "xxx" is undefined` → 500,编译期无提示(`checktpl` 也只查语法)。子目录已被目录项覆盖。 - `templates/user/moderation-strictness.html` 只用 shell/python 改(Edit/Write 会注入 `data-page-node-id`)。 ## 审核 - 线上通道 = 七牛(`ai.qiniuapi.com/v3/text/censor`,antispam);`[security] text_censor_source = qiniu|aliyun|auto`。 七牛只硬拦涉政/暴恐/色情,`review` 放行 → 裁决在 `censor/policy.go` + `moderation.Check`。 - **新增审核调用点必须走 `moderation.Check`**(直调 `censor.Text` 会绕过档位)。不可用时 **fail-closed**。 - ⚠️ **圈内黑话(代称+群体标签+避雷叙述)任何通用审核都拦不住**,别指望词库;靠处置 + 举报 + 人工判断。 - 严格度三档 lax/standard/strict,存 `users.moderation_strictness`。**受处置用户强制 strict**: `moderation.EffectiveStrictness(ctx, userID, raw)`(7 个入口已全部改用);**查询失败时回退用户设置** (否则 DB 抖动会让全站突然加严)。处置期内 `UpdateModerationStrictness` 拒绝保存 + 设置页 disabled。 ## 提示(Flash) - ⚠️ **同请求渲染只能用 `ctx.SetError` / `SetInternalError`,不能用 `*Flash`**:flash 写 session,下一次请求才读 → 提示静默丢失(「点了没反应」)。2026-10-18 清理 9 处;`Set*Flash + Redirect` 是正常模式。 扫描法:找 flash 调用 → 向下第一个 `Success(`/`Redirect(` → 前者即缺陷。 ## 处置与举报 - L1 停止分发(禁分享卡 + 首页/探索/标签/随机位摘除)/L2 收窄公开(新回答强制私密、禁止 set-public)/ L3 冻结发布(仍可收信)/L4 整页停用(`/restricted`)。到期靠 `expires_at` 自动失效,**无 cron**; `restrict` 是**替换语义**;级别比较用 `RestrictionAtLeast`。 - ⚠️ L2 强制私密必须在**审核通过之后**,且与写回答**同一条 UPDATE**(`AnswerByIDPrivate`): 拆成「先 SetPrivate 再 Answer」会留下「公开+已作答」窗口,第二条失败还会变成「私密+无回答」。 - ⚠️ **`ListAPI`(`/api/v1/user/{domain}/questions`)不经过 Pager 中间件**,L4 与页面密码必须自己校验 (否则「整页停用」和访问密码都能被 API 绕过)。 - ⚠️ **整箱分享卡 `ShareCard` 与单题分享卡都要拦**,且拦截必须在缓存查询之前(否则 30 分钟内仍给旧图)。 - 举报 `POST /_/{domain}/{qid}/report`:公开免登录 + 验证码 + 限流;同 IP 同题唯一索引 → **幂等且提示与首次一致**(否则成了内容存在性探测器)。箱主只看「被举报 N 次」,**不给 IP**。 - 闭环:箱主删除/设私密或 `hide-public` → `MarkAcceptedByQuestion`;待处理超 30 天视为过期。 ⚠️ `hide-public` 里必须**先快照目标 ID 再更新**(更新后用带 `is_private=false` 的子查询恒为空集,曾静默失效)。 - 告警:24h≥8 或 7d≥20 → 邮件到 `[security] security_alert_email`(24h 冷却,进程内);单条恰好 3 人举报 → 提醒箱主。 ⚠️ 该邮箱为空时静默跳过、无日志;改配置必须重启。 - `/rules` 是处置依据页(四语言);注册页必须勾选 `agree_rules`(服务端同样校验),改 e2e 脚本要同步。 - `restrict` 默认给箱主发处置通知(按 `users.language`,模板 `mail/restriction-notice.html`), **只含级别名/到期/规则链接,不含 `--reason`**;`--no-notify` 可跳过(L1 静默止血用)。 `--days > 90` 必须加 `--force`,交互模式还要再输一次级别名。`unrestrict` 也发解除通知,双方都有 `--no-notify`。 ⚠️ **到期自动解除不发信**(读表比较时间,无调度器)。 ## GORM - 默认值必须带引号;`default:` 会把零值替换,空串有语义的字段别加。 - ⚠️ 事务里复用同一 `tx` 会串条件,每句 `tx.WithContext(ctx).<链式>`。 - ⚠️ 软删除 + 唯一索引 = 槽位永久占用:停用(可恢复)= 物理删内容 + 软删用户行,注册侧 `Unscoped` 给 `ErrDeactivatedEmail/Domain`;封禁 = 物理删用户行,邮箱释放。级联删除一律 `Unscoped()`。 - ⚠️ GORM 链式对象 Count/Find 后带状态,统计/预览/更新必须各自重建 query。 ## 前端样式 - 基础样式 `static/css/base.css`(`/static/css/base.css?v=YYYYMMDDx`,**改完必须 +1**,静态缓存 1 天)。 设计令牌在文件开头 `:root`(亮)+ `html[data-theme="dark"]`(暗)。新样式一律用 `var(--tb-*)`,别写死色值,也别为深色另写分支。 - 页面级样式仍内联在各自模板 `