EPIDOCS API — Diagnostics¶
Vérifications rapides¶
État des conteneurs¶
Résultat attendu :
NAME IMAGE STATUS PORTS
epidocs_api ghcr.io/epitechafrik/epidocs-api:deploy Up X hours (healthy) 0.0.0.0:7010->8000/tcp
epidocs_db postgres:16 Up X hours (healthy)
Health check¶
# Depuis le VPS
curl -s http://127.0.0.1:7010/
# Vérifier uniquement le status code
curl -o /dev/null -s -w "%{http_code}" http://127.0.0.1:7010/
# 200 = OK
Connectivité PostgreSQL¶
docker compose exec web python manage.py shell -c "
from django.db import connection
cursor = connection.cursor()
cursor.execute('SELECT 1')
print('PostgreSQL OK')
"
Vérifier les migrations¶
docker compose exec web python manage.py showmigrations | grep "\[ \]"
# Si des migrations ne sont pas appliquées, elles apparaîtront avec [ ]
Vérifier les droits (permissions)¶
docker compose exec web python manage.py shell -c "
from apps.user.models import User, Right
for u in User.objects.filter(is_staff=True):
rights = u.rights.values_list('name', flat=True)
print(f'{u.email} (super={u.is_superuser}): {list(rights)}')
"
Vérifier les templates de documents¶
# Lister les templates uploadés
docker compose exec web ls -la /epidocs/media/modeles/
# Vérifier les types de documents actifs (FR + EN)
docker compose exec web python manage.py shell -c "
from apps.document_type.models import DocumentType
for d in DocumentType.objects.all():
en = d.modele_en or '—'
print(f'{d.doc_id}: active={d.is_active}, FR={d.modele}, EN={en}')
"
Vérifier les documents sans version EN¶
# Documents générés avant le support multilingue (remote_url_en NULL)
docker compose exec web python manage.py shell -c "
from apps.document_type.models import GenDocumentHistory
count = GenDocumentHistory.objects.filter(remote_url_en__isnull=True).count()
total = GenDocumentHistory.objects.count()
print(f'{count}/{total} documents sans version EN')
"
Vérifier la configuration campus¶
# Lister les campus et leurs overrides de templates
docker compose exec web python manage.py shell -c "
from apps.campus.models import Campus, CampusDocumentType
for c in Campus.objects.all():
overrides = CampusDocumentType.objects.filter(campus=c)
print(f'Campus {c.name} ({c.code}): {overrides.count()} override(s)')
for o in overrides:
print(f' {o.document_type.doc_id}: FR={bool(o.modele)}, EN={bool(o.modele_en)}')
"
# Vérifier le campus associé à un étudiant
docker compose exec web python manage.py shell -c "
from apps.student.models import Student
email = 'etudiant@epitech.bj' # remplacer
s = Student.objects.get(login=email)
print(f'Campus: {s.campus}')
"
Vérifier les droits fins (Right / UserRight)¶
# Droits d'un admin en particulier
docker compose exec web python manage.py shell -c "
from apps.user.models import User
u = User.objects.get(email='admin@epitech.bj') # remplacer
print(f'superuser={u.is_superuser}')
print(list(u.rights.values_list('name', flat=True)))
"
# Tous les droits existants et qui les possède
docker compose exec web python manage.py shell -c "
from apps.user.models import Right
for r in Right.objects.all():
holders = list(r.userright_set.values_list('user__email', flat=True))
print(f'{r.name}: {holders}')
"
Vérifier la présence campus (attendance)¶
# Agents de sécurité actifs
docker compose exec web python manage.py shell -c "
from apps.attendance.models import SecurityGuard
for g in SecurityGuard.objects.all():
print(f'{g.name} - actif={g.is_active} - campus={g.campus}')
"
# Secret(s) QR configurés (nombre, pas la valeur)
docker compose exec web python -c "
from django.conf import settings
print(f'{len(settings.ATTENDANCE_QR_SECRETS)} secret(s) configuré(s)')
"
# Derniers scans (accès refusés en premier pour investiguer un incident)
docker compose exec web python manage.py shell -c "
from apps.attendance.models import AttendanceLog
for l in AttendanceLog.objects.filter(decision='denied').order_by('-scanned_at')[:10]:
print(f'{l.scanned_at} - {l.student_nom_snapshot} {l.student_prenom_snapshot} - {l.denial_reason}')
"
# Résultat du dernier envoi de badges en masse (succès / échecs)
docker compose exec web python manage.py shell -c "
from apps.user.models import AuditLog
for action in ('attendance_qr_sent', 'attendance_qr_send_failed'):
logs = AuditLog.objects.filter(action=action).order_by('-timestamp')[:5]
print(f'--- {action} ---')
for l in logs:
print(f'{l.timestamp} - {l.target_id} - {l.details}')
"
Vérifier les overrides de templates par classe (ClassDocumentType)¶
docker compose exec web python manage.py shell -c "
from apps.document_type.models import ClassDocumentType
for o in ClassDocumentType.objects.select_related('document_type'):
print(f'{o.classe} / {o.document_type.doc_id}: FR={bool(o.modele)}, EN={bool(o.modele_en)}')
"
Nombre d'é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\")
"
Logs¶
Logs Docker (temps réel)¶
# Logs du backend
docker compose logs -f web --tail 100
# Logs PostgreSQL
docker compose logs -f db --tail 50
# Filtrer les erreurs
docker compose logs web --tail 500 | grep -i "error\|exception\|traceback"
Logs applicatifs¶
Les logs Django sont écrits dans /epidocs/logs/file.log à l'intérieur du conteneur :
Logs via Grafana/Loki¶
Si Pedago Watch est configuré avec Promtail, les logs EPIDOCS sont accessibles dans Grafana :
- Aller sur grafana.epitools.bj
- Explorer → Loki
- Filtrer par
container_name="epidocs_api"
Vérification des sessions¶
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')
for s in active[:10]:
user = s.user or s.student
print(f' {user} - role={s.role} - expires={s.expires_at}')
"
Purger les sessions expirées¶
docker compose exec web python manage.py shell -c "
from apps.user.models import UserSession
from django.utils import timezone
expired = UserSession.objects.filter(expires_at__lt=timezone.now())
count = expired.count()
expired.update(is_valid=False)
print(f'{count} sessions expirées invalidées')
"
État du système¶
Mode maintenance¶
docker compose exec web python manage.py shell -c "
from apps.user.models import SystemStatus
s = SystemStatus.objects.first()
if s:
print(f'Maintenance: {s.is_maintenance}')
print(f'Message: {s.maintenance_message}')
print(f'Fenêtre: {s.scheduled_start} → {s.scheduled_end}')
else:
print('Aucun SystemStatus configuré')
"
Annonces actives¶
docker compose exec web python manage.py shell -c "
from apps.user.models import Announcement
from django.utils import timezone
active = Announcement.objects.filter(is_active=True)
for a in active:
print(f'[{a.priority}] {a.title} - cible: {a.target} {a.target_class or \"\"}')
"
Espace disque¶
Media¶
# Taille totale du dossier media
du -sh /root/projects/mydocs/epidocs-api/media/
# Détail par sous-dossier
du -sh /root/projects/mydocs/epidocs-api/media/*/
Volume PostgreSQL¶
Nettoyage des images Docker inutilisées¶
Environnement de développement local (macOS)¶
Qui écoute réellement sur un port (Colima / conflit de port)¶
docker ps ne montre que ce que Docker croit avoir mappé, pas qui répond réellement sur le port hôte — utile en cas de "Connection refused" malgré un mapping qui semble correct, ou d'erreur CORS qui n'en est pas une (processus orphelin qui squatte le port en IPv6) :
Si le port ne répond pas alors que le conteneur est healthy sous Colima, redémarrer le conteneur (pas colima restart) :