2026-05-18 19:49:31 +08:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
|
|
. "$ROOT_DIR/scripts/lib.sh"
|
|
|
|
|
|
feat: implement complete private deployment scripts (P1-P4)
- upgrade.sh/rollback.sh: backup→pull→rolling restart→healthcheck→auto-rollback
- backup.sh/restore.sh: mysqldump+redis BGSAVE+config tar, SHA256 manifest, restore with checksum verification
- healthcheck.sh: Docker/container/MySQL/Redis/HTTP/disk checks, JSON output to .deploy-state/
- doctor.sh: sanitized diagnostics archive, vendor API TCP connectivity, cert expiry
- export-offline-bundle.sh: docker pull+save for all profile images, load-images.sh, SHA256
- configure.sh: interactive/non-interactive mode, MySQL/Redis mode selection, domain prompts
- enable-service.sh: domain validation, docker pull + compose up, healthcheck
- disable-service.sh: compose stop+rm, profile removal, render-config
- renew-cert.sh: acme.sh/certbot, --dry-run, backup old cert, nginx reload on success
- alert-webhook.sh: WeCom/DingTalk/Feishu webhook, message sanitization
- bench.sh: ab/wrk/curl benchmark, JSON report with docker stats
- rotate-secrets.sh: JWT and internal token rotation
- vendor credential templates: push.env and store-submit.env with full credential comments
- render-config.sh: auto-sync SDK URL env vars (SDK_FILE_SERVICE_URL, SDK_IM_API_URL, SDK_IM_WS_URL)
- All scripts pass bash -n syntax check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:49:25 +08:00
|
|
|
# Non-interactive mode: skip prompts if all required vars are already set
|
|
|
|
|
NON_INTERACTIVE="${NON_INTERACTIVE:-false}"
|
|
|
|
|
[ -t 0 ] || NON_INTERACTIVE=true
|
|
|
|
|
|
2026-05-18 19:49:31 +08:00
|
|
|
audit "configure" "STARTED" "rendering initial config"
|
|
|
|
|
progress "configure" "STARTED" "rendering initial config"
|
|
|
|
|
|
feat: implement complete private deployment scripts (P1-P4)
- upgrade.sh/rollback.sh: backup→pull→rolling restart→healthcheck→auto-rollback
- backup.sh/restore.sh: mysqldump+redis BGSAVE+config tar, SHA256 manifest, restore with checksum verification
- healthcheck.sh: Docker/container/MySQL/Redis/HTTP/disk checks, JSON output to .deploy-state/
- doctor.sh: sanitized diagnostics archive, vendor API TCP connectivity, cert expiry
- export-offline-bundle.sh: docker pull+save for all profile images, load-images.sh, SHA256
- configure.sh: interactive/non-interactive mode, MySQL/Redis mode selection, domain prompts
- enable-service.sh: domain validation, docker pull + compose up, healthcheck
- disable-service.sh: compose stop+rm, profile removal, render-config
- renew-cert.sh: acme.sh/certbot, --dry-run, backup old cert, nginx reload on success
- alert-webhook.sh: WeCom/DingTalk/Feishu webhook, message sanitization
- bench.sh: ab/wrk/curl benchmark, JSON report with docker stats
- rotate-secrets.sh: JWT and internal token rotation
- vendor credential templates: push.env and store-submit.env with full credential comments
- render-config.sh: auto-sync SDK URL env vars (SDK_FILE_SERVICE_URL, SDK_IM_API_URL, SDK_IM_WS_URL)
- All scripts pass bash -n syntax check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:49:25 +08:00
|
|
|
# Initialize from template if missing
|
2026-05-18 19:49:31 +08:00
|
|
|
if [ ! -f "$ROOT_DIR/.env" ]; then
|
|
|
|
|
cp "$ROOT_DIR/.env.example" "$ROOT_DIR/.env"
|
|
|
|
|
fi
|
|
|
|
|
ensure_secret_file
|
feat: implement complete private deployment scripts (P1-P4)
- upgrade.sh/rollback.sh: backup→pull→rolling restart→healthcheck→auto-rollback
- backup.sh/restore.sh: mysqldump+redis BGSAVE+config tar, SHA256 manifest, restore with checksum verification
- healthcheck.sh: Docker/container/MySQL/Redis/HTTP/disk checks, JSON output to .deploy-state/
- doctor.sh: sanitized diagnostics archive, vendor API TCP connectivity, cert expiry
- export-offline-bundle.sh: docker pull+save for all profile images, load-images.sh, SHA256
- configure.sh: interactive/non-interactive mode, MySQL/Redis mode selection, domain prompts
- enable-service.sh: domain validation, docker pull + compose up, healthcheck
- disable-service.sh: compose stop+rm, profile removal, render-config
- renew-cert.sh: acme.sh/certbot, --dry-run, backup old cert, nginx reload on success
- alert-webhook.sh: WeCom/DingTalk/Feishu webhook, message sanitization
- bench.sh: ab/wrk/curl benchmark, JSON report with docker stats
- rotate-secrets.sh: JWT and internal token rotation
- vendor credential templates: push.env and store-submit.env with full credential comments
- render-config.sh: auto-sync SDK URL env vars (SDK_FILE_SERVICE_URL, SDK_IM_API_URL, SDK_IM_WS_URL)
- All scripts pass bash -n syntax check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:49:25 +08:00
|
|
|
load_env
|
|
|
|
|
|
|
|
|
|
ask() {
|
|
|
|
|
local prompt="$1"
|
|
|
|
|
local current="${2:-}"
|
|
|
|
|
local default="${3:-}"
|
|
|
|
|
[ "$NON_INTERACTIVE" = "true" ] && { printf '%s' "${current:-$default}"; return; }
|
|
|
|
|
local display_default=""
|
|
|
|
|
[ -n "$current" ] && display_default=" [current: $current]"
|
|
|
|
|
[ -z "$current" ] && [ -n "$default" ] && display_default=" [default: $default]"
|
|
|
|
|
printf '%s%s: ' "$prompt" "$display_default" >&2
|
|
|
|
|
read -r value
|
|
|
|
|
printf '%s' "${value:-${current:-$default}}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ask_secret() {
|
|
|
|
|
local prompt="$1"
|
|
|
|
|
[ "$NON_INTERACTIVE" = "true" ] && { printf 'change-me'; return; }
|
|
|
|
|
printf '%s (leave blank to generate): ' "$prompt" >&2
|
|
|
|
|
read -rs value
|
|
|
|
|
printf '\n' >&2
|
|
|
|
|
if [ -z "$value" ]; then
|
|
|
|
|
value="$(random_secret)"
|
|
|
|
|
printf ' → generated strong password\n' >&2
|
|
|
|
|
fi
|
|
|
|
|
printf '%s' "$value"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf '\n=== XuqmGroup Private Deployment Configuration ===\n\n'
|
|
|
|
|
|
|
|
|
|
# Domains
|
|
|
|
|
CONSOLE_DOMAIN="$(ask "Console domain (e.g. https://console.customer.com)" "${CONSOLE_DOMAIN:-}" "")"
|
|
|
|
|
OPS_DOMAIN="$(ask "Ops domain (e.g. https://ops.customer.com)" "${OPS_DOMAIN:-}" "")"
|
|
|
|
|
DOCS_DOMAIN="$(ask "Docs domain (e.g. https://docs.customer.com)" "${DOCS_DOMAIN:-}" "")"
|
|
|
|
|
FILE_DOMAIN="$(ask "File domain (e.g. https://file.customer.com)" "${FILE_DOMAIN:-}" "")"
|
|
|
|
|
|
|
|
|
|
# Optional service domains (only ask if enabling)
|
|
|
|
|
ENABLE_IM="${ENABLE_IM:-false}"
|
|
|
|
|
ENABLE_PUSH="${ENABLE_PUSH:-false}"
|
|
|
|
|
ENABLE_UPDATE="${ENABLE_UPDATE:-false}"
|
|
|
|
|
ENABLE_LICENSE="${ENABLE_LICENSE:-false}"
|
|
|
|
|
|
|
|
|
|
if [ "$NON_INTERACTIVE" != "true" ]; then
|
|
|
|
|
printf '\nWhich optional services to enable now? (space-separated, e.g. "im update")\n'
|
|
|
|
|
printf 'Available: im push update license\nCurrent: %s\n' "$COMPOSE_PROFILES"
|
|
|
|
|
printf 'Enter services (blank to keep current): '
|
|
|
|
|
read -r OPT_SVCS
|
|
|
|
|
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 '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
|
|
|
|
|
printf '\nMySQL mode (external=you provide connection / managed=deploy script installs MySQL)\n'
|
|
|
|
|
MYSQL_MODE="$(ask "MySQL mode" "${MYSQL_MODE:-external}" "external")"
|
|
|
|
|
if [ "$MYSQL_MODE" = "external" ]; then
|
|
|
|
|
MYSQL_HOST="$(ask "MySQL host" "${MYSQL_HOST:-127.0.0.1}" "127.0.0.1")"
|
|
|
|
|
MYSQL_PORT="$(ask "MySQL port" "${MYSQL_PORT:-3306}" "3306")"
|
|
|
|
|
MYSQL_DATABASE="$(ask "MySQL database" "${MYSQL_DATABASE:-xuqm_private}" "xuqm_private")"
|
|
|
|
|
MYSQL_USERNAME="$(ask "MySQL username" "${MYSQL_USERNAME:-xuqm}" "xuqm")"
|
|
|
|
|
MYSQL_PASSWORD="$(ask_secret "MySQL password")"
|
|
|
|
|
else
|
|
|
|
|
MYSQL_HOST="mysql"
|
|
|
|
|
MYSQL_PORT="3306"
|
|
|
|
|
MYSQL_DATABASE="$(ask "MySQL database" "${MYSQL_DATABASE:-xuqm_private}" "xuqm_private")"
|
|
|
|
|
MYSQL_USERNAME="$(ask "MySQL username" "${MYSQL_USERNAME:-xuqm}" "xuqm")"
|
|
|
|
|
MYSQL_PASSWORD="$(ensure_env_value "$ROOT_DIR/config/secrets.env" "MYSQL_PASSWORD" "${MYSQL_PASSWORD:-}" "$(random_secret)")"
|
|
|
|
|
MYSQL_ROOT_PASSWORD="$(ensure_env_value "$ROOT_DIR/config/secrets.env" "MYSQL_ROOT_PASSWORD" "${MYSQL_ROOT_PASSWORD:-}" "$(random_secret)")"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Redis mode
|
|
|
|
|
printf '\nRedis mode (external=you provide connection / managed=deploy script installs Redis)\n'
|
|
|
|
|
REDIS_MODE="$(ask "Redis mode" "${REDIS_MODE:-external}" "external")"
|
|
|
|
|
if [ "$REDIS_MODE" = "external" ]; then
|
|
|
|
|
REDIS_HOST="$(ask "Redis host" "${REDIS_HOST:-127.0.0.1}" "127.0.0.1")"
|
|
|
|
|
REDIS_PORT="$(ask "Redis port" "${REDIS_PORT:-6379}" "6379")"
|
|
|
|
|
REDIS_PASSWORD="$(ask_secret "Redis password")"
|
|
|
|
|
else
|
|
|
|
|
REDIS_HOST="redis"
|
|
|
|
|
REDIS_PORT="6379"
|
|
|
|
|
REDIS_PASSWORD="$(ensure_env_value "$ROOT_DIR/config/secrets.env" "REDIS_PASSWORD" "${REDIS_PASSWORD:-}" "$(random_secret)")"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Bootstrap tenant
|
|
|
|
|
printf '\nBootstrap admin account:\n'
|
|
|
|
|
TENANT_BOOTSTRAP_EMAIL="$(ask "Admin email" "${TENANT_BOOTSTRAP_EMAIL:-admin@customer.com}" "admin@customer.com")"
|
|
|
|
|
TENANT_BOOTSTRAP_APP_KEY="$(ask "App key" "${TENANT_BOOTSTRAP_APP_KEY:-ak_private_default}" "ak_private_default")"
|
|
|
|
|
|
|
|
|
|
# Registry
|
|
|
|
|
REGISTRY="$(ask "Docker registry" "${REGISTRY:-registry.example.com/xuqm}" "registry.example.com/xuqm")"
|
|
|
|
|
IMAGE_TAG="$(ask "Image tag" "${IMAGE_TAG:-$(cat "$ROOT_DIR/VERSION" | tr -d '[:space:]')}" "")"
|
|
|
|
|
|
|
|
|
|
# Write .env (idempotent — only update values that changed)
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "CONSOLE_DOMAIN" "$CONSOLE_DOMAIN"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "OPS_DOMAIN" "$OPS_DOMAIN"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "DOCS_DOMAIN" "$DOCS_DOMAIN"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "FILE_DOMAIN" "$FILE_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 "${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_PUSH" "$ENABLE_PUSH"
|
|
|
|
|
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_HOST" "$MYSQL_HOST"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "MYSQL_PORT" "$MYSQL_PORT"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "MYSQL_DATABASE" "$MYSQL_DATABASE"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "MYSQL_USERNAME" "$MYSQL_USERNAME"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "REDIS_MODE" "$REDIS_MODE"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "REDIS_HOST" "$REDIS_HOST"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "REDIS_PORT" "$REDIS_PORT"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "REGISTRY" "$REGISTRY"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "IMAGE_TAG" "$IMAGE_TAG"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "TENANT_BOOTSTRAP_EMAIL" "$TENANT_BOOTSTRAP_EMAIL"
|
|
|
|
|
set_env_value "$ROOT_DIR/.env" "TENANT_BOOTSTRAP_APP_KEY" "$TENANT_BOOTSTRAP_APP_KEY"
|
|
|
|
|
|
|
|
|
|
# Write secrets.env
|
|
|
|
|
set_env_value "$ROOT_DIR/config/secrets.env" "MYSQL_PASSWORD" "$MYSQL_PASSWORD"
|
|
|
|
|
[ -n "${MYSQL_ROOT_PASSWORD:-}" ] && \
|
|
|
|
|
set_env_value "$ROOT_DIR/config/secrets.env" "MYSQL_ROOT_PASSWORD" "$MYSQL_ROOT_PASSWORD"
|
|
|
|
|
set_env_value "$ROOT_DIR/config/secrets.env" "REDIS_PASSWORD" "$REDIS_PASSWORD"
|
2026-05-18 19:49:31 +08:00
|
|
|
|
|
|
|
|
"$ROOT_DIR/scripts/render-config.sh"
|
|
|
|
|
|
|
|
|
|
audit "configure" "DONE" "config ready"
|
|
|
|
|
progress "configure" "DONE" "config ready"
|
feat: implement complete private deployment scripts (P1-P4)
- upgrade.sh/rollback.sh: backup→pull→rolling restart→healthcheck→auto-rollback
- backup.sh/restore.sh: mysqldump+redis BGSAVE+config tar, SHA256 manifest, restore with checksum verification
- healthcheck.sh: Docker/container/MySQL/Redis/HTTP/disk checks, JSON output to .deploy-state/
- doctor.sh: sanitized diagnostics archive, vendor API TCP connectivity, cert expiry
- export-offline-bundle.sh: docker pull+save for all profile images, load-images.sh, SHA256
- configure.sh: interactive/non-interactive mode, MySQL/Redis mode selection, domain prompts
- enable-service.sh: domain validation, docker pull + compose up, healthcheck
- disable-service.sh: compose stop+rm, profile removal, render-config
- renew-cert.sh: acme.sh/certbot, --dry-run, backup old cert, nginx reload on success
- alert-webhook.sh: WeCom/DingTalk/Feishu webhook, message sanitization
- bench.sh: ab/wrk/curl benchmark, JSON report with docker stats
- rotate-secrets.sh: JWT and internal token rotation
- vendor credential templates: push.env and store-submit.env with full credential comments
- render-config.sh: auto-sync SDK URL env vars (SDK_FILE_SERVICE_URL, SDK_IM_API_URL, SDK_IM_WS_URL)
- All scripts pass bash -n syntax check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:49:25 +08:00
|
|
|
printf '\nConfiguration complete. Review .env and config/secrets.env before deployment.\n'
|