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