This commit is contained in:
@@ -47,24 +47,32 @@ npm run build
|
|||||||
`https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest`,生产机不执行 `git pull` 或现场编译。
|
`https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest`,生产机不执行 `git pull` 或现场编译。
|
||||||
安装脚本内置固定的发布公钥指纹,下载的应用包、清单、SBOM 和更新脚本仍须通过 RSA 签名与 SHA-256 校验。
|
安装脚本内置固定的发布公钥指纹,下载的应用包、清单、SBOM 和更新脚本仍须通过 RSA 签名与 SHA-256 校验。
|
||||||
本版只支持由运维人员预先准备的外部 MySQL 8.4.x;安装器不会安装 MySQL、创建数据库容器或修改现有
|
本版只支持由运维人员预先准备的外部 MySQL 8.4.x;安装器不会安装 MySQL、创建数据库容器或修改现有
|
||||||
PostgreSQL 18。PostgreSQL 兼容开发已冻结,不属于本次 Preview.9 发布范围。
|
PostgreSQL 18。PostgreSQL 兼容开发已冻结,不属于本次 Preview.10 发布范围。
|
||||||
|
|
||||||
执行命令的机器需预装 `bash`、`sudo`、`curl`、`mktemp` 和 `sha256sum`,并能访问目标 Gitea;`jq`、Java、
|
执行命令的机器需预装 `bash`、`sudo`、`curl`、`mktemp` 和 `sha256sum`,并能访问目标 Gitea;`jq`、Java
|
||||||
Nginx 和数据库客户端由安装器补齐。应用固定安装到 `/opt/kaidi`、`/var/lib/kaidi`、
|
和数据库客户端由安装器补齐。应用固定安装到 `/opt/kaidi`、`/var/lib/kaidi`、
|
||||||
`/var/lib/kaidi-update` 和 `/etc/kaidi`。目标机应为专用主机,或确认现有 Nginx 默认站点可以被替换且
|
`/var/lib/kaidi-update` 和 `/etc/kaidi`。安装器不安装、不启动也不修改 Nginx、Caddy、宝塔或其他反向代理。
|
||||||
80 端口可用;安装器会接管默认 HTTP 站点。
|
|
||||||
|
|
||||||
### 直接 curl 安装
|
### 直接 curl 安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.9/install.sh | sudo bash
|
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
这条命令会安装最新签名 Release,并默认进入 `/setup` 安装向导。需要在执行前独立校验安装脚本时使用:
|
这条命令会提示填写 Java 应用端口,直接回车使用 `18080`;随后安装最新签名 Release,并默认进入 `/setup`
|
||||||
|
安装向导。Java 默认只监听 `127.0.0.1:所选端口`,前端页面、API 和健康检查均由同一端口提供。无人值守安装可直接指定:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.9/install.sh -o /tmp/kaidi-install.sh
|
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh \
|
||||||
printf '%s %s\n' faf52cc902abbc2bd48571caee3f4207de50ce339b82ce88fe23f9c4ce8f89ef /tmp/kaidi-install.sh | sha256sum -c -
|
| sudo env KAIDI_APP_PORT=19090 bash
|
||||||
|
```
|
||||||
|
|
||||||
|
端口必须在 `1024-65535` 范围且未被其他程序监听;安装器会在安装前检查冲突。只有明确需要让其他主机直连 Java
|
||||||
|
时才设置 `KAIDI_SERVER_ADDRESS=0.0.0.0`,通常应保持默认回环绑定并由本机反向代理访问。需要在执行前独立校验安装脚本时使用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh -o /tmp/kaidi-install.sh
|
||||||
|
printf '%s %s\n' cc9ae48fc3e36fbeb13b0660b0d91e59bd099a1386d49df699f214285c1afc0a /tmp/kaidi-install.sh | sha256sum -c -
|
||||||
sudo bash /tmp/kaidi-install.sh
|
sudo bash /tmp/kaidi-install.sh
|
||||||
rm -f /tmp/kaidi-install.sh
|
rm -f /tmp/kaidi-install.sh
|
||||||
```
|
```
|
||||||
@@ -75,7 +83,7 @@ rm -f /tmp/kaidi-install.sh
|
|||||||
包装器会在 `sudo` 前校验 `deploy/install.sh` 的固定 SHA-256,再按同一公钥信任链安装最新签名 Release。
|
包装器会在 `sudo` 前校验 `deploy/install.sh` 的固定 SHA-256,再按同一公钥信任链安装最新签名 Release。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone --branch v1.0.0-preview.9 --depth 1 https://git.awaioi.com/ERP-Team/kaidi.git kaidi-preview
|
git clone --branch v1.0.0-preview.10 --depth 1 https://git.awaioi.com/ERP-Team/kaidi.git kaidi-preview
|
||||||
cd kaidi-preview
|
cd kaidi-preview
|
||||||
./deploy/install-from-git.sh
|
./deploy/install-from-git.sh
|
||||||
```
|
```
|
||||||
@@ -91,19 +99,35 @@ cd kaidi-preview
|
|||||||
- 使用安装器内置的 SHA-256 指纹校验 Release 公钥,再用该公钥验证发布清单 RSA 签名。
|
- 使用安装器内置的 SHA-256 指纹校验 Release 公钥,再用该公钥验证发布清单 RSA 签名。
|
||||||
- 首次安装默认启用 `/setup` 向导,不在命令行保存数据库密码;向导只接受 MySQL 8.4.x,并在提交前验证 DDL/DML 权限。
|
- 首次安装默认启用 `/setup` 向导,不在命令行保存数据库密码;向导只接受 MySQL 8.4.x,并在提交前验证 DDL/DML 权限。
|
||||||
- 安装签名 Release 到 `/opt/kaidi/releases/<version>`,以 `/opt/kaidi/current` 原子切换当前版本。
|
- 安装签名 Release 到 `/opt/kaidi/releases/<version>`,以 `/opt/kaidi/current` 原子切换当前版本。
|
||||||
- 安装 Nginx、`kaidi-finance.service`、更新监听服务和健康检查。
|
- 安装 `kaidi-finance.service`、更新监听服务和健康检查;Java 同时托管 TDesign 前端静态资源及 Vue 路由回退。
|
||||||
- 向导只初始化一个由操作者填写的 `SYSTEM_ADMIN` 管理员,不创建项目、财务、资料或演示账号。
|
- 向导只初始化一个由操作者填写的 `SYSTEM_ADMIN` 管理员,不创建项目、财务、资料或演示账号。
|
||||||
- 安装器把一次性安装码写入仅 root 可读的 `/root/kaidi-first-login.txt`;完成向导后写入锁定标记并切换正式应用。
|
- 安装器把一次性安装码写入仅 root 可读的 `/root/kaidi-first-login.txt`;完成向导后写入锁定标记并切换正式应用。
|
||||||
|
|
||||||
安装完成后先执行 `sudo cat /root/kaidi-first-login.txt`,访问其中的 `/setup` 地址完成数据库和管理员配置;完成后再访问
|
安装完成后先执行 `sudo cat /root/kaidi-first-login.txt`,其中会显示准确的反向代理目标和一次性安装码。配置反向代理后,
|
||||||
`http://SERVER_IP/` 登录。Preview 使用 HTTP 时安装器默认设置
|
通过域名的 `/setup` 完成数据库和管理员配置,再访问域名根路径登录。Preview 使用 HTTP 时安装器默认设置
|
||||||
`SESSION_COOKIE_SECURE=false`;配置 HTTPS 反向代理后,应在 `/etc/kaidi/kaidi.env` 改为
|
`SESSION_COOKIE_SECURE=false`;配置 HTTPS 反向代理后,应在 `/etc/kaidi/kaidi.env` 改为
|
||||||
`SESSION_COOKIE_SECURE=true` 并执行 `sudo systemctl restart kaidi-finance`。
|
`SESSION_COOKIE_SECURE=true` 并执行 `sudo systemctl restart kaidi-finance`。
|
||||||
|
|
||||||
安装后执行以下命令确认应用、反向代理和首次登录信息:
|
以安装端口 `19090` 为例,Nginx 只需代理到 Java,不需要单独托管前端文件:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:19090;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_connect_timeout 10s;
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
client_max_body_size 500m;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
宝塔、Caddy 或云网关使用同一个上游地址 `http://127.0.0.1:所选端口`。安装后执行以下命令确认应用和首次登录信息:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsS http://127.0.0.1/actuator/health | jq -e '.status == "UP"'
|
curl -fsS http://127.0.0.1:19090/actuator/health | jq -e '.status == "UP"'
|
||||||
sudo systemctl --no-pager --full status kaidi-finance kaidi-update.path
|
sudo systemctl --no-pager --full status kaidi-finance kaidi-update.path
|
||||||
sudo cat /root/kaidi-first-login.txt
|
sudo cat /root/kaidi-first-login.txt
|
||||||
```
|
```
|
||||||
@@ -114,7 +138,7 @@ sudo cat /root/kaidi-first-login.txt
|
|||||||
32 位服务端镜像,因此 32 位主机需要预先连接一台 MySQL 8.4 数据库,之后仍然只执行一个安装命令:
|
32 位服务端镜像,因此 32 位主机需要预先连接一台 MySQL 8.4 数据库,之后仍然只执行一个安装命令:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.9/install.sh | sudo bash
|
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
32 位主机不能运行安装器自动创建的 MySQL 容器,因此在打开向导前,需要预先创建 `kaidi_finance`,并授予安装账号该库的
|
32 位主机不能运行安装器自动创建的 MySQL 容器,因此在打开向导前,需要预先创建 `kaidi_finance`,并授予安装账号该库的
|
||||||
@@ -134,17 +158,17 @@ GRANT ALL PRIVILEGES ON kaidi_finance.* TO 'kaidi'@'KAIDI_SERVER_IP';
|
|||||||
管理员数据和运维人员新增的环境变量:
|
管理员数据和运维人员新增的环境变量:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.9/install.sh \
|
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh \
|
||||||
| sudo env KAIDI_REINSTALL=true KAIDI_SETUP_WIZARD=false bash
|
| sudo env KAIDI_REINSTALL=true KAIDI_SETUP_WIZARD=false bash
|
||||||
```
|
```
|
||||||
|
|
||||||
重装失败会恢复原应用链接、Java 运行时、环境文件、systemd 单元和 Nginx 配置;脚本会明确报告回滚不完整,
|
没有显式设置 `KAIDI_APP_PORT` 时,修复性重装会沿用 `/etc/kaidi/kaidi.env` 中的原端口。重装失败会恢复原应用链接、Java 运行时、环境文件和 systemd 单元;脚本会明确报告回滚不完整,
|
||||||
不会把恢复失败吞掉。
|
不会把恢复失败吞掉。
|
||||||
|
|
||||||
如果安装器已完成但向导尚未提交,可执行下面的命令重新生成一次性安装码;该恢复路径只接受仍处于向导模式且未锁定的安装,正式模式不会被覆盖。
|
如果安装器已完成但向导尚未提交,可执行下面的命令重新生成一次性安装码;该恢复路径只接受仍处于向导模式且未锁定的安装,正式模式不会被覆盖。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.9/install.sh \
|
curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.10/install.sh \
|
||||||
| sudo env KAIDI_REINSTALL=true bash
|
| sudo env KAIDI_REINSTALL=true bash
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -152,14 +176,12 @@ curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-previe
|
|||||||
|
|
||||||
以下命令移除应用程序和服务,但保留 `/var/lib/kaidi`、`/var/lib/kaidi-update`、`/etc/kaidi` 以及数据库,
|
以下命令移除应用程序和服务,但保留 `/var/lib/kaidi`、`/var/lib/kaidi-update`、`/etc/kaidi` 以及数据库,
|
||||||
便于审计、备份或重新安装。确认数据备份前不要删除这些保留目录或 MySQL 数据卷。
|
便于审计、备份或重新安装。确认数据备份前不要删除这些保留目录或 MySQL 数据卷。
|
||||||
安装器已经删除原 Nginx 默认站点;停用后需按该主机原有配置恢复或另行创建默认站点。
|
反向代理由运维人员独立管理,停用程序不会修改其配置。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl disable --now kaidi-update.path kaidi-update.service kaidi-finance.service
|
sudo systemctl disable --now kaidi-update.path kaidi-update.service kaidi-finance.service
|
||||||
sudo rm -f /etc/systemd/system/kaidi-finance.service /etc/systemd/system/kaidi-update.service /etc/systemd/system/kaidi-update.path
|
sudo rm -f /etc/systemd/system/kaidi-finance.service /etc/systemd/system/kaidi-update.service /etc/systemd/system/kaidi-update.path
|
||||||
sudo rm -f /etc/nginx/conf.d/kaidi-finance.conf
|
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo nginx -t && sudo systemctl reload nginx
|
|
||||||
sudo rm -rf /opt/kaidi
|
sudo rm -rf /opt/kaidi
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -182,8 +204,8 @@ act_runner 提供 `ubuntu-24.04` 标签,并在 tag 发布时执行后端、前
|
|||||||
Preview 属性由 SemVer 版本名表达。之后推送 tag 即会构建、测试、签名并发布:
|
Preview 属性由 SemVer 版本名表达。之后推送 tag 即会构建、测试、签名并发布:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git tag v1.0.0-preview.9
|
git tag v1.0.0-preview.10
|
||||||
git push origin v1.0.0-preview.9
|
git push origin v1.0.0-preview.10
|
||||||
```
|
```
|
||||||
|
|
||||||
在线更新使用独立的 TDesign 页面:隔离的系统管理员进入“系统治理 → 系统更新”。权限与配置页只管理用户、角色、数据范围、表单模板和参数版本,不配置系统名称或域名。
|
在线更新使用独立的 TDesign 页面:隔离的系统管理员进入“系统治理 → 系统更新”。权限与配置页只管理用户、角色、数据范围、表单模板和参数版本,不配置系统名称或域名。
|
||||||
@@ -196,8 +218,8 @@ URL、Token、脚本或命令。更新流程固定为:
|
|||||||
的包不能进入安装。
|
的包不能进入安装。
|
||||||
4. 安装请求持久领取到 `/var/lib/kaidi-update/processing`;进程或主机中断后由 systemd 恢复未完成事务。
|
4. 安装请求持久领取到 `/var/lib/kaidi-update/processing`;进程或主机中断后由 systemd 恢复未完成事务。
|
||||||
5. root 更新器重新验签和验哈希,确认 `mysqldump` 成功并生成权限为 `0600` 的备份,默认保留最近 5 份。
|
5. root 更新器重新验签和验哈希,确认 `mysqldump` 成功并生成权限为 `0600` 的备份,默认保留最近 5 份。
|
||||||
6. 校验更新脚本和 systemd 单元后,原子切换 updater、systemd、Nginx 和应用版本。
|
6. 校验更新脚本和 systemd 单元后,原子切换 updater、systemd 和应用版本,不修改反向代理。
|
||||||
7. 同时检查后端直连、Nginx 健康端点、更新 path unit 和静态首页。全部通过后页面显示 10 秒倒计时并自动
|
7. 同时检查 Java 健康端点、更新 path unit 和 Java 托管的静态首页。全部通过后页面显示 10 秒倒计时并自动
|
||||||
刷新;刷新或短暂断线发生在安装中时,页面会恢复 3 秒轮询。任一检查失败则恢复并验证上一版本。
|
刷新;刷新或短暂断线发生在安装中时,页面会恢复 3 秒轮询。任一检查失败则恢复并验证上一版本。
|
||||||
|
|
||||||
忙碌期间检查、下载和安装按钮保持禁用,防止重复请求;这就是更新执行冷却。10 秒只用于成功后的页面刷新,
|
忙碌期间检查、下载和安装按钮保持禁用,防止重复请求;这就是更新执行冷却。10 秒只用于成功后的页面刷新,
|
||||||
@@ -217,7 +239,7 @@ cat /var/lib/kaidi-update/status.json
|
|||||||
|
|
||||||
如果 `status.json` 显示 `FAILED` 且日志提示回滚未完成,不要删除
|
如果 `status.json` 显示 `FAILED` 且日志提示回滚未完成,不要删除
|
||||||
`/var/lib/kaidi-update/processing/request.json` 或活动事务目录。systemd 在 300 秒内连续失败 3 次后会停止自动重试,
|
`/var/lib/kaidi-update/processing/request.json` 或活动事务目录。systemd 在 300 秒内连续失败 3 次后会停止自动重试,
|
||||||
修复日志所示的磁盘、权限、Nginx 或旧版本健康问题后执行:
|
修复日志所示的磁盘、权限或旧版本健康问题后执行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl reset-failed kaidi-update.service kaidi-update.path
|
sudo systemctl reset-failed kaidi-update.service kaidi-update.path
|
||||||
@@ -234,7 +256,7 @@ cat /var/lib/kaidi-update/status.json
|
|||||||
```bash
|
```bash
|
||||||
KAIDI_RELEASE_SIGNING_KEY=/secure/release-signing-private.pem \
|
KAIDI_RELEASE_SIGNING_KEY=/secure/release-signing-private.pem \
|
||||||
KAIDI_TRUSTED_RELEASE_PUBLIC_KEY_SHA256=807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9 \
|
KAIDI_TRUSTED_RELEASE_PUBLIC_KEY_SHA256=807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9 \
|
||||||
./scripts/package-release.sh 1.0.0-preview.9
|
./scripts/package-release.sh 1.0.0-preview.10
|
||||||
KAIDI_TRUSTED_RELEASE_PUBLIC_KEY_SHA256=807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9 \
|
KAIDI_TRUSTED_RELEASE_PUBLIC_KEY_SHA256=807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9 \
|
||||||
./scripts/verify-release.sh dist/release
|
./scripts/verify-release.sh dist/release
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package com.kaidi.finance.setup;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnProperty(name = "finance.setup.enabled", havingValue = "true")
|
@ConditionalOnProperty(name = "finance.setup.enabled", havingValue = "true")
|
||||||
@@ -19,8 +21,20 @@ public class SetupSecurityConfig {
|
|||||||
.formLogin(AbstractHttpConfigurer::disable)
|
.formLogin(AbstractHttpConfigurer::disable)
|
||||||
.logout(AbstractHttpConfigurer::disable)
|
.logout(AbstractHttpConfigurer::disable)
|
||||||
.authorizeHttpRequests(authorize -> authorize
|
.authorizeHttpRequests(authorize -> authorize
|
||||||
.requestMatchers("/api/v1/setup/**", "/actuator/health/**", "/error").permitAll()
|
.requestMatchers("/api/v1/setup/**", "/actuator/health/**", "/error",
|
||||||
|
"/", "/index.html", "/favicon.ico", "/assets/**").permitAll()
|
||||||
|
.requestMatchers((RequestMatcher) request -> isSpaRoute(request)).permitAll()
|
||||||
.anyRequest().denyAll());
|
.anyRequest().denyAll());
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSpaRoute(jakarta.servlet.http.HttpServletRequest request) {
|
||||||
|
if (!HttpMethod.GET.matches(request.getMethod()) && !HttpMethod.HEAD.matches(request.getMethod())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String path = request.getRequestURI();
|
||||||
|
return !path.contains(".") && !path.equals("/api") && !path.startsWith("/api/")
|
||||||
|
&& !path.equals("/actuator") && !path.startsWith("/actuator/")
|
||||||
|
&& !path.equals("/error");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||||
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
@@ -37,7 +38,9 @@ public class SecurityConfig {
|
|||||||
.authorizeHttpRequests(authorize -> authorize
|
.authorizeHttpRequests(authorize -> authorize
|
||||||
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
||||||
.requestMatchers("/api/v1/auth/csrf", "/api/v1/auth/login", "/actuator/health/**",
|
.requestMatchers("/api/v1/auth/csrf", "/api/v1/auth/login", "/actuator/health/**",
|
||||||
"/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/error").permitAll()
|
"/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/error",
|
||||||
|
"/", "/index.html", "/favicon.ico", "/assets/**").permitAll()
|
||||||
|
.requestMatchers((RequestMatcher) request -> isSpaRoute(request)).permitAll()
|
||||||
.requestMatchers("/api/v1/**").permitAll()
|
.requestMatchers("/api/v1/**").permitAll()
|
||||||
.anyRequest().authenticated())
|
.anyRequest().authenticated())
|
||||||
.exceptionHandling(exceptions -> exceptions
|
.exceptionHandling(exceptions -> exceptions
|
||||||
@@ -48,6 +51,18 @@ public class SecurityConfig {
|
|||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSpaRoute(jakarta.servlet.http.HttpServletRequest request) {
|
||||||
|
if (!HttpMethod.GET.matches(request.getMethod()) && !HttpMethod.HEAD.matches(request.getMethod())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String path = request.getRequestURI();
|
||||||
|
return !path.contains(".") && !path.equals("/api") && !path.startsWith("/api/")
|
||||||
|
&& !path.equals("/actuator") && !path.startsWith("/actuator/")
|
||||||
|
&& !path.equals("/api-docs") && !path.startsWith("/api-docs/")
|
||||||
|
&& !path.equals("/swagger-ui.html") && !path.startsWith("/swagger-ui/")
|
||||||
|
&& !path.equals("/error");
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
PasswordEncoder passwordEncoder() {
|
PasswordEncoder passwordEncoder() {
|
||||||
return Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
|
return Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.kaidi.finance.shared.web;
|
||||||
|
|
||||||
|
import jakarta.servlet.DispatcherType;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
/** Forwards extensionless browser routes to the bundled Vue entry point. */
|
||||||
|
@Component
|
||||||
|
public class SpaForwardFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
if (isBrowserRoute(request)) {
|
||||||
|
request.getRequestDispatcher("/index.html").forward(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBrowserRoute(HttpServletRequest request) {
|
||||||
|
if (!"GET".equalsIgnoreCase(request.getMethod()) && !"HEAD".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String path = request.getRequestURI();
|
||||||
|
String contextPath = request.getContextPath();
|
||||||
|
if (contextPath != null && !contextPath.isEmpty() && path.startsWith(contextPath)) {
|
||||||
|
path = path.substring(contextPath.length());
|
||||||
|
}
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
path = "/";
|
||||||
|
}
|
||||||
|
if (isReserved(path) || path.contains(".")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return DispatcherType.REQUEST.equals(request.getDispatcherType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isReserved(String path) {
|
||||||
|
return path.equals("/api") || path.startsWith("/api/")
|
||||||
|
|| path.equals("/actuator") || path.startsWith("/actuator/")
|
||||||
|
|| path.equals("/api-docs") || path.startsWith("/api-docs/")
|
||||||
|
|| path.equals("/swagger-ui.html") || path.startsWith("/swagger-ui/")
|
||||||
|
|| path.equals("/error");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.kaidi.setup;
|
package com.kaidi.setup;
|
||||||
|
|
||||||
|
import com.kaidi.finance.setup.SetupProperties;
|
||||||
|
import com.kaidi.finance.shared.web.SpaForwardFilter;
|
||||||
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
|
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
@@ -9,7 +11,7 @@ import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
|
|||||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import com.kaidi.finance.setup.SetupProperties;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal first-run context. It deliberately does not create a business DataSource or run
|
* Minimal first-run context. It deliberately does not create a business DataSource or run
|
||||||
@@ -28,6 +30,7 @@ import com.kaidi.finance.setup.SetupProperties;
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
@EnableConfigurationProperties(SetupProperties.class)
|
@EnableConfigurationProperties(SetupProperties.class)
|
||||||
|
@Import(SpaForwardFilter.class)
|
||||||
public class SetupApplication {
|
public class SetupApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
server:
|
server:
|
||||||
port: ${SERVER_PORT:18080}
|
port: ${SERVER_PORT:18080}
|
||||||
|
address: ${SERVER_ADDRESS:127.0.0.1}
|
||||||
shutdown: graceful
|
shutdown: graceful
|
||||||
servlet:
|
servlet:
|
||||||
session:
|
session:
|
||||||
@@ -12,6 +13,9 @@ server:
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: kaidi-finance
|
name: kaidi-finance
|
||||||
|
web:
|
||||||
|
resources:
|
||||||
|
static-locations: ${FINANCE_STATIC_LOCATIONS:file:./public/}
|
||||||
profiles:
|
profiles:
|
||||||
default: local
|
default: local
|
||||||
datasource:
|
datasource:
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class SetupContextSmokeTest {
|
|||||||
registry.add("finance.setup.env-file", () -> STATE_ROOT.resolve("application.env").toString());
|
registry.add("finance.setup.env-file", () -> STATE_ROOT.resolve("application.env").toString());
|
||||||
registry.add("finance.setup.marker-file", () -> STATE_ROOT.resolve("locked").toString());
|
registry.add("finance.setup.marker-file", () -> STATE_ROOT.resolve("locked").toString());
|
||||||
registry.add("finance.setup.restart-after-complete", () -> false);
|
registry.add("finance.setup.restart-after-complete", () -> false);
|
||||||
|
registry.add("spring.web.resources.static-locations", () -> "classpath:/spa-fixture/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -49,6 +50,14 @@ class SetupContextSmokeTest {
|
|||||||
assertThat(status.getBody().path("data").path("supportedDatabaseTypes").toString())
|
assertThat(status.getBody().path("data").path("supportedDatabaseTypes").toString())
|
||||||
.isEqualTo("[\"MYSQL\"]");
|
.isEqualTo("[\"MYSQL\"]");
|
||||||
|
|
||||||
|
ResponseEntity<String> setupPage = rest.getForEntity(url("/setup"), String.class);
|
||||||
|
assertThat(setupPage.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(setupPage.getBody()).contains("kaidi-spa-fixture");
|
||||||
|
|
||||||
|
ResponseEntity<String> asset = rest.getForEntity(url("/assets/app.js"), String.class);
|
||||||
|
assertThat(asset.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(asset.getBody()).contains("kaidi-spa-fixture");
|
||||||
|
|
||||||
ResponseEntity<JsonNode> business = rest.getForEntity(url("/api/v1/auth/session"), JsonNode.class);
|
ResponseEntity<JsonNode> business = rest.getForEntity(url("/api/v1/auth/session"), JsonNode.class);
|
||||||
assertThat(business.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
assertThat(business.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.kaidi.finance.shared.web;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
|
||||||
|
class SpaForwardFilterTest {
|
||||||
|
|
||||||
|
private final SpaForwardFilter filter = new SpaForwardFilter();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void forwardsExtensionlessBrowserRouteToVueEntryPoint() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/finance/workbench");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
AtomicBoolean continued = new AtomicBoolean();
|
||||||
|
|
||||||
|
filter.doFilter(request, response, (ignoredRequest, ignoredResponse) -> continued.set(true));
|
||||||
|
|
||||||
|
assertThat(response.getForwardedUrl()).isEqualTo("/index.html");
|
||||||
|
assertThat(continued).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void leavesApiAndStaticAssetRequestsToSpringMvc() throws Exception {
|
||||||
|
assertContinues("/api/v1/unknown-resource");
|
||||||
|
assertContinues("/actuator/health");
|
||||||
|
assertContinues("/assets/index-a1b2c3.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertContinues(String uri) throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", uri);
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
AtomicBoolean continued = new AtomicBoolean();
|
||||||
|
|
||||||
|
filter.doFilter(request, response, (ignoredRequest, ignoredResponse) -> continued.set(true));
|
||||||
|
|
||||||
|
assertThat(continued).isTrue();
|
||||||
|
assertThat(response.getForwardedUrl()).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
window.__KAIDI_SPA_FIXTURE__ = 'kaidi-spa-fixture';
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head><meta charset="UTF-8"><title>kaidi-spa-fixture</title></head>
|
||||||
|
<body><div id="app">kaidi-spa-fixture</div></body>
|
||||||
|
</html>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
SPRING_PROFILES_ACTIVE=production
|
SPRING_PROFILES_ACTIVE=production
|
||||||
SERVER_PORT=18080
|
SERVER_PORT=18080
|
||||||
|
SERVER_ADDRESS=127.0.0.1
|
||||||
SESSION_COOKIE_SECURE=false
|
SESSION_COOKIE_SECURE=false
|
||||||
|
|
||||||
DB_URL=jdbc:mysql://127.0.0.1:3307/kaidi_finance?useUnicode=true&characterEncoding=utf8&connectionTimeZone=UTC&serverTimezone=UTC
|
DB_URL=jdbc:mysql://127.0.0.1:3307/kaidi_finance?useUnicode=true&characterEncoding=utf8&connectionTimeZone=UTC&serverTimezone=UTC
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ umask 077
|
|||||||
|
|
||||||
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
INSTALLER="$ROOT/deploy/install.sh"
|
INSTALLER="$ROOT/deploy/install.sh"
|
||||||
INSTALLER_SHA256=${KAIDI_INSTALLER_SHA256:-faf52cc902abbc2bd48571caee3f4207de50ce339b82ce88fe23f9c4ce8f89ef}
|
INSTALLER_SHA256=${KAIDI_INSTALLER_SHA256:-cc9ae48fc3e36fbeb13b0660b0d91e59bd099a1386d49df699f214285c1afc0a}
|
||||||
RELEASE_API_URL=${KAIDI_RELEASE_API_URL:-https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest}
|
RELEASE_API_URL=${KAIDI_RELEASE_API_URL:-https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest}
|
||||||
PUBLIC_KEY_SHA256=${KAIDI_RELEASE_PUBLIC_KEY_SHA256:-807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9}
|
PUBLIC_KEY_SHA256=${KAIDI_RELEASE_PUBLIC_KEY_SHA256:-807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9}
|
||||||
TOKEN_FILE=${KAIDI_RELEASE_TOKEN_FILE:-}
|
TOKEN_FILE=${KAIDI_RELEASE_TOKEN_FILE:-}
|
||||||
@@ -44,6 +44,8 @@ fi
|
|||||||
|
|
||||||
KAIDI_REINSTALL=${KAIDI_REINSTALL:-}
|
KAIDI_REINSTALL=${KAIDI_REINSTALL:-}
|
||||||
KAIDI_SETUP_WIZARD=${KAIDI_SETUP_WIZARD:-}
|
KAIDI_SETUP_WIZARD=${KAIDI_SETUP_WIZARD:-}
|
||||||
|
KAIDI_APP_PORT=${KAIDI_APP_PORT:-}
|
||||||
|
KAIDI_SERVER_ADDRESS=${KAIDI_SERVER_ADDRESS:-}
|
||||||
KAIDI_DB_URL=${KAIDI_DB_URL:-}
|
KAIDI_DB_URL=${KAIDI_DB_URL:-}
|
||||||
KAIDI_DB_USERNAME=${KAIDI_DB_USERNAME:-}
|
KAIDI_DB_USERNAME=${KAIDI_DB_USERNAME:-}
|
||||||
KAIDI_DB_PASSWORD=${KAIDI_DB_PASSWORD:-}
|
KAIDI_DB_PASSWORD=${KAIDI_DB_PASSWORD:-}
|
||||||
@@ -54,7 +56,8 @@ KAIDI_DB_CONTAINER=${KAIDI_DB_CONTAINER:-}
|
|||||||
KAIDI_SESSION_COOKIE_SECURE=${KAIDI_SESSION_COOKIE_SECURE:-}
|
KAIDI_SESSION_COOKIE_SECURE=${KAIDI_SESSION_COOKIE_SECURE:-}
|
||||||
KAIDI_FILE_SCANNER_ENABLED=${KAIDI_FILE_SCANNER_ENABLED:-}
|
KAIDI_FILE_SCANNER_ENABLED=${KAIDI_FILE_SCANNER_ENABLED:-}
|
||||||
|
|
||||||
for name in KAIDI_REINSTALL KAIDI_SETUP_WIZARD KAIDI_DB_URL KAIDI_DB_USERNAME KAIDI_DB_PASSWORD \
|
for name in KAIDI_REINSTALL KAIDI_SETUP_WIZARD KAIDI_APP_PORT KAIDI_SERVER_ADDRESS \
|
||||||
|
KAIDI_DB_URL KAIDI_DB_USERNAME KAIDI_DB_PASSWORD \
|
||||||
KAIDI_DB_HOST KAIDI_DB_PORT KAIDI_DB_NAME KAIDI_DB_CONTAINER \
|
KAIDI_DB_HOST KAIDI_DB_PORT KAIDI_DB_NAME KAIDI_DB_CONTAINER \
|
||||||
KAIDI_SESSION_COOKIE_SECURE KAIDI_FILE_SCANNER_ENABLED; do
|
KAIDI_SESSION_COOKIE_SECURE KAIDI_FILE_SCANNER_ENABLED; do
|
||||||
value=${!name}
|
value=${!name}
|
||||||
|
|||||||
+90
-26
@@ -11,7 +11,11 @@ RELEASE_BASE_URL=${KAIDI_RELEASE_BASE_URL:-}
|
|||||||
RELEASE_API_URL=${KAIDI_RELEASE_API_URL:-https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest}
|
RELEASE_API_URL=${KAIDI_RELEASE_API_URL:-https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest}
|
||||||
RELEASE_TOKEN=${KAIDI_RELEASE_TOKEN:-}
|
RELEASE_TOKEN=${KAIDI_RELEASE_TOKEN:-}
|
||||||
RELEASE_TOKEN_FILE=${KAIDI_RELEASE_TOKEN_FILE:-}
|
RELEASE_TOKEN_FILE=${KAIDI_RELEASE_TOKEN_FILE:-}
|
||||||
HEALTH_URL=${KAIDI_HEALTH_URL:-http://127.0.0.1:18080/actuator/health}
|
HEALTH_URL=${KAIDI_HEALTH_URL:-}
|
||||||
|
APP_INDEX_URL=${KAIDI_APP_INDEX_URL:-}
|
||||||
|
APP_PORT=${KAIDI_APP_PORT:-}
|
||||||
|
SERVER_ADDRESS=${KAIDI_SERVER_ADDRESS:-127.0.0.1}
|
||||||
|
PROXY_TARGET=
|
||||||
TRUSTED_PUBLIC_KEY_SHA256=${KAIDI_RELEASE_PUBLIC_KEY_SHA256:-807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9}
|
TRUSTED_PUBLIC_KEY_SHA256=${KAIDI_RELEASE_PUBLIC_KEY_SHA256:-807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9}
|
||||||
REINSTALL=${KAIDI_REINSTALL:-false}
|
REINSTALL=${KAIDI_REINSTALL:-false}
|
||||||
SETUP_WIZARD=${KAIDI_SETUP_WIZARD:-true}
|
SETUP_WIZARD=${KAIDI_SETUP_WIZARD:-true}
|
||||||
@@ -36,7 +40,7 @@ preflight_host() {
|
|||||||
[ "$(uname -s)" = Linux ] || die "The installer only supports Linux"
|
[ "$(uname -s)" = Linux ] || die "The installer only supports Linux"
|
||||||
[ "$APP_ROOT" = /opt/kaidi ] && [ "$STATE_ROOT" = /var/lib/kaidi ] \
|
[ "$APP_ROOT" = /opt/kaidi ] && [ "$STATE_ROOT" = /var/lib/kaidi ] \
|
||||||
&& [ "$UPDATE_STATE_ROOT" = /var/lib/kaidi-update ] && [ "$CONFIG_ROOT" = /etc/kaidi ] \
|
&& [ "$UPDATE_STATE_ROOT" = /var/lib/kaidi-update ] && [ "$CONFIG_ROOT" = /etc/kaidi ] \
|
||||||
|| die "Custom installation roots are not supported by the packaged systemd and Nginx configuration"
|
|| die "Custom installation roots are not supported by the packaged systemd configuration"
|
||||||
command -v systemctl >/dev/null 2>&1 || die "systemd is required"
|
command -v systemctl >/dev/null 2>&1 || die "systemd is required"
|
||||||
[ -d /run/systemd/system ] || die "systemd is not running as PID 1"
|
[ -d /run/systemd/system ] || die "systemd is not running as PID 1"
|
||||||
[ -d /etc/systemd/system ] || die "/etc/systemd/system is missing"
|
[ -d /etc/systemd/system ] || die "/etc/systemd/system is missing"
|
||||||
@@ -148,11 +152,11 @@ install_packages() {
|
|||||||
if command -v apt-get >/dev/null 2>&1; then
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get install -y -qq ca-certificates coreutils curl findutils gzip jq openssl tar nginx util-linux default-mysql-client
|
apt-get install -y -qq ca-certificates coreutils curl findutils gzip jq openssl tar util-linux default-mysql-client
|
||||||
elif command -v dnf >/dev/null 2>&1; then
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
dnf install -y ca-certificates coreutils curl findutils gzip jq openssl tar nginx util-linux mysql
|
dnf install -y ca-certificates coreutils curl findutils gzip jq openssl tar util-linux mysql
|
||||||
elif command -v yum >/dev/null 2>&1; then
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
yum install -y ca-certificates coreutils curl findutils gzip jq openssl tar nginx util-linux mysql
|
yum install -y ca-certificates coreutils curl findutils gzip jq openssl tar util-linux mysql
|
||||||
else
|
else
|
||||||
die "Supported package managers are apt, dnf, and yum"
|
die "Supported package managers are apt, dnf, and yum"
|
||||||
fi
|
fi
|
||||||
@@ -264,6 +268,72 @@ read_reinstall_env() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
port_is_listening() {
|
||||||
|
local port_hex files=(/proc/net/tcp)
|
||||||
|
port_hex=$(printf '%04X' "$APP_PORT")
|
||||||
|
[ -r /proc/net/tcp ] || return 1
|
||||||
|
[ ! -r /proc/net/tcp6 ] || files+=(/proc/net/tcp6)
|
||||||
|
awk -v port="$port_hex" '
|
||||||
|
toupper($2) ~ (":" port "$") && $4 == "0A" { found=1 }
|
||||||
|
END { exit(found ? 0 : 1) }
|
||||||
|
' "${files[@]}" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_app_port() {
|
||||||
|
[[ "$1" =~ ^[0-9]{1,5}$ ]] && [ "$1" -ge 1024 ] && [ "$1" -le 65535 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_app_port() {
|
||||||
|
local default_port=18080 existing_port entered probe_host probe_authority
|
||||||
|
if [ -z "$APP_PORT" ]; then
|
||||||
|
default_port=$(read_reinstall_env SERVER_PORT || true)
|
||||||
|
[[ "$default_port" =~ ^[0-9]{1,5}$ ]] || default_port=18080
|
||||||
|
if [ -t 1 ] && [ -r /dev/tty ]; then
|
||||||
|
printf '[kaidi-install] Application port [%s]: ' "$default_port" > /dev/tty
|
||||||
|
if IFS= read -r entered < /dev/tty; then
|
||||||
|
APP_PORT=${entered:-$default_port}
|
||||||
|
else
|
||||||
|
APP_PORT=$default_port
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
APP_PORT=$default_port
|
||||||
|
log "No interactive terminal detected; using application port $APP_PORT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
valid_app_port "$APP_PORT" \
|
||||||
|
|| die "KAIDI_APP_PORT must be an integer between 1024 and 65535"
|
||||||
|
case "$SERVER_ADDRESS" in
|
||||||
|
''|*[!A-Za-z0-9_.:-]*) die "KAIDI_SERVER_ADDRESS contains unsupported characters" ;;
|
||||||
|
esac
|
||||||
|
if port_is_listening; then
|
||||||
|
existing_port=$(read_existing_env SERVER_PORT || true)
|
||||||
|
if [ "$REINSTALL" = true ] && [ "$existing_port" = "$APP_PORT" ] \
|
||||||
|
&& systemctl is-active --quiet kaidi-finance.service; then
|
||||||
|
log "Application port $APP_PORT is already held by the existing Kaidi service"
|
||||||
|
else
|
||||||
|
die "Application port $APP_PORT is already in use; choose another port with KAIDI_APP_PORT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
probe_host=${KAIDI_HEALTH_HOST:-$SERVER_ADDRESS}
|
||||||
|
case "$probe_host" in
|
||||||
|
0.0.0.0) probe_host=127.0.0.1 ;;
|
||||||
|
::) probe_host=::1 ;;
|
||||||
|
esac
|
||||||
|
case "$probe_host" in
|
||||||
|
*:*) probe_authority="[$probe_host]" ;;
|
||||||
|
*) probe_authority="$probe_host" ;;
|
||||||
|
esac
|
||||||
|
if [ -z "$HEALTH_URL" ]; then
|
||||||
|
HEALTH_URL="http://${probe_authority}:${APP_PORT}/actuator/health"
|
||||||
|
fi
|
||||||
|
if [ -z "$APP_INDEX_URL" ]; then
|
||||||
|
APP_INDEX_URL="http://${probe_authority}:${APP_PORT}/"
|
||||||
|
fi
|
||||||
|
PROXY_TARGET="http://${probe_authority}:${APP_PORT}"
|
||||||
|
log "Application will bind ${SERVER_ADDRESS}:${APP_PORT}"
|
||||||
|
log "Reverse proxy target: $PROXY_TARGET"
|
||||||
|
}
|
||||||
|
|
||||||
is_semver() {
|
is_semver() {
|
||||||
[ "${#1}" -le 128 ] \
|
[ "${#1}" -le 128 ] \
|
||||||
&& LC_ALL=C grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$' \
|
&& LC_ALL=C grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$' \
|
||||||
@@ -454,8 +524,6 @@ backup_managed_state() {
|
|||||||
/etc/systemd/system/kaidi-finance.service
|
/etc/systemd/system/kaidi-finance.service
|
||||||
/etc/systemd/system/kaidi-update.service
|
/etc/systemd/system/kaidi-update.service
|
||||||
/etc/systemd/system/kaidi-update.path
|
/etc/systemd/system/kaidi-update.path
|
||||||
/etc/nginx/conf.d/kaidi-finance.conf
|
|
||||||
/etc/nginx/sites-enabled/default
|
|
||||||
)
|
)
|
||||||
BACKUP_DIR="$WORK_DIR/rollback"
|
BACKUP_DIR="$WORK_DIR/rollback"
|
||||||
mkdir -p "$BACKUP_DIR/files"
|
mkdir -p "$BACKUP_DIR/files"
|
||||||
@@ -470,8 +538,6 @@ backup_managed_state() {
|
|||||||
systemctl is-enabled --quiet kaidi-finance.service && PREVIOUS_APP_ENABLED=true || PREVIOUS_APP_ENABLED=false
|
systemctl is-enabled --quiet kaidi-finance.service && PREVIOUS_APP_ENABLED=true || PREVIOUS_APP_ENABLED=false
|
||||||
systemctl is-active --quiet kaidi-update.path && PREVIOUS_UPDATE_ACTIVE=true || PREVIOUS_UPDATE_ACTIVE=false
|
systemctl is-active --quiet kaidi-update.path && PREVIOUS_UPDATE_ACTIVE=true || PREVIOUS_UPDATE_ACTIVE=false
|
||||||
systemctl is-enabled --quiet kaidi-update.path && PREVIOUS_UPDATE_ENABLED=true || PREVIOUS_UPDATE_ENABLED=false
|
systemctl is-enabled --quiet kaidi-update.path && PREVIOUS_UPDATE_ENABLED=true || PREVIOUS_UPDATE_ENABLED=false
|
||||||
systemctl is-active --quiet nginx && PREVIOUS_NGINX_ACTIVE=true || PREVIOUS_NGINX_ACTIVE=false
|
|
||||||
systemctl is-enabled --quiet nginx && PREVIOUS_NGINX_ENABLED=true || PREVIOUS_NGINX_ENABLED=false
|
|
||||||
INSTALL_TRANSACTION_ARMED=true
|
INSTALL_TRANSACTION_ARMED=true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,10 +595,6 @@ rollback_install() {
|
|||||||
|| rollback_failed=true
|
|| rollback_failed=true
|
||||||
restore_unit_state kaidi-update.path "$PREVIOUS_UPDATE_ENABLED" "$PREVIOUS_UPDATE_ACTIVE" \
|
restore_unit_state kaidi-update.path "$PREVIOUS_UPDATE_ENABLED" "$PREVIOUS_UPDATE_ACTIVE" \
|
||||||
|| rollback_failed=true
|
|| rollback_failed=true
|
||||||
restore_unit_state nginx "$PREVIOUS_NGINX_ENABLED" "$PREVIOUS_NGINX_ACTIVE" || rollback_failed=true
|
|
||||||
if [ "$PREVIOUS_NGINX_ACTIVE" = true ]; then
|
|
||||||
nginx -t >/dev/null 2>&1 && systemctl reload nginx >/dev/null 2>&1 || rollback_failed=true
|
|
||||||
fi
|
|
||||||
set -e
|
set -e
|
||||||
[ "$rollback_failed" = false ]
|
[ "$rollback_failed" = false ]
|
||||||
}
|
}
|
||||||
@@ -542,7 +604,7 @@ cleanup() {
|
|||||||
trap - EXIT
|
trap - EXIT
|
||||||
if [ "$result" -ne 0 ] && [ "$INSTALL_TRANSACTION_ARMED" = true ] \
|
if [ "$result" -ne 0 ] && [ "$INSTALL_TRANSACTION_ARMED" = true ] \
|
||||||
&& [ "$INSTALL_COMMITTED" != true ]; then
|
&& [ "$INSTALL_COMMITTED" != true ]; then
|
||||||
rollback_install || log "ERROR: rollback was incomplete; inspect systemd and Nginx state"
|
rollback_install || log "ERROR: rollback was incomplete; inspect the managed systemd state"
|
||||||
fi
|
fi
|
||||||
[ -z "$WORK_DIR" ] || rm -rf "$WORK_DIR"
|
[ -z "$WORK_DIR" ] || rm -rf "$WORK_DIR"
|
||||||
exit "$result"
|
exit "$result"
|
||||||
@@ -550,6 +612,7 @@ cleanup() {
|
|||||||
|
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
configure_app_port
|
||||||
preflight_host
|
preflight_host
|
||||||
install_packages
|
install_packages
|
||||||
|
|
||||||
@@ -655,7 +718,8 @@ if [ "$SETUP_WIZARD" = true ]; then
|
|||||||
fi
|
fi
|
||||||
write_env_file_preserving_unknown "$CONFIG_ROOT/kaidi.env" \
|
write_env_file_preserving_unknown "$CONFIG_ROOT/kaidi.env" \
|
||||||
SPRING_PROFILES_ACTIVE production \
|
SPRING_PROFILES_ACTIVE production \
|
||||||
SERVER_PORT 18080 \
|
SERVER_PORT "$APP_PORT" \
|
||||||
|
SERVER_ADDRESS "$SERVER_ADDRESS" \
|
||||||
SESSION_COOKIE_SECURE "$SESSION_COOKIE_SECURE" \
|
SESSION_COOKIE_SECURE "$SESSION_COOKIE_SECURE" \
|
||||||
DB_URL "$DB_URL" \
|
DB_URL "$DB_URL" \
|
||||||
DB_USERNAME "$DB_USERNAME" \
|
DB_USERNAME "$DB_USERNAME" \
|
||||||
@@ -692,6 +756,7 @@ write_env_file "$CONFIG_ROOT/update.env" \
|
|||||||
KAIDI_UPDATE_STATE_ROOT "$UPDATE_STATE_ROOT" \
|
KAIDI_UPDATE_STATE_ROOT "$UPDATE_STATE_ROOT" \
|
||||||
KAIDI_SERVICE_NAME kaidi-finance.service \
|
KAIDI_SERVICE_NAME kaidi-finance.service \
|
||||||
KAIDI_HEALTH_URL "$HEALTH_URL" \
|
KAIDI_HEALTH_URL "$HEALTH_URL" \
|
||||||
|
KAIDI_APP_INDEX_URL "$APP_INDEX_URL" \
|
||||||
KAIDI_DB_CONTAINER "${KAIDI_DB_CONTAINER:-}" \
|
KAIDI_DB_CONTAINER "${KAIDI_DB_CONTAINER:-}" \
|
||||||
KAIDI_DB_HOST "$(database_host)" \
|
KAIDI_DB_HOST "$(database_host)" \
|
||||||
KAIDI_DB_PORT "$(database_port)" \
|
KAIDI_DB_PORT "$(database_port)" \
|
||||||
@@ -703,17 +768,12 @@ chmod 0600 "$CONFIG_ROOT/update.env"
|
|||||||
install -m 0644 "$RELEASE_DIR/ops/kaidi-finance.service" /etc/systemd/system/kaidi-finance.service
|
install -m 0644 "$RELEASE_DIR/ops/kaidi-finance.service" /etc/systemd/system/kaidi-finance.service
|
||||||
install -m 0644 "$RELEASE_DIR/ops/kaidi-update.service" /etc/systemd/system/kaidi-update.service
|
install -m 0644 "$RELEASE_DIR/ops/kaidi-update.service" /etc/systemd/system/kaidi-update.service
|
||||||
install -m 0644 "$RELEASE_DIR/ops/kaidi-update.path" /etc/systemd/system/kaidi-update.path
|
install -m 0644 "$RELEASE_DIR/ops/kaidi-update.path" /etc/systemd/system/kaidi-update.path
|
||||||
install -m 0644 "$RELEASE_DIR/ops/kaidi-finance.conf" /etc/nginx/conf.d/kaidi-finance.conf
|
|
||||||
rm -f /etc/nginx/sites-enabled/default
|
|
||||||
nginx -t
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable kaidi-finance.service
|
systemctl enable kaidi-finance.service
|
||||||
systemctl restart kaidi-finance.service
|
systemctl restart kaidi-finance.service
|
||||||
wait_for_health
|
wait_for_health
|
||||||
systemctl enable --now nginx
|
curl -fsS "$APP_INDEX_URL" | grep -Eiq '<!doctype|<html' \
|
||||||
systemctl reload nginx
|
|| die "Application frontend entry point is unavailable"
|
||||||
curl -fsS http://127.0.0.1/actuator/health | jq -e '.status == "UP"' >/dev/null \
|
|
||||||
|| die "Nginx proxy health check failed"
|
|
||||||
|
|
||||||
if [ "$SETUP_WIZARD" != true ]; then
|
if [ "$SETUP_WIZARD" != true ]; then
|
||||||
sed -i 's/^FINANCE_BOOTSTRAP_ENABLED="true"$/FINANCE_BOOTSTRAP_ENABLED="false"/' "$CONFIG_ROOT/kaidi.env"
|
sed -i 's/^FINANCE_BOOTSTRAP_ENABLED="true"$/FINANCE_BOOTSTRAP_ENABLED="false"/' "$CONFIG_ROOT/kaidi.env"
|
||||||
@@ -727,14 +787,16 @@ systemctl enable --now kaidi-update.path
|
|||||||
|
|
||||||
if [ "$SETUP_WIZARD" = true ]; then
|
if [ "$SETUP_WIZARD" = true ]; then
|
||||||
cat > /root/kaidi-first-login.txt <<EOF
|
cat > /root/kaidi-first-login.txt <<EOF
|
||||||
URL: http://SERVER_IP/setup
|
URL after reverse proxy: http://SERVER_IP/setup
|
||||||
|
Reverse proxy target: $PROXY_TARGET
|
||||||
Setup code: $SETUP_CODE
|
Setup code: $SETUP_CODE
|
||||||
Version: $VERSION
|
Version: $VERSION
|
||||||
EOF
|
EOF
|
||||||
chmod 0600 /root/kaidi-first-login.txt
|
chmod 0600 /root/kaidi-first-login.txt
|
||||||
elif [ "$REINSTALL" != true ]; then
|
elif [ "$REINSTALL" != true ]; then
|
||||||
cat > /root/kaidi-first-login.txt <<EOF
|
cat > /root/kaidi-first-login.txt <<EOF
|
||||||
URL: http://SERVER_IP/
|
URL after reverse proxy: http://SERVER_IP/
|
||||||
|
Reverse proxy target: $PROXY_TARGET
|
||||||
Username: admin
|
Username: admin
|
||||||
Temporary password: $ADMIN_PASSWORD
|
Temporary password: $ADMIN_PASSWORD
|
||||||
Version: $VERSION
|
Version: $VERSION
|
||||||
@@ -746,10 +808,12 @@ INSTALL_TRANSACTION_ARMED=false
|
|||||||
|
|
||||||
log "Kaidi Finance $VERSION is installed"
|
log "Kaidi Finance $VERSION is installed"
|
||||||
if [ "$SETUP_WIZARD" = true ]; then
|
if [ "$SETUP_WIZARD" = true ]; then
|
||||||
log "Open http://SERVER_IP/setup and complete the first-run wizard"
|
log "Configure your reverse proxy to $PROXY_TARGET"
|
||||||
|
log "Open /setup through your reverse-proxy domain and complete the first-run wizard"
|
||||||
log "Setup code: /root/kaidi-first-login.txt"
|
log "Setup code: /root/kaidi-first-login.txt"
|
||||||
else
|
else
|
||||||
log "Open http://SERVER_IP/ and sign in as admin"
|
log "Configure your reverse proxy to $PROXY_TARGET"
|
||||||
|
log "Open the reverse-proxy domain and sign in as admin"
|
||||||
fi
|
fi
|
||||||
if [ "$REINSTALL" != true ] && [ "$SETUP_WIZARD" != true ]; then
|
if [ "$REINSTALL" != true ] && [ "$SETUP_WIZARD" != true ]; then
|
||||||
log "Temporary credentials: /root/kaidi-first-login.txt"
|
log "Temporary credentials: /root/kaidi-first-login.txt"
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ server {
|
|||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
listen [::]:80 default_server;
|
listen [::]:80 default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
||||||
root /opt/kaidi/current/public;
|
|
||||||
index index.html;
|
|
||||||
client_max_body_size 500m;
|
client_max_body_size 500m;
|
||||||
|
|
||||||
location /api/v1/ {
|
# Optional example. The installer does not install or modify Nginx.
|
||||||
|
# Replace 18080 with the KAIDI_APP_PORT selected during installation.
|
||||||
|
location / {
|
||||||
proxy_pass http://127.0.0.1:18080;
|
proxy_pass http://127.0.0.1:18080;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
@@ -17,24 +16,4 @@ server {
|
|||||||
proxy_connect_timeout 10s;
|
proxy_connect_timeout 10s;
|
||||||
proxy_read_timeout 120s;
|
proxy_read_timeout 120s;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = /actuator/health {
|
|
||||||
proxy_pass http://127.0.0.1:18080;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
add_header X-Content-Type-Options nosniff always;
|
|
||||||
add_header Referrer-Policy same-origin always;
|
|
||||||
add_header X-Frame-Options SAMEORIGIN always;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(?:js|css|woff2?|png|jpe?g|gif|svg|ico)$ {
|
|
||||||
try_files $uri =404;
|
|
||||||
expires 7d;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
add_header X-Content-Type-Options nosniff always;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ IOSchedulingPriority=6
|
|||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
ReadWritePaths=/opt/kaidi /var/lib/kaidi /var/lib/kaidi-update /var/log/kaidi /etc/systemd/system /etc/nginx/conf.d
|
ReadWritePaths=/opt/kaidi /var/lib/kaidi /var/lib/kaidi-update /var/log/kaidi /etc/systemd/system
|
||||||
UMask=0077
|
UMask=0077
|
||||||
|
|||||||
+9
-23
@@ -19,13 +19,11 @@ RELEASE_TOKEN=${UPDATE_RELEASE_TOKEN:-}
|
|||||||
CACHE_ROOT=${KAIDI_UPDATE_CACHE_ROOT:-$STATE_ROOT/cache}
|
CACHE_ROOT=${KAIDI_UPDATE_CACHE_ROOT:-$STATE_ROOT/cache}
|
||||||
SERVICE_NAME=${KAIDI_SERVICE_NAME:-kaidi-finance.service}
|
SERVICE_NAME=${KAIDI_SERVICE_NAME:-kaidi-finance.service}
|
||||||
HEALTH_URL=${KAIDI_HEALTH_URL:-http://127.0.0.1:18080/actuator/health}
|
HEALTH_URL=${KAIDI_HEALTH_URL:-http://127.0.0.1:18080/actuator/health}
|
||||||
PUBLIC_HEALTH_URL=${KAIDI_PUBLIC_HEALTH_URL:-http://127.0.0.1/actuator/health}
|
APP_INDEX_URL=${KAIDI_APP_INDEX_URL:-http://127.0.0.1:18080/}
|
||||||
PUBLIC_INDEX_URL=${KAIDI_PUBLIC_INDEX_URL:-http://127.0.0.1/}
|
|
||||||
LOCK_FILE=$STATE_ROOT/update.lock
|
LOCK_FILE=$STATE_ROOT/update.lock
|
||||||
BACKUP_ROOT=${KAIDI_BACKUP_ROOT:-$STATE_ROOT/backups}
|
BACKUP_ROOT=${KAIDI_BACKUP_ROOT:-$STATE_ROOT/backups}
|
||||||
UPDATER_PATH=${KAIDI_UPDATER_PATH:-$APP_ROOT/bin/update.sh}
|
UPDATER_PATH=${KAIDI_UPDATER_PATH:-$APP_ROOT/bin/update.sh}
|
||||||
SYSTEMD_ROOT=${KAIDI_SYSTEMD_ROOT:-/etc/systemd/system}
|
SYSTEMD_ROOT=${KAIDI_SYSTEMD_ROOT:-/etc/systemd/system}
|
||||||
NGINX_CONFIG=${KAIDI_NGINX_CONFIG:-/etc/nginx/conf.d/kaidi-finance.conf}
|
|
||||||
LOG_ROOT=${KAIDI_LOG_ROOT:-/var/log/kaidi}
|
LOG_ROOT=${KAIDI_LOG_ROOT:-/var/log/kaidi}
|
||||||
HEALTH_ATTEMPTS=${KAIDI_UPDATE_HEALTH_ATTEMPTS:-60}
|
HEALTH_ATTEMPTS=${KAIDI_UPDATE_HEALTH_ATTEMPTS:-60}
|
||||||
HEALTH_INTERVAL_SECONDS=${KAIDI_UPDATE_HEALTH_INTERVAL_SECONDS:-2}
|
HEALTH_INTERVAL_SECONDS=${KAIDI_UPDATE_HEALTH_INTERVAL_SECONDS:-2}
|
||||||
@@ -161,10 +159,10 @@ wait_for_health() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_public_surface() {
|
verify_app_surface() {
|
||||||
wait_for_health "$PUBLIC_HEALTH_URL" || return 1
|
wait_for_health "$HEALTH_URL" || return 1
|
||||||
index_file=$(mktemp "$STATE_ROOT/work/public-index.XXXXXX")
|
index_file=$(mktemp "$STATE_ROOT/work/public-index.XXXXXX")
|
||||||
if curl -fsS "$PUBLIC_INDEX_URL" -o "$index_file" \
|
if curl -fsS "$APP_INDEX_URL" -o "$index_file" \
|
||||||
&& grep -Eiq '<!doctype|<html' "$index_file"; then
|
&& grep -Eiq '<!doctype|<html' "$index_file"; then
|
||||||
rm -f "$index_file"
|
rm -f "$index_file"
|
||||||
return 0
|
return 0
|
||||||
@@ -266,8 +264,7 @@ backup_operations() {
|
|||||||
backup_managed_file "$UPDATER_PATH" update.sh \
|
backup_managed_file "$UPDATER_PATH" update.sh \
|
||||||
&& backup_managed_file "$SYSTEMD_ROOT/kaidi-finance.service" kaidi-finance.service \
|
&& backup_managed_file "$SYSTEMD_ROOT/kaidi-finance.service" kaidi-finance.service \
|
||||||
&& backup_managed_file "$SYSTEMD_ROOT/kaidi-update.service" kaidi-update.service \
|
&& backup_managed_file "$SYSTEMD_ROOT/kaidi-update.service" kaidi-update.service \
|
||||||
&& backup_managed_file "$SYSTEMD_ROOT/kaidi-update.path" kaidi-update.path \
|
&& backup_managed_file "$SYSTEMD_ROOT/kaidi-update.path" kaidi-update.path
|
||||||
&& backup_managed_file "$NGINX_CONFIG" kaidi-finance.conf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_operations() {
|
apply_operations() {
|
||||||
@@ -278,9 +275,7 @@ apply_operations() {
|
|||||||
"$SYSTEMD_ROOT/kaidi-update.service" 0644 \
|
"$SYSTEMD_ROOT/kaidi-update.service" 0644 \
|
||||||
&& atomic_install "$RELEASE_DIR/ops/kaidi-update.path" \
|
&& atomic_install "$RELEASE_DIR/ops/kaidi-update.path" \
|
||||||
"$SYSTEMD_ROOT/kaidi-update.path" 0644 \
|
"$SYSTEMD_ROOT/kaidi-update.path" 0644 \
|
||||||
&& atomic_install "$RELEASE_DIR/ops/kaidi-finance.conf" "$NGINX_CONFIG" 0644 \
|
&& systemctl daemon-reload
|
||||||
&& systemctl daemon-reload \
|
|
||||||
&& nginx -t
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_operations() {
|
restore_operations() {
|
||||||
@@ -289,13 +284,7 @@ restore_operations() {
|
|||||||
restore_managed_file "$SYSTEMD_ROOT/kaidi-finance.service" kaidi-finance.service || restore_failed=1
|
restore_managed_file "$SYSTEMD_ROOT/kaidi-finance.service" kaidi-finance.service || restore_failed=1
|
||||||
restore_managed_file "$SYSTEMD_ROOT/kaidi-update.service" kaidi-update.service || restore_failed=1
|
restore_managed_file "$SYSTEMD_ROOT/kaidi-update.service" kaidi-update.service || restore_failed=1
|
||||||
restore_managed_file "$SYSTEMD_ROOT/kaidi-update.path" kaidi-update.path || restore_failed=1
|
restore_managed_file "$SYSTEMD_ROOT/kaidi-update.path" kaidi-update.path || restore_failed=1
|
||||||
restore_managed_file "$NGINX_CONFIG" kaidi-finance.conf || restore_failed=1
|
|
||||||
systemctl daemon-reload || restore_failed=1
|
systemctl daemon-reload || restore_failed=1
|
||||||
if nginx -t; then
|
|
||||||
systemctl reload nginx || restore_failed=1
|
|
||||||
else
|
|
||||||
restore_failed=1
|
|
||||||
fi
|
|
||||||
[ "$restore_failed" -eq 0 ]
|
[ "$restore_failed" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +327,7 @@ rollback_active_transaction() {
|
|||||||
fi
|
fi
|
||||||
restore_operations || rollback_ok=false
|
restore_operations || rollback_ok=false
|
||||||
systemctl start "$SERVICE_NAME" || rollback_ok=false
|
systemctl start "$SERVICE_NAME" || rollback_ok=false
|
||||||
if [ "$rollback_ok" = true ] && wait_for_health "$HEALTH_URL" && verify_public_surface; then
|
if [ "$rollback_ok" = true ] && verify_app_surface; then
|
||||||
remove_failed_release "$failed_release"
|
remove_failed_release "$failed_release"
|
||||||
rm -rf "$ACTIVE_TRANSACTION"
|
rm -rf "$ACTIVE_TRANSACTION"
|
||||||
fail "$reason; previous release was restored and verified"
|
fail "$reason; previous release was restored and verified"
|
||||||
@@ -499,7 +488,6 @@ fi
|
|||||||
[ -s "$WORK_DIR/extracted/ops/kaidi-finance.service" ] || fail "Release application unit is missing"
|
[ -s "$WORK_DIR/extracted/ops/kaidi-finance.service" ] || fail "Release application unit is missing"
|
||||||
[ -s "$WORK_DIR/extracted/ops/kaidi-update.service" ] || fail "Release updater unit is missing"
|
[ -s "$WORK_DIR/extracted/ops/kaidi-update.service" ] || fail "Release updater unit is missing"
|
||||||
[ -s "$WORK_DIR/extracted/ops/kaidi-update.path" ] || fail "Release updater path unit is missing"
|
[ -s "$WORK_DIR/extracted/ops/kaidi-update.path" ] || fail "Release updater path unit is missing"
|
||||||
[ -s "$WORK_DIR/extracted/ops/kaidi-finance.conf" ] || fail "Release Nginx configuration is missing"
|
|
||||||
validate_release_operations || fail "Release operations validation failed"
|
validate_release_operations || fail "Release operations validation failed"
|
||||||
|
|
||||||
if [ "$REQUEST_ACTION" = DOWNLOAD ]; then
|
if [ "$REQUEST_ACTION" = DOWNLOAD ]; then
|
||||||
@@ -562,10 +550,8 @@ write_transaction_value phase APP_SWITCHED
|
|||||||
status RUNNING "Starting and verifying release $TARGET_VERSION" "$TARGET_VERSION"
|
status RUNNING "Starting and verifying release $TARGET_VERSION" "$TARGET_VERSION"
|
||||||
write_transaction_value phase HEALTH_CHECKING
|
write_transaction_value phase HEALTH_CHECKING
|
||||||
if systemctl start "$SERVICE_NAME" \
|
if systemctl start "$SERVICE_NAME" \
|
||||||
&& wait_for_health "$HEALTH_URL" \
|
|
||||||
&& systemctl reload nginx \
|
|
||||||
&& systemctl is-active --quiet kaidi-update.path \
|
&& systemctl is-active --quiet kaidi-update.path \
|
||||||
&& verify_public_surface; then
|
&& verify_app_surface; then
|
||||||
write_transaction_value phase COMMITTED
|
write_transaction_value phase COMMITTED
|
||||||
status SUCCEEDED "Release $TARGET_VERSION is running" "$TARGET_VERSION"
|
status SUCCEEDED "Release $TARGET_VERSION is running" "$TARGET_VERSION"
|
||||||
TERMINAL_STATUS_WRITTEN=true
|
TERMINAL_STATUS_WRITTEN=true
|
||||||
@@ -574,4 +560,4 @@ if systemctl start "$SERVICE_NAME" \
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rollback_active_transaction "Release health or public-surface verification failed"
|
rollback_active_transaction "Release health or application-surface verification failed"
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ cp "$JAR" "$STAGE/app.jar"
|
|||||||
cp -R "$ROOT/frontend/dist/." "$STAGE/public/"
|
cp -R "$ROOT/frontend/dist/." "$STAGE/public/"
|
||||||
printf '%s\n' "$VERSION" > "$STAGE/VERSION"
|
printf '%s\n' "$VERSION" > "$STAGE/VERSION"
|
||||||
cp "$ROOT/deploy/update.sh" "$STAGE/ops/update.sh"
|
cp "$ROOT/deploy/update.sh" "$STAGE/ops/update.sh"
|
||||||
|
# Kept in the archive so Preview.9's updater can complete the one-time transition.
|
||||||
cp "$ROOT/deploy/nginx/kaidi-finance.conf" "$STAGE/ops/kaidi-finance.conf"
|
cp "$ROOT/deploy/nginx/kaidi-finance.conf" "$STAGE/ops/kaidi-finance.conf"
|
||||||
cp "$ROOT/deploy/systemd/kaidi-finance.service" "$STAGE/ops/kaidi-finance.service"
|
cp "$ROOT/deploy/systemd/kaidi-finance.service" "$STAGE/ops/kaidi-finance.service"
|
||||||
cp "$ROOT/deploy/systemd/kaidi-update.service" "$STAGE/ops/kaidi-update.service"
|
cp "$ROOT/deploy/systemd/kaidi-update.service" "$STAGE/ops/kaidi-update.service"
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ set -Eeuo pipefail
|
|||||||
|
|
||||||
[ "$KAIDI_RELEASE_API_URL" = 'https://git.example.test/api/v1/repos/TEAM/REPO/releases/latest' ]
|
[ "$KAIDI_RELEASE_API_URL" = 'https://git.example.test/api/v1/repos/TEAM/REPO/releases/latest' ]
|
||||||
[ "$KAIDI_RELEASE_PUBLIC_KEY_SHA256" = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ]
|
[ "$KAIDI_RELEASE_PUBLIC_KEY_SHA256" = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ]
|
||||||
|
if [ "${EXPECT_PORT:-false}" = true ]; then
|
||||||
|
[ "$KAIDI_APP_PORT" = '19090' ]
|
||||||
|
[ "$KAIDI_SERVER_ADDRESS" = '127.0.0.1' ]
|
||||||
|
fi
|
||||||
if [ "${KAIDI_SETUP_WIZARD:-false}" = true ]; then
|
if [ "${KAIDI_SETUP_WIZARD:-false}" = true ]; then
|
||||||
[ -z "${KAIDI_DB_URL:-}${KAIDI_DB_USERNAME:-}${KAIDI_DB_PASSWORD:-}" ]
|
[ -z "${KAIDI_DB_URL:-}${KAIDI_DB_USERNAME:-}${KAIDI_DB_PASSWORD:-}" ]
|
||||||
else
|
else
|
||||||
@@ -44,9 +48,12 @@ chmod 0600 "$FIXTURE/token"
|
|||||||
installer_sha256=$(sha256sum "$FIXTURE/repo/deploy/install.sh" | awk '{print $1}')
|
installer_sha256=$(sha256sum "$FIXTURE/repo/deploy/install.sh" | awk '{print $1}')
|
||||||
|
|
||||||
PATH="$FIXTURE/bin:$PATH" \
|
PATH="$FIXTURE/bin:$PATH" \
|
||||||
|
EXPECT_PORT=true \
|
||||||
KAIDI_INSTALLER_SHA256="$installer_sha256" \
|
KAIDI_INSTALLER_SHA256="$installer_sha256" \
|
||||||
KAIDI_RELEASE_API_URL=https://git.example.test/api/v1/repos/TEAM/REPO/releases/latest \
|
KAIDI_RELEASE_API_URL=https://git.example.test/api/v1/repos/TEAM/REPO/releases/latest \
|
||||||
KAIDI_RELEASE_PUBLIC_KEY_SHA256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
|
KAIDI_RELEASE_PUBLIC_KEY_SHA256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
|
||||||
|
KAIDI_APP_PORT=19090 \
|
||||||
|
KAIDI_SERVER_ADDRESS=127.0.0.1 \
|
||||||
KAIDI_DB_URL=jdbc:mysql://DB_HOST:3306/kaidi_finance \
|
KAIDI_DB_URL=jdbc:mysql://DB_HOST:3306/kaidi_finance \
|
||||||
KAIDI_DB_USERNAME=kaidi \
|
KAIDI_DB_USERNAME=kaidi \
|
||||||
KAIDI_DB_PASSWORD=fixture-password \
|
KAIDI_DB_PASSWORD=fixture-password \
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ fail() {
|
|||||||
sed -n '/^read_existing_env()/,/^}/p' "$ROOT/deploy/install.sh"
|
sed -n '/^read_existing_env()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
sed -n '/^read_setup_env()/,/^}/p' "$ROOT/deploy/install.sh"
|
sed -n '/^read_setup_env()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
sed -n '/^read_reinstall_env()/,/^}/p' "$ROOT/deploy/install.sh"
|
sed -n '/^read_reinstall_env()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
|
sed -n '/^port_is_listening()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
|
sed -n '/^valid_app_port()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
|
sed -n '/^configure_app_port()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
sed -n '/^is_semver()/,/^}/p' "$ROOT/deploy/install.sh"
|
sed -n '/^is_semver()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
sed -n '/^write_env_file_preserving_unknown()/,/^}/p' "$ROOT/deploy/install.sh"
|
sed -n '/^write_env_file_preserving_unknown()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
sed -n '/^azul_arch()/,/^}/p' "$ROOT/deploy/install.sh"
|
sed -n '/^azul_arch()/,/^}/p' "$ROOT/deploy/install.sh"
|
||||||
@@ -78,6 +81,31 @@ grep -qx 'jdbc:mysql://runtime/kaidi_finance' <(read_reinstall_env DB_URL) \
|
|||||||
grep -qx 'runtime-user' <(read_reinstall_env DB_USERNAME) \
|
grep -qx 'runtime-user' <(read_reinstall_env DB_USERNAME) \
|
||||||
|| fail 'reinstall did not prefer the completed setup runtime database user'
|
|| fail 'reinstall did not prefer the completed setup runtime database user'
|
||||||
|
|
||||||
|
for port in 1024 18080 65535; do
|
||||||
|
valid_app_port "$port" || fail "installer rejected valid application port $port"
|
||||||
|
done
|
||||||
|
for port in 0 80 1023 65536 invalid 18080.0; do
|
||||||
|
! valid_app_port "$port" || fail "installer accepted invalid application port $port"
|
||||||
|
done
|
||||||
|
port_is_listening() { return 1; }
|
||||||
|
log() { printf '%s\n' "$*" >/dev/null; }
|
||||||
|
# shellcheck disable=SC2329 # Invoked by the extracted installer helper.
|
||||||
|
die() { printf '%s\n' "$*" >&2; return 1; }
|
||||||
|
export APP_PORT=19090 SERVER_ADDRESS=127.0.0.1 HEALTH_URL='' APP_INDEX_URL='' REINSTALL=false
|
||||||
|
configure_app_port
|
||||||
|
[ "$HEALTH_URL" = 'http://127.0.0.1:19090/actuator/health' ] \
|
||||||
|
|| fail 'selected application port did not reach the health URL'
|
||||||
|
[ "$APP_INDEX_URL" = 'http://127.0.0.1:19090/' ] \
|
||||||
|
|| fail 'selected application port did not reach the frontend URL'
|
||||||
|
export APP_PORT=19091 SERVER_ADDRESS=::1 HEALTH_URL='' APP_INDEX_URL=''
|
||||||
|
configure_app_port
|
||||||
|
[ "$HEALTH_URL" = 'http://[::1]:19091/actuator/health' ] \
|
||||||
|
|| fail 'IPv6 bind address did not produce a bracketed health URL'
|
||||||
|
[ "$APP_INDEX_URL" = 'http://[::1]:19091/' ] \
|
||||||
|
|| fail 'IPv6 bind address did not produce a bracketed frontend URL'
|
||||||
|
[ "$PROXY_TARGET" = 'http://[::1]:19091' ] \
|
||||||
|
|| fail 'IPv6 bind address did not produce a bracketed reverse-proxy target'
|
||||||
|
|
||||||
for version in 0.0.0 1.2.3-alpha- 1.2.3--alpha 1.2.3-alpha+build.07; do
|
for version in 0.0.0 1.2.3-alpha- 1.2.3--alpha 1.2.3-alpha+build.07; do
|
||||||
is_semver "$version" || fail "installer rejected valid SemVer $version"
|
is_semver "$version" || fail "installer rejected valid SemVer $version"
|
||||||
"$ROOT/scripts/check-semver.sh" "$version" || fail "release workflow rejected valid SemVer $version"
|
"$ROOT/scripts/check-semver.sh" "$version" || fail "release workflow rejected valid SemVer $version"
|
||||||
@@ -218,6 +246,14 @@ grep -Fq 'KAIDI_SETUP_WIZARD' "$ROOT/deploy/install.sh" \
|
|||||||
|| fail 'installer no longer supports first-run setup mode'
|
|| fail 'installer no longer supports first-run setup mode'
|
||||||
grep -Fq 'FINANCE_SETUP_TOKEN_SHA256' "$ROOT/deploy/install.sh" \
|
grep -Fq 'FINANCE_SETUP_TOKEN_SHA256' "$ROOT/deploy/install.sh" \
|
||||||
|| fail 'installer no longer writes the one-time setup-code hash'
|
|| fail 'installer no longer writes the one-time setup-code hash'
|
||||||
|
# shellcheck disable=SC2016 # Match literal installer source.
|
||||||
|
grep -Fq 'SERVER_PORT "$APP_PORT"' "$ROOT/deploy/install.sh" \
|
||||||
|
|| fail 'installer does not persist the selected application port'
|
||||||
|
# shellcheck disable=SC2016 # Match literal installer source.
|
||||||
|
grep -Fq 'SERVER_ADDRESS "$SERVER_ADDRESS"' "$ROOT/deploy/install.sh" \
|
||||||
|
|| fail 'installer does not persist the selected bind address'
|
||||||
|
! grep -Eqi 'nginx|/etc/nginx/' "$ROOT/deploy/install.sh" \
|
||||||
|
|| fail 'installer must not install, start, or modify Nginx'
|
||||||
grep -Fq 'EnvironmentFile=-/var/lib/kaidi/setup/application.env' \
|
grep -Fq 'EnvironmentFile=-/var/lib/kaidi/setup/application.env' \
|
||||||
"$ROOT/deploy/systemd/kaidi-finance.service" \
|
"$ROOT/deploy/systemd/kaidi-finance.service" \
|
||||||
|| fail 'application service no longer loads the setup-completion environment'
|
|| fail 'application service no longer loads the setup-completion environment'
|
||||||
@@ -230,4 +266,4 @@ grep -Fq 'chown root:kaidi "$UPDATE_STATE_ROOT"' "$ROOT/deploy/install.sh" \
|
|||||||
grep -Fq 'install.sh | sudo bash' "$ROOT/README.md" \
|
grep -Fq 'install.sh | sudo bash' "$ROOT/README.md" \
|
||||||
|| fail 'README does not document the public one-line setup-wizard install path'
|
|| fail 'README does not document the public one-line setup-wizard install path'
|
||||||
|
|
||||||
printf 'Install configuration, public Gitea, optional private token, i686, setup wizard, and MySQL 8.4 fixtures passed\n'
|
printf 'Install configuration, custom port, public Gitea, optional private token, i686, setup wizard, and MySQL 8.4 fixtures passed\n'
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ esac
|
|||||||
if [ -n "$output" ]; then
|
if [ -n "$output" ]; then
|
||||||
if [ -n "${MOCK_RELEASE_API_URL:-}" ] && [ "$url" = "$MOCK_RELEASE_API_URL" ]; then
|
if [ -n "${MOCK_RELEASE_API_URL:-}" ] && [ "$url" = "$MOCK_RELEASE_API_URL" ]; then
|
||||||
cp "$FIXTURE_RELEASE_ROOT/release-api.json" "$output"
|
cp "$FIXTURE_RELEASE_ROOT/release-api.json" "$output"
|
||||||
elif [ "$url" = "${KAIDI_PUBLIC_INDEX_URL:-http://127.0.0.1/}" ]; then
|
elif [ "$url" = "${KAIDI_APP_INDEX_URL:-http://127.0.0.1:18080/}" ]; then
|
||||||
cp "$MOCK_APP_ROOT/current/public/index.html" "$output"
|
cp "$MOCK_APP_ROOT/current/public/index.html" "$output"
|
||||||
else
|
else
|
||||||
[ "${MOCK_DOWNLOAD_FAILURE:-}" != "${url##*/}" ] || exit 22
|
[ "${MOCK_DOWNLOAD_FAILURE:-}" != "${url##*/}" ] || exit 22
|
||||||
@@ -75,12 +75,6 @@ SH
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
printf '%s\n' "$*" >> "$MOCK_SYSTEMCTL_LOG"
|
printf '%s\n' "$*" >> "$MOCK_SYSTEMCTL_LOG"
|
||||||
exit 0
|
exit 0
|
||||||
SH
|
|
||||||
|
|
||||||
cat > "$mock_bin/nginx" <<'SH'
|
|
||||||
#!/bin/sh
|
|
||||||
printf '%s\n' "$*" >> "$MOCK_NGINX_LOG"
|
|
||||||
exit 0
|
|
||||||
SH
|
SH
|
||||||
|
|
||||||
cat > "$mock_bin/flock" <<'SH'
|
cat > "$mock_bin/flock" <<'SH'
|
||||||
@@ -138,7 +132,7 @@ build_release() {
|
|||||||
printf '%s\n' "$version" > "$stage/VERSION"
|
printf '%s\n' "$version" > "$stage/VERSION"
|
||||||
printf '#!/bin/sh\nprintf "new updater\\n"\n' > "$stage/ops/update.sh"
|
printf '#!/bin/sh\nprintf "new updater\\n"\n' > "$stage/ops/update.sh"
|
||||||
chmod 0755 "$stage/ops/update.sh"
|
chmod 0755 "$stage/ops/update.sh"
|
||||||
for name in kaidi-finance.service kaidi-update.service kaidi-update.path kaidi-finance.conf; do
|
for name in kaidi-finance.service kaidi-update.service kaidi-update.path; do
|
||||||
printf 'new %s\n' "$name" > "$stage/ops/$name"
|
printf 'new %s\n' "$name" > "$stage/ops/$name"
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -174,7 +168,7 @@ prepare_installation() {
|
|||||||
local fixture=$1
|
local fixture=$1
|
||||||
local version=$2
|
local version=$2
|
||||||
mkdir -p "$fixture/app/releases/1.0.0-preview.1/public" "$fixture/app/bin" \
|
mkdir -p "$fixture/app/releases/1.0.0-preview.1/public" "$fixture/app/bin" \
|
||||||
"$fixture/state/inbox" "$fixture/systemd" "$fixture/nginx" "$fixture/log"
|
"$fixture/state/inbox" "$fixture/systemd" "$fixture/log"
|
||||||
printf 'old application\n' > "$fixture/app/releases/1.0.0-preview.1/app.jar"
|
printf 'old application\n' > "$fixture/app/releases/1.0.0-preview.1/app.jar"
|
||||||
printf '<!doctype html><title>old</title>\n' > "$fixture/app/releases/1.0.0-preview.1/public/index.html"
|
printf '<!doctype html><title>old</title>\n' > "$fixture/app/releases/1.0.0-preview.1/public/index.html"
|
||||||
printf '1.0.0-preview.1\n' > "$fixture/app/releases/1.0.0-preview.1/VERSION"
|
printf '1.0.0-preview.1\n' > "$fixture/app/releases/1.0.0-preview.1/VERSION"
|
||||||
@@ -184,7 +178,6 @@ prepare_installation() {
|
|||||||
for name in kaidi-finance.service kaidi-update.service kaidi-update.path; do
|
for name in kaidi-finance.service kaidi-update.service kaidi-update.path; do
|
||||||
printf 'old %s\n' "$name" > "$fixture/systemd/$name"
|
printf 'old %s\n' "$name" > "$fixture/systemd/$name"
|
||||||
done
|
done
|
||||||
printf 'old kaidi-finance.conf\n' > "$fixture/nginx/kaidi-finance.conf"
|
|
||||||
write_request "$fixture" "$version" DOWNLOAD
|
write_request "$fixture" "$version" DOWNLOAD
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,12 +220,10 @@ run_update() {
|
|||||||
MOCK_RELEASE_ORIGIN="${FIXTURE_RELEASE_ORIGIN:-https://release.fixture.invalid}" \
|
MOCK_RELEASE_ORIGIN="${FIXTURE_RELEASE_ORIGIN:-https://release.fixture.invalid}" \
|
||||||
MOCK_EXPECT_RELEASE_TOKEN="${FIXTURE_RELEASE_TOKEN-}" \
|
MOCK_EXPECT_RELEASE_TOKEN="${FIXTURE_RELEASE_TOKEN-}" \
|
||||||
MOCK_SYSTEMCTL_LOG="$fixture/systemctl.log" \
|
MOCK_SYSTEMCTL_LOG="$fixture/systemctl.log" \
|
||||||
MOCK_NGINX_LOG="$fixture/nginx.log" \
|
|
||||||
KAIDI_APP_ROOT="$fixture/app" \
|
KAIDI_APP_ROOT="$fixture/app" \
|
||||||
KAIDI_UPDATE_STATE_ROOT="$fixture/state" \
|
KAIDI_UPDATE_STATE_ROOT="$fixture/state" \
|
||||||
KAIDI_LOG_ROOT="$fixture/log" \
|
KAIDI_LOG_ROOT="$fixture/log" \
|
||||||
KAIDI_SYSTEMD_ROOT="$fixture/systemd" \
|
KAIDI_SYSTEMD_ROOT="$fixture/systemd" \
|
||||||
KAIDI_NGINX_CONFIG="$fixture/nginx/kaidi-finance.conf" \
|
|
||||||
KAIDI_UPDATER_PATH="$fixture/app/bin/update.sh" \
|
KAIDI_UPDATER_PATH="$fixture/app/bin/update.sh" \
|
||||||
KAIDI_SKIP_DB_BACKUP="$skip_backup" \
|
KAIDI_SKIP_DB_BACKUP="$skip_backup" \
|
||||||
KAIDI_UPDATE_HEALTH_ATTEMPTS=1 \
|
KAIDI_UPDATE_HEALTH_ATTEMPTS=1 \
|
||||||
@@ -243,9 +234,8 @@ run_update() {
|
|||||||
UPDATE_PUBLIC_KEY="$fixture/release-public.pem" \
|
UPDATE_PUBLIC_KEY="$fixture/release-public.pem" \
|
||||||
UPDATE_REQUEST_FILE="$fixture/state/inbox/request.json" \
|
UPDATE_REQUEST_FILE="$fixture/state/inbox/request.json" \
|
||||||
UPDATE_STATUS_FILE="$fixture/state/status.json" \
|
UPDATE_STATUS_FILE="$fixture/state/status.json" \
|
||||||
KAIDI_HEALTH_URL=http://127.0.0.1:18080/actuator/health \
|
KAIDI_HEALTH_URL=http://127.0.0.1:19090/actuator/health \
|
||||||
KAIDI_PUBLIC_HEALTH_URL=http://127.0.0.1/actuator/health \
|
KAIDI_APP_INDEX_URL=http://127.0.0.1:19090/ \
|
||||||
KAIDI_PUBLIC_INDEX_URL=http://127.0.0.1/ \
|
|
||||||
"$ROOT/deploy/update.sh"
|
"$ROOT/deploy/update.sh"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +309,7 @@ assert_success_case() {
|
|||||||
[ ! -e "$fixture/state/processing/request.json" ] \
|
[ ! -e "$fixture/state/processing/request.json" ] \
|
||||||
|| fail 'success case left a claimed request behind'
|
|| fail 'success case left a claimed request behind'
|
||||||
grep -qx 'daemon-reload' "$fixture/systemctl.log" || fail 'systemd units were not reloaded'
|
grep -qx 'daemon-reload' "$fixture/systemctl.log" || fail 'systemd units were not reloaded'
|
||||||
grep -qx 'reload nginx' "$fixture/systemctl.log" || fail 'Nginx was not reloaded'
|
! grep -qi nginx "$fixture/systemctl.log" || fail 'updater unexpectedly managed Nginx'
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_rollback_case() {
|
assert_rollback_case() {
|
||||||
|
|||||||
Reference in New Issue
Block a user