feat: 新增备份恢复与公网加固
- 新增备份管理页与 API:在线快照、自动周期备份、按份数清理、下载、一键恢复(恢复前自动兜底) - 新增 /profile/export,普通用户可导出本人全部数据(不含 Cookie 明文) - 修复 X-Forwarded-For 可伪造导致三道 IP 防线失效,统一走 client_ip() 取客户端地址 - 取消 admin123 硬编码默认口令,留空则生成随机初始口令并仅打印一次 - .dockerignore 排除 backups/ 并加构建期断言,防止密钥随镜像分发 - 新增会话版本号,改密/停用/删除及恢复备份后其他会话立即失效 - 新增容器资源上限、采集跨度硬顶 31 天、重操作最小间隔与并发 409 - 新增访问日志、HSTS 条件下发、口令黑名单、验证码抗模板匹配、instance.json 0600 - 版本号升至 1.4.0,同步更新 README、SECURITY、.env.example 与 compose 配置
这个提交包含在:
+102
-9
@@ -139,7 +139,8 @@
|
||||
| 查看概览、用量大屏 | ✅(只有你自己的数据) |
|
||||
| 查看 / 搜索数据明细、展开 Prompt | ✅(只有你自己的记录) |
|
||||
| 导出 CSV(当前筛选条件) | ✅(只有你自己的记录) |
|
||||
| 手动「立即采集一次」、按区间补采 | ✅(只采你自己的) |
|
||||
| **导出我的全部数据**(zip:记录 / 采集历史 / 审计 / 账号与配置) | ✅ **不含 Cookie 明文**,见 [10](#十个人中心) |
|
||||
| 手动「立即采集一次」、按区间补采 | ✅(只采你自己的,且有频率与跨度限制,见 [8.2](#82-手动采集你可以用)) |
|
||||
| 「补全 Prompt」「导出我的 CSV」 | ✅(只动你自己的) |
|
||||
| 查看采集运行历史 | ✅(只有你自己的) |
|
||||
| **设置定时任务频率 / 开关 / 补跑策略** | ❌ **只读** |
|
||||
@@ -147,6 +148,7 @@
|
||||
| **查看日志管理页 / 应用日志** | ❌ 403 |
|
||||
| **改实例级设置**(接口地址、开放注册、验证码策略) | ❌ |
|
||||
| **用户管理**(建号 / 停用 / 删号 / 改权限) | ❌ 403 |
|
||||
| **备份管理**:下载数据库归档、从备份恢复 | ❌ 403(恢复等于对全库数据有完整读写权,只给管理员) |
|
||||
| **整理数据库**(`VACUUM`,整库操作) | ❌ |
|
||||
|
||||
> **为什么调度不给你改**:采集策略是**整机一套**的(一台部署一个调度时刻表,
|
||||
@@ -160,9 +162,9 @@
|
||||
| 是「你的」 | 是「共用的 / 管理员管」 |
|
||||
|---|---|
|
||||
| Cookie 与 User-Agent | 接口基址与路径 |
|
||||
| 用量记录、采集历史、导出的 CSV | 采集调度时刻表与全部采集参数 |
|
||||
| 用量记录、采集历史、导出的 CSV / zip | 采集调度时刻表与全部采集参数 |
|
||||
| 个人资料、登录密码 | 是否开放自助注册、注册限额、验证码策略 |
|
||||
| — | 数据库文件本身、应用日志 |
|
||||
| — | 数据库文件本身、应用日志、**数据库备份归档** |
|
||||
|
||||
三条值得记牢:
|
||||
|
||||
@@ -172,6 +174,9 @@
|
||||
**记录条数 / 积分合计 / 最后登录时间与 IP** —— 只给「有多少」,不给「是什么」。
|
||||
- **采集只使用本人的凭证。** 系统不会拿别人的 Cookie 去替你采集(那会串号),
|
||||
所以每个账号都必须各自配一次 Cookie。
|
||||
- **但备份是管理员的能力**:管理员能下载整个数据库的归档、也能用归档覆盖回来。
|
||||
这是**运维必需**(不然数据丢了没人能救),代价是管理员对全库数据有完整的读写权。
|
||||
介意这一点的话,就自己用「导出我的全部数据」留一份,见 [10](#十个人中心)。
|
||||
|
||||
---
|
||||
|
||||
@@ -371,9 +376,17 @@ python manage.py import-creds -u alice # 想导给谁就写谁的用户名
|
||||
重扫**不会产生重复** —— 主键去重,已存在的记录按「更早的本地时间」保留。
|
||||
|
||||
> 这两个动作**只采集你自己的**数据,不会碰到别人的。
|
||||
>
|
||||
> **同一时刻只能有一个采集在跑**。重复点击会返回「忙碌」提示,这是设计如此
|
||||
> (SQLite 是单写者,并发只会互相拖慢)。等它跑完再点。
|
||||
|
||||
**三条限制**(v1.4.0 起,页面上会写出来):
|
||||
|
||||
| 限制 | 表现 | 为什么 |
|
||||
|---|---|---|
|
||||
| **有任务在跑时不能再发起** | 点「立即采集」返回「**正在采集**,请等它结束」 | SQLite 是单写者,并发采集只会互相拖慢,还可能把云端接口打得太密 |
|
||||
| **两次手动采集之间有最小间隔**(默认 60 秒) | 返回「操作太频繁,请 N 秒后再试」 | 防止反复点按钮把请求刷爆 |
|
||||
| **单次最长跨度 31 天**(约 1 个月) | 起始日期填得太早会被自动收窄,运行日志里出现一行 `[warn] 请求跨度超过上限 N 天,已自动收窄起点` | 避免一次拉取过长周期的数据(云端要分很多页,慢且容易失败) |
|
||||
|
||||
> 想补更久以前的数据?**分几次做**:比如先补 1 月,再补 2 月。
|
||||
> 跨度过长的请求不是被拒就是跑到一半超时,分段的成功率反而更高。
|
||||
|
||||
### 8.3 运行历史
|
||||
|
||||
@@ -459,14 +472,37 @@ python manage.py import-creds -u alice # 想导给谁就写谁的用户名
|
||||
|---|---|
|
||||
| 四张卡片 | 我的记录数 / 我的积分 / 采集次数 / 我的 Cookie 状态 |
|
||||
| 修改资料 | 改显示名、邮箱(**用户名只读**) |
|
||||
| 修改登录密码 | 需要原密码;改完当前会话仍然有效 |
|
||||
| 修改登录密码 | 需要原密码。**改成功后其他设备上的登录会立刻失效**(本机这次会话保留,不然改完立刻被自己踢下线) |
|
||||
| 我的采集凭证 | 是否已配置、多少字符、结尾 4 位、最后更新时间、当前调度时刻(只读) |
|
||||
| **导出我的全部数据** | 下载一个 zip,把**属于你的**东西一次带走(见下) |
|
||||
|
||||
顶栏右侧会有一个 **「普通账号」** 小标签(管理员则是「管理员」)——
|
||||
不确定自己是什么权限时看一眼这里。
|
||||
|
||||
> 卡片上的「我的积分」只统计**归属你本人的数据**,别人账号的记录不会算进来。
|
||||
|
||||
#### 导出我的全部数据
|
||||
|
||||
页头有一个「**导出我的全部数据**」按钮(也有单独一张卡片说明它在打包什么)。
|
||||
点下去会下载一个 zip,里面是:
|
||||
|
||||
| 文件 | 内容 |
|
||||
|---|---|
|
||||
| `使用记录.csv` | 你**全部**的用量明细(不受页面筛选条件影响) |
|
||||
| `采集历史.csv` | 你的采集运行记录 |
|
||||
| `操作审计.csv` | **你自己**的操作审计(别人的看不到) |
|
||||
| `我的账号与配置.json` | 用户名 / 显示名 / 邮箱 / 角色 / 状态 / 本人的采集参数(只读项也在内) |
|
||||
| `说明.txt` | 各文件的口径说明 |
|
||||
|
||||
> **里面没有 Cookie 明文。** 导出自己的数据不等于把凭证交出去 —— 凭证是密文入库的,
|
||||
> 导出接口不碰它。需要迁移凭证就在新环境重新粘一次。
|
||||
>
|
||||
> 这个动作有 **10 秒的最小间隔**(防止连点生成一堆大文件)。数据量很大时
|
||||
> 服务端是**边生成边下发**的,浏览器会先等一下再开始下载。
|
||||
|
||||
> 想留在系统里、由运维统一保管的那种备份(含所有人的数据),那是「备份管理」页的事,
|
||||
> 只有管理员能做。你的 zip 只包含你自己。
|
||||
|
||||
---
|
||||
|
||||
## 十一、管理员专属功能
|
||||
@@ -479,14 +515,20 @@ python manage.py import-creds -u alice # 想导给谁就写谁的用户名
|
||||
|
||||
| 项 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| 启用调度 | 开 | 总开关。关掉后只有手动采集会跑 |
|
||||
| 启用调度 | 开 | 总开关。关掉后只有手动采集会跑(**注意:自动备份也一起停**,见 [11.5](#115-备份管理页)) |
|
||||
| 每日时刻 | `09:00,17:00` | 逗号分隔的本地时刻。**保存即生效,不用重启** |
|
||||
| 每日时刻上限 | 6 | 最多允许几个时刻(上限 12,代码硬顶)。时刻数直接决定采集频次 |
|
||||
| 启动补跑 | 开 | 启动时把今天已错过、且还在宽限期内的时刻补采一次 |
|
||||
| 补跑宽限期 | 12 小时 | 超过多少小时就不补了 |
|
||||
| 采集最小间隔 | 60 秒 | 同一账号两次**手动**采集之间的最小间隔 |
|
||||
| 单次最长跨度 | 31 天 | 一次采集最多覆盖多少天(硬顶 31 = 1 个月,改大也没用) |
|
||||
| 采集参数 | 见 9.2 | 分页 / 回退 / 超时 / 截断 / 证书校验等 |
|
||||
|
||||
> ⚠️ **改 `schedule_times` 会清理槽位簿记**:系统只清掉「不再存在的时刻」对应的
|
||||
> 槽位标记(所有账号一起清),**不做全清** —— 全清会让全部账号在宽限期内一起重采。
|
||||
>
|
||||
> ⚠️ **把「每日时刻上限」调小**时,超出上限的那几个时刻会被一并清掉(这是有意的:
|
||||
> 上限本身就是刹车,留着超限的配置只会让人以为它还生效)。
|
||||
|
||||
### 11.2 实例级设置
|
||||
|
||||
@@ -506,6 +548,9 @@ python manage.py import-creds -u alice # 想导给谁就写谁的用户名
|
||||
> 验证码的答案只存在服务端 `captchas` 表里,5 分钟过期、用一次就删 ——
|
||||
> 所以它不会随会话 Cookie 泄漏出去。
|
||||
|
||||
> **自动备份的三个设置**(开关 / 周期 / 保留份数)不在这里,它们有自己的页面,
|
||||
> 见 [11.5 备份管理页](#115-备份管理页)。
|
||||
|
||||
### 11.3 日志管理页
|
||||
|
||||

|
||||
@@ -535,7 +580,7 @@ python manage.py import-creds -u alice # 想导给谁就写谁的用户名
|
||||
| 改显示名 | 行内直接改,点该行「保存」生效 |
|
||||
| 改权限 | 管理员 ↔ 普通 |
|
||||
| 改状态 | 启用 ↔ 停用(**停用立即生效**,不必等会话过期) |
|
||||
| 改密码 | 给忘了密码的同事重置 |
|
||||
| 改密码 | 给忘了密码的同事重置。**重置后他在所有设备上的登录立刻失效**,需重新登录 |
|
||||
| 删除 | **不可逆**,会连同该账号的用量数据与 Cookie 一起删除 |
|
||||
|
||||
列表还给出每个账号的**记录条数 / 积分合计 / 最后登录时间与 IP** —— 但**看不到内容**:
|
||||
@@ -553,6 +598,54 @@ python manage.py import-creds -u alice # 想导给谁就写谁的用户名
|
||||
|
||||
> **给同事开账号时默认选「普通」**:管理员是能停用别人账号的角色,没必要扩大。
|
||||
|
||||
### 11.5 备份管理页
|
||||
|
||||

|
||||
|
||||
> 这一页**只有管理员能看到**(普通账号导航里没有,直接敲 `/backups` 返回 403)。
|
||||
> 理由很实际:**能下载或恢复整个数据库的人,等于对全库数据有完整的读写权。**
|
||||
|
||||
**顶部四张 KPI**:现有份数 / 占用空间 / 自动备份开关 / 上次与下次自动备份时间。
|
||||
|
||||
**自动备份设置**(实例级,改完即生效):
|
||||
|
||||
| 设置 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| 开启自动备份 | 开 | 关掉后调度线程不再打新快照(已有的不会删) |
|
||||
| 备份周期 | 24 小时 | 1 ~ 720 小时。到期就打一份,**有采集在跑时跳过**,等下轮 |
|
||||
| 保留份数 | 7 | 超出的**按时间删最旧的**。只按磁盘上**真实存在**的文件算份数 |
|
||||
|
||||
**备份列表**:每行给出文件名、大小、记录条数、积分、**来源**(自动 / 手动 / 命令行 /
|
||||
恢复前的自动快照)、生成时间,以及三个按钮:
|
||||
|
||||
| 按钮 | 做什么 |
|
||||
|---|---|
|
||||
| 下载 | 把 zip 存到本地。**归档里含 `instance.json`,也就是全库的加密主密钥**,所以它本身就是最高机密 —— 别放进公开网盘、别随手发人 |
|
||||
| 恢复 | 把这份归档灌回数据库。**会先弹两次确认**(第二次专门问「是否连密钥一起回滚」) |
|
||||
| 删除 | 删掉这一份(文件 + 索引行) |
|
||||
|
||||
#### 「恢复」究竟做了什么(要点)
|
||||
|
||||
1. **先校验**这份归档能不能用;验不过就**一个字节都不动**。
|
||||
2. **自动给当前库打一份快照**(来源标 `pre-restore`)—— 恢复错了还能回去。
|
||||
3. **整表替换**记录 / 配置 / 账号等数据(在一个事务里,中途失败自动回滚)。
|
||||
4. 可选把归档里的密钥文件一起覆盖回来。
|
||||
5. **让所有人的登录立即失效** —— 恢复是全局性事件,旧会话描述的账号与权限
|
||||
可能已经整个被换掉了。
|
||||
|
||||
> **几个常见疑问**
|
||||
>
|
||||
> - **归档是怎么来的?** 不是 `cp` 出来的。走的是 SQLite 官方在线备份 API,
|
||||
> 按页复制并持有读事务 —— **采集正在写的时候拿到的也是一个完整一致的库**。
|
||||
> 直接拷文件可能缺最近一段数据,而且**不报错**。
|
||||
> - **为什么归档里要带密钥?** 不带的话,恢复出来的库里所有账号的 Cookie 都会
|
||||
> 变成「无法解密」,等于把大家的凭证弄丢。所以默认带上;不想带就在恢复时选「否」。
|
||||
> - **备份留在同一台机器上算备份吗?** 只算一半 —— 它防的是「改错了」,
|
||||
> 防不了「机器没了」。**请定期点「下载」把归档拿到别的地方去**,
|
||||
> 异地那一份是这套机制替不了你的部分。
|
||||
> - **磁盘占用怎么办?** 「保留份数」会自动清理。也可以手动点删除,
|
||||
> 或跑 `python manage.py backups --prune --keep 5`。
|
||||
|
||||
---
|
||||
|
||||
## 十二、信息安全与隐私安全
|
||||
|
||||
在新工单中引用
屏蔽一个用户