Le site public charge l'intégralité du corpus une seconde après l'ouverture de la page (loadAllHeatPoints), pour la heatmap et la recherche. Rien ne compressait dans la chaîne — ni Fastify, ni Traefik. Ce n'était donc pas un risque théorique mais la facture de chaque visiteur, à chaque visite. Mesuré sur 100 000 groupes avec des données réalistes, en-tête Accept-Encoding d'un vrai navigateur : 21,5 Mo deviennent 1,9 Mo. gzip est préféré à brotli, contre l'intuition : br sort 2,3 Mo là où gzip fait 1,9 Mo. La qualité brotli par défaut est réglée pour la vitesse, et sur ce JSON très répétitif elle perd sur les deux tableaux à la fois — taille ET CPU. Seuil à 1 Ko, en dessous duquel compresser coûte plus que ça ne rapporte. /api/clusters plafonnait par ailleurs sa réponse à 2000 groupes ou 1000 clusters sans le dire, ni côté API ni côté client : dans une zone dense, des groupes disparaissaient de la carte sans aucun signal. La réponse porte désormais `truncated` et `limit`, et l'interface affiche un avertissement invitant à zoomer. Clé de traduction ajoutée dans les 21 langues, la convention du fichier étant une couverture complète. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| .githooks | ||
| apps | ||
| infra | ||
| scripts | ||
| .gitignore | ||
| docker-compose.dev.yml | ||
| docker-compose.test.yml | ||
| docker-compose.yml | ||
| eslint.config.js | ||
| jsconfig.json | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.js | ||
| pyproject.toml | ||
| README-CI.md | ||
| README.md | ||
| requirements-dev.txt | ||
| stryker.config.json | ||
| stryker.full.config.json | ||
| vitest.config.js | ||
| vitest.integration.config.js | ||
| vitest.mutation.config.js | ||
| vitest.mutation.full.config.js | ||
Stack metalfrom.eu
Carte des groupes de metal européens, données issues de Metal Archives (avec accord du propriétaire du site source).
Services
| Service | Rôle |
|---|---|
web |
Site public (carte Leaflet) |
admin |
Back-office (nginx + reverse-proxy vers l'API) |
api |
API Fastify (public + routes admin), migrations DB |
crawler |
Scraping incrémental/complet de Metal Archives via FlareSolverr |
flaresolverr |
Contournement Cloudflare (Chrome headless) pour le crawler |
geocoder-enqueue |
Peuple band_locations depuis bands.location_text |
geocoder-worker |
Géocodage Geoapify |
groq-worker |
Désambiguïsation LLM (Groq) des lieux non géocodés |
crawler et flaresolverr ne tournent qu'en dev pour l'instant : ils sont
absents de docker-compose.yml. La prod partage la même base, alimentée depuis
l'environnement de dev.
Déploiement
docker-compose.yml: productiondocker-compose.dev.yml: environnement de dev (branchedev, auto-déployé via webhook Forgejo → Coolify)
Base PostgreSQL + PostGIS gérée séparément par Coolify. Variables d'env : voir
infra/.env.example.
Qualité
Le déploiement se déclenche sur webhook à chaque push : rien ne s'interpose
entre git push et la mise en ligne. La porte de qualité est donc locale,
et doit être installée une fois par clone :
npm install
npm run hooks:install
| Commande | Portée |
|---|---|
npm run check |
lint + types + tests JS + ruff + pytest (~6 s) |
npm run test:e2e |
parcours Playwright du dashboard admin (~22 s) |
npm run test:integration:full |
SQL validé contre un vrai PostgreSQL+PostGIS (exige Docker) |
npm run check:full |
tout ci-dessus + mutation + audits de dépendances |
Le hook pre-push lance check puis test:e2e. .forgejo/workflows/ci.yml
existe mais reste inerte tant qu'aucun runner Forgejo n'est enregistré.
Outillage Python de test : pip install -r requirements-dev.txt.