---
title: 自建更新服务器说明
doc_type: operations-runbook
domain: runtime
keywords:
  - 自建更新服务器
  - 动态更新端点
  - macOS 更新
  - Windows 自助构建包
summary: 说明如何用你自己的服务器承接桌面端更新检查，并在当前阶段正式支持 macOS 动态更新、为 Windows 提供下载页入口。
when_to_read:
  - 你准备把桌面端更新检查切到自己的服务器
  - 你不再依赖 GitHub Releases，但仍希望应用内能发现新版本
read_next:
  - ./app-update-delivery.md
  - ./self-hosted-release-publish.md
  - ./mixed-release-bundle.md
  - ./windows-self-build-for-testers.md
---
# 自建更新服务器说明

这篇文档只解决一件事：在当前仓库不再依赖 GitHub 自动发版的前提下，如何用你自己的服务器承接应用内更新检查。

当前正式口径：

- macOS：应用内检查更新，发现新版本后下载你服务器提供的安装包或更新包
- Windows：当前阶段仍建议提供发布页或自助构建包页面，不强行伪装成已有稳定的自动更新安装包

## 当前实现

当前仓库已经提供一个零依赖 Node 服务：

- [self_hosted_update_service.mjs](/Users/winkd/my-projects/ai-stock-assistant-project/ai-stock-assistant-desktop-tauri/scripts/self_hosted_update_service.mjs)

默认监听：

- `127.0.0.1:3800`

默认读取：

- `<release-root>/release-index.json`

建议由 Nginx 反代：

- `/update/*`
- `/downloads`

当前服务只读取一份真值文件：

- `<release-root>/release-index.json`

它不会：

- 扫描目录临时推断最新版本
- 维护第二份版本状态
- 为 Windows 伪造自动更新协议

## 配置入口

当前桌面端通过 `sys_config` 维护 2 个正式键：

- `app.updater.endpoint_template`
- `app.updater.release_page_url`

应用设置页里的“应用更新”区域已经提供这 2 个字段的可视化编辑入口。

## 动态 endpoint 模板

`app.updater.endpoint_template` 需要是一个 HTTPS URL 模板，支持下面这些占位符：

- `{{target}}`
- `{{arch}}`
- `{{current_version}}`

示例：

```text
https://updates.example.com/update/{{target}}/{{arch}}/{{current_version}}
```

桌面端运行时会按当前平台自动替换这些占位符，再向你的服务器发起更新检查。

## 服务路由

当前最小服务提供：

- `GET /healthz`
- `GET /downloads`
- `GET /update/:target/:arch/:currentVersion`
- `GET /release-index.json`

其中 `/downloads` 当前已经收口成正式下载页：

- 首屏展示版本、平台下载入口和安装步骤
- 支持 `macOS / Windows` 平台切换
- Windows 自助构建步骤提供“已进入解压目录前提下”可直接复制的真实命令块
- FAQ 明确说明 macOS 提示“安装包已损坏”时的处理命令
- Windows 保持自助构建包说明，不伪装成自动更新下载页

## 真值文件结构

发布脚本每次会生成一份 `release-index.json`，服务只读这一份。

当前正式结构至少包含：

- `version`
- `pub_date`
- `notes`
- `release_page_url`
- `provider`
- `platforms.macos.installer`
- `platforms.macos.updater`
- `platforms.windows.self_build_kit`

示例：

```json
{
  "schema_version": 1,
  "version": "1.0.9",
  "pub_date": "2026-04-04T16:00:00Z",
  "notes": "Bug fixes",
  "release_page_url": "https://stock.peakarea.online/downloads",
  "provider": "stock.peakarea.online",
  "platforms": {
    "macos": {
      "target": "darwin",
      "arch": "aarch64",
      "release_name": "AI Stock Assistant Desktop v1.0.9",
      "installer": {
        "name": "AI-Stock-Assistant-Desktop_1.0.9_aarch64.dmg",
        "url": "https://stock.peakarea.online/AI-Stock-Assistant-Desktop_1.0.9_aarch64.dmg",
        "size": 245998771
      },
      "updater": {
        "name": "AI-Stock-Assistant-Desktop.app.tar.gz",
        "url": "https://stock.peakarea.online/AI-Stock-Assistant-Desktop.app.tar.gz",
        "signature": "<.sig 文件内容>",
        "size": 198765432
      }
    },
    "windows": {
      "target": "windows",
      "arch": "x86_64",
      "release_name": "AI Stock Assistant Desktop v1.0.9",
      "self_build_kit": {
        "name": "ai-stock-assistant-desktop-windows-self-build-kit-1.0.9.zip",
        "url": "https://stock.peakarea.online/ai-stock-assistant-desktop-windows-self-build-kit-1.0.9.zip",
        "size": 312445678
      }
    }
  }
}
```

## 服务器返回协议

### 无更新

直接返回：

```http
204 No Content
```

### 有更新

返回 `200` 和 JSON。最小必填字段：

```json
{
  "version": "1.1.0",
  "url": "https://updates.example.com/releases/AI-Stock-Assistant-Desktop.app.tar.gz",
  "signature": "<.sig 文件内容>",
  "notes": "Bug fixes",
  "pub_date": "2026-04-04T16:00:00Z"
}
```

## 当前仓库额外支持的扩展字段

为了兼容“应用内下载最新安装包 + 手动安装”的现有页面交互，当前仓库还支持下面这些可选扩展字段：

```json
{
  "release_name": "AI Stock Assistant Desktop v1.1.0",
  "release_page_url": "https://updates.example.com/downloads",
  "installer_url": "https://updates.example.com/releases/AI-Stock-Assistant-Desktop_1.1.0_aarch64.dmg",
  "installer_name": "AI-Stock-Assistant-Desktop_1.1.0_aarch64.dmg",
  "installer_size": 245998771,
  "provider": "updates.example.com"
}
```

这些扩展字段的作用：

- `release_page_url`：应用点击“打开版本发布页”时优先打开这里
- `installer_url`：应用点击“下载最新安装包”时优先下载这里，而不是直接下载 updater 包
- `installer_name`：控制前端展示和落盘文件名
- `installer_size`：补充下载进度总大小
- `provider`：前端展示更新来源

如果你不提供 `installer_url`，桌面端会回退到正式 updater 的 `url` 字段。

## `/update/:target/:arch/:currentVersion` 行为

### macOS

- 如果 `currentVersion >= latestVersion`，返回 `204`
- 如果有更新，返回 `200` 和 Tauri 动态更新 JSON
- 同时附加 `release_name`、`release_page_url`、`installer_url`、`installer_name`、`installer_size`、`provider`

### Windows

当前正式实现：

- 一律返回 `204`
- 下载入口统一放在 `/downloads`
- 应用内如需查看新版本，打开 `release_page_url`

这样可以保持契约单一：macOS 走真正的 updater JSON，Windows 先不伪装成已经具备自动安装闭环

## 平台建议

### macOS

建议服务器同时提供：

- 正式 updater 包：给版本检查和后续可能的自动安装能力
- `installer_url` 对应的 `dmg`：给当前“手动下载安装包”交互

### Windows

当前阶段建议：

- 更新检查接口直接返回 `204`
- `release_page_url` 指向你的下载页
- 下载页里提供 Windows 自助构建包链接

这样桌面端仍然能提示用户去正确的页面，但不会假装自己已经具备稳定的 Windows 自动安装闭环

## 部署

当前仓库已经提供部署脚本：

- [deploy_self_hosted_update_service.mjs](/Users/winkd/my-projects/ai-stock-assistant-project/ai-stock-assistant-desktop-tauri/scripts/deploy_self_hosted_update_service.mjs)

默认入口：

```bash
npm run deploy:self-hosted-update-service
```

它会：

1. 上传 Node 服务脚本到服务器
2. 生成并安装 `systemd` unit
3. 更新服务器上的 `nginx.conf`
4. 启动或重启更新服务
5. 给当前 Nginx 主进程发送 `HUP` 触发 reload

## 失败策略

当前服务默认 fail fast：

- `release-index.json` 不存在，返回 `500`
- JSON 非法，返回 `500`
- 必填字段缺失，返回 `500`
- 不支持的 `target/arch`，返回 `204`

服务不会用目录扫描、默认值或旧字段兜底掩盖结构性错误

## 当前边界

这套方案解决的是：

- 不依赖 GitHub Releases 的应用内更新检查
- 自建服务器承接版本发现与下载页路由

它当前不解决：

- Windows 自动下载安装
- 完整多平台自动发版流水线
- 更新服务器上的构建产物生成
