note/tech/immich.sh
2025-11-19 10:16:05 +08:00

167 lines
7.5 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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<strlen ; pos++ )); do c=${string:$pos:1}; case "$c" in [-_.a-zA-Z0-9] ) o="${c}" ;; * ) printf -v o '%%%02x' "'$c"; esac; encoded+="${o}"; done
echo "${encoded}"
}
send_dingtalk_notification() {
if [[ -z "${DINGTALK_ACCESS_TOKEN}" || -z "${DINGTALK_SECRET}" || "${DINGTALK_ACCESS_TOKEN}" == "YOUR_ACCESS_TOKEN_HERE" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - 未配置钉钉机器人,跳过发送通知。" >> "${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}