Files
proxdeploy/src/server/api.ts
T
florian.wildeandClaude Opus 5.5 9310164bc2 feat(ausrollen): VM ausrollen mit Assistent, Dry-Run und CLI-Befehl
Eine VM aus dem ProxDeploy-Template ausrollen, vollständig per API:
Klonen, Hardware, cloud-init, Start, Guest-Agent und Provisionierung der
Profile über den Agent – ohne SSH. Der Fortschritt kommt aus der
Logdatei im Gast, je Profil eine Marke.

Der Dry-Run läuft durch dieselben Phasen mit einem Trocken-Client und
zeigt jeden geplanten Proxmox-Aufruf und den passenden CLI-Befehl. Der
Assistent hat 9 Schritte, den Zustand in der URL und bietet an, ein
fehlendes Template im selben Auftrag zu bauen.

Auf pve-demo abgenommen: pd-test01 (base, docker) per API, pd-test02
(base, zabbix-agent) über den Assistenten, beide per SSH geprüft. Dabei
gefunden: file-write scheitert an Umlauten – Skripte gehen jetzt
base64-kodiert hinüber.

Closes #008

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:34:15 +02:00

70 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextResponse } from "next/server";
import { ZodError } from "@/lib/validierung";
import { logger } from "@/lib/logger";
import { AnwendungsFehler } from "@/server/fehler";
/**
* Einheitliche Fehlerantwort der REST-API – Web-GUI und CLI lesen dasselbe Format:
* `{ fehler: { code, nachricht, hinweis?, felder? } }`.
*/
export type FehlerAntwort = {
fehler: { code: string; nachricht: string; hinweis?: string; felder?: Record<string, string> };
};
export function fehlerAntwort(fehler: unknown): NextResponse<FehlerAntwort> {
if (fehler instanceof AnwendungsFehler) {
return NextResponse.json(
{
fehler: {
code: fehler.code,
nachricht: fehler.message,
...(fehler.hinweis ? { hinweis: fehler.hinweis } : {}),
...(fehler.felder ? { felder: fehler.felder } : {}),
},
},
{ status: fehler.status },
);
}
if (fehler instanceof ZodError) {
const felder: Record<string, string> = {};
for (const problem of fehler.issues) {
const pfad = problem.path.join(".") || "_";
felder[pfad] ??= problem.message;
}
return NextResponse.json(
{
fehler: {
code: "ungueltig",
nachricht: "Die Eingaben sind nicht vollständig oder ungültig.",
felder,
},
},
{ status: 400 },
);
}
logger.error({ err: fehler }, "Unerwarteter Fehler in der API");
return NextResponse.json({ fehler: { code: "intern", nachricht: "Interner Fehler." } }, { status: 500 });
}
/** Umhüllt einen Route Handler: wandelt geworfene Fehler in das einheitliche Format. */
export function api<A extends unknown[]>(
handler: (...argumente: A) => Promise<Response>,
): (...argumente: A) => Promise<Response> {
return async (...argumente: A) => {
try {
return await handler(...argumente);
} catch (fehler) {
return fehlerAntwort(fehler);
}
};
}
/** Liest einen JSON-Rumpf; ein kaputter Rumpf ist ein 400, kein 500. */
export async function jsonLesen(anfrage: Request): Promise<unknown> {
try {
return await anfrage.json();
} catch {
throw new AnwendungsFehler(400, "kein_json", "Der Anfragerumpf ist kein gültiges JSON.");
}
}