Skip to content

Commit a65d590

Browse files
committed
feat(notification): add Jira Service Management as a notification provider
1 parent f5578da commit a65d590

6 files changed

Lines changed: 174 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
const NotificationProvider = require("./notification-provider");
2+
const axios = require("axios");
3+
const { UP, DOWN } = require("../../src/util");
4+
5+
const okMsg = "Sent Successfully.";
6+
7+
class JiraServiceManagement extends NotificationProvider {
8+
name = "JiraServiceManagement";
9+
10+
/**
11+
* @inheritdoc
12+
*/
13+
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
14+
const priority = notification.jsmPriority || 3;
15+
const baseUrl = `https://api.atlassian.com/jsm/ops/api/${notification.jsmCloudId}/v1`;
16+
const textMsg = "Uptime Kuma Alert";
17+
18+
try {
19+
if (heartbeatJSON == null) {
20+
// Test notification
21+
let notificationTestAlias = "uptime-kuma-notification-test";
22+
let data = {
23+
message: msg,
24+
alias: notificationTestAlias,
25+
source: "Uptime Kuma",
26+
priority: "P5",
27+
tags: ["Uptime Kuma"],
28+
};
29+
30+
return this.post(notification, `${baseUrl}/alerts`, data);
31+
}
32+
33+
if (heartbeatJSON.status === DOWN) {
34+
let data = {
35+
message: monitorJSON ? `${textMsg}: ${monitorJSON.name}` : textMsg,
36+
alias: monitorJSON.name,
37+
description: msg,
38+
source: "Uptime Kuma",
39+
priority: `P${priority}`,
40+
tags: ["Uptime Kuma"],
41+
};
42+
43+
return this.post(notification, `${baseUrl}/alerts`, data);
44+
}
45+
46+
if (heartbeatJSON.status === UP) {
47+
// JSM requires getting the alert ID first, then closing by ID
48+
const getUrl = `${baseUrl}/alerts/alias?alias=${encodeURIComponent(monitorJSON.name)}`;
49+
const config = this.getConfig(notification);
50+
51+
let alertResponse = await axios.get(getUrl, config);
52+
const alertId = alertResponse.data.id;
53+
54+
const closeUrl = `${baseUrl}/alerts/${alertId}/close`;
55+
let data = {
56+
source: "Uptime Kuma",
57+
};
58+
59+
return this.post(notification, closeUrl, data);
60+
}
61+
} catch (error) {
62+
this.throwGeneralAxiosError(error);
63+
}
64+
}
65+
66+
/**
67+
* Get axios config with Basic Auth for JSM
68+
* @param {BeanModel} notification Notification details
69+
* @returns {object} Axios config object
70+
*/
71+
getConfig(notification) {
72+
const authToken = Buffer.from(`${notification.jsmEmail}:${notification.jsmApiToken}`).toString("base64");
73+
let config = {
74+
headers: {
75+
"Content-Type": "application/json",
76+
Accept: "application/json",
77+
Authorization: `Basic ${authToken}`,
78+
},
79+
};
80+
return this.getAxiosConfigWithProxy(config);
81+
}
82+
83+
/**
84+
* Make POST request to Jira Service Management
85+
* @param {BeanModel} notification Notification to send
86+
* @param {string} url Request url
87+
* @param {object} data Request body
88+
* @returns {Promise<string>} Success message
89+
*/
90+
async post(notification, url, data) {
91+
let config = this.getConfig(notification);
92+
93+
let res = await axios.post(url, data, config);
94+
if (res.status == null) {
95+
return "Jira Service Management notification failed with invalid response!";
96+
}
97+
if (res.status < 200 || res.status >= 300) {
98+
return `Jira Service Management notification failed with status code ${res.status}`;
99+
}
100+
101+
return okMsg;
102+
}
103+
}
104+
105+
module.exports = JiraServiceManagement;

server/notification.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const Octopush = require("./notification-providers/octopush");
3636
const OneChat = require("./notification-providers/onechat");
3737
const OneBot = require("./notification-providers/onebot");
3838
const Opsgenie = require("./notification-providers/opsgenie");
39+
const JiraServiceManagement = require("./notification-providers/jira-service-management");
3940
const PagerDuty = require("./notification-providers/pagerduty");
4041
const Pumble = require("./notification-providers/pumble");
4142
const FlashDuty = require("./notification-providers/flashduty");
@@ -138,6 +139,7 @@ class Notification {
138139
new OneBot(),
139140
new Onesender(),
140141
new Opsgenie(),
142+
new JiraServiceManagement(),
141143
new PagerDuty(),
142144
new FlashDuty(),
143145
new PagerTree(),

src/components/NotificationDialog.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ export default {
292292
HeiiOnCall: "Heii On-Call",
293293
Keep: "Keep",
294294
Opsgenie: "Opsgenie",
295+
JiraServiceManagement: this.$t("Jira Service Management"),
295296
PagerDuty: "PagerDuty",
296297
PagerTree: "PagerTree",
297298
SIGNL4: "SIGNL4",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<template>
2+
<div class="mb-3">
3+
<label for="jsm-cloud-id" class="form-label">
4+
{{ $t("Cloud ID") }}
5+
<span style="color: red"><sup>*</sup></span>
6+
</label>
7+
<input id="jsm-cloud-id" v-model="$parent.notification.jsmCloudId" type="text" class="form-control" required />
8+
<i18n-t tag="p" keypath="aboutJiraCloudId" style="margin-top: 8px">
9+
<a href="https://support.atlassian.com/jira/kb/retrieve-my-atlassian-sites-cloud-id/" target="_blank">
10+
{{ $t("see Jira Cloud Docs") }}
11+
</a>
12+
</i18n-t>
13+
</div>
14+
<div class="mb-3">
15+
<label for="jsm-email" class="form-label">
16+
{{ $t("Email") }}
17+
<span style="color: red"><sup>*</sup></span>
18+
</label>
19+
<input id="jsm-email" v-model="$parent.notification.jsmEmail" type="email" class="form-control" required />
20+
</div>
21+
<div class="mb-3">
22+
<label for="jsm-api-token" class="form-label">
23+
{{ $t("API Token") }}
24+
<span style="color: red"><sup>*</sup></span>
25+
</label>
26+
<HiddenInput
27+
id="jsm-api-token"
28+
v-model="$parent.notification.jsmApiToken"
29+
required="true"
30+
autocomplete="false"
31+
></HiddenInput>
32+
</div>
33+
<div class="mb-3">
34+
<label for="jsm-priority" class="form-label">{{ $t("Priority") }}</label>
35+
<input
36+
id="jsm-priority"
37+
v-model="$parent.notification.jsmPriority"
38+
type="number"
39+
class="form-control"
40+
min="1"
41+
max="5"
42+
step="1"
43+
/>
44+
</div>
45+
<div class="form-text">
46+
<span style="color: red"><sup>*</sup></span>
47+
{{ $t("Required") }}
48+
</div>
49+
</template>
50+
51+
<script>
52+
import HiddenInput from "../HiddenInput.vue";
53+
export default {
54+
components: {
55+
HiddenInput,
56+
},
57+
};
58+
</script>

src/components/notifications/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import OneChat from "./OneChat.vue";
3636
import OneBot from "./OneBot.vue";
3737
import Onesender from "./Onesender.vue";
3838
import Opsgenie from "./Opsgenie.vue";
39+
import JiraServiceManagement from "./JiraServiceManagement.vue";
3940
import PagerDuty from "./PagerDuty.vue";
4041
import FlashDuty from "./FlashDuty.vue";
4142
import PagerTree from "./PagerTree.vue";
@@ -126,6 +127,7 @@ const NotificationFormList = {
126127
OneBot: OneBot,
127128
Onesender: Onesender,
128129
Opsgenie: Opsgenie,
130+
JiraServiceManagement: JiraServiceManagement,
129131
PagerDuty: PagerDuty,
130132
FlashDuty: FlashDuty,
131133
PagerTree: PagerTree,

src/lang/en.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,11 @@
441441
"RadiusCallingStationId": "Calling Station Id",
442442
"RadiusCallingStationIdDescription": "Identifier of the calling device",
443443
"Certificate Expiry Notification": "Certificate Expiry Notification",
444+
"Cloud ID": "Cloud ID",
444445
"API Username": "API Username",
445446
"API Key": "API Key",
447+
"API Token": "API Token",
448+
"See Jira Cloud Docs": "See Jira Cloud Docs",
446449
"Show update if available": "Show update if available",
447450
"Also check beta release": "Also check beta release",
448451
"Using a Reverse Proxy?": "Using a Reverse Proxy?",
@@ -856,6 +859,9 @@
856859
"Icon Emoji": "Icon Emoji",
857860
"signalImportant": "IMPORTANT: You cannot mix groups and numbers in recipients!",
858861
"aboutWebhooks": "More info about Webhooks on: {0}",
862+
"aboutJiraCloudId": "More info about Jira Cloud ID: {0}",
863+
"see Jira Cloud Docs": "see Jira Cloud Docs",
864+
"Jira Service Management": "Jira Service Management",
859865
"aboutSlackUsername": "Changes the display name of the message sender. If you want to mention someone, include it in the friendly name instead.",
860866
"aboutChannelName": "Enter the channel name on {0} Channel Name field if you want to bypass the Webhook channel. Ex: #other-channel",
861867
"aboutKumaURL": "If you leave the Uptime Kuma URL field blank, it will default to the Project GitHub page.",

0 commit comments

Comments
 (0)