ADR: Architecture de Sidebar Bimodale Starlight et Séparation des Index Algolia
ADR: Architecture de Sidebar Bimodale Starlight et Séparation des Index Algolia
Décision architecturale pour l'expérience de documentation publique vs authentifiée et isolation de la recherche Algolia DocSearch
- Statut : Proposé
- Date : 2026-10-12
- Auteur : Team (Agent)
- Cycle : DOC → SPEC → CODE → TEST → INTÉG
1. Contexte et Problématique1/3
Dans l’état actuel de la plateforme, la documentation Starlight (/docs/*, /gettingstarted/*) présente l’intégralité de l’arborescence des documents dans la barre latérale (Sidebar), quel que soit le statut de l’utilisateur.
1. Contexte et Problématique2/3
Cette organisation pose trois problèmes majeurs :
- Expérience utilisateur publique dégradée : La majorité des spécifications techniques (
docs/) et des guides d’architecture étant marquéevisibility: 'private', un visiteur non authentifié naviguant surhttp://localhost:8787/docsvoit une liste massive de documents verrouillés. Chaque clic vers l’un d’eux déclenche une redirection brutale vers/login. Cela donne l’impression erronée que la consultation de la documentation requiert obligatoirement un compte, nuisant à l’adoption des guides publics.
1. Contexte et Problématique3/3
- Point d’entrée
/docsmanquant ou renvoyant 404 : La navigation vershttp://localhost:8787/docsn’atterrit pas sur un document d’accueil public cohérent et peut être interceptée comme un slug manquant. - Risque de fuite d’informations via Algolia DocSearch : L’API Key de recherche Algolia (
PUBLIC_ALGOLIA_SEARCH_API_KEY) est exposée côté client dans les bundles JavaScript. Si les documents privés sont indexés dans le même index Algolia que les documents publics, un attaquant ou un utilisateur curieux peut interroger l’API Algolia directement pour récupérer les titres, extraits de code et contenus sensibles des documents privés, contournant ainsi le filtrage d’affichage côté navigateur.
2. Décisions Architecturales
Nous décidons d’adopter une architecture bimodale stricte :
┌──────────────────────────────────────────────┐ │ Requête entrante (/docs/...) │ └──────────────────────┬───────────────────────┘ │ ▼ [Astro SSR Middleware] Vérifie session Better Auth (locals.user) │ ┌───────────────┴───────────────┐ │ │ [Non Authentifié : Public] [Authentifié : Membre] │ │ ▼ ▼ [Starlight routeMiddleware] [Starlight routeMiddleware] - Sidebar : Publics uniquement - Sidebar : Publics + Privés - Groupes vides éliminés - Badge 'Privé' sur docs privés - Pagination : Publics seuls - Pagination : Complète │ │ ▼ ▼ [Algolia DocSearch Client] [Algolia DocSearch Client] - Index : docsearch_public - Index : docsearch_all - Zéro donnée privée stockée - Accès complet aux résultats2.1. Bimodalité de la Sidebar via Starlight routeMiddleware
Starlight fournit nativement le point d’extension routeMiddleware via l’utilitaire officiel defineRouteMiddleware (@astrojs/starlight/route-data).
- Enregistrement officiel :
Dans
apps/web/astro.config.mjs, déclarerrouteMiddleware: './src/starlightRouteData.ts'. - Filtrage dynamique par SSR :
Le middleware de route s’exécute côté serveur lors de chaque rendu de page Starlight.
- Il lit l’état
context.locals.userinjecté en amont par le middleware d’authentification Astro (apps/web/src/middleware.ts). - Visiteur Public (
!locals.user) :- Filtrage récursif de l’arbre
starlightRoute.sidebar: seuls les liens vers des documentsvisibility: 'public'sont conservés. - Élagage (pruning) des groupes vides : tout groupe de navigation (
SidebarGroup) dont tous les enfants sont privés est retiré pour éviter les sections vides. - Assainissement de la pagination (
starlightRoute.pagination) : suppression des liensprevounexts’ils pointent vers un document privé.
- Filtrage récursif de l’arbre
- Membre Authentifié (
locals.userprésent) :- Préservation de tous les documents (publics et privés).
- Ajout dynamique d’un badge visuel
{ text: 'Privé', variant: 'caution' }sur les éléments privés pour distinguer immédiatement le contenu interne du contenu public.
- Il lit l’état
2.2. Routage et redirection de l’atterrissage /docs
Pour garantir un accès fluide :
- La route
/docs(ou/docs/) redirige proprement (302/301) vers la page d’accueil ou le premier guide public de référence (ex:/docs/gettingstarted/01-setup-local/ou/docs/l1_contenu/sanity/visual-editing/astro-visual-editing-by-sanity/). - Le fichier
apps/web/src/pages/[slug].astroredirige les slugs réservés vers la documentation au lieu de renvoyer une erreur 404.
2.3. Cloisonnement étanche de la recherche Algolia DocSearch1/2
Pour interdire toute fuite de contenu sensible :
- Deux index distincts sur Algolia :
docsearch_public(ou${ALGOLIA_INDEX_NAME}_public) : contient exclusivement les documents avecvisibility: 'public'.docsearch_all(ou${ALGOLIA_INDEX_NAME}_all) : contient l’ensemble des documents (publics + privés).
2.3. Cloisonnement étanche de la recherche Algolia DocSearch2/2
- Script d’indexation (
index-algolia-docs.ts) :- Génère les records pour les deux cibles lors de la commande
task index:algolia. - Pousse les records publics vers l’index public et l’ensemble des records vers l’index complet.
- Génère les records pour les deux cibles lors de la commande
- Sélection dynamique côté client (
docsearch.ts) :- Le composant DocSearch charge l’index
docsearch_publicpar défaut. - Pour les sessions authentifiées, il bascule sur
docsearch_all.
- Le composant DocSearch charge l’index
3. Justification du Choix
3.1. Pourquoi routeMiddleware plutôt que le remplacement du composant Sidebar.astro ?
- API officielle Starlight :
defineRouteMiddlewareest la méthode standard et recommandée par l’équipe Starlight pour modifier les données de route sans toucher aux composants d’interface. - Résilience aux mises à jour : Le remplacement de composant (
components: { Sidebar: './src/components/MySidebar.astro' }) oblige à recopier et maintenir le balisage interne HTML/CSS/A11y de Starlight, qui change au gré des versions mineures. - Cohérence globale : En modifiant directement
starlightRoute.sidebar, la version mobile (drawer) et la version desktop reçoivent automatiquement les mêmes données filtrées sans duplication de code.
3.2. Pourquoi deux index Algolia distincts plutôt qu’un filtre facetFilters: ['visibility:public'] ?
- Sécurité et étanchéité absolue : Tout filtre client (
facetFiltersoufilters) peut être retiré par un utilisateur effectuant un appelPOSTdirect sur l’API Algolia avec la clé de recherche publique. - Zéro fuite : Avec un index public physiquement séparé, les documents privés n’existent pas sur cet index. Aucune manipulation réseau ne peut exposer de données confidentielles.
4. Conséquences de la Décision
Avantages
- Expérience publique irréprochable : Un visiteur public bénéficie d’une documentation claire, fluide, sans avertissements de connexion intempestifs.
- Expérience membre enrichie : Les utilisateurs connectés accèdent à toute la documentation avec un repérage visuel clair (badges ‘Privé’).
- Sécurité maximale : Aucune donnée privée n’est transmise ni dans le HTML des visiteurs non authentifiés, ni dans l’index de recherche public.
Inconvénients & Points de Vigilance
- Maintien de 2 index Algolia : Le script d’indexation gère deux index (consommation de quota Algolia double pour les documents publics, mais restant très faible sur un site documentaire).
- Résolution des métadonnées de visibilité : Le middleware de route doit connaître rapidement la visibilité d’un document pour filtrer la sidebar. Une fonction de cache ou un mappage in-memory permet de garantir un temps d’exécution < 1ms.
5. Alternatives Exclues
- Créer deux sites Starlight distincts (multi-site) : Rejeté car cela complexifierait le déploiement Cloudflare et la synchronisation du contenu.
- Masquer les liens uniquement en CSS (
display: none) : Rejeté car le HTML des liens et titres privés resterait visible dans le DOM public. - Filtre Algolia côté client seul : Rejeté car non sécurisé face à l’inspection réseau.
6. Historique des Modifications
| Date | Version | Description | Auteur |
|---|---|---|---|
| 2026-10-12 | 1.0 | Création initiale de l’ADR Sidebar Bimodale & Algolia Dual-Index | Team (Agent) |