refactor(route): streamline Open Graph meta tag generation for bots

- Introduced a new metaTag function to simplify the creation of meta tags for Open Graph and Twitter.
- Replaced regex-based bot detection with the isbot library for improved accuracy.
- Enhanced the GET route to conditionally render meta tags based on available link metadata, improving SEO and social media sharing capabilities.
This commit is contained in:
crbon 2026-01-22 13:22:04 +10:00
parent 39d4c6fc93
commit dd2de94548

View file

@ -1,5 +1,6 @@
export const dynamic = 'force-dynamic'; export const dynamic = 'force-dynamic';
import { isbot } from 'isbot';
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { POST } from '@/app/api/send/route'; import { POST } from '@/app/api/send/route';
import type { Link } from '@/generated/prisma/client'; import type { Link } from '@/generated/prisma/client';
@ -16,6 +17,14 @@ function escapeHtml(str: string): string {
.replace(/'/g, '''); .replace(/'/g, ''');
} }
function metaTag(property: string, content: string | undefined, isName = false): string {
if (!content) return '';
const escaped = escapeHtml(content);
return isName
? `<meta name="${property}" content="${escaped}">`
: `<meta property="${property}" content="${escaped}">`;
}
export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) { export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params; const { slug } = await params;
@ -50,27 +59,12 @@ export async function GET(request: Request, { params }: { params: Promise<{ slug
} }
const userAgent = request.headers.get('user-agent') || ''; const userAgent = request.headers.get('user-agent') || '';
const isBot =
/facebookexternalhit|twitterbot|linkedinbot|whatsapp|slackbot|discordbot|telegrambot|applebot|bingbot|googlebot/i.test(
userAgent,
);
if (isBot) { if (isbot(userAgent)) {
const ogTitle = escapeHtml(link.ogTitle || link.name); const ogTitle = link.ogTitle || link.name;
const ogDescription = escapeHtml(link.ogDescription || ''); const ogDescription = link.ogDescription || undefined;
const ogImageUrl = escapeHtml(link.ogImageUrl || ''); const ogImageUrl = link.ogImageUrl || undefined;
const ogDescriptionTag = ogDescription
? `<meta property="og:description" content="${ogDescription}">`
: '';
const ogImageTag = ogImageUrl ? `<meta property="og:image" content="${ogImageUrl}">` : '';
const twitterCard = ogImageUrl ? 'summary_large_image' : 'summary'; const twitterCard = ogImageUrl ? 'summary_large_image' : 'summary';
const metaDescriptionTag = ogDescription
? `<meta name="description" content="${ogDescription}">`
: '';
const twitterDescriptionTag = ogDescription
? `<meta name="twitter:description" content="${ogDescription}">`
: '';
const twitterImageTag = ogImageUrl ? `<meta name="twitter:image" content="${ogImageUrl}">` : '';
return new Response( return new Response(
` `
@ -78,29 +72,29 @@ export async function GET(request: Request, { params }: { params: Promise<{ slug
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>${ogTitle}</title> <title>${escapeHtml(ogTitle)}</title>
${metaTag('title', ogTitle, true)}
<meta name="title" content="${ogTitle}"> ${metaTag('description', ogDescription, true)}
${metaDescriptionTag} ${metaTag('og:type', 'website')}
${metaTag('og:site_name', 'Umami')}
<meta property="og:type" content="website"> ${metaTag('og:title', ogTitle)}
<meta property="og:site_name" content="Umami"> ${metaTag('og:url', request.url)}
<meta property="og:title" content="${ogTitle}"> ${metaTag('og:description', ogDescription)}
<meta property="og:url" content="${request.url}"> ${metaTag('og:image', ogImageUrl)}
${ogDescriptionTag}
${ogImageTag}
<meta name="twitter:card" content="${twitterCard}"> <meta name="twitter:card" content="${twitterCard}">
<meta name="twitter:title" content="${ogTitle}"> ${metaTag('twitter:title', ogTitle, true)}
${twitterDescriptionTag} ${metaTag('twitter:description', ogDescription, true)}
${twitterImageTag} ${metaTag('twitter:image', ogImageUrl, true)}
</head> </head>
<body></body> <body>
<p>Redirecting to ${escapeHtml(link.url)}...</p>
</body>
</html> </html>
`, `,
{ {
headers: { headers: {
'content-type': 'text/html', 'content-type': 'text/html',
'cache-control': 's-maxage=300, stale-while-revalidate',
}, },
}, },
); );