Le crawler n'avait aucun arrêt propre. Sa session FlareSolverr n'est détruite qu'au moment d'en ouvrir une neuve : chaque redeploy abandonnait donc une instance Chrome persistante côté FlareSolverr. Il gère désormais SIGTERM/SIGINT, dort par tranches d'une seconde pour ne pas faire attendre une minute à l'arrêt, et rend sa session dans un finally. Le worker de géocodage ne mémorisait pas les résultats VIDES. Le commentaire annonçait « toujours mettre en cache pour éviter de rappeler », mais l'insertion était à l'intérieur du `if res:` : une requête sans résultat était re-payée à chaque tentative — jusqu'à MAX_GEO_TRIES passages, multipliés par les requêtes de repli, puis de nouveau après chaque aller-retour LLM qui remet les compteurs à zéro. Elles sont désormais mises en cache avec lat/lon NULL, et la lecture distingue « absent du cache » de « connu sans résultat ». crawl-runs/cleanup utilisait 30 minutes par défaut, plus court qu'un crawl complet Europe qui dure des heures. Le déclencher pendant un crawl légitime le marquait en erreur alors qu'il tournait toujours, et update_crawl_run_progress (filtré sur status='running') cessait silencieusement de publier : l'affichage restait figé jusqu'à la fin. Le défaut passe à 24 h, et les runs dont l'annulation est déjà demandée sont laissés au chemin coopératif. 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.