mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-08-09 14:03:12 +08:00
Merge branch 'master' into 1.23.X-merge-to-2.X.X-2
# Conflicts: # package.json
This commit is contained in:
@@ -185,6 +185,30 @@ async function sendDockerHostList(socket) {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send list of docker hosts to client
|
||||
* @param {Socket} socket Socket.io socket instance
|
||||
* @returns {Promise<Bean[]>} List of docker hosts
|
||||
*/
|
||||
async function sendRemoteBrowserList(socket) {
|
||||
const timeLogger = new TimeLogger();
|
||||
|
||||
let result = [];
|
||||
let list = await R.find("remote_browser", " user_id = ? ", [
|
||||
socket.userID,
|
||||
]);
|
||||
|
||||
for (let bean of list) {
|
||||
result.push(bean.toJSON());
|
||||
}
|
||||
|
||||
io.to(socket.userID).emit("remoteBrowserList", result);
|
||||
|
||||
timeLogger.print("Send Remote Browser List");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendNotificationList,
|
||||
sendImportantHeartbeatList,
|
||||
@@ -192,5 +216,6 @@ module.exports = {
|
||||
sendProxyList,
|
||||
sendAPIKeyList,
|
||||
sendInfo,
|
||||
sendDockerHostList
|
||||
sendDockerHostList,
|
||||
sendRemoteBrowserList,
|
||||
};
|
||||
|
@@ -1,10 +1,10 @@
|
||||
const axios = require("axios");
|
||||
const { R } = require("redbean-node");
|
||||
const version = require("../package.json").version;
|
||||
const https = require("https");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Database = require("./database");
|
||||
const { axiosAbortSignal } = require("./util-server");
|
||||
|
||||
class DockerHost {
|
||||
|
||||
@@ -70,9 +70,11 @@ class DockerHost {
|
||||
static async testDockerHost(dockerHost) {
|
||||
const options = {
|
||||
url: "/containers/json?all=true",
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
"Accept": "*/*",
|
||||
},
|
||||
signal: axiosAbortSignal(6000),
|
||||
};
|
||||
|
||||
if (dockerHost.dockerType === "socket") {
|
||||
@@ -82,26 +84,33 @@ class DockerHost {
|
||||
options.httpsAgent = new https.Agent(DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL));
|
||||
}
|
||||
|
||||
let res = await axios.request(options);
|
||||
try {
|
||||
let res = await axios.request(options);
|
||||
|
||||
if (Array.isArray(res.data)) {
|
||||
if (Array.isArray(res.data)) {
|
||||
|
||||
if (res.data.length > 1) {
|
||||
if (res.data.length > 1) {
|
||||
|
||||
if ("ImageID" in res.data[0]) {
|
||||
return res.data.length;
|
||||
} else {
|
||||
throw new Error("Invalid Docker response, is it Docker really a daemon?");
|
||||
}
|
||||
|
||||
if ("ImageID" in res.data[0]) {
|
||||
return res.data.length;
|
||||
} else {
|
||||
throw new Error("Invalid Docker response, is it Docker really a daemon?");
|
||||
return res.data.length;
|
||||
}
|
||||
|
||||
} else {
|
||||
return res.data.length;
|
||||
throw new Error("Invalid Docker response, is it Docker really a daemon?");
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.code === "ECONNABORTED" || e.name === "CanceledError") {
|
||||
throw new Error("Connection to Docker daemon timed out.");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Error("Invalid Docker response, is it Docker really a daemon?");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -29,13 +29,14 @@ class Heartbeat extends BeanModel {
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
monitorID: this.monitor_id,
|
||||
status: this.status,
|
||||
time: this.time,
|
||||
msg: this.msg,
|
||||
ping: this.ping,
|
||||
important: this.important,
|
||||
duration: this.duration,
|
||||
monitorID: this._monitorId,
|
||||
status: this._status,
|
||||
time: this._time,
|
||||
msg: this._msg,
|
||||
ping: this._ping,
|
||||
important: this._important,
|
||||
duration: this._duration,
|
||||
retries: this._retries,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
const https = require("https");
|
||||
const dayjs = require("dayjs");
|
||||
const axios = require("axios");
|
||||
const { Prometheus } = require("../prometheus");
|
||||
@@ -23,6 +22,8 @@ const jsonata = require("jsonata");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const crypto = require("crypto");
|
||||
const { UptimeCalculator } = require("../uptime-calculator");
|
||||
const { CookieJar } = require("tough-cookie");
|
||||
const { HttpsCookieAgent } = require("http-cookie-agent/http");
|
||||
|
||||
const rootCertificates = rootCertificatesFingerprints();
|
||||
|
||||
@@ -153,6 +154,7 @@ class Monitor extends BeanModel {
|
||||
kafkaProducerAllowAutoTopicCreation: this.getKafkaProducerAllowAutoTopicCreation(),
|
||||
kafkaProducerMessage: this.kafkaProducerMessage,
|
||||
screenshot,
|
||||
remote_browser: this.remote_browser,
|
||||
};
|
||||
|
||||
if (includeSensitiveData) {
|
||||
@@ -351,6 +353,9 @@ class Monitor extends BeanModel {
|
||||
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
|
||||
this.id,
|
||||
]);
|
||||
if (previousBeat) {
|
||||
retries = previousBeat.retries;
|
||||
}
|
||||
}
|
||||
|
||||
const isFirstBeat = !previousBeat;
|
||||
@@ -504,7 +509,12 @@ class Monitor extends BeanModel {
|
||||
}
|
||||
|
||||
if (!options.httpsAgent) {
|
||||
options.httpsAgent = new https.Agent(httpsAgentOptions);
|
||||
let jar = new CookieJar();
|
||||
let httpsCookieAgentOptions = {
|
||||
...httpsAgentOptions,
|
||||
cookies: { jar }
|
||||
};
|
||||
options.httpsAgent = new HttpsCookieAgent(httpsCookieAgentOptions);
|
||||
}
|
||||
|
||||
if (this.auth_method === "mtls") {
|
||||
@@ -622,6 +632,7 @@ class Monitor extends BeanModel {
|
||||
// If the previous beat was down or pending we use the regular
|
||||
// beatInterval/retryInterval in the setTimeout further below
|
||||
if (previousBeat.status !== (this.isUpsideDown() ? DOWN : UP) || msSinceLastBeat > beatInterval * 1000 + bufferTime) {
|
||||
bean.duration = Math.round(msSinceLastBeat / 1000);
|
||||
throw new Error("No heartbeat in the time window");
|
||||
} else {
|
||||
let timeout = beatInterval * 1000 - msSinceLastBeat;
|
||||
@@ -637,6 +648,7 @@ class Monitor extends BeanModel {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
bean.duration = beatInterval;
|
||||
throw new Error("No heartbeat in the time window");
|
||||
}
|
||||
|
||||
@@ -909,9 +921,14 @@ class Monitor extends BeanModel {
|
||||
} else if ((this.maxretries > 0) && (retries < this.maxretries)) {
|
||||
retries++;
|
||||
bean.status = PENDING;
|
||||
} else {
|
||||
// Continue counting retries during DOWN
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
|
||||
bean.retries = retries;
|
||||
|
||||
log.debug("monitor", `[${this.name}] Check isImportant`);
|
||||
let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status);
|
||||
|
||||
@@ -1425,10 +1442,7 @@ class Monitor extends BeanModel {
|
||||
* @returns {Promise<LooseObject<any>>} Previous heartbeat
|
||||
*/
|
||||
static async getPreviousHeartbeat(monitorID) {
|
||||
return await R.getRow(`
|
||||
SELECT ping, status, time FROM heartbeat
|
||||
WHERE id = (select MAX(id) from heartbeat where monitor_id = ?)
|
||||
`, [
|
||||
return await R.findOne("heartbeat", " id = (select MAX(id) from heartbeat where monitor_id = ?)", [
|
||||
monitorID
|
||||
]);
|
||||
}
|
||||
|
17
server/model/remote_browser.js
Normal file
17
server/model/remote_browser.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
|
||||
class RemoteBrowser extends BeanModel {
|
||||
/**
|
||||
* Returns an object that ready to parse to JSON
|
||||
* @returns {object} Object ready to parse
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
url: this.url,
|
||||
name: this.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RemoteBrowser;
|
@@ -8,6 +8,7 @@ const path = require("path");
|
||||
const Database = require("../database");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const config = require("../config");
|
||||
const { RemoteBrowser } = require("../remote-browser");
|
||||
|
||||
let browser = null;
|
||||
|
||||
@@ -86,6 +87,19 @@ async function getBrowser() {
|
||||
return browser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current instance of the browser. If there isn't one, create it
|
||||
* @param {integer} remoteBrowserID Path to executable
|
||||
* @param {integer} userId User ID
|
||||
* @returns {Promise<Browser>} The browser
|
||||
*/
|
||||
async function getRemoteBrowser(remoteBrowserID, userId) {
|
||||
let remoteBrowser = await RemoteBrowser.get(remoteBrowserID, userId);
|
||||
log.debug("MONITOR", `Using remote browser: ${remoteBrowser.name} (${remoteBrowser.id})`);
|
||||
browser = chromium.connect(remoteBrowser.url);
|
||||
return browser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the chrome executable path
|
||||
* @param {string} executablePath Path to chrome executable
|
||||
@@ -192,11 +206,21 @@ async function testChrome(executablePath) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// test remote browser
|
||||
/**
|
||||
* TODO: connect remote browser? https://playwright.dev/docs/api/class-browsertype#browser-type-connect
|
||||
*
|
||||
* @param {string} remoteBrowserURL Remote Browser URL
|
||||
* @returns {Promise<boolean>} Returns if connection worked
|
||||
*/
|
||||
async function testRemoteBrowser(remoteBrowserURL) {
|
||||
try {
|
||||
const browser = await chromium.connect(remoteBrowserURL);
|
||||
browser.version();
|
||||
await browser.close();
|
||||
return true;
|
||||
} catch (e) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
class RealBrowserMonitorType extends MonitorType {
|
||||
|
||||
name = "real-browser";
|
||||
@@ -205,7 +229,7 @@ class RealBrowserMonitorType extends MonitorType {
|
||||
* @inheritdoc
|
||||
*/
|
||||
async check(monitor, heartbeat, server) {
|
||||
const browser = await getBrowser();
|
||||
const browser = monitor.remote_browser ? await getRemoteBrowser(monitor.remote_browser, monitor.user_id) : await getBrowser();
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
||||
@@ -238,4 +262,5 @@ module.exports = {
|
||||
RealBrowserMonitorType,
|
||||
testChrome,
|
||||
resetChrome,
|
||||
testRemoteBrowser,
|
||||
};
|
||||
|
@@ -78,12 +78,12 @@ class Mattermost extends NotificationProvider {
|
||||
{
|
||||
fallback:
|
||||
"Your " +
|
||||
monitorJSON.name +
|
||||
monitorJSON.pathName +
|
||||
" service went " +
|
||||
statusText,
|
||||
color: color,
|
||||
title:
|
||||
monitorJSON.name +
|
||||
monitorJSON.pathName +
|
||||
" service went " +
|
||||
statusText,
|
||||
title_link: monitorJSON.url,
|
||||
|
@@ -4,6 +4,8 @@ const HttpsProxyAgent = require("https-proxy-agent");
|
||||
const SocksProxyAgent = require("socks-proxy-agent");
|
||||
const { debug } = require("../src/util");
|
||||
const { UptimeKumaServer } = require("./uptime-kuma-server");
|
||||
const { CookieJar } = require("tough-cookie");
|
||||
const { createCookieAgent } = require("http-cookie-agent/http");
|
||||
|
||||
class Proxy {
|
||||
|
||||
@@ -95,10 +97,13 @@ class Proxy {
|
||||
let httpAgent;
|
||||
let httpsAgent;
|
||||
|
||||
let jar = new CookieJar();
|
||||
|
||||
const proxyOptions = {
|
||||
protocol: proxy.protocol,
|
||||
host: proxy.host,
|
||||
port: proxy.port,
|
||||
cookies: { jar },
|
||||
};
|
||||
|
||||
if (proxy.auth) {
|
||||
@@ -112,12 +117,17 @@ class Proxy {
|
||||
switch (proxy.protocol) {
|
||||
case "http":
|
||||
case "https":
|
||||
httpAgent = new HttpProxyAgent({
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const HttpCookieProxyAgent = createCookieAgent(HttpProxyAgent);
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const HttpsCookieProxyAgent = createCookieAgent(HttpsProxyAgent);
|
||||
|
||||
httpAgent = new HttpCookieProxyAgent({
|
||||
...httpAgentOptions || {},
|
||||
...proxyOptions
|
||||
...proxyOptions,
|
||||
});
|
||||
|
||||
httpsAgent = new HttpsProxyAgent({
|
||||
httpsAgent = new HttpsCookieProxyAgent({
|
||||
...httpsAgentOptions || {},
|
||||
...proxyOptions,
|
||||
});
|
||||
@@ -126,7 +136,9 @@ class Proxy {
|
||||
case "socks5":
|
||||
case "socks5h":
|
||||
case "socks4":
|
||||
agent = new SocksProxyAgent({
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const SocksCookieProxyAgent = createCookieAgent(SocksProxyAgent);
|
||||
agent = new SocksCookieProxyAgent({
|
||||
...httpAgentOptions,
|
||||
...httpsAgentOptions,
|
||||
...proxyOptions,
|
||||
|
84
server/remote-browser.js
Normal file
84
server/remote-browser.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const { R } = require("redbean-node");
|
||||
const { testRemoteBrowser } = require("./monitor-types/real-browser-monitor-type.js");
|
||||
class RemoteBrowser {
|
||||
|
||||
/**
|
||||
* Gets remote browser from ID
|
||||
* @param {number} remoteBrowserID ID of the remote browser
|
||||
* @param {number} userID ID of the user who created the remote browser
|
||||
* @returns {Promise<Bean>} Remote Browser
|
||||
*/
|
||||
static async get(remoteBrowserID, userID) {
|
||||
let bean = await R.findOne("remote_browser", " id = ? AND user_id = ? ", [ remoteBrowserID, userID ]);
|
||||
|
||||
if (!bean) {
|
||||
throw new Error("Remote browser not found");
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a Remote Browser
|
||||
* @param {object} remoteBrowser Remote Browser to save
|
||||
* @param {?number} remoteBrowserID ID of the Remote Browser to update
|
||||
* @param {number} userID ID of the user who adds the Remote Browser
|
||||
* @returns {Promise<Bean>} Updated Remote Browser
|
||||
*/
|
||||
static async save(remoteBrowser, remoteBrowserID, userID) {
|
||||
let bean;
|
||||
|
||||
if (remoteBrowserID) {
|
||||
bean = await R.findOne("remote_browser", " id = ? AND user_id = ? ", [ remoteBrowserID, userID ]);
|
||||
|
||||
if (!bean) {
|
||||
throw new Error("Remote browser not found");
|
||||
}
|
||||
|
||||
} else {
|
||||
bean = R.dispense("remote_browser");
|
||||
}
|
||||
|
||||
bean.user_id = userID;
|
||||
bean.name = remoteBrowser.name;
|
||||
bean.url = remoteBrowser.url;
|
||||
|
||||
await R.store(bean);
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a Remote Browser
|
||||
* @param {number} remoteBrowserID ID of the Remote Browser to delete
|
||||
* @param {number} userID ID of the user who created the Remote Browser
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async delete(remoteBrowserID, userID) {
|
||||
let bean = await R.findOne("remote_browser", " id = ? AND user_id = ? ", [ remoteBrowserID, userID ]);
|
||||
|
||||
if (!bean) {
|
||||
throw new Error("Remote Browser not found");
|
||||
}
|
||||
|
||||
// Delete removed remote browser from monitors if exists
|
||||
await R.exec("UPDATE monitor SET remote_browser = null WHERE remote_browser = ?", [ remoteBrowserID ]);
|
||||
|
||||
await R.trash(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the connection to Remote Browser
|
||||
* @param {object} remoteBrowser Docker host to check for
|
||||
* @returns {boolean} Returns if connection worked
|
||||
*/
|
||||
static async test(remoteBrowser) {
|
||||
const testResult = await testRemoteBrowser(remoteBrowser.id, remoteBrowser.user_id);
|
||||
return testResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RemoteBrowser,
|
||||
};
|
@@ -64,38 +64,57 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||
|
||||
const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id);
|
||||
|
||||
if (monitor.isUpsideDown()) {
|
||||
status = flipStatus(status);
|
||||
}
|
||||
|
||||
let isFirstBeat = true;
|
||||
let previousStatus = status;
|
||||
let duration = 0;
|
||||
|
||||
let bean = R.dispense("heartbeat");
|
||||
bean.time = R.isoDateTimeMillis(dayjs.utc());
|
||||
bean.monitor_id = monitor.id;
|
||||
bean.ping = ping;
|
||||
bean.msg = msg;
|
||||
bean.downCount = previousHeartbeat?.downCount || 0;
|
||||
|
||||
if (previousHeartbeat) {
|
||||
isFirstBeat = false;
|
||||
previousStatus = previousHeartbeat.status;
|
||||
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
|
||||
bean.duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
|
||||
}
|
||||
|
||||
if (await Monitor.isUnderMaintenance(monitor.id)) {
|
||||
msg = "Monitor under maintenance";
|
||||
status = MAINTENANCE;
|
||||
bean.status = MAINTENANCE;
|
||||
} else {
|
||||
determineStatus(status, previousHeartbeat, monitor.maxretries, monitor.isUpsideDown(), bean);
|
||||
}
|
||||
|
||||
log.debug("router", `/api/push/ called at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`);
|
||||
log.debug("router", "PreviousStatus: " + previousStatus);
|
||||
log.debug("router", "Current Status: " + status);
|
||||
// Calculate uptime
|
||||
let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitor.id);
|
||||
let endTimeDayjs = await uptimeCalculator.update(bean.status, parseFloat(bean.ping));
|
||||
bean.end_time = R.isoDateTimeMillis(endTimeDayjs);
|
||||
|
||||
bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status);
|
||||
bean.monitor_id = monitor.id;
|
||||
bean.status = status;
|
||||
bean.msg = msg;
|
||||
bean.ping = ping;
|
||||
bean.duration = duration;
|
||||
log.debug("router", `/api/push/ called at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`);
|
||||
log.debug("router", "PreviousStatus: " + previousHeartbeat?.status);
|
||||
log.debug("router", "Current Status: " + bean.status);
|
||||
|
||||
bean.important = Monitor.isImportantBeat(isFirstBeat, previousHeartbeat?.status, status);
|
||||
|
||||
if (Monitor.isImportantForNotification(isFirstBeat, previousHeartbeat?.status, status)) {
|
||||
// Reset down count
|
||||
bean.downCount = 0;
|
||||
|
||||
log.debug("monitor", `[${this.name}] sendNotification`);
|
||||
await Monitor.sendNotification(isFirstBeat, monitor, bean);
|
||||
} else {
|
||||
if (bean.status === DOWN && this.resendInterval > 0) {
|
||||
++bean.downCount;
|
||||
if (bean.downCount >= this.resendInterval) {
|
||||
// Send notification again, because we are still DOWN
|
||||
log.debug("monitor", `[${this.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`);
|
||||
await Monitor.sendNotification(isFirstBeat, this, bean);
|
||||
|
||||
// Reset down count
|
||||
bean.downCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await R.store(bean);
|
||||
|
||||
@@ -107,11 +126,6 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||
response.json({
|
||||
ok: true,
|
||||
});
|
||||
|
||||
if (Monitor.isImportantForNotification(isFirstBeat, previousStatus, status)) {
|
||||
await Monitor.sendNotification(isFirstBeat, monitor, bean);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
response.status(404).json({
|
||||
ok: false,
|
||||
@@ -562,4 +576,58 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Determines the status of the next beat in the push route handling.
|
||||
* @param {string} status - The reported new status.
|
||||
* @param {object} previousHeartbeat - The previous heartbeat object.
|
||||
* @param {number} maxretries - The maximum number of retries allowed.
|
||||
* @param {boolean} isUpsideDown - Indicates if the monitor is upside down.
|
||||
* @param {object} bean - The new heartbeat object.
|
||||
* @returns {void}
|
||||
*/
|
||||
function determineStatus(status, previousHeartbeat, maxretries, isUpsideDown, bean) {
|
||||
if (isUpsideDown) {
|
||||
status = flipStatus(status);
|
||||
}
|
||||
|
||||
if (previousHeartbeat) {
|
||||
if (previousHeartbeat.status === UP && status === DOWN) {
|
||||
// Going Down
|
||||
if ((maxretries > 0) && (previousHeartbeat.retries < maxretries)) {
|
||||
// Retries available
|
||||
bean.retries = previousHeartbeat.retries + 1;
|
||||
bean.status = PENDING;
|
||||
} else {
|
||||
// No more retries
|
||||
bean.retries = 0;
|
||||
bean.status = DOWN;
|
||||
}
|
||||
} else if (previousHeartbeat.status === PENDING && status === DOWN && previousHeartbeat.retries < maxretries) {
|
||||
// Retries available
|
||||
bean.retries = previousHeartbeat.retries + 1;
|
||||
bean.status = PENDING;
|
||||
} else {
|
||||
// No more retries or not pending
|
||||
if (status === DOWN) {
|
||||
bean.retries = previousHeartbeat.retries + 1;
|
||||
bean.status = status;
|
||||
} else {
|
||||
bean.retries = 0;
|
||||
bean.status = status;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// First beat?
|
||||
if (status === DOWN && maxretries > 0) {
|
||||
// Retries available
|
||||
bean.retries = 1;
|
||||
bean.status = PENDING;
|
||||
} else {
|
||||
// Retires not enabled
|
||||
bean.retries = 0;
|
||||
bean.status = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
@@ -131,9 +131,10 @@ const testMode = !!args["test"] || false;
|
||||
const e2eTestMode = !!args["e2e"] || false;
|
||||
|
||||
// Must be after io instantiation
|
||||
const { sendNotificationList, sendHeartbeatList, sendInfo, sendProxyList, sendDockerHostList, sendAPIKeyList } = require("./client");
|
||||
const { sendNotificationList, sendHeartbeatList, sendInfo, sendProxyList, sendDockerHostList, sendAPIKeyList, sendRemoteBrowserList } = require("./client");
|
||||
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
|
||||
const databaseSocketHandler = require("./socket-handlers/database-socket-handler");
|
||||
const { remoteBrowserSocketHandler } = require("./socket-handlers/remote-browser-socket-handler");
|
||||
const TwoFA = require("./2fa");
|
||||
const StatusPage = require("./model/status_page");
|
||||
const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudflaredStop } = require("./socket-handlers/cloudflared-socket-handler");
|
||||
@@ -827,6 +828,7 @@ let needSetup = false;
|
||||
bean.kafkaProducerAllowAutoTopicCreation =
|
||||
monitor.kafkaProducerAllowAutoTopicCreation;
|
||||
bean.gamedigGivenPortOnly = monitor.gamedigGivenPortOnly;
|
||||
bean.remote_browser = monitor.remote_browser;
|
||||
|
||||
bean.validate();
|
||||
|
||||
@@ -1508,6 +1510,7 @@ let needSetup = false;
|
||||
dockerSocketHandler(socket);
|
||||
maintenanceSocketHandler(socket);
|
||||
apiKeySocketHandler(socket);
|
||||
remoteBrowserSocketHandler(socket);
|
||||
generalSocketHandler(socket, server);
|
||||
|
||||
log.debug("server", "added all socket handlers");
|
||||
@@ -1616,6 +1619,7 @@ async function afterLogin(socket, user) {
|
||||
sendProxyList(socket);
|
||||
sendDockerHostList(socket);
|
||||
sendAPIKeyList(socket);
|
||||
sendRemoteBrowserList(socket);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
|
82
server/socket-handlers/remote-browser-socket-handler.js
Normal file
82
server/socket-handlers/remote-browser-socket-handler.js
Normal file
@@ -0,0 +1,82 @@
|
||||
const { sendRemoteBrowserList } = require("../client");
|
||||
const { checkLogin } = require("../util-server");
|
||||
const { RemoteBrowser } = require("../remote-browser");
|
||||
|
||||
const { log } = require("../../src/util");
|
||||
const { testRemoteBrowser } = require("../monitor-types/real-browser-monitor-type");
|
||||
|
||||
/**
|
||||
* Handlers for docker hosts
|
||||
* @param {Socket} socket Socket.io instance
|
||||
* @returns {void}
|
||||
*/
|
||||
module.exports.remoteBrowserSocketHandler = (socket) => {
|
||||
socket.on("addRemoteBrowser", async (remoteBrowser, remoteBrowserID, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
let remoteBrowserBean = await RemoteBrowser.save(remoteBrowser, remoteBrowserID, socket.userID);
|
||||
await sendRemoteBrowserList(socket);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "Saved.",
|
||||
msgi18n: true,
|
||||
id: remoteBrowserBean.id,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("deleteRemoteBrowser", async (dockerHostID, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
await RemoteBrowser.delete(dockerHostID, socket.userID);
|
||||
await sendRemoteBrowserList(socket);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "successDeleted",
|
||||
msgi18n: true,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("testRemoteBrowser", async (remoteBrowser, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
let check = await testRemoteBrowser(remoteBrowser.url);
|
||||
log.info("remoteBrowser", "Tested remote browser: " + check);
|
||||
let msg;
|
||||
|
||||
if (check) {
|
||||
msg = "Connected Successfully.";
|
||||
}
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
log.error("remoteBrowser", e);
|
||||
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user