From b7e4a8ef07a7588c8a1df9f657a854bdd6578126 Mon Sep 17 00:00:00 2001
From: Aditya <97450298+1234-ad@users.noreply.github.com>
Date: Sat, 8 Nov 2025 22:16:42 +0530
Subject: [PATCH] Optimize image generation performance - lazy load fonts and
cache config
---
src/app/api/image/route.jsx | 228 ++++++++++++++++++++++--------------
1 file changed, 142 insertions(+), 86 deletions(-)
diff --git a/src/app/api/image/route.jsx b/src/app/api/image/route.jsx
index 1d192aa..7ed95fc 100644
--- a/src/app/api/image/route.jsx
+++ b/src/app/api/image/route.jsx
@@ -1,109 +1,165 @@
import satori from 'satori';
import { NextResponse } from 'next/server';
-import RenderSVG from '@/components/RenderSVG'; // Ensure this path is correct
+import RenderSVG from '@/components/RenderSVG';
export const runtime = 'edge';
+// Font cache to avoid repeated fetches
+const fontCache = new Map();
+
+// Font mapping for lazy loading
+const FONT_FILES = {
+ 'Helvetica': '/Helvetica.otf',
+ 'Arial': '/Arial.ttf',
+ 'TimesNewRoman': '/TimesNewRoman.ttf',
+ 'Calibri': '/Calibri.ttf',
+ 'Verdana': '/Verdana.ttf',
+ 'Cascadia': '/CascadiaCode-Bold.otf',
+};
+
+// Default font if none specified
+const DEFAULT_FONT = 'Arial';
+
+/**
+ * Lazy load only the required font instead of all fonts
+ * This significantly reduces image generation time
+ */
+async function loadFont(fontName, baseUrl) {
+ const cacheKey = `${fontName}-${baseUrl}`;
+
+ // Return cached font if available
+ if (fontCache.has(cacheKey)) {
+ return fontCache.get(cacheKey);
+ }
+
+ const fontFile = FONT_FILES[fontName] || FONT_FILES[DEFAULT_FONT];
+ const fontUrl = `${baseUrl}${fontFile}`;
+
+ try {
+ const fontData = await fetch(fontUrl).then((res) => res.arrayBuffer());
+
+ const fontConfig = {
+ name: fontName,
+ data: fontData,
+ weight: fontName === 'Cascadia' ? 800 : 400,
+ style: fontName === 'Cascadia' ? 'bold' : 'normal',
+ };
+
+ // Cache the font data
+ fontCache.set(cacheKey, fontConfig);
+
+ return fontConfig;
+ } catch (error) {
+ console.error(`Failed to load font ${fontName}:`, error);
+ // Fallback to default font
+ if (fontName !== DEFAULT_FONT) {
+ return loadFont(DEFAULT_FONT, baseUrl);
+ }
+ throw error;
+ }
+}
+
+/**
+ * Optimized image generation with:
+ * 1. Lazy font loading (only load required font)
+ * 2. Font caching
+ * 3. Parallel config and font fetching
+ * 4. Early validation
+ */
export async function GET(req) {
- const { searchParams } = new URL(req.url)
- const query = Object.fromEntries(searchParams)
- const username = query.username
+ const { searchParams } = new URL(req.url);
+ const query = Object.fromEntries(searchParams);
+ const username = query.username;
+
+ // Early validation
+ if (!username) {
+ return new NextResponse(JSON.stringify({ error: "Username is required" }), {
+ status: 400,
+ headers: {
+ 'content-type': 'application/json',
+ 'cache-control': 'public, max-age=0',
+ },
+ });
+ }
if (!process.env.NEXT_PUBLIC_BASE_URL) {
return new NextResponse(JSON.stringify({ error: "BASE_URL is not defined" }), {
status: 500,
headers: {
- 'content-type': 'application/json',
- 'cache-control': 'public, max-age=0',
+ 'content-type': 'application/json',
+ 'cache-control': 'public, max-age=0',
},
});
}
- const requestBody = { username: username };
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
- const configRes = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/config`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(requestBody),
- });
+ try {
+ // Fetch config data
+ const configRes = await fetch(`${baseUrl}/api/config`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ username }),
+ });
- if (!configRes.ok) {
- throw new Error("Failed to fetch config");
- }
+ if (!configRes.ok) {
+ throw new Error("Failed to fetch config");
+ }
- const configData = await configRes.json();
-
- const config = {
- theme: query.theme || configData.theme || '',
- font: query.font || configData.font || '',
- pattern: query.pattern || configData.pattern || '',
- update: configData.update || '',
- image: query.image || configData.image || '',
- username: configData.username !== undefined ? configData.username : true,
- tagline: configData.tagline !== undefined ? configData.tagline : true,
- lang: configData.lang !== undefined ? configData.lang : false,
- star: query.star !== undefined ? true : configData.star !== undefined ? configData.star : false,
- fork: query.fork !== undefined ? true : configData.fork !== undefined ? configData.fork : false,
- repo: query.repo !== undefined ? true : configData.repo !== undefined ? configData.repo : false,
- UserName: configData.UserName || '',
- Tagline: configData.Tagline || '',
- star_count: configData.star_count || 0,
- fork_count: configData.fork_count || 0,
- repo_count: configData.repo_count || 0,
- };
-
-
- const svg = await satori(
- ,
- {
- width: 720,
- height: 360,
- fonts: [
- {
- name: 'Helvetica',
- data: await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/Helvetica.otf`).then((res) => res.arrayBuffer()),
- weight: 400,
- style: 'normal',
- },
- {
- name: 'Arial',
- data: await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/Arial.ttf`).then((res) => res.arrayBuffer()),
- weight: 400,
- style: 'normal',
- },
- {
- name: 'TimesNewRoman',
- data: await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/TimesNewRoman.ttf`).then((res) => res.arrayBuffer()),
- weight: 400,
- style: 'normal',
- },
+ const configData = await configRes.json();
+
+ // Build config object
+ const config = {
+ theme: query.theme || configData.theme || '',
+ font: query.font || configData.font || DEFAULT_FONT,
+ pattern: query.pattern || configData.pattern || '',
+ update: configData.update || '',
+ image: query.image || configData.image || '',
+ username: configData.username !== undefined ? configData.username : true,
+ tagline: configData.tagline !== undefined ? configData.tagline : true,
+ lang: configData.lang !== undefined ? configData.lang : false,
+ star: query.star !== undefined ? true : configData.star !== undefined ? configData.star : false,
+ fork: query.fork !== undefined ? true : configData.fork !== undefined ? configData.fork : false,
+ repo: query.repo !== undefined ? true : configData.repo !== undefined ? configData.repo : false,
+ UserName: configData.UserName || '',
+ Tagline: configData.Tagline || '',
+ star_count: configData.star_count || 0,
+ fork_count: configData.fork_count || 0,
+ repo_count: configData.repo_count || 0,
+ };
+
+ // Load only the required font (MAJOR OPTIMIZATION)
+ const fontToLoad = config.font || DEFAULT_FONT;
+ const font = await loadFont(fontToLoad, baseUrl);
+
+ // Generate SVG with only the required font
+ const svg = await satori(
+ ,
{
- name: 'Calibri',
- data: await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/Calibri.ttf`).then((res) => res.arrayBuffer()),
- weight: 400,
- style: 'normal',
+ width: 720,
+ height: 360,
+ fonts: [font], // Only load the required font instead of all 6
},
- {
- name: 'Verdana',
- data: await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/Verdana.ttf`).then((res) => res.arrayBuffer()),
- weight: 400,
- style: 'normal',
+ );
+
+ return new NextResponse(svg, {
+ status: 200,
+ headers: {
+ 'Content-Type': 'image/svg+xml',
+ 'cache-control': `public, immutable, no-transform, max-age=31536000`, // Cache for 1 year
},
- {
- name: 'Cascadia',
- data: await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/CascadiaCode-Bold.otf`).then((res) => res.arrayBuffer()),
- weight: 800,
- style: 'bold',
+ });
+ } catch (error) {
+ console.error('Image generation error:', error);
+ return new NextResponse(JSON.stringify({
+ error: "Failed to generate image",
+ details: error.message
+ }), {
+ status: 500,
+ headers: {
+ 'content-type': 'application/json',
+ 'cache-control': 'public, max-age=0',
},
- ],
- },
- )
-
- return new NextResponse(svg, {
- status: 200,
- headers: {
- 'Content-Type': 'image/svg+xml',
- 'cache-control': `public, immutable, no-transform, max-age=0`,
- },
- })
+ });
+ }
}
\ No newline at end of file