Artentia
Plateforme communautaire en production depuis 2025
En bref
50+
Inscrits sur la plateforme, selon mes propres chiffres
35
Joueurs connectés au moins une fois au serveur, selon mes propres chiffres
92
Tests de bout en bout (Playwright) recensés
12 Mo
Galerie d’images, contre 161 Mo de PNG au départ
Contexte
Artentia est une communauté de jeu en ligne francophone autour de Minecraft. Le projet date de 2023 et a connu plusieurs versions avant l’actuelle. J’ai conçu, développé et déployé la plateforme actuelle sur artentia.fr, et je l’exploite depuis sa mise en production en 2025.
Le site gère les comptes des joueurs et une boutique de grades, avec un panneau d’administration pour moi. Il est relié au serveur de jeu : l’ajout d’un joueur à la liste blanche et la livraison d’un grade acheté sont déclenchés par le site.
Plus de 50 personnes sont inscrites sur la plateforme, 35 joueurs se sont connectés au moins une fois au serveur et 15 sont très actifs. Ce sont des chiffres que je communique moi-même, pas une mesure externe.
La boutique est actuellement fermée. Je décris ici son fonctionnement tel qu’il est dans le code.
Ce que j’ai construit
Le site public est une application React servie en fichiers statiques. Elle appelle une API Node.js (Express) hébergée à part, sur un VPS Linux avec Nginx en reverse proxy, HTTPS et un service systemd. Les deux parties se déploient séparément et partagent un cookie de connexion sur le domaine commun.
- Comptes : inscription par email avec un code de vérification à 6 chiffres valable 10 minutes, puis connexion par mot de passe ou avec Google. Les mots de passe sont hachés avec bcrypt. Les codes sont stockés sous forme d’empreinte salée, et des tentatives échouées répétées verrouillent temporairement l’adresse.
- Comptes liés : un joueur peut relier à son profil plusieurs comptes (Discord, Twitch, Steam) et son compte Microsoft pour Minecraft. La liaison Microsoft passe par un flux OAuth avec PKCE, et le pseudo confirmé par Microsoft sert ensuite à la liste blanche et aux achats.
- Sessions : le cookie de connexion est un jeton signé, en httpOnly, valable 30 jours. Les sessions côté serveur, utilisées pendant les liaisons de compte, sont stockées dans PostgreSQL.
- Boutique : paiement par Stripe Checkout, avec des prix calculés côté serveur. Le panier ne transmet que des identifiants de produit et des quantités (de 1 à 12).
- Webhook Stripe : la signature de chaque événement est vérifiée. Un événement rejoué ne livre rien une seconde fois, car une commande ne passe de « non payée » à « payée » qu’une seule fois. La création de la session de paiement porte aussi une clé d’idempotence.
- Livraison automatique : un grade payé ajoute des commandes dans une file, stockée dans PostgreSQL. Un service relié au serveur de jeu par RCON les consomme. L’API les lui remet par lots sans doublon, et il rapporte la réussite ou l’échec. Une commande réservée mais jamais confirmée est reprise au bout de 5 minutes, avec 10 tentatives au maximum.
- Chaîne site vers jeu : quand un joueur lie son compte Minecraft, le site met son pseudo dans la file de la liste blanche, à condition que je l’aie activée dans le panneau d’administration. Les grades achetés suivent le même chemin vers le serveur.
- Administration : un panneau réservé aux administrateurs, dont l’accès est vérifié côté serveur à chaque requête. Il donne accès aux comptes, aux achats et aux grades, aux journaux du serveur (un fichier avec rotation à 5 Mo), aux métriques du serveur et à la liste blanche.
- Garde-fou du panneau : un administrateur ne peut ni retirer son propre statut ni supprimer son propre compte, ce qui évite de se verrouiller dehors. Chaque écriture est consignée dans les journaux.
- Performance : la galerie est passée d’environ 161 Mo de PNG à 12 Mo de WebP, miniatures comprises. Le fond animé n’utilise que des transformations CSS, peu coûteuses à afficher. Un mode performance adaptatif coupe les effets coûteux sur un appareil modeste, sur une connexion lente ou quand le visiteur demande moins de mouvement.
Décisions et incidents
Les prix ne viennent plus du navigateur. Mon audit a révélé une faille « prix côté client » : le montant d’un panier pouvait venir de l’extérieur. J’ai déplacé les prix dans une table côté serveur, qui est la seule référence. Le navigateur n’envoie plus que des identifiants et des quantités, et un prix glissé dans la requête est ignoré ou refusé. De plus, la livraison ne part que si le montant encaissé par Stripe, remise d’un code promo comprise, correspond au prix calculé par le serveur. Sinon l’achat reste payé mais non livré, et je le traite à la main.
J’ai mené un audit de production sur ma propre plateforme. La note interne de départ était de 74 sur 100, et deux défauts bloquants en sont ressortis. Le premier : aucune limitation de débit sur la connexion par mot de passe, donc des essais en nombre illimité. Elle est désormais limitée, comme l’envoi des codes. Le second : la politique CORS reflétait n’importe quelle adresse publique, cookies autorisés. Elle est désormais restreinte aux origines autorisées.
Le front envoie des en-têtes de sécurité, dont HSTS.
Un incident m’a appris où regarder. Le webhook de paiement de Stripe pointait vers le front statique au lieu de l’API. Le front répondait 200 avec sa page d’accueil, puisqu’il la renvoie pour toute adresse inconnue. Stripe croyait donc l’événement livré, et aucun paiement n’arrivait au serveur. J’ai trouvé la cause en regardant la réponse HTTP, puis corrigé l’URL. J’ai ensuite ajouté un garde-fou : une commande n’est marquée payée que si Stripe confirme l’encaissement.
Les sessions étaient d’abord gardées en mémoire par le serveur, donc perdues à chaque redémarrage. Je les ai déplacées dans PostgreSQL. Elles survivent maintenant à un redémarrage du service, ce qui évite aussi d’interrompre une liaison de compte Microsoft en cours.
Fermer la boutique ne dépend pas seulement de la page. Tous les articles sont retirés de la vente dans le code, la page affiche « La boutique est fermée », et l’API refuse de créer un paiement tant qu’un indicateur d’ouverture n’est pas activé côté serveur.
Preuves
Je ne publie pas le dépôt. Les chiffres ci-dessous viennent du code de la plateforme et de ses suites de tests.
- 92 tests Playwright recensés dans trois fichiers, tous en lecture seule. Les tests de fumée chargent 14 pages publiques du site en ligne, vérifient le repli de l’application, le formulaire de connexion, la santé de l’API et le refus d’une requête sans session. Les autres simulent les réponses de l’API. Deux tests sont ignorés par défaut : l’un exige un compte de test, l’autre un article en vente, donc une boutique ouverte.
- Huit fichiers de tests unitaires côté serveur, dont ceux de la table des prix, de la politique CORS et de la liste blanche. Les prix de la boutique sont dans un module sans accès à la base ni à Stripe, ce qui permet de les tester seuls.
- Audit de production de ma propre plateforme : note de départ de 74 sur 100, deux défauts bloquants corrigés, puis la faille de prix côté client.
- Redirection forcée vers HTTPS et en-têtes de sécurité (dont HSTS) posés par la configuration Apache du front.
- Exploitation : API en service systemd derrière Nginx avec HTTPS, journaux avec rotation, et une route de santé que mes tests interrogent.
Ces tests vérifient que les pages se chargent et que les garde-fous répondent. Je ne lance pas de paiement complet sur le site en ligne, pour ne pas créer de vraies commandes.
Technologies
- Front
- React 18
- TypeScript
- Vite
- Tailwind
- react-router
- API
- Node.js
- Express
- Données
- PostgreSQL
- Paiement
- Stripe Checkout
- Hébergement
- Mutualisé OVH (front)
- VPS Linux (API)
- Nginx
- HTTPS
- systemd
- Livraison
- Déploiement continu du front
- Tests
- Playwright
- Tests unitaires Node.js