补强生产数据路径与备份来源护栏(07-26-internal-ops-recovery-baseline)
这个提交包含在:
@@ -99,7 +99,7 @@ IM_NOTIFIER_ENABLED=true
|
||||
|
||||
# ===== 数据库(Compose 内部连接) =====
|
||||
# Compose 项目名由 deploy/timer 命令通过 -p 显式传入,不写入本文件。
|
||||
# 数据目录默认相对 deploy/trialrun/;生产切换时必须与项目名在同一停机窗口确认和启用。
|
||||
# 以下相对目录仅供本地直接 Compose;正式 deploy.sh 必须另行显式传入绝对路径和 DEPLOY_DATA_MODE。
|
||||
POSTGRES_DATA_DIR=./data/postgres
|
||||
POSTGRES_DB=<managed-db-name>
|
||||
POSTGRES_USER=<managed-db-user>
|
||||
|
||||
+11
-3
@@ -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`,并从本地执行 `bash scripts/qa/run-release-gate.sh all` 或至少执行 `bash deploy/trialrun/deploy.sh`
|
||||
- [ ] 已显式提供 `DEPLOY_SERVER`、`REMOTE_DIR`、绝对 `POSTGRES_DATA_DIR`、绝对 `STORAGE_HOST_DIR` 和 `DEPLOY_DATA_MODE`,并从本地执行发布入口
|
||||
- [ ] 已通过 `https://<oa-domain>/ready` 或首页登录完成最小探活
|
||||
- [ ] 首次上线先执行系统初始化(migration + seed),不要直接导入旧库整库备份
|
||||
- [ ] 若要导数据,仅准备最小主数据:部门、员工、角色、在用资产、活跃项目、合同元数据
|
||||
@@ -26,8 +26,12 @@
|
||||
|
||||
## A. 发布前(Pre-flight)
|
||||
- [ ] 已冻结唯一 `COMPOSE_PROJECT_NAME`,所有 deploy、timer、runbook 命令均显式使用 `-p`,未依赖目录名或固定容器名
|
||||
- [ ] 已用 `docker compose ls` 实测旧 Compose project;停止旧栈命令使用旧 project,新栈使用批准的新 project
|
||||
- [ ] 已核对 `POSTGRES_DATA_DIR` 与 `STORAGE_HOST_DIR` 指向活动数据;项目名与数据目录只在同一次获批停机窗口切换
|
||||
- [ ] 正式 deploy 使用绝对数据路径;既有实例使用 `DEPLOY_DATA_MODE=existing`,PGDATA 已存在且含 `PG_VERSION`,附件目录已存在
|
||||
- [ ] 已证明没有两个 PostgreSQL 实例同时挂载同一 PGDATA;任何不一致均停止发布
|
||||
- [ ] 已记录活动 server/db 容器、不可变镜像 ID(及可用 repo digest)、Caddy 目标、挂载源、环境摘要,不记录 env 正文
|
||||
- [ ] 已冻结成功 migration 数量(当前预期 58)和关键业务表逐表计数基线
|
||||
- [ ] 确认当前版本已打 tag:`git tag -a trialrun-YYYYMMDD-NN -m "trialrun release"`
|
||||
- [ ] 本地门禁:`./scripts/check-minimal.sh`
|
||||
- [ ] 部署契约校验:`node deploy/trialrun/validate-env-contract.js`
|
||||
@@ -36,17 +40,21 @@
|
||||
- [ ] 全量回归(建议):`cd server && RUN_AUDIT_REGRESSION=1 RUN_OCR_REGRESSION=1 ./scripts/regression-all.sh`
|
||||
- [ ] 备份(DB + 文件存储):
|
||||
- [ ] `set -a && source deploy/trialrun/.env && set +a`
|
||||
- [ ] `DB_CONTAINER="$(docker-compose -p "$COMPOSE_PROJECT_NAME" -f deploy/trialrun/docker-compose.yml --env-file deploy/trialrun/.env ps -q db)"` 返回唯一非空容器 ID
|
||||
- [ ] `DB_TOOL_MODE=docker DB_CONTAINER="$DB_CONTAINER" STORAGE_DIR_HOST=<verified-active-storage-host-directory> ./scripts/backup-all.sh`
|
||||
- [ ] `mapfile` 读取 `docker-compose -p "$COMPOSE_PROJECT_NAME" ... ps -q db` 后,容器数量严格等于 1
|
||||
- [ ] `DB_TOOL_MODE=docker DB_CONTAINER="$DB_CONTAINER" STORAGE_DIR_HOST=<verified-active-storage-host-directory> SOURCE_IMAGE_DIGEST=<verified-sha256-image-id> ./scripts/backup-all.sh`
|
||||
- [ ] `DB_CONTAINER` 或 `STORAGE_DIR_HOST` 缺失时备份硬失败,没有回退到历史容器名或仓库默认附件目录
|
||||
- [ ] manifest 至少含真实 `buildSha` 或活动 `sourceImageDigest`;旧实例无 build SHA 时未填写待部署提交
|
||||
- [ ] 同一批次包含 `database.dump`、`storage.tar.gz`、`manifest.json`,两份 SHA-256 校验通过
|
||||
- [ ] 完整批次已复制到获批异机/异盘,并记录保留周期
|
||||
- [ ] 同批次已在隔离影子目标恢复:migration 数、关键业务表逐表计数、附件普通文件数均与冻结基线一致,兼容镜像 `/ready` 通过
|
||||
- [ ] 停机复制方案已冻结:旧 PostgreSQL 完全停止后复制到空新目录并保留所有权/权限/链接/扩展属性;旧目录原位保留且观察期内不再写入
|
||||
|
||||
## B. 发布(Deploy)
|
||||
- [ ] 本地工作树 clean,目标 commit 已进入 upstream;部署日志中的 commit 与 `/ready.data.buildSha` 一致
|
||||
- [ ] **EXTERNAL_GATED**:操作人已在边缘暂停 `/api/auth/forgot-password`、`/api/auth/reset-password`、`/api/auth/email-login/code`、`/api/auth/email-login/verify`;部署脚本不会自动修改边缘配置,未人工确认四端点关闭不得发布,配置预检、迁移、新应用和 smoke 全部通过后才人工恢复
|
||||
- [ ] 标准路径已成功刷新 upstream;若因受控离线恢复使用 unpublished override,日志包含批准原因码和 `UNPUBLISHED-OVERRIDE` 标识
|
||||
- [ ] 标准发布通过 `bash deploy/trialrun/deploy.sh` 执行;脚本自动注入完整后端 SHA 与 12 位前端 SHA,并完成构建、init 与 smoke
|
||||
- [ ] 发布命令显式携带批准的新 project、两个绝对数据路径和 `DEPLOY_DATA_MODE=existing`;未依赖 `.env.example` 相对默认值
|
||||
- [ ] 若为首次初始化或受控应急而直接使用 Compose,已显式设置 `BUILD_SHA=<approved-full-commit>` 与 `VITE_BUILD_SHA=<matching-12-char-commit>`;未使用 `unknown`
|
||||
- [ ] 探活:
|
||||
- [ ] 已用 `docker-compose -p "$COMPOSE_PROJECT_NAME" ... port server 3000` 解析实际宿主端口,并对该端口执行 `/ready`,未依赖当前 shell 中可能缺失的 `APP_HOST_PORT`
|
||||
|
||||
+47
-2
@@ -9,6 +9,9 @@ DEPLOY_SERVER="${DEPLOY_SERVER:-}"
|
||||
REMOTE_DIR="${REMOTE_DIR:-/opt/hyb-oa-trialrun}"
|
||||
REMOTE_ENV_FILE="${REMOTE_ENV_FILE:-${REMOTE_DIR}/deploy/trialrun/.env}"
|
||||
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-hyb-oa-trialrun}"
|
||||
POSTGRES_DATA_DIR="${POSTGRES_DATA_DIR:-}"
|
||||
STORAGE_HOST_DIR="${STORAGE_HOST_DIR:-}"
|
||||
DEPLOY_DATA_MODE="${DEPLOY_DATA_MODE:-}"
|
||||
ENABLE_WORKER="${ENABLE_WORKER:-0}"
|
||||
ENABLE_MINIO="${ENABLE_MINIO:-0}"
|
||||
SYNC_ENV="${SYNC_ENV:-0}"
|
||||
@@ -53,6 +56,33 @@ if [[ ! "$COMPOSE_PROJECT_NAME" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
validate_data_path() {
|
||||
local name="$1"
|
||||
local value="$2"
|
||||
if [[ -z "$value" || "$value" != /* || ! "$value" =~ ^/[A-Za-z0-9._/-]+$ ]]; then
|
||||
echo "[ERROR] $name 必须显式提供受限字符集的绝对路径" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$value" == "/" || "$value" == */ || "$value" == *"//"* \
|
||||
|| "/$value/" == *"/../"* || "/$value/" == *"/./"* ]]; then
|
||||
echo "[ERROR] $name 不得是根目录、尾随斜杠或包含空、.、.. 路径段" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"
|
||||
validate_data_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
|
||||
echo "[ERROR] PostgreSQL 与附件宿主目录必须相互独立" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$DEPLOY_DATA_MODE" != "existing" && "$DEPLOY_DATA_MODE" != "initialize" ]]; then
|
||||
echo "[ERROR] DEPLOY_DATA_MODE 必须显式设为 existing 或 initialize" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${ROOT_DIR}/${PROVENANCE_SCRIPT}" ]]; then
|
||||
echo "[ERROR] 缺少 ${PROVENANCE_SCRIPT}" >&2
|
||||
exit 1
|
||||
@@ -143,10 +173,25 @@ fi
|
||||
|
||||
echo ">>> 6. 在服务器执行部署与 smoke"
|
||||
ssh "${DEPLOY_SERVER}" \
|
||||
"REMOTE_DIR='${REMOTE_DIR}' REMOTE_ENV_FILE='${REMOTE_ENV_FILE}' COMPOSE_FILE='${COMPOSE_FILE}' COMPOSE_PROJECT_NAME='${COMPOSE_PROJECT_NAME}' ENABLE_WORKER='${ENABLE_WORKER}' ENABLE_MINIO='${ENABLE_MINIO}' BUILD_SHA='${SOURCE_COMMIT}' VITE_BUILD_SHA='${SOURCE_SHORT_COMMIT}' bash -s" <<'REMOTE'
|
||||
"REMOTE_DIR='${REMOTE_DIR}' REMOTE_ENV_FILE='${REMOTE_ENV_FILE}' COMPOSE_FILE='${COMPOSE_FILE}' COMPOSE_PROJECT_NAME='${COMPOSE_PROJECT_NAME}' POSTGRES_DATA_DIR='${POSTGRES_DATA_DIR}' STORAGE_HOST_DIR='${STORAGE_HOST_DIR}' DEPLOY_DATA_MODE='${DEPLOY_DATA_MODE}' ENABLE_WORKER='${ENABLE_WORKER}' ENABLE_MINIO='${ENABLE_MINIO}' BUILD_SHA='${SOURCE_COMMIT}' VITE_BUILD_SHA='${SOURCE_SHORT_COMMIT}' bash -s" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
cd "$REMOTE_DIR"
|
||||
mkdir -p deploy/trialrun/data/postgres deploy/trialrun/data/storage deploy/trialrun/data/minio
|
||||
|
||||
if [[ "$DEPLOY_DATA_MODE" == "initialize" ]]; then
|
||||
mkdir -p "$POSTGRES_DATA_DIR" "$STORAGE_HOST_DIR"
|
||||
else
|
||||
if [[ ! -d "$POSTGRES_DATA_DIR" || ! -f "$POSTGRES_DATA_DIR/PG_VERSION" ]]; then
|
||||
echo "[ERROR] existing 模式要求 POSTGRES_DATA_DIR 已存在且包含 PG_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d "$STORAGE_HOST_DIR" ]]; then
|
||||
echo "[ERROR] existing 模式要求 STORAGE_HOST_DIR 已存在" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ "$ENABLE_MINIO" == "1" ]]; then
|
||||
mkdir -p deploy/trialrun/data/minio
|
||||
fi
|
||||
|
||||
if [[ ! -f "$REMOTE_ENV_FILE" ]]; then
|
||||
echo "[ERROR] 远端缺少 $REMOTE_ENV_FILE,请先手工创建或使用 SYNC_ENV=1" >&2
|
||||
|
||||
+25
-6
@@ -12,7 +12,7 @@
|
||||
1. 服务器以 root 执行 `bash deploy/trialrun/setup-server.sh`
|
||||
2. 手工创建 `deploy/trialrun/.env`
|
||||
3. 手工更新 OA 的 `Caddyfile` 站点块并确认 TLS 正常
|
||||
4. 本地通过受控配置显式提供 `DEPLOY_SERVER`、`REMOTE_DIR`,再执行 `bash scripts/qa/run-release-gate.sh all`(或至少执行 `bash deploy/trialrun/deploy.sh`)
|
||||
4. 本地通过受控配置显式提供 `DEPLOY_SERVER`、`REMOTE_DIR`、绝对数据目录和 `DEPLOY_DATA_MODE`,再执行 `bash scripts/qa/run-release-gate.sh all`(或至少执行 `bash deploy/trialrun/deploy.sh`)
|
||||
|
||||
## 0B. 历史部署基线(基础设施信息已脱敏)
|
||||
- SSH 目标、部署账号和机器地址只保存在受控运维配置中,通过 `DEPLOY_SERVER` 显式传入。
|
||||
@@ -47,7 +47,8 @@ cp deploy/trialrun/.env.example deploy/trialrun/.env
|
||||
- `BUILD_SHA` / `VITE_BUILD_SHA` 由发布入口按已批准提交临时注入,不写入长期 `.env`;前者供后端运行时核对,后者只允许 12 位短 SHA 进入公网 bundle。
|
||||
- 推荐使用无引号值,例如 `COOKIE_SECURE=true`、`PORT=3000`。
|
||||
- `PORT=3000` 是容器内端口;`APP_HOST_PORT` 是宿主回环端口。发布脚本通过 Compose 动态解析后者,Caddy 必须指向相同值。
|
||||
- `POSTGRES_DATA_DIR` 和 `STORAGE_HOST_DIR` 是宿主数据事实源;默认相对 `deploy/trialrun/`。生产环境更换项目名或任一数据目录只能在同一次获批停机窗口完成,禁止两个 PostgreSQL 实例同时挂载同一 PGDATA。
|
||||
- `POSTGRES_DATA_DIR` 和 `STORAGE_HOST_DIR` 是宿主数据事实源。示例相对值仅供本地直接 Compose;正式 `deploy.sh` 要求在命令环境中显式提供两个绝对路径和 `DEPLOY_DATA_MODE=existing|initialize`。
|
||||
- `existing` 要求 PGDATA 已存在且包含 `PG_VERSION`,附件目录也必须已存在;`initialize` 才会创建目录。生产环境更换项目名或任一数据目录只能在同一次获批停机窗口完成,禁止两个 PostgreSQL 实例同时挂载同一 PGDATA。
|
||||
- 历史上已经写成带引号的 `.env` 当前仍兼容,但建议后续逐步改成无引号。
|
||||
- 正式部署前可先执行 `node deploy/trialrun/validate-env-contract.js` 做静态契约检查。
|
||||
- **EXTERNAL_GATED**:涉及认证安全迁移时,发布或回滚前必须由操作人先在边缘暂停 `/api/auth/forgot-password`、`/api/auth/reset-password`、`/api/auth/email-login/code`、`/api/auth/email-login/verify` 四个 challenge 端点,再执行“新镜像配置预检 -> additive migration -> 新应用切换”。部署脚本不会自动修改边缘配置;未人工确认四端点已关闭不得发布或回滚开放。仅在安全版本及 smoke 全部通过后人工恢复;若回滚到旧认证代码,四端点必须继续关闭。
|
||||
@@ -183,15 +184,19 @@ set +a
|
||||
mapfile -t DB_CONTAINERS < <(docker-compose -p "$COMPOSE_PROJECT_NAME" -f deploy/trialrun/docker-compose.yml --env-file deploy/trialrun/.env ps -q db)
|
||||
test "${#DB_CONTAINERS[@]}" -eq 1
|
||||
DB_CONTAINER="${DB_CONTAINERS[0]}"
|
||||
SOURCE_IMAGE_DIGEST="$(docker inspect --format '{{.Image}}' <verified-active-server-container>)"
|
||||
|
||||
DB_TOOL_MODE=docker \
|
||||
DB_CONTAINER="$DB_CONTAINER" \
|
||||
STORAGE_DIR_HOST="<verified-active-storage-host-directory>" \
|
||||
SOURCE_IMAGE_DIGEST="$SOURCE_IMAGE_DIGEST" \
|
||||
./scripts/backup-all.sh
|
||||
```
|
||||
|
||||
`backup-all.sh` 可使用宿主 Node.js 或 Python 3 生成同结构 manifest;Docker-only 主机没有 Node.js 时会自动使用 Python 3,两者都没有则失败且不发布完整批次。`DB_CONTAINER` 和 `STORAGE_DIR_HOST` 均无隐式默认值:前者必须由已核验 Compose 项目的 `ps -q db` 解析,后者必须与活动 server 的附件挂载源一致。真实运行环境执行前,不得只相信可能漂移的 `.env`:先脱敏确认所选 PostgreSQL 容器的数据库/用户与至少一个活动应用连接一致,再把已验证的运行参数传给脚本;不在日志中输出参数值。
|
||||
|
||||
manifest 必须至少记录一个真实来源标识:活动 `/ready` 有构建标识时显式传 `BUILD_SHA`;没有时,从活动 server 容器 `docker inspect --format '{{.Image}}'` 取得 `sha256:` 镜像 ID并传 `SOURCE_IMAGE_DIGEST`。不得用待部署提交填充旧实例的 `buildSha`。
|
||||
|
||||
输出默认在 `deploy/trialrun/backups/hyb-oa-backup-<run-id>/`,其中包含:
|
||||
|
||||
- `database.dump`
|
||||
@@ -233,11 +238,24 @@ RESTORE_SCOPE=shadow \
|
||||
|
||||
`restore-db.sh` 和 `restore-storage.sh` 默认只接受 `shadow`。影子库名必须包含独立的 `shadow`、`drill` 或 `restore_test` 标记;数据库确认不匹配、库名不安全、附件目录非空、目标为默认试运行附件目录或归档路径危险时均在写入前退出。
|
||||
|
||||
### 1.5.3 真实事故恢复
|
||||
真实格式影子恢复的验收不仅是 `/ready`:恢复后的 `_prisma_migrations` 成功项必须等于冻结基线(当前预期 58),预先列出的关键业务表计数必须逐表一致,附件解包后的普通文件数必须与活动源目录一致。任一项不一致即判定批次不可作为切换回滚点。
|
||||
|
||||
### 1.5.3 生产切换前冻结记录
|
||||
|
||||
执行正确备份和影子恢复前,先用只读命令记录:
|
||||
|
||||
- `docker compose ls` 实测的旧 Compose project 名;不得按目录名推断,后续停止旧栈必须使用该实测值。
|
||||
- 活动 server/db 容器 ID、活动镜像 ID(`sha256:`)及可用的 repo digest;回滚至少保留不可变镜像 ID。
|
||||
- 活动 DB/附件真实宿主挂载源、Caddy 目标端口和环境文件摘要,不输出环境变量正文。
|
||||
- 成功 migration 数量和固定关键表计数基线。至少覆盖组织人员、角色、采购、报销、发票池、支付批次、合同、项目和附件关联表。
|
||||
|
||||
活动数据复制策略在停机窗口前冻结:旧 PostgreSQL 完全停止后,把 PGDATA 和附件复制到预先确认的空绝对目录,并保留所有权、权限、链接和扩展属性;旧目录留在原位且观察期内不再挂载写入。不得移动或改名后只留下单份已迁移数据,也不得复制运行中的 PGDATA。
|
||||
|
||||
### 1.5.4 真实事故恢复
|
||||
|
||||
真实库/附件恢复不是普通脚本路径。必须另建或更新获批 Trellis task,记录停机窗口、恢复批次、实际目标、两名负责人、当前备份、回滚点和验证清单。当前恢复脚本不会接受 `production` 模式,不得通过修改环境变量或脚本临时绕过。恢复后必须验证 `/ready`、运行 commit、核心权限和 DB/附件关联完整性。
|
||||
|
||||
### 1.5.4 小团队上线前演练记录
|
||||
### 1.5.5 小团队上线前演练记录
|
||||
上线前至少记录一次演练结果:
|
||||
|
||||
- 演练日期:
|
||||
@@ -275,8 +293,9 @@ RESTORE_SCOPE=shadow \
|
||||
|
||||
步骤:
|
||||
1) 先做备份(见 E14 脚本)
|
||||
2) 从本地仓库执行 `DEPLOY_SERVER=<ssh-target> REMOTE_DIR=<remote-dir> COMPOSE_PROJECT_NAME=<approved-compose-project> bash deploy/trialrun/deploy.sh`(推荐);脚本只同步已验证提交的 Git archive,不依赖远端 `git pull`,也不会把本地 ignored 文件带入发布。
|
||||
- 在现网目录统一任务中,新的 `COMPOSE_PROJECT_NAME`、`POSTGRES_DATA_DIR`、`STORAGE_HOST_DIR` 必须与活动数据目录迁移在同一停机窗口启用;本地提交本身不授权切换。
|
||||
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 文件带入发布。
|
||||
- 在现网目录统一任务中,新的 `COMPOSE_PROJECT_NAME`、`POSTGRES_DATA_DIR`、`STORAGE_HOST_DIR` 必须与活动数据目录复制在同一停机窗口启用;本地提交本身不授权切换。
|
||||
- 旧栈停止命令必须使用冻结阶段由 `docker compose ls` 实测的旧 project;不得拿新 project 名去停止旧容器。
|
||||
- `artifacts/` 和 `output/` 是运行期证据/截图目录,不作为源码发布输入,也不由 `rsync --delete` 清理远端历史证据。
|
||||
3) 构建并重启:
|
||||
```bash
|
||||
|
||||
@@ -243,6 +243,18 @@ for (const [name, content] of [
|
||||
}
|
||||
}
|
||||
|
||||
for (const requiredFragment of [
|
||||
'validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR"',
|
||||
'validate_data_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR"',
|
||||
'DEPLOY_DATA_MODE" != "existing"',
|
||||
'DEPLOY_DATA_MODE" != "initialize"',
|
||||
'"$POSTGRES_DATA_DIR/PG_VERSION"',
|
||||
]) {
|
||||
if (!deployScriptContent.includes(requiredFragment)) {
|
||||
errors.push(`deploy.sh 缺少正式部署数据目录护栏: ${requiredFragment}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const { filePath, content } of nginxContents) {
|
||||
const relativePath = path.relative(rootDir, filePath);
|
||||
if (content.includes('$proxy_add_x_forwarded_for')) {
|
||||
|
||||
在新工单中引用
屏蔽一个用户