diff --git a/apps/web/app/about/opengraph-image.tsx b/apps/web/app/about/opengraph-image.tsx new file mode 100644 index 0000000..8fe70ec --- /dev/null +++ b/apps/web/app/about/opengraph-image.tsx @@ -0,0 +1,23 @@ +import { ImageResponse } from "next/og"; +import { OGImageTemplate } from "../../src/components/OGImageTemplate"; +import { getOgFonts, OG_IMAGE_SIZE } from "../../src/lib/og-helper"; + +export const size = OG_IMAGE_SIZE; +export const contentType = "image/png"; +export const runtime = "nodejs"; + +export default async function Image() { + const fonts = await getOgFonts(); + + return new ImageResponse( + , + { + ...OG_IMAGE_SIZE, + fonts: fonts as any, + }, + ); +} diff --git a/apps/web/app/api/og/[[...slug]]/route.tsx b/apps/web/app/api/og/[[...slug]]/route.tsx deleted file mode 100644 index 8279bc6..0000000 --- a/apps/web/app/api/og/[[...slug]]/route.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { ImageResponse } from "next/og"; -import { blogPosts } from "../../../../src/data/blogPosts"; -import { blogThumbnails } from "../../../../src/data/blogThumbnails"; -import { OGImageTemplate } from "../../../../src/components/OGImageTemplate"; -import { getOgFonts, OG_IMAGE_SIZE } from "../../../../src/lib/og-helper"; - -export const runtime = "nodejs"; - -export async function GET( - request: Request, - { params }: { params: Promise<{ slug?: string[] }> }, -) { - const { slug: slugArray } = await params; - const slug = slugArray?.[0] || "home"; - - let title: string; - let description: string; - let label: string | undefined; - let accentColor: string | undefined; - let keyword: string | undefined; - - if (slug === "home") { - title = "Marc Mintel"; - description = - "Technical problem solver's blog - practical insights and learning notes"; - label = "Engineering"; - } else { - const post = blogPosts.find((p) => p.slug === slug); - const thumbnail = blogThumbnails[slug]; - title = post?.title || "Marc Mintel"; - description = - post?.description || - "Technical problem solver's blog - practical insights and learning notes"; - label = post ? "Blog Post" : "Engineering"; - accentColor = thumbnail?.accent; - keyword = thumbnail?.keyword; - } - - const fonts = await getOgFonts(); - - return new ImageResponse( - , - { - ...OG_IMAGE_SIZE, - fonts: fonts as any, - }, - ); -} diff --git a/apps/web/app/blog/[slug]/opengraph-image.tsx b/apps/web/app/blog/[slug]/opengraph-image.tsx new file mode 100644 index 0000000..6cd959d --- /dev/null +++ b/apps/web/app/blog/[slug]/opengraph-image.tsx @@ -0,0 +1,43 @@ +import { ImageResponse } from "next/og"; +import { blogPosts } from "../../../src/data/blogPosts"; +import { blogThumbnails } from "../../../src/data/blogThumbnails"; +import { OGImageTemplate } from "../../../src/components/OGImageTemplate"; +import { getOgFonts, OG_IMAGE_SIZE } from "../../../src/lib/og-helper"; + +export const size = OG_IMAGE_SIZE; +export const contentType = "image/png"; +export const runtime = "nodejs"; + +export default async function Image({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const post = blogPosts.find((p) => p.slug === slug); + const thumbnail = blogThumbnails[slug]; + + const title = post?.title || "Marc Mintel"; + const description = + post?.description || + "Technical problem solver's blog - practical insights and learning notes"; + const label = post ? "Blog Post" : "Engineering"; + const accentColor = thumbnail?.accent; + const keyword = thumbnail?.keyword; + + const fonts = await getOgFonts(); + + return new ImageResponse( + , + { + ...OG_IMAGE_SIZE, + fonts: fonts as any, + }, + ); +} diff --git a/apps/web/app/blog/[slug]/page.tsx b/apps/web/app/blog/[slug]/page.tsx index 7436b9e..c9cb5d2 100644 --- a/apps/web/app/blog/[slug]/page.tsx +++ b/apps/web/app/blog/[slug]/page.tsx @@ -33,20 +33,11 @@ export async function generateMetadata({ title: post.title, description: post.description, type: "article", - images: [ - { - url: `/api/og/${slug}`, - width: 1200, - height: 630, - alt: post.title, - }, - ], }, twitter: { card: "summary_large_image", title: post.title, description: post.description, - images: [`/api/og/${slug}`], }, }; } diff --git a/apps/web/app/contact/opengraph-image.tsx b/apps/web/app/contact/opengraph-image.tsx new file mode 100644 index 0000000..ab9509f --- /dev/null +++ b/apps/web/app/contact/opengraph-image.tsx @@ -0,0 +1,23 @@ +import { ImageResponse } from "next/og"; +import { OGImageTemplate } from "../../src/components/OGImageTemplate"; +import { getOgFonts, OG_IMAGE_SIZE } from "../../src/lib/og-helper"; + +export const size = OG_IMAGE_SIZE; +export const contentType = "image/png"; +export const runtime = "nodejs"; + +export default async function Image() { + const fonts = await getOgFonts(); + + return new ImageResponse( + , + { + ...OG_IMAGE_SIZE, + fonts: fonts as any, + }, + ); +} diff --git a/apps/web/app/opengraph-image.tsx b/apps/web/app/opengraph-image.tsx new file mode 100644 index 0000000..8e247e8 --- /dev/null +++ b/apps/web/app/opengraph-image.tsx @@ -0,0 +1,23 @@ +import { ImageResponse } from "next/og"; +import { OGImageTemplate } from "../src/components/OGImageTemplate"; +import { getOgFonts, OG_IMAGE_SIZE } from "../src/lib/og-helper"; + +export const size = OG_IMAGE_SIZE; +export const contentType = "image/png"; +export const runtime = "nodejs"; + +export default async function Image() { + const fonts = await getOgFonts(); + + return new ImageResponse( + , + { + ...OG_IMAGE_SIZE, + fonts: fonts as any, + }, + ); +} diff --git a/apps/web/src/lib/og-helper.tsx b/apps/web/src/lib/og-helper.tsx index 284ddc9..2bd1ab7 100644 --- a/apps/web/src/lib/og-helper.tsx +++ b/apps/web/src/lib/og-helper.tsx @@ -6,14 +6,36 @@ import { join } from "path"; * Since we are using runtime = 'nodejs', we can read them from the filesystem. */ export async function getOgFonts() { - const boldFontPath = join( - process.cwd(), - "apps/web/public/fonts/Inter-Bold.woff", - ); - const regularFontPath = join( - process.cwd(), - "apps/web/public/fonts/Inter-Regular.woff", - ); + // Use relative path from process.cwd() which usually is the monorepo root or apps/web + // We try both to be safe during different execution contexts (dev vs build vs runtime) + const pathsToTry = [ + join(process.cwd(), "apps/web/public/fonts"), + join(process.cwd(), "public/fonts"), + ]; + + let boldFontPath = ""; + let regularFontPath = ""; + + for (const basePath of pathsToTry) { + const b = join(basePath, "Inter-Bold.woff"); + const r = join(basePath, "Inter-Regular.woff"); + try { + if (readFileSync(b) && readFileSync(r)) { + boldFontPath = b; + regularFontPath = r; + break; + } + } catch (e) { + continue; + } + } + + if (!boldFontPath || !regularFontPath) { + console.error( + `[OG] Could not find fonts in either ${pathsToTry.join(" or ")}`, + ); + return []; + } try { console.log( @@ -40,7 +62,7 @@ export async function getOgFonts() { }, ]; } catch (error) { - console.error(`[OG] Failed to load fonts from ${process.cwd()}:`, error); + console.error(`[OG] Failed to load fonts:`, error); return []; } }