server detail modal
This commit is contained in:
@@ -1667,11 +1667,13 @@ def get_dashboard_html() -> str:
|
|||||||
<div class="modal-overlay" id="passwordModal"><div class="modal"><h3 class="modal-title">🔑 Passwort ändern</h3><form id="passwordForm"><div class="form-group"><label>Aktuelles Passwort</label><input type="password" name="current" required></div><div class="form-group"><label>Neues Passwort</label><input type="password" name="new_pw" required minlength="8"></div><div class="form-group"><label>Bestätigen</label><input type="password" name="confirm" required></div><div class="modal-actions"><button type="button" class="btn btn-secondary" onclick="closeModal('passwordModal')">Abbrechen</button><button type="submit" class="btn btn-primary">Speichern</button></div></form></div></div>
|
<div class="modal-overlay" id="passwordModal"><div class="modal"><h3 class="modal-title">🔑 Passwort ändern</h3><form id="passwordForm"><div class="form-group"><label>Aktuelles Passwort</label><input type="password" name="current" required></div><div class="form-group"><label>Neues Passwort</label><input type="password" name="new_pw" required minlength="8"></div><div class="form-group"><label>Bestätigen</label><input type="password" name="confirm" required></div><div class="modal-actions"><button type="button" class="btn btn-secondary" onclick="closeModal('passwordModal')">Abbrechen</button><button type="submit" class="btn btn-primary">Speichern</button></div></form></div></div>
|
||||||
<div class="modal-overlay" id="allShopsModal"><div class="modal xlarge"><h3 class="modal-title"><span>📊 Alle Shops</span><button class="btn btn-secondary" onclick="closeModal('allShopsModal')">✕</button></h3><div style="margin-bottom:16px"><button class="btn" id="sortByReq" onclick="sortAllShops('req_per_min')">Nach Requests</button> <button class="btn btn-secondary" id="sortByBans" onclick="sortAllShops('active_bans')">Nach Bans</button></div><table><thead><tr><th>#</th><th>Domain</th><th>Server</th><th>Status</th><th>Req/min</th><th>Bans</th><th>Typ</th></tr></thead><tbody id="allShopsTable"></tbody></table></div></div>
|
<div class="modal-overlay" id="allShopsModal"><div class="modal xlarge"><h3 class="modal-title"><span>📊 Alle Shops</span><button class="btn btn-secondary" onclick="closeModal('allShopsModal')">✕</button></h3><div style="margin-bottom:16px"><button class="btn" id="sortByReq" onclick="sortAllShops('req_per_min')">Nach Requests</button> <button class="btn btn-secondary" id="sortByBans" onclick="sortAllShops('active_bans')">Nach Bans</button></div><table><thead><tr><th>#</th><th>Domain</th><th>Server</th><th>Status</th><th>Req/min</th><th>Bans</th><th>Typ</th></tr></thead><tbody id="allShopsTable"></tbody></table></div></div>
|
||||||
<div class="modal-overlay" id="detailModal"><div class="modal large"><h3 class="modal-title"><a href="#" id="detailDomainLink" target="_blank" style="color:var(--accent);text-decoration:none" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'"><span id="detailDomain">-</span> 🔗</a><button class="btn btn-secondary" onclick="closeModal('detailModal')">✕</button></h3><div style="color:var(--text-secondary);margin:-8px 0 16px 0;font-size:13px">Server: <span id="detailServer">-</span></div><div class="detail-grid"><div class="detail-card"><div class="detail-card-label">Status</div><div class="detail-card-value" id="detailStatus">-</div></div><div class="detail-card"><div class="detail-card-label">Modus</div><div class="detail-card-value" id="detailMode">-</div></div><div class="detail-card"><div class="detail-card-label">Unlimitiert</div><div class="detail-card-value" id="detailRegion">-</div></div><div class="detail-card"><div class="detail-card-label">Rate-Limit</div><div class="detail-card-value" id="detailRateLimit">-</div></div><div class="detail-card"><div class="detail-card-label">Ban-Dauer</div><div class="detail-card-value" id="detailBanDuration">-</div></div><div class="detail-card"><div class="detail-card-label">Laufzeit</div><div class="detail-card-value" id="detailRuntime">-</div></div></div><div class="detail-grid"><div class="detail-card"><div class="detail-card-label">Human/min</div><div class="detail-card-value success" id="detailHumanRpm">0</div></div><div class="detail-card"><div class="detail-card-label">Bot/min</div><div class="detail-card-value warning" id="detailBotRpm">0</div></div><div class="detail-card"><div class="detail-card-label">Aktive Bans</div><div class="detail-card-value" id="detailActiveBans">0</div></div><div class="detail-card"><div class="detail-card-label">Total Bans</div><div class="detail-card-value" id="detailTotalBans">0</div></div></div><div class="detail-section" id="detailActions"><div class="detail-section-title">⚙️ Steuerung</div><div style="display:flex;flex-wrap:wrap;gap:8px;padding:12px;background:var(--card-bg);border-radius:8px;align-items:center"><button class="btn btn-danger" id="detailBtnDeactivate" onclick="detailDeactivate()">⏹️ Deaktivieren</button><div style="border-left:1px solid var(--border);margin:0 8px;height:24px"></div><span style="color:var(--text-secondary);font-size:13px">Wechseln zu:</span><button class="btn" onclick="detailSwitchMode('bot-monitor')">🔍 Monitor</button><button class="btn" onclick="toggleRateLimitForm()">🤖 Bot-Limit ▾</button><button class="btn" onclick="detailSwitchMode('country-dach')">🇩🇪🇦🇹🇨🇭 DACH</button><button class="btn" onclick="detailSwitchMode('country-eu')">🇪🇺 EU+</button></div><div id="rateLimitFormArea" style="display:none;margin-top:12px;padding:12px;background:var(--card-bg);border-radius:8px;border:1px solid var(--border)"><div style="display:flex;gap:16px;align-items:flex-end;flex-wrap:wrap"><div><label style="display:block;font-size:12px;color:var(--text-secondary);margin-bottom:4px">Rate-Limit (Req/min)</label><input type="number" id="detailRateLimitInput" value="30" min="1" max="1000" style="width:100px;padding:8px;border-radius:4px;border:1px solid var(--border);background:var(--bg);color:var(--text)"></div><div><label style="display:block;font-size:12px;color:var(--text-secondary);margin-bottom:4px">Ban-Dauer</label><select id="detailBanDurationInput" style="padding:8px;border-radius:4px;border:1px solid var(--border);background:var(--bg);color:var(--text)"><option value="60">1 Minute</option><option value="300" selected>5 Minuten</option><option value="600">10 Minuten</option><option value="1800">30 Minuten</option><option value="3600">1 Stunde</option><option value="86400">24 Stunden</option></select></div><button class="btn btn-primary" onclick="applyRateLimit()">✓ Anwenden</button><button class="btn btn-secondary" onclick="toggleRateLimitForm()">Abbrechen</button></div></div></div><div class="detail-section"><div class="detail-section-title">📈 Bot-Aktivität über Zeit</div><div class="chart-container"><canvas id="requestChart"></canvas></div><div class="chart-legend" id="chartLegend"></div></div><div class="detail-section"><div class="detail-section-title">🌍 Country-Aktivität über Zeit</div><div class="chart-container"><canvas id="countryChart"></canvas></div><div class="chart-legend" id="countryChartLegend"></div></div><div class="detail-section"><div class="detail-section-title">🤖 Top Bots</div><div class="bot-list" id="detailTopBots"></div></div><div class="detail-section"><div class="detail-section-title">🌍 Top Countries</div><div class="bot-list" id="detailTopCountries"></div></div><div class="detail-section"><div class="detail-section-title">🚫 Aktuell gebannt</div><div class="bot-list" id="detailBannedBots"></div></div></div></div>
|
<div class="modal-overlay" id="detailModal"><div class="modal large"><h3 class="modal-title"><a href="#" id="detailDomainLink" target="_blank" style="color:var(--accent);text-decoration:none" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'"><span id="detailDomain">-</span> 🔗</a><button class="btn btn-secondary" onclick="closeModal('detailModal')">✕</button></h3><div style="color:var(--text-secondary);margin:-8px 0 16px 0;font-size:13px">Server: <span id="detailServer">-</span></div><div class="detail-grid"><div class="detail-card"><div class="detail-card-label">Status</div><div class="detail-card-value" id="detailStatus">-</div></div><div class="detail-card"><div class="detail-card-label">Modus</div><div class="detail-card-value" id="detailMode">-</div></div><div class="detail-card"><div class="detail-card-label">Unlimitiert</div><div class="detail-card-value" id="detailRegion">-</div></div><div class="detail-card"><div class="detail-card-label">Rate-Limit</div><div class="detail-card-value" id="detailRateLimit">-</div></div><div class="detail-card"><div class="detail-card-label">Ban-Dauer</div><div class="detail-card-value" id="detailBanDuration">-</div></div><div class="detail-card"><div class="detail-card-label">Laufzeit</div><div class="detail-card-value" id="detailRuntime">-</div></div></div><div class="detail-grid"><div class="detail-card"><div class="detail-card-label">Human/min</div><div class="detail-card-value success" id="detailHumanRpm">0</div></div><div class="detail-card"><div class="detail-card-label">Bot/min</div><div class="detail-card-value warning" id="detailBotRpm">0</div></div><div class="detail-card"><div class="detail-card-label">Aktive Bans</div><div class="detail-card-value" id="detailActiveBans">0</div></div><div class="detail-card"><div class="detail-card-label">Total Bans</div><div class="detail-card-value" id="detailTotalBans">0</div></div></div><div class="detail-section" id="detailActions"><div class="detail-section-title">⚙️ Steuerung</div><div style="display:flex;flex-wrap:wrap;gap:8px;padding:12px;background:var(--card-bg);border-radius:8px;align-items:center"><button class="btn btn-danger" id="detailBtnDeactivate" onclick="detailDeactivate()">⏹️ Deaktivieren</button><div style="border-left:1px solid var(--border);margin:0 8px;height:24px"></div><span style="color:var(--text-secondary);font-size:13px">Wechseln zu:</span><button class="btn" onclick="detailSwitchMode('bot-monitor')">🔍 Monitor</button><button class="btn" onclick="toggleRateLimitForm()">🤖 Bot-Limit ▾</button><button class="btn" onclick="detailSwitchMode('country-dach')">🇩🇪🇦🇹🇨🇭 DACH</button><button class="btn" onclick="detailSwitchMode('country-eu')">🇪🇺 EU+</button></div><div id="rateLimitFormArea" style="display:none;margin-top:12px;padding:12px;background:var(--card-bg);border-radius:8px;border:1px solid var(--border)"><div style="display:flex;gap:16px;align-items:flex-end;flex-wrap:wrap"><div><label style="display:block;font-size:12px;color:var(--text-secondary);margin-bottom:4px">Rate-Limit (Req/min)</label><input type="number" id="detailRateLimitInput" value="30" min="1" max="1000" style="width:100px;padding:8px;border-radius:4px;border:1px solid var(--border);background:var(--bg);color:var(--text)"></div><div><label style="display:block;font-size:12px;color:var(--text-secondary);margin-bottom:4px">Ban-Dauer</label><select id="detailBanDurationInput" style="padding:8px;border-radius:4px;border:1px solid var(--border);background:var(--bg);color:var(--text)"><option value="60">1 Minute</option><option value="300" selected>5 Minuten</option><option value="600">10 Minuten</option><option value="1800">30 Minuten</option><option value="3600">1 Stunde</option><option value="86400">24 Stunden</option></select></div><button class="btn btn-primary" onclick="applyRateLimit()">✓ Anwenden</button><button class="btn btn-secondary" onclick="toggleRateLimitForm()">Abbrechen</button></div></div></div><div class="detail-section"><div class="detail-section-title">📈 Bot-Aktivität über Zeit</div><div class="chart-container"><canvas id="requestChart"></canvas></div><div class="chart-legend" id="chartLegend"></div></div><div class="detail-section"><div class="detail-section-title">🌍 Country-Aktivität über Zeit</div><div class="chart-container"><canvas id="countryChart"></canvas></div><div class="chart-legend" id="countryChartLegend"></div></div><div class="detail-section"><div class="detail-section-title">🤖 Top Bots</div><div class="bot-list" id="detailTopBots"></div></div><div class="detail-section"><div class="detail-section-title">🌍 Top Countries</div><div class="bot-list" id="detailTopCountries"></div></div><div class="detail-section"><div class="detail-section-title">🚫 Aktuell gebannt</div><div class="bot-list" id="detailBannedBots"></div></div></div></div>
|
||||||
|
<div class="modal-overlay" id="serverDetailModal"><div class="modal large"><h3 class="modal-title"><span>🖥️ <span id="serverDetailHostname">-</span></span><button class="btn btn-secondary" onclick="closeModal('serverDetailModal')">✕</button></h3><div style="color:var(--text-secondary);margin:-8px 0 16px 0;font-size:13px">Status: <span id="serverDetailStatus">-</span> | Load: <span id="serverDetailLoad">-</span> | Memory: <span id="serverDetailMemory">-</span></div><div class="detail-grid"><div class="detail-card"><div class="detail-card-label">Shops</div><div class="detail-card-value" id="serverDetailShops">0/0</div></div><div class="detail-card"><div class="detail-card-label">🤖 Bot-Mode</div><div class="detail-card-value" id="serverDetailBotMode">0</div></div><div class="detail-card"><div class="detail-card-label">🌍 Country-Mode</div><div class="detail-card-value" id="serverDetailCountryMode">0</div></div><div class="detail-card"><div class="detail-card-label">🔍 Monitor</div><div class="detail-card-value" id="serverDetailMonitor">0</div></div><div class="detail-card"><div class="detail-card-label">Human/min</div><div class="detail-card-value success" id="serverDetailHumanRpm">0</div></div><div class="detail-card"><div class="detail-card-label">Bot/min</div><div class="detail-card-value warning" id="serverDetailBotRpm">0</div></div></div><div class="detail-grid"><div class="detail-card"><div class="detail-card-label">🚫 Aktive Bans</div><div class="detail-card-value danger" id="serverDetailActiveBans">0</div></div><div class="detail-card"><div class="detail-card-label">Total Bans</div><div class="detail-card-value" id="serverDetailTotalBans">0</div></div><div class="detail-card"><div class="detail-card-label">🛡️ Link11</div><div class="detail-card-value link11" id="serverDetailLink11">0</div></div><div class="detail-card"><div class="detail-card-label">⚡ Direkt</div><div class="detail-card-value direct" id="serverDetailDirect">0</div></div></div><div class="detail-section"><div class="detail-section-title">⚙️ Server-Aktionen</div><div style="display:flex;flex-wrap:wrap;gap:8px;padding:12px;background:var(--card-bg);border-radius:8px;align-items:center"><button class="btn btn-success" onclick="serverActivateAll()">▶️ Alle aktivieren</button><button class="btn btn-danger" onclick="serverDeactivateAll()">⏹️ Alle deaktivieren</button><div style="border-left:1px solid var(--border);margin:0 8px;height:24px"></div><span style="color:var(--text-secondary);font-size:13px">Nur inaktive:</span><button class="btn" onclick="serverActivateInactive('bot')">🤖 Bot</button><button class="btn" onclick="serverActivateInactive('monitor')">🔍 Monitor</button><button class="btn" onclick="serverActivateInactive('dach')">🇩🇪🇦🇹🇨🇭 DACH</button><button class="btn" onclick="serverActivateInactive('eu')">🇪🇺 EU+</button></div></div><div class="detail-section"><div class="detail-section-title">📈 Bot-Aktivität über Zeit (Server-gesamt)</div><div class="chart-container"><canvas id="serverRequestChart"></canvas></div><div class="chart-legend" id="serverChartLegend"></div></div><div class="detail-section"><div class="detail-section-title">🌍 Country-Aktivität über Zeit (Server-gesamt)</div><div class="chart-container"><canvas id="serverCountryChart"></canvas></div><div class="chart-legend" id="serverCountryChartLegend"></div></div><div class="detail-section"><div class="detail-section-title">🤖 Top Bots (Server-gesamt)</div><div class="bot-list" id="serverDetailTopBots"></div></div><div class="detail-section"><div class="detail-section-title">🌍 Top Countries (Server-gesamt)</div><div class="bot-list" id="serverDetailTopCountries"></div></div><div class="detail-section"><div class="detail-section-title">🚫 Aktuell gebannt (Server-gesamt)</div><div class="bot-list" id="serverDetailBannedBots"></div></div><div class="detail-section"><div class="detail-section-title">📋 Shops auf diesem Server</div><div style="max-height:300px;overflow-y:auto"><table style="width:100%"><thead><tr><th>Status</th><th>Domain</th><th>Modus</th><th>Req/min</th><th>Bans</th></tr></thead><tbody id="serverShopsTable"></tbody></table></div></div></div></div>
|
||||||
<div class="toast-container" id="toastContainer"></div>
|
<div class="toast-container" id="toastContainer"></div>
|
||||||
<script>
|
<script>
|
||||||
let ws=null,agents={},shops={},currentLogsShop=null,currentSortBy='req_per_min',currentDetailShop=null;
|
let ws=null,agents={},shops={},currentLogsShop=null,currentSortBy='req_per_min',currentDetailShop=null,currentDetailServer=null;
|
||||||
let updateInterval=10000,pendingRender=false,renderTimer=null;
|
let updateInterval=10000,pendingRender=false,renderTimer=null;
|
||||||
let notifications=[],maxNotifications=50,unreadCount=0;
|
let notifications=[],maxNotifications=50,unreadCount=0;
|
||||||
|
let serverBotChart=null,serverCountryChart=null,serverBotHistory={},serverCountryHistory={};
|
||||||
const BOT_COLORS=['#4a9eff','#00d26a','#ff4757','#ffc107','#9b59b6','#1abc9c','#e74c3c','#3498db','#f39c12','#2ecc71'];
|
const BOT_COLORS=['#4a9eff','#00d26a','#ff4757','#ffc107','#9b59b6','#1abc9c','#e74c3c','#3498db','#f39c12','#2ecc71'];
|
||||||
function setUpdateInterval(ms){updateInterval=parseInt(ms);if(renderTimer)clearInterval(renderTimer);renderTimer=setInterval(()=>{if(pendingRender){renderAgents();renderShops();renderTopShops();refreshStats();pendingRender=false;}},updateInterval);}
|
function setUpdateInterval(ms){updateInterval=parseInt(ms);if(renderTimer)clearInterval(renderTimer);renderTimer=setInterval(()=>{if(pendingRender){renderAgents();renderShops();renderTopShops();refreshStats();pendingRender=false;}},updateInterval);}
|
||||||
function scheduleRender(){pendingRender=true;}
|
function scheduleRender(){pendingRender=true;}
|
||||||
@@ -1686,7 +1688,7 @@ def get_dashboard_html() -> str:
|
|||||||
setInterval(updateClock,1000);updateClock();
|
setInterval(updateClock,1000);updateClock();
|
||||||
function connect(){const p=location.protocol==='https:'?'wss:':'ws:';ws=new WebSocket(p+'//'+location.host+'/ws/dashboard');ws.onopen=()=>{document.getElementById('wsStatus').classList.add('connected');document.getElementById('wsStatusText').textContent='Verbunden';};ws.onclose=()=>{document.getElementById('wsStatus').classList.remove('connected');document.getElementById('wsStatusText').textContent='Getrennt';setTimeout(connect,3000);};ws.onmessage=e=>handleMessage(JSON.parse(e.data));}
|
function connect(){const p=location.protocol==='https:'?'wss:':'ws:';ws=new WebSocket(p+'//'+location.host+'/ws/dashboard');ws.onopen=()=>{document.getElementById('wsStatus').classList.add('connected');document.getElementById('wsStatusText').textContent='Verbunden';};ws.onclose=()=>{document.getElementById('wsStatus').classList.remove('connected');document.getElementById('wsStatusText').textContent='Getrennt';setTimeout(connect,3000);};ws.onmessage=e=>handleMessage(JSON.parse(e.data));}
|
||||||
function handleMessage(msg){switch(msg.type){case'initial_state':case'refresh':agents={};msg.data.agents.forEach(a=>agents[a.id]=a);shops={};msg.data.shops.forEach(s=>shops[s.domain]=s);updateStats(msg.data.stats);renderAgents();renderShops();renderTopShops();break;case'agent.online':case'agent.update':case'agent.pending':const a=msg.data;if(!agents[a.agent_id])agents[a.agent_id]={};Object.assign(agents[a.agent_id],{id:a.agent_id,hostname:a.hostname,status:a.status||'online',approved:a.approved,shops_total:a.shops_summary?.total||0,shops_active:a.shops_summary?.active||0,load_1m:a.system?.load_1m,memory_percent:a.system?.memory_percent,last_seen:new Date().toISOString()});scheduleRender();break;case'agent.offline':if(agents[msg.data.agent_id]){agents[msg.data.agent_id].status='offline';scheduleRender();}break;case'agent.approved':if(agents[msg.data.agent_id]){agents[msg.data.agent_id].status='online';agents[msg.data.agent_id].approved=true;scheduleRender();toast('Agent freigegeben','success');}break;case'shop.full_update':msg.data.shops.forEach(s=>{shops[s.domain]={...s,agent_id:msg.data.agent_id,agent_hostname:msg.data.hostname};});scheduleRender();break;case'shop.stats':if(shops[msg.data.domain]){shops[msg.data.domain].stats=msg.data.stats;scheduleRender();}break;case'shop_history':updateBotChart(msg.data);updateCountryChart(msg.data);break;case'top_shops':case'all_shops_sorted':renderAllShopsTable(msg.data.shops,msg.data.sort_by);break;case'log.entry':if(msg.data.shop===currentLogsShop)addLogEntry(msg.data.line);break;case'bot.banned':toast('🚫 '+msg.data.bot_name+' gebannt','warning');break;case'command.result':toast(msg.data.message,msg.data.status==='success'?'success':'error');break;case'livestats.result':renderLiveStats(msg.data);break;}}
|
function handleMessage(msg){switch(msg.type){case'initial_state':case'refresh':agents={};msg.data.agents.forEach(a=>agents[a.id]=a);shops={};msg.data.shops.forEach(s=>shops[s.domain]=s);updateStats(msg.data.stats);renderAgents();renderShops();renderTopShops();break;case'agent.online':case'agent.update':case'agent.pending':const a=msg.data;if(!agents[a.agent_id])agents[a.agent_id]={};Object.assign(agents[a.agent_id],{id:a.agent_id,hostname:a.hostname,status:a.status||'online',approved:a.approved,shops_total:a.shops_summary?.total||0,shops_active:a.shops_summary?.active||0,load_1m:a.system?.load_1m,memory_percent:a.system?.memory_percent,last_seen:new Date().toISOString()});scheduleRender();break;case'agent.offline':if(agents[msg.data.agent_id]){agents[msg.data.agent_id].status='offline';scheduleRender();}break;case'agent.approved':if(agents[msg.data.agent_id]){agents[msg.data.agent_id].status='online';agents[msg.data.agent_id].approved=true;scheduleRender();toast('Agent freigegeben','success');}break;case'shop.full_update':msg.data.shops.forEach(s=>{shops[s.domain]={...s,agent_id:msg.data.agent_id,agent_hostname:msg.data.hostname};});scheduleRender();break;case'shop.stats':if(shops[msg.data.domain]){shops[msg.data.domain].stats=msg.data.stats;scheduleRender();}break;case'shop_history':updateBotChart(msg.data);updateCountryChart(msg.data);break;case'top_shops':case'all_shops_sorted':renderAllShopsTable(msg.data.shops,msg.data.sort_by);break;case'log.entry':if(msg.data.shop===currentLogsShop)addLogEntry(msg.data.line);break;case'bot.banned':toast('🚫 '+msg.data.bot_name+' gebannt','warning');break;case'command.result':toast(msg.data.message,msg.data.status==='success'?'success':'error');break;case'livestats.result':renderLiveStats(msg.data);break;}}
|
||||||
function renderAgents(){const t=document.getElementById('agentsTable'),l=getSortedAgents();document.getElementById('agentCount').textContent=l.length+' Agents';t.innerHTML=l.map(a=>'<tr><td><span class="status-badge status-'+(a.status||'offline')+'">'+(a.status==='online'?'🟢':a.status==='pending'?'🟡':'🔴')+' '+(a.status==='pending'?'Warte':a.status==='online'?'Online':'Offline')+'</span></td><td><strong>'+a.hostname+'</strong></td><td>'+(a.shops_active||0)+'/'+(a.shops_total||0)+'</td><td>'+(a.load_1m!=null?a.load_1m.toFixed(2):'-')+'</td><td>'+(a.memory_percent!=null?a.memory_percent.toFixed(1)+'%':'-')+'</td><td>'+(a.last_seen?formatTime(a.last_seen):'-')+'</td><td>'+(a.status==='pending'?'<button class="btn btn-primary" onclick="approveAgent(\\''+a.id+'\\')">✓</button>':'')+'</td></tr>').join('');}
|
function renderAgents(){const t=document.getElementById('agentsTable'),l=getSortedAgents();document.getElementById('agentCount').textContent=l.length+' Agents';document.getElementById('agentCountDropdown').textContent=l.length;t.innerHTML=l.map(a=>'<tr><td><span class="status-badge status-'+(a.status||'offline')+'">'+(a.status==='online'?'🟢':a.status==='pending'?'🟡':'🔴')+' '+(a.status==='pending'?'Warte':a.status==='online'?'Online':'Offline')+'</span></td><td><span class="domain-link" onclick="openServerDetailModal(\\''+a.id+'\\')"><strong>'+a.hostname+'</strong></span></td><td>'+(a.shops_active||0)+'/'+(a.shops_total||0)+'</td><td>'+(a.load_1m!=null?a.load_1m.toFixed(2):'-')+'</td><td>'+(a.memory_percent!=null?a.memory_percent.toFixed(1)+'%':'-')+'</td><td>'+(a.last_seen?formatTime(a.last_seen):'-')+'</td><td>'+(a.status==='pending'?'<button class="btn btn-primary" onclick="approveAgent(\\''+a.id+'\\')">✓</button>':'')+'</td></tr>').join('');}
|
||||||
function renderShops(){const all=Object.values(shops);let l11=all.filter(s=>s.link11),dir=all.filter(s=>!s.link11);l11=getSortedShops(l11,'link11');dir=getSortedShops(dir,'direct');document.getElementById('link11Count').textContent=l11.length+' Shops';document.getElementById('directCount').textContent=dir.length+' Shops';document.getElementById('shopsLink11Table').innerHTML=renderShopRows(l11);document.getElementById('shopsDirectTable').innerHTML=renderShopRows(dir);}
|
function renderShops(){const all=Object.values(shops);let l11=all.filter(s=>s.link11),dir=all.filter(s=>!s.link11);l11=getSortedShops(l11,'link11');dir=getSortedShops(dir,'direct');document.getElementById('link11Count').textContent=l11.length+' Shops';document.getElementById('directCount').textContent=dir.length+' Shops';document.getElementById('shopsLink11Table').innerHTML=renderShopRows(l11);document.getElementById('shopsDirectTable').innerHTML=renderShopRows(dir);}
|
||||||
function renderShopRows(l){return l.map(s=>{const modeStr=s.monitor_only?'🔍 Monitor':((s.bot_mode?'🤖':'')+(s.country_mode?' 🌍':''))||'-';const bansStr=s.monitor_only?'-':((s.stats?.active_bot_bans||0)+(s.stats?.active_country_bans||0));return '<tr><td><span class="status-badge status-'+(s.status||'inactive')+'">'+(s.status==='active'?'✅':'⭕')+'</span></td><td><span class="domain-link" onclick="openDetailModal(\\''+s.domain+'\\')">'+s.domain+'</span></td><td>'+(s.agent_hostname||'-')+'</td><td>'+modeStr+'</td><td>'+((s.stats?.req_per_min||0).toFixed(1))+'</td><td>'+bansStr+'</td><td>'+formatRuntime(s.runtime_minutes)+'</td><td class="actions"><a href="https://'+s.domain+'" target="_blank" class="btn-icon">🔗</a>'+(s.status==='active'?'<button class="btn-icon" onclick="openLogs(\\''+s.domain+'\\')">📜</button><button class="btn btn-danger" onclick="deactivateShop(\\''+s.domain+'\\')">Stop</button>':'<button class="btn btn-primary" onclick="openActivateModal(\\''+s.domain+'\\')">Start</button>')+'</td></tr>';}).join('');}
|
function renderShopRows(l){return l.map(s=>{const modeStr=s.monitor_only?'🔍 Monitor':((s.bot_mode?'🤖':'')+(s.country_mode?' 🌍':''))||'-';const bansStr=s.monitor_only?'-':((s.stats?.active_bot_bans||0)+(s.stats?.active_country_bans||0));return '<tr><td><span class="status-badge status-'+(s.status||'inactive')+'">'+(s.status==='active'?'✅':'⭕')+'</span></td><td><span class="domain-link" onclick="openDetailModal(\\''+s.domain+'\\')">'+s.domain+'</span></td><td>'+(s.agent_hostname||'-')+'</td><td>'+modeStr+'</td><td>'+((s.stats?.req_per_min||0).toFixed(1))+'</td><td>'+bansStr+'</td><td>'+formatRuntime(s.runtime_minutes)+'</td><td class="actions"><a href="https://'+s.domain+'" target="_blank" class="btn-icon">🔗</a>'+(s.status==='active'?'<button class="btn-icon" onclick="openLogs(\\''+s.domain+'\\')">📜</button><button class="btn btn-danger" onclick="deactivateShop(\\''+s.domain+'\\')">Stop</button>':'<button class="btn btn-primary" onclick="openActivateModal(\\''+s.domain+'\\')">Start</button>')+'</td></tr>';}).join('');}
|
||||||
function renderTopShops(){const l=Object.values(shops).filter(s=>s.status==='active').sort((a,b)=>(b.stats?.req_per_min||0)-(a.stats?.req_per_min||0)).slice(0,10);document.getElementById('topShopsList').innerHTML=l.map(s=>{const bans=(s.stats?.active_bot_bans||0)+(s.stats?.active_country_bans||0);return '<div class="top-shop-item" onclick="openDetailModal(\\''+s.domain+'\\')"><div class="top-shop-domain">'+s.domain+'</div><div class="top-shop-stats"><span class="top-shop-req">'+((s.stats?.req_per_min||0).toFixed(1))+' req/m</span><span class="top-shop-bans">'+bans+' bans</span></div></div>';}).join('')||'<div style="color:var(--text-secondary)">Keine aktiven Shops</div>';}
|
function renderTopShops(){const l=Object.values(shops).filter(s=>s.status==='active').sort((a,b)=>(b.stats?.req_per_min||0)-(a.stats?.req_per_min||0)).slice(0,10);document.getElementById('topShopsList').innerHTML=l.map(s=>{const bans=(s.stats?.active_bot_bans||0)+(s.stats?.active_country_bans||0);return '<div class="top-shop-item" onclick="openDetailModal(\\''+s.domain+'\\')"><div class="top-shop-domain">'+s.domain+'</div><div class="top-shop-stats"><span class="top-shop-req">'+((s.stats?.req_per_min||0).toFixed(1))+' req/m</span><span class="top-shop-bans">'+bans+' bans</span></div></div>';}).join('')||'<div style="color:var(--text-secondary)">Keine aktiven Shops</div>';}
|
||||||
@@ -1747,6 +1749,19 @@ def get_dashboard_html() -> str:
|
|||||||
document.addEventListener('click',e=>{const dd=document.querySelector('.update-dropdown');if(dd&&!dd.contains(e.target)){document.getElementById('updateDropdownMenu').style.display='none';}});
|
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 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');}}
|
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');}}
|
||||||
|
// Server Detail Modal Functions
|
||||||
|
function openServerDetailModal(agentId){currentDetailServer=agentId;const a=agents[agentId];if(!a)return;renderServerDetail();document.getElementById('serverDetailModal').classList.add('open');startServerHistoryUpdate();}
|
||||||
|
function getServerShops(agentId){return Object.values(shops).filter(s=>s.agent_id===agentId);}
|
||||||
|
function aggregateServerStats(serverShops){const stats={total:serverShops.length,active:0,botMode:0,countryMode:0,monitorMode:0,humanRpm:0,botRpm:0,activeBans:0,totalBans:0,link11:0,direct:0,topBots:{},topCountries:{},bannedItems:[]};serverShops.forEach(s=>{if(s.status==='active')stats.active++;if(s.bot_mode&&!s.monitor_only)stats.botMode++;if(s.country_mode&&!s.monitor_only)stats.countryMode++;if(s.monitor_only)stats.monitorMode++;if(s.link11)stats.link11++;else stats.direct++;const st=s.stats||{};stats.humanRpm+=(st.human_rpm||0);stats.botRpm+=(st.bot_rpm||0);stats.activeBans+=(st.active_bot_bans||0)+(st.active_country_bans||0);stats.totalBans+=(st.bot_bans||0)+(st.country_bans||0);Object.entries(st.top_bots||{}).forEach(([bot,cnt])=>{stats.topBots[bot]=(stats.topBots[bot]||0)+cnt;});Object.entries(st.top_countries||{}).forEach(([country,cnt])=>{stats.topCountries[country]=(stats.topCountries[country]||0)+cnt;});(st.banned_bots||[]).forEach(b=>{if(!stats.bannedItems.includes(b))stats.bannedItems.push(b);});(st.banned_countries||[]).forEach(c=>{if(!stats.bannedItems.includes(c))stats.bannedItems.push(c);});});return stats;}
|
||||||
|
function renderServerDetail(){if(!currentDetailServer)return;const a=agents[currentDetailServer];if(!a)return;const serverShops=getServerShops(currentDetailServer);const stats=aggregateServerStats(serverShops);document.getElementById('serverDetailHostname').textContent=a.hostname;document.getElementById('serverDetailStatus').innerHTML=a.status==='online'?'<span style="color:var(--success)">🟢 Online</span>':'<span style="color:var(--danger)">🔴 Offline</span>';document.getElementById('serverDetailLoad').textContent=a.load_1m!=null?a.load_1m.toFixed(2):'-';document.getElementById('serverDetailMemory').textContent=a.memory_percent!=null?a.memory_percent.toFixed(1)+'%':'-';document.getElementById('serverDetailShops').textContent=stats.active+'/'+stats.total;document.getElementById('serverDetailBotMode').textContent=stats.botMode;document.getElementById('serverDetailCountryMode').textContent=stats.countryMode;document.getElementById('serverDetailMonitor').textContent=stats.monitorMode;document.getElementById('serverDetailHumanRpm').textContent=stats.humanRpm.toFixed(1);document.getElementById('serverDetailBotRpm').textContent=stats.botRpm.toFixed(1);document.getElementById('serverDetailActiveBans').textContent=stats.activeBans;document.getElementById('serverDetailTotalBans').textContent=stats.totalBans;document.getElementById('serverDetailLink11').textContent=stats.link11;document.getElementById('serverDetailDirect').textContent=stats.direct;document.getElementById('serverDetailTopBots').innerHTML=Object.entries(stats.topBots).sort((a,b)=>b[1]-a[1]).slice(0,15).map(([n,c])=>'<div class="bot-item"><span>'+n+'</span><span>'+c+'</span></div>').join('')||'<div style="color:var(--text-secondary);padding:8px">Keine Daten</div>';document.getElementById('serverDetailTopCountries').innerHTML=Object.entries(stats.topCountries).sort((a,b)=>b[1]-a[1]).slice(0,15).map(([n,c])=>'<div class="bot-item"><span>'+n.toUpperCase()+'</span><span>'+c+'</span></div>').join('')||'<div style="color:var(--text-secondary);padding:8px">Keine Daten</div>';document.getElementById('serverDetailBannedBots').innerHTML=stats.bannedItems.length>0?stats.bannedItems.map(n=>'<div class="bot-item"><span>🚫 '+n+'</span></div>').join(''):'<div style="color:var(--text-secondary);padding:8px">Keine aktiven Bans</div>';document.getElementById('serverShopsTable').innerHTML=serverShops.sort((a,b)=>(b.stats?.req_per_min||0)-(a.stats?.req_per_min||0)).map(s=>{const modeStr=s.monitor_only?'🔍':((s.bot_mode?'🤖':'')+(s.country_mode?'🌍':''))||'-';const bans=(s.stats?.active_bot_bans||0)+(s.stats?.active_country_bans||0);return '<tr style="cursor:pointer" onclick="closeModal(\\'serverDetailModal\\');openDetailModal(\\''+s.domain+'\\')"><td><span class="status-badge status-'+(s.status||'inactive')+'">'+(s.status==='active'?'✅':'⭕')+'</span></td><td>'+s.domain+'</td><td>'+modeStr+'</td><td>'+(s.stats?.req_per_min||0).toFixed(1)+'</td><td>'+bans+'</td></tr>';}).join('')||'<tr><td colspan="5" style="color:var(--text-secondary);text-align:center;padding:16px">Keine Shops</td></tr>';updateServerCharts(serverShops);}
|
||||||
|
function updateServerCharts(serverShops){const botData={},countryData={};serverShops.forEach(s=>{const st=s.stats||{};Object.entries(st.top_bots||{}).forEach(([bot,cnt])=>{botData[bot]=(botData[bot]||0)+cnt;});Object.entries(st.top_countries||{}).forEach(([country,cnt])=>{countryData[country]=(countryData[country]||0)+cnt;});});const now=new Date().toISOString().substring(0,19).replace('T',' ');Object.entries(botData).forEach(([bot,cnt])=>{if(!serverBotHistory[bot])serverBotHistory[bot]=[];serverBotHistory[bot].push({timestamp:now,count:cnt});if(serverBotHistory[bot].length>60)serverBotHistory[bot].shift();});Object.entries(countryData).forEach(([country,cnt])=>{if(!serverCountryHistory[country])serverCountryHistory[country]=[];serverCountryHistory[country].push({timestamp:now,count:cnt});if(serverCountryHistory[country].length>60)serverCountryHistory[country].shift();});renderServerBotChart();renderServerCountryChart();}
|
||||||
|
function renderServerBotChart(){const cv=document.getElementById('serverRequestChart'),ctx=cv.getContext('2d'),ct=cv.parentElement,w=ct.clientWidth-32,h=230;cv.width=w;cv.height=h;ctx.clearRect(0,0,w,h);const bn=Object.keys(serverBotHistory).sort((a,b)=>(serverBotHistory[b][serverBotHistory[b].length-1]?.count||0)-(serverBotHistory[a][serverBotHistory[a].length-1]?.count||0)).slice(0,10);if(bn.length===0){ctx.fillStyle='#a0a0b0';ctx.font='14px sans-serif';ctx.fillText('Noch keine Bot-Daten',w/2-60,h/2);return;}let ts=new Set();bn.forEach(b=>serverBotHistory[b].forEach(p=>ts.add(p.timestamp)));ts=[...ts].sort();if(ts.length<2){ctx.fillStyle='#a0a0b0';ctx.font='14px sans-serif';ctx.fillText('Warte auf Daten...',w/2-50,h/2);return;}let mx=1;bn.forEach(b=>serverBotHistory[b].forEach(p=>{if(p.count>mx)mx=p.count;}));const pd={t:20,r:20,b:40,l:50},cW=w-pd.l-pd.r,cH=h-pd.t-pd.b;ctx.strokeStyle='rgba(255,255,255,0.1)';ctx.lineWidth=1;for(let i=0;i<=4;i++){const y=pd.t+(cH/4)*i;ctx.beginPath();ctx.moveTo(pd.l,y);ctx.lineTo(w-pd.r,y);ctx.stroke();ctx.fillStyle='#a0a0b0';ctx.font='10px sans-serif';ctx.fillText(Math.round(mx-(mx/4)*i),5,y+4);}const step=Math.max(1,Math.floor(ts.length/6));ctx.fillStyle='#a0a0b0';ctx.font='10px sans-serif';for(let i=0;i<ts.length;i+=step){const t=ts[i].split(' ')[1]?.substring(0,5)||ts[i],x=pd.l+(cW/(ts.length-1))*i;ctx.fillText(t,x-15,h-10);}const lg=document.getElementById('serverChartLegend');lg.innerHTML='';bn.forEach((bot,idx)=>{const c=BOT_COLORS[idx%BOT_COLORS.length],pts=serverBotHistory[bot];ctx.strokeStyle=c;ctx.lineWidth=2;ctx.beginPath();pts.forEach((p,i)=>{const ti=ts.indexOf(p.timestamp),x=pd.l+(cW/(ts.length-1))*ti,y=pd.t+cH-(p.count/mx)*cH;i===0?ctx.moveTo(x,y):ctx.lineTo(x,y);});ctx.stroke();lg.innerHTML+='<div class="legend-item"><div class="legend-color" style="background:'+c+'"></div><span>'+bot+'</span></div>';});}
|
||||||
|
function renderServerCountryChart(){const cv=document.getElementById('serverCountryChart'),ctx=cv.getContext('2d'),ct=cv.parentElement,w=ct.clientWidth-32,h=230;cv.width=w;cv.height=h;ctx.clearRect(0,0,w,h);const cn=Object.keys(serverCountryHistory).sort((a,b)=>(serverCountryHistory[b][serverCountryHistory[b].length-1]?.count||0)-(serverCountryHistory[a][serverCountryHistory[a].length-1]?.count||0)).slice(0,10);if(cn.length===0){ctx.fillStyle='#a0a0b0';ctx.font='14px sans-serif';ctx.fillText('Noch keine Country-Daten',w/2-70,h/2);return;}let ts=new Set();cn.forEach(c=>serverCountryHistory[c].forEach(p=>ts.add(p.timestamp)));ts=[...ts].sort();if(ts.length<2){ctx.fillStyle='#a0a0b0';ctx.font='14px sans-serif';ctx.fillText('Warte auf Daten...',w/2-50,h/2);return;}let mx=1;cn.forEach(c=>serverCountryHistory[c].forEach(p=>{if(p.count>mx)mx=p.count;}));const pd={t:20,r:20,b:40,l:50},cW=w-pd.l-pd.r,cH=h-pd.t-pd.b;ctx.strokeStyle='rgba(255,255,255,0.1)';ctx.lineWidth=1;for(let i=0;i<=4;i++){const y=pd.t+(cH/4)*i;ctx.beginPath();ctx.moveTo(pd.l,y);ctx.lineTo(w-pd.r,y);ctx.stroke();ctx.fillStyle='#a0a0b0';ctx.font='10px sans-serif';ctx.fillText(Math.round(mx-(mx/4)*i),5,y+4);}const step=Math.max(1,Math.floor(ts.length/6));ctx.fillStyle='#a0a0b0';ctx.font='10px sans-serif';for(let i=0;i<ts.length;i+=step){const t=ts[i].split(' ')[1]?.substring(0,5)||ts[i],x=pd.l+(cW/(ts.length-1))*i;ctx.fillText(t,x-15,h-10);}const lg=document.getElementById('serverCountryChartLegend');lg.innerHTML='';cn.forEach((country,idx)=>{const clr=BOT_COLORS[idx%BOT_COLORS.length],pts=serverCountryHistory[country];ctx.strokeStyle=clr;ctx.lineWidth=2;ctx.beginPath();pts.forEach((p,i)=>{const ti=ts.indexOf(p.timestamp),x=pd.l+(cW/(ts.length-1))*ti,y=pd.t+cH-(p.count/mx)*cH;i===0?ctx.moveTo(x,y):ctx.lineTo(x,y);});ctx.stroke();lg.innerHTML+='<div class="legend-item"><div class="legend-color" style="background:'+clr+'"></div><span>'+country.toUpperCase()+'</span></div>';});}
|
||||||
|
let serverUpdateInterval=null;
|
||||||
|
function startServerHistoryUpdate(){if(serverUpdateInterval)clearInterval(serverUpdateInterval);serverBotHistory={};serverCountryHistory={};serverUpdateInterval=setInterval(()=>{if(!currentDetailServer||!document.getElementById('serverDetailModal').classList.contains('open')){clearInterval(serverUpdateInterval);serverUpdateInterval=null;return;}renderServerDetail();},updateInterval);}
|
||||||
|
async function serverActivateAll(){if(!currentDetailServer)return;const serverShops=getServerShops(currentDetailServer);const inactiveShops=serverShops.filter(s=>s.status!=='active');if(inactiveShops.length===0){toast('Alle Shops sind bereits aktiv','info');return;}if(!confirm(inactiveShops.length+' inaktive Shops auf diesem Server mit Bot-Mode aktivieren?'))return;toast('Aktiviere '+inactiveShops.length+' Shops...','info');const restartFpm=document.getElementById('autoFpmRestart').checked?'true':'false';for(const s of inactiveShops){const fd=new FormData();fd.append('domain',s.domain);fd.append('bot_mode','true');fd.append('bot_rate_limit','30');fd.append('bot_ban_duration','300');fd.append('country_mode','false');fd.append('monitor_only','false');fd.append('restart_fpm',restartFpm);await fetch('/api/shops/activate',{method:'POST',body:fd});await new Promise(r=>setTimeout(r,200));}toast('Alle Shops aktiviert','success');}
|
||||||
|
async function serverDeactivateAll(){if(!currentDetailServer)return;const serverShops=getServerShops(currentDetailServer);const activeShops=serverShops.filter(s=>s.status==='active');if(activeShops.length===0){toast('Keine aktiven Shops','info');return;}if(!confirm(activeShops.length+' aktive Shops auf diesem Server deaktivieren?'))return;toast('Deaktiviere '+activeShops.length+' Shops...','info');const restartFpm=document.getElementById('autoFpmRestart').checked?'true':'false';for(const s of activeShops){const fd=new FormData();fd.append('domain',s.domain);fd.append('restart_fpm',restartFpm);await fetch('/api/shops/deactivate',{method:'POST',body:fd});await new Promise(r=>setTimeout(r,200));}toast('Alle Shops deaktiviert','success');}
|
||||||
|
async function serverActivateInactive(mode){if(!currentDetailServer)return;const serverShops=getServerShops(currentDetailServer);const inactiveShops=serverShops.filter(s=>s.status!=='active');if(inactiveShops.length===0){toast('Keine inaktiven Shops','info');return;}const modeNames={bot:'🤖 Bot-Mode',monitor:'🔍 Monitor',dach:'🇩🇪🇦🇹🇨🇭 DACH',eu:'🇪🇺 EU+'};if(!confirm(inactiveShops.length+' inaktive Shops mit '+modeNames[mode]+' aktivieren?'))return;toast('Aktiviere '+inactiveShops.length+' Shops...','info');const restartFpm=document.getElementById('autoFpmRestart').checked?'true':'false';for(const s of inactiveShops){const fd=new FormData();fd.append('domain',s.domain);fd.append('restart_fpm',restartFpm);if(mode==='bot'){fd.append('bot_mode','true');fd.append('bot_rate_limit','30');fd.append('bot_ban_duration','300');fd.append('country_mode','false');fd.append('monitor_only','false');}else if(mode==='monitor'){fd.append('monitor_only','true');}else if(mode==='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==='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});await new Promise(r=>setTimeout(r,200));}toast('Shops aktiviert','success');}
|
||||||
connect();
|
connect();
|
||||||
setUpdateInterval(10000);
|
setUpdateInterval(10000);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user