import { useRef, useState } from 'react'; import { errorMessage } from '../../api/client'; import { Alert, Badge, Button, Card, FileInput, FormField, Input, Spinner, useToast, } from '@cameleer/design-system'; import type { FileInputHandle } from '@cameleer/design-system'; import { Upload, ShieldCheck, RotateCcw, Trash2, FileKey, KeyRound, ShieldPlus } from 'lucide-react'; import { useVendorCertificates, useStageCertificate, useActivateCertificate, useRestoreCertificate, useDiscardStaged, } from '../../api/certificate-hooks'; import type { CertificateResponse } from '../../api/certificate-hooks'; import styles from '../../styles/platform.module.css'; function formatDate(iso: string | null | undefined): string { if (!iso) return '—'; return new Date(iso).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }); } function daysUntil(iso: string | null | undefined): number { if (!iso) return 0; return Math.ceil((new Date(iso).getTime() - Date.now()) / 86_400_000); } function expiryColor(iso: string | null | undefined): string | undefined { const days = daysUntil(iso); if (days <= 0) return 'var(--error)'; if (days <= 30) return 'var(--warning)'; return undefined; } function shortenFingerprint(fp: string | null | undefined): string { if (!fp) return '—'; // Show first 23 chars (8 hex pairs) + ellipsis return fp.length > 23 ? fp.slice(0, 23) + '...' : fp; } function CertCard({ cert, title, actions, }: { cert: CertificateResponse; title: string; actions?: React.ReactNode; }) { const days = daysUntil(cert.notAfter); return (
Subject {cert.subject}
Issuer {cert.issuer}
Valid from {formatDate(cert.notBefore)}
Expires {formatDate(cert.notAfter)} ({days}d)
Fingerprint {shortenFingerprint(cert.fingerprint)}
CA bundle {cert.hasCa ? 'Yes' : 'No'}
{cert.selfSigned && (
Type
)} {cert.activatedAt && (
Activated {formatDate(cert.activatedAt)}
)} {actions &&
{actions}
}
); } export function CertificatesPage() { const { toast } = useToast(); const { data, isLoading, isError } = useVendorCertificates(); const stageMutation = useStageCertificate(); const activateMutation = useActivateCertificate(); const restoreMutation = useRestoreCertificate(); const discardMutation = useDiscardStaged(); const certRef = useRef(null); const keyRef = useRef(null); const caRef = useRef(null); const [keyPassword, setKeyPassword] = useState(''); if (isLoading) { return (
); } if (isError || !data) { return (
Could not fetch certificate data. Please refresh.
); } async function handleUpload() { const certFile = certRef.current?.file; const keyFile = keyRef.current?.file; const caFile = caRef.current?.file; if (!certFile || !keyFile) { toast({ title: 'Certificate and key files are required', variant: 'error' }); return; } const formData = new FormData(); formData.append('cert', certFile); formData.append('key', keyFile); if (caFile) formData.append('ca', caFile); if (keyPassword) formData.append('password', keyPassword); try { const result = await stageMutation.mutateAsync(formData); if (result.valid) { toast({ title: 'Certificate staged successfully', variant: 'success' }); certRef.current?.clear(); keyRef.current?.clear(); caRef.current?.clear(); setKeyPassword(''); } else { toast({ title: 'Validation failed', description: result.errors.join(', '), variant: 'error' }); } } catch (err) { toast({ title: 'Upload failed', description: errorMessage(err), variant: 'error' }); } } async function handleActivate() { try { await activateMutation.mutateAsync(); toast({ title: 'Certificate activated', variant: 'success' }); } catch (err) { toast({ title: 'Activation failed', description: errorMessage(err), variant: 'error' }); } } async function handleRestore() { try { await restoreMutation.mutateAsync(); toast({ title: 'Certificate restored from archive', variant: 'success' }); } catch (err) { toast({ title: 'Restore failed', description: errorMessage(err), variant: 'error' }); } } async function handleDiscard() { try { await discardMutation.mutateAsync(); toast({ title: 'Staged certificate discarded', variant: 'success' }); } catch (err) { toast({ title: 'Discard failed', description: errorMessage(err), variant: 'error' }); } } const expired = data.archived ? daysUntil(data.archived.notAfter) <= 0 : false; return (

Certificates

{data.staleTenantCount > 0 && ( {data.staleTenantCount} tenant{data.staleTenantCount > 1 ? 's' : ''} need a restart to pick up the updated CA bundle. )}
{data.active && ( )} {data.staged && ( } /> )} {data.archived && ( {expired ? 'Expired' : 'Restore'} } /> )} {/* Upload card */}
} /> } /> setKeyPassword(e.target.value)} /> } />
); }