mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-02-26 13:35:56 +00:00
Compare commits
5 commits
934c547cf2
...
c25956eced
Author | SHA1 | Date | |
---|---|---|---|
|
c25956eced | ||
|
46d8744fa4 | ||
|
7d8dc55dbe | ||
|
ff197839b3 | ||
|
c90aacc62c |
6 changed files with 321 additions and 122 deletions
|
@ -27,7 +27,6 @@ RUN mkdir ./data
|
||||||
# ⭐ Main Image
|
# ⭐ Main Image
|
||||||
############################################
|
############################################
|
||||||
FROM $BASE_IMAGE AS release
|
FROM $BASE_IMAGE AS release
|
||||||
USER node
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source="https://github.com/louislam/uptime-kuma"
|
LABEL org.opencontainers.image.source="https://github.com/louislam/uptime-kuma"
|
||||||
|
@ -46,6 +45,7 @@ CMD ["node", "server/server.js"]
|
||||||
# Rootless Image
|
# Rootless Image
|
||||||
############################################
|
############################################
|
||||||
FROM release AS rootless
|
FROM release AS rootless
|
||||||
|
USER node
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# Mark as Nightly
|
# Mark as Nightly
|
||||||
|
|
|
@ -9,6 +9,7 @@ const mysql = require("mysql2/promise");
|
||||||
const { Settings } = require("./settings");
|
const { Settings } = require("./settings");
|
||||||
const { UptimeCalculator } = require("./uptime-calculator");
|
const { UptimeCalculator } = require("./uptime-calculator");
|
||||||
const dayjs = require("dayjs");
|
const dayjs = require("dayjs");
|
||||||
|
const { SimpleMigrationServer } = require("./utils/simple-migration-server");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database & App Data Folder
|
* Database & App Data Folder
|
||||||
|
@ -382,9 +383,11 @@ class Database {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patch the database
|
* Patch the database
|
||||||
|
* @param {number} port Start the migration server for aggregate tables on this port if provided
|
||||||
|
* @param {string} hostname Start the migration server for aggregate tables on this hostname if provided
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static async patch() {
|
static async patch(port = undefined, hostname = undefined) {
|
||||||
// Still need to keep this for old versions of Uptime Kuma
|
// Still need to keep this for old versions of Uptime Kuma
|
||||||
if (Database.dbConfig.type === "sqlite") {
|
if (Database.dbConfig.type === "sqlite") {
|
||||||
await this.patchSqlite();
|
await this.patchSqlite();
|
||||||
|
@ -409,7 +412,7 @@ class Database {
|
||||||
await R.exec("PRAGMA foreign_keys = ON");
|
await R.exec("PRAGMA foreign_keys = ON");
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.migrateAggregateTable();
|
await this.migrateAggregateTable(port, hostname);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Allow missing patch files for downgrade or testing pr.
|
// Allow missing patch files for downgrade or testing pr.
|
||||||
|
@ -735,9 +738,11 @@ class Database {
|
||||||
* Normally, it should be in transaction, but UptimeCalculator wasn't designed to be in transaction before that.
|
* Normally, it should be in transaction, but UptimeCalculator wasn't designed to be in transaction before that.
|
||||||
* I don't want to heavily modify the UptimeCalculator, so it is not in transaction.
|
* I don't want to heavily modify the UptimeCalculator, so it is not in transaction.
|
||||||
* Run `npm run reset-migrate-aggregate-table-state` to reset, in case the migration is interrupted.
|
* Run `npm run reset-migrate-aggregate-table-state` to reset, in case the migration is interrupted.
|
||||||
|
* @param {number} port Start the migration server on this port if provided
|
||||||
|
* @param {string} hostname Start the migration server on this hostname if provided
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static async migrateAggregateTable() {
|
static async migrateAggregateTable(port, hostname = undefined) {
|
||||||
log.debug("db", "Enter Migrate Aggregate Table function");
|
log.debug("db", "Enter Migrate Aggregate Table function");
|
||||||
|
|
||||||
// Add a setting for 2.0.0-dev users to skip this migration
|
// Add a setting for 2.0.0-dev users to skip this migration
|
||||||
|
@ -758,6 +763,18 @@ class Database {
|
||||||
throw new Error("Aggregate table migration is already in progress");
|
throw new Error("Aggregate table migration is already in progress");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start migration server for displaying the migration status
|
||||||
|
* @type {SimpleMigrationServer}
|
||||||
|
*/
|
||||||
|
let migrationServer;
|
||||||
|
let msg;
|
||||||
|
|
||||||
|
if (port) {
|
||||||
|
migrationServer = new SimpleMigrationServer();
|
||||||
|
await migrationServer.start(port, hostname);
|
||||||
|
}
|
||||||
|
|
||||||
await Settings.set("migrateAggregateTableState", "migrating");
|
await Settings.set("migrateAggregateTableState", "migrating");
|
||||||
|
|
||||||
log.info("db", "Migrating Aggregate Table");
|
log.info("db", "Migrating Aggregate Table");
|
||||||
|
@ -777,6 +794,7 @@ class Database {
|
||||||
let count = countResult.count;
|
let count = countResult.count;
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
log.warn("db", `Aggregate table ${table} is not empty, migration will not be started (Maybe you were using 2.0.0-dev?)`);
|
log.warn("db", `Aggregate table ${table} is not empty, migration will not be started (Maybe you were using 2.0.0-dev?)`);
|
||||||
|
await migrationServer?.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -811,7 +829,9 @@ class Database {
|
||||||
`, [ monitor.monitor_id, date.date ]);
|
`, [ monitor.monitor_id, date.date ]);
|
||||||
|
|
||||||
if (heartbeats.length > 0) {
|
if (heartbeats.length > 0) {
|
||||||
log.info("db", `[DON'T STOP] Migrating monitor data ${monitor.monitor_id} - ${date.date} [${progressPercent.toFixed(2)}%][${i}/${monitors.length}]`);
|
msg = `[DON'T STOP] Migrating monitor data ${monitor.monitor_id} - ${date.date} [${progressPercent.toFixed(2)}%][${i}/${monitors.length}]`;
|
||||||
|
log.info("db", msg);
|
||||||
|
migrationServer?.update(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let heartbeat of heartbeats) {
|
for (let heartbeat of heartbeats) {
|
||||||
|
@ -829,9 +849,13 @@ class Database {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Database.clearHeartbeatData(true);
|
msg = "Clearing non-important heartbeats";
|
||||||
|
log.info("db", msg);
|
||||||
|
migrationServer?.update(msg);
|
||||||
|
|
||||||
|
await Database.clearHeartbeatData(true);
|
||||||
await Settings.set("migrateAggregateTableState", "migrated");
|
await Settings.set("migrateAggregateTableState", "migrated");
|
||||||
|
await migrationServer?.stop();
|
||||||
|
|
||||||
if (monitors.length > 0) {
|
if (monitors.length > 0) {
|
||||||
log.info("db", "Aggregate Table Migration Completed");
|
log.info("db", "Aggregate Table Migration Completed");
|
||||||
|
|
|
@ -1716,7 +1716,7 @@ async function initDatabase(testMode = false) {
|
||||||
log.info("server", "Connected to the database");
|
log.info("server", "Connected to the database");
|
||||||
|
|
||||||
// Patch the database
|
// Patch the database
|
||||||
await Database.patch();
|
await Database.patch(port, hostname);
|
||||||
|
|
||||||
let jwtSecretBean = await R.findOne("setting", " `key` = ? ", [
|
let jwtSecretBean = await R.findOne("setting", " `key` = ? ", [
|
||||||
"jwtSecret",
|
"jwtSecret",
|
||||||
|
|
84
server/utils/simple-migration-server.js
Normal file
84
server/utils/simple-migration-server.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
const express = require("express");
|
||||||
|
const http = require("node:http");
|
||||||
|
const { log } = require("../../src/util");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SimpleMigrationServer
|
||||||
|
* For displaying the migration status of the server
|
||||||
|
* Also, it is used to let Docker healthcheck know the status of the server, as the main server is not started yet, healthcheck will think the server is down incorrectly.
|
||||||
|
*/
|
||||||
|
class SimpleMigrationServer {
|
||||||
|
/**
|
||||||
|
* Express app instance
|
||||||
|
* @type {?Express}
|
||||||
|
*/
|
||||||
|
app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server instance
|
||||||
|
* @type {?Server}
|
||||||
|
*/
|
||||||
|
server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response object
|
||||||
|
* @type {?Response}
|
||||||
|
*/
|
||||||
|
response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the server
|
||||||
|
* @param {number} port Port
|
||||||
|
* @param {string} hostname Hostname
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
start(port, hostname) {
|
||||||
|
this.app = express();
|
||||||
|
this.server = http.createServer(this.app);
|
||||||
|
|
||||||
|
this.app.get("/", (req, res) => {
|
||||||
|
res.set("Content-Type", "text/plain");
|
||||||
|
res.write("Migration is in progress, listening message...\n");
|
||||||
|
if (this.response) {
|
||||||
|
this.response.write("Disconnected\n");
|
||||||
|
this.response.end();
|
||||||
|
}
|
||||||
|
this.response = res;
|
||||||
|
// never ending response
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.server.listen(port, hostname, () => {
|
||||||
|
if (hostname) {
|
||||||
|
log.info("migration", `Migration server is running on http://${hostname}:${port}`);
|
||||||
|
} else {
|
||||||
|
log.info("migration", `Migration server is running on http://localhost:${port}`);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the message
|
||||||
|
* @param {string} msg Message to update
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
update(msg) {
|
||||||
|
this.response?.write(msg + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the server
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async stop() {
|
||||||
|
this.response?.write("Finished, please refresh this page.\n");
|
||||||
|
this.response?.end();
|
||||||
|
await this.server?.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SimpleMigrationServer,
|
||||||
|
};
|
201
src/components/DebugMonitorDialog.vue
Normal file
201
src/components/DebugMonitorDialog.vue
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
<template>
|
||||||
|
<div ref="modal" class="modal fade" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div v-if="monitor?.type === 'http'">
|
||||||
|
<textarea id="curl-debug" v-model="curlCommand" class="form-control mb-3" readonly wrap="off"></textarea>
|
||||||
|
<button
|
||||||
|
id="debug-copy-btn" class="btn btn-outline-primary position-absolute top-0 end-0 mt-3 me-3 border-0"
|
||||||
|
type="button" @click.stop="copyToClipboard"
|
||||||
|
>
|
||||||
|
<font-awesome-icon icon="copy" />
|
||||||
|
</button>
|
||||||
|
<i18n-t keypath="CurlDebugInfo" tag="p" class="form-text">
|
||||||
|
<template #newiline>
|
||||||
|
<br>
|
||||||
|
</template>
|
||||||
|
<template #firewalls>
|
||||||
|
<a href="https://xkcd.com/2259/" target="_blank">{{ $t('firewalls') }}</a>
|
||||||
|
</template>
|
||||||
|
<template #dns_resolvers>
|
||||||
|
<a
|
||||||
|
href="https://www.reddit.com/r/sysadmin/comments/rxho93/thank_you_for_the_running_its_always_dns_joke_its/"
|
||||||
|
target="_blank"
|
||||||
|
>{{ $t('dns resolvers') }}</a>
|
||||||
|
</template>
|
||||||
|
<template #docker_networks>
|
||||||
|
<a href="https://youtu.be/bKFMS5C4CG0" target="_blank">{{ $t('docker networks') }}</a>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<div
|
||||||
|
v-if="monitor.authMethod === 'oauth2-cc'" class="alert alert-warning d-flex align-items-center gap-2"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
<div role="img" aria-label="Warning:">⚠️</div>
|
||||||
|
<i18n-t keypath="CurlDebugInfoOAuth2CCUnsupported" tag="div">
|
||||||
|
<template #curl>
|
||||||
|
<code>curl</code>
|
||||||
|
</template>
|
||||||
|
<template #newline>
|
||||||
|
<br>
|
||||||
|
</template>
|
||||||
|
<template #oauth2_bearer>
|
||||||
|
<code>--oauth2-bearer TOKEN</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
<div v-if="monitor.proxyId" class="alert alert-warning d-flex align-items-center gap-2" role="alert">
|
||||||
|
<div role="img" aria-label="Warning:">⚠️</div>
|
||||||
|
<i18n-t keypath="CurlDebugInfoProxiesUnsupported" tag="div">
|
||||||
|
<template #curl>
|
||||||
|
<code>curl</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { Modal } from "bootstrap";
|
||||||
|
import { version } from "../../package.json";
|
||||||
|
import { useToast } from "vue-toastification";
|
||||||
|
const toast = useToast();
|
||||||
|
export default {
|
||||||
|
name: "DebugMonitor",
|
||||||
|
props: {
|
||||||
|
/** Monitor this represents */
|
||||||
|
monitor: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
modal: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
curlCommand() {
|
||||||
|
if (this.monitor === null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
let method = this.monitor.method;
|
||||||
|
if ([ "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS" ].indexOf(method) === -1) {
|
||||||
|
// set to a custom value => could lead to injections
|
||||||
|
method = this.escapeShell(method);
|
||||||
|
}
|
||||||
|
const command = [ "curl", "--verbose", "--head", "--request", method, "\\\n" ];
|
||||||
|
command.push("--user-agent", `'Uptime-Kuma/${version}'`, "\\\n");
|
||||||
|
if (this.monitor.ignoreTls) {
|
||||||
|
command.push("--insecure", "\\\n");
|
||||||
|
}
|
||||||
|
if (this.monitor.headers) {
|
||||||
|
try {
|
||||||
|
for (const [ key, value ] of Object.entries(JSON.parse(this.monitor.headers))) {
|
||||||
|
command.push("--header", `'${this.escapeShellNoQuotes(key)}: ${this.escapeShellNoQuotes(value)}'`, "\\\n");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
command.push("--header", this.escapeShell(this.monitor.headers), "\\\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.monitor.authMethod === "basic") {
|
||||||
|
command.push("--basic", "--user", `'${this.escapeShellNoQuotes(this.monitor.basic_auth_user)}:${this.escapeShellNoQuotes(this.monitor.basic_auth_pass)}'`, "\\\n");
|
||||||
|
} else if (this.monitor.authMethod === "mtls") {
|
||||||
|
command.push("--cacert", this.escapeShell(this.monitor.tlsCa), "\\\n");
|
||||||
|
command.push("--key", this.escapeShell(this.monitor.tlsKey), "\\\n");
|
||||||
|
command.push( "--cert", this.escapeShell(this.monitor.tlsCert), "\\\n");
|
||||||
|
} else if (this.monitor.authMethod === "ntlm") {
|
||||||
|
let domain = "";
|
||||||
|
if (this.monitor.authDomain) {
|
||||||
|
domain = `${this.monitor.authDomain}/`;
|
||||||
|
}
|
||||||
|
command.push("--ntlm", "--user", `'${this.escapeShellNoQuotes(domain)}${this.escapeShellNoQuotes(this.monitor.basic_auth_user)}:${this.escapeShellNoQuotes(this.monitor.basic_auth_pass)}'`, "\\\n");
|
||||||
|
}
|
||||||
|
if (this.monitor.body && this.monitor.httpBodyEncoding === "json") {
|
||||||
|
let json = "";
|
||||||
|
try {
|
||||||
|
// trying to parse the supplied data as json to trim whitespace
|
||||||
|
json = JSON.stringify(JSON.parse(this.monitor.body));
|
||||||
|
} catch (e) {
|
||||||
|
json = this.monitor.body;
|
||||||
|
}
|
||||||
|
command.push("--header", "'Content-Type: application/json'", "\\\n");
|
||||||
|
command.push("--data", this.escapeShell(json), "\\\n");
|
||||||
|
} else if (this.monitor.body && this.monitor.httpBodyEncoding === "xml") {
|
||||||
|
command.push("--headers", "'Content-Type: application/xml'", "\\\n");
|
||||||
|
command.push("--data", this.escapeShell(this.monitor.body), "\\\n");
|
||||||
|
}
|
||||||
|
if (this.monitor.maxredirects) {
|
||||||
|
command.push("--location", "--max-redirs", this.escapeShell(this.monitor.maxredirects), "\\\n");
|
||||||
|
}
|
||||||
|
if (this.monitor.timeout) {
|
||||||
|
command.push("--max-time", this.escapeShell(this.monitor.timeout), "\\\n");
|
||||||
|
}
|
||||||
|
if (this.monitor.maxretries) {
|
||||||
|
command.push("--retry", this.escapeShell(this.monitor.maxretries), "\\\n");
|
||||||
|
}
|
||||||
|
command.push("--url", this.escapeShell(this.monitor.url));
|
||||||
|
return command.join(" ");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.modal = new Modal(this.$refs.modal);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Show the dialog
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
show() {
|
||||||
|
this.modal.show();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Escape a string for use in a shell
|
||||||
|
* @param {string|number} s string to escape
|
||||||
|
* @returns {string} escaped, quoted string
|
||||||
|
*/
|
||||||
|
escapeShell(s) {
|
||||||
|
if (typeof s == "number") {
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
return "'" + this.escapeShellNoQuotes(s) + "'";
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Escape a string for use in a shell
|
||||||
|
* @param {string} s string to escape
|
||||||
|
* @returns {string} escaped string
|
||||||
|
*/
|
||||||
|
escapeShellNoQuotes(s) {
|
||||||
|
return s.replace(/(['"$`\\])/g, "\\$1");
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Copies a value to the clipboard and shows toasts with the success/error
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
async copyToClipboard() {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(this.curlCommand);
|
||||||
|
toast.success(this.$t("CopyToClipboardSuccess"));
|
||||||
|
} catch (err) {
|
||||||
|
toast.error(this.$t("CopyToClipboardError", { error: err.message }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "../assets/vars";
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#curl-debug {
|
||||||
|
font-family: monospace;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1042,7 +1042,7 @@
|
||||||
class="btn btn-outline-primary"
|
class="btn btn-outline-primary"
|
||||||
type="button"
|
type="button"
|
||||||
:disabled="processing"
|
:disabled="processing"
|
||||||
@click.stop="modal.show()"
|
@click="$refs.debugMonitorDialog.show()"
|
||||||
>
|
>
|
||||||
{{ $t("Debug") }}
|
{{ $t("Debug") }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1057,58 +1057,10 @@
|
||||||
<RemoteBrowserDialog ref="remoteBrowserDialog" />
|
<RemoteBrowserDialog ref="remoteBrowserDialog" />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<div ref="modal" class="modal fade" tabindex="-1">
|
<DebugMonitorDialog ref="debugMonitorDialog" :monitor="monitor" />
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-body">
|
|
||||||
<textarea id="curl-debug" v-model="curlCommand" class="form-control mb-3" readonly wrap="off"></textarea>
|
|
||||||
<button id="debug-copy-btn" class="btn btn-outline-primary position-absolute top-0 end-0 mt-3 me-3 border-0" type="button" @click.stop="copyToClipboard">
|
|
||||||
<font-awesome-icon icon="copy" />
|
|
||||||
</button>
|
|
||||||
<i18n-t keypath="CurlDebugInfo" tag="p" class="form-text">
|
|
||||||
<template #newiline>
|
|
||||||
<br>
|
|
||||||
</template>
|
|
||||||
<template #firewalls>
|
|
||||||
<a href="https://xkcd.com/2259/" target="_blank">{{ $t('firewalls') }}</a>
|
|
||||||
</template>
|
|
||||||
<template #dns_resolvers>
|
|
||||||
<a href="https://www.reddit.com/r/sysadmin/comments/rxho93/thank_you_for_the_running_its_always_dns_joke_its/" target="_blank">{{ $t('dns resolvers') }}</a>
|
|
||||||
</template>
|
|
||||||
<template #docker_networks>
|
|
||||||
<a href="https://youtu.be/bKFMS5C4CG0" target="_blank">{{ $t('docker networks') }}</a>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
<div v-if="monitor.authMethod === 'oauth2-cc'" class="alert alert-warning d-flex align-items-center gap-2" role="alert">
|
|
||||||
<div role="img" aria-label="Warning:">⚠️</div>
|
|
||||||
<i18n-t keypath="CurlDebugInfoOAuth2CCUnsupported" tag="div">
|
|
||||||
<template #curl>
|
|
||||||
<code>curl</code>
|
|
||||||
</template>
|
|
||||||
<template #newline>
|
|
||||||
<br>
|
|
||||||
</template>
|
|
||||||
<template #oauth2_bearer>
|
|
||||||
<code>--oauth2-bearer TOKEN</code>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
</div>
|
|
||||||
<div v-if="monitor.proxyId" class="alert alert-warning d-flex align-items-center gap-2" role="alert">
|
|
||||||
<div role="img" aria-label="Warning:">⚠️</div>
|
|
||||||
<i18n-t keypath="CurlDebugInfoProxiesUnsupported" tag="div">
|
|
||||||
<template #curl>
|
|
||||||
<code>curl</code>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Modal } from "bootstrap";
|
|
||||||
import VueMultiselect from "vue-multiselect";
|
import VueMultiselect from "vue-multiselect";
|
||||||
import { useToast } from "vue-toastification";
|
import { useToast } from "vue-toastification";
|
||||||
import ActionSelect from "../components/ActionSelect.vue";
|
import ActionSelect from "../components/ActionSelect.vue";
|
||||||
|
@ -1123,8 +1075,7 @@ import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } fro
|
||||||
import { hostNameRegexPattern } from "../util-frontend";
|
import { hostNameRegexPattern } from "../util-frontend";
|
||||||
import HiddenInput from "../components/HiddenInput.vue";
|
import HiddenInput from "../components/HiddenInput.vue";
|
||||||
import EditMonitorConditions from "../components/EditMonitorConditions.vue";
|
import EditMonitorConditions from "../components/EditMonitorConditions.vue";
|
||||||
import { version } from "../../package.json";
|
import DebugMonitorDialog from "../components/DebugMonitorDialog.vue";
|
||||||
const userAgent = `'Uptime-Kuma/${version}'`;
|
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
|
@ -1177,6 +1128,7 @@ const monitorDefaults = {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
DebugMonitorDialog,
|
||||||
HiddenInput,
|
HiddenInput,
|
||||||
ActionSelect,
|
ActionSelect,
|
||||||
ProxyDialog,
|
ProxyDialog,
|
||||||
|
@ -1219,58 +1171,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
curlCommand() {
|
|
||||||
const command = [ "curl", "--verbose", "--head", "--request", this.monitor.method, "\\\n", "--user-agent", userAgent, "\\\n" ];
|
|
||||||
if (this.monitor.ignoreTls) {
|
|
||||||
command.push("--insecure", "\\\n");
|
|
||||||
}
|
|
||||||
if (this.monitor.headers) {
|
|
||||||
try {
|
|
||||||
// trying to parse the supplied data as json to trim whitespace
|
|
||||||
for (const [ key, value ] of Object.entries(JSON.parse(this.monitor.headers))) {
|
|
||||||
command.push("--header", `'${key}: ${value}'`, "\\\n");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
command.push("--header", `'${this.monitor.headers}'`, "\\\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.monitor.authMethod === "basic") {
|
|
||||||
command.push("--user", `${this.monitor.basic_auth_user}:${this.monitor.basic_auth_pass}`, "--basic", "\\\n");
|
|
||||||
} else if (this.monitor.authmethod === "mtls") {
|
|
||||||
command.push("--cacert", `'${this.monitor.tlsCa}'`, "\\\n", "--key", `'${this.monitor.tlsKey}'`, "\\\n", "--cert", `'${this.monitor.tlsCert}'`, "\\\n");
|
|
||||||
} else if (this.monitor.authMethod === "ntlm") {
|
|
||||||
command.push("--user", `'${this.monitor.authDomain ? `${this.monitor.authDomain}/` : ""}${this.monitor.basic_auth_user}:${this.monitor.basic_auth_pass}'`, "--ntlm", "\\\n");
|
|
||||||
}
|
|
||||||
if (this.monitor.body && this.monitor.httpBodyEncoding === "json") {
|
|
||||||
let json = "";
|
|
||||||
try {
|
|
||||||
// trying to parse the supplied data as json to trim whitespace
|
|
||||||
json = JSON.stringify(JSON.parse(this.monitor.body));
|
|
||||||
} catch (e) {
|
|
||||||
json = this.monitor.body;
|
|
||||||
}
|
|
||||||
command.push("--header", "'Content-Type: application/json'", "\\\n", "--data", `'${json}'`, "\\\n");
|
|
||||||
} else if (this.monitor.body && this.monitor.httpBodyEncoding === "xml") {
|
|
||||||
command.push("--headers", "'Content-Type: application/xml'", "\\\n", "--data", `'${this.monitor.body}'`, "\\\n");
|
|
||||||
}
|
|
||||||
if (this.monitor.maxredirects) {
|
|
||||||
command.push("--location", "--max-redirs", this.monitor.maxredirects, "\\\n");
|
|
||||||
}
|
|
||||||
if (this.monitor.timeout) {
|
|
||||||
command.push("--max-time", this.monitor.timeout, "\\\n");
|
|
||||||
}
|
|
||||||
if (this.monitor.maxretries) {
|
|
||||||
command.push("--retry", this.monitor.maxretries, "\\\n");
|
|
||||||
}
|
|
||||||
command.push("--url", this.monitor.url);
|
|
||||||
return command.join(" ");
|
|
||||||
},
|
|
||||||
|
|
||||||
ipRegex() {
|
ipRegex() {
|
||||||
|
|
||||||
// Allow to test with simple dns server with port (127.0.0.1:5300)
|
// Allow to test with simple dns server with port (127.0.0.1:5300)
|
||||||
if (! isDev) {
|
if (!isDev) {
|
||||||
return this.ipRegexPattern;
|
return this.ipRegexPattern;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -1623,7 +1526,6 @@ message HealthCheckResponse {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.modal = new Modal(this.$refs.modal);
|
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
let acceptedStatusCodeOptions = [
|
let acceptedStatusCodeOptions = [
|
||||||
|
@ -1664,14 +1566,6 @@ message HealthCheckResponse {
|
||||||
this.kafkaSaslMechanismOptions = kafkaSaslMechanismOptions;
|
this.kafkaSaslMechanismOptions = kafkaSaslMechanismOptions;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async copyToClipboard() {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(this.curlCommand);
|
|
||||||
toast.success(this.$t("CopyToClipboardSuccess"));
|
|
||||||
} catch (err) {
|
|
||||||
toast.error(this.$t("CopyToClipboardError", { error: err.message }));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Initialize the edit monitor form
|
* Initialize the edit monitor form
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
|
@ -1975,8 +1869,4 @@ message HealthCheckResponse {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#curl-debug {
|
|
||||||
font-family: monospace;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Add table
Reference in a new issue