import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { getProfiles, deleteProfile, createProfile } from '../api/client'; import PageHeader from '../components/PageHeader'; import DataTable from '../components/DataTable'; import type { Column } from '../components/DataTable'; import StatusBadge from '../components/StatusBadge'; import ErrorState from '../components/ErrorState'; import { formatDateTime } from '../api/utils'; import type { CertificateProfile } from '../api/types'; function formatTTL(seconds: number): string { if (seconds === 0) return 'No limit'; if (seconds < 60) return `${seconds}s`; if (seconds < 3600) return `${Math.floor(seconds / 60)}m`; if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`; return `${Math.floor(seconds / 86400)}d`; } interface CreateProfileModalProps { isOpen: boolean; onClose: () => void; onSuccess: () => void; isLoading: boolean; error: string | null; } const AVAILABLE_ALGORITHMS = ['RSA', 'ECDSA', 'Ed25519']; const ALGORITHM_MIN_SIZES: Record = { RSA: [2048, 3072, 4096], ECDSA: [256, 384], Ed25519: [0], }; const AVAILABLE_EKUS = [ { value: 'serverAuth', label: 'Server Authentication (TLS)' }, { value: 'clientAuth', label: 'Client Authentication' }, { value: 'codeSigning', label: 'Code Signing' }, { value: 'emailProtection', label: 'Email Protection (S/MIME)' }, { value: 'timeStamping', label: 'Time Stamping' }, ]; interface KeyAlgorithmEntry { algorithm: string; min_size: number; } function CreateProfileModal({ isOpen, onClose, onSuccess, isLoading, error }: CreateProfileModalProps) { const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [ttl, setTtl] = useState('86400'); const [shortLived, setShortLived] = useState(false); const [keyAlgorithms, setKeyAlgorithms] = useState([ { algorithm: 'ECDSA', min_size: 256 }, { algorithm: 'RSA', min_size: 2048 }, ]); const [selectedEkus, setSelectedEkus] = useState(['serverAuth']); const [sanPatterns, setSanPatterns] = useState(''); const [spiffePattern, setSpiffePattern] = useState(''); const addAlgorithm = () => { const unused = AVAILABLE_ALGORITHMS.find(a => !keyAlgorithms.some(ka => ka.algorithm === a)); if (unused) { setKeyAlgorithms([...keyAlgorithms, { algorithm: unused, min_size: ALGORITHM_MIN_SIZES[unused][0] }]); } }; const removeAlgorithm = (idx: number) => { setKeyAlgorithms(keyAlgorithms.filter((_, i) => i !== idx)); }; const updateAlgorithm = (idx: number, field: 'algorithm' | 'min_size', value: string | number) => { const updated = [...keyAlgorithms]; if (field === 'algorithm') { updated[idx] = { algorithm: value as string, min_size: ALGORITHM_MIN_SIZES[value as string]?.[0] || 0 }; } else { updated[idx] = { ...updated[idx], min_size: value as number }; } setKeyAlgorithms(updated); }; const toggleEku = (eku: string) => { setSelectedEkus(prev => prev.includes(eku) ? prev.filter(e => e !== eku) : [...prev, eku]); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name.trim()) return; await createProfile({ name: name.trim(), description: description.trim(), max_ttl_seconds: parseInt(ttl) || 86400, allow_short_lived: shortLived, allowed_key_algorithms: keyAlgorithms, allowed_ekus: selectedEkus, required_san_patterns: sanPatterns.trim() ? sanPatterns.split(',').map(s => s.trim()).filter(Boolean) : [], spiffe_uri_pattern: spiffePattern.trim() || '', enabled: true, }); setName(''); setDescription(''); setTtl('86400'); setShortLived(false); setKeyAlgorithms([{ algorithm: 'ECDSA', min_size: 256 }, { algorithm: 'RSA', min_size: 2048 }]); setSelectedEkus(['serverAuth']); setSanPatterns(''); setSpiffePattern(''); onSuccess(); }; if (!isOpen) return null; const inputClass = 'w-full bg-white border border-surface-border rounded px-3 py-2 text-sm text-ink focus:outline-none focus:border-brand-400'; const selectClass = 'bg-white border border-surface-border rounded px-3 py-2 text-sm text-ink focus:outline-none focus:border-brand-400'; return (
e.stopPropagation()}>

Create Profile

{error &&
{error}
}
setName(e.target.value)} className={inputClass} placeholder="e.g., Web Server Certs" required />