File size: 3,872 Bytes
3894c0f 0318d4c d558b55 3894c0f 0318d4c 3894c0f 0318d4c 42dc41b 884ad4a 0318d4c d558b55 0318d4c 42dc41b 3894c0f 0318d4c 3894c0f 42dc41b 3894c0f 0318d4c 42dc41b d558b55 0318d4c d558b55 0318d4c d558b55 0318d4c 42dc41b d558b55 0318d4c d558b55 0318d4c 3894c0f 0318d4c d558b55 8c2c76a 0318d4c 3894c0f 0318d4c d558b55 0318d4c d558b55 0318d4c d558b55 0318d4c 9b31860 0318d4c 3894c0f 0318d4c 42dc41b 0318d4c 3894c0f 0318d4c d558b55 0318d4c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
#!/bin/bash
# --- 1. 参数与环境检查 ---
LOG_PREFIX="[Koishi-Cloud-Sync]"
if [ -z "$WEBDAV_URL" ] || [ -z "$WEBDAV_USERNAME" ] || [ -z "$WEBDAV_PASSWORD" ]; then
echo "$LOG_PREFIX [WARN] WebDAV 未配置,直接启动 Koishi。"
exec yarn start
fi
# 确保路径不以斜杠结尾
CLEAN_URL=$(echo "${WEBDAV_URL}" | sed 's:/*$::')
WEBDAV_BACKUP_PATH=${WEBDAV_BACKUP_PATH:-""}
FULL_WEBDAV_URL="${CLEAN_URL}${WEBDAV_BACKUP_PATH:+/$WEBDAV_BACKUP_PATH}"
# 统一前缀名 (确保恢复和备份用的是同一个)
FILE_PREFIX="koishi_backup_"
# --- 2. 启动前恢复备份 ---
restore_backup() {
echo "$LOG_PREFIX [Restore] 正在从 WebDAV 恢复数据..."
python3 <<EOF
import sys, os, tarfile, requests
from webdav3.client import Client
options = {
"webdav_hostname": "${FULL_WEBDAV_URL}",
"webdav_login": "${WEBDAV_USERNAME}",
"webdav_password": "${WEBDAV_PASSWORD}",
}
client = Client(options)
try:
# 这里的 list() 会受 hostname 里的子路径影响,处理文件名过滤
files = client.list()
backups = sorted([f for f in files if f.startswith("${FILE_PREFIX}") and f.endswith(".tar.gz")])
if not backups:
print("$LOG_PREFIX [Restore] WebDAV 上没有发现备份文件,跳过恢复。")
sys.exit(0)
latest = backups[-1]
print(f"$LOG_PREFIX [Restore] 发现最新备份: {latest}")
# 构造下载链接
download_url = f"${FULL_WEBDAV_URL}/{latest}"
r = requests.get(download_url, auth=("${WEBDAV_USERNAME}", "${WEBDAV_PASSWORD}"), stream=True)
if r.status_code == 200:
tmp_path = f"/tmp/{latest}"
with open(tmp_path, "wb") as f:
for chunk in r.iter_content(8192):
f.write(chunk)
with tarfile.open(tmp_path, "r:gz") as tar:
tar.extractall("/app")
print("$LOG_PREFIX [Restore] 数据恢复成功!")
else:
print(f"$LOG_PREFIX [Restore] 下载失败,状态码: {r.status_code}")
except Exception as e:
print(f"$LOG_PREFIX [Restore] 发生错误: {e}")
EOF
}
# --- 3. 周期性备份任务 ---
sync_loop() {
# 给系统留出启动时间
sleep 60
while true; do
ts=$(date +%Y%m%d_%H%M%S)
file_name="${FILE_PREFIX}${ts}.tar.gz"
local_tmp="/tmp/${file_name}"
echo "$LOG_PREFIX [Sync] 正在打包数据 (包含 node_modules)..."
# 使用 /app 目录进行打包
tar -czf "$local_tmp" -C /app .
echo "$LOG_PREFIX [Sync] 打包完成 ($(du -h $local_tmp | cut -f1)),准备上传..."
python3 <<EOF
from webdav3.client import Client
import os
options = {
"webdav_hostname": "${FULL_WEBDAV_URL}",
"webdav_login": "${WEBDAV_USERNAME}",
"webdav_password": "${WEBDAV_PASSWORD}",
}
client = Client(options)
try:
# webdav3 的 upload_file 第一个参数是远程文件名,第二个是本地路径
# 注意:如果 options 已经包含完整路径,此处只需传文件名
client.upload_file("$file_name", "$local_tmp")
print(f"$LOG_PREFIX [Sync] 上传成功: $file_name")
# 清理旧备份
files = sorted([f for f in client.list() if f.startswith("${FILE_PREFIX}") and f.endswith(".tar.gz")])
if len(files) > 5:
for old_file in files[:-5]:
client.clean(old_file)
print(f"$LOG_PREFIX [Clean] 已删除旧备份: {old_file}")
except Exception as e:
print(f"$LOG_PREFIX [Error] 上传过程中出错: {e}")
EOF
rm -f "$local_tmp"
# 默认 2 小时同步一次
echo "$LOG_PREFIX [Sync] 本次任务结束,等待下一次循环..."
sleep ${SYNC_INTERVAL:-7200}
done
}
# --- 执行流 ---
restore_backup
# 启动备份进程 (后台)
sync_loop &
# 启动 Koishi (前台)
echo "$LOG_PREFIX 正在启动 Koishi 服务..."
exec yarn start |