Comment the image generator

This commit is contained in:
Jade Ellis 2024-09-15 17:53:30 +01:00
parent 076433b21c
commit 9522e043a1
No known key found for this signature in database
GPG key ID: 8705A2A3EBF77BD2

View file

@ -9,41 +9,36 @@ import { format } from "@tusbar/cache-control";
const cache = new TTLCache({ max: 10000, ttl: 1000 * 60 * 60 })
import fnv from "fnv-plus"
// import type { Endpoints } from "@octokit/types";
// let repoRegex = new RegExp("https?://github\.com/(?<repo>[a-zA-Z0-9]+/[a-zA-Z0-9]+)/?")
const fontFile = await fetch('https://og-playground.vercel.app/inter-latin-ext-700-normal.woff');
const fontData: ArrayBuffer = await fontFile.arrayBuffer();
// const height = 630;
// const width = 1200;
const defaultWidth = 800;
const defaultRatio = 0.5
// const defaultWidth = 800;
// JSX stub
const h = (type: any, props: any) => { return { type, props } }
type a = RequestHandler;
/** @type {RequestHandler} */
export async function GET({ url, request }) {
// First, get the information about the post
// We have the slug and date of the post, which we can use to look up the post
const slug = url.searchParams.get('slug')
let dateParts = url.searchParams.get('date')?.split(/[\/-]/)?.map((p: string) => parseInt(p, 10))
if (dateParts && dateParts.length > 3) {
throw error(404, 'Post not found (bad date)')
}
// Next, get the width and ratio of the image
// to determine the size of the image
const width = Number(url.searchParams.get('width'))
const ratio = Number(url.searchParams.get('ratio'))
// If the width or ratio is too big, don't render the image to prevent DoS attacks
if (width > 10000 || ratio > 50) {
throw error(400, 'Image too big')
}
let image;
// let start = new Date(dateParts[0] || 1, dateParts[1] || 0, dateParts[2] || 0);
// // @ts-ignore
// let end = new Date(...dateParts);
// console.log(dateParts)
// get post with metadata
// Look up the post in the database
const page = pages
.filter((post) => slug === post.slug)
.filter((post) => {
@ -61,16 +56,21 @@ export async function GET({ url, request }) {
throw error(404, 'Post not found')
}
// Generate a cache key based on the post's canonical URL, reading time, width, and ratio
// Caching the image based on this key ensures that the image is not regenerated every time
// The cache key is also used for browser caching
let cache_key = fnv.hash(page.canonical + "\x00" + page.readingTime.text + "\x00" + width + "\x00" + ratio).str()
let recieved_etag = request.headers.get("if-none-match");
if (recieved_etag == cache_key) {
console.log("304")
let received_etag = request.headers.get("if-none-match");
// If the client has a cached version of the image, return a 304 Not Modified response, indicating that the image has not changed
// This means we don't even have to have the image cached in memory
if (received_etag == cache_key) {
return new Response(null, { status: 304 })
}
// If the image is not cached, generate the image and cache it
if (!cache.has(cache_key)) {
// First, render the HTML / JSX-based template
let template = h("div", {
style: {
display: 'flex',
@ -114,6 +114,7 @@ export async function GET({ url, request }) {
children: `Published on ${new Date(page.date).toLocaleDateString()} by Jade Ellis · ${page.readingTime.text}`
})]
});
// Then, convert the vdom to SVG using satori
const svg = await satori(template, {
fonts: [
{
@ -126,6 +127,7 @@ export async function GET({ url, request }) {
width: defaultWidth,
});
// Then, convert the SVG to a PNG image using resvg
const resvg = new Resvg(svg, {
fitTo: {
mode: 'width',
@ -134,20 +136,25 @@ export async function GET({ url, request }) {
});
image = resvg.render().asPng();
// Finally, save the image to the cache
cache.set(cache_key, image);
} else {
// If the image is cached, return it
image = cache.get(cache_key) as Buffer
}
// Finally, return the image as a response
return new Response(image, {
headers: {
'Content-Type': 'image/png',
// Cache the image for 24 hours
'Cache-Control': format({
public: true,
// immutable: true
maxAge: 60 * 60 * 24
}),
// Set the cache key as the ETag
'ETag': cache_key,
// Allow cross-origin requests to serve the image
'Cross-Origin-Resource-Policy': 'cross-origin'
}
});