#!/usr/bin/env python3 """Deploy landing pages to SatsSpin (CT146), Signal Miner (CT548), WorkBrain (CT144). Run ON Proxmox: /usr/bin/python3 /tmp/deploy_landings.py """ import re, subprocess, sys def sh(cmd, timeout=60): r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout) return r.stdout + r.stderr def pct(vmid, cmd, timeout=60): return sh(f"pct exec {vmid} -- bash -c {sh_quote(cmd)}", timeout) def sh_quote(s): return "'" + s.replace("'", "'\\''") + "'" results = {} # ── 1. SatsSpin CT146: replace dist/index.html ────────────────────── print("=== SATSSPIN CT146 ===") out = pct(146, "cp /opt/casino/dist/index.html /opt/casino/dist/index.html.aistudio.bak && " "cp /tmp/satsspin.html /opt/casino/dist/index.html && " "chmod 644 /opt/casino/dist/index.html && echo COPIED") results['satsspin'] = 'COPIED' in out print(out[:200]) sh("pct exec 146 -- systemctl restart satsspin", timeout=60) print("restarted satsspin") # ── 2. Signal Miner CT548: landing file + patch "/" route ─────────── print("=== SIGNAL MINER CT548 ===") out = pct(548, "cp /opt/signal-miner/app.py /opt/signal-miner/app.py.bak-landing-20260813 && " "cp /tmp/signalminer.html /opt/signal-miner/landing.html && echo BACKED_UP") results['signalminer_backup'] = 'BACKED_UP' in out print(out[:200]) # patch app.py: replace old landing() body with file read patch_script = ''' import re src = open("/opt/signal-miner/app.py").read() old_start = src.find("@app.route(\\"/\\")\\ndef landing():") assert old_start != -1, "landing route not found" old_end = src.find("\\"\\"\\"", old_start) assert old_end != -1, "landing end marker not found" old_end += len("\\"\\"\\"") new_code = """@app.route(\\"/\\") def landing(): with open("/opt/signal-miner/landing.html") as f: return f.read()""" src = src[:old_start] + new_code + src[old_end:] open("/opt/signal-miner/app.py","w").write(src) print("PATCHED") ''' open("/tmp/patch_sm.py", "w").write(patch_script) sh("pct push 548 /tmp/patch_sm.py /tmp/patch_sm.py", timeout=60) out = pct(548, "python3 /tmp/patch_sm.py && systemctl restart signal-miner && echo RESTARTED") results['signalminer'] = 'RESTARTED' in out print(out[:300]) # ── 3. WorkBrain CT144: landing file + "/" → landing, "/app" → dashboard ── print("=== WORKBRAIN CT144 ===") svc = pct(144, "systemctl list-units --type=service --state=running | grep -oiE '[a-z-]*workbrain[a-z-]*' | head -1").strip() print("service:", svc or "?") out = pct(144, "cp /opt/workbrain/app.py /opt/workbrain/app.py.bak-landing-20260813 && " "cp /tmp/workbrain.html /opt/workbrain/landing.html && echo BACKED_UP") results['workbrain_backup'] = 'BACKED_UP' in out print(out[:200]) patch_script2 = ''' src = open("/opt/workbrain/app.py").read() old = "@app.route(\\"/\\")\\ndef dashboard():\\n return render_template(\\"dashboard.html\\")" new = """@app.route(\\"/\\") def landing(): with open("/opt/workbrain/landing.html") as f: return f.read() @app.route(\\"/app\\") def dashboard(): return render_template(\\"dashboard.html\\")""" assert old in src, "dashboard route pattern not found" src = src.replace(old, new, 1) open("/opt/workbrain/app.py","w").write(src) print("PATCHED") ''' open("/tmp/patch_wb.py", "w").write(patch_script2) sh("pct push 144 /tmp/patch_wb.py /tmp/patch_wb.py", timeout=60) out = pct(144, "python3 /tmp/patch_wb.py && echo PATCHED") print(out[:200]) if svc: out = pct(144, f"systemctl restart {svc} && echo RESTARTED") results['workbrain'] = 'RESTARTED' in out print(out[:200]) else: out = pct(144, "systemctl list-units --type=service --state=running | grep -iE 'flask|gunicorn|python' | head -3") print("fallback services:", out[:300]) results['workbrain'] = 'manual' print("=== RESULTS ===") for k, v in results.items(): print(k, "OK" if v else "FAILED")