TrainHub — Vue d'ensemble¶
Description¶
TrainHub (nom de code local : c-training) est une plateforme d'entraînement quotidien au langage C, développée pour les étudiants du C-Pool EPITECH (initialement le campus d'Abidjan). Elle propose des sessions chronométrées avec plusieurs types de questions, des exercices de code notés automatiquement, et du contenu pédagogique (guides de projets, tutoriels) rédigé en pseudocode + Mermaid plutôt qu'en code C, pour ne pas donner de solutions toutes faites.
- Repository : EpitechAfrik/trainhub
- Origine : développé et versionné sur le repo personnel
YemalinModeste/c-training-abj, transmis à l'org EPITECH lors du départ du développeur. - Hébergement : Vercel (projet
daily-c-language-training-platform) - URL production :
https://trainhub.vercel.apppar défaut dans le code (app/layout.tsx,app/sitemap.ts) — à confirmer sur le dashboard Vercel, un domaine personnalisé a pu être configuré depuis.
Fonctionnalités principales¶
Côté étudiant (app/etudiant/)¶
- Entraînement libre et sessions planifiées (créées par un admin, ciblées sur un étudiant ou un groupe), avec statistiques par session et globales.
- Types de questions : QCM (
qcm), prédiction de sortie (code_output), détection d'erreur (error_detection), texte à trous (fill_blank). - Éditeur Monaco intégré pour les questions à code.
- Recode : exercices de code notés automatiquement (compilation + tests cachés via Judge0/Piston). Soumission automatique en cas de fermeture/déconnexion (
navigator.sendBeacon→/api/recode/auto-submit) pour ne pas perdre le travail en cas de timer expiré. - Contenu pédagogique : blog de tutoriels C (pointeurs, varargs, listes chaînées...), guides par projet Epitech (CPE, PSU, MUL, MATH — ex. Setting Up, my_ls, my_sudo, 101pong, my_hunter), diagnostic de lacunes, vocabulaire, planning. Convention du projet : jamais de code C dans les guides de projets — pseudocode en français + diagrammes Mermaid + visualisations React, pour forcer la réflexion plutôt que le copier-coller.
Côté admin (app/admin/)¶
- Gestion des questions et catégories, avec prévisualisation/test d'une question avant publication.
- Planification de sessions (ciblage étudiant ou groupe), gestion des groupes d'étudiants et de leurs membres.
- Import CSV d'étudiants, reset de mot de passe, révocation de comptes.
- Gestion des droits (rôles
admin/super_admin). - Gestion des exercices Recode et de leurs soumissions.
- Dashboard de suivi d'activité (engagement, temps passé par page/section).
Cron¶
Un seul cron Vercel configuré (vercel.json), tous les jours à 7h : GET/POST /api/cron/training-reminders. Il liste les sessions planifiées publiées du jour (ciblage direct ou via groupe) non complétées, et envoie un rappel par email via Resend (par lot, avec délai pour respecter le rate limit). Protégé par CRON_SECRET — fail-closed : sans ce secret configuré, la route échoue volontairement plutôt que de tourner sans protection.
Architecture¶
Stack technique¶
| Composant | Technologie |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19, TypeScript, Tailwind CSS 4, Shadcn UI |
| Éditeur de code | Monaco (@monaco-editor/react, chargé en dynamique client-only) |
| Auth + Base de données | Supabase (PostgreSQL + Auth) — seule couche de données active |
| Exécution de code | Judge0 (auto-hébergé, judge0/docker-compose.yml) par défaut, Piston en alternative |
| Emails | Resend |
| Hébergement | Vercel (build + cron) |
| Middleware | proxy.ts (convention Next 16 — remplace middleware.ts), rafraîchit la session Supabase et bloque /auth/signup* |
Tentative de migration self-managed — abandonnée
Une tentative de migration vers un backend Postgres auto-hébergé + Drizzle ORM a été menée sur une branche (migration/self-managed, 4 commits), puis entièrement annulée (commit revert: abandon de la migration self-managed — retour à Supabase). Les branches main et development — celles poussées vers l'org EPITECH — n'ont jamais contenu cette tentative. Décision produit actuelle : rester 100% Supabase. Des résidus subsistent dans package.json (pg, @types/pg, tsx en devDependencies), utilisés uniquement par un script standalone d'import de questions (scripts/import_questions.ts), pas par le runtime de l'app.
Exécution de code (Judge0 / Piston)¶
Judge0 déprécié — repris par devkora (Zeno)
Tel que trouvé dans le code au moment de cette documentation, lib/compiler/index.ts sélectionne le backend via COMPILER_BACKEND (judge0 par défaut ou piston). Cette approche Judge0/Piston auto-hébergé est dépréciée : la sandbox d'exécution de code est reprise par Edwin sous forme d'un module nommé devkora, côté projet Zeno. Se référer à Edwin / au repo Zeno pour l'implémentation à jour plutôt qu'à lib/compiler/judge0.ts ci-dessous, qui documente l'ancien fonctionnement.
- Judge0 (
lib/compiler/judge0.ts) : soumission asynchrone + polling (30 tentatives max, 1s d'intervalle). URL par défauthttp://localhost:2358(instance auto-hébergée, voirjudge0/docker-compose.yml— Redis + Postgres + workers, auth par tokenAUTHN_TOKEN). Choix automatique du compilateur : Clang (language_id=75, défaut) ou GCC (language_id=50, si les flags de compilation contiennent-std=gnu). - Piston (
lib/compiler/piston.ts) : alternative configurée viaPISTON_API_URL.
Modèle de données (Supabase / PostgreSQL)¶
Source de vérité : scripts/000_full_schema.sql + migrations numérotées (012 à 018) + tracking_migration.sql, appliquées manuellement sur le projet Supabase (pas de système de migration automatisé type Supabase CLI ou Drizzle en prod).
| Table | Rôle |
|---|---|
profiles |
Comptes utilisateurs (étend auth.users), rôle student / admin / super_admin |
categories |
Catégories de questions (Variables, Boucles, Pointeurs...) |
questions |
Banque de questions (type, difficulté, snippet de code, options, réponse correcte) |
sessions |
Session d'entraînement libre d'un étudiant |
session_questions |
Questions liées à une session (avec ordre) |
attempts |
Réponses/scores par session |
question_mastery |
Suivi de maîtrise par question/utilisateur |
planned_sessions |
Session planifiée par un admin (titre, date, statut de publication) |
planned_session_questions |
Questions d'une session planifiée |
planned_session_targets |
Ciblage d'une session planifiée (étudiant ou groupe) |
planned_session_attempts |
Tentative d'un étudiant sur une session planifiée |
planned_session_answers |
Réponses détaillées d'une tentative planifiée |
student_groups / student_group_members |
Groupes d'étudiants et appartenance |
recode_exercises |
Exercices de code notés (Recode) |
recode_exercise_targets |
Ciblage des exercices Recode |
recode_submissions |
Soumissions de code + résultats de tests |
student_activity |
Tracking d'engagement (page_enter/page_leave/section_enter/section_leave, durée) — RLS : un étudiant écrit ses propres événements, admin/super_admin lisent/suppriment tout |
Autres migrations notables : 013_roles_and_classes.sql (rôles étendus), 014_microsoft_auth.sql (auth Microsoft/Azure), 016-018 (toggles et fonctions du mode Recode).
Routes API (app/api/**)¶
| Route | Description |
|---|---|
POST /api/compile |
Compile/exécute du code C via Judge0 ou Piston ; vérifie header Epitech, fonctions bannies, et applique un linter de coding style Epitech maison ; auth Supabase requise |
POST /api/recode/auto-submit |
Soumission serveur-side (compile + tests + sauvegarde recode_submissions) pour timer expiré ou fermeture de page |
GET/POST /api/cron/training-reminders |
Cron quotidien (protégé CRON_SECRET), rappels d'entraînement par email |
POST /api/admin/invite |
Invite un admin/super_admin (crée le compte + envoie les identifiants) |
POST /api/admin/import-students |
Import en masse d'étudiants (CSV/JSON) + envoi des identifiants |
POST /api/admin/groups/import-csv |
Import CSV de groupes d'étudiants |
POST /api/admin/notify-session |
Notifie par email les cibles d'une session planifiée |
POST /api/admin/reset-password |
Réinitialise le mot de passe d'un étudiant (admin) |
POST /api/admin/revoke |
Révoque un compte admin (super_admin uniquement) |
DELETE /api/admin/students/[id] |
Supprime un compte étudiant |
POST /api/auth/forgot-password |
Mot de passe oublié (self-service étudiant) |
Toutes les routes utilisant la clé de service (SUPABASE_SERVICE_ROLE_KEY) instancient un client Supabase admin dédié (autoRefreshToken: false, persistSession: false) et vérifient le rôle de l'appelant avant d'agir.
Variables d'environnement¶
Aucun .env.example n'est présent dans le repo — variables inférées du code source :
| Variable | Rôle |
|---|---|
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY |
Client Supabase (browser/SSR) |
SUPABASE_SERVICE_ROLE_KEY |
Client admin Supabase (routes API privilégiées, cron) |
NEXT_PUBLIC_APP_URL |
Base URL pour les liens dans les emails et les redirects d'auth (défaut : https://trainhub.vercel.app) |
NEXT_PUBLIC_DEV_SUPABASE_REDIRECT_URL |
Override du redirect d'auth en dev |
CRON_SECRET |
Secret Bearer pour authentifier l'appel cron Vercel — obligatoire (fail-closed sans lui) |
RESEND_API_KEY, RESEND_FROM_EMAIL |
Envoi d'emails (défaut expéditeur : TrainHub <noreply@trainhub.epitech.eu>) |
COMPILER_BACKEND |
judge0 (défaut) ou piston |
JUDGE0_API_URL (défaut http://localhost:2358), JUDGE0_API_KEY |
Sandbox Judge0 |
PISTON_API_URL |
Si backend Piston choisi |
POSTGRES_URL_NON_POOLING, NODE_TLS_REJECT_UNAUTHORIZED |
Utilisés uniquement par le script standalone scripts/import_questions.ts (connexion Postgres directe pour import de questions) — pas par l'app runtime |
Déploiement¶
- Scripts standards Next.js :
pnpm dev,pnpm build(next build),pnpm lint,pnpm start. - Déployé sur Vercel (projet lié via
.vercel/project.json— toujours vérifier ce fichier ou demander confirmation avant tout déploiement, cf.AGENTS.mddu projet). vercel.jsonne configure que le crontraining-reminders(7h quotidien).- Aucun Dockerfile ni CI applicatif dans le repo (le
docker-compose.ymlde la tentative self-managed a été supprimé au revert). Seuljudge0/docker-compose.ymlreste, pour l'instance Judge0 auto-hébergée — à héberger séparément de l'app (VPS, conteneur dédié) et pointer viaJUDGE0_API_URLen prod. - Migrations SQL (
scripts/*.sql) à appliquer manuellement, dans l'ordre numérique, sur le projet Supabase.
Points de vigilance connus¶
next.config.mjsatypescript.ignoreBuildErrors: true— les erreurs TypeScript ne bloquent pas le build Vercel, une régression de types peut passer inaperçue jusqu'en prod.JUDGE0_API_URLdoit être explicitement configuré en prod (le défautlocalhost:2358échouera silencieusement en timeout sur/api/compileet/api/recode/auto-submit) — sauf si la bascule vers devkora (Zeno, repris par Edwin) est déjà en place, auquel cas ce point ne s'applique plus.- Si des artefacts
lib/db/,drizzle.config.tsouinfra/réapparaissent sur une branche, c'est probablement une reprise accidentelle de la tentative de migration abandonnée — la décision produit est de rester 100% Supabase.
Détails et procédures : Erreurs courantes, Diagnostics, Runbook.