Historique des évolutions LB'XMB (site, bot Discord, forum, boutique, intégrations). Cette page est synchronisée avec le document interne de récapitulatif.
Patch Notes (journal horodaté)
### 2026-04-26 21:02 (UTC+2)
Affichage des salons configurés avec trop d'indicateurs (✅/❌, 📎/🖼️, doublon du nom), lecture peu claire.
Le bouton retour renvoyait souvent au menu principal au lieu de la page parent logique.
Besoin de traduction des panels et réponses bot selon la langue client Discord.
### 2026-04-26 21:08 (UTC+2)
Impossible de garantir 100% sans observer les chaînes qui passent en fallback.
Besoin d'un comportement persistant : journaliser chaque changement en patch note dans ce document, même si la conversation change.
### 2026-04-26 21:12 (UTC+2)
Les informations existaient dans un document interne, mais pas dans une page web dédiée et proprement présentée.
La nouvelle page devait être accessible sans URL tapée manuellement.
### 2026-04-26 21:10 (UTC+2)
Après activation de l’i18n globale, certaines interactions pouvaient dépasser le délai Discord (3s) ou échouer dans le patch i18n.
### 2026-04-26 21:15 (UTC+2)
Plusieurs textes restaient en français dans les embeds, boutons et descriptions d’options, et le bouton retour affichait Back / Back.
### 2026-04-26 21:30 (UTC+2)
Plusieurs retours de panneau ne répondaient plus.
Un grand nombre de textes restaient partiellement en français (titres, descriptions, placeholders, variables, statuts, labels d’actions).
### 2026-04-26 22:00 (UTC+2)
La description de la commande affichée dans le sélecteur / liste des commandes restait en français pour tous les clients.
### 2026-04-26 22:40 (UTC+2)
### 2026-04-26 22:43 (UTC+2)
### 2026-04-26 22:44 (UTC+2)
### 2026-04-26 22:46 (UTC+2)
### 2026-04-26 23:00 (UTC+2)
### 2026-04-26 23:20 (UTC+2)
### 2026-04-26 23:45 (UTC+2)
### 2026-04-27 00:00 (UTC+2)
### 2026-04-27 00:15 (UTC+2)
### 2026-04-26 23:35 (UTC+2)
### 2026-04-27 16:38 (UTC+2)
### 2026-04-28 09:34 (UTC)
### 2026-04-28 10:20 (UTC)
### 2026-04-28 12:37 (UTC)
### 2026-04-28 12:55 (UTC)
### 2026-04-28 13:06 (UTC)
### 2026-04-28 13:19 (UTC)
### 2026-05-01 (heure locale serveur)
1. POST génère un lien temporaire signé (2 minutes)
2. GET exige un token valide + cohérent avec l'utilisateur + nom de fichier ;
1. Import serveur (FTP) doit démarrer et rester sous Ressources/BackupRessources (équivalent disque uploads/Ressources/BackupRessources).
2. Téléchargements multiples (variantes) : ajout de sous-options sur une catégorie fermait la possibilité d’en avoir sur les autres ; la barre Catégorie / Fichier racine disparaissait quand un sous-formulaire était ouvert ; renommage peu explicite.
3. Modale fichier / FTP : rester centrée à l’écran (pas entraînée par le scroll du formulaire).
4. Les tags doivent être trouvables via la barre de recherche (liste ressources et recherche globale).
Correctif : simplification de la liste en format minimal • #salon ; renommage du bouton de config vers Options par salon pour rendre l'action plus explicite.
Impact : panneau plus lisible, navigation de configuration plus claire pour les admins.
Fichier : bot/commands/setup_server.py
Souci (bouton Retour revenait à l'accueil)
Correctif : logique de retour contextuel (détection de l'écran actuel et remontée vers le parent : messages automatiques, catégorie arrivée, modération, communauté, etc.).
Impact : UX cohérente en configuration, moins d'allers-retours.
Fichier : bot/commands/setup_server.py
Souci (langues limitées / traduction partielle)
Correctif : ajout d'une couche i18n globale branchée au démarrage, appliquée aux content, embeds, vues (labels, placeholders, options) et followups.
Langues : fr, en, es, pt, it, de, ru, ar (fallback anglais hors fr et hors langue supportée).
Impact : couverture multi-langue étendue sur les interactions du bot.
Fichiers : bot/utils/global_i18n.py, bot/main.py
Souci (vérifier les textes encore non mappés)
Correctif : ajout d'un mode debug fallback i18n (I18N_FALLBACK_DEBUG=1) avec logs des traductions via handler LibreTranslate.
Impact : audit facilité pour finir la couverture “full propre” des textes.
Ressources & expérience lecture
Changelogs sur la fiche ressource (/ressources/[slug]) : le détail n’est plus entièrement déplié par défaut ; on affiche la version (et l’essentiel), avec possibilité d’ouvrir le reste (comportement proche d’une description repliée), pour un affichage plus propre.
Lien de téléchargement / fichier manquant (exemple corrigé) : lorsqu’une fiche pointait vers un nom de fichier qui n’existait pas sur le serveur (tandis qu’un autre binaire présent sur le disque devait être utilisé), la configuration (structure.json + base) a été alignée sur le fichier réellement hébergé pour supprimer les erreurs 404 côté /api/uploads/....
Intégration Pterodactyl (hébergement)
Création de service : le provisionnement sur Pterodactyl est devenu systématique à la création d’un hébergement depuis le dashboard (plus de dépendance à une case à cocher oubliée).
Requêtes API : possibilité de journaliser les appels (mode debug) pour diagnostiquer les réponses Ptero (variable d’environnement côté backend).
Résolution des œufs (eggs) : logique plus robuste (différents formats de réponse API, listes, fallbacks) ; variables d’environnement pour forcer l’UUID d’un œuf par type d’offre ; bouton « Œufs Ptero » sur le dashboard des services pour lister les œufs réellement présents sur le panel.
Création de serveur : le corps de requête inclut correctement image Docker et variables d’environnement issus de l’œuf, afin d’éviter les erreurs classiques du type *docker image required* / *environment must be present*.
Gestion côté staff : ébauche de panneau (suspendre, supprimer, dates, propriétaire, offre) dans la continuité des besoins d’exploitation.
Clé API Client : rappel important — les actions fichiers / console / état d’alimentation côté site passent par la clé client Pterodactyl ; en cas d’401 sur l’API client, vérifier PTERODACTYL_CLIENT_API_KEY (ou équivalent) et le redémarrage de l’app.
Synchronisation des releases GitHub (ressources)
Démon / cron : mécanisme pour déclencher périodiquement la route protégée de synchro (ex. via PM2 + CRON_SECRET), afin de ne plus dépendre uniquement d’un déclenchement manuel.
Détection des mises à jour : au-delà de l’ID GitHub de release, une empreinte (hash) du contenu (tag, titre, texte, assets, etc.) permet de re-synchroniser quand un développeur modifie la dernière release au lieu d’en créer une nouvelle (cas fréquent). Le changelog existant pour une même version peut être mis à jour plutôt que dupliqué.
Base de données : colonne de type github_last_sync_fingerprint (ajout géré côté app + script SQL de référence).
Espace « Mes services » (utilisateurs)
Accès : la page et le lien de menu « Mes services » limités aux rôles fondateur et admin pour ne pas exposer des versions encore en travail.
Liste & détail : parcours pour voir les services, interface type « panel » (console, fichiers, backups, configuration, utilisateurs).
Comptes applicatifs : section pour renseigner / gérer des infos (type mail, Jellyfin, Nextcloud) — évolution progressive.
Partage d’accès : onglet Utilisateurs pour donner des droits granulaires (console, fichiers, backups, config, etc.) sur un service, avec routes API associées.
Pterodactyl sans ouvrir le panel : simplification de l’onglet configuration (démarrage, image / runtime) ; indicateur d’état réseau côté serveur ; retrait de raccourcis redondants vers le panel.
Permissions & sync : prise en charge des comptes staff sur les mêmes actions que le propriétaire ; amélioration de la résolution d’instance et du power (alignement des états start/stop avec le panel, messages d’erreur si la clé client n’est pas valide).
Ordre des onglets : Utilisateurs positionné après Fichiers, comme demandé.
Bot Discord (configuration serveur)
Messages automatiques (config) : le sélecteur de salons accepte les fils (threads) en plus des salons texte / forum / annonces, avec multi-sélection (jusqu’à 25 salons) pour appliquer le même contenu sur plusieurs cibles d’un coup.
Résolution des salons : correction importante — en Python, guild.get_channel() ne voit pas les threads ; le code résout maintenant get_thread + fetch_channel quand c’est nécessaire, y compris à l’envoi des messages (bienvenue, départ, périodiques) pour ne pas cibler un ID de fil « introuvable » par erreur.
Boutique
Parcours vendeur : soumission d’un produit (nom, prix, description, visuels, catégorie, stock, lien vidéo, options type variantes) ; statut en attente jusqu’à validation côté équipe.
Base : support des product_options (groupes d’options) en JSON pour décrire des choix côté fiche, sans surcharger l’écran d’achat d’un formulaire monolithique.
Visibilité côté staff : notification aux administrateurs en plus de la file Discord (voir section suivante) pour traiter la demande rapidement.
Lien canonique : l’annonce automatisée pointe vers la boutique sur le site (/shop).
Partenariats
Espace dédié au dashboard : gestion des fiches partenaires (logo, texte, liens Discord / site / réseaux) avec un schéma de liens sociaux extensible (YouTube, Twitch, GitHub, TikTok, moyens de soutien, etc.).
Compte rattaché : possibilité d’associer un compte utilisateur du site à une fiche partenaire (recherche de profil) pour l’identification côté communauté.
API : persistance et lecture via les routes partenaires (/api/partners et variantes) alignées sur la page d’administration.
Description du site & aperçus (Open Graph)
Métadonnées globales : titre, description et mots-clés du site définis dans le layout principal (metadata Next.js) avec champs Open Graph et Twitter (cartes de partage cohérentes sur les réseaux et messageries).
Module og-lbxmb : helpers pour ligne de méta (ex. Ressource / console / type / nom), troncature, nettoyage HTML / markdown — logique proche des aperçus type embed (Description + extrait) pour un rendu unifié entre Discord et le web.
Fiches détaillées : certaines pages (ex. ressource par slug) enrichissent title / description / image par contenu pour un partage plus parlant qu’un simple titre générique.
Événements site → Discord (file partagée)
Mécanisme : le site enfile des commandes dans un fichier JSON (web/data/discord_communication/commands.json) via enqueueDiscordCommand ; le bot (ou un worker) les consomme pour poster dans les bons salons.
Types d’événements (exemples côté site) : nouvelle ressource validée, nouveau guide publié, demande d’ajout en boutique (produit en attente), création de compte (log avec lien profil) — chaque type transporte un petit payload (titre, auteur, URL, visuels, etc.).
Dons : un type dédié peut aussi alimenter le salon lors d’un don confirmé (hors sujet des autres sections mais même pipeline).
Forum (groupes) — programmation des posts
Champs côté base : scheduled_for (date/heure cible) et is_published pour distinguer brouillon / en attente / visible.
Publication : un job logique (requête au chargement de la liste) publie les messages dont l’heure programmée est passée, pour qu’ils apparaissent dans le fil sans action manuelle au moment T.
Tri : la liste s’appuie sur scheduled_for quand il est renseigné, sinon sur la date de création, pour un ordre cohérent avec l’intention de publication (y compris différée).
Fichiers techniques touchés (aperçu)
Site : web/src/app/ressources/..., web/src/lib/pterodactyl*.ts, web/src/lib/sync-resource-github-release.ts, web/src/lib/ensure-resource-github-columns.ts, web/src/app/mes-services/..., routes API mes-services, cron/sync-github-resources, dashboard/..., boutique (api/shop/..., app/shop/...), partenaires (dashboard/partners, api/partners, lib/partner-social-links), Open Graph (app/layout.tsx, lib/og-lbxmb.ts, layouts ressource), Discord (lib/discord-commands.ts, api/resources/add, api/guides/create, lib/auth pour les comptes, api/shop/request), forum groupes (api/forum-social/groups/[slug]/posts), etc.
Données / config : uploads/Structure/.../structure.json, ressources en base, ecosystem PM2 pour le sync GitHub, etc.
Remerciements
Ces changements s’inscrivent dans une amélioration continue du site, de l’hébergement, et de l’expérience sur Discord. Merci à la communauté pour les retours et la patience sur les itérations.
Fichier : bot/utils/global_i18n.py
Souci (suivi des modifications entre sessions)
Correctif : ajout d'une règle projet Cursor “always apply” imposant l'ajout d'entrées patch notes dans ce fichier.
Impact : traçabilité continue des interventions.
Fichier : .cursor/rules/patch-notes-journal.mdc
Souci (absence d'une page publique de changelog sur le site)
Correctif : création de la page \/changelogs côté Next.js, avec lecture du fichier récap, parsing des sections (##), rendu en cartes, et priorité visuelle pour la section patch notes.
Impact : page publique centralisée des évolutions site/bot, maintenable depuis un seul fichier source.
Fichier : web/src/app/changelogs/page.tsx
Souci (accès rapide à la page changelogs)
Correctif : ajout d’un lien Changelogs dans le footer (bloc légal).
Impact : découverte simplifiée de l’historique des mises à jour.
Fichier : web/src/components/layout/Footer.tsx
Souci critique (commande Discord “The application did not respond” en locale EN)
Correctif :
fallback de traduction via handler (LibreTranslate) désactivé par défaut (I18N_ENABLE_HANDLER_FALLBACK=0),
wrappers i18n sécurisés avec try/catch + repli automatique vers les méthodes Discord originales.
Impact : suppression du blocage des commandes en anglais, réponse immédiate restaurée.
Fichier : bot/utils/global_i18n.py
Souci (traduction incomplète dans le flux /config)
Correctif :
enrichissement de la base de traduction pour les textes les plus utilisés dans setup_server.py (placeholders, actions, statuts, libellés),
correction de l’algorithme de remplacement (priorité aux chaînes longues pour éviter les collisions de type Retour / Back),
remise du label source du bouton retour à Retour pour laisser la couche i18n produire Back proprement.
Impact : meilleure couverture de traduction sur tout le panneau /config (embeds + menus + boutons), suppression du bug visuel Back / Back.
Cause : BackButton appelait des méthodes statiques sur la mauvaise classe (SetupServerSelect), provoquant des AttributeError en runtime.
Correctif : reroutage vers la classe qui porte réellement ces méthodes statiques (ModerationCategorySelect) + nettoyage d’une référence invalide dans une méthode statique catégorie arrivée.
Impact : les boutons Back redeviennent fonctionnels sur les sous-panneaux concernés.
Fichier : bot/commands/setup_server.py
Souci (fragments FR persistants dans /config EN)
Correctif : enrichissement massif des mappings i18n globaux (phrases complètes + remplacements partiels) pour couvrir les cas du panneau /config et des messages d’action associés.
Impact : meilleure cohérence de traduction des embeds, menus déroulants, descriptions d’options, footers et messages contextuels.
Fichier : bot/utils/global_i18n.py
Déploiement correctif
Action : redémarrage du process PM2 bot-python après patch.
Impact : correctifs actifs immédiatement en production.
Commande /help (affichage côté client Discord)
Correctif : description par défaut en anglais, avec description_localizations pour le français (en-US / en-GB et fr).
Impact : les utilisateurs en locale anglophone voient une description cohérente dans l’UI Discord ; les clients en français voient le texte FR.
Fichier : bot/commands/help_command.py
Note : un resync des commandes d’application (sync) est nécessaire pour que Discord prenne en compte les nouvelles localisations.
Souci : beaucoup de chaînes restaient en français hors /config (notamment modération, utilitaires, gestion de salons, profil, VIP, AI).
Correctif : enrichissement important des mappings globaux (_BASE_FULL + _BASE_PARTIAL) avec de nouvelles clés FR→EN couvrant :
titres et descriptions d’embed,
labels de boutons et placeholders,
retours d’erreurs / confirmations,
textes de commandes fréquentes (moderation, utility, channel_management, profil, vip, ai).
Impact : meilleure couverture de traduction en runtime sur un périmètre plus large du bot pour les locales non-FR (avec propagation vers ES/PT/IT/DE/RU/AR via la couche existante).
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 2) sur vues et commandes supplémentaires
Correctif : ajout d’un nouveau lot de traductions globales pour les zones suivantes :
lecteur musique (youtube_music : !play / groupe slash /musique),
gestion mails (mail),
panneau staff (staff),
système tickets (tickets_view),
vue d’accueil/règles/disclaimer (welcome_view).
Impact : davantage de textes d’UI traduits (boutons, placeholders, états, confirmations/erreurs, libellés d’embed) pour les utilisateurs non-FR.
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 3) sur commandes et support
Correctif : ajout d’un lot supplémentaire de clés FR→EN pour :
casier, connexion, resolu,
send_welcome,
export_resources_forum,
support_view (tickets, recrutement, signalement, messages d’action et d’erreur).
Impact : meilleure traduction des flux de support et des commandes de modération/outillage restantes pour les locales non-FR.
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 4) sur modules secondaires
Correctif : ajout de clés FR→EN supplémentaires pour les commandes/vues :
base64_command, translate_command, temps (voctime),
acces, page, dmall.
Impact : réduction des derniers textes FR visibles dans les flux utilitaires/administration et meilleure homogénéité des messages en locales non-FR.
Fichier : bot/utils/global_i18n.py
Extension i18n (passe 5) sur vues annexes
Correctif : ajout d’un lot de clés FR→EN (phrases entières + partiels) pour translation_view, vote_view (embed par défaut, commentaires, suggestions, stats) et github_releases_config (panneau releases, dépôts, salon, rôle, intervalle).
Partiels : Traduire en , Note moyenne :, évaluation / évaluations, 💬 Commentaire de, liens de vote ⭐ à, suite des dépôts … et / autre(s), etc.
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python pour activer le lot en production.
Correctif : ajout massif de clés FR→EN pour accueil_view (erreurs courtes, messages partagés avec d’autres vues), script_panel_view (placeholder, service de traduction, en-têtes, erreurs, confirmation d’édition du message public), giveaway_view (modals, embeds, boutons, relances, participants, gagnant), et le bloc WELCOME_MESSAGE (aligné sur la version anglaise déjà présente dans TRANSLATED_MESSAGES).
Détail : correction de la clé d’apostrophe typographique pour ❌ Le service de traduction n’est pas disponible. (\u2019) afin d’assortir le texte de script_panel_view.py.
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python.
Parité ES/PT/IT/DE/RU/AR sur la couche i18n (valeurs = anglais → locale)
Problème : pour it, de, ru, ar, la table réutilisait surtout les valeurs anglaises de _BASE_FULL (quelques clés seulement surchargées) ; le résultat restait en pratique très anglophone. ES/PT avaient une longue chaîne de .replace sur l’anglais, non alignée sur les autres.
Correctif :
nouveau module utils/i18n_en_chains.py : blocs de libellés UI + phrases fréquentes, avec regroupement par ligne (une colonne par langue) et tri des remplacements par longueur d’anglais décroissante (évite de couper de plus longues chaînes) ;
_LANG_REPLACEMENTS_FULL pour es / pt / it / de / ru / ar est désormais construit par build_localized_base_full(_BASE_FULL, lang) : pour chaque clé FR, la valeur anglaise est passée par la même chaîne EN→langue cible.
I18n « fluide » : LibreTranslate par défaut, timeout, pas d’écran d’erreur, embeds/vues en parallèle
Dictionnaire + chaînes, puis texte restant : LibreTranslate (désactiver : I18N_ENABLE_HANDLER_FALLBACK=0) ; 2,3 s max par appel (I18N_HANDLER_TIMEOUT) → sinon texte source ; les réponses d’échec ❌ Erreur de traduction du handler sont ignorées côté affichage.
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python.
Franglais dans les descriptions des menus déroulants (Traduire en …)
Cause : le partiel Traduire en → Translate to n’existait que pour la locale en ; les autres langues gardaient le préfixe français.
Correctif : partiels dédiés es / pt / it / de / ru / ar + quelques fragments FR supplémentaires (vote, Choisir/Sélectionner).
Fichier : bot/utils/global_i18n.py
Déploiement : pm2 restart bot-python.
Cap (captcha self-hosted) déployé sur cap.lbxmb.fr
Contexte : finir l'intégration Cap commencée plus tôt — l'instance Standalone n'était pas encore en ligne. Sous-domaine cap.lbxmb.fr créé côté Cloudflare par le mainteneur.
Changements :
Installation de docker-compose (1.29.2) et certbot (2.1.0) sur l'hôte.
web/cap/.env : ADMIN_KEY aléatoire (32 octets, openssl rand -hex 32), exclu de git via .env*.
Conf nginx : nouveaux server blocks pour cap.lbxmb.fr (HTTP→HTTPS + HTTPS sur 127.0.0.1:4443 proxy vers 127.0.0.1:3010), avec propagation des headers CF-Connecting-IP, HSTS, Referrer-Policy, X-Content-Type-Options.
Certificat ECDSA Let's Encrypt obtenu via webroot (/var/www/html/.well-known/acme-challenge/), expire le 2026-07-25.
web/cap/README.md mis à jour (suppression du bloc nginx d'exemple, ajout du flot dashboard / .env.local).
Tests : https://cap.lbxmb.fr répond HTTP/2 200 avec x-powered-by: Cap Standalone. Redirection 80→443 OK. nginx -t OK.
Reste à faire (humain) : se connecter au dashboard, créer un site key, reporter CAP_SITE_KEY / CAP_SECRET_KEY dans web/.env.local, basculer CAP_DISABLED=0 puis pm2 restart web.
Login : Discord mis en avant, alternatives discrètes
Contexte/problème : les utilisateurs connectés via Discord choisissaient souvent Google/GitHub, ce qui casse les avantages liés au serveur Discord.
Changements appliqués : sur la page de login, le bouton Discord reste principal et visible immédiatement ; Google/GitHub sont masqués derrière un bouton discret Je n'ai pas Discord.
Impact visible / technique : parcours d’auth recentré sur Discord, baisse attendue des comptes liés via mauvais provider.
Contexte/problème : la bannière demandée devait reprendre la structure visuelle de /presentation avec un rendu plus propre et des données réelles.
Changements appliqués : remplacement du HTML de bannière par une version 960x540 avec fond/effets proches de la présentation, layout propre et statistiques dynamiques chargées via API (ressources, stockage, utilisateurs, guides).
Impact visible / technique : rendu homogène avec l’identité visuelle du site, chiffres affichés automatiquement depuis les endpoints du site.
Salon ressources : vrai auteur + avatar dans la miniature
Contexte/problème : certaines annonces affichaient Utilisateur au lieu du vrai nom d’auteur/créateur, et la miniature n’utilisait pas l’avatar de l’auteur.
Changements appliqués : enrichissement du payload site_new_resource avec author_name et author_avatar; côté bot, fallback intelligent pour remplacer les placeholders (Utilisateur, unknown, etc.) par le vrai auteur, puis priorité avatar auteur pour thumbnail.
Impact visible / technique : embeds de nouvelles ressources plus fiables et lisibles dans le salon 1497192095679774823.
Login : Discord seul par défaut + alternatives à la demande
Contexte/problème : la page de connexion affichait trop de providers immédiatement, alors que le flux attendu est centré Discord.
Changements appliqués : conservation du bouton Discord visible en premier, ajout d’un texte-action Je n'ai pas Discord qui dévoile uniquement Google et GitHub.
Impact visible / technique : parcours simplifié et orienté Discord, avec alternatives disponibles seulement si nécessaire.
Contexte/problème : impossible de programmer l’envoi d’une annonce depuis l’UI de création de post.
Changements appliqués : ajout d’un champ datetime-local dans l’éditeur d’annonce, envoi de scheduledFor vers l’API, reset après envoi et message explicite quand l’annonce est planifiée.
Impact visible / technique : les annonces peuvent être publiées immédiatement ou différées à une date/heure précise.
Image en Thread : affichage salons + réglages avancés
Contexte/problème : les salons configurés s’affichaient mal, il manquait une vraie section de configuration des réactions, et l’option de suppression thread/message source devait être pilotable.
Changements appliqués : correction d’affichage des salons (<#id> (nom)), ajout des réactions globales visibles/éditables, ajout des réactions par salon, ajout d’un bouton pour basculer la suppression du thread lié quand le message source est supprimé, enrichissement de l’embed de configuration.
Impact visible / technique : configuration imagethread plus lisible et complète (réactions + comportement de suppression) directement depuis le panel bot.
Contexte/problème : la commande /deplacer n’était plus disponible et devait être remplacée.
Changements appliqués : ajout de la commande slash /move (déplacer un membre ciblé ou tout le vocal courant), mise à jour de l’aide, et remplacement de la clé de permission affichée en configuration.
Impact visible / technique : commande de modération vocale rétablie avec nom anglais unique.
Programmation des posts (date/heure) étendue aux discussions
Contexte/problème : la programmation semblait ne pas fonctionner pour les posts de discussion.
Changements appliqués : ajout du sélecteur date/heure aussi dans l’éditeur discussion; côté API, création du thread avec created_at planifié si date future, filtrage des threads futurs en lecture, et retour d’état scheduledFor.
Impact visible / technique : les posts discussion et annonces peuvent être publiés à l’heure prévue.
Image en Thread : suppression des réglages globaux réactions + suppression thread en option salon
Contexte/problème : les réactions globales n’étaient pas souhaitées et l’option de suppression thread devait être déplacée au niveau salon.
Changements appliqués : retrait de la section réactions globales, ajout du flag deleteThreadOnSourceDelete par salon, ajout du toggle dans les options du salon, et adaptation de la suppression on_message_delete pour lire le paramètre par salon (fallback legacy conservé).
Impact visible / technique : configuration plus fine et plus cohérente par canal imagethread.
Banner/Logo : import HTML déplacé dans “Configurer” + pièce jointe Discord
Contexte/problème : l’import HTML via web externe et le bouton auto-update n’étaient pas adaptés au flux voulu.
Changements appliqués : suppression des boutons dédiés “import HTML” et “auto-update” de la vue principale, ajout d’un choix dans “Configurer Bannière/Logo” (URL ou import HTML), import HTML direct depuis une pièce jointe envoyée dans Discord, activation automatique permanente des updates, et tentative de mise à jour immédiate après configuration.
Impact visible / technique : flux unifié dans la config Discord, sans dépendance à la page web d’upload.
Fichiers touchés : bot/commands/setup_server.py
Forum : programmation visible lors de la création d’un post
Contexte/problème : sur la page forum principale, l’option de programmation n’était pas affichée dans le formulaire de création de sujet.
Changements appliqués : ajout du champ datetime-local “Programmer l’envoi (optionnel)” dans les deux variantes du formulaire (desktop/mobile), envoi de scheduled_for à l’API, reset après création.
Impact visible / technique : l’utilisateur peut programmer date/heure directement depuis la page forum.
Fichiers touchés : web/src/app/forum/page.tsx
API création thread : prise en charge de la programmation
Contexte/problème : même avec un champ front, l’API thread standard ne traitait pas la date planifiée.
Changements appliqués : ajout de scheduled_for dans thread/create, validation de date, insertion de created_at/updated_at à la date future si programmation.
Impact visible / technique : la publication est différée correctement sans créer un post visible immédiatement.
Contexte/problème : après création d’un post planifié, il manquait un retour visuel immédiat indiquant que l’envoi est différé.
Changements appliqués : ajout d’un état scheduledNotice et affichage d’un bandeau/badge “Post programmé pour …” sur la page forum; en cas de post planifié, suppression de la redirection immédiate vers le thread.
Impact visible / technique : confirmation claire que le post est en file d’attente de publication.
Fichiers touchés : web/src/app/forum/page.tsx
Posts programmés visibles uniquement à l’auteur dans la catégorie
Contexte/problème : les posts programmés devaient apparaître dans la liste de la catégorie pour l’auteur uniquement (pas pour les autres utilisateurs).
Changements appliqués : filtre API des threads ajusté pour inclure les posts futurs seulement si l’utilisateur courant est l’auteur; conservation du masquage pour les autres.
Impact visible / technique : l’auteur peut suivre ses posts programmés directement dans le forum sans fuite de visibilité.
Contexte/problème : besoin d’une indication claire sur les posts programmés (date de publication + temps restant).
Changements appliqués : ajout du badge/icône “Programmé” sur les threads programmés de l’auteur avec infobulle (title) affichant date prévue et temps restant calculé.
Impact visible / technique : meilleure lisibilité de l’état des publications planifiées.
Fichiers touchés : web/src/app/forum/page.tsx
Visibilité personnalisée des posts (public / followers)
Contexte/problème : besoin de choisir si un post est visible par tout le monde ou uniquement par les followers.
Changements appliqués : ajout du champ visibility en création de thread, ajout/maintenance du schéma SQL côté API (forum_threads.visibility), filtrage d’accès dans la liste des threads et sur la route détail thread.
Impact visible / technique : publication ciblée possible avec contrôle d’accès cohérent en lecture liste + détail.
UX création post : actions programmation/visibilité près de Média
Contexte/problème : la programmation devait être pilotée via un bouton près de “Média”, avec calendrier + horloge, et la visibilité au même endroit.
Changements appliqués : ajout de boutons Programmer et Visibilité dans la barre d’actions, ouverture de panneaux inline avec sélecteur date (type=date) + heure (type=time) et choix visibilité Tout le monde/Mes followers.
Impact visible / technique : flux de publication plus clair et ergonomique, aligné sur la demande produit.
Fichiers touchés : web/src/app/forum/page.tsx
Correction position boutons Programmer/Visibilité dans le formulaire post
Contexte/problème : les boutons n’étaient pas réellement affichés juste à côté du bouton Média dans toutes les variantes du formulaire.
Changements appliqués : restructuration de la barre d’actions pour grouper Média, Programmer et Visibilité sur la même ligne (modal + version inline), suppression du bloc dupliqué, maintien des panneaux date/heure et visibilité après la barre.
Impact visible / technique : les boutons demandés apparaissent désormais au bon endroit, au même niveau que Média.
Fichiers touchés : web/src/app/forum/page.tsx
Programmation forum : correction fuseau horaire (publication + badge)
Contexte/problème : un post programmé pouvait rester marqué “Programmé” et non visible publiquement après l’heure attendue.
Changements appliqués : conversion date+heure locale en ISO timezone-aware (toISOString()) avant envoi vers l’API de création.
Impact visible / technique : la date de publication est interprétée correctement côté serveur, ce qui rétablit la bascule de visibilité et la disparition du statut “Programmé” au bon moment.
Fichiers touchés : web/src/app/forum/page.tsx
Publication programmée forum : bascule fiable après échéance
Contexte/problème : certains posts restaient bloqués en “programmé” après l’heure prévue et n’étaient pas visibles aux autres utilisateurs.
Changements appliqués : passage à un modèle dédié scheduled_for + is_published pour forum_threads, publication automatique côté API quand scheduled_for <= NOW(), et migration legacy des anciens posts programmés (détection via created_at > NOW()).
Impact visible / technique : la visibilité publique se débloque correctement à l’échéance, et le badge “programmé” ne reste plus bloqué indéfiniment.
Contexte/problème : les deux menus pouvaient s’ouvrir en même temps.
Changements appliqués : ajout de toggles exclusifs (toggleScheduleMenu, toggleVisibilityMenu) pour fermer automatiquement l’autre menu à l’ouverture.
Impact visible / technique : interaction plus claire, un seul panneau de configuration visible à la fois.
Fichiers touchés : web/src/app/forum/page.tsx
Endpoint de publication immédiate d’un thread programmé
Contexte/problème : besoin d’une action manuelle pour publier un thread sans attendre l’échéance planifiée.
Changements appliqués : ajout d’un handler POST sur la route thread détail qui force is_published = 1, vide scheduled_for, et met à jour updated_at; autorisation limitée à l’auteur du thread ou un admin/fondateur.
Impact visible / technique : possibilité de débloquer instantanément un post planifié en cas de correction manuelle.
Page VIP : VIP Booster vs Premium, hub Jellyfin + API
Contexte/problème : afficher la source VIP (Discord vs paiement site), distinguer avantages booster / premium (retirer hébergement, Jellyfin, Nextcloud annoncé 30 Go, webmail côté booster ; ajouter Nextcloud aux premium), onglets de gestion des accès pour les abonnés payés avec création Jellyfin via API et suspension/réactivation selon vip_subscriptions.
Changements appliqués : fetchPaidPremiumState + resolveVipTier dans /api/vip/status, sync Jellyfin syncJellyfinForVipPaidState, route POST/GET /api/vip/provision/jellyfin, badge vip_source = stripe à l’activation checkout, colonne user_id BIGINT dans vip_jellyfin_accounts (+ ALTER si ancienne INT), page vip refactor (comparaison marketing, grille avantages selon tier, Tabs Premium avec Jellyfin + placeholders), clés vipPage FR/EN.
Impact visible / technique : texte métier clarifié pour les tiers ; Jellyfin créé à la demande pour Premium actif avec mot de passe affiché une fois ; compte Jellyfin désactivé quand l’abonnement payé n’est plus actif (lecture status resync).
Jellyfin : sync sur perte / retour du rôle VIP (permissions)
Contexte/problème : la suspension ne tenait compte que de l’abonnement payé, pas du retrait du flag vip en base (rôle Discord synchronisé avec le site).
Changements appliqués : syncJellyfinForVipEntitlement + jellyfinVipShouldBeActive : compte réactivé uniquement si à la fois hasVipPermission et abonnement Premium actif ; suspension dès qu’il manque le rôle VIP ou la période payée. /api/vip/status passe isVip et paidPremiumActive.
Impact visible / technique : un utilisateur qui perd le VIP côté site se voit désactiver Jellyfin au prochain passage sur la page VIP (ou tout appel /api/vip/status) ; retour du rôle + Premium actif réactive.
Contexte/problème : avantage « −10% » unique ; souhait −5% VIP Booster vs −10% VIP Premium sur les achats boutique.
Changements appliqués : resolveBuyerVipDiscountPercent dans vip-tier ; application sur POST /api/stripe/checkout (montant line item + commission calculée sur prix payé ; métadonnées buyer_discount_pct, list_price_cents) ; GET /api/vip/shop-buyer-discount ; page produit shop/[id] : prix barré + prix VIP ; page VIP + vipPage FR/EN avec textes séparés booster/premium.
Impact visible / technique : remise cohérente entre fiche produit et session Stripe ; tiers non-VIP inchangé (0%).
Traduction automatique Discord : mise en forme + menu réinitialisé
Contexte/problème : préfixe « Traduit » en français quel que soit la langue cible ; liste déroulante qui ne permettait pas de rechoisir la même langue.
Changements appliqués : en-tête <traduction> <drapeau> avec libellés statiques par langue cible (Traduction, Translation, Übersetzung, etc.) puis texte ; après réponse éphémère, edit du message avec le menu pour régénérer la vue Discord.
Impact visible / technique : message traduit lisible comme section localisée + re-sélection de la même langue possible.
/config : ancrage message + rafraîchissement sans bouton dans plusieurs écrans
Contexte/problème : suivre une consigne utilisateur après ajout/suppression hors message principal ; correctifs éviter messages « Cliquez Actualiser » orphelins.
Changements appliqués : enregistrement du message éphem /config par (guild,user) (_register_config_panel_anchor / refresh_config_panel_anchor), traduction auto : toggle met à jour l’embed directement ; ajouts/retraits de salons via liste éphémère rafraîchissent l’embed si titre encore « Traduction Automatique » ; messages périodiques : toggle + retrait synchro embed ; même logique pour retrait messages Welcome/Goodbye/DM avec garde titre ; bouton Rafraîchir retiré uniquement pour Traduction automatique, les autres sous-menus (notifications, tickets, anti-mots, etc.) gardent leur Actualiser ; votes : toggle fait edit_original_response après followup confirmation.
Impact visible / technique : moins de clics où le flux était brisé après sed ; hors périmètre : nombreux autres textes « Actualiser » restent tant que pas branchés sur edit ou ancrage titre.
Contexte/problème : slugs séparés côté site (vip_premium achat Stripe, vip_booster boost Discord) ; ne plus attribuer automatiquement le rôle cosmétique Discord 1385546866942410813 lorsque l’utilisateur a le booster 1308907900751577181 ; l’API check-vip doit représenter uniquement le Premium payé avec abonnement actif.
Changements appliqués : activation paiement ajoute vip_premium ; Jellyfin vérifie qualifiesForPaidPremiumBenefits ; checkout vendeur/remise acheteur via vip-tier ; synchro Discord vip_premium (+ legacy vip) vers le rôle doré, pas vip_booster ; connexion découpant les permissions CSV ; UI (header, sidebar forum, dashboard édition, profil) ; bot : synchro DB vip_booster depuis Discord, giveaways + ticket avantages VIP incluent booster ; Flask sync_user_roles connaît vip_premium.
Impact visible / technique : Premium doré sur Discord réservé au mapping site hors booster ; booster identifié sur le site après synchro cron ; giveaways/tickets peuvent autoriser booster sans rôle VIP Discord.
Contexte/problème : page /dashboard/maintenance cassait avec l’erreur React minifiée #31 (« object with keys code, summary, detail ») quand npm renvoie une erreur structurée : le JSX rendait {packagesStatus.error} alors que ce champ était un objet ; besoin de lancer bun run deploy:prod en arrière-plan et de piloter PM2 (bot-python).
Changements appliqués : normalisation serveur du JSON npm outdated avec extraction texte { code, summary, detail } ; helpers maintenanceErrorMessage + affichage sûr (Stripe / PM2 / NPM) ; cellules tableau outdated forcées en chaîne ; carte « Actions serveur » avec confirmations — enqueueDeployProd (spawn détaché, cwd projet Next), pm2RestartBot / pm2StartBot / pm2StopBot, nom process LBXMB_PM2_BOT_NAME (défaut bot-python).
Impact visible / technique : la page maintenance ne plante plus sur erreur npm objet ; équipe peut déclencher build/deploy et lifecycle bot depuis le dashboard (droits inchangés : fondateur / admin / développeur, comme le reste de l’onglet maintenance). Les erreurs réseau WebSocket ou notifications (AxiosError: Network Error) relèvent du proxy / TLS / CSP côté navigateur, hors périmètre de ce correctif.
Contexte/problème : un utilisateur à la fois boosteur Discord et abonné Premium voyait la logique ou vip_source=boost l’emporter (hasVipPremiumSlot faux avec slug vip + source boost), alors que le payant doit primer (API /api/bot/check-vip, −10 % boutique, affichage tier).
Changements appliqués : hasVipPremiumSlot(..., paidPremiumActive) : abonnement actif + ancien slug vip ⇒ slot premium même si vip_source mal synchronisé ; resolveVipTier et resolveBuyerVipDiscountPercent s’appuient là-dessus ; checkout acheteur charge vip_source ; bot : si booster + (vip_premium OU source stripe OU ligne vip_subscriptions encore active), on n’écrit plus vip_source=boost (effacement si c’était boost).
Impact visible / technique : même compte (« LoupBlanke », booster + Premium) : le payant est reconnu sur le site et pour /api/bot/check-vip ; la synchro booster n’écrit plus vip_source=boost tant qu’un abonnement actif existe.
2026-05-01 — Maintenance : deux actions seulement (Build Site, Redémarrer Bot)
Changements appliqués : retrait des boutons PM2 start/stop sur /dashboard/maintenance ; libellés « Build Site » et « Redémarrer Bot » ; suppression des server actions pm2StartBot / pm2StopBot.
Impact visible : carte « Actions serveur » limitée aux deux commandes demandées.
Contexte/problème : trois entrées VIP dont l’ancien slug vip et des mentions entre parenthèses réservées au contexte dev.
Changements appliqués : liste PERMISSIONS réduite à VIP Premium et VIP Booster (sans parenthèses) ; suppression de la case « VIP (ancien slug) » côté formulaire. Migration à l’affichage et au PUT : token vip remplacé par vip_premium ou vip_booster selon vip_source (boost → booster, sinon premium) ; champ vip_source exposé sur GET /api/dashboard/users/[id].
Impact visible : deux rôles affichés ; anciens comptes vip ne restent pas orphelins à l’enregistrement.
2026-05-01 — Rôle partenaire : hors sélection manuelle dashboard
Contexte/problème : « partenaires » ne doit plus être ajouté à la main dans les badges utilisateur ; il doit suivre uniquement la fiche partenaire.
Changements appliqués : retrait de partenaires de la liste PERMISSIONS ; pastille lecture seule « Partenaires » si le slug est encore en base ; PUT /api/dashboard/users/[id] retire partenaires/partenaire si aucune ligne partners pour users.id ; synchro Discord inclut toujours l’ID rôle partenaire quand le slug est présent.
2026-05-01 — Page VIP : CTA « Passer en VIP Premium » pour les boosters
Contexte/problème : les boosters devaient pouvoir souscrire au Premium depuis /vip avec message clair sur le cumul et la préseance du payant.
Changements appliqués : bouton vers POST /api/vip/checkout (plan mensuel) lorsque tier === "boost" ; clés i18n dédiées ; upgradeFromBoostHint réécrit en ce sens.
2026-05-01 — VIP tier : Premium si abonnement actif même sans jeton vip_premium en base
Contexte/problème : comptes boost + paiement Stripe vus comme « boost » uniquement si la colonne permission ne contenait plus vip/vip_premium alors que vip_subscriptions est actif (stripStale/décalage webhook).
Changements appliqués : hasVipPremiumSlot retourne vrai lorsque paidPremiumActive ; resolveVipTier teste l’abo avant la branche « aucun jeton » ; qualifiesForPaidPremiumBenefits délègue au slot Premium.
Impact visible : /api/vip/status → tier: "premium" et hub Premium (tabs Jellyfin etc.) tant que Stripe est actif ; bot /api/bot/check-vip aligné sur l’abo.
Contexte/problème : hub Premium invisible pour certains payeurs (tier resté « boost » dès que vip_booster était présent ; ligne vip_subscriptions reliée au mauvais identifiant user_id vs users.id/session).
Changements appliqués : resolveVipTier teste hasVipPremiumSlot(..., false) avant tout jugement booster ; fetchPaidPremiumState accepte plusieurs clés (subscriberLookupKeys auth + ligne users) ; statuts Stripe complete/succeeded comptés comme actifs tant que la date de fin est valide ; /api/vip/status, /api/vip/portal, remise boutique, Jellyfin provisioning alignés sur l’identifiant canonique et la recherche multi-clés.
Impact visible : onglets Premium sur /vip, portail Stripe et Jellyfin si l’abo est retrouvé sur une clé plausible ; booster + badge Premium → affichage Premium.
Contexte/problème : besoin d’un point d’entrée unique (desktop + mobile) pour chercher ressources, guides, boutique, membres, sujets/réponses forum publics, offres services.
Changements appliqués : GET /api/site-search (requêtes SQL parallèles, filtre ressources listables, fil visibilité forum public + publié) ; composant GlobalSiteSearch (dialog plein écran sur viewport mobile, debounce) ; bouton loupe desktop dans Header ; entrée Recherche dans MobileBottomNav entre Messages et Notifications (utilisateurs connectés) ; clés nav.siteSearchAria, globalSearch, mobileNav.search ; lien secondaire vers liste ressources + prise en charge ?search= sur /ressources.
Impact visible : modal de recherche depuis la barre (desktop) ou la barre du bas (mobile) ; résultats groupés par type avec liens profil / ressource / guide / boutique / forum / services/detail.
Contexte/problème : liste conversations sans group_owner_user_id / group_avatar_url en SQL ; avatars de groupe absents ; nom « Groupe (n) » ; compositeur ne vidait pas la recherche après ajout d’un contact ; add_member non réservé au créateur ; suppression des messages limitée à l’auteur ; besoin d’UI minimale de gestion groupe (nom, logo URL, retrait membres).
Changements appliqués : SELECT conversations enrichi + other_photo groupe depuis group_avatar_url ; GET conversation groupe : other_photo aligné ; POST /api/messages/group/create : titre auto à partir des pseudos (le client n’envoie plus groupName) ; PATCH add_member réservé au propriétaire ; DELETE message vendor : auteur ou propriétaire d’un groupe ; MessageBell : reset composerQuery / résultats à l’ajout d’un destinataire ; menus message : suppression pour modération propriétaire ; en-tête groupe : avatar + sous-titre membres ; dialogue réglages (renommer, URL logo, retirer membre) pour le créateur.
Impact visible : groupes nommés lisiblement ; compositeur plus fluide ; contrôle cohérent côté créateur ; modération des messages des autres dans les groupes.
Contexte/problème : plusieurs libellés boutique restaient codés en dur (FR) et n’étaient pas traduits côté site (badges vendeur, sécurité/promo, options produit, anti-bot, CTA contact, labels galerie/statistiques).
Changements appliqués :
remplacement des textes en dur sur shop/page.tsx par des clés sitePages.shop (verifiedSeller, securePayment, stats et bandeau confiance) ;
remplacement des textes en dur sur shop/[id]/page.tsx (optionsTitle, previousImageAria, nextImageAria) ;
internationalisation complète des libellés restants du formulaire boutique (BoutiqueProductForm) : anti-bot, options produit, placeholders options, boutons d’ajout, placeholder YouTube ;
internationalisation du bouton ContacterVendeurButton via shopProductPage.contactSeller ;
ajout des nouvelles clés FR/EN dans web/messages/fr.json et web/messages/en.json.
Impact visible / technique : les pages et actions liées à la boutique passent désormais par i18n (FR/EN) sans texte FR figé dans l’UI.
2026-05-01 13:30 — Refonte présentation boutique + création produit enrichie
Contexte/problème : besoin d’un style de card proche de la maquette fournie (visuel haut + garanties + CTA), d’un choix logo/bannière à la création, d’un système de garanties personnalisées, d’une gestion catégorie existante/nouvelle et d’un filtre vendeur côté listing.
Changements appliqués :
Cards boutique (shop/page) : nouveau layout visuel type showcase (header image/logo, prix badge, garanties listées, bouton achat, fallback garanties) + support display_mode (logo/banner) et banner_image.
Filtres listing (shop/page) : ajout filtres par catégorie et vendeur en plus du tri/recherche ; synchronisation URL via query params (category, seller) ; reset global des filtres.
API shop : migration douce SQL (ALTER TABLE ... ADD COLUMN IF NOT EXISTS) pour banner_image, display_mode, guarantees + persistance/lecture dans POST /api/shop/request, PUT /api/shop/[id], GET /api/shop et parse JSON des garanties.
i18n FR/EN : ajout des nouvelles clés boutique (sitePages.shop, boutiqueFormPage) pour labels, filtres et garanties.
Impact visible / technique : la boutique affiche désormais des cards orientées “offre/service” plus proches de la capture ; la création d’article devient plus flexible (branding visuel, garanties marketing, catégories exploitables pour filtrage) ; le listing peut être filtré par vendeur.
2026-05-01 15:45 — Options boutique avec ajustement de prix · /vip (UI + thème booster) · grille catalogue logo + cartes élargies
Contexte/problème : variante produit (ex. carte SD) doit faire varier le prix affiché et facturé ; page VIP encore trop “ambre halo” sur la carte facturation, badges Premium/À renouveler à retirer, textes Discord/boost à retirer, CTA Stripe en bas de carte ; boosters demandent une ambiance violette ; listing boutique doit forcer la vignette logo, réafficher clairement note + nombre d’avis + photo vendeur, colonnes plus larges.
Options prix
Changements : nouveau module web/src/lib/shop-product-options.ts ; options persistées sous forme { label, price_delta } (compat strings historiques → delta 0) ; validation + somme des deltas dans POST /api/stripe/checkout ; prix catalogue et remises VIP calculés sur prix base + deltas ; formulaire création : par variante libellé + champ € (signé) ; fiche /shop/[id] : prix total dynamique et libellé “prix de base / options”.
Changements : suppression des deux paragraphes Discord/premium differentiation sous le titre ; suppression des badges “Premium” / “À renouveler” ; bouton « Gérer la facturation » déplacé en pied de carte (aligné à droite) avec style contour (plus de halo ambre focalisé) ; utilisateur VIP booster uniquement : fond dégradés + halos violet/indigo + accent titre + grille avantages Spotlight violette + CTA upgrade violet ; aide Stripe compactée ; carte calendrier : bord neutre sans surbrillance dorée parasite.
Fichiers touchés : web/src/app/vip/page.tsx
Listing /shop
Changements : la grille liste toujours en mode vignette/logo (sans bannière) ; photo vendeur (getPhotoUrl) + ligne étoiles + note + nombre d’avis ; max-w-[90rem], grille xl:grid-cols-3 et espacements plus larges pour des cards plus imposing.
Fichiers touchés : web/src/app/shop/page.tsx
i18n formulaire boutique : complément des clés manquantes dans le namespace boutiqueFormPage (catégorie, visuel, garanties, options, anti-bot, YouTube) pour éviter MISSING_MESSAGE sur le flux ajout/modification — FR/EN.
Contexte/problème : vignettes vendeurs mal rendues ; besoin de règles visuelles claires par carte et d’une page d’accueil boutique simplifiée (logo, titre, accroche, CTA, stats discrètes, filtres légers, grille aérée responsive).
Changements appliqués :
Avatars vendeur : résolution d’URL (/api/ déjà complet, sinon getPhotoUrl après normalisation des / initiaux) + object-cover, taille petite (28px), repli lettre si chargement KO (onError).
Cartes produits : image dominante (aspect-square pour viser ~60–67 % de hauteur), prix en badge haut-droite sur l’image, titre gras agrandi, description line-clamp-2, 2–3 pastilles garanties mini en ligne horizontale, ligne vendeur (mini photo + pseudo + étoiles + note), CTA plein largeur dégradé bleu clair.
Haut de page : logo /lbxmb.png, titre via heroHeadline (« LA BOUTIQUE LB'XMB » en FR), accroche heroTagline, bouton « Ajouter un produit » avec icône + (vert).
Stats : deux petites cartes seulement (vendeurs vérifiés = nombre de vendeurs uniques Stripe ok, pas le nombre de produits ; avis clients total).
Zone catalogue : suppression du bandeau « trust » trois colonnes ; grille 1 col mobile, 2 cols md, 3 cols xl, gap-x/gap-y très larges (max-w-[82rem]).
Impact visible / technique : listing plus lisible et moins chargé ; avatars vendeurs stabilisés sur chemins relatifs/absolus variables ; grille plus aérée sur desktop.
2026-05-01 16:05 — Boutique /shop : alignement « ce qui colle bien » (CM-like sans copier CM)
Contexte/problème : matérialiser la feuille de route discutée avec l’utilisateur : image forte ~70 %, ligne type « by vendeur » + confiance, badges standardisés, filtre vendeurs vérifiés, respir / premium.
Changements appliqués :
Carte : vignette aspect-[5/7] ; titre line-clamp-1 ; ligne Par [lien vendeur] (/shop?seller=…) + dans {category} · note/avis ; prix + pastille vérifié (Stripe) sur l’image ; pastilles marketing (max 3) = Populaire si ≥ 5 avis + garanties sinon défauts rapide/safe ; overlay cliquable vers fiche produit + lien vendeur pointer-events-auto (sans <a> imbriqués).
Filtres : case à cocher Vendeurs vérifiés uniquement + query verified=1 ; chips filtres + message vide / reset cohérents.
Contexte/problème : compléter la feuille de route type Creative Market avec une rangée de vendeurs mis en avant au-dessus de la grille produits.
Changements appliqués :
Agrégation par seller_user_id depuis tout le catalogue chargé (/api/shop) : nb d’annonces, avis totaux, note moyenne (pondérée par les avis si possible, sinon moyenne sur fiches notées) ; tri vérifié Stripe d’abord, puis note, volume d’avis, nb de produits ; max 6 vendeurs.
UI : section titre + sous-texte, cartes cliquables (/shop?seller=…, conserve verified=1 si le filtre global est actif), avatar lg, badge check sur photo si vérifié, pastille offres + ligne avis + CTA « Voir la boutique » (icône Store).
SellerAvatar : variante taille lg (56px) pour les vedettes.
i18n : featuredSellersTitle, featuredSellersSubtitle, featuredSellerOffers, featuredSellerReviews, featuredSellerNoReviews, featuredSellerSeeShop (FR/EN + locales déjà enrichies du namespace sitePages.shop).
Impact visible / technique : découverte des vendeurs sans ouvrir le select ; liens cohérents avec le filtre vendeur existant ; section masquée si aucun vendeur identifié après chargement (edge case rare).
2026-05-01 — /shop liste : visuel produit en paysage
Contexte/problème : vignette carte en portrait (aspect-[5/7]). Souhait d’une zone image horizontale (format paysage), sans modifier la grille ni le style général des cartes.
Changements appliqués : conteneur visuel → aspect-video (ratio 16:9) ; object-cover conservé pour le remplissage de l’image.
Impact visible : rendu type bannière sur toute la largeur ; bloc texte/CTA reprend davantage la hauteur sous le visuel (carte globalement un peu moins haute à largeur égale).
2026-05-01 16:15 — Boutique + menu utilisateur + page commandes
Contexte/problème : besoin d’un rendu boutique plus lisible/cohérent (description, couleurs, densité, 4 colonnes, filtres plus confortables, suppression du « vendeur vérifié » et des tags « 100% fiable »), puis ajout d’une section utilisateur « Mes commandes » avec vue vendeur des achats.
retrait du badge visuel « vérifié » + retrait du filtre « vendeurs vérifiés » ;
suppression des fallback tags defaultGuaranteeFast/defaultGuaranteeSafe (affichage seulement des garanties réelles + tag « Populaire » si applicable) ;
grille catalogue à 4 cartes/ligne en desktop large (xl:grid-cols-4) ;
zone “Vendeurs en vedette” gardée mais triée désormais sur qualité/activité, et texte sous-titre aligné à gauche.
Menu utilisateur : ajout entrée Mes commandes (desktop dropdown + sheet mobile) via clé i18n profileMenu.myOrders.
Nouvelle API : GET /api/mes-commandes :
récupère les sessions Stripe complete,
construit les ventes liées au vendeur courant (metadata.seller_id) et les achats du compte courant (metadata.buyer_id),
enrichit avec pseudo acheteur/vendeur depuis users,
expose montant, méthode de paiement, date/heure de paiement, email acheteur (si dispo).
Nouvelle page : /mes-commandes avec deux onglets :
Ventes (vendeur : qui a acheté, quel produit, date+heure, moyen de paiement, montant),
Achats (historique acheteur).
i18n : ajout profileMenu.myOrders sur les locales actives ; adaptation du sous-titre featuredSellersSubtitle FR/EN pour supprimer la logique “vérifié en priorité”.
Impact visible / technique : page boutique plus homogène visuellement et moins “marketing absolu”; menu compte enrichi; traçabilité des commandes côté vendeur disponible en self-service.
2026-05-01 15:22 — Open Graph / Discord : descriptions ressources en format embed
Contexte/problème : souhait d’un texte og:description aligné sur le rendu type embed Discord (titre Markdown, ligne méta discrète, sous-titre « Description », extrait dans un bloc).
Changements appliqués : nouvelle fonction buildOgResourceDiscordDescription dans web/src/lib/og-lbxmb.ts (# titre, -# + méta entre backticks via metaLineResource, ### Description :, corps tronqué/strip dans une fence markdown) ; generateMetadata des layouts ressources/[slug] et resources/[id] bascule de buildOgDescriptionBlock à ce builder pour les métadonnées (guides/forum/boutique inchangés).
Impact visible / technique : lors du collage d’un lien ressource (Discord ou client OG), la description structurée se rapproche du format attendu ; limite d’extrait inchangée (480 car. + ellipses) ; guillemets simples si backtick parasite dans la méta.
2026-05-01 16:27 — Boutique : ajout des jeux (multi-sélection optionnelle) + filtre/tri côté listing
Contexte/problème : besoin de trier/filtrer la boutique par jeux, et de permettre au vendeur d’associer zéro, un ou plusieurs jeux à son produit lors de l’ajout/modification.
Changements appliqués :
Backend shop : ajout colonne games (JSON) avec migration défensive ADD COLUMN IF NOT EXISTS sur les routes /api/shop, /api/shop/[id], /api/shop/request; normalisation (trim, suppression vides, max 12) et persistance JSON à la création/édition.
API lecture : parsing JSON de games dans les réponses produits (/api/shop et /api/shop/[id]).
Formulaire boutique : nouveau bloc Jeux (optionnel) dans BoutiqueProductForm avec sélection multi depuis jeux existants + ajout manuel d’un jeu (Enter/bouton), tags retirables, envoi games dans le payload.
Listing /shop : nouveau filtre game (query string partagée), chips de filtre actif, clear global, et nouveau tri game_asc (A→Z).
Impact visible / technique : vendeur plus précis sur la cible produit ; navigation acheteur plus rapide via filtre jeu ; compatibilité conservée pour anciens produits sans jeux.
2026-05-01 16:30 — Boutique /shop : alignement des menus filtres/tri
Contexte/problème : les menus filtres/tri se cassaient visuellement sur deux lignes non alignées selon la largeur (ex. tri passant en dessous), ce qui donnait un rendu irrégulier.
Changements appliqués : refonte du layout de la barre de filtres en grille responsive (1 → 2 → 4 colonnes) pour les 4 selects (catégorie, vendeur, jeu, tri) ; suppression des largeurs fixes par select ; compteur de résultats déplacé sous la barre et aligné à droite.
Impact visible / technique : menus visuellement alignés et cohérents, sans décalage parasite entre lignes ; meilleure stabilité responsive.
2026-05-01 16:39 — Mes commandes : lien profil acheteur + boutique : correction finale alignement filtres
Contexte/problème : dans Mes commandes, l’acheteur n’était pas cliquable ; sur /shop, les menus de filtre restaient visuellement superposés selon la largeur.
Changements appliqués :
Mes commandes : en onglet ventes, buyerPseudo devient un lien vers /profil/[pseudo] (URL encodée), avec fallback “Acheteur inconnu”.
Shop filtres : séparation claire en 2 lignes (recherche puis grille filtres), grille des 4 selects forcée en sm:2 / lg:4 colonnes pour éviter l’empilement incohérent observé.
Impact visible / technique : navigation directe vers le profil acheteur depuis les ventes ; filtres boutique stables et alignés sans chevauchement.
Contexte/problème : erreurs HTTP 500 en GET/PUT /api/shop/{id} pendant l’édition boutique (stack navigateur + échec PUT), dues à l’usage direct de la colonne games selon l’état réel du schéma DB.
Changements appliqués :
ajout d’un garde-fou runtime hasGamesColumn() sur /api/shop/[id] (lecture information_schema) ;
fallback si colonne absente : lecture sans games, games=[] côté payload ;
PUT /api/shop/[id] : SQL dynamique avec/sans games pour éviter tout crash ;
POST /api/shop/request : insertion dynamique avec/sans games selon schéma disponible ;
/api/shop : retrait de l’ALTER auto games qui pouvait provoquer un échec de route selon contexte DB.
Impact visible / technique : fin des 500 sur fiche produit/édition lorsque la colonne games n’est pas encore présente ; compatibilité ascendante maintenue sans bloquer la boutique.
2026-05-01 16:59 — Boutique : réorganisation UX du formulaire ajout/modification produit
Contexte/problème : le formulaire /boutique/ajouter / /boutique/modifier était perçu comme “en bazar” (ordre des champs et densité visuelle peu lisibles).
Changements appliqués :
restructuration du contenu en sections visuelles cohérentes : Informations produit, Classement et jeux, Présentation visuelle et médias, Garanties & options ;
regroupement logique des champs (prix/stock ensemble, catégorie + jeux ensemble, mode visuel + YouTube ensemble, médias principaux côte à côte sur desktop) ;
amélioration du rythme vertical (espacements, blocs card internes homogènes) tout en conservant la logique métier et les validations existantes.
Impact visible / technique : formulaire plus lisible, hiérarchie claire, meilleure navigation lors de la création/édition sans régression fonctionnelle.
2026-05-01 17:01 — Correctif Internal Server Error sur création demande boutique
Contexte/problème : retour “Internal Server Error” après soumission produit ; cause probable sur la vérification de schéma (information_schema) dans POST /api/shop/request.
Changements appliqués : encapsulation du check de colonne games dans un try/catch avec fallback propre (gamesColumnAvailable = false) et warning serveur non bloquant.
Impact visible / technique : la soumission continue même si la vérification de schéma échoue (droits SQL restreints, environnement spécifique), ce qui évite les 500 côté utilisateur.
2026-05-01 17:10 — Boutique : bypass validation pour Dexus + suppression depuis l’édition
Contexte/problème : besoin de publier directement les produits de l’utilisateur Dexus sans étape de validation admin ; besoin d’un bouton de suppression sur la page de modification produit.
Changements appliqués :
POST /api/shop/request : si user.pseudo === "dexus" (insensible à la casse), insertion en status='approved' (pas pending) + pendingReview=false et pas de notification de validation admin.
Formulaire BoutiqueProductForm (mode édition) : ajout d’un bouton Supprimer le produit avec confirmation, appel DELETE /api/shop/{id}, état de chargement dédié, puis redirection vers /shop.
Impact visible / technique : Dexus publie immédiatement ses produits ; suppression possible sans passer par d’autres écrans lors de l’édition.
Contexte/problème : vignettes boutique en ratio paysage alors que les médias sont carrés ; besoin après achat de contacter le vendeur (messagerie) et de signaler un problème ; section dashboard commandes doit distinguer paiements réussis et signalements boutique.
Changements appliqués :
Shop listing : zone visuel des cartes produit en aspect-square (au lieu de aspect-video), min-h carte légèrement ajusté.
Mes commandes (onglet Achats) : boutons Contacter le vendeur (POST /api/messages/vendor/create-or-get puis ouverture panneau messagerie) et Signaler un problème (modale + description 15–8000 car.) ; badge Signalé si déjà en base ; GET /api/mes-commandes enrichi avec hasReport par session Stripe.
Persistance : table auto-créée boutique_order_reports (session Stripe unique, acheteur/vendeur/meta produit, montant, description, statut open) ; POST /api/shop/order-report vérifie acheteur via Stripe + idempotence ; GET /api/dashboard/boutique-order-reports pour le staff.
Notifications : notifyBoutiqueOrderReport notifie le vendeur (users.id), puis fondateurs + admins (pas modérateurs/développeurs) ; type boutique_order_report inclus pour DM Discord si activé ; lien staff /dashboard/orders?tab=signalements .
Dashboard → Commandes : onglets Radix Commandes réussies (filtres Toutes/Atelier/Boutique inchangés) vs Signalements boutique (cartes détail + compteur badge, chargement dédié), Suspense pour useSearchParams, rappel total signalements au chargement.
Contacter le vendeur : ContacterVendeurButton accepte vendorUserId / productId string ou number pour cohérence avec les user_id Stripe.
Impact visible / technique : grille boutique alignée sur des images carrées ; parcours acheteur clair ; staff centralise les litiges boutique ; schéma créé à la volée sur première utilisation (MySQL).
2026-05-02 — Bannière Discord HTML : CSS cassé après export navigateur
Contexte/problème : /discord/banner/1232966743241130005 servait une page « Enregistrer sous » avec des <link href="LA RÉFÉRENCE DU MODDING_files/….css"> et scripts locaux introuvables à cette URL relative → mise en page sans styles (classes Tailwind non appliquées).
Changements appliqués : helper sanitizeDiscordExportedBannerHtml — suppression scripts et feuilles de style relatives, preload invalides ; src/href vers lbxmb.png passés en absolu (NEXT_PUBLIC_SITE_URL sinon https://lbxmb.fr) ; injection de Tailwind Play CDN + couleurs background/foreground + CSS pour .text-gradient et .animate-float. Utilisation dans GET /discord/banner/[guildId] et /discord/logo/[guildId].
Impact visible / technique : rendu proche de la capture quand ouvert dans un navigateur (JS étranger cdn.tailwindcss.com). Si un proxy impose une CSP très stricte sans ce domaine, il faudra une CSS statique même origine.
2026-05-02 — Nouveau logo LB’XMB (fichier 1000232690-removebg-preview.png)
Contexte/problème : mise à jour de l’identité visuelle sur le site, les URLs Flask/bot (/static/…), Open Graph / Twitter et embarquements Discord qui pointaient sur l’ancien lbxmb.png.
Changements appliqués :
Remplacement binaire du logo public : web/public/lbxmb.png (500×500, fond transparent).
Miroirs pour URLs historiques bot / webhooks Flask : web/public/static/lbxmb.png, web/public/static/images/logo.png, et fichiers équivalents sous web/static/ pour server.py (QR 2FA, image par défaut upload).
Open Graph / Twitter : layout.tsx racine dimensions 500×500 + twitter.images ; fallback siteIcon en 500×500 dans les layouts forum / ressources.
PWA : manifest.json entrée icône 512 remplacée par 500×500 (toujours une entrée 192 pour compatibilité).
Bot : site_content_embeds.py — auteur embed « LB'XMB.FR » utilise par défaut {LBXMB_PUBLIC_URL}/lbxmb.png (surcharge possible DISCORD_GUILD_ICON_URL).
Impact visible / technique : previews sociaux et UI site servent le nouveau visuel ; footers/embeds utilisant déjà https://lbxmb.fr/static/images/logo.png reçoivent le même fichier. Avatar Discord de l’application bot reste celui défini dans le portail développeur Discord (hors dépôt) — à mettre à jour manuellement avec le même visuel si souhaité.
Contexte/problème : élargir les moyens de paiement aux achats ponctuels (boutique, dons, services one-shot, VIP) en plus de la carte.
Changements appliqués :
Module web/src/lib/stripe-payment-method-types.ts : défaut card, paypal, amazon_pay ; variables STRIPE_CHECKOUT_PAYMENT_METHOD_TYPES (global) et STRIPE_VIP_PAYMENT_METHOD_TYPES (VIP seul si défini — sinon même liste que le global après parse).
Branchement dans POST /api/stripe/checkout (boutique), /api/checkout (services : carte uniquement si mode subscription / hébergement récurrent pour éviter incompatibilités), /api/donate/checkout, /api/vip/checkout.
GET /api/mes-commandes : libellés français PayPal / Amazon Pay dans normalizePaymentMethod.
.env.example : commentaires sur les variables Stripe Checkout.
Impact visible / technique : la page Stripe Checkout affichera PayPal et Amazon Pay si activés dans le Dashboard Stripe pour le compte. Les abonnements services restent carte seule jusqu’à support explicite.
Contexte/problème : messages type « Content-Security-Policy warnings », police WOFF2 préchargée peu utilisée vite, « WebGL context was lost », lignes « Feature Policy: Skipping unsupported feature name … » (iframes / scripts tiers).
Changements appliqués :
web/src/lib/csp.ts : connect-src + frame-src étendus pour Amazon Pay (domaines *.amazon.com, *.amazonpay.com, pay.amazon.com, static-fe.payments-amazon.com) et iframe https://checkout.stripe.com pour cohérence avec Checkout hébergé.
web/src/styles/font.ts : suppression de Press Start 2P (jamais utilisée dans les pages alors que le module font.ts était chargé avec Montserrat) pour retirer préchargements / @font-face inutiles à l’accueil.
web/src/components/LightRays.jsx + web/src/components/ui/backgrounds/LightRays.tsx : respect de prefers-reduced-motion: reduce (pas d’init WebGL), écoute webglcontextlost avec arrêt propre de la boucle, suppression de WEBGL_lose_context.loseContext() au démontage (évitait des logs « context was lost » volontaires lors des changements de route).
web/next.config.js : commentaire précisant que Next.js 16 utilise src/proxy.ts (export proxy) — ne pas ajouter en parallèle middleware.ts (conflit build).
Impact visible / technique : CSP chargée via le Proxy (Middleware) comme sur le build (ƒ Proxy) ; moins de bruit précharge / WebGL lors de la navigation ; Amazon Pay moins exposé aux blocages CSP. Les avertissements Feature Policy liés à YouTube/embeds tiers restent du côté navigateur / contenu intégré, pas corrigibles localement sans retirer ces intégrations.
Contexte/problème : (1) réponses API /api/banner/import et config JSON avec ancienne URL /banner/{guildId} ; (2) page /discord/banner/{guildId} à valoriser pour affichage large + logo/stats ; (3) ne plus exposer /banner/ comme contenu servi ; (4) Stripe : aligner la version API sur le SDK npm ; (5) dashboard trop compact.
Changements appliqués :
Bot : normalize_lbxmb_discord_banner_url (/banner/(\d+)→/discord/banner/\2) dans banner_update_handler.load_config (persist JSON si correction) ; même normalisation lors de la soumission du modal Configurer l’URL bannière (setup_server.py).
POST /api/banner/import : écrit désormais data/discord/banner_html/{guildId}/banner.html, met à jour banner_config.json via setBannerConfigUrls, retourne {base}/discord/banner/{guildId}.
GET /banner/[guildId] : redirection 308 permanente vers /discord/banner/{guildId} (plus de rendu depuis uploads/banner-pages sur cette route).
GET /discord/banner/[guildId] : lecture HTML custom → fallback legacy uploads/banner-pages/.../index.html → sinon template défaut buildDiscordDefaultBannerHtml (stats MySQL ressources, guides, utilisateurs, Go) ; custom toujours passé par sanitizeDiscordExportedBannerHtml.
Impact visible / technique : anciens bookmarks /banner/id rejoignent la route canonique ; sans fichier HTML custom pour un guild, les captures bot voient une bannière « premium » mise à jour côté serveur ; mise à niveau npm stripe continuera à piloter la version API attendue ; UI staff plus aérée. Pour remplacer un vieux HTML importé par le nouveau template, supprimer ou remplacer le fichier data/discord/banner_html/{guildId}/banner.html sur le serveur.
Contexte/problème : /discord/banner/{guildId} s’affiche sans mise en forme (polices serif, mise en page plate) ; l’upload HTML prioritaire continuait à servir l’ancienne « présentation » sans Guides ; la CSP globale (strict-dynamic + nonce) bloquait cdn.tailwindcss.com donc aucune classe Tailwind ne s’appliquait.
Changements appliqués :
web/src/proxy.ts : pas d’en-tête CSP sur /discord/banner/* et /discord/logo/* pour permettre CDN / exports « Enregistrer sous ».
web/src/lib/discord-default-banner-html.ts : gabarit par défaut entièrement en CSS inline (aucun script, aucun CDN) — conforme CSP même sans exception de route.
web/src/lib/discord-banner-html-sanitize.ts : si le HTML importé n’a pas de <head>, enveloppe <!DOCTYPE html>… et injecte le patch (Tailwind + styles de secours).
GET /discord/banner/[guildId] : paramètres ?default ou ?template=default pour forcer le template serveur même si banner.html existe (aperçu sans supprimer le fichier).
Impact visible / technique : URL sans fichier custom affiche déjà une bannière stylée sans dépendre du CDN ; avec fichier ancien exporter, soit utiliser ?default, soit supprimer data/discord/banner_html/{guildId}/banner.html pour que le bot utilise le rendu défaut ; exports avec <head> manquant récupèrent Tailwind où la CSP route est assouplie.
2026-05-02 — Dashboard : bordures et zones plus lisibles (sidebar, stats, guides, todos, analytics)
Contexte/problème : zones admin perçues comme trop serrées ; bordures à faible contraste (border-white/5) ; carte Guides avec rounded-[2.5rem] qui nuisait à l’alignement visuel avec la sidebar ; fil d’Ariane « flottant » sans lien avec les cartons de contenu.
Changements appliqués :
Sidebar : bordure droite et séparateurs en white/[0.10–0.12], fond un peu plus marqué, espacement vertical entre liens (space-y-2, py-3), anneau léger sur l’élément actif / hover.
Header : barre inférieure plus visible ; fil d’Ariane encapsulé dans un pilule (ring + fond) pour le dissocier du contenu sans le coller aux bords.
DashboardStatsv2 : cartes rounded-2xl, bordure white/[0.14], ombre intérieure ; grille 3 colonnes jusqu’à 2xl (6 cols seulement sur très grand écran) ; gaps et paddings internes augmentés.
Vue d’ensemble : space-y-10 / 12, bloc trafic avec séparateur supérieur plus net.
AnalyticsDashboard : même logique de bordure et en-tête de section.
Todos (tasks-table-main) : carte arrondie, bordures et en-tête de carte plus marqués, padding tableau augmenté.
Guides : carte rounded-2xl uniquement ; bandeau titre séparé par une bordure basse ; tableau desktop dans un cadre rounded-xl + border avec en-tête sur fond léger et lignes hover.
Impact visible / technique : hiérarchie des blocs plus claire sans refonte de maquette ; cohérence des approx. 14 % blanc sur les contours.
Montserrat (Google Fonts 600–800) pour badge, titre, ligne « par », pastilles consoles.
Textes alignés messages/fr.json → home : badge, titleLine1 / titleLine2, sous-titre, stats Ressources / Utilisateurs / Guides / Stockage (données live inchangées) ; ligne par LoupBlanke · Interverti comme le Hero ; pastilles Xbox / Nintendo / PlayStation (couleurs type Hero).
Dégradé titre MODDING #60a5fa → #2563eb ; grille stats avec séparateur en dégradé comme l’accueil ; bloc logo avec halobleu / drop-shadow.
Impact visible / technique : GET .../discord/banner/{guildId}?default (ou sans fichier banner.html) affiche une bannière lisible comme une capture du hero, sans animation ni interactif ; charge fonts.googleapis.com (CSP déjà levée sur cette route).
2026-05-03 — Discord : plus d’embeds auto dans le salon vitrine, /script → website
Contexte/problème : le salon 1486674093121077379 recevait des mises à jour répétées (statut site ~2 min + vitrine ~6 h). Souhait : arrêter l’envoi / l’édition continue et publier à la demande une carte « site » (état, stats, stockage).
Changements appliqués :
LBXMB_SITE_EMBED_AUTO_ENABLED : la boucle LbxmbSiteEmbedHandler (édition périodique du message statut + boutons) ne démarre plus par défaut ; mettre à true / 1 pour réactiver. Pied d’embed adapté selon ce réglage.
SITE_SHOWCASE_AUTO_ENABLED : la boucle SiteShowcaseHandler (vitrine /api/stats/home) ne démarre plus par défaut ; true / 1 pour réactiver.
Commande /script : nouveau choix website — envoie un embed (état HTTP du site, membres, ressources, guides, stockage depuis /api/stats/home) + les boutons lien (Site, Forum, Ressources, Guides).
Impact visible / technique : plus de spam automatique dans ce salon tant que les variables ne sont pas activées ; le staff publie la carte via /script → website après sync slash du bot.
2026-05-03 ~14:00 — Web : fluidité / perf (accueil + layout, mobile & desktop)
Contexte/problème : objectif expérience plus fluide (LCP, moins de JS initial, WebGL et images moins agressifs sur mobile) après constats type Lighthouse.
Changements appliqués :
Accueil : HomeBackdrop charge LightRays en dynamic(..., { ssr: false }) avec fond CSS immédiat (dégradés type néons) ; bundle ogl sort du chemin critique.
HomeBelowFold : sections sous le hero (FeaturedMods, annonces, témoignages, partenaires, FAQ, Footer) en imports dynamiques avec placeholders légers.
LightRays.jsx : DPR plafonné selon largeur d’écran (mobile plus bas) pour soulager GPU / batterie.
Hero : image mockup avec sizes="(max-width: 1023px) 0px, …", priority={false} (colonne lg masquée sur mobile — évite gros chargement hors écran).
next.config.js : experimental.optimizePackageImports pour lucide-react et react-icons.
Layout : DeferredClientWidgets (client) regroupe Umami, Service Worker, modal d’accueil en dynamic ssr: false (compatible Next 16 — pas de dynamic ssr:false dans le Server Component racine).
app/loading.tsx : indicateur minimal pour navigations / attente segment.
Impact visible / technique : premier paint plus léger sur / ; scroll et interactions moins monolithiques ; à valider en prod avec Lighthouse + Network throttling mobile. Poursuites possibles : même logique sur Header (icônes), pages lourdes (dashboard, forum), entêtes cache Nginx sur assets.
Contexte/problème : poursuivre la fluidité au-delà de l’accueil : moins de WebGL/JS sur les parcours listés (ressources, guides, shop, etc.) + menu global.
Changements appliqués :
web/src/components/ui/DeferredLightRays.tsx : wrapper unique (fond CSS + dynamic(LightRays, { ssr: false })). HomeBackdrop re-exporte ce module pour l’existant.
Remplacement de l’import direct LightRays par DeferredLightRays sur : ressources/page, ressources/[slug]/ResourceDetailClient, guides/page, support/page, shop/page, shop/[id]/page, download, checker, demarrer-modding/*Client, etc.
Footer en next/dynamic sur les mêmes pages lourdes + forum (liste) pour scinder le chunk.
Header : chargement différé de recherche globale, menu profil sheet, cloches notif / messages, barre mobile basse (chunks séparés ; recherche + profil en ssr: false).
dashboard/layout : DashboardMobileSidebar en import dynamique (visible surtout sur petit écran).
Impact visible / technique : ogl n’est plus dans le bundle initial de ces pages ; en-têtes cache /_next/static déjà gérés côté Nginx (nginx-lbxmb.fr.conf) — pas de changement serveur requis.
2026-05-03 ~21:05 — Web : liste forum /forum, chunks dynamiques création sujet + création groupe
Contexte/problème : poursuivre l’allègement JS de la page liste forum en sortant Dialog / MentionTextarea / TagsMenu et le panneau création groupe du bundle initial synchrone.
Changements appliqués :
forum/page.tsx : remplacement du gros JSX inline par ForumThreadComposerLazy et ForumGroupCreatePanelLazy (next/dynamic, ssr: false, loading: () => null) ; filtrage d’affichage activeTab === 'fil' / 'groupe' comme avant fonctionnel ; imports retirés (Dialog, TagsMenu, MentionTextarea, Textarea, doubles Lucide), ForumCategoryRow réutilisé depuis forum-page-types.
ForumThreadComposer : props popularTags / allTags typées TagItem[] ; suppression des props setShowScheduleOptions / setShowVisibilityOptions (non utilisées dans le composant — toggles pilotés depuis la page).
TagsMenu.tsx : export du type TagItem pour le typage commun.
Impact visible / technique : premier chargement liste forum : MentionTextarea, TagsMenu, Dialog et panneau groupe chargés à la demande ; npm run build OK.
2026-05-03 ~22:15 — Bot Discord : VIP cosmétique vs booster, retour sur le serveur (pseudo + rôles site)
Contexte/problème : (1) les membres avec le rôle booster Discord 1308907900751577181 recevaient aussi le rôle cosmétique Premium 1385546866942410813 via _sync_booster_role_to_site et/ou la colonne vip du site. (2) au rejoin, compte lié : besoin de réappliquer pseudo + rôles issus du site (ex. permissions type support_playstation + table roles / roster).
Booster : plus d’ajout du rôle Discord 1385546866942410813 lors du sync boost (aligné sur GET /api/bot/check-vip = Premium payant uniquement).
get_expected_roles_for_user(..., paid_vip_discord=...) : le rôle VIP cosmétique n’est ajouté que si check_vip_api renvoie vrai ; appels mis à jour (on_member_join, on_member_update, sync_all_members_roles).
on_member_join : charge l’utilisateur via get_user_from_site (inclut roles, badges, all_permissions avec normalisation CSV/JSON depuis permission + all_permissions) ; sync_on_join_linked ( défaut true dans auto_nickname ) pour appliquer le pseudo au retour même si enabled est à false ; retrait du rôle cosmétique si présent alors que check-vip est faux.
Import check_vip_api depuis commands/vip (variable WEBSITE_BASE pour joindre l’API).
Impact visible / technique : boost seul ⇒ rôle serveur booster, pas le rôle Premium cosmétique ; abo actif ⇒ inchangé. Rejoin ⇒ nick + rôles site (dont mapping DB roles → Discord). Pour désactiver le nick auto au rejoin : config serveur auto_nickname.sync_on_join_linked = false.
Alignement synchro fichier / login : plus de mapping direct vip → 138554… dans les routes web et partner-role-sync ; sync_user_roles (Flask) reçoit vip_source et n’attribue le rôle cosmétique pour le slug vip que si ce n’est pas boost (sinon vip_premium / vip_booster côté site).
Contexte/problème : éviter une commande slash ; besoin d’un script CLI analysant les membres dont username / pseudo serveur / nom global correspondent exactement à user_12345 (préfixe + _ + 5 chiffres par défaut).
Changements appliqués :
Suppression de bot/commands/temp_accounts_audit.py (/comptes-temporaires retirée).
Nouveau bot/scripts/list_temp_pattern_members.py : API REST (urllib uniquement), pagination membres /guilds/{id}/members, motifs --prefix (défaut user) et --digits, export TSV (défaut bot/scripts/liste_comptes_user_pattern.txt).
.gitignore : bot/scripts/liste_comptes_*.txt pour ne pas versionner les listes membres (PII).
Impact visible / technique : python3 bot/scripts/list_temp_pattern_members.py ; options --mode contains, --stats, diagnostic automatique si --mode exact et 0 ligne (histogramme préfixe_ + chiffres).
2026-05-03 fin de journée — Bot : script comptes temp, motif type luigi_45892 / lol45_78965
Contexte/problème : le motif n’est pas limité à user_##### : toute partie gauche alphanum minuscule + _ + N chiffres (ex. luigi_45892, lol45_78965).
Changements appliqués :
list_temp_pattern_members.py : sans --prefix, filtre exact / contains sur ^[a-z0-9]{min…max}_\d{N}$ (bornes --min-left / --max-left, défaut 1–32) ; avec --prefix luigi, uniquement ce préfixe + --digits.
Sortie TSV par défaut : bot/scripts/liste_comptes_temp_pattern.txt (toujours ignoré via liste_comptes_*.txt).
Correction en-tête commentaire TSV : parenthèse fermée sur la ligne « Motif décrit ».
Impact visible / technique : sur le serveur de test, 315 membres matchés en mode exact (5 chiffres) ; relancer sur le bon guild avec un token GUILD_MEMBERS pour la prod.
2026-05-04 — Bot : script temp, même jour création (~snowflake) vs joined_at
Contexte/problème : mesurer parmi les pseudo au motif xxx_XXXXX combien ont rejoint le serveur le même jour calendaire (UTC) que la création du compte approximée par le snowflake utilisateur ; signal complémentaire « join quasi immédiat ».
Changements appliqués : list_temp_pattern_members.py — colonnes TSV join_serveur_date_utc, meme_jour_crea_join ; après export, lignes stdout avec décompte même jour et sous-total joined_at dans les 120 s après timestamp snowflake.
Impact visible / technique : exemple exécution locale (guild configuré, 315 hits motif) : 152 / 315 même jour UTC ; 92 avec join ≤ 120 s après création approx ; joined_at toujours présent sur cet échantillon (0 inconnu).
2026-05-04 (suite) — Bot : export dédié « même jour » (--only-same-day)
Contexte/problème : où consulter la liste des comptes au motif xxx_XXXXX avec création et join même jour UTC sans filtrer le TSV à la main.
Changements appliqués : --only-same-day — TSV et aperçu console limités à meme_jour_crea_join=oui ; sortie par défaut bot/scripts/liste_comptes_temp_same_day.txt (-o pour un autre chemin ; toujours couvert par liste_comptes_*.txt). En-têtes du fichier résument au motif / même jour.
Contexte/problème : le site tournait en rechargement infini : trafic HTML / navigation Next.js passait par Anubis alors que /_next/static, WebSocket tickets, etc. contournent déjà → incohérences (RSC, cookies, challenge) côté client.
Changements appliqués : location / pour lbxmb.fr / www.lbxmb.fr : proxy_pass vers web_cluster au lieu de anubis_lbxmb ; commentaires expliquant la cause ; timeouts lecture/envoi 120s (aligné pages lourdes). L’upstream anubis_lbxmb reste dans le fichier pour une réactivation ciblée ultérieure.
Impact visible / technique : plus de boucle de challenge sur le site principal ; protection anti-bot Anubis inactive sur ce vhost jusqu’à reconfiguration (ex. autre sous-domaine ou règles fines). À faire sur le serveur : sudo nginx -t && sudo systemctl reload nginx après déploiement du fichier.
Contexte/problème : aligner l’ajout du bot sur les règles discutées : Premium payant (API check-vip) sans limite de serveurs uniquement si le Discord a le rôle VIP (couronne) sur le serveur principal ; sinon le bot quitte. Booster / Partenaire (sans ce flux Premium) restent à 3 serveurs max.
Changements appliqués : _has_premium_crown_on_main (présence de VIP_ROLE_ID sur MAIN_GUILD_ID). Branchement vip_site avant booster_or_partner ; embeds MP dédiés (couronne manquante, limite 3, accueil illimité / cap 3). Suppression de _has_privileged_access (logique inline).
Impact visible / technique : abo Premium sans rôle sur LB’XMB ⇒ refus d’entrée ; Nitro booster / partenaire sans abo Premium site ⇒ inchangé hormis textes d’embed limite ; Premium + couronne ⇒ plus de plafond à 3 pour cet utilisateur.
Contexte/problème : suite des idées (profil bot par serveur, backups, commandes masse rôles/salons, permissions /backup orientées propriétaire par défaut).
Changements appliqués :
/config : entrée Profil du bot (pseudo via guild.me.edit, avatar/bannière via PATCH .../members/@me, description stockée dans bot_guild_profile), ligne d’aperçu dans l’embed d’accueil + retour BackButton.
/backup : stockage bot/data/guild_backups/ (snapshots + index 25 max / utilisateur), création snapshot (guild_snapshot : rôles, salons, overwrites, bans, rôles-membres plafonnés), liste menu, suppression (modal SUPPRIMER), application métadonnées serveur (nom, description, icône, bannière si feature BANNER) avec modal APPLIQUER ; accès backup : créateur ou propriétaire du serveur cible ; permission globale via has_backup_command_permission.
Site : web/messages/discordBot/fr.json & en.json (entrées commandes).
Impact visible / technique : resynchroniser les commandes slash (tree.sync / redémarrage bot). Restauration rôles/salons depuis backup : non implémentée dans ce lot (données déjà capturées pour évolution). /role//salon : défaut Admin/Support si pas de rôles dédiés dans /config (comme les autres commandes modéré).
Contexte/problème : aligner l’UI Discord sur un embed type Notification vidéos (état, nombre de suivis, intervalle par défaut, liste des suivis avec salon / options) ; boutons Ajouter / Modifier / Retirer + Retour ; plus de bouton Actualiser — le panneau ancré doit se mettre à jour après chaque action ; par suivi : intervalle, actif, message, rôle mention, salon (à l’ajout via modal après choix du salon).
Changements appliqués :
VideosNotificationsView : suppression des contrôles globaux (toggle global, rôle global, intervalle global UI, actualiser) au profit du flux ci-dessus ; refresh_config_panel_anchor(..., title_must_contain="vidé") après ajout / édition / retrait.
VideoFinishModal : fin de configuration après ChannelSelect (intervalle, message optionnel, actif, ID rôle optionnel) ; custom_message jusqu’à 1900 caractères.
EditVideoModal : max 5 champs Discord (pas d’édition du rôle dans ce modal) ; défaut salon si ID absent ; comparaison d’intervalle par suivi dans l’embed avec garde TypeError/ValueError ; on_submit : erreurs via response.is_done() + followup.
_sync_videos_master_enabled : flag global videos.enabled si au moins un suivi actif.
Impact visible / technique : l’embed titre « 🎥 Notification vidéos » reste filtré par vidé pour l’ancre ; pour modifier rôle ou message personnalisé après coup : soit recréer le suivi, soit évolution ultérieure (2ᵉ modal / select rôle).
Contexte/problème : backup = JSON serveur complet + application destructive contrôlée ; pas de modal APPLIQUER mais menu multi-sélection ; une seule commande /roles (plus de groupe /role) ; /salons (ex-/salon) sans hiérarchie rôle sur les overwrites ; /backup, /roles, /salons réservés au propriétaire par défaut (comme backup) ; option propriétaire pour autoriser l’attribution de rôles au-dessus du modérateur (sous le bot).
Changements appliqués :
guild_snapshot v2 : rôles non gérés + champs salons (topic toujours, nsfw/slowmode texte, bitrate/user_limit vocal/stage) ; unicode_emoji rôle si présent.
guild_backup_restore : suppression salons (profondeur) → rôles gérables → recréation rôles + positions → catégories/salons + overwrites (map IDs ou fallback nom si pas de restauration rôles) → apply_guild_branding → bans optionnels.
guild_backup.py : application via embed + ApplyPartsSelect (branding / roles / channels / bans) + bouton Lancer ; apply_guild_branding déplacé dans le module restore ; embed d’aide mis à jour.
bulk_roles_channels.py : /roles (menu action ajouter/retirer + rôles + membres) ; /salons ; permissions has_roles_command_permission / has_salons_command_permission (fallback clés role / salon pour anciennes configs) ; salons : manage_channels modérateur + bot par salon, plus de _hierarchy_ok sur le rôle cible.
Contexte/problème : après application, rôles dans le sens inverse du serveur d’origine ; création des salons incomplète (position à la création sans réordonnancement) ; métadonnées peu fiables (un seul guild.edit + téléchargement CDN sans en-têtes) ; besoin d’exclure encore les rôles bots / intégrations de la sauvegarde.
Changements appliqués :
guild_snapshot v3 : fonction _exclude_role_from_backup (managed, tags.bot_id, tags.integration_id) ; champ hierarchy_rank (ordre bas→haut parmi les rôles sauvegardés).
guild_backup_restore : tri restauration avec hierarchy_rank puis position ; après création, Role.edit(position=…) sous le rôle du bot (base + k) plutôt que edit_role_positions seul ; téléchargement icône/bannière avec User-Agent puis guild.edit séparés pour nom/description / icône / bannière ; salons sans position à la création, puis reorder_restored_channels (catégories puis groupes parent + bucket Discord) via ch.edit(position=i) ; petite pause asyncio.sleep anti rate-limit ; unicode_emoji au create_role avec repli si TypeError.
Impact visible / technique : recréer une nouvelle backup après déploiement pour embarquer hierarchy_rank (les anciennes backups retombent sur position API). Métadonnées : si une étape échoue, le rapport liste les segments en erreur sans tout bloquer d’un coup.
Contexte/problème : vérification après nouvelles captures : le JSON de backup contient bien les rôles/salons attendus, mais l'application pouvait encore produire un ordre de rôles inversé (effet de décalage pendant role.edit(position=...)), des salons manquants/non comptés et des métadonnées partiellement ignorées si description échouait dans le même guild.edit.
Changements appliqués :
guild_snapshot : ordre source des rôles basé sur guild.roles (ordre hiérarchique natif discord.py) au lieu d'un tri manuel (position, id).
guild_backup_restore :
apply_guild_branding en appels séparés (nom, puis description, puis icône, puis bannière) pour éviter qu'une erreur sur un champ bloque le reste.
_finalize_role_positions applique désormais les positions du haut vers le bas (reversed(zip(...))) pour supprimer l'inversion observée.
_map_overwrites(..., include_member_overwrites=False) : restauration structurelle centrée sur les overwrites de rôles (plus stable, moins d'échecs intermittents).
recreate_channels compte aussi les catégories créées et ajoute un diagnostic explicite des canaux manquants (Canaux manquants: X/Y + exemples noms).
Impact visible / technique : le snapshot 1fa830a8-854c-4ff2-9ec6-c794ae639575 est structurellement correct (17 rôles, 59 salons, types text/voice/news/forum/category). Le problème principal était bien côté application de la backup ; le rapport de restauration expose désormais clairement les éventuels restants.
Contexte/problème : après déploiement des correctifs précédents (« reversed » + Role.edit(position=…)), constat aucun changement côté utilisateur : ordre des rôles encore incohérent ; salons qui échouent sans détail exploitable.
Changements appliqués :
_finalize_role_positions : abandon des déplacements séquentiels (collisions avec les rôles managed encore présents sous le bot) ; liste managed (ordre actuel) + autres non gérés sous le bot + rôles recréés (backup) puis guild.edit_role_positions en une passe (entrées triées par position croissante).
recreate_channels : erreurs salon avec code HTTP et extrait de réponse Discord (forum/news/permissions notamment).
Impact visible / technique : redémarrer le bot pour charger le module ; le rôle du bot doit rester au-dessus des rôles repositionnés. En cas d’échec salon, le rapport doit afficher HTTP xxx exploitable pour la suite du correctif (features serveur / overwrites).
Contexte/problème : refaire le système de bout en bout pour un usage type Xenon : capturer un serveur puis l’appliquer sur un autre — l’ancienne logique mélangait tous les salons sans respecter l’ordre racine (salons sans catégorie vs catégories), ne restituait pas les réglages serveur Discord, ne réappliquait pas les rôles membres, et forum/news plantaient sans filet sur serveur cible plus pauvre en features.
Changements appliqués :
guild_snapshot v4 : sorting_bucket par salon ; guild_features liste triée ; bloc guild étendu (verification_level, default_notifications, explicit_content_filter, default_role_permissions).
guild_backup_restore : recréation salons en deux passes (entrées racine triées par position = catégories + canaux sans parent, puis enfants par parent_id et sorting_bucket/position) ; création centralisée _create_channel_from_snapshot avec forum→texte si pas COMMUNITY, news→texte en secours, fallback texte générique sur erreur HTTP ; apply_guild_server_settings (guild.edit groupé + default_role.edit) ; restore_member_roles_from_snapshot ; run_restore enrichi (avertissement features manquantes, ordre canaux → branding → réglages serveur → membres → bans) ; option server_settings séparée de branding`**.
guild_backup.py : menu d’application étendu (réglages serveur, rôles des membres) ; textes embed alignés sur le flux template.
Impact visible / technique : nouvelle backup pour profiter des champs v4 (les anciennes gardent un tri enfant moins précis sur le bucket). Cocher rôles + membres pour une restitution des rôles-utilisateurs ; limite 1200 lignes member_roles traitées par passe. Redémarrage bot + resync slash si besoin.
2026-05-04 23:50 — Backup : un rôle en bas à tort + salons sans Communauté
Contexte/problème : après restauration, un rôle (ex. Support PlayStation) restait tout en bas alors que le reste de la hiérarchie était correct ; salons forums/stages incomplets parce que le serveur cible n’avait pas la Communauté Discord activée alors que la backup source en dépendait.
Changements appliqués :
Rôles : éviter les doublons de même `id source dans le JSON (ne plus recréer ni dupliquer le map) ; dédoublonnage des entrées snapshot avant repositionnement ; refresh puis bulk systématique sur tout le bloc cible avec deuxième passe si Discord n’a pas appliqué les positions au premier coup ; recherche get_role + discord.utils.get` avant d’écarter une entrée ; snapshot : ignore les doubles rôles même id en boucle (garde-fou Discord).
Communauté : si la backup impose COMMUNITY (feature source ou salons forum / stage), tentative d’activation avant recréation des salons : niveau vérif/filtres tendance communautaire, guild.edit(community=True, rules_channel=…, public_updates_channel=…) avec création préalable de lbxmb-backup-reglement et lbxmb-backup-annonces ; flag community_effective propagé aux créations (cache `features` parfois en retard sur le client discord.py).
Salons : scène sans communautaire → création vocal avec note ; rapport [communauté] ….
Impact visible / technique : il faut Gérer le serveur sur le bot pour activer Communauté ; si Discord refuse (non-proprio, quotas, …), fallback forum/stage comme avant. Nouvelle backup utile après correctif snapshot doublons.
2026-05-05 00:25 — Communauté : vrais salons règlement + annonces depuis la backup
Contexte/problème : l’utilisateur veut le flux « comme Discord » : créer d’abord le salon annonces (texte), activer Communauté avec les mêmes salons que sur le serveur source (règlement + annonces), puis poursuivre la restauration sans ignorer ces canaux.
Changements appliqués :
guild_snapshot v5 : enregistrement rules_channel_id et public_updates_channel_id (API serveur source).
bootstrap_community_from_snapshot : résolution ids backup puis heuristiques (noms règle/charte/CGU ; type news ou noms annonces/actus) ; création texte + catégories parentes si besoin ; ch_id_map rempli pour que la suite saute la recréation de ces salons ; guild.edit(community=True, …) ; après réordonnancement, passage news pour le salon annonces si la source était news (pas le salon règlement).
Boucle enfants : parcourt toutes les catégories du JSON présentes dans la map (y compris catégories créées au bootstrap).
Impact visible / technique : nouvelle backup recommandée pour avoir les ids officiels ; sinon heuristique + repli lbxmb-backup-*. Toujours Gérer le serveur + Gérer les salons.
2026-05-05 00:45 — Restauration salons : enfants de catégorie + passe secours
Contexte/problème : après bootstrap Communauté (règlement + annonces OK), le reste des salons n’était pas créé.
Changements appliqués :
Boucle enfants : ne plus abandonner silencieusement si `guild.get_channel ne voit pas la catégorie — résolution via utils.get puis guild.fetch_channel ; si la catégorie reste introuvable, création des enfants à la racine avec avertissement au lieu de continue` (comportement ancien qui sautait toute la catégorie).
Passe secours après les boucles : tout salon encore absent (catégorie source absente de la map, parent invalide, etc.) est créé avec parent résolu ou sans catégorie.
`_snap_parent_id : 0 traité comme absence de parent ; snapshot : n’écrire parent_id que si category_id` est non nul.
Impact visible / technique : rapport plus explicite (⚠ catégorie cache / secours) ; redémarrage bot.
2026-05-05 01:10 — Salons sous catégories : regroupement par parent + types Discord
Contexte/problème : catégories recréées mais tous les salons à l’intérieur absents (capture LB’XMB bac à sable).
Changements appliqués :
_norm_snowflake : ids/parents normalisés (évite échecs de matching int/str).
fetch_channels() après la passe racine pour rafraîchir le cache guild avant les enfants.
Enfants : plus d’itération « une catégorie du JSON → liste des enfants » seule ; construction by_parent en parcourant tous les salons dont le parent est une catégorie présente dans ch_id_map (même logique de filtrage, autre ordre d’agrégation).
_is_category_snap : type entier 4 (API), chaînes 4, guild_category, .category.
_dtype_from_channel_entry : type salon entier API (0 texte, 5 news, 15 forum, etc.) en plus des noms d’enum.
Heuristiques règlement/annonces alignées sur _dtype_from_channel_entry.
Impact visible / technique : redémarrage bot ; nouvelle restauration ; si encore des trous, le rapport « Canaux manquants » + passes secours restent actifs.
2026-05-05 01:40 — Salons dans les catégories : @everyone source + objet catégorie + bitrate vocal
Contexte/problème : après les passes précédentes, constat utilisateur toujours : catégories visibles mais peu ou pas de salons à l’intérieur (souvent quelques salons racine seulement).
Changements appliqués :
_map_overwrites : si `source_guild_id (champ source_guild_id du JSON) est connu, l’overwrite rôle dont l’id vaut ce snowflake est mappé sur le @everyone du serveur cible (sur la source, c’est le même id que le guild ; ce rôle n’est pas dans la liste roles` de la backup).
recreate_channels : dictionnaire id catégorie source → objet `CategoryChannel rempli à la création ; complétion pour les catégories déjà dans ch_id_map (ex. bootstrap) puis résolution parent enfants/secours en priorité sur cet objet (évite get_channel`/cache fragile juste après création).
_ensure_parent_category_for_bootstrap : retour du parent via `_resolve_guild_category au lieu d’un simple get_channel`.
_create_channel_from_snapshot : bitrate vocal/scène plafonné à `guild.bitrate_limit` (serveur cible moins boosté que la source).
Journal restauration : jusqu’à 55 lignes d’erreurs salons (au lieu de 25) pour diagnostiquer les échecs résiduels.
Impact visible / technique : les salons sous catégories devraient être créés avec la bonne visibilité (deny/allow @everyone) et restés sous la bonne catégorie ; vocaux moins susceptibles d’échouer sur bitrate trop haut. Redémarrage bot.
Contexte/problème : besoin de voir ce qui se passe pendant création backup et restauration (jalons, groupes par catégorie, échecs HTTP) sans dépendre uniquement du rapport Discord tronqué.
Changements appliqués :
_restore_log dans guild_backup_restore : préfixe commun `[restore guild=… backup=… src=…] ; INFO pour jalons (run_restore, passes salons, bootstrap Communauté, fin secours / complet) ; DEBUG pour détail (groupes by_parent, résolution cat_obj) ; WARNING échecs création enfant/racine/secours, map incomplète, create_snapshot` définitif.
guild_snapshot.build_guild_snapshot : ligne INFO `[backup snapshot guild=… backup_id=…]` avec compteurs.
guild_backup.py : INFO après création JSON ; avant/après run_restore (parts + taille rapport).
Impact visible / technique : configurer le logger racine ou utils.guild_backup_restore / utils.guild_snapshot / commands.guild_backup en DEBUG pour le détail ; défaut INFO suffit pour les jalons. Redémarrage bot.
Contexte/problème : d’après `bot-python-error-*.log, la restauration allait jusqu’à by_parent (48 enfants sur 10 catégories) puis plantait sur Guild.create_text_channel() (argument type` / salon news) — d’où serveur sans tous les salons ; le message Discord ne montrait qu’une partie du rapport.
Changements appliqués :
`mk_text : n’envoie topic que si non None ; try/except TypeError sur create_text_channel puis repli sans news` (texte classique).
`_create_channel_from_snapshot : le except principal inclut aussi TypeError` avec message texte dans le fallback.
`run_restore retourne (log, Path | None) et écrit tout le rapport dans bot/data/guild_backups/restore_reports/restore_g\<guild\>_\<backup\>_\<ts\>.txt` ; une ligne dans le rapport indique le chemin.
`guild_backup.py` : prévisualisation 70 lignes + pièce jointe du fichier rapport si < 7,5 Mo.
Impact visible / technique : relance pm2 restart bot-python ; après restauration, ouvrir le fichier joint ou le dossier sur le serveur pour tout le détail ; les salons news ne doivent plus bloquer toute la suite.
Contexte/problème : besoin de retirer un avertissement depuis le flux casier avec choix interactif, et exposition d’une commande dédiée /unwarn (même logique).
Changements appliqués :
sanctions_handler : remove_warn_at_index(guild_id, user_id, index) pour supprimer une entrée dans warns (persisté dans sanctions.json).
views/warn_removal_view.py : menu Select (jusqu’à 25 warns les plus récents si liste longue), callback qui met à jour le message en succès.
/casier : paramètre action (« Consulter le casier » / « Retirer un avertissement ») ; retrait sous permission unwarn, même hiérarchie que pour les sanctions (helper can_moderate_sanctions_target ; membre absent du serveur = pas de check de rôles).
/unwarn : même menu de retrait, permission unwarn ; ajout dans PUBLIC_MODERATION_COMMANDS (setup_server), /help, i18n global_i18n pour les nouvelles descriptions.
Impact visible / technique : redémarrage / resync des commandes slash pour voir /unwarn et l’option action sur /casier ; en /config serveur, la commande unwarn peut recevoir des rôles dédiés (sinon même défaut Admin/Support que les autres mod_cmds).
Contexte/problème : besoin d’un équivalent analytics Discord directement via slash commands (sans dépendre de salons logs), d’un suivi DM bot, et correction de blocage /roles quand un rôle cible est au-dessus du modérateur mais sous le bot.
Changements appliqués :
Nouveau AnalyticsHandler (bot/handlers/analytics_handler.py) avec stockage JSON (bot/data/analytics/server_analytics.json) : comptage messages (total, par heure, par jour, par user, par salon), joins (avec tentative de code d’invite), événements récents, DMs reçus par le bot.
Hook runtime : initialisation dans main.py; collecte message/DM dans events/on_message.py; collecte join/leave + cache invitations dans events/logs_events.py.
Nouvelle commande /analytique (bot/commands/analytique.py) avec vues : générale, activité messages (tops users/salons), comptes temporaires (regex + même jour création/rejoin), invitations (top codes utilisés).
Nouvelle commande /logs (bot/commands/logs.py) pour consulter les événements récents en embed ; vue spéciale DM bot réservée aux owners bot.
Permissions/config UX : ajout analytique + logs dans les commandes mod configurables (PUBLIC_MODERATION_COMMANDS), descriptions dans /help, ajouts i18n.
Correction /roles (bulk_roles_channels.py) : un utilisateur avec permission administrateur peut attribuer des rôles au-dessus de lui tant que le rôle reste sous le bot.
Impact visible / technique : après redémarrage/sync, les nouvelles commandes apparaissent ; les analytics sont progressifs (plus de données au fil des heures/jours) ; les DM au bot sont consultables depuis /logs type=dm (owner bot).
Contexte/problème : la section Mini-Jeux n’apparaissait pas dans le menu /config.
Changements appliqués :
ajout de l’option Mini-Jeux dans le sélecteur principal /config (SetupServerSelect) ;
ajout du routage de callback vers une vue dédiée (MiniGamesView) ;
ajout d’un embed Mini-Jeux (_create_mini_games_embed) avec état Compteur, Suite de mots, Pendu ;
ajout de la ligne d’état Mini-Jeux dans le résumé principal /config ;
ajout du bloc mini_games dans la config par défaut serveur (server_config_handler) avec paramètres de base (counter/word_chain/hangman).
Impact visible / technique : la section Mini-Jeux est maintenant visible et ouvrable dans /config ; base de configuration prête pour brancher les boutons/handlers de gameplay.
2026-05-07 19:13 — Site : permissions spécifiques séparées (annonces/actualités/contenus)
Contexte/problème : les grades existent, mais besoin de permissions fines et séparées (annonces, actualités, ressources, guides, boutique, ajout de contenu) pour éviter de dépendre systématiquement d’un admin.
Changements appliqués :
Dashboard utilisateurs : ajout d’une section UI Permissions spécifiques (séparées) avec toggles dédiés :
ajout de checkers dédiés (canPublishResourceDirectly, canPublishGuideDirectly, canPublishShopDirectly, canWriteAnnouncements, canWriteNews) ;
requiresGuideChangeModeration / requiresResourceChangeModeration reposent maintenant sur ces permissions dédiées (et non plus uniquement fondateur/admin).
Routes API reliées :
resources/add : publication directe autorisée si permission ressource dédiée.
shop/request : auto-approbation autorisée si permission boutique dédiée.
forum-social/thread/create : contrôle explicite pour catégories annonce / actualite avec permissions dédiées.
Impact visible / technique : les comptes peuvent recevoir des droits de publication ciblés sans monter en grade global ; publication directe possible selon permission sans intervention admin sur les flux concernés.
2026-05-07 19:15 — Refacto “permissions spécifiques” (propre, centralisée, sans dépendre des rôles)
Contexte/problème : besoin d’un système de permissions fines soigné (pas du cas par cas resources/add) et clairement utilisable pour accorder des privilèges sans attribuer un rôle global.
Changements appliqués :
création d’un catalogue central web/src/lib/specific-permissions.ts :
clés canoniques, labels, descriptions ;
normalisation d’alias historiques vers clés officielles ;
utilitaire de normalisation des tokens.
resource-permissions.ts branché sur cette normalisation :
permission globale add_content utilisée comme “master switch” contenu.
dashboard édition utilisateur : UI branchée sur le catalogue central (labels/desc cohérents, maintenance facilitée).
API update user (dashboard/users/[id]) : normalisation des permissions au save (évite doublons/slug incohérents en base).
Impact visible / technique : système plus lisible, stable et extensible ; les privilèges spécifiques restent décorrélés des rôles globaux et s’appliquent de manière uniforme sur les routes métier.
2026-05-07 19:23 — Site : auto-remplissage GitHub (nom associé + version) lors d’ajout de ressource
Contexte/problème : lors de l’ajout d’une ressource, saisir un lien GitHub devait automatiquement compléter le nom associé (auteur GitHub) et la version (dernière release), pour éviter les saisies manuelles.
Changements appliqués :
ajout d’une nouvelle route API GET /api/github/repo-info qui :
valide et normalise l’URL GitHub ;
récupère les infos dépôt (owner, repo) ;
récupère la dernière release exploitable pour proposer un tag de version.
auto-remplissage de auteur avec le login owner GitHub ;
auto-remplissage de version avec le dernier tag release ;
protection anti-écrasement : si l’admin modifie manuellement auteur/version, l’auto-remplissage ne force plus ces champs.
Impact visible / technique : UX plus rapide à la création de ressource ; cohérence des métadonnées GitHub améliorée ; comportement robuste en cas d’erreur/rate limit GitHub (pas de blocage du formulaire).
2026-05-07 19:25 — Traduction : conservation des emojis (profil/ressources/vente/forum)
Contexte/problème : lors d’une traduction (ex. anglais -> français), les emojis présents dans les descriptions pouvaient disparaître dans le texte traduit.
Changements appliqués :
mise à jour de web/src/lib/libretranslate.ts :
ajout d’un masquage des emojis via jetons stables avant l’appel API LibreTranslate ;
restauration des emojis après traduction ;
adaptation de la clé de cache pour prendre en compte la version masquée.
Impact visible / technique : les emojis sont désormais conservés dans les contenus traduits sur toutes les zones utilisant /api/translate (forum, blocs description profil/ressource, etc.).
Contexte/problème : l’URL profil basée uniquement sur le pseudo devient fragile quand un utilisateur change de pseudo.
Changements appliqués :
ajout d’un système d’identifiant public stable (public_id) via web/src/lib/user-public-id.ts :
création de colonne users.public_id (auto-ensure) + index unique ;
si compte lié Discord, public_id = discord_id (quand possible) ;
sinon génération d’un identifiant unique pour comptes non Discord.
hooks/auth :
web/src/lib/auth.ts : nouveaux comptes initialisés avec public_id (Discord ID ou ID généré) ;
liaison Discord existante : attribution de public_id si manquant.
web/src/lib/auth-helpers.ts : backfill public_id à la volée pour comptes existants sans identifiant.
API profil :
web/src/app/api/users/[id]/route.ts accepte désormais public_id en lookup (/api/users/{id|public_id|pseudo|id_interne}) ;
expose publicId dans la réponse.
URL canonique :
web/src/app/profil/page.tsx redirige automatiquement vers /profil/<pseudo-courant> si l’accès se fait via ID ou ancien pseudo.
Impact visible / technique : un lien type /profil/<id> continue de fonctionner même après changement de pseudo, puis se corrige automatiquement vers l’URL pseudo ; identifiant stable unifié pour nouveaux comptes et comptes historiques touchés.
2026-05-07 19:34 — Dashboard admin : affichage/copie de l’identifiant public (PID)
Contexte/problème : besoin d’exposer l’identifiant stable dans l’interface admin pour faciliter le support, la modération et le partage de liens robustes.
Changements appliqués :
web/src/app/api/dashboard/users/route.ts :
inclut public_id dans la liste utilisateurs ;
assure/backfill public_id à la volée pour chaque ligne (Discord ID prioritaire, sinon généré).
web/src/app/api/dashboard/users/[id]/route.ts :
accepte public_id dans la résolution utilisateur ;
retourne public_id dans la payload de détail.
web/src/app/dashboard/users/page.tsx :
affiche PID sous le pseudo (mobile + desktop) ;
ajoute un bouton de copie rapide (feedback visuel check).
web/src/app/dashboard/users/[id]/edit/page.tsx :
affiche PID dans l’en-tête fiche utilisateur ;
bouton de copie dédié avec feedback visuel.
Impact visible / technique : les admins disposent immédiatement d’un identifiant stable copiable, indépendant des changements de pseudo ; lookup API dashboard compatible avec cet identifiant.
Contexte/problème : besoin d’éviter la multiplication de commandes (/acces_retirer, variantes ajouter/retirer) et d’avoir un flux unique dans /acces, avec un retour visible (non éphémère) pour le destinataire.
Changements appliqués :
refonte de bot/commands/acces.py :
/acces accepte maintenant une option action (Ajouter / Retirer) ;
suppression de la commande dédiée /acces_retirer (logique intégrée dans /acces) ;
duree rendue optionnelle (par défaut illimite pour l’ajout).
messages de confirmation :
réponses d’ajout/retrait envoyées en non éphémère (visibles dans le salon), avec mention explicite de l’utilisateur concerné ;
erreurs de permission/validation conservées en éphémère.
Impact visible / technique : UX simplifiée côté modération propriétaire (une seule commande /acces) ; suppression des anciennes commandes d’ajout/retrait ; notification visible immédiatement par le destinataire dans le salon.
2026-05-07 19:39 — Bot /acces : notification en MP au destinataire (pas de message salon)
Contexte/problème : la confirmation ne devait pas être envoyée dans le salon, mais directement en message privé à l’utilisateur ciblé.
Changements appliqués :
bot/commands/acces.py :
ajout/retrait d’accès envoie désormais un embed en DM à l’utilisateur concerné ;
réponse au propriétaire conservée en éphémère avec statut d’envoi (MP envoyé / DM fermés).
Impact visible / technique : plus de notification publique dans le salon ; information transmise discrètement au destinataire en MP ; meilleur contrôle en cas de DMs fermés.
Contexte/problème : le déploiement bun run deploy:prod échouait sur Turbopack avec erreur defined multiple times dans src/app/api/forum-social/thread/create/route.ts.
Changements appliqués :
suppression d’une ligne d’import dupliquée (parseUserPermissions, canWriteAnnouncements, canWriteNews) dans la route de création de thread.
relance du build local (bun run build) pour validation.
Impact visible / technique : l’erreur bloquante de compilation est levée ; build terminé avec succès. Il reste uniquement des warnings NFT liés à next.config.js (non bloquants pour cette release).
2026-05-07 20:22 — Rôles support unifiés + menu permissions/plateformes en édition utilisateur
Contexte/problème : besoin de supprimer le rôle gestionnaire_actualite, fusionner les rôles support Xbox/PlayStation/Nintendo en un rôle unique support_technique, et disposer d’un menu permissions ergonomique comme les menus rôles/badges.
Changements appliqués :
web/src/app/dashboard/users/[id]/edit/page.tsx :
retrait des rôles support_xbox, support_playstation, support_nintendo, gestionnaire_actualite de la sélection manuelle ;
ajout du rôle unique support_technique ;
ajout d’une sous-section plateformes support (Xbox, PlayStation, Nintendo et variantes demandées) avec sélection dédiée ;
refonte de la section permissions spécifiques vers un menu déroulant multi-sélection (style menu rôles/badges).
alignement des mappings de synchro Discord vers le rôle unifié support :
les anciens tokens support_xbox, support_playstation, support_nintendo restent acceptés en compatibilité mais mappent désormais vers le rôle Discord support unique.
accès dashboard mis à jour pour reconnaître support_technique :
web/src/context/DashboardContext.tsx
web/src/components/layout/Header.tsx
web/src/components/dashboard/DashboardHeader.tsx
web/src/lib/dashboard-utils.ts
publication forum catégorie actualités adaptée :
web/src/components/forum/ForumThreadComposer.tsx
web/src/app/forum/preview/page.tsx
acceptation de post_news (avec compatibilité gestionnaire_actualite conservée).
style profil ajouté pour le nouveau token support_technique :
web/src/app/profil/page.tsx.
Impact visible / technique : l’admin assigne maintenant un rôle support unifié, peut détailler les plateformes dans une sous-section dédiée, et gère les permissions spécifiques via un menu plus propre ; la synchro Discord reste rétrocompatible pour les anciens comptes.
2026-05-07 20:30 — Forum Actualité : correction lecture permissions + Guides : toast discret à la soumission
Contexte/problème : un utilisateur avec permission de publication actualités ne pouvait pas sélectionner la catégorie Actualité sur le forum ; en parallèle, l’envoi de guide affichait encore des modales navigateur (alert) au lieu d’une notification discrète.
correction de la normalisation des permissions utilisateur ;
découpe des permissions CSV/; (ex: membres,post_news) en tokens exploitables avant vérification catégorie.
web/src/app/forum/preview/page.tsx :
même correction de normalisation côté preview forum.
web/src/app/guides/ajouter/page.tsx :
remplacement des alert(...) par des toasts via useToast();
message discret de succès lors d’une soumission en attente de validation admin ;
redirection légèrement différée pour laisser le toast visible.
Impact visible / technique : la catégorie Actualité devient sélectionnable dès que la permission est réellement présente dans les tokens ; l’expérience d’envoi guide est modernisée avec notifications discrètes en bas d’écran (plus de popup navigateur bloquant).
2026-05-07 20:37 — Remplacement complémentaire des alert(...) par toasts discrets (UX)
Contexte/problème : plusieurs écrans utilisaient encore des modales navigateur bloquantes (alert) au lieu de notifications discrètes cohérentes avec l’UI.
2026-05-07 20:35 (UTC+2) — Fin du nettoyage global alert(...) → toasts (dont chat)
Contexte/problème : il restait encore des alert(...) bloquants sur plusieurs écrans majeurs, surtout chat, ce qui cassait la cohérence UX des notifications discrètes.
Changements appliqués :
conversion complète des alert(...) en toasts sur les écrans restants :
web/src/app/chat/page.tsx
web/src/app/checker/components/Checkers.tsx
web/src/app/dashboard/services/page.tsx
web/src/app/projects/[slug]/page.tsx
web/src/components/chat/VoiceChannel.tsx
ajout/intégration du hook toast (useToast) là où nécessaire ;
adaptation des appels avec niveaux (error/success/info) via wrappers typés compatibles avec l’API toast interne.
vérification de lint sur tous les fichiers modifiés (aucune erreur restante).
Impact visible / technique : plus de popup navigateur native dans le front (web/src) ; feedback utilisateur homogène en bas d’écran sur erreurs/succès/copie/actions de modération ; base UI plus stable pour les prochaines itérations.
Contexte/problème : besoin de transformer la section “Vérification” en “Outils” avec onglets dédiés, d’ajouter un convertisseur PSN→Base64 (site + bot), de corriger le rendu OpenGraph des ressources (markdown Discord inadapté + priorité média), d’ajuster la logique des permissions de publication sans validation, et de fiabiliser l’affichage avatar auteur sur certaines ressources.
ajout d’un outil PSN Base64 côté web avec deux sorties :
Version Simple (8 premiers caractères, padding \0, base64),
Version Unique (Recommandée) (SHA256, 8 premiers bytes, base64).
Commande bot /psn-base64 :
ajout d’une fonction Python build_psn_base64_versions() (base64 + sha256 digest tronqué) ;
nouvelle slash commande /psn-base64 avec embed :
titre PSN Account-ID,
champs Version Simple et Version Unique (Recommandée) en bloc code.
OpenGraph ressources :
abandon du format markdown Discord dans la description OG, remplacé par un bloc texte propre ;
priorité de l’image OG au média/rendu (incluant miniatures YouTube déjà calculées côté add), fallback logo ;
ajout de l’icône (logo/site) via icons metadata pour favoriser le petit visuel de carte ;
twitter:card passe en summary_large_image seulement si média dispo, sinon summary.
Permissions publication sans validation :
publish_resources, publish_guides, publish_shop restent les permissions spécifiques de bypass ;
retrait du bypass implicite via add_content pour ressources/guides/boutique ;
clarification du catalogue permissions : add_content devient permission héritée de création, sans auto-publication.
Avatar auteur ressource :
fallback avatar créateur amélioré (profil_auteur puis author_photo) pour éviter les cartes auteur sans image (cas type RaiDougie SPRX).
Impact visible / technique : navigation alignée avec “Outils”, checker plus clair par onglets, convertisseur PSN disponible sur site et Discord, embeds ressources plus robustes pour Discord/OpenGraph, permissions conformes au flux “tout le monde soumet / certains publient direct”, et affichage avatar auteur plus fiable.
2026-05-08 07:20 (UTC) — Embed Discord ressources : thumbnail/logo prioritaire + image large média
Contexte/problème : sur Discord, certains embeds “nouvelle ressource” affichaient l’avatar auteur en miniature au lieu du logo ressource, ce qui contredisait le rendu souhaité (logo en petit visuel, média/YT en grand visuel).
priorité forcée du thumbnail sur icon_url (logo ressource) ;
fallback sur author_avatar uniquement si logo absent ;
conservation de image_url pour la grande image (rendu/image ou miniatura YouTube).
Impact visible / technique : les prochains embeds Discord “nouvelle ressource” respectent le pattern demandé : logo en miniature et média principal en image large.
2026-05-08 07:31 (UTC) — Harmonisation embeds site (guides + boutique) : miniature/logo + image large
Contexte/problème : nécessité d’aligner les embeds “site” sur le même rendu visuel que les ressources (logo en thumbnail, média en grande image) pour guides et boutique.
Changements appliqués :
bot/handlers/flask_handler.py :
extension de _send_site_embed(...) avec support image_url en plus de thumbnail_url ;
application du rendu image large si URL distincte de la miniature ;
site_new_guide et site_new_shop câblés sur ce nouveau format.
web/src/app/api/guides/create/route.ts :
enrichissement de la commande Discord site_new_guide avec icon_url/image_url récupérés à partir de la première ressource liée (si présente) ;
conversion vers URLs absolues (/api/uploads/...) pour compatibilité Discord.
web/src/app/api/shop/request/route.ts :
enrichissement de la commande Discord site_new_shop avec image_url (priorité miniature YouTube, sinon galerie, sinon bannière) ;
conversion des assets (icon_url/images) en URLs absolues pour embeds Discord.
Impact visible / technique : les prochains embeds guides et boutique suivent la même logique visuelle que les ressources (logo en petit visuel + rendu média en grand visuel), avec des URLs directement exploitables par Discord.
Contexte/problème : besoin de n’afficher que la version unique pour PSN Account-ID, de passer la navigation sur une URL explicite /outils, et d’utiliser une icône “outils” dans la navbar.
Changements appliqués :
web/src/app/checker/components/Checkers.tsx :
retrait de l’affichage Version Simple dans l’outil PSN Account-ID ;
rendu conservé uniquement sur Version Unique (Recommandée).
bot/commands/utility.py :
embed /psn-base64 simplifié pour afficher uniquement Version Unique (Recommandée).
web/src/components/layout/Header.tsx :
lien navbar “Outils” redirigé vers /outils ;
icône remplacée par une icône dédiée outils (Wrench).
web/src/app/outils/page.tsx :
ajout de la route /outils (pointant sur la page outils actuelle).
Impact visible / technique : la section Outils est accessible via /outils depuis la navbar avec une icône adaptée, et la conversion PSN Account-ID n’expose plus la version simple sur le site et le bot.
Contexte/problème : malgré l’URL /outils, le contenu de page conservait un wording orienté “checker”, et l’onglet navigateur n’avait pas de metadata dédiée à la section Outils.
Changements appliqués :
web/messages/fr.json :
adaptation des textes de la section checker vers un wording “Outils” (badge, heroTitle, heroSubtitle, footerTitle, footerBody) ;
ajout de sitePages.outils.metaTitle et sitePages.outils.metaDescription.
web/messages/en.json :
mêmes ajustements de wording et ajout des clés metadata sitePages.outils.
web/src/app/outils/layout.tsx :
ajout d’un layout avec generateMetadata() pour définir le titre/description (OpenGraph + Twitter) spécifiques à /outils.
Impact visible / technique : la page /outils affiche désormais un intitulé cohérent “Outils” dans son hero et son bloc bas de page, et l’onglet navigateur utilise un titre/description propres à cette section.
Contexte/problème : la section "Codes d'erreur" ne couvrait que la PS5, alors que le besoin etait d'inclure aussi PS4, PS3 et Xbox 360 avec une lecture des codes et des pistes de resolution.
ajout des checkers PS4ErrorChecker, PS3ErrorChecker, Xbox360ErrorChecker.
web/src/app/checker/page.tsx :
integration des 3 nouvelles cartes dans l'onglet Codes d'erreur ;
conservation des checkers de compatibilite dans l'onglet dedie via filtrage d'IDs.
Ajout de bases locales versionnees :
web/public/json/ps4-error-codes.json
web/public/json/ps3-error-codes.json
web/public/json/xbox360-error-codes.json
chaque entree contient code, description, solution.
web/messages/fr.json et web/messages/en.json :
ajout des libelles de cartes, placeholders, etat de chargement, messages "introuvable", titre d'erreur par console, et label solution.
Impact visible / technique : l'onglet erreurs devient multi-consoles (PS5/PS4/PS3/Xbox 360) avec consultation immediate de codes et solutions de base, extensible simplement par enrichissement des JSON publics.
2026-05-08 09:58 (UTC+2) — Script de synchronisation base erreurs (PS4 officiel)
Contexte/problème : besoin d'une base "vivante" des codes d'erreur avec mise a jour automatique depuis une source fiable, plutot qu'un simple JSON statique maintenu a la main.
Changements appliqués :
ajout du script web/scripts/sync-error-codes.mjs :
telechargement de la page officielle PS4 (playstation.com/.../error-codes/ps4) ;
parsing automatique des codes + descriptions ;
generation de solutions generiques par famille de code (NW, SU, WS, etc.) ;
merge intelligent avec la base locale (conservation des solutions/overrides manuels existants) ;
ecriture de web/public/json/ps4-error-codes.json avec metadata de source et horodatage.
ajout d'une commande npm :
web/package.json -> sync:error-codes pour lancer la synchro en une commande.
execution de la synchro :
resultat: parsed=215, written=216 dans la base PS4 locale.
Impact visible / technique : la base PS4 peut maintenant etre maintenue automatiquement depuis la source officielle tout en preservant les enrichissements locaux ; l'equipe peut rafraichir les codes via npm run sync:error-codes.
2026-05-08 10:40 (UTC+2) — Ressources OpenGraph: format description style Discohook (# [Titre](url) + meta en code inline)
Contexte/problème : demande d'aligner le rendu OpenGraph des ressources sur un style Discord/Discohook avec titre cliquable dans la description et ligne meta visuelle en code inline.
Changements appliqués :
web/src/lib/og-lbxmb.ts :
adaptation de buildOgResourceDiscordDescription(...) pour generer :
# [Titre](url) quand l'URL est disponible ;
ligne meta en inline code : ` Ressource / ... ` ;
description nettoyee en texte simple (sans heading/bloc code additionnel).
web/src/app/ressources/[slug]/layout.tsx :
bascule de la generation de description OG vers buildOgResourceDiscordDescription(...) ;
passage de l'URL ressource canonique a la fonction pour produire un titre cliquable.
Impact visible / technique : les embeds de ressources utilisent maintenant un format markdown plus proche de ton usage Discohook dans la description ; rendu plus propre avec titre-lien et ligne meta sur fond code (selon support client Discord).
2026-05-08 11:06 (UTC+2) — Outils PSN: retrait "(Recommandée)" + URL directe des onglets via query param
Contexte/problème : demande de simplifier le libelle PSN Base64 ("Version Unique" sans suffixe) et de pouvoir ouvrir directement chaque onglet Outils via une URL dediee.
Changements appliqués :
web/messages/fr.json et web/messages/en.json :
mise a jour du libelle PSN Base64 : suppression de "(Recommandée)/(Recommended)".
web/src/app/checker/page.tsx :
ajout de la lecture du parametre URL tab (compatibility, errors, psn) ;
synchronisation de l'etat d'onglet avec l'URL ;
mise a jour de l'URL a chaque changement d'onglet (sans reload complet) via router.replace.
Impact visible / technique : l'outil PSN affiche maintenant "Version Unique" uniquement ; chaque onglet est partageable et accessible en lien direct (ex: ?tab=psn, ?tab=errors).
2026-05-08 11:18 (UTC+2) — OpenGraph ressources: gestion propre des videos pour la grande image Discord
Contexte/problème : verification du rendu OpenGraph Discord quand une ressource contient une video ; Discord attend une image pour og:image (pas un flux video brut).
Changements appliqués :
web/src/app/ressources/[slug]/layout.tsx :
ajout du support video_youtube dans la generation metadata ;
priorite a la miniature YouTube (img.youtube.com/.../maxresdefault.jpg) comme grande image OG quand un lien YouTube est present ;
filtrage des medias media_rendu/medias pour ne garder que les formats image (ignore les .mp4, .webm, etc.) ;
fallback maintenu vers logo/site icon si aucune image exploitable.
Impact visible / technique : les liens ressources avec video YouTube obtiennent une grande image stable dans l'embed Discord ; les medias video locaux ne cassent plus le summary_large_image car seuls les medias image sont utilises pour og:image.
Contexte/problème : besoin d'un rendu Discord plus propre et homogene pour les ressources, avec focus visuel sur le titre-lien, la meta lisible et une description compacte.
Changements appliqués :
web/src/lib/og-lbxmb.ts :
ajustement du format OG ressources pour inclure la description en bloc code, tout en gardant :
Contexte/problème : le deploy etait bloque par une erreur TypeScript sur buildOgResourceDiscordDescription(...) (signature mise a jour, appel legacy dans la route /resources/[id]), et un lien direct vers un .apk retournait 404.
Changements appliqués :
web/src/app/resources/[id]/layout.tsx :
alignement de l'appel buildOgResourceDiscordDescription(...) avec la nouvelle signature (ajout de resourceUrl) ;
reutilisation de resourceUrl dans openGraph.url.
verification stockage uploads :
controle du dossier /root/lbxmb.fr/uploads/Ressources/PS4/Tools/Chiaki : seul chiaki.nro present ;
aucun fichier Chiaki-v2.2.0-Android.apk trouve sur le serveur (d'ou le 404).
Impact visible / technique : build debloque cote TypeScript pour ce point ; le 404 du lien .apk est confirme comme un fichier manquant (pas un bug route OpenGraph).
2026-05-08 11:54 (UTC+2) — Audit automatique des liens Chiaki (detection de liens morts)
Contexte/problème : besoin d'un scan rapide et repetable pour detecter les liens telechargement morts des ressources Chiaki, au lieu d'un controle manuel URL par URL.
Changements appliqués :
ajout du script web/scripts/audit-chiaki-links.mjs :
recupere les ressources ciblees (nom/description/dossier_ressource contenant chiaki) ;
inspecte les chemins de telechargement (DB, telechargements.json, variantes via /api/resources/{id}) ;
verifie l'existence physique des fichiers dans /root/lbxmb.fr/uploads ;
genere un rapport JSON : web/scripts/audit-chiaki-links-report.json.
ajout de la commande npm :
web/package.json -> audit:chiaki-links.
execution du scan :
1 ressource inspectee, 1 ressource avec liens morts ;
liens manquants detectes sur variantes Windows, Linux, MacOS, Android de Chiaki.
Impact visible / technique : diagnostic des 404 Chiaki automatisé et reproductible ; permet de lister immediatement les fichiers manquants a uploader/corriger.
2026-05-08 12:18 (UTC+2) — /analytique temp_accounts : grille de risque + bouton liste utilisateurs
Contexte/problème : besoin de qualifier rapidement si les ratios comptes suspects sont "normaux" et d'obtenir un accès direct a la liste des utilisateurs concernes.
Changements appliqués :
bot/handlers/analytics_handler.py :
enrichissement du resume statistique avec les IDs :
temp_account_ids
same_day_account_ids.
bot/commands/analytique.py :
ajout d'une grille de seuils visuelle (vert/orange/rouge) :
regex pseudo : avertissement 3%, critique 7%
cree+rejoint meme jour : avertissement 8%, critique 12%
ajout d'un bouton interactif Afficher la liste des utilisateurs ;
envoi d'un embed detaille listant les membres detectes dans chaque categorie.
Impact visible / technique : la vue temp_accounts devient actionnable (lecture du niveau de risque + drill-down immediat des comptes), sans quitter Discord.
Contexte/problème : les uploads de variantes (Chiaki) provoquaient des 500 pendant l'edition car le backend stoppait toute sauvegarde si une ancienne variante pointait vers un fichier absent (Fichier variante introuvable).
Changements appliqués :
web/src/lib/execute-dashboard-resource-put.ts :
dans le traitement des variantes, si node.chemin est une URL HTTP(S), conservation directe sans copie locale ;
si un ancien chemin local est introuvable, conversion du throw en warning non bloquant et conservation des metadonnees du noeud ;
permet une reparation progressive (upload variante par variante) sans echec global.
verification:
build Next.js valide (npm run build OK).
Impact visible / technique : l'UI VariantEditor ne tombe plus en erreur 500 a chaque ajout de fichier lorsqu'il existe deja des variantes orphelines; les modifications peuvent etre enregistrees incrementalement.
2026-05-08 13:07 (UTC+2) — Traduction FR complete de la page Outils
Contexte/problème : la section checker en français contenait encore plusieurs libellés anglais (sous-titres de cartes, intitulés PSN, textes techniques), ce qui donnait une interface mixte FR/EN.
Contexte/problème : besoin d'un onglet dedie pour verifier rapidement numero/mail/domaine (risque + appartenance), et demande d'afficher plus d'infos exploitables pour l'Account-ID PSN (Apollo/Chiaki).
Changements appliqués :
web/src/app/checker/components/Checkers.tsx :
enrichissement du tool PSN avec derivees supplementaires :
Account-ID hexadecimal (0x...)
Account-ID decimal
pseudo normalise + longueur
ajout d'un nouveau composant SafetyIntelTool :
detection du type d'entree (telephone, email, domaine/url)
normalisation de la valeur
score de risque heuristique (faible/moyen/eleve)
tentative de recuperation d'infos de proprietaire/registrar/pays via RDAP.
web/src/app/checker/page.tsx :
ajout d'un 4e onglet tab=safety dans la page /outils
nouvelle section Vérification anti-arnaque avec la carte Analyse rapide anti-arnaque.
web/messages/fr.json et web/messages/en.json :
ajout complet des labels/traductions pour l'onglet sécurité et les nouveaux champs PSN.
Impact visible / technique : la page Outils couvre maintenant un usage anti-phishing de premier niveau (SMS/mail/liens) et fournit des informations PSN plus utiles pour les workflows jailbreak (Apollo/Chiaki).
2026-05-08 13:16 (UTC+2) — Enrichissement base codes erreur PS3 depuis source communautaire
Contexte/problème : besoin d'etendre la couverture des codes PS3 dans l'onglet Outils a partir de la page partagee (ps3tuto.com/codeserreur.php), la base locale etant trop courte.
Changements appliqués :
web/public/json/ps3-error-codes.json :
ajout d'un lot important de codes PS3 manquants (reseau, PSN, mise a jour, disque, stockage, affichage, NAT, DNS) ;
normalisation des descriptions/solutions en format court et actionnable ;
conservation des codes deja presents et dedoublonnage final.
Impact visible / technique : le checker PS3 retourne des resultats sur beaucoup plus de cas concrets, avec des conseils immediats de depannage.
Impact visible / technique : les releases GitHub peuvent maintenant se mettre a jour automatiquement a intervalle raisonnable, sans depender uniquement des modifications manuelles de ressources.
2026-05-08 15:31 (UTC+2) — Ban IP compatible Cloudflare proxied (extraction + blocage global + API admin)
Contexte/problème : avec Cloudflare en mode proxied, l'IP client reelle doit etre lue via les headers Cloudflare; besoin de bannir efficacement une IP depuis le site.
Changements appliqués :
ajout de web/src/lib/client-ip.ts :
extraction/normalisation IP robuste (CF-Connecting-IP, True-Client-IP, X-Forwarded-For, X-Real-IP) ;
normalisation IPv6 mapped (::ffff:) et localhost.
ajout de web/src/lib/ip-bans.ts :
creation auto de la table ip_bans ;
helpers banIp, unbanIp, isIpBanned.
ajout de web/src/app/api/internal/ip-ban/check/route.ts :
endpoint interne securise par IP_BAN_INTERNAL_SECRET pour verifier si une IP est bannie.
ajout de web/src/app/api/admin/ip-bans/route.ts :
GET liste des bans ;
POST ban/maj ban ;
DELETE unban ;
acces restreint aux comptes staff eleves (admin/fondateur).
mise a jour de web/src/proxy.ts :
verification ban IP sur toutes les routes dynamiques (pages + API) via endpoint interne ;
reponse 403 immediate si IP bannie ;
garde-fous anti-boucle (x-ip-ban-check) et fail-open en cas d'erreur interne.
mise a jour de web/src/lib/rate-limit.ts et web/src/app/api/resources/[id]/download/route.ts :
harmonisation de l'extraction IP avec priorite Cloudflare.
Impact visible / technique : bannissement IP possible depuis le back-office API tout en conservant l'IP reelle client derriere Cloudflare; blocage applique en amont sur la navigation et les appels API du site.
2026-05-08 15:45 (UTC+2) — Tracking IP visiteurs + ip-api + association compte + blocage VPN/Datacenter
Contexte/problème : besoin de journaliser les IP dès la visite anonyme, scanner seulement les nouvelles IP via ip-api, associer les IP au compte à la connexion/création, bloquer IP bannies et IP VPN/proxy/datacenter, puis afficher les IP utilisées dans la fiche utilisateur dashboard.
Changements appliqués :
ajout de web/src/lib/ip-tracking.ts :
creation auto des tables ip_intel, ip_visits, user_ips ;
scan ip-api uniquement pour IP nouvelles publiques ;
enregistrement des visites anonymes (visitor_token) ;
association visiteur -> utilisateur ;
verification de blocage (IP bannie, VPN/Proxy, Datacenter/Hosting).
ajout de web/src/app/api/internal/ip-track/hit/route.ts :
endpoint interne securise (IP_TRACK_INTERNAL_SECRET) pour log visite + scan.
mise a jour de web/src/proxy.ts :
creation/maintien d'un cookie visitor_token ;
envoi asynchrone d'un hit de tracking a chaque visite ;
blocage global 403 via verif interne (IP bannie + flags ip-api).
mise a jour de web/src/app/api/auth/login/route.ts :
utilisation du nouveau moteur de controle IP ;
enregistrement IP de connexion et association au compte.
mise a jour de web/src/lib/auth-helpers.ts :
association IP -> utilisateur sur sessions authentifiees (y compris comptes crees automatiquement via BetterAuth).
mise a jour de web/src/app/api/dashboard/users/[id]/ips/route.ts :
suppression de l'ancien decrypt shell Python ;
retour des IP + infos ip-api (pays, ville, ISP, proxy/hosting, date scan).
mise a jour de web/src/app/dashboard/users/[id]/edit/page.tsx :
affichage enrichi des infos IP (geoloc/ISP/flag risque).
Impact visible / technique : tracking IP complet et coherent (anonyme -> compte), scans ip-api optimises (uniquement nouvelles IP), blocage automatique des IP a risque, et meilleure visibilité moderation dans le dashboard utilisateur.
2026-05-08 15:58 (UTC+2) — Sauvegarde disaster-recovery: dump MariaDB complet intégré aux archives
Contexte/problème : les backups existants ne garantissaient pas la restauration complete des donnees applicatives (users, guides, etc.) car la base MariaDB n'etait pas incluse dans l'archive principale.
Changements appliqués :
web/src/app/api/dashboard/backups/route.ts :
ajout d'un dump MariaDB complet (mysqldump puis fallback mariadb-dump) lors de la creation d'un backup dashboard ;
injection d'un dossier backup-meta/ dans l'archive contenant :
mariadb.sql
RESTORE-QUICKSTART.md (commandes de reprise rapide) ;
conservation de l'exclusion uploads/Ressources pour eviter l'explosion de taille (50Go+).
scripts/backup_web.sh :
passage d'un backup "web seul" a un backup complet projet + dump MariaDB ;
ajout de backup-meta/mariadb.sql + guide de restauration dans l'archive cron ;
retention ajustee sur le nouveau pattern lbxmb_backup_*.tar.gz.
Impact visible / technique : en cas de crash serveur, une archive suffit a remettre l'application et la base en ligne rapidement (hors gros assets uploads/Ressources volontairement exclus).
Contexte/problème : la route de téléchargement de backup était directement exploitable via URL si la session était active ; besoin d'un verrou supplémentaire côté serveur.
ajout d'un nonce stocké en Redis (backup-dl:*) pour invalider le lien après 1 utilisation ;
refus explicite sans token / token expiré / token réutilisé.
web/src/app/dashboard/sauvegardes/page.tsx :
le bouton Télécharger passe par la génération du lien temporaire avant ouverture.
Impact visible / technique : même en connaissant la route de fichier, un téléchargement n'est plus possible sans jeton court, signé, lié à l'utilisateur et consommé une seule fois.
Contexte/problème : le déploiement bun run deploy:prod échouait sur plusieurs routes API car getClientIp n'était plus exporté depuis src/lib/rate-limit.ts alors que ces routes l'importaient encore.
Changements appliqués :
web/src/lib/rate-limit.ts :
réintroduction d'un export getClientIp(request) en compatibilité ;
délégation vers le helper central src/lib/client-ip.ts.
Impact visible / technique : build Next.js de production à nouveau valide sans casser les imports existants; pas de régression fonctionnelle attendue.
Contexte/problème : la bannière Discord envoyée par le bot pouvait rester figée sur un ancien rendu (capture de page non rafraîchie) malgré les mises à jour.
Changements appliqués :
banner_generator.py :
ajout d'un cache-buster _banner_ts dans l'URL de capture ;
passage de page.goto(..., wait_until="domcontentloaded") à wait_until="networkidle" pour attendre un rendu plus stable.
bot/handlers/banner_update_handler.py :
ajout d'un cache-buster _bot_ts appliqué aux URLs banner_url et logo_url avant génération Playwright.
Impact visible / technique : chaque update de bannière force désormais un rendu frais (anti-cache CDN/navigateur), ce qui évite l'envoi répétitif d'un ancien screenshot.
Contexte/problème : l'affichage dashboard montrait souvent uniquement l'IPv6 cliente alors que l'objectif est de conserver les deux familles d'adresses quand elles sont présentes.
Changements appliqués :
web/src/lib/client-ip.ts :
ajout de getClientIps(request) pour extraire toutes les IP utiles (CF, True-Client-IP, X-Forwarded-For, X-Real-IP) ;
getClientIp devient un fallback sur la première IP.
web/src/lib/ip-tracking.ts :
ajout de recordIpVisits(...) pour journaliser un lot d'IP (dedup) ;
ajout de checkIpSecurityBlockMany(...) pour valider toutes les IP candidates.
web/src/proxy.ts :
envoi des IP multiples (ips) au hit interne de tracking.
web/src/app/api/internal/ip-track/hit/route.ts :
support du payload ips[] + compat ip unique.
web/src/app/api/auth/login/route.ts :
contrôle sécurité sur toutes les IP détectées ;
enregistrement en base des IP multiples à la connexion.
web/src/lib/auth-helpers.ts :
association session -> utilisateur avec enregistrement des IP multiples.
Impact visible / technique : la fiche utilisateur peut désormais contenir les deux adresses (IPv4 + IPv6) lorsqu'elles transitent dans les headers, avec un tracking cohérent anonyme puis authentifié.
2026-05-08 16:40 (UTC+2) — Filtrage IP locales/privées (suppression bruit 127.0.0.1)
Contexte/problème : la section dashboard des IP affichait des adresses locales (127.0.0.1) issues de trafic interne, ce qui polluait l'historique utile moderation.
Changements appliqués :
web/src/lib/ip-tracking.ts :
arrêt de l'enregistrement des IP locales/privées dans recordIpVisit ;
filtre identique appliqué lors de l'association ip_visits -> user_ips.
Impact visible / technique : la liste "Adresses IP de connexion" n'affiche plus les adresses internes parasites et reste centrée sur les IP clientes publiques réellement exploitables.
Contexte/problème : la bannière ne se mettait pas à jour de façon fiable car banner_config.json contenait plusieurs entrées pour un même guild_id (certaines anciennes en /presentation), ce qui pouvait réinjecter un rendu obsolète selon le flux d'update.
Changements appliqués :
bot/handlers/banner_update_handler.py :
ajout d'un dédoublonnage automatique au chargement (_dedupe_servers) ;
ajout d'un score de priorité (_score_server_config) pour conserver la meilleure entrée par serveur (auto-update actif + URL /discord/banner/ priorisée) ;
fusion des doublons en conservant les attributs utiles et union logique de auto_update.
Impact visible / technique : une seule configuration cohérente par serveur est désormais utilisée, ce qui stabilise les mises à jour de bannière et évite le retour involontaire à des URLs legacy.
Contexte/problème : le menu utilisateur exposait "Mes commandes" alors que le besoin est une entrée unique "Espace Client" regroupant achats, ventes, services, facturation et éléments en attente, avec traduction complète.
Changements appliqués :
web/src/components/layout/ProfileMenuSheet.tsx :
remplacement de l'entrée "Mes commandes" par "Espace Client" pointant vers /espace-client.
web/src/components/layout/Header.tsx :
remplacement du lien dropdown "Mes commandes" par "Espace Client" ;
retrait de l'entrée dédiée "Mes services" dans le dropdown pour éviter les doublons de navigation.
web/src/app/espace-client/page.tsx (nouveau) :
création d'une page centralisée avec KPI + onglets : achats, ventes, services, facturation, en attente ;
agrégation des données via /api/mes-commandes et /api/mes-services ;
ajout d'ouverture du portail Stripe par service via /api/services/portal.
web/messages/fr.json et web/messages/en.json :
ajout des clés i18n complètes pour le menu profil et toute la page clientSpace.
web/src/lib/ticket-notifications.ts :
mise à jour du lien vendeur vers /espace-client (anciennement /mes-commandes).
Impact visible / technique : navigation utilisateur simplifiée (une seule entrée), nouvelle page client plus lisible et transversale, cohérence multilingue FR/EN sur tous les labels de l'espace client.
2026-05-08 17:12 (UTC+2) — Correctif build TypeScript après refonte menu profil
Contexte/problème : next build échouait sur ProfileMenuSheet.tsx avec Property 'requireMesServices' does not exist on type ... après remplacement de "Mes commandes" par "Espace Client".
Changements appliqués :
web/src/components/layout/ProfileMenuSheet.tsx :
suppression de la prop hasMesServicesAccess devenue inutile ;
suppression du filtre item.requireMesServices obsolète.
web/src/components/layout/Header.tsx :
retrait de l'argument hasMesServicesAccess passé à ProfileMenuSheet ;
suppression du helper local hasMesServicesAccess désormais inutilisé.
Impact visible / technique : correction du blocage TypeScript, compilation rétablie sur la partie menu profil.
2026-05-08 17:24 (UTC+2) — Bot /script: option Avantages + traduction publique avec reset FR 2 min
Contexte/problème : besoin d’un nouveau script "Avantages" sans embed, avec le même menu de traduction que l’accueil, et retour automatique au français après 2 minutes. Le libellé de traduction devait aussi devenir plus discret (-#).
publication du message simple avec une vue publique de traduction.
bot/views/script_panel_view.py :
extension du mode "traduction publique" à welcome et advantages ;
ajout d’un reset automatique en français après 120 secondes (annule/remplace les timers précédents sur le même message) ;
ajout de ScriptPublicTranslateView (menu déroulant uniquement) pour les messages publics hors accueil ;
mise à jour du préfixe des traductions éphémères en format discret -# Traduction (...).
Impact visible / technique : les messages publics traduits reviennent automatiquement au français au bout de 2 minutes (accueil + avantages), nouvelle commande prête pour publication des avantages VIP, et rendu des en-têtes de traduction allégé pour les réponses privées.
2026-05-08 17:33 (UTC+2) — Confirmation de traduction multilingue (message staff)
Contexte/problème : le feedback après traduction publique restait en français fixe, ce qui était incohérent lorsque l’utilisateur venait de sélectionner une autre langue.
Changements appliqués :
bot/views/script_panel_view.py :
ajout d’un mapping de messages de succès par langue ;
remplacement du texte fixe par un message localisé selon la langue cible.
Impact visible / technique : la confirmation de traduction est maintenant renvoyée dans la langue choisie, avec la mention de retour automatique en français dans 2 minutes.
Contexte/problème : le fallback des notifications vidéo affichait un rendu brut (UC..., YOUTUBE) peu lisible, et l’édition d’un suivi existant ne permettait pas de modifier le message personnalisé.
Changements appliqués :
bot/handlers/videos_notifications_handler.py :
enrichissement du parsing RSS YouTube avec channel_name (nom de chaîne réel) ;
amélioration du message par défaut (format lisible : plateforme, chaîne, titre, lien) ;
ajout de variables template supplémentaires : {channel_name} et {platform_name}.
bot/commands/setup_server.py :
mise à jour du placeholder de template vidéo (exemple plus propre) ;
ajout du champ Message personnalisé (optionnel) dans le modal Modifier une Vidéo ;
sauvegarde du custom_message lors de l’édition d’un suivi existant.
Impact visible / technique : les notifications sont désormais lisibles même sans template perso, et chaque suivi vidéo peut être ajusté proprement après création sans devoir supprimer/recréer l’abonnement.
Contexte/problème : la traduction via clic droit Discord pouvait rendre un résultat trop littéral (“mot à mot”), avec une fluidité insuffisante.
Changements appliqués :
bot/handlers/translation_handler.py :
ajout d’un moteur prioritaire DeepL (si DEEPL_API_KEY est configurée) pour une traduction plus naturelle ;
fallback automatique vers LibreTranslate en cas d’indisponibilité/erreur ;
application de la même stratégie sur textes courts et textes découpés.
Impact visible / technique : les commandes de traduction existantes (dont clic droit “Traduire ce message”) conservent la même UX, mais produisent des sorties plus fluides dès qu’une clé DeepL est fournie ; sans clé, comportement précédent conservé via LibreTranslate.
Contexte/problème : certaines visites anonymes n’étaient pas enregistrées quand la route interne de tracking rejetait la requête (secret interne non injecté côté runtime/proxy).
Changements appliqués :
web/src/app/api/internal/ip-track/hit/route.ts :
conservation du mode sécurisé standard (secret requis quand configuré) ;
ajout d’un fallback contrôlé si IP_TRACK_INTERNAL_SECRET est absent : autorisation uniquement des appels marqués internes (x-ip-track-hit: 1) pour éviter la perte totale de tracking.
Impact visible / technique : le tracking des visites anonymes continue de fonctionner même en cas de défaut d’injection d’env, tout en gardant le contrôle strict par secret dès qu’il est présent.
Contexte/problème : l’application n’était plus reconnue comme installable (Chrome : pas d’invite « installer » / comportement type raccourci navigateur) ; sur le forum un clic sur une image ouvrait souvent un nouvel onglet ou téléchargeait plutôt qu’un aperçu confortable sur la page.
Changements appliqués :
PWA :
web/public/sw.js : ajout d’un listener fetch (proxy réseau minimal) — condition courante d’éligibilité à l’installation côté Chromium.
web/public/manifest.json : orientation passée à any (meilleur sur desktop) ; entrées d’icônes 512x512 explicites.
web/src/components/ServiceWorkerRegistration.tsx : bump APP_VERSION → 2.1.4 pour forcer prise en compte du nouveau SW après déploiement.
web/src/lib/markdown.ts : prise en charge  avec classe discord-inline-image.
web/src/components/forum/MarkdownRenderer.tsx : clic sur images inline → même modale.
web/src/app/forum/thread/[id]/page.tsx, web/src/app/forum/g/[slug]/page.tsx + variantes preview/* : pièces jointes image branchées sur le lightbox.
i18n : clés forum.imageLightbox* dans web/messages/*.json (locales du site).
Impact visible / technique : après déploiement + un rechargement (ou passage par la mécanique de version SW), Chrome/Edge peuvent proposer l’installation en mode app autonome ; les images de posts s’ouvrent en grand sur le site, avec téléchargement optionnel depuis la modale.
Contexte/problème : faire jouer correctement URLs YouTube / YouTube Music en vocal avec une base « propre » (file, erreurs utilisateur), sans passer par Lavalink pour rester mono-processus Python.
Changements appliqués :
bot/handlers/music_handler.py :
normalisation des URLs (+ YouTube Music) ; playlists uniquement youtube.com/playlist?list= avec cap MUSIC_MAX_PLAYLIST_TRACKS (défaut 25) ;
sémaphore d’extractions concurrentes (MUSIC_MAX_CONCURRENT_EXTRACTS) et timeouts configurables ;
sélection d’URL audio (pick_audio_stream_url) + boucle dans play_next qui ignore les entrées sans flux (plus de récursion infinie sur URL vide) ;
previous : ré-injection depuis l’historique des titres terminés + stop → play_next ;
fin de dernière piste en file : mise en historique du titre terminé puis arrêt ;
FFMPEG_PATH configurable.
bot/handlers/music_voice_cleanup.py (nouveau) + bot/main.py : cog chargé depuis handlers_to_load, déconnexion après MUSIC_EMPTY_CHANNEL_SEC (défaut 120 s) si plus aucun humain dans le salon, nettoyage si le bot quitte le vocal.
bot/views/music_view.py : messages multi-pistes après ajout (playlist), erreurs yt-dlp en langage utilisateur ; retour précis sur previous.
bot/requirements.txt : yt-dlp>=2025.05.22 (tests locaux contre format string obsolète).
Impact visible / technique : meilleure résilience extraction/lecture, playlists courte durée utilisables depuis le même modal, salon vocal libéré quand tous les utilisateurs sont partis après délai, dépendances yt-dlp à jour après pip install -r bot/requirements.txt.
non réalisé (volontaire) — PoC Lavalink / Wavelink
Contexte/problème : la voie B (serveur Lavalink séparé) n’a pas été implémentée dans cette passe ; réservée si la voie ffmpeg+yt-dlp reste insuffisante en prod.
Impact visible / technique : aucun nouveau service Java / conteneur — déployer comme avant après mise à jour des deps Python du bot.
Contexte/problème : lecture vocale Discord (FFmpegPCMAudio) impossible sans binaire ffmpeg sur le PATH ; besoin d’appliquer pip install -r bot/requirements.txt et de recharger le process bot-python.
2026-05-09 00:05 (UTC+2) — Bot musique : bot qui quitte le vocal malgré le message « lecture démarre »
Contexte/problème : l’embed indiquait une lecture démarrée alors que le bot se déconnectait du salon vocal (pas d’audio). Cause probable combinée à (1) VoiceChannel.members dérivée du cache guild.get_member : sur grosses guildes peut être vide alors que des humains ont bien un voice_state, déclenchant à tort MUSIC_EMPTY_CHANNEL_SEC / logique « salon vide » ; (2) branche voice_clients déjà connecté sans move_to si l’utilisateur n’est plus dans le même salon que le bot (bot seul → timer de vide réel ou chaîne FFmpeg sans public).
Changements appliqués :
bot/handlers/music_voice_cleanup.py : décompte humans_in_voice_channel basé sur channel.voice_states + bot filtrés ; synchro sync_empty_disconnect_task après connexion / déplacement du bot (plus seulement au return silencieux) ; prise en charge VocalGuildChannel (stages inclus).
bot/views/music_view.py : si le bot est déjà connecté à un autre salon que interaction.user.voice.channel, move_to avant play_next ; si l’utilisateur n’est plus en vocal au moment du modal, message file d’attente sans prétendre lancer tout de suite.
Impact visible / technique : le cog ne doit plus planifier de déconnexion « vide » alors que des humains sont encore en vocal mais hors cache membres ; l’écoute démarre dans le salon de l’utilisateur qui ajoute une piste.
2026-05-09 00:35 (UTC+2) — Musique : alignement UX / permissions sur le modèle joek13/py-music-bot
Contexte/problème : prise en compte du dépôt open source [joek13/py-music-bot](https://github.com/joek13/py-music-bot) (commandes in_voice_channel, is_audio_requester, admin) pour durcir les boutons du lecteur /music sans changer la stack technique (nous restons sur yt-dlp + handler interne, plus adapté que youtube-dl utilisé en amont par ce repo).
Changements appliqués : bot/views/music_view.py — fonction music_control_denial : pause / skip / previous / volume exigent d’être dans le même salon vocal que le bot lorsqu’il est connecté, puis demandeur de la piste en cours (champ user_id) ou administrateur Discord ; contrôle répété sur le modal volume.
Impact visible / technique : liste d’attente et bouton « Ajouter » inchangés pour tout le monde ; moins de « griefing » depuis le salon texte ou un autre vocal.
2026-05-09 01:05 (UTC+2) — Musique : poursuite corrections « quitte dès que la piste est trouvée »
Contexte/problème : après la première passe (voice_states + move_to), le bot continuait à quitter le vocal au moment où l’extrait yt-dlp / la lecture démarrait — symptôme typique soit d’un sync « salon vide » déclenché trop tôt ou sur des événements non liés au salon du bot, soit d’un délai MUSIC_EMPTY_CHANNEL_SEC très court / état gateway incohérent juste après connect/move.
Changements appliqués :
bot/handlers/music_handler.py : MUSIC_JOIN_GRACE_SEC (défaut 15) + dict voice_join_grace_until ; bump_voice_join_grace avant connect/move ou avant play_next quand le bot était déjà en vocal dans le même salon ; suppression de la fenêtre dans leave_voice.
bot/handlers/music_voice_cleanup.py : on_voice_state_update ne relance sync que si la mise à jour touche le salon où est le voice_client du bot (pas chaque mute ailleurs) ; fenêtre de grâce : pas de programme de déconnexion tant que voice_join_grace_until ; re-vérification différée après la grace si besoin ; tâche de délais respecte encore la grace avant cleanup_guild ; MUSIC_DISABLE_VOICE_EMPTY_CLEANUP pour diagnostic (coupe uniquement la logique salon vide sans toucher lecture) ; humans_in_music_channel sur channel.voice_states.
bot/views/music_view.py : bump_voice_join_grace systématique avant play_next sur la branche « bot déjà connecté » + conservation du bump après move_to (équivalent maintenant avant play).
Impact visible / technique : beaucoup moins de risque de cleanup_guild intempestif après arrivée vocale ou ajout de piste ; observabilité : logs warning sur déconnexion « salon vide » effective ; option env pour désactiver le cleanup sans désactiver /music.
Contexte/problème : besoin de traces PM2 lisibles pour comprendre pourquoi le bot quitte encore le vocal (timing cleanup, FFmpeg, déconnexion Discord, etc.).
Changements appliqués :
bot/handlers/music_handler.py : music_diag_verbose() via env MUSIC_DIAG_LOG (défaut activé ; couper avec MUSIC_DIAG_LOG=0) ; logs WARNING cleanup_guild / leave_voice avec reason ; play_next (état guild.voice_client vs handler), after_playing, join_voice / bump_voice_join_grace en mode verbeux.
bot/handlers/music_voice_cleanup.py : cleanup_guild(..., reason=...) ; logs BOT vsu join/leave ; sync_empty salon vu VIDE avec UIDs vocaux et grace ; empty_disconnect SCHEDULED ; timer qui tire avec humains + uids avant cleanup.
bot/views/music_view.py : log après create_track_list (modal) si diag actif ; import handlers.music_handler.music_diag_verbose (+ sys.path local au module).
Impact visible / technique : après pm2 restart bot-python : grep -E 'music_diag|\\[music\\] (cleanup_guild|leave_voice)' ~/.pm2/logs/*bot* (adapter chemin logs) pour voir si la sortie est cleanup_guild:empty_channel…, leave_voice seul ou BOT vsu LEFT_VOICE (cause externe voice Discord).
2026-05-09 01:50 (UTC+2) — Musique : correction racine identifiée par logs (BOT vsu LEFT + file vidée)
Contexte/problème : traces music_diag montraient une rafale BOT LEFT/JOIN pendant le handshake vocal puis join_voice connect_ok … is_connected=False et play_next … queue_pending=0 (file vide) — alors qu’une piste venait d’être ajoutée.
Cause : sur chaque on_voice_state_update du bot quittant un salon, le cog music_voice_cleanup appelait get_queue(...).clear_all(). Discord peut émettre des LEFT transitoires pendant channel.connect ; la file était vidée plusieurs fois avant play_next.
Changements appliqués :
bot/handlers/music_voice_cleanup.py : sur BOT LEFT — conservation du voice_clients.pop (+ annulation timers) mais plus de clear_all() ; nettoyage file + arrêt forcé réservés à cleanup_guild.
bot/handlers/music_handler.py : après channel.connect(), boucle d’attente jusqu’à is_connected (budget MUSIC_VOICE_CONNECT_READY_SEC, défaut 20) ; play_next petite attente si VC présent mais pas connecté (MUSIC_PLAY_READY_SEC, défaut 8).
Impact visible / technique : la file d’attente survit aux oscillations d’état vocal ; play_next voit encore les pistes après connexion réelle ou quasi-réelle.
2026-05-09 02:15 (UTC+2) — Musique : debounce BOT LEFT + pop voice_clients différé
Contexte/problème : malgré suppression de clear_all() sur BOT LEFT, voice_clients.pop() immédiat retirait encore la référence pendant la rafale gateway LEFT/JOIN / handshake (is_connected encore False alors qu’un VoiceClient existe), ce qui faisait échouer play_next ou laissait la guilde sans lien cohérent avec le handler.
Changements appliqués :
bot/handlers/music_voice_cleanup.py : MUSIC_BOT_LEAVE_DEBOUNCE_SEC (défaut 1) — tâche différée puis pop du handler uniquement si hors MUSIC_JOIN_GRACE_SEC et guild.voice_client is None ; annulation du debounce au BOT JOIN ; plus de pop synchrone au premier LEFT.
bot/handlers/music_handler.py : après attente is_connected sur connect(), ré-assignation voice_clients[guild_id] ; play_next : repli si clé absente mais guild.voice_client présent (resync).
Impact visible / technique : même scénario PM2 qu’avant (LEFT en rafales) ne doit plus désenregistrer le client vocal du handler au mauvais moment.
2026-05-08 (session) — Musique : groupe slash /pmusic (parcours type py-music-bot, isolé)
Contexte/problème : poursuite du diagnostic « le bot quitte le vocal » avec demande utilisateur — tester une voie quasi identique au petit bot GitHub (slash + yt-dlp + FFmpeg) sans partager music_handler.voice_clients avec le panneau /music.
Changements appliqués : bot/commands/simple_music.py — cog SimpleSlashMusic avec app_commands.Group pmusic (sous-commandes leave, skip, queue, now, et play) plus commande /play de premier niveau (même _play_impl que /pmusic play) ; état _states séparée du handler ; même extraction (build_ydl_opts, normalize_youtube_url, _build_track_payload) ; refresh_stream_url via bot.music_handler si disponible ; ne pas mixer panneau /music et ce lecteur sur la même session vocal abusivement.
Impact visible / technique : commandes à resynchroniser côté Discord (redémarrage bot + jusqu’à 1 h pour propagation globale, ou tree.sync(guild=...) selon votre config existante).
Contexte/problème : intégration explicite du code source du dépôt [joek13/py-music-bot](https://github.com/joek13/py-music-bot) (commandes préfixe type !play) comme demandé, sans remplacer le bot principal.
Changements appliqués :
Répertoire bot/vendor/joek13_py_music_bot/ : copie adaptée du paquet upstream (LICENSE MIT) ; extraction yt_dlp à la place de youtube-dl ; display_avatar ; cog CommandErrorHandler non chargé (évite les réponses globales aux commandes inexistantes).
Renommage des cogs en Joek13Music, Joek13Meta, Joek13Tips pour ne pas entrer en conflit avec commands/music.py (/music, classe Music).
bot/commands/joek13_music_integration.py : ajout du dossier vendor au sys.path, lecture TOML (bot/data/joek13_py_music_bot.toml auto-créé ou JOEK13_MUSIC_CONFIG), JOEK13_MUSIC_DISABLED pour désactiver.
bot/requirements.txt : toml>=0.10.2 pour le fichier de config.
Impact visible / technique : avec préfixe ! du bot principal — !play, !leave, !skip, !pause, !volume, !queue, !uptime, !tip, etc. Ne pas mélanger /music + !play joek13 sur la même session vocal sans risque de double lecture / deux gestionnaires audio.
2026-05-09 — Musique : pas d’audio en salon vocal (micro / flux)
Contexte/problème : le bot annonce une lecture mais aucun son en vocal ; confusion possible avec un « micro » — côté Discord le bot envoie un flux audio encodé (FFmpeg → PCM → Opus), pas un micro utilisateur.
Causes identifiées / corrections :
join_voice : si un autre cog avait déjà connecté le bot (guild.voice_client présent sans entrée dans music_handler.voice_clients), VoiceChannel.connect() levait ClientException: Already connected → pas de play_next ou état incohérent. Désormais réutilisation / move_to sur le VoiceClient existant + filet sur la même exception après connect().
Modal « Ajouter » : should_play_now était basé sur queue.is_playing (état logique pouvant rester « lecture » alors que FFmpeg est mort). Désormais actually_playing = voice_client.is_playing() ou is_paused() quand le client est connecté → relance réelle de la lecture quand il n’y a plus de flux audio.
FFmpeg : en-tête User-Agent + -protocol_whitelist sur les options « before » pour les URLs googlevideo / HLS souvent bloquées sans navigateur ; MUSIC_FFMPEG_USER_AGENT et MUSIC_FFMPEG_EXTRA_BEFORE en surcharge.
Impact visible / technique : après pm2 restart bot-python, tester /music puis ajout de piste en étant en vocal ; si besoin grep -E 'music\\]|music_diag|after_playing|lecture erreur' sur les logs erreur PM2 pour voir une erreur FFmpeg résiduelle.
bot/requirements.txt : suppression dépendance toml (utilisée uniquement par joek13).
bot/commands/help_command.py : retrait de music dans EXCLUDED_COMMANDS (ancienne commande slash supprimée).
Impact visible / technique : plus de panneau boutons /music — usage !play <url ou recherche> ou /musique play ; pm2 restart bot-python + resync slash pour /musique.
Contexte/problème : après refonte youtube_music, retour utilisateur « ne marche pas mieux » — causes probables multiples (pas d’opus natif sous PM2, VoiceClient résiduel is_connected=False bloquant un nouveau connect(), recherche ytsearch: trop large).
Changements appliqués :
bot/main.py : tentative discord.opus.load_opus (variable OPUS_LIBRARY ou ctypes.util.find_library("opus")) avant bot.run() ; warning si aucune lib opus.
bot/commands/youtube_music.py : disconnect(force=True) sur client vocal hors ligne avant connect() ; connect(..., self_mute=False, self_deaf=False, timeout=MUSIC_VOICE_CONNECT_TIMEOUT) ; ytsearch1: pour les requêtes texte ; volume par défaut 100% ; MUSIC_FFMPEG_PROTOCOL_WHITELIST (désactiver avec 0 pour retirer -protocol_whitelist) ; exclusion formats http_dash_segments dans pick_stream_url ; tâche _verify_play_started (log si pas de is_playing après 1,2s) ; timer salon vide : ne pas couper si is_playing/is_paused ou file non vide ; MUSIC_DEBUG=1 pour log options FFmpeg.
Impact visible / technique : redémarrage bot ; si toujours muet : vérifier logs [music] Pas de flux audio et paquet libopus0 sur l’hôte ; essayer MUSIC_FFMPEG_PROTOCOL_WHITELIST=0 si ffmpeg refuse le flux.
Contexte/problème : mise en place du serveur réseau PS3 (ps3netsrv, utilisé par webMAN-MOD / Multiman) avec la racine des ROMs sous /root/Webman-Games.
Volume /root/Webman-Games → /games (racine attendue par l’image ; sous-dossiers type PS3ISO, GAMES, etc. au bon niveau).
Port 38008 exposé sur l’hôte (0.0.0.0:38008).
Impact visible / technique : sur la PS3 (webMAN), renseigner l’IP de cette machine et le port 38008 comme serveur net ; commandes utiles : docker logs ps3netsrv, docker restart ps3netsrv ; mise à jour : docker pull shawly/ps3netsrv && docker rm -f ps3netsrv puis relancer le même docker run avec les mêmes options.
Fichiers touchés : conteneur Docker système ps3netsrv (pas de fichier dans le dépôt), docs/RECAP-TRAVAIL-COMMUNAUTE-2026-04.md
2026-05-09 18:12 — Hôte : ps3netsrv — whitelist IP publique
Contexte/problème : restreindre l’accès au serveur net PS3 à une seule IP publique (90.29.245.70).
Changements appliqués : recréation du conteneur ps3netsrv avec -e PS3NETSRV_WHITELIST=90.29.245.70 (les autres options inchangées : port 38008, volume /root/Webman-Games:/games).
Impact visible / technique : seules les connexions dont l’adresse source correspond au motif sont acceptées par ps3netsrv ; si l’IP FAI du client change ( DHCP / reconnexion ), il faudra mettre à jour la whitelist ou passer par --network host si la filtre Docker ne voyait pas la bonne IP source (voir doc image shawly/ps3netsrv). Pour une exposition Internet, renforcer avec un firewall (nftables / hébergeur) 38008/tcp depuis 90.29.245.70 uniquement reste recommandé.
Notifications « Lecture en cours » / « Fin de file » sur le dernier salon texte utilisé pour la musique ; préfixe optionnel d’emoji dans les embeds : MUSIC_EMBED_PREFIX.
bot/Archy Music.js/index.js : suppression du fallback token en clair (ne garder que DISCORD_TOKEN en prod).
Impact visible / technique : resync slash au prochain pm2 restart bot (ou équivalent). /ping inchangé (déjà sur utility). Playlist YouTube encore prise en charge via MUSIC_MAX_PLAYLIST_TRACKS.
2026-05-09 — Bot musique : slash bloqué « réfléchit »
Contexte/problème : après interaction.response.defer() sur /play, aucune réponse / indicateur qui tourne en boucle — exception silencieuse côté traitement.
Changements appliqués : discord.Embed n’a pas de méthode set_timestamp() sous discord.py 2.3.x ; remplacé par embed.timestamp = datetime.now(timezone.utc) dans _embed() et /aide-musique. Garde-fou try/except autour de _play_core + followup pour toujours fermer l’interaction après defer.
Impact visible / technique : /play et les embeds musique ne lèvent plus AttributeError ; l’utilisateur reçoit bien le message de confirmation ou d’erreur.
2026-05-09 — Bot musique : après connexion vocal, « réfléchit » prolongé puis déconnexion
Contexte/problème : /play rejoignait le vocal mais gardait longtemps « réfléchit » puis le bot quittait — interaction mal terminée jusqu’après _refresh_stream + _play_next ; humans_in_channel utilisait guild.get_member sur voice_states, peu fiable selon cache (faux salon « vide », déclenchement idle disconnect au mauvais moment).
Changements appliqués :
Follow-up immédiat après defer (embed « Chargement… » puis edit vers le résultat) au lieu d’attendre toute la chaîne avant le premier followup.
_play_next déclenché en asyncio.create_task depuis _play_core (plus d’await bloquant jusqu’après tentative de lecture).
humans_in_channel : préf channel.members (membres réellement dans le vocal), repli prudent sur get_member seulement si absent.
_play_next_safe avec log en cas d’exception non gérée.
Impact visible / technique : fin de la bulle Discord bloquée sur la récupération yt-dlp une fois le message placeholder envoyée ; faux positifs « salon vide » diminués. Si le bot coupe encore après quelques secondes, vérifier FFmpeg/flux (logs [music] after ffmpeg).
2026-05-09 — Bot musique : leave vocal avant confirmation (idle + handshake)
Contexte/problème : titre récupéré → join vocal → quitte tout de suite → message « Musique ajoutée » après ; triggers idle incorrects lors des voice_update du bot + \_play_next trop tôt + ARCHY_AFTER_QUEUE_LEAVE_SEC pouvant être 0.
Changements appliqués : on_voice_state_update filtré sur bot_ch ∈ {before_channel, after_channel} ; attente VoiceClient (MUSIC_VOICE_PLAY_WAIT_SEC, défaut 6 s) dans _play_next ; AFTER_QUEUE_EMPTY_SEC plafonné min 10 s / max 3600 s.
Impact visible / technique : moins de déconnexions fantômes ; lecture toujours dépendante de FFmpeg/URL YouTube.
2026-05-09 — Bot : pont musique Python + Node.js (sans 2ᵉ Discord)
Contexte/problème : avoir une alternative aux extractions yt-dlp côté Python (stack Archy/play-dl) sans lancer un second client Discord avec le même jeton (impossible ou instable pour la même session).
Changements appliqués :
bot/music_bridge/ : server.mjs (Express local), resolve titre + stream_url via play-dl + yt-dlp (proc), GET /health ; défaut 127.0.0.1:37991 (npm start après npm install).
bot/commands/youtube_music.py : vars MUSIC_BRIDGE_URL (ex. http://127.0.0.1:37991) ; POST /resolve essayé avant yt-dlp Python pour les pistes simples (pas playlist liste YouTube uniquement avec chaîne http actuelle ; sinon fallback yt-dlp) ; _refresh_stream passe aussi par le pont si configuré puis fallback yt-dlp.
Impact visible / technique : faire tourner le pont en parallèle du bot (PM2 2ᵉ process, ou systemd) ; yt-dlp dans le PATH côté machine du pont ; désactiver le pont en vidant MUSIC_BRIDGE_URL. Un seul process discord.py porte toujours le Discord Gateway + vocal.
Contexte/problème : lancer le pont musique sous PM2, activer MUSIC_BRIDGE_URL sur le bot ; restreindre la config profil du bot (pseudo / avatar / bannière / description serveur) au propriétaire du serveur uniquement (plus permission mod configuration générique).
commands/setup_server.py : menu « Profil du bot » — refus + message éphémère si non‑propriétaire (defer + followup).
Impact visible / technique : seul le transfer owner Discord effectif peut ajuster le profil apparence bot ; admins ne voient plus passer par l’ancienne règle has_mod_permissions(..., "configuration") sur ce panneau.
2026-05-09 22:05 — Pont musique / play-dl : « Aucun résultat » + fallback Python
Contexte/problème : message utilisateur « Aucun résultat (play-dl). » lors d’un /play — resolveMetadata Node renvoyait null (recherche YouTube vide ou API play-dl fragile) et le bot Python couperait la chaîne avec l’erreur au lieu d’essayer yt-dlp en local.
Changements appliqués :
bot/music_bridge/server.mjs : après play-dl, fallback yt-dlp --dump-single-json sur la requête (ytsearch1: ou URL) avec player_client=android,web,ios ; message d’erreur final unifié si les deux échecs ; normalizeYoutubeUrl stricte avant réponse ; --get-url aligné sur les mêmes player_client ; env optionnelle MUSIC_BRIDGE_META_TIMEOUT_MS.
bot/commands/youtube_music.py : si le pont répond ok: false avec un error, log puis continuation vers l’extraction yt-dlp Python (plus de retour immédiat d’erreur).
Impact visible / technique : les titres qui échouent côté play-dl peuvent encore passer par yt-dlp (Node puis Python) ; pm2 restart music-bridge après déploiement du server.mjs.
Contexte/problème : message « Aucune piste trouvée (play-dl puis yt-dlp). … pip install » alors que yt-dlp est installé dans le venv du bot — le process music-bridge lancé par PM2 n’a pas le venv activé, donc spawn("yt-dlp") tombait souvent sur ENOENT / binaire absent du PATH ; yt-dlp ne tournait jamais côté pont.
Changements appliqués :
ecosystem.config.js (music-bridge) : YT_DLP_PATH par défaut /root/lbxmb.fr/venv/bin/yt-dlp (surcharge possible via process.env.YT_DLP_PATH).
bot/music_bridge/server.mjs : résolution automatique du binaire (env puis chemin venv projet) ; enchaînement de plusieurs valeurs extractor-args YouTube (android/web, web_creator, tv_embedded) pour métadonnées et --get-url ; GET /health renvoie yt_dlp_binary ; journaux spawn ENOENT / stderr tronqués.
Impact visible / technique : après pm2 reload ecosystem.config.js (ou restart music-bridge) /resolve retrouve des pistes tant que yt-dlp du venv est à jour et joignable.
2026-05-09 22:35 — Slash : pas de doublon Python/JS musique ; sync par serveur nettoyée
Contexte/problème : crainte de doublons entre /play JS (Archy) et Python, et effets bizarres sur les commandes.
Analyse : un seul cog musique Python (ArchyMusic / youtube_music.py) ; ecosystem.config.js ne lance pas le bot Node Archy — music-bridge n’est pas Discord. Vrai risque : deux process avec le même DISCORD_TOKEN (Discord n’en garde qu’un).
Changements appliqués : setup_hook (main.py) : tree.sync() global + un seul tree.sync(guild=…) pour utils.permissions.GUILD_ID (commandes @app_commands.guilds, ex. /remodile) — suppression de la boucle sur tous les self.guilds. Même logique dans on_ready (retrait de la re-sync par serveur). /sync (commands/sync.py) aligné (global + même guild). En-tête bot/Archy Music.js/index.js : rappel production = Python, un token = un client.
Impact visible / technique : moins d’appels API bulk_upsert_guild_commands inutiles ; pas de « deux /play » côté lbxmb.fr tant qu’un seul bot tourne avec ce token. Redémarrage bot pour prendre en compte la sync.
musicManager.js : yt-dlp via ARCHY_YT_DLP_PATH / YT_DLP_PATH ou /root/lbxmb.fr/venv/bin/yt-dlp (plus seulement bin/yt-dlp.exe) ; option MUSIC_BRIDGE_URL en repli après play-dl ; ARCHY_MUSIC_BRIDGE_FIRST=1 pour prioriser le pont ; extractor-args YouTube sur yt-dlp ; flux googlevideo via FFmpeg direct.
index.js : jeton ARCHY_MUSIC_DISCORD_TOKEN (repli DISCORD_TOKEN) ; ARCHY_MUSIC_CLIENT_ID ; ARCHY_MUSIC_GUILD_ID ; sortie si pas de token ; déploiement slash inchangé (guild si GUILD_ID défini).
Impact visible / technique : pm2 start ecosystem.config.js --only archy-music-node après avoir défini ARCHY_* dans l’env (souvent source .env avant pm2, ou env dans YAML dédié). Une session Discord par jeton : couper bot-python ou utiliser une deuxième application Discord pour Archy Node.
Contexte/problème : les commandes /archy-* n’apparaissaient pas — l’enregistrement REST utilisait un fallback client.user.id, qui n’est pas l’ID application OAuth2 attendu par Routes.applicationCommands / applicationGuildCommands ; déploiement global sans guild_id = latence Discord longue.
Changements appliqués : index.js — await client.application.fetch() puis applicationId = app.id uniquement ; avertissement si ARCHY_MUSIC_CLIENT_ID ≠ id du jeton ; guild_id par défaut depuis bot/config.json (sans lire le token) si ARCHY_MUSIC_GUILD_ID vide ; logs explicites (route guild vs global, liste des noms, erreurs rawError).
Impact visible / technique : après pm2 restart archy-music-node, les slash archy-* doivent apparaître dans la minute sur le serveur cible tant que le bot y est invité avec le scope applications.commands ; sinon vérifier les logs [archy-music].
2026-05-09 23:40 — Archy Node : snowflake guild_id faux à cause du JSON
Contexte/problème : slashes toujours absentes alors que REST semblait OK — guild_id lu via JSON.parse sur config.json était tronqué par la précision Number JavaScript (1232966743241130005 → 1232966743241130000). Les applicationGuildCommands partaient donc pour un mauvais serveur.
Changements appliqués : index.js — readBotConfigGuildId() extrait guild_id en texte brut (regex chaîne "…" ou nombre littéral, sans conversion float) ; GET après PUT pour lister les noms tel que Discord les voit ; register-slash.js (+ npm run register-slash) sans lancer tout le bot, avec Routes.oauth2CurrentApplication().
Impact visible / technique : après pm2 restart archy-music-node ou npm run register-slash (env chargé avec le bon jeton Archy), les /archy-* doivent apparaître sur LB’XMB ; dans le champ /, bien choisir l’intégration du bot Archy (nouvelle appli Discord).
2026-05-09 23:55 — Archy Node / PM2 : jeton toujours vide
Contexte/problème : npm run register-slash et archy-music-node sortent « Aucun jeton » alors que .env existe — source .env ne couvre pas les process npm selon le format du fichier ; PM2 n’injecte pas .env ; et ecosystem.config.js passait ARCHY_MUSIC_DISCORD_TOKEN: "", ce qui masque une valeur chargée ensuite par dotenv (variable déjà définie vide).
Changements appliqués : dotenv dans bot/Archy Music.js ; index.js + register-slash.js — require('dotenv').config({ path: ../../.env, override: true }) ; retrait des entrées token / client id / guild vides dans ecosystem pour archy-music-node ; exec_mode: "fork" ; message d’erreur register-slash avec chemin .env attendu ; .env.example rappel dotenv.
Impact visible / technique : ajouter ARCHY_MUSIC_DISCORD_TOKEN=… (ou DISCORD_TOKEN pour test) dans /root/lbxmb.fr/.env puis pm2 delete archy-music-node & pm2 start ecosystem.config.js --only archy-music-node (ou reload avec config à jour).
2026-05-10 — Archy Node : pas de DISCORD_TOKEN dans .env
Contexte/problème : register-slash charge .env mais ARCHY_ / DISCORD_TOKEN absents — seul bot/config.json contenait déjà le jeton (comme bot Python).
Changements appliqués : resolve-discord-token.js — ordre ARCHY_MUSIC_DISCORD_TOKEN → DISCORD_TOKEN → lecture texte du champ "token" dans bot/config.json (sans JSON.parse sur le fichier entier) ; index.js + register-slash.js l’utilisent ; log de la source et longueur uniquement ; .env.example rappel du repli.
Impact visible / technique : npm run register-slash et archy-music-node démarrent sans nouvelle ligne dans .env si config.json est déjà renseigné ; ne pas commit le jeton en clair.
2026-05-10 — Archy : jeton config.json via JSON.parse
Contexte/problème : sortie terminal encore au message ancien (register-slash sans mention config.json) ou échec lecture — regex seule fragile selon fichier.
Changements appliqués : resolve-discord-token.js — JSON.parse du fichier puis cfg.token ; candidats bot/config.json et config.json racine ; repli regex ; label source lisible.
Impact visible / technique : npm run register-slash doit afficher Jeton depuis: bot/config.json si .env sans token ; déployer le dossier bot/Archy Music.js à jour sur le serveur (git pull).
2026-05-10 — Archy Node /archy-play : « L'application ne répond plus »
Contexte/problème : Discord coupe l’interaction si aucune réponse sous ~3 s ; enqueue faisait resolveTrack (play-dl / pont) puis connexion vocal (jusqu’à 15 s) avant le premier interaction.reply.
Changements appliqués : musicManager.enqueue — deferReply() juste après la vérif salon vocal ; editReply pour succès / erreurs (plus de reply après travail long).
Impact visible / technique : indicateur « Archy réfléchit… » puis embed ; pm2 restart archy-music-node pour prendre en compte.
Contexte/problème : renforcer /play côté bot/commands/youtube_music.py ; éviter la fragilité YouTube seul ; documenter alternatives (SoundCloud, cookies).
Changements appliqués :
_extract_sync : enchaînement de plusieurs player_client YouTube (MUSIC_YTDLP_PLAYER_CLIENTS, défaut android+web+ios → web_creator → tv_embedded → android+web) ; une seule tentative si la requête ne cible pas YouTube (URLs SoundCloud, etc.).
MUSIC_TEXT_SEARCH_PREFIX (défaut ytsearch1:) pour orienter la recherche texte (scsearch1: = SoundCloud).
ecosystem.config.js : commentaire — musique prod = bot-python ; Archy Music.js plus dans PM2.
.env.example : MUSIC_BRIDGE_URL vide possible, YOUTUBE_COOKIES_PATH, exemples vars ; /aide-musique texte mis à jour.
Impact visible / technique : pm2 restart bot-python ; pip install -U yt-dlp régulier ; sans Spotify natif (DRM) — liens SoundCloud / Bandcamp / Twitch usuels via yt-dlp.
2026-05-10 — Musique : full Python uniquement (plus de Node)
Contexte/problème : tout centraliser sur bot-python — plus de pont music-bridge, plus de bot Archy Music.js.
Changements appliqués :
bot/commands/youtube_music.py : suppression aiohttp, MUSIC_BRIDGE_URL, _bridge_post_resolve, _bridge_json_to_track et tout appel au pont dans _extract_tracks / _refresh_stream — extraction yt-dlp uniquement.
src/registerSlash.js : enregistrement global + guild si guild_id dans la config.
src/index.js : intents Guilds, GuildMembers, GuildVoiceStates, GuildMessages ; dispatcher central ; README.md mis à jour.
Impact visible / technique : npm install && npm start dans Bot-JS/ ; activer intents membres (et messages si besoin) sur le portail Discord ; /logs renvoie un message « stub » tant que l’analytics reste en Python.
2026-05-10 — Discord : deux applications (Python + Bot-JS) et jeton dédié
Contexte/problème : éviter conflits slash / communauté — bot Python sur l’app principale, bot JS sur une autre application Discord ; clarifier l’enregistrement côté Python (.env + restart).
Changements appliqués :
Bot-JS/src/config/loadConfig.js : priorité DISCORD_JS_TOKEN pour le jeton du bot JS ; DISCORD_TOKEN reste un repli / même fichier que Python pour compat.
Bot-JS/.env.example : DISCORD_JS_TOKEN — à copier en Bot-JS/.env sur le serveur (non versionné).
.env.example racine : précision DISCORD_TOKEN = app Python ; pas le même jeton que la migration JS.
ecosystem.config.js : entrée PM2 bot-js (npm run start dans Bot-JS/).
Bot-JS/README.md : doc deux apps, sync slash Python au démarrage / pm2 restart bot-python, rappel régénération jeton si fuite.
Impact visible / technique : créer la 2ᵉ app sur le portail Discord ; DISCORD_JS_TOKEN dans Bot-JS/.env ; pm2 start ecosystem.config.js ou pm2 restart bot-js ; Python inchangé si DISCORD_TOKEN toujours dans .env racine — pm2 restart bot-python pour resynchroniser les slash après changement de commandes.
Contexte/problème : exposer la même vue configuration serveur que le bot Python (setup_server) côté Bot-JS, en lisant les mêmes JSON (bot/data/servers/{id}.json, bannière data/discord/banner_config.json), sans dupliquer toute la logique d’édition (panneau lecture / navigation).
Changements appliqués :
src/commands/config.js : slash /config + embed résumé + menu déroulant.
src/commands/configPanel.js : états résumé, embeds, buildBackRow renvoie un tableau de lignes (compatible spread Discord).
src/index.js : routage composants avant les slash (évite d’ignorer les menus).
src/commands/index.js : enregistrement de config dans ALL_LIST ; meta.js : /help mentionne /config.
Impact visible / technique : après /sync ou redémarrage, /config ouvre le panneau ; menu et bouton Retour fonctionnent ; pm2 restart bot-js pour prise en compte.
2026-05-09 — Bot-JS : /config écrit dans bot/data/servers (vocal, tickets, notifs, profil)
Contexte/problème : le panneau ne devait pas rester lecture seule — besoin d’éditer les mêmes JSON que le bot Python pour vocal temporaire, tickets (on/off), notifications YouTube (on/off), profil bot (description JSON, pseudo / avatar via API ; propriétaire seul).
Changements appliqués :
src/lib/serverConfig.js : saveServerConfig, updateServerConfig (merge récursif comme Python).
src/commands/configInteractions.js : lignes d’action sous le menu pour les catégories concernées.
src/index.js : routage cfg: / cfg:m: avant les slash.
src/commands/config.js : description slash mise à jour.
Impact visible / technique : pm2 restart bot-js ; les changements TempVoice dans le JSON sont pris en charge par le bot Python après tempvoice_handler.load_config() ou redémarrage — message éphemère rappelé après ajout config ; gestion avancée tickets (types, embeds, etc.) reste surtout sur Python ; profil bot = transfer owner uniquement.
2026-05-09 — Bot-JS : /config — migration étendue (toutes les lignes du menu)
Contexte/problème : tout migrer au sens couverture des entrées du menu (setup_server.py) : au minimum actions sur le même JSON que Python pour chaque catégorie (toggles, modals, second fichier banner_config.json pour l’URL bannière).
Bot-JS/src/commands/configActions.js : réexporte le routeur (setup_server JS).
Bot-JS/src/commands/configPanel.js : import buildCategoryActionRows depuis configHandlers/actionRows ; pied d’embed pour toutes les catégories éditables.
Impact visible / technique : pm2 restart bot-js ; les flux ultra-complets Python (sous-menus RoleSelect, envoi du panel ticket, etc.) peuvent encore être utilisés en parallèle — cette vague couvre l’essentiel fonctionnel fichier JSON ; restart bot-python pour recharger handlers mémoire après changements critiques.
Contexte/problème : recentrer les correctifs sur le bot Python uniquement : MP départ inexploitable, Retour depuis pseudo → racine /config, anti‑raid comptes récents difficile via seul multi‑select, mini‑jeux sans actions, tickets sans retour racine, libellés Actualiser, profil bot trop restrictif vs /config.
Changements appliqués :
bot/commands/setup_server.py : retrait du type Message de Départ (Privé) dans le menu messages auto + résumés sans goodbye_dm ; embed Arrivée du serveur (texte MP) ; BackButton : retiré l’heuristique pseudo automatique → arrival ; AutoNicknameView : BackButton(..., "main") ; ToggleAntiCompteRecentButton + ConfigureCompteRecentButton en row=3 ; MiniGamesView : toggles compteur / suite de mots / pendu + RefreshMiniGamesButton ; TicketsView (impl. complète) : BackButton(..., "main"), toggle tickets vert/rouge, libellé refresh ; remplacement global des libellés 🔄 Actualiser → 🔄 Mettre à jour (et Actualiser isolé) dans ce fichier.
bot/commands/config_bot_profile.py : _can_edit_bot_profile aligné sur /config (BOT_OWNER_IDS, owner guild, has_mod_permissions(..., "configuration")) ; messages de refus + embed d’aide mis à jour.
Impact visible / technique : plus d’entrée UI pour MP départ ; retour cohérent vers l’embed racine configuration ; activation Anti‑Compte‑Récent via bouton dédié ; mini‑jeux et tickets utilisables depuis le panel ; même droits que /config pour le profil bot serveur — pm2 restart bot-python (ou équivalent) après déploiement.
2026-05-10 12:00 — Web : formulaire ressources / liste — FTP BackupRessources, variantes, modale, tags
Contexte/problème
Changements appliqués
web/src/components/files/FileSelector.tsx : defaultRelativePath + lockSubtreeRoot (BackupRessources là où c’est l’import téléchargement) ; rendu via createPortal(document.body) ; overflow du body bloqué tant que la modale est ouverte ; Retour désactivé à la racine d’import (« À la racine import »).
web/src/components/resources/VariantEditor.tsx : plusieurs panneaux « Ajouter dans … » ouverts en parallèle (Set + brouillon par nœud) ; barre racine (catégorie / fichier) toujours affichée ; renommage racine : champ .nom seulement pour fichiers feuilles ; BackupRessources pour le FTP des variantes ; type="button" sur boutons à risque de soumission formulaire.
web/src/app/api/resources/route.ts : condition de recherche élargie aux tags (CAST(r.tags AS CHAR) + LIKE) ; invalidation cache liste v7.
web/src/app/api/site-search/route.ts, web/src/app/api/resources/search/route.ts : idem tags pour les requêtes « ressources ».
Impact visible / technique : imports Backups uniquement dans l’UI concernée ; plusieurs catégories éditables sans se gêner ; modale fixed au viewport ; recherche par mots présents dans tags JSON (sous-chaîne).
2026-05-10 14:30 — Web : UX ressources, nav recherche, traduction auto navigateur
Contexte/problème : masquer les tags à l’affichage (usage recherche uniquement) ; scroll liste ressources / détail où seule la zone contenu défile pas le menu ; barre recherche navbar plus explicite ; bouton ajouter une ressource aligné avec la recherche locale ; logos sans cadre ni fond noir ; visiteurs langue navigateur ≠ français avec interface FR → traduction automatique.
Changements appliqués
ResourceDetailClient.tsx : suppression affichage des tags ; même colonne défilante que la liste (flex + lg:overflow-y-auto) ; avatar / logo carte : bg-transparent, sans bordure épaisse.
ressources/page.tsx : colonne principale lg:max-h-[calc(100dvh-5rem)] + overflow-y ; recherche + bouton Ajouter sur une ligne sous le titre ; cartes liste : wrapper logo sans bord/fond.
ResourcesSidebar.tsx : retrait sticky (hauteur viewport + défilement interne déjà prévu dans le nav).
Header.tsx + messages/*.json : bouton recherche desktop libellé + icône (nav.siteSearchButton) ; mobile : bouton compact à droite avant le menu.
DeferredClientWidgets.tsx + BrowserAutoTranslate.tsx : si locale === 'fr' et langue primaire navigateur hors fr, cookie googtrans + chargement translate.google.com ; si locale !== 'fr' → suppression du cookie (évite conflit avec next-intl).
globals.css : CSS pour réduire l’impact visuel de la barre Google translate (html.translated-*).
Impact visible / technique : menu consoles fixe dans la colonne gauche (hauteur écran), grille et fiche qui scrollent dans la colonne droite ; libellés i18n nav enrichis où manquants ; traduction automatique dépend du réseau et des politiques navigateur / Google — désactivation locale possible : localStorage.setItem('lbxmb_auto_translate_off','1').
2026-05-10 ~19:00 — Web : Forum — navigation Info/Sujets, Culture, Suivis + API, Communauté, carte post
Contexte/problème : regrouper Annonce / Actualité sous Info, Entraide / Technique / Hors sujet sous Sujet, ajouter la rubrique Culture ; onglet Groupes → Communauté avec URL tab=communaute (rétrocompat tab=groupe → redirection) ; liste Suivis avec compteurs et Ne plus suivre ; mise en page du premier post (traduction + modifier / supprimer alignés avec l’auteur).
Changements appliqués
web/src/components/forum/ForumPostTranslate.tsx : rendu optionnel children(renderProps) (bouton traduire + texte titre/corps/erreur) tout en gardant le comportement par défaut pour les autres usages.
web/src/app/forum/thread/[id]/page.tsx : refactor carte OP — en-tête (auteur + Traduire + crayon/poubelle), badges, titre, corps Markdown, pièces jointes ; catégorie culture (icône, filtres) ; onglets Communauté ; libellés forumThreadPage.
web/src/app/forum/page.tsx + web/src/app/forum/preview/page.tsx : colonne latérale Info / Sujet + entrée Culture ; filtres/API slugMapping ; menu catégorie segmenté ; onglet communaute + handleForumTabChange ; cartes suivis avec followersCount / followingCount et bouton Ne plus suivre.
web/src/app/api/forum-social/following/route.ts : champs followersCount et followingCount par utilisateur suivi (sous-requêtes user_following).
web/src/app/api/forum-social/categories/route.ts : insertion idempotente de la catégorie culture si absente ; invalidation forum:categories:* seulement après insertion.
web/data/forum/locales/*.json : entrée culture dans slugLabels.
web/src/app/forum/g/[slug]/page.tsx, web/src/app/forum/preview/g/[slug]/page.tsx, web/src/app/forum/preview/thread/[id]/page.tsx : liens tab=communaute alignés avec l’accueil.
web/messages/*.json : tabCommunity, sidebarSectionInfo, sidebarSectionTopics, catCulture, statsFollowers / statsFollowing, communitySectionTitle, etc. ; groupAsideHint (FR/EN) enrichi avec pistes UX pour les groupes.
Impact visible / technique : navigation forum plus lisible ; Culture créée en base au premier GET catégories (si droits DDL) ; URLs ?tab=groupe redirigées vers communaute ; tsc --noEmit OK sur web.
2026-05-10 ~20:15 — Stripe Checkout : erreur « payment method type paypal is invalid »
Contexte/problème : payment_method_types incluait paypal (et amazon_pay) par défaut ; sans activation sur le compte Stripe, l’API refuse la création de session Checkout.
Changements appliqués : défaut ["card"] dans getStripeCheckoutPaymentMethodTypes / VIP ; commentaire expliquant d’ajouter STRIPE_CHECKOUT_PAYMENT_METHOD_TYPES=card,paypal,amazon_pay après activation Dashboard ; .env.example aligné sur le nouveau défaut.
Impact visible / technique : checkouts (hors abonnements déjà carte seule) fonctionnent sans config PayPal côté Stripe ; réactivation PayPal/Amazon Pay = Dashboard + variable d’env.
Contexte/problème : en-tête des cartes outil via react-apple-emojis → rendu « texte » / noms emoji selon environnement ; onglet Sécurité : téléphone/email perçus comme « faux » car sans lien avec registre légal.
Changements appliqués
checker/page.tsx : icône carte = PlatformIcon (react-icons / lucide) dans le bloc 14×14 ; retrait EmojiProvider pour cette page.
Checkers.tsx / Safety : deux panneaux Indices / Entreprises (France) ; disclaimers honnêtes ; recherche registre publique recherche-entreprises.api.gouv.fr via proxy GET /api/checker/entreprise (+ rate-limit IP).
2026-05-14 ~16:00 — Web : compositeur forum plus grand + SEO (JSON-LD, mots-clés contenu)
Contexte/problème : zone de saisie trop petite pour rédiger un post forum confortablement ; besoin d’une base « propre » pour que les pages ressources / boutique / sujets forum soient mieux comprises par les moteurs (titres + descriptions déjà présents, enrichissement cohérent).
Changements appliqués
ForumThreadComposer.tsx : MentionTextarea nouvelle discussion — min-h et minHeight doublés (~200 / 240 px).
forum/thread/[id]/page.tsx : réponses / édition — hauteurs minimales alignées (dont minHeight synchronisé avec les classes Tailwind).
lib/seo-jsonld.ts (nouveau) : extraction de tokens pour meta keywords, JSON-LD SoftwareApplication (ressources), Product (boutique), Article (fil forum).
ressources/[slug]/layout.tsx, shop/[id]/layout.tsx, forum/thread/[id]/layout.tsx : cache() sur le fetch métadonnées pour une seule requête par navigation ; injection <script type="application/ld+json"> ; keywords fusionnés (champs structurés + mots issus titre/description).
Impact visible / technique : zone d’écriture forum nettement plus haute ; balises structurées exploitables par Google (sans garantie de positionnement) ; meta keywords enrichis — rappel : le classement repose surtout sur contenu visible, titres H1, liens internes, Search Console et sitemap déjà en place côté projet.
2026-05-14 ~17:15 — Web : sitemap — sujets forum indexables + catégorie culture
Contexte/problème : étendre le sitemap XML aux fils de discussion pour faciliter la découverte / indexation Google, sans exposer les contenus restreints (groupes, followers, brouillons).
Changements appliqués
web/src/app/api/sitemap-xml/route.ts : URLs /forum/thread/{id} pour jusqu’à 5000 sujets récents (last_reply_at / updated_at / created_at) ; filtres group_id IS NULL, visibility = public, is_published = 1, auteur actif ; lastmod sur la date d’activité ; changefreq daily ; constante SITEMAP_FORUM_THREADS_LIMIT (priorité sitemap des fils alignée ensuite sur les ressources, voir entrée 17:45).
Liste forum?category= : ajout du slug culture (aligné avec le forum).
Impact visible / technique : Search Console peut ramasser les sujets publics du forum ; les fils groupes ou followers-only ne sont pas listés (comportement voulu).
2026-05-14 ~18:30 — Web : fiche ressource — titre principal en <h1> (SEO)
Contexte/problème : les fiches /ressources/... affichaient le nom de la ressource dans CardTitle (rendu div) : pas de <h1> explicite pour le sujet de la page, ce qui nuit à la compréhension / mise en avant par les moteurs.
Changements appliqués : passage ultérieur à un <h1> dans le layout serveur + extrait (voir ~19:00) ; retrait du titre dupliqué dans la carte client.
Impact visible / technique : voir entrée 19:00 (rendu serveur du texte pour crawl).
2026-05-14 ~19:00 — Web : fiche ressource — en-tête HTML serveur (site: / crawl sans JS)
Contexte/problème : requête site:lbxmb.fr velora sans résultat malgré URL valide et sitemap : le <body> servi contenait surtout le flux RSC (BAILOUT_TO_CLIENT_SIDE_RENDERING) — peu de texte « Velora » hors <head> / hors exécution JS, ce qui complique l’indexation sur requêtes site: + mot du corps.
Changements appliqués : ressources/[slug]/layout.tsx — bloc <header> serveur avec <h1> (nom) + <p> (extrait description nettoyée, ~360 car.) ; ResourceDetailClient.tsx — suppression du titre répété dans l’en-tête de carte (évite doublon visuel).
Impact visible / technique : bandeau titre + accroche au-dessus du contenu client ; texte lisible dans le HTML initial pour les robots ; après déploiement + réexploration Google, les recherches du type site:lbxmb.fr velora devraient pouvoir matcher.
2026-05-14 ~17:45 — Web : sitemap — priorité ressources + sujets forum
Contexte/problème : le référencement cible surtout les fiches ressources et les sujets forum (un URL = fil complet avec premier message + réponses), pas la boutique en priorité.
Changements appliqués : web/src/app/api/sitemap-xml/route.ts — pages statiques avec priorités différenciées (/ressources + /forum en 0.95, hub boutique 0.55) ; chaque /ressources/{slug} et /forum/thread/{id} en 0.9 ; fiches guides 0.75 ; produits boutique 0.5 ; filtres forum?category= 0.65.
Impact visible / technique : signal relatif dans le sitemap uniquement (hint pour l’exploration interne) ; pas de suppression des URLs boutique.
Contexte/problème : /discord/banner/{guildId} servait un HTML « Enregistrer sous » Next avec *_files/*.css supprimés par le sanitizer → CSS cassé ; chiffres figés dans le HTML ; logo parfois absent (chemins relatifs / préchargements cassés).
Changements appliqués
web/src/lib/discord-banner-export-detection.ts (nouveau) : détecte les exports Next sans pack d’assets (_files/, MODDING_files, etc.).
web/src/app/discord/banner/[guildId]/route.ts, web/src/app/discord/logo/[guildId]/route.ts : si export cassé → gabarit intégré (buildDiscordDefaultBannerHtml + stats live pour la bannière ; logo 404 si export logo uniquement cassé).
web/src/lib/banner-stats-internal.ts : compteur ressources = même filtre listables que /api/stats/home ; utilisateurs = supprime + desactive avec repli si colonne absente ; stockage = parcours async du dossier uploads/Ressources (comme la home), libellé Go.
web/src/lib/discord-default-banner-html.ts : logo avec alt, loading="eager", referrerpolicy="no-referrer" pour limiter les soucis d’affichage côté client Discord.
Impact visible / technique : URL bannière serveur principal → rendu stable + chiffres à jour à chaque requête ; ?default=1 reste utilisable pour forcer le gabarit même avec un HTML custom valide.
2026-05-14 ~20:20 — Web : ressources (favicon, YouTube, version GitHub, avatar créateur, footer liste)
Contexte/problème : (1) favicon ressource persistant hors fiche ; (2) vignette lite-youtube absente dans le carrousel ; (3) préfixe v en double sur la version (GitHub + affichage) ; (4) photo créateur affichée alors qu’aucun avatar dédié (repli sur données legacy / membre) ; (5) footer /ressources sous toute la largeur y compris la colonne catégories.
Changements appliqués
YouTube : retrait du conteneur yt-fill + overrides CSS associés ; LiteYouTube en max-w-none w-full dans le bloc 16:9 pour laisser le ratio natif de lite-youtube-embed (aperçu HQ).
Version : stripSemverLeadingV côté /api/github/repo-info, sync sync-resource-github-release.ts (version / changelog / entrées téléchargement), formulaire ressources/ajouter (auto-remplissage tag).
Avatar créateur : GET /api/resources/[id] — si structure.json est chargé et sans fichiers.avatar_createur, profil_auteur renvoyé null (pas de repli colonne DB seule).
Créateur UI : bloc avatar déjà basé sur profil_auteur + Avatar Radix (sans image si pas de fichier).
Footer : ressources/page.tsx — <Footer /> déplacé dans la colonne de contenu scrollable (sous les cartes), pas sous la sidebar.
Impact visible / technique : favicon site conservé hors métadonnées dynamiques par ressource ; liste ressources : pied de page aligné zone cartes ; fiches : aperçu vidéo YouTube rétabli ; versions sans v redondant ; pas d’avatar créateur inventé.
Header.tsx : entrée menu support vers Aide consoles.
api/admin/tickets/route.ts : filtre staff support PS inclut aide_consoles_ps2.
Impact visible / technique : création de tickets aide_consoles_<console> ; staff PS voit les tickets PS2 ; libellés admin/messagerie inchangés pour les types déjà connus.
2026-05-14 ~22:30 — Web : header recherche scroll + ressources (GitHub par plateforme, lien externe)
Contexte/problème : (1) en navbar compacte (scroll), la zone recherche restait large ; (2) releases GitHub multi-fichiers mélangées ; (3) besoin de lien de téléchargement externe en plus des fichiers hébergés.
Changements appliqués
Header.tsx (desktop) : si isScrolled, bouton recherche = icône loupe seule (h-9 w-9), sinon libellé + icône inchangé.
sync-resource-github-release.ts : assets triés par plateforme ; chaque entrée plateforme_fichier dans structure.json.
ResourceDetailClient.tsx : modal téléchargements regroupée par plateforme fichier ; sous-libellé console pour blocs *Archives / Autre* ; prise en charge url_externe et liens https dans chemin (legacy) ; handleDownload ouvre les URLs en nouvel onglet après compteur.
DownloadVariantsModal.tsx : nœuds avec url_externe ; cible via getDownloadTarget.
resource-add-from-form.ts, execute-dashboard-resource-put.ts : champ download_url_externe_* ; entrées url_externe + plateforme_fichier ; liens http dans filePath → url_externe (plus de chemin seul pour URL).
ressources/ajouter/page.tsx : champ Lien direct par téléchargement ; validation fichier ou URL.
Impact visible / technique : UX navbar plus dense au scroll ; fiches ressources sans variantes manuelles : arborescence de téléchargement lisible par OS/format ; création/édition avec URL distante sans uploader le binaire.
Piste non codée (produit) : étape « prévisualiser la release GitHub » avant synchro (cases à cocher par asset) — à brancher sur une API preview + stockage des règles dans structure.json ou colonne JSON ressource.
2026-05-14 ~23:45 — Web : /ressources — suppression bande vide en bas du viewport (desktop)
Contexte/problème : bande horizontale sombre en bas de l’écran sur la bibliothèque modding (cartes tronquées / zone morte), peu esthétique.
Changements appliqués : le bloc principal combinait pt-12 md:pt-20 (réserve pour le header fixe) avec lg:max-h-[calc(100dvh-5rem)] — la hauteur utile était donc réduite deux fois d’environ la hauteur du header → ~5rem de vide en bas. Passage à lg:h-[100dvh] lg:max-h-[100dvh] sur le conteneur flex ; colonne scroll sans lg:max-h-[calc(...)] redondant ; sidebar desktop self-stretch + min-h-0 sans hauteur calc(100dvh-5rem) figée.
Impact visible / technique : la zone scroll + sidebar occupe correctement la hauteur viewport sous le header ; plus de bande vide systématique en bas en layout lg.
2026-05-14 ~23:58 — Web : fiche ressource (favicon logo), modal téléchargements, tri GitHub iOS
Contexte/problème : (1) favicon LBXMB sur les fiches au lieu du logo ; après navigation SPA le favicon pouvait rester incohérent ; (2) modal choix des fichiers : débordement / scroll horizontal ; (3) assets iOS (IPA, zip jailbreak, .deb iPhone…) mal rangés côté Android ou Linux / archives sans distinction.
Changements appliqués
ressources/[slug]/layout.tsx : generateMetadata expose icons (shortcut + apple) pointant vers logoUrl quand présent.
ResourceDetailClient.tsx : effet client avec applied (logo / fallback / none) : logo seulement si generateSlug(nom) = slug URL ; sinon ou sans logo → /lbxmb.png (évite un favicon d’une autre fiche pendant le chargement) ; cleanup annule le cas logo en restaurant l’instantané d’href, sinon maintient le fallback.
github-asset-platform.ts : plateforme ios + ordre d’affichage ; inférence .ipa / .tipa, .deb (iphoneos / cydia / rootless…), archives et noms avec signaux iOS vs Android ; groupKeyForDownload : si le fichier ressemble à iOS mais la base dit android/linux/macos/archive/autre → iOS.
derive-plateforme.ts : famille iOS si libellé console contient ios / iphone / ipad sans android (évite de confondre avec Android).
Impact visible / technique : onglet navigateur = icône ressource sur la fiche avec logo ; accueil et autres pages = LBXMB ; modal téléchargement centrée sans scroll horizontal gênant ; synchro / liste téléchargements : section iOS distincte d’Android.
2026-05-15 ~00:25 — Web : bandeau SEO fiche ressource + uploads releases/ + ps5-linux
Contexte/problème : (1) titre / extrait du <header> serveur masqués ou confondus avec le fond (calque du resourceDetailPageBg fixe du <main> client, ordre de peinture) ; (2) URL /api/uploads/Ressources/PS5/Payload/ps5-linux/ps5-linux-loader.elf en 404 alors que le binaire est sous releases/v1.0/ ; chemin_relatif dans structure.json obsolète.
Changements appliqués
ressources/[slug]/layout.tsx : relative z-[15] sur le bandeau + padding-top avec max(..., env(safe-area-inset-top)) pour rester sous la navbar (z-50) mais au-dessus du fond fixe.
api/uploads/[...path]/route.ts : si le fichier direct n’existe pas, recherche releases/<dossier-version>/<même nom de fichier> sous le parent attendu (anciens chemins sans segment releases).
uploads/Structure/PS5/Payload/ps5-linux/structure.json : chemin_relatif corrigé vers .../releases/v1.0/ps5-linux-loader.elf.
Impact visible / technique : bandeau lisible ; téléchargement ELF et liens partagés anciens : 200 via repli API ; API et structure.json alignés sur le disque.
2026-05-15 ~01:05 — Bots Discord : on_ready à chaque reconnexion + logs
Contexte/problème : bot Python parfois instable / « hors ligne » sans cause évidente ; risque d’accumulation de tâches @tasks.loop et de handlers dupliqués : en discord.py, on_ready est rappelé après chaque reconnexion Gateway, et tout le bloc d’init recréait handlers + start() (nouvelles instances avec boucles parallèles même si chaque handler garde if not poll.is_running() sur sa méthode liée).
Changements appliqués
bot/main.py : garde _main_initial_setup_done — au 2e+ on_ready : uniquement register_persistent_views() + log ; premier run pose le flag en fin de setup ; on_disconnect / on_resumed en logs ; loop.set_exception_handler pour erreurs asyncio non catchées ; import asyncio ; doublons fcntl/atexit supprimés.
Impact visible / technique : moins de charge et de conflits après coupures WS Discord ; journaux plus exploitables pour corréler avec PM2 / Discord status.
2026-05-15 ~01:25 — Web : bannière Discord — logo lbxmb.png inaccessible
Contexte/problème : le gabarit HTML servi par /discord/banner/{guildId} (template défaut + sanitizer export) injectait src="${origin}/lbxmb.png" avec origin = NEXT_PUBLIC_APP_URL en priorité — souvent http://127.0.0.1:PORT en prod (workers Next / PM2). Les serveurs de Discord ne peuvent pas charger cette URL → logo cassé.
discord/banner/[guildId]/route.ts, discord-banner-html-sanitize.ts, api/internal/discord/banner-html/route.ts : utilisation de cette origine pour lbxmb.png et les banner_url enregistrés.
Impact visible / technique : prévisualisation / capture bannière Discord avec logo LB’XMB joignable depuis Internet.
2026-05-15 ~01:40 — Web : fiche ressource — titre / description dans la carte
Contexte/problème : bandeau serveur layout.tsx (titre + extrait) au-dessus du main client → héros séparé du fil d’Ariane, de la sidebar et de la carte (expérience disjointe, cf. capture Dusklight).
Changements appliqués : suppression du <header> visible du layout — conservation du script JSON-LD + generateMetadata ; ResourceDetailClient.tsx — libellé « Ressource LB’XMB » + <h1> (nom) dans CardHeader au-dessus des pastilles console / catégorie ; la description reste dans le corps de carte (bloc existant).
Impact visible / technique : un seul flux visuel : breadcrumbs → carte avec titre aligné sur l’icône et les actions ; SEO : SoftwareApplication JSON-LD + meta inchangés (texte principal toujours côté client après hydratation).
Contexte/problème : fond d’accueil quasi noir ; le WebGL LightRays (chunk différé) peut arriver tard ou rester léger — peu de dégradés cyan visibles par rapport à l’habitude.
Changements appliqués : HomeContent.tsx — couche HomeHeroAmbientCss (radials type hero / bannière Discord, #00ccff + bas bleu nuit) sous le canvas, toujours peinte ; DeferredLightRays.tsx — GradientFallback (chargement WebGL) aligné sur les mêmes radials plus marqués.
Impact visible / technique : bleu / ambiance retrouvée tout de suite au premier paint ; WebGL enrichit par-dessus sans être le seul support visuel.
Contexte/problème : composant en LightRays.jsx + LightRays.css ; doublon inutilisé ui/backgrounds/LightRays.tsx (ancienne variante avec init WebGL conditionnée à la visibilité).
Changements appliqués : web/src/components/LightRays.tsx — source alignée React Bits (RaysOrigin, LightRaysProps, shader ogl) ; conteneur en Tailwind (cn) ; conservation des garde-fous LB’XMB : prefers-reduced-motion, visibilité via isVisibleRef + skip rendu (pas de recréation contexte), requestAnimationFrame pour l’init, DPR adaptatif, ResizeObserver, premier render synchrone, webglcontextlost, cleanup loseContext ; DeferredLightRays.tsx — DeferredLightRaysProps = LightRaysProps (import type) ; suppressions LightRays.jsx, LightRays.css, ui/backgrounds/LightRays.tsx.
Impact visible / technique : typage strict des props ; même comportement runtime qu’avant pour les pages existantes ; import dynamique @/components/LightRays inchangé (résolution .tsx).
Contexte/problème : harmoniser le fond LightRays sur les grandes pages publiques ; forum et dons utilisaient d’autres fonds (dégradés / halos) ; formulaires boutique sans même stack.
Changements appliqués : nouveau PageLightRaysBackdrop (fixed + DeferredLightRays + props catalogue identiques) ; forum — ForumPageBackground : base #020202 + PageLightRaysBackdrop #6366f1 + grille légère ; liste forum — main en z-10 ; accueil / ressources / shop / guides / support — remplacement du bloc inline par PageLightRaysBackdrop (couleurs inchangées : cyan, orange, violet, vert, jaune) ; fiche ressource + produit shop idem ; don / donateurs / donate/success — rayons rose / fuchsia / vert succès ; support/[type] — jaune comme /support ; boutique/ajouter & modifier — violet comme /shop ; guides ajouter + lecture guide (guides/[folder]) — vert comme /guides.
Impact visible / technique : une seule primitive pour les props WebGL ; contenu au-dessus via relative z-10 où nécessaire ; npx tsc --noEmit OK.
2026-05-14 — Web : LightRays — fond visible sans WebGL / « réduire les animations »
Contexte/problème : certains profils (ex. toi + un admin) ne voyaient pas le fond bleu / rayons alors que le reste de l’équipe oui — causes probables : prefers-reduced-motion: reduce (le code n’initialisait pas du tout le WebGL) ; ou WebGL indisponible / contexte perdu → conteneur vide sur #020202.
Changements appliqués : lib/light-rays-ambient-css.ts — style radial statique dérivé de raysColor ; LightRaysCssFallback.tsx ; LightRays.tsx — affichage de ce fallback si !allowMotion ou webglFailed ; échecs renderer / shader / premier render / boucle / webglcontextlost → releaseGl() + setWebglFailed(true) ; sortie « réduire les animations » → setWebglFailed(false) pour retenter le WebGL ; DeferredLightRays.tsx — GradientFallback teinté avec raysColor (via useMemo sur la couleur pour le chunk dynamique).
Impact visible / technique : fond teinté même sans GPU WebGL ou avec OS « réduire les mouvements » ; pas d’animation sur le fallback (CSS pur) ; npx tsc --noEmit OK.
2026-05-15 06:58 — Web : modification ressource ouverte à tous (validation admin/fondateur)
Contexte/problème : à la modification d’une ressource, certains comptes recevaient « Accès refusé » (403) — notamment les membres avec publish_resources qui n’étaient pas l’auteur, ou lorsque author_id ne correspondait pas.
Changements appliqués :
requiresResourceEditModeration : toute modification/suppression d’une ressource existante passe par la file resource_change_requests, sauf fondateur/admin (application directe).
PUT /api/dashboard/resources/[id] : suppression du contrôle auteur + publish_resources ; envoi en attente (202) pour tous les non-staff.
DELETE : même logique — demande de suppression modérée pour tous ; suppression immédiate réservée au staff.
La permission publish_resources reste utilisée pour la création (/api/resources/add), pas pour contourner la validation à l’édition.
Impact visible / technique : tout membre connecté peut proposer une modification ; message de confirmation « demande envoyée » ; validation dans Dashboard → Validations ressources (admin/fondateur).
2026-05-15 07:05 — Web : miniatures YouTube (fond noir sans vignette)
Contexte/problème : sur les fiches ressource / boutique, l’aperçu YouTube affichait un fond noir sans miniature — le custom element lite-youtube chargeait en différé et le poster i.ytimg.com n’était pas appliqué à temps (ou le ratio ::after ne remplissait pas le carrousel 16:9).
Changements appliqués :
lite-youtube-loader.ts : chargement unique du module + helper youtubePosterUrl.
LiteYouTube.tsx : poster inline (backgroundImage + <img> de secours) avant/après init du CE ; import CSS synchrone ; prop fillContainer pour le carrousel.
globals.css : classe .yt-fill-host (désactive le ::after 56.25 % quand le parent est déjà en 16:9).
Impact visible / technique : vignette HQ YouTube visible immédiatement ; lecture au clic inchangée (lite-youtube) ; plus de bloc noir vide dans le carrousel médias.
Contexte/problème : autoriser deux nouvelles IP publiques à se connecter au serveur net PS3 (webMAN / ps3netsrv), en conservant l’IP déjà autorisée.
Changements appliqués : recréation du conteneur ps3netsrv avec PS3NETSRV_WHITELIST="90.29.245.70 78.112.49.253 86.241.102.88" (trois motifs passés à ps3netsrv ; port 38008, volume /root/Webman-Games:/games inchangés).
Impact visible / technique : les PS3 derrière 78.112.49.253 et 86.241.102.88 peuvent joindre le serveur net (IP hôte + port 38008) ; 90.29.245.70 reste autorisée ; les autres IP sont refusées par ps3netsrv ; vérification : docker exec ps3netsrv ps aux | grep ps3netsrv.
Contexte/problème : PS3 ne listait pas les jeux (comme non connectée) ; logs ps3netsrv sans aucune connexion client ; port 38008 absent des règles UFW ; whitelist peu fiable en mode bridge Docker (IP source masquée).
Changements appliqués :
UFW : autorisation 38008/tcp depuis 90.29.245.70, 78.112.49.253, 86.241.102.88 uniquement.
Conteneur ps3netsrv recréé en --network host (écoute directe sur 38008, whitelist sur la vraie IP source).
Impact visible / technique : IP publique serveur 151.240.100.62, port 38008 ; 8 ISO dans /root/Webman-Games/PS3ISO ; côté PS3 : activer serveur net dans setup webMAN puis rescanner ; si échec : vérifier IP publique actuelle de la box (doit être dans la whitelist).
2026-05-15 18:35 — Web : page support (cartes + aide consoles)
Contexte/problème : les 4 cartes catégories n’étaient pas alignées sur grand écran (grille à 3 colonnes) ; l’aide consoles affichait des emojis génériques ; en-tête du formulaire (icône + description) écrasé sur mobile.
Changements appliqués :
TicketsManager.tsx : grille grid-cols-1 sm:grid-cols-2 lg:grid-cols-4, min-h/ h-full pour des cartes de même gabarit ; en-tête en pile mobile puis ligne, min-w-0 / break-words / text-pretty sur la description ; choix aide consoles en sections PlayStation / Xbox / Nintendo avec vignettes /api/consoles/images (réutilisation des IDs PLATFORM_TO_IMAGE_ID).
support-aide-consoles.ts : groupes et options (mêmes value qu’avant pour l’API tickets).
Impact visible / technique : 4 colonnes dès lg ; aide consoles alignée visuellement sur filtres ressources / guides ; formulaire lisible au toucher/small viewport.
Contexte/problème : échec d’interaction Discord au clic sur Afficher la liste des utilisateurs (champs embed > 1024 caractères avec ~90 / ~260 lignes).
Changements appliqués : defer + followup ; aperçu tronqué sous la limite ; fichier .txt (TSV id / pseudo / nick) avec les deux listes (regex pseudo + créé/rejoint même jour).
Impact visible / technique : le bouton répond sans erreur API ; exports exploitables dans un tableur.
Extension : Bouton Expulser temp. hors ligne sur la même vue (kick réservé aux membres au motif regex temp_accounts lorsque la présence est hors ligne — invisible assimilé hors ligne pour le gateway) ; en ligne / absent / NPD non expulsés ; désactivation des boutons (pas du menu) après action ; champ Actions sur l’embed ; timeout vue 900 s.
Correction / UX : fichier analytique.py cassé (méthodes cog fusionnées par erreur dans TempAccountsDetailView) → cog restaurée ; /analytique sans option slash ; choix uniquement via menu déroulant (discord.ui.Select) sur l’embed + boutons liste/kick lorsque Comptes temporaires est sélectionné.
2026-05-15 — Bot : script expulsion comptes « temp » + même jour (offline / NPD)
Contexte/problème : besoin d’expulser en masse les comptes au motif temporaire (TEMP_ACCOUNT_RE) et créés + rejoints le même jour (UTC), uniquement s’ils sont hors ligne ou en Ne pas déranger.
Changements appliqués : bot/scripts/kick_temp_same_day_offline_dnd.py — client discord.py avec intents membres + presences, guild.chunk(cache=True) (API 2.3, plus de wait= / timeout sur chunk) + asyncio.wait_for 240 s ; client.start(..., reconnect=False) et close() unique en finally (réduit Unclosed client session aiohttp) ; simulation par défaut ; --execute pour expulser (--delay). Python : venv racine /root/lbxmb.fr/venv ou source venv/bin/activate ; import protégé par message explicite.
Impact visible / technique : opération manuelle et traçable ; raison d’audit fixe ; liste des gardés (en ligne ou Absent hors DND/offline) avant toute action réelle.
2026-05-15 19:56 — Bot : anti-raid « compte temporaire » — ban immédiat remplacé par file 72 h + kick
Contexte/problème : l’option anti_compte_temporaire bannissait tout pseudo type utilisateur_12345 à l’arrivée ; souhait : ne pas sanctionner tout de suite, mais expulser (~72 h) seulement si pas d’avatar, compte très récent (même jour UTC ou arrivée dans les 10 jours après création — ex. créé le 25, rejoint le 29 avec theo_53201), et aucune présence online / idle / dnd observée (invisible reste offline côté gateway).
Changements appliqués : antiraid_handler.py — persistance bot/data/antiraid_deferred_temp.json ; enqueue au join si critères réunis ; annulation sur présence non-offline, départ membre, ou critères plus remplis à l’échéance ; boucle check_antiraid_deferred_temp (30 min) dans main.py ; hooks on_presence_update + on_member_remove ; libellés setup / défaut server_config_handler.
Impact visible / technique : plus de ban auto pour ce motif ; kick différé au plus tard sous ~72 h + 30 min ; intents déjà all (présences).
Contexte/problème : /analytique passait par has_mod_permissions (fallback rôles Admin/Support même sans config), au lieu du modèle « owner serveur ou rôles définis dans les permissions commande ».
Changements appliqués : analytique.py — has_owner_configured_command(member, "analytique") via _can_use_analytique (commande, menu Select, bouton kick groupé) ; message d’erreur explicite (/config → Commandes, option permissions par commande activée pour les rôles) ; utils/permissions.py — commentaire doc liste mod (sans analytique).
Impact visible / technique : seuls le propriétaire du serveur, les super-admins bot (BOT_OWNER_IDS) et les membres avec un rôle autorisé pour analytique (si le module Commandes est activé) peuvent utiliser la commande et les actions associées ; plus d’accès implicite par rôle Admin/Support hors config pour cette commande.
Contexte/problème : les libellés « Publier… » ne correspondaient pas au besoin ; un compte avec publish_resources voyait encore ses modifications passer en file de validation (seuls fondateur/admin appliquaient sans modération à l’édition) ; la boutique ignorait publish_shop à la mise à jour produit.
Changements appliqués :
catalogue specific-permissions.ts : libellés Ajouter / modifier (ressources, guides, produits) et Poster (annonces, actualités) ; alias legacy (gestionnaire_ressources, gestionnaire_guides, etc.) normalisés vers les clés canoniques ;
requiresResourceEditModeration aligné sur requiresResourceChangeModeration (même règle création + édition + suppression) ;
shop/[id] PUT : auto-approbation si publish_shop (via canPublishShopDirectly) ;
annonces / actualités : plus de dérivation via add_content (permissions dédiées uniquement).
Impact visible / technique : cocher Ajouter / modifier des ressources sur un utilisateur → ajout et modification sur /ressources sans message « en attente de validation » ; idem guides et produits boutique selon permission ; forum annonces/actualités inchangé côté clés (post_announcements, post_news).
Contexte/problème : (1) sous-catégorie « Archives / générique · Windows » listait aussi les binaires macOS (regroupement par console ressource au lieu du nom de fichier) ; (2) fiche ressource sans bloc ressources requises ; (3) fiche guide sans ressources ni guides requis.
Changements appliqués
github-asset-platform.ts : détection macOS / Windows / Linux dans les noms d’assets (-macos-, win32, etc.) ; groupKeyForDownload : si la base dit archive/autre mais le nom est explicite → plateforme fichier déduite.
download-variants-tree.ts (nouveau) : construction de l’arborescence modal ; sous-libellé console seulement si le nom reste archive/autre.
2026-05-17 — Web : mobile navbar, fiche ressource, YouTube, icônes
Contexte/problème : (1) recherche encombrante dans la barre mobile ; (2) titre ressource sous le logo sur mobile ; (3) vignettes YouTube noires ; (4) pastilles média (cercle vert) coupées ; (5) icônes ressources : carré arrondi, cadre invisible si PNG transparent.
Changements appliqués
Header.tsx : suppression du bouton recherche de la navbar mobile ; champ recherche pleine largeur sous « Navigation » dans le menu latéral.
ResourceDetailClient.tsx : en-tête logo + titre sur une ligne (mobile/desktop) ; carrousel : YouTubeThumbnail, contour actif en outline + padding pour éviter la coupe ; ResourceIconImage.
2026-05-17 — Fix build : guides en double dans PUT /api/guides/[folder]
Contexte/problème : bun run deploy:prod échouait — Turbopack : the name guides is defined multiple times (résultat SQL const [guides] + champ body guides).
Changements appliqués : requête → guideRows ; body → guides: guidesRequis ; guidesJson et payload modération alignés.
Impact visible / technique : build production à nouveau possible.
Contexte/problème : PS5 Linux — guide d’installation enregistré (resources.guides = ["12"]) mais invisible sur la fiche ; requête SQL SELECT … folder FROM guides échouait (colonne folder absente) → erreur avalée, liste vide. UX : requis/guides mélangés dans la carte des boutons.
API GET /api/resources/[id] et GET /api/guides/[folder] : chargement prérequis via l’helper.
ressources/ajouter/page.tsx : envoi systématique de requis et guides (y compris tableaux vides à l’édition).
ResourceDetailClient.tsx : cartes Ressources requises et Guides requis distinctes, placées sous la carte Télécharger / Modifier / Partager / Signaler.
Impact visible / technique : fiche PS5 Linux affiche le guide « Utilisation de Linux sur la ps5 » ; édition peut vider les listes ; sidebar plus lisible.
Contexte/problème : /guides/p/ps3/autre/1 → « Guide non trouvé » ; colonne SQL guides.guides absente → requête DB en échec puis fallback fichier inexistant ; GET /api/dashboard/resources/[id] sans auth ; édition guide/ressource d’un autre auteur possible pour comptes privilégiés.
API guide : SELECT sans colonne manquante (guide-db-schema.ts) ; ID numérique → pas de fallback disque si absent ; erreur DB → 500.
IDOR : GET/PUT/DELETE /api/dashboard/resources/[id] et PUT /api/guides/[folder] → canActOnDashboardResource (auteur ou staff).
Bot : forum_entraide_handler.py — message d’accueil forum 1490782914580054066 + bouton « Problème résolu » (auteur du fil ou rôles support) ; archivage du fil.
Impact visible / technique : guides à nouveau lisibles après deploy ; fils entraide accueillis automatiquement ; accès dashboard ressource/guide restreint au propriétaire ou staff.
Contexte/problème : section Mini-Jeux limitée à 3 boutons on/off + un seul salon ; besoin d’un menu par type (compteur, suite de mots, pendu) et de plusieurs salons avec états distincts (compteur infini / objectif).
Changements appliqués
mini_games_config.py (nouveau) : menu déroulant par jeu ; ajout de salons (ChannelSelect) ; gestion par salon ; compteur : mode infini / objectif + options (reset erreur, même user, etc.) ; suite de mots : longueur min. ; pendu : activation salon.
Config JSON : channels[] par jeu (migration auto depuis channel_id legacy).
setup_server.py : embed + vue délégués au nouveau module.
Impact visible / technique : /config → Mini-Jeux → choisir le jeu → ajouter/gérer plusieurs salons ; chaque compteur peut avoir son propre état.
2026-05-19 — Site : menu Applications, ressources mobile, GitHub, bannière Discord
Contexte/problème : demande menu Applications (services tiers), icônes OS en carrés sur mobile, autofill GitHub incomplet, bannière serveur avec logo et chiffres peu lisibles.
Changements appliqués
Header : entrée Applications (desktop : menu déroulant ; mobile : sous-liens) — Nextcloud, Webmail, Umami, Immich, Statut, Pterodactyl (applications-links.ts).
Ressources : ConsolePlatformGlyph — PNG prioritaire, react-icons en secours ; feuille mobile Header alignée ; android.png ajouté ; iOS dans PlatformOsIcon.
Contexte/problème : menu Applications mal placé (nav centrale) ; prise en charge tickets invisible ; description markdown peu lisible ; bannière guild 1232966743241130005 avec ancien HTML ; commande todo-list demandée.
Changements appliqués
ApplicationsLauncher : icône grille type Google à droite du profil (desktop + mobile), popover 3×3 avec icônes par service.
Tickets : bouton Prendre en charge dans la conversation staff ; permissions assign alignées (support_technique, permissions CSV) ; champs structurés aide consoles + panneau info (Console / Sujet / Description).
Bannière : suppression banner.html custom guild 1232966743241130005 ; template défaut sans badge « Site en ligne ».
Impact visible / technique : UX applications et support tickets corrigée ; bannière Discord régénérée au prochain /update-banner ou cycle auto ; todo personnel par utilisateur Discord.
2026-05-19 — Bouton prise en charge (liste) + sécurité dashboard
Contexte/problème : bouton « Prendre en charge » manquant dans la liste des conversations ; compte INF3C7ONxR7 (Discord 447088749914882080) accédait au dashboard alors qu’il était perçu comme simple membre.
Changements appliqués
MessageBell : icône UserCheck sur chaque ticket ouvert non assigné dans la liste fusionnée.
Dashboard : accès en liste blanche (fondateur, admin, développeur / developpeur uniquement) — corrige la faille VIP/modérateur seul.
INF3C7ONxR7 : en base il avait le rôle developpeur (pas membres) → permission remise à membres (id 272).
GET /api/admin/audit-dashboard-access (fondateur/admin) : liste tous les comptes avec accès dashboard.
Impact visible / technique : prise en charge en un clic depuis la liste ; seuls les rôles staff prévus voient /dashboard ; audit possible via API admin.
2026-05-19 — Rôle GitHub « développeur » ≠ accès dashboard
Contexte/problème : la liaison GitHub accorde le rôle developpeur pour afficher les dépôts sur le profil ; ce rôle ne doit pas ouvrir /dashboard ni les APIs staff (confusion avec INF3C7ON et comptes membres,developpeur).
Impact visible / technique : badge Développeur + repos GitHub sur le profil inchangés ; dashboard réservé fondateur/admin ; comptes GitHub-only ne voient plus le tableau de bord.
Contexte/problème : badges rôles dans la liste utilisateurs du dashboard sans les couleurs du profil ; besoin d’un journal d’activité par utilisateur (navigation, téléchargements, profil, rôles, forum, signalements…).
Changements appliqués
role-styles.ts + PermissionRoleBadge : palette alignée sur le profil ; liste utilisateurs desktop + mobile.
Table user_activity_logs + user-activity-log.ts : insertion non bloquante, libellés FR.
Dashboard /dashboard/logs : filtres pseudo/chemin, type d’action, pagination ; API GET /api/dashboard/activity-logs.
PageViewTracker : consultations de pages (utilisateurs connectés, hors dashboard) via POST /api/activity/page-view.
Impact visible / technique : rôles colorés comme sur le profil ; staff voit l’historique d’actions ; base extensible pour brancher messages DM, CRUD ressources/guides, etc.
Contexte/problème : bun run deploy:prod échouait (Can't resolve 'net'/'tls') car Header.tsx importait canAccessDashboard depuis permission-sync.ts (qui charge pool/mysql2) ; idem dashboard/logs via user-activity-log.ts.
Changements appliqués : modules client-safe permission-roles.ts et user-activity-log-labels.ts ; permission-sync.ts / user-activity-log.ts réservés au serveur.
Contexte/problème : menu Applications mal positionné ; icônes génériques ; services Affin/Bitwarden manquants ; /tag choix trop verbeux ; bloc analytique et stats dashboard avec grands vides et pourcentages fictifs.
Changements appliqués
Header : ApplicationsLauncher après l’avatar utilisateur (extrême droite desktop ; mobile : après le menu hamburger).
Applications : Vaultwarden (vaultwarden selfh.st) ; Immich retiré ; webmail → https://mail.lbxmb.fr ; iconOnWhite sur les tuiles.
Bot activité : délai hors ligne (jours + heures, NPD optionnel) avec suivi présence ; seuil messages (max / période) via analytics ; toggles compte temporaire + création/join même jour ; mode ET/OU ; modales /config pour régler les seuils.
Impact : menu apps à jour ; staff configure finement qui est expulsé (simulation dry-run conservée).
Contexte/problème : restauration qui s’arrêtait après les vocaux ; échec si serveur Communauté déjà actif ; libellés menu apply incorrects ; pas d’import config /config.
Changements appliqués
Salons : types en entier dans le snapshot ; exceptions par salon pour ne pas bloquer la suite ; passe secours + forums ; ch_id_map renvoyé.
Communauté : désactivation avant wipe si active ; bootstrap puis rattachement règlement/annonces ; cycle désactiver/réactiver en secours.
Config bot : bot_server_config dans le snapshot v6 ; option Importer les paramètres du bot avec remapping IDs.
UI : libellés Identité du serveur, Réglages, Rôles & permissions, Catégories & salons, Attribution des rôles, Liste des bannissements.
Contexte/problème : footer en pleine largeur sous la zone 100dvh + scroll interne, donnant l’impression d’être fixé en bas d’écran et chevauchant le menu catégories (sidebar).
Changements appliqués : suppression du piège 100dvh / overflow-hidden sur la liste ; défilement naturel de la page ; footer uniquement dans la colonne contenu (détail : déplacé hors du sibling pleine largeur) ; sidebar en sticky au lieu de stretch pleine hauteur ; fond opaque #020202 sur le footer.
Impact visible : un seul footer en bas du contenu, après scroll ; plus de superposition sur les catégories ; ne suit plus la fenêtre.
2026-05-19 — Sidebar catégories ressources : suit le scroll (desktop)
Contexte/problème : après passage au défilement page entière, le menu catégories ne restait plus visible (sticky neutralisé par overflow-x-hidden sur main/body).
Changements appliqués : panneau desktop en position: fixed sous le header (top-20, hauteur viewport) ; colonne fantôme w-72 pour réserver la place ; scroll interne du nav conservé si liste longue.
Impact : le menu consoles/catégories reste affiché pendant tout le scroll du contenu (liste + détail) ; footer inchangé en bas de colonne droite.
2026-05-19 — UX globale : recherche inline, ressources, menu mobile apps
Contexte/problème : recherche en modale peu pratique ; footer ressources mal placé ; sidebar catégories ; icônes Linux/Windows en carrés colorés ; détail ressource désaligné ; applications éparpillées sur mobile.
Changements appliqués
Recherche : SiteSearchField + useSiteSearch — saisie directe, suggestions sous la barre (header desktop + menu mobile).
Ressources : footer en bas de colonne (mt-auto) ; sidebar fixed sous le header ; métadonnées (console, catégorie, jeu, version) sous l’icône en colonne alignée.
Icônes OS : PNG /api/consoles/images/ via getConsoleImageUrlFromPlatform (guides, sidebar, PlatformOsIcon).
Menu mobile (droite) : sections Navigation / Applications ; bascule vers la grille apps (Umami, Pterodactyl, etc.) ; icône Applications retirée de la barre du header mobile.
2026-05-19 — Messagerie : DM, groupes, modération, menu mobile
Contexte/problème : erreur duplicate uk_client_vendor_product à la création DM ; groupes rattachés à un DM ; pas de messages join/leave ; modération groupe incomplète ; bouton réglages proche de la fermeture ; menu mobile nav/apps.
Changements appliqués
DM : findExistingDmConversation + gestion ER_DUP_ENTRY ; réutilisation de la conversation existante.
Groupe : nom à la création, upload avatar PNG/JPG/WebP, quitter le groupe, modérateurs (mêmes droits sauf expulsion du créateur).
UI : bouton réglages déplacé à gauche (après retour) ; messages système centrés dans le fil.
Menu mobile : volets Navigation / Applications (un seul ouvert) ; apps en liste ; recherche retirée du menu ; modale recherche via barre du bas (desktop inchangé : champ inline).
2026-05-19 — UX mobile / messagerie / recherche (retours utilisateur)
Contexte/problème : apps mobile avec logos tiers incohérents et onglet Applications en bas ; DM duplicate 137-142-0 ; bouton paramètres groupe ; pas de croix fermeture ; avatar groupe uploadé invisible ; apps absentes si déconnecté (desktop) ; recherche sans vignettes.
Changements appliqués
Menu mobile : Applications juste après Navigation (plus de flex-1 qui poussait en bas) ; icônes Lucide comme la nav ; volets inchangés.
DM : findExistingDm sur product_id = 0 sans filtre is_group ; recherche users → id PK ; gestion dup errno 1062.
Messagerie : réglages groupe au clic sur le nom ; croix fermeture ; getPhotoUrl accepte les chemins /api/....
Desktop : ApplicationsLauncher visible même non connecté.
Recherche : iconUrl / platform dans l’API + vignettes dans SiteSearchField et modale mobile.
Changements appliqués : extraction parse/affichage messages système dans vendor-conversation-messages.ts (client-safe) ; imports ajustés dans MessageBell et route GET vendor.
Impact : npm run build OK ; messagerie groupes/DM déployable.
2026-05-20 — Accueil, apps, messagerie mobile, DM vs groupe
Contexte/problème : badge « site en ligne / chasse aux bugs » sur l’accueil ; icône Navigation mobile ; copie de texte impossible (menu contexte bloqué) ; apps à regrouper (services vs pages) ; DM Interverti ouvrant un groupe ; long-press liste = supprimer au lieu de quitter / transfert couronne.
Changements appliqués
Hero : suppression du bandeau badge animé.
Header : icône boussole sur l’accordéon Navigation ; applications en sections Nos services / Nos pages (Nextcloud, Pterodactyl, Vaultwarden, Webmail, Affine, Umami, Status + Discord, Twitter, Youtube, Trustpilot, Github) avec icônes Lucide/react-icons en liste mobile.
Messagerie mobile : plus de preventDefault global sur le sheet ; menu message au long press sur l’avatar uniquement ; select-text sur les bulles ; menu conversation groupe → quitter (membre) ou transfert couronne + supprimer groupe (créateur) ; dialogue choix du nouveau propriétaire ; DELETE conversation groupe réservé au créateur.
DM : findExistingDmConversation filtre is_group = 0 pour ne jamais réutiliser un fil groupe ; fallback duplicate bidirectionnel.
API : PATCH transfer_owner + message système owner_transferred.
2026-05-20 — Messagerie mobile : barre du bas + nettoyage BDD LoupBlanke / interverti
Contexte/problème : la nav mobile (z-40) passait sous le sheet messagerie (z-50) ; conversations corrompues entre users 137 et 142 (Groupe (3) en product_id=0 + groupe test id 31).
Changements appliqués
UI : MobileBottomNav en z-[100] pour rester au-dessus du sheet ; MessageBell sheet mobile avec max-md:pb-16 pour ne pas masquer le bas du contenu sous la barre.
Données : suppression en base des conversations vendor_conversations id 4 et 31 (messages, pièces jointes, participants) ; script SQL archivé sous scripts/sql/cleanup-vendor-conversations-lb-interverti-2026-05-20.sql.
Impact : LoupBlanke et interverti peuvent recréer un DM propre ; barre du bas utilisable pendant la messagerie.
Contexte/problème : libellés et regroupement ne correspondaient pas au flux demandé (vue d’ensemble, etc.).
Changements appliqués : menu à 6 options (Vue d’ensemble, Rôles, Salons, Bot, Attribution rôles, Bans) ; « Vue d’ensemble » applique aussi réglages serveur + langue + règlement par nom si salons existants ; locale sauvegardée dans le snapshot.
Contexte/problème : ancienne commande /tag choix encore visible côté Discord pour certains clients.
Changements appliqués : commande racine /tag avec autocomplete (description mise à jour) ; /tags modifier inchangé. Resync via /sync admin pour retirer l’ancienne entrée.
Fichiers touchés : bot/commands/tag_commands.py
Backup /config plug-and-play + salons incomplets
Contexte/problème : config /config pas toujours réappliquée ; restauration des salons s’arrêtait après #général (catégories vides).
Changements appliqués : bot_server_config toujours dans le JSON ; import auto de /config si Catégories & salons coché ; restauration en 3 phases + secours 3 passes + anti rate-limit.
Contexte/problème : backups JSON sans bot_server_config ; salons enfants non créés après #général (429 Discord).
Changements appliqués : retries 429 avec retry_after sur toutes créations/suppressions ; 5 passes secours ; message explicite si backup sans /config ; rappel de cocher Rôles pour remap complet.
Contexte/problème : le lien « Espace Staff » pointait vers /dashboard ; le dashboard devait rester fonda/admin uniquement.
Changements appliqués : page /espace-staff (tickets en attente / mes tickets) ; menu profil avec Espace Staff (rôles support/modération) et Tableau de bord (fonda/admin) ; permissions canAccessStaffSpace / canUseStaffTicketTools.
Contexte/problème : le fond LightRays ne s’affichait pas pour une partie des utilisateurs. Les surcouches ajoutées (détection prefers-reduced-motion désactivant complètement le canvas, gestion d’échec WebGL bascule sur fallback CSS, ResizeObserver + handler webglcontextlost, premier rendu synchrone) introduisaient des chemins où le canvas n’était jamais monté.
Changements appliqués : remplacement de LightRays.tsx par le code source officiel de [React Bits — Light Rays](https://reactbits.dev/backgrounds/light-rays) (lib ogl), avec uniquement les export nécessaires pour la compatibilité projet (RaysOrigin, LightRaysProps, default export). Toutes les raysColor configurées par page (ressources, shop, guides, support, donate, démarrer-modding, forum, etc.) sont conservées à l’identique via PageLightRaysBackdrop / DeferredLightRays.
Impact : le fond animé s’affiche désormais pour tous les utilisateurs (y compris ceux avec prefers-reduced-motion: reduce) ; suppression des fausses pannes WebGL qui faisaient tomber le rendu sur le gradient CSS minimaliste. LightRaysCssFallback.tsx n’est plus référencé par LightRays mais conservé sur disque (non bloquant).
Contexte/problème : (1) le lien Vaultwarden pointait vers https://vault.lbxmb.fr (sous-domaine non utilisé) ; (2) l’icône Trustpilot était absente du lanceur desktop (ApplicationsLauncher) car le slug trustpilot n’existe pas sur selfh.st/icons (404/403 jsDelivr). Sur mobile l’icône était OK car servie par SiTrustpilot (react-icons).
Icône Trustpilot : ajout d’un champ optionnel iconUrl sur ApplicationLink (override de l’URL d’icône, prioritaire sur iconSlug selfh.st) ; l’item trustpilot pointe désormais sur le SVG local /brands/trustpilot.svg (étoile officielle, fill #00B67A) servi depuis public/, donc sans dépendance externe ni besoin d’ajouter un remotePatterns dans next.config.js.
Impact : Vaultwarden ouvre la bonne instance ; l’icône Trustpilot s’affiche correctement dans le lanceur Applications PC (mobile inchangé).
2026-05-20 20:16 — CSP RDAP + barre de recherche desktop compacte au scroll
Contexte/problème : (1) /checker?tab=safety (vérif domaine) : fetch('https://rdap.org/domain/<domain>') bloqué par CSP connect-src → outil de vérification de domaines KO en prod. (2) En mode scrollé (navbar « pill »), la SiteSearchField desktop conservait son min-w-[14rem] et débordait dans la barre compacte, écrasant les autres icônes / poussant le layout.
Changements appliqués
CSP : ajout de https://rdap.org à CONNECT_SOURCES dans src/lib/csp.ts (commentaire pointant vers /checker?tab=safety).
Header desktop : quand isScrolled est vrai, l’input plein-large est remplacé par un bouton icône Search (hidden md:inline-flex h-9 w-9 ...) qui ouvre le GlobalSiteSearch (dialog déjà utilisé pour le mobile et compatible desktop via md:max-w-lg). L’input redevient inline dès qu’on remonte en haut de page.
Impact : le checker safety appelle de nouveau RDAP sans erreur CSP ; la barre du haut redevient lisible quand elle se rétracte au scroll, la recherche reste accessible en un clic via l’icône.
Contexte/problème : après whitelist de rdap.org dans la CSP, l’appel échouait toujours en prod. rdap.org répond par un 301/302 vers le serveur RDAP du TLD (rdap.nic.fr pour .fr, rdap.verisign.com pour .com, etc.) → la CSP connect-src s’applique à chaque saut, donc soit on whiteliste tous les RDAP TLD serveurs (fragile, jamais complet), soit le navigateur se prend une NS_ERROR_DOM_BAD_URI. En plus, certains serveurs RDAP n’envoient pas les bons headers CORS → Cross-Origin Request Blocked … Status code: (null).
Changements appliqués
Nouvelle route serveur src/app/api/checker/rdap/route.ts : GET ?domain=..., validation regex stricte (RFC 1035 + punycode), rate-limit Redis (25/min/IP), timeout 8 s, fetch upstream https://rdap.org/domain/<d> avec redirect: "follow" (résolu côté Node, hors CSP/CORS), renvoie le JSON tel quel avec cache-control: private, max-age=60.
src/app/checker/components/Checkers.tsx : lookupRdapDomain() appelle désormais /api/checker/rdap?domain=... au lieu du host externe.
src/lib/csp.ts : suppression de https://rdap.org dans CONNECT_SOURCES (plus aucun appel direct depuis le navigateur).
Impact : le bouton « Vérifier » sur /checker?tab=safety redevient fonctionnel pour tous les TLDs (.fr, .com, .io, …), sans dépendance fragile à la liste des serveurs RDAP, sans CORS, et avec une surface CSP réduite.
Contexte/problème : (1) URL GitHub par défaut pointait sur l'org inexistante lbxmb (vrai handle = LB-XMB). (2) Sur premier chargement PC (cold cache), le canvas LightRays du fond d'accueil mettait 1-2 s à apparaître (flash noir) alors que mobile s'affichait correctement. (3) Demande d'audit sécu/perf : bun audit remontait 21 CVE (10 high, 8 moderate, 3 low) ; pas de sanitization XSS finale sur le rendu Markdown forum ; en-têtes de cache absents pour /brands et /static.
Changements appliqués
GitHub : getApplicationLinkSections() fallback https://github.com/LB-XMB (variable NEXT_PUBLIC_GITHUB_URL reste prioritaire).
Fond LightRays anti-flash :
DeferredLightRays.tsx : dynamic() sorti au scope module (était instancié dans useMemo), loading: GradientFallback retiré. Élimine le remount entre SSR et hydratation.
PageLightRaysBackdrop.tsx : ajout d'un calque CSS ambientLightRaysBackgroundStyle(raysColor) toujours rendu (SSR + statique). Le canvas WebGL alpha:true se superpose dessus quand le chunk async est prêt. Premier paint immédiat, plus aucune frame noire.
LightRays.tsx : suppression du await new Promise(r => setTimeout(r, 10)) artificiel — création du Renderer ogl dès que isVisible est true.
XSS hardening Markdown forum : ajout isomorphic-dompurify. parseDiscordMarkdown() passe sa sortie par DOMPurify.sanitize() avec whitelist explicite (tags + attrs alignés sur le markup généré). Spoilers : onclick inline remplacé par data-spoiler + délégation click/keydown dans MarkdownRenderer.tsx (accessible clavier).
XSS hardening JSON-LD : nouveau helper safeJsonLdString() dans seo-jsonld.ts qui échappe <>&\u2028\u2029 dans la sérialisation JSON. Utilisé dans shop/[id]/layout.tsx, forum/thread/[id]/layout.tsx, ressources/[slug]/page.tsx et ressources/[slug]/layout.tsx à la place de JSON.stringify() brut → un </script> glissé dans une description user ne casse plus le tag <script type="application/ld+json">.
Cache headers : next.config.js headers() ajoute Cache-Control: public, max-age=31536000, immutable sur /brands/* et /static/* (en complément de /_next/static/* déjà géré par Next).
Impact : org GitHub correcte dans la navbar ; / (et autres pages avec PageLightRaysBackdrop) cesse de flasher en noir au premier chargement PC ; surface XSS réduite (Markdown forum + JSON-LD) ; zéro CVE remontée par bun audit ; assets statiques /brands et /static mis en cache 1 an.
Hors scope (suivi) : audit Lighthouse complet, mesure bundle < 200 KB (ANALYZE=true), revue N+1, instrumentation Web Vitals — à traiter dans une session dédiée.
Contexte/problème : (1) npm install échouait : Override for postcss@^8.5.14 conflicts with direct dependency — [EOVERRIDE] (dep directe postcss ^8.5.14, override en ^8.5.13). (2) Sur /dashboard, la sidebar (DashboardSidebar) débordait : le bouton « collapse » et le dernier item (Logs) étaient poussés hors viewport.
Cause racine sidebar : CustomScrollbar (src/components/ui/custom-scrollbar.tsx) appliquait minHeight: 100vh sur la <Scrollbar> interne. Or il est utilisé dans <nav className="flex-1 min-h-0"> du DashboardSidebar qui occupe l'espace restant (100vh - header - footer). Le min-height: 100vh du scrollbar dépassait donc systématiquement son parent → le contenu poussait le <div className="p-4 border-t"> (chevron collapse) sous la ligne de flottaison.
Changements appliqués
postcss : override aligné sur la dep directe (^8.5.14) dans package.json (overrides + resolutions). npm install --dry-run et bun install OK.
Sidebar dashboard : suppression de minHeight: "100vh" dans custom-scrollbar.tsx. Le Scrollbar suit maintenant la taille effective de son parent (height={"100%"}), ce qui permet au footer du DashboardSidebar (chevron + collapse) de rester accroché en bas, et au scroll interne de fonctionner correctement quand les items dépassent la hauteur disponible.
Padding mobile résiduel : app/layout.tsx n'utilise plus un <div className="pb-20 md:pb-0"> inline. Création d'un wrapper client RootBodyShell qui n'applique pb-20 que sur les pages servies par le Header public (le MobileBottomNav n'est rendu que là). Sur /dashboard*, plus de gap noir de 80 px sous la mise en page sur mobile.
Impact : npm install repasse sans erreur ; la sidebar dashboard affiche tous ses items (Vue d'ensemble → Logs) avec le bouton collapse correctement positionné en bas ; mise en page dashboard mobile sans gap résiduel.
Contexte/demande : nettoyer la page /dashboard (Vue d'ensemble) en retirant les 4 blocs TasksTableMain (todoSite, todoPc, todoPhone, todoConsoles) et faire en sorte que le LightRays (background WebGL) démarre sans attendre un chunk séparé — quitte à augmenter le bundle initial.
Changements appliqués
src/app/dashboard/page.tsx : import et 4 sections <TasksTableMain category=... /> supprimés. OverviewPage ne rend plus que DashboardStatsv2 + AnalyticsDashboard. Le composant tasks-table-main.tsx est conservé en tant que tel (encore utilisable ailleurs si besoin), mais plus consommé depuis la vue d'ensemble.
src/components/ui/DeferredLightRays.tsx : remplacement de dynamic(() => import("@/components/LightRays"), { ssr: false }) par un import direct import LightRays, { type LightRaysProps } from "@/components/LightRays". Le composant LightRays reste « client only » via "use client" ; tous les accès navigateur (WebGL, IntersectionObserver, window) sont déjà confinés dans des useEffect, donc l'arbre SSR sort un <div> vide et la WebGL s'initialise à l'hydration sans flash (le calque gradient SSR de PageLightRaysBackdrop reste en dessous comme filet de sécurité).
Impact bundle : ogl (~20 kB gzip) et LightRays rentrent désormais dans le chunk client principal au lieu d'un chunk séparé chargé en différé. Compromis assumé pour avoir le background en place dès la première frame.
Vérifications : tsc --noEmit OK, pas de lint sur les fichiers touchés.
Contenu : page /dashboard/boutique + onglets DashboardContentTabs sur ressources, guides, boutique ; redirects /dashboard/validation, shop-requests, etc.
Équipe : règlement seed STAFF_REGLEMENT_DEFAULT, grades en liste verticale, API logs merge BDD + fallback API Discord (canAccessStaffHub).
Rôles : retirés des grades, role-styles, profil ; alias conservés en lecture legacy dans permission-roles.
UX : DashboardLoadingSkeleton pendant checkingPermissions.
Impact : hub staff cohérent avec les retours UX ; validations et boutique au même endroit que le catalogue ; logs Discord exploitables même si la table bot est vide (token bot requis).
Contexte : le chat staff restait une petite carte au centre ; demande d’un layout type Discord (salons à gauche, conversation au centre, staff en ligne/hors ligne à droite).
Changements : StaffHubChatLayout (3 colonnes), API GET /api/staff-hub/chat/bootstrap (salons chat-staff / annonces-staff, sync membres staff), messages via chat_messages + /api/chat/channels/[id]/messages, dashboardStaffChatInnerClass + overflow-hidden sur la zone scroll pour hauteur 100 %, présence via socket tickets (user_id corrigé).
Contexte : sur /mes-services/[orderId], statut/boutons power désynchronisés, console vide, fichiers et métriques à zéro ; page limitée aux admins alors que les APIs utilisaient déjà canAccessService.
Changements
Client API (pterodactyl-client.ts) : écriture fichier en corps brut + ?file=, upload en 2 étapes (URL signée), delete/rename avec root du répertoire courant, pteroGetServer propage les erreurs, pteroGetResources + limites, resolvePteroSocketUrl.
Helpers : getPterodactylInstance via resolveServiceOwner ; normalizePteroRoot.
Routes : stats/logs sur canAccessService + erreurs 502 explicites (plus de zéros silencieux) ; power erreur Ptero ; files passe directory ; console-stream reconnexion + keep-alive SSE ; ptero-health diagnostic.
UI (mes-services/[orderId]/page.tsx) : accès via GET service, isPterodactyl si pterodactyl_identifier, bannières/toasts erreur, console SSE avec reconnexion, stats avec limites.
Impact : expérience proche du panel Ptero pour propriétaires et délégués ; messages clairs si API Client ou Wings indisponibles.
Contexte : PTERODACTYL_CLIENT_API_KEY (ptlc_) renvoie 401 sur tout le panel ; power/stats en 502. La clé Application (ptla_) et l’API Wings du nœud fonctionnent.
Changements : nouveau pterodactyl-wings.ts (power, métriques, logs, commandes via token nœud) ; pteroGetServer / pteroGetResources / pteroPower / pteroSendCommand basculent automatiquement sur Wings si auth Client échoue ; console-stream passe en polling logs Wings ; message explicite pour les fichiers (toujours Client API).
Action admin : régénérer une clé Identifiants API (compte admin du panel) → PTERODACTYL_CLIENT_API_KEY pour fichiers + WebSocket temps réel.
Contexte : nouvelle installation Pterodactyl sur ms.lbxmb.fr ; clés ptla_ / ptlc_ régénérées ; tests provisioning sur l’œuf Vanilla Minecraft (f7aa8da4-918a-4fbb-8f6e-cf1b101b0fe2, nid 5f1c65a1-5562-4b9a-b778-705f2145a028).
Changements : .env / web/.env.local → PTERODACTYL_URL=https://ms.lbxmb.fr, nouvelles clés Application + Client, PTERODACTYL_EGG_GAME_SERVER et PTERODACTYL_EGG_DEFAULT = Vanilla ; mapping PTERO_EGG_MAP Minecraft aligné ; URLs pterodactyl_panel_url en BDD passées de panel.lbxmb.fr à ms.lbxmb.fr ; pm2 restart web-prod ; whitelist IP clé Client (BDD gameservice.api_keys) : allowed_ips vidé pour autoriser 127.0.0.1 (appels Next.js locaux).
Impact : provisioning jeu → Vanilla par défaut ; mes-services (power, fichiers, console) utilisent le nouveau panel si la clé Client est acceptée depuis le serveur web.
Contexte : règlement modifiable par tous les admins ; PJ chat affichées comme nom de fichier ; espacement et regroupement messages ; header mobile masqué au clavier ; pas de mentions @staff vers la boîte à lettres.
Changements : règlement en lecture seule + bouton « Modifier » réservé au fondateur (API PATCH idem) ; preview images/vidéos (png, jpeg, webp, gif, mp4, mkv…) via URLs corrigées et Content-Disposition: inline ; regroupement messages même auteur sur 1 min ; texte compact (sauts de ligne réduits) ; layout mobile chat fixe sous le header dashboard ; autocomplete @pseudo + notifications chat_mention avec lien /dashboard/equipe/chat.
Impact : UX chat staff type Discord ; mentions notifient les staff dans la cloche ; fondateur seul édite le règlement.
Contexte : images chat 404 (Fichier non trouvé) ; menu profil mobile coupé ; chat staff sans fil d’Ariane + header ; PWA en simple raccourci ; règlement en texte brut.
Changements : uploads chat dans uploads/chat/ (getUploadsRoot) + repli anciens chemins ; UPLOADS_DIR en .env ; fil d’Ariane mobile dans le header dashboard ; menu profil mobile en sheet (déconnexion visible) ; layout chat flex (header + messages + composer) ; manifest PWA simplifié (sans shortcuts) ; SW conservé à la mise à jour ; règlement rendu via MarkdownRenderer (style Discord).
Contexte : clic image ouvrait toute la zone block max-w-md ; zone de saisie chat hors écran (colonne centrale sans flex-1) ; fil d’Ariane mobile sur deux lignes ; --- du règlement sans ligne pleine largeur.
Changements : images en inline-block w-fit + ouverture au clic uniquement sur l’<img> ; wrapper flex-1 min-h-0 autour de StaffHubChannelPanel ; breadcrumb flex-nowrap + défilement horizontal ; --- → <hr class="discord-hr"> dans markdown.ts + style CSS.
Impact : composer toujours visible en bas du chat ; navigation mobile lisible sur une ligne ; règlement avec vrais séparateurs.
2026-05-22 — Mobile dashboard/support, IP visiteurs, messagerie clavier
Contexte : page édition utilisateur hors champ sur mobile ; IP non enregistrées après retrait du proxy Cloudflare ; chat staff et messagerie masqués par la barre d’URL / clavier ; cartes support alignées à droite.
Changements : responsive users/[id]/edit ; nginx CF-Connecting-IP → $real_client_ip ; getClientIps priorise X-Real-IP ; enregistrement IP à l’inscription OAuth (oauth-bootstrap) ; hook useMobileVisualViewport (chat staff 100dvh, composer messagerie sans pb-16 quand clavier ouvert) ; cartes support centrées sur mobile.
Contexte : mod/support ne doivent voir que la section Équipe + tickets filtrés par type ; support sans logs Discord ; logs Discord vides sur le site malgré le bot actif.
Logs Discord : API fusionne salons Discord (token) + BDD ; BOT_INTERNAL_SECRET + DISCORD_TOKEN dans env web ; bot persiste via 127.0.0.1:25581 ; log des changements de rôles membre côté bot.
2026-05-22 — Bot Discord : traduction en User Install
Contexte : pouvoir installer l’app sur son compte et utiliser Traduire ce message (menu contextuel) partout, pas seulement sur le serveur LB’XMB.
Changements : allowed_installs(users=True) + allowed_contexts (DM, salons privés, guildes) sur /traduire et le menu Traduire ce message ; /traduire utilisable en DM (fetch dans le salon courant) ; menu contextuel en méthode de cog (sync global) ; doc USER_INSTALL.md mise à jour ; pm2 restart bot-python.
Impact : clic droit → Apps → Traduire ce message sur n’importe quel serveur/DM après « Ajouter à mes apps » + User Install activé dans le Developer Portal.
2026-05-22 — Web : Socket Firewall, icônes OS, catégorie Sites
Contexte : protection supply chain npm/bun ; icônes Linux/Windows cassées sur /ressources (PNG linux = copie Android) ; besoin d’une liste de sites avec ajout par URL + metadata auto.
Socket Firewall : web/scripts/install-deps.sh (sfw bun install) ; deploy.sh et scripts install:safe ; doc [Socket Firewall](https://socket.dev/blog/introducing-socket-firewall).
Icônes OS : PlatformOsIcon → react-icons (SiLinux, FaWindows, etc.) ; ConsolePlatformGlyph priorise OS + icône Globe pour « Sites ».
Sites : plateforme Sites + catégories dans consoles-config ; sidebar toujours visible ; /ressources/ajouter/sites ; API url-metadata + add-site (OG fetch, favicon, lien externe).
2026-05-23 — Cap : branding LB'XMB (logo à la place du lien Cap)
Contexte : le widget affichait le lien « Cap » en bas à droite ; demande de branding site + taille cohérente.
Changements : masquage .credits dans le shadow DOM ; logo /lbxmb.png en overlay ; variables CSS (52px, max 320px, padding pour le logo) ; lien dépannage → support LB'XMB.
Contexte : LSO (PS4 + Xbox 360) invisible côté Xbox 360 ; demande de catégories DLC/Update/CFW, sous-catégories PKG/Apps, filtre mods par jeu, métadonnées fiche sur 2 lignes.
Changements :
Filtre API plateforme élargi (categories_by_console_json) + backfill BDD depuis structure.json (144 ressources).
Contexte : la fiche pointait vers Ressources/Xbox 360/DLC/... mais le zip était resté sous .../Online/... après changement de catégorie.
Changements : déplacement du dossier GTA ONLINE DLC vers uploads/Ressources/Xbox 360/DLC/Grand Theft Auto V/.
Impact : le lien /api/uploads/Ressources/Xbox 360/DLC/.../DLC GTA ONLINE Xbox 360.zip répond à nouveau (HTTP 200).
2026-05-25 — Déplacement auto des fichiers lors d’un changement console/catégorie
Contexte : après reclassification (ex. Online → DLC), les métadonnées pointaient vers un nouveau chemin mais les fichiers restaient à l’ancien emplacement (404).
Changements : module resource-relocate-on-change.ts ; lors de la modification d’une ressource, déplacement automatique des dossiers Ressources/… (par console) et mise à jour des chemins téléchargements / médias / logo.
Impact : plus besoin de déplacer manuellement sur le disque quand on change console, catégorie, jeu ou nom via le formulaire d’édition.
Captcha connexion : le widget affichait « Vérifié » mais les boutons OAuth restaient grisés — React ne recevait pas l’événement solve du web component cap-widget. Correction via addEventListener natif dans CapWidget.tsx.
Lien Website ressource : champ ignoré à l’édition (non lu depuis reseaux_sociaux, non persisté dans execute-dashboard-resource-put.ts). Ajout de website au chargement du formulaire et à la sauvegarde PUT.
Impact : connexion Discord/Google/GitHub possible après validation du captcha ; le champ Website est enregistré à l’ajout et à la modification.
Phase 2 (A11y + auth) : aria-controls sur combobox ressources/guides/recherche ; reduced motion global (globals.css, ReducedMotionObserver, login/VIP/home orbes) ; auth explicite en tête des server actions maintenance (throw si non admin).
2026-05-25 ~ — Navigation perçue plus instantanée (style Twitter/X)
Contexte : changement de page avec écran vide (loading.tsx 50vh) + Header qui refetch auth/structure à chaque montage → sensation lente vs apps type Twitter.
Changements :
Barre de progression fine en haut (NavigationProgress) au clic sur un lien interne.
loading.tsx racine neutralisé (plus d'écran plein page pendant la transition).
Cache React Query partagé pour auth (useAuthMe) et données menu Header (forum, structure ressources) — réutilisé entre pages sans refetch.
2026-05-25 — Rollback partiel post React Doctor + fix URLSearchParams
Contexte : codemods React Doctor (button-type, split chat, router-destructure) ont cassé des pages ; erreur runtime 'get' called on an object that does not implement interface URLSearchParams sur le dashboard.
Rollback : restauration depuis web-old-20260307-231626 des pages lourdes (chat, forum, profil, shop, MessageBell, VoiceChannel, TicketsManager, UsersSidebar, dashboard orders, mes-services).
Fix critique : le codemod router-destructure destructurait get depuis useSearchParams() — appel sans this → crash. Corrigé dans ~15 fichiers (DashboardContext, support, login, ressources, guides, checker, etc.) ; codemod désactivé pour useSearchParams.
2026-05-25 — Prod servait encore l'ancien build .next (URLSearchParams)
Cause : PM2 tournait sans NEXT_DIST_DIR → Next utilisait .next (build 19h12, bug { get } = useSearchParams()), pas .next-staging (build corrigé 19h26).
Correctifs : miroir .next-staging → .next à chaque deploy ; server.js force .next-staging en prod si env absent ; pm2 restart ecosystem.config.js --update-env.
Impact : le dashboard et les pages avec query params ne crashent plus côté serveur.
Contexte : fond plat noir sur dashboard/boutique après rollback React Doctor ; pseudos/noms en pilules blanches dans les tableaux staff ; points colorés sous icônes header ; scrollbar dashboard sans style animé.
Changements :
PageLightRaysBackdrop réintégré au layout dashboard (indigo) et page /shop (violet) ; fond opaque #050507 retiré.
Boutons liens utilisateurs/ressources : variant="link" + h-auto p-0 (plus de fond blanc bg-primary).
Header nav : underline au repos w-0 au lieu de size-0.5 (points visibles).
Zone scroll principale dashboard enveloppée dans CustomScrollbar (classes scrollbars-* + hover animé).
Impact : rayons lumineux visibles, tableaux staff lisibles, nav header propre, scrollbar fine avec transition au survol.
2026-05-26 ~21:00 — Infra : 3 environnements dev / ptb / prod isolés
Contexte : besoin d’une chaîne dev.lbxmb.fr → ptb.lbxmb.fr → lbxmb.fr avec DBs séparées et workflow de promotion piloté depuis le dashboard dev (sans toucher la prod). La prod reste intacte ; les futurs refontes (messagerie, ressources, React Doctor, LightRays) se feront uniquement sur dev.
Architecture posée :
lbxmb.fr (prod) : web/, port 25581, DB lbxmb, web-prod 5x cluster — inchangé.
ptb.lbxmb.fr (pré-prod) : ptb-web/, port 25583, DB lbxmb_ptb, web-ptb 2x cluster — clone T0 prod (111 users importés).
dev.lbxmb.fr (dev) : dev-web/, port 25582, DB lbxmb_dev, web-dev 1x fork — schéma prod seul + seed minimal (interverti + LoupBlanke avec permission admin + follow mutuel).
DBs : scripts/sql/00-init-envs.sql (CREATE DB + users lbxmb_ptb / lbxmb_dev, mots de passe dans backups/.db-{dev,ptb}-pass chmod 600) + scripts/sql/01-seed-dev.sql (seed admin + follow mutuel) ; orchestré par scripts/init-envs-databases.sh.
Code source : rsync web/ → ptb-web/ et web/ → dev-web/ (exclus : node_modules, .next*, .env.local, uploads, cap/data). bun install --frozen-lockfile + next build séparés.
.env.local dédiés : DB / NEXT_PUBLIC_APP_URL / UPLOADS_DIR distincts ; dev ajoute NEXT_PUBLIC_ENV=development + DEV_ALLOWED_DISCORD_IDS=674329017339346955,1139951514694193323.
Nginx : nouveau vhost ptb.lbxmb.fr (HTTP→HTTPS + HTTPS proxy 25583) ; cert Let’s Encrypt obtenu via webroot ; le vhost dev.lbxmb.fr existant (port 25582) est intact.
PM2 : web/ecosystem.config.js réécrit pour exposer web-prod, web-ptb, web-dev (chacun avec son cwd, PORT, NEXT_PUBLIC_APP_URL, base Redis dédiée). beta-web-dev supprimé.
Hardening dev : dev-web/src/proxy.ts redirige toute requête sans cookie BetterAuth vers /login (sauf assets/auth) ; nouveau composant DevAccessGuard server-side dans le RootLayout vérifie le Discord ID et redirige vers https://lbxmb.fr si hors whitelist.
Console déploiement : nouvelle section "Développement" en bas de la sidebar (visible uniquement quand NEXT_PUBLIC_ENV=development, masquée sur prod/ptb au build). Page /dashboard/developpement : statut PM2 des 3 envs + 2 boutons "Promouvoir dev → ptb" / "Promouvoir ptb → prod" + flux de logs SSE en temps réel.
Script de promotion : scripts/promote.sh --from <env> --to <env> : backup tar + dump DB cible, rsync --delete, diff de schéma MariaDB (opt-in via --apply-schema), bun install + next build atomique sur .next-staging-new + mirror .next, pm2 restart web-<TO>, sanity-check HTTP, rollback automatique tar+dump en cas d’échec.
API : POST /api/dev/promote (lance le script en background, renvoie jobId) + GET /api/dev/promote/stream?jobId=… (SSE tail du log) ; les deux protégés par la même whitelist Discord, retournent 401 sans session.
Bot Discord : WEBSITE_API_URL du bot corrigé de http://localhost:25583/api (ptb) à http://localhost:25581/api (prod), bot redémarré.
Vérifications post-déploiement :
pm2 list montre web-prod (5), web-ptb (2), web-dev (1) tous online.
curl -I https://lbxmb.fr → 200, curl -I https://ptb.lbxmb.fr → 200, curl -I https://dev.lbxmb.fr sans cookie → 307 vers /login?from=/.
mysql lbxmb_dev contient bien les 2 admins seed + follow mutuel ; mysql lbxmb_ptb contient 111 users (clone prod intact).
bash scripts/promote.sh --from foo --to bar refuse correctement les combinaisons invalides.
Reste à faire (manuel) : ajouter les callback URLs dans le portail Discord Developer pour https://dev.lbxmb.fr/api/auth/callback/discord et https://ptb.lbxmb.fr/api/auth/callback/discord (BetterAuth les construit automatiquement à partir de baseURL, donc indispensable pour finaliser l’OAuth Discord sur dev/ptb).
Hors phase 1 : refonte messagerie unifiée + popup type Messenger, refonte page ajout ressources (parcours candidature), background LightRays / React Doctor — seront faits uniquement sur dev une fois cette infra stabilisée.
PageLightRaysBackdrop : z-index -z-10 pour éviter que le fond masque le contenu.
Fichier Background Site.html (poste local Windows) non présent sur le serveur — à déposer dans le repo ou coller le snippet pour affiner les paramètres OGL.
3. Messagerie (MessageBell) :
Suppression des onglets Conversations / Tickets : liste unique triée Tickets → Vendeur → DM (sections sticky).
Tickets staff non assignés fusionnés ; masqués si pris en charge par un autre staff.
Composeur : sticky + safe-area mobile, + pièces jointes, micro (WebM), Entrée = envoi sur PC / saut de ligne sur mobile.
Bouton Nouveau message + modale MessageComposer (DM / groupe).
Menu contextuel : Copier, Voir profil, Envoyer un message, etc.
Socket join_vendor_conv pour rafraîchir les messages vendor/DM.
Reste à faire (phase 2 suite) : popup Messenger déplaçable PC (FloatingMessagePanel), gestion groupe complète (couronne, quitter), page ressources type candidature multi-étapes.