Commit Graph

89 Commits

Author SHA1 Message Date
Gwadaking
96dc79a297 fix: workers supervisés + tiles régénérées après ingestion et au démarrage 2026-04-14 00:31:46 -04:00
Gwadaking
e0ed71535e fix: Tippecanoe compilé depuis source (multi-stage build, absent de bookworm)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 03:44:00 -04:00
Gwadaking
9712cb54db feat: phase A — backup B2, healthcheck /api/status, vector tiles Tippecanoe
- A1: scripts/backup-postgres.sh — pg_dump quotidien compressé → Backblaze B2 (rclone), rétention 30j
- A2: StatusController retourne HTTP 503 + healthy/alertReason si dernière observation > 6h
- A4: Dockerfile installe tippecanoe, GenerateTilesCommand génère 5 tilesets (obs + h6/12/24/48), Caddyfile sert /tiles/* sans fallback SPA, SargassesMap.jsx passe en sources vector tiles statiques
- chore: backend/public/assets/ ajouté au .gitignore (build artifacts)
- chore: setup vitest frontend + ImpactScoreServiceTest (session précédente)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 03:40:32 -04:00
Gwadaking
1d8771cdcb feat: script backup PostgreSQL → Backblaze B2 2026-04-10 03:37:18 -04:00
Gwadaking
eff47f14bc test: éliminer les PHPUnit Notices (createMock → createStub)
Remplace createMock() par createStub() pour tous les collaborateurs
sans expectations configurées. Les mocks avec expects() restent des
mocks locaux créés dans le test concerné. 0 notices sur 49 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:05:48 -04:00
Gwadaking
cda9a7a8ff security: appliquer les findings de l'audit OWASP (H1→B3)
H1 — trusted_proxies RFC-1918 dans framework.yaml : rate limiting
     opérationnel derrière Traefik (IP client réelle, pas IP Traefik)

H2 — En-têtes HTTP dans Caddyfile : X-Frame-Options DENY,
     X-Content-Type-Options nosniff, Referrer-Policy, Permissions-Policy,
     suppression header Server

H3 — API Platform docs désactivés en when@prod (Swagger UI, ReDoc)

M1 — Rate limiter sur DELETE /api/push/subscribe (manquant)
M2 — Validation FILTER_VALIDATE_URL sur endpoint push avant stockage
M3 — APP_ENV=prod dans backend/.env (était dev — risque si .env.local absent)
M4 — Limite 4096 octets sur le body JSON (FeedbackController + PushController)
M5 — Service Worker : open redirect corrigé (targetUrl validé contre l'origine)

B1 — robots.txt créé (bloque /api/ et /bundles/)
B3 — --time-limit=3600 sur les workers Messenger (rotation + libération mémoire)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:49:15 -04:00
Gwadaking
0d9c85e71b feat: icône radar SVG (topbar + favicon) et titre Radar Sargasses Caraïbes
Remplace le logo PNG par un SVG radar inline (cercles concentriques +
bras rotatif + blip) en bleu #2563eb/#60a5fa sur fond #1a1a2e.
Même SVG utilisé pour favicon.svg et le picto dans la topbar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:39:00 -04:00
Gwadaking
a4d57d7961 fix: sélectionner l'observation la plus proche (pas la plus récente) pour patches et horizons
Les subqueries de loadHorizonScores() et loadPatches() triaient par
detected_at DESC, ce qui provoquait une contamination inter-zones :
une ingestion récente de Guadeloupe avec des patches à ~189km de la Barbade
écrasait les données locales pourtant plus proches.

Aligner sur la logique de loadLatestScore() (ImpactScoreService) qui
trie déjà par ST_Distance ASC — les 3 sources (score now, patches,
horizons) utilisent maintenant la même observation de référence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:30:55 -04:00
Gwadaking
50a9e49468 fix: ordre géographique N→S (St-Martin, Guadeloupe, Dominique, Martinique, Ste-Lucie, Barbade) 2026-04-09 01:07:27 -04:00
Gwadaking
81cc9736d8 feat: Dominique et Sainte-Lucie dans les filtres navigation 2026-04-09 01:06:45 -04:00
Gwadaking
d0113de4c0 fix: import OSM en 1 seule requête Overpass (évite le 429 rate limit) 2026-04-09 01:04:44 -04:00
Gwadaking
fb00ec8fa2 fix: topbar 2 lignes sur mobile (boutons îles toujours visibles), logo 44px 2026-04-09 01:02:25 -04:00
Gwadaking
38e810bd79 fix: supprimer stat Distance sargasses (redondante avec la liste des bancs) 2026-04-09 01:00:25 -04:00
Gwadaking
9175f63e39 fix: distance sargasses = min(patches) live ST_Dump, plus fiable que la valeur stockée 2026-04-09 00:57:30 -04:00
Gwadaking
9143cf857d fix: bboxes précises par île (Basse-Terre, Grande-Terre, Désirade séparés), requête Overpass élargie 2026-04-09 00:46:09 -04:00
Gwadaking
ddfa6a3ecb fix: pagination désactivée sur /spots — retourne tous les spots OSM 2026-04-09 00:37:28 -04:00
Gwadaking
8823d33eb5 fix: clic carte ferme le panel, stopPropagation sur marker, max-height panel 2026-04-09 00:35:41 -04:00
Gwadaking
3a69283983 feat: import points côtiers depuis OpenStreetMap (Overpass API) 2026-04-09 00:31:40 -04:00
Gwadaking
3c2d174edb fix: fallback S3 sur toute erreur S2 (pas seulement cloud coverage) 2026-04-09 00:21:29 -04:00
Gwadaking
354ccef917 fix: colonne requested_at (pas created_at) dans StatusController 2026-04-09 00:18:11 -04:00
Gwadaking
544ba24af2 fix: alerte critique si 0 zones ingérées + lastIngestionJob dans /api/status 2026-04-09 00:14:34 -04:00
Gwadaking
e5e835298e feat: logo dans la topbar à la place de l'emoji+texte 2026-04-08 03:09:55 -04:00
Gwadaking
90bee1bfe9 fix: rebuild propre — 1 seul bundle, plugin clean-assets avant chaque build 2026-04-08 03:08:02 -04:00
Gwadaking
9f73639f25 clean: supprimer les artifacts Vite du repo, gitignore public/assets 2026-04-08 03:00:02 -04:00
Gwadaking
1b50bd4b87 fix: import logo via Vite asset pour résoudre la résolution d'URL dev/prod 2026-04-08 02:58:56 -04:00
Gwadaking
003de64fc4 fix: logo dans frontend/public/assets pour que Vite le serve en dev et le copie au build 2026-04-08 02:56:53 -04:00
Gwadaking
cc33c63d46 add: logo top-left sur la carte, compressé à 20ko (110px)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 02:51:11 -04:00
Gwadaking
d60951cae8 fix: score basé sur l'observation la plus proche du spot, pas la dernière globale
impact_score stocke source_observation_id — la lecture trie par distance(obs→spot)
ASC pour garantir que Barbade n'écrase jamais Guadeloupe. Suppression du garde
"closest wins" au write, remplacé par un ORDER BY déterministe au read.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 02:38:48 -04:00
Gwadaking
51ca910e11 fix: l'observation la plus proche gagne — évite l'écrasement par zone distante
ImpactScoreService : ne pas écraser le score si la nouvelle observation est plus
loin que le score actuel (sauf après 6h de stale). Empêche Barbade d'écraser
la Guadeloupe avec 367km quand des sargasses sont à 8km.

SpotScoreController : horizons et bancs filtrés par proximité (<300km) du spot,
plus la dernière observation globale quelle que soit sa zone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 02:33:31 -04:00
Gwadaking
a607fd3c99 fix: Veille/ambre uniquement si sargasses à <100km, "banc" au lieu de "patch"
- scoreLabel/etaLabel/SpotMarker conditionnent Veille à distance<100km
  pour éviter l'alerte ambre sur les spots sans sargasses proches
- "patch" → "banc de sargasses" dans le SpotPanel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 02:25:51 -04:00
Gwadaking
0d47a7ee89 ux: refonte jauge — label dominant, score /100 en détail discret
- "Indice de risque" en petit header explicite au-dessus
- Le label (VEILLE / RISQUE / IMPACT) devient l'élément visuel principal (30px)
- Le nombre /100 rétrogradé en hint discret sous la barre
- Suppression de l'ambiguïté "est-ce une note ou un risque ?"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 02:03:33 -04:00
Gwadaking
364bb5dfcc fix: label "Veille" pour scores low 15-30, marqueur ambre, ETA sous surveillance
- scoreLabel() : low <15 → "OK", low ≥15 → "Veille", cohérence avec valeur numérique
- etaLabel() : score ≥15 sans ETA → "Sargasses en surveillance" (eta--watch)
- SpotMarker : étoile ambre pour level=low + scoreValue≥15 vs étoile verte pure OK
- spotScores stocke {level, value} au lieu de level seul

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 01:58:11 -04:00
Gwadaking
5dfa000247 chore: add symfony/messenger + symfony/scheduler + phpunit infrastructure
- composer.json : symfony/messenger, symfony/scheduler, symfony/test-pack
- PHPStan fix : is_string() pour narrower mixed dans buildForecastGeometry
- phpunit.dist.xml + tests/bootstrap.php + bin/phpunit pour la suite de tests
- Suppression de src/Schedule.php (schedule vide généré par Flex, inutile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 01:51:42 -04:00
Gwadaking
c89560aea2 feat: suivi par patch — décomposition MULTIPOLYGON avec distance et surface par patch
ST_Dump sur la dernière observation → 5 patches les plus proches dans 200km, exposés
dans /spots/{id}/score. SpotPanel affiche le nombre de patches et la distance+surface
de chacun sous forme de barres (échelle 0–100km).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 23:36:19 -04:00
Gwadaking
d4d2314f7b feat: explainability — décomposition des 4 composantes du score dans le SpotPanel
Expose breakdown {distance, density, velocity, trend} dans l'API /spots/{id}/score
(pour le score courant reconstruit depuis les champs stockés, et pour chaque horizon
avec confidence appliquée). Affiche les barres avec valeur/max dans le SpotPanel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 23:33:07 -04:00
Gwadaking
779c07ced0 feat: courants océaniques OSCAR dans le modèle de dérive (v3)
- OceanCurrentClient : requête ERDDAP/OSCAR sans clé API, JSON direct
  Résolution 1/3°, snap au point de grille le plus proche
  Fallback gracieux (wind-only) si OSCAR indisponible
- DriftSimulationService v3 : déplacement total = vent Stokes (3%) + courant OSCAR (100%)
  Physique correcte : les sargasses dérivent avec les courants de surface,
  pas uniquement sous l'effet du vent
  MODEL_VERSION 2.0.0 → 3.0.0
- Courant Caraïbe typique ~0.3–0.5 m/s (courant des Antilles NW) — contribution
  majeure sur 24-48h vs vent Stokes seul (~0.15-0.25 m/s)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 22:39:11 -04:00
Gwadaking
48e6e5060c feat: ingestion automatique toutes les 6h + indicateur fraîcheur UI
- Symfony Scheduler (MainSchedule) : cron 0,6,12,18h UTC
- IngestionScheduleMessage + IngestionScheduleHandler : pipeline complet
  ingestion → forecasts → impact scores pour toutes les zones Antilles
- messenger.yaml : transport scheduler_main + async Redis (rs971_async)
- entrypoint.sh : 2 workers en background (scheduler_main + async)
- GET /api/status : date/heure de la dernière observation
- SargassesMap : badge "Données du JJ/MM à HH:MM" en bas à gauche

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 22:31:58 -04:00
Gwadaking
55caca001c fix: scores horizons — même formule 4 composantes que le score now
Avant : horizons = (1 - dist/50km) × 100 × confidence — échelle incomparable
avec le score now qui utilise distance(40) + densité(30) + vitesse(20) + tendance(10).

Après : même décomposition pour les 4 horizons, en passant currentDistanceKm
pour calculer la vitesse d'approche réelle par rapport à la position courante.
La confiance s'applique sur le score total (incertitude du modèle).

Résultat : si la sargasse s'éloigne à H+6, le score baisse. Si elle approche,
le score monte — mais atténué par la confiance du modèle (0.85 à H+6).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:51:56 -04:00
Gwadaking
0014870c3b fix: pondérer le score horizon par la confiance
Sans pondération, H+6 pouvait afficher 67 quand "now" était 34 car les
deux formules n'ont pas la même échelle. Multiplier par confidence (0.85
à H+6, 0.45 à H+48) rend les scores cohérents entre horizons : un risque
ne peut pas paraître croître si c'est juste l'incertitude qui augmente.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:49:09 -04:00
Gwadaking
163c2c3a56 fix: nettoyer assets/ avant npm build — plus de bundles orphelins
rm -rf assets/ + index.html avant chaque build React pour ne garder
que les fichiers du dernier build. index.php et sw.js Symfony sont préservés.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:37:14 -04:00
Gwadaking
3136b5e7fc fix: couleur forecasts → même orange sargasses que observations
Les prévisions utilisent la même couleur (#f4a020/#c47a10) que les
observations confirmées, avec fill à 20% (vs 50%) et contour tirets.
Lecture immédiate : "sargasses futures" sans rupture visuelle arbitraire.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 21:01:46 -04:00
Gwadaking
cdc8fe90ac fix: forecasts fill opacity 0.45→0.12 — zones prédictives non opaques
Les prévisions sont des estimations approximatives : réduire le fill à 0.12
laisse voir la carte (et la terre) en dessous, communiquant visuellement
"zone probable" vs observation confirmée. Contour tirets [6,4] plus lisible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 20:43:08 -04:00
Gwadaking
a14782b067 fix: 3 bugs introduits lors du refactoring DriftSimulation/--force
1. DriftSimulationService : suppression ST_Buffer(::geography) — produisait des
   géométries invalides/dégénérées sur grand MULTIPOLYGON → ST_Distance retournait
   652 029km. Remplacé par ST_Translate + ST_SimplifyPreserveTopology uniquement.

2. ImpactScoreService : filtre MAX_RELEVANT_M (300km) — une observation distante
   ne remplace plus le score d'un spot proche. Les scores existants sont préservés
   si l'observation est hors zone pertinente.

3. ComputeForecastsCommand --force : suppression du DELETE FROM impact_score.
   On ne purge plus les scores existants ; computeForObservation ajoute les
   nouveaux scores, les anciens restent valides pour les spots éloignés.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 20:25:49 -04:00
Gwadaking
1429eaa71c fix: DriftSimulationService v2 — ST_Translate au lieu de sample+ConcaveHull
L'ancienne approche (ST_GeneratePoints × 40 + ST_ConcaveHull) avait un défaut
majeur : les points aléatoires pouvaient ne pas couvrir les patches locaux
(ex: le patch à 3.61km de Barbados), donnant des distances absurdes à H+6 (654km).

Nouvelle approche :
- ST_Translate(geometry, dx_deg, dy_deg) : translation directe du MULTIPOLYGON
  source → chaque patch individuel se déplace correctement
- ST_Buffer(... DIFFUSION_M × √horizon) : diffusion turbulente via dilatation
  (H+6 ≈ +3.7km, H+48 ≈ +10.4km) — formes qui s'élargissent sans perdre la structure
- Déterministe : pas d'aléatoire, distances ST_Distance fiables

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 20:14:22 -04:00
Gwadaking
fb25142bad feat: diffusion turbulente + recalcul forcé — polygones qui évoluent dans le temps
- DriftSimulationService : ajout diffusion turbulente 600m/h (marche aléatoire
  indépendante par point) → les patches se dispersent et déforment au fil des horizons
  au lieu d'une simple translation rigide avec les alizés constants
- SAMPLE_POINTS 30→40, CONCAVITY 0.8→0.7, MODEL_VERSION 1.0→1.1
- ComputeForecastsCommand : option --force pour supprimer et recalculer les
  forecasts existants (utile après changement de modèle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 20:03:51 -04:00
Gwadaking
492ed02b6f feat: ForecastController avec filtres bbox+horizon — polygones par horizon sur la carte 2026-04-03 19:38:22 -04:00
Gwadaking
878174404e fix: calculer ETA depuis horizons API — évite dépendance aux données stale en base 2026-04-03 19:35:21 -04:00
Gwadaking
22a3ce23a4 fix: corriger null paint et line-dasharray invalide dans cartoonizeStyle 2026-04-03 19:27:15 -04:00
Gwadaking
8bf6e20ddf fix: corriger chemin DEPLOY_DIR et docker run dans le hook 2026-04-03 19:14:19 -04:00
Gwadaking
744f76f85e feat: ETA côtier — transformer la map en outil de décision actionnable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:42:21 -04:00