修正CEO报销财务兜底与部署护栏(07-28-ceo-finance-fallback)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-07-28 13:42:48 +08:00
父节点 37b625c5dc
当前提交 35948729cf
共修改 31 个文件,包含 980 行新增和 74 行删除
+1 -1
查看文件
@@ -16,7 +16,7 @@
- [ ] 已将 OA `Caddyfile` 站点块配置到 `/etc/caddy/Caddyfile`
- [ ] 已确认 Caddy 反代到 `.env` 中 `APP_HOST_PORT` 对应的宿主回环端口,并用 `header_up X-Forwarded-For {remote_host}` 覆盖客户端 XFF
- [ ] 已执行 `systemctl reload caddy`
- [ ] 已显式提供 `DEPLOY_SERVER`、`REMOTE_DIR`、绝对 `POSTGRES_DATA_DIR`、绝对 `STORAGE_HOST_DIR` 和 `DEPLOY_DATA_MODE`,并从本地执行发布入口
- [ ] 已显式提供 `DEPLOY_SERVER`、`REMOTE_DIR`、绝对 `POSTGRES_DATA_DIR`、绝对 `STORAGE_HOST_DIR` 和 `DEPLOY_DATA_MODE`,并确认源码同步根目录与两个数据目录任一方向均不嵌套,再从本地执行发布入口
- [ ] 已通过 `https://<oa-domain>/ready` 或首页登录完成最小探活
- [ ] 首次上线先执行系统初始化(migration + seed),不要直接导入旧库整库备份
- [ ] 若要导数据,仅准备最小主数据:部门、员工、角色、在用资产、活跃项目、合同元数据
+30 -3
查看文件
@@ -22,6 +22,7 @@ RUNTIME_SMOKE_SCRIPT="server/src/scripts/smoke-trialrun-runtime.js"
EXTERNAL_DEPS_SMOKE_SCRIPT="server/src/scripts/smoke-external-deps.js"
PROVENANCE_SCRIPT="deploy/trialrun/check-source-provenance.mjs"
STAGING_DIR=""
RSYNC_PROTECT_ARGS=()
cleanup() {
if [[ -n "$STAGING_DIR" && -d "$STAGING_DIR" ]]; then
@@ -56,7 +57,7 @@ if [[ ! "$COMPOSE_PROJECT_NAME" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
exit 1
fi
validate_data_path() {
validate_absolute_path() {
local name="$1"
local value="$2"
if [[ -z "$value" || "$value" != /* || ! "$value" =~ ^/[A-Za-z0-9._/-]+$ ]]; then
@@ -70,8 +71,16 @@ validate_data_path() {
fi
}
validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"
validate_data_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"
paths_overlap() {
local first="$1"
local second="$2"
[[ "$first" == "$second" || "$first" == "$second"/* || "$second" == "$first"/* ]]
}
validate_absolute_path REMOTE_DIR "$REMOTE_DIR"
validate_absolute_path REMOTE_ENV_FILE "$REMOTE_ENV_FILE"
validate_absolute_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"
validate_absolute_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"
if [[ "$POSTGRES_DATA_DIR" == "$STORAGE_HOST_DIR" \
|| "$POSTGRES_DATA_DIR" == "$STORAGE_HOST_DIR"/* \
|| "$STORAGE_HOST_DIR" == "$POSTGRES_DATA_DIR"/* ]]; then
@@ -82,6 +91,23 @@ if [[ "$DEPLOY_DATA_MODE" != "existing" && "$DEPLOY_DATA_MODE" != "initialize" ]
echo "[ERROR] DEPLOY_DATA_MODE 必须显式设为 existing 或 initialize" >&2
exit 1
fi
if paths_overlap "$REMOTE_DIR" "$POSTGRES_DATA_DIR"; then
echo "[ERROR] POSTGRES_DATA_DIR 不得与 REMOTE_DIR 同目录或相互嵌套,避免源码同步触碰数据库" >&2
exit 1
fi
if paths_overlap "$REMOTE_DIR" "$STORAGE_HOST_DIR"; then
echo "[ERROR] STORAGE_HOST_DIR 不得与 REMOTE_DIR 同目录或相互嵌套,避免源码同步触碰附件" >&2
exit 1
fi
if [[ "$REMOTE_ENV_FILE" == "$REMOTE_DIR" ]]; then
echo "[ERROR] REMOTE_ENV_FILE 不得等于 REMOTE_DIR" >&2
exit 1
fi
if [[ "$REMOTE_ENV_FILE" == "$REMOTE_DIR"/* ]]; then
REMOTE_ENV_RELATIVE="${REMOTE_ENV_FILE#"$REMOTE_DIR"/}"
RSYNC_PROTECT_ARGS+=(--filter="protect /${REMOTE_ENV_RELATIVE}")
RSYNC_PROTECT_ARGS+=(--exclude="/${REMOTE_ENV_RELATIVE}")
fi
if [[ ! -f "${ROOT_DIR}/${PROVENANCE_SCRIPT}" ]]; then
echo "[ERROR] 缺少 ${PROVENANCE_SCRIPT}" >&2
@@ -139,6 +165,7 @@ ssh "${DEPLOY_SERVER}" "mkdir -p '${REMOTE_DIR}'"
echo ">>> 4. 同步已验证提交到服务器"
rsync -az --delete \
"${RSYNC_PROTECT_ARGS[@]}" \
--exclude '.git' \
--exclude '.github' \
--exclude '.agent' \
+2 -2
查看文件
@@ -119,7 +119,7 @@ docker-compose -p "$COMPOSE_PROJECT_NAME" -f deploy/trialrun/docker-compose.yml
```
说明:
- `init` 是一次性任务:`migrate:deploy + seed:config + seed:trialrun`。
- `init` 是一次性任务:`migrate:deploy + seed:config + seed:trialrun`。`seed:config` 仅在审批规则表为空时创建默认三档;已有任意审批规则时必须全部保留,生产规则变更另走获批的原子事务。
- 初始化必须发生在依赖新增列的新 server 切换之前;正式部署脚本会先用新 init 镜像校验生产配置并执行迁移,再替换 server。
- 若 `TRIALRUN_ADMIN_PASSWORD` 为空,seed 会生成一次性口令并输出到控制台(请立刻保存)。
- 正式启用前,推荐先保持“空基线 + 默认种子”,不要直接导入开发库、测试库或旧 OA 整库备份。
@@ -392,7 +392,7 @@ systemctl status hyb-oa-public-app-health.service --no-pager
步骤:
1) 先做备份(见 E14 脚本)
2) 从本地仓库执行 `DEPLOY_SERVER=<ssh-target> REMOTE_DIR=<remote-dir> COMPOSE_PROJECT_NAME=<approved-compose-project> POSTGRES_DATA_DIR=<absolute-pgdata> STORAGE_HOST_DIR=<absolute-storage> DEPLOY_DATA_MODE=existing bash deploy/trialrun/deploy.sh`(推荐);脚本只同步已验证提交的 Git archive,不依赖远端 `git pull`,也不会把本地 ignored 文件带入发布。
2) 从本地仓库执行 `DEPLOY_SERVER=<ssh-target> REMOTE_DIR=<remote-dir> COMPOSE_PROJECT_NAME=<approved-compose-project> POSTGRES_DATA_DIR=<absolute-pgdata> STORAGE_HOST_DIR=<absolute-storage> DEPLOY_DATA_MODE=existing bash deploy/trialrun/deploy.sh`(推荐);脚本只同步已验证提交的 Git archive,不依赖远端 `git pull`,也不会把本地 ignored 文件带入发布。`REMOTE_DIR` 必须与 PostgreSQL、附件宿主目录完全分离;任一方向嵌套都会在 SSH 和 `rsync --delete` 前阻断。位于同步树内的 `REMOTE_ENV_FILE` 按实际相对路径动态保护。
- 在现网目录统一任务中,新的 `COMPOSE_PROJECT_NAME`、`POSTGRES_DATA_DIR`、`STORAGE_HOST_DIR` 必须与活动数据目录复制在同一停机窗口启用;本地提交本身不授权切换。
- 旧栈停止命令必须使用冻结阶段由 `docker compose ls` 实测的旧 project;不得拿新 project 名去停止旧容器。
- `artifacts/` 和 `output/` 是运行期证据/截图目录,不作为源码发布输入,也不由 `rsync --delete` 清理远端历史证据。
+8 -2
查看文件
@@ -428,8 +428,14 @@ for (const requiredFragment of [
}
for (const requiredFragment of [
'validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"',
'validate_data_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"',
'validate_absolute_path REMOTE_DIR "$REMOTE_DIR"',
'validate_absolute_path REMOTE_ENV_FILE "$REMOTE_ENV_FILE"',
'validate_absolute_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"',
'validate_absolute_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"',
'paths_overlap "$REMOTE_DIR" "$POSTGRES_DATA_DIR"',
'paths_overlap "$REMOTE_DIR" "$STORAGE_HOST_DIR"',
'RSYNC_PROTECT_ARGS+=(--filter="protect /${REMOTE_ENV_RELATIVE}")',
'RSYNC_PROTECT_ARGS+=(--exclude="/${REMOTE_ENV_RELATIVE}")',
'DEPLOY_DATA_MODE" != "existing"',
'DEPLOY_DATA_MODE" != "initialize"',
'"$POSTGRES_DATA_DIR/PG_VERSION"',