This commit is contained in:
tamakyi
2026-10-19 01:58:00 +08:00
parent bd613a6f5a
commit 4592b6ef24
14 changed files with 608 additions and 244 deletions
+292
View File
@@ -0,0 +1,292 @@
# TamaBox 命令行操作手册(站方运维)
本项目**没有管理后台**:站方的全部运营动作只落在三类操作上 —— **CLI 命令 / 邮件告警 / 改配置后重启**。
这份文档只讲 CLI,覆盖处置、举报取证、批量隐藏与账号管理。
---
## 0. 前置
### 二进制从哪里来
```bash
# 仓库根目录构建(与 Dockerfile 是两条不同入口,命令列表必须双向同步)
go build -o tamabox .
# Docker 用的是这一条
go build -o NekoBox ./cmd/
```
两处都注册在同一份 `app.Commands` 列表中。**新增命令时必须同时改 `tamabox.go` 和 `cmd/tamabox.go`**,
否则会出现「本机二进制有、线上没有」(或反之),典型报错是 `No help topic for '<cmd>'`。
确认手头二进制包含全部命令:
```bash
./tamabox --help | sed -n '/COMMANDS/,/GLOBAL/p'
# web / censor / uid / ban / restore / reports / restrict / unrestrict / hide-public
```
### 配置文件
CLI 与 `web` 读同一份配置,路径由环境变量指定:
```bash
NEKOBOX_CONFIG_PATH=/path/to/app.ini ./tamabox reports --domain xxx
```
必要条件(缺失会直接启动失败):
- `app.external_url`
- `server.salt`、`server.xsrf_key`
- `[database]` 段的 type / host / port / user / password / name
⚠️ CLI 启动时会跑 **AutoMigrate**(与 web 相同)。这是特性也是风险:迁移有问题时连只读的
`reports` 都跑不起来。做重要操作前先 `pg_dump`。
### 操作前先备份
```bash
pg_dump -Fc <dbname> > before-$(date +%F-%H%M).dump
```
---
## 1. 命令总览
| 命令 | 作用 | 是否改数据 | 典型用途 |
|---|---|---|---|
| `reports` | 举报取证 / 待处理列表 / 全站统计 | 否 | 处置前看证据 |
| `restrict` | 施加处置(L1–L4) | 是 | 停止分发、冻结发布、停用页面 |
| `unrestrict` | 撤销全部生效中处置 | 是 | 解封、误操作回滚 |
| `hide-public` | 批量把公开回答转为私密 | 是 | 清理存量挂人内容 |
| `ban` | 永久封禁(删用户+内容) | 是(不可逆) | 严重违规账号 |
| `restore` | 恢复已停用账号 | 是 | 用户申诉后恢复 |
| `censor` | 重新审核历史内容 | 是 | 审核规则变更后复检 |
| `uid` | 为用户生成/补齐 uid | 是 | 数据修复 |
| `web` | 启动 Web 服务 | — | 生产入口 |
除 `reports` 外,所有写操作都要求交互式输入大写 `YES` 确认;脚本化时加 `--yes`。
---
## 2. `reports` —— 处置前的取证
三种互斥用法(`--stats` / `--pending` 优先于 `--email/--domain`)。
### 2.1 单个箱主的证据包
```bash
./tamabox reports --domain 52mxtx1314
./tamabox reports --email [email protected] --days 90 --limit 50
```
输出包含:
- **当前处置**:级别、到期时间、原因
- **近 N 天:被举报 X 条 / 公开回答 Y 条(累计,Z%);举报 W 次,其中待处理 V 次**
- **被举报条目**:按举报数降序,带题号、内容前 48 字与状态(`待处理 N` / `已处理`)
- **历史处置**:每条的时间、级别、原因、状态(生效中 / 已撤销 / 已到期)
口径(容易误读,务必看清):
- 分子 = 窗口内**被举报的条目数**(不是举报次数)。同一条被 5 个人举报只算 1 条。
- 分母 = 该箱主**当前累计**的公开回答数,**不是**窗口内产出。
所以 L2 收窄公开之后,分母会变小,比例可能超过 100% —— 这不是 bug,是分母口径问题。
判断「组织化挂人」时优先看**绝对条数**与**指涉对象分布**,比例只作参考。
- 已删除的提问不计入(`deleted_at IS NULL`)。
### 2.2 全站待处理举报
```bash
./tamabox reports --pending --limit 50
```
按箱主分组列出所有 `status = pending` 的举报(不受 30 天窗口限制,方便清理积压)。
### 2.3 全站维度统计(识别「举报被武器化」)
```bash
./tamabox reports --stats --days 7
```
输出两个 Top 10:
- **被举报最多的箱主**(含 domain / 昵称 / 待处理举报数)
- **举报来源 IP 最多**(同一 IP 反复举报同一批内容)
怎么读:
| 现象 | 更可能是 |
|---|---|
| 多个 IP 打同一个箱主,且内容指向多个不同对象 | 组织化挂人 |
| 少量 IP 打多个不同箱主 | 举报被武器化(报复性举报) |
| 同一 IP 单点爆发 | 脚本刷量(已由验证码 + 唯一索引限制) |
⚠️ 统计只覆盖**待处理**举报,且最长回溯 30 天(超期未处理的举报会自动退出统计池)。
### 2.4 举报计数的前提:客户端 IP
举报去重键是 `(question_id, reporter_ip)`,计数与统计全部依赖真实客户端 IP。
- 站点在反向代理 / CDN 之后时,**必须**在 `[security]` 配置 `trust_proxy = true`,
并按需配置 `trusted_proxies`。
- 若 `trust_proxy = false` 而站点实际在代理后,所有访客会被识别成同一个代理 IP →
每条内容最多只有 1 条举报,阈值告警永远不会触发。
- 验证方法:从两个不同网络各举报一次,然后 `reports --pending` 看是否出现两个不同 IP。
---
## 3. `restrict` —— 分级处置
```bash
./tamabox restrict --domain <domain> --level L1 --days 7 --reason "组织挂人投稿"
./tamabox restrict --email <email> --level L3 --days 14 --reason "复发" --yes
```
参数:
| 参数 | 说明 |
|---|---|
| `--domain` / `--email` | 二选一;都填时按 email 查 |
| `--level` | `L1` / `L2` / `L3` / `L4`,默认 L1,大小写敏感 |
| `--days` | 生效天数,从当前时间起算,必须 > 0 |
| `--reason` | **必填**,写入审计记录 |
| `--yes` | 跳过 `YES` 确认 |
### 级别语义
| 级别 | 名称 | 实际效果 | 生效点 |
|---|---|---|---|
| **L1** | 停止分发 | 单题/整箱分享卡不可生成;从首页、探索、标签检索、随机位摘除;热门标签不再出现 | `share.go`、`users.go` |
| **L2** | 收窄公开 | L1 全部 + 新回答强制私密(单条 UPDATE 同时写回答与 `is_private`)+ 禁止「设为公开」+ 禁止回答追问 | `question.go`、`follow_up.go` |
| **L3** | 冻结发布 | L2 全部 + 禁止回答(**仍可收信**) | `question.go` |
| **L4** | 页面停用 | 整个 `/_/{domain}` 全部 302 到 `/restricted`(单题、私聊、分享卡、JSON 列表接口一并挡住) | `page.go` Pager + `ListAPI` |
设计要点:
- **L3 不禁收信**。伤害来自「发布」,剥夺收信能力没有治理收益,还会给对方「平台封口」的说法。
- **到期自动解除**:判定是 `expires_at > now()`,没有定时任务,也不需要手工解封。
- **替换语义**:同一用户再次 `restrict` 会先撤销旧的生效记录再写新的,任何时刻只有一条生效。
升级(L1→L3)与降级(L3→L1)都走这条路径。
- **处置期间审核严格度锁定为最高档**(`strict`),箱主无法自行改档;解禁后自动恢复他原本的设置
(`users.moderation_strictness` 全程未被改写)。
### 副作用(需要提前知道)
- 处置期间箱主在自己后台能看到橙色条幅(级别 + 到期日 + 规则页链接)。
- **L1 就强制 strict**:L1 只停分发、内容仍能发布,但审核已按最严处理,
正常的吐槽式提问也可能被拦。若线上发现大面积误拒,把 `moderation.EffectiveStrictness`
改成「L1 沿用原设置」即可(一行)。
- **没有任何邮件通知箱主**。当前只有登录后的条幅,纯邮件用户不会知道。
---
## 4. `unrestrict` —— 撤销
```bash
./tamabox unrestrict --domain <domain>
./tamabox unrestrict --email <email>
```
- 把该用户**所有生效中**的处置写 `revoked_at`(保留记录,不删除,审计可追溯)。
- 幂等:没有生效中处置时输出「没有生效中的处置,无需操作」。
- 撤销后:页面/发布/分享卡立即恢复;`hide-public` 已经转为私密的内容**不会**自动变回公开,
需要箱主自己逐条「设为公开」。
---
## 5. `hide-public` —— 清理存量内容
```bash
# 最推荐:只隐藏「有未处理举报」的公开回答
./tamabox hide-public --domain <domain> --only-reported
# 全部公开回答(激进的兜底)
./tamabox hide-public --domain <domain>
# 只处理最近 30 天
./tamabox hide-public --domain <domain> --only-reported --since 30
```
| 参数 | 说明 |
|---|---|
| `--only-reported` | 只处理有 `pending` 举报的题目 |
| `--since N` | 只处理最近 N 天创建的题目;0 = 不限 |
| `--yes` | 跳过确认 |
行为:
- 只动「公开且已回答」的题(`answer != '' AND is_private = false`),不碰私密与未回答。
- 执行前打印总数 + 最新 10 条预览。
- **可逆**:只改可见性,箱主可逐条改回公开(但该箱主处于 L2 及以上时改不回去)。
- 隐藏成功后自动把相关 `pending` 举报标记为 `accepted`,让它们退出告警统计池。
- 单独用它不足以「停止分发」,建议配合 `restrict --level L1`。
---
## 6. 账号管理(`ban` / `restore`)
```bash
# 永久封禁:物理删除用户与其内容,邮箱释放。不可恢复。
./tamabox ban --email [email protected] --yes
./tamabox ban --domain xxx --yes
# 恢复「自助停用」的账号(软删除行),可同时重置密码
./tamabox restore --email [email protected]
./tamabox restore --email [email protected] --password 'NewPassw0rd' --yes
```
两者的区别(涉及唯一索引,容易踩):
- **停用(可恢复)**:内容物理删除 + **用户行软删除**。邮箱/域名继续被占用是**有意为之**,
恢复靠 `restore`。
- **封禁(不可恢复)**:内容物理删除 + **用户行物理删除**,邮箱立即可重新注册。
- `restore --password` 必须在恢复之后执行,否则改不到(普通作用域查不到软删除行)。
---
## 7. 典型处置流程
1. **发现**:收到告警邮件(同一箱主 24h ≥ 8 条 或 7d ≥ 20 条)→ 或箱主后台出现「被举报 N 次」。
2. **取证**:`reports --domain <domain>` 看条目、占比、历史处置。
3. **冷静期**:举报后 24 小时内不作判定(避免被集中举报推着走,也避免情绪化处置)。
4. **止血**:`restrict --level L1 --days 7 --reason "..."`(静默生效,不单独通知)。
5. **清存量**:`hide-public --domain <domain> --only-reported`。
6. **观察**:7 天内复发 → 升 L2/L3;双方互挂 → **两边同级别处理,不选边**。
7. **收尾**:到期自动解除;需要提前解封用 `unrestrict`。
8. **留痕**:`reports --domain <domain>` 的历史处置段落就是处置记录(谁、何时、级别、原因)。
判定「挂人」的三要件(同时成立才处理):
- **可识别性**:能让圈内人定位到具体账号/作品/主页(昵称、代称、@、外链、作品标签)
- **贬损或号召规避**:带「避雷/挂/别信/辱骂」这类定性,而不是中性描述
- **无公共利益**:纯私人纠纷、粉籍立场之争
只成立一两条 → 不处理或仅提示。
---
## 8. 排错
| 现象 | 原因 | 处理 |
|---|---|---|
| `No help topic for 'xxx'` | 构建入口不同步(根 `tamabox.go` vs `cmd/tamabox.go`) | 两处都注册后重新编译 |
| `find user: record not found` | `--domain` 填了昵称/登录名 | domain 是 URL 里的那一段,`/_/{domain}` |
| 处置后页面无变化 | 二进制是旧的 / 服务没重启 | `--help` 看命令在不在;抓线上 HTML 比对标记 |
| 待处理列表是空的 | 举报已被箱主处理(自动 accepted)或超过 30 天 | 正常;必要时直接查库 |
| 告警邮件一直没收到 | `[security] security_alert_email` 为空(静默跳过,无日志) | 配好并**重启** |
| 举报数始终是 1 | `trust_proxy` 未按部署环境配置,全部识别为代理 IP | 见 §2.4 |
| 箱主说「我改了但没生效」 | 处置期内严格度被锁定 / L2 禁止设为公开 | 看后台条幅;`reports` 查当前处置 |
改完配置**必须重启**才生效(配置只在启动时读取一次)。
---
## 9. 明确不做的事
- **不做自动处置**:举报数达标只发邮件,不自动封禁。这个场景里举报本身就是攻击武器。
- **不做申诉后台**:申诉走邮件,处置结果由 CLI 写入。
- **不用配置文件当黑名单**:无法表达「N 天后自动解除」,最终会变成手工维护灾难。
- **不要在 CLI 之外直接改库**:`user_restrictions` 的写入必须带 `revoked_at` 语义,
手工 UPDATE 容易留下多条同时生效的记录(读取端会取最高级别,但不便于审计)。