diff --git a/astro.config.mjs b/astro.config.mjs index 6a9ed12..6e4b3b2 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -3,14 +3,22 @@ import { defineConfig } from 'astro/config'; import vue from '@astrojs/vue'; import node from '@astrojs/node'; import tailwindcss from '@tailwindcss/vite'; +import sitemap from '@astrojs/sitemap'; // PC7 — bascule SSR (mode 'server' Astro 6) pour endpoint /api/chatbot proxy. // Toutes les pages publiques restent statiques via `export const prerender = true`. // Coolify deploy (PC8) : `node ./dist/server/entry.mjs` (Node adapter standalone). +// PC8 — sitemap auto-genere + site URL pour canonical + redirects SEO. export default defineConfig({ + site: 'https://trans-former.fr', output: 'server', adapter: node({ mode: 'standalone' }), - integrations: [vue()], + integrations: [ + vue(), + sitemap({ + filter: (page) => !page.includes('/api/'), + }), + ], vite: { plugins: [tailwindcss()], }, diff --git a/package-lock.json b/package-lock.json index 4692d93..0a17e6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "dependencies": { "@astrojs/node": "^10.1.0", + "@astrojs/sitemap": "^3.7.2", "@astrojs/vue": "^6.0.1", "@fontsource-variable/roboto-condensed": "^5.2.8", "@tailwindcss/vite": "^4.2.4", @@ -98,6 +99,17 @@ "node": ">=22.12.0" } }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.2.tgz", + "integrity": "sha512-PqkzkcZTb5ICiyIR8VoKbIAP/laNRXi5tw616N1Ckk+40oNB8Can1AzVV56lrbC5GKSZFCyJYUVYqVivMisvpA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^4.3.6" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz", @@ -2661,6 +2673,24 @@ "@types/unist": "*" } }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -2936,6 +2966,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -6982,6 +7018,25 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "license": "MIT" }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -7040,6 +7095,12 @@ "node": ">= 0.8" } }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -7222,6 +7283,12 @@ "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", "license": "MIT" }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, "node_modules/unicorn-magic": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", diff --git a/package.json b/package.json index d6746a9..c0eec44 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@astrojs/node": "^10.1.0", + "@astrojs/sitemap": "^3.7.2", "@astrojs/vue": "^6.0.1", "@fontsource-variable/roboto-condensed": "^5.2.8", "@tailwindcss/vite": "^4.2.4", diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..75a958d --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,19 @@ +User-agent: * +Allow: / + +User-agent: GPTBot +Allow: / + +User-agent: ClaudeBot +Allow: / + +User-agent: Google-Extended +Allow: / + +User-agent: Applebot-Extended +Allow: / + +User-agent: PerplexityBot +Allow: / + +Sitemap: https://trans-former.fr/sitemap-index.xml diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 3f0224c..673d186 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -6,12 +6,63 @@ import SiteHeader from '../components/astro/SiteHeader.astro'; interface Props { title?: string; description?: string; + ogImage?: string; + canonical?: string; + /** Article JSON-LD : passer true sur les pages articles (manifeste, etc.) */ + isArticle?: boolean; + articleDate?: string; + articleDescription?: string; } const { - title = 'trans-former.fr', - description = "Architecture d'ecologie politique - journal, carte conceptuelle, manifeste", + title, + description, + ogImage, + canonical, + isArticle = false, + articleDate, + articleDescription, } = Astro.props; + +const SITE_NAME = 'trans-former.fr'; +const SITE_URL = 'https://trans-former.fr'; +const DEFAULT_DESC = "Architecture, ecologie, politique : un commun a construire ensemble. Manifeste et infrastructure vivante de Jules NENY."; +const DEFAULT_OG = '/og-default.png'; + +const fullTitle = title ? `${title} - ${SITE_NAME}` : SITE_NAME; +const finalDesc = description || DEFAULT_DESC; +const finalCanonical = canonical || new URL(Astro.url.pathname, SITE_URL).href; +const finalOg = ogImage || DEFAULT_OG; +const finalOgAbsolute = finalOg.startsWith('http') ? finalOg : `${SITE_URL}${finalOg}`; + +const websiteSchema = { + "@context": "https://schema.org", + "@type": "WebSite", + "name": SITE_NAME, + "url": SITE_URL, + "author": { + "@type": "Person", + "name": "Jules NENY", + "url": `${SITE_URL}/a-propos`, + "sameAs": [ + "https://www.instagram.com/julesneny/", + "https://www.instagram.com/aep.politique/", + "https://julesneny.substack.com", + "https://git.trans-former.fr/jules" + ] + }, + "description": DEFAULT_DESC +}; + +const articleSchema = isArticle ? { + "@context": "https://schema.org", + "@type": "Article", + "headline": title || SITE_NAME, + "author": { "@type": "Person", "name": "Jules NENY" }, + "datePublished": articleDate || "2026-05-01", + "publisher": { "@type": "Organization", "name": SITE_NAME }, + "description": articleDescription || finalDesc +} : null; --- @@ -20,14 +71,38 @@ const { - {title} - - - - + + + {fullTitle} + + + + + + + + + + + + + + - - + + + + + + + + +