feat(templates): Template-Bau ohne SSH über eine Bau-Adresse
Ohne SSH zum Node kommt der Guest-Agent jetzt über eine feste Bau-Adresse ins Template: cloud-init setzt Adresse und den Bereitstellungsschlüssel für den Standardbenutzer, ProxDeploy meldet sich per SSH in der VM an, installiert den Agent und entfernt danach Schlüssel, Adresse und cloud-init-Angaben. Dabei korrigiert: der Verbindungstest meldete „Template-Bau per API“ auch ohne Weg, den Agent einzubauen. Im Protokoll erscheinen unveränderte JSON-Felder nicht mehr als geändert. Refs #021 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import ssh2 from "ssh2";
|
||||
import type { Server } from "@/generated/prisma/client";
|
||||
import { Prisma, type Server } from "@/generated/prisma/client";
|
||||
import { ProxmoxClient, type ProxmoxVerbindung } from "@/engine/proxmox/client";
|
||||
import { erkunden, type VerbindungsBericht } from "@/engine/proxmox/erkundung";
|
||||
import { ProxmoxFehler } from "@/engine/proxmox/fehler";
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ServerAendern,
|
||||
ServerAnlegen,
|
||||
ServerPruefen,
|
||||
type BauNetz,
|
||||
type ServerSicht,
|
||||
} from "@/lib/schemas/server";
|
||||
import { AnwendungsFehler, nichtGefunden } from "@/server/fehler";
|
||||
@@ -32,6 +33,7 @@ export function serverSicht(server: Server): ServerSicht {
|
||||
templateVmidVon: server.templateVmidVon,
|
||||
templateVmidBis: server.templateVmidBis,
|
||||
gastVmidVon: server.gastVmidVon,
|
||||
bauNetz: (server.bauNetz as BauNetz | null) ?? null,
|
||||
erreichbar: server.erreichbar,
|
||||
pveVersion: server.pveVersion,
|
||||
geprueftAm: server.geprueftAm?.toISOString() ?? null,
|
||||
@@ -83,6 +85,7 @@ export async function serverAnlegen(
|
||||
const angelegt = await tx.server.create({
|
||||
data: {
|
||||
...eingabe,
|
||||
bauNetz: eingabe.bauNetz ?? Prisma.JsonNull,
|
||||
fingerabdruck: fingerabdruckOderFehler(eingabe.fingerabdruck),
|
||||
tokenGeheimnis: verschluesseln(eingabe.tokenGeheimnis),
|
||||
},
|
||||
@@ -117,12 +120,19 @@ export async function serverAendern(
|
||||
const { tokenGeheimnis, ...rest } = eingabe;
|
||||
const daten = {
|
||||
...rest,
|
||||
bauNetz: rest.bauNetz ?? Prisma.JsonNull,
|
||||
fingerabdruck: fingerabdruckOderFehler(eingabe.fingerabdruck),
|
||||
...(tokenGeheimnis ? { tokenGeheimnis: verschluesseln(tokenGeheimnis) } : {}),
|
||||
};
|
||||
// Welche Felder sich geändert haben, steht im Protokoll – die Werte von Geheimnissen nie.
|
||||
// JSON-Felder (bauNetz) vergleichen nach Inhalt; Prisma.JsonNull steht für „leer“ wie null.
|
||||
const vergleichbar = (wert: unknown) => JSON.stringify(wert === Prisma.JsonNull ? null : (wert ?? null));
|
||||
const geaendert = Object.entries(daten)
|
||||
.filter(([feld, wert]) => feld === "tokenGeheimnis" || (bisher as Record<string, unknown>)[feld] !== wert)
|
||||
.filter(
|
||||
([feld, wert]) =>
|
||||
feld === "tokenGeheimnis" ||
|
||||
vergleichbar((bisher as Record<string, unknown>)[feld]) !== vergleichbar(wert),
|
||||
)
|
||||
.map(([feld]) => feld);
|
||||
|
||||
const server = await prisma.$transaction(async (tx) => {
|
||||
@@ -287,6 +297,7 @@ export async function serverTesten(
|
||||
try {
|
||||
const bericht = await erkunden(client, {
|
||||
serverHost: new URL(server.url).hostname,
|
||||
bauNetz: Boolean(server.bauNetz),
|
||||
ssh: server.sshAktiv
|
||||
? {
|
||||
benutzer: server.sshBenutzer,
|
||||
|
||||
Reference in New Issue
Block a user