Aller au contenu

PD-229 — Plan d'implémentation


📚 Navigation User Story | Document | | | ---------- | -- | | 📋 [Spécification](PD-229-specification.md) | | | 🛠️ **Plan d'implémentation** | *(ce document)* | | ✅ [Critères d'acceptation](PD-229-acceptability.md) | | | 📝 [Retour d'expérience](PD-229-rex.md) | | [← Retour à site-vitrine](../PD-225-epic.md) · [↑ Index User Story](index.md)

1. Découpage en composants

Structure SEO

src/
├── components/
│   └── SEO.astro             # Composant meta tags unifié
├── seo/
│   ├── schemas/
│   │   ├── organization.ts   # JSON-LD Organization
│   │   ├── website.ts        # JSON-LD WebSite
│   │   └── faq.ts            # JSON-LD FAQPage
│   └── config.ts             # Configuration SEO globale
├── layouts/
│   └── BaseLayout.astro      # Intègre SEO.astro
└── pages/
    └── *.astro               # Props SEO par page
public/
├── og/                       # Images OpenGraph (1200x630)
├── robots.txt                # Directives crawlers
└── sitemap.xml               # Plan du site (ou généré)

Composants clés

Composant Responsabilité
SEO.astro Génération meta tags + JSON-LD
seo/schemas/*.ts Schémas JSON-LD typés
robots.txt Directives crawlers
sitemap.xml URLs indexables avec hreflang

2. Flux techniques

Injection SEO

Page.astro
    └── <BaseLayout title={} description={} ogImage={}>
            └── <SEO {props} />
                    ├── <title>
                    ├── <meta name="description">
                    ├── <link rel="canonical">
                    ├── <meta property="og:*">
                    ├── <meta name="twitter:*">
                    └── <script type="application/ld+json">

Génération sitemap

astro build
    └── @astrojs/sitemap
            ├── Collecte toutes les pages
            ├── Ajoute hreflang alternates
            └── Génère dist/sitemap-index.xml

3. Mapping invariants → mécanismes

Invariant Mécanisme technique
INV-1 : title + description Props obligatoires SEO.astro
INV-2 : JSON-LD valide Schémas TypeScript validés
INV-3 : Métadonnées SEO explicites Composant SEO.astro
INV-4 : JSON-LD conforme schema.org Validation build-time

Composant SEO.astro

---
// src/components/SEO.astro
interface Props {
  title: string;
  description: string;
  canonical?: string;
  ogImage?: string;
  ogType?: 'website' | 'article';
  noindex?: boolean;
  jsonLd?: object | object[];
}

const {
  title,
  description,
  canonical = Astro.url.href,
  ogImage = '/og/default.png',
  ogType = 'website',
  noindex = false,
  jsonLd
} = Astro.props;

const site = 'https://probatiovault.com';
const fullOgImage = ogImage.startsWith('http') ? ogImage : `${site}${ogImage}`;
---

<!-- SEO obligatoire -->
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
<meta name="robots" content={noindex ? 'noindex, nofollow' : 'index, follow'} />

<!-- OpenGraph obligatoire -->
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content={ogType} />
<meta property="og:url" content={canonical} />
<meta property="og:image" content={fullOgImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content={Astro.currentLocale === 'en' ? 'en_US' : 'fr_FR'} />

<!-- Twitter Cards -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={fullOgImage} />

<!-- JSON-LD -->
{jsonLd && (
  <script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
)}

Schéma Organization

// src/seo/schemas/organization.ts
export const organizationSchema = {
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "ProbatioVault",
  "legalName": "ProbatioVault SASU",
  "url": "https://probatiovault.com",
  "logo": "https://probatiovault.com/logo.png",
  "founder": {
    "@type": "Person",
    "name": "Loïc Pontani"
  },
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "30 rue de la Varenne",
    "addressLocality": "Saint-Maur-des-Fossés",
    "postalCode": "94100",
    "addressCountry": "FR"
  }
};

Schéma WebSite

// src/seo/schemas/website.ts
export const websiteSchema = {
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "ProbatioVault",
  "url": "https://probatiovault.com",
  "potentialAction": {
    "@type": "SearchAction",
    "target": "https://probatiovault.com/fr/search?q={search_term_string}",
    "query-input": "required name=search_term_string"
  }
};

Configuration sitemap

// astro.config.mjs
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://probatiovault.com',
  integrations: [
    sitemap({
      i18n: {
        defaultLocale: 'fr',
        locales: {
          fr: 'fr-FR',
          en: 'en-US'
        }
      },
      filter: (page) => !page.includes('/admin/')
    })
  ]
});

4. Gestion des erreurs

Erreur Cause Mitigation
title manquant Props non fournis TypeScript required
JSON-LD invalide Schéma mal formé Validation schema.org
og:image 404 Image non générée CI vérifie existence
canonical incorrect URL hardcodée Utiliser Astro.url

Script de validation SEO

#!/bin/bash
# scripts/validate-seo.sh

# Vérifier que chaque page HTML a les meta obligatoires
for html in dist/**/*.html; do
  if ! grep -q '<title>' "$html"; then
    echo "ERROR: title manquant dans $html"
    exit 1
  fi
  if ! grep -q 'meta name="description"' "$html"; then
    echo "ERROR: description manquante dans $html"
    exit 1
  fi
  if ! grep -q 'og:image' "$html"; then
    echo "ERROR: og:image manquant dans $html"
    exit 1
  fi
done

# Valider JSON-LD
npx jsonld-lint dist/**/*.html

5. Impacts sécurité

Aspect Mesure
XSS dans JSON-LD Échappement JSON.stringify
Open redirect Canonical = même domaine
Information disclosure Pas de données sensibles dans meta

6. Hypothèses techniques

Hypothèse Justification
@astrojs/sitemap stable Plugin officiel Astro
Images OG 1200x630px Standard OpenGraph
schema.org validateur externe Google Rich Results Test
Pas de SSR pour sitemap Génération build-time

7. Points de vigilance

Point Risque Action
Images OG 14 images à générer (7 pages × 2 langues) Template Figma
Canonical dupliqué /fr/ vs /fr Normaliser trailing slash
noindex en prod Oubli flag CI vérifie absence noindex
Sitemap à jour Pages oubliées Génération automatique
JSON-LD validité Propriétés manquantes Test Rich Results

Fichiers à créer

Fichier Description
src/components/SEO.astro Composant meta unifié
src/seo/schemas/organization.ts Schéma Organization
src/seo/schemas/website.ts Schéma WebSite
src/seo/schemas/faq.ts Schéma FAQPage
public/robots.txt Directives crawlers
public/og/*.png Images OpenGraph
scripts/validate-seo.sh Validation CI