feat: add managed online updates
Signed Release / release (push) Successful in 9m24s

This commit is contained in:
Qiufeng
2026-08-04 20:05:50 +08:00
parent 4f2ea26e8e
commit d3892320dd
20 changed files with 1365 additions and 111 deletions
+29 -16
View File
@@ -111,7 +111,7 @@ PostgreSQL-only 正式构建:
```bash
cd oa-backend
./gradlew clean bootJar -PreleaseVersion=0.2.0 -PproductionBuild=true
./gradlew clean bootJar -PreleaseVersion=0.3.5 -PproductionBuild=true
```
正式 JAR 必须包含 PostgreSQL 驱动,并且不得包含 `sqlite-jdbc``hibernate-community-dialects`
@@ -181,12 +181,15 @@ curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
Linux 生产服务要求主机使用 systemd;没有 systemd 的容器、WSL 或精简系统只能显式使用 `--no-service` 做开发验收,在线更新也会保持关闭。
安装器启动后会输出带一次性令牌的局域网地址,例如
安装器启动后会输出带一次性令牌的访问地址。优先级依次为:命令行 `--public-url`(或 `ERP_PUBLIC_URL`)、HTTPS 服务探测到的公网 IP、局域网 IP。无论使用哪一种方式,都会同时输出仅服务器本机可用的 `Local URL`;公网探测失败时还会明确提示正在回退局域网地址。公网服务器建议显式传入地址,避免 NAT、多网卡或代理环境识别错误
```text
Setup URL: http://192.168.1.20:8091/?token=<one-time-token>
Setup URL: http://38.76.196.225:8091/?token=<one-time-token>
Local URL: http://127.0.0.1:8091/?token=<one-time-token>
```
`--public-url` 支持域名、端口、路径和已有查询参数,安装器会安全追加一次性 `token`。使用公网 IP 直连时需要在防火墙或安全组放行 ERP 端口;通过 HTTPS 反向代理安装时,应将公开域名作为 `--public-url`
首次打开该地址进入网页向导,依次完成环境检查、PostgreSQL 地址/端口/库名/账号/密码/SSL 测试、管理员账号/姓名/密码设置、数据库迁移和初始化。项目当前没有 Redis 依赖,因此向导不会显示 Redis 配置项。正式服务真实健康检查通过后,启动器才会原子写入安装锁并物理删除 `installer/``install.pending`
PostgreSQL 必须使用专用空数据库,网页中填写的账号必须是该数据库的所有者。该约束保证账号拥有 `public` schema 建表权限,并能持有安装器创建的 `pg_trgm` 扩展;只授予 `CONNECT` 权限不足以完成迁移。
@@ -211,16 +214,18 @@ curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
set -e
tmp="$(mktemp)"
trap 'rm -f -- "$tmp"' EXIT
curl -fsSL http://38.76.196.225:10099/awaioi/ERP/raw/tag/v0.3.1/install.sh -o "$tmp"
printf '%s %s\n' '89a3c45e76f500c9475cb596ea29e3518bfafdc59f36dd3c7b316ed3cdd0448c' "$tmp" | sha256sum -c -
curl -fsSL http://38.76.196.225:10099/awaioi/ERP/raw/tag/v0.3.5/install.sh -o "$tmp"
printf '%s %s\n' '76917da519895bec815bb5492b12f0894e8cff26d6754cb9b2569b45e52ed83c' "$tmp" | sha256sum -c -
sudo -E bash "$tmp" \
--gitea-url http://38.76.196.225:10099 \
--repository awaioi/ERP \
--version 0.3.5 \
--public-url http://38.76.196.225:8091 \
--allow-insecure
)
```
只有 `v0.3.1` Release 发布后这条命令才可下载安装包。固定 tag 和 SHA-256 只用于保护当前 HTTP 引导脚本不被传输途中篡改;Release 资产仍会继续执行 Ed25519 和 SHA-256 双重校验。HTTP 会暴露请求、Release 元数据和可能使用的访问令牌,不得作为生产方案。
只有 `v0.3.5` Release 发布后这条命令才可下载安装包。固定 tag 和 SHA-256 只用于保护当前 HTTP 引导脚本不被传输途中篡改;Release 资产仍会继续执行 Ed25519 和 SHA-256 双重校验。HTTP 会暴露请求、Release 元数据和可能使用的访问令牌,不得作为长期生产方案。
### 完整卸载后重装
@@ -231,7 +236,7 @@ curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
set -e
tmp="$(mktemp)"
trap 'rm -f -- "$tmp"' EXIT
curl -fsSL http://38.76.196.225:10099/awaioi/ERP/raw/tag/v0.3.1/uninstall.sh -o "$tmp"
curl -fsSL http://38.76.196.225:10099/awaioi/ERP/raw/tag/v0.3.5/uninstall.sh -o "$tmp"
printf '%s %s\n' '98c56fed2fd4d01874e4ab5a1a4f3ec42ec3e29b315ffd87385a95488d587546' "$tmp" | sha256sum -c -
sudo -E bash "$tmp" --purge-database --yes
)
@@ -256,11 +261,11 @@ Linux 默认路径:
### HTTPS 反向代理
正式服务监听 `8090`。Nginx、宝塔、Caddy 或 CDN 终止 HTTPS 后,必须把公网协议和主机转发给 Spring Boot;否则浏览器的同源 API 请求会被误判为跨域,并收到纯文本 `403 Invalid CORS request`,前端表现为“响应非 JSON (HTTP 403)”。Nginx 的代理位置至少包含:
正式安装默认监听 `8091`(可通过 `ERP_SERVER_PORT` 覆盖)。Nginx、宝塔、Caddy 或 CDN 终止 HTTPS 后,必须把公网协议和主机转发给 Spring Boot;否则浏览器的同源 API 请求会被误判为跨域,并收到纯文本 `403 Invalid CORS request`,前端表现为“响应非 JSON (HTTP 403)”。Nginx 的代理位置至少包含:
```nginx
location / {
proxy_pass http://127.0.0.1:8090;
proxy_pass http://127.0.0.1:8091;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
@@ -275,13 +280,20 @@ location / {
## 在线更新与回滚
管理员登录后进入:
管理员登录后可从以下任一入口进入:
```text
顶部工具栏 -> 系统更新
用户菜单 -> 系统更新
手机导航抽屉 -> 系统更新
应用定制平台 -> 系统更新
```
对应前端路由为 `/appdev/update`,后端 API 为 `/api/oa/system-update/*`更新过程如下:
入口只对 `ADMIN` 角色显示,对应前端路由为 `/appdev/update`,后端 API 为 `/api/oa/system-update/*`页面会显示当前版本、最新版本、检查时间、Release 更新日志、发布日期、发布文件、Ed25519/SHA-256 校验方式,以及下载、验签、安装、重启和自动回滚阶段。顶部和手机入口发现新版本时会显示版本提示。
“更新源设置”可在线修改启用状态、Gitea 地址、`owner/repository`、正式版/预览版通道、私有仓库 Token 和 HTTP 测试开关。设置原子写回 `/etc/kaidi-erp/erp.env`,只允许修改 `OA_UPDATE_*` 白名单,不会覆盖 PostgreSQL 密码等其他配置;Token 永不通过 API 回显,页面只显示“Token 已配置”。公开仓库无需填写 Token,正式环境应使用 HTTPS。
更新过程如下:
1. 从 Gitea 读取 stable channel 的最新 Release。
2. 下载归档、`SHA256SUMS` 和 Ed25519 签名(Release 里的独立安装器资产只用于首次安装)。
@@ -295,7 +307,7 @@ location / {
同一安装目录使用操作系统文件锁,不能并发执行两个更新任务。也可以手工触发:
```bash
/opt/kaidi-erp/current/bin/erp-update install 0.2.0
/opt/kaidi-erp/current/bin/erp-update install 0.3.5
```
应用回滚不等于数据库回滚。包含不可逆 Flyway 迁移的版本必须先保证旧应用仍兼容新结构,并建议在安装配置中启用:
@@ -336,8 +348,8 @@ base64 < ~/.config/kaidi-erp/release-signing-key.pem | tr -d '\n'
```bash
git switch main
git pull --ff-only origin main
git tag -a v0.2.0 -m 'Kaidi ERP v0.2.0'
git push origin v0.2.0
git tag -a v0.3.5 -m 'Kaidi ERP v0.3.5'
git push origin v0.3.5
```
发布完成后必须确认 Release 页面存在四个资产,并使用仓库中的 `distribution/release-public-key.pem` 验证签名。私钥与该公钥不匹配时打包脚本会直接失败。
@@ -346,7 +358,7 @@ git push origin v0.2.0
```bash
ERP_RELEASE_PRIVATE_KEY_FILE="$HOME/.config/kaidi-erp/release-signing-key.pem" \
bash scripts/package-release.sh 0.2.0
bash scripts/package-release.sh 0.3.5
```
## 配置参考
@@ -365,6 +377,7 @@ ERP_RELEASE_PRIVATE_KEY_FILE="$HOME/.config/kaidi-erp/release-signing-key.pem" \
| `OA_UPDATE_CHANNEL` | 更新通道 | `stable` |
| `OA_UPDATE_TOKEN` | 私有仓库下载令牌 | 空;公开仓库不需要 |
| `OA_UPDATE_ALLOW_INSECURE_HTTP` | 允许 HTTP 更新地址 | `false` |
| `ERP_PUBLIC_URL` | 首次安装向导的公网 URL,等价于 `--public-url` | 自动探测公网 IP |
| `OA_SEED_DEMO` | 是否生成演示数据 | 正式安装为 `false` |
| `ERP_UPDATE_BACKUP_MODE` | 更新前数据库备份 | `none`,可设 `pg_dump` |
| `ERP_UPDATE_HEALTH_TIMEOUT_SECONDS` | 新旧版本健康检查超时 | `120` |
@@ -438,7 +451,7 @@ Gitea 仓库尚未发布首个可安装版本,或 Release 缺少四个必需
### 反向代理后提示“响应非 JSON (HTTP 403)”
应用自身的 401/403 权限错误始终是 JSON。该提示表示 Nginx、WAF 或 Spring CORS 层提前返回了纯文本/HTML。先确认代理目标为 `http://127.0.0.1:8090`,再按“HTTPS 反向代理”一节补齐 `Host``X-Forwarded-*` 请求头;响应正文为 `Invalid CORS request` 时即可确认是协议/主机转发不完整。
应用自身的 401/403 权限错误始终是 JSON。该提示表示 Nginx、WAF 或 Spring CORS 层提前返回了纯文本/HTML。先确认代理目标为正式安装端口(默认 `http://127.0.0.1:8091`,再按“HTTPS 反向代理”一节补齐 `Host``X-Forwarded-*` 请求头;响应正文为 `Invalid CORS request` 时即可确认是协议/主机转发不完整。
### 签名验证失败