#!/usr/bin/env python3 """offrestore.py APP — the restore wizard's „Teljes visszaállítás" for one app, endpoint-level (the form POSTs the wizard sends): 1) /backup/offbox/restore mode=full (size check), 2) the same with confirm=1 (download to the scratch), 3) /backup/offbox/reconstitute confirm=1 (files + database into the live app). Waits on /api/backup/restore-status after 2 and 3. Prints redirects (flash keys) and the status lines only.""" import json, os, subprocess, sys, time sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import ctl app = sys.argv[1] def say(*a): print(time.strftime("%H:%M:%SZ", time.gmtime()), *a, flush=True) def post(path, fields): if ctl._SESS is None: ctl.login() args = ["curl", "-sk", "-D", "-", "-o", "/dev/null", "-H", ctl.HOST, "-H", "Cookie: " + ctl._SESS, "-X", "POST", "--data-urlencode", "_csrf=" + ctl._CSRF] for k, v in fields.items(): args += ["--data-urlencode", f"{k}={v}"] out = subprocess.run(args + [ctl.BASE + path], capture_output=True, text=True, timeout=300).stdout loc = [l for l in out.splitlines() if l.lower().startswith("location")] say("POST", path, fields, "->", out.splitlines()[0] if out else "?", loc[:1]) def wait(): t0, last = time.time(), None while time.time() - t0 < 3600: c, d = ctl.call("GET", "/api/backup/restore-status", timeout=90) dd = (d.get("data") or {}) if isinstance(d, dict) else {} s = json.dumps(dd, ensure_ascii=False)[:600] if s != last: say("status:", s); last = s if not dd.get("running") and time.time() - t0 > 10: return time.sleep(10) ack = "--ack-placement" in sys.argv # the wizard's second confirm: „yes, into a different place than the backup recorded" if "--reconstitute-only" not in sys.argv: post("/backup/offbox/restore", {"app": app, "mode": "full"}) post("/backup/offbox/restore", {"app": app, "mode": "full", "confirm": "1"}) wait() post("/backup/offbox/reconstitute", dict({"app": app, "confirm": "1"}, **({"ack_placement": "1"} if ack else {}))) wait() c, d = ctl.call("GET", f"/api/stacks/{app}") say("app state:", (d.get("data") or {}).get("state"))