mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-12 04:09:14 +00:00
97 lines
2.2 KiB
TypeScript
97 lines
2.2 KiB
TypeScript
import type { Context } from "@netlify/edge-functions";
|
|
|
|
// List of known social media and search engine bots
|
|
const BOTS = [
|
|
"facebookexternalhit",
|
|
"twitterbot",
|
|
"linkedinbot",
|
|
"slackbot",
|
|
"discordbot",
|
|
"telegrambot",
|
|
"whatsapp",
|
|
"pinterest",
|
|
"opengraph",
|
|
"opengraphbot",
|
|
"bot ",
|
|
"crawler",
|
|
"embedly",
|
|
"vkshare",
|
|
"quora link preview",
|
|
"redditbot",
|
|
"rogerbot",
|
|
"showyoubot",
|
|
"google",
|
|
"bingbot",
|
|
"baiduspider",
|
|
"duckduckbot",
|
|
];
|
|
|
|
function isBot(userAgent: string | null): boolean {
|
|
if (!userAgent) return false;
|
|
const ua = userAgent.toLowerCase();
|
|
return BOTS.some((bot) => ua.includes(bot));
|
|
}
|
|
|
|
export default async function handler(
|
|
request: Request,
|
|
context: Context,
|
|
): Promise<Response> {
|
|
const url = new URL(request.url);
|
|
const userAgent = request.headers.get("user-agent");
|
|
|
|
// Only intercept post pages for bots
|
|
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
|
|
// Skip if it's the home page, static assets, API routes, or raw markdown files
|
|
if (
|
|
pathParts.length === 0 ||
|
|
pathParts[0].includes(".") ||
|
|
pathParts[0] === "api" ||
|
|
pathParts[0] === "_next" ||
|
|
pathParts[0] === "raw"
|
|
) {
|
|
return context.next();
|
|
}
|
|
|
|
// If not a bot, continue to the SPA
|
|
if (!isBot(userAgent)) {
|
|
return context.next();
|
|
}
|
|
|
|
// For bots, fetch the Open Graph metadata from Convex
|
|
const slug = pathParts[0];
|
|
const convexUrl =
|
|
Deno.env.get("VITE_CONVEX_URL") || Deno.env.get("CONVEX_URL");
|
|
|
|
if (!convexUrl) {
|
|
return context.next();
|
|
}
|
|
|
|
try {
|
|
// Construct the Convex site URL for the HTTP endpoint
|
|
const convexSiteUrl = convexUrl.replace(".cloud", ".site");
|
|
const metaUrl = `${convexSiteUrl}/meta/post?slug=${encodeURIComponent(slug)}`;
|
|
|
|
const response = await fetch(metaUrl, {
|
|
headers: {
|
|
Accept: "text/html",
|
|
},
|
|
});
|
|
|
|
if (response.ok) {
|
|
const html = await response.text();
|
|
return new Response(html, {
|
|
headers: {
|
|
"Content-Type": "text/html; charset=utf-8",
|
|
"Cache-Control": "public, max-age=60, s-maxage=300",
|
|
},
|
|
});
|
|
}
|
|
|
|
// If meta endpoint fails, fall back to SPA
|
|
return context.next();
|
|
} catch {
|
|
return context.next();
|
|
}
|
|
}
|