mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
8 Commits
release
...
probe-prox
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b601c7b53 | ||
|
|
7cf3ed713f | ||
|
|
8b6d4ded73 | ||
|
|
a4bd182b66 | ||
|
|
794482f851 | ||
|
|
e374595b14 | ||
|
|
9fb8a9c2d6 | ||
|
|
e62dd0dbbe |
@@ -50,11 +50,29 @@ export default class URL extends DatabaseProperty {
|
||||
this._protocol = v;
|
||||
}
|
||||
|
||||
private _username: string | null = null;
|
||||
public get username(): string | null {
|
||||
return this._username;
|
||||
}
|
||||
public set username(v: string | null) {
|
||||
this._username = v;
|
||||
}
|
||||
|
||||
private _password: string | null = null;
|
||||
public get password(): string | null {
|
||||
return this._password;
|
||||
}
|
||||
public set password(v: string | null) {
|
||||
this._password = v;
|
||||
}
|
||||
|
||||
public constructor(
|
||||
protocol: Protocol,
|
||||
hostname: Hostname | string | Email,
|
||||
route?: Route,
|
||||
queryString?: string,
|
||||
username?: string | null,
|
||||
password?: string | null,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -89,14 +107,33 @@ export default class URL extends DatabaseProperty {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.username = username || null;
|
||||
this.password = password || null;
|
||||
}
|
||||
|
||||
public isHttps(): boolean {
|
||||
return this.protocol === Protocol.HTTPS;
|
||||
public getUsername(): string | null {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public getPassword(): string | null {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public override toString(): string {
|
||||
let urlString: string = `${this.protocol}${this.hostname || this.email}`;
|
||||
let urlString: string = `${this.protocol}`;
|
||||
|
||||
// Add auth if present
|
||||
if (this.username) {
|
||||
urlString += `${this.username}`;
|
||||
if (this.password) {
|
||||
urlString += `:${this.password}`;
|
||||
}
|
||||
urlString += `@`;
|
||||
}
|
||||
|
||||
urlString += `${this.hostname || this.email}`;
|
||||
|
||||
if (!this.email && !urlString.startsWith("mailto:")) {
|
||||
if (this.route && this.route.toString().startsWith("/")) {
|
||||
if (urlString.endsWith("/")) {
|
||||
@@ -130,37 +167,47 @@ export default class URL extends DatabaseProperty {
|
||||
|
||||
public static fromString(url: string): URL {
|
||||
let protocol: Protocol = Protocol.HTTPS;
|
||||
let username: string | null = null;
|
||||
let password: string | null = null;
|
||||
|
||||
if (url.startsWith("https://")) {
|
||||
protocol = Protocol.HTTPS;
|
||||
url = url.replace("https://", "");
|
||||
}
|
||||
|
||||
if (url.startsWith("http://")) {
|
||||
} else if (url.startsWith("http://")) {
|
||||
protocol = Protocol.HTTP;
|
||||
url = url.replace("http://", "");
|
||||
}
|
||||
|
||||
if (url.startsWith("wss://")) {
|
||||
} else if (url.startsWith("wss://")) {
|
||||
protocol = Protocol.WSS;
|
||||
url = url.replace("wss://", "");
|
||||
}
|
||||
|
||||
if (url.startsWith("ws://")) {
|
||||
} else if (url.startsWith("ws://")) {
|
||||
protocol = Protocol.WS;
|
||||
url = url.replace("ws://", "");
|
||||
}
|
||||
|
||||
if (url.startsWith("mongodb://")) {
|
||||
} else if (url.startsWith("mongodb://")) {
|
||||
protocol = Protocol.MONGO_DB;
|
||||
url = url.replace("mongodb://", "");
|
||||
}
|
||||
|
||||
if (url.startsWith("mailto:")) {
|
||||
} else if (url.startsWith("mailto:")) {
|
||||
protocol = Protocol.MAIL;
|
||||
url = url.replace("mailto:", "");
|
||||
}
|
||||
|
||||
// Parse auth if present (username:password@)
|
||||
if (url.includes('@')) {
|
||||
const parts = url.split('@');
|
||||
if (parts.length > 1 && parts[0] && parts[1]) {
|
||||
const authPart = parts[0];
|
||||
if (authPart.includes(':')) {
|
||||
const authSplit = authPart.split(':');
|
||||
if (authSplit.length >= 2 && authSplit[0] && authSplit[1]) {
|
||||
username = decodeURIComponent(authSplit[0]);
|
||||
password = decodeURIComponent(authSplit[1]);
|
||||
}
|
||||
} else {
|
||||
username = decodeURIComponent(authPart);
|
||||
}
|
||||
url = parts[1]; // Remove auth part from URL
|
||||
}
|
||||
}
|
||||
|
||||
const hostname: Hostname = new Hostname(url.split("/")[0] || "");
|
||||
|
||||
let route: Route | undefined;
|
||||
@@ -173,7 +220,7 @@ export default class URL extends DatabaseProperty {
|
||||
|
||||
const queryString: string | undefined = url.split("?")[1] || "";
|
||||
|
||||
return new URL(protocol, hostname, route, queryString);
|
||||
return new URL(protocol, hostname, route, queryString, username, password);
|
||||
}
|
||||
|
||||
public removeQueryString(): URL {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import HTTPMethod from "./API/HTTPMethod";
|
||||
import Headers from "./API/Headers";
|
||||
import URL from "./API/URL";
|
||||
import Protocol from "./API/Protocol";
|
||||
import Dictionary from "./Dictionary";
|
||||
import HTML from "./Html";
|
||||
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
@@ -22,6 +23,7 @@ export default class WebsiteRequest {
|
||||
timeout?: number | undefined;
|
||||
isHeadRequest?: boolean | undefined;
|
||||
doNotFollowRedirects?: boolean | undefined;
|
||||
proxyUrl?: URL | undefined;
|
||||
},
|
||||
): Promise<WebsiteResponse> {
|
||||
const axiosOptions: AxiosRequestConfig = {
|
||||
@@ -41,6 +43,24 @@ export default class WebsiteRequest {
|
||||
axiosOptions.maxRedirects = 0;
|
||||
}
|
||||
|
||||
if (options.proxyUrl) {
|
||||
axiosOptions.proxy = {
|
||||
host: options.proxyUrl.hostname.hostname,
|
||||
port: options.proxyUrl.hostname.port?.toNumber() || 80,
|
||||
protocol: options.proxyUrl.protocol === Protocol.HTTPS ? 'https' : 'http',
|
||||
};
|
||||
|
||||
// Handle auth if present in URL
|
||||
const username = options.proxyUrl.getUsername();
|
||||
const password = options.proxyUrl.getPassword();
|
||||
if (username && password) {
|
||||
axiosOptions.proxy.auth = {
|
||||
username: decodeURIComponent(username),
|
||||
password: decodeURIComponent(password),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// use axios to fetch an HTML page
|
||||
let response: AxiosResponse | null = null;
|
||||
|
||||
|
||||
@@ -19,6 +19,23 @@ export interface RequestOptions {
|
||||
exponentialBackoff?: boolean | undefined;
|
||||
timeout?: number | undefined;
|
||||
doNotFollowRedirects?: boolean | undefined;
|
||||
proxyUrl?: URL | undefined;
|
||||
}
|
||||
|
||||
// Declare AggregateError interface for environments where it's not available
|
||||
interface AggregateError extends Error {
|
||||
errors: Error[];
|
||||
}
|
||||
|
||||
// Type guard for AggregateError
|
||||
function isAggregateError(error: unknown): error is AggregateError {
|
||||
return (
|
||||
error instanceof Error &&
|
||||
'name' in error &&
|
||||
(error as any).name === 'AggregateError' &&
|
||||
'errors' in error &&
|
||||
Array.isArray((error as any).errors)
|
||||
);
|
||||
}
|
||||
|
||||
export default class API {
|
||||
@@ -169,7 +186,7 @@ export default class API {
|
||||
return Promise.resolve(headers);
|
||||
}
|
||||
|
||||
public static getDefaultHeaders(_props?: any): Headers {
|
||||
public static getDefaultHeaders(_props?: unknown): Headers {
|
||||
const defaultHeaders: Headers = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
Accept: "application/json",
|
||||
@@ -400,6 +417,24 @@ export default class API {
|
||||
axiosOptions.maxRedirects = 0;
|
||||
}
|
||||
|
||||
if (options?.proxyUrl) {
|
||||
axiosOptions.proxy = {
|
||||
host: options.proxyUrl.hostname.hostname,
|
||||
port: options.proxyUrl.hostname.port?.toNumber() || 80,
|
||||
protocol: options.proxyUrl.protocol === Protocol.HTTPS ? 'https' : 'http',
|
||||
};
|
||||
|
||||
// Handle auth if present in URL
|
||||
const username = options.proxyUrl.getUsername();
|
||||
const password = options.proxyUrl.getPassword();
|
||||
if (username && password) {
|
||||
axiosOptions.proxy.auth = {
|
||||
username: decodeURIComponent(username),
|
||||
password: decodeURIComponent(password),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
result = await axios(axiosOptions);
|
||||
|
||||
break;
|
||||
@@ -471,12 +506,8 @@ export default class API {
|
||||
}
|
||||
|
||||
// Handle AggregateError by extracting the underlying error messages
|
||||
if (
|
||||
error &&
|
||||
(error as any).name === "AggregateError" &&
|
||||
(error as any).errors
|
||||
) {
|
||||
const aggregateErrors: Error[] = (error as any).errors as Error[];
|
||||
if (isAggregateError(error)) {
|
||||
const aggregateErrors: Error[] = (error as AggregateError).errors as Error[];
|
||||
const errorMessages: string[] = aggregateErrors
|
||||
.map((err: Error) => {
|
||||
return err.message || err.toString();
|
||||
|
||||
@@ -16,6 +16,18 @@ docker run --name oneuptime-probe --network host -e PROBE_KEY=<probe-key> -e PRO
|
||||
|
||||
If you are self hosting OneUptime, you can change `ONEUPTIME_URL` to your custom self hosted instance.
|
||||
|
||||
#### Proxy Configuration
|
||||
|
||||
If your network requires outgoing requests to go through a proxy, you can configure the probe to use a proxy server by setting the `PROXY_URL` environment variable. This is useful for monitoring resources that require proxy access.
|
||||
|
||||
The `PROXY_URL` should be in the format: `http://proxy.example.com:8080` or `https://user:pass@proxy.example.com:8080` for authenticated proxies.
|
||||
|
||||
Example with proxy:
|
||||
|
||||
```
|
||||
docker run --name oneuptime-probe --network host -e PROBE_KEY=<probe-key> -e PROBE_ID=<probe-id> -e ONEUPTIME_URL=https://oneuptime.com -e PROXY_URL=http://proxy.example.com:8080 -d oneuptime/probe:release
|
||||
```
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
You can also run the probe using docker-compose. Create a `docker-compose.yml` file with the following content:
|
||||
@@ -31,6 +43,7 @@ services:
|
||||
- PROBE_KEY=<probe-key>
|
||||
- PROBE_ID=<probe-id>
|
||||
- ONEUPTIME_URL=https://oneuptime.com
|
||||
- PROXY_URL=http://proxy.example.com:8080 # Optional: Proxy URL for monitoring requests
|
||||
network_mode: host
|
||||
restart: always
|
||||
```
|
||||
@@ -70,6 +83,8 @@ spec:
|
||||
value: "<probe-id>"
|
||||
- name: ONEUPTIME_URL
|
||||
value: "https://oneuptime.com"
|
||||
- name: PROXY_URL
|
||||
value: "http://proxy.example.com:8080" # Optional: Proxy URL for monitoring requests
|
||||
```
|
||||
|
||||
Then run the following command:
|
||||
|
||||
@@ -99,6 +99,10 @@ spec:
|
||||
{{- end }}
|
||||
- name: PROBE_MONITOR_FETCH_LIMIT
|
||||
value: {{ $val.monitorFetchLimit | squote }}
|
||||
{{- if $val.proxyUrl }}
|
||||
- name: PROXY_URL
|
||||
value: {{ $val.proxyUrl }}
|
||||
{{- end }}
|
||||
{{- if $val.disableTelemetryCollection }}
|
||||
- name: DISABLE_TELEMETRY
|
||||
value: {{ $val.disableTelemetryCollection | quote }}
|
||||
|
||||
@@ -200,6 +200,7 @@ probes:
|
||||
monitoringWorkers: 3
|
||||
monitorFetchLimit: 10
|
||||
monitorRetryLimit: 3
|
||||
proxyUrl: ""
|
||||
key:
|
||||
replicaCount: 1
|
||||
syntheticMonitorScriptTimeoutInMs: 60000
|
||||
|
||||
@@ -60,6 +60,10 @@ export const PROBE_MONITOR_FETCH_LIMIT: number = monitorFetchLimit;
|
||||
|
||||
export const HOSTNAME: string = process.env["HOSTNAME"] || "localhost";
|
||||
|
||||
export const PROXY_URL: URL | null = process.env["PROXY_URL"]
|
||||
? URL.fromString(process.env["PROXY_URL"])
|
||||
: null;
|
||||
|
||||
export const PROBE_SYNTHETIC_MONITOR_SCRIPT_TIMEOUT_IN_MS: number = process.env[
|
||||
"PROBE_SYNTHETIC_MONITOR_SCRIPT_TIMEOUT_IN_MS"
|
||||
]
|
||||
|
||||
@@ -11,6 +11,7 @@ import PositiveNumber from "Common/Types/PositiveNumber";
|
||||
import Sleep from "Common/Types/Sleep";
|
||||
import API from "Common/Utils/API";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import { PROXY_URL } from "../../../Config";
|
||||
|
||||
export interface APIResponse {
|
||||
url: URL;
|
||||
@@ -69,6 +70,7 @@ export default class ApiMonitor {
|
||||
{
|
||||
timeout: options.timeout?.toNumber() || 5000,
|
||||
doNotFollowRedirects: options.doNotFollowRedirects || false,
|
||||
proxyUrl: PROXY_URL || undefined,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -87,6 +89,7 @@ export default class ApiMonitor {
|
||||
{
|
||||
timeout: options.timeout?.toNumber() || 5000,
|
||||
doNotFollowRedirects: options.doNotFollowRedirects || false,
|
||||
proxyUrl: PROXY_URL || undefined,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PROBE_SYNTHETIC_MONITOR_SCRIPT_TIMEOUT_IN_MS } from "../../../Config";
|
||||
import { PROBE_SYNTHETIC_MONITOR_SCRIPT_TIMEOUT_IN_MS, PROXY_URL } from "../../../Config";
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import ReturnResult from "Common/Types/IsolatedVM/ReturnResult";
|
||||
import BrowserType from "Common/Types/Monitor/SyntheticMonitors/BrowserType";
|
||||
@@ -17,6 +17,13 @@ export interface SyntheticMonitorOptions {
|
||||
script: string;
|
||||
}
|
||||
|
||||
interface BrowserLaunchOptions {
|
||||
executablePath: string;
|
||||
proxy?: {
|
||||
server: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default class SyntheticMonitor {
|
||||
public static async execute(
|
||||
options: SyntheticMonitorOptions,
|
||||
@@ -132,8 +139,8 @@ export default class SyntheticMonitor {
|
||||
}
|
||||
|
||||
scriptResult.screenshots[screenshotName] = (
|
||||
result.returnValue.screenshots[screenshotName] as any
|
||||
).toString("base64"); // convert screennshots to base 64
|
||||
result.returnValue.screenshots[screenshotName] as Buffer
|
||||
).toString("base64"); // convert screenshots to base 64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,16 +279,32 @@ export default class SyntheticMonitor {
|
||||
let browser: Browser | null = null;
|
||||
|
||||
if (data.browserType === BrowserType.Chromium) {
|
||||
browser = await chromium.launch({
|
||||
const launchOptions: BrowserLaunchOptions = {
|
||||
executablePath: await this.getChromeExecutablePath(),
|
||||
});
|
||||
};
|
||||
|
||||
if (PROXY_URL) {
|
||||
launchOptions.proxy = {
|
||||
server: PROXY_URL.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
browser = await chromium.launch(launchOptions);
|
||||
page = await browser.newPage();
|
||||
}
|
||||
|
||||
if (data.browserType === BrowserType.Firefox) {
|
||||
browser = await firefox.launch({
|
||||
const launchOptions: BrowserLaunchOptions = {
|
||||
executablePath: await this.getFirefoxExecutablePath(),
|
||||
});
|
||||
};
|
||||
|
||||
if (PROXY_URL) {
|
||||
launchOptions.proxy = {
|
||||
server: PROXY_URL.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
browser = await firefox.launch(launchOptions);
|
||||
page = await browser.newPage();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import WebsiteRequest, { WebsiteResponse } from "Common/Types/WebsiteRequest";
|
||||
import API from "Common/Utils/API";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import { AxiosError } from "axios";
|
||||
import { PROXY_URL } from "../../../Config";
|
||||
|
||||
export interface ProbeWebsiteResponse {
|
||||
url: URL;
|
||||
@@ -64,6 +65,7 @@ export default class WebsiteMonitor {
|
||||
isHeadRequest: options.isHeadRequest,
|
||||
timeout: options.timeout?.toNumber() || 5000,
|
||||
doNotFollowRedirects: options.doNotFollowRedirects || false,
|
||||
proxyUrl: PROXY_URL || undefined,
|
||||
});
|
||||
|
||||
if (
|
||||
@@ -76,6 +78,7 @@ export default class WebsiteMonitor {
|
||||
isHeadRequest: false,
|
||||
timeout: options.timeout?.toNumber() || 5000,
|
||||
doNotFollowRedirects: options.doNotFollowRedirects || false,
|
||||
proxyUrl: PROXY_URL || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ GLOBAL_PROBE_1_ONEUPTIME_URL=http://localhost
|
||||
GLOBAL_PROBE_1_SYNTHETIC_MONITOR_SCRIPT_TIMEOUT_IN_MS=60000
|
||||
GLOBAL_PROBE_1_CUSTOM_CODE_MONITOR_SCRIPT_TIMEOUT_IN_MS=60000
|
||||
GLOBAL_PROBE_1_PORT=3874
|
||||
GLOBAL_PROBE_1_PROXY_URL= # Optional: Proxy URL for monitoring requests
|
||||
|
||||
GLOBAL_PROBE_2_NAME="Probe-2"
|
||||
GLOBAL_PROBE_2_DESCRIPTION="Global probe to monitor oneuptime resources"
|
||||
@@ -186,6 +187,7 @@ GLOBAL_PROBE_2_ONEUPTIME_URL=http://localhost
|
||||
GLOBAL_PROBE_2_SYNTHETIC_MONITOR_SCRIPT_TIMEOUT_IN_MS=60000
|
||||
GLOBAL_PROBE_2_CUSTOM_CODE_MONITOR_SCRIPT_TIMEOUT_IN_MS=60000
|
||||
GLOBAL_PROBE_2_PORT=3875
|
||||
GLOBAL_PROBE_2_PROXY_URL= # Optional: Proxy URL for monitoring requests
|
||||
|
||||
SMS_DEFAULT_COST_IN_CENTS=
|
||||
CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE=
|
||||
|
||||
@@ -362,6 +362,7 @@ services:
|
||||
PROBE_MONITOR_FETCH_LIMIT: ${GLOBAL_PROBE_1_MONITOR_FETCH_LIMIT}
|
||||
DISABLE_TELEMETRY: ${DISABLE_TELEMETRY_FOR_PROBE}
|
||||
PORT: ${GLOBAL_PROBE_1_PORT}
|
||||
PROXY_URL: ${GLOBAL_PROBE_1_PROXY_URL}
|
||||
logging:
|
||||
driver: "local"
|
||||
options:
|
||||
@@ -382,6 +383,7 @@ services:
|
||||
PROBE_MONITOR_FETCH_LIMIT: ${GLOBAL_PROBE_2_MONITOR_FETCH_LIMIT}
|
||||
DISABLE_TELEMETRY: ${DISABLE_TELEMETRY_FOR_PROBE}
|
||||
PORT: ${GLOBAL_PROBE_2_PORT}
|
||||
PROXY_URL: ${GLOBAL_PROBE_2_PROXY_URL}
|
||||
logging:
|
||||
driver: "local"
|
||||
options:
|
||||
|
||||
Reference in New Issue
Block a user