kuronavi-collector 升级协议 (Server ↔ Client)
kuronavi-collector 升级协议 (Server ↔ Client)#
本文档是客户端与服务端之间的"合约"。两端都得遵守, 升级流程才稳。
1. 协议概览#
[Client] [Server]
│ │
│ 启动 / [U] 键 / --auto-upgrade │
│ │ │
│ ▼ │
│ GET /api/kuronavi-collector/latest? │
│ platform=linux-x86_64¤t=0.1.0 │
│ ─────────────────────────────────────────► │
│ │
│ ◄──────────────────────────────────── │
│ 200 { │
│ "version": "0.2.1", │
│ "download_url": "https://api.z.h3hu.com/ │
│ kuronavi-releases/v0.2.1.tar.gz", │
│ "sha256": "abc123...", │
│ "size": 4400000, │
│ "release_notes": "feat: ...", │
│ "min_version": "0.1.0", │
│ "force": false │
│ } │
│ │
│ 下载 tarball → sha256 校验 → │
│ 解压 → 替换 binary → execv 重启 │
2. 协议字段详解#
2.1 GET /api/kuronavi-collector/latest#
| Query 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
platform | string | 是 | linux-x86_64 / linux-aarch64 / darwin-x86_64 / darwin-aarch64 / windows-x86_64 |
current | string | 是 | 客户端当前版本, semver (如 0.1.0) |
2.2 响应 (200)#
{
"version": "0.2.1",
"download_url": "https://api.z.h3hu.com/kuronavi-releases/v0.2.1/kuronavi-collector-0.2.1-linux-x86_64.tar.gz",
"sha256": "a3b4c5...",
"size": 4400000,
"release_notes": "feat: 支持自定义下载目录",
"min_version": "0.1.0",
"force": false,
"released_at": "2026-06-08T10:30:00Z"
}
| 字段 | 类型 | 说明 |
|---|---|---|
version | string | 服务端最新版本号 (semver) |
download_url | string | tarball 直链 (公开可读) |
sha256 | string | tarball SHA-256 校验和 (hex) |
size | int | tarball 字节数 |
release_notes | string | 人类可读的更新说明 (Markdown 短) |
min_version | string | 最低支持版本, 低于此版本必须升 (否则客户端不工作) |
force | bool | 强制升级 (含降级), 客户端不能跳过 |
released_at | string | 发布时间 ISO 8601 |
2.3 响应 (404 / 304)#
404 Not Found: 服务端无此 platform 的 release304 Not Modified:current已是最新, 不返 body
2.4 强制升级 (force) 语义#
服务端在某版本上标 force=true 后:
- 客户端无论什么
current, 都升到version字段指定的版本 - 降级也允许 (服务端可发比 current 更老的版本)
用途: 紧急 bug 修复 / 回滚有问题的版本。
3. 客户端流程 (Rust 实现)#
3.1 查更#
// src/updater.rs
let url = format!("{}/api/kuronavi-collector/latest?platform={}¤t={}",
update_url, platform, current_ver);
let resp: LatestResponse = reqwest::get(&url).await?.json().await?;
3.2 比对#
if !resp.force && current_ver == resp.version {
return Ok(()); // 已是最新
}
if semver::compare(¤t_ver, &resp.min_version) < 0 {
info!("需升级 (current < min_version)");
}
3.3 下载 + 校验#
let bytes = reqwest::get(&resp.download_url).await?.bytes().await?;
let mut hasher = Sha256::new();
hasher.update(&bytes);
let got = format!("{:x}", hasher.finalize());
if got != resp.sha256 {
bail!("sha256 mismatch: got={}, expect={}", got, resp.sha256);
}
3.4 解压 + 替换#
// tar -xzf (走 std::process::Command, 不引入 tar crate)
let tmpdir = format!("/tmp/kuro-update-v{}", resp.version);
fs::create_dir_all(&tmpdir).await?;
let tar_path = format!("/tmp/kuro-update-v{}.tar.gz", resp.version);
fs::write(&tar_path, &bytes).await?;
Command::new("tar").args(&["xzf", &tar_path, "-C", &tmpdir]).status()?;
// 找 binary (在解压目录里)
let bin_path = format!("{}/kuronavi-collector", tmpdir);
// 替换 (用 .new + rename 避 ETXTBSY)
let current = std::env::current_exe()?;
let new = current.with_extension("new");
fs::copy(&bin_path, &new).await?;
fs::set_permissions(&new, perms)?; // 755
fs::rename(&new, ¤t)?; // 原子替换
3.5 重启#
// 关键: 用 run_upgrade 返的 target 路径, 不用 current_exe (rename 后变 (deleted))
Command::new(&target_path).arg("--auto-upgrade").exec();
4. 服务端流程 (FastAPI 实现)#
4.1 路由#
# routers/public.py
@router.get("/api/kuronavi-collector/latest")
async def latest(platform: str, current: str):
# 拿最新 release
release = await db.get_latest_release(platform=platform)
if not release:
raise HTTPException(404)
# 比对 current
is_force = release.force
if is_force or current != release.version:
return {
"version": release.version,
"download_url": build_download_url(platform, release.version),
"sha256": release.sha256,
"size": release.size_bytes,
"release_notes": release.notes,
"min_version": release.min_version,
"force": release.force,
"released_at": release.created_at.isoformat(),
}
raise HTTPException(304) # 已是最新
4.2 上传新版本 (admin)#
# routers/admin.py
@router.post("/api/admin/releases")
async def upload_release(
file: UploadFile,
version: str,
force: bool = False,
min_version: str = "0.0.1",
notes: str = "",
platforms: list[str] = ["linux-x86_64"],
user: User = Depends(require_admin),
):
# 1. 算 sha256
sha256 = hashlib.sha256(file.file.read()).hexdigest()
# 2. 存到 MinIO (或本地)
key = f"kuronavi-releases/v{version}/{file.filename}"
await minio.put(key, file.file)
# 3. 写 DB
for plat in platforms:
await db.insert_release(
version=version, platform=plat, sha256=sha256,
size_bytes=file.size, min_version=min_version,
force=force, notes=notes, download_key=key,
)
4.3 MinIO 公开读 policy#
# minio_client.py
async def ensure_bucket():
bucket = "kuronavi-releases"
# 公开读
policy = json.dumps({
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": ["*"]},
"Action": ["s3:GetObject"],
"Resource": [f"arn:aws:s3:::{bucket}/*"],
}],
})
try:
client.set_bucket_policy(bucket, policy)
except Exception:
pass
5. 部署架构#
[Client 1] [Client 2] [Client N]
│ │ │
└──────────┬──────────────┴────────────┬────────────┘
│ GET /latest │
▼ ▼
┌─────────────────────────────────────────────┐
│ api.z.h3hu.com (nginx 反代 → 8088) │
│ ┌───────────────────────────────────────┐ │
│ │ FastAPI (systemd: kuronavi-upgrade) │ │
│ │ /api/kuronavi-collector/latest │ │
│ │ /admin (admin 后台) │ │
│ │ /static (内置管理 UI) │ │
│ └───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ MinIO S3 │ │
│ │ 19000/19001 │ │
│ │ bucket: │ │
│ │ kuronavi- │ │
│ │ releases │ │
│ │ (公开读) │ │
│ └──────────────┘ │
└─────────────────────────────────────────────┘
6. 实际请求示例#
客户端发#
GET /api/kuronavi-collector/latest?platform=linux-x86_64¤t=0.1.0 HTTP/1.1
Host: api.z.h3hu.com
User-Agent: kuronavi-collector/0.1.0
服务端返 (有新版)#
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"version": "0.2.1",
"download_url": "https://api.z.h3hu.com/kuronavi-releases/v0.2.1/kuronavi-collector-0.2.1-linux-x86_64.tar.gz",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"size": 4400000,
"release_notes": "feat: 支持自定义下载目录\nfix: 修复 cookie 解析",
"min_version": "0.1.0",
"force": false,
"released_at": "2026-06-08T10:30:00Z"
}
客户端下载 tarball#
GET /kuronavi-releases/v0.2.1/kuronavi-collector-0.2.1-linux-x86_64.tar.gz HTTP/1.1
Host: api.z.h3hu.com
7. 客户端命令行#
# 默认启动 (查更, 但不自动下载)
kuronavi-collector
# 自动升级模式 (查更 + 下载 + 替换)
kuronavi-collector --auto-upgrade
# 指定 DIR (db + library 都在这)
kuronavi-collector /home/data/kuro
# TUI 里 [U] 键: 触发一次查更 + 升级
# TUI 里 [V] 键: 查看版本详情 (含 latest info + 是否需要升级)
8. 服务端管理#
# 启停
sudo systemctl start|stop|restart kuronavi-upgrade
# 查日志
journalctl -u kuronavi-upgrade -f
# 看 MinIO
open http://139.224.208.63:19001
# 登录 minio / FknJ55k5aLG7SyK8
# bucket: kuronavi-releases
9. 版本兼容#
| 协议版本 | 客户端 | 服务端 | 备注 |
|---|---|---|---|
| 1 | 0.1.0+ | 0.1.0+ | 当前 |
未来如改协议(加签名 / 加更多字段), 走 URL path 加版本号 (/api/v2/kuronavi-collector/latest), 老客户端继续走 v1。