import type { Certificate, CertificateVersion, Agent, Job, Notification, AuditEvent, PolicyRule, PolicyViolation, Issuer, Target, CertificateProfile, Owner, Team, AgentGroup, PaginatedResponse, DashboardSummary, CertificateStatusCount, ExpirationBucket, JobTrendDataPoint, IssuanceRateDataPoint, MetricsResponse, DiscoveredCertificate, DiscoveryScan, DiscoverySummary, NetworkScanTarget } from './types'; const BASE = '/api/v1'; // API key stored in memory (not localStorage for security) let apiKey: string | null = null; export function setApiKey(key: string | null) { apiKey = key; } export function getApiKey(): string | null { return apiKey; } function authHeaders(): Record { const headers: Record = { 'Content-Type': 'application/json' }; if (apiKey) { headers['Authorization'] = `Bearer ${apiKey}`; } return headers; } async function fetchJSON(url: string, init?: RequestInit): Promise { const res = await fetch(url, { headers: { ...authHeaders(), ...init?.headers }, ...init, }); if (res.status === 401) { // Trigger re-auth const event = new CustomEvent('certctl:auth-required'); window.dispatchEvent(event); throw new Error('Authentication required'); } if (!res.ok) { let errorMsg = res.statusText; try { const body = await res.json(); errorMsg = body.message || body.error || errorMsg; } catch { // Response body is not JSON, use status text } throw new Error(errorMsg || `HTTP ${res.status}`); } if (res.status === 204) return {} as T; return res.json(); } // Auth export const getAuthInfo = () => fetch(`${BASE}/auth/info`, { headers: { 'Content-Type': 'application/json' } }) .then(r => r.json() as Promise<{ auth_type: string; required: boolean }>); export const checkAuth = (key: string) => fetch(`${BASE}/auth/check`, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` }, }).then(r => { if (!r.ok) throw new Error('Invalid API key'); return r.json() as Promise<{ status: string }>; }); // Certificates export const getCertificates = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/certificates?${qs}`); }; export const getCertificate = (id: string) => fetchJSON(`${BASE}/certificates/${id}`); export const getCertificateVersions = (id: string) => fetchJSON>(`${BASE}/certificates/${id}/versions`); export const createCertificate = (data: Partial) => fetchJSON(`${BASE}/certificates`, { method: 'POST', body: JSON.stringify(data) }); export const triggerRenewal = (id: string) => fetchJSON<{ message: string }>(`${BASE}/certificates/${id}/renew`, { method: 'POST' }); export const updateCertificate = (id: string, data: Partial) => fetchJSON(`${BASE}/certificates/${id}`, { method: 'PUT', body: JSON.stringify(data) }); export const archiveCertificate = (id: string) => fetchJSON<{ message: string }>(`${BASE}/certificates/${id}`, { method: 'DELETE' }); export const triggerDeployment = (id: string, targetId: string) => fetchJSON<{ message: string }>(`${BASE}/certificates/${id}/deploy`, { method: 'POST', body: JSON.stringify({ target_id: targetId }), }); export const revokeCertificate = (id: string, reason: string) => fetchJSON<{ status: string }>(`${BASE}/certificates/${id}/revoke`, { method: 'POST', body: JSON.stringify({ reason }), }); // Agents export const getAgents = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/agents?${qs}`); }; export const getAgent = (id: string) => fetchJSON(`${BASE}/agents/${id}`); export const registerAgent = (data: Partial) => fetchJSON(`${BASE}/agents`, { method: 'POST', body: JSON.stringify(data) }); // Jobs export const getJobs = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/jobs?${qs}`); }; export const cancelJob = (id: string) => fetchJSON<{ message: string }>(`${BASE}/jobs/${id}/cancel`, { method: 'POST' }); // Notifications export const getNotifications = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/notifications?${qs}`); }; export const markNotificationRead = (id: string) => fetchJSON<{ message: string }>(`${BASE}/notifications/${id}/read`, { method: 'POST' }); // Audit export const getAuditEvents = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/audit?${qs}`); }; // Policies export const getPolicies = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/policies?${qs}`); }; export const createPolicy = (data: Partial) => fetchJSON(`${BASE}/policies`, { method: 'POST', body: JSON.stringify(data) }); export const updatePolicy = (id: string, data: Partial) => fetchJSON(`${BASE}/policies/${id}`, { method: 'PUT', body: JSON.stringify(data) }); export const deletePolicy = (id: string) => fetchJSON<{ message: string }>(`${BASE}/policies/${id}`, { method: 'DELETE' }); export const getPolicyViolations = (id: string) => fetchJSON>(`${BASE}/policies/${id}/violations`); // Issuers export const getIssuers = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/issuers?${qs}`); }; export const createIssuer = (data: Partial) => fetchJSON(`${BASE}/issuers`, { method: 'POST', body: JSON.stringify(data) }); export const testIssuerConnection = (id: string) => fetchJSON<{ message: string }>(`${BASE}/issuers/${id}/test`, { method: 'POST' }); export const deleteIssuer = (id: string) => fetchJSON<{ message: string }>(`${BASE}/issuers/${id}`, { method: 'DELETE' }); // Targets export const getTargets = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/targets?${qs}`); }; export const createTarget = (data: Partial) => fetchJSON(`${BASE}/targets`, { method: 'POST', body: JSON.stringify(data) }); export const deleteTarget = (id: string) => fetchJSON<{ message: string }>(`${BASE}/targets/${id}`, { method: 'DELETE' }); // Profiles export const getProfiles = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/profiles?${qs}`); }; export const getProfile = (id: string) => fetchJSON(`${BASE}/profiles/${id}`); export const createProfile = (data: Partial) => fetchJSON(`${BASE}/profiles`, { method: 'POST', body: JSON.stringify(data) }); export const updateProfile = (id: string, data: Partial) => fetchJSON(`${BASE}/profiles/${id}`, { method: 'PUT', body: JSON.stringify(data) }); export const deleteProfile = (id: string) => fetchJSON<{ message: string }>(`${BASE}/profiles/${id}`, { method: 'DELETE' }); // Owners export const getOwners = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/owners?${qs}`); }; export const getOwner = (id: string) => fetchJSON(`${BASE}/owners/${id}`); export const createOwner = (data: Partial) => fetchJSON(`${BASE}/owners`, { method: 'POST', body: JSON.stringify(data) }); export const updateOwner = (id: string, data: Partial) => fetchJSON(`${BASE}/owners/${id}`, { method: 'PUT', body: JSON.stringify(data) }); export const deleteOwner = (id: string) => fetchJSON<{ message: string }>(`${BASE}/owners/${id}`, { method: 'DELETE' }); // Teams export const getTeams = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/teams?${qs}`); }; export const getTeam = (id: string) => fetchJSON(`${BASE}/teams/${id}`); export const createTeam = (data: Partial) => fetchJSON(`${BASE}/teams`, { method: 'POST', body: JSON.stringify(data) }); export const updateTeam = (id: string, data: Partial) => fetchJSON(`${BASE}/teams/${id}`, { method: 'PUT', body: JSON.stringify(data) }); export const deleteTeam = (id: string) => fetchJSON<{ message: string }>(`${BASE}/teams/${id}`, { method: 'DELETE' }); // Agent Groups export const getAgentGroups = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/agent-groups?${qs}`); }; export const getAgentGroup = (id: string) => fetchJSON(`${BASE}/agent-groups/${id}`); export const createAgentGroup = (data: Partial) => fetchJSON(`${BASE}/agent-groups`, { method: 'POST', body: JSON.stringify(data) }); export const updateAgentGroup = (id: string, data: Partial) => fetchJSON(`${BASE}/agent-groups/${id}`, { method: 'PUT', body: JSON.stringify(data) }); export const deleteAgentGroup = (id: string) => fetchJSON<{ message: string }>(`${BASE}/agent-groups/${id}`, { method: 'DELETE' }); export const getAgentGroupMembers = (id: string) => fetchJSON>(`${BASE}/agent-groups/${id}/members`); // Renewal Approvals export const approveRenewal = (jobId: string) => fetchJSON<{ message: string }>(`${BASE}/jobs/${jobId}/approve`, { method: 'POST' }); export const rejectRenewal = (jobId: string, reason: string) => fetchJSON<{ message: string }>(`${BASE}/jobs/${jobId}/reject`, { method: 'POST', body: JSON.stringify({ reason }) }); // Discovery export const getDiscoveredCertificates = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/discovered-certificates?${qs}`); }; export const getDiscoveredCertificate = (id: string) => fetchJSON(`${BASE}/discovered-certificates/${id}`); export const claimDiscoveredCertificate = (id: string, managedCertificateId: string) => fetchJSON<{ message: string }>(`${BASE}/discovered-certificates/${id}/claim`, { method: 'POST', body: JSON.stringify({ managed_certificate_id: managedCertificateId }), }); export const dismissDiscoveredCertificate = (id: string) => fetchJSON<{ message: string }>(`${BASE}/discovered-certificates/${id}/dismiss`, { method: 'POST' }); export const getDiscoveryScans = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/discovery-scans?${qs}`); }; export const getDiscoverySummary = () => fetchJSON(`${BASE}/discovery-summary`); // Network Scan Targets export const getNetworkScanTargets = (params: Record = {}) => { const qs = new URLSearchParams({ page: '1', per_page: '50', ...params }).toString(); return fetchJSON>(`${BASE}/network-scan-targets?${qs}`); }; export const getNetworkScanTarget = (id: string) => fetchJSON(`${BASE}/network-scan-targets/${id}`); export const createNetworkScanTarget = (data: Partial) => fetchJSON(`${BASE}/network-scan-targets`, { method: 'POST', body: JSON.stringify(data) }); export const updateNetworkScanTarget = (id: string, data: Partial) => fetchJSON(`${BASE}/network-scan-targets/${id}`, { method: 'PUT', body: JSON.stringify(data) }); export const deleteNetworkScanTarget = (id: string) => fetchJSON<{ message: string }>(`${BASE}/network-scan-targets/${id}`, { method: 'DELETE' }); export const triggerNetworkScan = (id: string) => fetchJSON<{ message: string }>(`${BASE}/network-scan-targets/${id}/scan`, { method: 'POST' }); // Stats export const getDashboardSummary = () => fetchJSON(`${BASE}/stats/summary`); export const getCertificatesByStatus = () => fetchJSON(`${BASE}/stats/certificates-by-status`); export const getExpirationTimeline = (days = 30) => fetchJSON(`${BASE}/stats/expiration-timeline?days=${days}`); export const getJobTrends = (days = 30) => fetchJSON(`${BASE}/stats/job-trends?days=${days}`); export const getIssuanceRate = (days = 30) => fetchJSON(`${BASE}/stats/issuance-rate?days=${days}`); export const getMetrics = () => fetchJSON(`${BASE}/metrics`); // Health export const getHealth = () => fetchJSON<{ status: string }>('/health');