jtl-wafi-dashboard.py aktualisiert
This commit is contained in:
@@ -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, '<update>', '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:
|
||||
<div class="clock" id="clock">--:--:--</div>
|
||||
<div class="connection-status"><div class="status-dot" id="wsStatus"></div><span id="wsStatusText">Verbinde...</span></div>
|
||||
<div style="display:flex;align-items:center;gap:8px"><span style="font-size:12px;color:var(--text-secondary)">Update:</span><select id="updateInterval" onchange="setUpdateInterval(this.value)" style="padding:4px 8px;border-radius:4px;border:1px solid var(--border);background:var(--card-bg);color:var(--text);font-size:12px"><option value="2000">2s</option><option value="5000">5s</option><option value="10000" selected>10s</option><option value="15000">15s</option></select></div><div style="display:flex;align-items:center;gap:6px;padding:4px 8px;border-radius:4px;border:1px solid var(--border);background:var(--card-bg)"><label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;color:var(--text-secondary)" title="OPcache nach Aktivierung/Deaktivierung leeren"><input type="checkbox" id="autoFpmRestart" checked style="margin:0"><span>FPM-Restart</span></label></div>
|
||||
<div class="update-dropdown" style="position:relative"><button class="btn-header" onclick="toggleUpdateDropdown()" id="updateDropdownBtn">🔄 v{VERSION}</button><div id="updateDropdownMenu" style="display:none;position:absolute;right:0;top:100%;margin-top:4px;background:var(--card-bg);border:1px solid var(--border);border-radius:8px;padding:8px 0;min-width:200px;z-index:1000;box-shadow:0 4px 12px rgba(0,0,0,0.3)"><button onclick="updateDashboard()" style="display:block;width:100%;text-align:left;padding:8px 16px;border:none;background:none;color:var(--text);cursor:pointer;font-size:13px" onmouseover="this.style.background='var(--bg)'" onmouseout="this.style.background='none'">📊 Dashboard updaten</button><button onclick="updateAgents()" style="display:block;width:100%;text-align:left;padding:8px 16px;border:none;background:none;color:var(--text);cursor:pointer;font-size:13px" onmouseover="this.style.background='var(--bg)'" onmouseout="this.style.background='none'">🖥️ Alle Agents updaten (<span id="agentCountDropdown">0</span>)</button></div></div>
|
||||
<div style="display:flex;gap:8px"><button class="btn-header" onclick="openPasswordModal()">🔑</button><a href="/logout" class="btn-header">Abmelden</a></div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -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);
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user