Spaces:
Sleeping
Sleeping
File size: 11,214 Bytes
d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 2725d1a de9ddf2 c63c907 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 c63c907 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 c63c907 de9ddf2 c63c907 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 2725d1a de9ddf2 d7dd7b9 c63c907 de9ddf2 2725d1a de9ddf2 2725d1a de9ddf2 2725d1a de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 2725d1a c63c907 de9ddf2 c63c907 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 c63c907 de9ddf2 c63c907 de9ddf2 c63c907 de9ddf2 c63c907 de9ddf2 c63c907 de9ddf2 c63c907 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 d7dd7b9 de9ddf2 c63c907 de9ddf2 2725d1a de9ddf2 c63c907 de9ddf2 c63c907 de9ddf2 |
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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
#!/bin/bash
# --- 配置检查 ---
# 检查 WebDAV 环境变量是否设置
if [[ -z "$WEBDAV_URL" ]] || [[ -z "$WEBDAV_USERNAME" ]] || [[ -z "$WEBDAV_PASSWORD" ]]; then
echo "错误:缺少必要的 WebDAV 环境变量 (WEBDAV_URL, WEBDAV_USERNAME, WEBDAV_PASSWORD)。"
echo "备份功能将不可用。"
# 根据你的需求,如果缺少环境变量就完全退出,或者只跳过备份相关功能
# 这里选择退出,因为脚本的主要目的是备份
exit 1
fi
# --- 路径和常量定义 ---
# 定义要备份的本地目录 (修改点 1:明确指定路径)
LOCAL_DATA_DIR="/home/user/data"
# 定义备份文件名前缀 (修改点 2:更改前缀)
BACKUP_PREFIX="data_backup_"
# 定义 WebDAV 上的可选子目录路径
WEBDAV_BACKUP_PATH=${WEBDAV_BACKUP_PATH:-""} # 默认为空,即 WebDAV 根目录
# 构造完整的 WebDAV URL
FULL_WEBDAV_URL="${WEBDAV_URL}"
if [ -n "$WEBDAV_BACKUP_PATH" ]; then
# 确保 URL 拼接时斜杠正确
if [[ "${WEBDAV_URL}" != */ ]]; then
WEBDAV_URL="${WEBDAV_URL}/"
fi
if [[ "${WEBDAV_BACKUP_PATH}" == /* ]]; then
WEBDAV_BACKUP_PATH="${WEBDAV_BACKUP_PATH:1}"
fi
FULL_WEBDAV_URL="${WEBDAV_URL}${WEBDAV_BACKUP_PATH}"
fi
# 定义 Python 虚拟环境路径 (如果需要,请修改)
VENV_PATH="$HOME/venv/bin/activate"
# 定义同步间隔(秒),默认为 600 秒 (10 分钟)
SYNC_INTERVAL=${SYNC_INTERVAL:-600}
# 定义保留的备份数量
KEEP_BACKUPS=5
# --- 虚拟环境激活 ---
# 检查并激活 Python 虚拟环境 (如果 Python 脚本需要)
if [ -f "$VENV_PATH" ]; then
echo "激活 Python 虚拟环境: $VENV_PATH"
source "$VENV_PATH"
else
echo "警告:未找到 Python 虚拟环境 $VENV_PATH。如果 Python 脚本需要特定库,可能会失败。"
fi
# --- 函数定义 ---
# 函数:从 WebDAV 恢复最新备份
restore_backup() {
echo "开始从 WebDAV 下载最新备份..."
python3 -c "
import sys
import os
import tarfile
import requests
import shutil
from webdav3.client import Client
options = {
'webdav_hostname': '$FULL_WEBDAV_URL',
'webdav_login': '$WEBDAV_USERNAME',
'webdav_password': '$WEBDAV_PASSWORD'
}
local_data_dir = '$LOCAL_DATA_DIR' # 使用变量
backup_prefix = '$BACKUP_PREFIX' # 使用变量
try:
client = Client(options)
# 过滤文件,使用正确的前缀 (修改点 2)
backups = [f for f in client.list() if f.endswith('.tar.gz') and f.startswith(backup_prefix)]
if not backups:
print(f'在 {options['webdav_hostname']} 没有找到前缀为 {backup_prefix} 的备份文件。')
# 如果没有备份,确保目标目录存在但为空可能是期望行为,或者直接退出
os.makedirs(local_data_dir, exist_ok=True)
print(f'确保目录 {local_data_dir} 存在。')
sys.exit(0) # 正常退出,因为没有备份可恢复
latest_backup = sorted(backups)[-1]
print(f'找到最新备份文件:{latest_backup}')
remote_file_url = f'{options['webdav_hostname']}/{latest_backup}' # 假设 URL 构造正确
local_temp_file = f'/tmp/{latest_backup}'
print(f'正在从 {remote_file_url} 下载...')
with requests.get(remote_file_url, auth=(options['webdav_login'], options['webdav_password']), stream=True) as r:
r.raise_for_status() # 检查 HTTP 错误
with open(local_temp_file, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f'成功下载备份文件到 {local_temp_file}')
if os.path.exists(local_temp_file):
print(f'准备恢复到目录: {local_data_dir}')
# 如果目录已存在,先清空它 (或者删除重建,取决于需求)
if os.path.exists(local_data_dir):
print(f'目录 {local_data_dir} 已存在,正在清空...')
# shutil.rmtree(local_data_dir) # 删除整个目录树
# 或者清空目录内容
for item in os.listdir(local_data_dir):
item_path = os.path.join(local_data_dir, item)
if os.path.isfile(item_path) or os.path.islink(item_path):
os.unlink(item_path)
elif os.path.isdir(item_path):
shutil.rmtree(item_path)
else:
os.makedirs(local_data_dir, exist_ok=True)
print(f'正在解压 {local_temp_file} 到 {local_data_dir}...')
# 解压备份文件 (修改点 1: 确保解压到正确的目录)
with tarfile.open(local_temp_file, 'r:gz') as tar:
tar.extractall(path=local_data_dir)
print(f'成功从 {latest_backup} 恢复备份到 {local_data_dir}')
os.remove(local_temp_file) # 清理临时文件
else:
print(f'错误:下载的备份文件 {local_temp_file} 未找到。')
except requests.exceptions.RequestException as e:
print(f'下载备份时发生网络错误: {e}')
sys.exit(1)
except Exception as e:
print(f'恢复备份时发生错误: {e}')
sys.exit(1)
"
# 检查 Python 脚本的退出码
if [ $? -ne 0 ]; then
echo "恢复备份过程中发生错误,脚本终止。"
exit 1
fi
}
# 函数:同步数据到 WebDAV
sync_data() {
while true; do
echo "-----------------------------------------------------"
echo "开始同步过程于: $(date)"
# 检查本地数据目录是否存在
if [ ! -d "$LOCAL_DATA_DIR" ]; then
echo "警告:本地数据目录 $LOCAL_DATA_DIR 不存在。跳过此次备份。"
sleep $SYNC_INTERVAL
continue # 继续下一次循环
fi
# 检查本地数据目录是否为空
if [ -z "$(ls -A $LOCAL_DATA_DIR)" ]; then
echo "信息:本地数据目录 $LOCAL_DATA_DIR 为空。跳过此次备份。"
sleep $SYNC_INTERVAL
continue # 继续下一次循环
fi
timestamp=$(date +%Y%m%d_%H%M%S)
# 使用新的前缀生成备份文件名 (修改点 2)
backup_file="${BACKUP_PREFIX}${timestamp}.tar.gz"
local_temp_backup_path="/tmp/${backup_file}"
remote_backup_path="${FULL_WEBDAV_URL}/${backup_file}" # 确保 URL 没有双斜杠
echo "正在压缩目录: $LOCAL_DATA_DIR 到 $local_temp_backup_path"
# 使用 tar 压缩指定目录的内容 (修改点 1: 使用正确的源目录)
# -C "$LOCAL_DATA_DIR" 表示先切换到该目录,然后压缩 '.' (当前目录的所有内容)
# --warning=no-file-changed 忽略文件在压缩过程中改变的警告
tar --warning=no-file-changed -czf "$local_temp_backup_path" -C "$LOCAL_DATA_DIR" .
if [ $? -ne 0 ]; then
echo "错误:压缩目录 $LOCAL_DATA_DIR 失败。"
rm -f "$local_temp_backup_path" # 清理可能存在的坏文件
sleep $SYNC_INTERVAL
continue # 继续下一次循环
fi
# 检查压缩文件大小
if [ ! -s "$local_temp_backup_path" ]; then
echo "错误:压缩后的文件 $local_temp_backup_path 大小为 0 或不存在。跳过上传。"
rm -f "$local_temp_backup_path"
sleep $SYNC_INTERVAL
continue
fi
echo "正在上传备份文件: $local_temp_backup_path 到 $remote_backup_path"
# 使用 curl 上传文件到 WebDAV
curl --fail -u "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" -T "$local_temp_backup_path" "$remote_backup_path"
if [ $? -eq 0 ]; then
echo "成功上传 ${backup_file} 到 WebDAV"
# 上传成功后,清理本地临时文件
echo "清理本地临时文件: $local_temp_backup_path"
rm -f "$local_temp_backup_path"
# 清理 WebDAV 上的旧备份文件
echo "开始清理 WebDAV 上的旧备份..."
python3 -c "
import sys
from webdav3.client import Client
options = {
'webdav_hostname': '$FULL_WEBDAV_URL',
'webdav_login': '$WEBDAV_USERNAME',
'webdav_password': '$WEBDAV_PASSWORD'
}
backup_prefix = '$BACKUP_PREFIX' # 使用变量
keep_backups = $KEEP_BACKUPS # 使用变量
try:
client = Client(options)
# 过滤文件,使用正确的前缀 (修改点 2)
backups = [f for f in client.list() if f.endswith('.tar.gz') and f.startswith(backup_prefix)]
backups.sort() # 按名称排序(时间戳)
if len(backups) > keep_backups:
to_delete_count = len(backups) - keep_backups
print(f'找到 {len(backups)} 个备份,超过了保留数量 {keep_backups},将删除最早的 {to_delete_count} 个。')
for file_to_delete in backups[:to_delete_count]:
try:
# 确保路径正确,对于根目录下的文件,不需要加斜杠
delete_path = file_to_delete
print(f'正在删除旧备份: {delete_path}')
client.clean(delete_path) # clean 方法通常用于删除文件或目录
print(f'成功删除 {delete_path}')
except Exception as delete_err:
print(f'删除文件 {delete_path} 时出错: {delete_err}')
else:
print(f'找到 {len(backups)} 个备份,未达到保留上限 {keep_backups},无需清理。')
except Exception as e:
print(f'清理旧备份时发生错误: {e}')
"
if [ $? -ne 0 ]; then
echo "警告:清理 WebDAV 旧备份时发生错误。"
fi
else
echo "错误:上传 ${backup_file} 到 WebDAV 失败。返回码: $?"
# 上传失败,也清理本地临时文件
echo "清理本地临时文件: $local_temp_backup_path"
rm -f "$local_temp_backup_path"
fi
echo "下次同步将在 ${SYNC_INTERVAL} 秒后进行..."
sleep $SYNC_INTERVAL
done
}
# --- 主逻辑 ---
# 1. 首次启动时尝试恢复最新备份 (可选,根据你的需求决定是否需要)
echo "检查并恢复最新备份(如果存在)..."
restore_backup
# 2. 等待一段时间后启动应用程序 (如果需要)
# 如果你不需要这个脚本来启动其他应用,可以注释掉下面两行
# echo "等待 30 秒后启动应用程序..."
# sleep 30
# echo "启动应用程序..."
# ./app server & # 你的应用程序启动命令
# 3. 启动后台同步进程
echo "启动后台数据同步进程..."
sync_data &
# 让主脚本保持运行(如果需要,例如在 Docker 容器中)
# 如果上面的 `./app server &` 是前台运行的,或者你用其他方式保持脚本运行,就不需要这个
# 如果 `sync_data` 是唯一需要长时间运行的,它已经在后台了,主脚本可以退出
# 但如果这是容器的主进程,你可能需要一个前台等待
# 例如: wait $! # 等待最后一个后台进程 (sync_data)
# 或者,如果启动了 app server: wait $(pgrep -f app server) # 等待 app server 进程
# 或者简单地无限循环:
# tail -f /dev/null # 保持脚本在前台运行
echo "脚本初始化完成,同步进程已在后台运行。"
# 根据你的运行环境决定脚本最后如何结束或保持运行 |