Spaces:
Runtime error
Runtime error
Update sync_manager.py
Browse files- sync_manager.py +29 -30
sync_manager.py
CHANGED
|
@@ -18,7 +18,7 @@ PREFIX = "openclawai_backup_"
|
|
| 18 |
CONFIG_FILE_NAME = "openclaw.json"
|
| 19 |
KEEP_LAST = 5
|
| 20 |
|
| 21 |
-
# กรองเฉพาะไฟล์ที่ต้องการ
|
| 22 |
ALLOWED_EXTENSIONS = {'.md', '.json', '.db', '.sqlite', '.py'}
|
| 23 |
IGNORE_PATTERNS = {'.lock', '.tmp', '.wal', '.shm', '__pycache__'}
|
| 24 |
|
|
@@ -29,6 +29,9 @@ def log(msg):
|
|
| 29 |
print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
|
| 30 |
|
| 31 |
def is_valid_file(path: Path):
|
|
|
|
|
|
|
|
|
|
| 32 |
if path.suffix.lower() not in ALLOWED_EXTENSIONS:
|
| 33 |
return False
|
| 34 |
if any(pattern in path.name for pattern in IGNORE_PATTERNS):
|
|
@@ -45,14 +48,14 @@ def create_zip(zip_path):
|
|
| 45 |
log("⚠️ No important files found to backup")
|
| 46 |
return False
|
| 47 |
|
| 48 |
-
log(f"📦 Zipping {len(files)} files...")
|
| 49 |
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
|
| 50 |
for p in files:
|
| 51 |
z.write(p, p.relative_to(BASE_DIR))
|
| 52 |
return True
|
| 53 |
|
| 54 |
def backup():
|
| 55 |
-
if not REPO_ID or not TOKEN: return log("❌ Config missing")
|
| 56 |
|
| 57 |
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
| 58 |
zip_name = f"{PREFIX}{timestamp}.zip"
|
|
@@ -60,7 +63,7 @@ def backup():
|
|
| 60 |
|
| 61 |
try:
|
| 62 |
if BASE_DIR.exists() and create_zip(zip_path):
|
| 63 |
-
log(f"📤 Uploading
|
| 64 |
api.upload_file(
|
| 65 |
path_or_fileobj=str(zip_path),
|
| 66 |
path_in_repo=zip_name,
|
|
@@ -69,19 +72,11 @@ def backup():
|
|
| 69 |
token=TOKEN
|
| 70 |
)
|
| 71 |
|
| 72 |
-
#
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
api.upload_file(
|
| 76 |
-
path_or_fileobj=str(config_file),
|
| 77 |
-
path_in_repo=CONFIG_FILE_NAME,
|
| 78 |
-
repo_id=REPO_ID,
|
| 79 |
-
repo_type="dataset",
|
| 80 |
-
token=TOKEN
|
| 81 |
-
)
|
| 82 |
-
|
| 83 |
cleanup_old_backups()
|
| 84 |
-
log("✅ Backup successful")
|
| 85 |
finally:
|
| 86 |
if zip_path.exists(): zip_path.unlink()
|
| 87 |
|
|
@@ -103,38 +98,38 @@ def restore():
|
|
| 103 |
try:
|
| 104 |
contents = list_repo_files(REPO_ID, repo_type="dataset", token=TOKEN)
|
| 105 |
|
| 106 |
-
# 1
|
| 107 |
backups = sorted([f for f in contents if f.startswith(PREFIX) and f.endswith(".zip")])
|
| 108 |
if backups:
|
| 109 |
latest = backups[-1]
|
| 110 |
-
log(f"📥 Step 1:
|
| 111 |
zip_path = hf_hub_download(
|
| 112 |
repo_id=REPO_ID, filename=latest, repo_type="dataset", token=TOKEN
|
| 113 |
)
|
| 114 |
if BASE_DIR.exists():
|
| 115 |
shutil.rmtree(BASE_DIR)
|
| 116 |
BASE_DIR.mkdir(parents=True)
|
| 117 |
-
|
| 118 |
with zipfile.ZipFile(zip_path, "r") as z:
|
| 119 |
z.extractall(BASE_DIR)
|
| 120 |
-
log("
|
| 121 |
else:
|
| 122 |
-
log("⚠️ No
|
| 123 |
BASE_DIR.mkdir(parents=True, exist_ok=True)
|
| 124 |
|
| 125 |
-
# 2
|
| 126 |
if CONFIG_FILE_NAME in contents:
|
| 127 |
-
log(f"📥 Step 2:
|
| 128 |
try:
|
| 129 |
-
|
| 130 |
repo_id=REPO_ID, filename=CONFIG_FILE_NAME, repo_type="dataset", token=TOKEN
|
| 131 |
)
|
| 132 |
-
shutil.copy(
|
| 133 |
-
log("✅
|
| 134 |
except Exception as e:
|
| 135 |
-
log(f"⚠️ Failed to restore config: {e}")
|
| 136 |
else:
|
| 137 |
-
log("⚠️
|
| 138 |
|
| 139 |
return True
|
| 140 |
except Exception as e:
|
|
@@ -145,11 +140,14 @@ def restore():
|
|
| 145 |
# MAIN
|
| 146 |
# =========================
|
| 147 |
if __name__ == "__main__":
|
| 148 |
-
# รับ action จาก command line
|
| 149 |
action = sys.argv[1].lower() if len(sys.argv) > 1 else "restore"
|
| 150 |
|
| 151 |
if action == "backup":
|
| 152 |
-
log("🔄 Backup
|
|
|
|
|
|
|
|
|
|
| 153 |
while True:
|
| 154 |
now = time.localtime()
|
| 155 |
# รอจนถึงต้นชั่วโมงถัดไป
|
|
@@ -157,4 +155,5 @@ if __name__ == "__main__":
|
|
| 157 |
time.sleep(max(0, wait_sec))
|
| 158 |
backup()
|
| 159 |
else:
|
|
|
|
| 160 |
restore()
|
|
|
|
| 18 |
CONFIG_FILE_NAME = "openclaw.json"
|
| 19 |
KEEP_LAST = 5
|
| 20 |
|
| 21 |
+
# กรองเฉพาะไฟล์ที่ต้องการ
|
| 22 |
ALLOWED_EXTENSIONS = {'.md', '.json', '.db', '.sqlite', '.py'}
|
| 23 |
IGNORE_PATTERNS = {'.lock', '.tmp', '.wal', '.shm', '__pycache__'}
|
| 24 |
|
|
|
|
| 29 |
print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
|
| 30 |
|
| 31 |
def is_valid_file(path: Path):
|
| 32 |
+
# รวม openclaw.json เข้าไปใน ZIP ด้วยเพื่อเก็บเป็น Snapshot
|
| 33 |
+
if path.name == CONFIG_FILE_NAME:
|
| 34 |
+
return True
|
| 35 |
if path.suffix.lower() not in ALLOWED_EXTENSIONS:
|
| 36 |
return False
|
| 37 |
if any(pattern in path.name for pattern in IGNORE_PATTERNS):
|
|
|
|
| 48 |
log("⚠️ No important files found to backup")
|
| 49 |
return False
|
| 50 |
|
| 51 |
+
log(f"📦 Zipping {len(files)} files (including current config snapshot)...")
|
| 52 |
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
|
| 53 |
for p in files:
|
| 54 |
z.write(p, p.relative_to(BASE_DIR))
|
| 55 |
return True
|
| 56 |
|
| 57 |
def backup():
|
| 58 |
+
if not REPO_ID or not TOKEN: return log("❌ Config missing (HF_DATASET or HF_TOKEN)")
|
| 59 |
|
| 60 |
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
| 61 |
zip_name = f"{PREFIX}{timestamp}.zip"
|
|
|
|
| 63 |
|
| 64 |
try:
|
| 65 |
if BASE_DIR.exists() and create_zip(zip_path):
|
| 66 |
+
log(f"📤 Uploading backup snapshot: {zip_name}")
|
| 67 |
api.upload_file(
|
| 68 |
path_or_fileobj=str(zip_path),
|
| 69 |
path_in_repo=zip_name,
|
|
|
|
| 72 |
token=TOKEN
|
| 73 |
)
|
| 74 |
|
| 75 |
+
# หมายเหตุ: จะไม่มีการ upload CONFIG_FILE_NAME แยกต่างหาก
|
| 76 |
+
# เพื่อรักษาไฟล์ "Master" บน Cloud ไว้จากการแก้ไขด้วยมือของคุณ
|
| 77 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
cleanup_old_backups()
|
| 79 |
+
log("✅ Backup successful. Master config on Cloud remains untouched.")
|
| 80 |
finally:
|
| 81 |
if zip_path.exists(): zip_path.unlink()
|
| 82 |
|
|
|
|
| 98 |
try:
|
| 99 |
contents = list_repo_files(REPO_ID, repo_type="dataset", token=TOKEN)
|
| 100 |
|
| 101 |
+
# --- Step 1: Restore จาก ZIP (Snapshot) ---
|
| 102 |
backups = sorted([f for f in contents if f.startswith(PREFIX) and f.endswith(".zip")])
|
| 103 |
if backups:
|
| 104 |
latest = backups[-1]
|
| 105 |
+
log(f"📥 Step 1: Extracting snapshot from {latest}")
|
| 106 |
zip_path = hf_hub_download(
|
| 107 |
repo_id=REPO_ID, filename=latest, repo_type="dataset", token=TOKEN
|
| 108 |
)
|
| 109 |
if BASE_DIR.exists():
|
| 110 |
shutil.rmtree(BASE_DIR)
|
| 111 |
BASE_DIR.mkdir(parents=True)
|
| 112 |
+
|
| 113 |
with zipfile.ZipFile(zip_path, "r") as z:
|
| 114 |
z.extractall(BASE_DIR)
|
| 115 |
+
log("📦 Files extracted (includes snapshot config).")
|
| 116 |
else:
|
| 117 |
+
log("⚠️ No backup ZIP found.")
|
| 118 |
BASE_DIR.mkdir(parents=True, exist_ok=True)
|
| 119 |
|
| 120 |
+
# --- Step 2: Restore Master Config (ทับไฟล์จาก ZIP) ---
|
| 121 |
if CONFIG_FILE_NAME in contents:
|
| 122 |
+
log(f"📥 Step 2: Overwriting with Master '{CONFIG_FILE_NAME}' from Cloud")
|
| 123 |
try:
|
| 124 |
+
master_config_path = hf_hub_download(
|
| 125 |
repo_id=REPO_ID, filename=CONFIG_FILE_NAME, repo_type="dataset", token=TOKEN
|
| 126 |
)
|
| 127 |
+
shutil.copy(master_config_path, BASE_DIR / CONFIG_FILE_NAME)
|
| 128 |
+
log("✅ Master config restored and active.")
|
| 129 |
except Exception as e:
|
| 130 |
+
log(f"⚠️ Failed to restore Master config: {e}")
|
| 131 |
else:
|
| 132 |
+
log(f"⚠️ Master '{CONFIG_FILE_NAME}' not found on Cloud. Using config from ZIP.")
|
| 133 |
|
| 134 |
return True
|
| 135 |
except Exception as e:
|
|
|
|
| 140 |
# MAIN
|
| 141 |
# =========================
|
| 142 |
if __name__ == "__main__":
|
| 143 |
+
# รับ action จาก command line: 'python script.py backup' หรือ 'python script.py restore'
|
| 144 |
action = sys.argv[1].lower() if len(sys.argv) > 1 else "restore"
|
| 145 |
|
| 146 |
if action == "backup":
|
| 147 |
+
log("🔄 Backup mode started")
|
| 148 |
+
backup()
|
| 149 |
+
elif action == "backup_loop":
|
| 150 |
+
log("🔄 Backup loop active (Hourly)")
|
| 151 |
while True:
|
| 152 |
now = time.localtime()
|
| 153 |
# รอจนถึงต้นชั่วโมงถัดไป
|
|
|
|
| 155 |
time.sleep(max(0, wait_sec))
|
| 156 |
backup()
|
| 157 |
else:
|
| 158 |
+
log("🔄 Restore mode started")
|
| 159 |
restore()
|