Aller au contenu

Alert-Parent Backend

Description

Alert-Parent est une plateforme interne qui permet aux équipes pédagogiques d'EPITECH Bénin d'envoyer des alertes aux parents et référents financiers des étudiants concernant leurs résultats académiques.

Fonctionnalités principales

  • Gestion des promos : création de promotions par cycle (Bachelor, MSc, Codac) et année de fin
  • Gestion des étudiants : création unitaire, import CSV en masse, changement de classe/promo
  • Alertes pédagogiques : Tepitech, Stumpers, Modules, Présences, Projets, Roadblocks, -42, Évaluation de stage
  • File d'attente email : les alertes sont mises en queue dans MongoDB et envoyées progressivement via l'API Resend
  • Authentification : JWT classique + Microsoft Azure AD (SSO)

Architecture

Stack technique

Composant Technologie
Framework Express.js (Node.js 20)
Base de données MongoDB Atlas
Email transactionnel Resend API
Authentification JWT + Microsoft Azure AD
Conteneur Docker (node:20-alpine, pnpm)

Routes API

Route Description
/api/users Inscription, login, gestion des comptes admin
/api/students CRUD étudiants, import CSV, bulk update
/api/promos CRUD promos, mapping classe/promo
/api/alerts Envoi d'alertes pédagogiques par type
/api/docs Documentation Swagger interactive
/health Endpoint de vérification de santé

Modèle de données

Student
├── firstname, lastname, email, sexe
├── promo (ObjectId → Promo)
├── classe (TEK1, TEK1 PSO, TEK2, TEK3, PreMSC, MSC1, MSC2, CODAC)
├── ref_financier_1_* (prénom, nom, email, sexe)
└── ref_financier_2_* (optionnel)

Promo
├── cycle (BACHELOR, MSC, CODAC)
└── end_year (ex: 2027)

MailSended
├── from (user qui a envoyé), to (email destinataire)
├── student (ObjectId → Student)
├── topic (Tepitech, Module, Présence, etc.)
├── obj (sujet), message (HTML)
├── isSent (yes/no)
└── createdAt, updatedAt

File d'attente email (Resend)

Un processus asynchrone tourne en boucle dans server.js :

  1. Toutes les 5 secondes, il cherche un MailSended avec isSent: "no"
  2. Il l'envoie via l'API Resend
  3. En cas de succès, il marque isSent: "yes"
  4. En cas d'erreur, il attend 60 secondes avant de réessayer

Ce mécanisme découple la création d'alertes de leur envoi effectif.

Collections MongoDB

Collection Rôle
students Étudiants avec leurs référents financiers
promos Promotions par cycle et année
mailsendeds File d'attente des mails à envoyer
users Comptes administrateurs
emailconfirms Tokens de confirmation email
passwordresets Tokens de reset mot de passe

Diagrammes

Architecture globale

graph TB
    User[Utilisateur / Navigateur]
    Frontend["Frontend<br/>alert.epitools.bj"]
    Nginx["Nginx<br/>Reverse proxy + TLS"]
    Backend["Backend API<br/>Express.js :5010"]
    MongoDB["MongoDB Atlas<br/>(Cloud)"]
    Resend["Resend API<br/>(Email)"]
    AzureAD["Azure AD<br/>(SSO)"]

    User --> Nginx
    Nginx --> Frontend
    Nginx --> Backend
    Backend --> MongoDB
    Backend --> Resend
    Backend --> AzureAD

Flux d'envoi d'alerte

sequenceDiagram
    participant Admin as Admin (Frontend)
    participant API as Backend API
    participant DB as MongoDB Atlas
    participant Queue as Mail Queue (loop)
    participant Resend as Resend API
    participant Parent as Parent (Email)

    Admin->>API: POST /api/alerts/tepitech
    API->>DB: Student.findOne({email})
    API->>DB: MailSended.create({isSent: "no"})
    API-->>Admin: {success: [...], failed: [...]}

    loop Toutes les 5 secondes
        Queue->>DB: MailSended.find({isSent: "no"})
        Queue->>Resend: resend.emails.send(...)
        Resend->>Parent: Email envoyé
        Queue->>DB: MailSended.update({isSent: "yes"})
    end

Docker

Dockerfile

L'image utilise node:20-alpine avec pnpm comme gestionnaire de paquets. L'application tourne en tant qu'utilisateur non-root pour la sécurité.

docker-compose.yml

services:
  backend:
    image: ghcr.io/epitechafrik/alert-parent-backend:${IMAGE_TAG:-latest}
    env_file: .env
    ports:
      - "127.0.0.1:5011:5010"
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5010/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Points importants :

  • L'image est tirée depuis GHCR (ghcr.io/epitechafrik/alert-parent-backend)
  • Le port est bindé sur 127.0.0.1 uniquement (accessible seulement via Nginx)
  • Les variables d'environnement sont chargées depuis .env via env_file
  • Le healthcheck vérifie /health sur le port interne 5010

Health Endpoint

GET /health

Utilisé par Docker Compose (healthcheck), Uptime Kuma (monitoring) et le pipeline CI/CD (vérification post-déploiement).

Réponse (200 OK)

{
  "status": "ok",
  "timestamp": "2026-02-22T10:30:00.000Z",
  "uptime_seconds": 86400,
  "environment": "production",
  "dependencies": {
    "mongodb": {
      "status": "ok",
      "latency_ms": 12
    },
    "resend": {
      "status": "configured"
    },
    "smtp": {
      "status": "configured"
    }
  }
}

Réponse (503 Service Unavailable)

Retourné quand MongoDB est inaccessible :

{
  "status": "degraded",
  "timestamp": "2026-02-22T10:30:00.000Z",
  "uptime_seconds": 120,
  "environment": "production",
  "dependencies": {
    "mongodb": {
      "status": "error",
      "latency_ms": null
    },
    "resend": {
      "status": "configured"
    },
    "smtp": {
      "status": "configured"
    }
  }
}

Utilisation

# Test simple
curl http://127.0.0.1:5011/health

# Test avec formatage
curl -s http://127.0.0.1:5011/health | python3 -m json.tool

# Vérifier uniquement le status code
curl -o /dev/null -s -w "%{http_code}" http://127.0.0.1:5011/health
# 200 = OK, 503 = dégradé

Variables d'environnement

Toutes les variables sont définies dans le fichier .env à la racine du projet (jamais commité dans git). Le template est dans .env.example.

Application

Variable Description Exemple
NODE_ENV Mode d'exécution production
PORT Port interne du serveur 5010

Base de données

Variable Description Exemple
MONGO_URI URI MongoDB Atlas mongodb+srv://user:pass@cluster.mongodb.net/dbname

Authentification

Variable Description Exemple
JWT_SECRET Clé secrète JWT (64 chars min) chaîne aléatoire longue
ADMIN_EMAIL Email du premier admin créé au démarrage admin@epitech.eu
PASSWORD_SIZE Taille minimum des mots de passe 8
PASSWORD_CAPITAL Exiger une majuscule true
PASSWORD_LOWERCASE Exiger une minuscule true
PASSWORD_NUMBER Exiger un chiffre true
PASSWORD_CHAR Exiger un caractère spécial true

Email

Variable Description Exemple
MAIL_NAME Nom d'expéditeur EPITECH Info
MAIL_HOST Serveur SMTP alouette.o2switch.net
MAIL_PORT Port SMTP 465
MAIL_ADDR Adresse d'envoi no-reply@epitech.bj
MAIL_PASS Mot de passe SMTP secret
RESEND_TOKEN Token API Resend (envoi des alertes) re_xxxxx

URLs

Variable Description Exemple
CORS_ORIGIN Origine autorisée CORS https://alert.epitools.bj
FRONT_URL URL du frontend https://alert.epitools.bj
API_URL URL publique de l'API https://api.epitools.bj

Microsoft Azure AD (optionnel)

Variable Description
MICROSOFT_CLIENT_ID Client ID de l'App Registration Azure
MICROSOFT_TENANT_ID Tenant ID Azure

Docker

Variable Description Exemple
IMAGE_TAG Tag de l'image Docker à utiliser latest ou sha-abc1234

Rollback

Quand faire un rollback

  • Le déploiement a cassé une fonctionnalité critique
  • Le health check échoue après un déploiement
  • Une régression est détectée en production

Procédure

# 1. Trouver la version précédente
docker images ghcr.io/epitechafrik/alert-parent-backend --format "table {{.Tag}}\t{{.CreatedAt}}"

# 2. Exécuter le rollback
cd /root/projects/alert-parent/alert-parent-backend
./scripts/rollback.sh sha-XXXXXXX

# 3. Vérifier
curl -s http://127.0.0.1:5011/health | python3 -m json.tool
docker compose logs backend --tail 20

Après le rollback

  1. Investiguer la cause du problème sur la branche main
  2. Corriger et pousser un fix
  3. Le pipeline redéploiera automatiquement

Backups

MongoDB Atlas

La base de données est hébergée sur MongoDB Atlas, qui gère automatiquement :

  • Snapshots automatiques : toutes les 6 heures
  • Rétention : configurable dans Atlas (7 jours par défaut)
  • Point-in-time recovery : restauration à n'importe quel moment

Backup manuel

mongodump --uri="mongodb+srv://USER:PASSWORD@cluster.mongodb.net/al3rtParent" --out=./dump-$(date +%Y%m%d)

Restauration

# Restaurer un dump dans une base locale (pour test)
mongorestore --db alertParentLocal ./dump-YYYYMMDD/al3rtParent

# Restaurer dans Atlas
mongorestore --uri="mongodb+srv://USER:PASSWORD@cluster.mongodb.net/al3rtParent" ./dump-YYYYMMDD/al3rtParent