Système de gestion de traductions pensé en CMS headless : un back-office pour ceux qui traduisent, une API pour ce qui consomme. Architecture - Symfony 7.4 / API Platform 4.3 / MariaDB 11.4, SPA React 19 servie en même origine — ce qui rend viable le cookie de session plutôt qu'un jeton en localStorage. - Deux APIs séparées : Management (session ou clé) et Delivery (stateless, clé seule). Les fusionner ferait porter à chaque lecture de bundle le coût de la session. - Stockage canonique en ICU MessageFormat, sérialisation par plateforme. Le format d'une plateforme ne contamine pas la base. - Publication par releases immuables ; le déploiement est un déplacement de pointeur, donc le rollback aussi. - Isolation multi-organisation par filtre Doctrine, avec un test d'architecture qui casse la CI si une entité échappe à l'invariant. Éditeur, deux vues - Par langue : source et cible, jamais douze colonnes. Grille virtualisée, saisie sans bouton « Enregistrer », panneau de contexte permanent. - Par clé : une clé, toutes ses langues empilées et repliées. Répond à « ce libellé est-il prêt partout ? ». - Mode Focus dans les deux : une file à vider, ⌘↵ pour enchaîner. - Le traducteur ne voit jamais d'ICU : pastilles de variables, un champ par catégorie CLDR de la langue cible. Administration - Deux niveaux : projet (membres, clés API, plateformes) et organisation (annuaire des comptes, création de projets). - Invitations par e-mail, jeton 256 bits stocké haché. - Désactiver un compte coupe les sessions en cours, pas seulement les connexions suivantes. - Les plateformes s'archivent ; ni elles ni les environnements ne se suppriment — la trace explique pourquoi telle clé existe. CLI tqs - PHAR autonome de 3 Mo, autoloader généré : le dépôt client ne dépend ni de Composer ni de la disponibilité de TQ-Slator. - init / push / pull / status ; le sync est non destructif par défaut et son prune est scopé plateforme. 126 tests, PHPStan niveau 8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
119 lines
4.2 KiB
YAML
119 lines
4.2 KiB
YAML
services:
|
|
php:
|
|
build:
|
|
context: .
|
|
target: app_dev
|
|
depends_on:
|
|
database:
|
|
condition: service_healthy
|
|
cache:
|
|
condition: service_started
|
|
environment:
|
|
SERVER_NAME: ':80'
|
|
# APP_ENV n'est PAS répété ici : le Dockerfile le positionne déjà, et le
|
|
# redéclarer dans Compose en ferait une variable que PHPUnit ne peut pas
|
|
# surcharger — `bin/phpunit` tournerait alors en environnement dev.
|
|
#
|
|
# Ce service est le seul à appliquer les migrations (voir entrypoint.dev.sh).
|
|
RUN_MIGRATIONS: '1'
|
|
DATABASE_URL: 'mysql://tqslator:tqslator@database:3306/tqslator?serverVersion=11.4.0-MariaDB&charset=utf8mb4'
|
|
REDIS_URL: 'redis://cache:6379'
|
|
MAILER_DSN: 'smtp://mailer:1025'
|
|
BACK_OFFICE_URL: 'http://localhost:8080'
|
|
# Caddy tente sinon d'obtenir un certificat pour un domaine public.
|
|
CADDY_GLOBAL_OPTIONS: 'auto_https off'
|
|
ports:
|
|
- '8080:80'
|
|
volumes:
|
|
- .:/app
|
|
- app_storage:/app/var/storage
|
|
restart: unless-stopped
|
|
|
|
# Consommateur Messenger : statistiques de complétion, construction des bundles,
|
|
# envoi des invitations. Séparé du serveur web pour que la publication d'une
|
|
# release ne bloque jamais une requête HTTP.
|
|
worker:
|
|
build:
|
|
context: .
|
|
target: app_dev
|
|
depends_on:
|
|
database:
|
|
condition: service_healthy
|
|
environment:
|
|
DATABASE_URL: 'mysql://tqslator:tqslator@database:3306/tqslator?serverVersion=11.4.0-MariaDB&charset=utf8mb4'
|
|
REDIS_URL: 'redis://cache:6379'
|
|
MAILER_DSN: 'smtp://mailer:1025'
|
|
BACK_OFFICE_URL: 'http://localhost:8080'
|
|
volumes:
|
|
- .:/app
|
|
- app_storage:/app/var/storage
|
|
command: ['php', 'bin/console', 'messenger:consume', 'async', '--time-limit=3600', '-vv']
|
|
# Le healthcheck hérité de l'image de base interroge l'endpoint admin de
|
|
# Caddy, que ce service ne fait pas tourner : il échouerait toujours.
|
|
#
|
|
# On ne le remplace pas. Le consommateur est PID 1 du conteneur : s'il meurt,
|
|
# le conteneur sort et `restart` le relance. Une sonde ne ferait que dupliquer
|
|
# ce que Docker sait déjà, au prix d'un paquet supplémentaire dans l'image
|
|
# (pgrep n'y est pas). Aucun service ne dépend de la santé du worker.
|
|
healthcheck:
|
|
disable: true
|
|
restart: unless-stopped
|
|
|
|
database:
|
|
image: mariadb:11.4
|
|
environment:
|
|
MARIADB_DATABASE: tqslator
|
|
MARIADB_USER: tqslator
|
|
MARIADB_PASSWORD: tqslator
|
|
MARIADB_ROOT_PASSWORD: root
|
|
command:
|
|
# Voir docs/01-architecture-proposal.md §3.6.
|
|
# La collation serveur doit correspondre à default_table_options de
|
|
# doctrine.yaml, sinon les tables créées hors migration divergent en silence.
|
|
- --character-set-server=utf8mb4
|
|
- --collation-server=utf8mb4_uca1400_ai_ci
|
|
# Nécessaire pour les index UNIQUE larges (row format DYNAMIC).
|
|
- --innodb-default-row-format=dynamic
|
|
# Journalise les requêtes de plus de 200 ms sans index : c'est le filet qui
|
|
# attrapera la requête de grille avant qu'elle ne parte en production.
|
|
- --slow-query-log=1
|
|
- --long-query-time=0.2
|
|
- --log-queries-not-using-indexes=1
|
|
healthcheck:
|
|
test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 20
|
|
start_period: 30s
|
|
ports:
|
|
- '3306:3306'
|
|
volumes:
|
|
- db_data:/var/lib/mysql
|
|
# Exécuté une seule fois, au premier démarrage sur un volume vierge.
|
|
- ./docker/mariadb/init.sql:/docker-entrypoint-initdb.d/10-test-database.sql:ro
|
|
restart: unless-stopped
|
|
|
|
cache:
|
|
image: redis:7-alpine
|
|
command: ['redis-server', '--save', '', '--appendonly', 'no']
|
|
healthcheck:
|
|
test: ['CMD', 'redis-cli', 'ping']
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 10
|
|
restart: unless-stopped
|
|
|
|
# Capture tous les e-mails sortants (invitations, notifications).
|
|
# Interface sur http://localhost:8025
|
|
mailer:
|
|
image: axllent/mailpit:latest
|
|
environment:
|
|
MP_SMTP_AUTH_ACCEPT_ANY: 1
|
|
MP_SMTP_AUTH_ALLOW_INSECURE: 1
|
|
ports:
|
|
- '8025:8025'
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
db_data:
|
|
app_storage:
|