diff --git a/routers/admin_technik.py b/routers/admin_technik.py index abda331..8a69eec 100644 --- a/routers/admin_technik.py +++ b/routers/admin_technik.py @@ -1,5 +1,7 @@ -from fastapi import APIRouter, Depends, Request +from fastapi import APIRouter, Depends, Form, Request +from fastapi.responses import RedirectResponse +from anode_client import anode_request from auth import require_admin from templating import templates @@ -8,7 +10,173 @@ router = APIRouter(prefix="/admin/technik") @router.get("") -def index(request: Request, user: dict = Depends(require_admin)): +def index(user: dict = Depends(require_admin)): + return RedirectResponse("/admin/technik/merkmale", status_code=303) + + +# --- Merkmale --- + +@router.get("/merkmale") +def merkmale_liste(request: Request, user: dict = Depends(require_admin)): + merkmale_result = anode_request("GET", "/api/v1/merkmale") + merkmale = merkmale_result.get("merkmale", []) if merkmale_result.get("success") else [] + + kategorien_result = anode_request("GET", "/api/v1/kategorien") + kategorien = kategorien_result.get("kategorien", []) if kategorien_result.get("success") else [] + return templates.TemplateResponse( - request, "admin/platzhalter.html", {"user": user, "titel": "Technik/Datenbank"} + request, + "admin/technik_merkmale.html", + {"user": user, "merkmale": merkmale, "kategorien": kategorien}, + ) + + +@router.post("/merkmale") +def merkmal_anlegen( + key: str = Form(...), name: str = Form(...), description: str = Form(default=""), + user: dict = Depends(require_admin), +): + anode_request( + "POST", "/api/v1/merkmale", + json={"key": key, "name": name, "description": description or None}, + ) + + return RedirectResponse("/admin/technik/merkmale", status_code=303) + + +@router.post("/merkmale/{merkmal_id}") +def merkmal_aktualisieren( + merkmal_id: str, + name: str = Form(...), + description: str = Form(default=""), + kategorie_id: str = Form(default=""), + im_auftragskatalog: str = Form(default=""), + user: dict = Depends(require_admin), +): + anode_request( + "PATCH", f"/api/v1/merkmale/{merkmal_id}", + json={ + "name": name, + "description": description or None, + "kategorie_id": kategorie_id or None, + "im_auftragskatalog": im_auftragskatalog == "on", + }, + ) + + return RedirectResponse("/admin/technik/merkmale", status_code=303) + + +# --- Kategorien --- + +@router.get("/kategorien") +def kategorien_liste(request: Request, user: dict = Depends(require_admin)): + result = anode_request("GET", "/api/v1/kategorien") + kategorien = result.get("kategorien", []) if result.get("success") else [] + + return templates.TemplateResponse( + request, "admin/technik_kategorien.html", {"user": user, "kategorien": kategorien} + ) + + +@router.post("/kategorien") +def kategorie_anlegen( + key: str = Form(...), name: str = Form(...), description: str = Form(default=""), + sort_order: int = Form(default=0), user: dict = Depends(require_admin), +): + anode_request( + "POST", "/api/v1/kategorien", + json={"key": key, "name": name, "description": description or None, "sort_order": sort_order}, + ) + + return RedirectResponse("/admin/technik/kategorien", status_code=303) + + +# --- Workspaces --- + +@router.get("/workspaces") +def workspaces_liste(request: Request, user: dict = Depends(require_admin)): + result = anode_request("GET", "/api/v1/workspaces") + workspaces = result.get("workspaces", []) if result.get("success") else [] + + orgs_result = anode_request("GET", "/api/v1/organizations") + organizations = orgs_result.get("organizations", []) if orgs_result.get("success") else [] + + return templates.TemplateResponse( + request, + "admin/technik_workspaces.html", + {"user": user, "workspaces": workspaces, "organizations": organizations}, + ) + + +@router.post("/workspaces") +def workspace_anlegen( + key: str = Form(...), name: str = Form(...), description: str = Form(default=""), + organization_id: str = Form(default=""), user: dict = Depends(require_admin), +): + anode_request( + "POST", "/api/v1/workspaces", + json={ + "key": key, "name": name, "description": description or None, + "organization_id": organization_id or None, + }, + ) + + return RedirectResponse("/admin/technik/workspaces", status_code=303) + + +@router.get("/workspaces/{workspace_id}") +def workspace_detail(request: Request, workspace_id: str, user: dict = Depends(require_admin)): + result = anode_request("GET", f"/api/v1/workspaces/{workspace_id}") + workspace = result.get("workspace") if result.get("success") else None + + merkmale_result = anode_request("GET", "/api/v1/merkmale") + alle_merkmale = merkmale_result.get("merkmale", []) if merkmale_result.get("success") else [] + + komponierte_ids = {m["id"] for m in workspace["merkmale"]} if workspace else set() + verfuegbare_merkmale = [m for m in alle_merkmale if m["id"] not in komponierte_ids] + + return templates.TemplateResponse( + request, + "admin/technik_workspace_detail.html", + { + "user": user, + "workspace": workspace, + "workspace_id": workspace_id, + "verfuegbare_merkmale": verfuegbare_merkmale, + }, + ) + + +@router.post("/workspaces/{workspace_id}/merkmale") +def workspace_merkmal_hinzufuegen( + workspace_id: str, merkmal_id: str = Form(...), user: dict = Depends(require_admin) +): + anode_request("POST", f"/api/v1/workspaces/{workspace_id}/merkmale/{merkmal_id}") + + return RedirectResponse(f"/admin/technik/workspaces/{workspace_id}", status_code=303) + + +@router.post("/workspaces/{workspace_id}/merkmale/{merkmal_id}/entfernen") +def workspace_merkmal_entfernen( + workspace_id: str, merkmal_id: str, user: dict = Depends(require_admin) +): + anode_request("DELETE", f"/api/v1/workspaces/{workspace_id}/merkmale/{merkmal_id}") + + return RedirectResponse(f"/admin/technik/workspaces/{workspace_id}", status_code=303) + + +# --- Backends / Blueprints (nur Anzeige) --- + +@router.get("/backends") +def backends_liste(request: Request, user: dict = Depends(require_admin)): + backends_result = anode_request("GET", "/api/v1/backends") + backends = backends_result.get("backends", []) if backends_result.get("success") else [] + + blueprints_result = anode_request("GET", "/api/v1/blueprints") + blueprints = blueprints_result.get("blueprints", []) if blueprints_result.get("success") else [] + + return templates.TemplateResponse( + request, + "admin/technik_backends.html", + {"user": user, "backends": backends, "blueprints": blueprints}, ) diff --git a/templates/admin/technik_backends.html b/templates/admin/technik_backends.html new file mode 100644 index 0000000..e867b5b --- /dev/null +++ b/templates/admin/technik_backends.html @@ -0,0 +1,36 @@ +{% extends "admin/layout.html" %} +{% block title %}Backends — Tuxflotte Admin{% endblock %} +{% block admin_content %} + +

Backends

+

Nur Anzeige — Änderungen brauchen echten Ansible-Rollen-Code, nicht nur einen Datenbankeintrag.

+
+ + + + {% for b in backends %} + + {% endfor %} + +
KeyNameInstaller-TypVersion
{{ b.key }}{{ b.name }}{{ b.installer_type }}{{ b.version or "—" }}
+
+ +

Blueprints

+
+ + + + {% for bp in blueprints %} + + {% endfor %} + +
MerkmalBackendAnsible-Rolle
{{ bp.merkmal }}{{ bp.backend }}{{ bp.ansible_role }}
+
+{% endblock %} diff --git a/templates/admin/technik_kategorien.html b/templates/admin/technik_kategorien.html new file mode 100644 index 0000000..8aadc7d --- /dev/null +++ b/templates/admin/technik_kategorien.html @@ -0,0 +1,39 @@ +{% extends "admin/layout.html" %} +{% block title %}Kategorien — Tuxflotte Admin{% endblock %} +{% block admin_content %} + +

Kategorien

+ +
+ + + + {% for k in kategorien %} + + + + + + {% endfor %} + +
KeyNameReihenfolge
{{ k.key }}{{ k.name }}{{ k.sort_order }}
+
+ +
+Neue Kategorie anlegen +
+ + + + + +
+
+{% endblock %} diff --git a/templates/admin/technik_merkmale.html b/templates/admin/technik_merkmale.html new file mode 100644 index 0000000..386f9c1 --- /dev/null +++ b/templates/admin/technik_merkmale.html @@ -0,0 +1,61 @@ +{% extends "admin/layout.html" %} +{% block title %}Merkmale — Tuxflotte Admin{% endblock %} +{% block admin_content %} + +

Merkmale

+ +
+ + + + {% for m in merkmale %} + + + + + + + + {% endfor %} + +
KeyNameKategorieKatalogfähig
{{ m.key }}{{ m.name }}{{ m.kategorie_name or "—" }}{{ "ja" if m.im_auftragskatalog else "nein" }} +
+ Bearbeiten +
+ + + + + +
+
+
+
+ +
+Neues Merkmal anlegen +
+ + + + +
+
+{% endblock %} diff --git a/templates/admin/technik_workspace_detail.html b/templates/admin/technik_workspace_detail.html new file mode 100644 index 0000000..4066fe8 --- /dev/null +++ b/templates/admin/technik_workspace_detail.html @@ -0,0 +1,46 @@ +{% extends "admin/layout.html" %} +{% block title %}{{ workspace.name if workspace else workspace_id }} — Tuxflotte Admin{% endblock %} +{% block admin_content %} +

← Workspaces

+{% if not workspace %} +

Workspace wurde nicht gefunden.

+{% else %} +

{{ workspace.name }}

+

{{ workspace.organization_name or "Global" }}{% if workspace.description %} — {{ workspace.description }}{% endif %}

+ +

Zusammensetzung

+{% if not workspace.merkmale %} +

Noch keine Merkmale zugeordnet.

+{% else %} +
+ + + {% for m in workspace.merkmale %} + + + + + {% endfor %} + +
{{ m.name }} {{ m.key }} +
+ +
+
+
+{% endif %} + +{% if verfuegbare_merkmale %} +
+ + +
+{% endif %} +{% endif %} +{% endblock %} diff --git a/templates/admin/technik_workspaces.html b/templates/admin/technik_workspaces.html new file mode 100644 index 0000000..9c20b28 --- /dev/null +++ b/templates/admin/technik_workspaces.html @@ -0,0 +1,47 @@ +{% extends "admin/layout.html" %} +{% block title %}Workspaces — Tuxflotte Admin{% endblock %} +{% block admin_content %} + +

Workspaces

+ +
+ + + + {% for w in workspaces %} + + + + + + + {% endfor %} + +
KeyNameOrganisation
{{ w.key }}{{ w.name }}{{ w.organization_name or "Global" }}Zusammensetzung
+
+ +
+Neuen Workspace anlegen +
+ + + + + +
+
+{% endblock %}