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
+62 -16
View File
@@ -5,7 +5,9 @@ Runs in one Docker container, stores everything locally in SQLite, and mirrors e
and sign out to a Google Sheet so someone outside the building can see who is on site during
an evacuation.
- **Kiosk** at `/` — a stepped sign in, a PIN sign in for regulars, and sign out.
- **Kiosk** at `/` — a stepped sign in, a PIN sign in for regulars, and sign out. No link to
the admin console: a kiosk is a public terminal and administration does not belong on it.
Reach the console from a staff machine at `/admin`.
- **Admin** at `/admin` — who's on site now, the visit log, recurring visitors, the people a
visitor can ask for, sites, admin accounts, and system status.
@@ -19,7 +21,7 @@ two factor.
|---|---|---|
| First and last name | typed each visit | on file |
| Person being visited | picked from the list | picked each visit |
| Photo | taken at the kiosk | taken at the kiosk |
| Photo | taken at the kiosk | on file if saved, otherwise taken at the kiosk |
| WWCC / VIT / none | typed each visit | on file |
| Mobile and/or email | at least one required | on file |
@@ -103,25 +105,47 @@ staff list and log, and cannot touch the others.
Turn it on per site under **Sites → Edit**. After a visitor signs in, the kiosk loads the badge
into a hidden frame and prints it — one label, no dialog on most kiosk setups. The badge shows
the site, name, who they are visiting, time in, WWCC/VIT status (or a boxed **No WWCC / VIT**),
the photo if you want it, and an optional line of your own text.
the site, the visitor's name, who they are visiting, the time in, their WWCC/VIT number or a
boxed **No WWCC / VIT**, the photo if you want it, and an optional line of your own text.
Set the label size in millimetres to match your stock. Type scales with the smaller dimension,
so small labels stay legible. Common sizes:
| Stock | mm |
|---|---|
| Card size | 86 × 54 |
| Brother DK-11202 shipping | 100 × 62 |
| Brother DK-11209 small address | 62 × 29 |
| Dymo 99014 shipping | 101 × 54 |
Use **Preview badge** to check the layout in a browser before committing a roll to it. The kiosk
browser needs the label printer set as its default, with margins off and scaling at 100%.
Pick your stock from the **Label stock** list and the dimensions fill themselves in. A label
noticeably taller than it is wide gets a stacked layout — photo on top, name beneath — which is
what you want on a roll printer. Wider stock gets the photo alongside the text instead. Type
scales with the constraining dimension, so small labels stay readable.
Admins can reprint from the **On site** list, and the visitor gets a "Print the badge again"
button on the confirmation screen if the first one jams.
### Brother QL-820NWB
The default for a new site is 62 × 100 mm, which matches the DK-11202 die-cut label. For a
continuous roll, 62 × 90 mm is a good visitor badge.
It takes media 12 to 62 mm wide and prints up to 60.96 mm across at 300 × 300 dpi, so 62 mm is
the widest roll it will accept — the console warns you if you enter anything wider. Useful rolls:
| Roll | Size | Good for |
|---|---|---|
| DK-22205 | 62 mm continuous | The default. Cut to any length; 90 mm suits a visitor badge |
| DK-11202 | 62 × 100 mm die-cut | Pre-cut, no length to choose |
| DK-22251 | 62 mm continuous, black/red | Same as DK-22205 but supports the red option below |
| DK-11208 | 38 × 90 mm die-cut | Narrower; turn the photo off |
| DK-11209 | 29 × 62 mm die-cut | Name and host only |
**The red option.** Tick *Print the heading and the no-check warning in red* and the site name
and the **No WWCC / VIT** box print red instead of black, which makes a visitor without a check
obvious across a room. It only works on a DK-22251 roll — on any other roll the printer renders
it as grey. Two-colour printing is also far slower than black alone (Brother rate it at roughly
15 labels a minute against 110), which is irrelevant for one badge at a time but worth knowing.
**Driver setup on the kiosk.** Set the QL-820NWB as the default printer for the browser running
the kiosk, choose the matching paper size in the driver, and set margins to none and scaling to
100%. Then use **Preview badge** in the console and print one before committing a roll — the
preview renders at the exact millimetre size the printer will receive.
The QL-820NWB has Ethernet and Wi-Fi, so it does not need to hang off the kiosk tablet. Install
it as a network printer on whichever device drives the kiosk browser.
## WWCC and VIT expiry warnings
Give a recurring visitor an expiry date and the console watches it. Inside
@@ -275,12 +299,34 @@ spreadsheet covers every site.
## Recurring visitors and PINs
Each saved person is one record: **mobile number, email address and PIN are all unique**, checked
when a record is added or edited and enforced by the database. If an existing database already
contains duplicates, the startup log names who collides and the checks stay at the application
level until you fix them.
### A photo on file
Give a recurring visitor a photo in the admin console — from the machine's camera or an uploaded
file — and the kiosk stops asking them to pose. They enter their PIN, pick who they are visiting,
and the sign in completes with their pass printing immediately.
The stored photo is *copied* onto each visit rather than referenced, so the visit log stays a
true snapshot: replacing someone's photo later does not change what past visits show, and photo
retention cleaning up old visits can never delete a live profile photo.
Leave the photo blank and they are asked at the kiosk as before.
### PINs
The mobile number is the username, and the PIN is four digits. PINs are stored encrypted with
`APP_SECRET` rather than hashed, so an admin can reprint a lost card without resetting it. Four
digits is only 10,000 combinations, so hashing would add nothing against anyone holding a copy
of the database — the real protection is the lockout: five wrong PINs on a number locks it for
15 minutes.
Four digits gives 10,000 combinations and each must be unique, so that is the ceiling on
simultaneous recurring visitors. Deactivating someone frees theirs.
**Changing `APP_SECRET` makes every stored PIN unreadable.** If you have to change it, reissue
PINs from the admin console afterwards.