Niji.fr / Un site éco-conçu - Site statique et formulaire : comment faire ?

Niji.fr / Un site éco-conçu - Site statique et formulaire : comment faire ?

Créer un formulaire de contact sur un site totalement statique est une vraie problématique. Je me suis confronté à cette difficulté lors de la refonte du site niji.fr.

Comment créer un formulaire de contact sur un site statique ?
Où partent les données ?
Comment sont elles stockées ?
Comment sont elles consultées par les contributeurs ?

Il y avait bien l'option de simplement implémenter un lien mailto: à la place du formulaire de contact, mais en terme d'expérience utilisateur, on a vu mieux.

Comment faire ?

On les envoie dans un CMS ?

Dans notre architecture, il y a un CMS (Strapi), après tout, les soumissions de formulaire sont des données comme les autres, donc pourquoi ne pas les stocker dans le CMS ?
Le CMS n'étant allumé que temporairement pour la contribution, cette solution n'était pas envisageable.

On utilise un service externe pour gérer le formulaire ?

Dans notre optique d'éco-conception, ajouter une librairie JS dans notre site, qui aurait fait appel à un service externe pour lequel nous ne maitrisions pas du tout le niveau d'éco-conception, ce n'était pas une option viable.

On les stocke sur le serveur ?

En effet, c'est la solution, mais comment ?

Le site aurait pu être être servi par un CDN, mais heureusement, le choix a été fait d'héberger le site sur une VM dans un datacenter durable.

J'ai donc à ma disposition un serveur HTTP, en l'occurence NGINX, dont j'ai pu détourner une des fonctionnalités de base qu'est la gestion des logs.

Utiliser les logs NGINX pour stocker des données

Dans la configuration du serveur nginx.conf, j'ai :

  • Ajouté un nouveau format de logs
log_format json_ingest escape=json '{ "timestamp": "$time_iso8601", "body": "$request_body" }';

Dans ce format de log, j'enregistre au format json le contenu du formulaire de contact qui lui-même est envoyé en JSON, ce qui donne ceci :

{
    "timestamp": "2023-02-14T12:03:37+00:00", 
    "body": "{\"firstname\":\"Xxxxxx\",\"lastname\":\"Xxxxxx\",\"company\":\"Xxxxxx\",\"function\":\"Xxxxxx\",\"email\":\"xxx.xxx@xxx.com\",\"phone\":\"06 06 06 06 06\",\"message\":\"Bonjour Niji,\\n\\nXxxxxxx xxxxx xxxxx xxxxxx xxxxxx xxxxx xxxx\\n\\nCordialement,\"}"
}

Dans la configuration du host, j'ai ajouté une location /contact {} :

  • Limitée aux requêtes POST

  • Envoyant la requête vers une route "fake" qui se contente d'envoyer une réponse vide avec le code 202 - Accepted

  • Utilisant

    • la nouvelle limite précédement créée

    • le nouveau format de log (en plus de l'access log standard)

server {
    location /contact {
        limit_except POST {
            deny all;
        }
        access_log /xxx/xxx/xxx/contact.log json_ingest;
        access_log /var/log/nginx/access.log main;
        proxy_pass 'https://www.niji.fr/dev/null';
    }

    location /dev/null {
        access_log off;
        return 202;
    }
}

A ce stade, j'ai toutes les soumissions au formulaire de contact dans un fichier json.

Il fallait ensuite utiliser ces données pour les envoyer aux membres de notre service communication, qui se cachent derrière une adresse email générique.

J'ai donc écrit un script bash, qui est exécuté toutes les 5 minutes (crontab), et qui s'occupe d'envoyer par mail chaque demande

#!/usr/bin/env bash

set -e
date=$(date '+%Y%m%d-%H%M%S')
cd /xxx/xxx/xxx

if [ -s contact.log ] ;
then
    split -d -l 1 contact.log /xxx/xxx/xxx/messages/${date}_ --additional-suffix=.json --suffix-length=2 --numeric-suffixes=1
    : > contact.log
    for i in $(find /xxx/xxx/xxx/messages -type f -name "${date}_*.json") 
    do
        json=$(cat $i | jq --raw-output '(.body |= fromjson | [leaf_paths as $path | {"key": $path | join("_"), "value": getpath($path)}] | from_entries)')
        if [[ ! -z $(echo $json | jq .body_message) ]]; then echo $json | jq --raw-output 'to_entries[] | "\(.key): \(.value)"' | mail -s "[niji.fr] Demande de contact" contact@niji.fr; fi
    done
fi

Voilà comment à partir d'une solution très simple et pragmatique nous avons pu créer un formulaire de contact sur un site statique, sans recourir à des outils tiers.