fix: add open graph autogen image

This commit is contained in:
2025-12-05 15:25:09 +03:00
parent f9bf48899c
commit 144f4bfad6
10 changed files with 1127 additions and 26 deletions

View File

@@ -0,0 +1,10 @@
import core from 'puppeteer';
export async function getScreenshot(html: string) {
const browser = await core.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1200, height: 630, deviceScaleFactor: 0.5 });
await page.setContent(html);
return await page.screenshot({ type: 'png' });
}

View File

@@ -0,0 +1,38 @@
import { ParsedRequest } from './types';
import { FC } from 'react';
export const OGImage: FC<ParsedRequest> = ({ title, desc }) => {
return (
<div
style={{
height: '100%',
width: '100%',
background: 'linear-gradient(150deg, #58C644, #3f87a6)',
display: 'flex',
position: 'relative',
}}
>
<div style={{ position: 'absolute', top: '160px', left: '600px' }}>
<h1
style={{ color: 'white', fontFamily: 'sans-serif', fontSize: '80px' }}
>
{title}
</h1>
<p
style={{ color: 'white', fontFamily: 'sans-serif', fontSize: '40px' }}
>
{desc}
</p>
</div>
</div>
);
};
export async function getHtml(parsedReq: ParsedRequest) {
const { title, desc } = parsedReq;
const { renderToString } = await import('react-dom/server');
const result = renderToString(<OGImage title={title} desc={desc} />);
return result;
}

View File

@@ -0,0 +1,4 @@
export interface ParsedRequest {
title?: string;
desc?: string;
}

View File

@@ -0,0 +1,33 @@
import { getHtml } from './lib/template';
import { getScreenshot } from './lib/render';
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
try {
const params = Object.fromEntries(request.nextUrl.searchParams);
const html = await getHtml(params);
const file = await getScreenshot(html);
return new Response(file, {
status: 200,
statusText: 'OK',
headers: {
// 'Access-Control-Allow-Origin': '*',
// 'Access-Control-Allow-Methods': 'GET',
'Content-Type': `image/png`,
'Cache-Control': `public, immutable, no-transform, s-maxage=31536000, max-age=31536000`,
},
});
} catch (e) {
console.error(e);
return new Response(
'<h1>Internal Error</h1><p>Sorry, there was a problem</p>',
{
status: 500,
headers: {
'Content-Type': 'text/html',
},
},
);
}
}

53
src/app/api/og/route.ts Normal file
View File

@@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from 'next/server';
import puppeteer from 'puppeteer';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const title = req.nextUrl.searchParams.get('title') ?? 'Default title';
const desc = req.nextUrl.searchParams.get('desc') ?? 'Default description';
const imageUrl = `${req.nextUrl.origin}/images/ogBg.png`;
const logoUrl = `${req.nextUrl.origin}/images/logo-dtr-white.png`;
const html = `
<html lang="ru-RU">
<body style="
margin: 0;
padding: 0;
display: flex;
width: 600px;
height: 315px;
background: #111;
color: white;
font-family: sans-serif;
position: relative;
">
<img src="${imageUrl}" style="object-fit: cover; width:100%; height:100%; opacity:0.6; position:absolute; left:0; top:0;" />
<img src="${logoUrl}" style="width: 288px; height: 89px; position:absolute; left:20px; top:20px;" />
<div style="position: absolute; top: 110px; left: 280px; width:100%; height:100%;">
<h2 style="color: white; font-family: sans-serif; font-size: 42px">${title}</h2>
<p style="color: white; font-family: sans-serif; font-size: 24px">${desc}</p>
</div>
</body>
</html>
`;
// Запуск браузера
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
await page.setViewport({ width: 600, height: 315 });
const screenshot = await page.screenshot({ type: 'png' });
await browser.close();
return new NextResponse(screenshot, {
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'max-age=60',
},
});
}