- 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>
192 lines
8.2 KiB
Markdown
192 lines
8.2 KiB
Markdown
# ROADMAP — Sargasse-Sentry
|
|
|
|
> Checklist permanente d'avancement projet.
|
|
> Mettre à jour au fil des implémentations.
|
|
|
|
---
|
|
|
|
## Légende
|
|
|
|
- [ ] À faire
|
|
- [~] En cours
|
|
- [x] Terminé
|
|
|
|
---
|
|
|
|
## Phase 0 — Fondations
|
|
|
|
### Infrastructure & environnement
|
|
|
|
- [x] Initialisation dépôt Git (bare repo VPS + hook post-receive)
|
|
- [x] Configuration Docker (FrankenPHP + PostgreSQL/PostGIS + Redis)
|
|
- [x] Configuration Traefik (domaine radarsargasses971.com, SSL Let's Encrypt)
|
|
- [x] Variables d'environnement (.env + clés VAPID documentées)
|
|
- [x] Initialisation projet Symfony (`symfony new backend`)
|
|
- [x] Installation API Platform
|
|
- [x] Initialisation projet React (`npm create vite@latest frontend`)
|
|
- [x] Configuration MapLibre GL JS + react-map-gl
|
|
|
|
### Base de données
|
|
|
|
- [x] Extension PostGIS activée (dans la migration)
|
|
- [x] Migration : `SargassumObservation`
|
|
- [x] Migration : `SargassumForecast`
|
|
- [x] Migration : `DataIngestionJob`
|
|
- [x] Migration : `CoastalPoint`
|
|
- [x] Migration : `ImpactScore`
|
|
- [x] Migration : `UserFeedback`
|
|
- [x] Migration : `PushSubscription`
|
|
- [x] Index spatiaux (GIST) + index BTREE définis
|
|
|
|
---
|
|
|
|
## Phase 1 — MVP (Antilles, observations uniquement)
|
|
|
|
### Pipeline d'ingestion
|
|
|
|
- [x] Cron VPS (`docker exec` toutes les 3h) → `app:ingest-sentinel`
|
|
- [x] Appel API Sentinel Hub (bandes B04, B08, B11 via evalscript)
|
|
- [x] Création `DataIngestionJob` (pending → success/failed)
|
|
- [x] Retry automatique + logging erreurs
|
|
- [x] Filtrage nuages via Catalog API (seuil 60%, configurable)
|
|
- [x] Calcul AFAI dans evalscript Sentinel Hub (formule complète)
|
|
- [x] Rasterisation → raster UINT8 binaire (evalscript cloud-side)
|
|
- [x] Vectorisation GDAL (`gdal_polygonize.py`) + simplification PostGIS
|
|
- [x] Persistance `SargassumObservation`
|
|
- [ ] Génération vector tiles (Tippecanoe → `.pbf`) — différé Phase 2
|
|
|
|
### API Backend
|
|
|
|
- [x] `GET /api/spots` — liste CoastalPoints (API Platform)
|
|
- [x] `GET /api/spots/{id}` — détail (API Platform)
|
|
- [x] `GET /api/spots/{id}/score` — score + niveau + trend
|
|
- [x] `GET /api/observations?bbox=&date=&source=` — PostGIS ST_Intersects
|
|
- [x] `GET /api/observations/{id}` — détail avec geometry GeoJSON
|
|
- [x] `GET /api/forecasts` — API Platform
|
|
- [x] `POST /api/feedback` — feedback anonyme (IP hachée)
|
|
|
|
### Frontend React
|
|
|
|
- [x] Carte de base (MapLibre GL JS + fond CARTO dark)
|
|
- [x] Affichage observations GeoJSON (bbox dynamique au mouvement)
|
|
- [x] Marqueurs CoastalPoints avec sélection
|
|
- [x] SpotPanel : Spot Mode complet (score, niveau, distance, trend)
|
|
- [x] Bouton feedback terrain (confirmation/infirmation anonyme)
|
|
- [x] Build Vite → backend/public/ (SPA servie depuis la racine par Caddy)
|
|
|
|
### Données initiales
|
|
|
|
- [x] Seed `CoastalPoint` zone Antilles — 29 points (plages, ports, surf, pêche)
|
|
|
|
---
|
|
|
|
## Phase 2 — Dérive & Score
|
|
|
|
### Pipeline — simulation de dérive
|
|
|
|
- [x] Vent horaire via Open-Meteo (gratuit, sans clé, JSON)
|
|
- [x] Échantillonnage PostGIS ST_GeneratePoints (30 points internes)
|
|
- [x] Déplacement horaire itératif (modèle Stokes 3% vent)
|
|
- [x] Reconstruction ST_ConcaveHull (PostGIS 3.4)
|
|
- [x] Génération horizons H+6, H+12, H+24, H+48 avec confiance décroissante
|
|
- [x] Persistance `SargassumForecast`
|
|
- [ ] Génération vector tiles prédictions (Tippecanoe) — différé
|
|
|
|
### Calcul ImpactScore
|
|
|
|
- [x] Score par `CoastalPoint` : distance(40) + densité(30) + vitesse(20) + tendance(10)
|
|
- [x] Persistance `ImpactScore`
|
|
- [x] Mise en cache Redis (TTL 3h) via ImpactScoreService
|
|
|
|
### API Backend
|
|
|
|
- [x] `GET /api/spots/{id}/score` — score courant + horizons H+6/12/24/48
|
|
- [x] `GET /api/spots/{id}/score?at={datetime}` — score à un instant
|
|
- [x] `GET /api/forecasts?bbox=&horizon=` — prédictions par zone + horizon (API Platform)
|
|
|
|
### Frontend React
|
|
|
|
- [x] Slider temporel (Now → +6h → +12h → +24h → +48h)
|
|
- [x] Spot Mode complet par horizon (OK / Risque / Impact + confiance)
|
|
- [x] Mise à jour dynamique polygones + score sur slider
|
|
- [x] Couche forecasts (violet pointillé) vs observations (orange plein)
|
|
- [ ] Gradients radiaux densité — différé
|
|
|
|
---
|
|
|
|
## Phase 3 — Feedback & Alertes
|
|
|
|
### Boucle d'apprentissage
|
|
|
|
- [x] Bouton "Je confirme présence de sargasses"
|
|
- [x] `POST /api/feedback` — persistance `UserFeedback` anonyme
|
|
- [x] Intégration feedback dans calcul score (bonus +8 pts si présence confirmée < 6h dans rayon 20 km)
|
|
|
|
### Alertes push
|
|
|
|
- [x] Commande `app:generate-vapid-keys` (génération clés VAPID)
|
|
- [x] `GET /api/push/vapid-public-key` — clé publique VAPID
|
|
- [x] `POST /api/push/subscribe` — enregistrement abonnement push
|
|
- [x] `DELETE /api/push/subscribe` — désinscription
|
|
- [x] `PushNotificationService` — envoi WebPush VAPID, nettoyage abonnements expirés
|
|
- [x] Déclenchement alertes si score ≥ 70 (HIGH_SCORE_THRESHOLD)
|
|
- [x] Service worker (`sw.js`) — réception push + ouverture app au clic
|
|
- [x] `usePushSubscription` hook React — subscribe/unsubscribe lifecycle
|
|
- [x] Bouton "Activer les alertes" dans SpotPanel
|
|
|
|
---
|
|
|
|
## Transversal (continu)
|
|
|
|
- [x] Rate limiting endpoints publics (api_read 120/min, api_feedback 10/min, api_push 5/min)
|
|
- [x] Fallback Sentinel-2 → Sentinel-3 OLCI (mode `auto` dans IngestSentinelCommand)
|
|
- [x] Routing SPA corrigé (Caddyfile `try_files` + build vers `backend/public/`)
|
|
- [x] Vue mobile (SpotPanel bottom-sheet + TimelineSlider compact)
|
|
- [x] Post-receive hook complet (composer + npm build + migrations + cache + docker up)
|
|
- [ ] Monitoring pipeline (alertes si ingestion échoue > N fois)
|
|
- [ ] Logs structurés (Monolog JSON handler)
|
|
- [ ] Configuration Cloudflare free tier (cache tiles statiques)
|
|
- [ ] Tests fonctionnels pipeline (ingestion → score)
|
|
|
|
---
|
|
|
|
## Phase A — Blindage opérationnel *(priorité absolue)*
|
|
|
|
> Pré-requis à tout le reste. Un crash VPS sans backup = perte totale des données.
|
|
|
|
- [ ] **A1** Backup PostgreSQL automatisé — `pg_dump` quotidien compressé vers stockage externe (Backblaze B2), rétention 30 jours
|
|
- [ ] **A2** Monitoring pipeline — healthcheck externe (UptimeRobot) sur `/api/status`, alerte email si ingestion absente > 6h
|
|
- [ ] **A3** CI/CD via Gitea Actions — `phpstan + phpunit + vitest` bloquants sur chaque push ; deploy déclenché par action SSH uniquement si CI ✅ (Gitea installé sur VPS, runner à configurer)
|
|
- [ ] **A4** Vector tiles Tippecanoe — génération `.pbf` après chaque ingestion, served statiquement par Caddy ; remplacement des sources GeoJSON par sources `vector` côté frontend
|
|
|
|
---
|
|
|
|
## Phase B — Crédibilité produit
|
|
|
|
> Transforme le prototype en produit présentable à un partenaire.
|
|
|
|
- [ ] **B1** Analytics — Plausible (self-hosted sur VPS) ou Umami ; métriques sessions, pages vues, îles consultées
|
|
- [ ] **B2** Onboarding — modale premier lancement : légende polygones orange/violet, explication score, CTA "Sélectionnez un point côtier"
|
|
- [ ] **B3** Légende interactive carte — compléter le composant `Legend` existant (observations vs prédictions, niveaux de risque)
|
|
- [ ] **B4** CSP header — Content-Security-Policy adapté MapLibre GL JS + Web Workers + blob URLs
|
|
- [ ] **B5** HSTS — `Strict-Transport-Security` dans le Caddyfile
|
|
|
|
---
|
|
|
|
## Phase C — Valeur ajoutée produit
|
|
|
|
- [ ] **C1** Vue historique — date picker sur le slider temporel, chargement des observations/scores passés (données déjà en base)
|
|
- [ ] **C2** Tests d'intégration pipeline — scénario `ingestion GeoJSON synthétique → dérive → score` ; couvre le type de bug ST_Distance/detected_at
|
|
- [ ] **C3** TypeScript frontend — activation progressive sur les nouveaux fichiers, typage des interfaces API (score, horizons, patches) en priorité
|
|
- [ ] **C4** Refactoring `SargassesMap.jsx` — découpage du composant monolithique (320 lignes) en sous-composants testables
|
|
|
|
---
|
|
|
|
## Phase D — Expansion & B2B
|
|
|
|
> À démarrer seulement après A entièrement livré et B partiellement livré.
|
|
|
|
- [ ] **D1** Documenter le coût marginal d'ajout d'une zone — temps + quota Sentinel Hub + coût stockage tiles, pour chiffrer l'expansion (Yucatán, Brésil, Açores…)
|
|
- [ ] **D2** API publique documentée avec clé — endpoint `/api/v1/score` + quota + documentation OpenAPI ; ouvre la porte au B2B (offices de tourisme, collectivités, assureurs maritimes)
|
|
- [ ] **D3** Expansion géographique — première zone hors Caraïbes (candidat : côtes du Yucatán, Mexico)
|