Changeset 69815b2 in Klonkt for src


Ignore:
Timestamp:
06/24/2026 05:26:24 AM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
6bd25d1
Parents:
5eef817
Message:

feat: auto-generated per-site OG image from palette + accent

New OgImageService renders a themed 1200x630 social card (palette gradient +
accent + site title/tagline + klonkt wordmark) via @resvg/resvg-js, cached on
disk. Served at GET /og/:slug.png and used as the default og:image/twitter:image
when a site has no custom share image — so every site gets a branded preview.
Bundles a static Fraunces TTF for rendering; graceful no-op if resvg can't load.

Co-Authored-By: Claude <noreply@…>

Location:
src
Files:
3 added
3 edited

Legend:

Unmodified
Added
Removed
  • src/middleware/render.js

    r5eef817 r69815b2  
    137137    cspNonce: () => '',
    138138    currentPath: req.path,
     139    // Absolute origin (for building absolute URLs like the generated og:image).
     140    ogOrigin: (process.env.PUBLIC_BASE_URL || `${req.protocol}://${req.get('host') || ''}`).replace(/\/+$/, ''),
    139141    ...data,
    140142  };
  • src/server.js

    r5eef817 r69815b2  
    6363import adminEpkRoutes from './routes/admin-epk.js';
    6464import changelogRoutes from './routes/changelog.js';
     65import ogRoutes from './routes/og.js';
    6566
    6667// SESSION_SECRET: use the env var if set. Otherwise auto-generate a strong one
     
    222223app.use(federationRoutes);
    223224
     225// Themed OG cards (/og/:slug.png) — resolve the site by slug themselves, so they
     226// run before resolveSite and need no site context.
     227app.use('/og', ogRoutes);
     228
    224229app.use(resolveSite);
    225230app.use(loadAudioTracks);
  • src/views/shell.ejs

    r5eef817 r69815b2  
    5757  ? socialDescr
    5858  : (safeSite.default_description || safeSite.description || '');
    59 const _socialImage = (typeof socialImage !== 'undefined' && socialImage)
    60   ? socialImage
    61   : (safeSite.og_image_default || safeSite.default_cover || '');
     59// og:image — custom (post/site) first; otherwise the auto-generated themed card
     60// (/og/<slug>.png), so every site has a branded social preview by default.
     61let _socialImage = '', _ogGenerated = false;
     62if (typeof socialImage !== 'undefined' && socialImage) _socialImage = socialImage;
     63else if (safeSite.og_image_default) _socialImage = safeSite.og_image_default;
     64else if (safeSite.default_cover) _socialImage = safeSite.default_cover;
     65else if (safeSite.slug && typeof ogOrigin !== 'undefined' && ogOrigin) {
     66  _socialImage = ogOrigin + '/og/' + encodeURIComponent(safeSite.slug) + '.png';
     67  _ogGenerated = true;
     68}
    6269const _ogType = isPostPage ? 'article' : 'website';
    6370
     
    139146<meta property="og:image"     content="<%= _e(_socialImage) %>">
    140147<meta property="og:image:alt" content="<%= _e(_socialTitle) %>">
     148<% if (_ogGenerated) { %>
     149<meta property="og:image:width"  content="1200">
     150<meta property="og:image:height" content="630">
     151<meta property="og:image:type"   content="image/png">
     152<% } %>
    141153<% } %>
    142154<% if (_canonical) { %><meta property="og:url" content="<%= _e(_canonical) %>"><% } %>
Note: See TracChangeset for help on using the changeset viewer.