Changed
- Dashboard
<select>elements replaced with a custom dropdown component. The "All Status" filter (Sessions), "All Severities" filter (Logs), and language picker (Login) now use a reusableCustomSelectcomponent that matches the dashboard design system with proper dark/light theming, keyboard navigation (arrows, Home/End, type-ahead, Escape), and responsive behavior. Focus returns to the trigger on close, matching native<select>semantics. Thanks @haseeblodhi1899. - The "Install a plugin" modal is wider on desktop (480px → 680px) to give the plugin catalog list more room, while still collapsing to a full-width bottom sheet on small screens.
- Webhook delivery-failure records are pruned on a retention window.
webhook_delivery_failuresis an append-only log written on every terminally-failed delivery, so under a receiver outage it grew without bound. It is now pruned toWEBHOOK_FAILURE_RETENTION_DAYS(default 90; set<= 0to disable) once at startup and daily, mirroring the existing audit-log retention.
Fixed
- A malformed session id now returns
400instead of a500on PostgreSQL. The session routes validate the:idpath param as a UUID at the boundary, so a non-UUID id (a typo or path fuzzing) is rejected with a clean400rather than reaching theuuidprimary-key column and raising an uncaught cast error that surfaced as a generic500— a divergence that only appeared on PostgreSQL (SQLite treated the id as text and returned404). - Baileys API sends now emit
message.sent(parity with the whatsapp-web.js engine). The wwjs engine fires this for the account's own sends; the Baileys engine's own socket-sends echo back only as a skipped history-sync upsert, somessage.sentwebhooks / WebSocket events / themessage:senthook never fired for Baileys API sends. They now fire for text and every media/location/contact/poll/reply/forward send (reactions and deletes excluded). - Config & reliability hardening.
DATABASE_STATEMENT_TIMEOUT_MS,DATABASE_IDLE_TIMEOUT_MS, andDATABASE_CONNECTION_TIMEOUT_MSare validated at boot — a typo previously reached the config layer asNaNand broke the PostgreSQL pool. An unparseableBODY_SIZE_LIMITno longer silently disables the request body-size cap (it falls back to the 25 MB default). The channel-messages endpoint no longer forwardsNaNto the engine on a non-numeric?limit. The fire-and-forget session-row writes in the engine callbacks now handle a transient DB fault instead of surfacing an unhandled rejection, and a set of engine-adapter warning logs no longer mislabel their component name. - A terminally-failed or un-reinitializable session no longer strands its browser process or wedges at "already started". When an engine reports a terminal error, and when a reconnect attempt's re-initialization throws, the dead or half-built engine is now evicted from the session registry and its Chromium process is force-killed instead of being left in place — previously it kept holding a concurrency slot and caused a later start to be rejected as already running. Deleting a session likewise force-kills its browser (rather than a graceful close that could hang on a wedged Chromium and orphan the process).
- The dark theme now covers every dashboard surface. A number of components used hardcoded colors instead of the theme's CSS variables, so several surfaces stayed light in dark mode — most visibly the Infrastructure "Database Migrations" card, plus status/severity badges, toasts, danger-hover states, and toggle tracks across most pages. They now use the theme tokens (and translucent semantic fills) so they follow the active theme in both light and dark. A new
--infotoken themes the blue badges (permission, SQLite, info logs, qr-ready pill) that previously had no theme-aware color, and the root<html>background no longer stays white when the dark theme is selected on a light-OS device (visible on overscroll).