diff --git a/ui/src/lib/api.ts b/ui/src/lib/api.ts new file mode 100644 index 0000000..7c97d9e --- /dev/null +++ b/ui/src/lib/api.ts @@ -0,0 +1,122 @@ +import { CreateInstanceOptions, Instance } from "@/types/instance" + +const API_BASE = '/api/v1' + +// Configuration for API calls +interface ApiConfig { + apiKey?: string +} + +// Global config - can be updated when auth is added +let apiConfig: ApiConfig = {} + +export const setApiConfig = (config: ApiConfig) => { + apiConfig = config +} + +// Base API call function with error handling +async function apiCall( + endpoint: string, + options: RequestInit = {} +): Promise { + const url = `${API_BASE}${endpoint}` + + // Prepare headers + const headers: HeadersInit = { + 'Content-Type': 'application/json', + ...options.headers, + } + + // Add API key - not supported yet + // if (apiConfig.apiKey) { + // headers['Authorization'] = `Bearer ${apiConfig.apiKey}` + // } + + try { + const response = await fetch(url, { + ...options, + headers, + }) + + if (!response.ok) { + // Try to get error message from response + let errorMessage = `HTTP ${response.status}` + try { + const errorText = await response.text() + if (errorText) { + errorMessage += `: ${errorText}` + } + } catch { + // If we can't read the error, just use status + } + + throw new Error(errorMessage) + } + + // Handle empty responses (like DELETE) + if (response.status === 204) { + return undefined as T + } + + const data = await response.json() + return data + } catch (error) { + if (error instanceof Error) { + throw error + } + throw new Error('Network error occurred') + } +} + +// Instance API functions +export const instancesApi = { + // GET /instances + list: () => apiCall('/instances'), + + // GET /instances/{name} + get: (name: string) => apiCall(`/instances/${name}`), + + // POST /instances/{name} + create: (name: string, options: CreateInstanceOptions) => + apiCall(`/instances/${name}`, { + method: 'POST', + body: JSON.stringify(options), + }), + + // PUT /instances/{name} + update: (name: string, options: CreateInstanceOptions) => + apiCall(`/instances/${name}`, { + method: 'PUT', + body: JSON.stringify(options), + }), + + // DELETE /instances/{name} + delete: (name: string) => + apiCall(`/instances/${name}`, { + method: 'DELETE', + }), + + // POST /instances/{name}/start + start: (name: string) => + apiCall(`/instances/${name}/start`, { + method: 'POST', + }), + + // POST /instances/{name}/stop + stop: (name: string) => + apiCall(`/instances/${name}/stop`, { + method: 'POST', + }), + + // POST /instances/{name}/restart + restart: (name: string) => + apiCall(`/instances/${name}/restart`, { + method: 'POST', + }), + + // GET /instances/{name}/logs + getLogs: (name: string, lines?: number) => { + const params = lines ? `?lines=${lines}` : '' + return apiCall(`/instances/${name}/logs${params}`) + }, +} \ No newline at end of file