- location /tablet-checkin/api/devices/ (trailing slash, proxy_pass) made
nginx auto-redirect the bare /api/devices list endpoint, and since this
inner nginx sits behind NPM's TLS termination it built that redirect as
http:// even over an https:// request — browsers block that as mixed
content, surfacing as a bare 'Failed to fetch' on the Devices admin page
with no other clue. Dropped the trailing slash on both the location
pattern and proxy_pass target; still correctly prefix-matches every
/devices/:id/... sub-route, no more auto-redirect at all.
- pushNavigate/send() now only reports delivered:true if a write
demonstrably succeeded, not just because a stream entry exists for the
device — a stale-but-not-formally-closed connection can silently accept
a write that never reaches the far end.
- pushBookingToDevice can now self-heal a device stuck 'busy' with no live
session behind it (session already expired unused) rather than requiring
an admin to manually click Reset first. Real failure mode hit testing on
a physical tablet 2026-09-01: two pushes worked end-to-end, a third's
navigate event never reached the tablet (SSE connection silently died
during an idle gap), leaving the device stuck with no way to recover
except a manual admin reset.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Backend CORS is disabled by default (origin: false), and an unpacked
extension's ID isn't fixed/predictable per install anyway, so allowlisting
it server-side would be awkward. options.js now calls
chrome.permissions.request() for the entered base URL on Save — once
granted, the extension's own fetches bypass CORS entirely for that origin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- setBookingCustomFields: use bookings_update's own custom_fields array
(confirmed from the real legacy plugin code) instead of
instance_custom_fields_set, which was only ever proven for a different,
unrelated plugin
- online_checkin_complete is now a configurable per-hotel custom field
(online_checkin_complete_custom_field_id setting), not a bogus top-level
bookings_update field NewBook would silently ignore
- Fix field-name mismatches vs a real live NewBook booking response:
booking_arrival/departure/length, guest address (street/city/state/
postcode/country, not a single field), phone/mobile (in contact_details,
not top-level), and pickPrimaryGuest now checks primary_client (was
checking fields that don't exist in the real API)
- Bundle the standalone push extension into backend/extension-src/, add
GET /api/extension/download (zips on request via archiver) and a
download button on the Settings page
- Remove newbook_hotel_id — vestigial from the legacy system's multi-hotel
routing model, never actually reached NewBook's API, this stack is
single-hotel-per-deployment so there's nothing to route between
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>