Aller au contenu

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.

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_status ou PGE5_status est success, l'étudiant ne peut plus générer le certificat_scolarite. Il accède à la place à l'attestation_scolarite. Le statut est posé par l'admin via la route POST /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) et X-Has-En (true/false). Le frontend utilise X-Has-En pour 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 un CampusDocumentType configuré pour le document demandé. Si oui, les templates du campus priment sur les templates globaux du DocumentType.

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 AuditLog avec actor_role = service (incluant trackingCode, 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 premier docker 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 + collectstatic avant de lancer Gunicorn.
  • Port : 7010 cô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 :

  1. CI : Python 3.12, PostgreSQL 16 de test, linting (flake8), vérification des migrations, tests
  2. Build Docker : construction de l'image et push sur ghcr.io/epitechafrik/epidocs-api:deploy
  3. Déploiement : SCP du docker-compose.prod.yml et .env sur le VPS, puis docker compose pull && docker compose up -d
  4. 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

  1. Investiguer la cause sur la branche deploy
  2. Corriger et pusher le fix
  3. 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 :

# Depuis le VPS
cd /root/projects/mydocs/epidocs-api
tar czf media_backup_$(date +%Y%m%d).tar.gz media/