import { ImageResponse } from 'next/og'; import { getPostBySlug } from '@/lib/blog'; import { OGImageTemplate } from '@/components/OGImageTemplate'; import { getOgFonts, OG_IMAGE_SIZE } from '@/lib/og-helper'; import { SITE_URL } from '@/lib/schema'; export const size = OG_IMAGE_SIZE; export const contentType = 'image/png'; export const runtime = 'nodejs'; async function fetchImageAsBase64(url: string) { try { const res = await fetch(url); if (!res.ok) return undefined; const arrayBuffer = await res.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); const contentType = res.headers.get('content-type') || 'image/jpeg'; return `data:${contentType};base64,${buffer.toString('base64')}`; } catch (error) { console.error('Failed to fetch OG image:', url, error); return undefined; } } export default async function Image({ params, }: { params: Promise<{ locale: string; slug: string }>; }) { const { locale, slug } = await params; const post = await getPostBySlug(slug, locale); if (!post) { return new Response('Post not found', { status: 404 }); } const fonts = await getOgFonts(); // We don't have request.url here, but we can assume the domain from SITE_URL or config // For local images during dev, relative paths in might not work in Satori // but if we are in nodejs runtime, we could potentially read from disk. // For now, let's just make sure it's absolute. const featuredImage = post.frontmatter.featuredImage ? post.frontmatter.featuredImage.startsWith('http') ? post.frontmatter.featuredImage : `${SITE_URL}${post.frontmatter.featuredImage}` : undefined; // Fetch image explicitly and convert to base64 because Satori sometimes struggles // fetching remote URLs directly inside ImageResponse correctly in various environments. let base64Image: string | undefined = undefined; if (featuredImage) { base64Image = await fetchImageAsBase64(featuredImage); } return new ImageResponse( , { ...OG_IMAGE_SIZE, fonts, }, ); }