- src/lib/tags.ts (pur) : mots-clés par métier pour les 104 catégories,
vocabulaire transversal détecté dans la description (fait maison, bio,
livraison, sur rendez-vous, devis gratuit…), suggestTags() qui combine
métier, commune et description, autoTags() qui n'intervient qu'à vide.
- Enregistrement : addMember, updateMember et updateOwnProfile passent par
resolveMemberTags() ; un champ laissé vide reçoit les suggestions, une
saisie n'est jamais remplacée.
- Formulaires : composant TagsField (champ non contrôlé + pastilles de
suggestions recalculées à chaque frappe depuis catégorie, commune et
description ; « Tout ajouter »), dans l'espace adhérent et la fiche staff.
- Fiche publique : les fiches jamais ré-enregistrées affichent les tags
suggérés et les transmettent dans LocalBusiness.keywords.
- tests/tags.test.ts : couverture de chaque métier, nettoyage, détection,
ordre des suggestions, non-écrasement.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDbKbJrPWweXXYSxA8MXW7
- Fiches adhérents : URL canonique /adherents/12-au-bon-pain-frouard
(memberPath). L'identifiant en tête suffit ; /adherents/12 et tout slug
périmé sont redirigés de façon permanente vers la forme canonique, donc
les anciens liens et les partages sociaux restent valides. Tous les liens
(accueil, carrousel, annuaire, JSON-LD, sitemap, publications Facebook /
LinkedIn) passent par memberPath.
- Pages métier /annuaire/[categorie] : une page indexable par catégorie
avec titre, introduction (effectif, communes), grille d'adhérents, fil
d'Ariane et ItemList. Sans adhérent : noindex et hors sitemap. Section
« Parcourir par métier » sur l'annuaire et « Autres métiers » sur chaque
page pour le maillage interne.
- Composants partagés MemberCard et CategoryLinks ; slugify déplacé dans
src/lib/slug.ts.
- Backend : conseils de rédaction sous les champs description et tags des
formulaires adhérent (espace adhérent et fiche staff).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDbKbJrPWweXXYSxA8MXW7
- Accueil : le bandeau « Faites partie de l'aventure » affiche le nombre
d'adhérents validés (statut active) au lieu de « plus de 120 » codé en dur.
- Pied de page : les boutons Facebook / LinkedIn ont la même largeur (grille à
une colonne dimensionnée sur le plus long) et une icône à largeur fixe.
- Catégories : référentiel unique dans src/db/categories.ts (104 métiers, dont
assurance, bazar/discount, grande distribution, librairie-presse, tabac,
vétérinaire, auto-école, etc.), inséré par la migration 0012 avec
ON CONFLICT (slug) : les catégories manquantes apparaissent au prochain
démarrage, sans écraser un libellé renommé depuis le backend. Le seed et
l'action « Ajouter une catégorie » réutilisent ce référentiel et sa palette.
- tests/categories.test.ts vérifie unicité des slugs, contraintes de la table
et cohérence entre le référentiel et la migration.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDbKbJrPWweXXYSxA8MXW7
- Layout : metadataBase, gabarit de titre « %s · Plein R », description,
mots-clés, Open Graph / Twitter, robots, icônes, manifeste, viewport et
couleur de thème ; JSON-LD Organization + WebSite (recherche annuaire).
- Chaque page publique pose titre, description (≤ 160 caractères) et
canonique via pageMetadata() ; l'annuaire ignore ainsi ?q=. Fiche adhérent :
titre « Nom · Catégorie à Ville », description dérivée, LocalBusiness avec
adresse, contacts et horaires (OpeningHoursSpecification), BreadcrumbList.
Annuaire : ItemList des adhérents. Association : BusinessEvent par
rencontre à venir. Rencontres passées : fil d'Ariane.
- Nouvelles routes : robots.txt, sitemap.xml (pages statiques + adhérents
actifs), manifest.webmanifest, image de partage 1200×630 générée avec le
logo, page 404 dédiée (noindex).
- /backend, /login, /inscription/* et les fiches inactives en noindex.
- Landmarks <main> sur toutes les pages publiques, fil d'Ariane en <nav>.
- serializeJsonLd() échappe <, > et & : les saisies des adhérents ne peuvent
pas fermer le bloc <script>. Tests unitaires dans tests/seo.test.ts.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDbKbJrPWweXXYSxA8MXW7
Suite de l'audit : les cinq points laissés en suspens sont traités.
Mot de passe
- `changeOwnPassword` exige désormais le mot de passe actuel. Un poste laissé
ouvert ne suffit plus à s'approprier un compte. Les erreurs reviennent sur
l'écran avec un message au lieu d'une page d'erreur brute.
Sessions révocables
- Colonne `users.session_version`, portée dans le jeton et comparée à la base.
- `getSession()` (src/lib/session.ts) remplace `auth()` sur les 20 pages et
actions : rôle, rattachement adhérent et existence du compte sont relus à
chaque requête. Supprimer un compte ou réinitialiser un mot de passe coupe
immédiatement les sessions ouvertes, sans attendre l'expiration du jeton.
Dépendances — de 8 vulnérabilités (2 critiques) à zéro
- next 15.5.19 → 15.5.21, next-auth beta.25 → beta.32, drizzle-orm 0.38 → 0.45.
- postcss et sharp forcés par `overrides` sur leurs versions corrigées, Next ne
les ayant pas encore reprises ; drizzle-kit et esbuild montés côté outillage.
- `npm audit fix --force` a été écarté : il proposait de RÉTROGRADER Next en
9.3.3 et eslint-config-next en 12, ce qui aurait cassé l'application.
- `eslint-config-next` traînait dans node_modules sans être déclaré : retiré.
CSP complète
- Politique à nonce posée par le middleware, nonce régénéré à chaque requête,
`script-src` sans 'unsafe-inline'. `style-src` garde 'unsafe-inline' : tout le
design repose sur des attributs style, et une injection de style n'a pas la
portée d'une injection de script.
- Conséquence assumée : rendu dynamique pour toutes les pages, un HTML
pré-généré ne pouvant pas porter de nonce.
Tests
- `npm test` (runner natif node:test via tsx), 18 tests sur le filtre XSS, la
limitation des tentatives de connexion et le chiffrement des jetons.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb