2021-10-30 10:37:15 -07:00
|
|
|
const { R } = require("redbean-node");
|
2021-11-04 04:32:16 -07:00
|
|
|
const HttpProxyAgent = require("http-proxy-agent");
|
|
|
|
const HttpsProxyAgent = require("https-proxy-agent");
|
|
|
|
const SocksProxyAgent = require("socks-proxy-agent");
|
|
|
|
const { debug } = require("../src/util");
|
2022-04-19 00:38:59 -07:00
|
|
|
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 10:37:15 -07:00
|
|
|
|
|
|
|
class Proxy {
|
|
|
|
|
2022-11-22 02:18:16 -08:00
|
|
|
static SUPPORTED_PROXY_PROTOCOLS = [ "http", "https", "socks", "socks5", "socks5h", "socks4" ];
|
2021-11-04 04:32:16 -07:00
|
|
|
|
2021-10-30 10:37:15 -07:00
|
|
|
/**
|
|
|
|
* Saves and updates given proxy entity
|
2023-08-11 00:46:41 -07: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 10:37:15 -07:00
|
|
|
*/
|
|
|
|
static async save(proxy, proxyID, userID) {
|
|
|
|
let bean;
|
|
|
|
|
|
|
|
if (proxyID) {
|
2022-04-17 00:27:35 -07:00
|
|
|
bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [ proxyID, userID ]);
|
2021-10-30 10:37:15 -07:00
|
|
|
|
|
|
|
if (!bean) {
|
|
|
|
throw new Error("proxy not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
bean = R.dispense("proxy");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure given proxy protocol is supported
|
2021-11-04 04:32:16 -07: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 10:37:15 -07: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 00:46:41 -07:00
|
|
|
* @param {number} proxyID ID of proxy to delete
|
|
|
|
* @param {number} userID ID of proxy owner
|
|
|
|
* @returns {Promise<void>}
|
2021-10-30 10:37:15 -07:00
|
|
|
*/
|
|
|
|
static async delete(proxyID, userID) {
|
2022-04-17 00:27:35 -07:00
|
|
|
const bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [ proxyID, userID ]);
|
2021-10-30 10:37:15 -07:00
|
|
|
|
|
|
|
if (!bean) {
|
|
|
|
throw new Error("proxy not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removed proxy from monitors if exists
|
2022-04-17 00:27:35 -07:00
|
|
|
await R.exec("UPDATE monitor SET proxy_id = null WHERE proxy_id = ?", [ proxyID ]);
|
2021-10-30 10:37:15 -07:00
|
|
|
|
|
|
|
// Delete proxy from list
|
|
|
|
await R.trash(bean);
|
|
|
|
}
|
2021-11-04 04:32:16 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create HTTP and HTTPS agents related with given proxy bean object
|
2023-08-11 00:46:41 -07: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 04:32:16 -07: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 04:32:16 -07: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 04:32:16 -07: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 04:32:16 -07:00
|
|
|
...httpAgentOptions || {},
|
2023-11-29 01:25:33 -08:00
|
|
|
...proxyOptions,
|
2021-11-04 04:32:16 -07:00
|
|
|
});
|
|
|
|
|
2023-11-29 01:25:33 -08:00
|
|
|
httpsAgent = new HttpsCookieProxyAgent({
|
2021-11-04 04:32:16 -07:00
|
|
|
...httpsAgentOptions || {},
|
|
|
|
...proxyOptions,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "socks":
|
|
|
|
case "socks5":
|
2022-11-22 02:18:16 -08:00
|
|
|
case "socks5h":
|
2021-11-04 04:32:16 -07: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 04:32:16 -07:00
|
|
|
...httpAgentOptions,
|
|
|
|
...httpsAgentOptions,
|
|
|
|
...proxyOptions,
|
2023-03-27 20:40:19 -07:00
|
|
|
tls: {
|
|
|
|
rejectUnauthorized: httpsAgentOptions.rejectUnauthorized,
|
|
|
|
},
|
2021-11-04 04:32:16 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
httpAgent = agent;
|
|
|
|
httpsAgent = agent;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
httpAgent,
|
|
|
|
httpsAgent
|
|
|
|
};
|
|
|
|
}
|
2022-04-07 08:03:45 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reload proxy settings for current monitors
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
|
|
|
static async reloadProxy() {
|
2022-04-19 00:38:59 -07:00
|
|
|
const server = UptimeKumaServer.getInstance();
|
|
|
|
|
2022-04-07 08:03:45 -07:00
|
|
|
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 10:37:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Applies given proxy id to monitors
|
2023-08-11 00:46:41 -07:00
|
|
|
* @param {number} proxyID ID of proxy to apply
|
|
|
|
* @param {number} userID ID of proxy owner
|
|
|
|
* @returns {Promise<void>}
|
2021-10-30 10:37:15 -07:00
|
|
|
*/
|
|
|
|
async function applyProxyEveryMonitor(proxyID, userID) {
|
|
|
|
// Find all monitors with id and proxy id
|
2022-04-17 00:27:35 -07:00
|
|
|
const monitors = await R.getAll("SELECT id, proxy_id FROM monitor WHERE user_id = ?", [ userID ]);
|
2021-10-30 10:37:15 -07:00
|
|
|
|
|
|
|
// Update proxy id not match with given proxy id
|
|
|
|
for (const monitor of monitors) {
|
|
|
|
if (monitor.proxy_id !== proxyID) {
|
2022-04-17 00:27:35 -07:00
|
|
|
await R.exec("UPDATE monitor SET proxy_id = ? WHERE id = ?", [ proxyID, monitor.id ]);
|
2021-10-30 10:37:15 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
Proxy,
|
|
|
|
};
|