Openstatus www.openstatus.dev

refactor: notifications schema (#1624)

* feat: telegram bot notifications

* chore: dofigen

* chore: docs

* wip:

* chore: content

* chore: changelog

* chore: dofigen

* test: notification packages

* refactor: notifications schema

* fix: sms

authored by

Maximilian Kaske and committed by
GitHub
17b5eb9e 5de1e45a

+51 -60
+1 -1
apps/workflows/.dockerignore
··· 1 - # This file is generated by Dofigen v2.5.1 1 + # This file is generated by Dofigen v2.5.0 2 2 # See https://github.com/lenra-io/dofigen 3 3 4 4 node_modules
+2 -2
apps/workflows/Dockerfile
··· 1 1 # syntax=docker/dockerfile:1.11 2 - # This file is generated by Dofigen v2.5.1 2 + # This file is generated by Dofigen v2.5.0 3 3 # See https://github.com/lenra-io/dofigen 4 4 5 5 # ca-certs ··· 84 84 # runtime 85 85 FROM debian@sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b AS runtime 86 86 LABEL \ 87 - io.dofigen.version="2.5.1" \ 87 + io.dofigen.version="2.5.0" \ 88 88 org.opencontainers.image.authors="OpenStatus Team" \ 89 89 org.opencontainers.image.base.digest="sha256:530a3348fc4b5734ffe1a137ddbcee6850154285251b53c3425c386ea8fac77b" \ 90 90 org.opencontainers.image.base.name="docker.io/debian:bullseye-slim" \
+18 -5
packages/notifications/discord/src/index.ts
··· 1 1 import type { Monitor, Notification } from "@openstatus/db/src/schema"; 2 + import { discordDataSchema } from "@openstatus/db/src/schema"; 2 3 import type { Region } from "@openstatus/db/src/schema/constants"; 3 - import { DataSchema } from "./schema"; 4 + 4 5 const postToWebhook = async (content: string, webhookUrl: string) => { 5 6 await fetch(webhookUrl, { 6 7 method: "POST", ··· 32 33 latency?: number; 33 34 region?: Region; 34 35 }) => { 35 - const notificationData = DataSchema.parse(JSON.parse(notification.data)); 36 + const notificationData = discordDataSchema.parse( 37 + JSON.parse(notification.data), 38 + ); 36 39 const { discord: webhookUrl } = notificationData; // webhook url 37 40 const { name } = monitor; 38 41 39 42 try { 40 43 await postToWebhook( 41 - `**🚨 Alert [${name}](<${monitor.url}>)**\nStatus Code: ${statusCode || "_empty_"}\nMessage: ${message || "_empty_"}\nCron Timestamp: ${cronTimestamp} (${new Date(cronTimestamp).toISOString()})\n> Check your [Dashboard](<https://www.openstatus.dev/app/>).\n`, 44 + `**🚨 Alert [${name}](<${monitor.url}>)**\nStatus Code: ${ 45 + statusCode || "_empty_" 46 + }\nMessage: ${ 47 + message || "_empty_" 48 + }\nCron Timestamp: ${cronTimestamp} (${new Date( 49 + cronTimestamp, 50 + ).toISOString()})\n> Check your [Dashboard](<https://www.openstatus.dev/app/>).\n`, 42 51 webhookUrl, 43 52 ); 44 53 } catch (err) { ··· 66 75 latency?: number; 67 76 region?: Region; 68 77 }) => { 69 - const notificationData = DataSchema.parse(JSON.parse(notification.data)); 78 + const notificationData = discordDataSchema.parse( 79 + JSON.parse(notification.data), 80 + ); 70 81 const { discord: webhookUrl } = notificationData; // webhook url 71 82 const { name } = monitor; 72 83 ··· 100 111 latency?: number; 101 112 region?: Region; 102 113 }) => { 103 - const notificationData = DataSchema.parse(JSON.parse(notification.data)); 114 + const notificationData = discordDataSchema.parse( 115 + JSON.parse(notification.data), 116 + ); 104 117 const { discord: webhookUrl } = notificationData; // webhook url 105 118 const { name } = monitor; 106 119
-5
packages/notifications/discord/src/schema.ts
··· 1 - import { z } from "zod"; 2 - 3 - export const DataSchema = z.object({ 4 - discord: z.string(), 5 - });
+4 -4
packages/notifications/ntfy/src/index.ts
··· 1 1 import type { Monitor, Notification } from "@openstatus/db/src/schema"; 2 2 3 + import { ntfyDataSchema } from "@openstatus/db/src/schema"; 3 4 import type { Region } from "@openstatus/db/src/schema/constants"; 4 - import { NtfySchema } from "./schema"; 5 5 6 6 export const sendAlert = async ({ 7 7 monitor, ··· 20 20 latency?: number; 21 21 region?: Region; 22 22 }) => { 23 - const notificationData = NtfySchema.parse(JSON.parse(notification.data)); 23 + const notificationData = ntfyDataSchema.parse(JSON.parse(notification.data)); 24 24 const { name } = monitor; 25 25 26 26 const body = `Your monitor ${name} / ${monitor.url} is down with ${ ··· 68 68 latency?: number; 69 69 region?: Region; 70 70 }) => { 71 - const notificationData = NtfySchema.parse(JSON.parse(notification.data)); 71 + const notificationData = ntfyDataSchema.parse(JSON.parse(notification.data)); 72 72 const { name } = monitor; 73 73 74 74 const body = `Your monitor ${name} / ${monitor.url} is up again`; ··· 110 110 latency?: number; 111 111 region?: Region; 112 112 }) => { 113 - const notificationData = NtfySchema.parse(JSON.parse(notification.data)); 113 + const notificationData = ntfyDataSchema.parse(JSON.parse(notification.data)); 114 114 const { name } = monitor; 115 115 116 116 const body = `Your monitor ${name} / ${monitor.url} is degraded `;
+2 -6
packages/notifications/opsgenie/src/schema.ts
··· 1 + import { opsgenieDataSchema } from "@openstatus/db/src/schema"; 1 2 import { z } from "zod"; 2 3 3 - export const OpsGenieSchema = z.object({ 4 - opsgenie: z.object({ 5 - apiKey: z.string(), 6 - region: z.enum(["eu", "us"]), 7 - }), 8 - }); 4 + export const OpsGenieSchema = opsgenieDataSchema; 9 5 10 6 export const OpsGeniePayloadAlert = z.object({ 11 7 message: z.string(),
+4 -4
packages/notifications/slack/src/index.ts
··· 1 1 import type { Monitor, Notification } from "@openstatus/db/src/schema"; 2 + import { slackDataSchema } from "@openstatus/db/src/schema"; 2 3 import type { Region } from "@openstatus/db/src/schema/constants"; 3 - import { DataSchema } from "./schema"; 4 4 5 5 // biome-ignore lint/suspicious/noExplicitAny: <explanation> 6 6 const postToWebhook = async (body: any, webhookUrl: string) => { ··· 33 33 latency?: number; 34 34 region?: Region; 35 35 }) => { 36 - const notificationData = DataSchema.parse(JSON.parse(notification.data)); 36 + const notificationData = slackDataSchema.parse(JSON.parse(notification.data)); 37 37 const { slack: webhookUrl } = notificationData; // webhook url 38 38 const { name } = monitor; 39 39 ··· 94 94 region?: Region; 95 95 latency?: number; 96 96 }) => { 97 - const notificationData = DataSchema.parse(JSON.parse(notification.data)); 97 + const notificationData = slackDataSchema.parse(JSON.parse(notification.data)); 98 98 const { slack: webhookUrl } = notificationData; // webhook url 99 99 const { name } = monitor; 100 100 ··· 147 147 region?: Region; 148 148 latency?: number; 149 149 }) => { 150 - const notificationData = DataSchema.parse(JSON.parse(notification.data)); 150 + const notificationData = slackDataSchema.parse(JSON.parse(notification.data)); 151 151 const { slack: webhookUrl } = notificationData; // webhook url 152 152 const { name } = monitor; 153 153
+4
packages/notifications/slack/src/mock.ts
··· 27 27 status: "active", 28 28 method: "GET", 29 29 deletedAt: null, 30 + otelEndpoint: null, 31 + otelHeaders: [], 32 + retry: null, 33 + followRedirects: null, 30 34 }; 31 35 32 36 const notification: Notification = {
-5
packages/notifications/slack/src/schema.ts
··· 1 - import { z } from "zod"; 2 - 3 - export const DataSchema = z.object({ 4 - slack: z.string(), 5 - });
+10 -4
packages/notifications/telegram/src/index.ts
··· 1 1 import type { Monitor, Notification } from "@openstatus/db/src/schema"; 2 + import { telegramDataSchema } from "@openstatus/db/src/schema"; 2 3 3 4 import type { Region } from "@openstatus/db/src/schema/constants"; 4 - import { TelegramSchema } from "./schema"; 5 5 6 6 export const sendAlert = async ({ 7 7 monitor, ··· 20 20 latency?: number; 21 21 region?: Region; 22 22 }) => { 23 - const notificationData = TelegramSchema.parse(JSON.parse(notification.data)); 23 + const notificationData = telegramDataSchema.parse( 24 + JSON.parse(notification.data), 25 + ); 24 26 const { name } = monitor; 25 27 26 28 const body = `Your monitor ${name} / ${monitor.url} is down with ${ ··· 57 59 latency?: number; 58 60 region?: Region; 59 61 }) => { 60 - const notificationData = TelegramSchema.parse(JSON.parse(notification.data)); 62 + const notificationData = telegramDataSchema.parse( 63 + JSON.parse(notification.data), 64 + ); 61 65 const { name } = monitor; 62 66 63 67 const body = `Your monitor ${name} / ${monitor.url} is up again`; ··· 89 93 latency?: number; 90 94 region?: Region; 91 95 }) => { 92 - const notificationData = TelegramSchema.parse(JSON.parse(notification.data)); 96 + const notificationData = telegramDataSchema.parse( 97 + JSON.parse(notification.data), 98 + ); 93 99 const { name } = monitor; 94 100 95 101 const body = `Your monitor ${name} / ${monitor.url} is degraded `;
+4 -10
packages/notifications/twillio-sms/src/index.ts
··· 1 1 import type { Monitor, Notification } from "@openstatus/db/src/schema"; 2 2 3 + import { phoneDataSchema } from "@openstatus/db/src/schema"; 3 4 import type { Region } from "@openstatus/db/src/schema/constants"; 4 5 import { env } from "./env"; 5 - import { SmsConfigurationSchema } from "./schema/config"; 6 6 7 7 export const sendAlert = async ({ 8 8 monitor, ··· 21 21 latency?: number; 22 22 region?: Region; 23 23 }) => { 24 - const notificationData = SmsConfigurationSchema.parse( 25 - JSON.parse(notification.data), 26 - ); 24 + const notificationData = phoneDataSchema.parse(JSON.parse(notification.data)); 27 25 const { name } = monitor; 28 26 29 27 const body = new FormData(); ··· 74 72 latency?: number; 75 73 region?: Region; 76 74 }) => { 77 - const notificationData = SmsConfigurationSchema.parse( 78 - JSON.parse(notification.data), 79 - ); 75 + const notificationData = phoneDataSchema.parse(JSON.parse(notification.data)); 80 76 const { name } = monitor; 81 77 82 78 const body = new FormData(); ··· 120 116 latency?: number; 121 117 region?: Region; 122 118 }) => { 123 - const notificationData = SmsConfigurationSchema.parse( 124 - JSON.parse(notification.data), 125 - ); 119 + const notificationData = phoneDataSchema.parse(JSON.parse(notification.data)); 126 120 const { name } = monitor; 127 121 128 122 const body = new FormData();
-6
packages/notifications/twillio-sms/src/schema/config.ts
··· 1 - import isMobilephone from "validator/lib/isMobilePhone"; 2 - import { z } from "zod"; 3 - 4 - export const SmsConfigurationSchema = z.object({ 5 - sms: z.string().refine(isMobilephone), 6 - });
+2 -8
packages/notifications/webhook/src/schema.ts
··· 1 + import { webhookDataSchema } from "@openstatus/db/src/schema"; 1 2 import { z } from "zod"; 2 3 3 - export const WebhookSchema = z.object({ 4 - webhook: z.object({ 5 - endpoint: z.string().url(), 6 - headers: z 7 - .array(z.object({ key: z.string(), value: z.string() })) 8 - .optional(), 9 - }), 10 - }); 4 + export const WebhookSchema = webhookDataSchema; 11 5 12 6 export const PayloadSchema = z.object({ 13 7 monitor: z.object({