Files

92 lines
3.4 KiB
Bash
Executable File
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.
#!/usr/bin/env bash
# =============================================================================
# clover 备份脚本(跑在 VPS 宿主机上,建议 cron 每日执行)
#
# ./scripts/backup.sh # 备份到 ./backups/
# BACKUP_DIR=/mnt/backup ./scripts/backup.sh
#
# 依赖:curl;使用 .env 里的 RECONCILE_TOKEN 认证(面板设置页也可生成 API token)
# cron 示例(每日 3:30,保留 14 天):
# 30 3 * * * cd /root/clover && ./scripts/backup.sh >> /var/log/clover-backup.log 2>&1
# =============================================================================
set -euo pipefail
cd "$(dirname "$0")/.."
[ -f .env ] || { echo "找不到 .env(请在项目根目录运行)" >&2; exit 1; }
# shellcheck disable=SC1091
set -a; . ./.env; set +a
: "${PANEL_HOSTNAME:?PANEL_HOSTNAME 未配置}"
: "${RECONCILE_TOKEN:?RECONCILE_TOKEN 未配置(见 .env)}"
BASE_URL="${BACKUP_BASE_URL:-https://${PANEL_HOSTNAME}}"
BACKUP_DIR="${BACKUP_DIR:-./backups}"
RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-14}"
STAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "${BACKUP_DIR}"
echo "[backup] $(date -Is) 开始 → ${BACKUP_DIR}"
fetch() {
local what="$1" out="$2"
local downloaded=0
# 默认通过容器内回环访问面板,不依赖公网 DNS/TLS;自定义 URL 时保留 HTTPs 路径。
if [ -z "${BACKUP_BASE_URL:-}" ] && command -v docker >/dev/null 2>&1 \
&& [ "$(docker inspect -f '{{.State.Running}}' clover-panel 2>/dev/null || true)" = true ]; then
if docker exec -e "CLOVER_BACKUP_WHAT=${what}" clover-panel node -e '
(async () => {
const response = await fetch(`http://127.0.0.1:3000/api/v1/system/backup?what=${encodeURIComponent(process.env.CLOVER_BACKUP_WHAT)}`, {
headers: { Authorization: `Bearer ${process.env.RECONCILE_TOKEN || ""}` },
signal: AbortSignal.timeout(300000),
});
if (!response.ok) {
console.error(`[backup] HTTP ${response.status}: ${await response.text()}`);
process.exitCode = 1;
return;
}
process.stdout.write(Buffer.from(await response.arrayBuffer()));
})().catch((error) => { console.error(`[backup] ${error.message}`); process.exitCode = 1; });
' > "${out}"; then
downloaded=1
fi
fi
if [ "${downloaded}" -eq 0 ] && curl -fsS --max-time 300 \
-H "Authorization: Bearer ${RECONCILE_TOKEN}" \
"${BASE_URL}/api/v1/system/backup?what=${what}" \
-o "${out}"; then
downloaded=1
fi
if [ "${downloaded}" -eq 1 ]; then
local size
size="$(wc -c < "${out}")"
echo "[backup] OK ${what} → ${out} (${size} bytes)"
# 极小文件通常是错误 JSON,判一下
if [ "${size}" -lt 200 ]; then
echo "[backup] 警告:${out} 体积异常,内容如下:" >&2
head -c 300 "${out}" >&2; echo >&2
return 1
fi
else
echo "[backup] 失败:${what}" >&2
return 1
fi
}
fetch db "${BACKUP_DIR}/clover-db-${STAMP}.db"
fetch dms-config "${BACKUP_DIR}/dms-config-${STAMP}.tar.gz"
# 校验 SQLite 快照可读(有 sqlite3 才校验)
if command -v sqlite3 >/dev/null 2>&1; then
sqlite3 "${BACKUP_DIR}/clover-db-${STAMP}.db" 'PRAGMA integrity_check;' | head -1
fi
# 轮转:删除超过保留天数的备份
find "${BACKUP_DIR}" -maxdepth 1 -type f \
\( -name 'clover-db-*.db' -o -name 'dms-config-*.tar.gz' \) \
-mtime "+${RETENTION_DAYS}" -print -delete 2>/dev/null || true
echo "[backup] $(date -Is) 完成(保留 ${RETENTION_DAYS} 天)"