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.
- Repository : EpitechAfrik/alert-parent-backend
- URL production : https://api.epitools.bj
- Port :
127.0.0.1:5011(hôte) →5010(conteneur)
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, soft delete |
/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, populated: {cycle, end_year})
├── classe (TEK1, TEK1 PSO, TEK2, TEK3, PreMSC, MSC1, MSC2, CODAC)
├── active (Boolean, default: true) — soft delete
├── 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
Routes étudiants (détail)¶
| Action | Méthode | Route | Body |
|---|---|---|---|
| Lister (actifs par défaut) | GET | /api/students?active=true |
— |
| Lister les inactifs | GET | /api/students?active=false |
— |
| Créer | POST | /api/students |
{firstname, lastname, email, ...} |
| Modifier | PUT | /api/students/:id |
{firstname, lastname, ...} |
| Import CSV | POST | /api/students/import |
FormData |
| Bulk update (classe/promo) | PUT | /api/students/bulk |
{students: [ids], classe, promo} |
| Désactiver | PUT | /api/students/:id/deactivate |
— |
| Réactiver | PUT | /api/students/:id/reactivate |
— |
| Désactiver en masse | POST | /api/students/bulk/deactivate |
{students: [ids]} |
Soft delete
Les étudiants ne sont jamais supprimés définitivement. La route DELETE /api/students/:id n'existe plus.
La désactivation conserve l'historique des alertes associées.
Par défaut, GET /api/students ne retourne que les étudiants actifs (active=true).
Promo peuplée
Le champ promo est désormais peuplé automatiquement par l'API. Au lieu d'un simple ObjectId, il retourne {cycle: "BACHELOR", end_year: 2030}.
File d'attente email (Resend)¶
Un processus asynchrone tourne en boucle dans server.js :
- Toutes les 5 secondes, il cherche un
MailSendedavecisSent: "no" - Il l'envoie via l'API Resend
- En cas de succès, il marque
isSent: "yes" - 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.1uniquement (accessible seulement via Nginx) - Les variables d'environnement sont chargées depuis
.envviaenv_file - Le healthcheck vérifie
/healthsur le port interne5010
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¶
- Investiguer la cause du problème sur la branche
main - Corriger et pousser un fix
- 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)