import type { Certificate, CertificateVersion, Agent, Job, Notification, AuditEvent, PolicyRule, PolicyViolation, Issuer, Target, CertificateProfile, Owner, Team, AgentGroup, PaginatedResponse } 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}`); } 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 }), }); // 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 }) }); // Health export const getHealth = () => fetchJSON<{ status: string }>('/health');