jtl-wafi-dashboard.py aktualisiert
This commit is contained in:
@@ -113,7 +113,7 @@ class ShopData:
|
||||
|
||||
class DataStore:
|
||||
"""In-Memory Datenspeicher - Thread-safe durch asyncio."""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.agents: Dict[str, AgentData] = {}
|
||||
self.shops: Dict[str, ShopData] = {}
|
||||
@@ -121,11 +121,11 @@ class DataStore:
|
||||
self._password_hash: Optional[str] = None
|
||||
self._tokens: Dict[str, str] = {} # agent_id -> token
|
||||
self._load_persistent_data()
|
||||
|
||||
|
||||
def _load_persistent_data(self):
|
||||
"""Lädt persistente Daten (Passwort, Tokens)."""
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
|
||||
|
||||
# Config laden (Passwort)
|
||||
if os.path.exists(CONFIG_FILE):
|
||||
try:
|
||||
@@ -134,7 +134,7 @@ class DataStore:
|
||||
self._password_hash = config.get('password_hash')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# Tokens laden
|
||||
if os.path.exists(TOKENS_FILE):
|
||||
try:
|
||||
@@ -142,37 +142,37 @@ class DataStore:
|
||||
self._tokens = json.load(f)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def _save_config(self):
|
||||
"""Speichert Config."""
|
||||
with open(CONFIG_FILE, 'w') as f:
|
||||
json.dump({'password_hash': self._password_hash}, f)
|
||||
|
||||
|
||||
def _save_tokens(self):
|
||||
"""Speichert Tokens."""
|
||||
with open(TOKENS_FILE, 'w') as f:
|
||||
json.dump(self._tokens, f)
|
||||
|
||||
|
||||
# === Password ===
|
||||
def get_password_hash(self) -> Optional[str]:
|
||||
return self._password_hash
|
||||
|
||||
|
||||
def set_password(self, password: str):
|
||||
self._password_hash = hashlib.sha256(password.encode()).hexdigest()
|
||||
self._save_config()
|
||||
|
||||
|
||||
def verify_password(self, password: str) -> bool:
|
||||
if not self._password_hash:
|
||||
return False
|
||||
return hashlib.sha256(password.encode()).hexdigest() == self._password_hash
|
||||
|
||||
|
||||
# === Sessions ===
|
||||
def create_session(self, username: str) -> str:
|
||||
token = secrets.token_hex(32)
|
||||
expires = (utc_now() + timedelta(hours=24)).isoformat()
|
||||
self.sessions[token] = {'username': username, 'expires': expires}
|
||||
return token
|
||||
|
||||
|
||||
def verify_session(self, token: str) -> Optional[str]:
|
||||
if not token or token not in self.sessions:
|
||||
return None
|
||||
@@ -182,18 +182,18 @@ class DataStore:
|
||||
del self.sessions[token]
|
||||
return None
|
||||
return session['username']
|
||||
|
||||
|
||||
def delete_session(self, token: str):
|
||||
self.sessions.pop(token, None)
|
||||
|
||||
|
||||
# === Agent Tokens ===
|
||||
def get_agent_token(self, agent_id: str) -> Optional[str]:
|
||||
return self._tokens.get(agent_id)
|
||||
|
||||
|
||||
def set_agent_token(self, agent_id: str, token: str):
|
||||
self._tokens[agent_id] = token
|
||||
self._save_tokens()
|
||||
|
||||
|
||||
# === Agents ===
|
||||
def get_or_create_agent(self, agent_id: str, hostname: str) -> AgentData:
|
||||
if agent_id not in self.agents:
|
||||
@@ -208,10 +208,10 @@ class DataStore:
|
||||
self.agents[agent_id].token = self._tokens[agent_id]
|
||||
self.agents[agent_id].status = 'online'
|
||||
return self.agents[agent_id]
|
||||
|
||||
|
||||
def get_agent(self, agent_id: str) -> Optional[AgentData]:
|
||||
return self.agents.get(agent_id)
|
||||
|
||||
|
||||
def get_all_agents(self) -> List[Dict]:
|
||||
result = []
|
||||
for agent in self.agents.values():
|
||||
@@ -224,7 +224,7 @@ class DataStore:
|
||||
status = 'offline'
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
result.append({
|
||||
'id': agent.id,
|
||||
'hostname': agent.hostname,
|
||||
@@ -239,14 +239,14 @@ class DataStore:
|
||||
'shops_active': agent.shops_active
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
# === Shops ===
|
||||
def update_shop(self, agent_id: str, agent_hostname: str, shop_data: Dict) -> ShopData:
|
||||
domain = shop_data.get('domain')
|
||||
|
||||
|
||||
if domain not in self.shops:
|
||||
self.shops[domain] = ShopData(domain=domain, agent_id=agent_id)
|
||||
|
||||
|
||||
shop = self.shops[domain]
|
||||
shop.agent_id = agent_id
|
||||
shop.agent_hostname = agent_hostname
|
||||
@@ -260,7 +260,7 @@ class DataStore:
|
||||
shop.link11_ip = shop_data.get('link11_ip', '')
|
||||
shop.activated = shop_data.get('activated', '')
|
||||
shop.runtime_minutes = shop_data.get('runtime_minutes', 0)
|
||||
|
||||
|
||||
# Stats
|
||||
stats = shop_data.get('stats', {})
|
||||
if stats:
|
||||
@@ -273,20 +273,20 @@ class DataStore:
|
||||
shop.unique_bots = stats.get('unique_bots', 0)
|
||||
shop.top_bots = stats.get('top_bots', {})
|
||||
shop.top_ips = stats.get('top_ips', {})
|
||||
|
||||
|
||||
# History für Graph
|
||||
shop.history.append({
|
||||
'timestamp': utc_now_str(),
|
||||
'req_per_min': shop.req_per_min,
|
||||
'active_bans': shop.active_bans
|
||||
})
|
||||
|
||||
|
||||
return shop
|
||||
|
||||
|
||||
def update_shop_stats(self, domain: str, stats: Dict):
|
||||
if domain not in self.shops:
|
||||
return
|
||||
|
||||
|
||||
shop = self.shops[domain]
|
||||
shop.log_entries = stats.get('log_entries', shop.log_entries)
|
||||
shop.total_bans = stats.get('total_bans', shop.total_bans)
|
||||
@@ -297,16 +297,16 @@ class DataStore:
|
||||
shop.unique_bots = stats.get('unique_bots', shop.unique_bots)
|
||||
shop.top_bots = stats.get('top_bots', shop.top_bots)
|
||||
shop.top_ips = stats.get('top_ips', shop.top_ips)
|
||||
|
||||
|
||||
timestamp = utc_now_str()
|
||||
|
||||
|
||||
# Gesamt-History
|
||||
shop.history.append({
|
||||
'timestamp': timestamp,
|
||||
'req_per_min': shop.req_per_min,
|
||||
'active_bans': shop.active_bans
|
||||
})
|
||||
|
||||
|
||||
# Bot-History aktualisieren
|
||||
top_bots = stats.get('top_bots', {})
|
||||
for bot_name, count in top_bots.items():
|
||||
@@ -316,10 +316,10 @@ class DataStore:
|
||||
'timestamp': timestamp,
|
||||
'count': count
|
||||
})
|
||||
|
||||
|
||||
def get_shop(self, domain: str) -> Optional[ShopData]:
|
||||
return self.shops.get(domain)
|
||||
|
||||
|
||||
def get_all_shops(self) -> List[Dict]:
|
||||
result = []
|
||||
for shop in self.shops.values():
|
||||
@@ -350,22 +350,22 @@ class DataStore:
|
||||
}
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def get_shop_history(self, domain: str) -> Dict:
|
||||
shop = self.shops.get(domain)
|
||||
if not shop:
|
||||
return {'history': [], 'bot_history': {}}
|
||||
|
||||
|
||||
# Bot-History in JSON-serialisierbares Format
|
||||
bot_history = {}
|
||||
for bot_name, history in shop.bot_history.items():
|
||||
bot_history[bot_name] = list(history)
|
||||
|
||||
|
||||
return {
|
||||
'history': list(shop.history),
|
||||
'bot_history': bot_history
|
||||
}
|
||||
|
||||
|
||||
def get_top_shops(self, limit: int = 10, sort_by: str = 'req_per_min') -> List[Dict]:
|
||||
"""Gibt Top Shops sortiert nach req_per_min oder active_bans zurück."""
|
||||
shops_list = []
|
||||
@@ -378,30 +378,30 @@ class DataStore:
|
||||
'active_bans': shop.active_bans,
|
||||
'link11': shop.link11
|
||||
})
|
||||
|
||||
|
||||
# Sortieren
|
||||
if sort_by == 'active_bans':
|
||||
shops_list.sort(key=lambda x: x['active_bans'], reverse=True)
|
||||
else:
|
||||
shops_list.sort(key=lambda x: x['req_per_min'], reverse=True)
|
||||
|
||||
|
||||
if limit:
|
||||
return shops_list[:limit]
|
||||
return shops_list
|
||||
|
||||
|
||||
def get_stats(self) -> Dict:
|
||||
agents_online = sum(1 for a in self.agents.values()
|
||||
agents_online = sum(1 for a in self.agents.values()
|
||||
if a.approved and a.status == 'online')
|
||||
agents_pending = sum(1 for a in self.agents.values() if not a.approved)
|
||||
|
||||
|
||||
shops_active = sum(1 for s in self.shops.values() if s.status == 'active')
|
||||
shops_total = len(self.shops)
|
||||
shops_link11 = sum(1 for s in self.shops.values() if s.link11)
|
||||
shops_direct = shops_total - shops_link11
|
||||
|
||||
|
||||
req_per_min = sum(s.req_per_min for s in self.shops.values())
|
||||
active_bans = sum(s.active_bans for s in self.shops.values())
|
||||
|
||||
|
||||
return {
|
||||
'agents_online': agents_online,
|
||||
'agents_pending': agents_pending,
|
||||
@@ -423,12 +423,12 @@ store = DataStore()
|
||||
# =============================================================================
|
||||
def generate_ssl_certificate():
|
||||
os.makedirs(SSL_DIR, exist_ok=True)
|
||||
|
||||
|
||||
if os.path.exists(SSL_CERT) and os.path.exists(SSL_KEY):
|
||||
return
|
||||
|
||||
|
||||
print("🔐 Generiere SSL-Zertifikat...")
|
||||
|
||||
|
||||
try:
|
||||
subprocess.run([
|
||||
'openssl', 'req', '-x509', '-nodes',
|
||||
@@ -438,10 +438,10 @@ def generate_ssl_certificate():
|
||||
'-out', SSL_CERT,
|
||||
'-subj', '/CN=jtl-wafi/O=JTL-WAFi/C=DE'
|
||||
], check=True, capture_output=True)
|
||||
|
||||
|
||||
os.chmod(SSL_KEY, 0o600)
|
||||
os.chmod(SSL_CERT, 0o644)
|
||||
|
||||
|
||||
print(f"✅ SSL-Zertifikat generiert: {SSL_CERT}")
|
||||
except Exception as e:
|
||||
print(f"❌ SSL Fehler: {e}")
|
||||
@@ -456,7 +456,7 @@ class ConnectionManager:
|
||||
self.agent_connections: Dict[str, WebSocket] = {}
|
||||
self.browser_connections: Set[WebSocket] = set()
|
||||
self.agent_hostnames: Dict[str, str] = {}
|
||||
|
||||
|
||||
async def connect_agent(self, agent_id: str, hostname: str, websocket: WebSocket):
|
||||
# Alte Verbindung schließen
|
||||
if agent_id in self.agent_connections:
|
||||
@@ -464,36 +464,36 @@ class ConnectionManager:
|
||||
await self.agent_connections[agent_id].close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
self.agent_connections[agent_id] = websocket
|
||||
self.agent_hostnames[agent_id] = hostname
|
||||
print(f"✅ Agent verbunden: {hostname}")
|
||||
|
||||
|
||||
async def disconnect_agent(self, agent_id: str):
|
||||
self.agent_connections.pop(agent_id, None)
|
||||
hostname = self.agent_hostnames.pop(agent_id, "unknown")
|
||||
|
||||
|
||||
# Status updaten
|
||||
agent = store.get_agent(agent_id)
|
||||
if agent:
|
||||
agent.status = 'offline'
|
||||
agent.last_seen = utc_now_str()
|
||||
|
||||
|
||||
print(f"❌ Agent getrennt: {hostname}")
|
||||
|
||||
|
||||
await self.broadcast_to_browsers({
|
||||
'type': 'agent.offline',
|
||||
'data': {'agent_id': agent_id, 'hostname': hostname}
|
||||
})
|
||||
|
||||
|
||||
async def connect_browser(self, websocket: WebSocket):
|
||||
self.browser_connections.add(websocket)
|
||||
print(f"🌐 Browser verbunden (Total: {len(self.browser_connections)})")
|
||||
|
||||
|
||||
async def disconnect_browser(self, websocket: WebSocket):
|
||||
self.browser_connections.discard(websocket)
|
||||
print(f"🌐 Browser getrennt (Total: {len(self.browser_connections)})")
|
||||
|
||||
|
||||
async def send_to_agent(self, agent_id: str, message: Dict):
|
||||
ws = self.agent_connections.get(agent_id)
|
||||
if ws:
|
||||
@@ -501,7 +501,7 @@ class ConnectionManager:
|
||||
await ws.send_json(message)
|
||||
except Exception as e:
|
||||
print(f"Send to agent error: {e}")
|
||||
|
||||
|
||||
async def broadcast_to_browsers(self, message: Dict):
|
||||
dead = set()
|
||||
for ws in self.browser_connections:
|
||||
@@ -510,11 +510,11 @@ class ConnectionManager:
|
||||
except:
|
||||
dead.add(ws)
|
||||
self.browser_connections -= dead
|
||||
|
||||
|
||||
def get_agent_for_shop(self, domain: str) -> Optional[str]:
|
||||
shop = store.get_shop(domain)
|
||||
return shop.agent_id if shop else None
|
||||
|
||||
|
||||
def is_agent_connected(self, agent_id: str) -> bool:
|
||||
return agent_id in self.agent_connections
|
||||
|
||||
@@ -532,7 +532,7 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
|
||||
app = FastAPI(title="JTL-WAFi Dashboard", version=VERSION, lifespan=lifespan)
|
||||
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY, session_cookie="geoip_session", max_age=86400)
|
||||
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY, session_cookie="jtl_wafi_session", max_age=86400)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -550,14 +550,14 @@ async def get_current_user(request: Request) -> Optional[str]:
|
||||
async def agent_websocket(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
agent_id = None
|
||||
|
||||
|
||||
try:
|
||||
async for message in websocket.iter_text():
|
||||
try:
|
||||
data = json.loads(message)
|
||||
event_type = data.get('type')
|
||||
event_data = data.get('data', {})
|
||||
|
||||
|
||||
if event_type == 'agent.connect':
|
||||
agent_id = event_data.get('agent_id')
|
||||
hostname = event_data.get('hostname')
|
||||
@@ -565,7 +565,7 @@ async def agent_websocket(websocket: WebSocket):
|
||||
version = event_data.get('version', '')
|
||||
os_info = event_data.get('os_info', {})
|
||||
shops_summary = event_data.get('shops_summary', {})
|
||||
|
||||
|
||||
# Agent registrieren
|
||||
agent = store.get_or_create_agent(agent_id, hostname)
|
||||
agent.hostname = hostname
|
||||
@@ -574,16 +574,16 @@ async def agent_websocket(websocket: WebSocket):
|
||||
agent.last_seen = utc_now_str()
|
||||
agent.shops_total = shops_summary.get('total', 0)
|
||||
agent.shops_active = shops_summary.get('active', 0)
|
||||
|
||||
|
||||
# Token prüfen
|
||||
stored_token = store.get_agent_token(agent_id)
|
||||
if stored_token and token == stored_token:
|
||||
agent.approved = True
|
||||
agent.token = stored_token
|
||||
agent.status = 'online'
|
||||
|
||||
|
||||
await manager.connect_agent(agent_id, hostname, websocket)
|
||||
|
||||
|
||||
# Browser informieren
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'agent.online' if agent.approved else 'agent.pending',
|
||||
@@ -597,21 +597,21 @@ async def agent_websocket(websocket: WebSocket):
|
||||
'shops_active': shops_summary.get('active', 0)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
# Token senden wenn approved
|
||||
if agent.approved:
|
||||
await websocket.send_json({
|
||||
'type': 'auth.approved',
|
||||
'data': {'token': agent.token}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'agent.heartbeat':
|
||||
if agent_id:
|
||||
agent = store.get_agent(agent_id)
|
||||
if agent:
|
||||
system = event_data.get('system', {})
|
||||
shops_summary = event_data.get('shops_summary', {})
|
||||
|
||||
|
||||
agent.last_seen = utc_now_str()
|
||||
agent.load_1m = system.get('load_1m', 0)
|
||||
agent.load_5m = system.get('load_5m', 0)
|
||||
@@ -619,7 +619,7 @@ async def agent_websocket(websocket: WebSocket):
|
||||
agent.uptime_seconds = system.get('uptime_seconds', 0)
|
||||
agent.shops_total = shops_summary.get('total', 0)
|
||||
agent.shops_active = shops_summary.get('active', 0)
|
||||
|
||||
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'agent.update',
|
||||
'data': {
|
||||
@@ -629,16 +629,16 @@ async def agent_websocket(websocket: WebSocket):
|
||||
'shops_summary': shops_summary
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'shop.full_update':
|
||||
if agent_id:
|
||||
agent = store.get_agent(agent_id)
|
||||
hostname = agent.hostname if agent else ''
|
||||
shops = event_data.get('shops', [])
|
||||
|
||||
|
||||
for shop_data in shops:
|
||||
store.update_shop(agent_id, hostname, shop_data)
|
||||
|
||||
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'shop.full_update',
|
||||
'data': {
|
||||
@@ -647,42 +647,42 @@ async def agent_websocket(websocket: WebSocket):
|
||||
'shops': shops
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'shop.stats':
|
||||
if agent_id:
|
||||
domain = event_data.get('domain')
|
||||
stats = event_data.get('stats', {})
|
||||
|
||||
|
||||
store.update_shop_stats(domain, stats)
|
||||
|
||||
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'shop.stats',
|
||||
'data': {'domain': domain, 'stats': stats}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'log.entry':
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'log.entry',
|
||||
'data': event_data
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'bot.banned':
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'bot.banned',
|
||||
'data': event_data
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'command.result':
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'command.result',
|
||||
'data': event_data
|
||||
})
|
||||
|
||||
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Agent message error: {e}")
|
||||
|
||||
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
except Exception as e:
|
||||
@@ -699,7 +699,7 @@ async def agent_websocket(websocket: WebSocket):
|
||||
async def dashboard_websocket(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
await manager.connect_browser(websocket)
|
||||
|
||||
|
||||
try:
|
||||
# Initial state senden
|
||||
await websocket.send_json({
|
||||
@@ -710,13 +710,13 @@ async def dashboard_websocket(websocket: WebSocket):
|
||||
'stats': store.get_stats()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
async for message in websocket.iter_text():
|
||||
try:
|
||||
data = json.loads(message)
|
||||
event_type = data.get('type')
|
||||
event_data = data.get('data', {})
|
||||
|
||||
|
||||
if event_type == 'log.subscribe':
|
||||
domain = event_data.get('shop')
|
||||
agent_id = manager.get_agent_for_shop(domain)
|
||||
@@ -725,7 +725,7 @@ async def dashboard_websocket(websocket: WebSocket):
|
||||
'type': 'log.subscribe',
|
||||
'data': {'shop': domain}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'log.unsubscribe':
|
||||
domain = event_data.get('shop')
|
||||
agent_id = manager.get_agent_for_shop(domain)
|
||||
@@ -734,7 +734,7 @@ async def dashboard_websocket(websocket: WebSocket):
|
||||
'type': 'log.unsubscribe',
|
||||
'data': {'shop': domain}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'get_shop_history':
|
||||
domain = event_data.get('domain')
|
||||
data = store.get_shop_history(domain)
|
||||
@@ -742,7 +742,7 @@ async def dashboard_websocket(websocket: WebSocket):
|
||||
'type': 'shop_history',
|
||||
'data': {'domain': domain, **data}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'get_top_shops':
|
||||
sort_by = event_data.get('sort_by', 'req_per_min')
|
||||
limit = event_data.get('limit', 10)
|
||||
@@ -751,7 +751,7 @@ async def dashboard_websocket(websocket: WebSocket):
|
||||
'type': 'top_shops',
|
||||
'data': {'shops': shops, 'sort_by': sort_by}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'get_all_shops_sorted':
|
||||
sort_by = event_data.get('sort_by', 'req_per_min')
|
||||
shops = store.get_top_shops(limit=None, sort_by=sort_by)
|
||||
@@ -759,7 +759,7 @@ async def dashboard_websocket(websocket: WebSocket):
|
||||
'type': 'all_shops_sorted',
|
||||
'data': {'shops': shops, 'sort_by': sort_by}
|
||||
})
|
||||
|
||||
|
||||
elif event_type == 'refresh':
|
||||
await websocket.send_json({
|
||||
'type': 'refresh',
|
||||
@@ -769,10 +769,10 @@ async def dashboard_websocket(websocket: WebSocket):
|
||||
'stats': store.get_stats()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"Browser message error: {e}")
|
||||
|
||||
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
except Exception as e:
|
||||
@@ -833,30 +833,30 @@ async def approve_agent(agent_id: str, request: Request):
|
||||
user = await get_current_user(request)
|
||||
if not user:
|
||||
raise HTTPException(401)
|
||||
|
||||
|
||||
agent = store.get_agent(agent_id)
|
||||
if not agent:
|
||||
raise HTTPException(404, "Agent nicht gefunden")
|
||||
|
||||
|
||||
# Token generieren
|
||||
token = secrets.token_hex(32)
|
||||
agent.approved = True
|
||||
agent.token = token
|
||||
agent.status = 'online'
|
||||
store.set_agent_token(agent_id, token)
|
||||
|
||||
|
||||
# Token an Agent senden
|
||||
if manager.is_agent_connected(agent_id):
|
||||
await manager.send_to_agent(agent_id, {
|
||||
'type': 'auth.approved',
|
||||
'data': {'token': token}
|
||||
})
|
||||
|
||||
|
||||
await manager.broadcast_to_browsers({
|
||||
'type': 'agent.approved',
|
||||
'data': {'agent_id': agent_id}
|
||||
})
|
||||
|
||||
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@@ -873,14 +873,14 @@ async def activate_shop(
|
||||
user = await get_current_user(request)
|
||||
if not user:
|
||||
raise HTTPException(401)
|
||||
|
||||
|
||||
# String "true"/"false" zu Boolean konvertieren
|
||||
is_monitor_only = bot_monitor_only.lower() in ('true', '1', 'yes', 'on')
|
||||
|
||||
|
||||
agent_id = manager.get_agent_for_shop(domain)
|
||||
if not agent_id or not manager.is_agent_connected(agent_id):
|
||||
return JSONResponse({"success": False, "error": "Agent nicht verbunden"})
|
||||
|
||||
|
||||
command_id = secrets.token_hex(8)
|
||||
await manager.send_to_agent(agent_id, {
|
||||
'type': 'command.activate',
|
||||
@@ -894,7 +894,7 @@ async def activate_shop(
|
||||
'bot_monitor_only': is_monitor_only if mode == 'bot' else False
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return {"success": True, "command_id": command_id}
|
||||
|
||||
|
||||
@@ -903,17 +903,17 @@ async def deactivate_shop(request: Request, domain: str = Form(...)):
|
||||
user = await get_current_user(request)
|
||||
if not user:
|
||||
raise HTTPException(401)
|
||||
|
||||
|
||||
agent_id = manager.get_agent_for_shop(domain)
|
||||
if not agent_id or not manager.is_agent_connected(agent_id):
|
||||
return JSONResponse({"success": False, "error": "Agent nicht verbunden"})
|
||||
|
||||
|
||||
command_id = secrets.token_hex(8)
|
||||
await manager.send_to_agent(agent_id, {
|
||||
'type': 'command.deactivate',
|
||||
'data': {'command_id': command_id, 'shop': domain}
|
||||
})
|
||||
|
||||
|
||||
return {"success": True, "command_id": command_id}
|
||||
|
||||
|
||||
@@ -930,26 +930,26 @@ async def bulk_activate(
|
||||
user = await get_current_user(request)
|
||||
if not user:
|
||||
raise HTTPException(401)
|
||||
|
||||
|
||||
# String "true"/"false"/"on" zu Boolean konvertieren
|
||||
is_monitor_only = bot_monitor_only.lower() in ('true', '1', 'yes', 'on')
|
||||
|
||||
|
||||
activated = 0
|
||||
shops = store.get_all_shops()
|
||||
|
||||
|
||||
for shop in shops:
|
||||
if shop['status'] == 'active':
|
||||
continue
|
||||
|
||||
|
||||
if filter_type == 'direct' and shop['link11']:
|
||||
continue
|
||||
if filter_type == 'link11' and not shop['link11']:
|
||||
continue
|
||||
|
||||
|
||||
agent_id = shop.get('agent_id')
|
||||
if not agent_id or not manager.is_agent_connected(agent_id):
|
||||
continue
|
||||
|
||||
|
||||
command_id = secrets.token_hex(8)
|
||||
await manager.send_to_agent(agent_id, {
|
||||
'type': 'command.activate',
|
||||
@@ -964,11 +964,11 @@ async def bulk_activate(
|
||||
}
|
||||
})
|
||||
activated += 1
|
||||
|
||||
|
||||
# Kleine Pause um nicht zu überlasten
|
||||
if activated % 5 == 0:
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
|
||||
return {"success": True, "activated": activated}
|
||||
|
||||
|
||||
@@ -977,33 +977,33 @@ async def bulk_deactivate(request: Request, filter_type: str = Form("all")):
|
||||
user = await get_current_user(request)
|
||||
if not user:
|
||||
raise HTTPException(401)
|
||||
|
||||
|
||||
deactivated = 0
|
||||
shops = store.get_all_shops()
|
||||
|
||||
|
||||
for shop in shops:
|
||||
if shop['status'] != 'active':
|
||||
continue
|
||||
|
||||
|
||||
if filter_type == 'direct' and shop['link11']:
|
||||
continue
|
||||
if filter_type == 'link11' and not shop['link11']:
|
||||
continue
|
||||
|
||||
|
||||
agent_id = shop.get('agent_id')
|
||||
if not agent_id or not manager.is_agent_connected(agent_id):
|
||||
continue
|
||||
|
||||
|
||||
command_id = secrets.token_hex(8)
|
||||
await manager.send_to_agent(agent_id, {
|
||||
'type': 'command.deactivate',
|
||||
'data': {'command_id': command_id, 'shop': shop['domain']}
|
||||
})
|
||||
deactivated += 1
|
||||
|
||||
|
||||
if deactivated % 5 == 0:
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
|
||||
return {"success": True, "deactivated": deactivated}
|
||||
|
||||
|
||||
@@ -1017,14 +1017,14 @@ async def change_password(
|
||||
user = await get_current_user(request)
|
||||
if not user:
|
||||
raise HTTPException(401)
|
||||
|
||||
|
||||
if not store.verify_password(current):
|
||||
return {"success": False, "error": "Aktuelles Passwort falsch"}
|
||||
if new_pw != confirm:
|
||||
return {"success": False, "error": "Neue Passwörter stimmen nicht überein"}
|
||||
if len(new_pw) < 8:
|
||||
return {"success": False, "error": "Mindestens 8 Zeichen"}
|
||||
|
||||
|
||||
store.set_password(new_pw)
|
||||
return {"success": True}
|
||||
|
||||
@@ -1034,7 +1034,7 @@ async def get_shop_history_api(domain: str, request: Request):
|
||||
user = await get_current_user(request)
|
||||
if not user:
|
||||
raise HTTPException(401)
|
||||
|
||||
|
||||
data = store.get_shop_history(domain)
|
||||
return {"domain": domain, **data}
|
||||
|
||||
@@ -1368,27 +1368,27 @@ def main():
|
||||
parser.add_argument("--no-ssl", action="store_true")
|
||||
parser.add_argument("--install-service", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
if args.install_service:
|
||||
create_systemd_service()
|
||||
return
|
||||
|
||||
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
|
||||
|
||||
print("=" * 60)
|
||||
print(f"🌍 JTL-WAFi Dashboard v{VERSION} (In-Memory)")
|
||||
print("=" * 60)
|
||||
print(f"Host: {args.host}:{args.port}")
|
||||
print(f"SSL: {'Nein' if args.no_ssl else 'Ja'}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
ssl_config = {}
|
||||
if not args.no_ssl:
|
||||
generate_ssl_certificate()
|
||||
ssl_config = {"ssl_certfile": SSL_CERT, "ssl_keyfile": SSL_KEY}
|
||||
|
||||
|
||||
uvicorn.run(app, host=args.host, port=args.port, **ssl_config, log_level="info")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user