Files
nanami-web/docs/API.md
2026-03-18 17:13:27 +08:00

530 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Nanami API 文档
所有公开接口均无需认证,供客户端软件直接调用。
基础 URL: `https://nui.rucky.cn`
---
# 一、插件市场 API
供启动器拉取插件列表、详情、版本和下载。
---
## 1.1 获取插件列表
### 请求
```
GET /api/addons
```
### 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| category | string | 否 | 按分类筛选(如 `ui`, `quest`, `combat`, `general` |
| search | string | 否 | 按名称或简介模糊搜索 |
| published | string | 否 | 默认 `true` 只返回已发布插件,传 `false` 返回全部 |
### 响应示例
```json
[
{
"id": "cxxx001",
"name": "Nanami",
"slug": "nanami",
"summary": "一站式乌龟服插件管理器",
"description": "详细描述...",
"iconUrl": "/uploads/nanami-icon.png",
"category": "general",
"published": true,
"totalDownloads": 1234,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-03-18T10:00:00.000Z",
"releases": [
{
"id": "rel001",
"version": "1.0.0",
"isLatest": true
}
],
"_count": {
"releases": 3
}
}
]
```
### 响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 插件唯一 ID |
| name | string | 插件名称 |
| slug | string | 插件标识URL 友好) |
| summary | string | 简短描述 |
| description | string | 详细描述(支持长文本) |
| iconUrl | string\|null | 图标地址 |
| category | string | 分类标签 |
| published | boolean | 是否已发布 |
| totalDownloads | int | 累计下载次数 |
| releases | array | 最新版本信息(仅包含 isLatest=true 的版本) |
| _count.releases | int | 版本总数 |
---
## 1.2 获取插件详情
通过 ID 或 slug 获取单个插件的完整信息,包含所有版本和截图。
### 请求
```
GET /api/addons/{id_or_slug}
```
### 参数
| 参数 | 说明 |
|---|---|
| id_or_slug | 插件 ID 或 slug`nanami` |
### 响应示例
```json
{
"id": "cxxx001",
"name": "Nanami",
"slug": "nanami",
"summary": "一站式乌龟服插件管理器",
"description": "详细的 Markdown 描述...",
"iconUrl": "/uploads/nanami-icon.png",
"category": "general",
"published": true,
"totalDownloads": 1234,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-03-18T10:00:00.000Z",
"releases": [
{
"id": "rel003",
"addonId": "cxxx001",
"version": "1.2.0",
"changelog": "- 新增XX功能\n- 修复YY问题",
"downloadType": "local",
"filePath": "uploads/nanami-1.2.0.zip",
"externalUrl": null,
"gameVersion": "1.12.1",
"downloadCount": 500,
"isLatest": true,
"createdAt": "2026-03-18T10:00:00.000Z"
},
{
"id": "rel002",
"addonId": "cxxx001",
"version": "1.1.0",
"changelog": "- 修复界面问题",
"downloadType": "local",
"filePath": "uploads/nanami-1.1.0.zip",
"externalUrl": null,
"gameVersion": "1.12.1",
"downloadCount": 300,
"isLatest": false,
"createdAt": "2026-02-15T10:00:00.000Z"
}
],
"screenshots": [
{
"id": "ss001",
"addonId": "cxxx001",
"imageUrl": "/uploads/screenshot1.png",
"sortOrder": 0
}
]
}
```
### 错误响应
| HTTP 状态码 | 说明 |
|---|---|
| 404 | 插件不存在 |
---
## 1.3 获取版本列表
获取所有版本,可按插件 ID 筛选。
### 请求
```
GET /api/releases?addonId={addonId}
```
### 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| addonId | string | 否 | 按插件 ID 筛选 |
### 响应示例
```json
[
{
"id": "rel003",
"addonId": "cxxx001",
"version": "1.2.0",
"changelog": "- 新增XX功能",
"downloadType": "local",
"filePath": "uploads/nanami-1.2.0.zip",
"externalUrl": null,
"gameVersion": "1.12.1",
"downloadCount": 500,
"isLatest": true,
"createdAt": "2026-03-18T10:00:00.000Z",
"addon": {
"id": "cxxx001",
"name": "Nanami",
"slug": "nanami"
}
}
]
```
### 版本字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 版本唯一 ID |
| addonId | string | 所属插件 ID |
| version | string | 版本号(如 "1.2.0" |
| changelog | string | 更新日志 |
| downloadType | string | `local` 本地文件 / `url` 外部链接 |
| gameVersion | string | 适配的游戏版本(如 "1.12.1" |
| downloadCount | int | 下载次数 |
| isLatest | boolean | 是否为最新版本 |
| addon | object | 所属插件基本信息 |
---
## 1.4 下载插件
通过版本 ID 下载对应的插件压缩包。
### 请求
```
GET /api/download/{releaseId}
```
### 行为
- **本地文件**:返回文件二进制流,`Content-Disposition` 设为 `attachment`
- **外部链接**302 重定向到外部下载地址
- 每次调用自动递增版本下载计数和插件总下载计数
### 响应头(本地文件时)
```
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="nanami-1.2.0.zip"
Content-Length: 1048576
```
### 错误响应
| HTTP 状态码 | 说明 |
|---|---|
| 404 | 版本不存在或文件丢失 |
---
# 二、软件更新 API
供启动器检查自身版本更新和热更新。
---
## 软件标识slug
| slug | 用途 | 文件类型 |
|---|---|---|
| `nanami-launcher` | 启动器全量安装包 | Nanami-Launcher-x.x.x.exe |
| `nanami-launcher-patch` | 热更新补丁包 | app.asar |
---
## 2.1 检查更新
客户端启动时调用此接口,传入当前版本号以检测是否有新版本可用。
### 请求
```
GET /api/software/check-update?slug={slug}&versionCode={versionCode}
```
### 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| slug | string | 是 | 软件标识 |
| versionCode | int | 否 | 当前客户端的版本代码(整数),不传默认为 0 |
### 响应示例
**有更新:**
```json
{
"hasUpdate": true,
"forceUpdate": false,
"latest": {
"version": "1.2.0",
"versionCode": 120,
"changelog": "- 新增XX功能\n- 修复YY问题",
"downloadUrl": "https://nui.rucky.cn/api/software/download/cxxx123",
"fileSize": 5242880,
"minVersion": "1.0.0",
"createdAt": "2026-03-18T10:00:00.000Z"
}
}
```
**无更新:**
```json
{
"hasUpdate": false,
"forceUpdate": false,
"latest": {
"version": "1.0.0",
"versionCode": 100,
"changelog": "...",
"downloadUrl": "https://nui.rucky.cn/api/software/download/cxxx123",
"fileSize": 5242880,
"minVersion": null,
"createdAt": "2026-03-01T10:00:00.000Z"
}
}
```
### 响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| hasUpdate | boolean | 是否有新版本 |
| forceUpdate | boolean | 是否强制更新true 时客户端应阻止用户继续使用旧版) |
| latest.version | string | 最新版本号(如 "1.2.0" |
| latest.versionCode | int | 最新版本代码(整数,用于比较大小) |
| latest.changelog | string | 更新日志 |
| latest.downloadUrl | string | 下载地址(直接用于下载) |
| latest.fileSize | int | 文件大小(字节) |
| latest.minVersion | string\|null | 最低兼容版本号 |
| latest.createdAt | string | 发布时间 (ISO 8601) |
### 错误响应
| HTTP 状态码 | 说明 |
|---|---|
| 400 | 缺少 slug 参数 |
| 404 | 软件不存在或暂无版本 |
---
## 2.2 下载软件包
通过版本 ID 下载对应的软件安装包。一般从"检查更新"接口返回的 `downloadUrl` 直接获取。
### 请求
```
GET /api/software/download/{versionId}
```
### 行为
- **本地文件**:返回文件二进制流,`Content-Disposition` 设为 `attachment`
- **外部链接**302 重定向到外部下载地址
- 每次调用自动递增下载计数
### 响应头(本地文件时)
```
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="nanami-launcher-v1.2.0.exe"
Content-Length: 5242880
```
### 错误响应
| HTTP 状态码 | 说明 |
|---|---|
| 404 | 版本不存在或文件丢失 |
---
## 2.3 首页启动器最新版本下载
直接下载 `nanami-launcher` 的最新版本,用于首页下载按钮。
### 请求
```
GET /api/software/latest
```
下载最新全量包(自动递增下载计数)。
```
GET /api/software/latest?info=1
```
仅返回版本信息 JSON不下载。
---
## 2.4 客户端更新检查流程
Nanami 启动器在启动后应按以下顺序检查更新:
```
客户端启动(当前 versionCode = 76
├── 步骤 1: 检查全量更新
│ GET /api/software/check-update?slug=nanami-launcher&versionCode=76
│ │
│ ├── forceUpdate=true → 弹出强制更新对话框,下载完整安装包
│ │ 不再检查热更新
│ │
│ └── hasUpdate=true, forceUpdate=false → 显示"新版本"徽章(用户自行决定)
│ │
│ └── 继续步骤 2
├── 步骤 2: 检查热更新
│ GET /api/software/check-update?slug=nanami-launcher-patch&versionCode=76
│ │
│ ├── hasUpdate=true → 静默下载 app.asar替换本地文件
│ │ 显示"就绪"徽章,提示重启生效
│ │
│ └── hasUpdate=false → 当前已是最新,无需操作
└── 正常运行
```
### 版本号对应关系示例
| 发布类型 | 版本号 | versionCode | slug |
|---|---|---|---|
| 全量安装包 | 0.7.6 | 76 | nanami-launcher |
| 热更新补丁 | 0.7.7 | 77 | nanami-launcher-patch |
| 全量大版本 | 0.8.0 | 80 | nanami-launcher |
### 后台操作
- **发布全量版本**在「Nanami 启动器(全量包)」中上传 `.exe` 文件
- **发布热更新**在「Nanami 热更新包」中上传 `app.asar` 文件
- 两者的 `versionCode` 应保持递增关系,以便客户端正确比较
---
---
# 三、客户端集成示例Electron
## 3.1 启动器自身更新
```javascript
const BASE_URL = "https://nui.rucky.cn/api/software/check-update";
const CURRENT_VERSION_CODE = 76;
async function checkUpdates() {
// 步骤 1: 检查全量更新
const fullResp = await fetch(
`${BASE_URL}?slug=nanami-launcher&versionCode=${CURRENT_VERSION_CODE}`
);
const fullData = await fullResp.json();
if (fullData.hasUpdate && fullData.forceUpdate) {
showForceUpdateDialog(fullData.latest);
return;
}
if (fullData.hasUpdate) {
showUpdateBadge(fullData.latest);
}
// 步骤 2: 检查热更新
const patchResp = await fetch(
`${BASE_URL}?slug=nanami-launcher-patch&versionCode=${CURRENT_VERSION_CODE}`
);
const patchData = await patchResp.json();
if (patchData.hasUpdate) {
const asarResp = await fetch(patchData.latest.downloadUrl);
const buffer = await asarResp.arrayBuffer();
// 写入本地 app.asar 路径...
showRestartBadge();
}
}
```
## 3.2 插件市场拉取
```javascript
const API_BASE = "https://nui.rucky.cn";
// 获取全部已发布插件
async function fetchAddons(category?: string, search?: string) {
const params = new URLSearchParams();
if (category) params.set("category", category);
if (search) params.set("search", search);
const resp = await fetch(`${API_BASE}/api/addons?${params}`);
return resp.json();
}
// 获取单个插件详情(含所有版本和截图)
async function fetchAddonDetail(slug: string) {
const resp = await fetch(`${API_BASE}/api/addons/${slug}`);
return resp.json();
}
// 下载插件最新版本
async function downloadAddon(addon: any) {
const latestRelease = addon.releases?.find((r: any) => r.isLatest);
if (!latestRelease) return;
const downloadUrl = `${API_BASE}/api/download/${latestRelease.id}`;
const resp = await fetch(downloadUrl);
const buffer = await resp.arrayBuffer();
// 保存到 WoW 插件目录 Interface/AddOns/...
}
// 检查插件是否有更新(对比本地已安装版本)
async function checkAddonUpdates(installedAddons: { slug: string; version: string }[]) {
const addons = await fetchAddons();
const updates = [];
for (const installed of installedAddons) {
const remote = addons.find((a: any) => a.slug === installed.slug);
if (!remote) continue;
const latestRelease = remote.releases?.[0];
if (latestRelease && latestRelease.version !== installed.version) {
updates.push({
addon: remote,
currentVersion: installed.version,
newVersion: latestRelease.version,
});
}
}
return updates;
}
```