Der Test liest Version, Cluster, Nodes, Storages, Bridges, VNets und die effektiven Rechte des Tokens und leitet daraus ab, welcher Vorgang per API, per SSH oder gar nicht geht – je PVE-Version: Template-Bau per API erst ab 8.4 mit import-Storage, Snippets und LXC-Exec nur per SSH. Fehlende Rechte erscheinen einzeln mit Pfad und Zweck; die Rollenliste in docs/proxmox-rechte.md prüft ein Test gegen den Code. SSH-Host-Keys werden wie Zertifikate erst nach Bestätigung genutzt, und bestätigt werden kann nur ein Schlüssel, den der letzte Test gesehen hat. Statussymbole in der GUI sind jetzt gezeichnet: ◐ und ○ fehlen im Web-Subset von JetBrains Mono. Refs #003 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
4.6 KiB
TypeScript
113 lines
4.6 KiB
TypeScript
import { Pencil } from "lucide-react";
|
||
import type { Metadata } from "next";
|
||
import Link from "next/link";
|
||
import { notFound } from "next/navigation";
|
||
import { Code } from "@/components/formular";
|
||
import { Karte } from "@/components/karte";
|
||
import { Status } from "@/components/status";
|
||
import { prisma } from "@/lib/db";
|
||
import { darf } from "@/server/auth/rechte";
|
||
import { seiteSchuetzen } from "@/server/auth/zugang";
|
||
import { serverSicht } from "@/server/services/server";
|
||
import { ServerStatus } from "../server-status";
|
||
import { ServerAktionen } from "./server-aktionen";
|
||
import { Verbindungsbericht } from "./verbindungsbericht";
|
||
|
||
export async function generateMetadata({ params }: { params: Promise<{ name: string }> }): Promise<Metadata> {
|
||
return { title: (await params).name };
|
||
}
|
||
|
||
export default async function ServerDetail({ params }: { params: Promise<{ name: string }> }) {
|
||
const { name } = await params;
|
||
const benutzer = await seiteSchuetzen("lesen", `/server/${name}`);
|
||
const datensatz = await prisma.server.findUnique({ where: { name } });
|
||
if (!datensatz) notFound();
|
||
const server = serverSicht(datensatz);
|
||
const verwalten = darf(benutzer.rolle, "serverVerwalten");
|
||
|
||
return (
|
||
<div className="flex flex-col gap-6">
|
||
<header className="flex flex-wrap items-end justify-between gap-4">
|
||
<div className="min-w-0">
|
||
<p className="text-muted font-mono text-sm">{server.name}</p>
|
||
<h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">{server.titel}</h1>
|
||
</div>
|
||
{verwalten ? (
|
||
<Link
|
||
href={`/server/${server.name}/bearbeiten`}
|
||
className="border-linie bg-panel-hoch hover:border-muted inline-flex h-11 items-center gap-2 rounded-lg border px-4 text-sm font-semibold sm:h-10"
|
||
>
|
||
<Pencil size={16} aria-hidden="true" /> Bearbeiten
|
||
</Link>
|
||
) : null}
|
||
</header>
|
||
|
||
<Karte titel="Verbindung" aktion={<ServerStatus server={server} />}>
|
||
<dl className="grid gap-x-6 gap-y-2 text-sm sm:grid-cols-[max-content_1fr]">
|
||
<dt className="text-muted">Adresse</dt>
|
||
<dd className="font-mono break-all">{server.url}</dd>
|
||
<dt className="text-muted">Token-ID</dt>
|
||
<dd className="font-mono break-all">{server.tokenId}</dd>
|
||
<dt className="text-muted">TLS</dt>
|
||
<dd>
|
||
{server.fingerabdruck ? (
|
||
<span className="flex flex-col gap-1">
|
||
<span>Fingerprint gepinnt{server.tlsPruefen ? " und Kette geprüft" : ""}</span>
|
||
<span className="text-muted font-mono text-xs break-all">{server.fingerabdruck}</span>
|
||
</span>
|
||
) : (
|
||
"Kette gegen Systemzertifikate geprüft"
|
||
)}
|
||
</dd>
|
||
<dt className="text-muted">Templates</dt>
|
||
<dd className="font-mono">
|
||
{server.templateVmidVon}–{server.templateVmidBis}
|
||
</dd>
|
||
<dt className="text-muted">Gäste ab</dt>
|
||
<dd className="font-mono">{server.gastVmidVon}</dd>
|
||
</dl>
|
||
</Karte>
|
||
|
||
<Verbindungsbericht
|
||
name={server.name}
|
||
bericht={server.letzterTest}
|
||
geprueftAm={server.geprueftAm}
|
||
verwalten={verwalten}
|
||
/>
|
||
|
||
<Karte titel="SSH zu den Nodes (optional)">
|
||
{!server.sshAktiv ? (
|
||
<Status art="offen">Nicht genutzt. Deployments laufen dann nur über die API.</Status>
|
||
) : server.sshOeffentlich ? (
|
||
<div className="flex flex-col gap-3 text-sm">
|
||
<p>
|
||
Diesen Schlüssel auf jedem Node für <span className="font-mono">{server.sshBenutzer}</span>{" "}
|
||
hinterlegen. Im Cluster genügt ein Node:{" "}
|
||
<span className="font-mono">/root/.ssh/authorized_keys</span> zeigt dort auf die gemeinsame
|
||
Datei in <span className="font-mono">/etc/pve/priv/</span>.
|
||
</p>
|
||
<Code>{`echo '${server.sshOeffentlich}' >> ~/.ssh/authorized_keys`}</Code>
|
||
<p className="text-muted">
|
||
Host-Keys erscheinen nach dem Verbindungstest beim jeweiligen Node und werden erst nach
|
||
Bestätigung genutzt.
|
||
</p>
|
||
</div>
|
||
) : (
|
||
<Status art="laeuft">SSH ist eingeschaltet, aber es gibt noch keinen Schlüssel.</Status>
|
||
)}
|
||
{verwalten && server.sshAktiv ? (
|
||
<div className="mt-4">
|
||
<ServerAktionen name={server.name} art="ssh" erneuern={Boolean(server.sshOeffentlich)} />
|
||
</div>
|
||
) : null}
|
||
</Karte>
|
||
|
||
{verwalten ? (
|
||
<Karte titel="Gefahrenzone">
|
||
<ServerAktionen name={server.name} art="loeschen" />
|
||
</Karte>
|
||
) : null}
|
||
</div>
|
||
);
|
||
}
|