metalfrom.eu/docker-compose.dev.yml
Nicolas Fryder a7d7bc94de feat(admin): dashboard admin complet (auth forte, API, monitoring)
Nouveau service apps/admin (admin.metalfrom.eu / admin.dev.metalfrom.eu) :
- Frontend statique vanilla JS/CSS reprenant le design system du site
  (login, dashboard stats, table bands éditable, queue d'enrichissement,
  historique crawl_run, logs live, checkpoints, journal d'audit)
- nginx reverse-proxy /admin/api/* et /admin/auth/* vers le service api
  interne (same-origin côté navigateur, pas de CORS cross-site nécessaire
  pour le cookie de session)

apps/api :
- Nouvelle auth dédiée au dashboard, séparée du token BM_IMPORT_TOKEN
  existant : login bcrypt + session JWT en cookie httpOnly/secure/
  sameSite=strict, rate-limit + lockout après 5 échecs/15min, seeding
  du compte admin via env vars (jamais de mot de passe en clair en DB
  ou en git)
- Routes /admin/api/* : stats, queue (breakdown priorité identique au
  crawler Python), bands (recherche/tri/pagination/édition + audit log),
  crawl-runs, crawl-checkpoints, logs, audit-log
- trustProxy activé (Traefik + nginx en amont)

apps/crawler :
- log_event() écrit dans la nouvelle table crawl_log (run start/finish/
  erreurs) pour que le dashboard affiche les logs sans exposer le socket
  Docker (choix délibéré : pas de docker.sock monté, accès DB only)

migration 006_admin_dashboard.sql : admin_users, admin_login_attempts,
admin_audit_log, crawl_log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 22:15:13 +02:00

135 lines
3.8 KiB
YAML

services:
redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes:
- bm_dev_redis_data:/data
networks:
- coolify
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
environment:
LOG_LEVEL: info
CAPTCHA_SOLVER: none
networks:
- coolify
labels:
- traefik.enable=false
restart: unless-stopped
crawler:
build:
context: apps/crawler
environment:
DATABASE_URL: ${DATABASE_URL}
FLARESOLVERR_URL: http://flaresolverr:8191
CRAWLER_SCHED_INCREMENTAL_H: "4"
CRAWLER_SCHED_ENRICH_H: "2"
CRAWLER_SCHED_FULL_DAY: "0"
depends_on:
- flaresolverr
networks:
- coolify
labels:
- traefik.enable=false
restart: unless-stopped
geocoder-worker:
build:
context: apps/geocoder
working_dir: /app
environment:
DATABASE_URL: ${DATABASE_URL}
NOMINATIM_BASE: "https://nominatim.openstreetmap.org"
NOMINATIM_EMAIL: ${NOMINATIM_EMAIL}
NOMINATIM_USER_AGENT: ${NOMINATIM_USER_AGENT}
NOMINATIM_MIN_DELAY: "1.05"
NOMINATIM_JITTER: "0.35"
GEOCODE_MAX_PER_RUN: "5000"
command: ["python", "src/worker.py"]
networks:
- coolify
labels:
- traefik.enable=false
restart: "no"
pgadmin:
image: dpage/pgadmin4:8
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_EMAIL}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD}
PGADMIN_CONFIG_SERVER_MODE: "True"
volumes:
- bm_dev_pgadmin_data:/var/lib/pgadmin
networks:
- coolify
labels:
- traefik.enable=true
- traefik.docker.network=coolify
- traefik.http.routers.dev-pgadmin.rule=Host(`dev-pgadmin.metalfrom.eu`)
- traefik.http.routers.dev-pgadmin.entrypoints=https
- traefik.http.routers.dev-pgadmin.tls=true
- traefik.http.routers.dev-pgadmin.tls.certresolver=letsencrypt
- traefik.http.services.dev-pgadmin.loadbalancer.server.port=80
api:
build:
context: apps/api
environment:
PORT: "3000"
DATABASE_URL: ${DATABASE_URL}
BM_IMPORT_TOKEN: ${BM_IMPORT_TOKEN}
CORS_ORIGINS: ${CORS_ORIGINS:-https://dev.metalfrom.eu}
ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
ADMIN_SEED_USERNAME: ${ADMIN_SEED_USERNAME}
ADMIN_SEED_PASSWORD_HASH: ${ADMIN_SEED_PASSWORD_HASH}
networks:
- coolify
labels:
- traefik.enable=true
- traefik.docker.network=coolify
- traefik.http.routers.dev-api.rule=Host(`dev-api.metalfrom.eu`)
- traefik.http.routers.dev-api.entrypoints=https
- traefik.http.routers.dev-api.tls=true
- traefik.http.routers.dev-api.tls.certresolver=letsencrypt
- traefik.http.services.dev-api.loadbalancer.server.port=3000
admin:
build:
context: apps/admin
networks:
- coolify
labels:
- traefik.enable=true
- traefik.docker.network=coolify
- traefik.http.routers.dev-admin.rule=Host(`admin.dev.metalfrom.eu`)
- traefik.http.routers.dev-admin.entrypoints=https
- traefik.http.routers.dev-admin.tls=true
- traefik.http.routers.dev-admin.tls.certresolver=letsencrypt
- traefik.http.services.dev-admin.loadbalancer.server.port=80
web:
build:
context: apps/web
args:
API_BASE: https://dev-api.metalfrom.eu
networks:
- coolify
labels:
- traefik.enable=true
- traefik.docker.network=coolify
- traefik.http.routers.dev-web.rule=Host(`dev.metalfrom.eu`)
- traefik.http.routers.dev-web.entrypoints=https
- traefik.http.routers.dev-web.tls=true
- traefik.http.routers.dev-web.tls.certresolver=letsencrypt
- traefik.http.services.dev-web.loadbalancer.server.port=80
networks:
coolify:
external: true
name: coolify
volumes:
bm_dev_redis_data:
bm_dev_pgadmin_data: