Initial Camp Scan ticketing system

Backend (Fastify + TS): FluentForms webhook -> NocoDB row + QR + MailerSend
email; PIN auth; scan/lookup/redeem with per-code serialization; reusable QR
codes with count-based check-in; admin search.

App (Expo, one codebase): Android APK + iPhone PWA. Login, camera scanner
(native + web barcode-detector split), green/red overlay with sound + haptics,
admin lookup/redeem. Session token persisted per device.

Ops: multi-stage Dockerfile serving API + PWA same-origin, compose bound to
127.0.0.1; Forgejo Actions runner + tag-triggered signed APK build for Obtainium.
Docs in README.md and INSTALL.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hank 2026-07-08 03:47:59 +00:00
commit 3397e3e3ec
60 changed files with 14703 additions and 0 deletions

View file

@ -0,0 +1,31 @@
// Post-export step: inject PWA manifest link, theme color, and apple-touch meta
// into the SPA index.html. Expo's `single` web output does not use +html.tsx,
// so we patch the generated file directly. Idempotent.
import { readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
const dist = process.argv[2] || "dist";
const file = join(dist, "index.html");
const HEAD = `
<meta name="theme-color" content="#0f1a12" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/icon-192.png" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Camp Scan" />`;
let html = readFileSync(file, "utf8");
if (!html.includes('rel="manifest"')) {
html = html.replace("</head>", `${HEAD}\n </head>`);
}
// Allow full-screen camera: disable user zoom, cover the notch.
html = html.replace(
/<meta name="viewport"[^>]*\/>/,
'<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" />',
);
writeFileSync(file, html);
console.log("inject-pwa: patched", file);