feat: Setup projet Classeo avec infrastructure Docker et architecture DDD

Configure l'environnement de développement complet avec Docker Compose,
structure DDD 4 Bounded Contexts, et pipeline CI/CD GitHub Actions.

Corrections compatibilité CI:
- Symfony 8 nécessite monolog-bundle ^4.0 (la v3.x ne supporte que jusqu'à Symfony 7)
- ESLint v9 nécessite flat config (eslint.config.js) - le format .eslintrc.cjs est obsolète
This commit is contained in:
2026-01-30 09:55:58 +01:00
parent ddefa927c7
commit 6da5996340
125 changed files with 10032 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
api_platform:
title: 'Classeo API'
description: 'API for Classeo - School Management System'
version: '1.0.0'
# Enable OpenAPI documentation
formats:
jsonld: ['application/ld+json']
json: ['application/json']
html: ['text/html']
docs_formats:
jsonld: ['application/ld+json']
jsonopenapi: ['application/vnd.openapi+json']
html: ['text/html']
# Defaults
defaults:
stateless: true
cache_headers:
vary: ['Content-Type', 'Authorization', 'Origin']
extra_properties:
standard_put: true
rfc_7807_compliant_errors: true
pagination_items_per_page: 30
# Pagination
collection:
pagination:
enabled: true
items_per_page_parameter_name: 'itemsPerPage'

View File

@@ -0,0 +1,13 @@
framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
prefix_seed: classeo/backend
when@prod:
framework:
cache:
pools:
doctrine.system_cache_pool:
adapter: cache.adapter.system
doctrine.result_cache_pool:
adapter: cache.adapter.system

View File

@@ -0,0 +1,52 @@
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
profiling_collect_backtrace: '%kernel.debug%'
orm:
validate_xml_mapping: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
# Infrastructure mappings - keep entities separate from Domain
Administration:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Administration/Infrastructure/Persistence/Mapping'
prefix: 'App\Administration\Infrastructure\Persistence\Mapping'
alias: Administration
Scolarite:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Scolarite/Infrastructure/Persistence/Mapping'
prefix: 'App\Scolarite\Infrastructure\Persistence\Mapping'
alias: Scolarite
VieScolaire:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/VieScolaire/Infrastructure/Persistence/Mapping'
prefix: 'App\VieScolaire\Infrastructure\Persistence\Mapping'
alias: VieScolaire
Communication:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Communication/Infrastructure/Persistence/Mapping'
prefix: 'App\Communication\Infrastructure\Persistence\Mapping'
alias: Communication
controller_resolver:
auto_mapping: false
when@test:
doctrine:
dbal:
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
when@prod:
doctrine:
orm:
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool

View File

@@ -0,0 +1,8 @@
doctrine_migrations:
migrations_paths:
'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: false
organize_migrations: none
all_or_nothing: true
transactional: true
check_database_platform: true

View File

@@ -0,0 +1,25 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
csrf_protection: true
handle_all_throwables: true
http_method_override: false
trusted_proxies: '%env(TRUSTED_PROXIES)%'
trusted_hosts: '%env(TRUSTED_HOSTS)%'
# Enables session support
session:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
storage_factory_id: session.storage.factory.native
# Enable php_attributes routing
php_errors:
log: true
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file

View File

@@ -0,0 +1,17 @@
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600
user_id_claim: username
clock_skew: 0
# Automatically extracts the token from cookies
token_extractors:
authorization_header:
enabled: true
prefix: Bearer
name: Authorization
cookie:
enabled: true
name: BEARER

View File

@@ -0,0 +1,44 @@
framework:
messenger:
# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
failure_transport: failed
# Three buses: Command, Query, Event (CQRS + Event-driven)
default_bus: command.bus
buses:
command.bus:
default_middleware: true
middleware:
- doctrine_transaction
query.bus:
default_middleware: true
event.bus:
default_middleware:
allow_no_handlers: true
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
exchange:
name: classeo_messages
type: topic
queues:
messages:
binding_keys: ['#']
retry_strategy:
max_retries: 3
delay: 1000
multiplier: 2
max_delay: 60000
failed:
dsn: 'doctrine://default?queue_name=failed'
routing:
# Route your messages to the transports
# 'App\Message\YourMessage': async

View File

@@ -0,0 +1,57 @@
monolog:
channels:
- deprecation
- security
- audit
when@dev:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
formatter: monolog.formatter.json
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
when@test:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
channels: ["!event"]
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
when@prod:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
buffer_size: 50
nested:
type: stream
path: php://stderr
level: debug
formatter: monolog.formatter.json
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
deprecation:
type: stream
channels: [deprecation]
path: php://stderr
formatter: monolog.formatter.json

View File

@@ -0,0 +1,41 @@
security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
# Configure user provider when User entity is created
users_in_memory:
memory:
users:
admin: { password: 'admin', roles: ['ROLE_ADMIN'] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api:
pattern: ^/api
stateless: true
jwt: ~
main:
lazy: true
provider: users_in_memory
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/api/docs, roles: PUBLIC_ACCESS }
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
when@test:
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto
cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon

View File

@@ -0,0 +1,6 @@
twig:
file_name_pattern: '*.twig'
when@test:
twig:
strict_variables: true

View File

@@ -0,0 +1,8 @@
framework:
validation:
email_validation_mode: html5
when@test:
framework:
validation:
not_compromised_password: false