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.
This commit is contained in:
parent
877ef1a220
commit
31144617d7
36 changed files with 1182 additions and 53 deletions
29
server/routes/og/events/[slug].js
Normal file
29
server/routes/og/events/[slug].js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// GET /og/events/[slug].png — generated Open Graph image for an event.
|
||||
// Cached on disk by slug + event.updatedAt so any admin edit busts the cache.
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const slug = getRouterParam(event, 'slug')
|
||||
if (!slug) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Missing slug' })
|
||||
}
|
||||
|
||||
// .png suffix is part of the route filename, but slugs in DB don't include it.
|
||||
const cleanSlug = slug.replace(/\.png$/, '')
|
||||
|
||||
const eventDoc = await loadPublicEvent(event, cleanSlug, { lean: true })
|
||||
const key = eventCacheKey(eventDoc)
|
||||
|
||||
let png = await getCachedOG(key)
|
||||
if (!png) {
|
||||
png = await renderEventOG(eventDoc)
|
||||
await setCachedOG(key, png)
|
||||
}
|
||||
|
||||
setHeader(event, 'Content-Type', 'image/png')
|
||||
setHeader(
|
||||
event,
|
||||
'Cache-Control',
|
||||
'public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400'
|
||||
)
|
||||
return png
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue