feat: add support for email alert channel (#4599)

This commit is contained in:
Srikanth Chekuri 2024-03-01 15:05:28 +05:30 committed by GitHub
parent 1a62a13aea
commit d77389abe3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 754 additions and 28 deletions

View File

@ -133,7 +133,7 @@ services:
# - ./data/clickhouse-3/:/var/lib/clickhouse/
alertmanager:
image: signoz/alertmanager:0.23.4
image: signoz/alertmanager:0.23.5
volumes:
- ./data/alertmanager:/data
command:

View File

@ -54,7 +54,7 @@ services:
alertmanager:
container_name: signoz-alertmanager
image: signoz/alertmanager:0.23.4
image: signoz/alertmanager:0.23.5
volumes:
- ./data/alertmanager:/data
depends_on:

View File

@ -149,7 +149,7 @@ services:
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
alertmanager:
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.4}
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5}
container_name: signoz-alertmanager
volumes:
- ./data/alertmanager:/data

View File

@ -90,6 +90,13 @@ var BasicPlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.AlertChannelEmail,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.AlertChannelMsTeams,
Active: false,
@ -177,6 +184,13 @@ var ProPlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.AlertChannelEmail,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.AlertChannelMsTeams,
Active: true,
@ -264,6 +278,13 @@ var EnterprisePlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.AlertChannelEmail,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.AlertChannelMsTeams,
Active: true,
@ -279,17 +300,17 @@ var EnterprisePlan = basemodel.FeatureSet{
Route: "",
},
basemodel.Feature{
Name: Onboarding,
Active: true,
Usage: 0,
Name: Onboarding,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
Route: "",
},
basemodel.Feature{
Name: ChatSupport,
Active: true,
Usage: 0,
Name: ChatSupport,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
Route: "",
},
}

View File

@ -23,6 +23,12 @@
"field_opsgenie_api_key": "API Key",
"field_opsgenie_description": "Description",
"placeholder_opsgenie_description": "Description",
"help_email_to": "Email address(es) to send alerts to (comma separated)",
"field_email_to": "To",
"placeholder_email_to": "To",
"help_email_html": "Send email in html format",
"field_email_html": "Email body template",
"placeholder_email_html": "Email body template",
"field_webhook_username": "User Name (optional)",
"field_webhook_password": "Password (optional)",
"field_pager_routing_key": "Routing Key",

View File

@ -0,0 +1,34 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createEmail';
const create = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post('/channels', {
name: props.name,
email_configs: [
{
send_resolved: true,
to: props.to,
html: props.html,
headers: props.headers,
},
],
});
return {
statusCode: 200,
error: null,
message: 'Success',
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default create;

View File

@ -0,0 +1,34 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/editEmail';
const editEmail = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.put(`/channels/${props.id}`, {
name: props.name,
email_configs: [
{
send_resolved: true,
to: props.to,
html: props.html,
headers: props.headers,
},
],
});
return {
statusCode: 200,
error: null,
message: 'Success',
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default editEmail;

View File

@ -0,0 +1,34 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/channels/createEmail';
const testEmail = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post('/testChannel', {
name: props.name,
email_configs: [
{
send_resolved: true,
to: props.to,
html: props.html,
headers: props.headers,
},
],
});
return {
statusCode: 200,
error: null,
message: 'Success',
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default testEmail;

View File

@ -64,6 +64,16 @@ export interface OpsgenieChannel extends Channel {
priority?: string;
}
export interface EmailChannel extends Channel {
// comma separated list of email addresses to send alerts to
to: string;
// HTML body of the email notification.
html: string;
// Further headers email header key/value pairs.
// [ headers: { <string>: <tmpl_string>, ... } ]
headers: Record<string, string>;
}
export const ValidatePagerChannel = (p: PagerChannel): string => {
if (!p) {
return 'Received unexpected input for this channel, please contact your administrator ';

View File

@ -1,4 +1,4 @@
import { OpsgenieChannel, PagerChannel } from './config';
import { EmailChannel, OpsgenieChannel, PagerChannel } from './config';
export const PagerInitialConfig: Partial<PagerChannel> = {
description: `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
@ -50,3 +50,399 @@ export const OpsgenieInitialConfig: Partial<OpsgenieChannel> = {
priority:
'{{ if eq (index .Alerts 0).Labels.severity "critical" }}P1{{ else if eq (index .Alerts 0).Labels.severity "warning" }}P2{{ else if eq (index .Alerts 0).Labels.severity "info" }}P3{{ else }}P4{{ end }}',
};
export const EmailInitialConfig: Partial<EmailChannel> = {
send_resolved: true,
html: `<!--
Credits: https://github.com/mailgun/transactional-email-templates
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{{ template "__subject" . }}</title>
<style>
/* -------------------------------------
GLOBAL
A very basic CSS reset
------------------------------------- */
* {
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
box-sizing: border-box;
font-size: 14px;
}
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
width: 100% !important;
height: 100%;
line-height: 1.6em;
/* 1.6em * 14px = 22.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 22px;*/
}
/* Let's make sure all tables have defaults */
table td {
vertical-align: top;
}
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
body {
background-color: #f6f6f6;
}
.body-wrap {
background-color: #f6f6f6;
width: 100%;
}
.container {
display: block !important;
max-width: 600px !important;
margin: 0 auto !important;
/* makes it centered */
clear: both !important;
}
.content {
max-width: 600px;
margin: 0 auto;
display: block;
padding: 20px;
}
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background-color: #fff;
border: 1px solid #e9e9e9;
border-radius: 3px;
}
.content-wrap {
padding: 30px;
}
.content-block {
padding: 0 0 20px;
}
.header {
width: 100%;
margin-bottom: 20px;
}
.footer {
width: 100%;
clear: both;
color: #999;
padding: 20px;
}
.footer p,
.footer a,
.footer td {
color: #999;
font-size: 12px;
}
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3 {
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
color: #000;
margin: 40px 0 0;
line-height: 1.2em;
font-weight: 400;
}
h1 {
font-size: 32px;
font-weight: 500;
/* 1.2em * 32px = 38.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 38px;*/
}
h2 {
font-size: 24px;
/* 1.2em * 24px = 28.8px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 29px;*/
}
h3 {
font-size: 18px;
/* 1.2em * 18px = 21.6px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 22px;*/
}
h4 {
font-size: 14px;
font-weight: 600;
}
p,
ul,
ol {
margin-bottom: 10px;
font-weight: normal;
}
p li,
ul li,
ol li {
margin-left: 5px;
list-style-position: inside;
}
/* -------------------------------------
LINKS & BUTTONS
------------------------------------- */
a {
color: #348eda;
text-decoration: underline;
}
.btn-primary {
text-decoration: none;
color: #FFF;
background-color: #348eda;
border: solid #348eda;
border-width: 10px 20px;
line-height: 2em;
/* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
/*line-height: 28px;*/
font-weight: bold;
text-align: center;
cursor: pointer;
display: inline-block;
border-radius: 5px;
text-transform: capitalize;
}
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.aligncenter {
text-align: center;
}
.alignright {
text-align: right;
}
.alignleft {
text-align: left;
}
.clear {
clear: both;
}
/* -------------------------------------
ALERTS
Change the class depending on warning email, good email or bad email
------------------------------------- */
.alert {
font-size: 16px;
color: #fff;
font-weight: 500;
padding: 20px;
text-align: center;
border-radius: 3px 3px 0 0;
}
.alert a {
color: #fff;
text-decoration: none;
font-weight: 500;
font-size: 16px;
}
.alert.alert-warning {
background-color: #E6522C;
}
.alert.alert-bad {
background-color: #D0021B;
}
.alert.alert-good {
background-color: #68B90F;
}
/* -------------------------------------
INVOICE
Styles for the billing table
------------------------------------- */
.invoice {
margin: 40px auto;
text-align: left;
width: 80%;
}
.invoice td {
padding: 5px 0;
}
.invoice .invoice-items {
width: 100%;
}
.invoice .invoice-items td {
border-top: #eee 1px solid;
}
.invoice .invoice-items .total td {
border-top: 2px solid #333;
border-bottom: 2px solid #333;
font-weight: 700;
}
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 640px) {
body {
padding: 0 !important;
}
h1,
h2,
h3,
h4 {
font-weight: 800 !important;
margin: 20px 0 5px !important;
}
h1 {
font-size: 22px !important;
}
h2 {
font-size: 18px !important;
}
h3 {
font-size: 16px !important;
}
.container {
padding: 0 !important;
width: 100% !important;
}
.content {
padding: 0 !important;
}
.content-wrap {
padding: 10px !important;
}
.invoice {
width: 100% !important;
}
}
</style>
</head>
<body itemscope itemtype="http://schema.org/EmailMessage">
<table class="body-wrap">
<tr>
<td></td>
<td class="container" width="600">
<div class="content">
<table class="main" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ if gt (len .Alerts.Firing) 0 }}
<td class="alert alert-warning">
{{ else }}
<td class="alert alert-good">
{{ end }}
{{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }}
{{ end }}
</td>
</tr>
<tr>
<td class="content-wrap">
<table width="100%" cellpadding="0" cellspacing="0">
{{ if gt (len .Alerts.Firing) 0 }}
<tr>
<td class="content-block">
<strong>[{{ .Alerts.Firing | len }}] Firing</strong>
</td>
</tr>
{{ end }}
{{ range .Alerts.Firing }}
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />
</td>
</tr>
{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
{{ if gt (len .Alerts.Firing) 0 }}
<tr>
<td class="content-block">
<br />
<hr />
<br />
</td>
</tr>
{{ end }}
<tr>
<td class="content-block">
<strong>[{{ .Alerts.Resolved | len }}] Resolved</strong>
</td>
</tr>
{{ end }}
{{ range .Alerts.Resolved }}
<tr>
<td class="content-block">
<strong>Labels</strong><br />
{{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
{{ if gt (len .Annotations) 0 }}<strong>Annotations</strong><br />{{ end }}
{{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br />{{ end }}
<a href="{{ .GeneratorURL }}">Source</a><br />
</td>
</tr>
{{ end }}
</table>
</td>
</tr>
</table>
</div>
</td>
<td></td>
</tr>
</table>
</body>
</html>`,
};

View File

@ -1,9 +1,11 @@
import { Form } from 'antd';
import createEmail from 'api/channels/createEmail';
import createMsTeamsApi from 'api/channels/createMsTeams';
import createOpsgenie from 'api/channels/createOpsgenie';
import createPagerApi from 'api/channels/createPager';
import createSlackApi from 'api/channels/createSlack';
import createWebhookApi from 'api/channels/createWebhook';
import testEmail from 'api/channels/testEmail';
import testMsTeamsApi from 'api/channels/testMsTeams';
import testOpsGenie from 'api/channels/testOpsgenie';
import testPagerApi from 'api/channels/testPager';
@ -18,6 +20,7 @@ import { useTranslation } from 'react-i18next';
import {
ChannelType,
EmailChannel,
MsTeamsChannel,
OpsgenieChannel,
PagerChannel,
@ -25,7 +28,11 @@ import {
ValidatePagerChannel,
WebhookChannel,
} from './config';
import { OpsgenieInitialConfig, PagerInitialConfig } from './defaults';
import {
EmailInitialConfig,
OpsgenieInitialConfig,
PagerInitialConfig,
} from './defaults';
import { isChannelType } from './utils';
function CreateAlertChannels({
@ -42,7 +49,8 @@ function CreateAlertChannels({
WebhookChannel &
PagerChannel &
MsTeamsChannel &
OpsgenieChannel
OpsgenieChannel &
EmailChannel
>
>({
text: `{{ range .Alerts -}}
@ -94,6 +102,14 @@ function CreateAlertChannels({
...OpsgenieInitialConfig,
}));
}
// reset config to email defaults
if (value === ChannelType.Email && currentType !== value) {
setSelectedConfig((selectedConfig) => ({
...selectedConfig,
...EmailInitialConfig,
}));
}
},
[type, selectedConfig],
);
@ -293,6 +309,43 @@ function CreateAlertChannels({
setSavingState(false);
}, [prepareOpsgenieRequest, t, notifications]);
const prepareEmailRequest = useCallback(
() => ({
name: selectedConfig?.name || '',
send_resolved: true,
to: selectedConfig?.to || '',
html: selectedConfig?.html || '',
headers: selectedConfig?.headers || {},
}),
[selectedConfig],
);
const onEmailHandler = useCallback(async () => {
setSavingState(true);
try {
const request = prepareEmailRequest();
const response = await createEmail(request);
if (response.statusCode === 200) {
notifications.success({
message: 'Success',
description: t('channel_creation_done'),
});
history.replace(ROUTES.ALL_CHANNELS);
} else {
notifications.error({
message: 'Error',
description: response.error || t('channel_creation_failed'),
});
}
} catch (error) {
notifications.error({
message: 'Error',
description: t('channel_creation_failed'),
});
}
setSavingState(false);
}, [prepareEmailRequest, t, notifications]);
const prepareMsTeamsRequest = useCallback(
() => ({
webhook_url: selectedConfig?.webhook_url || '',
@ -339,6 +392,7 @@ function CreateAlertChannels({
[ChannelType.Pagerduty]: onPagerHandler,
[ChannelType.Opsgenie]: onOpsgenieHandler,
[ChannelType.MsTeams]: onMsTeamsHandler,
[ChannelType.Email]: onEmailHandler,
};
if (isChannelType(value)) {
@ -360,6 +414,7 @@ function CreateAlertChannels({
onPagerHandler,
onOpsgenieHandler,
onMsTeamsHandler,
onEmailHandler,
notifications,
t,
],
@ -392,6 +447,10 @@ function CreateAlertChannels({
request = prepareOpsgenieRequest();
response = await testOpsGenie(request);
break;
case ChannelType.Email:
request = prepareEmailRequest();
response = await testEmail(request);
break;
default:
notifications.error({
message: 'Error',
@ -427,6 +486,7 @@ function CreateAlertChannels({
prepareOpsgenieRequest,
prepareSlackRequest,
prepareMsTeamsRequest,
prepareEmailRequest,
notifications,
],
);
@ -455,6 +515,7 @@ function CreateAlertChannels({
...selectedConfig,
...PagerInitialConfig,
...OpsgenieInitialConfig,
...EmailInitialConfig,
},
}}
/>

View File

@ -3,7 +3,6 @@ import {
initialQueryPromQLData,
PANEL_TYPES,
} from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { AlertTypes } from 'types/api/alerts/alertTypes';
import {
AlertDef,
@ -79,7 +78,6 @@ export const logAlertDefaults: AlertDef = {
},
labels: {
severity: 'warning',
details: `${window.location.protocol}//${window.location.host}${ROUTES.LOGS_EXPLORER}`,
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
@ -110,7 +108,6 @@ export const traceAlertDefaults: AlertDef = {
},
labels: {
severity: 'warning',
details: `${window.location.protocol}//${window.location.host}/traces`,
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
@ -141,7 +138,6 @@ export const exceptionAlertDefaults: AlertDef = {
},
labels: {
severity: 'warning',
details: `${window.location.protocol}//${window.location.host}/exceptions`,
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,

View File

@ -1,9 +1,11 @@
import { Form } from 'antd';
import editEmail from 'api/channels/editEmail';
import editMsTeamsApi from 'api/channels/editMsTeams';
import editOpsgenie from 'api/channels/editOpsgenie';
import editPagerApi from 'api/channels/editPager';
import editSlackApi from 'api/channels/editSlack';
import editWebhookApi from 'api/channels/editWebhook';
import testEmail from 'api/channels/testEmail';
import testMsTeamsApi from 'api/channels/testMsTeams';
import testOpsgenie from 'api/channels/testOpsgenie';
import testPagerApi from 'api/channels/testPager';
@ -12,6 +14,7 @@ import testWebhookApi from 'api/channels/testWebhook';
import ROUTES from 'constants/routes';
import {
ChannelType,
EmailChannel,
MsTeamsChannel,
OpsgenieChannel,
PagerChannel,
@ -39,7 +42,8 @@ function EditAlertChannels({
WebhookChannel &
PagerChannel &
MsTeamsChannel &
OpsgenieChannel
OpsgenieChannel &
EmailChannel
>
>({
...initialValue,
@ -156,6 +160,36 @@ function EditAlertChannels({
setSavingState(false);
}, [prepareWebhookRequest, t, notifications, selectedConfig]);
const prepareEmailRequest = useCallback(
() => ({
name: selectedConfig?.name || '',
to: selectedConfig.to || '',
html: selectedConfig.html || '',
headers: selectedConfig.headers || {},
id,
}),
[id, selectedConfig],
);
const onEmailEditHandler = useCallback(async () => {
setSavingState(true);
const request = prepareEmailRequest();
const response = await editEmail(request);
if (response.statusCode === 200) {
notifications.success({
message: 'Success',
description: t('channel_edit_done'),
});
history.replace(ROUTES.ALL_CHANNELS);
} else {
notifications.error({
message: 'Error',
description: response.error || t('channel_edit_failed'),
});
}
setSavingState(false);
}, [prepareEmailRequest, t, notifications]);
const preparePagerRequest = useCallback(
() => ({
name: selectedConfig.name || '',
@ -300,6 +334,8 @@ function EditAlertChannels({
onMsTeamsEditHandler();
} else if (value === ChannelType.Opsgenie) {
onOpsgenieEditHandler();
} else if (value === ChannelType.Email) {
onEmailEditHandler();
}
},
[
@ -308,6 +344,7 @@ function EditAlertChannels({
onPagerEditHandler,
onMsTeamsEditHandler,
onOpsgenieEditHandler,
onEmailEditHandler,
],
);
@ -338,6 +375,10 @@ function EditAlertChannels({
request = prepareOpsgenieRequest();
if (request) response = await testOpsgenie(request);
break;
case ChannelType.Email:
request = prepareEmailRequest();
if (request) response = await testEmail(request);
break;
default:
notifications.error({
message: 'Error',
@ -373,6 +414,7 @@ function EditAlertChannels({
prepareSlackRequest,
prepareMsTeamsRequest,
prepareOpsgenieRequest,
prepareEmailRequest,
notifications,
],
);

View File

@ -0,0 +1,48 @@
import { Form, Input } from 'antd';
import { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import { EmailChannel } from '../../CreateAlertChannels/config';
function EmailForm({ setSelectedConfig }: EmailFormProps): JSX.Element {
const { t } = useTranslation('channels');
const handleInputChange = (field: string) => (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
): void => {
setSelectedConfig((value) => ({
...value,
[field]: event.target.value,
}));
};
return (
<>
<Form.Item
name="to"
help={t('help_email_to')}
label={t('field_email_to')}
required
>
<Input
onChange={handleInputChange('to')}
placeholder={t('placeholder_email_to')}
/>
</Form.Item>
{/* <Form.Item name="html" label={t('field_email_html')} required>
<TextArea
rows={4}
onChange={handleInputChange('html')}
placeholder={t('placeholder_email_html')}
/>
</Form.Item> */}
</>
);
}
interface EmailFormProps {
setSelectedConfig: Dispatch<SetStateAction<Partial<EmailChannel>>>;
}
export default EmailForm;

View File

@ -5,6 +5,7 @@ import { FeatureKeys } from 'constants/features';
import ROUTES from 'constants/routes';
import {
ChannelType,
EmailChannel,
OpsgenieChannel,
PagerChannel,
SlackChannel,
@ -16,6 +17,7 @@ import history from 'lib/history';
import { Dispatch, ReactElement, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import EmailSettings from './Settings/Email';
import MsTeamsSettings from './Settings/MsTeams';
import OpsgenieSettings from './Settings/Opsgenie';
import PagerSettings from './Settings/Pager';
@ -69,6 +71,8 @@ function FormAlertChannels({
return <MsTeamsSettings setSelectedConfig={setSelectedConfig} />;
case ChannelType.Opsgenie:
return <OpsgenieSettings setSelectedConfig={setSelectedConfig} />;
case ChannelType.Email:
return <EmailSettings setSelectedConfig={setSelectedConfig} />;
default:
return null;
}
@ -105,6 +109,9 @@ function FormAlertChannels({
<Select.Option value="opsgenie" key="opsgenie">
Opsgenie
</Select.Option>
<Select.Option value="email" key="email">
Email
</Select.Option>
{!isOssFeature?.active && (
<Select.Option value="msteams" key="msteams">
<div>
@ -151,7 +158,13 @@ interface FormAlertChannelsProps {
type: ChannelType;
setSelectedConfig: Dispatch<
SetStateAction<
Partial<SlackChannel & WebhookChannel & PagerChannel & OpsgenieChannel>
Partial<
SlackChannel &
WebhookChannel &
PagerChannel &
OpsgenieChannel &
EmailChannel
>
>
>;
onTypeChangeHandler: (value: ChannelType) => void;

View File

@ -81,6 +81,15 @@ function ChannelsEdit(): JSX.Element {
};
}
if (value && 'email_configs' in value) {
const emailConfig = value.email_configs[0];
channel = emailConfig;
return {
type: ChannelType.Email,
channel,
};
}
if (value && 'webhook_configs' in value) {
const webhookConfig = value.webhook_configs[0];
channel = webhookConfig;

View File

@ -0,0 +1,8 @@
import { EmailChannel } from 'container/CreateAlertChannels/config';
export type Props = EmailChannel;
export interface PayloadProps {
data: string;
status: string;
}

View File

@ -0,0 +1,10 @@
import { EmailChannel } from 'container/CreateAlertChannels/config';
export interface Props extends EmailChannel {
id: string;
}
export interface PayloadProps {
data: string;
status: string;
}

View File

@ -13,7 +13,7 @@ https://github.com/SigNoz/signoz/blob/main/CONTRIBUTING.md#to-run-clickhouse-set
- Change the alertmanager section in `signoz/deploy/docker/clickhouse-setup/docker-compose.yaml` as follows:
```console
alertmanager:
image: signoz/alertmanager:0.23.4
image: signoz/alertmanager:0.23.5
volumes:
- ./data/alertmanager:/data
expose:

View File

@ -21,6 +21,7 @@ const AlertChannelWebhook = "ALERT_CHANNEL_WEBHOOK"
const AlertChannelPagerduty = "ALERT_CHANNEL_PAGERDUTY"
const AlertChannelMsTeams = "ALERT_CHANNEL_MSTEAMS"
const AlertChannelOpsgenie = "ALERT_CHANNEL_OPSGENIE"
const AlertChannelEmail = "ALERT_CHANNEL_EMAIL"
var BasicPlan = FeatureSet{
Feature{
@ -100,6 +101,13 @@ var BasicPlan = FeatureSet{
UsageLimit: -1,
Route: "",
},
Feature{
Name: AlertChannelEmail,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
Feature{
Name: AlertChannelMsTeams,
Active: false,

View File

@ -866,7 +866,6 @@ func (m *Manager) TestNotification(ctx context.Context, ruleStr string) (int, *m
if parsedRule.RuleType == RuleTypeThreshold {
// add special labels for test alerts
parsedRule.Labels[labels.AlertAdditionalInfoLabel] = fmt.Sprintf("The rule threshold is set to %.4f, and the observed metric value is {{$value}}.", *parsedRule.RuleCondition.Target)
parsedRule.Annotations[labels.AlertSummaryLabel] = fmt.Sprintf("The rule threshold is set to %.4f, and the observed metric value is {{$value}}.", *parsedRule.RuleCondition.Target)
parsedRule.Labels[labels.RuleSourceLabel] = ""
parsedRule.Labels[labels.AlertRuleIdLabel] = ""

View File

@ -138,7 +138,7 @@ services:
# - ./data/clickhouse-3/:/var/lib/clickhouse/
alertmanager:
image: signoz/alertmanager:0.23.4
image: signoz/alertmanager:0.23.5
container_name: signoz-alertmanager
volumes:
- ./data/alertmanager:/data

View File

@ -16,8 +16,6 @@ const sep = '\xff'
const (
MetricNameLabel = "__name__"
AlertNameLabel = "alertname"
BucketLabel = "le"
InstanceName = "instance"
// AlertStateLabel is the label name indicating the state of an alert.
AlertStateLabel = "alertstate"
@ -25,9 +23,8 @@ const (
AlertRuleIdLabel = "ruleId"
RuleSourceLabel = "ruleSource"
RuleThresholdLabel = "threshold"
AlertAdditionalInfoLabel = "additionalInfo"
AlertSummaryLabel = "summary"
RuleThresholdLabel = "threshold"
AlertSummaryLabel = "summary"
)
// Label is a key/value pair of strings.