5.9 KiB
5.9 KiB
TamaBox-v4 项目长期笔记
审计/失败分级惯例(沿用用户偏好)
- 结论分级:P0/P1/P2,配 🔴(阻断级)/🟠(高)。给修复建议前先做根因取证与并行诊断。
- 改代码前先出「方案/变更清单」获批准,再分批落地;用户倾向方案 A 或明确指定方向。
- 工作场景用
@scene#N:"名称"标记(如@scene#15:"日常开发")。 - 用户常说「你先继续,后面我发现问题再和你说」→ 可自主推进,但重大问题仍要显式指出。
构建与测试(Windows 沙箱)
- 本机无 libwebp / GCC,而
chai2010/webp(cgo)会挡住go build ./...。 必须用 overlay 把internal/storage/s3.go换成dev/s3stub/s3_stub.go:(go build -overlay="C:/Users/Tamakyi Gekka/AppData/Local/Temp/tb-webpcheck/overlay.json" ./... go test -overlay="C:/Users/Tamakyi Gekka/AppData/Local/Temp/tb-webpcheck/overlay.json" ./...dev/overlay.json里的路径指向 TAMAKY~1 短名,可能失效;用上面 Temp 里的那份。) internal/db的测试需要真实 PostgreSQL/MySQL(否则报Unknown database type: ""), 与业务改动无关,可跳过;go vet已能覆盖其编译。proxy.golang.org在本沙箱返回 Bad Gateway:拉新依赖 /go mod tidy前先export GOPROXY=https://goproxy.cn,direct。internal/security/censor/censor.go、internal/security/censor/text_test.go存在升级前就有的 空格缩进(gofmt -l会列出)。除非专门整理,否则不要顺手 gofmt,避免污染 diff。
内容审核架构(简洁版)
- 线上生效的审核通道是七牛云(
ai.qiniuapi.com/v3/text/censor,scene=antispam);阿里云通道代码存在但 key 留空、不生效。 - 通道可选:
[security] text_censor_source = qiniu | aliyun | auto(留空同 auto=七牛优先、阿里云兜底,即历史行为)。 取值常量在conf(CensorSource*),装配在censor.censorChain(),启动校验在conf.validateTextCensorSource。 显式选通道却缺凭据会启动报错(避免运行期 fail-closed 导致整站不可用)。 - 关键事实:七牛
IsPass()只硬拦 politics/terrorism/porn,review一律放行; 七牛文本审核不支持白名单、自定义词库需提工单 → 服务端「拦得准不准」不可控。 - 因此本项目把「是否拦截」从服务商 Pass 判定剥离,收敛到策略层:
internal/security/censor/policy.go(档位裁决)+internal/security/moderation/moderation.go(唯一入口Check)。 - 任何新增的文本审核调用点都必须走
moderation.Check,不要再直接调censor.Text, 否则会绕过「审核严格度」档位。三场景:提问/回答、投票、私聊。 - 语义约定:
LabelScore.Score统一归一化 01(七牛本身即 01;阿里云 2.0 无逐标签置信度, 按riskLevel折算 high=1 / medium=0.6 / low=0.3,该响应内所有标签共用同一分值)。 - 审核服务不可用时 fail-closed(拒绝提交)—— 这是既有行为,未经批准不要改成 fail-open。 ⚠️ 推论:显式选定的通道一旦坏掉(账号未开通/欠费/权限不足),整站发布+私信会全部被拒,现象隐蔽(只在日志)。
阿里云 green 通道(/green/text/scan, 2018-05-09)
- 实现与上游 NekoBox 及阿里云官方 Go 示例逐字节一致;
TextScanRequest无独立Content字段, body 走内嵌RoaRequest.Content。SDK 的 ROA 默认 Content-Type 是application/octet-stream(sdk/auth/roa_signature_composer.go:88),现已显式SetContentType(requests.Json)对齐接口契约。 - 已知故障:HTTP 200 +
data为空 →"response data is empty"。该错误此前会吞掉顶层 code/msg/requestId (唯一的根因线索),现已把 code/msg/requestId + 原始响应摘要(≤512 字符)写进错误。 ✅ 2026-09-10 实证根因:code=596 "You have not opened Yundun Content Moderation Service"—— 阿里云回的是 HTTP 200 +success:false+ 无 data,即「请求被拒也返回 200」。 ⚠️ 硬门槛:「内容安全 1.0」开通前提是账号完成企业实名认证(官方 69806),个人认证账号永远开不了。 开通入口是common-buy.aliyun.com/?commodityCode=cdi#/open,不是产品介绍页aliyun.com/product/lvwang。 替代方案:迁「内容安全增强版 2.0」(green-cip,个人认证可开,但标签体系完全不同、需改代码), 或退回auto/qiniu。RAM 用户需AliyunYundunGreenWebFullAccess;欠费停服同理。 data[].code != 200(任务级失败)仍 fail-closed 置 block,但现在会打 Warn —— 否则「审核失败」与「真的违规」在日志里无法区分。- ⚠️
SetContentType(requests.Json)已加,但已证伪与上述故障无关,保留原因仅是接口契约对齐。
文件工具陷阱(重要)
templates/user/moderation-strictness.html:用 Write/Edit 写它会被自动注入data-page-node-id属性 (每次一批新 id);用 shell(sed)改则不会。注入方是「文件工具触发的后处理钩子」,不是文件监听。 → 该文件今后只用 shell 修改。相关断言(templates/render_test.go)已改成不依赖属性顺序的正则。- 往 gofmt 对齐组中间插 map 键,会把「拆分后剩余组」整组重新对齐 → 污染 diff。 新键应插在组末尾或独立成组(前后留空行)。i18n 字典已因此返工过一次。
仓库状态
- git 仓库已损坏(
.git/refs缺失、index 截断、对象库近乎空),用户明确要求「不要动 git」。 备份/回滚一律用 tar.gz(排除.git、.workbuddy),放在项目同级目录:C:/OtherFiles/Github/TamaBox-v4-mainline-<描述>-<日期>.tar.gz。