aappeekk commited on
Commit
0318d4c
·
verified ·
1 Parent(s): 42dc41b

Update sync_data.sh

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