| #!/bin/bash |
|
|
| |
| 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_" |
|
|
| |
| 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 |
| } |
|
|
| |
| 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)..." |
| |
| 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" |
| |
| |
| echo "$LOG_PREFIX [Sync] 本次任务结束,等待下一次循环..." |
| sleep ${SYNC_INTERVAL:-7200} |
| done |
| } |
|
|
| |
| restore_backup |
|
|
| |
| sync_loop & |
|
|
| |
| echo "$LOG_PREFIX 正在启动 Koishi 服务..." |
| exec yarn start |