Visitor sign in kiosk: multi-site, badge printing, WWCC expiry warnings, admin accounts with 2FA

This commit is contained in:
2026-09-01 15:14:46 +10:00
parent a011587d66
commit 9da7e88eb3
11 changed files with 629 additions and 99 deletions
+9 -2
View File
@@ -2,7 +2,7 @@ import express from 'express';
import rateLimit from 'express-rate-limit';
import db from '../db.js';
import config from '../config.js';
import { savePhoto, photoAbsolutePath } from '../photos.js';
import { savePhoto, photoAbsolutePath, copyStoredPhoto } from '../photos.js';
import { mirror } from '../sheets.js';
import { verifyPin } from '../pins.js';
import { listSites, resolveSite, badgeHtml } from '../sites.js';
@@ -150,10 +150,15 @@ router.post('/signin', signInLimiter, (req, res) => {
});
}
// A recurring visitor with a photo on file is not asked to pose again; the
// stored photo is copied onto this visit as its own snapshot.
let photoPath = null;
if (body.photo) {
photoPath = savePhoto(body.photo);
} else if (config.requirePhoto) {
} else if (isFrequent && frequent.photo_path) {
photoPath = copyStoredPhoto(frequent.photo_path);
}
if (!photoPath && config.requirePhoto) {
return res.status(400).json({ error: 'A photo is required to sign in.' });
}
@@ -336,6 +341,8 @@ router.post('/frequent/auth', pinLimiter, (req, res) => {
checkType: person.check_type,
checkNumber: person.check_number,
defaultHostId: person.default_host_id,
// Tells the kiosk it can skip the camera step entirely.
hasPhoto: Boolean(person.photo_path),
openVisit: open || null,
});
});