feat: add SEO fundamentals (robots.txt, sitemap, OG tags, JSON-LD, canonical) and 404 page
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Failing after 10m2s

This commit is contained in:
OpenClaw 2026-02-20 11:02:11 +00:00
parent db0d4eeed2
commit abf66d8017
5 changed files with 98 additions and 1 deletions

53
public/404.html Normal file
View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>404 — Page Not Found | SnapAPI</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📸</text></svg>">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{--bg:#0a0e17;--card:#141a28;--border:#1e2a3f;--text:#f0f2f7;--text-secondary:#94a3c0;--primary:#4f8fff;--primary-light:#6da3ff;--gradient:linear-gradient(135deg,#4f8fff 0%,#a78bfa 50%,#ec4899 100%)}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;-webkit-font-smoothing:antialiased;min-height:100vh;display:flex;flex-direction:column}
a{color:var(--primary-light);text-decoration:none;transition:color .2s}
a:hover{color:var(--primary)}
nav{padding:16px 24px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;max-width:1180px;margin:0 auto;width:100%}
.logo{font-size:1.25rem;font-weight:800;background:var(--gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.nav-links{display:flex;gap:24px;font-size:.9rem}
.content{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;padding:60px 24px}
.error-code{font-size:8rem;font-weight:900;background:var(--gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;line-height:1}
.error-title{font-size:1.5rem;font-weight:600;margin:16px 0 8px}
.error-desc{color:var(--text-secondary);margin-bottom:32px;max-width:400px}
.buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}
.btn{padding:12px 28px;border-radius:10px;font-weight:600;font-size:.95rem;transition:all .2s}
.btn-primary{background:var(--primary);color:#fff}
.btn-primary:hover{background:#3a6fd8;color:#fff}
.btn-secondary{border:1px solid var(--border);color:var(--text-secondary)}
.btn-secondary:hover{border-color:var(--primary);color:var(--text)}
footer{padding:24px;text-align:center;color:var(--text-secondary);font-size:.8rem;border-top:1px solid var(--border)}
</style>
</head>
<body>
<nav>
<a href="/" class="logo">📸 SnapAPI</a>
<div class="nav-links">
<a href="/docs">Docs</a>
<a href="/status">Status</a>
<a href="/#pricing">Pricing</a>
</div>
</nav>
<main class="content">
<div>
<div class="error-code">404</div>
<h1 class="error-title">Page Not Found</h1>
<p class="error-desc">The page you're looking for doesn't exist or has been moved.</p>
<div class="buttons">
<a href="/" class="btn btn-primary">Back to Home</a>
<a href="/docs" class="btn btn-secondary">API Docs</a>
</div>
</div>
</main>
<footer>&copy; 2025 Cloonar Technologies GmbH. All rights reserved.</footer>
</body>
</html>

View file

@ -193,6 +193,37 @@ footer{border-top:1px solid var(--border);padding:48px 24px 32px;background:var(
.trust-badges{flex-direction:column;align-items:center;gap:12px}
}
</style>
<link rel="canonical" href="https://snapapi.eu/">
<meta property="og:title" content="SnapAPI — Screenshot API for Developers">
<meta property="og:description" content="Convert any URL to a pixel-perfect screenshot with a simple API call. PNG, JPEG, WebP. EU-hosted, GDPR compliant.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://snapapi.eu">
<meta property="og:image" content="https://snapapi.eu/og-image.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="SnapAPI — Screenshot API for Developers">
<meta name="twitter:description" content="Convert any URL to a pixel-perfect screenshot. EU-hosted, GDPR compliant.">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "SnapAPI",
"description": "Screenshot API for developers. Convert any URL to PNG, JPEG, or WebP.",
"url": "https://snapapi.eu",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Any",
"offers": [
{"@type": "Offer", "name": "Starter", "price": "9.00", "priceCurrency": "EUR", "description": "1,000 screenshots/month"},
{"@type": "Offer", "name": "Pro", "price": "29.00", "priceCurrency": "EUR", "description": "5,000 screenshots/month"},
{"@type": "Offer", "name": "Business", "price": "79.00", "priceCurrency": "EUR", "description": "25,000 screenshots/month"}
],
"provider": {
"@type": "Organization",
"name": "Cloonar Technologies GmbH",
"url": "https://snapapi.eu",
"address": {"@type": "PostalAddress", "addressLocality": "Wien", "addressCountry": "AT"}
}
}
</script>
</head>
<body>

4
public/robots.txt Normal file
View file

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Disallow: /v1/
Sitemap: https://snapapi.eu/sitemap.xml

9
public/sitemap.xml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://snapapi.eu/</loc><changefreq>weekly</changefreq><priority>1.0</priority></url>
<url><loc>https://snapapi.eu/docs</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://snapapi.eu/status</loc><changefreq>always</changefreq><priority>0.3</priority></url>
<url><loc>https://snapapi.eu/impressum.html</loc><changefreq>yearly</changefreq><priority>0.2</priority></url>
<url><loc>https://snapapi.eu/privacy.html</loc><changefreq>yearly</changefreq><priority>0.2</priority></url>
<url><loc>https://snapapi.eu/terms.html</loc><changefreq>yearly</changefreq><priority>0.2</priority></url>
</urlset>

View file

@ -130,7 +130,7 @@ app.use((req, res) => {
if (req.path.startsWith("/v1/") || req.path.startsWith("/api")) {
res.status(404).json({ error: "Not Found: " + req.method + " " + req.path });
} else {
res.status(404).sendFile(path.join(__dirname, "../public/index.html"));
res.status(404).sendFile(path.join(__dirname, "../public/404.html"));
}
});