#!/bin/bash # ============================================================================== # RCLONE ENTERPRISE-GRADE SYNC SCRIPT (v4.1 - Corrected & Final) # ============================================================================== # # 版本: 4.1 # 修正: # - [关键] 恢复了 rclone sync 命令中缺失的 --backup-dir 参数,确保文件在 # 被覆盖或删除前能正确备份。 # # 完整功能: # - 单向同步 (rclone sync) # - 智能备份被覆盖/删除的文件到按天组织的目录 # - 防止并发执行的锁文件机制 # - 自动清理指定天数之前的旧备份 # - 自动日志轮转,防止日志文件无限增长 # - 成功或失败时发送钉钉通知 # - 失败时,通知中包含错误日志摘要 # # ============================================================================== # ============================ 用户配置区域 ============================ # --- Rclone 配置 --- SOURCE_DIR="/data2/immich/library/" REMOTE_PATH="onedrive:immich/library/" # 备份文件的父目录。脚本将在此目录下创建每日备份文件夹。 BACKUP_PARENT_PATH="onedrive:immich/backup/" # 动态生成当天的完整备份路径,用于 --backup-dir 参数 TODAY_BACKUP_PATH="${BACKUP_PARENT_PATH}$(date +%Y-%m-%d)" LOG_FILE="/data2/immich/log/rclone-immich-sync.log" # 这个log_file也需要上传到onedrive,使用另外一个进程 # --- 锁文件配置 --- LOCK_FILE="/data2/immich/rclone-immich-sync.lock" # --- 日志与备份清理配置 --- LOG_MAX_SIZE_MB=200 # 备份保留天数。所有文件修改时间早于这个天数的备份将被删除。 BACKUP_RETENTION_DAYS=180 # --- 钉钉机器人配置 --- DINGTALK_ACCESS_TOKEN="d10822224bf76dfcadad9da452cd37fafd3439aac0a1a4bf592214f9a3768a69" DINGTALK_SECRET="SECdb923bef42b35beca1254215d9abcfe142465628ff91e4fcaa22678bcec5c9da" # 加签:SECdb923bef42b35beca1254215d9abcfe142465628ff91e4fcaa22678bcec5c9da # https://oapi.dingtalk.com/robot/send?access_token=d10822224bf76dfcadad9da452cd37fafd3439aac0a1a4bf592214f9a3768a69 # --- 代理设置 --- PROXY_URL="http://127.0.0.1:1099" ROXY_TEST_URL="https://pac-9d7.pages.dev/access" PROXY_KEYWORD="ACCESS" # ============================ 初始化与检查 ============================ # --- 锁文件检查 --- if ! mkdir "${LOCK_FILE}" 2>/dev/null; then echo "$(date '+%Y-%m-%d %H:%M:%S') - 错误:检测到上一个同步任务仍在运行。脚本退出以防止重复执行。" | tee -a "${LOG_FILE}" exit 1 fi # --- 设定陷阱 (trap),确保脚本在任何情况下退出时都能删除锁文件 --- trap 'rm -rf "${LOCK_FILE}"' EXIT # ========================= 钉钉通知函数 (无需修改) ========================= # 此函数已包含 jq 依赖来安全处理消息内容 # 请确保已安装 jq: sudo apt-get install jq 或 sudo yum install jq urlencode() { string="${1}"; strlen=${#string}; encoded=""; pos c o for (( pos=0 ; pos> "${LOG_FILE}"; return fi message="$1"; timestamp=$(date +%s%N | cut -b1-13); string_to_sign="${timestamp}\n${DINGTALK_SECRET}" sign_base64=$(echo -ne "$string_to_sign" | openssl dgst -sha256 -hmac "$DINGTALK_SECRET" -binary | base64) sign_encoded=$(urlencode "$sign_base64") webhook_url="https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_ACCESS_TOKEN}&timetamp=${timestamp}&sign=${sign_encoded}" json_payload=$(jq -n --arg content "$message" '{"msgtype": "text", "text": {"content": $content}}') curl -s -X POST "${webhook_url}" -H 'Content-Type: application/json' -d "${json_payload}" >> "${LOG_FILE}" 2>&1 } # ============================ 主任务:文件同步 ============================ echo "==================================================" >> "${LOG_FILE}" echo "$(date '+%Y-%m-%d %H:%M:%S') - 开始执行 Immich 库同步任务..." >> "${LOG_FILE}" # ============================ 日志轮转逻辑 (无需修改) ============================ if [ -f "${LOG_FILE}" ]; then max_size_bytes=$((LOG_MAX_SIZE_MB * 1024 * 1024)); current_size_bytes=$(stat -c%s "${LOG_FILE}") if [ ${current_size_bytes} -gt ${max_size_bytes} ]; then archive_name="${LOG_FILE}.$(date +%Y%m%d-%H%M%S).gz"; echo "$(date '+%Y-%m-%d %H:%M:%S') - 日志文件大小超限,正在归档..." >> "${LOG_FILE}" gzip -c "${LOG_FILE}" > "${archive_name}" && > "${LOG_FILE}" echo "$(date '+%Y-%m-%d %H:%M:%S') - 日志归档完成。" >> "${LOG_FILE}" fi fi # -------- 设置代理 -------- echo "-------- 设置代理 ----------" >> "${LOG_FILE}" test_response=$(curl -s --proxy "$PROXY_URL" "$PROXY_TEST_URL" --connect-timeout 5) if [ "$test_response" != "$PROXY_KEYWORD" ]; then echo "代理测试失败 (返回值: '$response'),无代理模式连接..." >> "${LOG_FILE}" unset https_proxy else export https_proxy=$PROXY_URL echo "使用代理模式连接..." >> "${LOG_FILE}" fi # ============== rclone sync "${SOURCE_DIR}" "${REMOTE_PATH}" \ --fast-list \ --checkers 32 \ --transfers 8 \ --bwlimit 1000M \ --backup-dir "${TODAY_BACKUP_PATH}" \ --retries 5 \ --retries-sleep 30s \ --log-file "${LOG_FILE}" \ --onedrive-no-version \ -v sync_exit_code=$? # ======================== 结果检查与通知 ======================== if [ ${sync_exit_code} -eq 0 ]; then log_message="$(date '+%Y-%m-%d %H:%M:%S') - Immich 库同步成功完成。" dingtalk_message="【备份成功】服务器上的 Immich 库已于 $(date '+%Y-%m-%d %H:%M:%S') 成功同步到 OneDrive。" echo "${log_message}" >> "${LOG_FILE}" send_dingtalk_notification "${dingtalk_message}" # --- 子任务:清理旧备份 (仅在主任务成功后执行) --- echo "$(date '+%Y-%m-%d %H:%M:%S') - 开始清理 ${BACKUP_RETENTION_DAYS} 天前的旧备份..." >> "${LOG_FILE}" # 删除备份父目录中,所有修改时间早于指定天数的 文件,然后用 --rmdirs 删除因此变空的目录 rclone delete "${BACKUP_PARENT_PATH}" \ --min-age "${BACKUP_RETENTION_DAYS}d" \ --rmdirs \ --log-file "${LOG_FILE}" \ -v cleanup_exit_code=$? if [ ${cleanup_exit_code} -ne 0 ]; then echo "$(date '+%Y-%m-%d %H:%M:%S') - 警告:旧备份清理失败!退出代码: ${cleanup_exit_code}" >> "${LOG_FILE}" send_dingtalk_notification "【备份警告】主同步任务成功,但清理旧备份失败。请检查日志。" fi else # --- 主任务失败 --- log_message="$(date '+%Y-%m-%d %H:%M:%S') - 错误:Immich 库同步失败!退出代码: ${sync_exit_code}" error_details=$(tail -n 10 "${LOG_FILE}") dingtalk_message="【备份失败】警告!服务器上的 Immich 库同步任务失败,请立即检查! 退出代码: ${sync_exit_code} 错误摘要 (最后10行日志): --------------------- ${error_details}" echo "${log_message}" >> "${LOG_FILE}" send_dingtalk_notification "${dingtalk_message}" fi echo "$(date '+%Y-%m-%d %H:%M:%S') - 任务脚本执行完毕。" >> "${LOG_FILE}" echo "==================================================" >> "${LOG_FILE}" echo "" >> "${LOG_FILE}" exit ${sync_exit_code}