feat(server): Proxmox-Server verwalten mit Fingerprint-Pinning

Server lassen sich anlegen, prüfen, bearbeiten und löschen. Proxmox
liefert ab Werk ein selbst signiertes Zertifikat; der Admin sieht es
vor dem Speichern und bestätigt den SHA-256-Fingerprint.

Der Pin wird in einem eigenen undici-Connector geprüft, sodass eine
fremde Gegenstelle das Token nie zu sehen bekommt. Weder Prüfung noch
Pin ist im Schema, im Client und per CHECK ausgeschlossen.

Fehler von Proxmox werden zu Ursache und Hinweis; bei 403 nennt der
Hinweis das fehlende Recht samt Pfad. Getestet gegen einen HTTPS-Mock
und im gebauten Stand auf Desktop und 390 px.

Closes #002

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 09:35:10 +02:00
co-authored by Claude Opus 5.5
parent 871bb3a77c
commit 48e2a5cb8f
43 changed files with 2847 additions and 39 deletions
+30 -5
View File
@@ -1,12 +1,16 @@
import type { Metadata } from "next";
import { Karte } from "@/components/karte";
import { Status } from "@/components/status";
import Link from "next/link";
import { seiteSchuetzen } from "@/server/auth/zugang";
import { serverListe } from "@/server/services/server";
import { ServerStatus } from "./server/server-status";
export const metadata: Metadata = { title: "Übersicht" };
export default async function Uebersicht() {
const benutzer = await seiteSchuetzen("lesen", "/");
const server = await serverListe();
return (
<div className="flex flex-col gap-6">
@@ -16,11 +20,32 @@ export default async function Uebersicht() {
</header>
<div className="grid gap-6 lg:grid-cols-2">
<Karte titel="Server">
<Status art="offen">Noch kein Proxmox-Server angelegt.</Status>
<p className="text-muted mt-2 text-sm">
Server lassen sich anlegen, sobald die Server-Verwaltung (#002) fertig ist.
</p>
<Karte
titel="Server"
aktion={
<Link href="/server" className="text-akzent text-sm">
Alle
</Link>
}
>
{server.length === 0 ? (
<Status art="offen">Noch kein Proxmox-Server angelegt.</Status>
) : (
<ul className="divide-linie flex flex-col divide-y">
{server.map((eintrag) => (
<li
key={eintrag.name}
className="flex items-center justify-between gap-3 py-2.5 first:pt-0 last:pb-0"
>
<Link href={`/server/${eintrag.name}`} className="min-w-0 hover:underline">
<span className="font-medium">{eintrag.titel}</span>{" "}
<span className="text-muted font-mono text-sm">{eintrag.name}</span>
</Link>
<ServerStatus server={eintrag} />
</li>
))}
</ul>
)}
</Karte>
<Karte titel="Letzte Deployments">
<Status art="offen">Noch keine Deployments.</Status>