Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d3a2fb240 | |||
| e6d12e0417 | |||
| b52f8a9b99 | |||
| bb1699e9fd | |||
| a46e43b73c |
@@ -2,289 +2,77 @@
|
||||
|
||||
> 端到端加密文件传输 | 零知识 + 零合并 | 服务端无密钥不解密
|
||||
|
||||
**发行版下载 (deb 安装包):** Gitee [发行版 v1.0.0](https://gitee.com/edgevoid/7z-encrypt/releases) · Gitea [Releases v1.0.0](http://8.136.202.225:3000/edgevoid/7z-encrypt/releases)
|
||||
**发行版下载 (deb 安装包, 依赖自动安装):** Gitee [Releases v1.3.0](https://gitee.com/edgevoid/7z-encrypt/releases) · Gitea [Releases v1.3.0](http://8.136.202.225:3000/edgevoid/7z-encrypt/releases)
|
||||
|
||||
端到端加密 + 最高压缩 + 分卷传输的私有文件网盘。自托管、零知识、零合并——服务器只存密文卷,既看不到内容,也看不到文件名。
|
||||
自托管、零知识、零合并的私有文件网盘。服务器只存密文卷——看不到内容,也看不到文件名。
|
||||
|
||||
```
|
||||
本地 服务器 (你自有的任何机器) 本地
|
||||
文件 ──7z -mx=9 压缩──> ┌───────────────────────┐ ──逐卷下载──> 密文
|
||||
│ │ 只存密文卷, 不合并 │ │
|
||||
├─ cobblestone 加密 ─>│ 不持密钥, 不解密 │ ├─ 解密
|
||||
├─ 分卷 + SHA-256 ───>│ 不存明文文件名 │ ├─ 7z 解压
|
||||
└─ 分片上传(Bearer) ─>│ SQLite 元数据 │ └─ 还原原文件
|
||||
└───────────────────────┘
|
||||
本地 服务器 (任何自有机器) 本地
|
||||
文件 ──32MB 切卷──> ┌───────────────────────────┐ ──aria2 并发拉卷──> 密文卷
|
||||
│ │ 只存密文卷, 零合并不拼接 │ │
|
||||
├─ 逐卷 7z ─────>│ 不持密钥, 不解密 │ ├─ 逐卷解密 (context=vol:N)
|
||||
├─ 逐卷加密 ────>│ 不存明文文件名 │ ├─ 7z 解压
|
||||
└─ aria2 反向拉─>│ SQLite 元数据 + 目录管理 │ └─ 按序拼接还原
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
- **传输协议**: HTTPS + REST 分片上传(卷 = 分片,失败只重传该卷)
|
||||
- **加密**: cryptography.cobblestone(C2SP chunked AES-256-GCM,16KiB 块级流式 + 块级认证)
|
||||
- **压缩**: 7z -mx=9 最高压缩(文本类文件实测 95% 压缩率,传输量大幅下降)
|
||||
- **架构**: 客户端 / 服务端 / TUI 三项目解耦,客户端只依赖共享 protocol 契约
|
||||
## 核心设计
|
||||
|
||||
---
|
||||
- **零知识**: 密钥永不出客户端。AES-256-GCM(C2SP chunked 流式认证加密, 16KiB 分块)
|
||||
- **零合并**: 服务端不存在解密/拼接代码路径, 密文卷原样落盘(怎么传的怎么放), 物理上无明文还原能力
|
||||
- **卷级加密**: 32MB 统一切卷, 每卷 nonce 由 `context=7z-encrypt:v1:vol:N` 派生——加密参数零存储, 下载端按卷号重建
|
||||
- **逐卷压缩**: 可压缩格式逐卷 7z mx=9; 已压缩格式(apk/zip/图片/视频黑名单)跳过二次压缩直传
|
||||
- **aria2 全链路**: 上传走服务端反向拉取(客户端临时 HTTP 19000-19100)+ pull-status 实时进度; 下载 8 并发 × 4 连接/卷
|
||||
- **完整性**: 逐卷 SHA-256 校验 + complete 总哈希双校验
|
||||
- **账户隔离**: 一个账户一个空间, 跨账户 403 硬拒绝
|
||||
- **文件名加密**: 元数据文件名也加密(enc: URL-safe base64)
|
||||
|
||||
## 特色
|
||||
|
||||
### 1. 零知识 — 服务器没有密钥
|
||||
|
||||
服务端不持有任何密钥材料,不解密、不碰明文。密文卷上传 → 密文卷存储 → 密文卷下载,客户端本地完成全部解密。
|
||||
|
||||
```
|
||||
服务端被攻破 / 管理员偷看 / 服务器被扣押 → 手里只有密文
|
||||
文件内容 + 密钥都在你手里, 数据安全不依赖服务器可信度
|
||||
```
|
||||
|
||||
### 2. 零合并 — 服务器不做任何合并操作
|
||||
|
||||
上传的卷直接落盘(`chunk_0001..N`),complete 只校验卷齐 + 目录 move 入库,**秒回**。下载时客户端逐卷拉取、本地拼接。
|
||||
|
||||
```
|
||||
传统方案: 上传 100% → 等服务端合并大文件 (2G 要几分钟)
|
||||
本方案: 上传 100% → 秒回 done (6 卷实测全流程 1.1 秒)
|
||||
服务端 CPU 几乎为零: 收卷存卷、发卷, 纯 IO 转发
|
||||
```
|
||||
|
||||
### 3. 元数据零知识 — 文件名也加密
|
||||
|
||||
文件名用独立 context 加密成 URL-safe base64(`enc:` 前缀),服务器只见密文。客户端 ls / 下载时本地解密还原原名。
|
||||
|
||||
```
|
||||
服务端能看到: 密文文件名 / 大小 / 卷数 / 卷哈希 (完整性校验用)
|
||||
服务端看不到: 明文文件名 / 文件内容
|
||||
```
|
||||
|
||||
### 4. 压缩 → 加密 → 分卷 → 上传 管线
|
||||
|
||||
```
|
||||
源文件 → 7z -mx=9 → .7z → cobblestone 加密 → 密文 → 分卷 → 逐卷上传
|
||||
下载反向: 逐卷拉取 → 本地拼接密文 → 解密 → 7z 解压 → 原文件
|
||||
```
|
||||
|
||||
先压缩再加密(密文不可压缩,顺序反了压缩率归零)。文本实测 3.8MB → 0.2MB,传输量省 95%。
|
||||
|
||||
### 5. 卷粒度断点续传
|
||||
|
||||
每卷独立 PUT + 独立 SHA-256 校验。中断后 `GET /chunks` 查已收卷,只补传缺失卷,天然续传。
|
||||
|
||||
### 6. 认证 + 配额 + 文件管理
|
||||
|
||||
- Bearer token 认证(预共享,无/错 token 一律 401)
|
||||
- 空间配额查询(已用 / 配额 / 剩余 / 百分比)
|
||||
- ls / 下载 / 删除全套文件管理
|
||||
- 下载按卷粒度进度显示,与上传日志对称
|
||||
|
||||
### 7. 全面进度可视化 (tqdm)
|
||||
|
||||
上传卷条、下载双层嵌套条(外层卷 / 内层字节)、加密条、分卷条、解密条——所有耗时操作都有实时进度,拒绝黑盒等待。
|
||||
|
||||
### 8. 轻量部署 — SQLite + 标准库优先
|
||||
|
||||
服务端 Python 标准库 + FastAPI,数据库 SQLite(零进程零安装)。PRoot 容器(手机 ZeroTermux)、树莓派、任何 VPS 都能跑。
|
||||
|
||||
### 9. 输入安全加固
|
||||
|
||||
- 下载文件名消毒(basename,防路径穿越)
|
||||
- init json 大小限制 1MB / 卷数上限 10000 / 文件名清洗
|
||||
- 所有 SQL 参数化,token 不落库
|
||||
|
||||
### 10. 密钥系统级管理
|
||||
|
||||
- 密钥默认存系统标准位置 `~/.local/share/7z-encrypt/keyring.json`(XDG),环境变量 `SZ_KEYRING` 可指定;旧版位置自动迁移
|
||||
- 密钥导出/导入(TUI 菜单 7/8):红色风险警告 + 二次确认(yes/CONFIRM),导入自动备份 `keyring.json.bak`,导出文件权限 600
|
||||
- 重新生成密钥(TUI 菜单 12):红色警告 + 二次确认,旧密文永久不可解(先导出备份)
|
||||
- 换机迁移:旧机导出 → 新机导入 → 直接解密旧密文
|
||||
|
||||
### 11. 账号体系
|
||||
|
||||
- 注册 / 登录 / 注销(TUI 菜单 9/10/11 或 CLI `--register/--login/--logout`)
|
||||
- **登录门禁**: TUI 启动先验证登录, 不登录不能进行文件操作 (只有登录/注册/配置)
|
||||
- 密码 pbkdf2-hmac-sha256 哈希存储(20 万轮迭代,不落明文),登录 token 随机 32 字节
|
||||
- 文件按用户隔离:注册用户只见自己的文件,admin(SZ_TOKEN)可见全部
|
||||
- 登录后 token 自动保存到 config.json,注销即失效
|
||||
|
||||
---
|
||||
|
||||
## 与百度网盘对比
|
||||
|
||||
| 维度 | 百度网盘 | 7z-encrypt |
|
||||
|---|---|---|
|
||||
| **数据安全** | 服务器持有明文,可扫描/审查/和谐;VIP 可享"原画"也意味着服务器能看 | 端到端零知识:服务器只有密文,无密钥无法解密,内容+文件名双重加密 |
|
||||
| **速度** | 非会员限速 ~100KB/s,会员才跑满 | 自己服务器的带宽,不限速;7z 压缩省 95% 传输量 |
|
||||
| **空间** | 免费 2TB 但限速/限单文件大小,扩容要付费 | 服务器磁盘即空间,配额自定(环境变量 SZ_QUOTA) |
|
||||
| **费用** | 会员 30+/月,续费永无止境 | 自托管零月费,一台旧手机/小 VPS 即可 |
|
||||
| **隐私** | 内容会被 AI 扫描、分享链接可被举报和谐 | 零知识架构,服务器管理员都看不到内容 |
|
||||
| **审查** | 敏感内容秒和谐,资源类文件风险高 | 无审查无和谐,纯私有传输 |
|
||||
| **广告** | 客户端广告、推广位、全家桶 | 无广告,无推广 |
|
||||
| **文件操作** | 上传/下载受客户端控制,断点续传要会员 | 卷粒度断点续传免费,上传失败只重传缺失卷 |
|
||||
| **可控性** | 关停风险、政策风险、客户端强制更新 | 完全自托管,代码开源,随时审计 |
|
||||
| **部署** | 不可自部署 | 单文件服务端,SQLite,手机容器都能跑 |
|
||||
| **扩展** | 生态封闭,API 受限 | REST API 全开放,可编程上传/下载 |
|
||||
| **成本对照** | 100GB 文件传一年会员费 ≈ 400 元 | 0 元软件成本 + 自己的服务器电费 |
|
||||
|
||||
**一句话**: 百度网盘是"存给百度看的网盘",7z-encrypt 是"只存密文的管道"——前者买的是空间,后者买的是隐私和速度。
|
||||
|
||||
---
|
||||
|
||||
## 快速开始
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
# 1. 服务端 (任何有 Python 3.11+ 的机器)
|
||||
cd server
|
||||
pip install fastapi uvicorn cryptography tqdm
|
||||
SZ_TOKEN=你的token python3 main.py # 默认 0.0.0.0:8000
|
||||
# deb 安装 (postinst 自动安装 aria2/p7zip/venv 依赖)
|
||||
sudo dpkg -i 7z-encrypt-client_1.0.0_all.deb # 源码版 (手机/Termux)
|
||||
sudo dpkg -i 7z-encrypt-client-bin_1.0.0_amd64.deb # Nuitka 编译版 (PC, 免 Python)
|
||||
|
||||
# 2. 客户端
|
||||
cd 7z-encrypt
|
||||
uv pip install --python .venv/bin/python tqdm
|
||||
python config.py --init
|
||||
python config.py --set-server http://服务器IP:8000
|
||||
python config.py --set-token 你的token
|
||||
# 密钥库位置 (系统级): 默认 ~/.local/share/7z-encrypt/keyring.json
|
||||
# 环境变量 SZ_KEYRING 可指定; 旧版 config/keyring.json 自动迁移
|
||||
# 密钥导出/导入: TUI 菜单 7/8 (红色警告+二次确认)
|
||||
|
||||
# 3. TUI (推荐)
|
||||
python3 ../tui/scripts/tui.py
|
||||
# 启动先登录门禁 (不登录不能操作文件) -> 主菜单:
|
||||
# 1上传 / 2任务续传 / 3配置 / 4文件列表 / 5下载 / 6删除
|
||||
# 7密钥导出 / 8密钥导入 / 9注册 / 10登录 / 11注销 / 12重新生成密钥 / q退出
|
||||
# 源码运行
|
||||
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 原生编译 (Nuitka)
|
||||
命令: `sz-transfer` (CLI) / `sz-tui` (终端界面) / `sz-config` (配置) / `sz-backup` (一键备份)
|
||||
|
||||
把 Python 转成 C 再用 gcc 编译成原生二进制,无需解释器依赖(onefile 单文件自包含):
|
||||
**完整 CLI 使用文档: [docs/CLI.md](docs/CLI.md)** (上传/下载/备份/目录/账号/示例)
|
||||
|
||||
```bash
|
||||
# 客户端 CLI (含 cryptography/tqdm 打包)
|
||||
uv pip install --python .venv/bin/python nuitka
|
||||
.venv/bin/python -m nuitka --onefile --follow-imports \
|
||||
--include-package=cryptography --include-package=tqdm \
|
||||
--output-dir=bin --output-filename=sz-transfer transfer.py
|
||||
./bin/sz-transfer --server http://127.0.0.1:8000 --whoami
|
||||
## 目录管理
|
||||
|
||||
# 注意: cryptography 是 C 扩展, 必须 --include-package=cryptography
|
||||
# 否则运行时报 ModuleNotFoundError: cryptography.hazmat.primitives
|
||||
```
|
||||
sz-transfer --dir-create 相册备份 # 创建目录 (按用户隔离)
|
||||
sz-transfer --dir-list # 列表 (含每目录文件数)
|
||||
sz-transfer --dir-delete 1 # 删除目录
|
||||
sz-transfer --init a.init.json --manifest a_manifest.json --dir 1 # 上传到指定目录
|
||||
sz-transfer --move 1 2 # 移动文件到目录 (0=根)
|
||||
sz-transfer --clone 1 2 # 克隆文件 (物理复制卷 + 新记录)
|
||||
sz-transfer --list # 文件列表 (目录 + [目录名] 归属标记)
|
||||
```
|
||||
|
||||
TUI 自动优先调用编译产物(`bin/sz-transfer`),无则回退源码解释执行。
|
||||
## 一键备份
|
||||
|
||||
## Termux / 手机安装 (deb 包)
|
||||
|
||||
适配 PRoot Ubuntu / Termux 的安装包(配置走 XDG,`cwd` 无关):
|
||||
|
||||
```bash
|
||||
# 构建 (需 debhelper): dpkg-buildpackage -us -uc -b
|
||||
# 生成 ../7z-encrypt-client_1.0.0_all.deb (源码版, 手机用)
|
||||
# ../7z-encrypt-client-bin_1.0.0_amd64.deb (x86_64 编译版)
|
||||
|
||||
# 手机上 (ZeroTermux PRoot Ubuntu) 安装源码版:
|
||||
apt install -y python3 python3-venv p7zip-full
|
||||
dpkg -i 7z-encrypt-client_1.0.0_all.deb # postinst 自动建 venv + 装 cryptography/tqdm
|
||||
|
||||
# x86_64 本机安装编译版 (Nuitka 原生, 免 Python):
|
||||
sudo dpkg -i 7z-encrypt-client-bin_1.0.0_amd64.deb # 依赖 p7zip-full
|
||||
|
||||
# 使用 (两版相同):
|
||||
sz-config --init # 首次: 生成 ~/.config/7z-encrypt/config.json
|
||||
sz-config --set-server http://服务器:8000
|
||||
sz-config --set-token 你的token
|
||||
sz-transfer --list # CLI (服务器/token 自动读配置)
|
||||
sz-tui # TUI (登录门禁, 中文菜单)
|
||||
|
||||
# 换机密钥: 旧机导出 keyring -> 手机 ~/.local/share/7z-encrypt/keyring.json (权限 600)
|
||||
```
|
||||
|
||||
依赖: 源码版 `python3 >= 3.10` + `cryptography >= 50.0.0`(cobblestone 流式加密,postinst 自动装最新版);编译版无 Python 依赖。两版都需要 `p7zip-full`(7z 压缩)。
|
||||
|
||||
## 一键备份 (手机特色功能)
|
||||
|
||||
相册 / 文档 / 下载 / 聊天记录一键备份到私有加密服务器(增量,只传新文件):
|
||||
|
||||
```bash
|
||||
sz-backup # 备份全部 (photos+documents+download+chat)
|
||||
sz-backup photos # 只备份相册
|
||||
sz-backup list # 已备份清单
|
||||
sz-backup restore photos # 恢复相册
|
||||
sz-backup photos # 整个文件夹 = 一个文件: tar → 32MB 卷压缩 → 逐卷加密 → aria2 上传
|
||||
sz-backup documents # 同上 (不分批, 全量单文件)
|
||||
sz-backup restore photos # 下载 → 自动还原 → 解包
|
||||
```
|
||||
|
||||
- 增量: mtime+size 对比本地清单, 只传新增/修改文件; 批量 tar 打包 (500MB/批)
|
||||
- 源目录默认: photos=/sdcard/DCIM+Pictures, documents=/sdcard/Documents,
|
||||
download=/sdcard/Download; 可在 config.json `backup` 段自定义
|
||||
- **聊天记录**: PRoot 无 root 读不到微信数据库 (/data/data), 需先微信"备份与迁移/
|
||||
导出"到手机存储, 再把目录配到 config.json backup.chat
|
||||
- TUI 菜单 13 一键备份 / 14 恢复备份
|
||||
|
||||
## aria2 加速 (打满宽带)
|
||||
|
||||
安装 aria2 后上传/下载自动多连接并发(无 aria2 自动回退单线程):
|
||||
|
||||
```bash
|
||||
sudo apt install aria2 # PC / 服务端 / 手机都要装
|
||||
```
|
||||
|
||||
- **下载**: 卷 2..N 由 aria2 并发拉取(多任务并行, 局域网实测 22MB/s+)
|
||||
- **上传**: 客户端起临时 HTTP 服务 (端口 19000-19099, PC 需 `sudo ufw allow 19000:19100/tcp`),
|
||||
服务端 aria2 并发反向拉卷, 逐卷 SHA-256 校验落盘
|
||||
- 任一步失败自动回退原逐卷逻辑, 不影响使用
|
||||
|
||||
## Releases 下载
|
||||
|
||||
Gitee / Gitea 仓库 Releases 页下载 deb 包(tag v1.0.0):
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
cd 7z-encrypt && .venv/bin/pytest -q # 71 项
|
||||
cd server && .venv/bin/pytest -q # 12 项
|
||||
.venv/bin/pytest -q # 71 tests
|
||||
```
|
||||
|
||||
覆盖:加密/解密回环、7z 压缩回环、逐卷上传下载、断点续传、token 认证、文件名加密回环、路径穿越防护、配额、幂等、坏卷拒绝、超大清单拒绝、密钥路径 env/XDG 解析、注册/登录/注销、用户文件隔离。
|
||||
## 部署
|
||||
|
||||
---
|
||||
- 服务端: [7z-encrypt-server](https://gitee.com/edgevoid/7z-encrypt-server)(独立仓库, 含 deb 双包 + 依赖自动安装)
|
||||
- 终端界面: [7z-encrypt-tui](https://gitee.com/edgevoid/7z-encrypt-tui)
|
||||
|
||||
## 目录结构
|
||||
## 代价与适用场景
|
||||
|
||||
```
|
||||
7z-encrypt/ 客户端 (标准库优先)
|
||||
├─ file_reader.py 流式读取
|
||||
├─ crypto.py 加密/解密/文件名加密 (cobblestone AES-256-GCM)
|
||||
├─ splitter.py 分卷 + SHA-256
|
||||
├─ metadata.py 元数据 (init json / 卷清单)
|
||||
├─ transfer.py HTTP 传输 (上传/下载/删除/配额, Bearer 认证)
|
||||
├─ state.py 断点状态
|
||||
├─ config.py 配置 (server/token/keyring)
|
||||
└─ tests/ 70 项测试
|
||||
|
||||
server/ 服务端 (FastAPI + SQLite, 零知识零合并)
|
||||
├─ api.py REST 端点 (认证/传输/文件/配额)
|
||||
├─ receiver.py 卷接收 + 哈希校验
|
||||
├─ storage.py 卷目录存储 (move 入库, 不合并)
|
||||
├─ db.py SQLite (transfers/files/chunks)
|
||||
├─ task_manager.py 任务状态机 + 输入清洗
|
||||
└─ tests/ 9 项测试
|
||||
|
||||
tui/ 终端界面
|
||||
└─ scripts/tui.py Python TUI (input() REPL, 原生支持中文输入法)
|
||||
```
|
||||
|
||||
## 协议一览
|
||||
|
||||
```
|
||||
POST /api/transfer/init 建任务 {transfer_id}
|
||||
PUT /api/transfer/{id}/chunk/{n} 上传单卷密文 409 = 坏卷
|
||||
GET /api/transfer/{id}/chunks 已收卷列表 断点续传
|
||||
POST /api/transfer/{id}/complete 卷齐入库(秒回) {file_id}
|
||||
GET /api/files 文件列表 文件名是加密的
|
||||
GET /api/files/{id}/chunk/{n} 下载单卷密文 X-Enc-Params 头带解密参数
|
||||
DELETE /api/files/{id} 删除文件
|
||||
GET /api/quota 空间配额
|
||||
全部端点需 Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Python 3.11+ / FastAPI / uvicorn / SQLite
|
||||
- cryptography 50 (cobblestone C2SP chunked AES-256-GCM)
|
||||
- 7z (p7zip, -mx=9)
|
||||
- tqdm 进度条
|
||||
- 测试: pytest (真实行为测试, 无 mock)
|
||||
自持硬件可用性(无 SLA/异地容灾)、无 Web/移动 App(CLI + TUI)、密钥丢失不可恢复。适合对数据主权有硬性要求的场景: 备份、涉密归档、跨设备私有传输。
|
||||
|
||||
@@ -110,16 +110,21 @@ def _cleanup(tar_path: Path) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def backup(name: str, chunk_mb: int = VOL_MB) -> int:
|
||||
def backup(name: str, chunk_mb: int = VOL_MB, srcs: list[Path] | None = None) -> int:
|
||||
"""整个文件夹 -> 单个 tar -> 32MB 卷压缩 (逐卷 7z + 逐卷加密) -> aria2 上传
|
||||
|
||||
一个文件夹 = 一个文件 (file_id), 不分批次。
|
||||
srcs 为 None 时用预设/配置源目录 (photos/documents/...), 显式传入则
|
||||
备份指定目录 (PC 端任意路径, 如 /home/lou/图片)。
|
||||
"""
|
||||
from metadata import _build_vol_init
|
||||
import argparse as _ap
|
||||
from transfer import TransferClient
|
||||
|
||||
srcs = [s for s in _src_dirs(name) if s.exists()]
|
||||
if srcs is None:
|
||||
srcs = [s for s in _src_dirs(name) if s.exists()]
|
||||
else:
|
||||
srcs = [s for s in srcs if s.is_dir()]
|
||||
files = [p for s in srcs for p in s.rglob("*") if p.is_file()]
|
||||
if not files:
|
||||
print(f"[备份] {name}: 源目录为空")
|
||||
@@ -220,9 +225,10 @@ def list_backups() -> int:
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="一键备份: 相册/聊天记录/文档 -> 加密服务器")
|
||||
ap = argparse.ArgumentParser(description="一键备份: 相册/聊天记录/文档/指定目录 -> 加密服务器")
|
||||
ap.add_argument("action", nargs="?", default="all", help="备份项: photos/documents/download/chat/all/list/restore")
|
||||
ap.add_argument("target", nargs="?", help="restore 的备份项")
|
||||
ap.add_argument("--dir", help="备份指定目录 (不限于预设项, 如 /home/lou/图片)")
|
||||
ap.add_argument("--chunk-size", type=int, default=VOL_MB, help=f"卷压缩 MB (默认 {VOL_MB})")
|
||||
args = ap.parse_args()
|
||||
|
||||
@@ -230,9 +236,15 @@ def main() -> int:
|
||||
return list_backups()
|
||||
if args.action == "restore":
|
||||
if not args.target:
|
||||
print("[错误] 用法: sz-backup restore <photos|documents|download|chat>")
|
||||
print("[错误] 用法: sz-backup restore <备份项名>")
|
||||
return 1
|
||||
return restore(args.target)
|
||||
if args.dir:
|
||||
p = Path(args.dir).expanduser()
|
||||
if not p.is_dir():
|
||||
print(f"[错误] 目录不存在: {p}")
|
||||
return 1
|
||||
return backup(p.name, args.chunk_size, srcs=[p])
|
||||
names = ["photos", "documents", "download", "chat"] if args.action == "all" else [args.action]
|
||||
if args.action not in ("photos", "documents", "download", "chat", "all"):
|
||||
print(f"[错误] 未知备份项: {args.action} (photos/documents/download/chat/all)")
|
||||
|
||||
Vendored
+4
-3
@@ -3,14 +3,15 @@ set -e
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
echo "正在安装系统依赖 (aria2 + p7zip-full) ..."
|
||||
apt-get install -y aria2 p7zip-full >/dev/null 2>&1 || true
|
||||
echo "正在安装系统依赖 (aria2 + p7zip-full + python3-venv) ..."
|
||||
apt-get install -y aria2 p7zip-full python3-venv >/dev/null 2>&1 || true
|
||||
P=/usr/share/7z-encrypt/.venv
|
||||
if [ ! -x "$P/bin/python" ]; then
|
||||
echo "正在创建 7z-encrypt 运行环境 (venv + cryptography/tqdm) ..."
|
||||
python3 -m venv "$P"
|
||||
"$P/bin/pip" install --quiet --upgrade pip || true
|
||||
"$P/bin/pip" install --quiet cryptography tqdm
|
||||
"$P/bin/pip" install --quiet cryptography tqdm \
|
||||
|| "$P/bin/pip" install --quiet -i https://pypi.tuna.tsinghua.edu.cn/simple cryptography tqdm
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
# 7z-encrypt CLI 使用文档
|
||||
|
||||
端到端加密文件传输: 加密 -> 分卷 -> 上传 (零知识服务端只存密文)。
|
||||
|
||||
安装后提供 4 个命令:
|
||||
|
||||
| 命令 | 用途 |
|
||||
|---------------|-----------------------------------|
|
||||
| `sz-transfer` | 上传 / 下载 / 列表 / 删除 / 目录 / 账号 |
|
||||
| `sz-config` | 首次配置 (服务器 / token / 密钥轮换) |
|
||||
| `sz-backup` | 一键备份 (预设项或指定目录) |
|
||||
| `sz-tui` | 交互式终端界面 (菜单 1-17) |
|
||||
|
||||
---
|
||||
|
||||
## 0. 首次配置
|
||||
|
||||
```bash
|
||||
sz-config --init # 生成默认配置模板 (~/.config/7z-encrypt/config.json)
|
||||
sz-config --set-server http://192.168.10.133:8000
|
||||
sz-config --set-token <TOKEN> # 与服务端 SZ_TOKEN 一致, 或用账号登录获取
|
||||
```
|
||||
|
||||
配置保存在 `~/.config/7z-encrypt/config.json`, 包含 server.url / server.token /
|
||||
chunk_size / retries 等。`sz-transfer` 所有命令默认读这份配置, 可用 `--server`
|
||||
`--token` 临时覆盖。
|
||||
|
||||
密钥库 (加密/解密唯一凭证) 在 `~/.local/share/7z-encrypt/keyring.json`, 由
|
||||
`sz-config --init` 自动生成。**密钥丢失 = 所有密文永久无法解密**, 请用
|
||||
`sz-tui` 菜单 7 导出备份。
|
||||
|
||||
---
|
||||
|
||||
## 1. sz-transfer 文件操作
|
||||
|
||||
### 上传
|
||||
|
||||
```bash
|
||||
sz-transfer --init <文件>.init.json --manifest <文件>_manifest.json
|
||||
```
|
||||
|
||||
通常不需要手动生成 init/manifest: 完整上传管线 = 压缩 -> 加密 -> 分卷 -> 元数据
|
||||
-> 上传, 推荐走 TUI (菜单 1) 或以下手工三步:
|
||||
|
||||
```bash
|
||||
# 1. 卷压缩 + 逐卷加密 (32MB 切卷, 可压缩格式逐卷 7z, 已压缩格式直加密)
|
||||
python metadata.py 文件路径 --chunk-size 32 --vol-compress 32 --file-name 原名
|
||||
|
||||
# 2. 上传 (init json + manifest 是上一步的产物)
|
||||
sz-transfer --init 文件路径.init.json --manifest 文件路径_manifest.json
|
||||
```
|
||||
|
||||
上传到指定目录: `sz-transfer --dir <DIR_ID> --init ... --manifest ...`
|
||||
(目录 id 用 `--dir-list` 查看)。
|
||||
|
||||
### 下载
|
||||
|
||||
```bash
|
||||
sz-transfer --list # 查看文件 (序号. 文件名 (大小))
|
||||
sz-transfer --download 3 # 按列表序号下载
|
||||
sz-transfer --download <FILE_ID> # 或直接 file_id
|
||||
sz-transfer --download 3 --out /tmp/xxx
|
||||
sz-transfer --download 3 --decrypt # 下载后本地解密还原明文 (默认加密名/密文)
|
||||
```
|
||||
|
||||
- `--out` 可指定目录或完整文件名, 缺省保存在当前目录
|
||||
- 大文件自动走 aria2 并发拉卷, 每卷独立进度条
|
||||
- 文件名/序号通用: 纯数字 = 列表序号, 其他 = file_id
|
||||
|
||||
### 列表 / 删除 / 配额
|
||||
|
||||
```bash
|
||||
sz-transfer --list # [目录] 区块 + [文件] 区块, 每文件带 [目录名] 归属
|
||||
sz-transfer --delete 3 # 按序号删除 (先 --list 确认再删)
|
||||
sz-transfer --delete <FILE_ID>
|
||||
sz-transfer --quota # 已用 x MB / 配额 y MB (pct%) 剩余 z MB
|
||||
```
|
||||
|
||||
### 目录管理
|
||||
|
||||
```bash
|
||||
sz-transfer --dir-create 相册备份 # 创建目录 (按用户隔离, 重名 409)
|
||||
sz-transfer --dir-list # 列出目录 (序号. 名称 (N 个文件))
|
||||
sz-transfer --dir-delete 1 # 删除目录
|
||||
sz-transfer --move 3 2 # 文件序号 3 移到目录 2 (0 = 根目录)
|
||||
sz-transfer --clone 3 2 # 克隆文件序号 3 到目录 2 (物理复制)
|
||||
```
|
||||
|
||||
### 账号
|
||||
|
||||
```bash
|
||||
sz-transfer --register 用户名 密码 # 注册 (成功即登录, token 写入配置)
|
||||
sz-transfer --login 用户名 密码 # 登录 (token 写入配置)
|
||||
sz-transfer --logout # 注销 (服务端 token 失效 + 本地清空)
|
||||
sz-transfer --whoami # 显示当前登录用户
|
||||
```
|
||||
|
||||
每设备用各自账号 token (勿统一配 admin, 否则文件全混进 admin 空间)。
|
||||
|
||||
---
|
||||
|
||||
## 2. sz-config
|
||||
|
||||
```bash
|
||||
sz-config --init # 生成配置模板 (已存在不覆盖)
|
||||
sz-config --set-server URL # 更新服务器地址
|
||||
sz-config --set-token TOKEN # 更新 token
|
||||
sz-config --regenerate-key # 重新生成密钥 (交互确认; --yes 跳过确认)
|
||||
```
|
||||
|
||||
`--regenerate-key` 会删除旧密钥并生成新密钥: **旧密钥加密的已上传文件将永久
|
||||
无法解密**, 操作前请先导出密钥备份 (TUI 菜单 7)。
|
||||
|
||||
---
|
||||
|
||||
## 3. sz-backup 一键备份
|
||||
|
||||
备份 = 整个文件夹 -> 单个 tar -> 32MB 卷压缩 (逐卷 7z mx=1 + 逐卷加密) -> 上传,
|
||||
一个文件夹对应一个服务端文件 (file_id), 增量机制只传新增/修改的文件。
|
||||
|
||||
```bash
|
||||
sz-backup # 备份全部预设项 (photos + documents + download + chat)
|
||||
sz-backup photos # 只备份相册
|
||||
sz-backup download # 只备份下载目录
|
||||
sz-backup --dir /home/lou/图片 # 备份指定目录 (PC 端任意路径)
|
||||
sz-backup --dir /home/lou/图片 --chunk-size 16 # 指定卷大小
|
||||
sz-backup list # 查看已备份清单
|
||||
sz-backup restore 图片 # 恢复指定目录备份 (还原到原路径)
|
||||
sz-backup restore photos # 恢复预设项
|
||||
```
|
||||
|
||||
预设项默认源目录 (config.json 的 backup 段可覆盖):
|
||||
|
||||
| 项 | 默认源目录 |
|
||||
|-------------|-------------------------------------------------|
|
||||
| photos | ~/storage/dcim, ~/storage/pictures, /sdcard/DCIM, /sdcard/Pictures |
|
||||
| documents | /sdcard/Documents |
|
||||
| download | /sdcard/Download |
|
||||
| chat | 需在 config.json backup.chat 配置 (微信导出目录) |
|
||||
|
||||
`--dir` 备份的恢复用目录名 (`sz-backup restore <目录名>`)。
|
||||
|
||||
---
|
||||
|
||||
## 4. sz-tui 交互界面
|
||||
|
||||
```bash
|
||||
sz-tui
|
||||
```
|
||||
|
||||
启动后先验证登录状态: 未登录只显示 登录/注册/配置, 登录后显示完整菜单:
|
||||
|
||||
```
|
||||
1. 上传文件 10. 登录
|
||||
2. 任务续传 11. 注销
|
||||
3. 配置 12. 重新生成密钥
|
||||
4. 文件列表 (ls) 13. 一键备份 (相册/文档/下载/指定目录)
|
||||
5. 下载文件 14. 恢复备份
|
||||
6. 删除文件 15. 目录管理 (创建/列表/删除)
|
||||
7. 密钥导出 16. 移动文件到目录
|
||||
8. 密钥导入 17. 克隆文件
|
||||
9. 注册账号 q. 退出
|
||||
```
|
||||
|
||||
常用操作: 上传输入文件路径 (目录会提示用备份); 备份菜单可直接输入目录路径
|
||||
备份指定目录; 文件列表/删除/下载用序号操作。TUI 内部驱动 sz-transfer 实时
|
||||
透传进度, 与 CLI 完全等价。
|
||||
|
||||
---
|
||||
|
||||
## 5. 典型流程示例
|
||||
|
||||
```bash
|
||||
# 新设备接入
|
||||
sz-config --init
|
||||
sz-config --set-server http://192.168.10.133:8000
|
||||
sz-transfer --register boss 密码
|
||||
sz-transfer --whoami
|
||||
|
||||
# 备份一个目录
|
||||
sz-backup --dir /home/lou/图片
|
||||
|
||||
# 查服务器文件与配额
|
||||
sz-transfer --list
|
||||
sz-transfer --quota
|
||||
|
||||
# 下载并解密还原 (第 2 个文件)
|
||||
sz-transfer --download 2 --decrypt --out ~/下载/
|
||||
|
||||
# 删除 (确认后再删)
|
||||
sz-transfer --list
|
||||
sz-transfer --delete 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 参数速查
|
||||
|
||||
```
|
||||
sz-transfer: --init <json> --manifest <json> --dir <id>
|
||||
--list --quota --download <序号|id> --delete <序号|id>
|
||||
--out <路径> --decrypt --server <url> --token <tok>
|
||||
--dir-create <名> --dir-list --dir-delete <id>
|
||||
--move <文件> <目录|0> --clone <文件> <目录|0>
|
||||
--register <用户> <密码> --login <用户> <密码>
|
||||
--logout --whoami
|
||||
sz-config: --init --path <p> --set-server <url> --set-token <tok>
|
||||
--regenerate-key [--yes]
|
||||
sz-backup: [photos|documents|download|chat|all|list|restore <项>]
|
||||
--dir <路径> --chunk-size <MB>
|
||||
```
|
||||
|
||||
安全提示: 密钥库是唯一解密凭证, 备份/迁移请用 `sz-tui` 菜单 7/8 导出导入;
|
||||
服务端只存密文 (零知识), token 丢失/更换不影响已存文件。
|
||||
@@ -148,6 +148,8 @@ def _build_vol_init(src_path: Path, args: argparse.Namespace) -> int:
|
||||
workdir = Path(tempfile.gettempdir())
|
||||
init_path = src_path.with_name(src_path.name + ".init.json")
|
||||
manifest_path = src_path.with_name(src_path.name + "_manifest.json")
|
||||
if not src_path.is_file():
|
||||
raise ValueError(f"源路径不是文件 (目录请用备份流程): {src_path}")
|
||||
src_st = src_path.stat().st_size
|
||||
print(f"=== 卷压缩模式: {src_path.name} ({src_st / 1048576:.1f} MB), 每卷 {args.vol_compress} MB ===")
|
||||
chunks: list[dict[str, Any]] = []
|
||||
@@ -247,6 +249,9 @@ def main() -> int:
|
||||
if not src_path.exists():
|
||||
print(f"[错误] 文件不存在: {src_path}")
|
||||
return 1
|
||||
if not src_path.is_file():
|
||||
print(f"[错误] 不是文件 (目录上传请用 sz-backup 一键备份): {src_path}")
|
||||
return 1
|
||||
|
||||
# 卷压缩模式: 流式切卷 -> 逐卷 7z 压缩 -> 逐卷加密 (每卷内存 16MB 级, 规避大文件 OOM)
|
||||
if args.vol_compress > 0:
|
||||
|
||||
Reference in New Issue
Block a user