Apparence
Annexes techniques
Les inventaires que les sections rédigées du DAT citent sans les recopier. Tout ce qui suit est relevé dans le dépôt : ni saisie manuelle, ni capture d'un environnement en particulier.
Ce que vous ne trouverez pas ici : les versions effectivement installées. Elles dépendent de l'image construite et non du dépôt, et vivent donc dans Dépendances, qui les relève à part.
Les hôtes, les ports publiés et les identifiants ne sont pas des constantes du projet : ils viennent de la configuration décrite en annexe B.
A. Services de la stack
Les conteneurs que docker-compose.yml déclare. Ce qu'ils font les uns des autres, ce dont ils dépendent et ce que leur arrêt provoque : § 04.
Les deux surcouches — docker-compose.override.yml pour le développement, docker-compose.tls.yml pour la terminaison TLS — reconfigurent ces services, elles n'en ajoutent aucun.
| Service | Rôle | Image |
|---|---|---|
proxy | nginx : unique point d'entrée HTTP, route vers le front ou l'API selon le nom de domaine | construite depuis ./infra/nginx |
certbot | Obtention et renouvellement du certificat TLS (inactif hors profil tls) | construite depuis ./infra/certbot |
frontend | Le SPA : serveur Vite en développement, build statique derrière nginx en production | construite depuis ./frontend |
docs | Portail de documentation : le contenu de docs/, construit par VitePress et servi en statique | construite depuis Dockerfile.docs |
backend | L'API Django et le back-office, servis par Gunicorn | construite depuis ./backend |
worker | Exécutant Celery : traite les tâches de fond | construite depuis ./backend |
sftpgo | Serveur SFTP isolé qui reçoit les exports CEGI et expose son administration sur localhost | drakkan/sftpgo:v2.7.5-slim@sha256:021e23c5336ebf0a12b663dcb773081f7537e0ef330cbd1534f446ce26c77faa |
beat | Ordonnanceur Celery : déclenche les tâches planifiées | construite depuis ./backend |
db | MySQL : toutes les données applicatives | mysql:8.4 |
redis | Cache et sessions (base 0), file des tâches Celery (base 1) | redis:7.4-alpine |
minio | Stockage objet compatible S3 pour les fichiers joints | minio/minio:latest |
minio-init | Conteneur éphémère qui crée le bucket au premier démarrage | minio/mc:latest |
haproxy | Accès MySQL filtré par IP, pour les outils d'administration de base de données | construite depuis ./infra/haproxy |
B. Variables d'environnement
La surface de configuration, telle que .env.example la déclare. Un déploiement copie ce fichier et le renseigne ; aucune valeur n'est codée dans les images.
Une variable marquée secret n'a pas de colonne d'exemple : ce document est communicable, et le gabarit du dépôt suffit à qui doit l'installer. Les réglages SMTP (EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_USE_TLS) sont commentés dans le gabarit : ils ne servent que si DJANGO_EMAIL_BACKEND désigne le backend SMTP.
| Variable | Rôle | Exemple |
|---|---|---|
DJANGO_SECRET_KEY | Clé de signature des sessions, des jetons de réinitialisation et des cookies | secret |
DJANGO_DEBUG | Mode debug : traces d'erreur détaillées. Impérativement désactivé hors développement | true |
ADMIN_MFA_BYPASS | Autorise le mot de passe seul dans le back-office, uniquement avec le mode debug actif | false |
DJANGO_ALLOWED_HOSTS | Noms d'hôte que le backend accepte de servir ; tout autre est rejeté | api.monoutil.localhost,monoutil.localhost,localhost,127.0.0.1,backend |
DJANGO_CORS_ALLOWED_ORIGINS | Origines autorisées à appeler l'API depuis un navigateur (CORS) | http://monoutil.localhost,http://localhost:5173 |
ENABLE_USER_IMPERSONATION | Active le bouton « Assumer l'identité » du back-office. Réservé au développement et à la recette | false |
FRONTEND_HOST | Nom d'hôte du SPA, sur lequel le reverse proxy l'aiguille | monoutil.localhost |
API_HOST | Nom d'hôte de l'API et du back-office, sur lequel le reverse proxy les aiguille | api.monoutil.localhost |
DOCS_HOST | Nom d'hôte du portail de documentation. Non renseigné, le proxy retombe sur docs.localhost | docs.apajh.localhost |
DOCS_BASIC_AUTH | Garde auth_basic du portail de documentation, au format htpasswd. Vide en développement, à renseigner sur tout environnement joignable | secret |
VITE_API_BASE_URL | URL de l'API compilée dans le build du SPA, préfixe /api compris | http://api.monoutil.localhost/api |
FRONTEND_URL | Origine du SPA, dont le backend se sert pour composer les liens qu'il envoie par courriel | http://monoutil.localhost |
VITE_ENABLE_MOCK | Sert le jeu de démonstration du planning au lieu de l'API. Non renseignée, l'écran lit la base — la démonstration ne sert qu'aux tests end-to-end et aux situations que la base ne sait pas encore produire | false |
TLS_ENABLED | Termine le TLS sur le proxy. Exige des noms d'hôte publiquement résolvables | false |
CERTBOT_EMAIL | Adresse à laquelle Let's Encrypt signale une expiration ou un incident | admin@example.org |
CERTBOT_STAGING | Utilise l'environnement de test de Let's Encrypt : certificats non fiables, pas de quota atteint | false |
DJANGO_EMAIL_BACKEND | Mode d'envoi des courriels. La valeur console les écrit dans les journaux au lieu de les envoyer | django.core.mail.backends.console.EmailBackend |
DJANGO_DEFAULT_FROM_EMAIL | Expéditeur des courriels transactionnels | no-reply@monoutil.localhost |
PASSWORD_RESET_TIMEOUT | Durée de validité d'un lien de réinitialisation, en secondes | 3600 |
MYSQL_DATABASE | Nom de la base de données applicative | apajh |
MYSQL_USER | Compte de service dont le backend se sert pour ouvrir la base | apajh |
MYSQL_PASSWORD | Mot de passe du compte de service applicatif | secret |
MYSQL_ROOT_PASSWORD | Mot de passe du compte administrateur de MySQL, utilisé à l'initialisation du volume | secret |
MYSQL_HOST | Hôte de la base, dans le réseau Docker | db |
MYSQL_PORT | Port de la base | 3306 |
REDIS_URL | Cache, sessions et compteurs de limitation de débit. Base 0 | redis://redis:6379/0 |
CELERY_BROKER_URL | File des tâches de fond. Base 1, distincte du cache pour qu'un vidage de cache ne perde pas de tâche | redis://redis:6379/1 |
AUDIT_LOG_RETENTION_DAYS | Durée de conservation des écritures API historisées, en jours | 1095 |
SFTP_PUBLISHED_PORT | Port SSH publié directement sur l'hôte pour le dépôt des exports CEGI | 2222 |
SFTPGO_ADMIN_PORT | Port WebAdmin lié à localhost et accessible au travers d'un tunnel SSH | 8088 |
SFTPGO_DEFAULT_ADMIN_USERNAME | Compte temporaire qui initialise l'administration SFTPGo | bootstrap-admin |
SFTPGO_DEFAULT_ADMIN_PASSWORD | Secret temporaire de l'administrateur initial SFTPGo | secret |
SFTP_DEPOSITOR_USERNAME | Compte SFTP en écriture seule utilisé par le système CEGI | cegi-depositor |
SFTP_DEPOSITOR_PASSWORD | Mot de passe du compte qui dépose les exports CEGI | secret |
SFTP_IMPORTER_USERNAME | Compte SFTP de service utilisé par le worker Django | django-importer |
SFTP_IMPORTER_PASSWORD | Mot de passe du compte de collecte Django | secret |
SFTP_HOST | Nom du serveur SFTP vu par le worker | sftpgo |
SFTP_PORT | Port interne du serveur SFTP | 2022 |
SFTP_HOST_PUBLIC_KEY | Clé publique SSH épinglée par le connecteur pour authentifier le serveur | secret |
SFTP_INCOMING_DIRECTORY | Répertoire distant surveillé par le collecteur | /incoming |
SFTP_CONNECT_TIMEOUT_SECONDS | Délai maximal d'établissement d'une connexion SFTP | 10 |
SFTP_MAX_FILE_SIZE_MB | Taille maximale acceptée pour un fichier d'import | 50 |
IMPORT_PROCESSED_RETENTION_DAYS | Durée de conservation des fichiers importés avec succès | 30 |
IMPORT_REJECTED_RETENTION_DAYS | Durée de conservation des fichiers rejetés et rejouables | 90 |
IMPORT_LOG_RETENTION_DAYS | Durée de conservation du journal applicatif des imports | 1095 |
PUBLIC_HOLIDAYS_API_BASE_URL | Calendrier officiel des jours fériés interrogé par la synchronisation nocturne | https://calendrier.api.gouv.fr/jours-feries |
PUBLIC_HOLIDAYS_ZONE | Territoire que cette instance dessert : seule zone synchronisée et conservée | metropole |
MINIO_ROOT_USER | Compte de service du stockage objet, qui sert aussi d'identifiant S3 au backend | apajh-minio |
MINIO_ROOT_PASSWORD | Mot de passe du compte de service du stockage objet, qui sert aussi de clé secrète S3 | secret |
S3_BUCKET_NAME | Bucket où sont déposés les fichiers joints | apajh-media |
S3_ENDPOINT_URL | Adresse du stockage objet vue depuis le réseau Docker | http://minio:9000 |
S3_PUBLIC_URL | Adresse du stockage objet publiée dans les URL rendues au navigateur | http://localhost:9000 |
S3_REGION | Région S3 déclarée. Sans effet sur MinIO, exigée par le client | us-east-1 |
ALLOWED_DB_IPS | Plages d'adresses autorisées à joindre MySQL au travers d'HAProxy | 127.0.0.1/32 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 |
C. Routes de l'API
Cette annexe ne remplace pas le schéma OpenAPI : l'API publie le sien sur /api/schema/, et une interface Swagger sur /api/docs/, qui décrivent les corps de requête, les réponses et les codes d'erreur. Elle existe pour un lecteur qui n'exécutera pas la stack, et s'arrête donc là où le schéma commence : quels chemins existent, quels verbes ils acceptent, et quelle règle d'accès chacun applique.
Les routes sont groupées par application et gardent l'ordre de config/urls.py.
Lire la colonne « Accès » — c'est la classe de permission que DRF évalue avant d'exécuter la vue, pas le rôle métier qui la satisfait. Trois formulations à distinguer :
- une classe nommée, par exemple
IsStaffOrReadOnly: la règle est déclarée par le code de la vue. Ce qu'elle vérifie est décrit au § 05. IsAuthenticated(défaut du projet) : la vue ne déclare rien et hérite du réglageDEFAULT_PERMISSION_CLASSES, qui exige un compte authentifié.- aucune : la vue déclare explicitement une liste vide. La route est publique — comme
AllowAny, par une autre écriture.
Toute route répond en plus à OPTIONS (métadonnées DRF) et à HEAD (dérivé du GET par Django) ; ces deux verbes ne sont pas répétés ligne à ligne.
Câblage du projet (config/)
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/health/ | GET | HealthView | AllowAny |
Comptes et authentification (accounts)
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/auth/token/ | POST | LoginView | aucune |
/api/auth/me/ | GET | MeView | IsAuthenticated |
/api/auth/password/reset/ | POST | PasswordResetRequestView | AllowAny |
/api/auth/password/reset/confirm/ | POST | PasswordResetConfirmView | AllowAny |
Établissements (establishment)
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/sites/ | GET, POST | SiteViewSet | IsStaffOrReadOnly |
/api/sites/<pk>/ | GET, PUT, PATCH, DELETE | SiteViewSet | IsStaffOrReadOnly |
/api/payroll-codes/ | GET, POST | PayrollCodeViewSet | IsStaffOrReadOnly |
/api/payroll-codes/<pk>/ | GET, PUT, PATCH, DELETE | PayrollCodeViewSet | IsStaffOrReadOnly |
/api/hubs/ | GET, POST | HubViewSet | IsStaffOrReadOnly |
/api/hubs/<pk>/ | GET, PUT, PATCH, DELETE | HubViewSet | IsStaffOrReadOnly |
/api/establishments/ | GET, POST | EstablishmentViewSet | IsStaffOrReadOnly |
/api/establishments/<pk>/ | GET, PUT, PATCH, DELETE | EstablishmentViewSet | IsStaffOrReadOnly |
/api/divisions/ | GET, POST | DivisionViewSet | CanManageDivisions |
/api/divisions/<pk>/ | GET, PUT, PATCH, DELETE | DivisionViewSet | CanManageDivisions |
Types de contrat (contract_types)
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/contract-types/ | GET, POST | ContractTypeViewSet | IsStaffOrReadOnly |
/api/contract-types/<pk>/ | GET, PUT, PATCH, DELETE | ContractTypeViewSet | IsStaffOrReadOnly |
Salariés (employee)
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/employees/ | GET, POST | EmployeeViewSet | EmployeeRowPermission |
/api/employees/<pk>/ | GET, PUT, PATCH, DELETE | EmployeeViewSet | EmployeeRowPermission |
/api/employees/<pk>/attachments/ | PUT | EmployeeViewSet | CanEditAttachments |
/api/employees/<pk>/emergency-contacts/ | PUT | EmployeeViewSet | CanEditEmergencyContacts |
/api/employees/<pk>/perimeter/ | GET | EmployeeViewSet | EmployeeRowPermission |
/api/contracts/ | GET, POST | ContractViewSet | EmployeeDataPermission |
/api/contracts/<pk>/ | GET, PUT, PATCH, DELETE | ContractViewSet | EmployeeDataPermission |
/api/assignments/ | GET, POST | DivisionAssignmentViewSet | EmployeeDataPermission |
/api/assignments/by-division/<division_id>/ | PUT | DivisionAssignmentViewSet | CanEditAttachments |
/api/assignments/<pk>/ | GET, PUT, PATCH, DELETE | DivisionAssignmentViewSet | EmployeeDataPermission |
Compteurs (counters)
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/counters/ | GET, PUT | CounterSummaryView | EmployeeDataPermission |
/api/leave-counters/ | GET, POST | LeaveCounterViewSet | EmployeeDataPermission |
/api/leave-counters/<pk>/ | GET, PUT, PATCH, DELETE | LeaveCounterViewSet | EmployeeDataPermission |
/api/cet-balances/ | GET, POST | CetBalanceViewSet | EmployeeDataPermission |
/api/cet-balances/<pk>/ | GET, PUT, PATCH, DELETE | CetBalanceViewSet | EmployeeDataPermission |
/api/annualization-balances/ | GET, POST | AnnualizationBalanceViewSet | EmployeeDataPermission |
/api/annualization-balances/<pk>/ | GET, PUT, PATCH, DELETE | AnnualizationBalanceViewSet | EmployeeDataPermission |
Postes
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/postes/ | GET, POST | PosteViewSet | IsAuthenticated + IsOwner |
/api/postes/<pk>/ | GET, PUT, PATCH, DELETE | PosteViewSet | IsAuthenticated + IsOwner |
Trames
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/trames/ | GET, POST | TrameViewSet | IsAuthenticated + IsOwner |
/api/trames/<pk>/ | GET, PUT, PATCH, DELETE | TrameViewSet | IsAuthenticated + IsOwner |
Planification (planning)
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/planning-assignments/ | GET, POST | PlanningAssignmentViewSet | IsManagerWorkspace |
/api/planning-assignments/scope/ | GET | PlanningAssignmentViewSet | IsManagerWorkspace |
/api/planning-assignments/<pk>/ | GET, PUT, PATCH, DELETE | PlanningAssignmentViewSet | IsManagerWorkspace |
/api/shift-overrides/ | GET, POST | ShiftOverrideViewSet | IsManagerWorkspace |
/api/shift-overrides/<pk>/ | GET, PUT, PATCH, DELETE | ShiftOverrideViewSet | IsManagerWorkspace |
/api/planning/ | GET | PlanningPeriodView | IsManagerWorkspace |
drf-spectacular
| Chemin | Verbes | Vue | Accès |
|---|---|---|---|
/api/schema/ | GET | SpectacularAPIView | AllowAny |
/api/docs/ | GET | SpectacularSwaggerView | AllowAny |
Sous-arbres montés
Ces chemins montent une application entière, dont les routes internes appartiennent au framework et ne sont pas décidées ici.
| Chemin | Application |
|---|---|
/admin/ | Administration |
D. Ordonnancement
Ce que le service beat déclenche, et à quelle heure. Les horaires sont exprimés dans le fuseau Europe/Paris : le projet désactive la conversion en UTC, donc une heure lue ici est bien celle de l'exploitation, changements d'heure compris.
Un exécutant (worker) doit tourner pour que la tâche s'exécute : beat ne fait que la déposer dans la file.
| Déclenchement | Tâche | Entrée du calendrier |
|---|---|---|
| Tous les jours à 04:17 | holidays.sync_referential | holidays-sync-referential |
47 3 * * 0 | audit.purge_expired | audit-purge-expired |
| Tous les jours à 04:37 | data_imports.poll_sftp | data-imports-poll-sftp |
| Tous les jours à 03:35 | data_imports.purge_archives | data-imports-purge-archives |