Auf der Seite eines Schluessels: Public Key einem Host oder einer Gruppe zuweisen, Private Key (nur Admin) auf einen Quell-Host legen - mit ~/.ssh/config-Eintrag und dem Host-Key des Ziels in known_hosts -, Dry-Run fuer alle Ziele, anwenden und die Verbindung von A nach B testen. Der Kern-Anwendungsfall laeuft damit im Browser durch (E2E). Der Dry-Run nimmt Private-Key-Ziele nur fuer Admins auf: sonst koennte ein Operator einen vorbereiteten Private Key ausrollen lassen. Die Vorschau zeigt je Datei die Aktion; der Inhalt eines Private Keys erreicht den Browser nie. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
"use client";
|
||
|
||
import { Plus, X } from "lucide-react";
|
||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||
import { Button } from "@/components/ui/Button";
|
||
|
||
/**
|
||
* „+ Ausrollen“: Knopf mit nativem Dialog (#016, #017). Offen/zu als Zustand, der Effekt
|
||
* gleicht den Dialog an – so liest kein Render-Pfad die Ref.
|
||
*/
|
||
export function AusrollDialog({
|
||
titel,
|
||
children,
|
||
}: {
|
||
titel: string;
|
||
children: (schliessen: () => void) => ReactNode;
|
||
}) {
|
||
const dialog = useRef<HTMLDialogElement>(null);
|
||
const [offen, setOffen] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const element = dialog.current;
|
||
if (!element) return;
|
||
if (offen && !element.open) element.showModal();
|
||
if (!offen && element.open) element.close();
|
||
}, [offen]);
|
||
|
||
const schliessen = () => setOffen(false);
|
||
return (
|
||
<>
|
||
<Button klein icon={<Plus className="size-4" />} onClick={() => setOffen(true)}>
|
||
Ausrollen
|
||
</Button>
|
||
<dialog
|
||
ref={dialog}
|
||
aria-label={titel}
|
||
onClose={schliessen}
|
||
className="border-border-strong bg-surface text-text rounded-karte m-auto w-[calc(100%-32px)] max-w-lg border p-5 backdrop:bg-black/70 sm:p-6"
|
||
>
|
||
<div className="mb-4 flex items-start justify-between gap-3">
|
||
<h2 className="font-display text-[18px] font-semibold">{titel}</h2>
|
||
<button
|
||
type="button"
|
||
onClick={schliessen}
|
||
aria-label="Schließen"
|
||
className="text-muted hover:text-text hover:bg-raised rounded-feld -mt-2 -mr-2 flex size-11 items-center justify-center"
|
||
>
|
||
<X className="size-5" aria-hidden />
|
||
</button>
|
||
</div>
|
||
{offen && children(schliessen)}
|
||
</dialog>
|
||
</>
|
||
);
|
||
}
|