Files
tamabox/docs/cli.md
T
2026-10-19 01:58:00 +08:00

293 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 容易留下多条同时生效的记录(读取端会取最高级别,但不便于审计)。