diff --git a/routers/geraete.py b/routers/geraete.py index 0163371..6ceb877 100644 --- a/routers/geraete.py +++ b/routers/geraete.py @@ -3,6 +3,7 @@ from fastapi.responses import RedirectResponse from anode_client import anode_request from auth import get_current_user +from routers.gruppen import fetch_gruppen from templating import templates @@ -83,13 +84,64 @@ def geraete_liste(request: Request, user: dict = Depends(get_current_user)): oe_result = anode_request("GET", f"/api/v1/organizations/{user.organization_id}/organisationseinheiten") organisationseinheiten = oe_result.get("organisationseinheiten", []) if oe_result.get("success") else [] + gruppen = fetch_gruppen(user.organization_id) + + # anode bietet keinen direkten "Gruppen eines Geräts, für alle Geräte + # auf einmal"-Endpunkt an - daher hier je Gerät einzeln abfragen + # (gleiches N+1-Muster wie admin_kunden.py:gruppe_detail(), nur in + # umgekehrter Richtung: dort Geräte einer Gruppe, hier Gruppen eines + # Geräts). Bei der aktuellen Kundenzahl/Gerätezahl unproblematisch. + for device in devices: + dg_result = anode_request("GET", f"/api/v1/devices/{device['id']}/gruppen") + device_gruppen = dg_result.get("gruppen", []) if dg_result.get("success") else [] + device["gruppen_ids"] = {g["id"] for g in device_gruppen} + device["gruppen_namen"] = [g["name"] for g in device_gruppen] + return templates.TemplateResponse( request, "geraete_liste.html", - {"user": user, "devices": devices, "organisationseinheiten": organisationseinheiten}, + { + "user": user, + "devices": devices, + "organisationseinheiten": organisationseinheiten, + "gruppen": gruppen, + }, ) +@router.post("/geraete/{device_id}/gruppen") +async def gruppen_aktualisieren(request: Request, device_id: str, user: dict = Depends(get_current_user)): + """ + Setzt die Gruppen-Mitgliedschaft eines Geräts direkt aus der + Geräteliste heraus (Checkbox-Auswahl aller Gruppen der Organisation), + als Gegenstück zur bestehenden Verwaltung von der Gruppen-Seite aus + (/gruppen/{id}, siehe ADR-0012-Nachtrag). Berechnet die Differenz + zwischen aktueller und gewünschter Mitgliedschaft und ruft die + bestehenden anode-Endpunkte entsprechend auf - kein neuer + Bulk-Endpunkt nötig. + """ + + if find_device_in_organization(device_id, user.organization_id) is None: + raise HTTPException(status_code=404, detail="Gerät wurde nicht gefunden.") + + form = await request.form() + ausgewaehlt = set(form.getlist("gruppen_ids")) + + aktuell_result = anode_request("GET", f"/api/v1/devices/{device_id}/gruppen") + aktuell = ( + {g["id"] for g in aktuell_result.get("gruppen", [])} + if aktuell_result.get("success") else set() + ) + + for gruppe_id in ausgewaehlt - aktuell: + anode_request("POST", f"/api/v1/devices/{device_id}/gruppen/{gruppe_id}") + + for gruppe_id in aktuell - ausgewaehlt: + anode_request("DELETE", f"/api/v1/devices/{device_id}/gruppen/{gruppe_id}") + + return RedirectResponse("/geraete", status_code=303) + + @router.get("/geraete/{device_id}") def auftragskatalog_ansicht(request: Request, device_id: str, user: dict = Depends(get_current_user)): device = find_device_in_organization(device_id, user.organization_id) diff --git a/templates/geraete_liste.html b/templates/geraete_liste.html index 8a4c7f8..162479c 100644 --- a/templates/geraete_liste.html +++ b/templates/geraete_liste.html @@ -8,7 +8,7 @@
- + {% for device in devices %} @@ -16,6 +16,7 @@ +
HostnameKennungOELetzter Check-inStatus
HostnameKennungOEGruppenLetzter Check-inStatus
{{ device.hostname or "—" }} {{ device.device_fingerprint }} {{ device.oe_name or "—" }}{{ device.gruppen_namen | join(", ") if device.gruppen_namen else "—" }} {{ device.agent_last_checkin or "noch nie" }} {% if device.deprovisioned_at %} @@ -52,6 +53,24 @@ +
  • + {% if gruppen %} +
    +
    + Gruppen + {% for gruppe in gruppen %} + + {% endfor %} +
    + +
    + {% else %} + Keine Gruppen vorhanden. Gruppe anlegen + {% endif %} +
  • Gerät löschen