mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-12 07:20:43 +02:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a91147b86 | |||
| 2b20561461 | |||
| 9fc7e408c1 | |||
| 66748b28f1 | |||
| bd84119fd8 | |||
| 20c86108e6 | |||
| a4be9cc696 | |||
| 7909e792d0 | |||
| 8741885c85 | |||
| 4d839f1a1b | |||
| 842dd04556 | |||
| 897ed9c8d8 | |||
| a411df735f | |||
| c8217461b1 | |||
| 0a0ecd3b6f | |||
| 996160fadc |
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Задайте ПИН код на ресурса",
|
"actionSetResourcePincode": "Задайте ПИН код на ресурса",
|
||||||
"actionSetResourceEmailWhitelist": "Задайте списък на одобрените имейл адреси за ресурса",
|
"actionSetResourceEmailWhitelist": "Задайте списък на одобрените имейл адреси за ресурса",
|
||||||
"actionGetResourceEmailWhitelist": "Вземете списък на одобрените имейл адреси за ресурса",
|
"actionGetResourceEmailWhitelist": "Вземете списък на одобрените имейл адреси за ресурса",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Получете политика за ресурси",
|
"actionGetResourcePolicy": "Получете политика за ресурси",
|
||||||
"actionUpdateResourcePolicy": "Актуализирайте политика за ресурси",
|
"actionUpdateResourcePolicy": "Актуализирайте политика за ресурси",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Задайте потребители на ресурсна политика",
|
"actionSetResourcePolicyUsers": "Задайте потребители на ресурсна политика",
|
||||||
"actionSetResourcePolicyRoles": "Задайте роли на ресурсна политика",
|
"actionSetResourcePolicyRoles": "Задайте роли на ресурсна политика",
|
||||||
"actionSetResourcePolicyPassword": "Задайте парола на ресурсна политика",
|
"actionSetResourcePolicyPassword": "Задайте парола на ресурсна политика",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Nastavit zdrojový kód",
|
"actionSetResourcePincode": "Nastavit zdrojový kód",
|
||||||
"actionSetResourceEmailWhitelist": "Nastavit seznam povolených dokumentů",
|
"actionSetResourceEmailWhitelist": "Nastavit seznam povolených dokumentů",
|
||||||
"actionGetResourceEmailWhitelist": "Získat 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",
|
"actionGetResourcePolicy": "Získat zásady zdroje",
|
||||||
"actionUpdateResourcePolicy": "Aktualizovat zásady zdroje",
|
"actionUpdateResourcePolicy": "Aktualizovat zásady zdroje",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Nastavte uživatele pro zásady zdroje",
|
"actionSetResourcePolicyUsers": "Nastavte uživatele pro zásady zdroje",
|
||||||
"actionSetResourcePolicyRoles": "Nastavte role pro zásady zdroje",
|
"actionSetResourcePolicyRoles": "Nastavte role pro zásady zdroje",
|
||||||
"actionSetResourcePolicyPassword": "Nastavte heslo pro zásady zdroje",
|
"actionSetResourcePolicyPassword": "Nastavte heslo pro zásady zdroje",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Angiv ressource-PIN-kode",
|
"actionSetResourcePincode": "Angiv ressource-PIN-kode",
|
||||||
"actionSetResourceEmailWhitelist": "Angiv e-mailwhitelist for ressource",
|
"actionSetResourceEmailWhitelist": "Angiv e-mailwhitelist for ressource",
|
||||||
"actionGetResourceEmailWhitelist": "Hent e-mailwhitelist for ressource",
|
"actionGetResourceEmailWhitelist": "Hent e-mailwhitelist for ressource",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Få ressourceretningslinjen",
|
"actionGetResourcePolicy": "Få ressourceretningslinjen",
|
||||||
"actionUpdateResourcePolicy": "Opdater ressourceretningslinjen",
|
"actionUpdateResourcePolicy": "Opdater ressourceretningslinjen",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Angiv ressourceretningslinjens brugere",
|
"actionSetResourcePolicyUsers": "Angiv ressourceretningslinjens brugere",
|
||||||
"actionSetResourcePolicyRoles": "Angiv ressourceretningslinjens roller",
|
"actionSetResourcePolicyRoles": "Angiv ressourceretningslinjens roller",
|
||||||
"actionSetResourcePolicyPassword": "Angiv ressourceretningslinjen for adgangskode",
|
"actionSetResourcePolicyPassword": "Angiv ressourceretningslinjen for adgangskode",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Ressourcen-PIN festlegen",
|
"actionSetResourcePincode": "Ressourcen-PIN festlegen",
|
||||||
"actionSetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist festlegen",
|
"actionSetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist festlegen",
|
||||||
"actionGetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist abrufen",
|
"actionGetResourceEmailWhitelist": "Ressourcen-E-Mail-Whitelist abrufen",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Ressourcenrichtlinie abrufen",
|
"actionGetResourcePolicy": "Ressourcenrichtlinie abrufen",
|
||||||
"actionUpdateResourcePolicy": "Ressourcenrichtlinie aktualisieren",
|
"actionUpdateResourcePolicy": "Ressourcenrichtlinie aktualisieren",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Ressourcenrichtlinienbenutzer festlegen",
|
"actionSetResourcePolicyUsers": "Ressourcenrichtlinienbenutzer festlegen",
|
||||||
"actionSetResourcePolicyRoles": "Rollen der Ressourcenrichtlinie festlegen",
|
"actionSetResourcePolicyRoles": "Rollen der Ressourcenrichtlinie festlegen",
|
||||||
"actionSetResourcePolicyPassword": "Passwort der Ressourcenrichtlinie festlegen",
|
"actionSetResourcePolicyPassword": "Passwort der Ressourcenrichtlinie festlegen",
|
||||||
|
|||||||
+3
-6
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Set Resource Pincode",
|
"actionSetResourcePincode": "Set Resource Pincode",
|
||||||
"actionSetResourceEmailWhitelist": "Set Resource Email Whitelist",
|
"actionSetResourceEmailWhitelist": "Set Resource Email Whitelist",
|
||||||
"actionGetResourceEmailWhitelist": "Get Resource Email Whitelist",
|
"actionGetResourceEmailWhitelist": "Get Resource Email Whitelist",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Get Resource Policy",
|
"actionGetResourcePolicy": "Get Resource Policy",
|
||||||
"actionUpdateResourcePolicy": "Update Resource Policy",
|
"actionUpdateResourcePolicy": "Update Resource Policy",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Set Resource Policy Users",
|
"actionSetResourcePolicyUsers": "Set Resource Policy Users",
|
||||||
"actionSetResourcePolicyRoles": "Set Resource Policy Roles",
|
"actionSetResourcePolicyRoles": "Set Resource Policy Roles",
|
||||||
"actionSetResourcePolicyPassword": "Set Resource Policy Password",
|
"actionSetResourcePolicyPassword": "Set Resource Policy Password",
|
||||||
@@ -1695,8 +1698,6 @@
|
|||||||
"alertingRuleSaved": "Alert rule saved",
|
"alertingRuleSaved": "Alert rule saved",
|
||||||
"alertingRuleSavedCreatedDescription": "Your new alert rule was created. You can keep editing it on this page.",
|
"alertingRuleSavedCreatedDescription": "Your new alert rule was created. You can keep editing it on this page.",
|
||||||
"alertingRuleSavedUpdatedDescription": "Your changes to this alert rule were saved.",
|
"alertingRuleSavedUpdatedDescription": "Your changes to this alert rule were saved.",
|
||||||
"alertingTestAlertSent": "Test alert sent",
|
|
||||||
"alertingTestAlertSentDescription": "A test alert was sent to the actions configured on this rule.",
|
|
||||||
"alertingEditRule": "Edit Alert Rule",
|
"alertingEditRule": "Edit Alert Rule",
|
||||||
"alertingCreateRule": "Create Alert Rule",
|
"alertingCreateRule": "Create Alert Rule",
|
||||||
"alertingRuleCredenzaDescription": "Choose what to watch, when to fire, and how to notify",
|
"alertingRuleCredenzaDescription": "Choose what to watch, when to fire, and how to notify",
|
||||||
@@ -1806,10 +1807,6 @@
|
|||||||
"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.",
|
"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",
|
"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.",
|
"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",
|
"standaloneHcTableTitle": "Health Checks",
|
||||||
"standaloneHcSearchPlaceholder": "Search health checks…",
|
"standaloneHcSearchPlaceholder": "Search health checks…",
|
||||||
"standaloneHcAddButton": "Create Health Check",
|
"standaloneHcAddButton": "Create Health Check",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Establecer Pincode del recurso",
|
"actionSetResourcePincode": "Establecer Pincode del recurso",
|
||||||
"actionSetResourceEmailWhitelist": "Establecer lista blanca de correo de recursos",
|
"actionSetResourceEmailWhitelist": "Establecer lista blanca de correo de recursos",
|
||||||
"actionGetResourceEmailWhitelist": "Obtener correo electrónico de recursos",
|
"actionGetResourceEmailWhitelist": "Obtener correo electrónico de recursos",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Obtener política de recursos",
|
"actionGetResourcePolicy": "Obtener política de recursos",
|
||||||
"actionUpdateResourcePolicy": "Actualizar política de recursos",
|
"actionUpdateResourcePolicy": "Actualizar política de recursos",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Definir política de recursos para usuarios",
|
"actionSetResourcePolicyUsers": "Definir política de recursos para usuarios",
|
||||||
"actionSetResourcePolicyRoles": "Definir roles de política de recursos",
|
"actionSetResourcePolicyRoles": "Definir roles de política de recursos",
|
||||||
"actionSetResourcePolicyPassword": "Definir contraseña de política de recursos",
|
"actionSetResourcePolicyPassword": "Definir contraseña de política de recursos",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Définir le code PIN de la ressource",
|
"actionSetResourcePincode": "Définir le code PIN de la ressource",
|
||||||
"actionSetResourceEmailWhitelist": "Définir la liste blanche des emails de la ressource",
|
"actionSetResourceEmailWhitelist": "Définir la liste blanche des emails de la ressource",
|
||||||
"actionGetResourceEmailWhitelist": "Obtenir 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",
|
"actionGetResourcePolicy": "Obtenir une politique de ressources",
|
||||||
"actionUpdateResourcePolicy": "Mettre à jour la politique de ressources",
|
"actionUpdateResourcePolicy": "Mettre à jour la politique de ressources",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Définir les utilisateurs de la politique de ressources",
|
"actionSetResourcePolicyUsers": "Définir les utilisateurs de la politique de ressources",
|
||||||
"actionSetResourcePolicyRoles": "Définir les rôles 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",
|
"actionSetResourcePolicyPassword": "Définir le mot de passe de la politique de ressources",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Imposta Codice PIN Risorsa",
|
"actionSetResourcePincode": "Imposta Codice PIN Risorsa",
|
||||||
"actionSetResourceEmailWhitelist": "Imposta Lista Autorizzazioni Email Risorsa",
|
"actionSetResourceEmailWhitelist": "Imposta Lista Autorizzazioni Email Risorsa",
|
||||||
"actionGetResourceEmailWhitelist": "Ottieni Lista Autorizzazioni Email Risorsa",
|
"actionGetResourceEmailWhitelist": "Ottieni Lista Autorizzazioni Email Risorsa",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Ottieni la Politica della Risorsa",
|
"actionGetResourcePolicy": "Ottieni la Politica della Risorsa",
|
||||||
"actionUpdateResourcePolicy": "Aggiorna la Politica della Risorsa",
|
"actionUpdateResourcePolicy": "Aggiorna la Politica della Risorsa",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Imposta Utenti della Politica Risorsa",
|
"actionSetResourcePolicyUsers": "Imposta Utenti della Politica Risorsa",
|
||||||
"actionSetResourcePolicyRoles": "Imposta Ruoli della Politica Risorsa",
|
"actionSetResourcePolicyRoles": "Imposta Ruoli della Politica Risorsa",
|
||||||
"actionSetResourcePolicyPassword": "Imposta Password della Politica Risorsa",
|
"actionSetResourcePolicyPassword": "Imposta Password della Politica Risorsa",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "리소스 핀코드 설정",
|
"actionSetResourcePincode": "리소스 핀코드 설정",
|
||||||
"actionSetResourceEmailWhitelist": "리소스 이메일 화이트리스트 설정",
|
"actionSetResourceEmailWhitelist": "리소스 이메일 화이트리스트 설정",
|
||||||
"actionGetResourceEmailWhitelist": "리소스 이메일 화이트리스트 가져오기",
|
"actionGetResourceEmailWhitelist": "리소스 이메일 화이트리스트 가져오기",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "리소스 정책 가져오기",
|
"actionGetResourcePolicy": "리소스 정책 가져오기",
|
||||||
"actionUpdateResourcePolicy": "리소스 정책 업데이트",
|
"actionUpdateResourcePolicy": "리소스 정책 업데이트",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "리소스 정책 사용자 설정",
|
"actionSetResourcePolicyUsers": "리소스 정책 사용자 설정",
|
||||||
"actionSetResourcePolicyRoles": "리소스 정책 역할 설정",
|
"actionSetResourcePolicyRoles": "리소스 정책 역할 설정",
|
||||||
"actionSetResourcePolicyPassword": "리소스 정책 비밀번호 설정",
|
"actionSetResourcePolicyPassword": "리소스 정책 비밀번호 설정",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Angi ressurspinkode",
|
"actionSetResourcePincode": "Angi ressurspinkode",
|
||||||
"actionSetResourceEmailWhitelist": "Angi e-post-hviteliste for ressurs",
|
"actionSetResourceEmailWhitelist": "Angi e-post-hviteliste for ressurs",
|
||||||
"actionGetResourceEmailWhitelist": "Hent e-post-hviteliste for ressurs",
|
"actionGetResourceEmailWhitelist": "Hent e-post-hviteliste for ressurs",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Hent ressursregel",
|
"actionGetResourcePolicy": "Hent ressursregel",
|
||||||
"actionUpdateResourcePolicy": "Oppdater ressursregel",
|
"actionUpdateResourcePolicy": "Oppdater ressursregel",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Sett ressursregel for brukere",
|
"actionSetResourcePolicyUsers": "Sett ressursregel for brukere",
|
||||||
"actionSetResourcePolicyRoles": "Sett ressursregel for roller",
|
"actionSetResourcePolicyRoles": "Sett ressursregel for roller",
|
||||||
"actionSetResourcePolicyPassword": "Sett ressursregel for passord",
|
"actionSetResourcePolicyPassword": "Sett ressursregel for passord",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Stel Resource Pincode in",
|
"actionSetResourcePincode": "Stel Resource Pincode in",
|
||||||
"actionSetResourceEmailWhitelist": "Stel Resource e-mail whitelist in",
|
"actionSetResourceEmailWhitelist": "Stel Resource e-mail whitelist in",
|
||||||
"actionGetResourceEmailWhitelist": "Verkrijg Resource E-mail Whitelist",
|
"actionGetResourceEmailWhitelist": "Verkrijg Resource E-mail Whitelist",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Haal Bronbeleid op",
|
"actionGetResourcePolicy": "Haal Bronbeleid op",
|
||||||
"actionUpdateResourcePolicy": "Werk Bronbeleid bij",
|
"actionUpdateResourcePolicy": "Werk Bronbeleid bij",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Stel gebruikers van bronbeleid in",
|
"actionSetResourcePolicyUsers": "Stel gebruikers van bronbeleid in",
|
||||||
"actionSetResourcePolicyRoles": "Stel rollen van bronbeleid in",
|
"actionSetResourcePolicyRoles": "Stel rollen van bronbeleid in",
|
||||||
"actionSetResourcePolicyPassword": "Stel wachtwoord van bronbeleid in",
|
"actionSetResourcePolicyPassword": "Stel wachtwoord van bronbeleid in",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Ustaw kod PIN zasobu",
|
"actionSetResourcePincode": "Ustaw kod PIN zasobu",
|
||||||
"actionSetResourceEmailWhitelist": "Ustaw białą listę email zasobu",
|
"actionSetResourceEmailWhitelist": "Ustaw białą listę email zasobu",
|
||||||
"actionGetResourceEmailWhitelist": "Pobierz białą listę email zasobu",
|
"actionGetResourceEmailWhitelist": "Pobierz białą listę email zasobu",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Pobierz politykę zasobów",
|
"actionGetResourcePolicy": "Pobierz politykę zasobów",
|
||||||
"actionUpdateResourcePolicy": "Zaktualizuj politykę zasobów",
|
"actionUpdateResourcePolicy": "Zaktualizuj politykę zasobów",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Ustaw użytkowników polityki zasobów",
|
"actionSetResourcePolicyUsers": "Ustaw użytkowników polityki zasobów",
|
||||||
"actionSetResourcePolicyRoles": "Ustaw role polityki zasobów",
|
"actionSetResourcePolicyRoles": "Ustaw role polityki zasobów",
|
||||||
"actionSetResourcePolicyPassword": "Ustaw hasło polityki zasobów",
|
"actionSetResourcePolicyPassword": "Ustaw hasło polityki zasobów",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Definir Código PIN do Recurso",
|
"actionSetResourcePincode": "Definir Código PIN do Recurso",
|
||||||
"actionSetResourceEmailWhitelist": "Definir Lista Permitida de Emails do Recurso",
|
"actionSetResourceEmailWhitelist": "Definir Lista Permitida de Emails do Recurso",
|
||||||
"actionGetResourceEmailWhitelist": "Obter 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",
|
"actionGetResourcePolicy": "Obter Política de Recurso",
|
||||||
"actionUpdateResourcePolicy": "Atualizar Política de Recurso",
|
"actionUpdateResourcePolicy": "Atualizar Política de Recurso",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Definir Utilizadores da Política de Recurso",
|
"actionSetResourcePolicyUsers": "Definir Utilizadores da Política de Recurso",
|
||||||
"actionSetResourcePolicyRoles": "Definir Papéis da Política de Recurso",
|
"actionSetResourcePolicyRoles": "Definir Papéis da Política de Recurso",
|
||||||
"actionSetResourcePolicyPassword": "Definir Palavra-passe da Política de Recurso",
|
"actionSetResourcePolicyPassword": "Definir Palavra-passe da Política de Recurso",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Установить ПИН-код ресурса",
|
"actionSetResourcePincode": "Установить ПИН-код ресурса",
|
||||||
"actionSetResourceEmailWhitelist": "Настроить белый список ресурсов email",
|
"actionSetResourceEmailWhitelist": "Настроить белый список ресурсов email",
|
||||||
"actionGetResourceEmailWhitelist": "Получить белый список ресурсов email",
|
"actionGetResourceEmailWhitelist": "Получить белый список ресурсов email",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Получить политику ресурса",
|
"actionGetResourcePolicy": "Получить политику ресурса",
|
||||||
"actionUpdateResourcePolicy": "Обновить политику ресурса",
|
"actionUpdateResourcePolicy": "Обновить политику ресурса",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Установить пользователей политики ресурса",
|
"actionSetResourcePolicyUsers": "Установить пользователей политики ресурса",
|
||||||
"actionSetResourcePolicyRoles": "Установить роли политики ресурса",
|
"actionSetResourcePolicyRoles": "Установить роли политики ресурса",
|
||||||
"actionSetResourcePolicyPassword": "Установить пароль политики ресурса",
|
"actionSetResourcePolicyPassword": "Установить пароль политики ресурса",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "Kaynak PIN Kodunu Ayarla",
|
"actionSetResourcePincode": "Kaynak PIN Kodunu Ayarla",
|
||||||
"actionSetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesi Ayarla",
|
"actionSetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesi Ayarla",
|
||||||
"actionGetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesini Al",
|
"actionGetResourceEmailWhitelist": "Kaynak E-posta Beyaz Listesini Al",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "Kaynak Politikasını Al",
|
"actionGetResourcePolicy": "Kaynak Politikasını Al",
|
||||||
"actionUpdateResourcePolicy": "Kaynak Politikasını Güncelle",
|
"actionUpdateResourcePolicy": "Kaynak Politikasını Güncelle",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "Kaynak Politika Kullanıcılarını Ayarla",
|
"actionSetResourcePolicyUsers": "Kaynak Politika Kullanıcılarını Ayarla",
|
||||||
"actionSetResourcePolicyRoles": "Kaynak Politika Rolleri Ayarla",
|
"actionSetResourcePolicyRoles": "Kaynak Politika Rolleri Ayarla",
|
||||||
"actionSetResourcePolicyPassword": "Kaynak Politika Şifresini Ayarla",
|
"actionSetResourcePolicyPassword": "Kaynak Politika Şifresini Ayarla",
|
||||||
|
|||||||
@@ -1449,8 +1449,11 @@
|
|||||||
"actionSetResourcePincode": "设置资源粉码",
|
"actionSetResourcePincode": "设置资源粉码",
|
||||||
"actionSetResourceEmailWhitelist": "设置资源电子邮件白名单",
|
"actionSetResourceEmailWhitelist": "设置资源电子邮件白名单",
|
||||||
"actionGetResourceEmailWhitelist": "获取资源电子邮件白名单",
|
"actionGetResourceEmailWhitelist": "获取资源电子邮件白名单",
|
||||||
|
"actionListResourcePolicies": "List Resource Policies",
|
||||||
|
"actionCreateResourcePolicy": "Create Resource Policy",
|
||||||
"actionGetResourcePolicy": "获取资源策略",
|
"actionGetResourcePolicy": "获取资源策略",
|
||||||
"actionUpdateResourcePolicy": "更新资源策略",
|
"actionUpdateResourcePolicy": "更新资源策略",
|
||||||
|
"actionDeleteResourcePolicy": "Delete Resource Policy",
|
||||||
"actionSetResourcePolicyUsers": "设置资源策略用户",
|
"actionSetResourcePolicyUsers": "设置资源策略用户",
|
||||||
"actionSetResourcePolicyRoles": "设置资源策略角色",
|
"actionSetResourcePolicyRoles": "设置资源策略角色",
|
||||||
"actionSetResourcePolicyPassword": "设置资源策略密码",
|
"actionSetResourcePolicyPassword": "设置资源策略密码",
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ export enum ActionsEnum {
|
|||||||
createAlertRule = "createAlertRule",
|
createAlertRule = "createAlertRule",
|
||||||
updateAlertRule = "updateAlertRule",
|
updateAlertRule = "updateAlertRule",
|
||||||
deleteAlertRule = "deleteAlertRule",
|
deleteAlertRule = "deleteAlertRule",
|
||||||
testAlertRule = "testAlertRule",
|
|
||||||
listAlertRules = "listAlertRules",
|
listAlertRules = "listAlertRules",
|
||||||
listOrgLabels = "listOrgLabels",
|
listOrgLabels = "listOrgLabels",
|
||||||
createOrgLabel = "createOrgLabel",
|
createOrgLabel = "createOrgLabel",
|
||||||
|
|||||||
@@ -31,24 +31,9 @@ export type AlertNotificationProps = {
|
|||||||
orgId: string;
|
orgId: string;
|
||||||
data: Record<string, unknown>;
|
data: Record<string, unknown>;
|
||||||
dashboardLink: string;
|
dashboardLink: string;
|
||||||
isTestAlert?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getEventMeta(
|
function getEventMeta(eventType: AlertEventType): {
|
||||||
eventType: AlertEventType,
|
|
||||||
isTestAlert: boolean = false
|
|
||||||
): {
|
|
||||||
heading: string;
|
|
||||||
previewText: string;
|
|
||||||
summary: string;
|
|
||||||
statusLabel: string | null;
|
|
||||||
statusColor: string | null;
|
|
||||||
} {
|
|
||||||
const meta = getBaseEventMeta(eventType);
|
|
||||||
return isTestAlert ? { ...meta, heading: `[TEST] ${meta.heading}` } : meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBaseEventMeta(eventType: AlertEventType): {
|
|
||||||
heading: string;
|
heading: string;
|
||||||
previewText: string;
|
previewText: string;
|
||||||
summary: string;
|
summary: string;
|
||||||
@@ -195,14 +180,8 @@ function formatDataItems(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const AlertNotification = (props: AlertNotificationProps) => {
|
export const AlertNotification = (props: AlertNotificationProps) => {
|
||||||
const {
|
const { eventType, orgId, data, dashboardLink } = props;
|
||||||
eventType,
|
const meta = getEventMeta(eventType);
|
||||||
orgId,
|
|
||||||
data,
|
|
||||||
dashboardLink,
|
|
||||||
isTestAlert = false
|
|
||||||
} = props;
|
|
||||||
const meta = getEventMeta(eventType, isTestAlert);
|
|
||||||
const dataItems = formatDataItems(data);
|
const dataItems = formatDataItems(data);
|
||||||
|
|
||||||
const isToggle =
|
const isToggle =
|
||||||
@@ -263,12 +242,6 @@ export const AlertNotification = (props: AlertNotificationProps) => {
|
|||||||
Open your dashboard to view more details and manage
|
Open your dashboard to view more details and manage
|
||||||
your alert rules.
|
your alert rules.
|
||||||
</EmailText>
|
</EmailText>
|
||||||
{isTestAlert && (
|
|
||||||
<EmailText>
|
|
||||||
This is a test alert. No action is required,
|
|
||||||
and no real event has occurred.
|
|
||||||
</EmailText>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<EmailSection>
|
<EmailSection>
|
||||||
<ButtonLink href={dashboardLink}>
|
<ButtonLink href={dashboardLink}>
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
import { db, userOrgRoles, users } from "@server/db";
|
|
||||||
import logger from "@server/logger";
|
|
||||||
import type {
|
|
||||||
EmailAlertAction,
|
|
||||||
TestAlertContext,
|
|
||||||
WebhookAlertConfig
|
|
||||||
} from "@server/routers/alertRule/types";
|
|
||||||
import { eq, inArray } from "drizzle-orm";
|
|
||||||
import { sendAlertEmail } from "./sendAlertEmail";
|
|
||||||
import { decrypt } from "@server/lib/crypto";
|
|
||||||
import config from "@server/lib/config";
|
|
||||||
import { sendAlertWebhook } from "./sendAlertWebhook";
|
|
||||||
|
|
||||||
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,
|
|
||||||
isTest: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(`processTestAlerts: failed to send alert email`, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const webhookActions = context.actions.filter(
|
|
||||||
(action) => action.type === "webhook"
|
|
||||||
);
|
|
||||||
const serverSecret = config.getRawConfig().server.secret!;
|
|
||||||
|
|
||||||
for (const action of webhookActions) {
|
|
||||||
try {
|
|
||||||
let webhookConfig: WebhookAlertConfig = { authType: "none" };
|
|
||||||
|
|
||||||
if (action.config) {
|
|
||||||
try {
|
|
||||||
const decrypted = decrypt(action.config, serverSecret);
|
|
||||||
webhookConfig = JSON.parse(decrypted) as WebhookAlertConfig;
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(
|
|
||||||
`processTestAlerts: failed to decrypt webhook`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await sendAlertWebhook(action.webhookUrl, webhookConfig, context);
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(
|
|
||||||
`processTestAlerts: failed to send alert webhook `,
|
|
||||||
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 emailList: string[] = [];
|
|
||||||
|
|
||||||
emailList.push(...(action.emails ?? []));
|
|
||||||
|
|
||||||
if (action.userIds && action.userIds?.length > 0) {
|
|
||||||
const userList = await db
|
|
||||||
.select({ email: users.email })
|
|
||||||
.from(users)
|
|
||||||
.where(inArray(users.userId, action.userIds));
|
|
||||||
|
|
||||||
emailList.push(
|
|
||||||
...userList.filter((u) => u.email !== null).map((u) => u.email!)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (action.roleIds && action.roleIds?.length > 0) {
|
|
||||||
const userList = await db
|
|
||||||
.select({ email: users.email })
|
|
||||||
.from(userOrgRoles)
|
|
||||||
.innerJoin(users, eq(userOrgRoles.userId, users.userId))
|
|
||||||
.where(inArray(userOrgRoles.roleId, action.roleIds.map(Number)));
|
|
||||||
|
|
||||||
emailList.push(
|
|
||||||
...userList.filter((u) => u.email !== null).map((u) => u.email!)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [...new Set(emailList)];
|
|
||||||
}
|
|
||||||
@@ -15,24 +15,7 @@ import { sendEmail } from "@server/emails";
|
|||||||
import AlertNotification from "@server/emails/templates/AlertNotification";
|
import AlertNotification from "@server/emails/templates/AlertNotification";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import {
|
import { AlertContext } from "@server/routers/alertRule/types";
|
||||||
AlertContext,
|
|
||||||
type AlertEventType
|
|
||||||
} from "@server/routers/alertRule/types";
|
|
||||||
|
|
||||||
type EmailAlertContext = {
|
|
||||||
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<string, unknown>;
|
|
||||||
isTest?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an alert notification email to every address in `recipients`.
|
* Sends an alert notification email to every address in `recipients`.
|
||||||
@@ -44,7 +27,7 @@ type EmailAlertContext = {
|
|||||||
*/
|
*/
|
||||||
export async function sendAlertEmail(
|
export async function sendAlertEmail(
|
||||||
recipients: string[],
|
recipients: string[],
|
||||||
context: EmailAlertContext
|
context: AlertContext
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (recipients.length === 0) {
|
if (recipients.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -63,8 +46,7 @@ export async function sendAlertEmail(
|
|||||||
eventType: context.eventType,
|
eventType: context.eventType,
|
||||||
orgId: context.orgId,
|
orgId: context.orgId,
|
||||||
data: context.data,
|
data: context.data,
|
||||||
dashboardLink,
|
dashboardLink
|
||||||
isTestAlert: context.isTest
|
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
from,
|
from,
|
||||||
@@ -88,35 +70,34 @@ export async function sendAlertEmail(
|
|||||||
// Helpers
|
// Helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function buildSubject(context: EmailAlertContext): string {
|
function buildSubject(context: AlertContext): string {
|
||||||
const prefix = context.isTest ? "[Test Alert]" : "[Alert]";
|
|
||||||
switch (context.eventType) {
|
switch (context.eventType) {
|
||||||
case "site_online":
|
case "site_online":
|
||||||
return `${prefix} Site Back Online`;
|
return "[Alert] Site Back Online";
|
||||||
case "site_offline":
|
case "site_offline":
|
||||||
return `${prefix} Site Offline`;
|
return "[Alert] Site Offline";
|
||||||
case "site_toggle":
|
case "site_toggle":
|
||||||
return `${prefix} Site Status Changed`;
|
return "[Alert] Site Status Changed";
|
||||||
case "health_check_healthy":
|
case "health_check_healthy":
|
||||||
return `${prefix} Health Check Recovered`;
|
return "[Alert] Health Check Recovered";
|
||||||
case "health_check_unhealthy":
|
case "health_check_unhealthy":
|
||||||
return `${prefix} Health Check Failing`;
|
return "[Alert] Health Check Failing";
|
||||||
case "health_check_toggle":
|
case "health_check_toggle":
|
||||||
return `${prefix} Health Check Status Changed`;
|
return "[Alert] Health Check Status Changed";
|
||||||
case "resource_healthy":
|
case "resource_healthy":
|
||||||
return `${prefix} Resource Healthy`;
|
return "[Alert] Resource Healthy";
|
||||||
case "resource_unhealthy":
|
case "resource_unhealthy":
|
||||||
return `${prefix} Resource Unhealthy`;
|
return "[Alert] Resource Unhealthy";
|
||||||
case "resource_degraded":
|
case "resource_degraded":
|
||||||
return `${prefix} Resource Degraded`;
|
return "[Alert] Resource Degraded";
|
||||||
case "resource_toggle":
|
case "resource_toggle":
|
||||||
return `${prefix} Resource Status Changed`;
|
return "[Alert] Resource Status Changed";
|
||||||
default: {
|
default: {
|
||||||
// Exhaustiveness fallback – should never be reached with a
|
// Exhaustiveness fallback – should never be reached with a
|
||||||
// well-typed caller, but keeps runtime behaviour predictable.
|
// well-typed caller, but keeps runtime behaviour predictable.
|
||||||
const _exhaustive: never = context.eventType;
|
const _exhaustive: never = context.eventType;
|
||||||
void _exhaustive;
|
void _exhaustive;
|
||||||
return `${prefix} Event Notification`;
|
return "[Alert] Event Notification";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,3 @@ export * from "./updateAlertRule";
|
|||||||
export * from "./deleteAlertRule";
|
export * from "./deleteAlertRule";
|
||||||
export * from "./listAlertRules";
|
export * from "./listAlertRules";
|
||||||
export * from "./getAlertRule";
|
export * from "./getAlertRule";
|
||||||
export * from "./testAlertRule";
|
|
||||||
|
|||||||
@@ -1,212 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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,
|
|
||||||
type AlertAction,
|
|
||||||
type EmailAlertAction
|
|
||||||
} from "@server/routers/alertRule/types";
|
|
||||||
import { processTestAlerts } from "@server/private/lib/alerts/processTestAlerts";
|
|
||||||
import { getRandomItemInArray } from "@app/lib/getRandomItemInArray";
|
|
||||||
|
|
||||||
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.url(),
|
|
||||||
config: z.string().optional(),
|
|
||||||
enabled: z.boolean().optional().default(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
const bodySchema = z.object({
|
|
||||||
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()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = parsedBody.data;
|
|
||||||
const collectedActions: AlertAction[] = [];
|
|
||||||
if (
|
|
||||||
body.emails.length > 0 ||
|
|
||||||
body.roleIds.length > 0 ||
|
|
||||||
body.userIds.length > 0
|
|
||||||
) {
|
|
||||||
collectedActions.push({
|
|
||||||
type: "email",
|
|
||||||
emails: body.emails,
|
|
||||||
roleIds: body.roleIds,
|
|
||||||
userIds: body.userIds
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const action of body.webhookActions) {
|
|
||||||
collectedActions.push({
|
|
||||||
type: "webhook",
|
|
||||||
...action
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let data: Record<string, any> = {};
|
|
||||||
switch (body.eventType) {
|
|
||||||
case "site_toggle":
|
|
||||||
data = {
|
|
||||||
status: getRandomItemInArray(["online", "offline"]),
|
|
||||||
siteName: "Test Site Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "site_offline":
|
|
||||||
data = {
|
|
||||||
status: "offline",
|
|
||||||
siteName: "Test Site Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "site_online":
|
|
||||||
data = {
|
|
||||||
status: "online",
|
|
||||||
siteName: "Test Site Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "resource_toggle":
|
|
||||||
data = {
|
|
||||||
status: getRandomItemInArray([
|
|
||||||
"healthy",
|
|
||||||
"unhealthy",
|
|
||||||
"degraded"
|
|
||||||
]),
|
|
||||||
siteName: "Test Resource Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "resource_healthy":
|
|
||||||
data = {
|
|
||||||
status: "healthy",
|
|
||||||
siteName: "Test Resource Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "resource_unhealthy":
|
|
||||||
data = {
|
|
||||||
status: "unhealthy",
|
|
||||||
siteName: "Test Resource Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "resource_degraded":
|
|
||||||
data = {
|
|
||||||
status: "degraded",
|
|
||||||
siteName: "Test Resource Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "health_check_toggle":
|
|
||||||
data = {
|
|
||||||
status: getRandomItemInArray(["healthy", "unhealthy"]),
|
|
||||||
healthCheckName: "Test Health Check Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "health_check_healthy":
|
|
||||||
data = {
|
|
||||||
status: "healthy",
|
|
||||||
healthCheckName: "Test Health Check Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "health_check_unhealthy":
|
|
||||||
data = {
|
|
||||||
status: "unhealthy",
|
|
||||||
healthCheckName: "Test Health Check Alert"
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: process alert rule
|
|
||||||
await processTestAlerts({
|
|
||||||
eventType: body.eventType,
|
|
||||||
orgId,
|
|
||||||
actions: collectedActions,
|
|
||||||
data
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(error);
|
|
||||||
return next(
|
|
||||||
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -808,14 +808,6 @@ authenticated.get(
|
|||||||
alertRule.listAlertRules
|
alertRule.listAlertRules
|
||||||
);
|
);
|
||||||
|
|
||||||
authenticated.post(
|
|
||||||
"/org/:orgId/test-alert-rule",
|
|
||||||
verifyValidLicense,
|
|
||||||
verifyOrgAccess,
|
|
||||||
verifyUserHasAction(ActionsEnum.testAlertRule),
|
|
||||||
alertRule.testAlertRule
|
|
||||||
);
|
|
||||||
|
|
||||||
authenticated.get(
|
authenticated.get(
|
||||||
"/org/:orgId/alert-rule/:alertRuleId",
|
"/org/:orgId/alert-rule/:alertRuleId",
|
||||||
verifyValidLicense,
|
verifyValidLicense,
|
||||||
|
|||||||
@@ -124,26 +124,3 @@ export interface AlertContext {
|
|||||||
/** Human-readable context data included in emails and webhook payloads */
|
/** Human-readable context data included in emails and webhook payloads */
|
||||||
data: Record<string, unknown>;
|
data: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EmailAlertAction = {
|
|
||||||
type: "email";
|
|
||||||
userIds?: string[];
|
|
||||||
roleIds?: number[];
|
|
||||||
emails?: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type WebhookAlertAction = {
|
|
||||||
type: "webhook";
|
|
||||||
webhookUrl: string;
|
|
||||||
enabled: boolean;
|
|
||||||
config?: string | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export 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,
|
verifyApiKeyResourcePolicyAccess,
|
||||||
verifyApiKeyRoleAccess,
|
verifyApiKeyRoleAccess,
|
||||||
verifyLimits,
|
verifyLimits,
|
||||||
verifyUserHasAction(ActionsEnum.setResourcePolicyUsers),
|
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyUsers),
|
||||||
verifyUserHasAction(ActionsEnum.setResourcePolicyRoles),
|
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyRoles),
|
||||||
logActionAudit(ActionsEnum.setResourcePolicyUsers),
|
logActionAudit(ActionsEnum.setResourcePolicyUsers),
|
||||||
logActionAudit(ActionsEnum.setResourcePolicyRoles),
|
logActionAudit(ActionsEnum.setResourcePolicyRoles),
|
||||||
policy.setResourcePolicyAccessControl
|
policy.setResourcePolicyAccessControl
|
||||||
@@ -742,8 +742,8 @@ authenticated.put(
|
|||||||
verifyApiKeyResourcePolicyAccess,
|
verifyApiKeyResourcePolicyAccess,
|
||||||
verifyApiKeyRoleAccess,
|
verifyApiKeyRoleAccess,
|
||||||
verifyLimits,
|
verifyLimits,
|
||||||
verifyUserHasAction(ActionsEnum.setResourcePolicyUsers),
|
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyUsers),
|
||||||
verifyUserHasAction(ActionsEnum.setResourcePolicyRoles),
|
verifyApiKeyHasAction(ActionsEnum.setResourcePolicyRoles),
|
||||||
logActionAudit(ActionsEnum.setResourcePolicyUsers),
|
logActionAudit(ActionsEnum.setResourcePolicyUsers),
|
||||||
logActionAudit(ActionsEnum.setResourcePolicyRoles),
|
logActionAudit(ActionsEnum.setResourcePolicyRoles),
|
||||||
policy.setResourcePolicyAccessControl
|
policy.setResourcePolicyAccessControl
|
||||||
|
|||||||
@@ -115,8 +115,11 @@ function getActionsCategories(root: boolean) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"Resource Policy": {
|
"Resource Policy": {
|
||||||
|
[t("actionListResourcePolicies")]: "listResourcePolicies",
|
||||||
|
[t("actionCreateResourcePolicy")]: "createResourcePolicy",
|
||||||
[t("actionGetResourcePolicy")]: "getResourcePolicy",
|
[t("actionGetResourcePolicy")]: "getResourcePolicy",
|
||||||
[t("actionUpdateResourcePolicy")]: "updateResourcePolicy",
|
[t("actionUpdateResourcePolicy")]: "updateResourcePolicy",
|
||||||
|
[t("actionDeleteResourcePolicy")]: "deleteResourcePolicy",
|
||||||
[t("actionSetResourcePolicyUsers")]: "setResourcePolicyUsers",
|
[t("actionSetResourcePolicyUsers")]: "setResourcePolicyUsers",
|
||||||
[t("actionSetResourcePolicyRoles")]: "setResourcePolicyRoles",
|
[t("actionSetResourcePolicyRoles")]: "setResourcePolicyRoles",
|
||||||
[t("actionSetResourcePolicyPassword")]: "setResourcePolicyPassword",
|
[t("actionSetResourcePolicyPassword")]: "setResourcePolicyPassword",
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import {
|
|||||||
AlertRuleSourceFields,
|
AlertRuleSourceFields,
|
||||||
AlertRuleTriggerFields
|
AlertRuleTriggerFields
|
||||||
} from "@app/components/alert-rule-editor/AlertRuleFields";
|
} from "@app/components/alert-rule-editor/AlertRuleFields";
|
||||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
|
||||||
import { SettingsContainer } from "@app/components/Settings";
|
import { SettingsContainer } from "@app/components/Settings";
|
||||||
import { SwitchInput } from "@app/components/SwitchInput";
|
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import { Card, CardContent } from "@app/components/ui/card";
|
import { Card, CardContent } from "@app/components/ui/card";
|
||||||
import {
|
import {
|
||||||
@@ -21,7 +19,6 @@ import {
|
|||||||
FormMessage
|
FormMessage
|
||||||
} from "@app/components/ui/form";
|
} from "@app/components/ui/form";
|
||||||
import { Input } from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import {
|
import {
|
||||||
buildFormSchema,
|
buildFormSchema,
|
||||||
@@ -30,15 +27,19 @@ import {
|
|||||||
type AlertRuleFormValues
|
type AlertRuleFormValues
|
||||||
} from "@app/lib/alertRuleForm";
|
} from "@app/lib/alertRuleForm";
|
||||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
|
||||||
import type { CreateAlertRuleResponse } from "@server/routers/alertRule/types";
|
import type { CreateAlertRuleResponse } from "@server/routers/alertRule/types";
|
||||||
import type { AxiosResponse } from "axios";
|
import type { AxiosResponse } from "axios";
|
||||||
import { Cog, Flag, Zap, ZapIcon } from "lucide-react";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useTranslations } from "next-intl";
|
import { ChevronLeft, Cog, Flag, Zap } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useActionState, useMemo, useTransition, type ReactNode } from "react";
|
import { useMemo, useState, type ReactNode } from "react";
|
||||||
import { useFieldArray, useForm, type Resolver } from "react-hook-form";
|
import { useFieldArray, useForm, type Resolver } from "react-hook-form";
|
||||||
|
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 { Badge } from "../ui/badge";
|
import { Badge } from "../ui/badge";
|
||||||
|
|
||||||
const FORM_ID = "alert-rule-form";
|
const FORM_ID = "alert-rule-form";
|
||||||
@@ -114,6 +115,7 @@ export default function AlertRuleGraphEditor({
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const api = createApiClient(useEnvContext());
|
const api = createApiClient(useEnvContext());
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const schema = useMemo(() => buildFormSchema(t), [t]);
|
const schema = useMemo(() => buildFormSchema(t), [t]);
|
||||||
const form = useForm<AlertRuleFormValues>({
|
const form = useForm<AlertRuleFormValues>({
|
||||||
resolver: zodResolver(schema) as Resolver<AlertRuleFormValues>,
|
resolver: zodResolver(schema) as Resolver<AlertRuleFormValues>,
|
||||||
@@ -125,22 +127,8 @@ export default function AlertRuleGraphEditor({
|
|||||||
name: "actions"
|
name: "actions"
|
||||||
});
|
});
|
||||||
|
|
||||||
const saveAlert = async () => {
|
const onSubmit = form.handleSubmit(async (values) => {
|
||||||
const isValid = await form.trigger();
|
setIsSaving(true);
|
||||||
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 {
|
try {
|
||||||
const payload = formValuesToApiPayload(values);
|
const payload = formValuesToApiPayload(values);
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
@@ -170,65 +158,14 @@ export default function AlertRuleGraphEditor({
|
|||||||
description: formatAxiosError(e),
|
description: formatAxiosError(e),
|
||||||
variant: "destructive"
|
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();
|
|
||||||
try {
|
|
||||||
const payload = formValuesToApiPayload(values);
|
|
||||||
if (isNew) {
|
|
||||||
const res = await api.post<
|
|
||||||
AxiosResponse<CreateAlertRuleResponse>
|
|
||||||
>(`/org/${orgId}/test-alert-rule`, payload);
|
|
||||||
toast({
|
|
||||||
title: t("alertingTestAlertSent"),
|
|
||||||
description: t("alertingTestAlertSentDescription")
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await api.post(
|
|
||||||
`/org/${orgId}/alert-rule/${alertRuleId}`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
toast({
|
|
||||||
title: t("alertingTestAlertSent"),
|
|
||||||
description: t("alertingTestAlertSentDescription")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast({
|
|
||||||
title: t("error"),
|
|
||||||
description: formatAxiosError(e),
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const [, formAction, isSaving] = useActionState(saveAlert, null);
|
|
||||||
const [isTestingAlert, startTransition] = useTransition();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form id={FORM_ID} action={formAction}>
|
<form id={FORM_ID} onSubmit={onSubmit}>
|
||||||
<SettingsContainer>
|
<SettingsContainer>
|
||||||
<PaidFeaturesAlert tiers={tierMatrix.alertingRules} />
|
<PaidFeaturesAlert tiers={tierMatrix.alertingRules} />
|
||||||
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8 items-start">
|
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8 items-start">
|
||||||
@@ -326,29 +263,14 @@ export default function AlertRuleGraphEditor({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col items-center w-full gap-3">
|
<Button
|
||||||
<Button
|
type="submit"
|
||||||
type="submit"
|
className="w-full"
|
||||||
className="w-full"
|
disabled={isSaving}
|
||||||
disabled={isSaving}
|
loading={isSaving}
|
||||||
loading={isSaving}
|
>
|
||||||
>
|
{t("save")}
|
||||||
{t("save")}
|
</Button>
|
||||||
</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>
|
</fieldset>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export function getRandomItemInArray<T>(array: T[]) {
|
|
||||||
// Source - https://stackoverflow.com/a/4550514
|
|
||||||
const randomElement = array[Math.floor(Math.random() * array.length)];
|
|
||||||
return randomElement;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user