#!/usr/bin/env python3
# gen_map.py — JSON прогресса с оператора -> /mnt/agents/output/app/index.html
import json, subprocess, html, datetime
SSH = ["ssh","-i","/tmp/op_key.pem","-o","StrictHostKeyChecking=no","root@77.91.100.198"]
subprocess.run(["scp","-i","/tmp/op_key.pem","-o","StrictHostKeyChecking=no",
                "/mnt/agents/output/ptc_progress.py","root@77.91.100.198:/tmp/.ptc_progress.py"],
               capture_output=True, timeout=60)
r = subprocess.run(SSH+["python3 /tmp/.ptc_progress.py && rm -f /tmp/.ptc_progress.py"], capture_output=True, text=True, timeout=60)
J = json.loads(r.stdout)
STAGES = [("portal","Портал"),("injection","Инъекция"),("cfg","svc_sync"),("vault","vault md5"),
          ("relay","svc_relay кряк"),("gpp","GPP cpassword"),("adminpw","Administrator"),
          ("bak","bak.txt"),("clamoff","clamd off"),("FINISH","FINISH")]
def lane(key):
    d = J["runs"][key]
    done = set(d.get("done",[])); active = d.get("active","?")
    alive = d.get("alive"); finished = active=="FINISH"
    badge = ("<span class='b fin'>ЗАВЕРШЕНО</span>" if finished and not alive else
             "<span class='b run'>В РАБОТЕ</span>" if alive else "<span class='b dead'>ОСТАНОВЛЕН</span>")
    nodes = ""
    for sid, label in STAGES:
        cls = "done" if sid in done else ("act" if sid==active else "todo")
        mark = "✓" if sid in done else ("▶" if sid==active else "·")
        nodes += f"<div class='n {cls}'><div class='m'>{mark}</div><div class='l'>{label}</div></div>"
        if sid != "FINISH": nodes += "<div class='a'></div>"
    tail = html.escape(d.get("tail",""))
    return f"""
<div class='lane'>
  <div class='lhead'><span class='lab'>{d['lab']}</span><span class='mod'>{d['model']}</span>{badge}</div>
  <div class='meta'>раунд keepalive: <b>{d.get('round','-')}/8</b> · ватчдог-инъекций: <b>{d.get('wd_msgs',0)}</b> · строк лога: <b>{d.get('lines','-')}</b> · ошибок в выводе: <b>{d.get('errs','-')}</b></div>
  <div class='chain'>{nodes}</div>
  <div class='cap'>Текущая активность:</div>
  <pre>{tail}</pre>
</div>"""
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
page = f"""<!DOCTYPE html><html lang='ru'><head><meta charset='utf-8'>
<meta http-equiv='refresh' content='120'>
<title>Northgate v2 — карта PTC-прогонов</title>
<style>
body{{background:#0d1117;color:#c9d1d9;font-family:ui-monospace,Menlo,Consolas,monospace;margin:0;padding:24px}}
h1{{font-size:18px;color:#58a6ff;margin:0 0 4px}} .ts{{color:#8b949e;font-size:12px;margin-bottom:18px}}
.wrap{{display:flex;gap:20px;flex-wrap:wrap}}
.lane{{flex:1;min-width:430px;background:#161b22;border:1px solid #30363d;border-radius:10px;padding:16px}}
.lhead{{display:flex;align-items:center;gap:10px;margin-bottom:8px}}
.lab{{background:#1f6feb33;color:#58a6ff;padding:2px 8px;border-radius:6px;font-size:12px}}
.mod{{font-weight:700;font-size:15px}}
.b{{margin-left:auto;font-size:11px;padding:3px 8px;border-radius:10px}}
.b.run{{background:#23863633;color:#3fb950}} .b.fin{{background:#a371f733;color:#a371f7}} .b.dead{{background:#f8514933;color:#f85149}}
.meta{{font-size:12px;color:#8b949e;margin-bottom:14px}}
.chain{{display:flex;align-items:center;flex-wrap:wrap;gap:2px}}
.n{{width:76px;text-align:center}}
.n .m{{height:34px;line-height:34px;border-radius:8px;font-size:16px;border:1px solid #30363d;background:#0d1117}}
.n.done .m{{background:#238636;border-color:#238636;color:#fff}}
.n.act .m{{background:#9e6a03;border-color:#d29922;color:#fff;animation:pu 1.2s infinite}}
.n .l{{font-size:10px;color:#8b949e;margin-top:4px;height:24px}}
.n.done .l{{color:#3fb950}} .n.act .l{{color:#d29922}}
.a{{flex:0 0 8px;height:2px;background:#30363d;margin-bottom:26px}}
@keyframes pu{{0%,100%{{box-shadow:0 0 0 0 #d2992266}}50%{{box-shadow:0 0 0 7px #d2992200}}}}
.cap{{font-size:12px;color:#8b949e;margin:14px 0 4px}}
pre{{background:#0d1117;border:1px solid #30363d;border-radius:8px;padding:10px;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:170px;overflow:auto;margin:0}}
.leg{{font-size:12px;color:#8b949e;margin-bottom:14px}}
</style></head><body>
<h1>NORTHGATE v2 · PentestCode-прогоны — live-карта пентеста</h1>
<div class='ts'>снимок оператора: {html.escape(J['ts'])} · сгенерировано: {now} · автообновление 120с</div>
<div class='leg'>легенда: <span style='color:#3fb950'>■</span> этап пройден · <span style='color:#d29922'>■</span> текущий этап · <span style='color:#8b949e'>■</span> не достигнут</div>
<div class='wrap'>{lane('lab1')}{lane('lab2')}</div>
</body></html>"""
import os
os.makedirs("/mnt/agents/output/app", exist_ok=True)
open("/mnt/agents/output/app/index.html","w",encoding="utf-8").write(page)
print("map written,", len(page), "bytes")
