import { Key, LayoutGrid, List as ListIcon, Pencil, Plus, Search, Shield, Trash2, } from "lucide-react"; import React, { useMemo, useState } from "react"; import { KeyType } from "../domain/models"; import { cn } from "../lib/utils"; import { SSHKey } from "../types"; import { Button } from "./ui/button"; import { Card, CardDescription, CardTitle } from "./ui/card"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "./ui/dialog"; import { Input } from "./ui/input"; import { Label } from "./ui/label"; import { Textarea } from "./ui/textarea"; interface KeyManagerProps { keys: SSHKey[]; onSave: (key: SSHKey) => void; onDelete: (id: string) => void; } const KeyManager: React.FC = ({ keys, onSave, onDelete }) => { const [isDialogOpen, setIsDialogOpen] = useState(false); const [panelMode, setPanelMode] = useState<"new" | "edit">("new"); const [draftKey, setDraftKey] = useState>({ id: "", label: "", type: "RSA", privateKey: "", publicKey: "", created: Date.now(), }); const [generateMode, setGenerateMode] = useState(false); const [search, setSearch] = useState(""); const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const handleGenerate = () => { // Simulate Key Generation const mockKey = `-----BEGIN ${draftKey.type} PRIVATE KEY-----\n` + `MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC${Math.random().toString(36).substring(7)}\n` + `... (simulated generated content) ...\n` + `-----END ${draftKey.type} PRIVATE KEY-----`; setDraftKey({ ...draftKey, privateKey: mockKey }); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!draftKey.label || !draftKey.privateKey) return; const payload: SSHKey = { id: draftKey.id || crypto.randomUUID(), label: draftKey.label, type: (draftKey.type as KeyType) || "RSA", privateKey: draftKey.privateKey, publicKey: draftKey.publicKey?.trim() || undefined, created: draftKey.created || Date.now(), source: draftKey.source || (generateMode ? "generated" : "imported"), category: draftKey.category || "key", }; onSave(payload); setIsDialogOpen(false); setGenerateMode(false); }; const openPanelForKey = (key: SSHKey) => { setPanelMode("edit"); setDraftKey({ ...key }); setIsDialogOpen(true); setGenerateMode(false); }; const openPanelNew = (isGenerate = false) => { setPanelMode("new"); setGenerateMode(isGenerate); setDraftKey({ id: "", label: "", type: "RSA", privateKey: isGenerate ? "Click generate to create a new key pair..." : "", publicKey: "", created: Date.now(), }); setIsDialogOpen(true); }; const handleDelete = (id: string) => { onDelete(id); if (draftKey.id === id) { setIsDialogOpen(false); setDraftKey({ id: "", label: "", type: "RSA", privateKey: "", publicKey: "", created: Date.now(), }); } }; const filteredKeys = useMemo(() => { const term = search.trim().toLowerCase(); return keys.filter((k) => { if (!term) return true; return ( k.label.toLowerCase().includes(term) || (k.type || "").toString().toLowerCase().includes(term) ); }); }, [keys, search]); const derivedPublicKey = useMemo(() => { if (draftKey.publicKey) return draftKey.publicKey; if (!draftKey.label) return "Generated By netcatty"; return `ssh-${(draftKey.type || "ed25519").toLowerCase()} AAAAC3NzaC1lZDI1NTE5AAAA${( draftKey.label || "netcatty" ) .replace(/\s+/g, "") .slice(0, 8)} Generated By netcatty`; }, [draftKey.label, draftKey.type, draftKey.publicKey]); return (
setSearch(e.target.value)} placeholder="Search keys..." className="h-9 pl-8 w-44 md:w-56" />

Keys

{filteredKeys.length === 0 && (

Set up your keys

Import or generate SSH keys for secure authentication.

)}
{filteredKeys.map((key) => ( openPanelForKey(key)} >
{key.label} Type {key.type}
SHA256:{key.id.substring(0, 16)}...
))}
{panelMode === "new" ? "New Key" : "Edit Key"} {panelMode === "new" ? "Create a new SSH key entry" : "Edit the selected SSH key entry"}
setDraftKey({ ...draftKey, label: e.target.value }) } placeholder="Key label" required />