import React, { useMemo, useState } from 'react'; import { SSHKey } from '../types'; import { Key, Plus, Trash2, Shield, Search, LayoutGrid, List as ListIcon, Pencil } from 'lucide-react'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; import { Label } from './ui/label'; import { Textarea } from './ui/textarea'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from './ui/dialog'; import { cn } from '../lib/utils'; 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 any) || 'RSA', privateKey: draftKey.privateKey, publicKey: draftKey.publicKey?.trim() || undefined, created: draftKey.created || Date.now(), }; 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 || 'nebula') .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 && (

No keys found. Import or generate to get started.

)}
{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 />