Soumissions

Pipeline de soumission, vérification d'origine, anti-robots, pièces jointes, courriels et traitement des réponses dans l'admin.

Une soumission traverse sept étapes, dans cet ordre. Comprendre lesquelles rejettent et lesquelles laissent passer est la clé du diagnostic quand un formulaire ne fonctionne pas.

ÉtapeRejette avec
1. Limitation de débit429 Too Many Requests
2. Vérification d'origine403 Forbidden
3. Formulaire actif404 Not Found
4. Corps de formulaire lisible400 Bad Request
5. Anti-robotsfaux succès, ou 400 Bad Request
6. Validation des champs400 Bad Request avec le détail par champ
7. Enregistrement500 Internal Server Error

Les courriels partent après l'enregistrement, et leur échec n'a jamais d'effet sur la réponse.

Limitation de débit

Le routeur du core applique sa limite standard avant tout le reste : 300 requêtes par minute et par adresse, le triple pour un visiteur connecté, puis un blocage de deux minutes. Le point de soumission n'y échappe pas.

Derrière un proxy inverse, la résolution de l'adresse cliente demande que LRVO_TRUST_PROXY déclare le nombre de proxys traversés. Sans elle, l'adresse n'est pas résolue et la limitation est silencieusement inactive.

Vérification d'origine

Il n'existe aucun jeton CSRF dans ce système. La protection est une comparaison d'origine appliquée par le routeur du core à toute requête modifiante.

L'en-tête Origin de la requête, ou Referer à défaut, doit correspondre à l'origine déclarée dans NEXT_PUBLIC_URL, ou NEXT_PUBLIC_URL_DEV en développement.

Trois conséquences directes :

  • Un formulaire posté depuis un autre domaine est refusé par construction. Le module n'est pas conçu pour une intégration inter-domaines.
  • Si aucune des deux variables n'est renseignée, toute soumission est refusée. C'est la panne la plus fréquente à l'installation.
  • Une soumission depuis la même origine passe automatiquement, sans jeton à gérer côté client.

Voir Configuration.

Format de la requête

Le corps doit être un formulaire encodé. Les composants du module envoient du multipart/form-data, seul encodage capable de porter des fichiers. Le parseur accepte aussi application/x-www-form-urlencoded pour un formulaire sans pièce jointe. Un corps JSON est refusé dans tous les cas.

Chaque champ de saisie est envoyé sous son name, plus deux champs techniques décrits ci-dessous. Les composants fournis par le module s'en chargent. Voir Affichage.

Anti-robots

Deux mécanismes, aucun service externe, aucun captcha.

Le champ piège

Un champ nommé _hp est rendu invisible dans le formulaire. Un visiteur ne le voit pas, un robot le remplit.

S'il est rempli, la réponse est un faux succès : rien n'est enregistré, aucun courriel n'est envoyé, et l'appelant reçoit exactement la même réponse qu'une soumission réussie.

C'est délibéré : un robot qui recevrait une erreur adapterait son comportement. Mais cela a une contrepartie à connaître : un visiteur dont l'extension de remplissage automatique renseigne le champ perd sa soumission sans le savoir. Le cas est rare, il n'est pas impossible.

Le délai minimal

Le formulaire envoie dans _t l'horodatage du moment où il a été affiché. Le serveur en déduit le temps de remplissage et refuse en dessous du seuil du formulaire, trois secondes par défaut.

Le refus est explicite : « Soumission trop rapide. Réessayez dans un instant. »

Un _t absent, vide ou illisible est traité comme un délai négatif, donc refusé dès que le seuil est supérieur à zéro. Une intégration maison doit donc envoyer elle-même l'horodatage du moment où elle a affiché le formulaire, sans quoi elle échoue systématiquement.

Régler le seuil à 0 désactive entièrement ce contrôle, y compris pour un appelant qui n'envoie pas _t.

Ce contrôle repose sur une valeur fournie par le client : un robot qui envoie un _t suffisamment ancien passe. Il écarte les soumissions automatisées naïves, il ne prétend pas à davantage.

Pièces jointes

Pour chaque champ de type fichier, tous les fichiers non vides envoyés sous ce nom sont traités.

Chacun est chargé en mémoire, validé contre l'extension autorisée, la taille maximale du champ et sa signature binaire, puis envoyé au stockage objet sous la clé forms/<identifiant du formulaire>/<nom unique>.

Seules les métadonnées sont enregistrées avec la soumission : nom du champ, clé de stockage, nom d'origine, taille et type. Le contenu reste au stockage.

Ces fichiers sont privés. Aucune URL publique n'existe : ils ne sont accessibles que par la route de téléchargement de l'admin, protégée par forms.view.

Supprimer une soumission ou un formulaire ne supprime pas les objets du stockage. Ils deviennent orphelins et occupent de l'espace jusqu'à une purge manuelle.

Une soumission rejetée en produit également. Les fichiers sont téléversés au fil de la validation : si le troisième est refusé, les deux premiers sont déjà au stockage et y restent, alors qu'aucune soumission n'est enregistrée.

Ce qui est enregistré

ChampContenu
dataLes valeurs, indexées par nom de champ.
filesLes métadonnées des pièces jointes.
metaAdresse IP, agent utilisateur, référent.
statusnew à la création.

L'adresse IP est lue dans x-forwarded-for, en prenant la première entrée, puis dans x-real-ip. Absente des deux, elle vaut null, comme l'agent utilisateur et le référent absents.

Ces trois valeurs sont prises telles quelles dans les en-têtes, sans la vérification LRVO_TRUST_PROXY qui protège la clé de limitation de débit. Un appelant peut donc les falsifier. Elles servent au diagnostic, pas à l'identification.

Voir Modèle de données.

Courriels

Deux envois indépendants, tous deux facultatifs, lancés en parallèle après l'enregistrement. Ils passent par le service de courriel du core, donc par le fournisseur que le projet a configuré, Resend ou SMTP.

Notification à l'équipe

Envoyée aux destinataires configurés sur le formulaire. Elle porte un tableau récapitulatif des réponses, et un lien direct vers la soumission dans l'admin quand NEXT_PUBLIC_URL est renseignée.

L'adresse de réponse est positionnée sur la première valeur de champ courriel de la soumission, ce qui permet de répondre au visiteur directement depuis sa boîte.

Les champs cachés et les éléments de mise en page sont exclus du tableau. Les fichiers y apparaissent par leur nom.

Accusé de réception

Envoyé au visiteur, à l'adresse contenue dans le champ courriel désigné dans les réglages. Le repli sur la première valeur de champ courriel de la soumission ne joue que si aucun champ n'est désigné : un champ désigné mais laissé vide abandonne l'envoi, sans repli. L'envoi est aussi abandonné, silencieusement, si l'adresse trouvée ne contient pas d'arobase.

Le corps porte le message personnalisé du formulaire, ou un remerciement générique à défaut. Le récapitulatif des réponses est ajouté en dessous dans les deux cas.

Toutes les valeurs sont échappées avant insertion dans le HTML. Les couleurs des deux courriels, fonds, filets, texte et lien, sont les valeurs claires du thème du projet, lues au moment de l'envoi.

Réponse au client

En cas de succès, la réponse porte le mode de succès configuré : un message à afficher, ou une URL de redirection.

En cas d'erreur de validation, la réponse est un 400 dont le corps porte, en plus du message général, un objet des erreurs par champ. Les composants livrés par le module n'affichent aujourd'hui que le message général : une intégration maison peut exploiter le détail.

Traitement dans l'admin

Une soumission porte trois statuts : new, read et spam.

Ouvrir la fiche d'une soumission new la bascule automatiquement en read. Le statut spam est manuel, il ne déclenche aucun apprentissage ni filtrage, et la fiche permet de revenir à new.

La liste /admin/forms/submissions filtre par statut et par formulaire, cinquante par page, du plus récent au plus ancien.

Il n'existe pas d'export. Les soumissions se consultent une par une. Voir Administration pour le détail des écrans.

Notes

  • Une erreur de courriel est journalisée mais n'apparaît jamais au visiteur, dont la soumission a bien été enregistrée.
  • Supprimer un formulaire supprime ses soumissions par cascade, immédiatement et sans confirmation en base.
  • La table des soumissions n'a aucune purge automatique. Prévoir une politique de rétention si les formulaires collectent des données personnelles.

Sur cette page