aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYasutake Yohei <yohei@yasutakeyohei.com>2026-07-04 23:20:31 +0900
committerYasutake Yohei <yohei@yasutakeyohei.com>2026-07-04 23:20:31 +0900
commit38f811a24e9611c53105931399a4de9625923817 (patch)
treec9185af11f8cb136345bb020867157bfc9c6c814
parent055a3432d87a76bf0e0187a47c51e09c81a269f0 (diff)
og画像: タイトルハッシュによるキャッシュで再生成をスキップ
-rw-r--r--src/pages/og/[slug].png.ts37
1 files changed, 36 insertions, 1 deletions
diff --git a/src/pages/og/[slug].png.ts b/src/pages/og/[slug].png.ts
index 2b95898..1047647 100644
--- a/src/pages/og/[slug].png.ts
+++ b/src/pages/og/[slug].png.ts
@@ -1,6 +1,12 @@
import satori from "satori";
import { Resvg } from "@resvg/resvg-js";
-import { readFileSync, existsSync, readdirSync } from "node:fs";
+import {
+ readFileSync,
+ existsSync,
+ readdirSync,
+ writeFileSync,
+ mkdirSync,
+} from "node:fs";
import path from "node:path";
const contentDir = "src/content/docs";
@@ -136,6 +142,29 @@ export async function GET({ params }: { params: { slug: string } }) {
title = `${reiwa}${month}定例会`;
}
+ // Check cache: if title hash matches, return cached PNG
+ const distDir = path.join(process.cwd(), "dist", "og");
+ const cacheFile = path.join(distDir, `${slug}.png`);
+ const cacheJsonPath = path.join(distDir, ".og-cache.json");
+
+ let cache: Record<string, string> = {};
+ if (existsSync(cacheJsonPath)) {
+ cache = JSON.parse(readFileSync(cacheJsonPath, "utf-8"));
+ }
+
+ const { createHash } = await import("node:crypto");
+ const titleHash = createHash("md5").update(title).digest("hex");
+
+ if (existsSync(cacheFile) && cache[slug] === titleHash) {
+ const cachedBuffer = readFileSync(cacheFile);
+ return new Response(cachedBuffer, {
+ headers: {
+ "Content-Type": "image/png",
+ "Cache-Control": "public, max-age=31536000, immutable",
+ },
+ });
+ }
+
const svg = await satori(
{
type: "div",
@@ -264,6 +293,12 @@ export async function GET({ params }: { params: { slug: string } }) {
const resvg = new Resvg(svg, { fitTo: { mode: "width", value: 1200 } });
const pngBuffer = resvg.render().asPng();
+ // Save to cache
+ if (!existsSync(distDir)) mkdirSync(distDir, { recursive: true });
+ writeFileSync(cacheFile, pngBuffer);
+ cache[slug] = titleHash;
+ writeFileSync(cacheJsonPath, JSON.stringify(cache, null, 2));
+
return new Response(pngBuffer, {
headers: {
"Content-Type": "image/png",