feat(schluessel): Zuweisen und Ausrollen auf der Schluesselseite (#017)
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>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
"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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user