From 70d1d9a96498a6c37cfcc3b9ae106edc81b2e9c4 Mon Sep 17 00:00:00 2001 From: maryamsaleem <maryamsaleem874@gmail.com> Date: Thu, 6 Feb 2025 14:57:09 +0500 Subject: [PATCH] lint issues resolved --- server/model/monitor.js | 25 ++- server/notification-providers/sip.js | 53 ++++-- server/util-server.js | 84 +++++---- src/modules/dayjs/plugin/timezone/index.d.ts | 10 +- src/pages/EditMonitor.vue | 176 ++++++++++--------- 5 files changed, 202 insertions(+), 146 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index fed3278de..67d711e7e 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -5,7 +5,7 @@ const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MI SQL_DATETIME_FORMAT, evaluateJsonQuery } = require("../../src/util"); const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery, - redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal, sipRegisterRequest,sipOptionRequest + redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal, sipRegisterRequest, sipOptionRequest } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); @@ -315,9 +315,9 @@ class Monitor extends BeanModel { sipUrl: this.sipUrl, sipPort: this.sipPort, sipProtocol: this.sipProtocol, - sipMethod: this.sipMethod, - sipMaintainence: this.isSipMaintainence(), - sipAuthMethod: this.sipAuthMethod, + sipMethod: this.sipMethod, + sipMaintainence: this.isSipMaintainence(), + sipAuthMethod: this.sipAuthMethod, }; if (includeSensitiveData) { @@ -483,6 +483,7 @@ class Monitor extends BeanModel { getKafkaProducerAllowAutoTopicCreation() { return Boolean(this.kafkaProducerAllowAutoTopicCreation); } + /** * Parse to boolean * @returns {boolean} Sip Allow Maintainenece Option @@ -490,6 +491,7 @@ class Monitor extends BeanModel { isSipMaintainence() { return Boolean(this.sipMaintainence); } + /** * Start monitor * @param {Server} io Socket server instance @@ -1052,12 +1054,11 @@ class Monitor extends BeanModel { let sipMessage; let startTime = dayjs().valueOf(); let totalResponseTime; - let requestCount; if (this.sipMethod !== "OPTIONS") { sipResponse = await sipRegisterRequest(this.sipUrl, this.sipPort, this.sipProtocol, this.sip_basic_auth_user, this.sip_basic_auth_pass, version); let sipResponseTime = dayjs().valueOf() - startTime; totalResponseTime += sipResponseTime; - console.log("sipResponse", sipResponse); + console.log("sipResponse", totalResponseTime); console.log("this.sipMaintainence", this.sipMaintainence); const matchingStatus = sipStatusCodes.find(code => code.status === sipResponse?.status); if (matchingStatus) { @@ -1066,7 +1067,7 @@ class Monitor extends BeanModel { bean.status = sipResponse?.status === 200 ? UP : DOWN; console.log("sipResponse?.status", sipResponse?.status); // Additional check for 503 status within matchingStatus - if (sipResponse?.status === 503 && this.sipMaintainence == 1) { + if (sipResponse?.status === 503 && this.sipMaintainence === 1) { sipMessage = "Monitor under maintenance"; bean.status = MAINTENANCE; } @@ -1074,12 +1075,9 @@ class Monitor extends BeanModel { sipMessage = ` ${sipResponse?.status}-Not Ok`; bean.status = DOWN; } - + } else if (this.sipMethod === "OPTIONS") { sipResponse = await sipOptionRequest(this.sipUrl, this.sipPort, this.sipProtocol, this.sip_basic_auth_user, this.sip_basic_auth_pass, version); - let sipOptionsResponseTime = dayjs().valueOf() - startTime; - totalResponseTime = sipOptionsResponseTime; - requestCount++; console.log("=====resposne status", sipResponse?.status); console.log("this.sipMaintainence", this.sipMaintainence); const matchingStatus = sipStatusCodes.find(code => code.status === sipResponse?.status); @@ -1089,7 +1087,7 @@ class Monitor extends BeanModel { bean.status = sipResponse?.status === 200 ? UP : DOWN; // Additional check for 503 status within matchingStatus - if (sipResponse?.status === 503 && this.sipMaintainence == 1) { + if (sipResponse?.status === 503 && this.sipMaintainence === 1) { sipMessage = "Monitor under maintenance"; bean.status = MAINTENANCE; } @@ -1105,8 +1103,7 @@ class Monitor extends BeanModel { bean.msg = `Error: ${error.message}`; bean.status = DOWN; } - } - else { + } else { throw new Error("Unknown Monitor Type"); } diff --git a/server/notification-providers/sip.js b/server/notification-providers/sip.js index 19c8959ce..86c1e2687 100644 --- a/server/notification-providers/sip.js +++ b/server/notification-providers/sip.js @@ -2,14 +2,29 @@ const NotificationProvider = require("./notification-provider"); class SIP extends NotificationProvider { name = "sip"; + + /** + * Sends a SIP notification message. + * @param {object} notification - SIP notification configuration. + * @param {string} msg - The message content. + * @param {object | null} monitorJSON - The monitor data (if available). + * @param {object | null} heartbeatJSON - The heartbeat data (if available). + * @returns {Promise<string>} - Confirmation message if successful. + * @throws {Error} - If sending the SIP message fails. + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let monitorName = monitorJSON ? monitorJSON["name"] : "Unknown Monitor"; - let subject = this.updateSubject(msg, monitorName); - let body; + let body = ""; + if (heartbeatJSON) { body += `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`; } + try { + console.log("Sending SIP message:", { notification, + msg, + monitorName, + body }); return "SIP Message Sent Successfully."; } catch (error) { console.error("Error sending SIP message:", error); @@ -17,44 +32,58 @@ class SIP extends NotificationProvider { } } + /** + * Generates a subject line based on the message content. + * @param {string} message - The incoming status message. + * @param {string} monitorName - The name of the monitored service. + * @returns {string} - The formatted subject line. + */ updateSubject(message, monitorName) { - if (!message) return "Default Subject"; // Handle null/undefined message + if (!message) { + return "Default Subject"; + } message = message.toLowerCase(); // Normalize input if (/\bdown\b/i.test(message) || message.includes("offline")) { - return `🚨 ❌ Service Impacted...`; + return "🚨 ❌ Service Impacted..."; } if (/\bup\b/i.test(message) || message.includes("online")) { - return `🚨 ✅ Service Restored...`; + return "🚨 ✅ Service Restored..."; } if (message.includes("maintenance")) { if (message.includes("begin")) { - return `🚧 🔧 ❌ Maintenance Start...`; + return "🚧 🔧 ❌ Maintenance Start..."; } if (/\bend\b/i.test(message)) { - return `🚧 🔧 ✅ Maintenance Complete...`; + return "🚧 🔧 ✅ Maintenance Complete..."; } if (message.includes("scheduled")) { - return `🚧 🪟 📆 Maintenance Window Scheduled...`; + return "🚧 🪟 📆 Maintenance Window Scheduled..."; } if (message.includes("window begin")) { - return `🚧 🪟 🛑 Maintenance Window Start...`; + return "🚧 🪟 🛑 Maintenance Window Start..."; } if (message.includes("window end")) { - return `🚧 🪟 ✅ Maintenance Window Complete...`; + return "🚧 🪟 ✅ Maintenance Window Complete..."; } } if (message.includes("started on node")) { - return `📈 🔬 ✅ Monitoring Start...`; + return "📈 🔬 ✅ Monitoring Start..."; } if (message.includes("started")) { - return `📈 🔬 ✅ ${monitorName}`; + return `📈 🔬 ✅ ${monitorName}`; } return "Default Subject"; } + /** + * Sends a SIP message using the provided notification configuration. + * @param {object} notification - SIP notification settings. + * @param {string} sipMessage - The message content to send. + * @returns {void} + */ async sendSIPMessage(notification, sipMessage) { console.log("Sending SIP message with config:", notification); console.log("Message:", sipMessage); diff --git a/server/util-server.js b/server/util-server.js index 9101d3e30..4534b9198 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -33,8 +33,6 @@ const { Kafka, SASLOptions } = require("kafkajs"); const crypto = require("crypto"); let sip = require("sip"); const uuid = require("uuid"); -let sharedSipServer; -const SERVER_PORT = 25060; const isWindows = process.platform === /^win/.test(process.platform); /** @@ -266,23 +264,28 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa /** * Sends a SIP REGISTER request * @param {string} sipServer The SIP server to register with + * @param {number} sipPort The port of the SIP server * @param {string} transport The transport protocol to use (e.g., 'udp' or 'tcp') - * @returns {Promise<void>} + * @param {string} username The username for registration + * @param {string} password The password for registration + * @param {string} version The version of the SIP health monitor + * @returns {Promise<object>} The response from the SIP REGISTER request */ exports.sipRegisterRequest = function (sipServer, sipPort, transport, username, password, version) { - + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { try { const registerRequest = { - method: 'REGISTER', + method: "REGISTER", uri: `sip:${sipServer}:${sipPort}`, headers: { to: { uri: `sip:${sipServer}:${sipPort}` }, from: { uri: `sip:${username}` }, - 'call-id': uuid.v4(), - cseq: { method: 'REGISTER', seq: 1 }, - 'content-length': 0, + "call-id": uuid.v4(), + cseq: { method: "REGISTER", + seq: 1 }, + "content-length": 0, contact: { uri: `sip:${username}` }, "User-Agent": "SIP Health Monitor " + version, "Expires": 60, @@ -321,9 +324,16 @@ exports.sipRegister = function (registerRequest) { return new Promise((resolve, reject) => { const timeout = 5000; // Timeout duration in milliseconds let timeoutID; - // Cleanup function to clear the timeout and destroy the server + // Cleanup function to ensure proper resource management + /** + * Clears the timeout and destroys the SIP server instance. + * This function is called to prevent memory leaks and ensure that no lingering processes are left running. + * @returns {void} This function does not return any value. + */ function cleanup() { - if (timeoutID) clearTimeout(timeoutID); + if (timeoutID) { + clearTimeout(timeoutID); + } if (server && server.destroy) { server.destroy(); console.log("SIP server destroyed."); @@ -378,8 +388,12 @@ exports.constructAuthorizedRequest = function (request, username, password, prox /** * Sends a SIP OPTIONS request * @param {string} sipServer The SIP server to send OPTIONS to + * @param {number} sipPort The port of the SIP server * @param {string} transport The transport protocol to use (e.g., 'udp' or 'tcp') - * @returns {Promise<void>} + * @param {string} username The username for authentication (optional) + * @param {string} password The password for authentication (optional) + * @param {string} version The version of the SIP Health Monitor + * @returns {Promise<object>} The response from the SIP OPTIONS request */ exports.sipOptionRequest = function (sipServer, sipPort, transport, username, password, version) { const publicIP = process.env.PUBLIC_IP; @@ -387,28 +401,28 @@ exports.sipOptionRequest = function (sipServer, sipPort, transport, username, pa return new Promise(async (resolve, reject) => { try { const optionsRequest = { - method: 'OPTIONS', - uri: `sip:${sipServer}:${sipPort}`,//hostname + method: "OPTIONS", + uri: `sip:${sipServer}:${sipPort}`, //hostname headers: { - to: { uri: `sip:${sipServer}:${sipPort}` },//hostname - from: { uri: `sip:${publicIP}` },//live ip || primary url - 'call-id': 1234, - cseq: { method: 'OPTIONS', seq: 1 }, - 'content-length': 0, - contact: [ { uri: `sip:${publicIP}` }], + to: { uri: `sip:${sipServer}:${sipPort}` }, //hostname + from: { uri: `sip:${publicIP}` }, //live ip || primary url + "call-id": 1234, + cseq: { method: "OPTIONS", + seq: 1 }, + "content-length": 0, + contact: [{ uri: `sip:${publicIP}` }], "User-Agent": "SIP Health Monitor" + version, }, transport: transport, }; - let optionResponse - if(!username) { - console.log("will only send ok") + let optionResponse; + if (!username) { + console.log("will only send ok"); const optionResponse = await exports.sipOption(optionsRequest); console.log("optionResponse", optionResponse); - resolve(optionResponse) - } - else { + resolve(optionResponse); + } else { optionResponse = await exports.sipRegister(optionsRequest); console.log("optionResponse", optionResponse); if (optionResponse.status === 407 && optionResponse.headers["proxy-authenticate"]) { @@ -419,12 +433,12 @@ exports.sipOptionRequest = function (sipServer, sipPort, transport, username, pa password, proxyAuthenticateHeader ); - + const secondResponse = await exports.sipOption(authorizedOptionRequest); resolve(secondResponse); - } + } } - + } catch (error) { console.error("Error:", error.message); reject(error); @@ -439,18 +453,24 @@ exports.sipOption = function (optionsRequest) { console.log("SIP server created:", server); return new Promise((resolve, reject) => { - const timeout = 5000; // Timeout duration in milliseconds + let timeoutID; - // Cleanup function to clear the timeout and destroy the server + // Cleanup function to ensure proper resource management + /** + * Clears the timeout and destroys the SIP server instance. + * This function is called to prevent memory leaks and ensure that no lingering processes are left running. + * @returns {void} This function does not return any value. + */ function cleanup() { - if (timeoutID) clearTimeout(timeoutID); + if (timeoutID) { + clearTimeout(timeoutID); + } if (server) { server.destroy(); console.log("SIP server destroyed."); } } - try { // Send the SIP options request server.send(optionsRequest, (response) => { diff --git a/src/modules/dayjs/plugin/timezone/index.d.ts b/src/modules/dayjs/plugin/timezone/index.d.ts index 8d9035905..e9e5a48f8 100644 --- a/src/modules/dayjs/plugin/timezone/index.d.ts +++ b/src/modules/dayjs/plugin/timezone/index.d.ts @@ -1,12 +1,12 @@ -import { PluginFunc, ConfigType } from 'dayjs/esm' +import { PluginFunc, ConfigType } from "dayjs/esm"; -declare const plugin: PluginFunc +declare const plugin: PluginFunc; export = plugin -declare module 'dayjs/esm' { +declare module "dayjs/esm" { interface Dayjs { tz(timezone?: string, keepLocalTime?: boolean): Dayjs - offsetName(type?: 'short' | 'long'): string | undefined + offsetName(type?: "short" | "long"): string | undefined } interface DayjsTimezone { @@ -16,5 +16,5 @@ declare module 'dayjs/esm' { setDefault(timezone?: string): void } - const tz: DayjsTimezone + const tz: DayjsTimezone; } diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 745969a56..c83887d1a 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -91,7 +91,7 @@ <option v-if="!$root.info.isContainer" value="tailscale-ping"> Tailscale Ping </option> - <option value="sip">SIP</option> + <option value="sip">SIP</option> </optgroup> </select> <i18n-t v-if="monitor.type === 'rabbitmq'" keypath="rabbitmqHelpText" tag="div" class="form-text"> @@ -121,10 +121,10 @@ <!--SIP--> <div v-if="monitor.type === 'sip'" class="my-3"> <label for="sipprotocol" class="form-label">{{ $t("SipProtocol") }}</label> - <select id="sipprotocol" class="form-select" required v-model="monitor.sipProtocol"> - <option value="UDP">UDP</option> - <option value="TCP">TCP</option> - <option value="TLS">TLS</option> + <select id="sipprotocol" v-model="monitor.sipProtocol" class="form-select" required> + <option value="UDP">UDP</option> + <option value="TCP">TCP</option> + <option value="TLS">TLS</option> </select> </div> <!-- gRPC URL --> @@ -175,8 +175,10 @@ </div> <div v-if="monitor.type === 'sip'" class="my-3"> <label for="sipport" class="form-label mt-3">{{ $t("SipPort") }}</label> - <input v-if="monitor.sipProtocol !== 'SRV'" id="sipport" type="number" class="form-control" - v-model="monitor.sipPort" placeholder="Enter SIP Port"> + <input + v-if="monitor.sipProtocol !== 'SRV'" id="sipport" v-model="monitor.sipPort" type="number" + class="form-control" placeholder="Enter SIP Port" + > </div> <!-- Remote Browser --> <div v-if="monitor.type === 'real-browser'" class="my-3"> @@ -637,7 +639,7 @@ <input id="expiry-notification" v-model="monitor.expiryNotification" class="form-check-input" type="checkbox" :disabled="monitor.ignoreTls"> <label class="form-check-label" for="expiry-notification"> {{ $t("Certificate Expiry Notification") }} - </label> + </label> <div class="form-text"> </div> </div> @@ -681,8 +683,8 @@ <label class="form-check-label" for="process-503-as-maintenance"> {{ $t("process503AsMaintenanceLabel") }} </label> - </div> - + </div> + <div v-if="monitor.type === 'gamedig'" class="my-3 form-check"> <input id="gamedig-guess-port" v-model="monitor.gamedigGivenPortOnly" :true-value="false" :false-value="true" class="form-check-input" type="checkbox"> <label class="form-check-label" for="gamedig-guess-port"> @@ -858,83 +860,91 @@ </div> </div> </template> - <!-- SIP Options --> - - <template v-if="monitor.type === 'sip' - "> - <h2 class="mt-5 mb-2">{{ $t("SIP Options") }}</h2> + <!-- SIP Options --> - <!-- Method --> - <div class="my-3"> - <label for="method" class="form-label">{{ - $t("Method") - }}</label> - <select id="method" v-model="monitor.sipMethod" class="form-select"> - - <option value="REGISTER">REGISTER</option> - <option value="OPTIONS">OPTIONS</option> - </select> - </div> + <template + v-if="monitor.type === 'sip' + " + > + <h2 class="mt-5 mb-2">{{ $t("SIP Options") }}</h2> - <!-- Encoding --> - <div class="my-3"> - <label for="httpBodyEncoding" class="form-label">{{ - $t("Body Encoding") - }}</label> - <select id="httpBodyEncoding" v-model="monitor.httpBodyEncoding" class="form-select"> - <option value="json">JSON</option> - <option value="xml">XML</option> - </select> - </div> + <!-- Method --> + <div class="my-3"> + <label for="method" class="form-label">{{ + $t("Method") + }}</label> + <select id="method" v-model="monitor.sipMethod" class="form-select"> + <option value="REGISTER">REGISTER</option> + <option value="OPTIONS">OPTIONS</option> + </select> + </div> - <!-- Body --> - <div class="my-3"> - <label for="body" class="form-label">{{ $t("Body") }}</label> - <textarea id="body" v-model="monitor.body" class="form-control" - :placeholder="bodyPlaceholder"></textarea> - </div> + <!-- Encoding --> + <div class="my-3"> + <label for="httpBodyEncoding" class="form-label">{{ + $t("Body Encoding") + }}</label> + <select id="httpBodyEncoding" v-model="monitor.httpBodyEncoding" class="form-select"> + <option value="json">JSON</option> + <option value="xml">XML</option> + </select> + </div> - <!-- Headers --> - <div class="my-3"> - <label for="headers" class="form-label">{{ - $t("Headers") - }}</label> - <textarea id="headers" v-model="monitor.headers" class="form-control" - :placeholder="headersPlaceholder"></textarea> - </div> + <!-- Body --> + <div class="my-3"> + <label for="body" class="form-label">{{ $t("Body") }}</label> + <textarea + id="body" v-model="monitor.body" class="form-control" + :placeholder="bodyPlaceholder" + ></textarea> + </div> - <!-- HTTP Auth --> - <h4 class="mt-5 mb-2">{{ $t("Authentication") }}</h4> + <!-- Headers --> + <div class="my-3"> + <label for="headers" class="form-label">{{ + $t("Headers") + }}</label> + <textarea + id="headers" v-model="monitor.headers" class="form-control" + :placeholder="headersPlaceholder" + ></textarea> + </div> - <!-- Method --> - <div class="my-3"> - <label for="authmethod" class="form-label">{{ - $t("Method") - }}</label> - <select id="authsipmethod" v-model="monitor.sipAuthMethod" class="form-select"> - <option :value="null"> - {{ $t("None") }} - </option> - <option value="basic"> - {{ $t("SIP Basic Auth") }} - </option> - - </select> - </div> - <template v-if="monitor.sipAuthMethod === 'basic'"> - <div class="my-3"> - <label for="basicauth-user" class="form-label">{{ $t("Username") }}</label> - <input id="basicauth-user" v-model="monitor.sip_basic_auth_user" type="text" class="form-control" - :placeholder="$t('Username')" /> - </div> + <!-- HTTP Auth --> + <h4 class="mt-5 mb-2">{{ $t("Authentication") }}</h4> - <div class="my-3"> - <label for="basicauth-pass" class="form-label">{{ $t("Password") }}</label> - <input id="basicauth-pass" v-model="monitor.sip_basic_auth_pass" type="password" autocomplete="new-password" - class="form-control" :placeholder="$t('Password')" /> - </div> -</template> - </template> + <!-- Method --> + <div class="my-3"> + <label for="authmethod" class="form-label">{{ + $t("Method") + }}</label> + <select id="authsipmethod" v-model="monitor.sipAuthMethod" class="form-select"> + <option :value="null"> + {{ $t("None") }} + </option> + <option value="basic"> + {{ $t("SIP Basic Auth") }} + </option> + </select> + </div> + <template v-if="monitor.sipAuthMethod === 'basic'"> + <div class="my-3"> + <label for="basicauth-user" class="form-label">{{ $t("Username") }}</label> + <input + id="basicauth-user" v-model="monitor.sip_basic_auth_user" type="text" class="form-control" + :placeholder="$t('Username')" + /> + </div> + + <div class="my-3"> + <label for="basicauth-pass" class="form-label">{{ $t("Password") }}</label> + <input + id="basicauth-pass" v-model="monitor.sip_basic_auth_pass" type="password" autocomplete="new-password" + class="form-control" :placeholder="$t('Password')" + /> + </div> + </template> + </template> <!-- HTTP Options --> <template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' "> <h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2> @@ -1223,10 +1233,10 @@ const monitorDefaults = { conditions: [], sipProtocol: "UDP", sipPort: 5060, - sipUrl:null, + sipUrl: null, sipMethod: "OPTIONS", - sipMaintainence:false, - sipAuthMethod: null, + sipMaintainence: false, + sipAuthMethod: null, }; export default {