feat(api): dynamic generation using query params

This commit is contained in:
Pihkaal
2024-09-29 20:57:59 +02:00
parent c44709b68c
commit 42a6b3c2df
3 changed files with 5124 additions and 2449 deletions

View File

@@ -17,7 +17,8 @@
"nuxt": "^3.13.0", "nuxt": "^3.13.0",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"vue": "latest", "vue": "latest",
"vue-router": "latest" "vue-router": "latest",
"zod": "^3.23.8"
}, },
"packageManager": "pnpm@9.11.0", "packageManager": "pnpm@9.11.0",
"devDependencies": { "devDependencies": {

7522
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,31 @@
import { createCanvas, loadImage } from "canvas"; import { createCanvas, loadImage } from "canvas";
import QRCode from "qrcode"; import QRCode from "qrcode";
import { z } from "zod";
import { resolve } from "path";
const time = (label: string) => { const time = (label: string) => {
const start = performance.now(); const start = performance.now();
return () => { return () => {
console.log(label, "\t", performance.now() - start); console.log(label, "\t", performance.now() - start);
} };
} };
const querySchema = z.object({
logo: z.string().min(1),
content: z.string().min(1),
});
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
// TODO: define logo based on name const parsed = querySchema.safeParse(getQuery(event));
const name = getRouterParam(event, "name"); if (!parsed.success) {
name; return createError({
status: 400,
message: `Invalid request query: missing ${parsed.error.errors.map((x) => x.path.join(".")).join(", ")}`,
});
}
const text = "https://pihkaal.me" const logo = parsed.data.logo;
const text = parsed.data.content;
event.node.res.setHeader("Content-Type", "text/plain");
const tt = time("total"); const tt = time("total");
let t; let t;
@@ -25,16 +35,20 @@ export default defineEventHandler(async (event) => {
t = time("render"); t = time("render");
const canvas = createCanvas(SIZE, SIZE); const canvas = createCanvas(SIZE, SIZE);
await QRCode.toCanvas(canvas, text, { errorCorrectionLevel: 'H', width: SIZE, margin: 1 }); await QRCode.toCanvas(canvas, text, {
errorCorrectionLevel: "H",
width: SIZE,
margin: 1,
});
t(); t();
t = time("count"); t = time("count");
const qrCode = QRCode.create(text, { errorCorrectionLevel: 'H' }); const qrCode = QRCode.create(text, { errorCorrectionLevel: "H" });
const moduleCount = qrCode.modules.size + 2; const moduleCount = qrCode.modules.size + 2;
t(); t();
t = time("logo"); t = time("logo");
const logo = await loadImage("http://localhost:3000/session.png"); const logoImage = await loadImage(resolve("public", `${logo}.png`));
const moduleSize = SIZE / moduleCount; const moduleSize = SIZE / moduleCount;
@@ -44,15 +58,20 @@ export default defineEventHandler(async (event) => {
} }
const backgroundSize = logoModules * moduleSize + 1; const backgroundSize = logoModules * moduleSize + 1;
const backgroundPosition = moduleSize * (moduleCount - logoModules) / 2; const backgroundPosition = (moduleSize * (moduleCount - logoModules)) / 2;
const logoSize = backgroundSize - LOGO_PADDING * 2; const logoSize = backgroundSize - LOGO_PADDING * 2;
const logoPosition = backgroundPosition + LOGO_PADDING; const logoPosition = backgroundPosition + LOGO_PADDING;
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
ctx.fillStyle = "white"; ctx.fillStyle = "white";
ctx.fillRect(backgroundPosition, backgroundPosition, backgroundSize, backgroundSize); ctx.fillRect(
ctx.drawImage(logo, logoPosition, logoPosition, logoSize, logoSize); backgroundPosition,
backgroundPosition,
backgroundSize,
backgroundSize,
);
ctx.drawImage(logoImage, logoPosition, logoPosition, logoSize, logoSize);
t(); t();
t = time("buffer"); t = time("buffer");
@@ -61,5 +80,6 @@ export default defineEventHandler(async (event) => {
tt(); tt();
event.node.res.setHeader("Content-Type", "image/png");
return image; return image;
}); });