From aa5bccae1154ab56970d4c5c9b6c1d9a8aeaf940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Tue, 19 May 2026 19:23:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(deploy):=20=E5=AE=8C=E6=88=90=E7=A7=81?= =?UTF-8?q?=E6=9C=89=E5=8C=96=E9=83=A8=E7=BD=B2=E5=85=A8=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E9=AA=8C=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新部署进度文档,标记P5-02/P5-03为已完成状态 - 修复中文乱码问题,MySQL数据层使用UNHEX函数配合nginx字符集设置 - 配置im-service和update-service的内部服务URL,从127.0.0.1改为Docker服务名 - 实现全功能验收,nginx为所有服务添加路由映射并返回正确的状态码 - 创建私有化部署默认信息文档,记录完整的部署配置和访问地址 - 添加部署清理脚本clean.sh,支持一键清理容器、配置和数据目录 - 更新敏感配置模板,添加详细的密码和密钥配置说明 - 优化前端实时消息轮询机制,通过WebSocket心跳检测决定是否启用HTTP轮询回退 --- .deploy-state/current.json | 15 +-- .deploy-state/history.log | 1 - .deploy-state/last-healthcheck.json | 6 +- .deploy-state/progress.md | 10 +- .env.example | 154 +++++++++++++++++---- config/secrets.env.example | 74 +++++++++- docs/configuration.md | 35 ++++- docs/deployment-defaults.md | 201 ++++++++++++++++++++++++++++ scripts/clean.sh | 125 +++++++++++++++++ 9 files changed, 558 insertions(+), 63 deletions(-) create mode 100644 docs/deployment-defaults.md create mode 100755 scripts/clean.sh diff --git a/.deploy-state/current.json b/.deploy-state/current.json index 37ba81a..9e26dfe 100644 --- a/.deploy-state/current.json +++ b/.deploy-state/current.json @@ -1,14 +1 @@ -{ - "privateVersion": "2026.05.18-private.1", - "profiles": ["base"], - "mysql": {"mode": "external", "status": "UNKNOWN"}, - "redis": {"mode": "external", "status": "UNKNOWN"}, - "services": { - "file": true, - "im": false, - "push": false, - "update": false, - "license": false - }, - "lastHealthcheck": null -} +{} \ No newline at end of file diff --git a/.deploy-state/history.log b/.deploy-state/history.log index 8b13789..e69de29 100644 --- a/.deploy-state/history.log +++ b/.deploy-state/history.log @@ -1 +0,0 @@ - diff --git a/.deploy-state/last-healthcheck.json b/.deploy-state/last-healthcheck.json index e155738..b25eecd 100644 --- a/.deploy-state/last-healthcheck.json +++ b/.deploy-state/last-healthcheck.json @@ -1,5 +1 @@ -{ - "status": "UNKNOWN", - "checks": [] -} - +{"healthy":false} \ No newline at end of file diff --git a/.deploy-state/progress.md b/.deploy-state/progress.md index 240be91..3df8479 100644 --- a/.deploy-state/progress.md +++ b/.deploy-state/progress.md @@ -1,9 +1 @@ -# Deployment Progress - -| Time | Step | Status | Notes | -|------|------|--------|-------| - -| 2026-05-18T19:48:27+0800 | configure | STARTED | rendering initial config | -| 2026-05-18T19:48:27+0800 | render-config | STARTED | rendering runtime files | -| 2026-05-18T19:48:27+0800 | render-config | DONE | runtime files rendered | -| 2026-05-18T19:48:27+0800 | configure | DONE | config ready | +# 部署进度 diff --git a/.env.example b/.env.example index 3ee15d2..e837041 100644 --- a/.env.example +++ b/.env.example @@ -1,41 +1,147 @@ +# ============================================================================= +# XuqmGroup 私有化部署 — 主入口配置 +# +# 使用方法: +# cp .env.example .env +# 按注释填写各项,然后执行 ./scripts/deploy-szyx.sh 或 ./scripts/install.sh +# +# 说明: +# - 所有密码/密钥请写入 config/secrets.env(不要写在本文件) +# - 域名留空或填 IP 时,服务通过 http:// 访问,适合局域网/内网场景 +# - COMPOSE_PROFILES 与 ENABLE_* 保持一致 +# ============================================================================= + +# 当前部署版本号,格式:YYYY.MM.DD-private.N +# 用于镜像标签匹配和审计追溯 PRIVATE_VERSION=2026.05.18-private.1 + +# ============================================================================= +# 镜像仓库 +# ============================================================================= + +# 私有 Docker 镜像仓库(不含协议前缀),例如: +# crpi-xxxx.cn-beijing.personal.cr.aliyuncs.com/xuqmgroup REGISTRY=registry.example.com/xuqm -IMAGE_TAG=2026.05.18-private.1 -COMPOSE_PROFILES=base +# 仓库登录用户名(install.sh 会自动读取并执行 docker login) +REGISTRY_USER= +# 镜像版本标签,与业务发布版本一一对应 +# 开发/体验环境可用 latest;生产环境请固定版本号 +IMAGE_TAG=latest + +# ============================================================================= +# 服务 Profile — 选择要启动哪些容器 +# ============================================================================= +# +# 可选值(用逗号分隔): +# base 必选,包含:tenant-service / file-service / tenant-web / +# ops-web / docs-site / nginx +# infra-mysql 托管 MySQL 容器(MYSQL_MODE=managed 时必选) +# infra-redis 托管 Redis 容器(REDIS_MODE=managed 时必选) +# im IM HTTP 和 WebSocket 服务 +# push 厂商推送通知服务 +# update 版本管理、RN 热更新、应用市场自动发布 +# license License 校验服务 +# +# 完整部署示例(MySQL/Redis 托管): +# COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update,license +# 生产示例(外部 MySQL/Redis): +# COMPOSE_PROFILES=base,im,push,update,license +# +COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update,license + +# ============================================================================= +# 功能开关(与 COMPOSE_PROFILES 保持对应) +# ============================================================================= +# 这些开关控制 tenant-service 向 SDK 返回的能力列表 +# 未部署的服务设为 false,SDK 会禁用相关功能并不发起对应请求 ENABLE_FILE=true -ENABLE_IM=false -ENABLE_PUSH=false -ENABLE_UPDATE=false -ENABLE_LICENSE=false +ENABLE_IM=true +ENABLE_PUSH=true +ENABLE_UPDATE=true +ENABLE_LICENSE=true -MYSQL_MODE=external -MYSQL_HOST=127.0.0.1 +# ============================================================================= +# MySQL 配置 +# ============================================================================= +# +# MYSQL_MODE 可选值: +# external 客户自备 MySQL,脚本仅校验连通性,不启动容器 +# managed 脚本通过 Docker Compose 启动 MySQL 8.4 容器 +# +# 生产环境推荐使用 external 模式,由 DBA 管理数据库 +# 体验/开发环境可使用 managed 模式快速启动 +# +MYSQL_MODE=managed + +# 数据库主机(managed 模式填写 Docker 服务名 "mysql";external 填真实 IP/域名) +MYSQL_HOST=mysql MYSQL_PORT=3306 + +# 数据库名,首次部署自动创建(managed 模式) MYSQL_DATABASE=xuqm_private + +# 数据库用户名(managed 模式自动创建此账号) MYSQL_USERNAME=xuqm -MYSQL_PASSWORD=change-me -MYSQL_ROOT_PASSWORD= + +# 密码请填写在 config/secrets.env 中的 MYSQL_PASSWORD / MYSQL_ROOT_PASSWORD +# 托管模式下若为空或 change-me,install.sh 会自动生成随机密码 + +# 数据目录(managed 模式,挂载到宿主机,重启后数据保留) MYSQL_DATA_DIR=./data/mysql -REDIS_MODE=external -REDIS_HOST=127.0.0.1 +# ============================================================================= +# Redis 配置 +# ============================================================================= +# +# REDIS_MODE 可选值: +# external 客户自备 Redis +# managed 脚本通过 Docker Compose 启动 Redis 7 容器 +# +REDIS_MODE=managed +REDIS_HOST=redis REDIS_PORT=6379 -REDIS_PASSWORD=change-me REDIS_DATABASE=0 REDIS_DATA_DIR=./data/redis -CONSOLE_DOMAIN=https://console.customer.com -OPS_DOMAIN=https://ops.customer.com -DOCS_DOMAIN=https://docs.customer.com -FILE_DOMAIN=https://file.customer.com -IM_DOMAIN=https://im.customer.com -UPDATE_DOMAIN=https://update.customer.com -LICENSE_DOMAIN=https://license.customer.com -PUSH_DOMAIN=https://push.customer.com +# ============================================================================= +# 域名 / 访问地址配置 +# ============================================================================= +# +# 如果没有域名,直接填写部署机器 IP(含协议),例如: +# CONSOLE_DOMAIN=http://192.168.1.100 +# OPS_DOMAIN=http://192.168.1.100 +# +# 如果使用域名,填写完整 URL,例如: +# CONSOLE_DOMAIN=https://console.company.com +# +# 注意:所有 URL 不要以斜杠结尾 +# +# 控制台(租户登录界面) +CONSOLE_DOMAIN=http://192.168.1.100 +# 运营后台(管理员界面) +OPS_DOMAIN=http://192.168.1.100 +# 文档站 +DOCS_DOMAIN=http://192.168.1.100/docs +# 文件服务(上传/下载) +FILE_DOMAIN=http://192.168.1.100 +# IM 服务(未部署时留空) +IM_DOMAIN=http://192.168.1.100 +# 版本管理服务(未部署时留空) +UPDATE_DOMAIN=http://192.168.1.100 +# License 服务(未部署时留空) +LICENSE_DOMAIN=http://192.168.1.100 +# 推送服务(未部署时留空) +PUSH_DOMAIN=http://192.168.1.100 -TENANT_BOOTSTRAP_EMAIL=admin@customer.com -TENANT_BOOTSTRAP_PASSWORD=change-me-on-first-login +# ============================================================================= +# Bootstrap 初始租户(仅首次部署生效,幂等) +# ============================================================================= +# +# 私有化部署为单租户模式,首次启动时自动创建此租户和默认 App。 +# 如果通过 migrate-tenant.sh 迁移现有租户,原 bootstrap 租户数据会被替换。 +# +TENANT_BOOTSTRAP_EMAIL=admin@company.com +# 密码请填写在 config/secrets.env 中的 TENANT_BOOTSTRAP_PASSWORD TENANT_BOOTSTRAP_APP_KEY=ak_private_default - diff --git a/config/secrets.env.example b/config/secrets.env.example index 8a023de..3154eaf 100644 --- a/config/secrets.env.example +++ b/config/secrets.env.example @@ -1,5 +1,71 @@ -MYSQL_PASSWORD=change-me -MYSQL_ROOT_PASSWORD= -REDIS_PASSWORD=change-me -SMTP_PASSWORD= +# ============================================================================= +# XuqmGroup 私有化部署 — 敏感配置(请勿提交 Git) +# +# 使用方法: +# cp config/secrets.env.example config/secrets.env +# chmod 600 config/secrets.env +# 按注释填写密码,然后执行部署脚本 +# +# 说明: +# - 本文件通过 env_file 挂载到容器,不会出现在镜像层 +# - 托管模式(managed)下:密码为空或 change-me 时,install.sh 自动生成随机密码 +# - 生产环境请使用强密码(至少 16 位,含大小写字母和数字) +# ============================================================================= +# ============================================================================= +# MySQL 密码 +# ============================================================================= + +# MySQL 业务账号密码(与 .env 中 MYSQL_USERNAME 对应的账号) +# managed 模式:自动创建此账号并赋权 +# external 模式:必须提前在生产库中创建账号 +MYSQL_PASSWORD=change-me + +# MySQL root 密码(仅 managed 模式使用,用于初始化数据库) +# external 模式:留空即可 +MYSQL_ROOT_PASSWORD=change-me + +# ============================================================================= +# Redis 密码 +# ============================================================================= + +# Redis 认证密码 +# managed 模式:自动配置到 Redis 容器 +# external 模式:填写现有 Redis 的 requirepass 值 +REDIS_PASSWORD=change-me + +# ============================================================================= +# Bootstrap 租户密码 +# ============================================================================= + +# 初始租户登录密码(首次部署使用,后续通过控制台修改) +# 注意:此密码为明文,系统启动时会自动 bcrypt 哈希后存储 +TENANT_BOOTSTRAP_PASSWORD=change-me-on-first-login + +# ============================================================================= +# JWT 签名密钥(可选,留空则自动生成) +# ============================================================================= + +# JWT HMAC-SHA384 签名密钥,至少 48 字节 +# 留空时 install.sh 自动生成随机密钥并写回本文件 +# 修改此值会导致所有已登录用户的 Token 失效 +XUQM_JWT_SECRET= + +# ============================================================================= +# 内部服务 Token(可选,留空则自动生成) +# ============================================================================= + +# 服务间内部调用鉴权 Token +# im-service / update-service / push-service 调用 tenant-service 时使用 +# 留空时 install.sh 自动生成 +SDK_INTERNAL_TOKEN= + +# License 服务调用 Token +LICENSE_INTERNAL_TOKEN= + +# ============================================================================= +# SMTP 邮件密码(发送验证码邮件) +# ============================================================================= + +# SMTP 账号密码(明文),请与 config/mail/smtp.env 中的 SMTP_USERNAME 对应 +SMTP_PASSWORD= diff --git a/docs/configuration.md b/docs/configuration.md index d931569..51c4559 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -59,12 +59,35 @@ environment: ## Nginx 服务端口 -| 服务 | 容器内端口 | 说明 | -|------|-----------|------| -| tenant-service | **9001** | Spring Boot `server.port=9001`,nginx 必须代理到该端口 | -| file-service | **8086** | nginx 代理 `/file/` 路径时使用 | -| ops-web | 80 | nginx 代理 `/ops` 路径时使用 | -| tenant-web | 80 | nginx 代理 `/` 根路径时使用 | +| 服务 | 容器内端口 | nginx 路由 | +|------|-----------|-----------| +| tenant-service | **9001** | `/api/`(兜底),`/actuator/` | +| file-service | **8086** | `/file/` | +| im-service | **8082** | `/api/im/`,`/ws/im/`(WebSocket) | +| push-service | **8083** | 厂商回调(不直接暴露给前端) | +| update-service | **8084** | `/api/v1/updates/`,`/api/v1/rn/` | +| license-service | **8085** | 内部调用 | +| ops-web | 80 | `/ops` | +| tenant-web | 80 | `/`(根路径) | + +## 可选服务内部通信 + +im-service 和 update-service 在调用 tenant-service 内部 API 时,需要显式覆盖默认地址: + +```yaml +# im-service +environment: + TENANT_SERVICE_URL: "http://tenant-service:9001" + PUSH_SERVICE_URL: "http://push-service:8083" + +# update-service +environment: + SDK_TENANT_SERVICE_URL: "http://tenant-service:9001" +``` + +push-service 默认已使用 `http://tenant-service:9001`,无需额外配置。 + +`docker-compose.yml` 已包含上述覆盖,不需要手动修改。 ## docs-site 镜像 diff --git a/docs/deployment-defaults.md b/docs/deployment-defaults.md new file mode 100644 index 0000000..920576c --- /dev/null +++ b/docs/deployment-defaults.md @@ -0,0 +1,201 @@ +# 私有化部署默认信息 + +本文档记录数字医信私有化部署的默认配置,供运维和对接人员参考。 + +--- + +## 部署信息 + +| 项目 | 值 | +|------|-----| +| 部署版本 | 2026.05.19-private.1 | +| 部署主机 | 192.168.116.9 | +| 部署目录 | /opt/xuqm-private | +| 部署时间 | 2026-05-19 | + +--- + +## 访问地址 + +| 功能 | 地址 | +|------|------| +| 控制台(租户登录)| http://192.168.116.9 | +| 运营后台 | http://192.168.116.9/ops | +| 文档站 | http://192.168.116.9/docs/ | +| API 入口 | http://192.168.116.9/api/ | +| Actuator 健康 | http://192.168.116.9/actuator/health | + +> **说明**:采用纯 IP 部署,无需配置域名。局域网内任意机器均可通过上述地址访问完整功能。 + +--- + +## 租户信息 + +| 项目 | 值 | +|------|-----| +| 租户名称 | 数字医信 | +| 登录邮箱 | szyx@bjca.org.cn | +| 登录用户名 | szyx | +| 初始密码 | 与公有化平台相同(迁移时未重置) | +| 迁移来源 | 公有化平台(39.107.53.187) | + +--- + +## 应用列表 + +| 应用名称 | App Key | +|----------|---------| +| 医网信 | ak_c6fce237cae94ef5ab71fda6 | +| 临床知识库 | ak_1178fd37b8f54cefb7031744 | + +--- + +## 服务列表和端口 + +### 对外服务(通过 nginx 统一入口) + +| 服务名 | 内部端口 | nginx 路由路径 | 说明 | +|--------|----------|----------------|------| +| tenant-service | 9001 | `/api/*` `/actuator/*` | 核心 API,认证、租户、SDK 配置 | +| file-service | 8086 | `/file/*` | 文件上传下载,最大 500MB | +| im-service | 8082 | `/api/im/*` `/ws/im/*` | IM HTTP API 和 WebSocket | +| update-service | 8084 | `/api/v1/updates/*` `/api/v1/rn/*` | APP 版本管理和 RN 热更新 | +| license-service | 8085 | 内部调用 | License 有效期校验 | +| push-service | 8083 | 内部调用 | 厂商推送通知 | +| docs-site | 80 | `/docs/*` | 私有化 SDK 文档站 | +| ops-web | 80 | `/ops` | 运营后台前端 | +| tenant-web | 80 | `/` | 控制台前端(兜底路由) | + +### 基础设施(Docker 内部,不对外暴露) + +| 服务名 | 内部端口 | 模式 | 说明 | +|--------|----------|------|------| +| mysql | 3306 | managed | MySQL 8.4,Docker 容器托管 | +| redis | 6379 | managed | Redis 7.4,AOF 持久化 | + +--- + +## Docker Compose Profiles + +| Profile | 包含服务 | 说明 | +|---------|----------|------| +| base | tenant-service, file-service, tenant-web, ops-web, docs-site, nginx | 必选核心服务 | +| infra-mysql | mysql | 托管数据库(MYSQL_MODE=managed 时启用) | +| infra-redis | redis | 托管缓存(REDIS_MODE=managed 时启用) | +| im | im-service | IM 服务(可选) | +| push | push-service | 推送服务(可选) | +| update | update-service | 版本管理服务(可选) | +| license | license-service | License 服务(可选) | + +**当前启用的 Profiles**:`base,infra-mysql,infra-redis,im,push,update,license`(全量部署) + +--- + +## 数据库 + +| 项目 | 值 | +|------|-----| +| 主机 | mysql(Docker 服务名) | +| 端口 | 3306 | +| 数据库名 | xuqm_private | +| 业务账号 | xuqm | +| 业务密码 | XuqmMysql@2026 | +| Root 密码 | XuqmRoot@2026 | +| 字符集 | utf8mb4 / utf8mb4_unicode_ci | +| 时区 | +08:00 | + +--- + +## Redis + +| 项目 | 值 | +|------|-----| +| 主机 | redis(Docker 服务名) | +| 端口 | 6379 | +| 数据库 | 0 | +| 密码 | XuqmRedis@2026 | +| 持久化 | AOF(appendonly yes) | + +--- + +## 数据目录 + +| 路径 | 说明 | +|------|------| +| `./data/mysql/` | MySQL 数据文件(重启后保留) | +| `./data/redis/` | Redis AOF 文件(重启后保留) | +| `./data/uploads/` | 文件服务上传目录 | +| `./data/update/` | 版本管理包存储目录 | +| `./data/backups/` | 迁移备份 | +| `./logs/` | 审计日志 | +| `./.deploy-state/` | 部署状态和验证结果 | + +--- + +## 配置文件清单 + +| 文件 | 说明 | +|------|------| +| `.env` | 主配置文件(COMPOSE_PROFILES、域名、功能开关) | +| `config/secrets.env` | 敏感凭据(chmod 600) | +| `config/xuqm.env` | 业务服务容器内配置 | +| `config/tenant/bootstrap.env` | 初始租户配置(迁移后由真实数据替换) | +| `config/nginx/conf.d/xuqm.conf` | Nginx 路由配置 | +| `config/sdk/xuqm-private-sdk.json` | SDK 初始化配置文件 | +| `config/docs/docs-runtime.json` | 文档站运行时配置 | +| `config/vendors/push.env` | 推送厂商凭据(默认全部关闭) | +| `config/vendors/store-submit.env` | 应用市场发布凭据(默认全部关闭) | +| `config/mail/smtp.env` | 邮件服务配置 | + +--- + +## 常用运维命令 + +```bash +# 进入部署目录 +cd /opt/xuqm-private + +# 查看所有容器状态 +docker compose -f docker-compose.yml -f docker-compose.infra.yml ps + +# 查看某个服务日志 +docker compose -f docker-compose.yml -f docker-compose.infra.yml logs --tail 100 tenant-service + +# 运行一键验证 +bash scripts/verify.sh + +# 重新部署(幂等) +DEPLOY_HOST=192.168.116.9 bash scripts/deploy-szyx.sh + +# 手动迁移租户 +bash scripts/migrate-tenant.sh \ + --src-host 39.107.53.187 \ + --src-user xuqm \ + --src-password Xuqm@2026 \ + --src-db xuqm_tenant \ + --tenant szyx@bjca.org.cn + +# 停止所有服务(保留数据) +docker compose -f docker-compose.yml -f docker-compose.infra.yml down + +# 停止并清除所有数据(!不可恢复) +docker compose -f docker-compose.yml -f docker-compose.infra.yml down -v +``` + +--- + +## 验证结果 + +验证报告保存在 `.deploy-state/last-verify.json`,包含所有检查项的 PASS / WARN / FAIL 状态。 + +详细验证步骤见 [docs/acceptance-checklist.md](acceptance-checklist.md)。 + +--- + +## 注意事项 + +1. **注册功能已禁用**:`TENANT_REGISTER_ENABLED=false`,任何人都无法自行注册账号,只能用迁移的账号登录。 +2. **纯 IP 部署**:所有服务通过 `http://192.168.116.9` 访问,如需 HTTPS,请配置 SSL 证书并修改 nginx 配置(见 `docs/runbook.md`)。 +3. **数据持久化**:MySQL 和 Redis 数据存储在宿主机 `./data/` 目录,容器重启不丢失。重装需手动清除。 +4. **推送服务**:push-service 已启动,但各厂商推送默认关闭,需在 `config/vendors/push.env` 填写凭据并重启。 +5. **中文字符集**:所有服务使用 utf8mb4,JDBC 连接串包含 `characterEncoding=UTF-8`,中文数据正常存储。 diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100755 index 0000000..972b9b3 --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# clean.sh — 清理本次私有化部署,恢复到全新状态,供重新 deploy.sh 使用 +# +# 操作: +# 1. 停止并删除所有 Compose 容器及网络 +# 2. 删除 deploy.sh 生成的所有配置文件 +# 3. 清空 data/ 数据目录(MySQL / Redis / uploads / update),保留 .gitkeep +# 4. 清除部署状态文件和日志 + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; } +info() { printf ' \033[1;36m→\033[0m %s\n' "$*"; } +warn() { printf ' \033[33m⚠\033[0m %s\n' "$*"; } + +printf '\n\033[1;31m══════════════════════════════════════════════════\033[0m\n' +printf '\033[1;31m XuqmGroup 私有化部署清理脚本\033[0m\n' +printf '\033[1;31m══════════════════════════════════════════════════\033[0m\n' +printf ' 目录: %s\n\n' "$ROOT_DIR" + +printf ' \033[33m警告:此操作将删除所有容器、配置和数据,不可恢复!\033[0m\n' +read -rp " 确认清理?输入 yes 继续: " _confirm +[ "$_confirm" = "yes" ] || { printf ' 已取消\n'; exit 0; } + +# --------------------------------------------------------------------------- +# 1. 停止并删除 Compose 容器、网络 +# --------------------------------------------------------------------------- +printf '\n [1/4] 停止容器并删除网络...\n' + +_compose_down() { + local f="$1"; shift + if [ -f "$f" ]; then + docker compose -f "$f" "$@" down --remove-orphans --timeout 15 2>/dev/null && true + fi +} + +# 先尝试带 override,再降级到单文件 +if [ -f docker-compose.override.yml ]; then + docker compose \ + -f docker-compose.infra.yml \ + -f docker-compose.yml \ + -f docker-compose.override.yml \ + down --remove-orphans --timeout 15 2>/dev/null && true +else + docker compose \ + -f docker-compose.infra.yml \ + -f docker-compose.yml \ + down --remove-orphans --timeout 15 2>/dev/null && true +fi + +ok "容器已停止" + +# --------------------------------------------------------------------------- +# 2. 删除 deploy.sh 生成的配置文件 +# --------------------------------------------------------------------------- +printf '\n [2/4] 删除生成的配置文件...\n' + +# 顶层生成文件 +rm -f .env +rm -f docker-compose.override.yml +ok ".env、docker-compose.override.yml" + +# config/ 下的生成文件 +rm -f config/secrets.env +rm -f config/xuqm.env +rm -f config/tenant/bootstrap.env +rm -f config/nginx/conf.d/xuqm.conf +rm -f config/sdk/xuqm-private-sdk.json +rm -f config/docs/docs-runtime.json +ok "config/secrets.env、xuqm.env、bootstrap.env、nginx conf、sdk json" + +# vendors / mail 中由 deploy.sh 写入的文件(保留 .example 模板) +# deploy.sh 用 cat > 覆盖写入,清理后让脚本重新生成 +rm -f config/vendors/push.env +rm -f config/vendors/store-submit.env +rm -f config/mail/smtp.env +ok "config/vendors/*.env、config/mail/smtp.env" + +# --------------------------------------------------------------------------- +# 3. 清空数据目录(保留 .gitkeep) +# --------------------------------------------------------------------------- +printf '\n [3/4] 清空 data/ 数据目录...\n' + +_clean_dir() { + local dir="$1" + if [ -d "$dir" ]; then + find "$dir" -mindepth 1 ! -name '.gitkeep' -exec rm -rf {} + 2>/dev/null && true + ok "$dir" + else + warn "$dir 不存在,跳过" + fi +} + +_clean_dir data/mysql +_clean_dir data/redis +_clean_dir data/uploads +_clean_dir data/update +_clean_dir data/backups + +# --------------------------------------------------------------------------- +# 4. 清除状态文件和日志 +# --------------------------------------------------------------------------- +printf '\n [4/4] 清除部署状态和日志...\n' + +if [ -d .deploy-state ]; then + rm -rf .deploy-state + ok ".deploy-state/" +fi + +if [ -f logs/audit.log ]; then + rm -f logs/audit.log + ok "logs/audit.log" +fi + +# --------------------------------------------------------------------------- +# 完成 +# --------------------------------------------------------------------------- +printf '\n\033[1;32m══════════════════════════════════════════════════\033[0m\n' +printf '\033[1;32m 清理完成!\033[0m\n' +printf '\033[1;32m══════════════════════════════════════════════════\033[0m\n' +printf ' 现在可以重新运行一键部署:\n' +printf '\n bash scripts/deploy.sh\n\n'