diff --git a/config/nginx/conf.d/xuqm.conf b/config/nginx/conf.d/xuqm.conf index 7e92c28..87cd41b 100644 --- a/config/nginx/conf.d/xuqm.conf +++ b/config/nginx/conf.d/xuqm.conf @@ -63,8 +63,9 @@ server { } # IM WebSocket 长连接(客户端消息收发) - location /ws/im/ { - proxy_pass http://im-service:8082/ws/im/; + # 注意:不加尾部斜杠,否则 /ws/im?token=xxx 不匹配(nginx prefix matching 不含 ?) + location /ws/im { + proxy_pass http://im-service:8082/ws/im; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; @@ -83,6 +84,18 @@ server { proxy_read_timeout 60s; } + # ----------- 文件服务 API 路径(file-service:8086)----------- + # 注意:必须在通用 /api/ 之前声明,防止走 tenant-service 的 100m 限制 + location /api/file/ { + proxy_pass http://file-service:8086/api/file/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + client_max_body_size 500m; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } + # ----------- 核心 API(tenant-service:9001)----------- # 注意:tenant-service 运行在 9001 端口(不是 8080) # 包含:认证、租户管理、App 管理、SDK 配置、私有化部署状态 diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 6a6b328..a6ef86e 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -21,8 +21,8 @@ # - 在 XuqmGroup-PrivateDeploy 仓库根目录下执行本脚本 # # 覆盖默认值(可通过环境变量传入): -# DEPLOY_HOST 目标机器 IP / 主机名(默认 127.0.0.1) -# 纯 IP 部署无需域名,局域网内可全功能使用 +# DEPLOY_HOST 目标机器 IP / 主机名(默认 localhost) +# 部署向导会提示配置域名与 HTTPS # REGISTRY_PASSWORD ACR 密码(默认 xuqinmin1022) # MYSQL_ROOT_PASSWORD、MYSQL_PASSWORD、REDIS_PASSWORD 同理 @@ -42,8 +42,12 @@ IMAGE_TAG="latest" # 部署主机(用于健康检查 HTTP 请求) # 可以是 IP 地址,无需域名,局域网内即可完整使用所有服务 -DEPLOY_HOST="${DEPLOY_HOST:-127.0.0.1}" +DEPLOY_HOST="${DEPLOY_HOST:-localhost}" CONSOLE_BASE="http://${DEPLOY_HOST}" +_HTTP_SCHEME="http" +_WS_SCHEME="ws" +DEPLOY_DOMAIN="" +USE_HTTPS=false # MySQL(managed 模式,由 Docker 容器托管) MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-XuqmRoot@2026}" @@ -68,6 +72,7 @@ TOTAL_STEPS=8 # 工具函数 # --------------------------------------------------------------------------- log() { printf '\n\033[1;36m[%d/%d] %s\033[0m\n' "$STEP" "$TOTAL_STEPS" "$*"; } +info() { printf ' \033[1;36m→\033[0m %s\n' "$*"; } ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; } warn() { printf ' \033[33m⚠\033[0m %s\n' "$*"; } fail() { printf '\n\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; } @@ -180,6 +185,40 @@ else _BOOTSTRAP_PASSWORD="already-migrated-do-not-use" fi +# --------------------------------------------------------------------------- +# 0c. 域名 / HTTPS 配置(交互式) +# --------------------------------------------------------------------------- +printf '\n\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n' +printf '\033[1;33m 访问域名配置(可选)\033[0m\n' +printf '\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n' +printf ' 默认使用 localhost(适合本机或局域网访问)\n' +printf ' 如有公网域名,可在此配置并自动申请 HTTPS 证书\n\n' + +read -rp " 是否使用自定义域名?[y/N]: " _domain_yn +if [[ "${_domain_yn:-N}" =~ ^[Yy]$ ]]; then + while [ -z "$DEPLOY_DOMAIN" ]; do + read -rp " 请输入域名(如 xuqm.example.com,不含 http://): " DEPLOY_DOMAIN + if ! printf '%s' "$DEPLOY_DOMAIN" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9._-]+\.[a-zA-Z]{2,}$'; then + warn "域名格式不正确,请重新输入(例: xuqm.example.com)" + DEPLOY_DOMAIN="" + fi + done + DEPLOY_HOST="$DEPLOY_DOMAIN" + + read -rp " 是否配置 HTTPS(需域名已解析到本机,将自动申请 Let's Encrypt 证书)?[y/N]: " _https_yn + if [[ "${_https_yn:-N}" =~ ^[Yy]$ ]]; then + USE_HTTPS=true + _HTTP_SCHEME="https" + _WS_SCHEME="wss" + CONSOLE_BASE="https://${DEPLOY_DOMAIN}" + else + CONSOLE_BASE="http://${DEPLOY_DOMAIN}" + fi + ok "域名: ${DEPLOY_DOMAIN},协议: ${_HTTP_SCHEME}" +else + ok "使用默认地址: ${CONSOLE_BASE}" +fi + # --------------------------------------------------------------------------- # Step 1 — 预检 # --------------------------------------------------------------------------- @@ -316,7 +355,7 @@ PUSH_DOMAIN=${CONSOLE_BASE} # SDK 对外服务地址(客户端 SDK 使用) SDK_FILE_SERVICE_URL=${CONSOLE_BASE} SDK_IM_API_URL=${CONSOLE_BASE} -SDK_IM_WS_URL=ws://${DEPLOY_HOST}/ws/im +SDK_IM_WS_URL=${_WS_SCHEME}://${DEPLOY_HOST}/ws/im # 系统 IM 通信应用 key(私有化服务间消息通知使用此 app_key 连接 IM 服务) # 与公有化平台 xuqinmin12 租户下的平台系统应用 key 保持一致 @@ -335,13 +374,192 @@ EOF chmod 600 "$ROOT_DIR/config/tenant/bootstrap.env" ok "config/tenant/bootstrap.env 已写入 (chmod 600)" -# config/nginx/conf.d/xuqm.conf — 从仓库中的文件复制(不再内嵌 heredoc,避免两处维护) +# config/nginx/conf.d/xuqm.conf mkdir -p "$ROOT_DIR/config/nginx/conf.d" -NGINX_SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/config/nginx/conf.d/xuqm.conf" -if [ ! -f "$NGINX_SRC" ]; then - fail "nginx 配置文件不存在: $NGINX_SRC(请确保在仓库根目录运行本脚本)" +NGINX_CONF_PATH="$ROOT_DIR/config/nginx/conf.d/xuqm.conf" +if [ ! -f "$NGINX_CONF_PATH" ]; then + fail "nginx 配置文件不存在: $NGINX_CONF_PATH(请确保在仓库根目录运行本脚本)" fi -# (以下 heredoc 仅作本地备份,deploy 时以上面复制为准,此段不执行) + +if $USE_HTTPS; then + # ── HTTPS 模式:安装 certbot → 申请证书 → 生成 SSL nginx 配置 → compose override ── + + # 安装 certbot(若未安装) + if ! command -v certbot >/dev/null 2>&1; then + info "安装 certbot ..." + if [ "$(command -v apt-get)" ]; then + apt-get install -y -qq certbot 2>/dev/null || fail "certbot 安装失败,请手动执行: apt install certbot" + elif [ "$(command -v yum)" ]; then + yum install -y -q certbot 2>/dev/null || fail "certbot 安装失败,请手动执行: yum install certbot" + else + fail "无法自动安装 certbot,请手动安装后重新运行" + fi + fi + ok "certbot $(certbot --version 2>&1 | head -1)" + + # 申请 Let's Encrypt 证书(standalone,此时 nginx 尚未启动) + CERT_DIR="/etc/letsencrypt/live/${DEPLOY_DOMAIN}" + if [ -f "${CERT_DIR}/fullchain.pem" ]; then + ok "证书已存在,跳过申请: ${CERT_DIR}" + else + info "申请 Let's Encrypt 证书(域名: ${DEPLOY_DOMAIN},需要端口 80 可达)..." + certbot certonly --standalone -d "$DEPLOY_DOMAIN" \ + --agree-tos --register-unsafely-without-email --non-interactive \ + || fail "证书申请失败,请确认域名 ${DEPLOY_DOMAIN} 已解析到本机且端口 80 可访问" + ok "证书已申请: ${CERT_DIR}" + fi + + # 生成包含 SSL 的 nginx 配置(覆盖仓库静态文件) + cat > "$NGINX_CONF_PATH" << NGINX_CONF +# ============================================================================= +# XuqmGroup 私有化部署 — Nginx HTTPS 配置(由 deploy.sh 自动生成) +# 域名: ${DEPLOY_DOMAIN} +# ============================================================================= + +# HTTP → HTTPS 重定向 +server { + listen 80; + server_name ${DEPLOY_DOMAIN}; + return 301 https://\$host\$request_uri; +} + +server { + listen 443 ssl; + server_name ${DEPLOY_DOMAIN}; + + ssl_certificate /etc/letsencrypt/live/${DEPLOY_DOMAIN}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/${DEPLOY_DOMAIN}/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + + charset utf-8; + client_max_body_size 100m; + + location /health { + return 200 "ok\n"; + add_header Content-Type text/plain; + } + + location /api/v1/updates/ { + proxy_pass http://update-service:8084/api/v1/updates/; + 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; + } + + location /api/v1/rn/ { + proxy_pass http://update-service:8084/api/v1/rn/; + 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 120s; + } + + location /api/im/ { + proxy_pass http://im-service:8082/api/im/; + 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; + } + + location /ws/im { + proxy_pass http://im-service:8082/ws/im; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_read_timeout 3600s; + } + + location /api/license/ { + proxy_pass http://license-service:8085/api/license/; + 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; + } + + location /api/file/ { + proxy_pass http://file-service:8086/api/file/; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + client_max_body_size 500m; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } + + location /api/ { + proxy_pass http://tenant-service:9001/api/; + 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; + } + + location /actuator/ { + proxy_pass http://tenant-service:9001/actuator/; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + } + + location /file/ { + proxy_pass http://file-service:8086/file/; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + client_max_body_size 500m; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } + + location /docs/ { + proxy_pass http://tenant-web:80/docs/; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + } + + location /ops { + proxy_pass http://ops-web:80; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + } + + location / { + proxy_pass http://tenant-web:80; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header Accept-Encoding ""; + sub_filter 'wss://im.dev.xuqinmin.com/ws/im' 'wss://\$host/ws/im'; + sub_filter 'https://dev.xuqinmin.com' 'https://\$host'; + sub_filter_once off; + sub_filter_types text/javascript application/javascript; + } +} +NGINX_CONF + ok "nginx HTTPS 配置已生成(${DEPLOY_DOMAIN})" + + # docker-compose override:挂载 /etc/letsencrypt 到 nginx 容器 + cat > "$ROOT_DIR/docker-compose.override.yml" << 'OVERRIDE_YAML' +# 由 deploy.sh 自动生成 — HTTPS 模式需要挂载 Let's Encrypt 证书 +services: + nginx: + volumes: + - /etc/letsencrypt:/etc/letsencrypt:ro +OVERRIDE_YAML + ok "docker-compose.override.yml 已生成(SSL 证书挂载)" + +else + ok "config/nginx/conf.d/xuqm.conf 就绪(HTTP 模式,使用仓库内文件)" +fi + +# (以下 heredoc 为静态配置的备份注释,不执行) : <<'NGINX_CONF' # ============================================================================= # XuqmGroup 私有化部署 — Nginx 路由配置 @@ -474,8 +692,6 @@ server { } } NGINX_CONF -# 实际使用仓库中的文件(已跳过上方 heredoc) -ok "config/nginx/conf.d/xuqm.conf 就绪(使用仓库内文件)" # config/vendors/push.env — 推送服务厂商凭据(初始为关闭状态,按需开启) mkdir -p "$ROOT_DIR/config/vendors" @@ -528,7 +744,7 @@ cat > "$ROOT_DIR/config/sdk/xuqm-private-sdk.json" <