File size: 4,785 Bytes
8bf79eb
 
 
 
 
162d054
 
 
 
8bf79eb
 
 
 
 
 
 
 
 
 
 
 
162d054
 
8bf79eb
 
162d054
113c740
162d054
 
 
 
 
 
 
 
8bf79eb
162d054
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8bf79eb
162d054
 
 
 
8bf79eb
162d054
 
 
 
8bf79eb
 
162d054
 
 
8bf79eb
 
 
 
 
 
162d054
8bf79eb
 
 
162d054
8bf79eb
162d054
 
 
 
 
8bf79eb
162d054
 
 
 
 
 
 
 
 
8bf79eb
 
162d054
 
 
 
 
 
 
 
 
 
8bf79eb
 
 
cb13021
 
162d054
8bf79eb
 
 
162d054
 
8bf79eb
 
 
 
 
 
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
#!/usr/bin/env python3
import os
import sys
import tarfile
from huggingface_hub import HfApi, hf_hub_download

# 配置常量
BACKUP_DIR = "/root/.openclaw"
BACKUP_FILENAME = "backup.tar.gz"

def restore():
    repo_id = os.getenv("HF_DATASET")
    token = os.getenv("HF_TOKEN")
    
    if not repo_id or not token:
        print("--- [SYNC] 跳过恢复: 未配置 HF_DATASET 或 HF_TOKEN ---")
        return True # 返回 True 表示流程继续,只是没恢复数据

    try:
        print(f"--- [SYNC] 启动恢复流程, 目标仓库: {repo_id} ---")
        api = HfApi()
        
        # 获取文件列表
        files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
        
        if BACKUP_FILENAME  in files:
            print(f"--- [SYNC] 发现备份文件: {BACKUP_FILENAME}, 正在下载... ---")
            # 下载到本地临时位置
            downloaded_path = hf_hub_download(
                repo_id=repo_id, 
                filename=BACKUP_FILENAME, 
                repo_type="dataset", 
                token=token,
                force_download=True # 确保每次都是最新的
            )
            
            extract_path = BACKUP_DIR
            print(f"--- [SYNC] 正在解压至 {extract_path} ... ---")
            
            with tarfile.open(downloaded_path, "r:gz") as tar:
                # 安全解压逻辑 (兼容旧版本 Python)
                def is_within_directory(directory, target):
                    abs_directory = os.path.abspath(directory)
                    abs_target = os.path.abspath(target)
                    prefix = os.path.commonprefix([abs_directory, abs_target])
                    return prefix == abs_directory

                for member in tar.getmembers():
                    member_path = os.path.join(extract_path, member.name)
                    if not is_within_directory(extract_path, member_path):
                        raise Exception(f"尝试跳出目录的攻击行为: {member.name}")
                
                tar.extractall(path=extract_path)
            
            print(f"--- [SYNC] 恢复成功! 数据已覆盖至 {extract_path} ---")
            return True
        
        else:
            # 修正日志:不再谎称检查了时间
            print("--- [SYNC] 未在仓库中找到备份文件,将使用空状态启动 ---")
            return True
    except Exception as e:
        print(f"--- [SYNC] 恢复异常: {e} ---")
        import traceback
        traceback.print_exc()
        return True # 即使失败也返回 True,避免阻塞启动

def backup():
    repo_id = os.getenv("HF_DATASET")
    token = os.getenv("HF_TOKEN")

    if not repo_id or not token:
        print("--- [SYNC] 跳过备份: 未配置 HF_DATASET 或 HF_TOKEN ---")
        return

    try:
        print(f"--- [SYNC] 正在执行全量备份: {BACKUP_FILENAME} ---")
        
        base_path = BACKUP_DIR
        if not os.path.exists(base_path):
            print(f"--- [SYNC] 警告: 备份目录 {base_path} 不存在,跳过打包 ---")
            # 即使目录不存在,也可以上传一个空的标记文件或者直接返回,这里选择返回
            return
        
        # 创建临时压缩包
        with tarfile.open(BACKUP_FILENAME, "w:gz") as tar:
            # 修复点:将 arcname 设置为 '.',这样解压后直接是 .openclaw 的内容,
            # 或者设置为 os.path.basename(base_path) 保留文件夹名。
            # 通常为了还原方便,我们希望在 .openclaw 目录下直接看到内容,所以用 '.'
            # 如果希望解压出来是 /root/.openclaw/.openclaw/... 则用 basename
            arcname = "." 
            tar.add(base_path, arcname=arcname)
            print(f"--- [SYNC] 本地打包完成,大小: {os.path.getsize(BACKUP_FILENAME) / 1024 / 1024:.2f} MB ---")
        
        api = HfApi()
        print(f"--- [SYNC] 正在上传至 Hugging Face ({repo_id}) ... ---")
        
        api.upload_file(
            path_or_fileobj=BACKUP_FILENAME, 
            path_in_repo=BACKUP_FILENAME, 
            repo_id=repo_id, 
            repo_type="dataset", 
            token=token
        )
        
        print(f"--- [SYNC] 备份上传成功! ---")
        
        # 清理本地临时备份文件以节省空间
        if os.path.exists(BACKUP_FILENAME):
            os.remove(BACKUP_FILENAME)
            print("--- [SYNC] 本地临时文件已清理 ---")
            
    except Exception as e:
        print(f"--- [SYNC] 备份失败: {e} ---")
        import traceback
        traceback.print_exc()

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "backup":
        backup()
    else:
        restore()