issaocean commited on
Commit
4cd00cf
·
verified ·
1 Parent(s): 54c879d

Update sync_data.sh

Browse files
Files changed (1) hide show
  1. sync_data.sh +97 -255
sync_data.sh CHANGED
@@ -1,272 +1,114 @@
1
- #!/bin/bash
2
 
3
- # 建议添加,使脚本在命令失败时退出,并确保管道错误能被捕获
4
- # set -e
5
- # set -o pipefail
6
 
7
- # 检查环境变量
8
- if [[ -z "$WEBDAV_URL" ]] || [[ -z "$WEBDAV_USERNAME" ]] || [[ -z "$WEBDAV_PASSWORD" ]]; then
9
- echo "缺少 WEBDAV_URL、WEBDAV_USERNAMEWEBDAV_PASSWORD,启动时将不包含备份功能"
10
- exit 0
11
  fi
12
 
13
- # 设置备份路径
14
- WEBDAV_BACKUP_PATH=${WEBDAV_BACKUP_PATH:-""}
15
- FULL_WEBDAV_URL="${WEBDAV_URL}"
16
- if [ -n "$WEBDAV_BACKUP_PATH" ]; then
17
- # 确保URL拼接正确,避免双斜杠或无斜杠的问题
18
- # 移除WEBDAV_URL可能存在的尾部斜杠
19
- WEBDAV_URL_CLEAN=$(echo "$WEBDAV_URL" | sed 's:/*$::')
20
- # 移除WEBDAV_BACKUP_PATH可能存在的首部斜杠
21
- WEBDAV_BACKUP_PATH_CLEAN=$(echo "$WEBDAV_BACKUP_PATH" | sed 's:^/*::')
22
- FULL_WEBDAV_URL="${WEBDAV_URL_CLEAN}/${WEBDAV_BACKUP_PATH_CLEAN}"
23
- else
24
- FULL_WEBDAV_URL=$(echo "$FULL_WEBDAV_URL" | sed 's:/*$::') # 清理末尾斜杠
25
- fi
26
-
27
-
28
- # 下载最新备份并恢复
29
- restore_backup() {
30
- echo "开始从 WebDAV 下载最新备份..."
31
- # 将环境变量导出,以便Python脚本能通过os.environ获取,而不是直接注入字符串,稍微安全些
32
- export FULL_WEBDAV_URL_FOR_PYTHON="$FULL_WEBDAV_URL"
33
- export WEBDAV_USERNAME_FOR_PYTHON="$WEBDAV_USERNAME"
34
- export WEBDAV_PASSWORD_FOR_PYTHON="$WEBDAV_PASSWORD"
35
-
36
- python3 -c "
37
- import sys
38
- import os
39
- import tarfile
40
- import requests
41
- from webdav3.client import Client
42
- import shutil
43
-
44
- full_url = os.environ.get('FULL_WEBDAV_URL_FOR_PYTHON')
45
- username = os.environ.get('WEBDAV_USERNAME_FOR_PYTHON')
46
- password = os.environ.get('WEBDAV_PASSWORD_FOR_PYTHON')
47
-
48
- if not all([full_url, username, password]):
49
- print('Python: 缺失必要的环境变量')
50
- sys.exit(1)
51
-
52
- options = {
53
- 'webdav_hostname': full_url,
54
- 'webdav_login': username,
55
- 'webdav_password': password
56
  }
57
- client = Client(options)
58
- try:
59
- backups = [file for file in client.list() if file.endswith('.tar.gz') and file.startswith('webui_backup_')]
60
- except Exception as e:
61
- print(f'Python: 列出WebDAV文件失败: {e}')
62
- sys.exit(1)
63
-
64
- if not backups:
65
- print('Python: 没有找到备份文件')
66
- sys.exit(0) # 没有备份不是致命错误,可能是首次运行
67
-
68
- latest_backup = sorted(backups)[-1]
69
- print(f'Python: 最新备份文件:{latest_backup}')
70
-
71
- download_url = f'{full_url}/{latest_backup}'
72
- local_tmp_backup_file = f'/tmp/{latest_backup}'
73
-
74
- try:
75
- with requests.get(download_url, auth=(username, password), stream=True, timeout=60) as r:
76
- r.raise_for_status() # 会对4xx/5xx状态码抛出HTTPError
77
- with open(local_tmp_backup_file, 'wb') as f:
78
- for chunk in r.iter_content(chunk_size=8192):
79
- f.write(chunk)
80
- print(f'Python: 成功下载备份文件到 {local_tmp_backup_file}')
81
- except requests.exceptions.RequestException as e:
82
- print(f'Python: 下载备份失败: {e}')
83
- sys.exit(1)
84
-
85
- if os.path.exists(local_tmp_backup_file):
86
- temp_dir = '/tmp/restore_webui_db'
87
- if os.path.exists(temp_dir): # 先清理旧的临时解压目录
88
- shutil.rmtree(temp_dir)
89
- os.makedirs(temp_dir, exist_ok=True)
90
-
91
- try:
92
- with tarfile.open(local_tmp_backup_file, 'r:gz') as tar:
93
- tar.extractall(temp_dir)
94
- print(f'Python: 文件已解压到 {temp_dir}')
95
- except tarfile.TarError as e:
96
- print(f'Python: 解压备份文件失败: {e}')
97
- shutil.rmtree(temp_dir, ignore_errors=True)
98
- os.remove(local_tmp_backup_file)
99
- sys.exit(1)
100
-
101
- db_found = False
102
- for root, dirs, files in os.walk(temp_dir):
103
- if 'webui.db' in files:
104
- db_path = os.path.join(root, 'webui.db')
105
- target_data_dir = './data' # 脚本工作目录下的data文件夹
106
- os.makedirs(target_data_dir, exist_ok=True)
107
- target_db_path = os.path.join(target_data_dir, 'webui.db')
108
- os.replace(db_path, target_db_path) # 原子替换
109
- print(f'Python: 成功从 {latest_backup} 恢复备份到 {target_db_path}')
110
- db_found = True
111
- break
112
-
113
- if not db_found:
114
- print('Python: 备份文件中未找到 webui.db')
115
-
116
- try:
117
- shutil.rmtree(temp_dir)
118
- except Exception as e:
119
- print(f'Python: 删除临时目录 {temp_dir} 时出错:{e}')
120
- os.remove(local_tmp_backup_file)
121
- else:
122
- print('Python: 下载的备份文件不存在 (这不应该发生)') # 逻辑上不应到此
123
- "
124
- # 检查Python脚本的退出状态
125
- if [ $? -ne 0 ]; then
126
- echo "Bash: restore_backup中的Python脚本执行失败。"
127
- # 可以选择在这里exit 1,如果恢复失败是关键的
128
- fi
129
- # 清理导出的环境变量
130
- unset FULL_WEBDAV_URL_FOR_PYTHON WEBDAV_USERNAME_FOR_PYTHON WEBDAV_PASSWORD_FOR_PYTHON
131
- }
132
-
133
- # 首次启动时下载最新备份
134
- echo "正在检查是否需要从 WebDAV 恢复备份..."
135
- # 你可以添加一个逻辑,比如检查 ./data/webui.db 是否已存在,如果存在则不恢复
136
- # if [ ! -f "./data/webui.db" ]; then
137
- restore_backup
138
- # else
139
- # echo "./data/webui.db 已存在,跳过首次恢复。"
140
- # fi
141
 
 
 
 
 
 
 
142
 
143
- # 同步函数
144
  sync_data() {
145
- local db_file="./data/webui.db"
146
- local checksum_file="./data/.webui.db.checksum" # 存储在数据目录中,随数据一起考虑
147
-
148
- while true; do
149
- echo "-----------------------------------------------------"
150
- echo "在 $(date '+%Y-%m-%d %H:%M:%S') 开始同步进程"
151
-
152
- if [ -f "$db_file" ]; then
153
- current_checksum=$(sha256sum "$db_file" | awk '{print $1}')
154
- previous_checksum=""
155
- if [ -f "$checksum_file" ]; then
156
- previous_checksum=$(cat "$checksum_file")
157
- fi
158
-
159
- echo "当前 webui.db 校验和: $current_checksum"
160
- echo "上次 webui.db 校验和: $previous_checksum" # 如果checksum文件不存在,这里会是空
161
-
162
- if [ "$current_checksum" != "$previous_checksum" ]; then
163
- echo "webui.db 文件已更改或首次备份,执行备份上传操作。"
164
- timestamp=$(date +%Y%m%d_%H%M%S)
165
- backup_file="webui_backup_${timestamp}.tar.gz"
166
- local_tmp_tar_file="/tmp/${backup_file}"
167
-
168
- # 打包数据库文件,-C 改变目录,使得压缩包内不含data/前缀
169
- echo "正在打包 ${db_file} 为 ${local_tmp_tar_file}..."
170
- tar -czf "${local_tmp_tar_file}" -C "$(dirname "$db_file")" "$(basename "$db_file")"
171
 
172
- if [ $? -eq 0 ]; then
173
- echo "打包成功。"
174
- # 上传新备份到WebDAV
175
- upload_url="${FULL_WEBDAV_URL}/${backup_file}"
176
- echo "正在上传 ${local_tmp_tar_file} 至 ${upload_url} ..."
177
- curl --fail -u "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" -T "${local_tmp_tar_file}" "${upload_url}"
178
-
179
- if [ $? -eq 0 ]; then
180
- echo "成功将 ${backup_file} 上传至 WebDAV"
181
- echo "$current_checksum" > "$checksum_file" # 只有成功上传后才更新校验和
182
- echo "已更新校验和文件: $checksum_file"
183
-
184
- # 清理旧备份文件 (仅在成功上传新备份后执行)
185
- echo "开始清理 WebDAV 上的旧备份..."
186
- export FULL_WEBDAV_URL_FOR_PYTHON="$FULL_WEBDAV_URL" # 导出给Python
187
- export WEBDAV_USERNAME_FOR_PYTHON="$WEBDAV_USERNAME"
188
- export WEBDAV_PASSWORD_FOR_PYTHON="$WEBDAV_PASSWORD"
189
- python3 -c "
190
- import sys
191
- import os
192
- from webdav3.client import Client
193
-
194
- full_url = os.environ.get('FULL_WEBDAV_URL_FOR_PYTHON')
195
- username = os.environ.get('WEBDAV_USERNAME_FOR_PYTHON')
196
- password = os.environ.get('WEBDAV_PASSWORD_FOR_PYTHON')
197
- keep_backups = int(os.environ.get('WEBDAV_KEEP_BACKUPS', 5)) # 可配置保留数量
198
-
199
- if not all([full_url, username, password]):
200
- print('Python: 清理脚本缺少必要的环境变量')
201
- sys.exit(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
- options = {
204
- 'webdav_hostname': full_url,
205
- 'webdav_login': username,
206
- 'webdav_password': password
207
- }
208
- client = Client(options)
209
- try:
210
- backups = [file for file in client.list() if file.endswith('.tar.gz') and file.startswith('webui_backup_')]
211
- except Exception as e:
212
- print(f'Python: 列出WebDAV文件失败 (清理时): {e}')
213
- sys.exit(1) # 失败则不继续
214
 
215
- backups.sort() # 按名称排序,旧的在前
216
- if len(backups) > keep_backups:
217
- to_delete_count = len(backups) - keep_backups
218
- print(f'Python: 找到 {len(backups)} 个备份, 将删除最旧的 {to_delete_count} 个以保留 {keep_backups} 个。')
219
- for i in range(to_delete_count):
220
- file_to_delete = backups[i]
221
- try:
222
- client.clean(file_to_delete) # webdav3 的 clean 方法用于删除
223
- print(f'Python: 成功删除旧备份 {file_to_delete}')
224
- except Exception as e:
225
- print(f'Python: 删除旧备份 {file_to_delete} 失败: {e}')
226
- else:
227
- print(f'Python: 找到 {len(backups)} 个备份,少于或等于配置的 {keep_backups} 个,无需清理。')
228
- " 2>&1 # 将Python的stderr重定向到stdout
229
- if [ $? -ne 0 ]; then
230
- echo "Bash: 清理旧备份的Python脚本执行失败。"
231
- fi
232
- unset FULL_WEBDAV_URL_FOR_PYTHON WEBDAV_USERNAME_FOR_PYTHON WEBDAV_PASSWORD_FOR_PYTHON # 清理环境变量
233
- else
234
- echo "上传 ${backup_file} 至 WebDAV 失败。校验和文件未更新。"
235
- fi
236
  else
237
- echo "打包 ${db_file} 失败。"
 
 
 
238
  fi
239
- rm -f "${local_tmp_tar_file}" # 清理本地临时打包文件
240
- else
241
- echo "webui.db 文件未更改,跳过备份上传。"
242
- fi
243
- else
244
- echo "数据库文件 ${db_file} 尚不存在,等待下次同步..."
245
- # 如果数据库文件不存在,考虑是否要删除checksum文件,以便下次文件出现时强制备份
246
- # if [ -f "$checksum_file" ]; then
247
- # echo "删除旧的校验和文件 $checksum_file 因为数据库文件不存在。"
248
- # rm -f "$checksum_file"
249
- # fi
250
- fi
251
-
252
- SYNC_INTERVAL=${SYNC_INTERVAL:-600} # 默认10分钟
253
- WEBDAV_KEEP_BACKUPS=${WEBDAV_KEEP_BACKUPS:-5} # 默认保留5个备份
254
- export WEBDAV_KEEP_BACKUPS # 导出给Python清理脚本用
255
 
256
- echo "下次同步将在 ${SYNC_INTERVAL} 秒后进行..."
257
- sleep $SYNC_INTERVAL
258
- done
259
  }
260
 
261
  # 后台启动同步进程
262
- sync_data &
263
-
264
- # 保持脚本运行,如果这是Docker等环境的入口点且没有其他前台进程
265
- # 例如: wait $! 或 tail -f /dev/null
266
- # 如果 `sync_data &` 是脚本的最后一行,并且脚本需要持续运行,
267
- # 那么sync_data的父shell(即此脚本)会退出,但sync_data子进程会继续。
268
- # 在某些情况下(如简单的docker entrypoint),这可能是期望的行为。
269
- # 如果你需要这个主脚本保持存活,而不是仅仅启动一个后台进程然后退出,
270
- # 你可能需要一个前台阻塞命令,或者将 sync_data 直接运行而不加 `&` (但这会阻塞恢复之后的任何操作)。
271
- # 对于备份脚本,后台运行通常是合适的。
272
- echo "同步进程已在后台启动。PID: $!"
 
 
1
 
 
 
 
2
 
3
+ # 检查必要的环境变量
4
+ if [ -z "$G_NAME" ] || [ -z "$G_TOKEN" ]; then
5
+ echo "缺少必要的环境变量 G_NAMEG_TOKEN"
6
+ exit 1
7
  fi
8
 
9
+ # 解析仓库名和用户名
10
+ IFS='/' read -r GITHUB_USER GITHUB_REPO <<< "$G_NAME"
11
+
12
+ # 构建 GitHub 仓库的克隆 URL,包含令牌
13
+ REPO_URL="https://${G_TOKEN}@github.com/${G_NAME}.git"
14
+ mkdir -p ./data/github_data
15
+ # 克隆仓库
16
+ echo "正在克隆仓库……"
17
+ git clone "$REPO_URL" ./data/github_data || {
18
+ echo "克隆失败,请检查 G_NAME 和 G_TOKEN 是否正确。"
19
+ exit 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ if [ -f ./data/github_data/webui.db ]; then
23
+ cp ./data/github_data/webui.db ./data/webui.db
24
+ echo "从 GitHub 仓库中拉取成功"
25
+ else
26
+ echo "GitHub 仓库中未找到 webui.db,将在同步时推送"
27
+ fi
28
 
29
+ # 定义同步函数
30
  sync_data() {
31
+ while true; do
32
+ # 1. 同步到 GitHub
33
+ echo "正在开始同步"
34
+ # 进入仓库目录
35
+ cd ./data/github_data
36
+ # 配置 Git 用户信息
37
+ git config user.name "AutoSync Bot"
38
+ git config user.email "autosync@bot.com"
39
+
40
+ # 确保在正确的分支
41
+ git checkout main || git checkout master
42
+
43
+ # 复制最新的数据库文件
44
+ # cp ../webui.db ./webui.db
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ if [ -f "../webui.db" ]; then
47
+ cp ../webui.db ./webui.db
48
+ else
49
+ echo "数据库尚未初始化"
50
+ fi
51
+
52
+ # 检查是否有变化
53
+ if [[ -n $(git status -s) ]]; then
54
+ # 添加所有变更
55
+ git add webui.db
56
+
57
+ # 提交变更
58
+ git commit -m "Auto sync webui.db $(date '+%Y-%m-%d %H:%M:%S')"
59
+
60
+ # 推送到远程仓库
61
+ git push origin HEAD && {
62
+ echo "GitHub推送成功"
63
+ }|| {
64
+ echo "推送失败,等待重试..."
65
+ sleep 10
66
+ git push origin HEAD || {
67
+ echo "重试失败,放弃推送到Github。"
68
+ }
69
+ }
70
+ # 返回上级目录
71
+ cd ..
72
+ cd ..
73
+
74
+ # 2. 同步到 WebDAV
75
+ if [ -z "$WEBDAV_URL" ] || [ -z "$WEBDAV_USERNAME" ] || [ -z "$WEBDAV_PASSWORD" ]; then
76
+ echo "WebDAV 环境变量缺失,跳过 WebDAV 同步。"
77
+ else
78
+ echo "同步到 WebDAV..."
79
+ FILENAME="webui_$(date +'%m_%d').db"
80
+ # 检查是否存在要上传的文件
81
+ if [ -f ./data/webui.db ]; then
82
+ # 使用 curl 进行文件上传
83
+ curl -T ./data/webui.db --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$WEBDAV_URL/$FILENAME" && {
84
+ echo "WebDAV 上传成功"
85
+ } || {
86
+ echo "WebDAV 上传失败,等待重试..."
87
+ sleep 10
88
+ curl -T ./data/webui.db --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$WEBDAV_URL/$FILENAME" || {
89
+ echo "重试失败,放弃webdav上传。"
90
+ }
91
+ }
92
+ else
93
+ echo "未找到 webui.db 文件,跳过 WebDAV 同步"
94
+ fi
95
+ fi
96
 
 
 
 
 
 
 
 
 
 
 
 
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  else
99
+ # 返回上级目录
100
+ cd ..
101
+ cd ..
102
+ echo "GitHub: 没有检测到数据库变化"
103
  fi
104
+ # 3. 等待统一的时间间隔
105
+ SYNC_INTERVAL=${SYNC_INTERVAL:-7200} # 默认间隔时间为 7200 秒
106
+ echo "当前时间 $(date '+%Y-%m-%d %H:%M:%S')"
107
+ echo "等待 ${SYNC_INTERVAL} 秒后进行下一次同步..."
108
+ sleep $SYNC_INTERVAL
 
 
 
 
 
 
 
 
 
 
 
109
 
110
+ done
 
 
111
  }
112
 
113
  # 后台启动同步进程
114
+ sync_data &