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.
292 lines
9.4 KiB
YAML
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:
|