ghostguild-org/server/utils/og-cache.js
Jennie Robinson Faber 31144617d7 feat(seo): site meta composable + Open Graph image generation
Adds `useSiteMeta()` composable that wraps useSeoMeta with site defaults
(title template, canonical URL, og/twitter image, og:site_name) and
absolute-URL handling via runtimeConfig.public.appUrl.

Adds /og/events/[slug].png route that renders per-event OG images via
satori + @resvg/resvg-js, cached on disk by slug + updatedAt. Bundles
Brygada 1918 + Commit Mono fonts as server assets, ships a fallback
default.png, and patches @shuding/opentype.js via patch-package.

Converts ~25 pages from useHead to useSiteMeta and adds noindex on
private/auth/admin pages.
2026-05-21 17:50:34 +01:00

37 lines
1.1 KiB
JavaScript

import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const CACHE_DIR = resolve(__dirname, '../../.cache/og')
let ensured = false
async function ensureCacheDir() {
if (ensured) return
await mkdir(CACHE_DIR, { recursive: true })
ensured = true
}
// Returns cached PNG buffer or null. Key must be safe for a filename.
export async function getCachedOG(key) {
await ensureCacheDir()
try {
return await readFile(resolve(CACHE_DIR, `${key}.png`))
} catch {
return null
}
}
export async function setCachedOG(key, buffer) {
await ensureCacheDir()
await writeFile(resolve(CACHE_DIR, `${key}.png`), buffer)
}
// Build a safe, deterministic cache key from event slug + updatedAt timestamp.
// Slug is already URL-safe; updatedAt busts the cache on any edit.
export function eventCacheKey(event) {
const slug = String(event.slug || event._id).replace(/[^a-zA-Z0-9_-]/g, '_')
const stamp = event.updatedAt ? new Date(event.updatedAt).getTime() : 0
return `event-${slug}-${stamp}`
}