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

This commit is contained in:
2026-08-31 15:16:09 +10:00
parent 14678e13e8
commit a011587d66
9 changed files with 631 additions and 507 deletions
+28 -2
View File
@@ -46,8 +46,34 @@ app.get('/healthz', (req, res) => {
res.json({ ok: true, onSite: db.prepare('SELECT COUNT(*) AS n FROM visits WHERE signed_out_at IS NULL').get().n });
});
app.use(express.static(publicDir, { extensions: ['html'] }));
app.get('/admin', (req, res) => res.sendFile(path.join(publicDir, 'admin.html')));
/* ------------------------------------------------------------ admin pages */
// The console and the sign in screen are separate documents, so these must be
// declared before express.static or it would serve them itself and skip the
// redirect that keeps an unauthenticated browser off the console.
function sessionUser(req) {
if (!req.session?.adminUserId) return null;
const user = users.findById(req.session.adminUserId);
return user && user.active ? user : null;
}
app.get('/admin', (req, res) => {
const user = sessionUser(req);
if (!user || user.must_change_password) return res.redirect('/admin/login');
res.sendFile(path.join(publicDir, 'admin.html'));
});
app.get('/admin/login', (req, res) => {
const user = sessionUser(req);
if (user && !user.must_change_password) return res.redirect('/admin');
res.sendFile(path.join(publicDir, 'login.html'));
});
// Nobody should land on the raw filenames; keep one address per page.
app.get(['/admin.html', '/login.html'], (req, res) => res.redirect('/admin'));
app.use(express.static(publicDir, { extensions: ['html'], index: false }));
app.get('/favicon.ico', (req, res) => res.redirect(301, '/favicon.svg'));
app.use((req, res) => res.status(404).sendFile(path.join(publicDir, 'index.html')));
app.use((err, req, res, next) => {