/** * Vault Secrets API Client * * API client for interacting with vault secrets endpoints */ import { Secret, SecretWithValue, SecretHistory, CreateSecretRequest, UpdateSecretRequest, ListSecretsQuery, ListSecretsResponse, ApiError, } from '../types/secrets'; const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:8080'; class SecretsApiError extends Error { constructor(public error: ApiError) { super(error.message); this.name = 'SecretsApiError'; } } /** * Get authorization token from storage */ function getAuthToken(): string | null { return localStorage.getItem('auth_token'); } /** * Make authenticated API request */ async function apiRequest( endpoint: string, options: RequestInit = {} ): Promise { const token = getAuthToken(); const headers: HeadersInit = { 'Content-Type': 'application/json', ...options.headers, }; if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(`${API_BASE_URL}${endpoint}`, { ...options, headers, }); if (!response.ok) { const errorData: ApiError = await response.json(); throw new SecretsApiError(errorData); } // Handle 204 No Content if (response.status === 204) { return null as T; } return response.json(); } /** * Secrets API Client */ export const secretsApi = { /** * Create a new secret */ async createSecret(request: CreateSecretRequest): Promise { return apiRequest('/api/v1/secrets/vault', { method: 'POST', body: JSON.stringify(request), }); }, /** * Get a secret value (decrypted) */ async getSecret( path: string, version?: number, context?: string ): Promise { const params = new URLSearchParams(); if (version !== undefined) { params.append('version', version.toString()); } if (context) { params.append('context', context); } const queryString = params.toString(); const endpoint = `/api/v1/secrets/vault/${encodeURIComponent(path)}${ queryString ? `?${queryString}` : '' }`; return apiRequest(endpoint); }, /** * List secrets (metadata only) */ async listSecrets(query?: ListSecretsQuery): Promise { const params = new URLSearchParams(); if (query?.prefix) { params.append('prefix', query.prefix); } if (query?.limit !== undefined) { params.append('limit', query.limit.toString()); } if (query?.offset !== undefined) { params.append('offset', query.offset.toString()); } const queryString = params.toString(); const endpoint = `/api/v1/secrets/vault${queryString ? `?${queryString}` : ''}`; return apiRequest(endpoint); }, /** * Update a secret (creates new version) */ async updateSecret(path: string, request: UpdateSecretRequest): Promise { return apiRequest(`/api/v1/secrets/vault/${encodeURIComponent(path)}`, { method: 'PUT', body: JSON.stringify(request), }); }, /** * Delete a secret */ async deleteSecret(path: string): Promise { return apiRequest(`/api/v1/secrets/vault/${encodeURIComponent(path)}`, { method: 'DELETE', }); }, /** * Get secret history/versions */ async getSecretHistory(path: string): Promise { return apiRequest( `/api/v1/secrets/vault/${encodeURIComponent(path)}/history` ); }, /** * Restore a specific version of a secret */ async restoreSecretVersion(path: string, version: number): Promise { return apiRequest( `/api/v1/secrets/vault/${encodeURIComponent(path)}/versions/${version}/restore`, { method: 'POST', } ); }, }; export { SecretsApiError };