Jesús Pérez 09a97ac8f5
chore: update platform submodule to monorepo crates structure
Platform restructured into crates/, added AI service and detector,
       migrated control-center-ui to Leptos 0.8
2026-01-08 21:32:59 +00:00

171 lines
3.8 KiB
TypeScript

/**
* 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<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
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<Secret> {
return apiRequest<Secret>('/api/v1/secrets/vault', {
method: 'POST',
body: JSON.stringify(request),
});
},
/**
* Get a secret value (decrypted)
*/
async getSecret(
path: string,
version?: number,
context?: string
): Promise<SecretWithValue> {
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<SecretWithValue>(endpoint);
},
/**
* List secrets (metadata only)
*/
async listSecrets(query?: ListSecretsQuery): Promise<ListSecretsResponse> {
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<ListSecretsResponse>(endpoint);
},
/**
* Update a secret (creates new version)
*/
async updateSecret(path: string, request: UpdateSecretRequest): Promise<Secret> {
return apiRequest<Secret>(`/api/v1/secrets/vault/${encodeURIComponent(path)}`, {
method: 'PUT',
body: JSON.stringify(request),
});
},
/**
* Delete a secret
*/
async deleteSecret(path: string): Promise<void> {
return apiRequest<void>(`/api/v1/secrets/vault/${encodeURIComponent(path)}`, {
method: 'DELETE',
});
},
/**
* Get secret history/versions
*/
async getSecretHistory(path: string): Promise<SecretHistory> {
return apiRequest<SecretHistory>(
`/api/v1/secrets/vault/${encodeURIComponent(path)}/history`
);
},
/**
* Restore a specific version of a secret
*/
async restoreSecretVersion(path: string, version: number): Promise<Secret> {
return apiRequest<Secret>(
`/api/v1/secrets/vault/${encodeURIComponent(path)}/versions/${version}/restore`,
{
method: 'POST',
}
);
},
};
export { SecretsApiError };