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
|
|
|
load_env
|
2026-05-18 19:49:31 +08:00
|
|
|
|
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
|
|
|
BACKUP_DIR="$ROOT_DIR/data/backups"
|
|
|
|
|
TIMESTAMP="$(date +%Y%m%d%H%M%S)"
|
|
|
|
|
BACKUP_NAME="xuqm-backup-${PRIVATE_VERSION:-unknown}-${TIMESTAMP}"
|
|
|
|
|
BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
|
2026-05-18 19:49:31 +08:00
|
|
|
|
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
|
|
|
audit "backup" "STARTED" "backup=$BACKUP_NAME"
|
|
|
|
|
progress "backup" "STARTED" "backup=$BACKUP_NAME"
|
|
|
|
|
|
|
|
|
|
mkdir -p "$BACKUP_PATH"
|
|
|
|
|
|
|
|
|
|
# Config backup (excluding secrets.env for security)
|
|
|
|
|
tar --exclude='config/secrets.env' -czf "$BACKUP_PATH/config.tar.gz" \
|
2026-05-18 19:49:31 +08:00
|
|
|
-C "$ROOT_DIR" VERSION .env config .deploy-state
|
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
|
|
|
audit "backup" "CONFIG_DONE" "config.tar.gz"
|
|
|
|
|
|
|
|
|
|
# MySQL backup
|
|
|
|
|
if [ "${MYSQL_MODE:-external}" = "managed" ]; then
|
|
|
|
|
MYSQL_CTR="$(compose ps -q mysql 2>/dev/null | head -1 || true)"
|
|
|
|
|
if [ -n "$MYSQL_CTR" ]; then
|
|
|
|
|
docker exec "$MYSQL_CTR" \
|
|
|
|
|
mysqldump -u root -p"${MYSQL_ROOT_PASSWORD:-}" \
|
|
|
|
|
--single-transaction --routines --triggers --events \
|
|
|
|
|
"${MYSQL_DATABASE:-xuqm_private}" \
|
|
|
|
|
> "$BACKUP_PATH/mysql.sql"
|
|
|
|
|
audit "backup" "MYSQL_DONE" "mysqldump from managed container"
|
|
|
|
|
else
|
|
|
|
|
audit "backup" "MYSQL_SKIPPED" "managed mysql container not running"
|
|
|
|
|
printf '# managed MySQL container not running at backup time\n' > "$BACKUP_PATH/mysql.sql"
|
|
|
|
|
fi
|
|
|
|
|
elif [ -n "${MYSQL_HOST:-}" ] && [ "${MYSQL_HOST:-}" != "127.0.0.1" ]; then
|
|
|
|
|
if command -v mysqldump >/dev/null 2>&1; then
|
|
|
|
|
mysqldump -h "${MYSQL_HOST}" -P "${MYSQL_PORT:-3306}" \
|
|
|
|
|
-u "${MYSQL_USERNAME:-}" -p"${MYSQL_PASSWORD:-}" \
|
|
|
|
|
--single-transaction --routines --triggers --events \
|
|
|
|
|
"${MYSQL_DATABASE:-xuqm_private}" \
|
|
|
|
|
> "$BACKUP_PATH/mysql.sql"
|
|
|
|
|
audit "backup" "MYSQL_DONE" "mysqldump from external host"
|
|
|
|
|
else
|
|
|
|
|
printf '# mysqldump not available on this host. Run manually:\n' > "$BACKUP_PATH/mysql.sql"
|
|
|
|
|
printf '# mysqldump -h %s -P %s -u %s -p <password> --single-transaction %s > mysql.sql\n' \
|
|
|
|
|
"${MYSQL_HOST}" "${MYSQL_PORT:-3306}" "${MYSQL_USERNAME:-}" "${MYSQL_DATABASE:-}" >> "$BACKUP_PATH/mysql.sql"
|
|
|
|
|
audit "backup" "MYSQL_SKIPPED" "mysqldump not available; manual instructions written"
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
audit "backup" "MYSQL_SKIPPED" "external mode with localhost — run mysqldump manually"
|
|
|
|
|
printf '# External MySQL on localhost. Run mysqldump manually.\n' > "$BACKUP_PATH/mysql.sql"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Redis backup
|
|
|
|
|
if [ "${REDIS_MODE:-external}" = "managed" ]; then
|
|
|
|
|
REDIS_CTR="$(compose ps -q redis 2>/dev/null | head -1 || true)"
|
|
|
|
|
if [ -n "$REDIS_CTR" ]; then
|
|
|
|
|
docker exec "$REDIS_CTR" redis-cli -a "${REDIS_PASSWORD:-}" BGSAVE 2>/dev/null || true
|
|
|
|
|
sleep 2
|
|
|
|
|
docker cp "$REDIS_CTR:/data/dump.rdb" "$BACKUP_PATH/redis-dump.rdb" 2>/dev/null || true
|
|
|
|
|
audit "backup" "REDIS_DONE" "bgsave + copy from managed container"
|
|
|
|
|
else
|
|
|
|
|
audit "backup" "REDIS_SKIPPED" "managed redis container not running"
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
if command -v redis-cli >/dev/null 2>&1 && [ -n "${REDIS_HOST:-}" ]; then
|
|
|
|
|
redis-cli -h "${REDIS_HOST}" -p "${REDIS_PORT:-6379}" -a "${REDIS_PASSWORD:-}" \
|
|
|
|
|
--no-auth-warning BGSAVE 2>/dev/null || true
|
|
|
|
|
audit "backup" "REDIS_TRIGGERED" "BGSAVE triggered on external Redis"
|
|
|
|
|
else
|
|
|
|
|
audit "backup" "REDIS_SKIPPED" "redis-cli not available"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Write manifest
|
|
|
|
|
MANIFEST="$BACKUP_PATH/manifest.json"
|
|
|
|
|
cat > "$MANIFEST" <<EOF
|
|
|
|
|
{
|
|
|
|
|
"backupName": "$BACKUP_NAME",
|
|
|
|
|
"timestamp": "$(now)",
|
|
|
|
|
"privateVersion": "${PRIVATE_VERSION:-unknown}",
|
|
|
|
|
"mysqlMode": "${MYSQL_MODE:-external}",
|
|
|
|
|
"redisMode": "${REDIS_MODE:-external}",
|
|
|
|
|
"profiles": "${COMPOSE_PROFILES:-base}",
|
|
|
|
|
"files": {
|
|
|
|
|
"config": "config.tar.gz",
|
|
|
|
|
"mysql": "mysql.sql",
|
|
|
|
|
"redis": "redis-dump.rdb"
|
|
|
|
|
},
|
|
|
|
|
"restoreNotes": "Run: ./scripts/restore.sh $BACKUP_NAME"
|
|
|
|
|
}
|
|
|
|
|
EOF
|
2026-05-18 19:49:31 +08:00
|
|
|
|
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
|
|
|
# Compute checksums
|
|
|
|
|
(cd "$BACKUP_DIR" && find "$BACKUP_NAME" -type f -exec sha256sum {} \; > "$BACKUP_PATH/sha256sums.txt")
|
2026-05-18 19:49:31 +08:00
|
|
|
|
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
|
|
|
audit "backup" "DONE" "backup=$BACKUP_NAME path=$BACKUP_PATH"
|
|
|
|
|
progress "backup" "DONE" "backup=$BACKUP_NAME"
|
|
|
|
|
printf 'Backup complete: %s\n' "$BACKUP_PATH"
|