From d4d7015df6e26c9e9dc64d096b40a91c45709d30 Mon Sep 17 00:00:00 2001 From: vikingowl Date: Wed, 12 Nov 2025 20:10:07 +0100 Subject: [PATCH] feat: Add SEO sitemap and robots.txt generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement dynamic sitemap.xml and robots.txt for search engine optimization. ## New Files ### src/routes/sitemap.xml/+server.ts - Generate XML sitemap with all indexable pages - Include static pages (home, matches list) - Dynamically fetch and include recent 100 matches - Set appropriate priority and changefreq for each URL type - Cache response for 1 hour to reduce server load ### src/routes/robots.txt/+server.ts - Allow all crawlers to index the site - Point crawlers to sitemap.xml location - Set crawl-delay of 1 second to be polite to server - Cache response for 1 day ## Implementation Details **Sitemap Structure:** - Static pages: Priority 0.9-1.0, updated daily - Match pages: Priority 0.7, updated weekly - Fetches up to 100 most recent matches from API - Uses match date as lastmod timestamp for accurate indexing **SEO Benefits:** - Helps search engines discover all match pages efficiently - Provides crawlers with update frequency hints - Improves indexing of dynamic content - Reduces unnecessary crawling with robots.txt directives The sitemap automatically stays current by fetching recent matches on each request. The 1-hour cache balances freshness with server performance. Note: Player profile pages not included in sitemap due to lack of bulk listing API endpoint. Individual player pages will still be indexed via internal links. This completes Phase 2 Feature 3 - site now properly configured for SEO. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/routes/robots.txt/+server.ts | 25 ++++++++++++ src/routes/sitemap.xml/+server.ts | 65 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/routes/robots.txt/+server.ts create mode 100644 src/routes/sitemap.xml/+server.ts diff --git a/src/routes/robots.txt/+server.ts b/src/routes/robots.txt/+server.ts new file mode 100644 index 0000000..3ca0037 --- /dev/null +++ b/src/routes/robots.txt/+server.ts @@ -0,0 +1,25 @@ +import type { RequestHandler } from './$types'; + +const SITE_URL = 'https://cs2.wtf'; // Update with actual production URL + +/** + * Generate robots.txt for search engine crawlers + */ +export const GET: RequestHandler = async () => { + const robots = `User-agent: * +Allow: / + +# Sitemaps +Sitemap: ${SITE_URL}/sitemap.xml + +# Crawl-delay +Crawl-delay: 1 +`; + + return new Response(robots, { + headers: { + 'Content-Type': 'text/plain', + 'Cache-Control': 'public, max-age=86400' // Cache for 1 day + } + }); +}; diff --git a/src/routes/sitemap.xml/+server.ts b/src/routes/sitemap.xml/+server.ts new file mode 100644 index 0000000..3024251 --- /dev/null +++ b/src/routes/sitemap.xml/+server.ts @@ -0,0 +1,65 @@ +import type { RequestHandler } from './$types'; +import { matchesAPI } from '$lib/api/matches'; + +const SITE_URL = 'https://cs2.wtf'; // Update with actual production URL + +/** + * Generate XML sitemap for SEO + * Includes static pages and dynamic match pages + */ +export const GET: RequestHandler = async () => { + try { + // Static pages + const staticPages = [ + { url: '', priority: 1.0, changefreq: 'daily' }, // Home + { url: '/matches', priority: 0.9, changefreq: 'hourly' } // Matches listing + ]; + + // Fetch recent matches for dynamic URLs + let matchUrls: { url: string; lastmod: string }[] = []; + try { + const matchesResponse = await matchesAPI.getMatches({ limit: 100 }); + matchUrls = matchesResponse.matches.map((match) => ({ + url: `/match/${match.match_id}`, + lastmod: match.date || new Date().toISOString() + })); + } catch (error) { + console.error('Failed to fetch matches for sitemap:', error); + } + + // Build XML sitemap + const sitemap = ` + +${staticPages + .map( + (page) => ` + ${SITE_URL}${page.url} + ${new Date().toISOString()} + ${page.changefreq} + ${page.priority} + ` + ) + .join('\n')} +${matchUrls + .map( + (match) => ` + ${SITE_URL}${match.url} + ${match.lastmod} + weekly + 0.7 + ` + ) + .join('\n')} +`.trim(); + + return new Response(sitemap, { + headers: { + 'Content-Type': 'application/xml', + 'Cache-Control': 'public, max-age=3600' // Cache for 1 hour + } + }); + } catch (error) { + console.error('Error generating sitemap:', error); + return new Response('Error generating sitemap', { status: 500 }); + } +};