2026-03-09 21:33:34 +08:00
|
|
|
|
# Expo Updates Server (Go)
|
|
|
|
|
|
|
|
|
|
|
|
这个目录是 `expo-updates-server` 的 Go 版本实现,目标是:
|
|
|
|
|
|
|
|
|
|
|
|
- 保持客户端接口兼容:`GET /api/manifest`、`GET /api/assets`
|
|
|
|
|
|
- 增加便于上传和 CI 的管理接口:`/admin/publish`、`/admin/rollback`、`/admin/releases`
|
|
|
|
|
|
|
|
|
|
|
|
## 运行
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd expo-updates-server-go
|
|
|
|
|
|
go run ./cmd/server
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
默认监听 `:3001`,可通过环境变量覆盖:
|
|
|
|
|
|
|
|
|
|
|
|
- `PORT`:默认 `3001`
|
|
|
|
|
|
- `HOSTNAME`:默认 `http://localhost:3001`,用于拼接 assets URL
|
|
|
|
|
|
- `UPDATES_ROOT`:默认 `updates`
|
|
|
|
|
|
- `PRIVATE_KEY_PATH`:可选;若客户端请求 `expo-expect-signature`,必须配置
|
|
|
|
|
|
- `ADMIN_TOKEN`:管理接口 Bearer Token
|
|
|
|
|
|
|
|
|
|
|
|
示例(复用现有 `expo-updates-server/updates`):
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd expo-updates-server-go
|
|
|
|
|
|
PORT=3001 \
|
|
|
|
|
|
HOSTNAME=http://localhost:3001 \
|
|
|
|
|
|
UPDATES_ROOT=../expo-updates-server/updates \
|
|
|
|
|
|
PRIVATE_KEY_PATH=../expo-updates-server/code-signing-keys/private-key.pem \
|
|
|
|
|
|
ADMIN_TOKEN=dev-token \
|
|
|
|
|
|
go run ./cmd/server
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
生产环境建议将 `HOSTNAME` 配置为:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
https://updates.littlelan.cn
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 兼容接口
|
|
|
|
|
|
|
|
|
|
|
|
- `GET /api/manifest`
|
|
|
|
|
|
- 支持 `expo-platform`、`expo-runtime-version`、`expo-protocol-version`
|
|
|
|
|
|
- 支持 `rollBackToEmbedded`、`noUpdateAvailable`
|
|
|
|
|
|
- 支持 `expo-expect-signature`
|
|
|
|
|
|
- `GET /api/assets?asset=...&runtimeVersion=...&platform=...`
|
|
|
|
|
|
|
2026-03-26 03:29:44 +08:00
|
|
|
|
## APK 管理接口(公开)
|
|
|
|
|
|
|
|
|
|
|
|
### 获取最新 APK 版本信息
|
|
|
|
|
|
|
|
|
|
|
|
`GET /api/apk/latest`
|
|
|
|
|
|
|
|
|
|
|
|
返回最新 APK 的版本信息,用于客户端检查更新。
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"runtimeVersion": "1.0.11",
|
|
|
|
|
|
"versionName": "1.0.11",
|
|
|
|
|
|
"size": 52428800,
|
|
|
|
|
|
"downloadUrl": "https://updates.littlelan.cn/api/apk/download?runtimeVersion=1.0.11",
|
|
|
|
|
|
"createdAt": "2026-03-26T00:00:00Z"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 下载 APK
|
|
|
|
|
|
|
|
|
|
|
|
`GET /api/apk/download?runtimeVersion=<runtimeVersion>`
|
|
|
|
|
|
|
|
|
|
|
|
下载指定版本的 APK 文件。
|
|
|
|
|
|
|
2026-03-09 21:33:34 +08:00
|
|
|
|
## 管理接口(上传/CI)
|
|
|
|
|
|
|
|
|
|
|
|
### 1) 发布 zip
|
|
|
|
|
|
|
|
|
|
|
|
`POST /admin/publish?runtimeVersion=<rv>&platform=<ios|android>`
|
|
|
|
|
|
|
|
|
|
|
|
- Header:`Authorization: Bearer <ADMIN_TOKEN>`
|
|
|
|
|
|
- Body:`expo export` 产物 zip(zip 根目录需包含 `metadata.json`)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
curl -X POST \
|
|
|
|
|
|
"https://updates.littlelan.cn/admin/publish?runtimeVersion=2&platform=ios" \
|
|
|
|
|
|
-H "Authorization: Bearer dev-token" \
|
|
|
|
|
|
-H "Content-Type: application/zip" \
|
|
|
|
|
|
--data-binary @dist.zip
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2) 创建回滚标记
|
|
|
|
|
|
|
|
|
|
|
|
`POST /admin/rollback`
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
curl -X POST "https://updates.littlelan.cn/admin/rollback" \
|
|
|
|
|
|
-H "Authorization: Bearer dev-token" \
|
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
|
-d '{"runtimeVersion":"2","platform":"ios"}'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3) 查看发布列表
|
|
|
|
|
|
|
|
|
|
|
|
`GET /admin/releases?runtimeVersion=2`
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
curl "https://updates.littlelan.cn/admin/releases?runtimeVersion=2" \
|
|
|
|
|
|
-H "Authorization: Bearer dev-token"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-26 03:29:44 +08:00
|
|
|
|
### 4) 上传 APK
|
|
|
|
|
|
|
|
|
|
|
|
`POST /admin/apk/upload?runtimeVersion=<runtimeVersion>`
|
|
|
|
|
|
|
|
|
|
|
|
- Header:`Authorization: Bearer <ADMIN_TOKEN>`
|
|
|
|
|
|
- Content-Type:`application/vnd.android.package-archive` 或 `application/octet-stream`
|
|
|
|
|
|
- Body:APK 二进制文件
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
curl -X POST \
|
|
|
|
|
|
"https://updates.littlelan.cn/admin/apk/upload?runtimeVersion=1.0.11" \
|
|
|
|
|
|
-H "Authorization: Bearer dev-token" \
|
|
|
|
|
|
-H "Content-Type: application/vnd.android.package-archive" \
|
|
|
|
|
|
--data-binary @app-release.apk
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-09 21:33:34 +08:00
|
|
|
|
## CI 示例思路
|
|
|
|
|
|
|
|
|
|
|
|
1. 在客户端跑 `npx expo export`
|
|
|
|
|
|
2. 打包 `dist` 为 zip
|
|
|
|
|
|
3. 调用 `/admin/publish`
|
|
|
|
|
|
|
|
|
|
|
|
可直接参考根目录新增工作流:`.github/workflows/go-updates-server-ci.yml`
|
|
|
|
|
|
|
2026-03-26 03:29:44 +08:00
|
|
|
|
## CI/CD
|
|
|
|
|
|
|
|
|
|
|
|
项目使用 Gitea Actions 进行持续集成和部署:
|
|
|
|
|
|
|
|
|
|
|
|
### Build Workflow (`.gitea/workflows/build.yml`)
|
|
|
|
|
|
|
|
|
|
|
|
- 触发条件:push 到 master/main 分支,或 PR
|
|
|
|
|
|
- 步骤:
|
|
|
|
|
|
1. 编译 Go 二进制文件
|
|
|
|
|
|
2. 构建 Docker 镜像
|
|
|
|
|
|
3. 推送到私有镜像仓库 `code.littlelan.cn/carrot_bbs/updates-server`
|
|
|
|
|
|
|
|
|
|
|
|
### Deploy Workflow (`.gitea/workflows/deploy.yml`)
|
|
|
|
|
|
|
|
|
|
|
|
- 触发条件:PR 合并到 master/main,或手动触发
|
|
|
|
|
|
- 步骤:
|
|
|
|
|
|
1. SSH 到服务器
|
|
|
|
|
|
2. 拉取最新镜像
|
|
|
|
|
|
3. 重启容器
|
|
|
|
|
|
|
|
|
|
|
|
### 所需 Secrets
|
|
|
|
|
|
|
|
|
|
|
|
- `GIT_USERNAME` - Gitea 用户名
|
|
|
|
|
|
- `GIT_TOKEN` - Gitea Token(用于推送镜像)
|
|
|
|
|
|
- `SERVER_PASSWORD` - 服务器 SSH 密码
|
|
|
|
|
|
- `UPDATES_ADMIN_TOKEN` - 管理接口 Token
|
|
|
|
|
|
|
2026-03-09 21:33:34 +08:00
|
|
|
|
## Docker 打包与运行
|
|
|
|
|
|
|
|
|
|
|
|
### 1) 构建镜像并导出 tar
|
|
|
|
|
|
|
|
|
|
|
|
在 `expo-updates-server-go` 目录执行:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./build-docker-tar.sh
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
可指定 tag:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./build-docker-tar.sh 20260309-ota
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
执行后会生成:
|
|
|
|
|
|
|
|
|
|
|
|
- 镜像:`carrot-bbs-updates-server:<tag>`
|
|
|
|
|
|
- tar:`carrot-bbs-updates-server-<tag>.tar`
|
|
|
|
|
|
|
|
|
|
|
|
### 2) 运行容器(挂载更新目录和密钥)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
docker run -d \
|
|
|
|
|
|
--name carrot-bbs-updates-server \
|
|
|
|
|
|
-p 3001:3001 \
|
|
|
|
|
|
-e PORT=3001 \
|
|
|
|
|
|
-e HOSTNAME=https://updates.littlelan.cn \
|
|
|
|
|
|
-e UPDATES_ROOT=/data/updates \
|
|
|
|
|
|
-e PRIVATE_KEY_PATH=/data/privatekey.pem \
|
|
|
|
|
|
-e ADMIN_TOKEN=dev-token \
|
|
|
|
|
|
-v /opt/carrot-bbs/updates:/data/updates \
|
|
|
|
|
|
-v /opt/carrot-bbs/keys/privatekey.pem:/data/privatekey.pem:ro \
|
|
|
|
|
|
carrot-bbs-updates-server:latest
|
|
|
|
|
|
```
|
|
|
|
|
|
|