|
#!/bin/bash |
|
|
|
|
|
if [ -z "$WEBDAV_URL" ] || [ -z "$WEBDAV_USERNAME" ] || [ -z "$WEBDAV_PASSWORD" ]; then |
|
echo "缺少必要的环境变量: WEBDAV_URL、WEBDAV_USERNAME 或 WEBDAV_PASSWORD" |
|
exit 1 |
|
fi |
|
|
|
if [ -z "$HF_TOKEN" ] || [ -z "$DATASET_ID" ]; then |
|
echo "缺少必要的环境变量: HF_TOKEN 或 DATASET_ID" |
|
exit 1 |
|
fi |
|
|
|
|
|
mkdir -p ./data |
|
|
|
|
|
get_file_hash() { |
|
local file_path="$1" |
|
if [ -f "$file_path" ]; then |
|
md5sum "$file_path" | awk '{print $1}' |
|
else |
|
echo "文件不存在" |
|
fi |
|
} |
|
|
|
|
|
cat > /tmp/hf_sync.py << 'EOL' |
|
from huggingface_hub import HfApi |
|
import sys |
|
import os |
|
|
|
def manage_backups(api, repo_id, max_files=50): |
|
"""管理备份文件,保留最新的max_files个文件""" |
|
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset") |
|
backup_files = [f for f in files if f.startswith('webui_backup_') and f.endswith('.db')] |
|
|
|
|
|
backup_by_date = {} |
|
for file in backup_files: |
|
try: |
|
date_part = file.split('_')[2].split('.')[0] |
|
backup_by_date[date_part] = file |
|
except: |
|
continue |
|
|
|
|
|
sorted_dates = sorted(backup_by_date.keys(), reverse=True) |
|
if len(sorted_dates) > max_files: |
|
files_to_delete = [backup_by_date[date] for date in sorted_dates[max_files:]] |
|
for file in files_to_delete: |
|
api.delete_file(path_in_repo=file, repo_id=repo_id, repo_type="dataset") |
|
print(f"已删除旧备份: {file}") |
|
|
|
def upload_backup(file_path, file_name, token, repo_id): |
|
"""上传备份文件到Hugging Face""" |
|
api = HfApi(token=token) |
|
try: |
|
|
|
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset") |
|
if file_name in files: |
|
api.delete_file(path_in_repo=file_name, repo_id=repo_id, repo_type="dataset") |
|
print(f"已删除同名文件: {file_name}") |
|
|
|
|
|
api.upload_file( |
|
path_or_fileobj=file_path, |
|
path_in_repo=file_name, |
|
repo_id=repo_id, |
|
repo_type="dataset" |
|
) |
|
print(f"成功上传: {file_name}") |
|
manage_backups(api, repo_id) |
|
except Exception as e: |
|
print(f"上传失败: {str(e)}") |
|
|
|
def download_latest_backup(token, repo_id): |
|
"""从Hugging Face下载最新备份""" |
|
api = HfApi(token=token) |
|
try: |
|
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset") |
|
backup_files = [f for f in files if f.startswith('webui_backup_') and f.endswith('.db')] |
|
if not backup_files: |
|
return False |
|
|
|
|
|
latest_file = max(backup_files, key=lambda x: x.split('_')[2].split('.')[0]) |
|
file_path = api.hf_hub_download( |
|
repo_id=repo_id, |
|
filename=latest_file, |
|
repo_type="dataset" |
|
) |
|
|
|
if file_path and os.path.exists(file_path): |
|
os.makedirs('./data', exist_ok=True) |
|
os.system(f'cp "{file_path}" ./data/webui.db') |
|
print(f"成功从Hugging Face恢复: {latest_file}") |
|
return True |
|
else: |
|
return False |
|
except Exception as e: |
|
print(f"下载失败: {str(e)}") |
|
return False |
|
|
|
if __name__ == "__main__": |
|
action = sys.argv[1] |
|
token = sys.argv[2] |
|
repo_id = sys.argv[3] |
|
|
|
if action == "upload": |
|
file_path = sys.argv[4] |
|
file_name = sys.argv[5] |
|
upload_backup(file_path, file_name, token, repo_id) |
|
elif action == "download": |
|
download_latest_backup(token, repo_id) |
|
EOL |
|
|
|
|
|
echo "初始化数据恢复..." |
|
echo "WebDAV URL: $WEBDAV_URL" |
|
echo "WebDAV 用户名: $WEBDAV_USERNAME" |
|
echo "WebDAV 密码: $(echo $WEBDAV_PASSWORD | sed 's/./*/g')" |
|
|
|
|
|
echo "尝试从 WebDAV 获取文件列表..." |
|
webdav_files=$(curl -s -X PROPFIND --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" -H "Depth: 1" "$WEBDAV_URL/openwebui/" | grep '<d:href>' | grep 'webui_[0-9]\{8\}.db' | sed 's|</?d:href>||g') |
|
|
|
if [ -n "$webdav_files" ]; then |
|
latest_file=$(echo "$webdav_files" | sort -r | head -n 1) |
|
download_url="$WEBDAV_URL/openwebui/$latest_file" |
|
curl -L -o "./data/webui.db" --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$download_url" && { |
|
echo "成功从 WebDAV 下载最新数据库: $latest_file" |
|
} || { |
|
echo "WebDAV 下载失败,尝试从 Hugging Face 恢复..." |
|
python /tmp/hf_sync.py download "$HF_TOKEN" "$DATASET_ID" |
|
} |
|
else |
|
echo "WebDAV 无有效备份,尝试从 Hugging Face 恢复..." |
|
python /tmp/hf_sync.py download "$HF_TOKEN" "$DATASET_ID" || { |
|
echo "所有恢复失败,创建空数据库..." |
|
touch ./data/webui.db |
|
} |
|
fi |
|
|
|
|
|
webdav_sync() { |
|
SYNC_INTERVAL=${SYNC_INTERVAL:-7200} |
|
echo "WebDAV 同步启动,间隔: ${SYNC_INTERVAL} 秒" |
|
|
|
while true; do |
|
echo "开始 WebDAV 同步: $(date)" |
|
|
|
if [ -f "./data/webui.db" ]; then |
|
|
|
current_date=$(date +'%Y%m%d') |
|
file_name="webui_${current_date}.db" |
|
upload_url="$WEBDAV_URL/openwebui/${file_name}" |
|
|
|
|
|
local_hash=$(get_file_hash "./data/webui.db") |
|
|
|
|
|
remote_temp="/tmp/webui_remote.db" |
|
curl -s -o "$remote_temp" --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$upload_url" > /dev/null 2>&1 |
|
remote_hash=$(get_file_hash "$remote_temp") |
|
rm -f "$remote_temp" |
|
|
|
if [ "$local_hash" == "$remote_hash" ]; then |
|
echo "文件未变化,跳过 WebDAV 上传" |
|
else |
|
echo "检测到文件变化,开始上传到 WebDAV..." |
|
curl -L -T "./data/webui.db" --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$upload_url" && { |
|
echo "WebDAV 上传成功: $file_name" |
|
|
|
|
|
main_url="$WEBDAV_URL/openwebui/webui.db" |
|
curl -L -T "./data/webui.db" --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$main_url" && { |
|
echo "主文件更新成功" |
|
} || { |
|
echo "主文件更新失败" |
|
} |
|
} || { |
|
echo "WebDAV 上传失败,等待重试..." |
|
sleep 10 |
|
curl -L -T "./data/webui.db" --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$upload_url" || { |
|
echo "重试失败,放弃本次上传" |
|
} |
|
} |
|
fi |
|
|
|
|
|
cleanup_days=7 |
|
cutoff_date=$(date -d "-${cleanup_days} days" +%Y%m%d) |
|
for file in $webdav_files; do |
|
file_date=$(echo "$file" | grep -oE '[0-9]{8}') |
|
if [ "$file_date" -lt "$cutoff_date" ]; then |
|
delete_url="$WEBDAV_URL/openwebui/$file" |
|
curl -X DELETE --user "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" "$delete_url" && echo "删除过期文件: $file" |
|
fi |
|
done |
|
else |
|
echo "未找到 webui.db,跳过同步" |
|
fi |
|
|
|
sleep $SYNC_INTERVAL |
|
done |
|
} |
|
|
|
|
|
hf_sync() { |
|
SYNC_INTERVAL=${SYNC_INTERVAL:-7200} |
|
echo "Hugging Face 同步启动,间隔: ${SYNC_INTERVAL} 秒" |
|
|
|
while true; do |
|
echo "开始 Hugging Face 同步: $(date)" |
|
|
|
if [ -f "./data/webui.db" ]; then |
|
current_date=$(date +'%Y%m%d') |
|
backup_file="webui_backup_${current_date}.db" |
|
temp_path="/tmp/${backup_file}" |
|
cp "./data/webui.db" "$temp_path" |
|
|
|
echo "正在上传到 Hugging Face..." |
|
python /tmp/hf_sync.py upload "$HF_TOKEN" "$DATASET_ID" "$temp_path" "$backup_file" |
|
rm -f "$temp_path" |
|
else |
|
echo "未找到数据库文件,跳过 Hugging Face 同步" |
|
fi |
|
|
|
sleep $SYNC_INTERVAL |
|
done |
|
} |
|
|
|
|
|
webdav_sync & |
|
hf_sync & |