screentinker/server
Claude a3b668d32f Treat an empty device_info as "nothing new", not as "forget what you know"
Every web and BrightSign player nulled seventeen of its own device columns every five minutes.

The browser player's refresh-register sends `device_info: {}` on a 300-second timer — it has nothing
new to report, it just wants a fresh playlist. But `{}` is truthy, and applyDeviceInfo is a blind
full-row overwrite with no per-field presence check, so it bound undefined for every column.
better-sqlite3 stores undefined as NULL rather than throwing, so the write succeeded and the row was
quietly emptied: android_version, app_version, screen_width/height, render_*, ota_status and
attempts, tier, the four capability flags and the four volume/brightness columns.

Android never hit it, because it always sends the full object. So this degraded exactly the client
family that cannot be inspected any other way — a browser player has no adb, and the dashboard row
is all there is. Fleet view, resolution diagnostics and any version-based logic read blank for them,
which also makes evaluating a browser-based platform look worse than it is.

The surrounding code already anticipates the refresh shape: recordReconnect and persistIdentity are
both gated behind `if (!isPlaylistRefresh)`. This call was the one that was not.

5 tests, including one pinning the driver behaviour the bug depended on — undefined binds as NULL
rather than throwing, which is why this was a silent five-minutely wipe instead of a loud error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
2026-07-30 21:09:11 -05:00
..
config fix(logging): gate CF-Connecting-IP on a Cloudflare peer, not any trusted proxy 2026-07-26 10:23:04 -05:00
db Keep a workspace on schedules that outlive their device group 2026-07-30 20:53:21 -05:00
lib Apply a saved device snapshot only inside the workspace it was taken in 2026-07-30 20:56:59 -05:00
middleware Merge branch 'fix/recovery-grants' into release/auth-campaign 2026-07-26 16:22:59 -05:00
player Web player: survive the suspended-account card destroying the status element 2026-07-30 21:08:06 -05:00
routes Keep a workspace on schedules that outlive their device group 2026-07-30 20:53:21 -05:00
scripts fix(content+android): rotation-aware media — portrait upright on dashboard AND player (#170) (#172) 2026-07-12 22:05:11 -05:00
services Report zero event-loop lag when a window recorded no samples 2026-07-28 14:11:32 -05:00
test Treat an empty device_info as "nothing new", not as "forget what you know" 2026-07-30 21:09:11 -05:00
ws Treat an empty device_info as "nothing new", not as "forget what you know" 2026-07-30 21:09:11 -05:00
.gitignore feat(email): Microsoft Graph send + alert spam protection + preferences UI 2026-05-12 18:16:40 -05:00
config.js Add an operator override for self-update on MDM-managed panels 2026-07-28 23:07:30 -05:00
package-lock.json chore(release): v1.9.27 2026-07-30 19:16:55 -05:00
package.json chore(release): v1.9.27 2026-07-30 19:16:55 -05:00
server.js Serve a beta APK alongside the stable one, and let a display move between them 2026-07-30 19:12:46 -05:00
smoke-ui.js Keep the smoke test out of npm test, and update the lockfile 2026-07-28 20:34:34 -05:00
version.js chore(version): single-source VERSION, env-configurable data paths, bump tooling 2026-06-10 12:56:03 -05:00