Files
JWE/docs/architecture.md
T

95 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# JWE v2 — DĂ©cisions d'architecture (Agent Architecte)
Date : 2026-07-16 · Mission : refonte GeoGuessr souverain (voir `projet_geoguessr_97x.md`)
## 1. Décision back-end : SvelteKit seul (pas de Strapi)
**Décision : SvelteKit seul, avec les lieux dans un fichier JSON versionné dans Git.**
Arguments :
- **Frugalité OKI** : un seul processus Node à héberger, pas de CMS + admin + DB à maintenir, pas de surface d'attaque supplémentaire. SQLite serait de toute façon peu utile pour un corpus de ~40 lieux statiques.
- **CritÚre du prompt** (« qui ajoute les lieux ? ») : le corpus est éditorial, produit par l'Agent Contenu et relu. Le workflow Git (pull request = relecture + crédits photos vérifiés) est la modération. Le `docs/contribution.md` documente l'ajout d'un lieu par PR.
- **Livrables finaux** : le guide de dĂ©ploiement demandĂ© est « SvelteKit + Tile Server » — Strapi n'y figure pas.
- **Anti-triche** : le JSON reste cÎté serveur (`src/lib/server/`), jamais importé par le client ; les endpoints filtrent les champs. Impossible de faire plus simple.
Si un jour des contributeurs non-dev doivent saisir des lieux en masse, la porte vers Strapi reste ouverte (le schéma JSON est le content-type tout désigné), mais ce n'est pas le besoin actuel.
## 2. Architecture des tiles MapLibre
- **Rendu** : MapLibre GL JS (verrouillé), chargé en lazy (import dynamique uniquement sur les écrans de jeu / exploration).
- **Dev / dĂ©mo** : tiles vectorielles [OpenFreeMap](https://openfreemap.org) — libres, basĂ©es OpenStreetMap, sans clĂ© API. Style **custom OKI** (JSON de style propre, dĂ©rivĂ© du style Positron, re-teintĂ© : mer bleu profond caraĂŻbe, terre sable, vĂ©gĂ©tation verte dense, labels sobres — pas le « bleu Google »).
- **Prod auto-hĂ©bergĂ©e** : `tileserver-gl` + extraits `.mbtiles` OpenStreetMap des 4 rĂ©gions (Guadeloupe, Martinique, Guyane, La RĂ©union — quelques dizaines de Mo au total). ProcĂ©dure dans `docs/deploiement.md`. Le style OKI accepte l'URL des tiles par variable d'environnement (`PUBLIC_TILES_URL`), bascule sans changement de code.
- **Contrainte gĂ©ographique** : `maxBounds` = enveloppe union des 4 rĂ©gions (≈ lon −63
57, lat −23
18) + `minZoom`, donc impossible de scroller en Europe/AmĂ©rique du Nord. 4 boutons « saut de rĂ©gion » (GUADELOUPE / MARTINIQUE / GUYANE / RÉUNION) replacent la vue sur la rĂ©gion. Limite connue : l'enveloppe rectangulaire inclut l'Atlantique tropical et le BrĂ©sil — inoffensif (aucun indice sur le lieu), documentĂ©.
- Attribution © OpenStreetMap contributors affichée.
## 3. Stratégie Wikipedia / Wikidata
Principe : **rien de stocké en base**, interroge l'API en temps réel avec cache court, cÎté serveur uniquement.
Endpoint serveur : `GET /api/wiki/[qid]` (ex. `Q3077840` = Fort DelgrĂšs — l'exemple `Q2216838` du prompt initial Ă©tait erronĂ©, corrigĂ© par l'Agent Contenu)
1. `wbgetentities` sur `www.wikidata.org` → rĂ©cupĂšre le `sitelink` frwiki (et le libellĂ© fr en fallback).
2. `GET https://fr.wikipedia.org/api/rest_v1/page/summary/{titre}` → `title`, `extract`, `content_urls`, `thumbnail`.
3. Réponse normalisée : `{ qid, title, extract, url, thumbnail, description }`.
4. Pas de sitelink fr ou erreur → `404` : le front bascule sur l'Ă©cran **Cas B « Pwen blindĂ© »**.
5. **Cache** : `Map` en mémoire, TTL 24 h (+ `Cache-Control: max-age=3600`). User-Agent explicite (`JWE-OKI/x.y (contact)`) comme demandé par la politique Wikimédia.
## 4. Arborescence du projet
```
JWE/ (dĂ©pĂŽt existant — l'ancien PHP est conservĂ© tel quel)
├── app/ ← nouvelle application SvelteKit 2 / Svelte 5
│ ├── src/
│ │ ├── lib/
│ │ │ ├── server/
│ │ │ │ ├── lieux.ts (accĂšs donnĂ©es + sĂ©lection alĂ©atoire + scoring)
│ │ │ │ └── data/lieux.json (corpus 40 lieux — serveur uniquement)
│ │ │ ├── components/ (GameMap, PhotoPanel, ResultPanel, ScoreCard
)
│ │ │ ├── i18n/ (fr.ts, gcf.ts, store)
│ │ │ ├── styles/oki.css (tokens couleurs OKI)
│ │ │ └── utils/ (score.ts, geo.ts)
│ │ ├── routes/
│ │ │ ├── +layout.svelte (header, switch langue FR/CR)
│ │ │ ├── +page.svelte (accueil : 4 rĂ©gions, modes, Jouer)
│ │ │ ├── jeu/[mode]/ (Ă©cran de jeu split photo/carte)
│ │ │ ├── explorer/ (mode Aprann : carte pleine page)
│ │ │ └── api/
│ │ │ ├── round/+server.ts (GET : lieu alĂ©atoire SANITISÉ)
│ │ │ ├── guess/+server.ts (POST : {placeId, lat, lon, hints, timeMs} → score + vĂ©ritĂ©)
│ │ │ ├── lieux/+server.ts (GET : catalogue sanitise pour Explorer)
│ │ │ └── wiki/[qid]/+server.ts (GET : extrait Wikipedia, cache 24 h)
│ │ ├── app.html / app.d.ts
│ │ └── service-worker.ts (PWA)
│ ├── static/ (manifest.webmanifest, icĂŽnes, photos Ă©ventuelles)
│ └── package.json / svelte.config.js / vite.config.ts
├── docs/ (architecture, dĂ©ploiement, contribution, audit)
├── content/ (sources de travail de l'Agent Contenu : crĂ©dits photos)
└── projet_geoguessr_97x.md
```
## 5. Spécifications API REST (anti-triche)
| Endpoint | Méthode | Payload | Réponse |
|---|---|---|---|
| `/api/round?region=&mode=&exclude=` | GET | — | `{ id, nom, nom_creole, categorie, difficulte, photo{url,credit,alt}, indices[] }` — **jamais** `coordonnees`, `commune`, `region`, `wikidata_id` |
| `/api/guess` | POST | `{ id, lat, lon, hintsUsed, timeMs? }` | `{ distanceKm, score, coordonnees, commune, region, nom, wikidata_id }` |
| `/api/lieux` | GET | — | Catalogue sanitise (sans coordonnĂ©es prĂ©cises : coords arrondies Ă  0.05° pour l'affichage Explorer uniquement — mode sans score) |
| `/api/wiki/[qid]` | GET | — | `{ title, extract, url, thumbnail }` ou `404` |
RĂšgles :
- Le scoring est **recalculĂ© cĂŽtĂ© serveur** : `score = round(5000 × e^(−d/r))`, `r` selon difficultĂ© du lieu (50/15/5/1 km), pĂ©nalitĂ© −10 %/indice, bonus temps ≀ +20 % plafonnĂ© Ă  5000.
- Distance : haversine.
- Le client ne reçoit la vérité (coordonnées, commune, wikidata_id) **qu'aprÚs** la soumission du marqueur.
- `exclude` : liste d'ids dĂ©jĂ  jouĂ©s (mode DĂ©fi 5 rounds, stateless — pas de session serveur nĂ©cessaire, pas de leaderboard).
## 6. Décisions annexes
- **Adapter** : `adapter-node` (auto-hébergement sobre derriÚre nginx/caddy).
- **PWA** : manifest + service worker minimal (cache app shell, photos en stale-while-revalidate).
- **i18n** : dictionnaires maison `fr` / `gcf` (créole), store Svelte, `lang` persistée en localStorage.
- **Photos** : URLs Wikimedia Commons (Special:FilePath, `?width=1200`) vérifiées CC par l'Agent Contenu ; `<img loading="lazy">` + `srcset`.
- **A11y / calm tech** : `prefers-reduced-motion` respecté (pas de vol de marqueur), timer informatif non anxiogÚne, navigation clavier (carte : flÚches déplacent le marqueur, Entrée valide).
- **Licence code** : MIT (le dĂ©pĂŽt historique Ă©tait AGPL — la refonte est un nouveau module `app/` sous MIT comme exigĂ© par le prompt ; le `LICENSE` racine reste l'historique, un `app/LICENSE` MIT est ajoutĂ©).