"use client"; import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useParams, useRouter } from "next/navigation"; import Link from "next/link"; import { api, Server } from "@/lib/api"; import { Badge, Button, Card, CardHeader, CardTitle } from "@/components/ui"; import { Table, Thead, Tbody, Tr, Th, Td } from "@/components/ui"; function AssignModal({ keyId, assignedServerIds, onClose, }: { keyId: string; assignedServerIds: string[]; onClose: () => void; }) { const queryClient = useQueryClient(); const [selectedServer, setSelectedServer] = useState(""); const { data: servers } = useQuery({ queryKey: ["servers"], queryFn: api.listServers, }); const { mutate: assign, isPending, error } = useMutation({ mutationFn: () => api.assignKey(keyId, selectedServer), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["keys", keyId] }); queryClient.invalidateQueries({ queryKey: ["servers"] }); onClose(); }, }); const availableServers = servers?.filter( (s: Server) => !assignedServerIds.includes(s.server_id) ); return (

Assign Key to Server

{error && (
{(error as Error).message}
)}
{!availableServers || availableServers.length === 0 ? (

All servers already have this key assigned.

) : ( )}
); } export default function KeyDetailPage() { const params = useParams(); const router = useRouter(); const queryClient = useQueryClient(); const keyId = params.id as string; const [showAssign, setShowAssign] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const [copiedKey, setCopiedKey] = useState(false); const { data: key, isLoading, error } = useQuery({ queryKey: ["keys", keyId], queryFn: () => api.getKey(keyId), }); const { mutate: revokeKey } = useMutation({ mutationFn: (serverId: string) => api.revokeKey(keyId, serverId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["keys", keyId] }); queryClient.invalidateQueries({ queryKey: ["servers"] }); }, }); const { mutate: deleteKey, isPending: isDeleting } = useMutation({ mutationFn: () => api.deleteKey(keyId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["keys"] }); router.push("/keys"); }, }); const handleCopyKey = async () => { if (!key?.public_key) return; await navigator.clipboard.writeText(key.public_key); setCopiedKey(true); setTimeout(() => setCopiedKey(false), 2000); }; if (isLoading) { return (
); } if (error || !key) { return (
Key not found or failed to load.
); } const activeAssignments = key.assignments?.filter((a) => !a.revoked_at) ?? []; const assignedServerIds = activeAssignments.map((a) => a.server_id); return (
{showAssign && ( setShowAssign(false)} /> )}
← SSH Keys

{key.label}

{key.source}

{key.fingerprint}

{!confirmDelete ? ( ) : (
Delete permanently?
)}
Details
Key ID
{key.key_id}
Source
{key.source}
{key.generated_by_server_id && (
Generated By
{key.generated_by_server_id}
)}
Active Assignments
{activeAssignments.length}
Created
{new Date(key.created_at).toLocaleString()}
Public Key
                {key.public_key}
              

Server Assignments {activeAssignments.length} active

{!key.assignments || key.assignments.length === 0 ? (

Not assigned to any servers.

) : ( {key.assignments.map((assignment) => ( ))}
Server IP Address Status Assigned Revoked
{assignment.server?.hostname ?? assignment.server_id} {assignment.server?.ip_address ?? "—"} {assignment.revoked_at ? "revoked" : "active"} {new Date(assignment.assigned_at).toLocaleDateString()} {assignment.revoked_at ? new Date(assignment.revoked_at).toLocaleDateString() : "—"} {!assignment.revoked_at && ( )}
)}
); }