From df93f83fcdee7e4e654161fb946ce6e37932883f Mon Sep 17 00:00:00 2001 From: vikingowl Date: Sun, 22 Feb 2026 11:45:27 +0100 Subject: [PATCH] feat(seo): add sitemap, canonical URLs, structured data, and OG/Twitter tags - Add dynamic sitemap.xml with static pages and market entries - Add canonical URLs and base OG/Twitter meta tags in root layout - Add JSON-LD WebSite schema with SearchAction on home page - Add JSON-LD Event schema on market detail pages - Add twitter:card summary_large_image for markets with images - Add OG tags to impressum and datenschutz pages - Add font preloading for critical rendering path fonts - Add Sitemap directive to robots.txt --- web/src/app.html | 2 + web/src/routes/+layout.svelte | 8 ++++ web/src/routes/+page.svelte | 15 +++++++ web/src/routes/datenschutz/+page.svelte | 3 ++ web/src/routes/impressum/+page.svelte | 3 ++ web/src/routes/markt/[slug]/+page.svelte | 38 ++++++++++++++++ web/src/routes/sitemap.xml/+server.ts | 55 ++++++++++++++++++++++++ web/static/robots.txt | 2 + 8 files changed, 126 insertions(+) create mode 100644 web/src/routes/sitemap.xml/+server.ts diff --git a/web/src/app.html b/web/src/app.html index e65f5d0..3b6587d 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -9,6 +9,8 @@ + + Marktvogt - Mittelaltermärkte finden + + + + + diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index c312dd2..17ac186 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -11,6 +11,21 @@ Marktvogt - Mittelaltermärkte finden + + + + {@html ``}
diff --git a/web/src/routes/datenschutz/+page.svelte b/web/src/routes/datenschutz/+page.svelte index 312489f..496f023 100644 --- a/web/src/routes/datenschutz/+page.svelte +++ b/web/src/routes/datenschutz/+page.svelte @@ -1,6 +1,9 @@ Datenschutzerklärung - Marktvogt + + +
diff --git a/web/src/routes/impressum/+page.svelte b/web/src/routes/impressum/+page.svelte index 9461490..437cecb 100644 --- a/web/src/routes/impressum/+page.svelte +++ b/web/src/routes/impressum/+page.svelte @@ -1,6 +1,9 @@ Impressum - Marktvogt + + +
diff --git a/web/src/routes/markt/[slug]/+page.svelte b/web/src/routes/markt/[slug]/+page.svelte index cea0e07..2072a09 100644 --- a/web/src/routes/markt/[slug]/+page.svelte +++ b/web/src/routes/markt/[slug]/+page.svelte @@ -36,8 +36,46 @@ {#if market.image_url} + {/if} + {@html ``}
diff --git a/web/src/routes/sitemap.xml/+server.ts b/web/src/routes/sitemap.xml/+server.ts new file mode 100644 index 0000000..75940c0 --- /dev/null +++ b/web/src/routes/sitemap.xml/+server.ts @@ -0,0 +1,55 @@ +import type { RequestHandler } from './$types.js'; +import { apiFetch } from '$lib/api/client.js'; +import type { MarketSummary } from '$lib/api/types.js'; + +const ORIGIN = 'https://marktvogt.de'; + +function escapeXml(str: string): string { + return str.replace(/&/g, '&').replace(//g, '>'); +} + +export const GET: RequestHandler = async ({ fetch }) => { + const staticPages = [ + { path: '/', priority: '1.0', changefreq: 'daily' }, + { path: '/impressum', priority: '0.2', changefreq: 'yearly' }, + { path: '/datenschutz', priority: '0.2', changefreq: 'yearly' } + ]; + + let markets: MarketSummary[] = []; + try { + const res = await apiFetch('/markets?per_page=1000', { fetch }); + markets = res.data; + } catch { + // Backend unreachable — return static pages only + } + + const urls = staticPages + .map( + (p) => ` + ${ORIGIN}${p.path} + ${p.changefreq} + ${p.priority} + ` + ) + .concat( + markets.map( + (m) => ` + ${ORIGIN}/markt/${escapeXml(m.slug)} + weekly + 0.8 + ` + ) + ); + + const xml = ` + +${urls.join('\n')} +`; + + return new Response(xml, { + headers: { + 'Content-Type': 'application/xml', + 'Cache-Control': 'max-age=3600' + } + }); +}; diff --git a/web/static/robots.txt b/web/static/robots.txt index b6dd667..18b0899 100644 --- a/web/static/robots.txt +++ b/web/static/robots.txt @@ -1,3 +1,5 @@ # allow crawling everything by default User-agent: * Disallow: + +Sitemap: https://marktvogt.de/sitemap.xml