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

This commit is contained in:
2026-09-01 16:08:27 +10:00
parent cccba98d97
commit c21123885d
7 changed files with 211 additions and 65 deletions
+54 -3
View File
@@ -585,7 +585,18 @@ router.get('/frequent', (req, res) => {
router.get('/frequent/:id', (req, res) => {
const row = db.prepare('SELECT * FROM frequent_visitors WHERE id = ?').get(req.params.id);
if (!row) return res.status(404).json({ error: 'Not found.' });
res.json(shapeFrequent(row, true));
res.json({
...shapeFrequent(row, true),
// Context for the removal confirmation.
visitCount: db
.prepare('SELECT COUNT(*) AS n FROM visits WHERE frequent_visitor_id = ?')
.get(row.id).n,
onSite: Boolean(
db
.prepare('SELECT 1 FROM visits WHERE frequent_visitor_id = ? AND signed_out_at IS NULL')
.get(row.id)
),
});
});
/** Everyone whose check lapses inside the warning window, or already has. */
@@ -763,9 +774,49 @@ router.post('/frequent/:id/pin', (req, res) => {
res.json({ ok: true, pin });
});
/**
* Removes a recurring visitor for good.
*
* Their visit history is deliberately kept: visits store the name, contact details
* and host as their own columns, so the log stays a complete record of who was in
* the building even after the person's saved record is gone. Deleting the record
* frees their mobile number, email and PIN for someone else.
*
* To keep someone on file but stop them signing in, untick Active instead.
*/
router.delete('/frequent/:id', (req, res) => {
db.prepare('UPDATE frequent_visitors SET active = 0 WHERE id = ?').run(req.params.id);
res.json({ ok: true });
const row = db.prepare('SELECT * FROM frequent_visitors WHERE id = ?').get(req.params.id);
if (!row) return res.status(404).json({ error: 'Not found.' });
const scope = scopedSiteId(req);
if (scope && row.site_id && row.site_id !== scope) {
return res.status(403).json({ error: 'That visitor belongs to another site.' });
}
const openVisit = db
.prepare('SELECT id FROM visits WHERE frequent_visitor_id = ? AND signed_out_at IS NULL')
.get(row.id);
if (openVisit && !req.query.force) {
return res.status(409).json({
error: `${row.first_name} is signed in right now. Sign them out first, or confirm to remove anyway.`,
onSite: true,
});
}
const visitCount = db
.prepare('SELECT COUNT(*) AS n FROM visits WHERE frequent_visitor_id = ?')
.get(row.id).n;
if (row.photo_path) deletePhoto(row.photo_path);
db.prepare('DELETE FROM pin_attempts WHERE phone = ?').run(row.phone);
// visits.frequent_visitor_id is ON DELETE SET NULL, so the history survives.
db.prepare('DELETE FROM frequent_visitors WHERE id = ?').run(row.id);
res.json({
ok: true,
name: `${row.first_name} ${row.last_name}`,
visitsKept: visitCount,
});
});
/* ------------------------------------------------------------- visits */