From 10c81bbfd3d8cf0d396fff2ce4759fcf21bd6a70 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 13:51:47 +0100
Subject: [PATCH 01/17] Added new columns to monitor table for configuring
 advanced ping behavior: - ping_count: number of packets to send -
 ping_deadline: maximum total duration - ping_timeout: time to wait for each
 response - ping_numeric: output IP addresses instead of hostnames

---
 db/knex_init_db.js                                 | 14 ++++++++++++++
 .../patch-add-ping-advanced-options.sql            |  7 +++++++
 2 files changed, 21 insertions(+)
 create mode 100644 db/old_migrations/patch-add-ping-advanced-options.sql

diff --git a/db/knex_init_db.js b/db/knex_init_db.js
index 46bff4bfa..c5a542e29 100644
--- a/db/knex_init_db.js
+++ b/db/knex_init_db.js
@@ -548,6 +548,20 @@ ALTER TABLE monitor
         table.double("timeout").defaultTo(0).notNullable();
     });
 
+    /*
+    patch-add-ping-advanced-options.sql
+    ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
+    ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
+    ALTER TABLE monitor ADD ping_deadline INTEGER default 10 not null;
+    ALTER TABLE monitor ADD ping_timeout INTEGER default 2 not null;
+    */
+    await knex.schema.table("monitor", function (table) {
+        table.integer("ping_count").defaultTo(1).notNullable();
+        table.boolean("ping_numeric").defaultTo(true).notNullable();
+        table.integer("ping_deadline").defaultTo(10).notNullable();
+        table.integer("ping_timeout").defaultTo(2).notNullable();
+    });
+
     /*
     patch-add-gamedig-given-port.sql
     ALTER TABLE monitor
diff --git a/db/old_migrations/patch-add-ping-advanced-options.sql b/db/old_migrations/patch-add-ping-advanced-options.sql
new file mode 100644
index 000000000..9ce76b072
--- /dev/null
+++ b/db/old_migrations/patch-add-ping-advanced-options.sql
@@ -0,0 +1,7 @@
+-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
+BEGIN TRANSACTION;
+ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
+ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
+ALTER TABLE monitor ADD ping_deadline INTEGER default 10 not null;
+ALTER TABLE monitor ADD ping_timeout INTEGER default 2 not null;
+COMMIT;

From ffd0157474b8d0563a842c797b75b0def1ebd52b Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 13:58:30 +0100
Subject: [PATCH 02/17] added ping options patch to patchList

---
 server/database.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/server/database.js b/server/database.js
index 0e6a7405d..415624b9f 100644
--- a/server/database.js
+++ b/server/database.js
@@ -111,7 +111,8 @@ class Database {
         "patch-notification-config.sql": true,
         "patch-fix-kafka-producer-booleans.sql": true,
         "patch-timeout.sql": true,
-        "patch-monitor-tls-info-add-fk.sql": true, // The last file so far converted to a knex migration file
+        "patch-monitor-tls-info-add-fk.sql": true, 
+        "patch-add-ping-advanced-options.sql": true, // The last file so far converted to a knex migration file
     };
 
     /**

From 55075f10bd4bdd750f11b21c2543d7ed324ee4ab Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 14:02:55 +0100
Subject: [PATCH 03/17] build: added constants for ping advanced options and
 rebuilt util.js

Added MIN/MAX/DEFAULT constants for:
- ping count
- ping deadline
- ping timeout
- and packet size, too.
---
 src/util.js | 42 +++++++++++++++++++++++++++++++++++-------
 src/util.ts | 20 ++++++++++++++++++++
 2 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/src/util.js b/src/util.js
index df89cf92b..a116d16ee 100644
--- a/src/util.js
+++ b/src/util.js
@@ -8,17 +8,34 @@
 // Backend uses the compiled file util.js
 // Frontend uses util.ts
 */
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
 var __importDefault = (this && this.__importDefault) || function (mod) {
     return (mod && mod.__esModule) ? mod : { "default": mod };
 };
 var _a;
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
-exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0;
-exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0;
+exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.PING_TIMEOUT_DEFAULT = exports.PING_TIMEOUT_MAX = exports.PING_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_DEADLINE_DEFAULT = exports.PING_DEADLINE_MAX = exports.PING_DEADLINE_MIN = exports.PING_DEFAULT_PACKET_SIZE = exports.PING_MAX_PACKET_SIZE = exports.PING_MIN_PACKET_SIZE = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
+exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = void 0;
 const dayjs_1 = __importDefault(require("dayjs"));
-const dayjs = require("dayjs");
-const jsonata = require("jsonata");
+const jsonata = __importStar(require("jsonata"));
 exports.isDev = process.env.NODE_ENV === "development";
 exports.isNode = typeof process !== "undefined" && ((_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node);
 exports.appName = "Uptime Kuma";
@@ -35,6 +52,18 @@ exports.SQL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
 exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm";
 exports.MAX_INTERVAL_SECOND = 2073600;
 exports.MIN_INTERVAL_SECOND = 20;
+exports.PING_MIN_PACKET_SIZE = 1;
+exports.PING_MAX_PACKET_SIZE = 65500;
+exports.PING_DEFAULT_PACKET_SIZE = 56;
+exports.PING_DEADLINE_MIN = 0;
+exports.PING_DEADLINE_MAX = 300;
+exports.PING_DEADLINE_DEFAULT = 0;
+exports.PING_COUNT_MIN = 1;
+exports.PING_COUNT_MAX = 100;
+exports.PING_COUNT_DEFAULT = 3;
+exports.PING_TIMEOUT_MIN = 0;
+exports.PING_TIMEOUT_MAX = 60;
+exports.PING_TIMEOUT_DEFAULT = 0;
 exports.CONSOLE_STYLE_Reset = "\x1b[0m";
 exports.CONSOLE_STYLE_Bright = "\x1b[1m";
 exports.CONSOLE_STYLE_Dim = "\x1b[2m";
@@ -66,7 +95,6 @@ exports.CONSOLE_STYLE_BgMagenta = "\x1b[45m";
 exports.CONSOLE_STYLE_BgCyan = "\x1b[46m";
 exports.CONSOLE_STYLE_BgWhite = "\x1b[47m";
 exports.CONSOLE_STYLE_BgGray = "\x1b[100m";
-
 const consoleModuleColors = [
     exports.CONSOLE_STYLE_FgCyan,
     exports.CONSOLE_STYLE_FgGreen,
@@ -458,4 +486,4 @@ async function evaluateJsonQuery(data, jsonPath, jsonPathOperator, expectedValue
         throw new Error(`Error evaluating JSON query: ${err.message}. Response from server was: ${response}`);
     }
 }
-exports.evaluateJsonQuery = evaluateJsonQuery;
\ No newline at end of file
+exports.evaluateJsonQuery = evaluateJsonQuery;
diff --git a/src/util.ts b/src/util.ts
index b3bab4fff..4d215e94e 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -39,6 +39,26 @@ export const SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm";
 export const MAX_INTERVAL_SECOND = 2073600; // 24 days
 export const MIN_INTERVAL_SECOND = 20; // 20 seconds
 
+// Packet Size limits
+export const PING_PACKET_SIZE_MIN = 1;
+export const PING_PACKET_SIZE_MAX = 65500;
+export const PING_PACKET_SIZE_DEFAULT = 56;
+
+// Deadline limits (in seconds)
+export const PING_DEADLINE_MIN = 1;
+export const PING_DEADLINE_MAX = 300;
+export const PING_DEADLINE_DEFAULT = 10;
+
+// Ping count limits
+export const PING_COUNT_MIN = 1;
+export const PING_COUNT_MAX = 100;
+export const PING_COUNT_DEFAULT = 1;
+
+// Timeout limits (in seconds)
+export const PING_TIMEOUT_MIN = 1;
+export const PING_TIMEOUT_MAX = 60;
+export const PING_TIMEOUT_DEFAULT = 2;
+
 // Console colors
 // https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color
 export const CONSOLE_STYLE_Reset = "\x1b[0m";

From 4bd775e4bef9a049fb38d574a53aedc924e55800 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 14:08:07 +0100
Subject: [PATCH 04/17] added advanced ping options to server and monitor and
 also added validation using ping MIN/MAX constants in monitor

---
 server/model/monitor.js | 33 ++++++++++++++++++++++++++++++++-
 server/server.js        |  6 ++++++
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index 3ad8cfafc..67c295f68 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -2,7 +2,8 @@ const dayjs = require("dayjs");
 const axios = require("axios");
 const { Prometheus } = require("../prometheus");
 const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
-    SQL_DATETIME_FORMAT, evaluateJsonQuery
+    SQL_DATETIME_FORMAT, evaluateJsonQuery, PING_PACKET_SIZE_MIN, PING_PACKET_SIZE_MAX,
+    PING_DEADLINE_MIN, PING_DEADLINE_MAX, PING_COUNT_MIN, PING_COUNT_MAX, PING_TIMEOUT_MIN, PING_TIMEOUT_MAX
 } = require("../../src/util");
 const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
     redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
@@ -155,6 +156,12 @@ class Monitor extends BeanModel {
             snmpVersion: this.snmpVersion,
             rabbitmqNodes: JSON.parse(this.rabbitmqNodes),
             conditions: JSON.parse(this.conditions),
+
+            // ping advanced options
+            ping_numeric: this.isPingNumeric(),
+            ping_count: this.ping_count,
+            ping_deadline: this.ping_deadline,
+            ping_timeout: this.ping_timeout,
         };
 
         if (includeSensitiveData) {
@@ -247,6 +254,14 @@ class Monitor extends BeanModel {
         return Boolean(this.expiryNotification);
     }
 
+    /**
+     * Check if ping should use numeric output only
+     * @returns {boolean} True if IP addresses will be output instead of symbolic hostnames
+     */
+    isPingNumeric() {
+        return Boolean(this.ping_numeric);
+    }
+
     /**
      * Parse to boolean
      * @returns {boolean} Should TLS errors be ignored?
@@ -1500,6 +1515,22 @@ class Monitor extends BeanModel {
         if (this.interval < MIN_INTERVAL_SECOND) {
             throw new Error(`Interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`);
         }
+
+        if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) {
+            throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX}`);
+        }
+        
+        if (this.ping_deadline && (this.ping_deadline < PING_DEADLINE_MIN || this.ping_deadline > PING_DEADLINE_MAX)) {
+            throw new Error(`Deadline must be between ${PING_DEADLINE_MIN} and ${PING_DEADLINE_MAX} seconds`);
+        }
+        
+        if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
+            throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX}`);
+        }
+        
+        if (this.ping_timeout && (this.ping_timeout < PING_TIMEOUT_MIN || this.ping_timeout > PING_TIMEOUT_MAX)) {
+            throw new Error(`Timeout must be between ${PING_TIMEOUT_MIN} and ${PING_TIMEOUT_MAX} seconds`);
+        }
     }
 
     /**
diff --git a/server/server.js b/server/server.js
index ec5ad49f6..38b93acf9 100644
--- a/server/server.js
+++ b/server/server.js
@@ -875,6 +875,12 @@ let needSetup = false;
                 bean.rabbitmqPassword = monitor.rabbitmqPassword;
                 bean.conditions = JSON.stringify(monitor.conditions);
 
+                // ping advanced options
+                bean.ping_numeric = monitor.ping_numeric;
+                bean.ping_count = monitor.ping_count;
+                bean.ping_deadline = monitor.ping_deadline;
+                bean.ping_timeout = monitor.ping_timeout;
+
                 bean.validate();
 
                 await R.store(bean);

From e1bdc53ba5e8e0c9fcb6fbdefc5b6146e1ee2f1e Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 14:11:12 +0100
Subject: [PATCH 05/17] refactor: enhanced ping function with advanced options
 and docs

- Added advanced parameters (count, deadline, timeout)
- Updated parameter names for clarity
- Used constants for default values
- Improved method and parameter documentation
---
 server/util-server.js | 57 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 45 insertions(+), 12 deletions(-)

diff --git a/server/util-server.js b/server/util-server.js
index 5ebc62ac5..187872589 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -1,7 +1,11 @@
 const tcpp = require("tcp-ping");
 const ping = require("@louislam/ping");
 const { R } = require("redbean-node");
-const { log, genSecret, badgeConstants } = require("../src/util");
+const { 
+    log, genSecret, badgeConstants,
+    PING_PACKET_SIZE_DEFAULT, PING_DEADLINE_DEFAULT,
+    PING_COUNT_DEFAULT, PING_TIMEOUT_DEFAULT
+} = require("../src/util");
 const passwordHash = require("./password-hash");
 const { Resolver } = require("dns");
 const iconv = require("iconv-lite");
@@ -118,20 +122,33 @@ exports.tcping = function (hostname, port) {
 
 /**
  * Ping the specified machine
- * @param {string} hostname Hostname / address of machine
- * @param {number} size Size of packet to send
+ * @param {string} dest_address Hostname / IP address of machine to ping
+ * @param {number} count Number of packets to send before stopping
+ * @param {string} source_address Source address for sending/receiving echo requests
+ * @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames
+ * @param {number} size Size (in bytes) of echo request to send
+ * @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent
+ * @param {number} timeout Maximum time in seconds to wait for each response
  * @returns {Promise<number>} Time for ping in ms rounded to nearest integer
  */
-exports.ping = async (hostname, size = 56) => {
+exports.ping = async (
+    dest_address,
+    count = PING_COUNT_DEFAULT,
+    source_address = '',
+    numeric = true,
+    size = PING_PACKET_SIZE_DEFAULT,
+    deadline = PING_DEADLINE_DEFAULT,
+    timeout = PING_TIMEOUT_DEFAULT,
+) => {
     try {
-        return await exports.pingAsync(hostname, false, size);
+        return await exports.pingAsync(dest_address, false, count, source_address, numeric, size, deadline, timeout);
     } catch (e) {
         // If the host cannot be resolved, try again with ipv6
         log.debug("ping", "IPv6 error message: " + e.message);
 
         // As node-ping does not report a specific error for this, try again if it is an empty message with ipv6 no matter what.
         if (!e.message) {
-            return await exports.pingAsync(hostname, true, size);
+            return await exports.pingAsync(dest_address, true, count, source_address, numeric, size, deadline, timeout);
         } else {
             throw e;
         }
@@ -140,18 +157,34 @@ exports.ping = async (hostname, size = 56) => {
 
 /**
  * Ping the specified machine
- * @param {string} hostname Hostname / address of machine to ping
+ * @param {string} dest_address Hostname / IP address of machine to ping
  * @param {boolean} ipv6 Should IPv6 be used?
- * @param {number} size Size of ping packet to send
+ * @param {number} count Number of packets to send before stopping
+ * @param {string} source_address Source address for sending/receiving echo requests
+ * @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames
+ * @param {number} size Size (in bytes) of echo request to send
+ * @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent
+ * @param {number} timeout Maximum time in seconds to wait for each response
  * @returns {Promise<number>} Time for ping in ms rounded to nearest integer
  */
-exports.pingAsync = function (hostname, ipv6 = false, size = 56) {
+exports.pingAsync = function (
+    dest_address,
+    ipv6 = false,
+    count = PING_COUNT_DEFAULT,
+    source_address = '',
+    numeric = true,
+    size = PING_PACKET_SIZE_DEFAULT,
+    deadline = PING_DEADLINE_DEFAULT,
+    timeout = PING_TIMEOUT_DEFAULT,
+) {
     return new Promise((resolve, reject) => {
-        ping.promise.probe(hostname, {
+        ping.promise.probe(dest_address, {
             v6: ipv6,
-            min_reply: 1,
-            deadline: 10,
+            min_reply: count,
+            sourceAddr: source_address,
             packetSize: size,
+            deadline: deadline,
+            timeout: timeout
         }).then((res) => {
             // If ping failed, it will set field to unknown
             if (res.alive) {

From 7371cefc27cb7169427c40791bd2e105439a3d46 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 14:12:54 +0100
Subject: [PATCH 06/17] added UI for advanced ping configuration

Added form fields to configure:
- packet count
- timeout
- deadline
- numeric output option
in monitor creation/edit view
---
 src/pages/EditMonitor.vue | 82 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 77 insertions(+), 5 deletions(-)

diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index a83f91cab..3d3f3ca13 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -660,12 +660,54 @@
                                 </div>
                             </div>
 
-                            <!-- Ping packet size -->
+                            <!-- Max Packets / Count -->
+                            <div v-if="monitor.type === 'ping'" class="my-3">
+                                <label for="ping-count" class="form-label">{{ $t("Max Packets") }}</label>
+                                <input id="ping-count" v-model="monitor.ping_count" type="number" class="form-control" required :min="ping_count_min" :max="ping_count_max" step="1">
+                                <div class="form-text">
+                                    {{ $t("Number of packets to send before stopping") }}
+                                </div>
+                            </div>
+
+                            <!-- Numeric Output -->
+                            <div v-if="monitor.type === 'ping'" class="my-3 form-check">
+                                <input id="ping_numeric" v-model="monitor.ping_numeric" type="checkbox" class="form-check-input"
+                                    :checked="monitor.ping_numeric">
+                                <label class="form-check-label" for="ping_numeric">
+                                    {{ $t("Numeric Output") }}
+                                </label>
+                                <div class="form-text">
+                                    {{ $t("If checked, IP addresses will be output instead of symbolic hostnames") }}
+                                </div>
+                            </div>
+
+                            <!-- Packet size -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
                                 <label for="packet-size" class="form-label">{{ $t("Packet Size") }}</label>
-                                <input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control" required min="1" max="65500" step="1">
+                                <input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control"
+                                    required :min="packetSize_min" :max="packetSize_max" step="1">
                             </div>
 
+                            <!-- Max Duration / Deadline -->
+                            <div v-if="monitor.type === 'ping'" class="my-3">
+                                <label for="ping_deadline" class="form-label">{{ $t("Max Duration") }}</label>
+                                <input id="ping_deadline" v-model="monitor.ping_deadline" type="number" class="form-control"
+                                    required :min="ping_deadline_min" :max="ping_deadline_max" step="1">
+                                <div class="form-text">
+                                    {{ $t("Total time in seconds before ping stops, regardless of packets sent") }}
+                                </div>
+                            </div>
+
+                            <!-- Response Timeout -->
+                            <div v-if="monitor.type === 'ping'" class="my-3">
+                                <label for="ping_timeout" class="form-label">{{ $t("Response Timeout") }}</label>
+                                <input id="ping_timeout" v-model="monitor.ping_timeout" type="number" class="form-control" required :min="ping_timeout_min" :max="ping_timeout_max" step="1">
+                                <div class="form-text">
+                                    {{ $t("Maximum time in seconds to wait for each response") }}
+                                </div>
+                            </div>
+
+
                             <!-- HTTP / Keyword only -->
                             <template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword' ">
                                 <div class="my-3">
@@ -1060,7 +1102,25 @@ import DockerHostDialog from "../components/DockerHostDialog.vue";
 import RemoteBrowserDialog from "../components/RemoteBrowserDialog.vue";
 import ProxyDialog from "../components/ProxyDialog.vue";
 import TagsManager from "../components/TagsManager.vue";
-import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } from "../util.ts";
+import { 
+    genSecret, 
+    isDev, 
+    MAX_INTERVAL_SECOND, 
+    MIN_INTERVAL_SECOND, 
+    sleep,
+    PING_PACKET_SIZE_MIN,
+    PING_PACKET_SIZE_MAX,
+    PING_PACKET_SIZE_DEFAULT,
+    PING_DEADLINE_MIN,
+    PING_DEADLINE_MAX,
+    PING_DEADLINE_DEFAULT,
+    PING_COUNT_MIN,
+    PING_COUNT_MAX,
+    PING_COUNT_DEFAULT,
+    PING_TIMEOUT_MIN,
+    PING_TIMEOUT_MAX,
+    PING_TIMEOUT_DEFAULT
+} from "../util.ts";
 import { hostNameRegexPattern } from "../util-frontend";
 import HiddenInput from "../components/HiddenInput.vue";
 import EditMonitorConditions from "../components/EditMonitorConditions.vue";
@@ -1082,7 +1142,6 @@ const monitorDefaults = {
     notificationIDList: {},
     ignoreTls: false,
     upsideDown: false,
-    packetSize: 56,
     expiryNotification: false,
     maxredirects: 10,
     accepted_statuscodes: [ "200-299" ],
@@ -1133,6 +1192,14 @@ export default {
         return {
             minInterval: MIN_INTERVAL_SECOND,
             maxInterval: MAX_INTERVAL_SECOND,
+            ping_count_min: PING_COUNT_MIN,
+            ping_count_max: PING_COUNT_MAX,
+            packetSize_min: PING_PACKET_SIZE_MIN,
+            packetSize_max: PING_PACKET_SIZE_MAX,
+            ping_deadline_min: PING_DEADLINE_MIN,
+            ping_deadline_max: PING_DEADLINE_MAX,
+            ping_timeout_min: PING_TIMEOUT_MIN,
+            ping_timeout_max: PING_TIMEOUT_MAX,
             processing: false,
             monitor: {
                 notificationIDList: {},
@@ -1564,7 +1631,12 @@ message HealthCheckResponse {
             if (this.isAdd) {
 
                 this.monitor = {
-                    ...monitorDefaults
+                    ...monitorDefaults,
+                    ping_count: PING_COUNT_DEFAULT,
+                    ping_numeric: true,
+                    packetSize: PING_PACKET_SIZE_DEFAULT,
+                    ping_deadline: PING_DEADLINE_DEFAULT,
+                    ping_timeout: PING_TIMEOUT_DEFAULT,
                 };
 
                 if (this.$root.proxyList && !this.monitor.proxyId) {

From e661b9f801661532ae55d3ebb1a0a291abd8e37a Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 14:29:38 +0100
Subject: [PATCH 07/17] fixed ESLint issues and regenerated util.js

---
 server/database.js      |  2 +-
 server/model/monitor.js |  6 +++---
 server/util-server.js   | 26 +++++++++++++-------------
 src/util.js             | 10 +++++-----
 4 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/server/database.js b/server/database.js
index 415624b9f..db31c2d9e 100644
--- a/server/database.js
+++ b/server/database.js
@@ -111,7 +111,7 @@ class Database {
         "patch-notification-config.sql": true,
         "patch-fix-kafka-producer-booleans.sql": true,
         "patch-timeout.sql": true,
-        "patch-monitor-tls-info-add-fk.sql": true, 
+        "patch-monitor-tls-info-add-fk.sql": true,
         "patch-add-ping-advanced-options.sql": true, // The last file so far converted to a knex migration file
     };
 
diff --git a/server/model/monitor.js b/server/model/monitor.js
index 67c295f68..73612bd0b 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -1519,15 +1519,15 @@ class Monitor extends BeanModel {
         if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) {
             throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX}`);
         }
-        
+
         if (this.ping_deadline && (this.ping_deadline < PING_DEADLINE_MIN || this.ping_deadline > PING_DEADLINE_MAX)) {
             throw new Error(`Deadline must be between ${PING_DEADLINE_MIN} and ${PING_DEADLINE_MAX} seconds`);
         }
-        
+
         if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
             throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX}`);
         }
-        
+
         if (this.ping_timeout && (this.ping_timeout < PING_TIMEOUT_MIN || this.ping_timeout > PING_TIMEOUT_MAX)) {
             throw new Error(`Timeout must be between ${PING_TIMEOUT_MIN} and ${PING_TIMEOUT_MAX} seconds`);
         }
diff --git a/server/util-server.js b/server/util-server.js
index 187872589..33d2d78e6 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -1,7 +1,7 @@
 const tcpp = require("tcp-ping");
 const ping = require("@louislam/ping");
 const { R } = require("redbean-node");
-const { 
+const {
     log, genSecret, badgeConstants,
     PING_PACKET_SIZE_DEFAULT, PING_DEADLINE_DEFAULT,
     PING_COUNT_DEFAULT, PING_TIMEOUT_DEFAULT
@@ -122,9 +122,9 @@ exports.tcping = function (hostname, port) {
 
 /**
  * Ping the specified machine
- * @param {string} dest_address Hostname / IP address of machine to ping
+ * @param {string} destAddr Hostname / IP address of machine to ping
  * @param {number} count Number of packets to send before stopping
- * @param {string} source_address Source address for sending/receiving echo requests
+ * @param {string} sourceAddr Source address for sending/receiving echo requests
  * @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames
  * @param {number} size Size (in bytes) of echo request to send
  * @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent
@@ -132,23 +132,23 @@ exports.tcping = function (hostname, port) {
  * @returns {Promise<number>} Time for ping in ms rounded to nearest integer
  */
 exports.ping = async (
-    dest_address,
+    destAddr,
     count = PING_COUNT_DEFAULT,
-    source_address = '',
+    sourceAddr = "",
     numeric = true,
     size = PING_PACKET_SIZE_DEFAULT,
     deadline = PING_DEADLINE_DEFAULT,
     timeout = PING_TIMEOUT_DEFAULT,
 ) => {
     try {
-        return await exports.pingAsync(dest_address, false, count, source_address, numeric, size, deadline, timeout);
+        return await exports.pingAsync(destAddr, false, count, sourceAddr, numeric, size, deadline, timeout);
     } catch (e) {
         // If the host cannot be resolved, try again with ipv6
         log.debug("ping", "IPv6 error message: " + e.message);
 
         // As node-ping does not report a specific error for this, try again if it is an empty message with ipv6 no matter what.
         if (!e.message) {
-            return await exports.pingAsync(dest_address, true, count, source_address, numeric, size, deadline, timeout);
+            return await exports.pingAsync(destAddr, true, count, sourceAddr, numeric, size, deadline, timeout);
         } else {
             throw e;
         }
@@ -157,10 +157,10 @@ exports.ping = async (
 
 /**
  * Ping the specified machine
- * @param {string} dest_address Hostname / IP address of machine to ping
+ * @param {string} destAddr Hostname / IP address of machine to ping
  * @param {boolean} ipv6 Should IPv6 be used?
  * @param {number} count Number of packets to send before stopping
- * @param {string} source_address Source address for sending/receiving echo requests
+ * @param {string} sourceAddr Source address for sending/receiving echo requests
  * @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames
  * @param {number} size Size (in bytes) of echo request to send
  * @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent
@@ -168,20 +168,20 @@ exports.ping = async (
  * @returns {Promise<number>} Time for ping in ms rounded to nearest integer
  */
 exports.pingAsync = function (
-    dest_address,
+    destAddr,
     ipv6 = false,
     count = PING_COUNT_DEFAULT,
-    source_address = '',
+    sourceAddr = "",
     numeric = true,
     size = PING_PACKET_SIZE_DEFAULT,
     deadline = PING_DEADLINE_DEFAULT,
     timeout = PING_TIMEOUT_DEFAULT,
 ) {
     return new Promise((resolve, reject) => {
-        ping.promise.probe(dest_address, {
+        ping.promise.probe(destAddr, {
             v6: ipv6,
             min_reply: count,
-            sourceAddr: source_address,
+            sourceAddr: sourceAddr,
             packetSize: size,
             deadline: deadline,
             timeout: timeout
diff --git a/src/util.js b/src/util.js
index a116d16ee..644914472 100644
--- a/src/util.js
+++ b/src/util.js
@@ -32,7 +32,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
 };
 var _a;
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.PING_TIMEOUT_DEFAULT = exports.PING_TIMEOUT_MAX = exports.PING_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_DEADLINE_DEFAULT = exports.PING_DEADLINE_MAX = exports.PING_DEADLINE_MIN = exports.PING_DEFAULT_PACKET_SIZE = exports.PING_MAX_PACKET_SIZE = exports.PING_MIN_PACKET_SIZE = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
+exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.PING_TIMEOUT_DEFAULT = exports.PING_TIMEOUT_MAX = exports.PING_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_DEADLINE_DEFAULT = exports.PING_DEADLINE_MAX = exports.PING_DEADLINE_MIN = exports.PING_PACKET_SIZE_DEFAULT = exports.PING_PACKET_SIZE_MAX = exports.PING_PACKET_SIZE_MIN = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
 exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = void 0;
 const dayjs_1 = __importDefault(require("dayjs"));
 const jsonata = __importStar(require("jsonata"));
@@ -52,16 +52,16 @@ exports.SQL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
 exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm";
 exports.MAX_INTERVAL_SECOND = 2073600;
 exports.MIN_INTERVAL_SECOND = 20;
-exports.PING_MIN_PACKET_SIZE = 1;
-exports.PING_MAX_PACKET_SIZE = 65500;
-exports.PING_DEFAULT_PACKET_SIZE = 56;
+exports.PING_PACKET_SIZE_MIN = 1;
+exports.PING_PACKET_SIZE_MAX = 65500;
+exports.PING_PACKET_SIZE_DEFAULT = 56;
 exports.PING_DEADLINE_MIN = 0;
 exports.PING_DEADLINE_MAX = 300;
 exports.PING_DEADLINE_DEFAULT = 0;
 exports.PING_COUNT_MIN = 1;
 exports.PING_COUNT_MAX = 100;
 exports.PING_COUNT_DEFAULT = 3;
-exports.PING_TIMEOUT_MIN = 0;
+exports.PING_TIMEOUT_MIN_TIMEOU = 0;
 exports.PING_TIMEOUT_MAX = 60;
 exports.PING_TIMEOUT_DEFAULT = 0;
 exports.CONSOLE_STYLE_Reset = "\x1b[0m";

From c58c76ccf1e754a42702d6bf3cc074141aa6847d Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 31 Jan 2025 14:57:03 +0100
Subject: [PATCH 08/17] Fixed check-linters errors

---
 src/pages/EditMonitor.vue | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 3d3f3ca13..079bc4fca 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -671,8 +671,7 @@
 
                             <!-- Numeric Output -->
                             <div v-if="monitor.type === 'ping'" class="my-3 form-check">
-                                <input id="ping_numeric" v-model="monitor.ping_numeric" type="checkbox" class="form-check-input"
-                                    :checked="monitor.ping_numeric">
+                                <input id="ping_numeric" v-model="monitor.ping_numeric" type="checkbox" class="form-check-input" :checked="monitor.ping_numeric">
                                 <label class="form-check-label" for="ping_numeric">
                                     {{ $t("Numeric Output") }}
                                 </label>
@@ -684,15 +683,13 @@
                             <!-- Packet size -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
                                 <label for="packet-size" class="form-label">{{ $t("Packet Size") }}</label>
-                                <input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control"
-                                    required :min="packetSize_min" :max="packetSize_max" step="1">
+                                <input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control" required :min="packetSize_min" :max="packetSize_max" step="1">
                             </div>
 
                             <!-- Max Duration / Deadline -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
                                 <label for="ping_deadline" class="form-label">{{ $t("Max Duration") }}</label>
-                                <input id="ping_deadline" v-model="monitor.ping_deadline" type="number" class="form-control"
-                                    required :min="ping_deadline_min" :max="ping_deadline_max" step="1">
+                                <input id="ping_deadline" v-model="monitor.ping_deadline" type="number" class="form-control" required :min="ping_deadline_min" :max="ping_deadline_max" step="1">
                                 <div class="form-text">
                                     {{ $t("Total time in seconds before ping stops, regardless of packets sent") }}
                                 </div>
@@ -707,7 +704,6 @@
                                 </div>
                             </div>
 
-
                             <!-- HTTP / Keyword only -->
                             <template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword' ">
                                 <div class="my-3">
@@ -1102,11 +1098,11 @@ import DockerHostDialog from "../components/DockerHostDialog.vue";
 import RemoteBrowserDialog from "../components/RemoteBrowserDialog.vue";
 import ProxyDialog from "../components/ProxyDialog.vue";
 import TagsManager from "../components/TagsManager.vue";
-import { 
-    genSecret, 
-    isDev, 
-    MAX_INTERVAL_SECOND, 
-    MIN_INTERVAL_SECOND, 
+import {
+    genSecret,
+    isDev,
+    MAX_INTERVAL_SECOND,
+    MIN_INTERVAL_SECOND,
     sleep,
     PING_PACKET_SIZE_MIN,
     PING_PACKET_SIZE_MAX,

From e0166f5eca6041f0cd46b2067a0b91b9b041ff78 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Tue, 4 Mar 2025 10:11:34 +0100
Subject: [PATCH 09/17] migrated from old to new migration system based on knex

---
 db/knex_init_db.js                            | 14 ----------
 .../2025-03-04-0000-ping-advanced-options.js  | 27 +++++++++++++++++++
 .../patch-add-ping-advanced-options.sql       |  7 -----
 server/database.js                            |  3 +--
 4 files changed, 28 insertions(+), 23 deletions(-)
 create mode 100644 db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
 delete mode 100644 db/old_migrations/patch-add-ping-advanced-options.sql

diff --git a/db/knex_init_db.js b/db/knex_init_db.js
index c5a542e29..46bff4bfa 100644
--- a/db/knex_init_db.js
+++ b/db/knex_init_db.js
@@ -548,20 +548,6 @@ ALTER TABLE monitor
         table.double("timeout").defaultTo(0).notNullable();
     });
 
-    /*
-    patch-add-ping-advanced-options.sql
-    ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
-    ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
-    ALTER TABLE monitor ADD ping_deadline INTEGER default 10 not null;
-    ALTER TABLE monitor ADD ping_timeout INTEGER default 2 not null;
-    */
-    await knex.schema.table("monitor", function (table) {
-        table.integer("ping_count").defaultTo(1).notNullable();
-        table.boolean("ping_numeric").defaultTo(true).notNullable();
-        table.integer("ping_deadline").defaultTo(10).notNullable();
-        table.integer("ping_timeout").defaultTo(2).notNullable();
-    });
-
     /*
     patch-add-gamedig-given-port.sql
     ALTER TABLE monitor
diff --git a/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
new file mode 100644
index 000000000..319fb18ef
--- /dev/null
+++ b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
@@ -0,0 +1,27 @@
+/* SQL:
+ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
+ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
+ALTER TABLE monitor ADD ping_deadline INTEGER default 10 not null;
+ALTER TABLE monitor ADD ping_timeout INTEGER default 2 not null;
+*/
+exports.up = function (knex) {
+    // Add new columns to table monitor
+    return knex.schema
+        .alterTable("monitor", function (table) {
+            table.integer("ping_count").defaultTo(1).notNullable();
+            table.boolean("ping_numeric").defaultTo(true).notNullable();
+            table.integer("ping_deadline").defaultTo(10).notNullable();
+            table.integer("ping_timeout").defaultTo(2).notNullable();
+        });
+
+};
+
+exports.down = function (knex) {
+    return knex.schema
+        .alterTable("monitor", function (table) {
+            table.dropColumn("ping_count");
+            table.dropColumn("ping_numeric");
+            table.dropColumn("ping_deadline");
+            table.dropColumn("ping_timeout");
+        });
+};
diff --git a/db/old_migrations/patch-add-ping-advanced-options.sql b/db/old_migrations/patch-add-ping-advanced-options.sql
deleted file mode 100644
index 9ce76b072..000000000
--- a/db/old_migrations/patch-add-ping-advanced-options.sql
+++ /dev/null
@@ -1,7 +0,0 @@
--- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
-BEGIN TRANSACTION;
-ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
-ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
-ALTER TABLE monitor ADD ping_deadline INTEGER default 10 not null;
-ALTER TABLE monitor ADD ping_timeout INTEGER default 2 not null;
-COMMIT;
diff --git a/server/database.js b/server/database.js
index db31c2d9e..0e6a7405d 100644
--- a/server/database.js
+++ b/server/database.js
@@ -111,8 +111,7 @@ class Database {
         "patch-notification-config.sql": true,
         "patch-fix-kafka-producer-booleans.sql": true,
         "patch-timeout.sql": true,
-        "patch-monitor-tls-info-add-fk.sql": true,
-        "patch-add-ping-advanced-options.sql": true, // The last file so far converted to a knex migration file
+        "patch-monitor-tls-info-add-fk.sql": true, // The last file so far converted to a knex migration file
     };
 
     /**

From 0bb2756f9cb4b023bfc8974e931fd4fe02dc54b6 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Tue, 4 Mar 2025 11:01:23 +0100
Subject: [PATCH 10/17]  added/fixed translations

---
 src/lang/en.json          | 10 +++++++++-
 src/pages/EditMonitor.vue | 16 ++++++++--------
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/src/lang/en.json b/src/lang/en.json
index e215f1031..28bca7d87 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -1051,5 +1051,13 @@
     "RabbitMQ Password": "RabbitMQ Password",
     "rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.",
     "SendGrid API Key": "SendGrid API Key",
-    "Separate multiple email addresses with commas": "Separate multiple email addresses with commas"
+    "Separate multiple email addresses with commas": "Separate multiple email addresses with commas",
+    "pingCountLabel": "Max Packets",
+    "pingCountDescription": "Number of packets to send before stopping",
+    "pingNumericLabel": "Numeric Output",
+    "pingNumericDescription": "If checked, IP addresses will be output instead of symbolic hostnames",
+    "pingDeadlineLabel": "Max Duration",
+    "pingDeadlineDescription": "Total time in seconds before ping stops, regardless of packets sent",
+    "pingTimeoutLabel": "Response Timeout",
+    "pingTimeoutDescription": "Maximum time in seconds to wait for each response"
 }
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 079bc4fca..767e79b30 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -662,10 +662,10 @@
 
                             <!-- Max Packets / Count -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
-                                <label for="ping-count" class="form-label">{{ $t("Max Packets") }}</label>
+                                <label for="ping-count" class="form-label">{{ $t("pingCountLabel") }}</label>
                                 <input id="ping-count" v-model="monitor.ping_count" type="number" class="form-control" required :min="ping_count_min" :max="ping_count_max" step="1">
                                 <div class="form-text">
-                                    {{ $t("Number of packets to send before stopping") }}
+                                    {{ $t("pingCountDescription") }}
                                 </div>
                             </div>
 
@@ -673,10 +673,10 @@
                             <div v-if="monitor.type === 'ping'" class="my-3 form-check">
                                 <input id="ping_numeric" v-model="monitor.ping_numeric" type="checkbox" class="form-check-input" :checked="monitor.ping_numeric">
                                 <label class="form-check-label" for="ping_numeric">
-                                    {{ $t("Numeric Output") }}
+                                    {{ $t("pingNumericLabel") }}
                                 </label>
                                 <div class="form-text">
-                                    {{ $t("If checked, IP addresses will be output instead of symbolic hostnames") }}
+                                    {{ $t("pingNumericDescription") }}
                                 </div>
                             </div>
 
@@ -688,19 +688,19 @@
 
                             <!-- Max Duration / Deadline -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
-                                <label for="ping_deadline" class="form-label">{{ $t("Max Duration") }}</label>
+                                <label for="ping_deadline" class="form-label">{{ $t("pingDeadlineLabel") }}</label>
                                 <input id="ping_deadline" v-model="monitor.ping_deadline" type="number" class="form-control" required :min="ping_deadline_min" :max="ping_deadline_max" step="1">
                                 <div class="form-text">
-                                    {{ $t("Total time in seconds before ping stops, regardless of packets sent") }}
+                                    {{ $t("pingDeadlineDescription") }}
                                 </div>
                             </div>
 
                             <!-- Response Timeout -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
-                                <label for="ping_timeout" class="form-label">{{ $t("Response Timeout") }}</label>
+                                <label for="ping_timeout" class="form-label">{{ $t("pingTimeoutLabel") }}</label>
                                 <input id="ping_timeout" v-model="monitor.ping_timeout" type="number" class="form-control" required :min="ping_timeout_min" :max="ping_timeout_max" step="1">
                                 <div class="form-text">
-                                    {{ $t("Maximum time in seconds to wait for each response") }}
+                                    {{ $t("pingTimeoutDescription") }}
                                 </div>
                             </div>
 

From f275d5611bc9fe14590cc183a259c8a7a1866078 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Tue, 4 Mar 2025 12:11:51 +0100
Subject: [PATCH 11/17] fix: numeric flag now properly passed to the underlying
 function

---
 server/util-server.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/server/util-server.js b/server/util-server.js
index 33d2d78e6..5efe9116a 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -182,6 +182,7 @@ exports.pingAsync = function (
             v6: ipv6,
             min_reply: count,
             sourceAddr: sourceAddr,
+            numeric: numeric,
             packetSize: size,
             deadline: deadline,
             timeout: timeout

From d12a0b31781609556ac73d025489c7478a638c4a Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Thu, 6 Mar 2025 16:32:20 +0100
Subject: [PATCH 12/17] inline ping validation constants

---
 server/model/monitor.js   | 15 +++++++++------
 src/pages/EditMonitor.vue | 36 ++++++++----------------------------
 src/util.js               | 10 +++++-----
 3 files changed, 22 insertions(+), 39 deletions(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index 73612bd0b..86f53f3b3 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -2,8 +2,11 @@ const dayjs = require("dayjs");
 const axios = require("axios");
 const { Prometheus } = require("../prometheus");
 const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
-    SQL_DATETIME_FORMAT, evaluateJsonQuery, PING_PACKET_SIZE_MIN, PING_PACKET_SIZE_MAX,
-    PING_DEADLINE_MIN, PING_DEADLINE_MAX, PING_COUNT_MIN, PING_COUNT_MAX, PING_TIMEOUT_MIN, PING_TIMEOUT_MAX
+    SQL_DATETIME_FORMAT, evaluateJsonQuery,
+    PING_PACKET_SIZE_MIN, PING_PACKET_SIZE_MAX, PING_PACKET_SIZE_DEFAULT,
+    PING_DEADLINE_MIN, PING_DEADLINE_MAX, PING_DEADLINE_DEFAULT,
+    PING_COUNT_MIN, PING_COUNT_MAX, PING_COUNT_DEFAULT,
+    PING_TIMEOUT_MIN, PING_TIMEOUT_MAX, PING_TIMEOUT_DEFAULT
 } = require("../../src/util");
 const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
     redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
@@ -1517,19 +1520,19 @@ class Monitor extends BeanModel {
         }
 
         if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) {
-            throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX}`);
+            throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX} (default: ${PING_PACKET_SIZE_DEFAULT})`);
         }
 
         if (this.ping_deadline && (this.ping_deadline < PING_DEADLINE_MIN || this.ping_deadline > PING_DEADLINE_MAX)) {
-            throw new Error(`Deadline must be between ${PING_DEADLINE_MIN} and ${PING_DEADLINE_MAX} seconds`);
+            throw new Error(`Deadline must be between ${PING_DEADLINE_MIN} and ${PING_DEADLINE_MAX} seconds (default: ${PING_DEADLINE_DEFAULT})`);
         }
 
         if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
-            throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX}`);
+            throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX} (default: ${PING_COUNT_DEFAULT})`);
         }
 
         if (this.ping_timeout && (this.ping_timeout < PING_TIMEOUT_MIN || this.ping_timeout > PING_TIMEOUT_MAX)) {
-            throw new Error(`Timeout must be between ${PING_TIMEOUT_MIN} and ${PING_TIMEOUT_MAX} seconds`);
+            throw new Error(`Timeout must be between ${PING_TIMEOUT_MIN} and ${PING_TIMEOUT_MAX} seconds (default: ${PING_TIMEOUT_DEFAULT})`);
         }
     }
 
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 767e79b30..19d7c2a9e 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -663,7 +663,7 @@
                             <!-- Max Packets / Count -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
                                 <label for="ping-count" class="form-label">{{ $t("pingCountLabel") }}</label>
-                                <input id="ping-count" v-model="monitor.ping_count" type="number" class="form-control" required :min="ping_count_min" :max="ping_count_max" step="1">
+                                <input id="ping-count" v-model="monitor.ping_count" type="number" class="form-control" required min="1" max="100" step="1">
                                 <div class="form-text">
                                     {{ $t("pingCountDescription") }}
                                 </div>
@@ -683,13 +683,13 @@
                             <!-- Packet size -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
                                 <label for="packet-size" class="form-label">{{ $t("Packet Size") }}</label>
-                                <input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control" required :min="packetSize_min" :max="packetSize_max" step="1">
+                                <input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control" required min="1" :max="65500" step="1">
                             </div>
 
                             <!-- Max Duration / Deadline -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
                                 <label for="ping_deadline" class="form-label">{{ $t("pingDeadlineLabel") }}</label>
-                                <input id="ping_deadline" v-model="monitor.ping_deadline" type="number" class="form-control" required :min="ping_deadline_min" :max="ping_deadline_max" step="1">
+                                <input id="ping_deadline" v-model="monitor.ping_deadline" type="number" class="form-control" required min="0" max="300" step="1">
                                 <div class="form-text">
                                     {{ $t("pingDeadlineDescription") }}
                                 </div>
@@ -698,7 +698,7 @@
                             <!-- Response Timeout -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
                                 <label for="ping_timeout" class="form-label">{{ $t("pingTimeoutLabel") }}</label>
-                                <input id="ping_timeout" v-model="monitor.ping_timeout" type="number" class="form-control" required :min="ping_timeout_min" :max="ping_timeout_max" step="1">
+                                <input id="ping_timeout" v-model="monitor.ping_timeout" type="number" class="form-control" required min="1" max="60" step="1">
                                 <div class="form-text">
                                     {{ $t("pingTimeoutDescription") }}
                                 </div>
@@ -1104,18 +1104,6 @@ import {
     MAX_INTERVAL_SECOND,
     MIN_INTERVAL_SECOND,
     sleep,
-    PING_PACKET_SIZE_MIN,
-    PING_PACKET_SIZE_MAX,
-    PING_PACKET_SIZE_DEFAULT,
-    PING_DEADLINE_MIN,
-    PING_DEADLINE_MAX,
-    PING_DEADLINE_DEFAULT,
-    PING_COUNT_MIN,
-    PING_COUNT_MAX,
-    PING_COUNT_DEFAULT,
-    PING_TIMEOUT_MIN,
-    PING_TIMEOUT_MAX,
-    PING_TIMEOUT_DEFAULT
 } from "../util.ts";
 import { hostNameRegexPattern } from "../util-frontend";
 import HiddenInput from "../components/HiddenInput.vue";
@@ -1188,14 +1176,6 @@ export default {
         return {
             minInterval: MIN_INTERVAL_SECOND,
             maxInterval: MAX_INTERVAL_SECOND,
-            ping_count_min: PING_COUNT_MIN,
-            ping_count_max: PING_COUNT_MAX,
-            packetSize_min: PING_PACKET_SIZE_MIN,
-            packetSize_max: PING_PACKET_SIZE_MAX,
-            ping_deadline_min: PING_DEADLINE_MIN,
-            ping_deadline_max: PING_DEADLINE_MAX,
-            ping_timeout_min: PING_TIMEOUT_MIN,
-            ping_timeout_max: PING_TIMEOUT_MAX,
             processing: false,
             monitor: {
                 notificationIDList: {},
@@ -1628,11 +1608,11 @@ message HealthCheckResponse {
 
                 this.monitor = {
                     ...monitorDefaults,
-                    ping_count: PING_COUNT_DEFAULT,
+                    ping_count: 3,
                     ping_numeric: true,
-                    packetSize: PING_PACKET_SIZE_DEFAULT,
-                    ping_deadline: PING_DEADLINE_DEFAULT,
-                    ping_timeout: PING_TIMEOUT_DEFAULT,
+                    packetSize: 56,
+                    ping_deadline: 10,
+                    ping_timeout: 2,
                 };
 
                 if (this.$root.proxyList && !this.monitor.proxyId) {
diff --git a/src/util.js b/src/util.js
index 644914472..62ebba01c 100644
--- a/src/util.js
+++ b/src/util.js
@@ -55,15 +55,15 @@ exports.MIN_INTERVAL_SECOND = 20;
 exports.PING_PACKET_SIZE_MIN = 1;
 exports.PING_PACKET_SIZE_MAX = 65500;
 exports.PING_PACKET_SIZE_DEFAULT = 56;
-exports.PING_DEADLINE_MIN = 0;
+exports.PING_DEADLINE_MIN = 1;
 exports.PING_DEADLINE_MAX = 300;
-exports.PING_DEADLINE_DEFAULT = 0;
+exports.PING_DEADLINE_DEFAULT = 10;
 exports.PING_COUNT_MIN = 1;
 exports.PING_COUNT_MAX = 100;
-exports.PING_COUNT_DEFAULT = 3;
-exports.PING_TIMEOUT_MIN_TIMEOU = 0;
+exports.PING_COUNT_DEFAULT = 1;
+exports.PING_TIMEOUT_MIN = 1;
 exports.PING_TIMEOUT_MAX = 60;
-exports.PING_TIMEOUT_DEFAULT = 0;
+exports.PING_TIMEOUT_DEFAULT = 2;
 exports.CONSOLE_STYLE_Reset = "\x1b[0m";
 exports.CONSOLE_STYLE_Bright = "\x1b[1m";
 exports.CONSOLE_STYLE_Dim = "\x1b[2m";

From 7110c6d30ed75f49e7987358397af1da8699ac80 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Tue, 11 Mar 2025 11:55:27 +0100
Subject: [PATCH 13/17] added binding between ping model parameters and the new
 ping implementation

---
 server/model/monitor.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index 86f53f3b3..40ca4e384 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -635,7 +635,7 @@ class Monitor extends BeanModel {
                     bean.status = UP;
 
                 } else if (this.type === "ping") {
-                    bean.ping = await ping(this.hostname, this.packetSize);
+                    bean.ping = await ping(this.hostname, this.ping_count, "", this.ping_numeric, this.packetSize, this.ping_deadline, this.ping_timeout);
                     bean.msg = "";
                     bean.status = UP;
                 } else if (this.type === "push") {      // Type: Push
@@ -707,7 +707,7 @@ class Monitor extends BeanModel {
                         bean.msg = res.data.response.servers[0].name;
 
                         try {
-                            bean.ping = await ping(this.hostname, this.packetSize);
+                            bean.ping = await ping(this.hostname, PING_COUNT_DEFAULT, "", true, this.packetSize, PING_DEADLINE_DEFAULT, PING_TIMEOUT_DEFAULT);
                         } catch (_) { }
                     } else {
                         throw new Error("Server not found on Steam");

From e8207fed97639304026411eff09d0630c060f822 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Tue, 11 Mar 2025 11:59:02 +0100
Subject: [PATCH 14/17] add ping to timeout field and alphabetically sort
 monitor types

---
 src/pages/EditMonitor.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 19d7c2a9e..abc96bdf7 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -595,8 +595,8 @@
                                 <input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required :min="minInterval" step="1">
                             </div>
 
-                            <!-- Timeout: HTTP / Keyword / SNMP only -->
-                            <div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'snmp' || monitor.type === 'rabbitmq'" class="my-3">
+                            <!-- Timeout: HTTP / JSON query / Keyword / Ping / RabbitMQ / SNMP only -->
+                            <div v-if="monitor.type === 'http' || monitor.type === 'json-query' || monitor.type === 'keyword' || monitor.type === 'ping' || monitor.type === 'rabbitmq' || monitor.type === 'snmp'" class="my-3">
                                 <label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [ monitor.timeout || clampTimeout(monitor.interval) ]) }})</label>
                                 <input id="timeout" v-model="monitor.timeout" type="number" class="form-control" required min="0" step="0.1">
                             </div>

From a769a2d0e0556139e98d90685861b9be241c0b42 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Wed, 12 Mar 2025 12:15:37 +0100
Subject: [PATCH 15/17] removed ping_timeout field in favor of common timeout
 field

---
 .../2025-03-04-0000-ping-advanced-options.js  |   3 -
 server/model/monitor.js                       |  28 +++--
 server/server.js                              |   1 -
 src/lang/en.json                              |   3 +-
 src/pages/EditMonitor.vue                     | 119 ++++++++++++++----
 5 files changed, 118 insertions(+), 36 deletions(-)

diff --git a/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
index 319fb18ef..58b86036a 100644
--- a/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
+++ b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
@@ -2,7 +2,6 @@
 ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
 ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
 ALTER TABLE monitor ADD ping_deadline INTEGER default 10 not null;
-ALTER TABLE monitor ADD ping_timeout INTEGER default 2 not null;
 */
 exports.up = function (knex) {
     // Add new columns to table monitor
@@ -11,7 +10,6 @@ exports.up = function (knex) {
             table.integer("ping_count").defaultTo(1).notNullable();
             table.boolean("ping_numeric").defaultTo(true).notNullable();
             table.integer("ping_deadline").defaultTo(10).notNullable();
-            table.integer("ping_timeout").defaultTo(2).notNullable();
         });
 
 };
@@ -22,6 +20,5 @@ exports.down = function (knex) {
             table.dropColumn("ping_count");
             table.dropColumn("ping_numeric");
             table.dropColumn("ping_deadline");
-            table.dropColumn("ping_timeout");
         });
 };
diff --git a/server/model/monitor.js b/server/model/monitor.js
index 40ca4e384..5f9d08786 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -164,7 +164,6 @@ class Monitor extends BeanModel {
             ping_numeric: this.isPingNumeric(),
             ping_count: this.ping_count,
             ping_deadline: this.ping_deadline,
-            ping_timeout: this.ping_timeout,
         };
 
         if (includeSensitiveData) {
@@ -635,7 +634,7 @@ class Monitor extends BeanModel {
                     bean.status = UP;
 
                 } else if (this.type === "ping") {
-                    bean.ping = await ping(this.hostname, this.ping_count, "", this.ping_numeric, this.packetSize, this.ping_deadline, this.ping_timeout);
+                    bean.ping = await ping(this.hostname, this.ping_count, "", this.ping_numeric, this.packetSize, this.ping_deadline, this.timeout);
                     bean.msg = "";
                     bean.status = UP;
                 } else if (this.type === "push") {      // Type: Push
@@ -1523,16 +1522,25 @@ class Monitor extends BeanModel {
             throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX} (default: ${PING_PACKET_SIZE_DEFAULT})`);
         }
 
-        if (this.ping_deadline && (this.ping_deadline < PING_DEADLINE_MIN || this.ping_deadline > PING_DEADLINE_MAX)) {
-            throw new Error(`Deadline must be between ${PING_DEADLINE_MIN} and ${PING_DEADLINE_MAX} seconds (default: ${PING_DEADLINE_DEFAULT})`);
-        }
+        if (this.type === "ping") {
+            // ping parameters validation
+            if (this.ping_deadline && (this.ping_deadline < PING_DEADLINE_MIN || this.ping_deadline > PING_DEADLINE_MAX)) {
+                throw new Error(`Deadline must be between ${PING_DEADLINE_MIN} and ${PING_DEADLINE_MAX} seconds (default: ${PING_DEADLINE_DEFAULT})`);
+            }
 
-        if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
-            throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX} (default: ${PING_COUNT_DEFAULT})`);
-        }
+            if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
+                throw new Error(`Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX} (default: ${PING_COUNT_DEFAULT})`);
+            }
 
-        if (this.ping_timeout && (this.ping_timeout < PING_TIMEOUT_MIN || this.ping_timeout > PING_TIMEOUT_MAX)) {
-            throw new Error(`Timeout must be between ${PING_TIMEOUT_MIN} and ${PING_TIMEOUT_MAX} seconds (default: ${PING_TIMEOUT_DEFAULT})`);
+            if (this.timeout) {
+                const pingTimeout = Math.round(Number(this.timeout));
+
+                if (pingTimeout < PING_TIMEOUT_MIN || pingTimeout > PING_TIMEOUT_MAX) {
+                    throw new Error(`Timeout must be between ${PING_TIMEOUT_MIN} and ${PING_TIMEOUT_MAX} seconds (default: ${PING_TIMEOUT_DEFAULT})`);
+                }
+
+                this.timeout = pingTimeout;
+            }
         }
     }
 
diff --git a/server/server.js b/server/server.js
index 38b93acf9..41a730b68 100644
--- a/server/server.js
+++ b/server/server.js
@@ -879,7 +879,6 @@ let needSetup = false;
                 bean.ping_numeric = monitor.ping_numeric;
                 bean.ping_count = monitor.ping_count;
                 bean.ping_deadline = monitor.ping_deadline;
-                bean.ping_timeout = monitor.ping_timeout;
 
                 bean.validate();
 
diff --git a/src/lang/en.json b/src/lang/en.json
index 28bca7d87..9a01491c4 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -1059,5 +1059,6 @@
     "pingDeadlineLabel": "Max Duration",
     "pingDeadlineDescription": "Total time in seconds before ping stops, regardless of packets sent",
     "pingTimeoutLabel": "Response Timeout",
-    "pingTimeoutDescription": "Maximum time in seconds to wait for each response"
+    "pingTimeoutDescription": "Maximum time in seconds to wait for each response",
+    "pingIntervalAdjusted": "Interval has been adjusted according to deadline, timeout and packet count"
 }
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index abc96bdf7..fdd87ff26 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -597,8 +597,11 @@
 
                             <!-- Timeout: HTTP / JSON query / Keyword / Ping / RabbitMQ / SNMP only -->
                             <div v-if="monitor.type === 'http' || monitor.type === 'json-query' || monitor.type === 'keyword' || monitor.type === 'ping' || monitor.type === 'rabbitmq' || monitor.type === 'snmp'" class="my-3">
-                                <label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [ monitor.timeout || clampTimeout(monitor.interval) ]) }})</label>
-                                <input id="timeout" v-model="monitor.timeout" type="number" class="form-control" required min="0" step="0.1">
+                                <label for="timeout" class="form-label">
+                                    {{ timeoutLabel }} ({{ monitor.type === 'ping' ? $t("timeoutAfter", [monitor.timeout]) : $t("timeoutAfter", [monitor.timeout || clampTimeout(monitor.interval)]) }})
+                                </label>
+                                <input id="timeout" v-model="monitor.timeout" type="number" class="form-control" :min="timeoutMin" :max="timeoutMax" :step="timeoutStep" required>
+                                <div class="form-text">{{ timeoutDescription }}</div>
                             </div>
 
                             <div class="my-3">
@@ -695,15 +698,6 @@
                                 </div>
                             </div>
 
-                            <!-- Response Timeout -->
-                            <div v-if="monitor.type === 'ping'" class="my-3">
-                                <label for="ping_timeout" class="form-label">{{ $t("pingTimeoutLabel") }}</label>
-                                <input id="ping_timeout" v-model="monitor.ping_timeout" type="number" class="form-control" required min="1" max="60" step="1">
-                                <div class="form-text">
-                                    {{ $t("pingTimeoutDescription") }}
-                                </div>
-                            </div>
-
                             <!-- HTTP / Keyword only -->
                             <template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword' ">
                                 <div class="my-3">
@@ -1200,6 +1194,29 @@ export default {
     },
 
     computed: {
+        timeoutStep() {
+            return this.monitor.type === "ping" ? 1 : 0.1;
+        },
+
+        timeoutMin() {
+            return this.monitor.type === "ping" ? 1 : 0;
+        },
+
+        timeoutMax() {
+            return this.monitor.type === "ping" ? 60 : undefined;
+        },
+
+        timeoutLabel() {
+            return this.monitor.type === "ping" ? this.$t("pingTimeoutLabel") : this.$t("Request Timeout");
+        },
+
+        timeoutDescription() {
+            if (this.monitor.type === "ping") {
+                return this.$t("pingTimeoutDescription");
+            }
+            return "";
+        },
+
         ipRegex() {
 
             // Allow to test with simple dns server with port (127.0.0.1:5300)
@@ -1458,9 +1475,25 @@ message HealthCheckResponse {
         },
 
         "monitor.timeout"(value, oldValue) {
-            // keep timeout within 80% range
-            if (value && value !== oldValue) {
-                this.monitor.timeout = this.clampTimeout(value);
+            if (this.monitor.type === "ping") {
+                this.finishUpdateInterval();
+            } else {
+                // keep timeout within 80% range
+                if (value && value !== oldValue) {
+                    this.monitor.timeout = this.clampTimeout(value);
+                }
+            }
+        },
+
+        "monitor.ping_count"() {
+            if (this.monitor.type === "ping") {
+                this.finishUpdateInterval();
+            }
+        },
+
+        "monitor.ping_deadline"() {
+            if (this.monitor.type === "ping") {
+                this.finishUpdateInterval();
             }
         },
 
@@ -1489,8 +1522,10 @@ message HealthCheckResponse {
             // Set a default timeout if the monitor type has changed or if it's a new monitor
             if (oldType || this.isAdd) {
                 if (this.monitor.type === "snmp") {
-                // snmp is not expected to be executed via the internet => we can choose a lower default timeout
+                    // snmp is not expected to be executed via the internet => we can choose a lower default timeout
                     this.monitor.timeout = 5;
+                } else if (this.monitor.type === "ping") {
+                    this.monitor.timeout = 2;
                 } else {
                     this.monitor.timeout = 48;
                 }
@@ -1612,7 +1647,6 @@ message HealthCheckResponse {
                     ping_numeric: true,
                     packetSize: 56,
                     ping_deadline: 10,
-                    ping_timeout: 2,
                 };
 
                 if (this.$root.proxyList && !this.monitor.proxyId) {
@@ -1675,7 +1709,12 @@ message HealthCheckResponse {
                         }
                         // Handling for monitors that are missing/zeroed timeout
                         if (!this.monitor.timeout) {
-                            this.monitor.timeout = ~~(this.monitor.interval * 8) / 10;
+                            if (this.monitor.type === "ping") {
+                                // set to default
+                                this.monitor.timeout = 2;
+                            } else {
+                                this.monitor.timeout = ~~(this.monitor.interval * 8) / 10;
+                            }
                         }
                     } else {
                         this.$root.toastError(res.msg);
@@ -1888,11 +1927,49 @@ message HealthCheckResponse {
             return Number.isFinite(clamped) ? clamped : maxTimeout;
         },
 
+        calculatePingInterval() {
+            // If monitor.type is not "ping", simply return the configured interval
+            if (this.monitor.type !== "ping") {
+                return this.monitor.interval;
+            }
+
+            // Calculate the maximum theoretical time needed if all ping requests time out
+            const theoreticalTotal = this.monitor.ping_count * this.monitor.timeout;
+
+            // The deadline forces ping to terminate, so the effective limit
+            // is the smaller value between deadline and theoreticalTotal
+            const effectiveLimit = Math.min(this.monitor.ping_deadline, theoreticalTotal);
+
+            // Add a 10% margin to the effective limit to ensure proper handling
+            const adjustedLimit = Math.ceil(effectiveLimit * 1.1);
+
+            // If the calculated limit is less than the minimum allowed interval,
+            // use the minimum interval to ensure stability
+            if (adjustedLimit < this.minInterval) {
+                return this.minInterval;
+            }
+
+            return adjustedLimit;
+        },
+
         finishUpdateInterval() {
-            // Update timeout if it is greater than the clamp timeout
-            let clampedValue = this.clampTimeout(this.monitor.interval);
-            if (this.monitor.timeout > clampedValue) {
-                this.monitor.timeout = clampedValue;
+            if (this.monitor.type === "ping") {
+                // Calculate the minimum required interval based on ping configuration
+                const calculatedPingInterval = this.calculatePingInterval();
+
+                // If the configured interval is too small, adjust it to the minimum required value
+                if (this.monitor.interval < calculatedPingInterval) {
+                    this.monitor.interval = calculatedPingInterval;
+
+                    // Notify the user that the interval has been automatically adjusted
+                    toast.info(this.$t("pingIntervalAdjusted"));
+                }
+            } else {
+                // Update timeout if it is greater than the clamp timeout
+                let clampedValue = this.clampTimeout(this.monitor.interval);
+                if (this.monitor.timeout > clampedValue) {
+                    this.monitor.timeout = clampedValue;
+                }
             }
         },
 

From 59c379ade1b195648bfff963da56b681d32266dc Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Fri, 14 Mar 2025 15:11:06 +0100
Subject: [PATCH 16/17] clarify ping timeout labels and descriptions

---
 src/lang/en.json          | 5 +++--
 src/pages/EditMonitor.vue | 5 +++--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/lang/en.json b/src/lang/en.json
index 9a01491c4..4538d8728 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -1058,7 +1058,8 @@
     "pingNumericDescription": "If checked, IP addresses will be output instead of symbolic hostnames",
     "pingDeadlineLabel": "Max Duration",
     "pingDeadlineDescription": "Total time in seconds before ping stops, regardless of packets sent",
-    "pingTimeoutLabel": "Response Timeout",
-    "pingTimeoutDescription": "Maximum time in seconds to wait for each response",
+    "pingTimeoutLabel": "Per-Ping Timeout",
+    "pingTimeoutDescription": "applies to each individual ping packet sent",
+    "pingTimeoutHelp": "This is the maximum waiting time (in seconds) before considering a single ping packet lost",
     "pingIntervalAdjusted": "Interval has been adjusted according to deadline, timeout and packet count"
 }
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index fdd87ff26..30ce5459b 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -598,10 +598,11 @@
                             <!-- Timeout: HTTP / JSON query / Keyword / Ping / RabbitMQ / SNMP only -->
                             <div v-if="monitor.type === 'http' || monitor.type === 'json-query' || monitor.type === 'keyword' || monitor.type === 'ping' || monitor.type === 'rabbitmq' || monitor.type === 'snmp'" class="my-3">
                                 <label for="timeout" class="form-label">
-                                    {{ timeoutLabel }} ({{ monitor.type === 'ping' ? $t("timeoutAfter", [monitor.timeout]) : $t("timeoutAfter", [monitor.timeout || clampTimeout(monitor.interval)]) }})
+                                    {{ monitor.type === 'ping' ? $t("pingTimeoutLabel") : $t("Request Timeout") }}
+                                    ({{ monitor.type === 'ping' ? $t("pingTimeoutDescription") : $t("timeoutAfter", [monitor.timeout || clampTimeout(monitor.interval)]) }})
                                 </label>
                                 <input id="timeout" v-model="monitor.timeout" type="number" class="form-control" :min="timeoutMin" :max="timeoutMax" :step="timeoutStep" required>
-                                <div class="form-text">{{ timeoutDescription }}</div>
+                                <div v-if="monitor.type === 'ping'" class="form-text">{{ $t("pingTimeoutHelp") }}</div>
                             </div>
 
                             <div class="my-3">

From ab8d6dda75b8a3bd7056c52cc3273199326f8592 Mon Sep 17 00:00:00 2001
From: filippolauria <filippo.lauria@iit.cnr.it>
Date: Mon, 17 Mar 2025 16:15:36 +0100
Subject: [PATCH 17/17] remove deadline field and repurpose timeout semantics

---
 .../2025-03-04-0000-ping-advanced-options.js  |  6 ++--
 server/model/monitor.js                       | 30 ++++++++--------
 server/server.js                              |  2 +-
 server/util-server.js                         | 12 +++----
 src/lang/en.json                              | 11 +++---
 src/pages/EditMonitor.vue                     | 35 +++++++++----------
 src/util.js                                   | 14 ++++----
 src/util.ts                                   | 16 ++++-----
 8 files changed, 62 insertions(+), 64 deletions(-)

diff --git a/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
index 58b86036a..e8bd03e53 100644
--- a/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
+++ b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js
@@ -1,7 +1,7 @@
 /* SQL:
 ALTER TABLE monitor ADD ping_count INTEGER default 1 not null;
 ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null;
-ALTER TABLE monitor ADD ping_deadline INTEGER default 10 not null;
+ALTER TABLE monitor ADD ping_per_request_timeout INTEGER default 2 not null;
 */
 exports.up = function (knex) {
     // Add new columns to table monitor
@@ -9,7 +9,7 @@ exports.up = function (knex) {
         .alterTable("monitor", function (table) {
             table.integer("ping_count").defaultTo(1).notNullable();
             table.boolean("ping_numeric").defaultTo(true).notNullable();
-            table.integer("ping_deadline").defaultTo(10).notNullable();
+            table.integer("ping_per_request_timeout").defaultTo(2).notNullable();
         });
 
 };
@@ -19,6 +19,6 @@ exports.down = function (knex) {
         .alterTable("monitor", function (table) {
             table.dropColumn("ping_count");
             table.dropColumn("ping_numeric");
-            table.dropColumn("ping_deadline");
+            table.dropColumn("ping_per_request_timeout");
         });
 };
diff --git a/server/model/monitor.js b/server/model/monitor.js
index bb303cbec..b9cceaa87 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -4,9 +4,9 @@ const { Prometheus } = require("../prometheus");
 const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
     SQL_DATETIME_FORMAT, evaluateJsonQuery,
     PING_PACKET_SIZE_MIN, PING_PACKET_SIZE_MAX, PING_PACKET_SIZE_DEFAULT,
-    PING_DEADLINE_MIN, PING_DEADLINE_MAX, PING_DEADLINE_DEFAULT,
+    PING_GLOBAL_TIMEOUT_MIN, PING_GLOBAL_TIMEOUT_MAX, PING_GLOBAL_TIMEOUT_DEFAULT,
     PING_COUNT_MIN, PING_COUNT_MAX, PING_COUNT_DEFAULT,
-    PING_TIMEOUT_MIN, PING_TIMEOUT_MAX, PING_TIMEOUT_DEFAULT
+    PING_PER_REQUEST_TIMEOUT_MIN, PING_PER_REQUEST_TIMEOUT_MAX, PING_PER_REQUEST_TIMEOUT_DEFAULT
 } = require("../../src/util");
 const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
     redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
@@ -163,7 +163,7 @@ class Monitor extends BeanModel {
             // ping advanced options
             ping_numeric: this.isPingNumeric(),
             ping_count: this.ping_count,
-            ping_deadline: this.ping_deadline,
+            ping_per_request_timeout: this.ping_per_request_timeout,
         };
 
         if (includeSensitiveData) {
@@ -634,7 +634,7 @@ class Monitor extends BeanModel {
                     bean.status = UP;
 
                 } else if (this.type === "ping") {
-                    bean.ping = await ping(this.hostname, this.ping_count, "", this.ping_numeric, this.packetSize, this.ping_deadline, this.timeout);
+                    bean.ping = await ping(this.hostname, this.ping_count, "", this.ping_numeric, this.packetSize, this.timeout, this.ping_per_request_timeout);
                     bean.msg = "";
                     bean.status = UP;
                 } else if (this.type === "push") {      // Type: Push
@@ -706,7 +706,7 @@ class Monitor extends BeanModel {
                         bean.msg = res.data.response.servers[0].name;
 
                         try {
-                            bean.ping = await ping(this.hostname, PING_COUNT_DEFAULT, "", true, this.packetSize, PING_DEADLINE_DEFAULT, PING_TIMEOUT_DEFAULT);
+                            bean.ping = await ping(this.hostname, PING_COUNT_DEFAULT, "", true, this.packetSize, PING_GLOBAL_TIMEOUT_DEFAULT, PING_PER_REQUEST_TIMEOUT_DEFAULT);
                         } catch (_) { }
                     } else {
                         throw new Error("Server not found on Steam");
@@ -1518,14 +1518,14 @@ class Monitor extends BeanModel {
             throw new Error(`Interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`);
         }
 
-        if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) {
-            throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX} (default: ${PING_PACKET_SIZE_DEFAULT})`);
-        }
-
         if (this.type === "ping") {
             // ping parameters validation
-            if (this.ping_deadline && (this.ping_deadline < PING_DEADLINE_MIN || this.ping_deadline > PING_DEADLINE_MAX)) {
-                throw new Error(`Deadline must be between ${PING_DEADLINE_MIN} and ${PING_DEADLINE_MAX} seconds (default: ${PING_DEADLINE_DEFAULT})`);
+            if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) {
+                throw new Error(`Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX} (default: ${PING_PACKET_SIZE_DEFAULT})`);
+            }
+
+            if (this.ping_per_request_timeout && (this.ping_per_request_timeout < PING_PER_REQUEST_TIMEOUT_MIN || this.ping_per_request_timeout > PING_PER_REQUEST_TIMEOUT_MAX)) {
+                throw new Error(`Per-ping timeout must be between ${PING_PER_REQUEST_TIMEOUT_MIN} and ${PING_PER_REQUEST_TIMEOUT_MAX} seconds (default: ${PING_PER_REQUEST_TIMEOUT_DEFAULT})`);
             }
 
             if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) {
@@ -1533,13 +1533,13 @@ class Monitor extends BeanModel {
             }
 
             if (this.timeout) {
-                const pingTimeout = Math.round(Number(this.timeout));
+                const pingGlobalTimeout = Math.round(Number(this.timeout));
 
-                if (pingTimeout < PING_TIMEOUT_MIN || pingTimeout > PING_TIMEOUT_MAX) {
-                    throw new Error(`Timeout must be between ${PING_TIMEOUT_MIN} and ${PING_TIMEOUT_MAX} seconds (default: ${PING_TIMEOUT_DEFAULT})`);
+                if (pingGlobalTimeout < this.ping_per_request_timeout || pingGlobalTimeout < PING_GLOBAL_TIMEOUT_MIN || pingGlobalTimeout > PING_GLOBAL_TIMEOUT_MAX) {
+                    throw new Error(`Timeout must be between ${PING_GLOBAL_TIMEOUT_MIN} and ${PING_GLOBAL_TIMEOUT_MAX} seconds (default: ${PING_GLOBAL_TIMEOUT_DEFAULT})`);
                 }
 
-                this.timeout = pingTimeout;
+                this.timeout = pingGlobalTimeout;
             }
         }
     }
diff --git a/server/server.js b/server/server.js
index 41a730b68..e1a877fa9 100644
--- a/server/server.js
+++ b/server/server.js
@@ -878,7 +878,7 @@ let needSetup = false;
                 // ping advanced options
                 bean.ping_numeric = monitor.ping_numeric;
                 bean.ping_count = monitor.ping_count;
-                bean.ping_deadline = monitor.ping_deadline;
+                bean.ping_per_request_timeout = monitor.ping_per_request_timeout;
 
                 bean.validate();
 
diff --git a/server/util-server.js b/server/util-server.js
index 5efe9116a..08df728ed 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -3,8 +3,8 @@ const ping = require("@louislam/ping");
 const { R } = require("redbean-node");
 const {
     log, genSecret, badgeConstants,
-    PING_PACKET_SIZE_DEFAULT, PING_DEADLINE_DEFAULT,
-    PING_COUNT_DEFAULT, PING_TIMEOUT_DEFAULT
+    PING_PACKET_SIZE_DEFAULT, PING_GLOBAL_TIMEOUT_DEFAULT,
+    PING_COUNT_DEFAULT, PING_PER_REQUEST_TIMEOUT_DEFAULT
 } = require("../src/util");
 const passwordHash = require("./password-hash");
 const { Resolver } = require("dns");
@@ -137,8 +137,8 @@ exports.ping = async (
     sourceAddr = "",
     numeric = true,
     size = PING_PACKET_SIZE_DEFAULT,
-    deadline = PING_DEADLINE_DEFAULT,
-    timeout = PING_TIMEOUT_DEFAULT,
+    deadline = PING_GLOBAL_TIMEOUT_DEFAULT,
+    timeout = PING_PER_REQUEST_TIMEOUT_DEFAULT,
 ) => {
     try {
         return await exports.pingAsync(destAddr, false, count, sourceAddr, numeric, size, deadline, timeout);
@@ -174,8 +174,8 @@ exports.pingAsync = function (
     sourceAddr = "",
     numeric = true,
     size = PING_PACKET_SIZE_DEFAULT,
-    deadline = PING_DEADLINE_DEFAULT,
-    timeout = PING_TIMEOUT_DEFAULT,
+    deadline = PING_GLOBAL_TIMEOUT_DEFAULT,
+    timeout = PING_PER_REQUEST_TIMEOUT_DEFAULT,
 ) {
     return new Promise((resolve, reject) => {
         ping.promise.probe(destAddr, {
diff --git a/src/lang/en.json b/src/lang/en.json
index ce21769e6..2000d4e99 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -1059,12 +1059,11 @@
     "pingCountDescription": "Number of packets to send before stopping",
     "pingNumericLabel": "Numeric Output",
     "pingNumericDescription": "If checked, IP addresses will be output instead of symbolic hostnames",
-    "pingDeadlineLabel": "Max Duration",
-    "pingDeadlineDescription": "Total time in seconds before ping stops, regardless of packets sent",
-    "pingTimeoutLabel": "Per-Ping Timeout",
-    "pingTimeoutDescription": "applies to each individual ping packet sent",
-    "pingTimeoutHelp": "This is the maximum waiting time (in seconds) before considering a single ping packet lost",
-    "pingIntervalAdjusted": "Interval has been adjusted according to deadline, timeout and packet count",
+    "pingGlobalTimeoutLabel": "Global Timeout",
+    "pingGlobalTimeoutDescription": "Total time in seconds before ping stops, regardless of packets sent",
+    "pingPerRequestTimeoutLabel": "Per-Ping Timeout",
+    "pingPerRequestTimeoutDescription": "This is the maximum waiting time (in seconds) before considering a single ping packet lost",
+    "pingIntervalAdjustedInfo": "Interval adjusted based on packet count, global timeout and per-ping timeout",
     "YZJ Webhook URL": "YZJ Webhook URL",
     "YZJ Robot Token": "YZJ Robot token",
     "Plain Text": "Plain Text",
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 30ce5459b..63225ee6f 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -598,11 +598,11 @@
                             <!-- Timeout: HTTP / JSON query / Keyword / Ping / RabbitMQ / SNMP only -->
                             <div v-if="monitor.type === 'http' || monitor.type === 'json-query' || monitor.type === 'keyword' || monitor.type === 'ping' || monitor.type === 'rabbitmq' || monitor.type === 'snmp'" class="my-3">
                                 <label for="timeout" class="form-label">
-                                    {{ monitor.type === 'ping' ? $t("pingTimeoutLabel") : $t("Request Timeout") }}
-                                    ({{ monitor.type === 'ping' ? $t("pingTimeoutDescription") : $t("timeoutAfter", [monitor.timeout || clampTimeout(monitor.interval)]) }})
+                                    {{ monitor.type === 'ping' ? $t("pingGlobalTimeoutLabel") : $t("Request Timeout") }}
+                                    <span v-if="monitor.type !== 'ping'">({{ $t("timeoutAfter", [monitor.timeout || clampTimeout(monitor.interval)]) }})</span>
                                 </label>
                                 <input id="timeout" v-model="monitor.timeout" type="number" class="form-control" :min="timeoutMin" :max="timeoutMax" :step="timeoutStep" required>
-                                <div v-if="monitor.type === 'ping'" class="form-text">{{ $t("pingTimeoutHelp") }}</div>
+                                <div v-if="monitor.type === 'ping'" class="form-text">{{ $t("pingGlobalTimeoutDescription") }}</div>
                             </div>
 
                             <div class="my-3">
@@ -690,12 +690,12 @@
                                 <input id="packet-size" v-model="monitor.packetSize" type="number" class="form-control" required min="1" :max="65500" step="1">
                             </div>
 
-                            <!-- Max Duration / Deadline -->
+                            <!-- per-request timeout -->
                             <div v-if="monitor.type === 'ping'" class="my-3">
-                                <label for="ping_deadline" class="form-label">{{ $t("pingDeadlineLabel") }}</label>
-                                <input id="ping_deadline" v-model="monitor.ping_deadline" type="number" class="form-control" required min="0" max="300" step="1">
+                                <label for="ping_per_request_timeout" class="form-label">{{ $t("pingPerRequestTimeoutLabel") }}</label>
+                                <input id="ping_per_request_timeout" v-model="monitor.ping_per_request_timeout" type="number" class="form-control" required min="0" max="300" step="1">
                                 <div class="form-text">
-                                    {{ $t("pingDeadlineDescription") }}
+                                    {{ $t("pingPerRequestTimeoutDescription") }}
                                 </div>
                             </div>
 
@@ -1492,7 +1492,7 @@ message HealthCheckResponse {
             }
         },
 
-        "monitor.ping_deadline"() {
+        "monitor.ping_per_request_timeout"() {
             if (this.monitor.type === "ping") {
                 this.finishUpdateInterval();
             }
@@ -1526,7 +1526,7 @@ message HealthCheckResponse {
                     // snmp is not expected to be executed via the internet => we can choose a lower default timeout
                     this.monitor.timeout = 5;
                 } else if (this.monitor.type === "ping") {
-                    this.monitor.timeout = 2;
+                    this.monitor.timeout = 10;
                 } else {
                     this.monitor.timeout = 48;
                 }
@@ -1647,7 +1647,7 @@ message HealthCheckResponse {
                     ping_count: 3,
                     ping_numeric: true,
                     packetSize: 56,
-                    ping_deadline: 10,
+                    ping_per_request_timeout: 2,
                 };
 
                 if (this.$root.proxyList && !this.monitor.proxyId) {
@@ -1712,7 +1712,7 @@ message HealthCheckResponse {
                         if (!this.monitor.timeout) {
                             if (this.monitor.type === "ping") {
                                 // set to default
-                                this.monitor.timeout = 2;
+                                this.monitor.timeout = 10;
                             } else {
                                 this.monitor.timeout = ~~(this.monitor.interval * 8) / 10;
                             }
@@ -1934,18 +1934,17 @@ message HealthCheckResponse {
                 return this.monitor.interval;
             }
 
-            // Calculate the maximum theoretical time needed if all ping requests time out
-            const theoreticalTotal = this.monitor.ping_count * this.monitor.timeout;
+            // Calculate the maximum theoretical time needed if every ping request times out
+            const theoreticalTotal = this.monitor.ping_count * this.monitor.ping_per_request_timeout;
 
-            // The deadline forces ping to terminate, so the effective limit
+            // The global timeout (aka deadline) forces ping to terminate, so the effective limit
             // is the smaller value between deadline and theoreticalTotal
-            const effectiveLimit = Math.min(this.monitor.ping_deadline, theoreticalTotal);
+            const effectiveLimit = Math.min(this.monitor.timeout, theoreticalTotal);
 
             // Add a 10% margin to the effective limit to ensure proper handling
             const adjustedLimit = Math.ceil(effectiveLimit * 1.1);
 
-            // If the calculated limit is less than the minimum allowed interval,
-            // use the minimum interval to ensure stability
+            // If the calculated limit is lower than the minimum allowed interval, use the minimum interval
             if (adjustedLimit < this.minInterval) {
                 return this.minInterval;
             }
@@ -1963,7 +1962,7 @@ message HealthCheckResponse {
                     this.monitor.interval = calculatedPingInterval;
 
                     // Notify the user that the interval has been automatically adjusted
-                    toast.info(this.$t("pingIntervalAdjusted"));
+                    toast.info(this.$t("pingIntervalAdjustedInfo"));
                 }
             } else {
                 // Update timeout if it is greater than the clamp timeout
diff --git a/src/util.js b/src/util.js
index 62ebba01c..f094cdd63 100644
--- a/src/util.js
+++ b/src/util.js
@@ -32,7 +32,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
 };
 var _a;
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.PING_TIMEOUT_DEFAULT = exports.PING_TIMEOUT_MAX = exports.PING_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_DEADLINE_DEFAULT = exports.PING_DEADLINE_MAX = exports.PING_DEADLINE_MIN = exports.PING_PACKET_SIZE_DEFAULT = exports.PING_PACKET_SIZE_MAX = exports.PING_PACKET_SIZE_MIN = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
+exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.PING_PER_REQUEST_TIMEOUT_DEFAULT = exports.PING_PER_REQUEST_TIMEOUT_MAX = exports.PING_PER_REQUEST_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_GLOBAL_TIMEOUT_DEFAULT = exports.PING_GLOBAL_TIMEOUT_MAX = exports.PING_GLOBAL_TIMEOUT_MIN = exports.PING_PACKET_SIZE_DEFAULT = exports.PING_PACKET_SIZE_MAX = exports.PING_PACKET_SIZE_MIN = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
 exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = void 0;
 const dayjs_1 = __importDefault(require("dayjs"));
 const jsonata = __importStar(require("jsonata"));
@@ -55,15 +55,15 @@ exports.MIN_INTERVAL_SECOND = 20;
 exports.PING_PACKET_SIZE_MIN = 1;
 exports.PING_PACKET_SIZE_MAX = 65500;
 exports.PING_PACKET_SIZE_DEFAULT = 56;
-exports.PING_DEADLINE_MIN = 1;
-exports.PING_DEADLINE_MAX = 300;
-exports.PING_DEADLINE_DEFAULT = 10;
+exports.PING_GLOBAL_TIMEOUT_MIN = 1;
+exports.PING_GLOBAL_TIMEOUT_MAX = 300;
+exports.PING_GLOBAL_TIMEOUT_DEFAULT = 10;
 exports.PING_COUNT_MIN = 1;
 exports.PING_COUNT_MAX = 100;
 exports.PING_COUNT_DEFAULT = 1;
-exports.PING_TIMEOUT_MIN = 1;
-exports.PING_TIMEOUT_MAX = 60;
-exports.PING_TIMEOUT_DEFAULT = 2;
+exports.PING_PER_REQUEST_TIMEOUT_MIN = 1;
+exports.PING_PER_REQUEST_TIMEOUT_MAX = 60;
+exports.PING_PER_REQUEST_TIMEOUT_DEFAULT = 2;
 exports.CONSOLE_STYLE_Reset = "\x1b[0m";
 exports.CONSOLE_STYLE_Bright = "\x1b[1m";
 exports.CONSOLE_STYLE_Dim = "\x1b[2m";
diff --git a/src/util.ts b/src/util.ts
index 4d215e94e..277d860f4 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -44,20 +44,20 @@ export const PING_PACKET_SIZE_MIN = 1;
 export const PING_PACKET_SIZE_MAX = 65500;
 export const PING_PACKET_SIZE_DEFAULT = 56;
 
-// Deadline limits (in seconds)
-export const PING_DEADLINE_MIN = 1;
-export const PING_DEADLINE_MAX = 300;
-export const PING_DEADLINE_DEFAULT = 10;
+// Global timeout (aka deadline) limits in seconds
+export const PING_GLOBAL_TIMEOUT_MIN = 1;
+export const PING_GLOBAL_TIMEOUT_MAX = 300;
+export const PING_GLOBAL_TIMEOUT_DEFAULT = 10;
 
 // Ping count limits
 export const PING_COUNT_MIN = 1;
 export const PING_COUNT_MAX = 100;
 export const PING_COUNT_DEFAULT = 1;
 
-// Timeout limits (in seconds)
-export const PING_TIMEOUT_MIN = 1;
-export const PING_TIMEOUT_MAX = 60;
-export const PING_TIMEOUT_DEFAULT = 2;
+// per-request timeout (aka timeout) limits in seconds
+export const PING_PER_REQUEST_TIMEOUT_MIN = 1;
+export const PING_PER_REQUEST_TIMEOUT_MAX = 60;
+export const PING_PER_REQUEST_TIMEOUT_DEFAULT = 2;
 
 // Console colors
 // https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color