fix(og): match klz exact runner env and logic
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 53s
Build & Deploy / 🧪 QA (push) Successful in 1m40s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled

This commit is contained in:
2026-06-21 23:23:53 +02:00
parent e784b90de1
commit 64563dfcd8
3 changed files with 41 additions and 17 deletions

View File

@@ -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

View File

@@ -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(
<OGImageTemplate
title="E-TIB GmbH | Spezialist für Kabelnetzbau"
description="Ihr Partner für Kabelnetzbau, Horizontalspülbohrungen und Glasfaser-Montage in Guben und bundesweit."
title={t('title')}
description={t('description')}
label="Reliable Energy Infrastructure"
/>,
{

View File

@@ -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 [];
}
}