Skip to content

fix(notifications): deliver main server threshold alerts to owner organization (#5830) - #5832

Open
ghazy2001 wants to merge 1 commit into
Dokploy:canaryfrom
ghazy2001:fix/5830-server-threshold-alerts-main-server
Open

ghazy2001 wants to merge 1 commit into
Dokploy:canaryfrom
ghazy2001:fix/5830-server-threshold-alerts-main-server

Conversation

@ghazy2001

@ghazy2001 ghazy2001 commented Oct 31, 2026 •

Copy link
Copy Markdown

Summary

Resolves #5830 (Linear: DOK-931).

When ServerType === "Dokploy" in receiveNotification (apps/dokploy/server/api/routers/notification.ts), organizationId was hardcoded to "".
In packages/server/src/utils/notifications/server-threshold.ts, notifications were queried with:

where: and(
  eq(notifications.serverThreshold, true),
  eq(notifications.organizationId, organizationId),
)

Because notifications saved in the UI always belong to a valid non-empty organizationId, querying with organizationId: "" matched zero rows. As a result, CPU and memory threshold alerts for the Dokploy main server were silently dropped, even though the metrics client received an HTTP 200/success response.

Changes

  1. Resolve Owner Organization for Dokploy Host:

    • In receiveNotification mutation (apps/dokploy/server/api/routers/notification.ts), look up the owner member record (db.query.member.findFirst({ where: eq(member.role, "owner") })), mirroring the host instance pattern used in packages/server/src/utils/backups/index.ts.
    • Set organizationId = owner?.organizationId ?? "".
  2. Resilient Notification Delivery Query:

    • In sendServerThresholdNotifications (packages/server/src/utils/notifications/server-threshold.ts):
      • Broadened organizationId parameter type to string | null | undefined.
      • Conditioned organizationId filter: if provided, filter by organizationId; if empty/falsy, fall back to matching all enabled serverThreshold: true notifications (mirroring dokploy-restart.ts), ensuring alerts are never silently dropped even if an owner record is missing.
  3. Hermetic Automated Tests:

    • Added unit test suite apps/dokploy/__test__/notifications/server-threshold.test.ts covering:
      • Scoped notification dispatch when organizationId is present.
      • Fallback dispatch when organizationId is empty or undefined.
      • Email template subject and body formatting for CPU/Memory metrics.
      • Provider channel triggers (Resend, Slack, Discord, Telegram).
    • Added router integration test suite apps/dokploy/__test__/notifications/receive-notification.test.ts covering:
      • Token validation against server secret.
      • Main server ("Dokploy") owner organization resolution and notification dispatch.
      • Remote server token validation and notification dispatch.
    • All 15 tests pass hermetically; Biome formatting check passes with 0 errors.

RetriggerView in GreptileConfidence Score: 4/5

Resolve the main host's organization consistently before merging.

Summary

Routes main-server threshold alerts through an owner organization and adds a fallback when no organization is supplied.

  • Main-server threshold alerts go to the owner organization’s notification channels.

Reviews (1) · Last reviewed commit: "fix(notifications): deliver main server ..." · Reviewed by Greptile

Comment on lines +521 to +525
const owner = await db.query.member.findFirst({
where: eq(member.role, "owner"),
});

organizationId = owner?.organizationId ?? "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Alerts reach the wrong organization

Creating another organization adds another member with role: "owner", so this lookup can match several rows. The unordered findFirst can select another organization's channels—or drop the alert if that organization has none enabled. Resolve the main host's intended organization explicitly instead of selecting an arbitrary owner.

Comment on lines +77 to +80
expect(mocks.findMany).toHaveBeenCalledOnce();
const callArgs = mocks.findMany.mock.calls[0]?.[0];
expect(callArgs).toBeDefined();
expect(callArgs.where).toBeDefined();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tests miss incorrect organization filters

The scoped test only checks that where exists. The fallback tests return fixed rows regardless of the query, then check that a notification was sent. Both the old always-scoped query and an always-unscoped query would pass. Assert the generated SQL and parameters for populated and missing organizationId values so these tests catch wrong recipients or dropped alerts.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server threshold (CPU/Memory) alerts for the main Dokploy server never reach any notification

1 participant