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:
@@ -0,0 +1,29 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { aufruferAdresse } from "@/lib/adressen";
|
||||
import { api, jsonLesen } from "@/server/api";
|
||||
import { rechtErforderlich } from "@/server/auth/zugang";
|
||||
import { serverAendern, serverHolen, serverLoeschen, serverSicht } from "@/server/services/server";
|
||||
|
||||
type Kontext = { params: Promise<{ name: string }> };
|
||||
|
||||
export const GET = api(async (anfrage: Request, { params }: Kontext) => {
|
||||
await rechtErforderlich(anfrage, "lesen");
|
||||
return NextResponse.json(serverSicht(await serverHolen((await params).name)));
|
||||
});
|
||||
|
||||
export const PATCH = api(async (anfrage: Request, { params }: Kontext) => {
|
||||
const aufrufer = await rechtErforderlich(anfrage, "serverVerwalten");
|
||||
const server = await serverAendern(
|
||||
(await params).name,
|
||||
await jsonLesen(anfrage),
|
||||
aufrufer,
|
||||
aufruferAdresse(anfrage.headers),
|
||||
);
|
||||
return NextResponse.json(server);
|
||||
});
|
||||
|
||||
export const DELETE = api(async (anfrage: Request, { params }: Kontext) => {
|
||||
const aufrufer = await rechtErforderlich(anfrage, "serverVerwalten");
|
||||
await serverLoeschen((await params).name, aufrufer, aufruferAdresse(anfrage.headers));
|
||||
return new NextResponse(null, { status: 204 });
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { aufruferAdresse } from "@/lib/adressen";
|
||||
import { api } from "@/server/api";
|
||||
import { rechtErforderlich } from "@/server/auth/zugang";
|
||||
import { sshSchluesselErzeugen } from "@/server/services/server";
|
||||
|
||||
type Kontext = { params: Promise<{ name: string }> };
|
||||
|
||||
/** Erzeugt oder erneuert das SSH-Schlüsselpaar; der private Teil verlässt den Server nie. */
|
||||
export const POST = api(async (anfrage: Request, { params }: Kontext) => {
|
||||
const aufrufer = await rechtErforderlich(anfrage, "serverVerwalten");
|
||||
return NextResponse.json(
|
||||
await sshSchluesselErzeugen((await params).name, aufrufer, aufruferAdresse(anfrage.headers)),
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { api, jsonLesen } from "@/server/api";
|
||||
import { rechtErforderlich } from "@/server/auth/zugang";
|
||||
import { serverPruefen } from "@/server/services/server";
|
||||
|
||||
/**
|
||||
* Prüft Angaben vor dem Speichern: Zertifikat zum Bestätigen holen, optional ein erster Aufruf.
|
||||
* Nur für Admins – der Endpunkt baut Verbindungen zu beliebigen Adressen auf.
|
||||
*/
|
||||
export const POST = api(async (anfrage: Request) => {
|
||||
await rechtErforderlich(anfrage, "serverVerwalten");
|
||||
return NextResponse.json(await serverPruefen(await jsonLesen(anfrage)));
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { aufruferAdresse } from "@/lib/adressen";
|
||||
import { api, jsonLesen } from "@/server/api";
|
||||
import { rechtErforderlich } from "@/server/auth/zugang";
|
||||
import { serverAnlegen, serverListe } from "@/server/services/server";
|
||||
|
||||
export const GET = api(async (anfrage: Request) => {
|
||||
await rechtErforderlich(anfrage, "lesen");
|
||||
return NextResponse.json(await serverListe());
|
||||
});
|
||||
|
||||
export const POST = api(async (anfrage: Request) => {
|
||||
const aufrufer = await rechtErforderlich(anfrage, "serverVerwalten");
|
||||
const server = await serverAnlegen(await jsonLesen(anfrage), aufrufer, aufruferAdresse(anfrage.headers));
|
||||
return NextResponse.json(server, { status: 201 });
|
||||
});
|
||||
Reference in New Issue
Block a user