diff --git a/Dockerfile b/Dockerfile index 864d14446..dada11e39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -60,9 +60,19 @@ RUN pnpm build # Excel generation moved to post-deploy # Stage 2: Runner -FROM git.infra.mintel.me/mmintel/runtime:latest AS runner +FROM node:20-alpine AS runner WORKDIR /app +# Install dependencies for health checks and system compatibility +RUN apk add --no-cache libc6-compat curl + +# Create nextjs user and group +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs && \ + chown -R nextjs:nodejs /app + +USER nextjs + ENV HOSTNAME="0.0.0.0" ENV PORT=3000 ENV NODE_ENV=production diff --git a/app/[locale]/opengraph-image.tsx b/app/[locale]/opengraph-image.tsx index 179124d91..c28014fef 100644 --- a/app/[locale]/opengraph-image.tsx +++ b/app/[locale]/opengraph-image.tsx @@ -1,5 +1,5 @@ import { ImageResponse } from 'next/og'; - +import { getTranslations } from 'next-intl/server'; import { OGImageTemplate } from '@/components/OGImageTemplate'; import { getOgFonts, OG_IMAGE_SIZE } from '@/lib/og-helper'; @@ -8,12 +8,14 @@ export const contentType = 'image/png'; export const runtime = 'nodejs'; export default async function Image({ params }: { params: Promise<{ locale: string }> }) { + const { locale } = await params; + const t = await getTranslations({ locale, namespace: 'Index.meta' }); const fonts = await getOgFonts(); return new ImageResponse( , { diff --git a/lib/og-helper.tsx b/lib/og-helper.tsx index 9e750a6a9..4f36be5e1 100644 --- a/lib/og-helper.tsx +++ b/lib/og-helper.tsx @@ -1,37 +1,49 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ImageResponse } from 'next/og'; /** * Loads the Inter fonts for use in Satori (Next.js OG Image generation). * Since we are using runtime = 'nodejs', we can read them from the filesystem. */ export async function getOgFonts() { - try { - const boldFontData = await fetch( - new URL('../public/fonts/Inter-Bold.ttf', import.meta.url) - ).then((res) => res.arrayBuffer()); + const boldFontPath = join(process.cwd(), 'public/fonts/Inter-Bold.woff'); + const regularFontPath = join(process.cwd(), 'public/fonts/Inter-Regular.woff'); - const regularFontData = await fetch( - new URL('../public/fonts/Inter-Regular.ttf', import.meta.url) - ).then((res) => res.arrayBuffer()); + try { + console.log(`[OG] Loading fonts: bold=${boldFontPath}, regular=${regularFontPath}`); + const boldFontBuffer = readFileSync(boldFontPath); + const regularFontBuffer = readFileSync(regularFontPath); + + // Satori (Vercel OG) strictly requires an ArrayBuffer, not a Node Buffer view. + const boldFont = boldFontBuffer.buffer.slice( + boldFontBuffer.byteOffset, + boldFontBuffer.byteOffset + boldFontBuffer.byteLength, + ); + const regularFont = regularFontBuffer.buffer.slice( + regularFontBuffer.byteOffset, + regularFontBuffer.byteOffset + regularFontBuffer.byteLength, + ); + + console.log( + `[OG] Fonts loaded successfully (${boldFont.byteLength} and ${regularFont.byteLength} bytes)`, + ); return [ { name: 'Inter', - data: boldFontData, - style: 'normal' as const, + data: boldFont, weight: 700 as const, + style: 'normal' as const, }, { name: 'Inter', - data: regularFontData, - style: 'normal' as const, + data: regularFont, weight: 400 as const, + style: 'normal' as const, }, ]; } catch (error) { - console.error(`[OG] Failed to load fonts:`, error); + console.error(`[OG] Failed to load fonts from ${process.cwd()}:`, error); return []; } }