fix(contract): honor webhook timeout, bound shutdown, unify unsupported-op 501, validate engine/storage env#350
Closed
rmyndharis wants to merge 1 commit into
Closed
fix(contract): honor webhook timeout, bound shutdown, unify unsupported-op 501, validate engine/storage env#350rmyndharis wants to merge 1 commit into
rmyndharis wants to merge 1 commit into
Conversation
…ed-op 501, validate engine/storage env - WEBHOOK_TIMEOUT is now read on the primary (queued) webhook processor and the test endpoint, not just the deprecated direct path (both hardcoded 10s, so raising the timeout had no effect with QUEUE_ENABLED=true). - CacheService.onModuleDestroy bounds redis.quit() with a force-disconnect deadline so a half-open socket can't block app.close() until SIGKILL. - whatsapp-web.js status/catalog stubs that threw a raw Error (HTTP 500) now throw EngineNotSupportedError (501), matching Baileys; the silent return-null/[] read stubs are left for capability-gated follow-up. - validateEnv fails fast on an unknown ENGINE_TYPE / STORAGE_TYPE instead of silently falling back to the default. - /api/metrics render() is memoized for a few seconds so back-to-back scrapes don't each run a full session scan + aggregates. - Removed a dead if(!validKey) branch in the WS connect handler (validateApiKey always throws on failure). - Docs: documented webhook idempotencyKey/deliveryId + X-OpenWA-* headers (n8n dedup); corrected .env.example rate-limit var names (RATE_LIMIT_MEDIUM_TTL/LIMIT, ms).
Merged
rmyndharis
added a commit
that referenced
this pull request
Jun 19, 2026
* fix(webhook): deliver session lifecycle events and key webhook hardening (#335) * fix(security): pin outbound webhook and media fetches to validated IP (#338) * fix(plugins): persist plugin enable/config and restore (#339) * fix(message): persist bulk-sent messages, sanitize SSRF (#340) * fix(engine): return the real id for forwarded messages (#341) * fix(security): harden outbound requests, IPv6 SSRF (#344) * fix(security): secret-file perms, key pepper, allowedIps (#345) * fix(storage): bound tar imports, contain storage keys (#346) * fix(session): reconcile late acks, serialize reactions (#348) * fix(contract): webhook timeout, bounded shutdown, 501 (#350) * feat(session): force-kill a stuck session (#352) * merge #343 * merge #351 * chore(release): v0.4.3 — CHANGELOG, version bump (package.json/dashboard/swagger), README + docs
Owner
Author
|
Shipped in v0.4.3 (integrated via the release PR #354 and tagged |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Eight small contract / lifecycle / config / doc fixes. No breaking changes; defaults unchanged.
Fixed
WEBHOOK_TIMEOUTis honored on the primary (queued) path and the test endpoint. The configured timeout was applied only on the deprecated direct path; the BullMQ webhook processor andPOST /webhooks/:id/testhardcoded 10s, so raisingWEBHOOK_TIMEOUThad no effect whenQUEUE_ENABLED=true(the recommended production path). All three sites now readwebhook.timeout. (The processor gains aConfigServiceconstructor dependency;ConfigModuleis global so DI resolves it.)CacheService.onModuleDestroyawaitedredis.quit()with no deadline; on a half-open socket the QUIT reply never arrives, blockingapp.close()until the orchestratorSIGKILLs the process. It now force-disconnects after a short timeout (timer cleared on a clean quit) so shutdown always completes.501. Six whatsapp-web.js stubs threw a rawError→ HTTP 500, while Baileys returned 501 for the same ops. They now throwEngineNotSupportedError(501) uniformly. (The silentreturn null/[]read stubs are intentionally left for a capability-gated follow-up — flipping a 200-empty to 501 would change behavior for clients that treat empty as success.)ENGINE_TYPE/STORAGE_TYPEfails fast at boot. A typo (ENGINE_TYPE=bailys) was swallowed and silently fell back to the legacy engine;validateEnvnow whitelists the registered ids.Changed
/api/metricsis memoized for a few seconds. Each scrape ran a full session scan + several aggregate queries; back-to-back scrapes (or multiple Prometheus replicas) now share a short-lived rendered result. Stale-by-a-few-seconds metrics are expected for Prometheus. TheMETRICS_TOKENgate and@SkipThrottleare unchanged.if (!validKey)branch in the WebSocket connect handler —validateApiKeyalways throws on failure, so the surroundingcatchis the real rejection path. The load-bearinghandleSubscribenull-branch (revoked-mid-connection) is untouched.Documentation
idempotencyKey/deliveryIdfields (body +X-OpenWA-Idempotency-Key/X-OpenWA-Delivery-Idheaders) and the dedup-on-idempotencyKeyrule in the n8n integration guide..env.examplerate-limit variables to the names the app actually reads (RATE_LIMIT_MEDIUM_TTL/RATE_LIMIT_MEDIUM_LIMIT, in milliseconds); the advertisedRATE_LIMIT_TTL/RATE_LIMIT_MAXwere read by nothing.Tests
New/updated unit tests: webhook timeout flows from config on the processor and both
webhook.servicesites (AbortSignal.timeoutspy, distinct value); cache shutdown force-disconnects whenquit()hangs and doesn't when it's clean;validateEnvaccepts/rejects engine & storage ids; metricsrender()is memoized within the TTL and recomputes after; the WS connect rejection asserts the real throw contract. Full backend 736/736; dashboard build + lint clean.Deferred (own follow-up)
PUT:PUT /settingsmutates an in-memory field that's lost on restart and never re-applied to runtime config. Making it honest is an API-contract decision (persist + re-apply the truly-mutable values vs. return501/read-only for env-derived ones), so it's out of scope here.