EPIDOCS API — Backend¶
Description¶
EPIDOCS est une plateforme de gestion documentaire pour EPITECH Bénin. Elle permet aux étudiants de générer leurs documents administratifs (certificats de scolarité, attestations, lettres de recommandation) et aux administrateurs de gérer les étudiants, les templates, les migrations de promotions et la génération en lot.
- Repository : EpitechAfrik/epidocs-api
- URL production : https://api.mydocs.epitools.bj
- Port :
7010(hôte) →8000(conteneur) - Registry :
ghcr.io/epitechafrik/epidocs-api:deploy
Fonctionnalités principales¶
- Authentification Azure AD : SSO Microsoft pour étudiants et admins, sessions JWT en cookie HttpOnly
- Génération de documents bilingues : templates Word (.docx) en FR et EN remplis dynamiquement, convertis en PDF avec QR code de vérification. Chaque document partage un seul tracking code pour ses deux versions.
- Support multi-campus : chaque campus peut avoir ses propres templates de documents (surcharge
CampusDocumentType). Si un campus est associé à l'étudiant, ses templates priment sur les templates globaux. - Génération en lot (batch) : génération de documents pour toute une promotion, export en ZIP
- Gestion des étudiants : import CSV, migration de promotions (PGE1→PGE2, etc.), suspension de comptes
- Demandes de modification : les étudiants peuvent demander la correction de leurs informations personnelles
- Réclamations : système de tickets avec pièces jointes
- Mode maintenance : middleware qui bloque l'accès avec maintenance planifiée
- Annonces : système d'annonces ciblées par classe ou global
- Notes d'information : upload de PDFs (convocations, plannings) ciblés par classe, téléchargeables par les étudiants
- Audit log : traçabilité de toutes les actions admin
- Intégration service-à-service (Zeno) : API dédiée permettant à un service externe (Zeno) de lister, consulter, générer et télécharger les documents des étudiants via une clé API (sans session navigateur). Comptes de service et clés gérés depuis le dashboard. Doc OpenAPI live (ReDoc/Swagger).
Architecture¶
Stack technique¶
| Composant | Technologie |
|---|---|
| Framework | Django 5 + Django REST Framework |
| Base de données | PostgreSQL 16 |
| Authentification | JWT (cookie HttpOnly) + Azure AD (MSAL) ; clé API pour les services externes |
| Génération de documents | docxtpl + LibreOffice (conversion PDF) |
| QR Codes | qrcode + Pillow |
| Stockage fichiers | Local (media/) ou Cloudinary (remote) |
| Conteneur | Docker (python:3.12 + LibreOffice) |
| WSGI | Gunicorn (3 workers, timeout 120s) |
| API format | CamelCase JSON (djangorestframework-camel-case) |
Routes API¶
Authentification (/auth/, /student/auth/)¶
| Méthode | Route | Description |
|---|---|---|
| GET | /auth/sign_in |
Initie le flow Azure AD |
| GET | /auth/callback |
Callback OAuth Azure AD |
| GET | /auth/me |
Infos de l'admin connecté |
| POST | /auth/sign_out |
Déconnexion |
| GET | /student/auth/me |
Infos de l'étudiant connecté |
Documents (/documents/)¶
| Méthode | Route | Description |
|---|---|---|
| GET | /documents/available-document-types |
Documents disponibles pour une classe |
| POST | /documents/generate-document |
Générer un document (étudiant) — retourne le PDF FR |
| GET | /documents/download-en/:tracking_code |
Télécharger la version EN d'un document existant |
| GET | /documents/history |
Historique des documents générés |
| GET | /documents/tracking/:doc_ref |
Vérification d'un document via QR code |
| POST | /documents/admin/generate-for-student |
Générer un doc pour un étudiant (admin) |
| POST | /documents/admin/batch-generate |
Génération en lot pour une classe (admin) |
| GET | /documents/admin/dashboard/stats/ |
Statistiques du dashboard |
| GET | /documents/admin/student-history/:email/ |
Historique d'un étudiant (admin) |
| DELETE | /documents/admin/delete-document/:tracking_code/ |
Supprimer un document |
| GET/POST | /documents/admin/info-notes |
Lister / créer des notes d'information |
| GET/PUT/DELETE | /documents/admin/info-notes/:id |
Détail / modifier / supprimer une note |
| PATCH | /documents/admin/info-notes/:id/toggle |
Activer / désactiver une note |
| GET | /documents/info-notes/:id/download |
Télécharger le PDF d'une note (étudiant) |
Étudiants (/student/)¶
| Méthode | Route | Description |
|---|---|---|
| POST | /student/upload-csv/ |
Import d'étudiants via CSV |
| POST | /student/upload-tepitech-csv/ |
Import des scores TEPITECH |
| POST | /student/migrate-students/ |
Migration de promotion |
| GET | /student/admin/list/ |
Liste paginée des étudiants (filtres: section, annee, search, login) |
| POST | /student/admin/set-graduation-status/ |
Marquer des étudiants PGE3/PGE5 comme diplômés en masse |
| POST | /student/pending-students |
Ajouter un étudiant en attente |
| POST | /student/validate-students |
Valider les étudiants en attente |
| POST | /student/info-change-request/ |
Demande de modification (étudiant) |
| GET | /student/admin/info-change-requests/ |
Liste des demandes (admin) |
Administration (/admin/)¶
| Méthode | Route | Description |
|---|---|---|
| GET/PUT | /admin/system-status |
État du système / mode maintenance |
| GET/POST | /admin/announcements |
Gestion des annonces |
| GET | /admin/audit-logs |
Journal d'audit |
| GET | /admin/analytics |
Tableau de bord analytique |
| POST | /admin/backup/export |
Export de la base de données |
| POST | /admin/backup/import |
Import de la base de données |
Réclamations (/complaints/)¶
| Méthode | Route | Description |
|---|---|---|
| GET/POST | /complaints/student |
Réclamations de l'étudiant |
| GET/POST | /complaints/admin |
Gestion des réclamations (admin) |
Intégration Zeno — service-à-service (/integrations/v1/)¶
Authentification par clé API : header Authorization: Api-Key <clé> (pas de cookie). Réponses JSON camelCase, rate limit 1000 req/h par clé, accès global (tous étudiants / tous campus).
| Méthode | Route | Description |
|---|---|---|
| POST | /integrations/v1/documents/generate |
Générer (ou réutiliser) un doc — body { studentLogin, docId }, renvoie trackingCode + URLs de téléchargement |
| GET | /integrations/v1/students/:login/documents |
Lister les documents générés d'un étudiant (filtre ?section=) |
| GET | /integrations/v1/students/:login/available-documents |
Types de documents générables selon la classe de l'étudiant |
| GET | /integrations/v1/documents/:tracking_code |
Métadonnées + URLs de téléchargement fraîches |
| GET | /integrations/v1/documents/:tracking_code/download |
Télécharger le PDF (?lang=fr\|en, storage-agnostic) |
Documentation live (publique, sans clé) :
| Format | URL |
|---|---|
| ReDoc | /integrations/v1/docs |
| Swagger UI | /integrations/v1/swagger |
| Schéma OpenAPI | /integrations/v1/schema.json |
Administration des comptes & clés (session admin JWT, super-admin uniquement) :
| Méthode | Route | Description |
|---|---|---|
| GET/POST | /integrations/v1/admin/service-accounts |
Lister / créer un compte de service (la création renvoie la clé une seule fois) |
| GET/PATCH/DELETE | /integrations/v1/admin/service-accounts/:id |
Détail / modifier (isActive, name…) / supprimer |
| POST | /integrations/v1/admin/service-accounts/:id/keys |
Émettre une nouvelle clé |
| POST | /integrations/v1/admin/keys/:id/revoke |
Révoquer une clé |
| POST | /integrations/v1/admin/keys/:id/rotate |
Révoquer la clé et en émettre une nouvelle |
Clé en clair affichée une seule fois : seules les opérations de création / émission / rotation renvoient le champ
apiKey. Le backend ne stocke que le hash SHA-256. La création en CLI est possible :python manage.py create_service_account zeno.
Modèle de données¶
Student
├── login (PK, email EPITECH)
├── civilite, nom, prenom
├── date_naissance, ville_naissance, nationalite
├── section (PGE1, PGE2, PGE3, etc.)
├── promo, annee, ville_etude
├── inscription_year, inscription_month
├── nom_pere, prenom_pere, nom_mere, prenom_mere
├── specialite
├── PGE3_status, PGE5_status (success/null)
├── campus → FK(Campus, nullable)
└── account_suspended (0/1)
User (Admin)
├── email (PK), first_name, last_name
├── is_staff, is_superuser
├── rights → ManyToMany(Right)
└── azure_id
Campus
├── name (ex: "Bénin")
├── code (ex: "BJ")
├── director_title (titre du directeur en FR)
├── director_title_en (titre du directeur en EN)
└── extra_fields (JSON — champs supplémentaires par campus)
CampusDocumentType
├── campus → FK(Campus)
├── document_type → FK(DocumentType)
├── modele (template FR spécifique au campus, optionnel)
└── modele_en (template EN spécifique au campus, optionnel)
DocumentType
├── doc_id (certificat_scolarite, attestation_passage_pge, etc.)
├── modele (fichier .docx template FR)
├── modele_en (fichier .docx template EN, optionnel)
├── is_active
└── created_by → FK(User)
TrackingCode
├── tracking_code (PK, UUID)
├── doc_ref (REF/CS/abc/def)
├── generated_by → FK(Student)
└── generated_by_admin → FK(User)
GenDocumentHistory
├── tracking_code → OneToOne(TrackingCode)
├── doc_id, current_classe
├── generation_count
├── remote_url (chemin local ou Cloudinary public_id — version FR)
├── remote_url_en (chemin local ou Cloudinary public_id — version EN, nullable)
├── generated_by → FK(Student)
└── generated_by_admin → FK(User)
UserSession
├── session_id (UUID)
├── user → FK(User) ou student → FK(Student)
├── role (admin/student)
├── is_valid, expires_at
└── ip_address, user_agent
AuditLog
├── action (login, document_generated, document_downloaded, batch_generate_by_admin,
│ info_note_created/deleted, service_account_created/updated/deleted,
│ service_key_issued/revoked/rotated, etc.)
├── actor_email, actor_role (admin/superadmin/student/service)
├── target_type, target_id
├── details (JSON)
└── ip_address, timestamp
ServiceAccount (intégration Zeno)
├── name (unique, ex: "zeno")
├── description
├── is_active
└── created_at
ServiceApiKey
├── service → FK(ServiceAccount)
├── prefix (8 premiers caractères, pour identification)
├── key_hash (SHA-256 — la clé brute n'est jamais stockée)
├── is_active, revoked_at
├── last_used_at
└── created_at
SystemStatus
├── is_maintenance (bool)
├── maintenance_message
├── scheduled_start, scheduled_end
└── updated_by → FK(User)
Announcement
├── title, message
├── target (all/students/admins)
├── target_class (PGE1, PGE2, etc.)
├── priority (info/warning/danger)
├── is_active, start_date, end_date
└── created_by → FK(User)
InfoNote
├── title, description
├── pdf_file (upload PDF)
├── target_classes (JSONField, ex: ["PGE1", "PGE2"])
├── is_active
├── created_by → FK(User)
├── created_at, updated_at
└── downloads → InfoNoteDownload[]
InfoNoteDownload
├── info_note → FK(InfoNote)
├── student → FK(Student)
└── downloaded_at
Système de permissions¶
| Droit | Description | Superadmin | Admin |
|---|---|---|---|
can_upload_tepitech |
Import des scores TEPITECH | oui | oui |
can_upload_user |
Import CSV des étudiants | oui | oui |
can_generate |
Générer des documents | oui | oui |
can_generate_for_student |
Générer pour un étudiant | oui | oui |
can_migrate_students |
Migrer les promotions | oui | non |
can_batch_generate |
Génération en lot | oui | non |
Les droits sont initialisés par la commande python manage.py seed_rights (exécutée automatiquement au démarrage).
Types de documents¶
| ID | Nom | Classes autorisées | Condition |
|---|---|---|---|
certificat_scolarite |
Certificat de scolarité | Toutes | Étudiant actif (non diplômé) |
attestation_scolarite |
Attestation de scolarité | PGE3, PGE5 | PGE3_status=success ou PGE5_status=success |
attestation_passage_pge |
Attestation de passage PGE | PGE2+ | — |
attestation_validation_tepitech |
Attestation TEPITECH | Toutes | Score TEPITECH requis |
attestation_fin_succes_bachelor |
Attestation fin de Bachelor | PGE3 | PGE3_status=success |
attestation_fin_success_master |
Attestation fin de Master | PGE5 | PGE5_status=success |
lettre_recommandation |
Lettre de recommandation | PGE2+ | — |
attestation_passage_coding_academy |
Attestation Coding Academy | CodingAcademy | — |
Logique diplômé PGE3/PGE5 : dès que
PGE3_statusouPGE5_statusestsuccess, l'étudiant ne peut plus générer lecertificat_scolarite. Il accède à la place à l'attestation_scolarite. Le statut est posé par l'admin via la routePOST /student/admin/set-graduation-status/.
Diagrammes¶
Architecture globale¶
graph TB
Student[Étudiant / Navigateur]
Admin[Admin / Navigateur]
Frontend["Frontend React<br/>api.mydocs.epitools.bj"]
Nginx["Nginx<br/>Reverse proxy + TLS"]
Backend["Backend API<br/>Django :8000"]
PostgreSQL["PostgreSQL 16<br/>(conteneur)"]
AzureAD["Azure AD<br/>(SSO Microsoft)"]
LibreOffice["LibreOffice<br/>(conversion PDF)"]
Media["Media<br/>(bind mount)"]
Student --> Nginx
Admin --> Nginx
Nginx --> Frontend
Nginx --> Backend
Backend --> PostgreSQL
Backend --> AzureAD
Backend --> LibreOffice
Backend --> Media
Flux de génération de document¶
sequenceDiagram
participant User as Étudiant/Admin
participant API as Backend API
participant DB as PostgreSQL
participant TPL as DocxTemplate
participant LO as LibreOffice
participant FS as Stockage (media/)
User->>API: POST /documents/generate-document
API->>DB: Vérifier historique existant
alt Document déjà généré (FR + EN)
API->>FS: Récupérer le PDF FR existant
FS-->>API: PDF
API-->>User: PDF + X-Has-En: true + X-Tracking-Code
else Document déjà généré (FR seulement, template EN maintenant dispo)
API->>TPL: Remplir template EN (.docx)
TPL-->>API: .docx EN rempli
API->>LO: Convertir .docx → .pdf EN
API->>FS: Sauvegarder PDF EN
API->>DB: Mettre à jour remote_url_en (même tracking code conservé)
API->>FS: Récupérer le PDF FR existant
API-->>User: PDF FR + X-Has-En: true + X-Tracking-Code
else Nouveau document
API->>DB: Résoudre template (campus override si applicable)
API->>DB: Générer tracking code + référence
API->>TPL: Remplir template FR .docx
TPL-->>API: .docx FR rempli
API->>LO: Convertir .docx → .pdf FR
API->>TPL: Remplir template EN .docx (si disponible)
TPL-->>API: .docx EN rempli
API->>LO: Convertir .docx → .pdf EN
API->>API: Ajouter QR code, nettoyer pages vides
API->>FS: Sauvegarder PDF FR + PDF EN
API->>DB: Créer GenDocumentHistory (remote_url + remote_url_en)
API-->>User: PDF FR + X-Has-En: true/false + X-Tracking-Code
end
Headers de réponse : chaque génération retourne
X-Tracking-Code(code de suivi) etX-Has-En(true/false). Le frontend utiliseX-Has-Enpour afficher ou non le bouton de téléchargement EN.Résolution des templates (campus) : avant la génération,
resolve_template_paths()vérifie si l'étudiant a un campus associé et si ce campus a unCampusDocumentTypeconfiguré pour le document demandé. Si oui, les templates du campus priment sur les templates globaux duDocumentType.
Flux de génération en lot (batch)¶
sequenceDiagram
participant Admin as Admin
participant API as Backend API
participant DB as PostgreSQL
participant Gen as Génération unitaire
participant ZIP as Archive ZIP
Admin->>API: POST /documents/admin/batch-generate
API->>DB: Récupérer tous les étudiants de la classe
loop Pour chaque étudiant
API->>DB: Vérifier si document existe
alt Existe
API->>Gen: Récupérer document existant
else N'existe pas
API->>Gen: Générer nouveau document
end
Gen-->>API: PDF
API->>ZIP: Ajouter au ZIP
end
API->>DB: AuditLog (batch_generate_by_admin)
API-->>Admin: ZIP (Content-Disposition: attachment)
Middleware de maintenance¶
graph TD
Request[Requête entrante] --> CheckPath{Path exempté ?}
CheckPath -->|Oui| Pass[Laisser passer]
CheckPath -->|Non| CheckMaintenance{Mode maintenance actif ?}
CheckMaintenance -->|Non| Pass
CheckMaintenance -->|Oui| CheckAdmin{Utilisateur admin ?}
CheckAdmin -->|Oui| Pass
CheckAdmin -->|Non| Block[503 Service Unavailable]
Flux des notes d'information¶
Les notes d'information permettent aux admins de publier des PDFs (convocations, plannings, circulaires) ciblés par classe. Contrairement aux documents générés, il n'y a ni template docx, ni QR code, ni tracking code — juste un PDF uploadé et téléchargeable.
sequenceDiagram
participant Admin as Admin
participant API as Backend API
participant DB as PostgreSQL
participant FS as Stockage (media/info_notes/)
Admin->>API: POST /documents/admin/info-notes<br/>(multipart: title, pdf_file, target_classes)
API->>FS: Sauvegarder le PDF
API->>DB: Créer InfoNote
API-->>Admin: 201 Created
Note over Admin,API: Plus tard, l'admin peut toggle le statut
Admin->>API: PATCH /documents/admin/info-notes/:id/toggle
API->>DB: Inverser is_active
API-->>Admin: {id, isActive: false}
sequenceDiagram
participant Student as Étudiant (PGE1)
participant API as Backend API
participant DB as PostgreSQL
participant FS as Stockage
Student->>API: GET /documents/available-document-types?classe=PGE1
API->>DB: Récupérer docs + InfoNote actives pour PGE1
API-->>Student: {documents: [...], infoNotes: [{id, title, description}]}
Student->>API: GET /documents/info-notes/1/download
API->>DB: Vérifier classe étudiant ∈ target_classes
API->>DB: Enregistrer InfoNoteDownload
API->>FS: Lire le PDF
API-->>Student: PDF (Content-Disposition: attachment)
Paths exemptés du mode maintenance :
/auth/sign_in,/auth/callback,/auth/me/student/auth/me/admin/system-status,/admin/announcements
Flux d'intégration Zeno (service-à-service)¶
Zeno (service backend externe) consomme l'API EPIDOCS avec une clé API. Aucun cookie/session : l'authentification se fait par le header Authorization: Api-Key <clé>, validé contre le hash stocké. La génération réutilise exactement le moteur des documents étudiants/admin (generate_document_for_student).
sequenceDiagram
participant Zeno as Zeno (service)
participant API as Backend API
participant Auth as ServiceAccountAuthentication
participant Gen as Service génération
participant DB as PostgreSQL
participant FS as Stockage (media/)
Zeno->>API: POST /integrations/v1/documents/generate<br/>Authorization: Api-Key xxx
API->>Auth: Vérifier la clé (hash SHA-256, active, non révoquée)
Auth-->>API: ServiceAccount (sinon 401)
API->>Gen: generate_document_for_student(student, docId)
Gen->>DB: Vérifier classe + historique existant
alt Document déjà généré
Gen-->>API: GenDocumentHistory existant
else Nouveau
Gen->>FS: Générer + sauvegarder PDF (FR/EN)
Gen->>DB: Créer GenDocumentHistory
end
API->>DB: AuditLog (document_generated, role=service)
API-->>Zeno: JSON { trackingCode, downloadUrl, downloadUrlEn, ... }
Zeno->>API: GET /integrations/v1/documents/:tc/download?lang=fr
API->>Auth: Vérifier la clé
API->>FS: retrieve_student_document (local ou Cloudinary)
API->>DB: AuditLog (document_downloaded, role=service, IP)
API-->>Zeno: PDF (application/pdf)
Audit : chaque génération et chaque téléchargement effectués par un service sont tracés dans
AuditLogavecactor_role = service(incluanttrackingCode,studentLogin, langue et IP). Les lectures de métadonnées (liste/détail) ne sont volontairement pas tracées pour éviter le bruit.Gestion des clés : les comptes de service et leurs clés se gèrent depuis le dashboard (super-admin). Une clé peut être révoquée ou régénérée (rotate) à tout moment, coupant immédiatement l'accès du service concerné.
Docker¶
Dockerfile¶
L'image utilise python:3.12 avec LibreOffice installé (nécessaire pour la conversion DOCX → PDF). L'application ne tourne pas en tant qu'utilisateur non-root (à améliorer).
docker-compose.yml (production)¶
services:
db:
restart: always
image: postgres:16
container_name: epidocs_db
volumes:
- epidocs-api_postgres_data:/var/lib/postgresql/data/
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_DB: ${POSTGRES_DB:-epidocs_db}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
retries: 5
start_period: 30s
web:
image: ghcr.io/epitechafrik/epidocs-api:deploy
container_name: epidocs_api
restart: unless-stopped
ports:
- "7010:8000"
env_file:
- .env
volumes:
- ./media:/epidocs/media
- static_data:/epidocs/staticfiles
command: >
bash -c "python manage.py migrate &&
python manage.py seed_rights &&
python manage.py collectstatic --noinput &&
gunicorn project.wsgi:application --bind 0.0.0.0:8000 --workers 3 --timeout 120"
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
epidocs-api_postgres_data:
external: true
static_data:
Points importants :
- Volume externe
epidocs-api_postgres_data: les données PostgreSQL persistent entre les déploiements. Ce volume doit exister avant le premierdocker compose up. - Bind mount
./media: les templates Word et les documents générés sont stockés sur le disque du VPS, pas dans le conteneur. Cela permet de conserver les templates entre les déploiements. - Startup : au démarrage, le conteneur exécute
migrate+seed_rights+collectstaticavant de lancer Gunicorn. - Port :
7010côté hôte, accessible uniquement via Nginx. - Le port PostgreSQL n'est pas exposé sur l'hôte (sécurité).
Variables d'environnement¶
Django¶
| Variable | Description | Exemple |
|---|---|---|
DJANGO_ENV |
Mode d'exécution | production |
SECRET_KEY |
Clé secrète Django | chaîne aléatoire longue |
ALLOWED_HOSTS |
Hôtes autorisés (séparés par virgule) | api.mydocs.epitools.bj,localhost |
Base de données¶
| Variable | Description | Exemple |
|---|---|---|
POSTGRES_DB |
Nom de la base | epidocs_db |
POSTGRES_USER |
Utilisateur PostgreSQL | postgres |
POSTGRES_PASSWORD |
Mot de passe PostgreSQL | CHANGE_ME |
POSTGRES_HOST |
Hôte PostgreSQL | db (nom du service Docker) |
Authentification¶
| Variable | Description | Exemple |
|---|---|---|
JWT_SECRET_KEY |
Clé secrète JWT (optionnel, défaut = SECRET_KEY) | chaîne aléatoire |
JWT_COOKIE_DOMAIN |
Domaine du cookie (prod uniquement) | .epitools.bj |
Azure AD¶
| Variable | Description | Exemple |
|---|---|---|
AZURE_CLIENT_ID |
Client ID de l'App Registration | UUID |
AZURE_CLIENT_SECRET |
Client Secret Azure | CHANGE_ME |
AZURE_TENANT_ID |
Tenant ID Azure | UUID |
CALLBACK_URL |
URL de callback OAuth | https://api.mydocs.epitools.bj/auth/callback |
URLs¶
| Variable | Description | Exemple |
|---|---|---|
WEB_URL |
URL du frontend | https://app.epitools.bj |
SITE_URL |
URL de l'API | https://api.mydocs.epitools.bj |
Email (optionnel)¶
| Variable | Description | Exemple |
|---|---|---|
RESEND_API_KEY |
Clé API Resend | re_xxxxx |
SMTP_SERVER |
Serveur SMTP | smtp.example.com |
SMTP_PORT |
Port SMTP | 465 |
SMTP_USERNAME |
Utilisateur SMTP | no-reply@epitech.bj |
SMTP_PASSWORD |
Mot de passe SMTP | CHANGE_ME |
Cloudinary (si STORAGE_TYPE=remote)¶
| Variable | Description | Exemple |
|---|---|---|
CLOUDINARY_CLOUD_NAME |
Nom du cloud | my-cloud |
CLOUDINARY_API_KEY |
Clé API | 123456 |
CLOUDINARY_API_SECRET |
Secret API | CHANGE_ME |
Déploiement¶
Pipeline CI/CD¶
Le déploiement est déclenché automatiquement sur push vers la branche deploy via GitHub Actions.
graph LR
Push["Push sur deploy"] --> CI["CI: Build & Test"]
CI --> Docker["Build image Docker"]
Docker --> GHCR["Push sur GHCR"]
GHCR --> Deploy["SCP + SSH sur VPS"]
Deploy --> Health["Health check"]
Étapes détaillées :
- CI : Python 3.12, PostgreSQL 16 de test, linting (flake8), vérification des migrations, tests
- Build Docker : construction de l'image et push sur
ghcr.io/epitechafrik/epidocs-api:deploy - Déploiement : SCP du
docker-compose.prod.ymlet.envsur le VPS, puisdocker compose pull && docker compose up -d - Health check : vérification que le conteneur répond sur le port 7010
Secrets GitHub Actions¶
| Secret | Description |
|---|---|
SSH_PRIVATE_KEY |
Clé SSH pour accès au VPS |
VPS_IP |
Adresse IP du VPS |
VPS_USER |
Utilisateur SSH (root) |
ENV_FILE |
Contenu complet du fichier .env |
GITHUB_TOKEN |
Token pour GHCR (automatique) |
Rollback¶
Quand faire un rollback¶
- Le déploiement a cassé une fonctionnalité critique
- Le health check échoue après déploiement
- Régression détectée en production
Procédure¶
# 1. Se connecter au VPS
ssh root@VPS_IP
# 2. Aller dans le répertoire du projet
cd /root/projects/mydocs/epidocs-api
# 3. Voir les images disponibles
docker images ghcr.io/epitechafrik/epidocs-api --format "table {{.Tag}}\t{{.CreatedAt}}"
# 4. Modifier le tag dans docker-compose.yml
# Changer ghcr.io/epitechafrik/epidocs-api:deploy par le tag voulu
nano docker-compose.yml
# 5. Redéployer
docker compose pull
docker compose up -d
# 6. Vérifier
curl -s http://127.0.0.1:7010/
docker compose logs web --tail 30
Après le rollback¶
- Investiguer la cause sur la branche
deploy - Corriger et pusher le fix
- Le pipeline redéploiera automatiquement
Backups¶
PostgreSQL¶
La base de données utilise un volume Docker externe (epidocs-api_postgres_data). Les données persistent entre les redéploiements.
Backup manuel¶
# Depuis le VPS
cd /root/projects/mydocs/epidocs-api
# Dump complet
docker compose exec db pg_dump -U postgres epidocs_db > backup_$(date +%Y%m%d).sql
# Dump compressé
docker compose exec db pg_dump -U postgres -Fc epidocs_db > backup_$(date +%Y%m%d).dump
Restauration¶
# Restaurer un dump SQL
docker compose exec -T db psql -U postgres epidocs_db < backup_YYYYMMDD.sql
# Restaurer un dump compressé
docker compose exec -T db pg_restore -U postgres -d epidocs_db backup_YYYYMMDD.dump
Backup via l'API (superadmin)¶
# Export via l'API (nécessite cookie auth_token)
curl -b "auth_token=TOKEN" https://api.mydocs.epitools.bj/admin/backup/export -o backup.json
# Import
curl -b "auth_token=TOKEN" -X POST -F "file=@backup.json" https://api.mydocs.epitools.bj/admin/backup/import
Media (templates et documents générés)¶
Les fichiers media sont dans un bind mount ./media sur le VPS. Pour les sauvegarder :