kuronavi-collector 升级协议 (Server ↔ Client)

kuronavi-collector 升级协议 (Server ↔ Client)#

本文档是客户端与服务端之间的"合约"。两端都得遵守, 升级流程才稳。

1. 协议概览#

[Client]                                        [Server]
   │                                                │
   │  启动 / [U] 键 / --auto-upgrade                 │
   │       │                                        │
   │       ▼                                        │
   │  GET /api/kuronavi-collector/latest?           │
   │      platform=linux-x86_64&current=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 参数类型必填说明
platformstringlinux-x86_64 / linux-aarch64 / darwin-x86_64 / darwin-aarch64 / windows-x86_64
currentstring客户端当前版本, 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"
}
字段类型说明
versionstring服务端最新版本号 (semver)
download_urlstringtarball 直链 (公开可读)
sha256stringtarball SHA-256 校验和 (hex)
sizeinttarball 字节数
release_notesstring人类可读的更新说明 (Markdown 短)
min_versionstring最低支持版本, 低于此版本必须升 (否则客户端不工作)
forcebool强制升级 (含降级), 客户端不能跳过
released_atstring发布时间 ISO 8601

2.3 响应 (404 / 304)#

  • 404 Not Found: 服务端无此 platform 的 release
  • 304 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={}&current={}",
    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(&current_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, &current)?; // 原子替换

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&current=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. 版本兼容#

协议版本客户端服务端备注
10.1.0+0.1.0+当前

未来如改协议(加签名 / 加更多字段), 走 URL path 加版本号 (/api/v2/kuronavi-collector/latest), 老客户端继续走 v1。