feat: Centralize OG image font loading and sizing, simplify product page OG generation, and refine template styling.
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Successful in 21s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Successful in 1m36s
Build & Deploy KLZ Cables / 🏗️ Build & Push (push) Failing after 1m31s
Build & Deploy KLZ Cables / 🚀 Deploy (push) Has been skipped
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 3s

This commit is contained in:
2026-02-01 11:05:37 +01:00
parent 5f9ee7d976
commit 03e597442b
15 changed files with 3074 additions and 144 deletions

View File

@@ -1,6 +1,7 @@
import { ImageResponse } from 'next/og';
import { getPageBySlug } from '@/lib/pages';
import { OGImageTemplate } from '@/components/OGImageTemplate';
import { getOgFonts, OG_IMAGE_SIZE } from '@/lib/og-helper';
export const runtime = 'nodejs';
@@ -8,11 +9,11 @@ export default async function Image({ params: { locale, slug } }: { params: { lo
const pageData = await getPageBySlug(slug, locale);
if (!pageData) {
return new ImageResponse(
<div style={{ display: 'flex', width: '100%', height: '100%', backgroundColor: '#001a4d' }} />
);
return new Response('Page not found', { status: 404 });
}
const fonts = await getOgFonts();
return new ImageResponse(
(
<OGImageTemplate
@@ -22,8 +23,9 @@ export default async function Image({ params: { locale, slug } }: { params: { lo
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}

View File

@@ -3,6 +3,7 @@ import { getProductBySlug } from '@/lib/mdx';
import { getTranslations } from 'next-intl/server';
import { OGImageTemplate } from '@/components/OGImageTemplate';
import { NextRequest } from 'next/server';
import { getOgFonts, OG_IMAGE_SIZE } from '@/lib/og-helper';
export const runtime = 'nodejs';
@@ -10,7 +11,7 @@ export async function GET(
request: NextRequest,
{ params }: { params: { locale: string } }
) {
const { searchParams } = new URL(request.url);
const { searchParams, origin } = new URL(request.url);
const slug = searchParams.get('slug');
const locale = params.locale || 'en';
@@ -18,6 +19,7 @@ export async function GET(
return new Response('Missing slug', { status: 400 });
}
const fonts = await getOgFonts();
const t = await getTranslations({ locale, namespace: 'Products' });
// Check if it's a category page
@@ -36,8 +38,8 @@ export async function GET(
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}
@@ -45,16 +47,13 @@ export async function GET(
const product = await getProductBySlug(slug, locale);
if (!product) {
return new ImageResponse(
<div style={{ display: 'flex', width: '100%', height: '100%', backgroundColor: '#001a4d' }} />
);
return new Response('Product not found', { status: 404 });
}
const { origin } = new URL(request.url);
const featuredImage = product.frontmatter.images?.[0]
? (product.frontmatter.images[0].startsWith('http')
? product.frontmatter.images[0]
: `${origin}${product.frontmatter.images[0]}`)
? product.frontmatter.images[0]
: `${origin}${product.frontmatter.images[0]}`)
: undefined;
return new ImageResponse(
@@ -63,12 +62,13 @@ export async function GET(
title={product.frontmatter.title}
description={product.frontmatter.description}
label={product.frontmatter.categories?.[0] || 'Product'}
image={featuredImage?.startsWith('http') ? featuredImage : undefined}
image={featuredImage}
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}

View File

@@ -1,6 +1,7 @@
import { ImageResponse } from 'next/og';
import { getPostBySlug } from '@/lib/blog';
import { OGImageTemplate } from '@/components/OGImageTemplate';
import { getOgFonts, OG_IMAGE_SIZE } from '@/lib/og-helper';
export const runtime = 'nodejs';
@@ -8,15 +9,19 @@ export default async function Image({ params: { locale, slug } }: { params: { lo
const post = await getPostBySlug(slug, locale);
if (!post) {
return new ImageResponse(
<div style={{ display: 'flex', width: '100%', height: '100%', backgroundColor: '#001a4d' }} />
);
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 <img> 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
: `https://klz-cables.com${post.frontmatter.featuredImage}`)
? post.frontmatter.featuredImage
: `https://klz-cables.com${post.frontmatter.featuredImage}`)
: undefined;
return new ImageResponse(
@@ -25,12 +30,13 @@ export default async function Image({ params: { locale, slug } }: { params: { lo
title={post.frontmatter.title}
description={post.frontmatter.excerpt}
label={post.frontmatter.category || 'Blog'}
image={featuredImage?.startsWith('http') ? featuredImage : undefined}
image={featuredImage}
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}

View File

@@ -1,25 +1,25 @@
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';
export const runtime = 'nodejs';
export default async function Image({ params: { locale } }: { params: { locale: string } }) {
const t = await getTranslations({ locale, namespace: 'Blog.meta' });
const title = t('title');
const description = t('description');
const fonts = await getOgFonts();
return new ImageResponse(
(
<OGImageTemplate
title={title}
description={description}
title={t('title')}
description={t('description')}
label="Blog"
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}

View File

@@ -1,11 +1,14 @@
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';
export const runtime = 'nodejs';
export default async function Image({ params: { locale } }: { params: { locale: string } }) {
const t = await getTranslations({ locale, namespace: 'Contact' });
const fonts = await getOgFonts();
const title = t('meta.title') || t('title');
const description = t('meta.description') || t('subtitle');
@@ -18,8 +21,8 @@ export default async function Image({ params: { locale } }: { params: { locale:
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}

View File

@@ -1,11 +1,13 @@
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';
export const runtime = 'nodejs';
export default async function Image({ params: { locale } }: { params: { locale: string } }) {
const t = await getTranslations({ locale, namespace: 'Index.meta' });
const fonts = await getOgFonts();
return new ImageResponse(
(
@@ -16,8 +18,9 @@ export default async function Image({ params: { locale } }: { params: { locale:
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}

View File

@@ -1,83 +1,29 @@
import { ImageResponse } from 'next/og';
import { getProductBySlug } from '@/lib/mdx';
import { getTranslations } from 'next-intl/server';
import { OGImageTemplate } from '@/components/OGImageTemplate';
import { getOgFonts, OG_IMAGE_SIZE } from '@/lib/og-helper';
export const runtime = 'nodejs';
export default async function Image({ params: { locale, slug } }: { params: { locale: string, slug?: string[] } }) {
const t = await getTranslations('Products');
export default async function Image({ params: { locale } }: { params: { locale: string } }) {
const t = await getTranslations({ locale, namespace: 'Products' });
const fonts = await getOgFonts();
// If no slug, it's the main products page
if (!slug || slug.length === 0) {
const title = t('meta.title') || t('title');
const description = t('meta.description') || t('subtitle');
return new ImageResponse(
(
<OGImageTemplate
title={title}
description={description}
label="Products"
/>
),
{
width: 1200,
height: 630,
}
);
}
const productSlug = slug[slug.length - 1];
// Check if it's a category page
const categories = ['low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables'];
if (categories.includes(productSlug)) {
const categoryKey = productSlug.replace(/-cables$/, '').replace(/-([a-z])/g, (g) => g[1].toUpperCase());
const categoryTitle = t.has(`categories.${categoryKey}.title`) ? t(`categories.${categoryKey}.title`) : productSlug;
const categoryDesc = t.has(`categories.${categoryKey}.description`) ? t(`categories.${categoryKey}.description`) : '';
return new ImageResponse(
(
<OGImageTemplate
title={categoryTitle}
description={categoryDesc}
label="Product Category"
/>
),
{
width: 1200,
height: 630,
}
);
}
const product = await getProductBySlug(productSlug, locale);
if (!product) {
return new ImageResponse(
<div style={{ display: 'flex', width: '100%', height: '100%', backgroundColor: '#001a4d' }} />
);
}
const featuredImage = product.frontmatter.images?.[0]
? (product.frontmatter.images[0].startsWith('http')
? product.frontmatter.images[0]
: `https://klz-cables.com${product.frontmatter.images[0]}`)
: undefined;
const title = t('meta.title') || t('title');
const description = t('meta.description') || t('subtitle');
return new ImageResponse(
(
<OGImageTemplate
title={product.frontmatter.title}
description={product.frontmatter.description}
label={product.frontmatter.categories?.[0] || 'Product'}
image={featuredImage?.startsWith('http') ? featuredImage : undefined}
title={title}
description={description}
label="Products"
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}

View File

@@ -1,11 +1,14 @@
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';
export const runtime = 'nodejs';
export default async function Image({ params: { locale } }: { params: { locale: string } }) {
const t = await getTranslations({ locale, namespace: 'Team' });
const fonts = await getOgFonts();
const title = t('meta.title') || t('hero.subtitle');
const description = t('meta.description') || t('hero.title');
@@ -18,8 +21,9 @@ export default async function Image({ params: { locale } }: { params: { locale:
/>
),
{
width: 1200,
height: 630,
...OG_IMAGE_SIZE,
fonts,
}
);
}