Files
uptime-kuma/server/proxy.js
T

202 lines
6.5 KiB
JavaScript
Raw Normal View History

2021-10-30 20:37:15 +03:00
const { R } = require("redbean-node");
2021-11-04 14:32:16 +03:00
const HttpProxyAgent = require("http-proxy-agent");
const HttpsProxyAgent = require("https-proxy-agent");
const SocksProxyAgent = require("socks-proxy-agent");
const { debug } = require("../src/util");
const { UptimeKumaServer } = require("./uptime-kuma-server");
2023-11-29 01:25:33 -08:00
const { CookieJar } = require("tough-cookie");
const { createCookieAgent } = require("http-cookie-agent/http");
2021-10-30 20:37:15 +03:00
class Proxy {
2022-11-22 11:18:16 +01:00
static SUPPORTED_PROXY_PROTOCOLS = [ "http", "https", "socks", "socks5", "socks5h", "socks4" ];
2021-11-04 14:32:16 +03:00
2021-10-30 20:37:15 +03:00
/**
* Saves and updates given proxy entity
2023-08-11 09:46:41 +02:00
* @param {object} proxy Proxy to store
* @param {number} proxyID ID of proxy to update
* @param {number} userID ID of user the proxy belongs to
* @returns {Promise<Bean>} Updated proxy
2021-10-30 20:37:15 +03:00
*/
static async save(proxy, proxyID, userID) {
let bean;
if (proxyID) {
2022-04-17 15:27:35 +08:00
bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [ proxyID, userID ]);
2021-10-30 20:37:15 +03:00
if (!bean) {
throw new Error("proxy not found");
}
} else {
bean = R.dispense("proxy");
}
// Make sure given proxy protocol is supported
2021-11-04 14:32:16 +03:00
if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) {
throw new Error(`
Unsupported proxy protocol "${proxy.protocol}.
Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."`
);
2021-10-30 20:37:15 +03:00
}
// When proxy is default update deactivate old default proxy
if (proxy.default) {
await R.exec("UPDATE proxy SET `default` = 0 WHERE `default` = 1");
}
bean.user_id = userID;
bean.protocol = proxy.protocol;
bean.host = proxy.host;
bean.port = proxy.port;
bean.auth = proxy.auth;
bean.username = proxy.username;
bean.password = proxy.password;
bean.active = proxy.active || true;
bean.default = proxy.default || false;
await R.store(bean);
if (proxy.applyExisting) {
await applyProxyEveryMonitor(bean.id, userID);
}
return bean;
}
/**
* Deletes proxy with given id and removes it from monitors
2023-08-11 09:46:41 +02:00
* @param {number} proxyID ID of proxy to delete
* @param {number} userID ID of proxy owner
* @returns {Promise<void>}
2021-10-30 20:37:15 +03:00
*/
static async delete(proxyID, userID) {
2022-04-17 15:27:35 +08:00
const bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [ proxyID, userID ]);
2021-10-30 20:37:15 +03:00
if (!bean) {
throw new Error("proxy not found");
}
// Delete removed proxy from monitors if exists
2022-04-17 15:27:35 +08:00
await R.exec("UPDATE monitor SET proxy_id = null WHERE proxy_id = ?", [ proxyID ]);
2021-10-30 20:37:15 +03:00
// Delete proxy from list
await R.trash(bean);
}
2021-11-04 14:32:16 +03:00
/**
* Create HTTP and HTTPS agents related with given proxy bean object
2023-08-11 09:46:41 +02:00
* @param {object} proxy proxy bean object
* @param {object} options http and https agent options
* @returns {{httpAgent: Agent, httpsAgent: Agent}} New HTTP and HTTPS agents
* @throws Proxy protocol is unsupported
2021-11-04 14:32:16 +03:00
*/
static createAgents(proxy, options) {
const { httpAgentOptions, httpsAgentOptions } = options || {};
let agent;
let httpAgent;
let httpsAgent;
2023-11-29 01:25:33 -08:00
let jar = new CookieJar();
2021-11-04 14:32:16 +03:00
const proxyOptions = {
protocol: proxy.protocol,
host: proxy.host,
port: proxy.port,
2023-11-29 01:25:33 -08:00
cookies: { jar },
2021-11-04 14:32:16 +03:00
};
if (proxy.auth) {
proxyOptions.auth = `${proxy.username}:${proxy.password}`;
}
debug(`Proxy Options: ${JSON.stringify(proxyOptions)}`);
debug(`HTTP Agent Options: ${JSON.stringify(httpAgentOptions)}`);
debug(`HTTPS Agent Options: ${JSON.stringify(httpsAgentOptions)}`);
switch (proxy.protocol) {
case "http":
case "https":
2023-11-29 01:25:33 -08:00
// 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({
2021-11-04 14:32:16 +03:00
...httpAgentOptions || {},
2023-11-29 01:25:33 -08:00
...proxyOptions,
2021-11-04 14:32:16 +03:00
});
2023-11-29 01:25:33 -08:00
httpsAgent = new HttpsCookieProxyAgent({
2021-11-04 14:32:16 +03:00
...httpsAgentOptions || {},
...proxyOptions,
});
break;
case "socks":
case "socks5":
2022-11-22 11:18:16 +01:00
case "socks5h":
2021-11-04 14:32:16 +03:00
case "socks4":
2023-11-29 01:25:33 -08:00
// eslint-disable-next-line no-case-declarations
const SocksCookieProxyAgent = createCookieAgent(SocksProxyAgent);
agent = new SocksCookieProxyAgent({
2021-11-04 14:32:16 +03:00
...httpAgentOptions,
...httpsAgentOptions,
...proxyOptions,
2023-03-28 11:40:19 +08:00
tls: {
rejectUnauthorized: httpsAgentOptions.rejectUnauthorized,
},
2021-11-04 14:32:16 +03:00
});
httpAgent = agent;
httpsAgent = agent;
break;
default: throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
}
return {
httpAgent,
httpsAgent
};
}
/**
* Reload proxy settings for current monitors
* @returns {Promise<void>}
*/
static async reloadProxy() {
const server = UptimeKumaServer.getInstance();
let updatedList = await R.getAssoc("SELECT id, proxy_id FROM monitor");
for (let monitorID in server.monitorList) {
let monitor = server.monitorList[monitorID];
if (updatedList[monitorID]) {
monitor.proxy_id = updatedList[monitorID].proxy_id;
}
}
}
2021-10-30 20:37:15 +03:00
}
/**
* Applies given proxy id to monitors
2023-08-11 09:46:41 +02:00
* @param {number} proxyID ID of proxy to apply
* @param {number} userID ID of proxy owner
* @returns {Promise<void>}
2021-10-30 20:37:15 +03:00
*/
async function applyProxyEveryMonitor(proxyID, userID) {
// Find all monitors with id and proxy id
2022-04-17 15:27:35 +08:00
const monitors = await R.getAll("SELECT id, proxy_id FROM monitor WHERE user_id = ?", [ userID ]);
2021-10-30 20:37:15 +03:00
// Update proxy id not match with given proxy id
for (const monitor of monitors) {
if (monitor.proxy_id !== proxyID) {
2022-04-17 15:27:35 +08:00
await R.exec("UPDATE monitor SET proxy_id = ? WHERE id = ?", [ proxyID, monitor.id ]);
2021-10-30 20:37:15 +03:00
}
}
}
module.exports = {
Proxy,
};