Expo Updates Server (Go)

这个目录是 expo-updates-server 的 Go 版本实现,目标是:

  • 保持客户端接口兼容:GET /api/manifestGET /api/assets
  • 增加便于上传和 CI 的管理接口:/admin/publish/admin/rollback/admin/releases

运行

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

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 配置为:

https://updates.littlelan.cn

兼容接口

  • GET /api/manifest
    • 支持 expo-platformexpo-runtime-versionexpo-protocol-version
    • 支持 rollBackToEmbeddednoUpdateAvailable
    • 支持 expo-expect-signature
  • GET /api/assets?asset=...&runtimeVersion=...&platform=...

APK 管理接口(公开)

获取最新 APK 版本信息

GET /api/apk/latest

返回最新 APK 的版本信息,用于客户端检查更新。

{
  "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 文件。

管理接口(上传/CI

1) 发布 zip

POST /admin/publish?runtimeVersion=<rv>&platform=<ios|android>

  • HeaderAuthorization: Bearer <ADMIN_TOKEN>
  • Bodyexpo export 产物 zipzip 根目录需包含 metadata.json
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

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

curl "https://updates.littlelan.cn/admin/releases?runtimeVersion=2" \
  -H "Authorization: Bearer dev-token"

4) 上传 APK

POST /admin/apk/upload?runtimeVersion=<runtimeVersion>

  • HeaderAuthorization: Bearer <ADMIN_TOKEN>
  • Content-Typeapplication/vnd.android.package-archiveapplication/octet-stream
  • BodyAPK 二进制文件
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

CI 示例思路

  1. 在客户端跑 npx expo export
  2. 打包 dist 为 zip
  3. 调用 /admin/publish

可直接参考根目录新增工作流:.github/workflows/go-updates-server-ci.yml

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

Docker 打包与运行

1) 构建镜像并导出 tar

expo-updates-server-go 目录执行:

./build-docker-tar.sh

可指定 tag

./build-docker-tar.sh 20260309-ota

执行后会生成:

  • 镜像:carrot-bbs-updates-server:<tag>
  • tarcarrot-bbs-updates-server-<tag>.tar

2) 运行容器(挂载更新目录和密钥)

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
Description
No description provided
Readme 57 KiB
Languages
Go 97%
Shell 1.9%
Dockerfile 1.1%