Passer au contenu
megapromoting
Discutons

Expertise · Maintenance

La maintenance, c’est savoir que le formulaire livre aujourd’hui, pas que le serveur répond 200.

Une surveillance qui vérifie le parcours complet d’une requête jusqu’à un humain, des mises à jour avec gate avant la production et une vérification d’acceptation exécutée après chaque publication. Les trois fonctionnent sur ce site et peuvent être ouvertes de l’extérieur.

Déjà construitTrei implementări proprii, toate în depozitul acestui site și toate verificabile azi: `src/app/api/health/contact/route.ts` (sonda de livrare, răspunde 200 pe live chiar acum), `src/lib/lead-store.ts` (jurnalul append-only cu retenție aplicată la scriere) și `scripts/verify-redesign.ts` (verificarea de acceptare rulată după publicare). Le-am scris pentru că pe 06.09.2026 formularul propriu a căzut tăcut: tokenul botului era revocat, endpointul întorcea 500, iar cererile dispăreau fără urmă. Nu e o poveste de vânzare — e comitul care a produs fișierele de mai sus.

« Le site fonctionne » est une affirmation sur la page d’accueil. Un visiteur qui a rempli le formulaire et a reçu une erreur n’a pas été arrêté par une page en panne, mais par un canal de livraison qui a expiré en silence. La différence entre les deux, c’est tout ce que signifie une maintenance faite sérieusement : nous ne suivons pas si le serveur répond, mais si une requête d’un humain arrive à un autre humain.

Le 6 septembre 2026, c’est exactement ce qui s’est passé sur ce site. Le jeton du bot qui transmettait les demandes du formulaire à l’équipe était révoqué ; l’interface Telegram répondait `401 Unauthorized`, notre route renvoyait 500, et le visiteur voyait « le message n’a pas pu être envoyé ». La demande n’était écrite nulle part. Dans le journal d’erreurs du processus, il y avait trois échecs réels au cours des quelque sept heures écoulées depuis la publication précédente. Personne ne regardait.

Ce qui en est sorti, ce sont trois éléments que nous montons maintenant dans chaque projet que nous maintenons. Un journal append-only écrit *avant* la tentative de livraison, afin qu’un canal cassé se dégrade en « il faut regarder dans le fichier » au lieu de « la requête n’a jamais existé ». Une sonde de santé qui répond à une seule requête si un lead peut atteindre un humain maintenant. Et une vérification d’acceptation qui s’exécute après chaque publication et échoue si une adresse canonique a disparu, une ancre de questions, ou si des adresses qui ne peuvent pas s’ouvrir sont apparues dans le plan du site.

Le reste, c’est une discipline ennuyeuse et vérifiable : des mises à jour avec une porte d’audit avant d’atteindre la production, un redémarrage automatique avec seuil de mémoire et limite de redémarrages instables, une rétention appliquée par le code, non déclarée dans la politique, et des adresses IP tronquées au préfixe réseau avant d’être écrites quelque part.

Ce que cela comprend

Le travail, par composantes

Sonde qui vérifie la livraison, pas la disponibilité

`GET /api/health/contact` répond 200 si un lead peut atteindre un humain maintenant et 503 sinon. Il vérifie deux choses séparément : que le jeton du canal de notification est toujours valide (une vraie requête au fournisseur, avec un timeout de 8 secondes) et que le journal des requêtes est scriptable. La réponse n’est composée que de valeurs logiques — jamais le jeton, l’identifiant du canal ou le nom du bot. Le résultat est conservé en mémoire 60 secondes, afin qu’une sonde externe sollicitée souvent ne devienne pas elle-même du trafic. Lorsque le fournisseur est inaccessible, le champ devient `null`, pas `false` : « je ne sais pas » et « invalide » sont des états différents.

Journal écrit avant la livraison, pas après

Chaque requête reçoit un identifiant et est écrite dans un journal append-only, une ligne JSON par événement, *avant* que la notification ne soit tentée. La deuxième ligne, avec le même identifiant, indique ce qui s’est réellement passé : `delivered` ou `failed`, avec le motif tronqué à 300 caractères. Le dossier est créé avec les droits `0700`, les fichiers avec `0600`, et le chemin est volontairement placé en dehors du répertoire de version, afin que l’historique survive à une publication.

Vérification d’acceptation exécutée après publication

Un script de vérification ouvre chaque page produit, par groupes de trois, et échoue s’il manque le code 200, l’ancre des questions, l’ancre d’exemple ou l’adresse canonique. Il vérifie ensuite la carte du site — qu’elle ne contienne pas de variantes de langue qui ne peuvent pas s’ouvrir et qu’elle contienne chaque produit — ainsi que `robots.txt`, afin de ne pas bloquer les ressources nécessaires au rendu. Il échoue aussi si le contenu d’un ancien client réapparaît dans la page. C’est une liste de choses qui ont déjà cassé une fois.

Mises à jour avec gate, pas avec l’espoir

Le script de publication refuse de démarrer si le serveur a moins de 500 MB de mémoire libre, exécute `npm audit --audit-level=high` et s’arrête sur les vulnérabilités de niveau élevé si vous ne confirmez pas explicitement, puis construit à partir de zéro — `.next` et `node_modules` supprimés, installation à partir du fichier de verrouillage. Si le processus n’apparaît pas `online` après le démarrage, le script affiche les 50 dernières lignes du journal et quitte avec une erreur, au lieu de signaler un succès.

Redémarrage avec des seuils, pas au hasard

Le processus redémarre automatiquement au-dessus de 500 MB de mémoire, avec un délai de 4 secondes entre les tentatives, une augmentation exponentielle du délai et un arrêt après 10 redémarrages instables — afin qu’une boucle de plantage devienne visible au lieu de consommer le serveur en silence. Délai de grâce à l’arrêt : 5 secondes, puis arrêt forcé. Les journaux ont une date et un fuseau horaire, dans un seul flux.

Doublons traités, pas comptés deux fois

La même personne, le même message, deux fois — un double clic, un rechargement de la page — produisait deux notifications identiques pour une seule demande. Désormais, l’empreinte `sha256` de la paire adresse + message est conservée 10 minutes en mémoire ; la deuxième soumission reçoit le même identifiant de demande et le marquage `duplicate`, et la notification n’est pas répétée. Seuls les doublons d’une livraison réussie sont supprimés ; une livraison échouée a le droit de passer à nouveau.

Des codes d’erreur qui disent ce qui s’est cassé

400 pour des données invalides, 405 pour une mauvaise méthode, 500 pour un corps de requête corrompu, 502 quand le canal de livraison a mal répondu, 503 quand les identifiants manquent. La différence compte à 3 heures du matin : 502 signifie « le fournisseur », 503 signifie « notre configuration ». Le visiteur reçoit distinctement « nous avons reçu la demande, mais nous n’avons pas pu notifier » — pas un faux succès.

Rétention appliquée par le code, pas promise dans la politique

La note de confidentialité publiée indique que les demandes du formulaire sont conservées 24 mois. Le code applique exactement le même nombre : `LEAD_RETENTION_DAYS` par défaut 730, et les fichiers plus anciens que le seuil sont supprimés à chaque écriture, sans planificateur qui puisse être oublié. L’adresse IP est tronquée au préfixe de réseau — `/24` en IPv4, `/48` en IPv6 — et l’on lit le dernier saut dans l’en-tête, pas le premier, parce que le premier est envoyé par le client.

Nous trouvons aussi ce que personne n’a signalé

Le même passage dans le code a mis au jour deux choses qu’aucun utilisateur n’avait signalées : la limite de 10 requêtes par minute pouvait être complètement contournée, parce que le premier élément de l’en-tête des adresses redirigées était lu — celui contrôlé par le client — et une route d’initialisation des appels téléphoniques était ouverte anonymement, aux frais et depuis notre numéro, bien que le composant qui l’utilisait ne soit plus monté nulle part. Les deux ont été corrigées et vérifiées en production.

À quoi cela ressemble

Le parcours, étape par étape.

01

L’inventaire des chemins par lesquels une demande atteint un humain

La première livraison n’est pas un outil, c’est une liste : par quoi passe une demande depuis l’appui sur le bouton jusqu’à quelqu’un qui la lit, ce qui casse à chaque étape et ce qui devrait se produire quand cela casse. Sur ce site, la liste comptait trois maillons et l’un était invisible.

02

La sonde et le journal, montés avant toute autre chose

Nous livrons une adresse de santé qui vérifie le trajet complet, pas le processus, et le journal qui écrit avant la livraison. À partir de maintenant, une panne est une question avec réponse, pas une fouille dans les journaux. L’adresse peut être interrogée depuis n’importe quel service externe de surveillance, car elle ne renvoie rien de sensible.

03

La vérification d’acceptation, écrite à partir de défauts réels

Chaque chose qui a été cassée une fois entre dans le script de vérification exécuté après la publication. Nous n’écrivons pas de tests pour des cas hypothétiques ; nous écrivons pour ceux qui nous ont déjà coûté. Nous livrons le script, pas seulement son résultat — vous pouvez aussi l’exécuter.

04

Le rythme de mise à jour et le gate avant la production

Nous définissons ce qui se met à jour automatiquement, ce qui passe par une vérification humaine et ce qui ne se touche pas sans fenêtre annoncée. Le gate inclut l’audit de sécurité des dépendances et la vérification des ressources du serveur, tous deux exécutés avant de toucher à la production.

05

La remise, avec la liste des manques

À la fin, nous remettons la procédure de publication, la procédure de retour arrière, les adresses de santé et la liste écrite de ce qui n’est pas couvert. Sur ce site, par exemple, la branche d’expiration de la requête vers le fournisseur est implémentée et tombe dans le même traitement que l’erreur réseau, mais l’abort en lui-même n’a pas été provoqué en test — c’est écrit ainsi dans le journal d’exécution, pas dans une note interne.

1Sonda care verificălivrarea până la unom2jurnalul scrisînainte de încercare3verificarea deacceptare rulată dupăfiecare publicareTrei piese, toate în depozitul acestui site.
Traseul, în 3 pași

Les données

Ce que nous touchons, où elles sont et combien de temps elles restent

Les questions que pose toute personne ayant un délégué à la protection des données — posées ici avant qu'il ne les pose.

Journal des requêtes
Nom, adresse e-mail, téléphone, société, service choisi, message, page depuis laquelle il a été envoyé, hôte de la page d’origine (seulement l’hôte, pas l’adresse complète) et préfixe réseau du visiteur. Fichiers JSON par jour, sur le serveur propre, en dehors du répertoire de version. Durée : 24 mois, appliquée à l’écriture.
Journaux de processus et de serveur
La sortie standard et les erreurs du processus, avec date et fuseau horaire, plus les journaux du serveur web. Ici, on voit une panne silencieuse : les trois échecs de livraison de l’incident de référence se trouvaient dans le journal d’erreurs, pas dans une alerte. La rotation et la durée sont établies pour chaque serveur ; ce sont des données techniques, pas du contenu de compte.
Ce qui ne sort jamais du serveur
Les jetons, les identifiants de canal et les clés du fournisseur. La sonde de santé répond exclusivement avec des valeurs logiques, justement pour pouvoir être appelée par un outil externe sans rien divulguer. Même règle dans la notification : le message contient l’identifiant de la requête et la page, pas les identifiants.
Statistiques de trafic
La mesure du trafic passe par l’outil d’analyse configuré sur le projet et s’active après consentement. Le paramètre réel de rétention dans le compte d’analyse est une valeur que nous lisons dans le compte, pas une valeur que nous supposons — le registre des traitements de ce site l’a marqué explicitement comme élément à clarifier, et non comme fait établi.
Registre des traitements
Chaque type de données touché par le site a une entrée avec finalité, base, catégories et durée, dans un document versionné à côté du code. Quand le code change une durée, le document change dans le même commit — sinon la politique et le programme disent des choses différentes, et celui qui se trompe est généralement le document.

Un cas

Un formulaire qui a livré 500 au lieu de leads, sept heures

La situation

Un site vitrine avec formulaire de contact, publié récemment. Toutes les pages répondent 200, le tableau de bord est vert, personne ne signale rien. Le seul canal par lequel une requête parvenait à l’équipe était une notification dans une application de messagerie.

Ce que nous avons construit

Le jeton du bot de notification avait été révoqué ; l’interface du fournisseur répondait `401 Unauthorized`. La route du formulaire renvoyait 500 et n’écrivait rien. La première correction n’a pas été le jeton, mais l’ordre des opérations : la requête est désormais écrite dans un journal append-only, avec son propre identifiant, *avant* de tenter la livraison, et le résultat de la livraison est ajouté comme deuxième ligne. Par-dessus, on a monté une sonde publique de santé qui vérifie le jeton et la possibilité d’écriture, des codes d’erreur distincts pour « le fournisseur a répondu mal » et « il nous manque des identifiants », un timeout de 10 secondes sur la livraison et la suppression des doublons sur une fenêtre de 10 minutes. Le même passage dans le code a aussi fait sortir une limite de requêtes contournable et une route d’appels téléphoniques restée ouverte anonymement.

Ce qui en est sorti

La sonde répond maintenant 200 avec le jeton valide et le journal inscriptible ; elle peut être interrogée depuis n’importe quel service externe de surveillance, sans rien divulguer. Les tests en production ont couvert chaque code de réponse — 400, 405, 500, 502, 503 — et deux envois identiques ont renvoyé le même identifiant de requête, le second marqué comme doublon, avec deux lignes dans le journal, pas quatre. Une future panne du canal de notification n’efface plus la requête : elle reste dans le journal, avec la raison écrite.

Ce que le cas ne dit pas

La sonde dit que la livraison est possible maintenant, pas que quelqu’un lit les notifications. Qui les regarde et en combien de temps, c’est une décision de l’équipe, pas une fonction du code. Et la branche d’expiration de la requête vers le fournisseur, bien qu’implémentée, n’a pas été provoquée en test — elle tombe dans le même traitement que l’erreur réseau, qui a été testée ; nous la notons comme non exercée, pas comme vérifiée.

Questions

Ce que les gens nous demandent avant d'appeler

Qu’est-ce que vous surveillez, concrètement ?

Le parcours d’une demande jusqu’à un humain, pas la disponibilité du serveur. `GET /api/health/contact` vérifie en une seule requête deux choses : que le jeton du canal de notification est encore valide — via un appel réel au fournisseur, avec un timeout de 8 secondes — et que le journal des demandes peut être écrit. Il répond 200 quand les deux sont vraies et 503 quand ce n’est pas le cas. Vous pouvez l’appeler dès maintenant sur ce site : il est public, justement parce qu’il ne renvoie que des valeurs logiques.

Pourquoi un formulaire tomberait-il sans que personne ne le remarque ?

Parce que la partie visible continue de fonctionner. La page se charge, le bouton répond, le serveur renvoie 200 sur toutes les pages — seule la dernière étape se casse, celle qui n’a pas d’interface. Sur ce site, le jeton du bot de notification a été révoqué : le fournisseur répondait `401 Unauthorized`, la route renvoyait 500, et la demande n’était écrite nulle part. Trois échecs réels en environ sept heures, visibles uniquement dans le journal d’erreurs du processus. C’est pour cela que le journal s’écrit désormais avant la livraison : même si la notification échoue, la demande existe.

Que se passe-t-il si la notification échoue après que vous ayez réparé ?

La demande est déjà écrite dans le journal avec son propre identifiant, et la deuxième ligne du journal indique `failed` ainsi que la raison. Le visiteur reçoit une réponse distincte — « nous avons reçu la demande, mais nous n’avons pas pu notifier » — pas un faux succès. Le code de réponse différencie la cause : 502 signifie que le fournisseur a mal répondu, 503 que les identifiants manquent chez nous. À 3 heures du matin, la différence entre les deux décide si vous appelez quelqu’un ou non.

Mettez-vous à jour automatiquement les dépendances ?

Pas sans gate. La publication exécute `npm audit` au niveau `high` et s’arrête sur des vulnérabilités de ce niveau si la poursuite n’est pas confirmée explicitement ; elle vérifie d’abord aussi la mémoire disponible du serveur, avec un seuil de 500 MB, car une construction lancée sur un serveur étroit laisse l’application arrêtée. La construction se fait à partir du propre : le répertoire de construction et les dépendances sont supprimés, l’installation se fait à partir du fichier de verrouillage. Ce qui est mis à jour automatiquement et ce qui passe par une vérification humaine se décide par projet, pas par défaut.

Comment savez-vous qu’une publication n’a pas cassé autre chose ?

Par un script de vérification exécuté après la publication, qui ouvre chaque page produit et échoue s’il manque le code 200, l’ancre des questions, l’ancre de l’exemple ou l’adresse canonique. Il vérifie ensuite le sitemap — qu’il contienne chaque produit et ne contienne pas de variantes de langue qui ne peuvent pas s’ouvrir — et `robots.txt`, pour ne pas bloquer les ressources de rendu. Chaque vérification de la liste correspond à quelque chose qui s’est déjà cassé une fois. Le script est livré avec le projet.

Que faites-vous lorsque l’application se bloque ou consomme de la mémoire ?

Le processus redémarre automatiquement au-delà du seuil de 500 MB, avec 4 secondes de délai entre les tentatives et une augmentation exponentielle, mais il s’arrête après 10 redémarrages instables sur une courte période. C’est important : une boucle de plantage qui redémarre à l’infini paraît saine dans un tableau de bord et consomme le serveur en silence. Nous préférons que le processus reste arrêté et visible.

Combien de temps conservez-vous les données du formulaire et qui peut les lire ?

24 mois, appliqués par le code, pas seulement déclarés : le seuil est une variable avec la valeur par défaut de 730 jours, et les fichiers plus anciens sont supprimés à chaque nouvelle écriture, sans planificateur qui peut être oublié. Le répertoire a les droits `0700`, les fichiers `0600`, et le chemin se trouve en dehors du répertoire de version, afin que l’historique survive à la publication. L’adresse IP n’est pas conservée en entier : elle est tronquée à `/24` pour IPv4 et `/48` pour IPv6, en lisant le dernier saut dans l’en-tête, pas le premier — le premier est envoyé par le client et ne signifie rien.

Trouvez-vous aussi des problèmes que nous n’avons pas signalés ?

Cela arrive, et ce sont généralement les plus coûteux. Le même passage dans le code qui a réparé le formulaire a mis au jour deux choses que personne n’avait signalées : la limite de requêtes pouvait être contournée complètement, parce qu’on lisait le premier élément de l’en-tête d’adresses redirigées — exactement celui que le client envoie ; et une route qui initiait des appels téléphoniques était ouverte anonymement, à nos frais, alors que le composant qui l’utilisait avait été démonté. Les deux ont été réparées et vérifiées en production. Ce que nous ne pouvons pas promettre, c’est de tout trouver ; ce que nous pouvons promettre, c’est de signaler aussi ce que vous n’avez pas demandé.

Que ne couvre pas la maintenance ?

Ce n’est pas un service de sécurité avec surveillance non-stop et ce n’est pas un centre d’opérations. Nous ne garantissons pas un pourcentage de disponibilité sans une mesure qui le soutienne — et, pour que ce soit clair, à ce moment-ci nous ne publions pas un tel chiffre pour notre propre site. Nous ne prenons pas en charge la responsabilité d’une plateforme tierce à laquelle nous n’avons pas accès. Et nous ne traitons pas une page qui répond 200 comme une preuve que tout fonctionne : c’est exactement le problème à partir duquel tout ce qui est écrit ci-dessus a commencé.

Sur quoi reposent les affirmations ci-dessus (13 sources)
  1. Sonda de livrare răspunde 200 pe producție: `{"ok":true,"telegram":{"configured":true,"credentialsValid":true},"leadJournal":{"writable":true}}`https://www.megapromoting.com/api/health/contact · 2026-09-06
  2. Antete de securitate active pe producție: HSTS `max-age=63072000; includeSubDomains; preload`, `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy` cu `microphone=(self)`https://www.megapromoting.com/servicii · 2026-09-06

11 d'entre elles sont du code et des fichiers de nos dépôts. Nous n'en publions ni le nom ni la ligne : réunies sur une seule page, elles décriraient trop précisément comment sont construits des systèmes qui ne sont pas seulement les nôtres. Nous les parcourons avec vous, dans le dépôt, sur demande — la vérification reste possible, elle se fait simplement lors d'un échange.

Qu'est-ce que vous voudriez voir mieux fonctionner ?

Racontez-nous votre processus. Ensemble, nous décidons ce qui vaut la peine d'être construit, ce que nous pouvons connecter et comment nous vérifions le résultat.

Discutons