236 lines
10 KiB
JavaScript
Executable File
236 lines
10 KiB
JavaScript
Executable File
// [DEF:api_module:Module]
|
|
// @TIER: STANDARD
|
|
// @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:getAuthHeaders:Function]
|
|
// @PURPOSE: Returns headers with Authorization if token exists.
|
|
function getAuthHeaders() {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
if (typeof window !== 'undefined') {
|
|
const token = localStorage.getItem('auth_token');
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
}
|
|
return headers;
|
|
}
|
|
// [/DEF:getAuthHeaders: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<any> - 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}`, {
|
|
headers: getAuthHeaders()
|
|
});
|
|
console.log(`[api.fetchApi][Action] Received response context={{'status': ${response.status}, 'ok': ${response.ok}}}`);
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}));
|
|
const message = errorData.detail
|
|
? (typeof errorData.detail === 'string' ? errorData.detail : JSON.stringify(errorData.detail))
|
|
: `API request failed with status ${response.status}`;
|
|
throw new Error(message);
|
|
}
|
|
if (response.status === 204) return null;
|
|
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<any> - 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: getAuthHeaders(),
|
|
body: JSON.stringify(body),
|
|
});
|
|
console.log(`[api.postApi][Action] Received response context={{'status': ${response.status}, 'ok': ${response.ok}}}`);
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}));
|
|
const message = errorData.detail
|
|
? (typeof errorData.detail === 'string' ? errorData.detail : JSON.stringify(errorData.detail))
|
|
: `API request failed with status ${response.status}`;
|
|
throw new Error(message);
|
|
}
|
|
if (response.status === 204) return null;
|
|
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: getAuthHeaders(),
|
|
};
|
|
if (body) {
|
|
options.body = JSON.stringify(body);
|
|
}
|
|
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
|
|
console.log(`[api.requestApi][Action] Received response context={{'status': ${response.status}, 'ok': ${response.ok}}}`);
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}));
|
|
const message = errorData.detail
|
|
? (typeof errorData.detail === 'string' ? errorData.detail : JSON.stringify(errorData.detail))
|
|
: `API request failed with status ${response.status}`;
|
|
console.error(`[api.requestApi][Action] Request failed context={{'status': ${response.status}, 'message': '${message}'}}`);
|
|
throw new Error(message);
|
|
}
|
|
if (response.status === 204) {
|
|
console.log('[api.requestApi][Action] 204 No Content received');
|
|
return null;
|
|
}
|
|
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 = {
|
|
fetchApi,
|
|
postApi,
|
|
requestApi,
|
|
getPlugins: () => fetchApi('/plugins'),
|
|
getTasks: (options = {}) => {
|
|
const params = new URLSearchParams();
|
|
if (options.limit != null) params.append('limit', String(options.limit));
|
|
if (options.offset != null) params.append('offset', String(options.offset));
|
|
if (options.status) params.append('status', options.status);
|
|
if (options.task_type) params.append('task_type', options.task_type);
|
|
if (options.completed_only != null) params.append('completed_only', String(Boolean(options.completed_only)));
|
|
if (Array.isArray(options.plugin_id)) {
|
|
options.plugin_id.forEach((pluginId) => params.append('plugin_id', pluginId));
|
|
}
|
|
const query = params.toString();
|
|
return fetchApi(`/tasks${query ? `?${query}` : ''}`);
|
|
},
|
|
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),
|
|
getStorageSettings: () => fetchApi('/settings/storage'),
|
|
updateStorageSettings: (storage) => requestApi('/settings/storage', 'PUT', storage),
|
|
getEnvironmentsList: () => fetchApi('/environments'),
|
|
getEnvironmentDatabases: (id) => fetchApi(`/environments/${id}/databases`),
|
|
|
|
// Dashboards
|
|
getDashboards: (envId, options = {}) => {
|
|
const params = new URLSearchParams({ env_id: envId });
|
|
if (options.search) params.append('search', options.search);
|
|
if (options.page) params.append('page', options.page);
|
|
if (options.page_size) params.append('page_size', options.page_size);
|
|
return fetchApi(`/dashboards?${params.toString()}`);
|
|
},
|
|
getDatabaseMappings: (sourceEnvId, targetEnvId) => fetchApi(`/dashboards/db-mappings?source_env_id=${sourceEnvId}&target_env_id=${targetEnvId}`),
|
|
|
|
// Datasets
|
|
getDatasets: (envId, options = {}) => {
|
|
const params = new URLSearchParams({ env_id: envId });
|
|
if (options.search) params.append('search', options.search);
|
|
if (options.page) params.append('page', options.page);
|
|
if (options.page_size) params.append('page_size', options.page_size);
|
|
return fetchApi(`/datasets?${params.toString()}`);
|
|
},
|
|
getDatasetIds: (envId, options = {}) => {
|
|
const params = new URLSearchParams({ env_id: envId });
|
|
if (options.search) params.append('search', options.search);
|
|
return fetchApi(`/datasets/ids?${params.toString()}`);
|
|
},
|
|
getDatasetDetail: (envId, datasetId) => fetchApi(`/datasets/${datasetId}?env_id=${envId}`),
|
|
|
|
// Settings
|
|
getConsolidatedSettings: () => fetchApi('/settings/consolidated'),
|
|
updateConsolidatedSettings: (settings) => requestApi('/settings/consolidated', 'PATCH', settings),
|
|
};
|
|
// [/DEF:api:Data]
|
|
|
|
// [/DEF:api_module:Module]
|
|
|
|
// Export individual functions for easier use in components
|
|
export { requestApi };
|
|
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;
|
|
export const getStorageSettings = api.getStorageSettings;
|
|
export const updateStorageSettings = api.updateStorageSettings;
|
|
export const getDashboards = api.getDashboards;
|
|
export const getDatasets = api.getDatasets;
|
|
export const getConsolidatedSettings = api.getConsolidatedSettings;
|
|
export const updateConsolidatedSettings = api.updateConsolidatedSettings;
|