Aide-mémoire — EPIDOCS API¶
Référence rapide pour le backend Django de EPIDOCS.
Accès rapides¶
| Élément | Lien |
|---|---|
| API prod | api.mydocs.epitools.bj |
| Repo | GitHub |
Ports et conteneurs¶
| Service | Port hôte | Port conteneur | Image |
|---|---|---|---|
| API (Gunicorn) | 127.0.0.1:7010 |
8000 |
ghcr.io/epitechafrik/epidocs-api:deploy |
| PostgreSQL 16 | non exposé | 5432 |
postgres:16 |
Chemins VPS¶
Commandes essentielles¶
Vérification santé¶
Logs¶
cd /root/projects/mydocs/epidocs-api
# Logs applicatifs
docker compose logs -f web --tail 100
docker compose logs web --tail 500 | grep -i "error\|exception\|traceback"
# Logs Django (fichier dans conteneur)
docker compose exec web cat /epidocs/logs/file.log | tail -50
# Logs PostgreSQL
docker compose logs -f db --tail 50
Base de données¶
# Vérifier la connectivité PostgreSQL
docker compose exec web python manage.py shell -c \
"from django.db import connection; connection.cursor().execute('SELECT 1'); print('OK')"
# Migrations en attente
docker compose exec web python manage.py showmigrations | grep "\[ \]"
# Backup PostgreSQL
docker compose exec db pg_dump -U postgres epidocs_db > backup_$(date +%Y%m%d).sql
docker compose exec db pg_dump -U postgres -Fc epidocs_db > backup_$(date +%Y%m%d).dump
# Backup media (templates Word + documents générés)
tar czf media_backup_$(date +%Y%m%d).tar.gz media/
Corrections manuelles sur un étudiant¶
Passer par l'admin Django (/admin/student/student/), pas par SQL direct : le formulaire expose tous les champs de Student, et civilite y est une liste déroulante Monsieur/Madame depuis PR #13 — plus sûr qu'une valeur texte libre pour une correction ponctuelle.
Secours SQL direct (opération en masse, ou admin indisponible) :
docker compose exec db psql -U postgres -d epidocs_db -c \
"SELECT login, nom, prenom, civilite, promo FROM student_student WHERE login = 'LOGIN_ETUDIANT';"
docker compose exec db psql -U postgres -d epidocs_db -c \
"UPDATE student_student SET promo = '2024' WHERE login = 'LOGIN_ETUDIANT';"
Un document généré ne se corrige pas lui-même
L'année de délivrance et l'accord grammatical d'un document sont calculés à partir de civilite/promo au moment de la génération — corriger la donnée ne met pas à jour les PDF déjà générés. Voir Erreurs courantes → civilité/année incohérente.
Diagnostics utiles¶
# Étudiants par classe
docker compose exec web python manage.py shell -c "
from apps.student.models import Student
from django.db.models import Count
stats = Student.objects.filter(account_suspended=0).values('section').annotate(count=Count('login')).order_by('section')
for s in stats: print(f\"{s['section']}: {s['count']} étudiants\")"
# Sessions actives
docker compose exec web python manage.py shell -c "
from apps.user.models import UserSession
from django.utils import timezone
active = UserSession.objects.filter(is_valid=True, expires_at__gt=timezone.now())
print(f'{active.count()} sessions actives')"
# Mode maintenance
docker compose exec web python manage.py shell -c "
from apps.user.models import SystemStatus
s = SystemStatus.objects.first()
print(f'Maintenance: {s.is_maintenance}' if s else 'Pas de SystemStatus')"
# Templates documents
docker compose exec web ls -la /epidocs/media/modeles/
# Espace disque media
du -sh /root/projects/mydocs/epidocs-api/media/
Rollback¶
cd /root/projects/mydocs/epidocs-api
# Lister les images disponibles
docker images ghcr.io/epitechafrik/epidocs-api --format "table {{.Tag}}\t{{.CreatedAt}}"
# Modifier le tag dans docker-compose.yml puis :
docker compose pull web
docker compose up -d --no-deps web
Pipelines CI/CD¶
gh run list --repo EpitechAfrik/epidocs-api
gh run view RUN_ID --repo EpitechAfrik/epidocs-api --log-failed
Stack résumée¶
| Composant | Technologie |
|---|---|
| Framework | Django 5 + Django REST Framework |
| BDD | PostgreSQL 16 |
| Auth | JWT cookie HttpOnly + Azure AD (MSAL) |
| docxtpl + LibreOffice | |
| QR Codes | qrcode + Pillow |
| WSGI | Gunicorn (3 workers, timeout 120s) |
| Stockage | Local media/ ou Cloudinary |
Volumes Docker¶
| Volume | Usage |
|---|---|
epidocs-api_postgres_data (externe) |
Données PostgreSQL |
./media (bind mount) |
Templates Word + documents |
static_data |
Fichiers statiques Django |
Variables d'environnement clés¶
Ne jamais commiter les secrets
Les .env sont sur le VPS uniquement.
| Variable | Description |
|---|---|
SECRET_KEY |
Secret Django |
POSTGRES_PASSWORD |
Mot de passe PostgreSQL |
JWT_SECRET_KEY |
Secret JWT |
JWT_COOKIE_DOMAIN |
.epitools.bj |
AZURE_CLIENT_ID / AZURE_CLIENT_SECRET / AZURE_TENANT_ID |
Azure AD SSO |
RESEND_API_KEY |
Token API Resend (emails) |
ALLOWED_HOSTS |
api.mydocs.epitools.bj,localhost |
ATTENDANCE_QR_SECRET |
Secret(s) HMAC des QR de présence, liste séparée par virgules (rotation) — défaut SECRET_KEY |
Date d'expiration du secret Azure AD — À REMPLIR
AZURE_CLIENT_SECRET expire à une date fixée dans Azure Portal (App Registrations → Certificates & secrets). Date d'expiration actuelle : à vérifier et noter ici. Si ce secret expire sans renouvellement, plus personne ne peut se connecter (admins et étudiants) — voir Erreurs courantes pour la procédure de rotation, et Continuité — accès externes pour donner l'accès Azure Portal à une seconde personne.
Routes API principales¶
| Route | Description |
|---|---|
/auth/sign_in |
Connexion |
/auth/callback |
Callback Azure AD |
/auth/me |
Utilisateur courant |
/student/auth/me |
Étudiant courant |
/documents/generate-document |
Génération PDF |
/documents/admin/batch-generate |
Génération PDF en lot |
/student/upload-csv/ |
Import étudiants CSV |
/admin/system-status |
Mode maintenance |
/admin/audit-logs |
Journal d'audit |
/integrations/v1/docs |
Doc live de l'API Zeno (ReDoc) |
/integrations/v1/documents/generate |
Génération de doc par un service (clé API) |
/attendance/auth |
Connexion agent de sécurité (PIN) |
/attendance/scan |
Scanner un QR de présence |
/attendance/qrcodes/audience |
Aperçu des destinataires avant envoi de badges |
/attendance/qrcodes/send |
Envoi en masse des badges (async, 202) |
/documents/admin/classes/:classe/templates |
Override de template par classe (ClassDocumentType) |
Droits admin (Right / UserRight)¶
20 droits au total (liste faisant autorité : apps/user/management/commands/seed_rights.py). Seuls can_upload_tepitech, can_upload_user, can_generate, can_generate_for_student sont auto-accordés aux admins non-superadmin (base_rights) — les 16 autres (dont can_manage_attendance, can_manage_document_types, can_suspend_students, manage_campus, can_view_students...) doivent être accordés explicitement. Détail complet : overview.md § Système de permissions.
# Voir/accorder les droits d'un admin
docker compose exec web python manage.py shell -c "
from apps.user.models import User, Right, UserRight
u = User.objects.get(email='admin@epitech.bj')
print(list(u.rights.values_list('name', flat=True)))
"
docker compose exec web python manage.py shell -c "
from apps.user.models import User, Right, UserRight
u = User.objects.get(email='admin@epitech.bj')
r = Right.objects.get(name='can_manage_attendance')
UserRight.objects.get_or_create(user=u, right=r)
"
# Re-seeder les droits (créés si absents, jamais retirés)
docker compose exec web python manage.py seed_rights
Non-délégable (IsSuperAdmin uniquement, pas de Right possible) : créer un compte admin (CreateUserView), assigner un gestionnaire de campus (CampusManagerListView/CampusManagerDeleteView).
Présence campus (attendance)¶
Auth séparée : cookie attendance_session (agent de sécurité par PIN), distincte de auth_token (admin/étudiant). QR = signature:login, HMAC-SHA256 signé avec ATTENDANCE_QR_SECRETS (1er secret de la liste = signature, tous acceptés en vérification → rotation possible).
# Créer/lister les agents de sécurité
docker compose exec web python manage.py shell -c "
from apps.attendance.models import SecurityGuard
for g in SecurityGuard.objects.all(): print(g.name, g.is_active)
"
# Compter les secrets QR actifs (rotation en cours si > 1)
docker compose exec web python -c "
from django.conf import settings
print(len(settings.ATTENDANCE_QR_SECRETS))
"
# Vérifier les envois de badges récents (succès/échec)
docker compose exec db psql -U postgres -d epidocs_db -c \
"SELECT action, target_id, timestamp FROM user_auditlog WHERE action LIKE 'attendance_qr_%' ORDER BY timestamp DESC LIMIT 20;"
Toujours prévisualiser avant d'envoyer
POST /attendance/qrcodes/audience (lecture seule) avant POST /attendance/qrcodes/send — un envoi par section seule inclut par défaut les diplômés et suspendus ; _build_qr_audience() les exclut désormais par défaut, mais vérifier la liste reste la meilleure protection contre un envoi non désiré (incident réel du 2026-08-29).
Intégration Zeno (clés API)¶
API service-à-service authentifiée par header Authorization: Api-Key <clé>. Doc live : /integrations/v1/docs.
# Créer un compte de service + clé (la clé s'affiche UNE seule fois)
docker compose exec web python manage.py create_service_account zeno --description "Backend Zeno"
# Clé lecture seule (scopes: read uniquement → ne peut pas générer)
docker compose exec web python manage.py create_service_account zeno-readonly --scopes read
# Lister les comptes de service et l'état de leurs clés
docker compose exec db psql -U postgres -d epidocs_db -c \
"SELECT s.name, k.prefix, k.is_active, k.last_used_at, k.revoked_at
FROM integrations_serviceaccount s
JOIN integrations_serviceapikey k ON k.service_id = s.id ORDER BY s.name;"
# Audit des actions d'un service (génération + téléchargement)
docker compose exec db psql -U postgres -d epidocs_db -c \
"SELECT action, actor_email, target_id, ip_address, timestamp
FROM user_auditlog WHERE actor_role='service' ORDER BY timestamp DESC LIMIT 20;"
# Tester une clé (attendu: 400 = auth OK, 401 = clé invalide)
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
http://127.0.0.1:7010/integrations/v1/documents/generate \
-H "Authorization: Api-Key LA_CLE" -H "Content-Type: application/json" -d '{}'
Révocation / rotation d'une clé : depuis le dashboard (super-admin) ou via
POST /integrations/v1/admin/keys/:id/revoke(ou/rotate).