Server 总览
cr4sync serve 是把第三方网盘 → Cloudreve 迁移能力以 HTTP API 形式暴露给多个 Cloudreve 用户的服务模式。仅在编译时启用 server cargo feature 才包含。
设计原则
- CLI 模式完全不变:CLI 仍走
./credential/token.json+./credential/sources/baidu.json+./credential/migrate.db+ toml[[storage]]。 - Server 全状态收敛到
./credential/server.db:所有 user / 凭证 / storage / migrate / audit 都在这一个 sqlite 文件,与 CLI 完全隔离,备份只需一个文件。 - cloudreve 身份强隔离(P0 不变量):server 路径上传 cloudreve 必须使用调用方 JWT,严禁读取站长
credential/token.json。静态 grep 守护测试兜底。 - 行级隔离:所有 server.db Repo 方法签名首参数必带
user_id,admin 跨用户走专门*_admin方法。 - admin 自动识别:Cloudreve
/user/info响应的data.group.name默认命中"Admin"/"管理员"任一即为 admin;可用[serve.cr4].admin_group覆盖,moka 缓存 60s。 - JWT 不本地验签:cr4sync 不持有 Cloudreve secret,每次 cache miss 走远端
/user/info/{sub}校验。 - 第三方 refresh_token 加密落 server.db:AES-256-GCM 加密, 密钥编译进二进制混淆存储, 不入 config / DB / 日志。站长拿到 DB 只看到密文。
- cloudreve refresh_token 仅内存态:随
POST /migratebody 传入,活在JobTokenState内存里,job 完成即抛弃,不入 db。
路径前缀
server 所有 API 路径前缀固定 /api/v4/cr4sync(与 [serve.cr4].server_api_path 无关——后者是连上游 Cloudreve 用的)。
embed-web 构建下还会在 /extensions/cr4sync 伺服内嵌的前端 SPA(静态资产,不鉴权)。同源部署为 cloudreve 扩展时,nginx 把 /api/v4/cr4sync/ 与 /extensions/cr4sync 两个 location 反代到 server;详见 部署。
API 速览
| Method | Path | 说明 | 鉴权 |
|---|---|---|---|
| GET | /health | 健康检查 | 无 |
| GET | / | meta(version / user_id / is_admin / banned) | ✓ |
| POST | /auth/login | 账密登录 (独立部署) | ✗ |
| POST | /auth/login/2fa | 2FA 验证 | ✗ |
| GET | /auth/captcha/status | 验证码类型与配置 | ✗ |
| GET | /auth/captcha | 图形验证码图片 + ticket | ✗ |
| POST | /auth/token/refresh | 刷新 token | ✗ |
| POST | /auth/qr/create | 创建扫码会话 | ✗ |
| GET | /auth/qr/status/:id | 扫码状态轮询 | ✗ |
| GET | /auth/qr/result/:id | 取扫码结果 | ✗ |
| GET | /sources | 支持的 driver + 用户登录态 | ✓ |
| GET/POST/PUT/DELETE | /source/:driver/storage[/{name}] | storage CRUD, driver ∈ baidu/onedrive/aliyun/pikpak | ✓ |
| POST | /source/baidu/login-session | 百度 OAuth 启动 (含 oob 授权 URL) | ✓ |
| POST | /source/baidu/login-submit[?code=] | 百度 OAuth 提交 code 或 refresh_token | ✓ |
| POST | /source/onedrive/login-session | OneDrive OAuth 启动 | ✓ |
| POST | /source/onedrive/login-submit[?code=] | OneDrive OAuth 提交 code 或 refresh_token | ✓ |
| POST | /source/aliyun/login-session | 阿里云盘 OAuth 启动 | ✓ |
| POST | /source/aliyun/login-submit[?code=] | 阿里云盘 OAuth 提交 code 或 refresh_token | ✓ |
| POST | /source/:driver/credential | S3/OSS/COS 直接凭证 upsert(非 OAuth,driver ∈ s3/oss/cos) | ✓ |
| POST | /source/pikpak/credential | PikPak 账密登录(非 OAuth,直接提交 email/password) | ✓ |
| GET | /browse/storage/{name}?type=&path=&limit=&offset= | storage 双端浏览 (源+目标), 分页 | ✓ |
| GET | /browse/storage?is_create_storage=true&type=&path=&limit=&offset= | 新建 storage 浏览 (无 storage name) | ✓ |
| GET | /source/baidu/info | 百度网盘用户信息 + 容量 | ✓ |
| GET | /source/cloudreve/info | cloudreve 源用户信息 + 容量 | ✓ |
| GET/POST/PUT/DELETE | /proxies[/{name}] | 代理 CRUD (列表/创建/更新/删除) | ✓ |
| POST | /migrate | 异步创建 migrate job | ✓ |
| GET | /migrate/jobs | 枚举当前用户 job | ✓ |
| GET | /migrate/jobs/{job_id} | 查询 job 状态 | ✓ |
| GET | /migrate/jobs/{job_id}/tasks | 分页枚举该 job 的 task | ✓ |
| GET | /migrate/jobs/{job_id}/tasks/{task_id} | 查询该 job 下单 task | ✓ |
| GET | /migrate/sse | SSE 进度推送 | ✓ |
| POST | /migrate/jobs/{job_id}/cancel | 取消 job | ✓ |
| GET | /admin/users[/...] | admin 跨用户查询 | ✓ admin |
| DELETE | /admin/users/{user_id} | 级联清用户 server.db 痕迹 | ✓ admin |
| GET/POST | /admin/jobs[/...] | admin 持久化 job 看板 | ✓ admin |
| GET | /admin/audit | 审计日志查询 | ✓ admin |
| GET | /admin/stats | 全局统计 | ✓ admin |
| GET | /admin/doc/book[/*] | 内嵌 mdbook 文档 (仅 server/*.html 校验权限) | ✗ |
详见 API 接口 各页。
模块布局
cr4sync/src/server/
├── mod.rs # serve(cfg) 入口
├── state.rs # AppState (Arc bundle)
├── error.rs / response.rs # ApiError + Resp<T> envelope
├── db/{mod,user_repo,source_cred_repo,storage_repo,migrate_repo,audit_repo,proxy_repo}.rs
├── middleware/{request_id,auth,require_admin,audit,access_log}.rs
├── auth/{jwt,validator}.rs # base64 JWT 解 + moka cache + lazy upsert
├── providers/token.rs # TokenProvider + JobTokenState
├── jobs/{manager,migrate_job,event}.rs
├── sse/stream.rs # broadcast → SSE 按 user_id 过滤
├── token_isolation_guard.rs # #[cfg(test)] 静态 grep 守护 (P0 不变量)
├── routes/{meta,sources,storage,login_baidu,login_onedrive,login_aliyun,login_pikpak,browse,migrate,admin,proxy}.rs
源端 client 模块布局
cr4sync/src/sources/
├── mod.rs # CloudSource trait
├── local.rs # 本地 fs 扫描
├── baidu_pan/* # 百度网盘 (OAuth)
├── onedrive/* # OneDrive (OAuth)
├── aliyun/* # 阿里云盘 (OAuth)
├── proxy.rs # 代理 reqwest client 构造 helper (build_proxied_http_client)
├── s3/{mod,client,config,fs,download}.rs # S3 基底 (aws-sdk-s3)
├── oss/mod.rs # OSS 薄包装 (复用 S3Client)
└── cos/mod.rs # COS 薄包装 (复用 S3Client)
└── pikpak/{mod,api,auth,fs,download}.rs # PikPak (账密登录, 非 OAuth)
与 CLI 的交叉
- 共享
[serve.cr4]段连 Cloudreve 上游 - 共享
commands/migrate/{pipeline.rs}(通过MigrateProgressSinktrait 抽象),CLI 实现IndicatifSink,server 实现BroadcastSink - 共享
MigrateTaskStoretrait,CLI 实现是MigrateDb(migrate.db),server 实现是 user-scoped 的MigrateRepo(server.db) - 共享
sources/baidu_pan/*、sources/onedrive/*、sources/aliyun/*、sources/s3/*、sources/oss/*、sources/cos/*、sources/pikpak/*第三方网盘/对象存储客户端 - 共享
sources/proxy.rs代理 client 构造 helper:CLI 从[[proxy]]配置段加载,server 从proxy表加载。代理在 source_credential 级(driver 登录时绑定),migrate 时复用 source_credential 的代理。
与 CLI 的隔离
- 不读
token.json/sources/baidu.json(及同目录的 onedrive/aliyun 凭证文件) /migrate.db - 不调
commands::prepare_session/commands::prepare_provider - 不调
auth::token_store::default_path
静态 grep 守护测试 server::token_isolation_guard 在 cargo test --features server 时扫描 server/**/*.rs,命中上述任一禁字立即失败。