GET / now returns a dashboard for the reader: mode, port, uptime,
card presence, the current card, event counters and a live event log
seeded from /api/taps and updated over the /api/events SSE stream.
The page is read-only on purpose - it has no claim button, because
POST /api/card/claim spends the tap and merely viewing the status
would otherwise consume a real login.
Serving the page requires allowing same-origin browser requests: a
browser sends Origin even for a request to the same origin, so
_cors_ok() used to reject the page's own API calls with 403 unless
--allow-origin was passed. Loopback and same-host origins are now
accepted, and the Access-Control-Allow-Origin echo is applied
consistently in one helper instead of four copies.
--no-page reverts POST-free deployments to API-only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- drop unused BAR import, _VALID_MODES, _is_optional(),
OPTIONAL_IMPORTS and is_loopback()
- replace hand-written event name strings with EVENT_INSERTED,
EVENT_REMOVED and EVENT_INVALID, so a typo cannot silently
disable an event path
- drop the now-moot pytest/coverage entries from .gitignore
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- keyboard.py: emulate a commercial USB RFID reader by typing card ids
into the focused window via a /dev/uinput virtual keyboard
- led.py / bridge.py: drive the status LED from card events
- cli.py, card.py: wire up the new options and event flow
- deploy: harden install.sh and the systemd unit
- README: document keyboard mode, LED support, and deployment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>