mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-11 06:58:28 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 403b8a12e4 | |||
| 3f305e4d5c | |||
| 6689a8d93e |
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Задайте ПИН код на ресурса",
|
||||
"actionSetResourceEmailWhitelist": "Задайте списък на одобрените имейл адреси за ресурса",
|
||||
"actionGetResourceEmailWhitelist": "Вземете списък на одобрените имейл адреси за ресурса",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Получете политика за ресурси",
|
||||
"actionUpdateResourcePolicy": "Актуализирайте политика за ресурси",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Задайте потребители на ресурсна политика",
|
||||
"actionSetResourcePolicyRoles": "Задайте роли на ресурсна политика",
|
||||
"actionSetResourcePolicyPassword": "Задайте парола на ресурсна политика",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Nastavit zdrojový kód",
|
||||
"actionSetResourceEmailWhitelist": "Nastavit seznam povolených dokumentů",
|
||||
"actionGetResourceEmailWhitelist": "Získat seznam povolených dokumentů",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Získat zásady zdroje",
|
||||
"actionUpdateResourcePolicy": "Aktualizovat zásady zdroje",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Nastavte uživatele pro zásady zdroje",
|
||||
"actionSetResourcePolicyRoles": "Nastavte role pro zásady zdroje",
|
||||
"actionSetResourcePolicyPassword": "Nastavte heslo pro zásady zdroje",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Angiv ressource-PIN-kode",
|
||||
"actionSetResourceEmailWhitelist": "Angiv e-mailwhitelist for ressource",
|
||||
"actionGetResourceEmailWhitelist": "Hent e-mailwhitelist for ressource",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Få ressourceretningslinjen",
|
||||
"actionUpdateResourcePolicy": "Opdater ressourceretningslinjen",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Angiv ressourceretningslinjens brugere",
|
||||
"actionSetResourcePolicyRoles": "Angiv ressourceretningslinjens roller",
|
||||
"actionSetResourcePolicyPassword": "Angiv ressourceretningslinjen for adgangskode",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Ressourcen-PIN festlegen",
|
||||
"actionSetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist festlegen",
|
||||
"actionGetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist abrufen",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Ressourcenrichtlinie abrufen",
|
||||
"actionUpdateResourcePolicy": "Ressourcenrichtlinie aktualisieren",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Ressourcenrichtlinienbenutzer festlegen",
|
||||
"actionSetResourcePolicyRoles": "Rollen der Ressourcenrichtlinie festlegen",
|
||||
"actionSetResourcePolicyPassword": "Passwort der Ressourcenrichtlinie festlegen",
|
||||
|
||||
+4
-3
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Set Resource Pincode",
|
||||
"actionSetResourceEmailWhitelist": "Set Resource Email Whitelist",
|
||||
"actionGetResourceEmailWhitelist": "Get Resource Email Whitelist",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Get Resource Policy",
|
||||
"actionUpdateResourcePolicy": "Update Resource Policy",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Set Resource Policy Users",
|
||||
"actionSetResourcePolicyRoles": "Set Resource Policy Roles",
|
||||
"actionSetResourcePolicyPassword": "Set Resource Policy Password",
|
||||
@@ -1807,6 +1804,10 @@
|
||||
"alertingRulesBannerDescription": "Each rule ties together what to watch (a site, health check, or resource), when to fire (for example offline or unhealthy), and how to notify your team via email, webhooks, or integrations. Use this list to create, enable, and manage those rules.",
|
||||
"alertingHealthChecksBannerTitle": "Monitor Health & Resources",
|
||||
"alertingHealthChecksBannerDescription": "Health checks are HTTP or TCP monitors you define once. You can then use them as sources in alert rules so you get notified when a target becomes healthy or unhealthy. Health checks on resources also appear here.",
|
||||
"alertingTestRule": "Test Alert Rule",
|
||||
"alertingNoActionsTitle": "No actions configured",
|
||||
"alertingNoActionsSaveDescription": "Add at least one action so this rule can notify someone when it fires.",
|
||||
"alertingNoActionsTestDescription": "Add at least one action before you can test this rule.",
|
||||
"standaloneHcTableTitle": "Health Checks",
|
||||
"standaloneHcSearchPlaceholder": "Search health checks…",
|
||||
"standaloneHcAddButton": "Create Health Check",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Establecer Pincode del recurso",
|
||||
"actionSetResourceEmailWhitelist": "Establecer lista blanca de correo de recursos",
|
||||
"actionGetResourceEmailWhitelist": "Obtener correo electrónico de recursos",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Obtener política de recursos",
|
||||
"actionUpdateResourcePolicy": "Actualizar política de recursos",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Definir política de recursos para usuarios",
|
||||
"actionSetResourcePolicyRoles": "Definir roles de política de recursos",
|
||||
"actionSetResourcePolicyPassword": "Definir contraseña de política de recursos",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Définir le code PIN de la ressource",
|
||||
"actionSetResourceEmailWhitelist": "Définir la liste blanche des emails de la ressource",
|
||||
"actionGetResourceEmailWhitelist": "Obtenir la liste blanche des emails de la ressource",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Obtenir une politique de ressources",
|
||||
"actionUpdateResourcePolicy": "Mettre à jour la politique de ressources",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Définir les utilisateurs de la politique de ressources",
|
||||
"actionSetResourcePolicyRoles": "Définir les rôles de la politique de ressources",
|
||||
"actionSetResourcePolicyPassword": "Définir le mot de passe de la politique de ressources",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Imposta Codice PIN Risorsa",
|
||||
"actionSetResourceEmailWhitelist": "Imposta Lista Autorizzazioni Email Risorsa",
|
||||
"actionGetResourceEmailWhitelist": "Ottieni Lista Autorizzazioni Email Risorsa",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Ottieni la Politica della Risorsa",
|
||||
"actionUpdateResourcePolicy": "Aggiorna la Politica della Risorsa",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Imposta Utenti della Politica Risorsa",
|
||||
"actionSetResourcePolicyRoles": "Imposta Ruoli della Politica Risorsa",
|
||||
"actionSetResourcePolicyPassword": "Imposta Password della Politica Risorsa",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "리소스 핀코드 설정",
|
||||
"actionSetResourceEmailWhitelist": "리소스 이메일 화이트리스트 설정",
|
||||
"actionGetResourceEmailWhitelist": "리소스 이메일 화이트리스트 가져오기",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "리소스 정책 가져오기",
|
||||
"actionUpdateResourcePolicy": "리소스 정책 업데이트",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "리소스 정책 사용자 설정",
|
||||
"actionSetResourcePolicyRoles": "리소스 정책 역할 설정",
|
||||
"actionSetResourcePolicyPassword": "리소스 정책 비밀번호 설정",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Angi ressurspinkode",
|
||||
"actionSetResourceEmailWhitelist": "Angi e-post-hviteliste for ressurs",
|
||||
"actionGetResourceEmailWhitelist": "Hent e-post-hviteliste for ressurs",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Hent ressursregel",
|
||||
"actionUpdateResourcePolicy": "Oppdater ressursregel",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Sett ressursregel for brukere",
|
||||
"actionSetResourcePolicyRoles": "Sett ressursregel for roller",
|
||||
"actionSetResourcePolicyPassword": "Sett ressursregel for passord",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Stel Resource Pincode in",
|
||||
"actionSetResourceEmailWhitelist": "Stel Resource e-mail whitelist in",
|
||||
"actionGetResourceEmailWhitelist": "Verkrijg Resource E-mail Whitelist",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Haal Bronbeleid op",
|
||||
"actionUpdateResourcePolicy": "Werk Bronbeleid bij",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Stel gebruikers van bronbeleid in",
|
||||
"actionSetResourcePolicyRoles": "Stel rollen van bronbeleid in",
|
||||
"actionSetResourcePolicyPassword": "Stel wachtwoord van bronbeleid in",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Ustaw kod PIN zasobu",
|
||||
"actionSetResourceEmailWhitelist": "Ustaw białą listę email zasobu",
|
||||
"actionGetResourceEmailWhitelist": "Pobierz białą listę email zasobu",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Pobierz politykę zasobów",
|
||||
"actionUpdateResourcePolicy": "Zaktualizuj politykę zasobów",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Ustaw użytkowników polityki zasobów",
|
||||
"actionSetResourcePolicyRoles": "Ustaw role polityki zasobów",
|
||||
"actionSetResourcePolicyPassword": "Ustaw hasło polityki zasobów",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Definir Código PIN do Recurso",
|
||||
"actionSetResourceEmailWhitelist": "Definir Lista Permitida de Emails do Recurso",
|
||||
"actionGetResourceEmailWhitelist": "Obter Lista Permitida de Emails do Recurso",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Obter Política de Recurso",
|
||||
"actionUpdateResourcePolicy": "Atualizar Política de Recurso",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Definir Utilizadores da Política de Recurso",
|
||||
"actionSetResourcePolicyRoles": "Definir Papéis da Política de Recurso",
|
||||
"actionSetResourcePolicyPassword": "Definir Palavra-passe da Política de Recurso",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Установить ПИН-код ресурса",
|
||||
"actionSetResourceEmailWhitelist": "Настроить белый список ресурсов email",
|
||||
"actionGetResourceEmailWhitelist": "Получить белый список ресурсов email",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Получить политику ресурса",
|
||||
"actionUpdateResourcePolicy": "Обновить политику ресурса",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Установить пользователей политики ресурса",
|
||||
"actionSetResourcePolicyRoles": "Установить роли политики ресурса",
|
||||
"actionSetResourcePolicyPassword": "Установить пароль политики ресурса",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "Kaynak PIN Kodunu Ayarla",
|
||||
"actionSetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesi Ayarla",
|
||||
"actionGetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesini Al",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "Kaynak Politikasını Al",
|
||||
"actionUpdateResourcePolicy": "Kaynak Politikasını Güncelle",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "Kaynak Politika Kullanıcılarını Ayarla",
|
||||
"actionSetResourcePolicyRoles": "Kaynak Politika Rolleri Ayarla",
|
||||
"actionSetResourcePolicyPassword": "Kaynak Politika Şifresini Ayarla",
|
||||
|
||||
@@ -1449,11 +1449,8 @@
|
||||
"actionSetResourcePincode": "设置资源粉码",
|
||||
"actionSetResourceEmailWhitelist": "设置资源电子邮件白名单",
|
||||
"actionGetResourceEmailWhitelist": "获取资源电子邮件白名单",
|
||||
"actionListResourcePolicies": "List Resource Policies",
|
||||
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||
"actionGetResourcePolicy": "获取资源策略",
|
||||
"actionUpdateResourcePolicy": "更新资源策略",
|
||||
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||
"actionSetResourcePolicyUsers": "设置资源策略用户",
|
||||
"actionSetResourcePolicyRoles": "设置资源策略角色",
|
||||
"actionSetResourcePolicyPassword": "设置资源策略密码",
|
||||
|
||||
@@ -151,6 +151,7 @@ export enum ActionsEnum {
|
||||
createAlertRule = "createAlertRule",
|
||||
updateAlertRule = "updateAlertRule",
|
||||
deleteAlertRule = "deleteAlertRule",
|
||||
testAlertRule = "testAlertRule",
|
||||
listAlertRules = "listAlertRules",
|
||||
listOrgLabels = "listOrgLabels",
|
||||
createOrgLabel = "createOrgLabel",
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import logger from "@server/logger";
|
||||
import type {
|
||||
EmailAlertAction,
|
||||
TestAlertContext
|
||||
} from "@server/routers/alertRule/types";
|
||||
import { sendAlertEmail } from "./sendAlertEmail";
|
||||
import type { db, alertEmailRecipients, users, userOrgRoles } from "@server/db";
|
||||
import type { eq } from "drizzle-orm";
|
||||
|
||||
export async function processTestAlerts(context: TestAlertContext) {
|
||||
const emailActions = context.actions.filter(
|
||||
(action) => action.type === "email"
|
||||
);
|
||||
// Process email actions
|
||||
for (const action of emailActions) {
|
||||
try {
|
||||
const recipients = await resolveEmailRecipients(action);
|
||||
if (recipients.length > 0) {
|
||||
await sendAlertEmail(recipients, context);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`processAlerts: failed to send alert email`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves all email addresses for a given `emailActionId`.
|
||||
*
|
||||
* Recipients may be:
|
||||
* - Direct users (by `userId`)
|
||||
* - All users in a role (by `roleId`, resolved via `userOrgRoles`)
|
||||
* - Direct external email addresses
|
||||
*/
|
||||
async function resolveEmailRecipients(
|
||||
action: EmailAlertAction
|
||||
): Promise<string[]> {
|
||||
const emailSet = new Set<string>();
|
||||
|
||||
// for (const row of rows) {
|
||||
// if (row.email) {
|
||||
// emailSet.add(row.email);
|
||||
// }
|
||||
|
||||
// if (row.userId) {
|
||||
// const [user] = await db
|
||||
// .select({ email: users.email })
|
||||
// .from(users)
|
||||
// .where(eq(users.userId, row.userId))
|
||||
// .limit(1);
|
||||
// if (user?.email) {
|
||||
// emailSet.add(user.email);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (row.roleId) {
|
||||
// // Find all users with this role via userOrgRoles
|
||||
// const roleUsers = await db
|
||||
// .select({ email: users.email })
|
||||
// .from(userOrgRoles)
|
||||
// .innerJoin(users, eq(userOrgRoles.userId, users.userId))
|
||||
// .where(eq(userOrgRoles.roleId, Number(row.roleId)));
|
||||
|
||||
// for (const u of roleUsers) {
|
||||
// if (u.email) {
|
||||
// emailSet.add(u.email);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return Array.from(emailSet);
|
||||
}
|
||||
@@ -16,3 +16,4 @@ export * from "./updateAlertRule";
|
||||
export * from "./deleteAlertRule";
|
||||
export * from "./listAlertRules";
|
||||
export * from "./getAlertRule";
|
||||
export * from "./testAlertRule";
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
import {
|
||||
alertRules,
|
||||
alertSites,
|
||||
alertHealthChecks,
|
||||
alertResources
|
||||
} from "@server/db";
|
||||
import response from "@server/lib/response";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
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()
|
||||
});
|
||||
|
||||
export const SITE_EVENT_TYPES = [
|
||||
"site_online",
|
||||
"site_offline",
|
||||
"site_toggle"
|
||||
] as const;
|
||||
export const HC_EVENT_TYPES = [
|
||||
"health_check_healthy",
|
||||
"health_check_unhealthy",
|
||||
"health_check_toggle"
|
||||
] as const;
|
||||
export const RESOURCE_EVENT_TYPES = [
|
||||
"resource_healthy",
|
||||
"resource_unhealthy",
|
||||
"resource_degraded",
|
||||
"resource_toggle"
|
||||
] as const;
|
||||
|
||||
const webhookActionSchema = z.strictObject({
|
||||
webhookUrl: z.string().url(),
|
||||
config: z.string().optional(),
|
||||
enabled: z.boolean().optional().default(true)
|
||||
});
|
||||
|
||||
const bodySchema = z.strictObject({
|
||||
eventType: z.enum([
|
||||
...HC_EVENT_TYPES,
|
||||
...SITE_EVENT_TYPES,
|
||||
...RESOURCE_EVENT_TYPES
|
||||
]),
|
||||
// Email recipients (flat)
|
||||
userIds: z.array(z.string().nonempty()).optional().default([]),
|
||||
roleIds: z.array(z.number()).optional().default([]),
|
||||
emails: z.array(z.email()).optional().default([]),
|
||||
// Webhook actions
|
||||
webhookActions: z.array(webhookActionSchema).optional().default([])
|
||||
});
|
||||
|
||||
export async function testAlertRule(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<any> {
|
||||
try {
|
||||
const parsedParams = paramsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
fromError(parsedParams.error).toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
const { orgId } = parsedParams.data;
|
||||
|
||||
const parsedBody = bodySchema.safeParse(req.body);
|
||||
if (!parsedBody.success) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
fromError(parsedBody.error).toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: process alert rule
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return next(
|
||||
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -808,6 +808,14 @@ authenticated.get(
|
||||
alertRule.listAlertRules
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/org/:orgId/alert-rule/test",
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyUserHasAction(ActionsEnum.testAlertRule),
|
||||
alertRule.testAlertRule
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
"/org/:orgId/alert-rule/:alertRuleId",
|
||||
verifyValidLicense,
|
||||
|
||||
@@ -124,3 +124,26 @@ export interface AlertContext {
|
||||
/** Human-readable context data included in emails and webhook payloads */
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export type EmailAlertAction = {
|
||||
type: "email";
|
||||
userIds?: string[];
|
||||
roleIds?: string[];
|
||||
emails?: string[];
|
||||
};
|
||||
|
||||
export type WebhookAlertAction = {
|
||||
type: "webhook";
|
||||
webhookUrl: string;
|
||||
enabled: boolean;
|
||||
config?: string | undefined;
|
||||
};
|
||||
|
||||
type AlertAction = EmailAlertAction | WebhookAlertAction;
|
||||
export interface TestAlertContext {
|
||||
eventType: AlertEventType;
|
||||
actions: AlertAction[];
|
||||
orgId: string;
|
||||
/** Human-readable context data included in emails and webhook payloads */
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
@@ -726,8 +726,8 @@ authenticated.post(
|
||||
verifyApiKeyResourcePolicyAccess,
|
||||
verifyApiKeyRoleAccess,
|
||||
verifyLimits,
|
||||
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyUsers),
|
||||
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyRoles),
|
||||
verifyUserHasAction(ActionsEnum.setResourcePolicyUsers),
|
||||
verifyUserHasAction(ActionsEnum.setResourcePolicyRoles),
|
||||
logActionAudit(ActionsEnum.setResourcePolicyUsers),
|
||||
logActionAudit(ActionsEnum.setResourcePolicyRoles),
|
||||
policy.setResourcePolicyAccessControl
|
||||
@@ -742,8 +742,8 @@ authenticated.put(
|
||||
verifyApiKeyResourcePolicyAccess,
|
||||
verifyApiKeyRoleAccess,
|
||||
verifyLimits,
|
||||
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyUsers),
|
||||
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyRoles),
|
||||
verifyUserHasAction(ActionsEnum.setResourcePolicyUsers),
|
||||
verifyUserHasAction(ActionsEnum.setResourcePolicyRoles),
|
||||
logActionAudit(ActionsEnum.setResourcePolicyUsers),
|
||||
logActionAudit(ActionsEnum.setResourcePolicyRoles),
|
||||
policy.setResourcePolicyAccessControl
|
||||
|
||||
@@ -115,11 +115,8 @@ function getActionsCategories(root: boolean) {
|
||||
},
|
||||
|
||||
"Resource Policy": {
|
||||
[t("actionListResourcePolicies")]: "listResourcePolicies",
|
||||
[t("actionCreateResourcePolicy")]: "createResourcePolicy",
|
||||
[t("actionGetResourcePolicy")]: "getResourcePolicy",
|
||||
[t("actionUpdateResourcePolicy")]: "updateResourcePolicy",
|
||||
[t("actionDeleteResourcePolicy")]: "deleteResourcePolicy",
|
||||
[t("actionSetResourcePolicyUsers")]: "setResourcePolicyUsers",
|
||||
[t("actionSetResourcePolicyRoles")]: "setResourcePolicyRoles",
|
||||
[t("actionSetResourcePolicyPassword")]: "setResourcePolicyPassword",
|
||||
|
||||
@@ -6,7 +6,9 @@ import {
|
||||
AlertRuleSourceFields,
|
||||
AlertRuleTriggerFields
|
||||
} from "@app/components/alert-rule-editor/AlertRuleFields";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { SettingsContainer } from "@app/components/Settings";
|
||||
import { SwitchInput } from "@app/components/SwitchInput";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { Card, CardContent } from "@app/components/ui/card";
|
||||
import {
|
||||
@@ -19,6 +21,7 @@ import {
|
||||
FormMessage
|
||||
} from "@app/components/ui/form";
|
||||
import { Input } from "@app/components/ui/input";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import {
|
||||
buildFormSchema,
|
||||
@@ -27,19 +30,15 @@ import {
|
||||
type AlertRuleFormValues
|
||||
} from "@app/lib/alertRuleForm";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import type { CreateAlertRuleResponse } from "@server/routers/alertRule/types";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ChevronLeft, Cog, Flag, Zap } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useMemo, useState, type ReactNode } from "react";
|
||||
import { useFieldArray, useForm, type Resolver } from "react-hook-form";
|
||||
import { Cog, Flag, Zap, ZapIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { SwitchInput } from "@app/components/SwitchInput";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useActionState, useMemo, useTransition, type ReactNode } from "react";
|
||||
import { useFieldArray, useForm, type Resolver } from "react-hook-form";
|
||||
import { Badge } from "../ui/badge";
|
||||
|
||||
const FORM_ID = "alert-rule-form";
|
||||
@@ -115,7 +114,6 @@ export default function AlertRuleGraphEditor({
|
||||
const t = useTranslations();
|
||||
const router = useRouter();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const schema = useMemo(() => buildFormSchema(t), [t]);
|
||||
const form = useForm<AlertRuleFormValues>({
|
||||
resolver: zodResolver(schema) as Resolver<AlertRuleFormValues>,
|
||||
@@ -127,8 +125,22 @@ export default function AlertRuleGraphEditor({
|
||||
name: "actions"
|
||||
});
|
||||
|
||||
const onSubmit = form.handleSubmit(async (values) => {
|
||||
setIsSaving(true);
|
||||
const saveAlert = async () => {
|
||||
const isValid = await form.trigger();
|
||||
if (!isValid) {
|
||||
const values = form.getValues();
|
||||
if (values.actions.length === 0) {
|
||||
toast({
|
||||
variant: "warning",
|
||||
title: t("alertingNoActionsTitle"),
|
||||
description: t("alertingNoActionsSaveDescription")
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const values = form.getValues();
|
||||
|
||||
try {
|
||||
const payload = formValuesToApiPayload(values);
|
||||
if (isNew) {
|
||||
@@ -158,14 +170,37 @@ export default function AlertRuleGraphEditor({
|
||||
description: formatAxiosError(e),
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
});
|
||||
// const submit = form.handleSubmit(async (values) => {
|
||||
|
||||
// });
|
||||
|
||||
// await submit();
|
||||
};
|
||||
|
||||
const testAlert = async () => {
|
||||
const isValid = await form.trigger();
|
||||
if (!isValid) {
|
||||
const values = form.getValues();
|
||||
if (values.actions.length === 0) {
|
||||
toast({
|
||||
variant: "warning",
|
||||
title: t("alertingNoActionsTitle"),
|
||||
description: t("alertingNoActionsTestDescription")
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const values = form.getValues();
|
||||
};
|
||||
|
||||
const [, formAction, isSaving] = useActionState(saveAlert, null);
|
||||
const [isTestingAlert, startTransition] = useTransition();
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form id={FORM_ID} onSubmit={onSubmit}>
|
||||
<form id={FORM_ID} action={formAction}>
|
||||
<SettingsContainer>
|
||||
<PaidFeaturesAlert tiers={tierMatrix.alertingRules} />
|
||||
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8 items-start">
|
||||
@@ -263,14 +298,29 @@ export default function AlertRuleGraphEditor({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={isSaving}
|
||||
loading={isSaving}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
<div className="flex flex-col items-center w-full gap-3">
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={isSaving}
|
||||
loading={isSaving}
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="w-full gap-1.5"
|
||||
onClick={() =>
|
||||
startTransition(testAlert)
|
||||
}
|
||||
loading={isTestingAlert}
|
||||
>
|
||||
{t("alertingTestRule")}
|
||||
<ZapIcon className="size-3.5 flex-none" />
|
||||
</Button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user