diff --git a/server/private/lib/alerts/index.ts b/server/private/lib/alerts/index.ts index 10538c737..7f34aea34 100644 --- a/server/private/lib/alerts/index.ts +++ b/server/private/lib/alerts/index.ts @@ -11,7 +11,6 @@ * This file is not licensed under the AGPLv3. */ -export * from "./types"; export * from "./processAlerts"; export * from "./sendAlertWebhook"; export * from "./sendAlertEmail"; diff --git a/server/private/lib/alerts/processAlerts.ts b/server/private/lib/alerts/processAlerts.ts index 681eb3cd0..a08a55494 100644 --- a/server/private/lib/alerts/processAlerts.ts +++ b/server/private/lib/alerts/processAlerts.ts @@ -27,9 +27,9 @@ import { import config from "@server/lib/config"; import { decrypt } from "@server/lib/crypto"; import logger from "@server/logger"; -import { AlertContext, WebhookAlertConfig } from "./types"; import { sendAlertWebhook } from "./sendAlertWebhook"; import { sendAlertEmail } from "./sendAlertEmail"; +import { AlertContext, WebhookAlertConfig } from "@server/routers/alertRule/types"; /** * Core alert processing pipeline. diff --git a/server/private/lib/alerts/sendAlertEmail.ts b/server/private/lib/alerts/sendAlertEmail.ts index 5e818678d..598262e38 100644 --- a/server/private/lib/alerts/sendAlertEmail.ts +++ b/server/private/lib/alerts/sendAlertEmail.ts @@ -15,7 +15,7 @@ import { sendEmail } from "@server/emails"; import AlertNotification from "@server/emails/templates/AlertNotification"; import config from "@server/lib/config"; import logger from "@server/logger"; -import { AlertContext } from "./types"; +import { AlertContext } from "@server/routers/alertRule/types"; /** * Sends an alert notification email to every address in `recipients`. diff --git a/server/private/lib/alerts/sendAlertWebhook.ts b/server/private/lib/alerts/sendAlertWebhook.ts index 52c687cbc..5f309d577 100644 --- a/server/private/lib/alerts/sendAlertWebhook.ts +++ b/server/private/lib/alerts/sendAlertWebhook.ts @@ -12,7 +12,7 @@ */ import logger from "@server/logger"; -import { AlertContext, WebhookAlertConfig } from "./types"; +import { AlertContext, WebhookAlertConfig } from "@server/routers/alertRule/types"; const REQUEST_TIMEOUT_MS = 15_000; @@ -137,4 +137,4 @@ function buildHeaders(webhookConfig: WebhookAlertConfig): Record } return headers; -} \ No newline at end of file +} diff --git a/server/private/lib/alerts/types.ts b/server/private/lib/alerts/types.ts deleted file mode 100644 index 0679b7ece..000000000 --- a/server/private/lib/alerts/types.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of a proprietary work. - * - * Copyright (c) 2025-2026 Fossorial, Inc. - * All rights reserved. - * - * This file is licensed under the Fossorial Commercial License. - * You may not use this file except in compliance with the License. - * Unauthorized use, copying, modification, or distribution is strictly prohibited. - * - * This file is not licensed under the AGPLv3. - */ - -// --------------------------------------------------------------------------- -// Alert event types -// --------------------------------------------------------------------------- - -export type AlertEventType = - | "site_online" - | "site_offline" - | "site_toggle" - | "health_check_healthy" - | "health_check_unhealthy" - | "health_check_toggle" - | "resource_healthy" - | "resource_unhealthy" - | "resource_toggle"; - -// --------------------------------------------------------------------------- -// Webhook authentication config (stored as encrypted JSON in the DB) -// --------------------------------------------------------------------------- - -export type WebhookAuthType = "none" | "bearer" | "basic" | "custom"; - -/** - * Stored as an encrypted JSON blob in `alertWebhookActions.config`. - */ -export interface WebhookAlertConfig { - /** Authentication strategy for the webhook endpoint */ - authType: WebhookAuthType; - /** Bearer token – used when authType === "bearer" */ - bearerToken?: string; - /** Basic credentials – "username:password" – used when authType === "basic" */ - basicCredentials?: string; - /** Custom header name – used when authType === "custom" */ - customHeaderName?: string; - /** Custom header value – used when authType === "custom" */ - customHeaderValue?: string; - /** Extra headers to send with every webhook request */ - headers?: Array<{ key: string; value: string }>; - /** HTTP method (default POST) */ - method?: string; -} - -// --------------------------------------------------------------------------- -// Internal alert event passed through the processing pipeline -// --------------------------------------------------------------------------- - -export interface AlertContext { - eventType: AlertEventType; - orgId: string; - /** Set for site_online / site_offline events */ - siteId?: number; - /** Set for health_check_* events */ - healthCheckId?: number; - /** Set for resource_* events */ - resourceId?: number; - /** Human-readable context data included in emails and webhook payloads */ - data: Record; -} diff --git a/server/private/routers/alertRule/createAlertRule.ts b/server/private/routers/alertRule/createAlertRule.ts index b9d17d35d..ceaacf73c 100644 --- a/server/private/routers/alertRule/createAlertRule.ts +++ b/server/private/routers/alertRule/createAlertRule.ts @@ -31,6 +31,7 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; import { encrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; +import { CreateAlertRuleResponse } from "@server/routers/alertRule/types"; export const SITE_EVENT_TYPES = ["site_online", "site_offline", "site_toggle"] as const; export const HC_EVENT_TYPES = [ @@ -169,10 +170,6 @@ const bodySchema = z } }); -export type CreateAlertRuleResponse = { - alertRuleId: number; -}; - registry.registerPath({ method: "put", path: "/org/{orgId}/alert-rule", diff --git a/server/private/routers/alertRule/getAlertRule.ts b/server/private/routers/alertRule/getAlertRule.ts index 06a97e880..9fd0157e3 100644 --- a/server/private/routers/alertRule/getAlertRule.ts +++ b/server/private/routers/alertRule/getAlertRule.ts @@ -32,7 +32,7 @@ import { OpenAPITags, registry } from "@server/openApi"; import { and, eq } from "drizzle-orm"; import { decrypt } from "@server/lib/crypto"; import config from "@server/lib/config"; -import { WebhookAlertConfig } from "#private/lib/alerts/types"; +import { GetAlertRuleResponse, WebhookAlertConfig } from "@server/routers/alertRule/types"; const paramsSchema = z .object({ @@ -41,43 +41,6 @@ const paramsSchema = z }) .strict(); -export type GetAlertRuleResponse = { - alertRuleId: number; - orgId: string; - name: string; - eventType: - | "site_online" - | "site_offline" - | "site_toggle" - | "health_check_healthy" - | "health_check_unhealthy" - | "health_check_toggle" - | "resource_healthy" - | "resource_unhealthy" - | "resource_toggle"; - enabled: boolean; - cooldownSeconds: number; - lastTriggeredAt: number | null; - createdAt: number; - updatedAt: number; - siteIds: number[]; - healthCheckIds: number[]; - resourceIds: number[]; - recipients: { - recipientId: number; - userId: string | null; - roleId: number | null; - email: string | null; - }[]; - webhookActions: { - webhookActionId: number; - webhookUrl: string; - enabled: boolean; - lastSentAt: number | null; - config: WebhookAlertConfig | null; - }[]; -}; - registry.registerPath({ method: "get", path: "/org/{orgId}/alert-rule/{alertRuleId}", diff --git a/server/private/routers/alertRule/listAlertRules.ts b/server/private/routers/alertRule/listAlertRules.ts index 25374b0e5..a31a4d119 100644 --- a/server/private/routers/alertRule/listAlertRules.ts +++ b/server/private/routers/alertRule/listAlertRules.ts @@ -27,6 +27,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; import { and, asc, desc, eq, inArray, like, or, sql } from "drizzle-orm"; +import { ListAlertRulesResponse } from "@server/routers/alertRule/types"; const paramsSchema = z.strictObject({ orgId: z.string().nonempty() diff --git a/server/routers/alertRule/types.ts b/server/routers/alertRule/types.ts index af1658537..a9e66350e 100644 --- a/server/routers/alertRule/types.ts +++ b/server/routers/alertRule/types.ts @@ -19,3 +19,102 @@ export type ListAlertRulesResponse = { offset: number; }; }; + +export type CreateAlertRuleResponse = { + alertRuleId: number; +}; + +export type GetAlertRuleResponse = { + alertRuleId: number; + orgId: string; + name: string; + eventType: + | "site_online" + | "site_offline" + | "site_toggle" + | "health_check_healthy" + | "health_check_unhealthy" + | "health_check_toggle" + | "resource_healthy" + | "resource_unhealthy" + | "resource_toggle"; + enabled: boolean; + cooldownSeconds: number; + lastTriggeredAt: number | null; + createdAt: number; + updatedAt: number; + siteIds: number[]; + healthCheckIds: number[]; + resourceIds: number[]; + recipients: { + recipientId: number; + userId: string | null; + roleId: number | null; + email: string | null; + }[]; + webhookActions: { + webhookActionId: number; + webhookUrl: string; + enabled: boolean; + lastSentAt: number | null; + config: WebhookAlertConfig | null; + }[]; +}; + +/** + * Stored as an encrypted JSON blob in `alertWebhookActions.config`. + */ +export interface WebhookAlertConfig { + /** Authentication strategy for the webhook endpoint */ + authType: WebhookAuthType; + /** Bearer token – used when authType === "bearer" */ + bearerToken?: string; + /** Basic credentials – "username:password" – used when authType === "basic" */ + basicCredentials?: string; + /** Custom header name – used when authType === "custom" */ + customHeaderName?: string; + /** Custom header value – used when authType === "custom" */ + customHeaderValue?: string; + /** Extra headers to send with every webhook request */ + headers?: Array<{ key: string; value: string }>; + /** HTTP method (default POST) */ + method?: string; +} + +// --------------------------------------------------------------------------- +// Alert event types +// --------------------------------------------------------------------------- + +export type AlertEventType = + | "site_online" + | "site_offline" + | "site_toggle" + | "health_check_healthy" + | "health_check_unhealthy" + | "health_check_toggle" + | "resource_healthy" + | "resource_unhealthy" + | "resource_toggle"; + +// --------------------------------------------------------------------------- +// Webhook authentication config (stored as encrypted JSON in the DB) +// --------------------------------------------------------------------------- + +export type WebhookAuthType = "none" | "bearer" | "basic" | "custom"; + +// --------------------------------------------------------------------------- +// Internal alert event passed through the processing pipeline +// --------------------------------------------------------------------------- + +export interface AlertContext { + eventType: AlertEventType; + orgId: string; + /** Set for site_online / site_offline events */ + siteId?: number; + /** Set for health_check_* events */ + healthCheckId?: number; + /** Set for resource_* events */ + resourceId?: number; + /** Human-readable context data included in emails and webhook payloads */ + data: Record; +} diff --git a/src/app/[orgId]/settings/alerting/[ruleId]/page.tsx b/src/app/[orgId]/settings/alerting/[ruleId]/page.tsx index 427ac7c44..b57f5dea4 100644 --- a/src/app/[orgId]/settings/alerting/[ruleId]/page.tsx +++ b/src/app/[orgId]/settings/alerting/[ruleId]/page.tsx @@ -12,7 +12,7 @@ import { useParams, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { useEffect, useState } from "react"; import type { AxiosResponse } from "axios"; -import type { GetAlertRuleResponse } from "@server/private/routers/alertRule"; +import type { GetAlertRuleResponse } from "@server/routers/alertRule/types"; import type { AlertRuleFormValues } from "@app/lib/alertRuleForm"; export default function EditAlertRulePage() { diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx index 19a095419..faf0de959 100644 --- a/src/components/LayoutSidebar.tsx +++ b/src/components/LayoutSidebar.tsx @@ -131,7 +131,7 @@ export function LayoutSidebar({ const showTrial = build === "saas" && Boolean(orgId) && - subscriptionContext?.isTrial + subscriptionContext?.isTrial; return (