R1000 commited on
Commit
8dafd45
·
verified ·
1 Parent(s): 33729fa

Create sync.py

Browse files
Files changed (1) hide show
  1. sync.py +214 -0
sync.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import time
4
+ import shutil
5
+ import zipfile
6
+ from pathlib import Path
7
+ from huggingface_hub import HfApi, hf_hub_download, list_repo_files
8
+
9
+ # =========================
10
+ # CONFIG
11
+ # =========================
12
+ api = HfApi()
13
+ REPO_ID = os.getenv("HF_DATASET")
14
+ TOKEN = os.getenv("HF_TOKEN")
15
+
16
+ BASE_DIR = Path("/root/.openclaw")
17
+ PREFIX = "openclawai_backup_"
18
+ CONFIG_FILE_NAME = "openclaw.json"
19
+
20
+ KEEP_LAST = 5
21
+
22
+ # =========================
23
+ # UTILS
24
+ # =========================
25
+ def log(msg):
26
+ print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
27
+
28
+ def should_ignore(path):
29
+ ignore = ['.lock', '.tmp', '.wal', '.shm']
30
+ s = str(path)
31
+ return any(x in s or s.endswith(x) for x in ignore)
32
+
33
+ # =========================
34
+ # CREATE ZIP
35
+ # =========================
36
+ def create_zip(zip_path):
37
+ # เก็บ list ไฟล์ทั้งหมดก่อน
38
+ all_files = []
39
+ for root, _, files in os.walk(BASE_DIR):
40
+ for f in files:
41
+ p = Path(root) / f
42
+ rel = p.relative_to(BASE_DIR)
43
+
44
+ if should_ignore(rel):
45
+ continue
46
+
47
+ all_files.append((p, rel))
48
+
49
+ total = len(all_files)
50
+ if total == 0:
51
+ log("⚠️ No files to zip")
52
+ return
53
+
54
+ log(f"📦 Zipping {total} files...")
55
+
56
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
57
+ for i, (p, rel) in enumerate(all_files, 1):
58
+ z.write(p, rel)
59
+
60
+ # progress ทุก 2%
61
+ if i % max(1, total // 50) == 0 or i == total:
62
+ percent = (i / total) * 100
63
+ print(f"\r📊 Progress: {i}/{total} ({percent:.1f}%)", end="", flush=True)
64
+
65
+ print() # newline หลังจบ
66
+
67
+ # =========================
68
+ # BACKUP
69
+ # =========================
70
+ def backup():
71
+ if not REPO_ID or not TOKEN:
72
+ log("❌ Missing config")
73
+ return False
74
+
75
+ if not BASE_DIR.exists():
76
+ log("⚠️ Nothing to backup")
77
+ return False
78
+
79
+ timestamp = time.strftime("%Y%m%d_%H%M%S")
80
+ zip_name = f"{PREFIX}{timestamp}.zip"
81
+ zip_path = Path(f"/tmp/{zip_name}")
82
+
83
+ try:
84
+ log(f"📦 Creating {zip_name}")
85
+ create_zip(zip_path)
86
+
87
+ log("📤 Uploading...")
88
+ api.upload_file(
89
+ path_or_fileobj=str(zip_path),
90
+ path_in_repo=zip_name,
91
+ repo_id=REPO_ID,
92
+ repo_type="dataset",
93
+ token=TOKEN,
94
+ commit_message=f"Full backup {timestamp}"
95
+ )
96
+
97
+ log(f"✅ Uploaded: {zip_name}")
98
+
99
+ cleanup_old_backups()
100
+ return True
101
+
102
+ finally:
103
+ if zip_path.exists():
104
+ zip_path.unlink()
105
+
106
+ # =========================
107
+ # CLEANUP
108
+ # =========================
109
+ def cleanup_old_backups():
110
+ contents = list_repo_files(REPO_ID, repo_type="dataset", token=TOKEN)
111
+
112
+ backups = sorted([
113
+ f for f in contents
114
+ if f.startswith(PREFIX) and f.endswith(".zip")
115
+ ])
116
+
117
+ if len(backups) <= KEEP_LAST:
118
+ return
119
+
120
+ for old in backups[:-KEEP_LAST]:
121
+ try:
122
+ api.delete_file(
123
+ path_in_repo=old,
124
+ repo_id=REPO_ID,
125
+ repo_type="dataset",
126
+ token=TOKEN
127
+ )
128
+ log(f"🗑️ Deleted: {old}")
129
+ except Exception as e:
130
+ log(f"⚠️ delete fail {old}: {e}")
131
+
132
+ # =========================
133
+ # RESTORE
134
+ # =========================
135
+ def restore():
136
+ if not REPO_ID or not TOKEN:
137
+ log("❌ Missing config")
138
+ return False
139
+
140
+ contents = list_repo_files(REPO_ID, repo_type="dataset", token=TOKEN)
141
+
142
+ # 1. Restore Full Backup (ZIP)
143
+ backups = sorted([f for f in contents if f.startswith(PREFIX) and f.endswith(".zip")])
144
+ if backups:
145
+ latest = backups[-1]
146
+ log(f"📥 Step 1: Restoring full backup from {latest}")
147
+ zip_path = hf_hub_download(
148
+ repo_id=REPO_ID, filename=latest, repo_type="dataset", token=TOKEN
149
+ )
150
+ if BASE_DIR.exists():
151
+ shutil.rmtree(BASE_DIR)
152
+ BASE_DIR.mkdir(parents=True)
153
+ log("📦 Extracting full backup...")
154
+ with zipfile.ZipFile(zip_path, "r") as z:
155
+ z.extractall(BASE_DIR)
156
+ log("✅ Full backup restored.")
157
+ else:
158
+ log("⚠️ No full backup found. Skipping Step 1.")
159
+ BASE_DIR.mkdir(parents=True, exist_ok=True)
160
+
161
+ # 2. Restore openclaw.json (แยกไฟล์)
162
+ config_exists_in_repo = CONFIG_FILE_NAME in contents
163
+ if config_exists_in_repo:
164
+ log(f"📥 Step 2: Restoring openclaw.json from Dataset")
165
+ try:
166
+ config_path = hf_hub_download(
167
+ repo_id=REPO_ID, filename=CONFIG_FILE_NAME, repo_type="dataset", token=TOKEN
168
+ )
169
+ shutil.copy(config_path, BASE_DIR / CONFIG_FILE_NAME)
170
+ log("✅ Config file restored.")
171
+ except Exception as e:
172
+ log(f"⚠️ Failed to restore config: {e}")
173
+ else:
174
+ log("⚠️ No separate openclaw.json found in Dataset. Step 2 skipped.")
175
+
176
+ return True
177
+
178
+ # =========================
179
+ # LOOP: ทุกชั่วโมงที่ :00
180
+ # =========================
181
+ def backup_loop_every_1hour_at_minute_00():
182
+ log("🔄 Backup every hour at :00")
183
+
184
+ while True:
185
+ now = time.localtime()
186
+ wait_sec = (60 - now.tm_min) * 60 - now.tm_sec
187
+
188
+ log(f"⏳ Waiting {wait_sec}s...")
189
+ time.sleep(wait_sec)
190
+
191
+ backup()
192
+
193
+ # =========================
194
+ # LOOP: TEST 5 MIN
195
+ # =========================
196
+ def backup_loop_5min():
197
+ log("🧪 TEST MODE: every 5 minutes")
198
+
199
+ while True:
200
+ backup()
201
+ log("⏳ Waiting 5 minutes...")
202
+ time.sleep(300)
203
+
204
+ # =========================
205
+ # MAIN
206
+ # =========================
207
+ if __name__ == "__main__":
208
+ action = sys.argv[1].lower() if len(sys.argv) > 1 else "restore"
209
+
210
+ if action == "backup":
211
+ backup_loop_every_1hour_at_minute_00()
212
+ # backup_loop_5min()
213
+ else:
214
+ restore()