docs: add client release api integration guide
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
- [MVP 完整验收报告](mvp-acceptance.md):T-402 对 `02-requirements.md` P0 验收项的逐项结论、测试证据和已知限制。
|
- [MVP 完整验收报告](mvp-acceptance.md):T-402 对 `02-requirements.md` P0 验收项的逐项结论、测试证据和已知限制。
|
||||||
- [部署 / 运行文档](deployment.md):T-403 生产部署步骤、宝塔 / Nginx / Gunicorn 配置、静态文件、共享 cache、图片超时与上线检查。
|
- [部署 / 运行文档](deployment.md):T-403 生产部署步骤、宝塔 / Nginx / Gunicorn 配置、静态文件、共享 cache、图片超时与上线检查。
|
||||||
- [API 合约](api.md):对外接口、支付回调、AI 调用模块合约、错误码。
|
- [API 合约](api.md):对外接口、支付回调、AI 调用模块合约、错误码。
|
||||||
|
- [桌面端版本检查接口对接文档](client-release-api-integration.md):`GET /api/v1/client/releases/latest` 的请求 demo、响应结构、客户端处理和排查清单。
|
||||||
- [内容安全与本地敏感词过滤](moderation.md):T-604 的 prompt 敏感词过滤设计、时序、缓存和验收口径。
|
- [内容安全与本地敏感词过滤](moderation.md):T-604 的 prompt 敏感词过滤设计、时序、缓存和验收口径。
|
||||||
- [路由与页面结构](routes.md):API 路由与 django-admin 后台职责。
|
- [路由与页面结构](routes.md):API 路由与 django-admin 后台职责。
|
||||||
- [品牌基线 Brand Tokens](brand.md):配色/字体/logo/术语/语气的单一真相源(含可复制 CSS 变量);首页、portal、桌面端统一引用,对应任务 T-606。
|
- [品牌基线 Brand Tokens](brand.md):配色/字体/logo/术语/语气的单一真相源(含可复制 CSS 变量);首页、portal、桌面端统一引用,对应任务 T-606。
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ T-607 已实现 `GET /api/v1/client/releases/latest?platform=windows`,给桌
|
|||||||
|
|
||||||
桌面端检查最新客户端版本。该接口为公开只读接口,不需要 API Key,不关联用户账本。
|
桌面端检查最新客户端版本。该接口为公开只读接口,不需要 API Key,不关联用户账本。
|
||||||
|
|
||||||
|
对接请求 demo、客户端下载处理建议和排查清单见 [`client-release-api-integration.md`](client-release-api-integration.md)。
|
||||||
|
|
||||||
请求:
|
请求:
|
||||||
|
|
||||||
```http
|
```http
|
||||||
|
|||||||
@@ -0,0 +1,234 @@
|
|||||||
|
# 桌面端版本检查接口对接文档
|
||||||
|
|
||||||
|
> 面向桌面端客户端和其他项目 agent。本文只说明「获取当前软件最新版本」接口如何调用,不涉及 API Key、点数、生成接口或用户账本。
|
||||||
|
|
||||||
|
## 接口用途
|
||||||
|
|
||||||
|
桌面端启动时或用户点击「检查更新」时,请求 cmhub 获取当前平台的最新客户端版本、下载地址、SHA256 校验值和发布说明。
|
||||||
|
|
||||||
|
该接口是公开只读接口:
|
||||||
|
|
||||||
|
- 不需要 `Authorization`。
|
||||||
|
- 不需要登录态 cookie。
|
||||||
|
- 不读取用户信息。
|
||||||
|
- 不扣点。
|
||||||
|
- 不占用生成接口限流。
|
||||||
|
- 不返回后台 ID、本地文件系统路径、用户信息、API Key、模型配置或密钥。
|
||||||
|
|
||||||
|
## 请求地址
|
||||||
|
|
||||||
|
生产环境:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET https://cm.833729.com/api/v1/client/releases/latest?platform=windows
|
||||||
|
```
|
||||||
|
|
||||||
|
本地开发:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET http://127.0.0.1:8000/api/v1/client/releases/latest?platform=windows
|
||||||
|
```
|
||||||
|
|
||||||
|
## 请求参数
|
||||||
|
|
||||||
|
| 参数 | 必填 | 默认值 | 可选值 | 说明 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `platform` | 否 | `windows` | `windows` / `macos` / `linux` | 客户端运行平台。当前桌面端 Windows 客户端传 `windows` 即可。 |
|
||||||
|
|
||||||
|
建议桌面端显式传 `platform=windows`,不要依赖默认值。
|
||||||
|
|
||||||
|
## 请求 Demo
|
||||||
|
|
||||||
|
### curl
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s "https://cm.833729.com/api/v1/client/releases/latest?platform=windows"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows PowerShell
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Invoke-RestMethod `
|
||||||
|
-Method Get `
|
||||||
|
-Uri "https://cm.833729.com/api/v1/client/releases/latest?platform=windows" `
|
||||||
|
-TimeoutSec 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = "https://cm.833729.com/api/v1/client/releases/latest"
|
||||||
|
response = requests.get(url, params={"platform": "windows"}, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
release = data.get("release")
|
||||||
|
if release is None:
|
||||||
|
print("暂未发布")
|
||||||
|
else:
|
||||||
|
print(release["version"], release["download_url"])
|
||||||
|
```
|
||||||
|
|
||||||
|
### JavaScript / TypeScript
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const response = await fetch(
|
||||||
|
"https://cm.833729.com/api/v1/client/releases/latest?platform=windows",
|
||||||
|
{ method: "GET" },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`version check failed: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.release) {
|
||||||
|
console.log(data.release.version, data.release.download_url);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 成功响应:有当前版本
|
||||||
|
|
||||||
|
HTTP 状态码:`200`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"platform": "windows",
|
||||||
|
"release": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://cm.833729.com/media/downloads/cmhub-desktop-1.0.0.exe",
|
||||||
|
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||||
|
"release_notes": "本次更新说明",
|
||||||
|
"published_at": "2026-07-07T10:30:00+08:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `platform` | string | 服务端实际查询的平台。 |
|
||||||
|
| `release.version` | string | 最新客户端版本号。 |
|
||||||
|
| `release.download_url` | string | 下载地址。可能是本站 `/media/downloads/...` 的绝对 URL,也可能是后台配置的外部 CDN / 对象存储 URL。 |
|
||||||
|
| `release.sha256` | string | 安装包 SHA256。桌面端下载后应计算本地文件 SHA256 并比对。 |
|
||||||
|
| `release.release_notes` | string | 发布说明,可能为空字符串。 |
|
||||||
|
| `release.published_at` | string | ISO 8601 时间;当前实现使用发布记录更新时间。 |
|
||||||
|
|
||||||
|
## 成功响应:暂未发布
|
||||||
|
|
||||||
|
HTTP 状态码:`200`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"platform": "windows",
|
||||||
|
"release": null,
|
||||||
|
"message": "暂未发布"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
客户端处理建议:
|
||||||
|
|
||||||
|
- 把 `release === null` 视为「没有可下载版本」。
|
||||||
|
- 不要弹错误框。
|
||||||
|
- 可在日志中记录,也可在「检查更新」按钮下显示「暂未发布」。
|
||||||
|
|
||||||
|
## 错误响应:非法平台
|
||||||
|
|
||||||
|
HTTP 状态码:`400`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": {
|
||||||
|
"code": "bad_request",
|
||||||
|
"message": "参数错误"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
常见原因:`platform=android` 或其他未支持的平台。
|
||||||
|
|
||||||
|
## 客户端对接要求
|
||||||
|
|
||||||
|
1. 调用版本检查接口时不要带 API Key。
|
||||||
|
2. 不要解析首页 HTML 获取版本号,必须使用本 JSON 接口。
|
||||||
|
3. 网络超时建议 5 到 10 秒;失败时不影响主程序启动。
|
||||||
|
4. 客户端应把本地版本号与 `release.version` 比较,只有远端版本更新时才提示用户下载。
|
||||||
|
5. 下载完成后按 `release.sha256` 校验安装包;校验失败必须删除文件并提示重新下载。
|
||||||
|
6. 不要假设下载地址一定在 cmhub 域名下,后续可能切到 CDN 或对象存储。
|
||||||
|
7. 不要依赖 `message` 做业务判断;是否有版本以 `release` 是否为 `null` 为准。
|
||||||
|
|
||||||
|
## 版本比较建议
|
||||||
|
|
||||||
|
版本号建议采用语义化版本格式,例如:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1.0.0
|
||||||
|
1.1.0
|
||||||
|
1.1.1
|
||||||
|
2.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
客户端比较时不要做普通字符串比较。例如 `"1.10.0"` 应大于 `"1.9.0"`。
|
||||||
|
|
||||||
|
简化 Python 示例:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def parse_version(version: str) -> tuple[int, ...]:
|
||||||
|
return tuple(int(part) for part in version.split("."))
|
||||||
|
|
||||||
|
if parse_version(remote_version) > parse_version(local_version):
|
||||||
|
print("发现新版本")
|
||||||
|
```
|
||||||
|
|
||||||
|
如果未来版本号包含 `beta` / `rc` 等预发布标记,客户端需要使用更完整的语义化版本解析器。
|
||||||
|
|
||||||
|
## 下载校验建议
|
||||||
|
|
||||||
|
Python 计算 SHA256 示例:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import hashlib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def sha256_file(path: str) -> str:
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
with Path(path).open("rb") as file:
|
||||||
|
for chunk in iter(lambda: file.read(1024 * 1024), b""):
|
||||||
|
digest.update(chunk)
|
||||||
|
return digest.hexdigest()
|
||||||
|
```
|
||||||
|
|
||||||
|
比对时建议统一转小写:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if sha256_file(downloaded_path).lower() != release["sha256"].lower():
|
||||||
|
raise RuntimeError("安装包 SHA256 校验失败")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 服务端发布前置条件
|
||||||
|
|
||||||
|
运营在 django-admin 的「客户端下载版本」里维护发布记录:
|
||||||
|
|
||||||
|
- `platform`:选择 `windows`。
|
||||||
|
- `version`:填写客户端版本号。
|
||||||
|
- `file` 或 `external_url`:至少填写一个。
|
||||||
|
- `sha256`:填写安装包 SHA256。
|
||||||
|
- `is_current`:勾选当前版本。
|
||||||
|
- `release_notes`:填写发布说明。
|
||||||
|
|
||||||
|
同一平台只能有一个当前版本;保存新的当前版本时,旧当前版本会自动取消当前状态。
|
||||||
|
|
||||||
|
如果使用本地上传文件,生产环境必须确保 Nginx 正确服务 `MEDIA_ROOT/downloads/`,否则接口会返回下载 URL,但客户端下载可能 404。
|
||||||
|
|
||||||
|
## 排查清单
|
||||||
|
|
||||||
|
| 现象 | 优先检查 |
|
||||||
|
| --- | --- |
|
||||||
|
| 返回 `release:null` | django-admin 是否有该 `platform` 的 `is_current=True` 发布记录;发布记录是否有 `external_url` 或上传文件。 |
|
||||||
|
| 返回 `400 bad_request` | `platform` 是否为 `windows` / `macos` / `linux`。 |
|
||||||
|
| `download_url` 访问 404 | Nginx media 配置是否正确;本地上传文件是否存在;若使用 `external_url`,外部地址是否可访问。 |
|
||||||
|
| SHA256 校验失败 | 后台填写的 SHA256 是否对应当前安装包;安装包是否被重新打包但未更新 SHA256。 |
|
||||||
|
| 客户端每次都提示更新 | 客户端版本比较逻辑是否按数字段比较,而不是字符串比较。 |
|
||||||
+10
@@ -1445,3 +1445,13 @@
|
|||||||
- `git diff --check`:通过,仅 Windows CRLF 提示。
|
- `git diff --check`:通过,仅 Windows CRLF 提示。
|
||||||
- 决策:T-607 不继承 `ExternalApiView`,避免要求 API Key;也不挂 `GenerateRateThrottle`,避免版本检查占用生成额度。文件下载地址按请求生成绝对 URL;生产 HTTPS 由已有 proxy/HTTPS 配置保证。
|
- 决策:T-607 不继承 `ExternalApiView`,避免要求 API Key;也不挂 `GenerateRateThrottle`,避免版本检查占用生成额度。文件下载地址按请求生成绝对 URL;生产 HTTPS 由已有 proxy/HTTPS 配置保证。
|
||||||
- 下一步:看板当前无新的编号任务;建议继续按业务优先级拆真实支付回调到账闭环、客户端下载包发布或真实图片耗时验证。
|
- 下一步:看板当前无新的编号任务;建议继续按业务优先级拆真实支付回调到账闭环、客户端下载包发布或真实图片耗时验证。
|
||||||
|
|
||||||
|
## 2026-07-07 文档:桌面端版本检查接口对接 Demo
|
||||||
|
|
||||||
|
- 状态:DONE。
|
||||||
|
- 变更:
|
||||||
|
- 新增 `docs/client-release-api-integration.md`,整理 `GET /api/v1/client/releases/latest` 的用途、生产/本地请求地址、参数、curl / PowerShell / Python / JavaScript 请求 demo、成功/暂未发布/错误响应、客户端版本比较、下载 SHA256 校验、服务端发布前置条件和排查清单。
|
||||||
|
- `docs/README.md`:登记新文档入口。
|
||||||
|
- `docs/api.md`:在版本检查接口段落补充对接文档链接。
|
||||||
|
- 验证:纯文档修改;`git diff --check` 通过。
|
||||||
|
- 下一步:按业务优先级继续处理真实支付回调到账闭环、客户端下载包发布或真实图片耗时验证。
|
||||||
|
|||||||
Reference in New Issue
Block a user