Openstatus www.openstatus.dev
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 210 lines 4.5 kB view raw
1import { slackDataSchema } from "@openstatus/db/src/schema"; 2import { 3 COLORS, 4 type NotificationContext, 5 buildCommonMessageData, 6} from "@openstatus/notification-base"; 7import { 8 buildAlertBlocks, 9 buildDegradedBlocks, 10 buildRecoveryBlocks, 11} from "./blocks"; 12 13// biome-ignore lint/suspicious/noExplicitAny: <explanation> 14const postToWebhook = async (body: any, webhookUrl: string) => { 15 if (!webhookUrl || webhookUrl.trim() === "") { 16 throw new Error("Slack webhook URL is required"); 17 } 18 19 const res = await fetch(webhookUrl, { 20 method: "POST", 21 body: JSON.stringify(body), 22 }); 23 if (!res.ok) { 24 throw new Error(`Failed to send Slack notification: ${res.statusText}`); 25 } 26}; 27 28export const sendAlert = async ({ 29 monitor, 30 notification, 31 statusCode, 32 message, 33 cronTimestamp, 34 latency, 35 regions, 36}: NotificationContext) => { 37 const notificationData = slackDataSchema.parse(JSON.parse(notification.data)); 38 const { slack: webhookUrl } = notificationData; 39 40 const context = { 41 monitor, 42 notification, 43 statusCode, 44 message, 45 cronTimestamp, 46 latency, 47 regions, 48 }; 49 50 const data = buildCommonMessageData(context); 51 const blocks = buildAlertBlocks(data); 52 53 await postToWebhook( 54 { 55 attachments: [ 56 { 57 color: COLORS.red, 58 blocks, 59 }, 60 ], 61 }, 62 webhookUrl, 63 ); 64}; 65 66export const sendRecovery = async ({ 67 monitor, 68 notification, 69 statusCode, 70 message, 71 incident, 72 cronTimestamp, 73 regions, 74 latency, 75}: NotificationContext) => { 76 const notificationData = slackDataSchema.parse(JSON.parse(notification.data)); 77 const { slack: webhookUrl } = notificationData; 78 79 const context = { 80 monitor, 81 notification, 82 statusCode, 83 message, 84 cronTimestamp, 85 latency, 86 regions, 87 }; 88 89 const data = buildCommonMessageData(context, { incident }); 90 const blocks = buildRecoveryBlocks(data); 91 92 await postToWebhook( 93 { 94 attachments: [ 95 { 96 color: COLORS.green, 97 blocks, 98 }, 99 ], 100 }, 101 webhookUrl, 102 ); 103}; 104 105export const sendDegraded = async ({ 106 monitor, 107 notification, 108 statusCode, 109 message, 110 incident, 111 cronTimestamp, 112 regions, 113 latency, 114}: NotificationContext) => { 115 const notificationData = slackDataSchema.parse(JSON.parse(notification.data)); 116 const { slack: webhookUrl } = notificationData; 117 118 const context = { 119 monitor, 120 notification, 121 statusCode, 122 message, 123 cronTimestamp, 124 latency, 125 regions, 126 }; 127 128 const data = buildCommonMessageData(context, { incident }); 129 const blocks = buildDegradedBlocks(data); 130 131 await postToWebhook( 132 { 133 attachments: [ 134 { 135 color: COLORS.yellow, 136 blocks, 137 }, 138 ], 139 }, 140 webhookUrl, 141 ); 142}; 143 144export const sendTestSlackMessage = async (webhookUrl: string) => { 145 await postToWebhook( 146 { 147 attachments: [ 148 { 149 color: COLORS.green, 150 blocks: [ 151 { 152 type: "header", 153 text: { 154 type: "plain_text", 155 text: "Test Notification", 156 emoji: false, 157 }, 158 }, 159 { 160 type: "section", 161 text: { 162 type: "mrkdwn", 163 text: "`🧪 Your Slack webhook is configured correctly!`", 164 }, 165 }, 166 { 167 type: "divider", 168 }, 169 { 170 type: "section", 171 fields: [ 172 { 173 type: "mrkdwn", 174 text: "*Status*\nWebhook Connected", 175 }, 176 { 177 type: "mrkdwn", 178 text: "*Type*\nTest Notification", 179 }, 180 ], 181 }, 182 { 183 type: "section", 184 text: { 185 type: "mrkdwn", 186 text: "*Next Steps*\nYou will receive notifications here when your monitors trigger fail, recover, or degrades.", 187 }, 188 }, 189 { 190 type: "actions", 191 elements: [ 192 { 193 type: "button", 194 text: { 195 type: "plain_text", 196 text: "View Dashboard", 197 emoji: true, 198 }, 199 url: "https://app.openstatus.dev", 200 action_id: "view_dashboard", 201 }, 202 ], 203 }, 204 ], 205 }, 206 ], 207 }, 208 webhookUrl, 209 ); 210};