Backend : - OpenMeteoClient : vent horaire 72h (gratuit, sans clé) - DriftSimulationService : échantillonnage ST_GeneratePoints, déplacement itératif Stokes 3%, reconstruction ST_ConcaveHull, 4 horizons H+6/12/24/48, confiance décroissante - ImpactScoreService : score 4 composantes (distance/densité/ vitesse/tendance), cache Redis TTL 3h, invalidation à chaque calcul - ComputeForecastsCommand : traite les observations sans forecast - SpotScoreController : score courant + horizons depuis forecasts Frontend : - TimelineSlider : navigation Now/+6h/+12h/+24h/+48h - SargassesMap : couche observations (orange) vs forecasts (violet) rechargée à chaque changement d'étape - SpotPanel : affichage score par horizon actif + indicateur confiance - Build → backend/public/spa/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 lines
1.3 KiB
JavaScript
44 lines
1.3 KiB
JavaScript
const BASE_URL = import.meta.env.VITE_API_URL ?? '/api';
|
|
|
|
async function get(path, params = {}) {
|
|
const url = new URL(BASE_URL + path, window.location.origin);
|
|
Object.entries(params).forEach(([k, v]) => v !== undefined && url.searchParams.set(k, v));
|
|
|
|
const res = await fetch(url.toString(), {
|
|
headers: { Accept: 'application/json' },
|
|
});
|
|
|
|
if (!res.ok) throw new Error(`API error ${res.status}: ${path}`);
|
|
return res.json();
|
|
}
|
|
|
|
async function post(path, body) {
|
|
const res = await fetch(BASE_URL + path, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
if (!res.ok) throw new Error(`API error ${res.status}: ${path}`);
|
|
return res.json();
|
|
}
|
|
|
|
export const api = {
|
|
spots: {
|
|
list: () => get('/spots'),
|
|
get: (id) => get(`/spots/${id}`),
|
|
score: (id, at) => get(`/spots/${id}/score`, { at }),
|
|
},
|
|
observations: {
|
|
list: (bbox, date, source) => get('/observations', { bbox, date, source }),
|
|
get: (id) => get(`/observations/${id}`),
|
|
},
|
|
forecasts: {
|
|
list: (bbox, horizon) => get('/forecasts', { bbox, horizon }),
|
|
get: (id) => get(`/forecasts/${id}`),
|
|
},
|
|
feedback: {
|
|
create: (data) => post('/feedback', data),
|
|
},
|
|
};
|