Skip to content

Commit c114736

Browse files
committed
fix: add missed components library
1 parent 4cd618d commit c114736

190 files changed

Lines changed: 7096 additions & 2 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ dist/
2222
downloads/
2323
eggs/
2424
.eggs/
25-
lib/
26-
lib64/
25+
/lib/
26+
/lib64/
2727
parts/
2828
sdist/
2929
var/

frontend/app/src/lib/api/client.js

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/**
2+
* API client for PyPSA backend
3+
*
4+
* During development, Vite will proxy /api requests to the backend
5+
*/
6+
7+
const API_BASE = '/api/v1';
8+
9+
// Track active requests to enable cancellation
10+
const activeControllers = new Map();
11+
12+
/**
13+
* Make a request to the API
14+
* @param {string} endpoint - API endpoint (e.g., '/networks')
15+
* @param {RequestInit} options - Fetch options
16+
* @param {string} cancellationKey - Optional key for request cancellation
17+
* @returns {Promise<any>}
18+
*/
19+
async function request(endpoint, options = {}, cancellationKey = null) {
20+
const url = `${API_BASE}${endpoint}`;
21+
22+
// Handle request cancellation
23+
const controller = new AbortController();
24+
if (cancellationKey) {
25+
// Cancel any previous request with the same key
26+
if (activeControllers.has(cancellationKey)) {
27+
activeControllers.get(cancellationKey).abort();
28+
}
29+
activeControllers.set(cancellationKey, controller);
30+
}
31+
32+
const config = {
33+
headers: {
34+
'Content-Type': 'application/json',
35+
...options.headers,
36+
},
37+
credentials: 'include', // Include cookies for authentication
38+
signal: controller.signal,
39+
...options,
40+
};
41+
42+
try {
43+
const response = await fetch(url, config);
44+
45+
if (!response.ok) {
46+
// Handle authentication errors
47+
if (response.status === 401 && !endpoint.includes('/auth/')) {
48+
// Redirect to login page if not authenticated (except for auth endpoints)
49+
if (typeof window !== 'undefined' && !window.location.pathname.startsWith('/login')) {
50+
window.location.href = '/login';
51+
}
52+
}
53+
54+
const error = await response.json().catch(() => ({ detail: response.statusText }));
55+
const err = new Error(error.detail || `HTTP ${response.status}: ${response.statusText}`);
56+
err.status = response.status;
57+
throw err;
58+
}
59+
60+
return await response.json();
61+
} catch (error) {
62+
// Don't log AbortError as it's expected when cancelling requests
63+
if (error.name === 'AbortError') {
64+
console.log('Request cancelled:', endpoint);
65+
return null;
66+
}
67+
console.error('API request failed:', error);
68+
throw error;
69+
} finally {
70+
// Clean up the controller
71+
if (cancellationKey && activeControllers.get(cancellationKey) === controller) {
72+
activeControllers.delete(cancellationKey);
73+
}
74+
}
75+
}
76+
77+
// Auth API
78+
export const auth = {
79+
/**
80+
* Get current authenticated user
81+
* @returns {Promise<object>} User object
82+
*/
83+
async me() {
84+
return request('/auth/me');
85+
},
86+
87+
/**
88+
* Logout (redirects to login page)
89+
*/
90+
logout() {
91+
window.location.href = '/api/v1/auth/logout';
92+
},
93+
94+
/**
95+
* Login (redirects to GitHub OAuth)
96+
*/
97+
login() {
98+
window.location.href = '/api/v1/auth/login';
99+
}
100+
};
101+
102+
// Networks API
103+
export const networks = {
104+
/**
105+
* List all networks
106+
* @param {number} skip - Number of records to skip
107+
* @param {number} limit - Number of records to return
108+
*/
109+
async list(skip = 0, limit = 100) {
110+
return request(`/networks/?skip=${skip}&limit=${limit}`);
111+
},
112+
113+
/**
114+
* Get network by ID
115+
* @param {string} id - Network UUID
116+
*/
117+
async get(id) {
118+
return request(`/networks/${id}`, {}, `network-${id}`);
119+
},
120+
121+
/**
122+
* Get network summary
123+
* @param {string} id - Network UUID
124+
*/
125+
async getSummary(id) {
126+
return request(`/networks/${id}/summary`, {}, `network-summary-${id}`);
127+
},
128+
129+
/**
130+
* Scan for new networks
131+
*/
132+
async scan() {
133+
return request('/networks/', { method: 'PUT' });
134+
},
135+
136+
/**
137+
* Get scan task status
138+
* @param {string} taskId - Task ID
139+
*/
140+
async getScanStatus(taskId) {
141+
return request(`/tasks/status/${taskId}`);
142+
},
143+
144+
/**
145+
* Reset all network data (destructive operation)
146+
*/
147+
async reset() {
148+
return request('/networks/reset', { method: 'DELETE' });
149+
},
150+
151+
/**
152+
* Delete network
153+
* @param {string} id - Network UUID
154+
*/
155+
async delete(id) {
156+
return request(`/networks/${id}`, { method: 'DELETE' });
157+
}
158+
};
159+
160+
// Statistics API
161+
export const statistics = {
162+
/**
163+
* Get statistics data for single or multiple networks
164+
* @param {string|string[]} networkIds - Network UUID(s) - single ID or array of IDs
165+
* @param {string} statistic - Statistic name (e.g., 'installed_capacity')
166+
* @param {object} parameters - Additional parameters
167+
*/
168+
async get(networkIds, statistic, parameters = {}) {
169+
// Ensure networkIds is always an array
170+
const idsArray = Array.isArray(networkIds) ? networkIds : [networkIds];
171+
const cacheKey = idsArray.length === 1 ? idsArray[0] : idsArray.sort().join(',');
172+
173+
return request('/statistics/', {
174+
method: 'POST',
175+
body: JSON.stringify({
176+
network_ids: idsArray,
177+
statistic,
178+
parameters
179+
})
180+
}, `statistics-${cacheKey}-${statistic}`);
181+
}
182+
};
183+
184+
// Plots API
185+
export const plots = {
186+
/**
187+
* Generate plot for single or multiple networks
188+
* @param {string|string[]} networkIds - Network UUID(s) - single ID or array of IDs
189+
* @param {string} statistic - Statistic name
190+
* @param {string} plotType - Plot type (e.g., 'bar', 'area')
191+
* @param {object} parameters - Additional parameters
192+
* @returns {Promise<object>} Plot data or throws error
193+
*/
194+
async generate(networkIds, statistic, plotType, parameters = {}) {
195+
// Ensure networkIds is always an array
196+
const idsArray = Array.isArray(networkIds) ? networkIds : [networkIds];
197+
const cacheKey = idsArray.length === 1 ? idsArray[0] : idsArray.sort().join(',');
198+
199+
// Include parameters in cancellation key to ensure unique requests are properly cancelled
200+
const paramsKey = JSON.stringify(parameters);
201+
202+
const response = await request('/plots/generate', {
203+
method: 'POST',
204+
body: JSON.stringify({
205+
network_ids: idsArray,
206+
statistic,
207+
plot_type: plotType,
208+
parameters
209+
})
210+
}, `plot-${cacheKey}-${statistic}-${plotType}-${paramsKey}`);
211+
212+
// Check if response is cached (synchronous) or async (task-based)
213+
if (response.task_id) {
214+
// Async response - poll for results
215+
return await this.pollTaskStatus(response.task_id);
216+
} else {
217+
// Cached response - return immediately
218+
return response;
219+
}
220+
},
221+
222+
/**
223+
* Get status of a plot generation task
224+
* @param {string} taskId - Task ID
225+
*/
226+
async getStatus(taskId) {
227+
return request(`/tasks/status/${taskId}`);
228+
},
229+
230+
/**
231+
* Poll task status until completion
232+
* @param {string} taskId - Task ID to poll
233+
* @param {number} maxAttempts - Maximum number of poll attempts
234+
* @returns {Promise<object>} Plot data when ready
235+
*/
236+
async pollTaskStatus(taskId, maxAttempts = 30) {
237+
let attempts = 0;
238+
let delay = 200; // Start with 200ms
239+
240+
while (attempts < maxAttempts) {
241+
await new Promise(resolve => setTimeout(resolve, delay));
242+
243+
const status = await this.getStatus(taskId);
244+
245+
if (status.state === 'SUCCESS' && status.result) {
246+
// Check if the result contains an error (task succeeded but operation failed)
247+
if (status.result.status === 'error') {
248+
// Create detailed error message
249+
let errorMessage = status.result.error || 'Plot generation failed';
250+
251+
// Include error details if available (stack trace + parameters)
252+
if (status.result.error_details) {
253+
const details = status.result.error_details;
254+
errorMessage += '\n\nParameters:\n' + JSON.stringify(details.parameters, null, 2);
255+
256+
if (details.stack_trace) {
257+
errorMessage += '\n\nStack Trace:\n' + details.stack_trace;
258+
}
259+
}
260+
261+
throw new Error(errorMessage);
262+
}
263+
264+
return {
265+
plot_data: status.result.data,
266+
cached: false,
267+
generated_at: status.result.generated_at,
268+
statistic: status.result.request?.statistic,
269+
plot_type: status.result.request?.plot_type
270+
};
271+
} else if (status.state === 'FAILURE') {
272+
throw new Error(status.error || 'Plot generation failed');
273+
}
274+
275+
// Exponential backoff with max 2 seconds
276+
delay = Math.min(delay * 1.5, 2000);
277+
attempts++;
278+
}
279+
280+
throw new Error('Plot generation timed out');
281+
}
282+
283+
};
284+
285+
// Cache API
286+
export const cache = {
287+
/**
288+
* Clear cache for specific network
289+
* @param {string} networkId - Network UUID
290+
*/
291+
async clearNetwork(networkId) {
292+
return request(`/cache/${networkId}`, { method: 'DELETE' });
293+
},
294+
295+
/**
296+
* Clear all cache
297+
*/
298+
async clearAll() {
299+
return request('/cache/', { method: 'DELETE' });
300+
}
301+
};
302+
303+
// Version API
304+
export const version = {
305+
/**
306+
* Get version information
307+
*/
308+
async get() {
309+
return request('/version/');
310+
}
311+
};
312+
313+
// Health check
314+
export const health = {
315+
/**
316+
* Get application health status (includes cache health)
317+
*/
318+
async check() {
319+
// Health endpoint is outside /api/v1
320+
const url = '/health';
321+
const response = await fetch(url);
322+
if (!response.ok) {
323+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
324+
}
325+
return await response.json();
326+
}
327+
};
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 25 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)