#!/usr/bin/env bash set -Eeuo pipefail if [ "${EUID}" -ne 0 ]; then echo "需要 root 权限"; exit 1; fi SRC_DIR=$(cd "$(dirname "$0")" && pwd) DEST_DIR=/srv/taiko-web SONGS_DIR="$DEST_DIR/public/songs" BACKUP_DIR="$DEST_DIR/.backup_songs_$(date +%Y%m%d_%H%M%S)" echo "正在检测部署模式..." MODE="unknown" if [ -f "$DEST_DIR/docker-compose.yml" ]; then MODE="docker" elif systemctl list-unit-files | grep -q '^taiko-web\.service'; then MODE="direct" fi echo "部署模式: $MODE" # Function: Common Backup backup_songs() { if [ -d "$SONGS_DIR" ]; then echo "备份歌曲目录..." mkdir -p "$BACKUP_DIR" rsync -a "$SONGS_DIR/" "$BACKUP_DIR/" || cp -a "$SONGS_DIR/." "$BACKUP_DIR/" fi } restore_songs() { if [ -d "$BACKUP_DIR" ]; then echo "恢复歌曲目录..." mkdir -p "$SONGS_DIR" rsync -a "$BACKUP_DIR/" "$SONGS_DIR/" || cp -a "$BACKUP_DIR/." "$SONGS_DIR/" fi } sync_files() { echo "同步文件到 $DEST_DIR..." mkdir -p "$DEST_DIR" # Sync but exclude data directories and config rsync -a --delete \ --exclude '.git' \ --exclude '.venv' \ --exclude 'public/songs' \ --exclude 'mongo-data' \ --exclude 'redis-data' \ --exclude 'config.py' \ --exclude 'docker-compose.yml' \ "$SRC_DIR/" "$DEST_DIR/" # If config.py missing in dest, copy example (only for first time, but update shouldn't overwrite) if [ ! -f "$DEST_DIR/config.py" ] && [ -f "$SRC_DIR/config.example.py" ]; then cp "$SRC_DIR/config.example.py" "$DEST_DIR/config.py" fi # If docker mode, explicit check for docker-compose.yml? # Usually setup.sh generates it only. # If we updated setup.sh (like I just did), we might want to update docker-compose.yml? # No, user config might be there. Better leave it unless missing. # However, if setup.sh changed docker-compose template, existing one might be stale. # But usually docker-compose.yml is static enough. } if [ "$MODE" == "docker" ]; then echo "=== 更新 Docker 部署 ===" # Backup backup_songs # Stop containers cd "$DEST_DIR" if command -v docker-compose >/dev/null 2>&1; then docker-compose down else docker compose down fi # Sync sync_files # Restore (if needed, though rsync exclude should handle it, double safety) restore_songs # Rebuild and Start echo "重建并启动容器..." if command -v docker-compose >/dev/null 2>&1; then docker-compose up -d --build --remove-orphans else docker compose up -d --build --remove-orphans fi echo "清理旧镜像..." docker image prune -f || true echo "=== Docker 更新完成 ===" elif [ "$MODE" == "direct" ]; then echo "=== 更新直接部署 ===" systemctl stop taiko-web || true backup_songs sync_files # Update venv if [ -x "$DEST_DIR/.venv/bin/pip" ]; then echo "更新依赖..." "$DEST_DIR/.venv/bin/pip" install -U pip "$DEST_DIR/.venv/bin/pip" install -r "$DEST_DIR/requirements.txt" fi chown -R www-data:www-data "$DEST_DIR" restore_songs systemctl daemon-reload || true systemctl restart taiko-web || systemctl start taiko-web || true systemctl is-active --quiet taiko-web echo "=== 直接部署更新完成 ===" else echo "未检测到已知部署 (Docker 或 Systemd)。请先运行 setup.sh 进行安装。" exit 1 fi