Skip to content

v0.8.15

Choose a tag to compare

@rmyndharis rmyndharis released this 11 Jul 16:02
· 1642 commits to main since this release
  • WhatsApp Web sessions no longer wedge silently in INITIALIZING forever. engine.initialize() was awaited with no timeout, and neither whatsapp-web.js nor Puppeteer bounds the initial browser launch/navigation (page.goto is called with timeout: 0 and the web-version-cache fetch carries no timeout). If Chromium stalled under container memory pressure — realistic at the documented 2 GB Standard profile — the await never resolved or rejected: the session sat in INITIALIZING indefinitely, GET /sessions/:id/qr 400'd forever, and nothing was logged. The #635 abandoned-engine reaper is reactive (terminal onError / rejected re-init only), so nothing recovered it. initializeEngine() now races engine.initialize() against a deadline derived from the configured auth wait (floor 60 s, or WWEBJS_AUTH_TIMEOUT_MS + 30 s when an operator has raised that for slow first boots) so a legitimate slow init is never cut short; on timeout it force-kills the wedged browser, marks the session DISCONNECTED (retryable, so the existing reconnect backoff picks it up), and rethrows — surfacing the failure to a manual POST /start caller instead of hanging. The race's catch is scoped to the timeout only (EngineInitTimeoutError): a real init rejection (e.g. Chromium can't launch) propagates untouched so start()'s existing FAILED+reason diagnostics are preserved — handling both in one catch would downgrade real failures to DISCONNECTED and hide their reason. Thanks @INAPA-desarrolloTIC. [#667]

  • Dashboard primary buttons were invisible until hover in light mode. A leftover Vite template rule — :root:not([data-theme='dark']) button { background-color:#f9f9f9 } inside @media (prefers-color-scheme: light) — carried specificity (0,2,1) (:root + the :not([data-theme]) argument + button), higher than every page-scoped .X-page .btn-primary rule (0,2,0). In light mode (the default) it overrode the green design-system background with a near-white #f9f9f9, leaving the buttons' white text invisible until the leftover Vite button:hover { border-color:#646cff } ring revealed them on hover; the same (0,2,1) rule also pushed .btn-secondary and .btn-icon off their intended backgrounds. Removed that rule (Create-session and other primary buttons are green again), and cleared the remaining Vite scaffolding brand-colors from index.css (a { color:#646cff }, the purple a:hover, button { background-color:#1a1a1a }, button:hover { border-color:#646cff }) that competed with the App.css design system on import-order tiebreaks; the structural button CSS (border-radius, padding, font, cursor) is retained. Plain <button> elements now render with the browser default instead of the dark #1a1a1a. The defect was invisible to anyone developing the dashboard in dark mode. Refs #684 (addresses the dashboard-button part; the QR-with-whatsapp-web.js report in the same issue is separate and still under investigation).

  • PostgreSQL upgrade crash-loop for deployments formerly run with DATABASE_SYNCHRONIZE=true. On the PostgreSQL data connection, migrations always run at boot (migrationsRun: true), so a schema previously bootstrapped with DATABASE_SYNCHRONIZE=true — whose @PrimaryGeneratedColumn('uuid') columns TypeORM created as native uuid — collides with a migration chain that assumes varchar ids and crash-loops the container on boot. A new guard migration (NormalizeSynchronizeUuidColumns), ordered before the first colliding migration, now converts those uuid id and foreign-key columns to varchar (dropping and recreating the dependent cascade foreign keys, and re-applying the gen_random_uuid()::varchar defaults), so affected deployments self-heal on the next restart. It is a single-probe no-op on SQLite and on already-varchar (healthy) PostgreSQL. For very large messages tables the conversion holds an exclusive lock, so run npm run migration:run against the stopped app during a maintenance window if needed; DATABASE_SYNCHRONIZE=true on PostgreSQL remains unsupported for production. Fixes #690.

  • WhatsApp Web auto-version resolver now prefers a settled build. The whatsapp-web.js engine auto-pins its WhatsApp Web build from the wppconnect wa-version registry; the resolver previously took the registry's currentVersion verbatim — the absolute latest build, which can be minutes old and unvalidated and, on some setups, never reaches QR readiness (the #684 whatsapp-web.js "stuck at Starting, no QR" report class). It now picks the newest non-beta, unexpired build published at least 12 hours ago, falling back to currentVersion only when no build qualifies, so auto-pinning no longer latches onto a brand-new build. Operators who set WWEBJS_WEB_VERSION explicitly are unaffected. Fixes the whatsapp-web.js "stuck at Starting, no QR" report from #684 (Bug 2); #692 fixes the dashboard-button part (Bug 1) of the same issue.

  • Baileys engine: messages no longer get stuck on "Waiting for this message. This may take a while." on iOS recipients. Reported specifically on iOS WhatsApp: the recipient's chat shows the message as permanently pending, with no error and a stable connection. The Baileys makeWASocket config had no getMessage implementation, so WhatsApp's own retry-on-decrypt-failure protocol had nothing to resend, leaving the recipient stuck instead of recovering within seconds; it's now backed by the message store.

  • Baileys message-store lookups no longer fail their FK check. The engine factory keyed messageStore.put/getMessage/clearSession by the session's on-disk auth-directory name, but BaileysStoredMessage.sessionId is a foreign key to sessions.id — every write violated the FK and was silently dropped by the orphan guard, leaving the store empty and defeating the getMessage fix above. The engine config now carries the auth-directory key (sessionId, the session name) and the DB-row key (dbSessionId, the session's UUID) as two separate fields, so the message store is keyed correctly without disturbing the existing on-disk auth-directory layout (and purgeSessionData, still keyed by name, keeps working on delete).

  • Closed a write-then-immediate-read race in the Baileys signal key store. The raw signal key store is now wrapped in Baileys' own makeCacheableSignalKeyStore, which could previously make a freshly-established session appear "missing" and force an unnecessary fresh PreKey handshake.

  • Upgraded @whiskeysockets/baileys 6.7.237.0.0-rc13. Picks up an upstream concurrency rewrite (WhiskeySockets/Baileys#2571, #2587) that closes a "dual uncoordinated prekey queues" race in the same session-establishment path; no 6.x release (up to 6.17.16) carries this fix.

  • Fixed a first-message-after-reconnect drop on the Baileys engine. handleMessagesUpsert no longer treats every type: 'append' upsert as history sync — Baileys can tag a genuinely new customer message 'append' when it arrives in the same window as a reconnect's state-sync handshake, silently dropping it; it's now gated on the message's own timestamp against the connection's open time instead (with the account's own sent-message echoes still excluded, to avoid double-firing the existing emitOwnSendEcho webhook).

  • Dashboard Korean (ko) locale polish. Refined several ko strings for consistency, fluency, and accuracy (follow-up to #679): "Create New Session" → 새 세션 생성 (aligns with 생성 used elsewhere in the create flow); the plugin "Configuration" tab → 설정 (matches the rest of the config UI); "Start messaging" → 대화 시작하기 (idiomatic — in Korean one starts a conversation, not a "message"); the WhatsApp Web build "current known-good" now reads 현재 instead of 최신 (latest), which had inverted the version-pinning intent; the "New" session status → 신규 (the conventional Korean status label); and the template placeholder example re-phrases the {{orderId}} clause so the interpolation reads naturally.

  • Reliability and correctness hardening batch. A set of fixes across the engine, session, auth, infra, dashboard, and SDK layers:

    • The inbound media concurrency limiter now bounds its waiter queue and rejects when full; both engines route a rejected download to their existing emit-without-media path, so a burst of inbound media can no longer grow heap without bound.
    • start() now cancels any pending reconnect timer before recreating the engine, so a stale timer left by a failed reconnect can no longer destroy the engine or orphan the Chromium process.
    • The dashboard chat thread now refetches after a WebSocket reconnect, so messages that arrived during a transient gap (and ack/reaction/revoke updates) are no longer silently missing until the chat is reopened.
    • Updating an API key's role, allowed sessions, allowed IPs, or expiry now disconnects the key's already-connected WebSocket sockets, so a narrowed key stops streaming realtime events for sessions/IPs it just lost. A benign rename leaves connected sockets alone.
    • The SQLite→PostgreSQL export/import path now covers every data-owned table (plugin instances + ingress HMAC secrets, conversation mappings, ingress events, both dead-letter queues) instead of only seven, so a migration no longer silently drops the Integration Fabric and the DLQs. The main connection (API keys, audit logs) stays excluded; existing seven-table exports still import unchanged.
    • The bundled docker-compose.yml no longer shadows dashboard-saved Puppeteer headless / session-data-path / browser-args with pinned container defaults — those are blank-forwarded so a dashboard (or data/.env.generated) selection applies, with the sane container defaults supplied by the application config layer (headless, ./data/sessions, the full --no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage --disable-gpu flag set). Upgrade note: if you previously saved engine config in the dashboard, re-save it (or set PUPPETEER_ARGS) so the full Docker sandbox flag set is retained.
    • The PHP SDK messages.list() and catalog.products() docblocks now match the real {messages, total} / {products, pagination} envelopes the server returns (consistent with the JS/Python/Java SDKs), and the SDK CI path-filter now covers backend controllers and services so response-shape drift re-runs the SDK suites.