# dashboard/callbacks/overview_callbacks.py import sys print(sys.path) import threading import dash from dash import Input, Output, State, MATCH, html, dcc from flask import session from utils.db import get_session # Diese Funktion muss eine SQLAlchemy-Session liefern! from utils.mqtt_client import publish, start_loop from config import ENV import dash_bootstrap_components as dbc import os from server.models import Client mqtt_thread_started = False SCREENSHOT_DIR = "received-screenshots" def ensure_mqtt_running(): global mqtt_thread_started if not mqtt_thread_started: thread = threading.Thread(target=start_loop, daemon=True) thread.start() mqtt_thread_started = True def get_latest_screenshot(client_uuid): prefix = f"{client_uuid}_" try: files = [f for f in os.listdir('..', SCREENSHOT_DIR) if f.startswith(prefix)] if not files: return "/assets/placeholder.png" latest = max(files, key=lambda x: os.path.getmtime(os.path.join('.', SCREENSHOT_DIR, x))) return f"/received-screenshots/{latest}" except Exception: return "/assets/placeholder.png" @dash.callback( Output("clients-cards-container", "children"), Input("interval-update", "n_intervals") ) def update_clients(n): # Auto-Login im Development-Modus if "role" not in session: if ENV == "development": session["role"] = "admin" else: return dcc.Location(id="redirect-login", href="/login") ensure_mqtt_running() session_db = get_session() clients = session_db.query(Client).all() session_db.close() cards = [] for client in clients: uuid = client.uuid screenshot = get_latest_screenshot(uuid) card = dbc.Card( [ dbc.CardHeader(client.location or client.hardware_hash), dbc.CardBody([ html.Img( src=screenshot, style={"width": "160px", "height": "90px", "object-fit": "cover"}, ), html.P(f"IP: {client.ip_address or '-'}", className="card-text"), html.P(f"Letzte Aktivität: {client.last_alive or '-'}", className="card-text"), dbc.ButtonGroup([ dbc.Button("Reload Page", color="primary", id={"type": "btn-reload", "index": uuid}, n_clicks=0), dbc.Button("Restart Client", color="danger", id={"type": "btn-restart", "index": uuid}, n_clicks=0), ], className="mt-2"), html.Div(id={"type": "restart-feedback", "index": uuid}), html.Div(id={"type": "reload-feedback", "index": uuid}), ]), ], className="mb-4", style={"width": "18rem"}, ) cards.append(dbc.Col(card, width=4)) return dbc.Row(cards) @dash.callback( Output({"type": "restart-feedback", "index": MATCH}, "children"), Input({"type": "btn-restart", "index": MATCH}, "n_clicks"), State({"type": "btn-restart", "index": MATCH}, "id") ) def on_restart(n_clicks, btn_id): if n_clicks and n_clicks > 0: cid = btn_id["index"] payload = '{"command": "restart"}' ok = publish(f"clients/{cid}/control", payload) return "Befehl gesendet." if ok else "Fehler beim Senden." return "" @dash.callback( Output({"type": "reload-feedback", "index": MATCH}, "children"), Input({"type": "btn-reload", "index": MATCH}, "n_clicks"), State({"type": "btn-reload", "index": MATCH}, "id") ) def on_reload(n_clicks, btn_id): if n_clicks and n_clicks > 0: cid = btn_id["index"] payload = '{"command": "reload"}' ok = publish(f"clients/{cid}/control", payload) return "Befehl gesendet." if ok else "Fehler beim Senden." return ""