// [DEF:api_module:Module] // @SEMANTICS: api, client, fetch, rest // @PURPOSE: Handles all communication with the backend API. // @LAYER: Infra-API import { addToast } from './toasts.js'; import { PUBLIC_WS_URL } from '$env/static/public'; const API_BASE_URL = '/api'; // [DEF:getWsUrl:Function] // @PURPOSE: Returns the WebSocket URL for a specific task, with fallback logic. // @PRE: taskId is provided. // @POST: Returns valid WebSocket URL string. // @PARAM: taskId (string) - The ID of the task. // @RETURN: string - The WebSocket URL. export const getWsUrl = (taskId) => { let baseUrl = PUBLIC_WS_URL; if (!baseUrl) { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; // Use the current host and port to allow Vite proxy to handle the connection baseUrl = `${protocol}//${window.location.host}`; } return `${baseUrl}/ws/logs/${taskId}`; }; // [/DEF:getWsUrl:Function] // [DEF:fetchApi:Function] // @PURPOSE: Generic GET request wrapper. // @PRE: endpoint string is provided. // @POST: Returns Promise resolving to JSON data or throws on error. // @PARAM: endpoint (string) - API endpoint. // @RETURN: Promise - JSON response. async function fetchApi(endpoint) { try { console.log(`[api.fetchApi][Action] Fetching from context={{'endpoint': '${endpoint}'}}`); const response = await fetch(`${API_BASE_URL}${endpoint}`); if (!response.ok) { throw new Error(`API request failed with status ${response.status}`); } return await response.json(); } catch (error) { console.error(`[api.fetchApi][Coherence:Failed] Error fetching from ${endpoint}:`, error); addToast(error.message, 'error'); throw error; } } // [/DEF:fetchApi:Function] // [DEF:postApi:Function] // @PURPOSE: Generic POST request wrapper. // @PRE: endpoint and body are provided. // @POST: Returns Promise resolving to JSON data or throws on error. // @PARAM: endpoint (string) - API endpoint. // @PARAM: body (object) - Request payload. // @RETURN: Promise - JSON response. async function postApi(endpoint, body) { try { console.log(`[api.postApi][Action] Posting to context={{'endpoint': '${endpoint}'}}`); const response = await fetch(`${API_BASE_URL}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); if (!response.ok) { throw new Error(`API request failed with status ${response.status}`); } return await response.json(); } catch (error) { console.error(`[api.postApi][Coherence:Failed] Error posting to ${endpoint}:`, error); addToast(error.message, 'error'); throw error; } } // [/DEF:postApi:Function] // [DEF:requestApi:Function] // @PURPOSE: Generic request wrapper. // @PRE: endpoint and method are provided. // @POST: Returns Promise resolving to JSON data or throws on error. async function requestApi(endpoint, method = 'GET', body = null) { try { console.log(`[api.requestApi][Action] ${method} to context={{'endpoint': '${endpoint}'}}`); const options = { method, headers: { 'Content-Type': 'application/json', }, }; if (body) { options.body = JSON.stringify(body); } const response = await fetch(`${API_BASE_URL}${endpoint}`, options); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `API request failed with status ${response.status}`); } return await response.json(); } catch (error) { console.error(`[api.requestApi][Coherence:Failed] Error ${method} to ${endpoint}:`, error); addToast(error.message, 'error'); throw error; } } // [/DEF:requestApi:Function] // [DEF:api:Data] // @PURPOSE: API client object with specific methods. export const api = { getPlugins: () => fetchApi('/plugins'), getTasks: () => fetchApi('/tasks'), getTask: (taskId) => fetchApi(`/tasks/${taskId}`), createTask: (pluginId, params) => postApi('/tasks', { plugin_id: pluginId, params }), // Settings getSettings: () => fetchApi('/settings'), updateGlobalSettings: (settings) => requestApi('/settings/global', 'PATCH', settings), getEnvironments: () => fetchApi('/settings/environments'), addEnvironment: (env) => postApi('/settings/environments', env), updateEnvironment: (id, env) => requestApi(`/settings/environments/${id}`, 'PUT', env), deleteEnvironment: (id) => requestApi(`/settings/environments/${id}`, 'DELETE'), testEnvironmentConnection: (id) => postApi(`/settings/environments/${id}/test`, {}), updateEnvironmentSchedule: (id, schedule) => requestApi(`/environments/${id}/schedule`, 'PUT', schedule), getEnvironmentsList: () => fetchApi('/environments'), }; // [/DEF:api:Data] // [/DEF:api_module:Module] // Export individual functions for easier use in components export const getPlugins = api.getPlugins; export const getTasks = api.getTasks; export const getTask = api.getTask; export const createTask = api.createTask; export const getSettings = api.getSettings; export const updateGlobalSettings = api.updateGlobalSettings; export const getEnvironments = api.getEnvironments; export const addEnvironment = api.addEnvironment; export const updateEnvironment = api.updateEnvironment; export const deleteEnvironment = api.deleteEnvironment; export const testEnvironmentConnection = api.testEnvironmentConnection; export const updateEnvironmentSchedule = api.updateEnvironmentSchedule; export const getEnvironmentsList = api.getEnvironmentsList;