Files
Classeo/compose.yaml
Mathias STRASSER 713e408773
Some checks failed
CI / Naming Conventions (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
2026-04-10 15:24:27 +02:00

292 lines
9.4 KiB
YAML

services:
# =============================================================================
# BACKEND API - PHP 8.5 + FrankenPHP
# =============================================================================
php:
build:
context: ./backend
dockerfile: Dockerfile
target: dev
container_name: classeo_php
# FrankenPHP charge les variables d'environnement système AVANT que Symfony
# ne parse le fichier .env. Sans env_file, les variables du .env ne seraient
# pas disponibles au démarrage de FrankenPHP.
# Avantage : une seule source de vérité (.env), pas de duplication.
# Note : les variables dans 'environment:' ci-dessous écrasent celles du .env
env_file:
- ./backend/.env
environment:
# Overrides pour Docker : les hostnames des services utilisent les noms
# des containers (db, redis, rabbitmq...) au lieu de localhost
# APP_ENV peut être overridé en CI pour désactiver le rate limiting (test)
APP_ENV: ${APP_ENV:-dev}
DATABASE_URL: postgresql://classeo:classeo@db:5432/classeo_master?serverVersion=18&charset=utf8
REDIS_URL: redis://redis:6379
MESSENGER_TRANSPORT_DSN: amqp://guest:guest@rabbitmq:5672/%2f/messages
MERCURE_URL: http://mercure/.well-known/mercure
MEILISEARCH_URL: http://meilisearch:7700
MAILER_DSN: ${MAILER_DSN:-smtp://mailpit:1025}
ports:
- "18000:8000" # Port externe 18000 pour eviter conflit
volumes:
- ./backend:/app:cached
- caddy_data:/data
- caddy_config:/config
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
minio-init:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/docs"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
# =============================================================================
# FRONTEND - SvelteKit + Node.js
# =============================================================================
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
target: dev
container_name: classeo_frontend
environment:
# URL de fallback, sera remplacée dynamiquement par le hostname en multi-tenant
PUBLIC_API_URL: http://localhost:18000/api
PUBLIC_API_PORT: "18000"
PUBLIC_BASE_DOMAIN: classeo.local
PUBLIC_MERCURE_URL: http://localhost:3000/.well-known/mercure
ports:
- "5174:5173" # Port externe 5174 pour eviter conflit
volumes:
- ./frontend:/app:cached
- frontend_node_modules:/app/node_modules
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:5173/"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
# =============================================================================
# DATABASE - PostgreSQL 18.1
# =============================================================================
db:
image: postgres:18.1-alpine
container_name: classeo_db
environment:
POSTGRES_DB: classeo_master
POSTGRES_USER: classeo
POSTGRES_PASSWORD: classeo
ports:
- "5433:5432" # Port externe 5433 pour eviter conflit avec PostgreSQL local
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U classeo -d classeo_master"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
# =============================================================================
# CACHE & SESSIONS - Redis 7.4
# =============================================================================
redis:
image: redis:7.4-alpine
container_name: classeo_redis
command: redis-server --appendonly yes
ports:
- "6380:6379" # Port externe 6380 pour eviter conflit avec Redis local
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
restart: unless-stopped
# =============================================================================
# MESSAGE QUEUE - RabbitMQ 4.2
# =============================================================================
rabbitmq:
image: rabbitmq:4.2-management-alpine
container_name: classeo_rabbitmq
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
ports:
- "5672:5672"
- "15672:15672"
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 30s
timeout: 10s
retries: 5
start_period: 30s
restart: unless-stopped
# =============================================================================
# REAL-TIME SSE - Mercure
# =============================================================================
mercure:
image: dunglas/mercure:latest
container_name: classeo_mercure
environment:
MERCURE_PUBLISHER_JWT_KEY: "mercure_publisher_secret_change_me_in_production"
MERCURE_SUBSCRIBER_JWT_KEY: "mercure_subscriber_secret_change_me_in_production"
SERVER_NAME: ":80"
MERCURE_EXTRA_DIRECTIVES: |
cors_origins http://localhost:5174
anonymous
ports:
- "3000:80"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost/.well-known/mercure"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
# =============================================================================
# FULL-TEXT SEARCH - Meilisearch 1.12
# =============================================================================
meilisearch:
image: getmeili/meilisearch:v1.12
container_name: classeo_meilisearch
environment:
MEILI_MASTER_KEY: "masterKey"
MEILI_ENV: "development"
ports:
- "7700:7700"
volumes:
- meilisearch_data:/meili_data
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:7700/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
# =============================================================================
# ASYNC WORKER - Symfony Messenger
# =============================================================================
worker:
build:
context: ./backend
dockerfile: Dockerfile
target: dev
container_name: classeo_worker
env_file:
- ./backend/.env
environment:
APP_ENV: ${APP_ENV:-dev}
DATABASE_URL: postgresql://classeo:classeo@db:5432/classeo_master?serverVersion=18&charset=utf8
REDIS_URL: redis://redis:6379
MESSENGER_TRANSPORT_DSN: amqp://guest:guest@rabbitmq:5672/%2f/messages
MERCURE_URL: http://mercure/.well-known/mercure
MEILISEARCH_URL: http://meilisearch:7700
MAILER_DSN: ${MAILER_DSN:-smtp://mailpit:1025}
command: php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -vv
volumes:
- ./backend:/app:cached
depends_on:
rabbitmq:
condition: service_healthy
php:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "ps aux | grep 'messenger:consume' | grep -v grep"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
deploy:
resources:
limits:
memory: 256M
restart: unless-stopped
# =============================================================================
# OBJECT STORAGE - MinIO (S3-compatible)
# =============================================================================
minio:
image: minio/minio:latest
container_name: classeo_minio
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: classeo
MINIO_ROOT_PASSWORD: classeo_secret
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
restart: unless-stopped
# Init container: creates the S3 bucket if it doesn't exist
minio-init:
image: minio/mc
container_name: classeo_minio_init
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 classeo classeo_secret &&
mc mb --ignore-existing local/classeo
"
restart: "no"
# =============================================================================
# EMAIL TESTING - Mailpit
# =============================================================================
mailpit:
image: axllent/mailpit:latest
container_name: classeo_mailpit
ports:
- "1025:1025"
- "8025:8025"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8025"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
restart: unless-stopped
# =============================================================================
# VOLUMES PERSISTANTS
# =============================================================================
volumes:
postgres_data:
redis_data:
rabbitmq_data:
meilisearch_data:
minio_data:
frontend_node_modules:
caddy_data:
caddy_config: