File size: 8,641 Bytes
13ad5f7
51af98b
4ba79ba
51af98b
4ba79ba
51af98b
 
4ba79ba
51af98b
4ba79ba
51af98b
 
13ad5f7
4ba79ba
 
13ad5f7
4ba79ba
85c4208
 
 
 
 
4ba79ba
85c4208
 
 
4ba79ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b10884e
4ba79ba
 
 
 
 
51af98b
7d26d69
4ba79ba
85c4208
4ba79ba
 
 
85c4208
4ba79ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51af98b
4ba79ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51af98b
4ba79ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51af98b
4ba79ba
 
 
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
#!/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
}

# 创建 Hugging Face 同步脚本
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
    
    # 保留最新的max_files个文件
    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')"

# 尝试从 WebDAV 恢复最新文件
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 同步函数(仅上传变化文件)
webdav_sync() {
    SYNC_INTERVAL=${SYNC_INTERVAL:-7200}  # 默认 2 小时
    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"
                    
                    # 更新主文件(覆盖 webui.db)
                    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
            
            # 清理过期 WebDAV 文件(保留最近 7 天)
            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
}

# Hugging Face 同步函数
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 &