mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-04 11:25:52 +08:00
feat: add support for email alert channel (#4599)
This commit is contained in:
parent
1a62a13aea
commit
d77389abe3
@ -133,7 +133,7 @@ services:
|
|||||||
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.23.4
|
image: signoz/alertmanager:0.23.5
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
command:
|
command:
|
||||||
|
@ -54,7 +54,7 @@ services:
|
|||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
container_name: signoz-alertmanager
|
container_name: signoz-alertmanager
|
||||||
image: signoz/alertmanager:0.23.4
|
image: signoz/alertmanager:0.23.5
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -149,7 +149,7 @@ services:
|
|||||||
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.4}
|
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5}
|
||||||
container_name: signoz-alertmanager
|
container_name: signoz-alertmanager
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
|
@ -90,6 +90,13 @@ var BasicPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelEmail,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.AlertChannelMsTeams,
|
Name: basemodel.AlertChannelMsTeams,
|
||||||
Active: false,
|
Active: false,
|
||||||
@ -177,6 +184,13 @@ var ProPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelEmail,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.AlertChannelMsTeams,
|
Name: basemodel.AlertChannelMsTeams,
|
||||||
Active: true,
|
Active: true,
|
||||||
@ -264,6 +278,13 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelEmail,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.AlertChannelMsTeams,
|
Name: basemodel.AlertChannelMsTeams,
|
||||||
Active: true,
|
Active: true,
|
||||||
@ -279,17 +300,17 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: Onboarding,
|
Name: Onboarding,
|
||||||
Active: true,
|
Active: true,
|
||||||
Usage: 0,
|
Usage: 0,
|
||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: ChatSupport,
|
Name: ChatSupport,
|
||||||
Active: true,
|
Active: true,
|
||||||
Usage: 0,
|
Usage: 0,
|
||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
"field_opsgenie_api_key": "API Key",
|
"field_opsgenie_api_key": "API Key",
|
||||||
"field_opsgenie_description": "Description",
|
"field_opsgenie_description": "Description",
|
||||||
"placeholder_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_username": "User Name (optional)",
|
||||||
"field_webhook_password": "Password (optional)",
|
"field_webhook_password": "Password (optional)",
|
||||||
"field_pager_routing_key": "Routing Key",
|
"field_pager_routing_key": "Routing Key",
|
||||||
|
34
frontend/src/api/channels/createEmail.ts
Normal file
34
frontend/src/api/channels/createEmail.ts
Normal 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;
|
34
frontend/src/api/channels/editEmail.ts
Normal file
34
frontend/src/api/channels/editEmail.ts
Normal 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;
|
34
frontend/src/api/channels/testEmail.ts
Normal file
34
frontend/src/api/channels/testEmail.ts
Normal 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;
|
@ -64,6 +64,16 @@ export interface OpsgenieChannel extends Channel {
|
|||||||
priority?: string;
|
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 => {
|
export const ValidatePagerChannel = (p: PagerChannel): string => {
|
||||||
if (!p) {
|
if (!p) {
|
||||||
return 'Received unexpected input for this channel, please contact your administrator ';
|
return 'Received unexpected input for this channel, please contact your administrator ';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { OpsgenieChannel, PagerChannel } from './config';
|
import { EmailChannel, OpsgenieChannel, PagerChannel } from './config';
|
||||||
|
|
||||||
export const PagerInitialConfig: Partial<PagerChannel> = {
|
export const PagerInitialConfig: Partial<PagerChannel> = {
|
||||||
description: `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
|
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:
|
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 }}',
|
'{{ 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>`,
|
||||||
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
|
import createEmail from 'api/channels/createEmail';
|
||||||
import createMsTeamsApi from 'api/channels/createMsTeams';
|
import createMsTeamsApi from 'api/channels/createMsTeams';
|
||||||
import createOpsgenie from 'api/channels/createOpsgenie';
|
import createOpsgenie from 'api/channels/createOpsgenie';
|
||||||
import createPagerApi from 'api/channels/createPager';
|
import createPagerApi from 'api/channels/createPager';
|
||||||
import createSlackApi from 'api/channels/createSlack';
|
import createSlackApi from 'api/channels/createSlack';
|
||||||
import createWebhookApi from 'api/channels/createWebhook';
|
import createWebhookApi from 'api/channels/createWebhook';
|
||||||
|
import testEmail from 'api/channels/testEmail';
|
||||||
import testMsTeamsApi from 'api/channels/testMsTeams';
|
import testMsTeamsApi from 'api/channels/testMsTeams';
|
||||||
import testOpsGenie from 'api/channels/testOpsgenie';
|
import testOpsGenie from 'api/channels/testOpsgenie';
|
||||||
import testPagerApi from 'api/channels/testPager';
|
import testPagerApi from 'api/channels/testPager';
|
||||||
@ -18,6 +20,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
|
EmailChannel,
|
||||||
MsTeamsChannel,
|
MsTeamsChannel,
|
||||||
OpsgenieChannel,
|
OpsgenieChannel,
|
||||||
PagerChannel,
|
PagerChannel,
|
||||||
@ -25,7 +28,11 @@ import {
|
|||||||
ValidatePagerChannel,
|
ValidatePagerChannel,
|
||||||
WebhookChannel,
|
WebhookChannel,
|
||||||
} from './config';
|
} from './config';
|
||||||
import { OpsgenieInitialConfig, PagerInitialConfig } from './defaults';
|
import {
|
||||||
|
EmailInitialConfig,
|
||||||
|
OpsgenieInitialConfig,
|
||||||
|
PagerInitialConfig,
|
||||||
|
} from './defaults';
|
||||||
import { isChannelType } from './utils';
|
import { isChannelType } from './utils';
|
||||||
|
|
||||||
function CreateAlertChannels({
|
function CreateAlertChannels({
|
||||||
@ -42,7 +49,8 @@ function CreateAlertChannels({
|
|||||||
WebhookChannel &
|
WebhookChannel &
|
||||||
PagerChannel &
|
PagerChannel &
|
||||||
MsTeamsChannel &
|
MsTeamsChannel &
|
||||||
OpsgenieChannel
|
OpsgenieChannel &
|
||||||
|
EmailChannel
|
||||||
>
|
>
|
||||||
>({
|
>({
|
||||||
text: `{{ range .Alerts -}}
|
text: `{{ range .Alerts -}}
|
||||||
@ -94,6 +102,14 @@ function CreateAlertChannels({
|
|||||||
...OpsgenieInitialConfig,
|
...OpsgenieInitialConfig,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset config to email defaults
|
||||||
|
if (value === ChannelType.Email && currentType !== value) {
|
||||||
|
setSelectedConfig((selectedConfig) => ({
|
||||||
|
...selectedConfig,
|
||||||
|
...EmailInitialConfig,
|
||||||
|
}));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[type, selectedConfig],
|
[type, selectedConfig],
|
||||||
);
|
);
|
||||||
@ -293,6 +309,43 @@ function CreateAlertChannels({
|
|||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [prepareOpsgenieRequest, t, notifications]);
|
}, [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(
|
const prepareMsTeamsRequest = useCallback(
|
||||||
() => ({
|
() => ({
|
||||||
webhook_url: selectedConfig?.webhook_url || '',
|
webhook_url: selectedConfig?.webhook_url || '',
|
||||||
@ -339,6 +392,7 @@ function CreateAlertChannels({
|
|||||||
[ChannelType.Pagerduty]: onPagerHandler,
|
[ChannelType.Pagerduty]: onPagerHandler,
|
||||||
[ChannelType.Opsgenie]: onOpsgenieHandler,
|
[ChannelType.Opsgenie]: onOpsgenieHandler,
|
||||||
[ChannelType.MsTeams]: onMsTeamsHandler,
|
[ChannelType.MsTeams]: onMsTeamsHandler,
|
||||||
|
[ChannelType.Email]: onEmailHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isChannelType(value)) {
|
if (isChannelType(value)) {
|
||||||
@ -360,6 +414,7 @@ function CreateAlertChannels({
|
|||||||
onPagerHandler,
|
onPagerHandler,
|
||||||
onOpsgenieHandler,
|
onOpsgenieHandler,
|
||||||
onMsTeamsHandler,
|
onMsTeamsHandler,
|
||||||
|
onEmailHandler,
|
||||||
notifications,
|
notifications,
|
||||||
t,
|
t,
|
||||||
],
|
],
|
||||||
@ -392,6 +447,10 @@ function CreateAlertChannels({
|
|||||||
request = prepareOpsgenieRequest();
|
request = prepareOpsgenieRequest();
|
||||||
response = await testOpsGenie(request);
|
response = await testOpsGenie(request);
|
||||||
break;
|
break;
|
||||||
|
case ChannelType.Email:
|
||||||
|
request = prepareEmailRequest();
|
||||||
|
response = await testEmail(request);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -427,6 +486,7 @@ function CreateAlertChannels({
|
|||||||
prepareOpsgenieRequest,
|
prepareOpsgenieRequest,
|
||||||
prepareSlackRequest,
|
prepareSlackRequest,
|
||||||
prepareMsTeamsRequest,
|
prepareMsTeamsRequest,
|
||||||
|
prepareEmailRequest,
|
||||||
notifications,
|
notifications,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -455,6 +515,7 @@ function CreateAlertChannels({
|
|||||||
...selectedConfig,
|
...selectedConfig,
|
||||||
...PagerInitialConfig,
|
...PagerInitialConfig,
|
||||||
...OpsgenieInitialConfig,
|
...OpsgenieInitialConfig,
|
||||||
|
...EmailInitialConfig,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -3,7 +3,6 @@ import {
|
|||||||
initialQueryPromQLData,
|
initialQueryPromQLData,
|
||||||
PANEL_TYPES,
|
PANEL_TYPES,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
import {
|
import {
|
||||||
AlertDef,
|
AlertDef,
|
||||||
@ -79,7 +78,6 @@ export const logAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
severity: 'warning',
|
severity: 'warning',
|
||||||
details: `${window.location.protocol}//${window.location.host}${ROUTES.LOGS_EXPLORER}`,
|
|
||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
@ -110,7 +108,6 @@ export const traceAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
severity: 'warning',
|
severity: 'warning',
|
||||||
details: `${window.location.protocol}//${window.location.host}/traces`,
|
|
||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
@ -141,7 +138,6 @@ export const exceptionAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
severity: 'warning',
|
severity: 'warning',
|
||||||
details: `${window.location.protocol}//${window.location.host}/exceptions`,
|
|
||||||
},
|
},
|
||||||
annotations: defaultAnnotations,
|
annotations: defaultAnnotations,
|
||||||
evalWindow: defaultEvalWindow,
|
evalWindow: defaultEvalWindow,
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
|
import editEmail from 'api/channels/editEmail';
|
||||||
import editMsTeamsApi from 'api/channels/editMsTeams';
|
import editMsTeamsApi from 'api/channels/editMsTeams';
|
||||||
import editOpsgenie from 'api/channels/editOpsgenie';
|
import editOpsgenie from 'api/channels/editOpsgenie';
|
||||||
import editPagerApi from 'api/channels/editPager';
|
import editPagerApi from 'api/channels/editPager';
|
||||||
import editSlackApi from 'api/channels/editSlack';
|
import editSlackApi from 'api/channels/editSlack';
|
||||||
import editWebhookApi from 'api/channels/editWebhook';
|
import editWebhookApi from 'api/channels/editWebhook';
|
||||||
|
import testEmail from 'api/channels/testEmail';
|
||||||
import testMsTeamsApi from 'api/channels/testMsTeams';
|
import testMsTeamsApi from 'api/channels/testMsTeams';
|
||||||
import testOpsgenie from 'api/channels/testOpsgenie';
|
import testOpsgenie from 'api/channels/testOpsgenie';
|
||||||
import testPagerApi from 'api/channels/testPager';
|
import testPagerApi from 'api/channels/testPager';
|
||||||
@ -12,6 +14,7 @@ import testWebhookApi from 'api/channels/testWebhook';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
|
EmailChannel,
|
||||||
MsTeamsChannel,
|
MsTeamsChannel,
|
||||||
OpsgenieChannel,
|
OpsgenieChannel,
|
||||||
PagerChannel,
|
PagerChannel,
|
||||||
@ -39,7 +42,8 @@ function EditAlertChannels({
|
|||||||
WebhookChannel &
|
WebhookChannel &
|
||||||
PagerChannel &
|
PagerChannel &
|
||||||
MsTeamsChannel &
|
MsTeamsChannel &
|
||||||
OpsgenieChannel
|
OpsgenieChannel &
|
||||||
|
EmailChannel
|
||||||
>
|
>
|
||||||
>({
|
>({
|
||||||
...initialValue,
|
...initialValue,
|
||||||
@ -156,6 +160,36 @@ function EditAlertChannels({
|
|||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [prepareWebhookRequest, t, notifications, selectedConfig]);
|
}, [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(
|
const preparePagerRequest = useCallback(
|
||||||
() => ({
|
() => ({
|
||||||
name: selectedConfig.name || '',
|
name: selectedConfig.name || '',
|
||||||
@ -300,6 +334,8 @@ function EditAlertChannels({
|
|||||||
onMsTeamsEditHandler();
|
onMsTeamsEditHandler();
|
||||||
} else if (value === ChannelType.Opsgenie) {
|
} else if (value === ChannelType.Opsgenie) {
|
||||||
onOpsgenieEditHandler();
|
onOpsgenieEditHandler();
|
||||||
|
} else if (value === ChannelType.Email) {
|
||||||
|
onEmailEditHandler();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -308,6 +344,7 @@ function EditAlertChannels({
|
|||||||
onPagerEditHandler,
|
onPagerEditHandler,
|
||||||
onMsTeamsEditHandler,
|
onMsTeamsEditHandler,
|
||||||
onOpsgenieEditHandler,
|
onOpsgenieEditHandler,
|
||||||
|
onEmailEditHandler,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -338,6 +375,10 @@ function EditAlertChannels({
|
|||||||
request = prepareOpsgenieRequest();
|
request = prepareOpsgenieRequest();
|
||||||
if (request) response = await testOpsgenie(request);
|
if (request) response = await testOpsgenie(request);
|
||||||
break;
|
break;
|
||||||
|
case ChannelType.Email:
|
||||||
|
request = prepareEmailRequest();
|
||||||
|
if (request) response = await testEmail(request);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -373,6 +414,7 @@ function EditAlertChannels({
|
|||||||
prepareSlackRequest,
|
prepareSlackRequest,
|
||||||
prepareMsTeamsRequest,
|
prepareMsTeamsRequest,
|
||||||
prepareOpsgenieRequest,
|
prepareOpsgenieRequest,
|
||||||
|
prepareEmailRequest,
|
||||||
notifications,
|
notifications,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
48
frontend/src/container/FormAlertChannels/Settings/Email.tsx
Normal file
48
frontend/src/container/FormAlertChannels/Settings/Email.tsx
Normal 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;
|
@ -5,6 +5,7 @@ import { FeatureKeys } from 'constants/features';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
|
EmailChannel,
|
||||||
OpsgenieChannel,
|
OpsgenieChannel,
|
||||||
PagerChannel,
|
PagerChannel,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
@ -16,6 +17,7 @@ import history from 'lib/history';
|
|||||||
import { Dispatch, ReactElement, SetStateAction } from 'react';
|
import { Dispatch, ReactElement, SetStateAction } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import EmailSettings from './Settings/Email';
|
||||||
import MsTeamsSettings from './Settings/MsTeams';
|
import MsTeamsSettings from './Settings/MsTeams';
|
||||||
import OpsgenieSettings from './Settings/Opsgenie';
|
import OpsgenieSettings from './Settings/Opsgenie';
|
||||||
import PagerSettings from './Settings/Pager';
|
import PagerSettings from './Settings/Pager';
|
||||||
@ -69,6 +71,8 @@ function FormAlertChannels({
|
|||||||
return <MsTeamsSettings setSelectedConfig={setSelectedConfig} />;
|
return <MsTeamsSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
case ChannelType.Opsgenie:
|
case ChannelType.Opsgenie:
|
||||||
return <OpsgenieSettings setSelectedConfig={setSelectedConfig} />;
|
return <OpsgenieSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
|
case ChannelType.Email:
|
||||||
|
return <EmailSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -105,6 +109,9 @@ function FormAlertChannels({
|
|||||||
<Select.Option value="opsgenie" key="opsgenie">
|
<Select.Option value="opsgenie" key="opsgenie">
|
||||||
Opsgenie
|
Opsgenie
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
|
<Select.Option value="email" key="email">
|
||||||
|
Email
|
||||||
|
</Select.Option>
|
||||||
{!isOssFeature?.active && (
|
{!isOssFeature?.active && (
|
||||||
<Select.Option value="msteams" key="msteams">
|
<Select.Option value="msteams" key="msteams">
|
||||||
<div>
|
<div>
|
||||||
@ -151,7 +158,13 @@ interface FormAlertChannelsProps {
|
|||||||
type: ChannelType;
|
type: ChannelType;
|
||||||
setSelectedConfig: Dispatch<
|
setSelectedConfig: Dispatch<
|
||||||
SetStateAction<
|
SetStateAction<
|
||||||
Partial<SlackChannel & WebhookChannel & PagerChannel & OpsgenieChannel>
|
Partial<
|
||||||
|
SlackChannel &
|
||||||
|
WebhookChannel &
|
||||||
|
PagerChannel &
|
||||||
|
OpsgenieChannel &
|
||||||
|
EmailChannel
|
||||||
|
>
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
onTypeChangeHandler: (value: ChannelType) => void;
|
onTypeChangeHandler: (value: ChannelType) => void;
|
||||||
|
@ -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) {
|
if (value && 'webhook_configs' in value) {
|
||||||
const webhookConfig = value.webhook_configs[0];
|
const webhookConfig = value.webhook_configs[0];
|
||||||
channel = webhookConfig;
|
channel = webhookConfig;
|
||||||
|
8
frontend/src/types/api/channels/createEmail.ts
Normal file
8
frontend/src/types/api/channels/createEmail.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { EmailChannel } from 'container/CreateAlertChannels/config';
|
||||||
|
|
||||||
|
export type Props = EmailChannel;
|
||||||
|
|
||||||
|
export interface PayloadProps {
|
||||||
|
data: string;
|
||||||
|
status: string;
|
||||||
|
}
|
10
frontend/src/types/api/channels/editEmail.ts
Normal file
10
frontend/src/types/api/channels/editEmail.ts
Normal 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;
|
||||||
|
}
|
@ -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:
|
- Change the alertmanager section in `signoz/deploy/docker/clickhouse-setup/docker-compose.yaml` as follows:
|
||||||
```console
|
```console
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.23.4
|
image: signoz/alertmanager:0.23.5
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
expose:
|
expose:
|
||||||
|
@ -21,6 +21,7 @@ const AlertChannelWebhook = "ALERT_CHANNEL_WEBHOOK"
|
|||||||
const AlertChannelPagerduty = "ALERT_CHANNEL_PAGERDUTY"
|
const AlertChannelPagerduty = "ALERT_CHANNEL_PAGERDUTY"
|
||||||
const AlertChannelMsTeams = "ALERT_CHANNEL_MSTEAMS"
|
const AlertChannelMsTeams = "ALERT_CHANNEL_MSTEAMS"
|
||||||
const AlertChannelOpsgenie = "ALERT_CHANNEL_OPSGENIE"
|
const AlertChannelOpsgenie = "ALERT_CHANNEL_OPSGENIE"
|
||||||
|
const AlertChannelEmail = "ALERT_CHANNEL_EMAIL"
|
||||||
|
|
||||||
var BasicPlan = FeatureSet{
|
var BasicPlan = FeatureSet{
|
||||||
Feature{
|
Feature{
|
||||||
@ -100,6 +101,13 @@ var BasicPlan = FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
Feature{
|
||||||
|
Name: AlertChannelEmail,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
Feature{
|
Feature{
|
||||||
Name: AlertChannelMsTeams,
|
Name: AlertChannelMsTeams,
|
||||||
Active: false,
|
Active: false,
|
||||||
|
@ -866,7 +866,6 @@ func (m *Manager) TestNotification(ctx context.Context, ruleStr string) (int, *m
|
|||||||
if parsedRule.RuleType == RuleTypeThreshold {
|
if parsedRule.RuleType == RuleTypeThreshold {
|
||||||
|
|
||||||
// add special labels for test alerts
|
// 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.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.RuleSourceLabel] = ""
|
||||||
parsedRule.Labels[labels.AlertRuleIdLabel] = ""
|
parsedRule.Labels[labels.AlertRuleIdLabel] = ""
|
||||||
|
@ -138,7 +138,7 @@ services:
|
|||||||
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.23.4
|
image: signoz/alertmanager:0.23.5
|
||||||
container_name: signoz-alertmanager
|
container_name: signoz-alertmanager
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
|
@ -16,8 +16,6 @@ const sep = '\xff'
|
|||||||
const (
|
const (
|
||||||
MetricNameLabel = "__name__"
|
MetricNameLabel = "__name__"
|
||||||
AlertNameLabel = "alertname"
|
AlertNameLabel = "alertname"
|
||||||
BucketLabel = "le"
|
|
||||||
InstanceName = "instance"
|
|
||||||
|
|
||||||
// AlertStateLabel is the label name indicating the state of an alert.
|
// AlertStateLabel is the label name indicating the state of an alert.
|
||||||
AlertStateLabel = "alertstate"
|
AlertStateLabel = "alertstate"
|
||||||
@ -25,9 +23,8 @@ const (
|
|||||||
AlertRuleIdLabel = "ruleId"
|
AlertRuleIdLabel = "ruleId"
|
||||||
RuleSourceLabel = "ruleSource"
|
RuleSourceLabel = "ruleSource"
|
||||||
|
|
||||||
RuleThresholdLabel = "threshold"
|
RuleThresholdLabel = "threshold"
|
||||||
AlertAdditionalInfoLabel = "additionalInfo"
|
AlertSummaryLabel = "summary"
|
||||||
AlertSummaryLabel = "summary"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Label is a key/value pair of strings.
|
// Label is a key/value pair of strings.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user