mirror of
https://github.com/rajnandan1/kener.git
synced 2026-05-08 12:19:24 -05:00
Merge pull request #265 from rajnandan1/release/3.0.13
feat: test monitors in manage monitor dashboard
This commit is contained in:
+3
-3
@@ -14,7 +14,7 @@
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100
|
||||
"printWidth": 120
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24,7 +24,7 @@
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 80
|
||||
"printWidth": 120
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -34,7 +34,7 @@
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100
|
||||
"printWidth": 120
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kener",
|
||||
"version": "3.0.12",
|
||||
"version": "3.1.0",
|
||||
"private": false,
|
||||
"license": "MIT",
|
||||
"description": "Kener: An open-source Node.js status page application for real-time service monitoring, incident management, and customizable reporting. Simplify service outage tracking, enhance incident communication, and ensure a seamless user experience.",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import * as Card from "$lib/components/ui/card";
|
||||
import * as Select from "$lib/components/ui/select";
|
||||
import { storeSiteData, SortMonitor } from "$lib/clientTools.js";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import { dndzone } from "svelte-dnd-action";
|
||||
import { flip } from "svelte/animate";
|
||||
|
||||
@@ -239,6 +240,32 @@
|
||||
}
|
||||
let dropTargetStyle;
|
||||
let draggableMenu = false;
|
||||
|
||||
function testMonitor(i) {
|
||||
if (monitors[i].isTestRunning) {
|
||||
return;
|
||||
}
|
||||
monitors[i].isTestRunning = true;
|
||||
fetch(base + "/manage/app/api/", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
action: "testMonitor",
|
||||
data: { monitor_id: monitors[i].id }
|
||||
})
|
||||
})
|
||||
.then((resp) => resp.json())
|
||||
.then((data) => {
|
||||
monitors[i].isTestRunning = false;
|
||||
monitors[i].testResult = data;
|
||||
})
|
||||
.catch((error) => {
|
||||
monitors[i].isTestRunning = false;
|
||||
monitors[i].testResult = data;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if showAddMonitor}
|
||||
@@ -252,9 +279,7 @@
|
||||
/>
|
||||
{/if}
|
||||
{#if draggableMenu}
|
||||
<div
|
||||
class="moldal-container fixed left-0 top-0 z-50 h-screen w-full bg-card bg-opacity-30 backdrop-blur-sm"
|
||||
>
|
||||
<div class="moldal-container fixed left-0 top-0 z-50 h-screen w-full bg-card bg-opacity-30 backdrop-blur-sm">
|
||||
<div
|
||||
class="absolute left-1/2 top-1/2 h-fit w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-md border bg-background shadow-lg backdrop-blur-lg"
|
||||
>
|
||||
@@ -279,11 +304,7 @@
|
||||
<div animate:flip={{ duration: flipDurationMs }} class="mb-2 rounded-md bg-card p-2">
|
||||
<Grip class="mr-2 inline h-4 w-4" />
|
||||
{#if !!monitor.image}
|
||||
<img
|
||||
src={base + monitor.image}
|
||||
alt={monitor.name}
|
||||
class="mr-1 inline-block h-4 w-4"
|
||||
/>
|
||||
<img src={base + monitor.image} alt={monitor.name} class="mr-1 inline-block h-4 w-4" />
|
||||
{/if}
|
||||
{monitor.name}
|
||||
</div>
|
||||
@@ -312,12 +333,8 @@
|
||||
<Select.Content>
|
||||
<Select.Group>
|
||||
<Select.Label>Status</Select.Label>
|
||||
<Select.Item value="ACTIVE" label="ACTIVE" class="text-sm font-medium">
|
||||
ACTIVE
|
||||
</Select.Item>
|
||||
<Select.Item value="INACTIVE" label="INACTIVE" class="text-sm font-medium">
|
||||
INACTIVE
|
||||
</Select.Item>
|
||||
<Select.Item value="ACTIVE" label="ACTIVE" class="text-sm font-medium">ACTIVE</Select.Item>
|
||||
<Select.Item value="INACTIVE" label="INACTIVE" class="text-sm font-medium">INACTIVE</Select.Item>
|
||||
</Select.Group>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
@@ -370,7 +387,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
{#each monitors as monitor}
|
||||
{#each monitors as monitor, i}
|
||||
<Card.Root class="mb-4">
|
||||
<Card.Header class="relative">
|
||||
<Card.Title>
|
||||
@@ -382,8 +399,41 @@
|
||||
{#if !!monitor.description}
|
||||
<Card.Description>{@html monitor.description}</Card.Description>
|
||||
{/if}
|
||||
<div class="absolute right-2 top-0.5">
|
||||
<Button variant="secondary" class="h-8 w-8 p-2" on:click={() => openAlertMenu(monitor)}>
|
||||
<div class="absolute right-2 top-0.5 flex gap-x-1">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="secondary" class="h-8 p-2 text-xs" on:click={() => testMonitor(i)}>TEST</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content class="max-w-md">
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Label class="text-xs">Test Result</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<div class="px-2 text-xs">
|
||||
{#if monitor.isTestRunning}
|
||||
<div class="text-center">
|
||||
<Loader class="mx-auto inline h-4 w-4 animate-spin" />
|
||||
</div>
|
||||
{:else if !!monitor.testResult}
|
||||
{#if !!monitor.testResult.error}
|
||||
<div class="text-red-500">
|
||||
{monitor.testResult.error}
|
||||
</div>
|
||||
{:else if !!monitor.testResult.status && !!monitor.testResult.latency}
|
||||
<p class="text-muted-foreground">
|
||||
Status: <span class="text-api-{monitor.testResult.status.toLowerCase()}"
|
||||
>{monitor.testResult.status}</span
|
||||
><br /> Response Time: <span class="text-card-foreground">{monitor.testResult.latency}ms</span>
|
||||
</p>
|
||||
{:else}
|
||||
<div class="text-muted-foreground">No Test Result</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</DropdownMenu.Group>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
|
||||
<Button variant="secondary" class="h-8 w-8 p-2 " on:click={() => openAlertMenu(monitor)}>
|
||||
<Bell class="inline h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="secondary" class="h-8 w-8 p-2" href="#{monitor.tag}">
|
||||
@@ -424,9 +474,7 @@
|
||||
</div>
|
||||
|
||||
{#if shareMenusToggle}
|
||||
<div
|
||||
class="moldal-container fixed left-0 top-0 z-50 h-screen w-full bg-card bg-opacity-30 backdrop-blur-sm"
|
||||
>
|
||||
<div class="moldal-container fixed left-0 top-0 z-50 h-screen w-full bg-card bg-opacity-30 backdrop-blur-sm">
|
||||
<div
|
||||
class="absolute left-1/2 top-1/2 h-fit w-full max-w-2xl -translate-x-1/2 -translate-y-1/2 rounded-md border bg-background shadow-lg backdrop-blur-lg"
|
||||
>
|
||||
@@ -449,10 +497,7 @@
|
||||
<hr class="my-4" />
|
||||
{#each Object.entries(monitorTriggers) as [key, data]}
|
||||
<div class="flex justify-between">
|
||||
<h3
|
||||
class="font-semibold"
|
||||
style="color:{data.trigger_type == 'DOWN' ? colorDown : colorDegraded};"
|
||||
>
|
||||
<h3 class="font-semibold" style="color:{data.trigger_type == 'DOWN' ? colorDown : colorDegraded};">
|
||||
If Monitor {data.trigger_type}
|
||||
</h3>
|
||||
<div>
|
||||
@@ -479,12 +524,7 @@
|
||||
Failure Threshold
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
bind:value={data.failureThreshold}
|
||||
min="1"
|
||||
id="{key}failureThreshold"
|
||||
type="number"
|
||||
/>
|
||||
<Input bind:value={data.failureThreshold} min="1" id="{key}failureThreshold" type="number" />
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<div class="col-span-1">
|
||||
@@ -492,12 +532,7 @@
|
||||
Success Threshold
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
bind:value={data.successThreshold}
|
||||
min="1"
|
||||
id="{key}successThreshold"
|
||||
type="number"
|
||||
/>
|
||||
<Input bind:value={data.successThreshold} min="1" id="{key}successThreshold" type="number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
@@ -511,16 +546,11 @@
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="{key}createIncident">
|
||||
<Select.Value
|
||||
bind:value={data.createIncident}
|
||||
placeholder={data.createIncident}
|
||||
/>
|
||||
<Select.Value bind:value={data.createIncident} placeholder={data.createIncident} />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Group>
|
||||
<Select.Item value="YES" label="YES" class="text-sm font-medium">
|
||||
YES
|
||||
</Select.Item>
|
||||
<Select.Item value="YES" label="YES" class="text-sm font-medium">YES</Select.Item>
|
||||
<Select.Item value="NO" label="NO" class="text-sm font-medium">NO</Select.Item>
|
||||
</Select.Group>
|
||||
</Select.Content>
|
||||
@@ -544,12 +574,8 @@
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Group>
|
||||
<Select.Item value="critical" label="CRITICAL" class="text-sm font-medium">
|
||||
CRITICAL
|
||||
</Select.Item>
|
||||
<Select.Item value="warning" label="WARNING" class="text-sm font-medium">
|
||||
WARNING
|
||||
</Select.Item>
|
||||
<Select.Item value="critical" label="CRITICAL" class="text-sm font-medium">CRITICAL</Select.Item>
|
||||
<Select.Item value="warning" label="WARNING" class="text-sm font-medium">WARNING</Select.Item>
|
||||
</Select.Group>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
@@ -576,29 +602,13 @@
|
||||
}}
|
||||
/>
|
||||
{#if trigger.trigger_type == "webhook"}
|
||||
<img
|
||||
src={base + "/webhooks.svg"}
|
||||
alt={trigger.trigger_type}
|
||||
class="ml-2 inline-block h-4 w-4"
|
||||
/>
|
||||
<img src={base + "/webhooks.svg"} alt={trigger.trigger_type} class="ml-2 inline-block h-4 w-4" />
|
||||
{:else if trigger.trigger_type == "email"}
|
||||
<img
|
||||
src={base + "/email.png"}
|
||||
alt={trigger.trigger_type}
|
||||
class="ml-2 inline-block h-4 w-4"
|
||||
/>
|
||||
<img src={base + "/email.png"} alt={trigger.trigger_type} class="ml-2 inline-block h-4 w-4" />
|
||||
{:else if trigger.trigger_type == "slack"}
|
||||
<img
|
||||
src={base + "/slack.svg"}
|
||||
alt={trigger.trigger_type}
|
||||
class="ml-2 inline-block h-4 w-4"
|
||||
/>
|
||||
<img src={base + "/slack.svg"} alt={trigger.trigger_type} class="ml-2 inline-block h-4 w-4" />
|
||||
{:else if trigger.trigger_type == "discord"}
|
||||
<img
|
||||
src={base + "/discord.svg"}
|
||||
alt={trigger.trigger_type}
|
||||
class="ml-2 inline-block h-4 w-4"
|
||||
/>
|
||||
<img src={base + "/discord.svg"} alt={trigger.trigger_type} class="ml-2 inline-block h-4 w-4" />
|
||||
{/if}
|
||||
{trigger.name}
|
||||
</label>
|
||||
|
||||
+104
-83
@@ -10,90 +10,111 @@ const DOWN = "DOWN";
|
||||
const DEGRADED = "DEGRADED";
|
||||
const API_TIMEOUT = 10 * 1000; // 10 seconds
|
||||
const AnalyticsProviders = {
|
||||
GA: "https://unpkg.com/@analytics/google-analytics@1.0.7/dist/@analytics/google-analytics.min.js",
|
||||
AMPLITUDE: "https://unpkg.com/@analytics/amplitude@0.1.3/dist/@analytics/amplitude.min.js",
|
||||
MIXPANEL: "https://unpkg.com/@analytics/mixpanel@0.4.0/dist/@analytics/mixpanel.min.js"
|
||||
GA: "https://unpkg.com/@analytics/google-analytics@1.0.7/dist/@analytics/google-analytics.min.js",
|
||||
AMPLITUDE:
|
||||
"https://unpkg.com/@analytics/amplitude@0.1.3/dist/@analytics/amplitude.min.js",
|
||||
MIXPANEL:
|
||||
"https://unpkg.com/@analytics/mixpanel@0.4.0/dist/@analytics/mixpanel.min.js",
|
||||
};
|
||||
const AllRecordTypes = {
|
||||
A: 1,
|
||||
NS: 2,
|
||||
MD: 3,
|
||||
MF: 4,
|
||||
CNAME: 5,
|
||||
SOA: 6,
|
||||
MB: 7,
|
||||
MG: 8,
|
||||
MR: 9,
|
||||
NULL: 10,
|
||||
WKS: 11,
|
||||
PTR: 12,
|
||||
HINFO: 13,
|
||||
MINFO: 14,
|
||||
MX: 15,
|
||||
TXT: 16,
|
||||
RP: 17,
|
||||
AFSDB: 18,
|
||||
X25: 19,
|
||||
ISDN: 20,
|
||||
RT: 21,
|
||||
NSAP: 22,
|
||||
NSAP_PTR: 23,
|
||||
SIG: 24,
|
||||
KEY: 25,
|
||||
PX: 26,
|
||||
GPOS: 27,
|
||||
AAAA: 28,
|
||||
LOC: 29,
|
||||
NXT: 30,
|
||||
EID: 31,
|
||||
NIMLOC: 32,
|
||||
SRV: 33,
|
||||
ATMA: 34,
|
||||
NAPTR: 35,
|
||||
KX: 36,
|
||||
CERT: 37,
|
||||
A6: 38,
|
||||
DNAME: 39,
|
||||
SINK: 40,
|
||||
OPT: 41,
|
||||
APL: 42,
|
||||
DS: 43,
|
||||
SSHFP: 44,
|
||||
IPSECKEY: 45,
|
||||
RRSIG: 46,
|
||||
NSEC: 47,
|
||||
DNSKEY: 48,
|
||||
DHCID: 49,
|
||||
NSEC3: 50,
|
||||
NSEC3PARAM: 51,
|
||||
TLSA: 52,
|
||||
SMIMEA: 53,
|
||||
HIP: 55,
|
||||
NINFO: 56,
|
||||
RKEY: 57,
|
||||
TALINK: 58,
|
||||
CDS: 59,
|
||||
CDNSKEY: 60,
|
||||
OPENPGPKEY: 61,
|
||||
CSYNC: 62,
|
||||
SPF: 99,
|
||||
UINFO: 100,
|
||||
UID: 101,
|
||||
GID: 102,
|
||||
UNSPEC: 103,
|
||||
NID: 104,
|
||||
L32: 105,
|
||||
L64: 106,
|
||||
LP: 107,
|
||||
EUI48: 108,
|
||||
EUI64: 109,
|
||||
TKEY: 249,
|
||||
TSIG: 250,
|
||||
IXFR: 251,
|
||||
AXFR: 252,
|
||||
MAILB: 253,
|
||||
MAILA: 254,
|
||||
ANY: 255
|
||||
A: 1,
|
||||
NS: 2,
|
||||
MD: 3,
|
||||
MF: 4,
|
||||
CNAME: 5,
|
||||
SOA: 6,
|
||||
MB: 7,
|
||||
MG: 8,
|
||||
MR: 9,
|
||||
NULL: 10,
|
||||
WKS: 11,
|
||||
PTR: 12,
|
||||
HINFO: 13,
|
||||
MINFO: 14,
|
||||
MX: 15,
|
||||
TXT: 16,
|
||||
RP: 17,
|
||||
AFSDB: 18,
|
||||
X25: 19,
|
||||
ISDN: 20,
|
||||
RT: 21,
|
||||
NSAP: 22,
|
||||
NSAP_PTR: 23,
|
||||
SIG: 24,
|
||||
KEY: 25,
|
||||
PX: 26,
|
||||
GPOS: 27,
|
||||
AAAA: 28,
|
||||
LOC: 29,
|
||||
NXT: 30,
|
||||
EID: 31,
|
||||
NIMLOC: 32,
|
||||
SRV: 33,
|
||||
ATMA: 34,
|
||||
NAPTR: 35,
|
||||
KX: 36,
|
||||
CERT: 37,
|
||||
A6: 38,
|
||||
DNAME: 39,
|
||||
SINK: 40,
|
||||
OPT: 41,
|
||||
APL: 42,
|
||||
DS: 43,
|
||||
SSHFP: 44,
|
||||
IPSECKEY: 45,
|
||||
RRSIG: 46,
|
||||
NSEC: 47,
|
||||
DNSKEY: 48,
|
||||
DHCID: 49,
|
||||
NSEC3: 50,
|
||||
NSEC3PARAM: 51,
|
||||
TLSA: 52,
|
||||
SMIMEA: 53,
|
||||
HIP: 55,
|
||||
NINFO: 56,
|
||||
RKEY: 57,
|
||||
TALINK: 58,
|
||||
CDS: 59,
|
||||
CDNSKEY: 60,
|
||||
OPENPGPKEY: 61,
|
||||
CSYNC: 62,
|
||||
SPF: 99,
|
||||
UINFO: 100,
|
||||
UID: 101,
|
||||
GID: 102,
|
||||
UNSPEC: 103,
|
||||
NID: 104,
|
||||
L32: 105,
|
||||
L64: 106,
|
||||
LP: 107,
|
||||
EUI48: 108,
|
||||
EUI64: 109,
|
||||
TKEY: 249,
|
||||
TSIG: 250,
|
||||
IXFR: 251,
|
||||
AXFR: 252,
|
||||
MAILB: 253,
|
||||
MAILA: 254,
|
||||
ANY: 255,
|
||||
};
|
||||
// Export the constants
|
||||
export { MONITOR, UP, DOWN, SITE, DEGRADED, API_TIMEOUT, ENV, AnalyticsProviders, AllRecordTypes };
|
||||
const REALTIME = "realtime";
|
||||
const TIMEOUT = "timeout";
|
||||
const ERROR = "error";
|
||||
const MANUAL = "manual";
|
||||
|
||||
export {
|
||||
MONITOR,
|
||||
UP,
|
||||
DOWN,
|
||||
SITE,
|
||||
DEGRADED,
|
||||
API_TIMEOUT,
|
||||
ENV,
|
||||
AnalyticsProviders,
|
||||
AllRecordTypes,
|
||||
REALTIME,
|
||||
TIMEOUT,
|
||||
ERROR,
|
||||
MANUAL,
|
||||
};
|
||||
|
||||
+14
-352
@@ -2,12 +2,8 @@
|
||||
import axios from "axios";
|
||||
import { Ping, ExtractIPv6HostAndPort, TCP } from "./ping.js";
|
||||
import { UP, DOWN, DEGRADED } from "./constants.js";
|
||||
import {
|
||||
GetMinuteStartNowTimestampUTC,
|
||||
ReplaceAllOccurrences,
|
||||
GetRequiredSecrets,
|
||||
Wait,
|
||||
} from "./tool.js";
|
||||
import Service from "./services/service.js";
|
||||
import { GetMinuteStartNowTimestampUTC, ReplaceAllOccurrences, GetRequiredSecrets, Wait } from "./tool.js";
|
||||
|
||||
import alerting from "./alerting.js";
|
||||
import Queue from "queue";
|
||||
@@ -35,65 +31,10 @@ const apiQueue = new Queue({
|
||||
autostart: true, // Automatically start the queue (optional)
|
||||
});
|
||||
|
||||
const defaultEval = `(async function (statusCode, responseTime, responseData) {
|
||||
let statusCodeShort = Math.floor(statusCode/100);
|
||||
if(statusCode == 429 || (statusCodeShort >=2 && statusCodeShort <= 3)) {
|
||||
return {
|
||||
status: 'UP',
|
||||
latency: responseTime,
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 'DOWN',
|
||||
latency: responseTime,
|
||||
}
|
||||
})`;
|
||||
|
||||
const defaultPingEval = `(async function (responseDataBase64) {
|
||||
let arrayOfPings = JSON.parse(atob(responseDataBase64));
|
||||
let latencyTotal = arrayOfPings.reduce((acc, ping) => {
|
||||
return acc + ping.latency;
|
||||
}, 0);
|
||||
|
||||
let alive = arrayOfPings.reduce((acc, ping) => {
|
||||
return acc && ping.alive;
|
||||
}, true);
|
||||
|
||||
return {
|
||||
status: alive ? 'UP' : 'DOWN',
|
||||
latency: latencyTotal / arrayOfPings.length,
|
||||
}
|
||||
})`;
|
||||
const defaultTcpEval = `(async function (responseDataBase64) {
|
||||
let arrayOfPings = JSON.parse(atob(responseDataBase64));
|
||||
let latencyTotal = arrayOfPings.reduce((acc, ping) => {
|
||||
return acc + ping.latency;
|
||||
}, 0);
|
||||
|
||||
let alive = arrayOfPings.reduce((acc, ping) => {
|
||||
if (ping.status === "open") {
|
||||
return acc && true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
return {
|
||||
status: alive ? 'UP' : 'DOWN',
|
||||
latency: latencyTotal / arrayOfPings.length,
|
||||
}
|
||||
})`;
|
||||
|
||||
async function manualIncident(monitor) {
|
||||
let startTs = GetMinuteStartNowTimestampUTC();
|
||||
let incidentArr = await db.getIncidentsByMonitorTagRealtime(
|
||||
monitor.tag,
|
||||
startTs,
|
||||
);
|
||||
let maintenanceArr = await db.getMaintenanceByMonitorTagRealtime(
|
||||
monitor.tag,
|
||||
startTs,
|
||||
);
|
||||
let incidentArr = await db.getIncidentsByMonitorTagRealtime(monitor.tag, startTs);
|
||||
let maintenanceArr = await db.getMaintenanceByMonitorTagRealtime(monitor.tag, startTs);
|
||||
|
||||
let impactArr = incidentArr.concat(maintenanceArr);
|
||||
|
||||
@@ -105,11 +46,7 @@ async function manualIncident(monitor) {
|
||||
for (let i = 0; i < impactArr.length; i++) {
|
||||
const element = impactArr[i];
|
||||
|
||||
let autoIncidents = await db.getActiveAlertIncident(
|
||||
monitor.tag,
|
||||
element.monitor_impact,
|
||||
element.id,
|
||||
);
|
||||
let autoIncidents = await db.getActiveAlertIncident(monitor.tag, element.monitor_impact, element.id);
|
||||
|
||||
if (!!autoIncidents) {
|
||||
continue;
|
||||
@@ -160,275 +97,24 @@ const tcpCall = async (hosts, tcpEval, tag) => {
|
||||
type: REALTIME,
|
||||
};
|
||||
};
|
||||
const pingCall = async (hosts, pingEval, tag) => {
|
||||
if (hosts === undefined) {
|
||||
console.log(
|
||||
"Hosts is undefined. The ping monitor has changed in version 3.0.10. Please update your monitor with tag",
|
||||
tag,
|
||||
);
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: 0,
|
||||
type: ERROR,
|
||||
};
|
||||
}
|
||||
let arrayOfPings = [];
|
||||
for (let i = 0; i < hosts.length; i++) {
|
||||
const host = hosts[i];
|
||||
arrayOfPings.push(
|
||||
await Ping(host.type, host.host, host.timeout, host.count),
|
||||
);
|
||||
}
|
||||
let respBase64 = Buffer.from(JSON.stringify(arrayOfPings)).toString("base64");
|
||||
|
||||
let evalResp = undefined;
|
||||
|
||||
try {
|
||||
evalResp = await eval(pingEval + `("${respBase64}")`);
|
||||
} catch (error) {
|
||||
console.log(`Error in pingEval for ${tag}`, error.message);
|
||||
}
|
||||
//reduce to get the status
|
||||
return {
|
||||
status: evalResp.status,
|
||||
latency: evalResp.latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
};
|
||||
const apiCall = async (
|
||||
envSecrets,
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
timeout,
|
||||
monitorEval,
|
||||
tag,
|
||||
) => {
|
||||
let axiosHeaders = {};
|
||||
axiosHeaders["User-Agent"] = "Kener/3.0.2";
|
||||
axiosHeaders["Accept"] = "*/*";
|
||||
const start = Date.now();
|
||||
//replace all secrets
|
||||
for (let i = 0; i < envSecrets.length; i++) {
|
||||
const secret = envSecrets[i];
|
||||
if (!!body) {
|
||||
body = ReplaceAllOccurrences(body, secret.find, secret.replace);
|
||||
}
|
||||
if (!!url) {
|
||||
url = ReplaceAllOccurrences(url, secret.find, secret.replace);
|
||||
}
|
||||
if (!!headers) {
|
||||
headers = ReplaceAllOccurrences(headers, secret.find, secret.replace);
|
||||
}
|
||||
}
|
||||
if (!!headers) {
|
||||
headers = JSON.parse(headers);
|
||||
headers = headers.reduce((acc, header) => {
|
||||
acc[header.key] = header.value;
|
||||
return acc;
|
||||
}, {});
|
||||
axiosHeaders = { ...axiosHeaders, ...headers };
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: method,
|
||||
headers: headers,
|
||||
timeout: timeout,
|
||||
transformResponse: (r) => r,
|
||||
};
|
||||
if (!!headers) {
|
||||
options.headers = headers;
|
||||
}
|
||||
if (!!body) {
|
||||
options.data = body;
|
||||
}
|
||||
let statusCode = 500;
|
||||
let latency = 0;
|
||||
let resp = "";
|
||||
let timeoutError = false;
|
||||
try {
|
||||
let data = await axios(url, options);
|
||||
statusCode = data.status;
|
||||
resp = data.data;
|
||||
} catch (err) {
|
||||
console.log(`Error in apiCall ${tag}`, err.message);
|
||||
if (
|
||||
err.message.startsWith("timeout of") &&
|
||||
err.message.endsWith("exceeded")
|
||||
) {
|
||||
timeoutError = true;
|
||||
}
|
||||
if (err.response !== undefined && err.response.status !== undefined) {
|
||||
statusCode = err.response.status;
|
||||
}
|
||||
if (err.response !== undefined && err.response.data !== undefined) {
|
||||
resp = err.response.data;
|
||||
} else {
|
||||
resp = JSON.stringify(resp);
|
||||
}
|
||||
} finally {
|
||||
const end = Date.now();
|
||||
latency = end - start;
|
||||
if (resp === undefined || resp === null) {
|
||||
resp = "";
|
||||
}
|
||||
}
|
||||
resp = Buffer.from(resp).toString("base64");
|
||||
|
||||
let evalResp = undefined;
|
||||
|
||||
try {
|
||||
evalResp = await eval(
|
||||
monitorEval + `(${statusCode}, ${latency}, "${resp}")`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(`Error in monitorEval for ${tag}`, error.message);
|
||||
}
|
||||
|
||||
if (evalResp === undefined || evalResp === null) {
|
||||
evalResp = {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: ERROR,
|
||||
};
|
||||
} else if (
|
||||
evalResp.status === undefined ||
|
||||
evalResp.status === null ||
|
||||
[UP, DOWN, DEGRADED].indexOf(evalResp.status) === -1
|
||||
) {
|
||||
evalResp = {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: ERROR,
|
||||
};
|
||||
} else {
|
||||
evalResp.type = REALTIME;
|
||||
}
|
||||
|
||||
let toWrite = {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: ERROR,
|
||||
};
|
||||
if (evalResp.status !== undefined && evalResp.status !== null) {
|
||||
toWrite.status = evalResp.status;
|
||||
}
|
||||
if (evalResp.latency !== undefined && evalResp.latency !== null) {
|
||||
toWrite.latency = evalResp.latency;
|
||||
}
|
||||
if (evalResp.type !== undefined && evalResp.type !== null) {
|
||||
toWrite.type = evalResp.type;
|
||||
}
|
||||
if (timeoutError) {
|
||||
toWrite.type = TIMEOUT;
|
||||
}
|
||||
|
||||
return toWrite;
|
||||
};
|
||||
|
||||
async function dsnChecker(dnsResolver, host, recordType, matchType, values) {
|
||||
try {
|
||||
let queryStartTime = Date.now();
|
||||
let dnsRes = await dnsResolver.getRecord(host, recordType);
|
||||
let latency = Date.now() - queryStartTime;
|
||||
|
||||
if (dnsRes[recordType] === undefined) {
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
let data = dnsRes[recordType];
|
||||
let dnsData = data.map((d) => d.data);
|
||||
if (matchType === "ALL") {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (dnsData.indexOf(values[i].trim()) === -1) {
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: UP,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
} else if (matchType === "ANY") {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (dnsData.indexOf(values[i].trim()) !== -1) {
|
||||
return {
|
||||
status: UP,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error in dnsChecker", error);
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: 0,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const Minuter = async (monitor) => {
|
||||
let realTimeData = {};
|
||||
let manualData = {};
|
||||
|
||||
const startOfMinute = GetMinuteStartNowTimestampUTC();
|
||||
const serviceClient = new Service(monitor);
|
||||
if (monitor.monitor_type === "API") {
|
||||
let envSecrets = GetRequiredSecrets(
|
||||
`${monitor.type_data.url} ${monitor.type_data.body} ${JSON.stringify(monitor.type_data.headers)}`,
|
||||
);
|
||||
|
||||
if (monitor.type_data.eval === "") {
|
||||
monitor.type_data.eval = defaultEval;
|
||||
}
|
||||
|
||||
let apiResponse = await apiCall(
|
||||
envSecrets,
|
||||
monitor.type_data.url,
|
||||
monitor.type_data.method,
|
||||
JSON.stringify(monitor.type_data.headers),
|
||||
monitor.type_data.body,
|
||||
monitor.type_data.timeout,
|
||||
monitor.type_data.eval,
|
||||
monitor.tag,
|
||||
);
|
||||
let apiResponse = await serviceClient.execute();
|
||||
|
||||
realTimeData[startOfMinute] = apiResponse;
|
||||
|
||||
//if timeout, retry after 500ms
|
||||
if (apiResponse.type === TIMEOUT) {
|
||||
apiQueue.push(async (cb) => {
|
||||
await Wait(500); //wait for 500ms
|
||||
console.log(
|
||||
"Retrying api call for " +
|
||||
monitor.name +
|
||||
" at " +
|
||||
startOfMinute +
|
||||
" due to timeout",
|
||||
);
|
||||
apiCall(
|
||||
envSecrets,
|
||||
monitor.type_data.url,
|
||||
monitor.type_data.method,
|
||||
JSON.stringify(monitor.type_data.headers),
|
||||
monitor.type_data.body,
|
||||
monitor.type_data.timeout,
|
||||
monitor.type_data.eval,
|
||||
monitor.tag,
|
||||
).then(async (data) => {
|
||||
console.log("Retrying api call for " + monitor.name + " at " + startOfMinute + " due to timeout");
|
||||
serviceClient.execute().then(async (data) => {
|
||||
await db.insertMonitoringData({
|
||||
monitor_tag: monitor.tag,
|
||||
timestamp: startOfMinute,
|
||||
@@ -441,35 +127,11 @@ const Minuter = async (monitor) => {
|
||||
});
|
||||
}
|
||||
} else if (monitor.monitor_type === "PING") {
|
||||
if (!!!monitor.type_data.pingEval) {
|
||||
monitor.type_data.pingEval = defaultPingEval;
|
||||
}
|
||||
let pingResponse = await pingCall(
|
||||
monitor.type_data.hosts,
|
||||
monitor.type_data.pingEval,
|
||||
monitor.tag,
|
||||
);
|
||||
realTimeData[startOfMinute] = pingResponse;
|
||||
realTimeData[startOfMinute] = await serviceClient.execute();
|
||||
} else if (monitor.monitor_type === "TCP") {
|
||||
if (!!!monitor.type_data.tcpEval) {
|
||||
monitor.type_data.tcpEval = defaultTcpEval;
|
||||
}
|
||||
let pingResponse = await tcpCall(
|
||||
monitor.type_data.hosts,
|
||||
monitor.type_data.tcpEval,
|
||||
monitor.tag,
|
||||
);
|
||||
realTimeData[startOfMinute] = pingResponse;
|
||||
realTimeData[startOfMinute] = await serviceClient.execute();
|
||||
} else if (monitor.monitor_type === "DNS") {
|
||||
const dnsResolver = new DNSResolver(monitor.type_data.nameServer);
|
||||
let dnsResponse = await dsnChecker(
|
||||
dnsResolver,
|
||||
monitor.type_data.host,
|
||||
monitor.type_data.lookupRecord,
|
||||
monitor.type_data.matchType,
|
||||
monitor.type_data.values,
|
||||
);
|
||||
realTimeData[startOfMinute] = dnsResponse;
|
||||
realTimeData[startOfMinute] = await serviceClient.execute();
|
||||
}
|
||||
|
||||
manualData = await manualIncident(monitor);
|
||||
|
||||
+642
-649
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
// @ts-nocheck
|
||||
import axios from "axios";
|
||||
import { GetRequiredSecrets, ReplaceAllOccurrences } from "../tool.js";
|
||||
import { UP, DOWN, DEGRADED, REALTIME, TIMEOUT, ERROR, MANUAL } from "../constants.js";
|
||||
|
||||
const defaultEval = `(async function (statusCode, responseTime, responseData) {
|
||||
let statusCodeShort = Math.floor(statusCode/100);
|
||||
if(statusCode == 429 || (statusCodeShort >=2 && statusCodeShort <= 3)) {
|
||||
return {
|
||||
status: 'UP',
|
||||
latency: responseTime,
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 'DOWN',
|
||||
latency: responseTime,
|
||||
}
|
||||
})`;
|
||||
|
||||
class ApiCall {
|
||||
monitor;
|
||||
envSecrets;
|
||||
|
||||
constructor(monitor) {
|
||||
this.monitor = monitor;
|
||||
this.envSecrets = GetRequiredSecrets(
|
||||
`${monitor.type_data.url} ${monitor.type_data.body} ${JSON.stringify(monitor.type_data.headers)}`,
|
||||
);
|
||||
}
|
||||
|
||||
async execute() {
|
||||
let axiosHeaders = {};
|
||||
axiosHeaders["User-Agent"] = "Kener/" + "3.1.0";
|
||||
axiosHeaders["Accept"] = "*/*";
|
||||
|
||||
let body = this.monitor.type_data.body;
|
||||
let url = this.monitor.type_data.url;
|
||||
|
||||
//headers to string
|
||||
let headers = "";
|
||||
if (!!this.monitor.type_data.headers) {
|
||||
headers = JSON.stringify(this.monitor.type_data.headers);
|
||||
}
|
||||
|
||||
let method = this.monitor.type_data.method;
|
||||
let timeout = this.monitor.type_data.timeout || 5000;
|
||||
let tag = this.monitor.tag;
|
||||
let monitorEval = !!this.monitor.type_data.monitorEval ? this.monitor.type_data.monitorEval : defaultEval;
|
||||
|
||||
for (let i = 0; i < this.envSecrets.length; i++) {
|
||||
const secret = this.envSecrets[i];
|
||||
if (!!body) {
|
||||
body = ReplaceAllOccurrences(body, secret.find, secret.replace);
|
||||
}
|
||||
if (!!url) {
|
||||
url = ReplaceAllOccurrences(url, secret.find, secret.replace);
|
||||
}
|
||||
if (!!headers) {
|
||||
headers = ReplaceAllOccurrences(headers, secret.find, secret.replace);
|
||||
}
|
||||
}
|
||||
|
||||
if (!!headers) {
|
||||
try {
|
||||
headers = JSON.parse(headers);
|
||||
headers = headers.reduce((acc, header) => {
|
||||
acc[header.key] = header.value;
|
||||
return acc;
|
||||
}, {});
|
||||
axiosHeaders = { ...axiosHeaders, ...headers };
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: method,
|
||||
headers: axiosHeaders,
|
||||
timeout: timeout,
|
||||
transformResponse: (r) => r,
|
||||
};
|
||||
|
||||
if (!!body) {
|
||||
options.data = body;
|
||||
}
|
||||
let statusCode = 500;
|
||||
let latency = 0;
|
||||
let resp = "";
|
||||
let timeoutError = false;
|
||||
const start = Date.now();
|
||||
try {
|
||||
let data = await axios(url, options);
|
||||
statusCode = data.status;
|
||||
resp = data.data;
|
||||
} catch (err) {
|
||||
console.log(`Error in apiCall ${tag}`, err.message);
|
||||
if (err.message.startsWith("timeout of") && err.message.endsWith("exceeded")) {
|
||||
timeoutError = true;
|
||||
}
|
||||
if (err.response !== undefined && err.response.status !== undefined) {
|
||||
statusCode = err.response.status;
|
||||
}
|
||||
if (err.response !== undefined && err.response.data !== undefined) {
|
||||
resp = err.response.data;
|
||||
} else {
|
||||
resp = JSON.stringify(resp);
|
||||
}
|
||||
} finally {
|
||||
const end = Date.now();
|
||||
latency = end - start;
|
||||
if (resp === undefined || resp === null) {
|
||||
resp = "";
|
||||
}
|
||||
}
|
||||
|
||||
resp = Buffer.from(resp).toString("base64");
|
||||
|
||||
let evalResp = undefined;
|
||||
|
||||
try {
|
||||
evalResp = await eval(monitorEval + `(${statusCode}, ${latency}, "${resp}")`);
|
||||
} catch (error) {
|
||||
console.log(`Error in monitorEval for ${tag}`, error.message);
|
||||
}
|
||||
|
||||
if (evalResp === undefined || evalResp === null) {
|
||||
evalResp = {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: ERROR,
|
||||
};
|
||||
} else if (
|
||||
evalResp.status === undefined ||
|
||||
evalResp.status === null ||
|
||||
[UP, DOWN, DEGRADED].indexOf(evalResp.status) === -1
|
||||
) {
|
||||
evalResp = {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: ERROR,
|
||||
};
|
||||
} else {
|
||||
evalResp.type = REALTIME;
|
||||
}
|
||||
|
||||
let toWrite = {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: ERROR,
|
||||
};
|
||||
if (evalResp.status !== undefined && evalResp.status !== null) {
|
||||
toWrite.status = evalResp.status;
|
||||
}
|
||||
if (evalResp.latency !== undefined && evalResp.latency !== null) {
|
||||
toWrite.latency = evalResp.latency;
|
||||
}
|
||||
if (evalResp.type !== undefined && evalResp.type !== null) {
|
||||
toWrite.type = evalResp.type;
|
||||
}
|
||||
if (timeoutError) {
|
||||
toWrite.type = TIMEOUT;
|
||||
}
|
||||
|
||||
return toWrite;
|
||||
}
|
||||
}
|
||||
|
||||
export default ApiCall;
|
||||
@@ -0,0 +1,76 @@
|
||||
// @ts-nocheck
|
||||
import axios from "axios";
|
||||
import DNSResolver from "../dns.js";
|
||||
import { UP, DOWN, DEGRADED, REALTIME, TIMEOUT, ERROR, MANUAL } from "../constants.js";
|
||||
|
||||
class DnsCall {
|
||||
monitor;
|
||||
|
||||
constructor(monitor) {
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
async execute() {
|
||||
const dnsResolver = new DNSResolver(this.monitor.type_data.nameServer);
|
||||
let host = this.monitor.type_data.host;
|
||||
let recordType = this.monitor.type_data.lookupRecord;
|
||||
let matchType = this.monitor.type_data.matchType;
|
||||
let values = this.monitor.type_data.values;
|
||||
|
||||
try {
|
||||
let queryStartTime = Date.now();
|
||||
let dnsRes = await dnsResolver.getRecord(host, recordType);
|
||||
let latency = Date.now() - queryStartTime;
|
||||
|
||||
if (dnsRes[recordType] === undefined) {
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
let data = dnsRes[recordType];
|
||||
let dnsData = data.map((d) => d.data);
|
||||
if (matchType === "ALL") {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (dnsData.indexOf(values[i].trim()) === -1) {
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: UP,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
} else if (matchType === "ANY") {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (dnsData.indexOf(values[i].trim()) !== -1) {
|
||||
return {
|
||||
status: UP,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error in dnsChecker", error);
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: 0,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DnsCall;
|
||||
@@ -0,0 +1,81 @@
|
||||
// @ts-nocheck
|
||||
import axios from "axios";
|
||||
import { Ping } from "../ping.js";
|
||||
import {
|
||||
UP,
|
||||
DOWN,
|
||||
DEGRADED,
|
||||
REALTIME,
|
||||
TIMEOUT,
|
||||
ERROR,
|
||||
MANUAL,
|
||||
} from "../constants.js";
|
||||
|
||||
const defaultPingEval = `(async function (responseDataBase64) {
|
||||
let arrayOfPings = JSON.parse(atob(responseDataBase64));
|
||||
let latencyTotal = arrayOfPings.reduce((acc, ping) => {
|
||||
return acc + ping.latency;
|
||||
}, 0);
|
||||
|
||||
let alive = arrayOfPings.reduce((acc, ping) => {
|
||||
return acc && ping.alive;
|
||||
}, true);
|
||||
|
||||
return {
|
||||
status: alive ? 'UP' : 'DOWN',
|
||||
latency: latencyTotal / arrayOfPings.length,
|
||||
}
|
||||
})`;
|
||||
|
||||
class PingCall {
|
||||
monitor;
|
||||
|
||||
constructor(monitor) {
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
async execute() {
|
||||
let hosts = this.monitor.type_data.hosts;
|
||||
let pingEval = !!this.monitor.type_data.pingEval
|
||||
? this.monitor.type_data.pingEval
|
||||
: defaultPingEval;
|
||||
let tag = this.monitor.tag;
|
||||
if (hosts === undefined) {
|
||||
console.log(
|
||||
"Hosts is undefined. The ping monitor has changed in version 3.0.10. Please update your monitor with tag",
|
||||
tag,
|
||||
);
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: 0,
|
||||
type: ERROR,
|
||||
};
|
||||
}
|
||||
let arrayOfPings = [];
|
||||
for (let i = 0; i < hosts.length; i++) {
|
||||
const host = hosts[i];
|
||||
arrayOfPings.push(
|
||||
await Ping(host.type, host.host, host.timeout, host.count),
|
||||
);
|
||||
}
|
||||
let respBase64 = Buffer.from(JSON.stringify(arrayOfPings)).toString(
|
||||
"base64",
|
||||
);
|
||||
|
||||
let evalResp = undefined;
|
||||
|
||||
try {
|
||||
evalResp = await eval(pingEval + `("${respBase64}")`);
|
||||
} catch (error) {
|
||||
console.log(`Error in pingEval for ${tag}`, error.message);
|
||||
}
|
||||
//reduce to get the status
|
||||
return {
|
||||
status: evalResp.status,
|
||||
latency: evalResp.latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default PingCall;
|
||||
@@ -0,0 +1,31 @@
|
||||
// @ts-nocheck
|
||||
import ApiCall from "./apiCall.js";
|
||||
import PingCall from "./pingCall.js";
|
||||
import TcpCall from "./tcpCall.js";
|
||||
import DnsCall from "./dnsCall.js";
|
||||
|
||||
class Service {
|
||||
service;
|
||||
|
||||
constructor(monitor) {
|
||||
if (monitor.monitor_type === "API") {
|
||||
this.service = new ApiCall(monitor);
|
||||
} else if (monitor.monitor_type === "PING") {
|
||||
this.service = new PingCall(monitor);
|
||||
} else if (monitor.monitor_type === "TCP") {
|
||||
this.service = new TcpCall(monitor);
|
||||
} else if (monitor.monitor_type === "DNS") {
|
||||
this.service = new DnsCall(monitor);
|
||||
} else if (monitor.monitor_type === "NONE") {
|
||||
this.service = null;
|
||||
} else {
|
||||
console.log("Invalid monitor.monitor_type ", monitor.monitor_type);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
async execute() {
|
||||
return await this.service.execute();
|
||||
}
|
||||
}
|
||||
|
||||
export default Service;
|
||||
@@ -0,0 +1,72 @@
|
||||
// @ts-nocheck
|
||||
import axios from "axios";
|
||||
import { TCP } from "../ping.js";
|
||||
import { UP, DOWN, DEGRADED, REALTIME, TIMEOUT, ERROR, MANUAL } from "../constants.js";
|
||||
|
||||
const defaultTcpEval = `(async function (responseDataBase64) {
|
||||
let arrayOfPings = JSON.parse(atob(responseDataBase64));
|
||||
let latencyTotal = arrayOfPings.reduce((acc, ping) => {
|
||||
return acc + ping.latency;
|
||||
}, 0);
|
||||
|
||||
let alive = arrayOfPings.reduce((acc, ping) => {
|
||||
if (ping.status === "open") {
|
||||
return acc && true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
return {
|
||||
status: alive ? 'UP' : 'DOWN',
|
||||
latency: latencyTotal / arrayOfPings.length,
|
||||
}
|
||||
})`;
|
||||
|
||||
class TcpCall {
|
||||
monitor;
|
||||
|
||||
constructor(monitor) {
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
async execute() {
|
||||
let hosts = this.monitor.type_data.hosts;
|
||||
let tcpEval = !!this.monitor.type_data.tcpEval ? this.monitor.type_data.tcpEval : defaultTcpEval;
|
||||
let tag = this.monitor.tag;
|
||||
|
||||
if (hosts === undefined) {
|
||||
console.log(
|
||||
"Hosts is undefined. The ping monitor has changed in version 3.0.10. Please update your monitor with tag",
|
||||
tag,
|
||||
);
|
||||
return {
|
||||
status: DOWN,
|
||||
latency: 0,
|
||||
type: ERROR,
|
||||
};
|
||||
}
|
||||
let arrayOfPings = [];
|
||||
for (let i = 0; i < hosts.length; i++) {
|
||||
const host = hosts[i];
|
||||
arrayOfPings.push(await TCP(host.type, host.host, host.port, host.timeout));
|
||||
}
|
||||
let respBase64 = Buffer.from(JSON.stringify(arrayOfPings)).toString("base64");
|
||||
|
||||
let evalResp = undefined;
|
||||
|
||||
try {
|
||||
evalResp = await eval(tcpEval + `("${respBase64}")`);
|
||||
} catch (error) {
|
||||
console.log(`Error in tcpEval for ${tag}`, error.message);
|
||||
}
|
||||
//reduce to get the status
|
||||
return {
|
||||
status: evalResp.status,
|
||||
latency: evalResp.latency,
|
||||
type: REALTIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default TcpCall;
|
||||
@@ -1,34 +1,34 @@
|
||||
<script>
|
||||
import "../../app.postcss"
|
||||
import "../../kener.css"
|
||||
import "../../docs.css"
|
||||
import { Button } from "$lib/components/ui/button"
|
||||
import Sun from "lucide-svelte/icons/sun"
|
||||
import Moon from "lucide-svelte/icons/moon"
|
||||
import { onMount } from "svelte"
|
||||
import { base } from "$app/paths"
|
||||
let defaultTheme = "light"
|
||||
export let data
|
||||
let siteStructure = data.siteStructure
|
||||
let sidebar = siteStructure.sidebar
|
||||
let docFilePath = data.docFilePath
|
||||
let tableOfContents = []
|
||||
import "../../app.postcss";
|
||||
import "../../kener.css";
|
||||
import "../../docs.css";
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import Sun from "lucide-svelte/icons/sun";
|
||||
import Moon from "lucide-svelte/icons/moon";
|
||||
import { onMount } from "svelte";
|
||||
import { base } from "$app/paths";
|
||||
let defaultTheme = "light";
|
||||
export let data;
|
||||
let siteStructure = data.siteStructure;
|
||||
let sidebar = siteStructure.sidebar;
|
||||
let docFilePath = data.docFilePath;
|
||||
let tableOfContents = [];
|
||||
|
||||
function setTheme() {
|
||||
document.documentElement.classList.add("dark")
|
||||
document.documentElement.classList.add("dark");
|
||||
}
|
||||
|
||||
function activateSidebar(selectedDoc) {
|
||||
for (let i = 0; i < sidebar.length; i++) {
|
||||
const item = sidebar[i]
|
||||
const item = sidebar[i];
|
||||
for (let j = 0; j < item.children.length; j++) {
|
||||
const subItem = item.children[j]
|
||||
const subItem = item.children[j];
|
||||
if (subItem.file == selectedDoc) {
|
||||
subItem.active = true
|
||||
sidebar[i].children[j].active = true
|
||||
subItem.active = true;
|
||||
sidebar[i].children[j].active = true;
|
||||
} else {
|
||||
subItem.active = false
|
||||
sidebar[i].children[j].active = false
|
||||
subItem.active = false;
|
||||
sidebar[i].children[j].active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,18 +36,18 @@
|
||||
|
||||
function pageChange(e) {
|
||||
if (e.detail.docFilePath) {
|
||||
activateSidebar(e.detail.docFilePath)
|
||||
activateSidebar(e.detail.docFilePath);
|
||||
}
|
||||
}
|
||||
function updateTableOfContents(e) {
|
||||
if (e.detail.rightbar) {
|
||||
tableOfContents = e.detail.rightbar
|
||||
tableOfContents = e.detail.rightbar;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
setTheme()
|
||||
})
|
||||
setTheme();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window on:pagechange={pageChange} on:rightbar={updateTableOfContents} />
|
||||
@@ -57,13 +57,13 @@
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Q3MLRXCBFT"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || []
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments)
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag("js", new Date())
|
||||
gtag("js", new Date());
|
||||
|
||||
gtag("config", "G-Q3MLRXCBFT")
|
||||
gtag("config", "G-Q3MLRXCBFT");
|
||||
</script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
@@ -71,9 +71,7 @@
|
||||
/>
|
||||
|
||||
<!-- Highlight.js JS -->
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"
|
||||
></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||
</svelte:head>
|
||||
<div class="dark">
|
||||
<nav class="z-2 fixed left-0 right-0 top-0 z-30 h-16 bg-card">
|
||||
@@ -85,7 +83,7 @@
|
||||
<!-- Document Icon - Replace with your own logo -->
|
||||
<img src="https://kener.ing/logo.png" class="h-8 w-8" alt="" />
|
||||
<span class="text-xl font-medium">Kener Documentation</span>
|
||||
<span class="me-2 rounded border px-2.5 py-0.5 text-xs font-medium"> 3.0.12 </span>
|
||||
<span class="me-2 rounded border px-2.5 py-0.5 text-xs font-medium"> 3.1.0 </span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -99,12 +97,8 @@
|
||||
/>
|
||||
</a>
|
||||
<a href="/api-reference" class="text-sm font-medium"> API Reference </a>
|
||||
<a href="https://github.com/rajnandan1/kener/issues" class="text-sm font-medium">
|
||||
Report Issue
|
||||
</a>
|
||||
<a href="https://github.com/sponsors/rajnandan1" class="text-sm font-medium">
|
||||
Sponsor
|
||||
</a>
|
||||
<a href="https://github.com/rajnandan1/kener/issues" class="text-sm font-medium"> Report Issue </a>
|
||||
<a href="https://github.com/sponsors/rajnandan1" class="text-sm font-medium"> Sponsor </a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -112,12 +106,7 @@
|
||||
<div class="md:hidden">
|
||||
<button type="button" class="hover: text-muted-foreground">
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -163,9 +152,7 @@
|
||||
</div>
|
||||
</main>
|
||||
{#if tableOfContents.length > 0}
|
||||
<div
|
||||
class="blurry-bg fixed bottom-0 right-0 top-16 hidden w-64 overflow-y-auto px-6 py-10 lg:block"
|
||||
>
|
||||
<div class="blurry-bg fixed bottom-0 right-0 top-16 hidden w-64 overflow-y-auto px-6 py-10 lg:block">
|
||||
<h4 class="mb-3 text-sm font-semibold uppercase tracking-wider">On this page</h4>
|
||||
<nav class="space-y-2">
|
||||
{#each tableOfContents as item}
|
||||
|
||||
@@ -2,154 +2,147 @@
|
||||
// @ts-ignore
|
||||
import { json } from "@sveltejs/kit";
|
||||
import notification from "$lib/server/notification/notif.js";
|
||||
import Service from "$lib/server/services/service.js";
|
||||
import {
|
||||
CreateUpdateMonitor,
|
||||
InsertKeyValue,
|
||||
GetMonitors,
|
||||
CreateUpdateTrigger,
|
||||
GetAllTriggers,
|
||||
UpdateTriggerData,
|
||||
GetAllAlertsPaginated,
|
||||
GetAllAPIKeys,
|
||||
GetAllSiteData,
|
||||
CreateNewAPIKey,
|
||||
UpdateApiKeyStatus,
|
||||
VerifyToken,
|
||||
GetIncidentsDashboard,
|
||||
CreateIncident,
|
||||
AddIncidentMonitor,
|
||||
RemoveIncidentMonitor,
|
||||
GetIncidentActiveComments,
|
||||
UpdateCommentStatusByID,
|
||||
AddIncidentComment,
|
||||
UpdateCommentByID,
|
||||
UpdateIncident,
|
||||
GetTriggerByID
|
||||
CreateUpdateMonitor,
|
||||
InsertKeyValue,
|
||||
GetMonitors,
|
||||
CreateUpdateTrigger,
|
||||
GetAllTriggers,
|
||||
UpdateTriggerData,
|
||||
GetAllAlertsPaginated,
|
||||
GetMonitorsParsed,
|
||||
GetAllAPIKeys,
|
||||
GetAllSiteData,
|
||||
CreateNewAPIKey,
|
||||
UpdateApiKeyStatus,
|
||||
VerifyToken,
|
||||
GetIncidentsDashboard,
|
||||
CreateIncident,
|
||||
AddIncidentMonitor,
|
||||
RemoveIncidentMonitor,
|
||||
GetIncidentActiveComments,
|
||||
UpdateCommentStatusByID,
|
||||
AddIncidentComment,
|
||||
UpdateCommentByID,
|
||||
UpdateIncident,
|
||||
GetTriggerByID,
|
||||
} from "$lib/server/controllers/controller.js";
|
||||
|
||||
export async function POST({ request, cookies }) {
|
||||
const payload = await request.json();
|
||||
let action = payload.action;
|
||||
let data = payload.data || {};
|
||||
let resp = {};
|
||||
const payload = await request.json();
|
||||
let action = payload.action;
|
||||
let data = payload.data || {};
|
||||
let resp = {};
|
||||
|
||||
let tokenData = cookies.get("kener-user");
|
||||
let tokenData = cookies.get("kener-user");
|
||||
|
||||
if (!!!tokenData) {
|
||||
return json(
|
||||
{
|
||||
error: "Unauthorized"
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
if (!!!tokenData) {
|
||||
return json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
let tokenUser = await VerifyToken(tokenData);
|
||||
if (!!!tokenUser) {
|
||||
//redirect to signin page if user is not authenticated
|
||||
return json(
|
||||
{
|
||||
error: "Unauthorized"
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
let tokenUser = await VerifyToken(tokenData);
|
||||
if (!!!tokenUser) {
|
||||
//redirect to signin page if user is not authenticated
|
||||
return json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (action === "storeSiteData") {
|
||||
resp = await storeSiteData(data);
|
||||
} else if (action == "storeMonitorData") {
|
||||
resp = await CreateUpdateMonitor(data);
|
||||
} else if (action == "getMonitors") {
|
||||
resp = await GetMonitors(data);
|
||||
} else if (action == "createUpdateTrigger") {
|
||||
resp = await CreateUpdateTrigger(data);
|
||||
} else if (action == "getTriggers") {
|
||||
resp = await GetAllTriggers(data);
|
||||
} else if (action == "updateMonitorTriggers") {
|
||||
resp = await UpdateTriggerData(data);
|
||||
} else if (action == "getAllAlertsPaginated") {
|
||||
resp = await GetAllAlertsPaginated(data);
|
||||
} else if (action == "getAPIKeys") {
|
||||
resp = await GetAllAPIKeys();
|
||||
} else if (action == "createNewApiKey") {
|
||||
resp = await CreateNewAPIKey(data);
|
||||
} else if (action == "updateApiKeyStatus") {
|
||||
resp = await UpdateApiKeyStatus(data);
|
||||
} else if (action == "getIncidents") {
|
||||
resp = await GetIncidentsDashboard(data);
|
||||
} else if (action == "createIncident") {
|
||||
resp = await CreateIncident(data);
|
||||
} else if (action == "updateIncident") {
|
||||
resp = await UpdateIncident(data.id, data);
|
||||
} else if (action == "addMonitor") {
|
||||
resp = await AddIncidentMonitor(
|
||||
data.incident_id,
|
||||
data.monitor_tag,
|
||||
data.monitor_impact
|
||||
);
|
||||
} else if (action == "removeMonitor") {
|
||||
resp = await RemoveIncidentMonitor(data.incident_id, data.monitor_tag);
|
||||
} else if (action == "getComments") {
|
||||
resp = await GetIncidentActiveComments(data.incident_id);
|
||||
} else if (action == "addComment") {
|
||||
resp = await AddIncidentComment(
|
||||
data.incident_id,
|
||||
data.comment,
|
||||
data.state,
|
||||
data.commented_at
|
||||
);
|
||||
} else if (action == "deleteComment") {
|
||||
resp = await UpdateCommentStatusByID(data.incident_id, data.comment_id, "INACTIVE");
|
||||
} else if (action == "updateComment") {
|
||||
resp = await UpdateCommentByID(
|
||||
data.incident_id,
|
||||
data.comment_id,
|
||||
data.comment,
|
||||
data.state,
|
||||
data.commented_at
|
||||
);
|
||||
} else if (action == "testTrigger") {
|
||||
let trigger = await GetTriggerByID(data.trigger_id);
|
||||
let siteData = await GetAllSiteData();
|
||||
const notificationClient = new notification(trigger, siteData, {});
|
||||
const testObj = {
|
||||
id: "test",
|
||||
alert_name:
|
||||
"Test Alert " + (Math.floor(Math.random() * 100) % 2) == 0
|
||||
? "DOWN"
|
||||
: "DEGRADED",
|
||||
severity: Math.floor(Math.random() * 100) % 2 == 0 ? "critical" : "warning",
|
||||
status: Math.floor(Math.random() * 100) % 2 == 0 ? "TRIGGERED" : "RESOLVED",
|
||||
source: "Kener",
|
||||
timestamp: new Date().toISOString(),
|
||||
description: "Monitor has failed",
|
||||
details: {
|
||||
metric: "Test",
|
||||
current_value: Math.floor(Math.random() * 100),
|
||||
threshold: Math.floor(Math.random() * 100)
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
text: "View Monitor",
|
||||
url: siteData.siteURL + "/monitor-test"
|
||||
}
|
||||
]
|
||||
};
|
||||
resp = await notificationClient.send(testObj);
|
||||
}
|
||||
} catch (error) {
|
||||
resp = { error: error.message };
|
||||
return json(resp, { status: 500 });
|
||||
}
|
||||
return json(resp, { status: 200 });
|
||||
try {
|
||||
if (action === "storeSiteData") {
|
||||
resp = await storeSiteData(data);
|
||||
} else if (action == "storeMonitorData") {
|
||||
resp = await CreateUpdateMonitor(data);
|
||||
} else if (action == "getMonitors") {
|
||||
resp = await GetMonitors(data);
|
||||
} else if (action == "createUpdateTrigger") {
|
||||
resp = await CreateUpdateTrigger(data);
|
||||
} else if (action == "getTriggers") {
|
||||
resp = await GetAllTriggers(data);
|
||||
} else if (action == "updateMonitorTriggers") {
|
||||
resp = await UpdateTriggerData(data);
|
||||
} else if (action == "getAllAlertsPaginated") {
|
||||
resp = await GetAllAlertsPaginated(data);
|
||||
} else if (action == "getAPIKeys") {
|
||||
resp = await GetAllAPIKeys();
|
||||
} else if (action == "createNewApiKey") {
|
||||
resp = await CreateNewAPIKey(data);
|
||||
} else if (action == "updateApiKeyStatus") {
|
||||
resp = await UpdateApiKeyStatus(data);
|
||||
} else if (action == "getIncidents") {
|
||||
resp = await GetIncidentsDashboard(data);
|
||||
} else if (action == "createIncident") {
|
||||
resp = await CreateIncident(data);
|
||||
} else if (action == "updateIncident") {
|
||||
resp = await UpdateIncident(data.id, data);
|
||||
} else if (action == "addMonitor") {
|
||||
resp = await AddIncidentMonitor(data.incident_id, data.monitor_tag, data.monitor_impact);
|
||||
} else if (action == "removeMonitor") {
|
||||
resp = await RemoveIncidentMonitor(data.incident_id, data.monitor_tag);
|
||||
} else if (action == "getComments") {
|
||||
resp = await GetIncidentActiveComments(data.incident_id);
|
||||
} else if (action == "addComment") {
|
||||
resp = await AddIncidentComment(data.incident_id, data.comment, data.state, data.commented_at);
|
||||
} else if (action == "deleteComment") {
|
||||
resp = await UpdateCommentStatusByID(data.incident_id, data.comment_id, "INACTIVE");
|
||||
} else if (action == "updateComment") {
|
||||
resp = await UpdateCommentByID(data.incident_id, data.comment_id, data.comment, data.state, data.commented_at);
|
||||
} else if (action == "testTrigger") {
|
||||
let trigger = await GetTriggerByID(data.trigger_id);
|
||||
let siteData = await GetAllSiteData();
|
||||
const notificationClient = new notification(trigger, siteData, {});
|
||||
const testObj = {
|
||||
id: "test",
|
||||
alert_name: "Test Alert " + (Math.floor(Math.random() * 100) % 2) == 0 ? "DOWN" : "DEGRADED",
|
||||
severity: Math.floor(Math.random() * 100) % 2 == 0 ? "critical" : "warning",
|
||||
status: Math.floor(Math.random() * 100) % 2 == 0 ? "TRIGGERED" : "RESOLVED",
|
||||
source: "Kener",
|
||||
timestamp: new Date().toISOString(),
|
||||
description: "Monitor has failed",
|
||||
details: {
|
||||
metric: "Test",
|
||||
current_value: Math.floor(Math.random() * 100),
|
||||
threshold: Math.floor(Math.random() * 100),
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
text: "View Monitor",
|
||||
url: siteData.siteURL + "/monitor-test",
|
||||
},
|
||||
],
|
||||
};
|
||||
resp = await notificationClient.send(testObj);
|
||||
} else if (action == "testMonitor") {
|
||||
let monitorID = data.monitor_id;
|
||||
let monitors = await GetMonitorsParsed({ id: monitorID });
|
||||
let monitor = monitors[0];
|
||||
if (monitor.monitor_type === "NONE") {
|
||||
throw new Error("Tests can't be run on monitor type NONE");
|
||||
}
|
||||
const serviceClient = new Service(monitor);
|
||||
resp = await serviceClient.execute();
|
||||
}
|
||||
} catch (error) {
|
||||
resp = { error: error.message };
|
||||
return json(resp, { status: 500 });
|
||||
}
|
||||
return json(resp, { status: 200 });
|
||||
}
|
||||
async function storeSiteData(data) {
|
||||
for (const key in data) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
||||
const element = data[key];
|
||||
await InsertKeyValue(key, element);
|
||||
}
|
||||
}
|
||||
return { success: true };
|
||||
for (const key in data) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
||||
const element = data[key];
|
||||
await InsertKeyValue(key, element);
|
||||
}
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user