Skip to content

Commit

Permalink
feat: adding tcp monitor, reinstating ping as reported in #243
Browse files Browse the repository at this point in the history
  • Loading branch information
Raj Nandan Sharma authored and Raj Nandan Sharma committed Feb 7, 2025
1 parent 103d64a commit 07f59ac
Show file tree
Hide file tree
Showing 10 changed files with 587 additions and 238 deletions.
106 changes: 49 additions & 57 deletions docs/monitors-ping.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,14 @@ Ping monitors are used to monitor livenees of your servers. You can use Ping mon

</div>

## Host V4
## Hosts

You can add as many IP addresses as you want to monitor. The IP address should be a valid IPv4 address.
You can add as many hosts as you want to monitor. The host can be an IP(IP4 and IP6) address or a domain name.

Example of IP4 address is `106.12.43.232`, with the port number `80`. The port number is optional and defaults to `80`.

Example of IP4 address with port number is `66.51.120.219:465`.

Example of domain name with port number is `www.rajnandan.com:80`.

## Host V6

You can add as many IP addresses as you want to monitor. The IP address should be a valid IPv6 address. Example of IP6 address is `2001:0db8:85a3:0000:0000:8a2e:0370`.

Example of IP6 address with port number is `2606:50c0:8000::153:443`.

<p class="note danger">
Please note that at least one of the Host V4 or Host V6 is required.
<p>
- Type: Choose the type of host you want to monitor. It can be either `IP4` or `IP6` or `DOMAIN`.
- Host: Enter the IP address or domain name of the host you want to monitor.
- Timeout: Enter the timeout in milliseconds for each ping request of each host. For IP6 timeout is not supported.
- Count: The number of pings you want to do for each host. The average latency of all the pings will be used to evaluate the response.

## Eval

Expand All @@ -50,16 +39,12 @@ This is an anonymous JS function, it should return a **Promise**, that resolves
}, 0);

let alive = arrayOfPings.reduce((acc, ping) => {
if (ping.status === "open") {
return acc && true;
} else {
return false;
}
return acc && ping.alive;
}, true);

return {
status: alive ? "UP" : "DOWN",
latency: parseInt(latencyTotal / arrayOfPings.length)
latency: latencyTotal / arrayOfPings.length
};
});
```
Expand All @@ -72,41 +57,52 @@ let jsonResp = JSON.parse(decodedResp);
console.log(jsonResp);
/*
[
{
"host": "smtp.resend.com",
"port": 587,
"type": "IP4",
"status": "open",
"latency": 36.750917
},
{
"host": "66.51.120.219",
"port": 465,
"type": "IP4",
"status": "open",
"latency": 27.782792
},
{
"host": "2606:4700:4700::1111",
"port": 443,
"status": "open",
"type": "IP6",
"latency": 5.684375
}
{
alive: true,
min: '145.121',
max: '149.740',
avg: '147.430',
latencies: [ 145.121, 149.74 ],
latency: 145.121,
host: '45.91.169.49',
type: 'IP4'
},
{
alive: true,
min: '145.348',
max: '149.143',
avg: '147.245',
latencies: [ 145.348, 149.143 ],
latency: 145.348,
host: 'kener.ing',
type: 'DOMAIN'
},
{
alive: true,
min: '57.696',
max: '58.330',
avg: '58.013',
latencies: [ 57.696, 58.33 ],
latency: 57.696,
host: '2404:6800:4003:c1c::66',
type: 'IP6'
}
]
*/
```

### Understanding the Input

- `host`: The host that was pinged.
- `port`: The port that was pinged. Defaults to 80 if not provided.
- `type`: The type of IP address. Can be `IP4` or `IP6`.
- `status`: The status of the ping. Can be `open` , `error` or `timeout`.
- `open`: The host is reachable.
- `error`: There was an error while pinging the host.
- `timeout`: The host did not respond in time.
- `latency`: The time taken to ping the host. This is in milliseconds.
The input to the eval function is a base64 encoded string. You will have to decode it and then parse it to get the array of objects. Each object in the array represents the ping response of a host.

- `alive` is a boolean. It is true if the host is alive and false if the host is down.
- `min` is a string. It is the minimum latency of the pings.
- `max` is a string. It is the maximum latency of the pings.
- `avg` is a string. It is the average latency of the pings.
- `latencies` is an array of numbers. It is the latency of each ping.
- `latency` is a number. It is the average latency of the pings.
- `host` is a string. It is the host that was pinged.
- `type` is a string. It is the type of the host. It can be either `IP4` or `IP6` or `DOMAIN`.

### Example

Expand All @@ -120,11 +116,7 @@ The following example shows how to use the eval function to evaluate the respons
}, 0);

let areAllOpen = arrayOfPings.reduce((acc, ping) => {
if (ping.status === "open") {
return acc && true;
} else {
return false;
}
return acc && ping.alive;
}, true);

let avgLatency = latencyTotal / arrayOfPings.length;
Expand Down
135 changes: 135 additions & 0 deletions docs/monitors-tcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
title: TCP Monitors | Kener
description: Learn how to set up and work with TCP monitors in kener.
---

# TCP Monitors

TCP monitors are used to monitor the livenees of your servers. You can use TCP monitors to monitor the uptime of your servers and get notified when they are down.

<div class="border rounded-md">

![Monitors TCP](/m_tcp.png)

</div>

## Hosts

You can add as many hosts as you want to monitor. The host can be an IP(IP4 and IP6) address or a domain name.

- Type: Choose the type of host you want to monitor. It can be either `IP4` or `IP6` or `DOMAIN`.
- Host: Enter the IP address or domain name of the host you want to monitor.
- Port: Enter the port number of the host you want to monitor.
- Timeout: Enter the timeout in milliseconds for each ping request of each host

## Eval

The eval is used to define the JavaScript code that should be used to evaluate the response. It is optional and has be a valid JavaScript code.

This is an anonymous JS function, it should return a **Promise**, that resolves or rejects to `{status, latency}`, by default it looks like this.

> **_NOTE:_** The eval function should always return a json object. The json object can have only status(UP/DOWN/DEGRADED) and latency(number)
> `{status:"DEGRADED", latency: 200}`.
```javascript
(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
};
});
```

- `responseDataBase64` **REQUIRED** is a string. It is the base64 encoded response data. To use it you will have to decode it and the JSON parse it. Once parse it will be an array of objects.

```js
let decodedResp = atob(responseDataBase64);
let jsonResp = JSON.parse(decodedResp);
console.log(jsonResp);
/*
[
{
status: 'open',
latency: 31.93,
host: '66.51.120.219',
port: 465,
type: 'IP4'
},
{
status: 'open',
latency: 47.041417,
host: '2404:6800:4003:c1c::66',
port: 80,
type: 'IP6'
},
{
status: 'open',
latency: 82.1865,
host: 'rajnandan.com',
port: 80,
type: 'DOMAIN'
}
]
*/
```

### Understanding the Input

The input to the eval function is a base64 encoded string. You will have to decode it and then parse it to get the array of objects. Each object in the array represents the ping response of a host.

- `host`: The host that was pinged.
- `port`: The port that was pinged. Defaults to 80 if not provided.
- `type`: The type of IP address. Can be `IP4` or `IP6`.
- `status`: The status of the ping. Can be `open` , `error` or `timeout`.
- `open`: The host is reachable.
- `error`: There was an error while pinging the host.
- `timeout`: The host did not respond in time.
- `latency`: The time taken to ping the host. This is in milliseconds.

### Example

The following example shows how to use the eval function to evaluate the response. The function checks if the combined latency is more 10ms then returns `DEGRADED`.

```javascript
(async function (responseDataBase64) {
let arrayOfPings = JSON.parse(atob(responseDataBase64));
let latencyTotal = arrayOfPings.reduce((acc, ping) => {
return acc + ping.latency;
}, 0);

let areAllOpen = arrayOfPings.reduce((acc, ping) => {
if (ping.status === "open") {
return acc && true;
} else {
return false;
}
}, true);

let avgLatency = latencyTotal / arrayOfPings.length;

if (areAllOpen && avgLatency > 10) {
return {
status: "DEGRADED",
latency: avgLatency
};
}

return {
status: areAllOpen ? "UP" : "DOWN",
latency: avgLatency
};
});
```
5 changes: 5 additions & 0 deletions docs/structure.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@
"link": "/docs/monitors-ping",
"file": "/monitors-ping.md"
},
{
"title": "TCP Monitor",
"link": "/docs/monitors-tcp",
"file": "/monitors-tcp.md"
},
{
"title": "DNS Monitor",
"link": "/docs/monitors-dns",
Expand Down
12 changes: 6 additions & 6 deletions src/lib/clientTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,22 @@ const allRecordTypes = {
};
const ValidateIpAddress = function (input) {
// Check if input is a valid IPv4 address with an optional port
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}(:\d{1,5})?$/;
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
if (ipv4Regex.test(input)) {
return "IPv4";
return "IP4";
}

// Improved IPv6 regex that better handles compressed notation
const ipv6Regex =
/^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){0,7}:|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){6}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|(?:[0-9a-fA-F]{1,4}:){1,7}(?::|:[0-9a-fA-F]{1,4})|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:))(?::\d{1,5})?$/;
/^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){0,7}:|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){6}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|(?:[0-9a-fA-F]{1,4}:){1,7}(?::|:[0-9a-fA-F]{1,4})|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:))$/;
if (ipv6Regex.test(input)) {
return "IPv6";
return "IP6";
}

// Check if input is a valid domain name with an optional port
const domainRegex = /^[a-zA-Z0-9]+([\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,}(:\d{1,5})?$/;
const domainRegex = /^[a-zA-Z0-9]+([\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,}$/;
if (domainRegex.test(input)) {
return "Domain Name";
return "DOMAIN";
}

// If none of the above conditions match, the input is invalid
Expand Down
Loading

0 comments on commit 07f59ac

Please sign in to comment.