diff --git a/packages/website/package.json b/packages/website/package.json index 06188ca8..f007b152 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -20,7 +20,9 @@ "@sveltejs/adapter-node": "^5.1.1", "@sveltejs/kit": "^2.5.16", "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@types/fnv-plus": "^1.3.2", "@types/node": "^20.14.2", + "@types/polka": "^0.5.7", "@types/sharedworker": "^0.0.115", "github-slugger": "^2.0.0", "glob": "^10.4.1", @@ -76,6 +78,7 @@ "Notes": "file:Notes-1.0.0.tgz", "acorn": "^8.12.0", "codemirror": "^6.0.1", + "fnv-plus": "^1.3.1", "magic-string": "^0.30.10", "octokit": "^3.2.1", "satori": "^0.10.14", diff --git a/packages/website/src/app.d.ts b/packages/website/src/app.d.ts index 743f07b2..847e5674 100644 --- a/packages/website/src/app.d.ts +++ b/packages/website/src/app.d.ts @@ -1,12 +1,16 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces +import type { Middleware } from "polka" +type Req = Parameters[0] declare global { namespace App { // interface Error {} // interface Locals {} // interface PageData {} // interface PageState {} - // interface Platform {} + interface Platform { + req: Req + } } } diff --git a/packages/website/src/routes/blog/image/+server.ts b/packages/website/src/routes/blog/image/+server.ts index 68ec215d..7ee36f71 100644 --- a/packages/website/src/routes/blog/image/+server.ts +++ b/packages/website/src/routes/blog/image/+server.ts @@ -1,5 +1,5 @@ import { pages } from '../posts' -import { error } from '@sveltejs/kit' +import { error, type RequestHandler } from '@sveltejs/kit' import satori from 'satori'; import { Resvg } from '@resvg/resvg-js'; @@ -7,13 +7,12 @@ import { SITE_DOMAIN } from '$lib/metadata'; import TTLCache, { } from "@isaacs/ttlcache"; 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/(?[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(); @@ -24,9 +23,9 @@ const defaultRatio = 0.5 // const defaultWidth = 800; const h = (type: any, props: any) => { return { type, props } } - -/** @type {import('./$types').RequestHandler} */ -export async function GET({ url }) { +type a = RequestHandler; +/** @type {RequestHandler} */ +export async function GET({ url, request }) { const slug = url.searchParams.get('slug') let dateParts = url.searchParams.get('date')?.split(/[\/-]/)?.map((p: string) => parseInt(p, 10)) if (dateParts && dateParts.length > 3) { @@ -38,30 +37,40 @@ export async function GET({ url }) { throw error(400, 'Image too big') } let image; - if (!cache.has(slug + "/" + dateParts?.join("-") + "/" + width + "/" + ratio)) { - // let start = new Date(dateParts[0] || 1, dateParts[1] || 0, dateParts[2] || 0); - // // @ts-ignore - // let end = new Date(...dateParts); - // console.log(dateParts) + // 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 - const page = pages - .filter((post) => slug === post.slug) - .filter((post) => { - if (dateParts) { - let date = new Date(post.date) - return ( - (!dateParts[0] || date.getFullYear() == dateParts[0]) && - (!dateParts[1] || date.getMonth() + 1 == dateParts[1]) && - (!dateParts[2] || date.getDate() == dateParts[2]) - ) - } else { return true } - })[0] + // get post with metadata + const page = pages + .filter((post) => slug === post.slug) + .filter((post) => { + if (dateParts) { + let date = new Date(post.date) + return ( + (!dateParts[0] || date.getFullYear() == dateParts[0]) && + (!dateParts[1] || date.getMonth() + 1 == dateParts[1]) && + (!dateParts[2] || date.getDate() == dateParts[2]) + ) + } else { return true } + })[0] - if (!page) { - throw error(404, 'Post not found') - } + if (!page) { + throw error(404, 'Post not found') + } + + 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") + return new Response(null, { status: 304 }) + } + + if (!cache.has(cache_key)) { let template = h("div", { style: { display: 'flex', @@ -125,12 +134,11 @@ export async function GET({ url }) { }); image = resvg.render().asPng(); - cache.set(slug + "/" + dateParts?.join("-") + "/" + width, image) - ; + cache.set(cache_key, image); } else { - image = cache.get(slug + "/" + dateParts?.join("-") + "/" + width) as Buffer + image = cache.get(cache_key) as Buffer } - + return new Response(image, { headers: { 'Content-Type': 'image/png', @@ -139,6 +147,7 @@ export async function GET({ url }) { // immutable: true maxAge: 60 * 60 * 24 }), + 'ETag': cache_key, 'Cross-Origin-Resource-Policy': 'cross-origin' } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8ab1029..9b28e0e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ importers: codemirror: specifier: ^6.0.1 version: 6.0.1(@lezer/common@1.2.1) + fnv-plus: + specifier: ^1.3.1 + version: 1.3.1 magic-string: specifier: ^0.30.10 version: 0.30.10 @@ -147,9 +150,15 @@ importers: '@sveltejs/vite-plugin-svelte': specifier: ^3.1.1 version: 3.1.1(svelte@4.2.18)(vite@5.3.1(@types/node@20.14.2)(terser@5.31.1)) + '@types/fnv-plus': + specifier: ^1.3.2 + version: 1.3.2 '@types/node': specifier: ^20.14.2 version: 20.14.2 + '@types/polka': + specifier: ^0.5.7 + version: 0.5.7 '@types/sharedworker': specifier: ^0.0.115 version: 0.0.115 @@ -1097,9 +1106,15 @@ packages: '@types/aws-lambda@8.10.138': resolution: {integrity: sha512-71EHMl70TPWIAsFuHd85NHq6S6T2OOjiisPTrH7RgcjzpJpPh4RQJv7PvVvIxc6PIp8CLV7F9B+TdjcAES5vcA==} + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/btoa-lite@1.0.2': resolution: {integrity: sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==} + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -1109,12 +1124,24 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + + '@types/fnv-plus@1.3.2': + resolution: {integrity: sha512-Bgr5yn2dph2q8HZKDS002Pob6vaRTRfhqN9E+TOhjKsJvnfZXULPR3ihH8dL5ZjgxbNhqhTn9hijpbAMPtKZzw==} + '@types/hast@2.3.10': resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1124,21 +1151,42 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} '@types/node@20.14.2': resolution: {integrity: sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==} + '@types/polka@0.5.7': + resolution: {integrity: sha512-TH8CDXM8zoskPCNmWabtK7ziGv9Q21s4hMZLVYK5HFEfqmGXBqq/Wgi7jNELWXftZK/1J/9CezYa06x1RKeQ+g==} + '@types/pug@2.0.10': resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/sharedworker@0.0.115': resolution: {integrity: sha512-istxrCv9mbZQt7kXMVMsc4U+dbxG5y+ae5N+9f6pM9VOJmclF7FBWtTog9SMT16s1pZUTjga7ewaMVgqhDpTOg==} + '@types/trouter@3.1.4': + resolution: {integrity: sha512-4YIL/2AvvZqKBWenjvEpxpblT2KGO6793ipr5QS7/6DpQ3O3SwZGgNGWezxf3pzeYZc24a2pJIrR/+Jxh/wYNQ==} + '@types/unist@2.0.10': resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} @@ -1574,6 +1622,9 @@ packages: resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} engines: {node: '>=4.0.0'} + fnv-plus@1.3.1: + resolution: {integrity: sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw==} + foreground-child@3.2.1: resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} @@ -3526,8 +3577,17 @@ snapshots: '@types/aws-lambda@8.10.138': {} + '@types/body-parser@1.19.5': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.14.2 + '@types/btoa-lite@1.0.2': {} + '@types/connect@3.4.38': + dependencies: + '@types/node': 20.14.2 + '@types/cookie@0.6.0': {} '@types/debug@4.1.12': @@ -3536,6 +3596,22 @@ snapshots: '@types/estree@1.0.5': {} + '@types/express-serve-static-core@4.19.5': + dependencies: + '@types/node': 20.14.2 + '@types/qs': 6.9.15 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express@4.17.21': + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.19.5 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 + + '@types/fnv-plus@1.3.2': {} + '@types/hast@2.3.10': dependencies: '@types/unist': 2.0.10 @@ -3544,6 +3620,8 @@ snapshots: dependencies: '@types/unist': 3.0.2 + '@types/http-errors@2.0.4': {} + '@types/json-schema@7.0.15': {} '@types/jsonwebtoken@9.0.6': @@ -3554,18 +3632,44 @@ snapshots: dependencies: '@types/unist': 3.0.2 + '@types/mime@1.3.5': {} + '@types/ms@0.7.34': {} '@types/node@20.14.2': dependencies: undici-types: 5.26.5 + '@types/polka@0.5.7': + dependencies: + '@types/express': 4.17.21 + '@types/express-serve-static-core': 4.19.5 + '@types/node': 20.14.2 + '@types/trouter': 3.1.4 + '@types/pug@2.0.10': {} + '@types/qs@6.9.15': {} + + '@types/range-parser@1.2.7': {} + '@types/resolve@1.20.2': {} + '@types/send@0.17.4': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.14.2 + + '@types/serve-static@1.15.7': + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 20.14.2 + '@types/send': 0.17.4 + '@types/sharedworker@0.0.115': {} + '@types/trouter@3.1.4': {} + '@types/unist@2.0.10': {} '@types/unist@3.0.2': {} @@ -3990,6 +4094,8 @@ snapshots: dependencies: array-back: 3.1.0 + fnv-plus@1.3.1: {} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3