Added
- Dashboard chat thread UX: URLs in messages are now clickable links, WhatsApp text formatting (bold/italic/strikethrough/monospace) renders, images open in a photo lightbox, and the scroll position is remembered per chat. Thanks @softronicve. (#484)
- The Infrastructure page now shows the actual WhatsApp Web build the whatsapp-web.js engine is using (e.g.
2.3000.1042251103-alpha) and how it was chosen (pinned viaWWEBJS_WEB_VERSION, auto-resolved, or native), surfaced via/infra/status. The engine card previously showed only the npm library version (whatsapp-web.js 1.34.7), which is unrelated to the WA Web build that actually governs connection stability. (#488) - Infrastructure data backup & restore: export all Data-DB tables to a JSON file and import them back, wired into the database-switch flow. When you change the database backend, the restart dialog now warns that the new database starts empty and offers a one-click backup before switching; a storage switch warns that existing media is not moved. (#488)
- The Infrastructure page flags any database/redis/storage setting that is pinned by an environment variable (its running value differs from the saved config), so it's clear a dashboard change won't apply until that variable is unset, instead of the control silently having no effect. (#488)
- The storage card now warns when S3 is selected but unreachable (a dead/misconfigured bucket no longer shows a misleading green badge), via a new
s3Availablefield on/infra/status; the check re-probes (throttled) rather than latching the boot-time result, so a bundled MinIO that comes up after the app self-corrects. A backup import that exceeds the request size limit now reports an actionable message (raiseBODY_SIZE_LIMIT) instead of a bare "Payload Too Large". (#488) - Data-loss & availability hardening for the new infra flows: importing a backup now refuses an empty/garbage file (it no longer wipes the database and reports success) and asks for confirmation first; selecting the built-in Postgres/MinIO no longer crash-loops a production boot on the default-secret guard (the bundled containers run on the internal-only network); and a transient failure fetching the WhatsApp Web version is no longer cached, so it retries instead of permanently falling back. (#488)
- Human-readable console logs: the
LoggerServicenow renders a colorized, NestJS-style line ([OpenWA] <pid> - <timestamp> <LEVEL> [Context] <message>with dimmedkey=valuemetadata and stack traces on their own line) instead of always emitting raw JSON, so application logs line up visually with NestJS's own framework logs. The format defaults to structured JSON in production (NODE_ENV=production, for containers and log aggregators) and human-readable pretty everywhere else, and can be pinned withLOG_FORMAT=pretty|json.NO_COLOR/FORCE_COLORare honored. JSON output is byte-for-byte unchanged when selected. (#469)
Fixed
- whatsapp-web.js sessions that scanned the QR then immediately disconnected (looping
qr → authenticating → disconnected) when noWWEBJS_WEB_VERSIONwas pinned — the common Docker default. The engine now auto-resolves the current known-good WhatsApp Web build from the wppconnectwa-versionregistry and pins it, instead of relying on whatsapp-web.js's auto-select which could latch onto an incompatible bleeding-edge build that authenticates but never reaches "ready".WWEBJS_WEB_VERSION=offkeeps the old native auto-select; an explicit version still pins exactly. (#488) - Dashboard message-analytics charts no longer silently vanish on PostgreSQL:
/stats/messages(top-chats) ordered by an unquoted mixed-case alias (ORDER BY messageCount), which PostgreSQL case-folds and rejects withcolumn "messagecount" does not exist(500). It now orders by the aggregate directly, so the query — and the dashboard charts it feeds — work on PostgreSQL as they already did on SQLite. The chart section also shows a clear notice on a real error instead of rendering nothing (it previously treated every error as a non-admin 403 and hid itself). (#488) - The Infrastructure page now shows what is actually running for the database, Redis, storage, and engine — the badge/selected card follow the live
/infra/statusinstead of the saveddata/.env.generated, which could disagree when a setting is supplied via environment variable. Previously a stack running PostgreSQL viaDATABASE_TYPE=postgresshowed "SQLite" (the first-run default still in the saved file)./infra/statusnow also reportsredis.enabled. (#488) - The "Use Built-in PostgreSQL/Redis/MinIO Container" toggles now reflect whether OpenWA's bundled container is actually running and backing the service (detected from the labeled container + the configured host), not just the saved intent — so a Postgres stack started via the
postgrescompose profile correctly shows built-in, and a stopped/external one shows off. Falls back to the saved flag when Docker isn't reachable. (#488) - Switching away from a built-in backend (built-in → external/disabled) now tears down the bundled container reliably even after a page reload: removal is derived server-side from the saved
*_BUILTINflags + the running labeled containers, instead of only trusting the browser's in-memory list (which reset on reload and left the container orphaned). Named volumes are preserved, so re-enabling reuses the data. (#488) - Dashboard "by type" message chart: each message type now gets a stable, distinct color keyed by type name (with a deterministic hash fallback) instead of a rotating array-index palette, so a slice keeps its color when the set of present types changes between requests and types past the eighth no longer collide. (#486)
- Removed the oversized decorative watermark icons bleeding through the dashboard stat cards. (#488)
- Dashboard switches for the database, Redis, and storage backends now actually take effect after a restart, matching how the engine switch already worked. The bundled
docker-compose.ymlforwards these settings blank (${VAR:-}) so the dashboard's saved selection (indata/.env.generated) is honored, while a real value set in your.env/host still pins it (and the UI now says so). Previously compose forwarded concrete defaults that silently shadowed the dashboard's choice, so switching had no effect under Docker. (#488)
Changed
- ⚠️
docker-compose.ymlnow forwards the S3 credentials under their canonical namesS3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY(and addsS3_REGION), matching what the app and dashboard read. The legacyS3_ACCESS_KEY/S3_SECRET_KEYare still accepted as a fallback, so existing setups keep working, but updating your.envto the canonical names is recommended. (#488) - ⚠️ Database/Redis/storage selection is now sourced from the dashboard-managed
data/.env.generatedwhen not pinned by an environment variable (see Fixed, above). If you previously relied on the compose file's concrete defaults overriding a staledata/.env.generated, set the value explicitly in your.env/host to pin it. First-run defaults (SQLite, local storage, Redis off) are unchanged. (#488)