Files
mrp-qrcode/lib/qr.ts
T

40 lines
1.2 KiB
TypeScript
Raw Normal View History

2026-04-21 08:56:51 -05:00
import { randomBytes } from "node:crypto";
2026-04-21 09:29:44 -05:00
import QRCode from "qrcode";
import { env } from "@/lib/env";
2026-04-21 08:56:51 -05:00
/**
* Operation QR tokens are opaque, URL-safe, high-entropy identifiers
* printed on traveler cards. 24 bytes = 192 bits of entropy, encoded
* as base64url -> 32 characters.
*/
export function generateQrToken(): string {
return randomBytes(24)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
2026-04-21 09:29:44 -05:00
/**
* The payload baked into the QR image is a full absolute URL so any phone
* camera app (which opens scans in a browser) goes straight to the scan page.
* APP_URL must match the externally reachable origin — see docs/DEPLOY.md.
*/
export function scanUrlForToken(token: string): string {
const base = env.APP_URL.replace(/\/+$/, "");
return `${base}/op/scan/${token}`;
}
/**
* Render the scan URL as a data-URL PNG suitable for <img src=...> on the
* admin detail page and, later, on the printable traveler card. Error
* correction level M balances density against smudge tolerance on paper.
*/
export async function renderQrPng(token: string): Promise<string> {
return QRCode.toDataURL(scanUrlForToken(token), {
errorCorrectionLevel: "M",
margin: 1,
width: 256,
});
}