mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-04-20 15:46:02 -04:00
Merge aee2a0791f295e219c94d966f8fe8c574f9837fa into 0fc6998a31dcfa78bc0dfa8eea2cdcd4ed30bd1a
This commit is contained in:
commit
7ffcb32dcf
67
db/old_migrations/patch-add-sip-fields.sql
Normal file
67
db/old_migrations/patch-add-sip-fields.sql
Normal file
@ -0,0 +1,67 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE monitor
|
||||
|
||||
ADD sip_auth_method VARCHAR(10) default null;
|
||||
|
||||
COMMIT;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE monitor
|
||||
|
||||
ADD sip_protocol VARCHAR(10);
|
||||
|
||||
COMMIT;
|
||||
|
||||
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE monitor
|
||||
|
||||
ADD sip_port INT;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE monitor
|
||||
|
||||
ADD sip_url VARCHAR(255);
|
||||
|
||||
|
||||
|
||||
COMMIT;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE monitor
|
||||
|
||||
ADD sip_maintainence BOOLEAN;
|
||||
|
||||
|
||||
|
||||
COMMIT;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
|
||||
|
||||
ALTER TABLE monitor
|
||||
|
||||
ADD COLUMN sip_method VARCHAR(250) NULL;
|
||||
|
||||
|
||||
|
||||
COMMIT;
|
11
db/old_migrations/patch-sip-auth.sql
Normal file
11
db/old_migrations/patch-sip-auth.sql
Normal file
@ -0,0 +1,11 @@
|
||||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD sip_basic_auth_user TEXT default null;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD sip_basic_auth_pass TEXT default null;
|
||||
|
||||
|
||||
COMMIT;
|
65
package-lock.json
generated
65
package-lock.json
generated
@ -75,6 +75,7 @@
|
||||
"redbean-node": "~0.3.0",
|
||||
"redis": "~4.5.1",
|
||||
"semver": "~7.5.4",
|
||||
"sip": "^0.0.6",
|
||||
"socket.io": "~4.8.0",
|
||||
"socket.io-client": "~4.8.0",
|
||||
"socks-proxy-agent": "~8.0.5",
|
||||
@ -82,6 +83,7 @@
|
||||
"tcp-ping": "~0.1.1",
|
||||
"thirty-two": "~1.0.2",
|
||||
"tough-cookie": "~4.1.3",
|
||||
"uuid": "^11.0.5",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -1212,6 +1214,14 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-node/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
||||
@ -5270,6 +5280,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/aedes/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
@ -5686,6 +5705,11 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"node_modules/async-lock": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz",
|
||||
@ -10173,6 +10197,15 @@
|
||||
"uuid-parse": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hyperid/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
@ -15036,6 +15069,25 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/sip": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/sip/-/sip-0.0.6.tgz",
|
||||
"integrity": "sha512-t+FYic4EQ25GTsIRWFVvsq+GmVkoZhrcoghANlnN6CsWMHGcfjPDYMD+nTBNrHR/WnRykF4nqx4i+gahAnW5NA==",
|
||||
"dependencies": {
|
||||
"ws": "^6.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/sip/node_modules/ws": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
|
||||
"integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
|
||||
"dependencies": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz",
|
||||
@ -16721,12 +16773,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"version": "11.0.5",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz",
|
||||
"integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid-parse": {
|
||||
|
@ -133,6 +133,7 @@
|
||||
"redbean-node": "~0.3.0",
|
||||
"redis": "~4.5.1",
|
||||
"semver": "~7.5.4",
|
||||
"sip": "^0.0.6",
|
||||
"socket.io": "~4.8.0",
|
||||
"socket.io-client": "~4.8.0",
|
||||
"socks-proxy-agent": "~8.0.5",
|
||||
@ -140,6 +141,7 @@
|
||||
"tcp-ping": "~0.1.1",
|
||||
"thirty-two": "~1.0.2",
|
||||
"tough-cookie": "~4.1.3",
|
||||
"uuid": "^11.0.5",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -112,6 +112,8 @@ class Database {
|
||||
"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-add-sip-fields.sql": true,
|
||||
"patch-sip-auth.sql": true,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal, sipRegisterRequest, sipOptionRequest
|
||||
} = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
@ -34,6 +34,163 @@ const rootCertificates = rootCertificatesFingerprints();
|
||||
* 2 = PENDING
|
||||
* 3 = MAINTENANCE
|
||||
*/
|
||||
|
||||
const sipStatusCodes = [
|
||||
{ status: 100,
|
||||
msg: "Trying" },
|
||||
{ status: 180,
|
||||
msg: "Ringing" },
|
||||
{ status: 181,
|
||||
msg: "Call Being Forwarded" },
|
||||
{ status: 182,
|
||||
msg: "Queued" },
|
||||
{ status: 183,
|
||||
msg: "Session Progress" },
|
||||
{ status: 199,
|
||||
msg: "Early Dialog Terminated" },
|
||||
{ status: 200,
|
||||
msg: "OK" },
|
||||
{ status: 202,
|
||||
msg: "Accepted" },
|
||||
{ status: 204,
|
||||
msg: "No Notification" },
|
||||
{ status: 300,
|
||||
msg: "Multiple Choices" },
|
||||
{ status: 301,
|
||||
msg: "Moved Permanently" },
|
||||
{ status: 302,
|
||||
msg: "Moved Temporarily" },
|
||||
{ status: 305,
|
||||
msg: "Use Proxy" },
|
||||
{ status: 380,
|
||||
msg: "Alternate Service" },
|
||||
{ status: 400,
|
||||
msg: "Bad Request" },
|
||||
{ status: 401,
|
||||
msg: "Unauthorized" },
|
||||
{ status: 402,
|
||||
msg: "Payment Required" },
|
||||
{ status: 403,
|
||||
msg: "Forbidden" },
|
||||
{ status: 404,
|
||||
msg: "Not Found" },
|
||||
{ status: 405,
|
||||
msg: "Method Not Allowed" },
|
||||
{ status: 406,
|
||||
msg: "Not Acceptable" },
|
||||
{ status: 407,
|
||||
msg: "Proxy Authentication Required" },
|
||||
{ status: 408,
|
||||
msg: "Request Timeout" },
|
||||
{ status: 409,
|
||||
msg: "Conflict" },
|
||||
{ status: 410,
|
||||
msg: "Gone" },
|
||||
{ status: 411,
|
||||
msg: "Length Required" },
|
||||
{ status: 412,
|
||||
msg: "Conditional Request Failed" },
|
||||
{ status: 413,
|
||||
msg: "Request Entity Too Large" },
|
||||
{ status: 414,
|
||||
msg: "Request-URI Too Long" },
|
||||
{ status: 415,
|
||||
msg: "Unsupported Media Type" },
|
||||
{ status: 416,
|
||||
msg: "Unsupported URI Scheme" },
|
||||
{ status: 417,
|
||||
msg: "Unknown Resource-Priority" },
|
||||
{ status: 420,
|
||||
msg: "Bad Extension" },
|
||||
{ status: 421,
|
||||
msg: "Extension Required" },
|
||||
{ status: 422,
|
||||
msg: "Session Interval Too Small" },
|
||||
{ status: 423,
|
||||
msg: "Interval Too Brief" },
|
||||
{ status: 424,
|
||||
msg: "Bad Location Information" },
|
||||
{ status: 425,
|
||||
msg: "Bad Alert Message" },
|
||||
{ status: 428,
|
||||
msg: "Use Identity Header" },
|
||||
{ status: 429,
|
||||
msg: "Provide Referrer Identity" },
|
||||
{ status: 430,
|
||||
msg: "Flow Failed" },
|
||||
{ status: 433,
|
||||
msg: "Anonymity Disallowed" },
|
||||
{ status: 436,
|
||||
msg: "Bad Identity-Info" },
|
||||
{ status: 437,
|
||||
msg: "Unsupported Certificate" },
|
||||
{ status: 438,
|
||||
msg: "Invalid Identity Header" },
|
||||
{ status: 439,
|
||||
msg: "First Hop Lacks Outbound Support" },
|
||||
{ status: 440,
|
||||
msg: "Max-Breadth Exceeded" },
|
||||
{ status: 469,
|
||||
msg: "Bad Info Package" },
|
||||
{ status: 470,
|
||||
msg: "Consent Needed" },
|
||||
{ status: 480,
|
||||
msg: "Temporarily Unavailable" },
|
||||
{ status: 481,
|
||||
msg: "Call/Transaction Does Not Exist" },
|
||||
{ status: 482,
|
||||
msg: "Loop Detected" },
|
||||
{ status: 483,
|
||||
msg: "Too Many Hops" },
|
||||
{ status: 484,
|
||||
msg: "Address Incomplete" },
|
||||
{ status: 485,
|
||||
msg: "Ambiguous" },
|
||||
{ status: 486,
|
||||
msg: "Busy Here" },
|
||||
{ status: 487,
|
||||
msg: "Request Terminated" },
|
||||
{ status: 488,
|
||||
msg: "Not Acceptable Here" },
|
||||
{ status: 489,
|
||||
msg: "Bad Event" },
|
||||
{ status: 491,
|
||||
msg: "Request Pending" },
|
||||
{ status: 493,
|
||||
msg: "Undecipherable" },
|
||||
{ status: 494,
|
||||
msg: "Security Agreement Required" },
|
||||
{ status: 500,
|
||||
msg: "Internal Server Error" },
|
||||
{ status: 501,
|
||||
msg: "Not Implemented" },
|
||||
{ status: 502,
|
||||
msg: "Bad Gateway" },
|
||||
{ status: 503,
|
||||
msg: "Service Unavailable" },
|
||||
{ status: 504,
|
||||
msg: "Server Time-out" },
|
||||
{ status: 505,
|
||||
msg: "Version Not Supported" },
|
||||
{ status: 513,
|
||||
msg: "Message Too Large" },
|
||||
{ status: 555,
|
||||
msg: "Push Notification Service Not Supported" },
|
||||
{ status: 580,
|
||||
msg: "Precondition Failure" },
|
||||
{ status: 600,
|
||||
msg: "Busy Everywhere" },
|
||||
{ status: 603,
|
||||
msg: "Decline" },
|
||||
{ status: 604,
|
||||
msg: "Does Not Exist Anywhere" },
|
||||
{ status: 606,
|
||||
msg: "Not Acceptable" },
|
||||
{ status: 607,
|
||||
msg: "Unwanted" },
|
||||
{ status: 608,
|
||||
msg: "Rejected" },
|
||||
];
|
||||
class Monitor extends BeanModel {
|
||||
|
||||
/**
|
||||
@ -155,6 +312,12 @@ class Monitor extends BeanModel {
|
||||
snmpVersion: this.snmpVersion,
|
||||
rabbitmqNodes: JSON.parse(this.rabbitmqNodes),
|
||||
conditions: JSON.parse(this.conditions),
|
||||
sipUrl: this.sipUrl,
|
||||
sipPort: this.sipPort,
|
||||
sipProtocol: this.sipProtocol,
|
||||
sipMethod: this.sipMethod,
|
||||
sipMaintainence: this.isSipMaintainence(),
|
||||
sipAuthMethod: this.sipAuthMethod,
|
||||
};
|
||||
|
||||
if (includeSensitiveData) {
|
||||
@ -186,6 +349,8 @@ class Monitor extends BeanModel {
|
||||
kafkaProducerSaslOptions: JSON.parse(this.kafkaProducerSaslOptions),
|
||||
rabbitmqUsername: this.rabbitmqUsername,
|
||||
rabbitmqPassword: this.rabbitmqPassword,
|
||||
sip_basic_auth_user: this.sip_basic_auth_user,
|
||||
sip_basic_auth_pass: this.sip_basic_auth_pass,
|
||||
};
|
||||
}
|
||||
|
||||
@ -319,6 +484,14 @@ class Monitor extends BeanModel {
|
||||
return Boolean(this.kafkaProducerAllowAutoTopicCreation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse to boolean
|
||||
* @returns {boolean} Sip Allow Maintainenece Option
|
||||
*/
|
||||
isSipMaintainence() {
|
||||
return Boolean(this.sipMaintainence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start monitor
|
||||
* @param {Server} io Socket server instance
|
||||
@ -874,6 +1047,62 @@ class Monitor extends BeanModel {
|
||||
bean.status = UP;
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
|
||||
} else if (this.type === "sip") {
|
||||
try {
|
||||
console.log("Ping Result:", this.sipMethod);
|
||||
let sipResponse;
|
||||
let sipMessage;
|
||||
let startTime = dayjs().valueOf();
|
||||
let totalResponseTime;
|
||||
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", totalResponseTime);
|
||||
console.log("this.sipMaintainence", this.sipMaintainence);
|
||||
const matchingStatus = sipStatusCodes.find(code => code.status === sipResponse?.status);
|
||||
if (matchingStatus) {
|
||||
sipMessage = `${sipResponse?.status}-${matchingStatus.msg}`;
|
||||
// Assuming UP and DOWN are previously defined constants or variables
|
||||
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) {
|
||||
sipMessage = "Monitor under maintenance";
|
||||
bean.status = MAINTENANCE;
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
console.log("=====resposne status", sipResponse?.status);
|
||||
console.log("this.sipMaintainence", this.sipMaintainence);
|
||||
const matchingStatus = sipStatusCodes.find(code => code.status === sipResponse?.status);
|
||||
if (matchingStatus) {
|
||||
sipMessage = `${sipResponse?.status}-${matchingStatus.msg}`;
|
||||
// Assuming UP and DOWN are previously defined constants or variables
|
||||
bean.status = sipResponse?.status === 200 ? UP : DOWN;
|
||||
|
||||
// Additional check for 503 status within matchingStatus
|
||||
if (sipResponse?.status === 503 && this.sipMaintainence === 1) {
|
||||
sipMessage = "Monitor under maintenance";
|
||||
bean.status = MAINTENANCE;
|
||||
}
|
||||
} else {
|
||||
sipMessage = ` ${sipResponse?.status}-Not Ok`;
|
||||
bean.status = DOWN;
|
||||
}
|
||||
}
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
bean.msg = sipMessage;
|
||||
// bean.msg = `${sipResponse?.status} - ${sipResponse?.reason}`
|
||||
} catch (error) {
|
||||
bean.msg = `Error: ${error.message}`;
|
||||
bean.status = DOWN;
|
||||
}
|
||||
} else {
|
||||
throw new Error("Unknown Monitor Type");
|
||||
}
|
||||
|
93
server/notification-providers/sip.js
Normal file
93
server/notification-providers/sip.js
Normal file
@ -0,0 +1,93 @@
|
||||
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 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);
|
||||
throw new Error("Failed to send SIP message.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
|
||||
message = message.toLowerCase(); // Normalize input
|
||||
|
||||
if (/\bdown\b/i.test(message) || message.includes("offline")) {
|
||||
return "🚨 ❌ Service Impacted...";
|
||||
}
|
||||
if (/\bup\b/i.test(message) || message.includes("online")) {
|
||||
return "🚨 ✅ Service Restored...";
|
||||
}
|
||||
if (message.includes("maintenance")) {
|
||||
if (message.includes("begin")) {
|
||||
return "🚧 🔧 ❌ Maintenance Start...";
|
||||
}
|
||||
if (/\bend\b/i.test(message)) {
|
||||
return "🚧 🔧 ✅ Maintenance Complete...";
|
||||
}
|
||||
if (message.includes("scheduled")) {
|
||||
return "🚧 🪟 📆 Maintenance Window Scheduled...";
|
||||
}
|
||||
if (message.includes("window begin")) {
|
||||
return "🚧 🪟 🛑 Maintenance Window Start...";
|
||||
}
|
||||
if (message.includes("window end")) {
|
||||
return "🚧 🪟 ✅ Maintenance Window Complete...";
|
||||
}
|
||||
}
|
||||
if (message.includes("started on node")) {
|
||||
return "📈 🔬 ✅ Monitoring Start...";
|
||||
}
|
||||
if (message.includes("started")) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SIP;
|
@ -874,6 +874,13 @@ let needSetup = false;
|
||||
bean.rabbitmqUsername = monitor.rabbitmqUsername;
|
||||
bean.rabbitmqPassword = monitor.rabbitmqPassword;
|
||||
bean.conditions = JSON.stringify(monitor.conditions);
|
||||
bean.sipUrl = monitor.sipUrl;
|
||||
bean.sipPort = monitor.sipPort;
|
||||
bean.sip_basic_auth_user = monitor.sip_basic_auth_user;
|
||||
bean.sip_basic_auth_pass = monitor.sip_basic_auth_pass;
|
||||
bean.sipMaintainence = monitor.sipMaintainence;
|
||||
bean.sipMethod = monitor.sipMethod;
|
||||
bean.sipAuthMethod = monitor.sipAuthMethod;
|
||||
|
||||
bean.validate();
|
||||
|
||||
|
@ -31,6 +31,8 @@ const dayjs = require("dayjs");
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { Kafka, SASLOptions } = require("kafkajs");
|
||||
const crypto = require("crypto");
|
||||
let sip = require("sip");
|
||||
const uuid = require("uuid");
|
||||
|
||||
const isWindows = process.platform === /^win/.test(process.platform);
|
||||
/**
|
||||
@ -259,7 +261,235 @@ 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')
|
||||
* @param {string} username The username for registration
|
||||
* @param {string} userPassword The userPassword 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, userPassword = undefined, version) {
|
||||
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const registerRequest = {
|
||||
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,
|
||||
contact: { uri: `sip:${username}` },
|
||||
"User-Agent": "SIP Health Monitor " + version,
|
||||
"Expires": 60,
|
||||
},
|
||||
transport: transport,
|
||||
};
|
||||
const registrationResponse = await exports.sipRegister(registerRequest);
|
||||
console.log("registrationResponse", registrationResponse);
|
||||
if (registrationResponse.status === 407 && registrationResponse.headers["proxy-authenticate"]) {
|
||||
const proxyAuthenticateHeader = registrationResponse.headers["proxy-authenticate"][0];
|
||||
const authorizedRegisterRequest = exports.constructAuthorizedRequest(
|
||||
registerRequest,
|
||||
username,
|
||||
userPassword,
|
||||
proxyAuthenticateHeader
|
||||
);
|
||||
|
||||
const secondResponse = await exports.sipRegister(authorizedRegisterRequest);
|
||||
resolve(secondResponse);
|
||||
} else {
|
||||
resolve(registrationResponse);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error.message);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.sipRegister = function (registerRequest) {
|
||||
const server = sip.create({
|
||||
logger: "console",
|
||||
port: 25060,
|
||||
});
|
||||
console.log("SIP server created:", server);
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = 5000; // Timeout duration in milliseconds
|
||||
let timeoutID;
|
||||
// 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 (server && server.destroy) {
|
||||
server.destroy();
|
||||
console.log("SIP server destroyed.");
|
||||
}
|
||||
}
|
||||
// Set a timeout to handle request expiry
|
||||
timeoutID = setTimeout(() => {
|
||||
console.error("SIP Register request timed out.");
|
||||
reject(new Error("SIP Register request timed out."));
|
||||
cleanup();
|
||||
}, timeout);
|
||||
|
||||
try {
|
||||
// Send the SIP register request
|
||||
server.send(registerRequest, (response) => {
|
||||
console.log("Received SIP register response:", response);
|
||||
if (response) {
|
||||
resolve(response); // Resolve the promise with the response
|
||||
cleanup(); // Cleanup after resolving or rejecting
|
||||
} else {
|
||||
reject(new Error("Empty SIP response received."));
|
||||
cleanup(); // Cleanup after resolving or rejecting
|
||||
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error sending SIP register request:", error.message);
|
||||
reject(new Error("Error sending SIP register request: " + error.message));
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
};
|
||||
exports.constructAuthorizedRequest = function (request, username, userPassword = undefined, proxyAuthenticateHeader) {
|
||||
const digestChallenge = {
|
||||
realm: proxyAuthenticateHeader.realm.replace(/"/g, ""),
|
||||
nonce: proxyAuthenticateHeader.nonce.replace(/"/g, ""),
|
||||
};
|
||||
// Construct Digest authentication header manually
|
||||
const ha1 = crypto.createHash("sha256").update(`${username}:${digestChallenge.realm}:${passwordHash.generate(userPassword)}`).digest("hex");
|
||||
const ha2 = crypto.createHash("sha256").update(`${request.method}:${request.uri}`).digest("hex");
|
||||
const response = crypto.createHash("sha256").update(`${ha1}:${digestChallenge.nonce}:${ha2}`).digest("hex");
|
||||
const authorizationHeader = `Digest username="${username}", realm="${digestChallenge.realm}", nonce="${digestChallenge.nonce}", uri="${request.uri}", response="${response}"`;
|
||||
const authorizedRequest = {
|
||||
...request,
|
||||
headers: {
|
||||
...request.headers,
|
||||
"Proxy-Authorization": authorizationHeader,
|
||||
},
|
||||
};
|
||||
return authorizedRequest;
|
||||
};
|
||||
/**
|
||||
* 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')
|
||||
* @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;
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const optionsRequest = {
|
||||
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}` }],
|
||||
"User-Agent": "SIP Health Monitor" + version,
|
||||
|
||||
},
|
||||
transport: transport,
|
||||
};
|
||||
let optionResponse;
|
||||
if (!username) {
|
||||
console.log("will only send ok");
|
||||
const optionResponse = await exports.sipOption(optionsRequest);
|
||||
console.log("optionResponse", optionResponse);
|
||||
resolve(optionResponse);
|
||||
} else {
|
||||
optionResponse = await exports.sipRegister(optionsRequest);
|
||||
console.log("optionResponse", optionResponse);
|
||||
if (optionResponse.status === 407 && optionResponse.headers["proxy-authenticate"]) {
|
||||
const proxyAuthenticateHeader = optionResponse.headers["proxy-authenticate"][0];
|
||||
const authorizedOptionRequest = exports.constructAuthorizedRequest(
|
||||
optionsRequest,
|
||||
username,
|
||||
password,
|
||||
proxyAuthenticateHeader
|
||||
);
|
||||
|
||||
const secondResponse = await exports.sipOption(authorizedOptionRequest);
|
||||
resolve(secondResponse);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error:", error.message);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
exports.sipOption = function (optionsRequest) {
|
||||
const server = sip.create({
|
||||
logger: "console",
|
||||
port: 5060,
|
||||
});
|
||||
|
||||
console.log("SIP server created:", server);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let timeoutID;
|
||||
// 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 (server) {
|
||||
server.destroy();
|
||||
console.log("SIP server destroyed.");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Send the SIP options request
|
||||
server.send(optionsRequest, (response) => {
|
||||
console.log("Received SIP options response:", response);
|
||||
if (response) {
|
||||
resolve(response); // Resolve the promise with the response
|
||||
cleanup(); // Perform cleanup
|
||||
} else {
|
||||
reject(new Error("Empty SIP response received."));
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error sending SIP options request:", error.message);
|
||||
reject(new Error("Error sending SIP options request: " + error.message));
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Use NTLM Auth for a http request.
|
||||
* @param {object} options The http request options
|
||||
|
@ -1051,5 +1051,10 @@
|
||||
"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",
|
||||
"SipProtocol": "Sip Protocol",
|
||||
"SipPort": "SIP Port",
|
||||
"process503AsMaintenanceLabel": "Process 503 As Maintenance Label",
|
||||
"sipURL": "Hostname (IP/Domain)",
|
||||
"ignoreTLSErrorForSIP": "Ignore TLS/SSL error for SIP websites"
|
||||
}
|
||||
|
10
src/modules/dayjs/plugin/timezone/index.d.ts
vendored
10
src/modules/dayjs/plugin/timezone/index.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
@ -91,6 +91,7 @@
|
||||
<option v-if="!$root.info.isContainer" value="tailscale-ping">
|
||||
Tailscale Ping
|
||||
</option>
|
||||
<option value="sip">SIP</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<i18n-t v-if="monitor.type === 'rabbitmq'" keypath="rabbitmqHelpText" tag="div" class="form-text">
|
||||
@ -117,7 +118,15 @@
|
||||
<label for="url" class="form-label">{{ $t("URL") }}</label>
|
||||
<input id="url" v-model="monitor.url" type="url" class="form-control" pattern="https?://.+" required data-testid="url-input">
|
||||
</div>
|
||||
|
||||
<!--SIP-->
|
||||
<div v-if="monitor.type === 'sip'" class="my-3">
|
||||
<label for="sipprotocol" class="form-label">{{ $t("SipProtocol") }}</label>
|
||||
<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 -->
|
||||
<div v-if="monitor.type === 'grpc-keyword' " class="my-3">
|
||||
<label for="grpc-url" class="form-label">{{ $t("URL") }}</label>
|
||||
@ -156,7 +165,21 @@
|
||||
{{ $t("invertKeywordDescription") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--SIP URL-->
|
||||
<div v-if="monitor.type === 'sip'" class="my-3">
|
||||
<label for="sip-url" class="form-label">{{ $t("sipURL") }}</label>
|
||||
<!-- <input id="sip-url" v-model="monitor.sipURL" type="url" class="form-control" pattern="((https?|ftp):\/\/)?([a-zA-Z0-9.-]
|
||||
+\.[a-zA-Z]{2,})(:\d{1,5})?\/?|
|
||||
(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" required> -->
|
||||
<input id="sip-url" v-model="monitor.sipUrl" type="text" class="form-control" pattern="((https?|ftp):\/\/)?([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})(:\d{1,5})?\/?|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" required>
|
||||
</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" 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">
|
||||
<!-- Toggle -->
|
||||
@ -621,10 +644,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'redis' " class="my-3 form-check">
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'sip' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'redis' " class="my-3 form-check">
|
||||
<input id="ignore-tls" v-model="monitor.ignoreTls" class="form-check-input" type="checkbox" value="">
|
||||
<label class="form-check-label" for="ignore-tls">
|
||||
{{ monitor.type === "redis" ? $t("ignoreTLSErrorGeneral") : $t("ignoreTLSError") }}
|
||||
{{ monitor.type === "redis" || monitor.type === 'sip' ? $t("ignoreTLSErrorGeneral") : $t("ignoreTLSError") }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@ -649,6 +672,18 @@
|
||||
{{ $t("upsideDownModeDescription") }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="monitor.type === 'sip'" class="my-3 form-check">
|
||||
<input
|
||||
id="process-503-as-maintenance"
|
||||
v-model="monitor.sipMaintainence"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<label class="form-check-label" for="process-503-as-maintenance">
|
||||
{{ $t("process503AsMaintenanceLabel") }}
|
||||
</label>
|
||||
</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">
|
||||
@ -667,7 +702,7 @@
|
||||
</div>
|
||||
|
||||
<!-- HTTP / Keyword only -->
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword' ">
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword' || monitor.type === 'sip'">
|
||||
<div class="my-3">
|
||||
<label for="maxRedirects" class="form-label">{{ $t("Max. Redirects") }}</label>
|
||||
<input id="maxRedirects" v-model="monitor.maxredirects" type="number" class="form-control" required min="0" step="1">
|
||||
@ -825,7 +860,91 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- SIP Options -->
|
||||
|
||||
<template
|
||||
v-if="monitor.type === 'sip'
|
||||
"
|
||||
>
|
||||
<h2 class="mt-5 mb-2">{{ $t("SIP Options") }}</h2>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- HTTP Auth -->
|
||||
<h4 class="mt-5 mb-2">{{ $t("Authentication") }}</h4>
|
||||
|
||||
<!-- 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>
|
||||
@ -1111,7 +1230,13 @@ const monitorDefaults = {
|
||||
rabbitmqNodes: [],
|
||||
rabbitmqUsername: "",
|
||||
rabbitmqPassword: "",
|
||||
conditions: []
|
||||
conditions: [],
|
||||
sipProtocol: "UDP",
|
||||
sipPort: 5060,
|
||||
sipUrl: null,
|
||||
sipMethod: "OPTIONS",
|
||||
sipMaintainence: false,
|
||||
sipAuthMethod: null,
|
||||
};
|
||||
|
||||
export default {
|
||||
|
Loading…
x
Reference in New Issue
Block a user