比较提交

..

没有共同的提交。475067562cb115fe525ec057b8c5131cc2ea199d 和 6868cfcd8ea0193a4688cb04e9b228aa6cfb5a24 的历史完全不同。

共有 26 个文件被更改,包括 202 次插入37 次删除

查看文件

@ -44,13 +44,14 @@ IMAGE_TAG=latest
# im IM HTTP 和 WebSocket 服务 # im IM HTTP 和 WebSocket 服务
# push 厂商推送通知服务 # push 厂商推送通知服务
# update 版本管理、RN 热更新、应用市场自动发布 # update 版本管理、RN 热更新、应用市场自动发布
# license License 校验服务
# #
# 完整部署示例MySQL/Redis 托管): # 完整部署示例MySQL/Redis 托管):
# COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update # COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update,license
# 生产示例(外部 MySQL/Redis # 生产示例(外部 MySQL/Redis
# COMPOSE_PROFILES=base,im,push,update # COMPOSE_PROFILES=base,im,push,update,license
# #
COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update,license
# ============================================================================= # =============================================================================
# 功能开关(与 COMPOSE_PROFILES 保持对应) # 功能开关(与 COMPOSE_PROFILES 保持对应)
@ -61,6 +62,7 @@ ENABLE_FILE=true
ENABLE_IM=true ENABLE_IM=true
ENABLE_PUSH=true ENABLE_PUSH=true
ENABLE_UPDATE=true ENABLE_UPDATE=true
ENABLE_LICENSE=true
ENABLE_BUGCOLLECT=true ENABLE_BUGCOLLECT=true
# ============================================================================= # =============================================================================
@ -131,6 +133,8 @@ FILE_DOMAIN=http://192.168.1.100
IM_DOMAIN=http://192.168.1.100 IM_DOMAIN=http://192.168.1.100
# 版本管理服务(未部署时留空) # 版本管理服务(未部署时留空)
UPDATE_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 PUSH_DOMAIN=http://192.168.1.100

查看文件

@ -23,9 +23,17 @@ bash scripts/update.sh
### 变更 ### 变更
**授权管理License**
- 最大设备数配置迁移至租户平台(`/services/license/:appKey`),租户可自行修改,无需联系运营
- 移除了运营后台的授权管理入口
**Android SDK** **Android SDK**
- 版本升级至 `1.0.1`,新增 `serverUrl` 参数 - 版本升级至 `1.0.1`,新增 `serverUrl` 参数
- `XuqmSDK.initialize(context, appKey, serverUrl = "https://your-server.com")` 可使用指定私有化服务地址完成初始化 - `XuqmSDK.initialize(context, appKey, serverUrl = "https://your-server.com")` 即可完成私有化初始化,SDK 内部自动配置所有服务端点及 License 服务指向,无需额外调用
**私有化部署配置修复**
- 修复 `config/xuqm.env` 缺少 `LICENSE_PUBLIC_BASE_URL` 的问题:此前生成的 `license.xuqm``baseUrl` 指向公有云地址,导致 SDK 通过 `license.xuqm` 自动初始化时 License 请求打到公有云服务器
- `scripts/update.sh``scripts/deploy.sh` 均已补充此配置项,升级脚本会自动将其更新为私有服务器地址
### 升级方法 ### 升级方法

查看文件

@ -56,10 +56,11 @@ bash scripts/reset.sh
├── tenant-web /(兜底) ├── tenant-web /(兜底)
├── im-service /api/im/ /ws/im ├── im-service /api/im/ /ws/im
├── update-service /api/v1/updates/ /api/v1/rn/ ├── update-service /api/v1/updates/ /api/v1/rn/
├── license-service /api/license/
└── push-service (厂商回调,按需) └── push-service (厂商回调,按需)
``` ```
内置 nginx 容器直接绑定宿主机 `0.0.0.0:80`,**宿主机无需配置 nginx**,上层 nginx 直接 proxy_pass 到本机 IP 即可。各业务容器端口绑定 `127.0.0.1`,仅用于本地调试。 内置 nginx 容器直接绑定宿主机 `0.0.0.0:80`,**宿主机无需配置 nginx**,上层 nginx 直接 proxy_pass 到本机 IP 即可。各业务容器11224–11231绑定 `127.0.0.1`,仅用于本地调试。
--- ---
@ -82,6 +83,7 @@ bash scripts/reset.sh
| im | im-service | IM HTTP + WebSocket | | im | im-service | IM HTTP + WebSocket |
| push | push-service | 厂商推送 | | push | push-service | 厂商推送 |
| update | update-service | 版本管理 + RN 热更新 | | update | update-service | 版本管理 + RN 热更新 |
| license | license-service | License 校验 |
```bash ```bash
# 后期启用 / 禁用可选服务 # 后期启用 / 禁用可选服务

查看文件

@ -69,6 +69,16 @@ server {
proxy_read_timeout 60s; proxy_read_timeout 60s;
} }
# License — 必须在通用 /api/ 之前
location /api/license/ {
set $svc license-service;
proxy_pass http://$svc:8085;
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_read_timeout 60s;
}
# 文件上传下载 — 必须在通用 /api/ 之前 # 文件上传下载 — 必须在通用 /api/ 之前
location /api/file/ { location /api/file/ {
set $svc file-service; set $svc file-service;

查看文件

@ -60,6 +60,9 @@ XUQM_JWT_SECRET=
# 留空时 install.sh 自动生成 # 留空时 install.sh 自动生成
SDK_INTERNAL_TOKEN= SDK_INTERNAL_TOKEN=
# License 服务调用 Token
LICENSE_INTERNAL_TOKEN=
# ============================================================================= # =============================================================================
# SMTP 邮件密码(发送验证码邮件) # SMTP 邮件密码(发送验证码邮件)
# ============================================================================= # =============================================================================

查看文件

@ -163,6 +163,24 @@ services:
- ./data/update:/data/update # 版本包存储目录 - ./data/update:/data/update # 版本包存储目录
restart: unless-stopped restart: unless-stopped
# ---------------------------------------------------------------------------
# License 服务可选,profile: license
# 端口8085内部
# 负责设备激活数量校验和 License 有效期管理
# ---------------------------------------------------------------------------
license-service:
image: ${REGISTRY}/license-service:${IMAGE_TAG}
profiles: ["license"]
ports:
- "127.0.0.1:${SVC_PORT_LICENSE:-11231}:8085"
env_file:
- ./config/xuqm.env
- ./config/secrets.env
environment:
SPRING_DATASOURCE_URL: "jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT:-3306}/${MYSQL_DATABASE:-xuqm_private}?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true"
SPRING_DATASOURCE_USERNAME: "${MYSQL_USERNAME:-xuqm}"
restart: unless-stopped
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Bug 收集服务(可选) # Bug 收集服务(可选)
# 端口9006内部,nginx 代理 /api/bugcollect/* # 端口9006内部,nginx 代理 /api/bugcollect/*

查看文件

@ -19,6 +19,7 @@
- [ ] im-service 健康:`curl http://127.0.0.1:11228/actuator/health` 返回 200 - [ ] im-service 健康:`curl http://127.0.0.1:11228/actuator/health` 返回 200
- [ ] update-service 健康:`curl http://127.0.0.1:11230/actuator/health` 返回 200 - [ ] update-service 健康:`curl http://127.0.0.1:11230/actuator/health` 返回 200
- [ ] license-service 健康:`curl http://127.0.0.1:11231/actuator/health` 返回 200
- [ ] push-service 已启动(厂商凭据按需填写) - [ ] push-service 已启动(厂商凭据按需填写)
## 中间件 ## 中间件
@ -27,12 +28,18 @@
- [ ] Redis managed 模式容器自动启动,AOF 持久化 - [ ] Redis managed 模式容器自动启动,AOF 持久化
- [ ] 重启容器后数据完整 - [ ] 重启容器后数据完整
## License
- [ ] 所有应用在 `app_licenses` 表有对应记录
- [ ] `/api/license/admin/apps/:appKey` 返回 200,不出现 404
## Nginx 配置 ## Nginx 配置
- [ ] 宿主机 nginx 配置后,通过外部地址可访问控制台 - [ ] 宿主机 nginx 配置后,通过外部地址可访问控制台
- [ ] `/api/im/` 路由到 im-service11228 - [ ] `/api/im/` 路由到 im-service11228
- [ ] `/ws/im` WebSocket 连接正常(需每层 nginx 透传 `Upgrade` / `Connection` 头) - [ ] `/ws/im` WebSocket 连接正常(需每层 nginx 透传 `Upgrade` / `Connection` 头)
- [ ] `/api/v1/updates/` 路由到 update-service11230,不被通用 `/api/` 拦截 - [ ] `/api/v1/updates/` 路由到 update-service11230,不被通用 `/api/` 拦截
- [ ] `/api/license/` 路由到 license-service11231,不被通用 `/api/` 拦截
- [ ] `/file/` 支持大文件上传500MB - [ ] `/file/` 支持大文件上传500MB
## 公有化隔离 ## 公有化隔离

查看文件

@ -10,7 +10,7 @@
|------|------| |------|------|
| `REGISTRY` | Docker 镜像仓库地址 | | `REGISTRY` | Docker 镜像仓库地址 |
| `IMAGE_TAG` | 镜像版本,默认 `latest` | | `IMAGE_TAG` | 镜像版本,默认 `latest` |
| `COMPOSE_PROFILES` | 启用的服务集合,默认 `base,infra-mysql,infra-redis,im,push,update` | | `COMPOSE_PROFILES` | 启用的服务集合,默认 `base,infra-mysql,infra-redis,im,push,update,license` |
| `MYSQL_MODE` | `managed`(容器托管)或 `external`(客户自备) | | `MYSQL_MODE` | `managed`(容器托管)或 `external`(客户自备) |
| `REDIS_MODE` | `managed``external` | | `REDIS_MODE` | `managed``external` |
| `CONSOLE_DOMAIN` | 对外访问地址,用于服务间跳转和 SDK 配置 | | `CONSOLE_DOMAIN` | 对外访问地址,用于服务间跳转和 SDK 配置 |
@ -73,8 +73,9 @@ SDK_IM_WS_URL=wss://your-domain/ws/im
| 11228 | im-service | 8082 | `/api/im/` `/ws/im` | | 11228 | im-service | 8082 | `/api/im/` `/ws/im` |
| 11229 | push-service | 8083 | 厂商回调(按需) | | 11229 | push-service | 8083 | 厂商回调(按需) |
| 11230 | update-service | 8084 | `/api/v1/updates/` `/api/v1/rn/` | | 11230 | update-service | 8084 | `/api/v1/updates/` `/api/v1/rn/` |
| 11231 | license-service | 8085 | `/api/license/` |
业务容器端口全部绑定 `127.0.0.1`,仅用于直接调试。正常流量走内置 nginx80 / 11223 11224–11231 全部绑定 `127.0.0.1`,仅用于直接调试。正常流量走内置 nginx80 / 11223
--- ---

查看文件

@ -46,6 +46,7 @@
| 11228 | im-service | 8082 | | 11228 | im-service | 8082 |
| 11229 | push-service | 8083 | | 11229 | push-service | 8083 |
| 11230 | update-service | 8084 | | 11230 | update-service | 8084 |
| 11231 | license-service | 8085 |
宿主机 nginx 配置参考见 [docs/runbook.md](runbook.md)。 宿主机 nginx 配置参考见 [docs/runbook.md](runbook.md)。

查看文件

@ -68,7 +68,7 @@ location / {
|-----------|------| |-----------|------|
| **80** | 内置 nginx 入口(无宿主机 nginx 场景,`NGINX_BIND=80` | | **80** | 内置 nginx 入口(无宿主机 nginx 场景,`NGINX_BIND=80` |
| **11223** | 内置 nginx 入口(有宿主机 nginx 场景,`NGINX_BIND=127.0.0.1:11223` | | **11223** | 内置 nginx 入口(有宿主机 nginx 场景,`NGINX_BIND=127.0.0.1:11223` |
| 11224–11230 | 核心与可选业务容器(绑定 127.0.0.1,仅调试用) | | 11224–11231 | 各业务容器(绑定 127.0.0.1,仅调试用) |
各业务容器端口仅用于直接调试,正常流量全部走内置 nginx 入口。 各业务容器端口仅用于直接调试,正常流量全部走内置 nginx 入口。
@ -135,7 +135,7 @@ bash scripts/restore.sh <backup-file>
1. 前往公有化平台控制台 → 安全中心 → 私有化部署迁移 → 生成迁移密钥 1. 前往公有化平台控制台 → 安全中心 → 私有化部署迁移 → 生成迁移密钥
2. 将 `pmk_` 开头的密钥粘贴进向导 2. 将 `pmk_` 开头的密钥粘贴进向导
3. 脚本自动导出租户数据、导入私有库重启服务 3. 脚本自动导出租户数据、导入私有库、写入 license 记录、重启服务
迁移为单租户操作,会清空现有 bootstrap 数据后写入迁移数据。 迁移为单租户操作,会清空现有 bootstrap 数据后写入迁移数据。

查看文件

@ -9,6 +9,7 @@
{"name": "docs-site", "required": true}, {"name": "docs-site", "required": true},
{"name": "im-service", "required": false, "profile": "im"}, {"name": "im-service", "required": false, "profile": "im"},
{"name": "push-service", "required": false, "profile": "push"}, {"name": "push-service", "required": false, "profile": "push"},
{"name": "update-service", "required": false, "profile": "update"} {"name": "update-service", "required": false, "profile": "update"},
{"name": "license-service", "required": false, "profile": "license"}
] ]
} }

查看文件

@ -56,10 +56,11 @@ FILE_DOMAIN="$(ask "File domain (e.g. https://file.customer.com)" "${FILE_DOMAIN
ENABLE_IM="${ENABLE_IM:-false}" ENABLE_IM="${ENABLE_IM:-false}"
ENABLE_PUSH="${ENABLE_PUSH:-false}" ENABLE_PUSH="${ENABLE_PUSH:-false}"
ENABLE_UPDATE="${ENABLE_UPDATE:-false}" ENABLE_UPDATE="${ENABLE_UPDATE:-false}"
ENABLE_LICENSE="${ENABLE_LICENSE:-false}"
if [ "$NON_INTERACTIVE" != "true" ]; then if [ "$NON_INTERACTIVE" != "true" ]; then
printf '\nWhich optional services to enable now? (space-separated, e.g. "im update")\n' printf '\nWhich optional services to enable now? (space-separated, e.g. "im update")\n'
printf 'Available: im push update\nCurrent: %s\n' "$COMPOSE_PROFILES" printf 'Available: im push update license\nCurrent: %s\n' "$COMPOSE_PROFILES"
printf 'Enter services (blank to keep current): ' printf 'Enter services (blank to keep current): '
read -r OPT_SVCS read -r OPT_SVCS
fi fi
@ -67,6 +68,7 @@ fi
if printf '%s' "${OPT_SVCS:-}" | grep -q 'im'; then ENABLE_IM=true; IM_DOMAIN="$(ask "IM domain (e.g. https://im.customer.com)" "${IM_DOMAIN:-}" "")"; fi if printf '%s' "${OPT_SVCS:-}" | grep -q 'im'; then ENABLE_IM=true; IM_DOMAIN="$(ask "IM domain (e.g. https://im.customer.com)" "${IM_DOMAIN:-}" "")"; fi
if printf '%s' "${OPT_SVCS:-}" | grep -q 'push'; then ENABLE_PUSH=true; PUSH_DOMAIN="$(ask "Push domain" "${PUSH_DOMAIN:-}" "")"; fi if printf '%s' "${OPT_SVCS:-}" | grep -q 'push'; then ENABLE_PUSH=true; PUSH_DOMAIN="$(ask "Push domain" "${PUSH_DOMAIN:-}" "")"; fi
if printf '%s' "${OPT_SVCS:-}" | grep -q 'update'; then ENABLE_UPDATE=true; UPDATE_DOMAIN="$(ask "Update domain" "${UPDATE_DOMAIN:-}" "")"; fi if printf '%s' "${OPT_SVCS:-}" | grep -q 'update'; then ENABLE_UPDATE=true; UPDATE_DOMAIN="$(ask "Update domain" "${UPDATE_DOMAIN:-}" "")"; fi
if printf '%s' "${OPT_SVCS:-}" | grep -q 'license'; then ENABLE_LICENSE=true; LICENSE_DOMAIN="$(ask "License domain" "${LICENSE_DOMAIN:-}" "")"; fi
# MySQL mode # MySQL mode
printf '\nMySQL mode (external=you provide connection / managed=deploy script installs MySQL)\n' printf '\nMySQL mode (external=you provide connection / managed=deploy script installs MySQL)\n'
@ -116,10 +118,12 @@ set_env_value "$ROOT_DIR/.env" "FILE_DOMAIN" "$FILE_DOMAIN"
[ -n "${IM_DOMAIN:-}" ] && set_env_value "$ROOT_DIR/.env" "IM_DOMAIN" "$IM_DOMAIN" [ -n "${IM_DOMAIN:-}" ] && set_env_value "$ROOT_DIR/.env" "IM_DOMAIN" "$IM_DOMAIN"
[ -n "${PUSH_DOMAIN:-}" ] && set_env_value "$ROOT_DIR/.env" "PUSH_DOMAIN" "$PUSH_DOMAIN" [ -n "${PUSH_DOMAIN:-}" ] && set_env_value "$ROOT_DIR/.env" "PUSH_DOMAIN" "$PUSH_DOMAIN"
[ -n "${UPDATE_DOMAIN:-}" ] && set_env_value "$ROOT_DIR/.env" "UPDATE_DOMAIN" "$UPDATE_DOMAIN" [ -n "${UPDATE_DOMAIN:-}" ] && set_env_value "$ROOT_DIR/.env" "UPDATE_DOMAIN" "$UPDATE_DOMAIN"
[ -n "${LICENSE_DOMAIN:-}" ] && set_env_value "$ROOT_DIR/.env" "LICENSE_DOMAIN" "$LICENSE_DOMAIN"
set_env_value "$ROOT_DIR/.env" "ENABLE_IM" "$ENABLE_IM" set_env_value "$ROOT_DIR/.env" "ENABLE_IM" "$ENABLE_IM"
set_env_value "$ROOT_DIR/.env" "ENABLE_PUSH" "$ENABLE_PUSH" set_env_value "$ROOT_DIR/.env" "ENABLE_PUSH" "$ENABLE_PUSH"
set_env_value "$ROOT_DIR/.env" "ENABLE_UPDATE" "$ENABLE_UPDATE" set_env_value "$ROOT_DIR/.env" "ENABLE_UPDATE" "$ENABLE_UPDATE"
set_env_value "$ROOT_DIR/.env" "ENABLE_LICENSE" "$ENABLE_LICENSE"
set_env_value "$ROOT_DIR/.env" "MYSQL_MODE" "$MYSQL_MODE" set_env_value "$ROOT_DIR/.env" "MYSQL_MODE" "$MYSQL_MODE"
set_env_value "$ROOT_DIR/.env" "MYSQL_HOST" "$MYSQL_HOST" set_env_value "$ROOT_DIR/.env" "MYSQL_HOST" "$MYSQL_HOST"
@ -148,9 +152,11 @@ set_env_value "$XUQM_ENV" "CONSOLE_DOMAIN" "$CONSOLE_DOMAIN"
[ -n "${IM_DOMAIN:-}" ] && set_env_value "$XUQM_ENV" "IM_DOMAIN" "$IM_DOMAIN" [ -n "${IM_DOMAIN:-}" ] && set_env_value "$XUQM_ENV" "IM_DOMAIN" "$IM_DOMAIN"
[ -n "${PUSH_DOMAIN:-}" ] && set_env_value "$XUQM_ENV" "PUSH_DOMAIN" "$PUSH_DOMAIN" [ -n "${PUSH_DOMAIN:-}" ] && set_env_value "$XUQM_ENV" "PUSH_DOMAIN" "$PUSH_DOMAIN"
[ -n "${UPDATE_DOMAIN:-}" ] && set_env_value "$XUQM_ENV" "UPDATE_DOMAIN" "$UPDATE_DOMAIN" [ -n "${UPDATE_DOMAIN:-}" ] && set_env_value "$XUQM_ENV" "UPDATE_DOMAIN" "$UPDATE_DOMAIN"
[ -n "${LICENSE_DOMAIN:-}" ] && set_env_value "$XUQM_ENV" "LICENSE_DOMAIN" "$LICENSE_DOMAIN"
set_env_value "$XUQM_ENV" "ENABLE_IM" "$ENABLE_IM" set_env_value "$XUQM_ENV" "ENABLE_IM" "$ENABLE_IM"
set_env_value "$XUQM_ENV" "ENABLE_PUSH" "$ENABLE_PUSH" set_env_value "$XUQM_ENV" "ENABLE_PUSH" "$ENABLE_PUSH"
set_env_value "$XUQM_ENV" "ENABLE_UPDATE" "$ENABLE_UPDATE" set_env_value "$XUQM_ENV" "ENABLE_UPDATE" "$ENABLE_UPDATE"
set_env_value "$XUQM_ENV" "ENABLE_LICENSE" "$ENABLE_LICENSE"
# Write secrets.env # Write secrets.env
set_env_value "$ROOT_DIR/config/secrets.env" "MYSQL_PASSWORD" "$MYSQL_PASSWORD" set_env_value "$ROOT_DIR/config/secrets.env" "MYSQL_PASSWORD" "$MYSQL_PASSWORD"

查看文件

@ -7,7 +7,7 @@
# 2. 写入配置(.env / secrets.env / xuqm.env / bootstrap.env / nginx # 2. 写入配置(.env / secrets.env / xuqm.env / bootstrap.env / nginx
# 3. 登录 ACR 镜像仓库 # 3. 登录 ACR 镜像仓库
# 4. 启动基础设施容器MySQL、Redis并等待就绪 # 4. 启动基础设施容器MySQL、Redis并等待就绪
# 5. 拉取镜像并启动所有业务容器base + im + push + update # 5. 拉取镜像并启动所有业务容器base + im + push + update + license
# 6. Schema 扩展is_default / deletable 列 + 删除保护触发器)+ 系统应用初始化 # 6. Schema 扩展is_default / deletable 列 + 删除保护触发器)+ 系统应用初始化
# 7. 租户初始化(新建:填写信息并验证;迁移:凭迁移密钥调用 API 导入) # 7. 租户初始化(新建:填写信息并验证;迁移:凭迁移密钥调用 API 导入)
# 8. 运行一键验证脚本确认所有服务正常 # 8. 运行一键验证脚本确认所有服务正常
@ -91,7 +91,7 @@ printf '\033[1;35m XuqmGroup 私有化一键部署脚本(全量部署)\033[
printf '\033[1;35m══════════════════════════════════════════════════\033[0m\n' printf '\033[1;35m══════════════════════════════════════════════════\033[0m\n'
printf ' 部署目录: %s\n' "$ROOT_DIR" printf ' 部署目录: %s\n' "$ROOT_DIR"
printf ' 镜像仓库: %s\n' "$REGISTRY" printf ' 镜像仓库: %s\n' "$REGISTRY"
printf ' 服务集: base + im + push + update\n\n' printf ' 服务集: base + im + push + update + license\n\n'
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# 0b. 租户初始化方式选择(交互式,在 Step 1 预检前执行) # 0b. 租户初始化方式选择(交互式,在 Step 1 预检前执行)
@ -298,10 +298,10 @@ for port in "$_NGINX_PORT"; do
fi fi
done done
# 自动检测内部服务端口冲突WSL2/多套部署场景下 11224-11230 可能被宿主机占用) # 自动检测内部服务端口冲突WSL2/多套部署场景下 11224-11231 可能被宿主机占用)
_SVC_PORT_OFFSET=0 _SVC_PORT_OFFSET=0
if ss -tlnp 2>/dev/null | grep -qE ':(11224|11225|11226|11228|11229|11230) '; then if ss -tlnp 2>/dev/null | grep -qE ':(11224|11225|11226|11228|11229|11230|11231) '; then
warn "检测到内部服务端口11224-11230已被占用,自动使用偏移 +1000021224-21230" warn "检测到内部服务端口11224-11231已被占用,自动使用偏移 +1000021224-21231"
_SVC_PORT_OFFSET=10000 _SVC_PORT_OFFSET=10000
fi fi
SVC_PORT_TENANT=$((11224 + _SVC_PORT_OFFSET)) SVC_PORT_TENANT=$((11224 + _SVC_PORT_OFFSET))
@ -310,8 +310,9 @@ SVC_PORT_WEB=$((11226 + _SVC_PORT_OFFSET))
SVC_PORT_IM=$((11228 + _SVC_PORT_OFFSET)) SVC_PORT_IM=$((11228 + _SVC_PORT_OFFSET))
SVC_PORT_PUSH=$((11229 + _SVC_PORT_OFFSET)) SVC_PORT_PUSH=$((11229 + _SVC_PORT_OFFSET))
SVC_PORT_UPDATE=$((11230 + _SVC_PORT_OFFSET)) SVC_PORT_UPDATE=$((11230 + _SVC_PORT_OFFSET))
[ "$_SVC_PORT_OFFSET" -eq 0 ] && ok "内部服务端口段 11224-11230 空闲" || \ SVC_PORT_LICENSE=$((11231 + _SVC_PORT_OFFSET))
ok "内部服务端口段自动调整为 21224-21230" [ "$_SVC_PORT_OFFSET" -eq 0 ] && ok "内部服务端口段 11224-11231 空闲" || \
ok "内部服务端口段自动调整为 21224-21231"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Step 2 — 写入配置 # Step 2 — 写入配置
@ -334,7 +335,7 @@ REGISTRY_PASSWORD=${REGISTRY_PASSWORD}
IMAGE_TAG=${IMAGE_TAG} IMAGE_TAG=${IMAGE_TAG}
# 启用全量服务(含内置路由 nginx # 启用全量服务(含内置路由 nginx
COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update,license
# nginx 入口绑定80=直接监听宿主机80;127.0.0.1:11223=本地监听需宿主机nginx转发 # nginx 入口绑定80=直接监听宿主机80;127.0.0.1:11223=本地监听需宿主机nginx转发
NGINX_BIND=${NGINX_BIND} NGINX_BIND=${NGINX_BIND}
@ -358,6 +359,7 @@ DOCS_DOMAIN=${CONSOLE_BASE}/docs
FILE_DOMAIN=${CONSOLE_BASE} FILE_DOMAIN=${CONSOLE_BASE}
IM_DOMAIN=${CONSOLE_BASE} IM_DOMAIN=${CONSOLE_BASE}
UPDATE_DOMAIN=${CONSOLE_BASE} UPDATE_DOMAIN=${CONSOLE_BASE}
LICENSE_DOMAIN=${CONSOLE_BASE}
PUSH_DOMAIN=${CONSOLE_BASE} PUSH_DOMAIN=${CONSOLE_BASE}
# 功能开关(全量启用) # 功能开关(全量启用)
@ -365,17 +367,19 @@ ENABLE_FILE=true
ENABLE_IM=true ENABLE_IM=true
ENABLE_PUSH=true ENABLE_PUSH=true
ENABLE_UPDATE=true ENABLE_UPDATE=true
ENABLE_LICENSE=true
TENANT_BOOTSTRAP_EMAIL=${DEPLOY_TENANT_EMAIL} TENANT_BOOTSTRAP_EMAIL=${DEPLOY_TENANT_EMAIL}
TENANT_BOOTSTRAP_APP_KEY=ak_private_default TENANT_BOOTSTRAP_APP_KEY=ak_private_default
# 内部服务宿主机端口绑定(自动偏移后的值,默认 11224-11230 # 内部服务宿主机端口绑定(自动偏移后的值,默认 11224-11231
SVC_PORT_TENANT=${SVC_PORT_TENANT} SVC_PORT_TENANT=${SVC_PORT_TENANT}
SVC_PORT_FILE=${SVC_PORT_FILE} SVC_PORT_FILE=${SVC_PORT_FILE}
SVC_PORT_WEB=${SVC_PORT_WEB} SVC_PORT_WEB=${SVC_PORT_WEB}
SVC_PORT_IM=${SVC_PORT_IM} SVC_PORT_IM=${SVC_PORT_IM}
SVC_PORT_PUSH=${SVC_PORT_PUSH} SVC_PORT_PUSH=${SVC_PORT_PUSH}
SVC_PORT_UPDATE=${SVC_PORT_UPDATE} SVC_PORT_UPDATE=${SVC_PORT_UPDATE}
SVC_PORT_LICENSE=${SVC_PORT_LICENSE}
EOF EOF
ok ".env 已写入" ok ".env 已写入"
@ -409,6 +413,7 @@ ENABLE_FILE=true
ENABLE_IM=true ENABLE_IM=true
ENABLE_PUSH=true ENABLE_PUSH=true
ENABLE_UPDATE=true ENABLE_UPDATE=true
ENABLE_LICENSE=true
# 数据库(容器内使用 Docker 服务名) # 数据库(容器内使用 Docker 服务名)
MYSQL_HOST=mysql MYSQL_HOST=mysql
@ -427,6 +432,7 @@ DOCS_DOMAIN=${CONSOLE_BASE}/docs
FILE_DOMAIN=${CONSOLE_BASE} FILE_DOMAIN=${CONSOLE_BASE}
IM_DOMAIN=${CONSOLE_BASE} IM_DOMAIN=${CONSOLE_BASE}
UPDATE_DOMAIN=${CONSOLE_BASE} UPDATE_DOMAIN=${CONSOLE_BASE}
LICENSE_DOMAIN=${CONSOLE_BASE}
PUSH_DOMAIN=${CONSOLE_BASE} PUSH_DOMAIN=${CONSOLE_BASE}
# SDK 对外服务地址(客户端 SDK 使用) # SDK 对外服务地址(客户端 SDK 使用)
@ -436,6 +442,9 @@ SDK_IM_WS_URL=${_WS_SCHEME}://${DEPLOY_HOST}/ws/im
# BugCollect 上报地址SDK 会在此基础上拼接 /bugcollect/v1/issues/batch # BugCollect 上报地址SDK 会在此基础上拼接 /bugcollect/v1/issues/batch
SDK_BUGCOLLECT_API_URL=${CONSOLE_BASE}/api SDK_BUGCOLLECT_API_URL=${CONSOLE_BASE}/api
# License 文件内嵌的 baseUrlSDK 读取 license.xuqm 时用此地址连接私有 license 服务)
LICENSE_PUBLIC_BASE_URL=${CONSOLE_BASE}
# 系统 IM 通信应用 key私有化服务间消息通知使用 # 系统 IM 通信应用 key私有化服务间消息通知使用
SYSTEM_APP_KEY=ak_409e217e4aa14254ad73ad3c SYSTEM_APP_KEY=ak_409e217e4aa14254ad73ad3c
EOF EOF
@ -508,7 +517,8 @@ cat > "$ROOT_DIR/config/sdk/xuqm-private-sdk.json" <<EOF
"file": true, "file": true,
"im": true, "im": true,
"push": true, "push": true,
"update": true "update": true,
"license": true
}, },
"connectTimeoutMs": 10000, "connectTimeoutMs": 10000,
"readTimeoutMs": 30000, "readTimeoutMs": 30000,
@ -526,7 +536,7 @@ cat > "$ROOT_DIR/config/docs/docs-runtime.json" <<EOF
"im": "${CONSOLE_BASE}", "im": "${CONSOLE_BASE}",
"update": "${CONSOLE_BASE}" "update": "${CONSOLE_BASE}"
}, },
"features": { "file": true, "im": true, "push": true, "update": true } "features": { "file": true, "im": true, "push": true, "update": true, "license": true }
} }
EOF EOF
@ -623,7 +633,7 @@ done
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Step 5 — 启动所有业务容器(全服务) # Step 5 — 启动所有业务容器(全服务)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
step "拉取镜像并启动所有业务容器base + im + push + update" step "拉取镜像并启动所有业务容器base + im + push + update + license"
docker compose \ docker compose \
--env-file "$ROOT_DIR/.env" \ --env-file "$ROOT_DIR/.env" \
@ -631,7 +641,7 @@ docker compose \
-f "$ROOT_DIR/docker-compose.infra.yml" \ -f "$ROOT_DIR/docker-compose.infra.yml" \
--profile base \ --profile base \
--profile infra-mysql --profile infra-redis \ --profile infra-mysql --profile infra-redis \
--profile im --profile push --profile update \ --profile im --profile push --profile update --profile license \
up -d up -d
# 等待 tenant-service 健康(直接打 9001 端口,不经过 nginx # 等待 tenant-service 健康(直接打 9001 端口,不经过 nginx
@ -766,6 +776,23 @@ if [ "$DEPLOY_MODE" = "new" ]; then
warn "租户记录暂未就绪tenant-service 可能仍在初始化),继续部署" warn "租户记录暂未就绪tenant-service 可能仍在初始化),继续部署"
fi fi
# 为 bootstrap 创建的 app 补写 app_licenseslicense-service 独立 DB 写入)
_LIC_SQL_NEW="$(mktemp /tmp/xuqm-lic-new-XXXXXX.sql)"
cat > "$_LIC_SQL_NEW" <<'LIC_SQL_NEW'
INSERT INTO app_licenses (app_key, name, max_devices, registered_devices,
expires_at, is_active, remark, created_at, updated_at)
SELECT a.app_key, a.name, 1000, 0, NULL, 1, '私有化新建', NOW(), NOW()
FROM t_app a
WHERE a.app_key != 'ak_409e217e4aa14254ad73ad3c'
ON DUPLICATE KEY UPDATE updated_at = updated_at;
LIC_SQL_NEW
docker exec -i "$MYSQL_CTR" \
mysql -u "$MYSQL_USERNAME" -p"${MYSQL_PASSWORD}" "$MYSQL_DATABASE" \
--default-character-set=utf8mb4 < "$_LIC_SQL_NEW" 2>/dev/null \
&& ok "app_licenses 记录已为所有应用创建" \
|| warn "app_licenses 写入遇到警告,继续"
rm -f "$_LIC_SQL_NEW"
else else
# ── 迁移租户:调用公有平台 export API → 私有平台 import API ── # ── 迁移租户:调用公有平台 export API → 私有平台 import API ──
@ -822,6 +849,23 @@ print(t.get('email', ''), t.get('username', ''))
DEPLOY_TENANT_USERNAME=$(printf '%s' "$_TENANT_INFO" | awk '{print $2}') DEPLOY_TENANT_USERNAME=$(printf '%s' "$_TENANT_INFO" | awk '{print $2}')
ok "租户 ${DEPLOY_TENANT_EMAIL} 数据已成功导入" ok "租户 ${DEPLOY_TENANT_EMAIL} 数据已成功导入"
# 为迁移的所有 app 在 app_licenses 表创建 license 记录license-service 独立 DB 写入)
_LIC_SQL="$(mktemp /tmp/xuqm-lic-XXXXXX.sql)"
cat > "$_LIC_SQL" <<'LIC_SQL'
INSERT INTO app_licenses (app_key, name, max_devices, registered_devices,
expires_at, is_active, remark, created_at, updated_at)
SELECT a.app_key, a.name, 1000, 0, NULL, 1, '私有化迁移', NOW(), NOW()
FROM t_app a
WHERE a.app_key != 'ak_409e217e4aa14254ad73ad3c'
ON DUPLICATE KEY UPDATE updated_at = updated_at;
LIC_SQL
docker exec -i "$MYSQL_CTR" \
mysql -u "$MYSQL_USERNAME" -p"${MYSQL_PASSWORD}" "$MYSQL_DATABASE" \
--default-character-set=utf8mb4 < "$_LIC_SQL" 2>/dev/null \
&& ok "app_licenses 记录已为所有迁移应用创建" \
|| warn "app_licenses 写入遇到警告,继续"
rm -f "$_LIC_SQL"
# 更新 bootstrap.env,防止重启时重新创建占位租户 # 更新 bootstrap.env,防止重启时重新创建占位租户
cat > "$ROOT_DIR/config/tenant/bootstrap.env" <<BSENV cat > "$ROOT_DIR/config/tenant/bootstrap.env" <<BSENV
TENANT_BOOTSTRAP_EMAIL=${DEPLOY_TENANT_EMAIL} TENANT_BOOTSTRAP_EMAIL=${DEPLOY_TENANT_EMAIL}

查看文件

@ -5,7 +5,7 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
. "$ROOT_DIR/scripts/lib.sh" . "$ROOT_DIR/scripts/lib.sh"
SERVICE="${1:-}" SERVICE="${1:-}"
[ -n "$SERVICE" ] || fail_json "XUQM_PRIVATE_1002" "service name is required (im|push|update)" "disable-service" [ -n "$SERVICE" ] || fail_json "XUQM_PRIVATE_1002" "service name is required (im|push|update|license)" "disable-service"
if [ ! -f "$ROOT_DIR/.env" ]; then if [ ! -f "$ROOT_DIR/.env" ]; then
cp "$ROOT_DIR/.env.example" "$ROOT_DIR/.env" cp "$ROOT_DIR/.env.example" "$ROOT_DIR/.env"
@ -16,8 +16,8 @@ audit "disable-service" "STARTED" "$SERVICE"
progress "disable-service" "STARTED" "$SERVICE" progress "disable-service" "STARTED" "$SERVICE"
case "$SERVICE" in case "$SERVICE" in
im|push|update) ;; im|push|update|license) ;;
*) fail_json "XUQM_PRIVATE_1002" "unknown service: $SERVICE (valid: im push update)" "disable-service" ;; *) fail_json "XUQM_PRIVATE_1002" "unknown service: $SERVICE (valid: im push update license)" "disable-service" ;;
esac esac
# Stop the container first (data is preserved) # Stop the container first (data is preserved)

查看文件

@ -46,7 +46,7 @@ grep -v -E '(PASSWORD|SECRET|TOKEN|KEY|SMTP|AUTH)' "$ROOT_DIR/config/xuqm.env" 2
tail -200 "$ROOT_DIR/logs/audit.log" 2>/dev/null > "$DIAG_DIR/audit-recent.log" || true tail -200 "$ROOT_DIR/logs/audit.log" 2>/dev/null > "$DIAG_DIR/audit-recent.log" || true
# Recent container logs (last 100 lines per service, sanitized) # Recent container logs (last 100 lines per service, sanitized)
for svc in tenant-service file-service nginx im-service push-service update-service; do for svc in tenant-service file-service nginx im-service push-service update-service license-service; do
CTR="$(compose ps -q "$svc" 2>/dev/null | head -1 || true)" CTR="$(compose ps -q "$svc" 2>/dev/null | head -1 || true)"
if [ -n "$CTR" ]; then if [ -n "$CTR" ]; then
docker logs --tail=100 "$CTR" 2>&1 | \ docker logs --tail=100 "$CTR" 2>&1 | \

查看文件

@ -5,7 +5,7 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
. "$ROOT_DIR/scripts/lib.sh" . "$ROOT_DIR/scripts/lib.sh"
SERVICE="${1:-}" SERVICE="${1:-}"
[ -n "$SERVICE" ] || fail_json "XUQM_PRIVATE_1002" "service name is required (im|push|update)" "enable-service" [ -n "$SERVICE" ] || fail_json "XUQM_PRIVATE_1002" "service name is required (im|push|update|license)" "enable-service"
if [ ! -f "$ROOT_DIR/.env" ]; then if [ ! -f "$ROOT_DIR/.env" ]; then
cp "$ROOT_DIR/.env.example" "$ROOT_DIR/.env" cp "$ROOT_DIR/.env.example" "$ROOT_DIR/.env"
@ -28,8 +28,12 @@ case "$SERVICE" in
set_env_value "$ROOT_DIR/.env" "ENABLE_UPDATE" "true" set_env_value "$ROOT_DIR/.env" "ENABLE_UPDATE" "true"
[ -n "${UPDATE_DOMAIN:-}" ] || fail_json "XUQM_PRIVATE_1003" "UPDATE_DOMAIN must be set before enabling update" "enable-service" [ -n "${UPDATE_DOMAIN:-}" ] || fail_json "XUQM_PRIVATE_1003" "UPDATE_DOMAIN must be set before enabling update" "enable-service"
;; ;;
license)
set_env_value "$ROOT_DIR/.env" "ENABLE_LICENSE" "true"
[ -n "${LICENSE_DOMAIN:-}" ] || fail_json "XUQM_PRIVATE_1003" "LICENSE_DOMAIN must be set before enabling license" "enable-service"
;;
*) *)
fail_json "XUQM_PRIVATE_1002" "unknown service: $SERVICE (valid: im push update)" "enable-service" fail_json "XUQM_PRIVATE_1002" "unknown service: $SERVICE (valid: im push update license)" "enable-service"
;; ;;
esac esac

查看文件

@ -53,6 +53,7 @@ profile_contains() { case ",${PROFILES}," in *","$1","*) return 0;; esac; return
profile_contains "im" && pull_and_save "im-service" profile_contains "im" && pull_and_save "im-service"
profile_contains "push" && pull_and_save "push-service" profile_contains "push" && pull_and_save "push-service"
profile_contains "update" && pull_and_save "update-service" profile_contains "update" && pull_and_save "update-service"
profile_contains "license" && pull_and_save "license-service"
# Infrastructure images (always include for managed mode) # Infrastructure images (always include for managed mode)
docker pull nginx:1.27-alpine 2>/dev/null && docker save nginx:1.27-alpine -o "$IMAGES_DIR/nginx.tar" || true docker pull nginx:1.27-alpine 2>/dev/null && docker save nginx:1.27-alpine -o "$IMAGES_DIR/nginx.tar" || true

查看文件

@ -40,7 +40,7 @@ for svc in tenant-service file-service tenant-web nginx; do
done done
# Optional service containers (docs-site included here — warn only if present but unhealthy) # Optional service containers (docs-site included here — warn only if present but unhealthy)
for svc in docs-site im-service push-service update-service; do for svc in docs-site im-service push-service update-service license-service; do
STATE="$(compose ps -q "$svc" 2>/dev/null | head -1 || true)" STATE="$(compose ps -q "$svc" 2>/dev/null | head -1 || true)"
if [ -n "$STATE" ]; then if [ -n "$STATE" ]; then
STATUS="$(docker inspect --format='{{.State.Status}}' "$STATE" 2>/dev/null || echo 'unknown')" STATUS="$(docker inspect --format='{{.State.Status}}' "$STATE" 2>/dev/null || echo 'unknown')"
@ -150,6 +150,9 @@ fi
[ "${ENABLE_UPDATE:-false}" = "true" ] && [ -n "${UPDATE_DOMAIN:-}" ] && \ [ "${ENABLE_UPDATE:-false}" = "true" ] && [ -n "${UPDATE_DOMAIN:-}" ] && \
http_check "http.update-service" "${UPDATE_DOMAIN}/actuator/health" http_check "http.update-service" "${UPDATE_DOMAIN}/actuator/health"
[ "${ENABLE_LICENSE:-false}" = "true" ] && [ -n "${LICENSE_DOMAIN:-}" ] && \
http_check "http.license-service" "${LICENSE_DOMAIN}/actuator/health"
# Disk space # Disk space
DISK_USE="$(df -h "$ROOT_DIR" | awk 'NR==2{print $5}' | tr -d '%')" DISK_USE="$(df -h "$ROOT_DIR" | awk 'NR==2{print $5}' | tr -d '%')"
if [ -n "$DISK_USE" ]; then if [ -n "$DISK_USE" ]; then

查看文件

@ -20,7 +20,7 @@ usage() {
Usage: ./scripts/install.sh [options] Usage: ./scripts/install.sh [options]
Options: Options:
--profile <profiles> Docker Compose profiles, for example: base,im,push,update --profile <profiles> Docker Compose profiles, for example: base,im,push,update,license
--mysql-mode <mode> external or managed --mysql-mode <mode> external or managed
--redis-mode <mode> external or managed --redis-mode <mode> external or managed
--offline-bundle <path> Load images from an offline bundle before deployment --offline-bundle <path> Load images from an offline bundle before deployment

查看文件

@ -352,6 +352,39 @@ if [ -f "$_XUQM_ENV_FILE" ] && ! grep -q "^SYSTEM_APP_KEY=" "$_XUQM_ENV_FILE"; t
fi fi
audit "migrate-tenant" "SYSTEM_APP_CREATED" "app_key=$_SYS_APP_KEY tenant=$TENANT_ID" audit "migrate-tenant" "SYSTEM_APP_CREATED" "app_key=$_SYS_APP_KEY tenant=$TENANT_ID"
# ---------------------------------------------------------------------------
# Step 4c — 为迁移的 app 在 app_licenses 表补录 license 记录
# ---------------------------------------------------------------------------
printf '\n[3c/5] 同步 app_licenses 记录license-service 授权表)...\n'
if [ -n "$APP_KEYS_RAW" ]; then
_LICENSE_SQL="$(mktemp /tmp/xuqm-license-XXXXXX.sql)"
{
printf 'SET FOREIGN_KEY_CHECKS=0;\n'
# 为每个迁移的 app 创建 license 记录(若已存在则跳过,保证幂等)
printf "INSERT INTO app_licenses (app_key, name, max_devices, registered_devices, expires_at, is_active, remark, created_at, updated_at)\n"
printf "SELECT a.app_key, a.name, 1000, 0, NULL, 1,\n"
printf " CONCAT('迁移自 %s — ', NOW()), NOW(), NOW()\n" "$TENANT_NICKNAME"
printf "FROM t_app a\n"
printf "WHERE a.app_key IN (%s)\n" "$APP_KEYS_SQL"
printf "ON DUPLICATE KEY UPDATE updated_at = updated_at;\n"
printf 'SET FOREIGN_KEY_CHECKS=1;\n'
} > "$_LICENSE_SQL"
if dst_mysql < "$_LICENSE_SQL" 2>/dev/null; then
printf ' app_licenses 记录已创建(共 %s 个应用,max_devices=1000,永不过期\n' "$APP_COUNT"
else
warn "app_licenses 写入遇到警告,请手动检查"
fi
rm -f "$_LICENSE_SQL"
# 验证写入结果
_LICENSE_COUNT="$(dst_mysql -N -e "SELECT COUNT(*) FROM app_licenses WHERE app_key IN (${APP_KEYS_SQL})" 2>/dev/null || echo 0)"
printf ' DB check: %s/%s 个 app_licenses 记录就绪\n' "${_LICENSE_COUNT:-0}" "$APP_COUNT"
fi
audit "migrate-tenant" "LICENSE_SYNCED" "tenant=$TENANT_ID apps=$APP_COUNT"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Step 5 — Restart tenant-service to flush any cached state # Step 5 — Restart tenant-service to flush any cached state
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

查看文件

@ -38,13 +38,15 @@ cat > "$ROOT_DIR/config/docs/docs-runtime.json" <<EOF
"file": "${FILE_DOMAIN:-}", "file": "${FILE_DOMAIN:-}",
"im": "${IM_DOMAIN:-}", "im": "${IM_DOMAIN:-}",
"update": "${UPDATE_DOMAIN:-}", "update": "${UPDATE_DOMAIN:-}",
"license": "${LICENSE_DOMAIN:-}",
"push": "${PUSH_DOMAIN:-}" "push": "${PUSH_DOMAIN:-}"
}, },
"features": { "features": {
"file": ${ENABLE_FILE:-true}, "file": ${ENABLE_FILE:-true},
"im": ${ENABLE_IM:-false}, "im": ${ENABLE_IM:-false},
"push": ${ENABLE_PUSH:-false}, "push": ${ENABLE_PUSH:-false},
"update": ${ENABLE_UPDATE:-false} "update": ${ENABLE_UPDATE:-false},
"license": ${ENABLE_LICENSE:-false}
} }
} }
EOF EOF
@ -61,12 +63,14 @@ cat > "$ROOT_DIR/config/sdk/xuqm-private-sdk.json" <<EOF
"imWsUrl": "$(ws_url "${IM_DOMAIN:-}")", "imWsUrl": "$(ws_url "${IM_DOMAIN:-}")",
"pushBaseUrl": "${PUSH_DOMAIN:-}", "pushBaseUrl": "${PUSH_DOMAIN:-}",
"updateBaseUrl": "${UPDATE_DOMAIN:-}", "updateBaseUrl": "${UPDATE_DOMAIN:-}",
"licenseBaseUrl": "${LICENSE_DOMAIN:-}",
"docsBaseUrl": "${DOCS_DOMAIN:-}", "docsBaseUrl": "${DOCS_DOMAIN:-}",
"features": { "features": {
"file": ${ENABLE_FILE:-true}, "file": ${ENABLE_FILE:-true},
"im": ${ENABLE_IM:-false}, "im": ${ENABLE_IM:-false},
"push": ${ENABLE_PUSH:-false}, "push": ${ENABLE_PUSH:-false},
"update": ${ENABLE_UPDATE:-false} "update": ${ENABLE_UPDATE:-false},
"license": ${ENABLE_LICENSE:-false}
}, },
"connectTimeoutMs": 10000, "connectTimeoutMs": 10000,
"readTimeoutMs": 30000, "readTimeoutMs": 30000,
@ -84,7 +88,8 @@ cat > "$ROOT_DIR/.deploy-state/current.json" <<EOF
"file": ${ENABLE_FILE:-true}, "file": ${ENABLE_FILE:-true},
"im": ${ENABLE_IM:-false}, "im": ${ENABLE_IM:-false},
"push": ${ENABLE_PUSH:-false}, "push": ${ENABLE_PUSH:-false},
"update": ${ENABLE_UPDATE:-false} "update": ${ENABLE_UPDATE:-false},
"license": ${ENABLE_LICENSE:-false}
}, },
"lastHealthcheck": null "lastHealthcheck": null
} }

查看文件

@ -216,6 +216,16 @@ else
ok "SDK_FILE_SERVICE_URL 正常: ${_CURRENT_FILE}" ok "SDK_FILE_SERVICE_URL 正常: ${_CURRENT_FILE}"
fi fi
# LICENSE_PUBLIC_BASE_URL生成 license.xuqm 文件时写入的 baseUrl,SDK 读取后用于连接私有 license 服务
_CURRENT_LICENSE_URL="$(grep '^LICENSE_PUBLIC_BASE_URL=' "$ROOT_DIR/config/xuqm.env" 2>/dev/null | cut -d= -f2- || true)"
if [ -z "$_CURRENT_LICENSE_URL" ] || _url_needs_fix "$_CURRENT_LICENSE_URL"; then
_set_env "$ROOT_DIR/config/xuqm.env" "LICENSE_PUBLIC_BASE_URL" "$_CONSOLE_DOMAIN"
ok "LICENSE_PUBLIC_BASE_URL 已更新 → ${_CONSOLE_DOMAIN}"
_FIXED=1
else
ok "LICENSE_PUBLIC_BASE_URL 正常: ${_CURRENT_LICENSE_URL}"
fi
# SDK_BUGCOLLECT_API_URLBugCollect SDK 上报地址SDK 会在此基础上拼接 /bugcollect/v1/... # SDK_BUGCOLLECT_API_URLBugCollect SDK 上报地址SDK 会在此基础上拼接 /bugcollect/v1/...
# 正确值:{CONSOLE_DOMAIN}/api,不含 /bugcollect 后缀 # 正确值:{CONSOLE_DOMAIN}/api,不含 /bugcollect 后缀
_CORRECT_BC_URL="${_CONSOLE_DOMAIN}/api" _CORRECT_BC_URL="${_CONSOLE_DOMAIN}/api"
@ -504,7 +514,7 @@ _COMPOSE="docker compose --env-file ${ROOT_DIR}/.env \
# 使用 up -d 而非 restartrestart 只重启容器进程,不会切换到新拉取的镜像; # 使用 up -d 而非 restartrestart 只重启容器进程,不会切换到新拉取的镜像;
# up -d 会检测镜像变更并重建容器,是应用新版本的正确方式。 # up -d 会检测镜像变更并重建容器,是应用新版本的正确方式。
for _svc in tenant-service im-service file-service update-service push-service tenant-web nginx; do for _svc in tenant-service im-service file-service update-service license-service push-service tenant-web nginx; do
# 仅重启已激活的服务(跳过未运行的可选服务) # 仅重启已激活的服务(跳过未运行的可选服务)
if $_COMPOSE ps --services --filter status=running 2>/dev/null | grep -q "^${_svc}$"; then if $_COMPOSE ps --services --filter status=running 2>/dev/null | grep -q "^${_svc}$"; then
info "应用新镜像并重启 ${_svc} ..." info "应用新镜像并重启 ${_svc} ..."

查看文件

@ -156,7 +156,7 @@ for CTR_SUFFIX in $REQUIRED_CONTAINERS; do
fi fi
done done
OPTIONAL_CONTAINERS="file-service im-service push-service update-service bugcollect-service symbolicator mysql redis" OPTIONAL_CONTAINERS="file-service im-service push-service update-service license-service bugcollect-service symbolicator mysql redis"
for CTR_SUFFIX in $OPTIONAL_CONTAINERS; do for CTR_SUFFIX in $OPTIONAL_CONTAINERS; do
CTR_NAME=$(docker ps --filter "name=$CTR_SUFFIX" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1) CTR_NAME=$(docker ps --filter "name=$CTR_SUFFIX" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1)
if [ -n "$CTR_NAME" ]; then if [ -n "$CTR_NAME" ]; then

查看文件

@ -17,12 +17,14 @@
- IM - IM
- Push - Push
- Update - Update
- License
## 可选服务 ## 可选服务
- IM未部署 / 已部署 - IM未部署 / 已部署
- Push未部署 / 已部署 - Push未部署 / 已部署
- Update未部署 / 已部署 - Update未部署 / 已部署
- License未部署 / 已部署
## 验收 ## 验收

查看文件

@ -20,4 +20,5 @@ DOCS_DOMAIN=https://docs.customer.com
FILE_DOMAIN=https://file.customer.com FILE_DOMAIN=https://file.customer.com
IM_DOMAIN=https://im.customer.com IM_DOMAIN=https://im.customer.com
UPDATE_DOMAIN=https://update.customer.com UPDATE_DOMAIN=https://update.customer.com
LICENSE_DOMAIN=https://license.customer.com
PUSH_DOMAIN=https://push.customer.com PUSH_DOMAIN=https://push.customer.com

查看文件

@ -20,4 +20,5 @@ DOCS_DOMAIN=https://docs.customer.com
FILE_DOMAIN=https://file.customer.com FILE_DOMAIN=https://file.customer.com
IM_DOMAIN=https://im.customer.com IM_DOMAIN=https://im.customer.com
UPDATE_DOMAIN=https://update.customer.com UPDATE_DOMAIN=https://update.customer.com
LICENSE_DOMAIN=https://license.customer.com
PUSH_DOMAIN=https://push.customer.com PUSH_DOMAIN=https://push.customer.com