From aa35c24819c50f7ad76854b8117ee85855df5e0f Mon Sep 17 00:00:00 2001 From: thomasciesla Date: Tue, 30 Dec 2025 13:29:49 +0100 Subject: [PATCH] jtl-wafi-dashboard.py aktualisiert --- jtl-wafi-dashboard.py | 104 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/jtl-wafi-dashboard.py b/jtl-wafi-dashboard.py index 9dba073..79a84ba 100644 --- a/jtl-wafi-dashboard.py +++ b/jtl-wafi-dashboard.py @@ -48,6 +48,12 @@ HISTORY_MAX_POINTS = 1000 # Max Datenpunkte pro Shop SECRET_KEY = os.environ.get("DASHBOARD_SECRET", secrets.token_hex(32)) +# ============================================================================= +# AUTO-UPDATE +# ============================================================================= +DASHBOARD_UPDATE_URL = "https://git.jtl-hosting.de/thomasciesla/JTL-WAFI/raw/branch/main/jtl-wafi-dashboard.py" +AGENT_UPDATE_URL = "https://git.jtl-hosting.de/thomasciesla/JTL-WAFI/raw/branch/main/jtl-wafi-agent.py" + # ============================================================================= # UTILITY @@ -1176,6 +1182,99 @@ async def get_shop_history_api(domain: str, request: Request): return {"domain": domain, **data} +@app.post("/api/update-dashboard") +async def update_dashboard(request: Request): + """Dashboard selbst updaten.""" + import urllib.request + import urllib.error + + user = await get_current_user(request) + if not user: + raise HTTPException(401) + + try: + # 1. Download neue Version + req = urllib.request.Request( + DASHBOARD_UPDATE_URL, + headers={'User-Agent': f'JTL-WAFi-Dashboard/{VERSION}'} + ) + with urllib.request.urlopen(req, timeout=30) as response: + new_content = response.read().decode('utf-8') + + # 2. Syntax-Check + compile(new_content, '', 'exec') + + # 3. Version extrahieren + new_version = "unknown" + for line in new_content.split('\n')[:50]: + if 'VERSION = ' in line: + new_version = line.split('=')[1].strip().strip('"\'') + break + + # 4. Script-Pfad ermitteln + script_path = os.path.abspath(__file__) + backup_path = script_path + '.backup' + + # 5. Backup erstellen + import shutil + shutil.copy(script_path, backup_path) + + # 6. Neue Version schreiben + with open(script_path, 'w', encoding='utf-8') as f: + f.write(new_content) + + # 7. Neustart planen (nach Response) + async def restart_dashboard(): + await asyncio.sleep(2) + os.execv(sys.executable, [sys.executable, script_path]) + + asyncio.create_task(restart_dashboard()) + + return { + "success": True, + "message": f"Update erfolgreich ({VERSION} -> {new_version}). Dashboard startet neu...", + "old_version": VERSION, + "new_version": new_version + } + + except urllib.error.URLError as e: + return {"success": False, "error": f"Download fehlgeschlagen: {str(e)}"} + except SyntaxError as e: + return {"success": False, "error": f"Syntax-Fehler in neuer Version: {str(e)}"} + except Exception as e: + return {"success": False, "error": f"Update fehlgeschlagen: {str(e)}"} + + +@app.post("/api/update-agents") +async def update_agents(request: Request): + """Alle verbundenen Agents updaten.""" + user = await get_current_user(request) + if not user: + raise HTTPException(401) + + updated = 0 + failed = 0 + + for agent_id in list(manager.agent_connections.keys()): + if manager.is_agent_connected(agent_id): + try: + command_id = secrets.token_hex(8) + await manager.send_to_agent(agent_id, { + 'type': 'command.update', + 'data': {'command_id': command_id} + }) + updated += 1 + except Exception: + failed += 1 + + return { + "success": True, + "updated": updated, + "failed": failed, + "message": f"{updated} Agent(s) werden aktualisiert..." + } + + # ============================================================================= # HTML TEMPLATES # ============================================================================= @@ -1386,6 +1485,7 @@ def get_dashboard_html() -> str:
--:--:--
Verbinde...
Update:
+
Abmelden
@@ -1489,6 +1589,10 @@ def get_dashboard_html() -> str: function toggleRateLimitForm(){const area=document.getElementById('rateLimitFormArea');area.style.display=area.style.display==='none'?'block':'none';} async function applyRateLimit(){if(!currentDetailShop)return;const rateLimit=document.getElementById('detailRateLimitInput').value;const banDuration=document.getElementById('detailBanDurationInput').value;const restartFpm=document.getElementById('autoFpmRestart').checked?'true':'false';if(!confirm('Rate-Limit aktivieren: '+rateLimit+' Req/min?'))return;const s=shops[currentDetailShop];toast('Wechsle zu Rate-Limit...','info');if(s&&s.status==='active'){const dfd=new FormData();dfd.append('domain',currentDetailShop);dfd.append('restart_fpm',restartFpm);await fetch('/api/shops/deactivate',{method:'POST',body:dfd});await new Promise(r=>setTimeout(r,500));}const fd=new FormData();fd.append('domain',currentDetailShop);fd.append('bot_mode','true');fd.append('bot_rate_limit',rateLimit);fd.append('bot_ban_duration',banDuration);fd.append('country_mode','false');fd.append('monitor_only','false');fd.append('restart_fpm',restartFpm);await fetch('/api/shops/activate',{method:'POST',body:fd});closeModal('detailModal');} async function detailSwitchMode(mode){if(!currentDetailShop)return;const s=shops[currentDetailShop];const restartFpm=document.getElementById('autoFpmRestart').checked?'true':'false';const modeNames={'bot-monitor':'🔍 Monitor','country-dach':'🇩🇪 DACH','country-eu':'🇪🇺 EU+'};if(!confirm('Modus wechseln zu '+modeNames[mode]+'?'))return;toast('Wechsle Modus...','info');if(s&&s.status==='active'){const dfd=new FormData();dfd.append('domain',currentDetailShop);dfd.append('restart_fpm',restartFpm);await fetch('/api/shops/deactivate',{method:'POST',body:dfd});await new Promise(r=>setTimeout(r,500));}const fd=new FormData();fd.append('domain',currentDetailShop);fd.append('restart_fpm',restartFpm);if(mode==='bot-monitor'){fd.append('monitor_only','true');}else if(mode==='country-dach'){fd.append('bot_mode','true');fd.append('bot_rate_limit','30');fd.append('bot_ban_duration','300');fd.append('country_mode','true');fd.append('country_rate_limit','100');fd.append('country_ban_duration','600');fd.append('unlimited_countries','de,at,ch');}else if(mode==='country-eu'){fd.append('bot_mode','true');fd.append('bot_rate_limit','30');fd.append('bot_ban_duration','300');fd.append('country_mode','true');fd.append('country_rate_limit','100');fd.append('country_ban_duration','600');fd.append('unlimited_countries','de,at,ch,be,cy,ee,es,fi,fr,gb,gr,hr,ie,it,lt,lu,lv,mt,nl,pt,si,sk');}await fetch('/api/shops/activate',{method:'POST',body:fd});closeModal('detailModal');} + function toggleUpdateDropdown(){const menu=document.getElementById('updateDropdownMenu');menu.style.display=menu.style.display==='none'?'block':'none';const cnt=Object.values(agents).filter(a=>a.status==='online').length;document.getElementById('agentCountDropdown').textContent=cnt;} + document.addEventListener('click',e=>{const dd=document.querySelector('.update-dropdown');if(dd&&!dd.contains(e.target)){document.getElementById('updateDropdownMenu').style.display='none';}}); + async function updateDashboard(){document.getElementById('updateDropdownMenu').style.display='none';if(!confirm('Dashboard aktualisieren? Das Dashboard wird nach dem Update neu gestartet.'))return;toast('Dashboard wird aktualisiert...','info');try{const r=await fetch('/api/update-dashboard',{method:'POST'});const d=await r.json();if(d.success){toast(d.message,'success');setTimeout(()=>location.reload(),3000);}else{toast(d.error,'error');}}catch(e){toast('Update fehlgeschlagen: '+e,'error');}} + async function updateAgents(){document.getElementById('updateDropdownMenu').style.display='none';const cnt=Object.values(agents).filter(a=>a.status==='online').length;if(cnt===0){toast('Keine Agents online','warning');return;}if(!confirm(cnt+' Agent(s) aktualisieren? Die Agents werden nach dem Update neu gestartet.'))return;toast('Agents werden aktualisiert...','info');try{const r=await fetch('/api/update-agents',{method:'POST'});const d=await r.json();if(d.success){toast(d.message,'success');}else{toast(d.error,'error');}}catch(e){toast('Update fehlgeschlagen: '+e,'error');}} connect(); setUpdateInterval(10000);