Pipeline sécurisé d’onboarding enseignants & documents (NAS + Cloudflare + API locale)

Frontend statique d’onboarding relié à des uploads persistés sur NAS, des enregistrements SQLite et une génération automatique de dossier PDF via une API locale exposée par tunnel.

Rôle: Conception système, implémentation backend/API, débogage infraDurée: Build incrémental + durcissement productionStack: Next.js • Node.js • Docker • Cloudflare Tunnel • SQLite • ASUSTOR NAS • PDFKit
Next.jsNode.jsSQLiteASUSTOR NASCloudflare TunnelDockerPDFKitCORSShared Secret
En bref
  • Problème
    Frontend statique d’onboarding relié à des uploads persistés sur NAS, des enregistrements SQLite et une génération automatique de dossier PDF via une API locale exposée par tunnel.
  • Rôle
    Conception système, implémentation backend/API, débogage infra
  • Durée
    Build incrémental + durcissement production
  • Stack
    Next.js • Node.js • Docker • Cloudflare Tunnel
  • Focus
    Next.js • Node.js • SQLite
  • Impact
    Chaîne d’upload: Opérationnelle de bout en bout
Chaîne d’upload
Opérationnelle de bout en bout
Persistance
NAS + SQLite reliés
Génération dossier
PDF automatique

Problème

L’onboarding enseignants était fragmenté entre un formulaire statique, une collecte documentaire gérée à part et une réconciliation manuelle. Les informations de profil étaient captées, mais les pièces arrivaient par des chemins séparés et sans lien déterministe avec le dossier enseignant.

Ce découplage créait de la dérive opérationnelle. Les données structurées, les fichiers uploadés et le dossier final vivaient dans des emplacements différents, sans chemin de persistance unique, sans modèle de récupération stable et avec trop de traitement manuel pour un flux destiné à un staff non technique.

Le système devait transformer cela en pipeline unique : soumettre les données enseignant, uploader les pièces, persister les deux proprement, puis générer le dossier automatiquement sans demander aux opérateurs de réparer le stockage ou les conventions de nommage à la main.

Contexte

L’architecture était contrainte par un environnement réel plutôt que par un backend cloud greenfield. Le frontend était un déploiement Next.js statique, tandis que l’API et la persistance vivaient sur une infra locale/NAS. Il fallait donc relier un client public statique à une origine privée sans exposer directement l’hôte interne.

Le NAS ASUSTOR était la couche de persistance principale pour les documents uploadés et les dossiers générés. Les métadonnées structurées vivaient dans SQLite pour permettre des requêtes et des liens propres, mais le fichier source de vérité restait un stockage monté sur NAS plutôt qu’un object storage cloud.

Cloudflare Tunnel assurait l’exposition publique sécurisée de l’API locale. Cela évitait d’ouvrir des ports entrants, mais ajoutait une vraie dépendance à l’état du tunnel, au transport, au réseau des conteneurs et à la justesse de l’origine. La solution devait rester utilisable par un staff non technique, donc ces contraintes devaient être absorbées par le système.

Architecture

  1. Frontend
    • Déploiement Next.js statique hébergé séparément du runtime API.
    • Formulaire d’onboarding enseignant qui collecte données structurées et pièces justificatives.
    • Envoie les uploads multipart vers `/api/upload/teacher` et le JSON structuré vers `/api/teacher/application`.
    • Le frontend reste volontairement simple : capture, validation légère côté client, puis délégation de la persistance et de la génération du dossier au backend.
  2. Backend
    • L’endpoint `/api/upload/teacher` gère l’ingestion documentaire, l’authentification, la validation, la sanitation des noms de fichiers et l’écriture sur le NAS.
    • L’endpoint `/api/teacher/application` normalise le payload enseignant, persiste les données dans SQLite et déclenche la génération du dossier PDF.
    • CORS et authentification par secret partagé sont appliqués à la frontière API sur les deux routes.
    • Le contrat API sépare explicitement la gestion binaire des fichiers de la persistance des métadonnées pour garder des retries et validations prévisibles.
  3. Stockage
    • Le NAS est le système de fichiers de référence pour les uploads et les dossiers générés.
    • SQLite stocke les données de profil enseignant, les identifiants d’enregistrement et les références vers les chemins de fichiers persistés.
    • La structure NAS par enseignant est déterministe :
    • `teachers/{teacherId}/application.json`
    • `teachers/{teacherId}/documents/{documentType}-{originalFilename}`
    • `teachers/{teacherId}/dossier/{teacherId}-dossier.pdf`
  4. Infrastructure
    • Cloudflare Tunnel expose l’API publiquement sans ouvrir directement l’hôte NAS à Internet.
    • Les conteneurs Docker isolent le runtime API et simplifient le déploiement entre dev local et exécution NAS.
    • Les chemins local et NAS restent séparés, mais doivent s’aligner sur ports, secrets, contrats payload et réseau Docker.
    • L’architecture assume volontairement une topologie hybride : frontend statique à l’extérieur, API privée à l’intérieur, persistance sur NAS.
  5. Génération du dossier
    • PDFKit génère un dossier enseignant à partir des données d’application persistées dans SQLite.
    • Le PDF final est écrit à côté des documents de l’enseignant pour que le staff récupère un dossier unique plutôt que de reconstruire un cas manuellement.
    • Le livrable final dérive ainsi des données persistées et non d’un état transitoire côté navigateur.
  6. Flux de requête
    • 1. L’enseignant complète le formulaire d’onboarding sur le frontend statique.
    • 2. Les pièces justificatives sont envoyées en multipart vers `/api/upload/teacher`.
    • 3. L’API valide la requête, écrit les fichiers sur le NAS et renvoie les références stockées.
    • 4. Le frontend soumet ensuite le JSON structuré vers `/api/teacher/application`.
    • 5. SQLite persiste le dossier enseignant et le lie aux chemins de fichiers NAS.
    • 6. PDFKit génère le dossier et le stocke à côté des documents uploadés.

Démo

Flux d’onboarding de bout en bout : upload → stockage → API → génération du dossier

Sécurité / Modèle de menace

  • Des uploads non autorisés transformeraient un endpoint public en vecteur d’abus si les requêtes n’étaient pas filtrées.
  • Un décalage de secret entre frontend et backend rejetterait des soumissions valides tout en ressemblant à un bug applicatif.
  • Une mauvaise configuration CORS pourrait soit bloquer le vrai frontend, soit exposer trop largement la surface API.
  • Cloudflare Tunnel réduit l’exposition directe mais ne remplace ni l’autorisation applicative ni la validation de l’origine.
  • Des problèmes de validation fichier (nom, type MIME, taille) peuvent casser les hypothèses de stockage.
  • Perdre le lien entre métadonnées enseignant et documents uploadés recréerait exactement le problème de réconciliation manuelle que le système devait éliminer.

Parcours de débogage

  1. Secret de conteneur obsolète
    • Symptôme : le frontend envoyait des requêtes correctes, mais l’API rejetait encore l’authentification.
    • Diagnostic : le conteneur backend en cours d’exécution utilisait encore un ancien secret partagé alors que le code et l’env local avaient déjà été mis à jour.
    • Correctif : redémarrage du bon conteneur avec l’env attendue, puis revalidation du chemin de requête exact de bout en bout.
  2. Tunnel Cloudflare non démarré
    • Symptôme : l’API répondait sur l’hôte interne, mais le hostname public échouait complètement.
    • Diagnostic : le processus de tunnel n’était pas actif, donc aucun chemin public valide n’existait vers l’origine.
    • Correctif : redémarrage du service tunnel avant toute autre phase de débogage applicatif.
  3. Instabilité QUIC vs HTTP2
    • Symptôme : la connectivité via le tunnel restait intermittente alors que l’API locale était saine.
    • Diagnostic : le motif d’échec se situait dans la couche de transport du tunnel, pas dans le runtime applicatif.
    • Correctif : bascule du tunnel vers un mode HTTP2 plus stable pour cette origine.
  4. Isolation réseau entre conteneurs
    • Symptôme : le tunnel était en ligne mais n’atteignait pas correctement le conteneur backend.
    • Diagnostic : le service tunnel et le conteneur API ne se résolvaient pas sur le bon réseau Docker ni sur le bon port d’origine.
    • Correctif : correction de l’adressage interne et du routage réseau pour cibler le vrai service backend.
  5. 502 Bad Gateway
    • Symptôme : le hostname public répondait, mais Cloudflare retournait un 502.
    • Diagnostic : Cloudflare atteignait bien le tunnel mais ne pouvait pas forwarder vers une origine saine ; le problème était donc côté target origin et non côté DNS.
    • Correctif : correction de la cible backend puis re-test direct de l’origine avant de revérifier le chemin public.
  6. CORS bloquant le frontend statique
    • Symptôme : les soumissions navigateur échouaient alors que l’API était joignable côté serveur.
    • Diagnostic : la gestion du preflight et des headers autorisés ne correspondait pas à l’origine réelle du frontend ni à la forme des requêtes.
    • Correctif : allowlist explicite de l’origine frontend et prise en charge correcte des requêtes `OPTIONS` et des headers attendus.
  7. Décalage de payload (`personal.email` vs `email`)
    • Symptôme : la requête passait partiellement, mais l’email attendu n’était pas correctement persisté.
    • Diagnostic : le frontend envoyait un payload imbriqué alors que le parser backend attendait une structure plus plate.
    • Correctif : normalisation du parser backend sur la forme réelle de la requête et réalignement du contrat API à la frontière.

Compromis & retours d’expérience

NAS vs cloud storage — le NAS garde la persistance locale, prédictible et peu coûteuse, mais couple aussi la réussite du pipeline à la santé du stockage monté et à la disponibilité de l’infra locale.

Secret partagé vs vrai système d’auth — le secret partagé convenait à un flux interne contrôlé et rapide à mettre en place, mais il ne remplace pas un vrai modèle auth si le système s’élargit.

Frontend statique vs backend dynamique — un déploiement Next.js statique simplifie l’hébergement, mais rend le contrat de requête, CORS et la validation aux frontières non négociables.

Séparation uploads / données structurées — séparer le traitement binaire des fichiers de la persistance métier simplifie validation et retry, à condition que SQLite joue bien le rôle de couche de liaison autoritaire.

Runtime hybride local/NAS — l’architecture fonctionne une fois stabilisée, mais la plupart des pannes réelles vivaient dans les joints entre composants : tunnel vers origine, conteneur vers conteneur, navigateur vers API, schéma frontend vers parser backend.

Résultats

Le système final fournit un vrai chemin d’onboarding utilisable par un staff non technique : les candidatures enseignants persistent dans SQLite, les documents persistent sur des chemins NAS déterministes, l’API publique fonctionne de manière fiable via Cloudflare Tunnel, et les dossiers PDF sont générés automatiquement à côté des pièces uploadées. La valeur n’était pas seulement de construire le happy path, mais de durcir le chemin réel de production jusqu’à stabiliser stockage, comportement API et sortie dossier.

Stack technique

Next.jsNode.jsDockerCloudflare TunnelSQLiteASUSTOR NASPDFKit

FAQ

Où sont stockées les données enseignants ?

Les métadonnées structurées vivent dans SQLite, tandis que les documents uploadés et les dossiers générés sont stockés sur le NAS ASUSTOR dans des répertoires par enseignant.

Comment la sécurité est-elle appliquée ?

L’API publique passe par Cloudflare Tunnel mais reste protégée par des contrôles applicatifs : secret partagé, politique CORS explicite et validation serveur des fichiers.

Que se passe-t-il si le NAS est indisponible ?

La chaîne d’upload et de génération perd sa cible de persistance. Le bon comportement est d’échouer clairement plutôt que de déclarer une réussite fictive.

Comment les documents sont-ils liés au dossier enseignant ?

SQLite stocke l’identifiant enseignant et les références vers les chemins NAS, ce qui en fait la couche autoritaire reliant données métier, documents uploadés et dossier généré.

    Pipeline sécurisé d’onboarding enseignants & documents (NAS + Cloudflare + API locale) — Case Study