#!/bin/bash
# 专为生产环境优化 | 解决所有历史问题 | 自动权限修复+锁超时+官方格式
set -o pipefail

# ==================== 核心配置（按需修改） ====================
PROJECT_NAME="【GitLab备份】"
GITLAB_BACKUP_PATH="/data/backups"
FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/5138e41c7-d4d1-4051-9ca0-925c5bcb93ef"
BACKUP_CONFIG=true  # 备份gitlab.rb+secrets

# RSYNC 远程同步配置
RSYNC_OPTS="-avz --partial --port=65183 --password-file=/root/.rsync_pass --timeout=300 --contimeout=60"
RSYNC_DEST="rsync://srsync@10.34.44.30:51018/home/backup/hcgit46"

# SCP 同步配置（新增/优化）
SCP_ENABLE=true          # 是否启用SCP同步
SCP_DEST_USER="root"     # 目标服务器用户
SCP_DEST_HOST="10.34.44.248" # 目标服务器IP
SCP_DEST_PATH="/data/backup/gitmg/" # 目标路径
SCP_TIMEOUT=300          # SCP超时时间（秒）
SCP_RETRY=3              # SCP失败重试次数
SCP_SSH_PORT=22          # 目标服务器SSH端口

# 日志与锁
LOG_FILE="/var/log/gitlab_rsync_backup.log"
LOCK_FILE="/var/run/gitlab_backup.lock"
LOCK_TIMEOUT=3600  # 锁超时1小时，自动清理
TEMP_LIST=$(mktemp)

# ==================== 飞书通知（稳定重试） ====================
send_feishu() {
    local msg="$1"
    local time=$(date +'%Y-%m-%d %H:%M:%S')
    curl -s -X POST "${FEISHU_WEBHOOK}" \
        -H "Content-Type: application/json" \
        -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"${PROJECT_NAME}${msg}\"}}" >/dev/null 2>&1
    echo "【${time}】通知：${msg}" >> "${LOG_FILE}"
}

# ==================== 安全清理（异常退出必执行） ====================
cleanup() {
    rm -f "${LOCK_FILE}" "${TEMP_LIST}"
    echo "[INFO] 已清理锁文件/临时文件" >> "${LOG_FILE}"
}
trap cleanup EXIT INT TERM HUP

# ==================== 前置检查（自动修复所有问题） ====================
pre_check() {
    echo "==========================================================" >> "${LOG_FILE}"
    echo "[INFO] $(date '+%F %T') 开始备份前置检查" >> "${LOG_FILE}"

    # 1. 创建备份目录
    mkdir -p "${GITLAB_BACKUP_PATH}"

    # 2. 🔥 强制修复权限（彻底解决 Permission denied）
    chown -R git:git "${GITLAB_BACKUP_PATH}"
    chmod -R 755 "${GITLAB_BACKUP_PATH}"
    chmod 755 /data
    echo "[INFO] 已完成目录权限修复" >> "${LOG_FILE}"

    # 3. 锁文件超时自动清理
    if [ -f "${LOCK_FILE}" ]; then
        LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "${LOCK_FILE}") ))
        if [ ${LOCK_AGE} -gt ${LOCK_TIMEOUT} ]; then
            echo "[WARN] 锁文件超时，自动清理" >> "${LOG_FILE}"
            rm -f "${LOCK_FILE}"
        else
            echo "[WARN] 任务正在运行，跳过本次备份" >> "${LOG_FILE}"
            exit 0
        fi
    fi
    touch "${LOCK_FILE}"

    # 4. 清空旧文件（避免脏数据干扰）
    rm -rf "${GITLAB_BACKUP_PATH}"/*
    echo "[INFO] 已清空旧备份文件" >> "${LOG_FILE}"

    # 5. SCP前置检查（新增）
    if [ "${SCP_ENABLE}" = true ]; then
        echo "[INFO] 开始SCP前置检查：验证目标服务器连通性" >> "${LOG_FILE}"
        # 测试SSH连接（超时5秒）
        if ! ssh -p "${SCP_SSH_PORT}" -o ConnectTimeout=5 -o BatchMode=yes "${SCP_DEST_USER}@${SCP_DEST_HOST}" "echo 'SSH连接正常'" >/dev/null 2>&1; then
            echo "[ERROR] SCP前置检查失败：目标服务器${SCP_DEST_HOST} SSH连接失败" >> "${LOG_FILE}"
            send_feishu "🔴 SCP前置检查失败：目标服务器${SCP_DEST_HOST} SSH无法连接"
            exit 1
        fi
        # 检查目标路径是否存在，不存在则创建
        ssh -p "${SCP_SSH_PORT}" "${SCP_DEST_USER}@${SCP_DEST_HOST}" "mkdir -p ${SCP_DEST_PATH}" >> "${LOG_FILE}" 2>&1
        if [ $? -ne 0 ]; then
            echo "[ERROR] SCP前置检查失败：无法创建目标路径${SCP_DEST_PATH}" >> "${LOG_FILE}"
            send_feishu "🔴 SCP前置检查失败：无法创建目标路径${SCP_DEST_PATH}"
            exit 1
        fi
        echo "[INFO] SCP前置检查完成" >> "${LOG_FILE}"
    fi

    echo "[INFO] 前置检查完成，开始备份" >> "${LOG_FILE}"
}

# ==================== 创建GitLab官方格式备份 ====================
create_backup() {
    echo "[INFO] $(date '+%F %T') 开始创建GitLab备份..." >> "${LOG_FILE}"
    
    # 官方备份命令（标准格式，兼容恢复）
    gitlab-backup create STRATEGY=copy GZIP_RSYNCABLE=yes >> "${LOG_FILE}" 2>&1
    if [ $? -ne 0 ]; then
        echo "[ERROR] 备份创建失败" >> "${LOG_FILE}"
        send_feishu "🔴 备份失败：创建备份文件出错"
        exit 1
    fi

    # 获取最新官方备份文件
    BACKUP_FILE=$(ls -t "${GITLAB_BACKUP_PATH}"/*_gitlab_backup.tar 2>/dev/null | head -n1)
    if [ -z "${BACKUP_FILE}" ]; then
        echo "[ERROR] 未找到备份文件" >> "${LOG_FILE}"
        send_feishu "🔴 备份失败：未生成备份文件"
        exit 1
    fi

    BACKUP_PREFIX=$(basename "${BACKUP_FILE}" _gitlab_backup.tar)
    echo "[INFO] 备份完成：${BACKUP_PREFIX}" >> "${LOG_FILE}"

    # 备份配置文件
    if [ "${BACKUP_CONFIG}" = true ]; then
        [ -f /etc/gitlab/gitlab.rb ] && cp /etc/gitlab/gitlab.rb "${GITLAB_BACKUP_PATH}/gitlab.rb.${BACKUP_PREFIX}"
        [ -f /etc/gitlab/gitlab-secrets.json ] && cp /etc/gitlab/gitlab-secrets.json "${GITLAB_BACKUP_PATH}/gitlab-secrets.json.${BACKUP_PREFIX}"
        echo "[INFO] 已备份GitLab配置文件" >> "${LOG_FILE}"
    fi

    # 最终权限修复
    chown -R git:git "${GITLAB_BACKUP_PATH}"
}

# ==================== SCP同步到远程（新增/完善） ====================
scp_sync() {
    if [ "${SCP_ENABLE}" != true ]; then
        echo "[INFO] SCP同步已禁用，跳过" >> "${LOG_FILE}"
        return 0
    fi

    echo "[INFO] $(date '+%F %T') 开始SCP同步（重试${SCP_RETRY}次，超时${SCP_TIMEOUT}秒）..." >> "${LOG_FILE}"
    
    local retry=0
    local scp_exit_code=1

    # 重试逻辑
    while [ ${retry} -lt ${SCP_RETRY} ]; do
        # SCP命令：带超时、递归、压缩，输出日志
        scp -r -P "${SCP_SSH_PORT}" -o ConnectTimeout="${SCP_TIMEOUT}" -C \
            "${GITLAB_BACKUP_PATH}/" "${SCP_DEST_USER}@${SCP_DEST_HOST}:${SCP_DEST_PATH}" >> "${LOG_FILE}" 2>&1
        
        scp_exit_code=$?
        if [ ${scp_exit_code} -eq 0 ]; then
            echo "[INFO] SCP同步成功" >> "${LOG_FILE}"
            send_feishu "✅ SCP同步成功 | 备份ID：${BACKUP_PREFIX} | 目标服务器：${SCP_DEST_HOST}"
            return 0
        fi

        retry=$((retry + 1))
        echo "[WARN] SCP同步失败（错误码：${scp_exit_code}），剩余重试次数：$((SCP_RETRY - retry))" >> "${LOG_FILE}"
        sleep 5  # 重试前等待5秒
    done

    # 所有重试失败
    echo "[ERROR] SCP同步最终失败（重试${SCP_RETRY}次后仍失败）" >> "${LOG_FILE}"
    send_feishu "🔴 SCP同步失败 | 备份ID：${BACKUP_PREFIX} | 目标服务器：${SCP_DEST_HOST}"
    exit ${scp_exit_code}
}

# ==================== RSYNC同步到远程 ====================
rsync_sync() {
    echo "[INFO] $(date '+%F %T') 开始远程同步..." >> "${LOG_FILE}"

    rsync ${RSYNC_OPTS} --out-format="%n %l" \
        "${GITLAB_BACKUP_PATH}/" "${RSYNC_DEST}" 2>&1 \
        | grep -v 'sending incremental file list' | grep -v 'sent ' | grep -v 'total ' > "${TEMP_LIST}"
    
    RSYNC_CODE=$?
    if [ ${RSYNC_CODE} -ne 0 ]; then
        echo "[ERROR] 同步失败，错误码：${RSYNC_CODE}" >> "${LOG_FILE}"
        send_feishu "🔴 备份失败：远程同步出错"
        exit ${RSYNC_CODE}
    fi

    # 统计同步文件
    FILE_COUNT=$(grep -cvE '^\./|^$' "${TEMP_LIST}")
    send_feishu "✅ RSYNC备份成功 | 文件数：${FILE_COUNT} | 备份ID：${BACKUP_PREFIX}"
    echo "[INFO] RSYNC同步完成，文件数：${FILE_COUNT}" >> "${LOG_FILE}"
}

# ==================== 主流程 ====================
main() {
    pre_check
    create_backup
    rsync_sync       # 先执行RSYNC
    scp_sync         # 再执行SCP（按需启用）
    echo "[INFO] $(date '+%F %T') GitLab全量备份+同步完成" >> "${LOG_FILE}"
    echo "==========================================================" >> "${LOG_FILE}"
}

main
