mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-12-17 18:37:16 +00:00
feat: monitor debug curl (#5152)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
parent
a5faa4b225
commit
f791d4a9bf
2 changed files with 135 additions and 2 deletions
|
@ -97,6 +97,8 @@
|
||||||
"pushOthers": "Others",
|
"pushOthers": "Others",
|
||||||
"programmingLanguages": "Programming Languages",
|
"programmingLanguages": "Programming Languages",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
|
"Debug": "Debug",
|
||||||
|
"Copy": "Copy",
|
||||||
"Notifications": "Notifications",
|
"Notifications": "Notifications",
|
||||||
"Not available, please setup.": "Not available, please set up.",
|
"Not available, please setup.": "Not available, please set up.",
|
||||||
"Setup Notification": "Set Up Notification",
|
"Setup Notification": "Set Up Notification",
|
||||||
|
@ -249,6 +251,14 @@
|
||||||
"PushUrl": "Push URL",
|
"PushUrl": "Push URL",
|
||||||
"HeadersInvalidFormat": "The request headers are not valid JSON: ",
|
"HeadersInvalidFormat": "The request headers are not valid JSON: ",
|
||||||
"BodyInvalidFormat": "The request body is not valid JSON: ",
|
"BodyInvalidFormat": "The request body is not valid JSON: ",
|
||||||
|
"CopyToClipboardError": "Couldn't copy to clipbard: {error}",
|
||||||
|
"CopyToClipboardSuccess": "Copied!",
|
||||||
|
"CurlDebugInfo": "To debug the monitor, you can either paste this into your own machines terminal or into the machines terminal which uptime kuma is running on and see what you are requesting.{newiline}Please be aware of networking differences like {firewalls}, {dns_resolvers} or {docker_networks}.",
|
||||||
|
"firewalls": "firewalls",
|
||||||
|
"dns resolvers": "dns resolvers",
|
||||||
|
"docker networks": "docker networks",
|
||||||
|
"CurlDebugInfoOAuth2CCUnsupported": "Full oauth client credential flow is not supported in {curl}.{newline}Please get a bearer token and pass it via the {oauth2_bearer} option.",
|
||||||
|
"CurlDebugInfoProxiesUnsupported": "Proxy support in the above {curl} command is currently not implemented.",
|
||||||
"Monitor History": "Monitor History",
|
"Monitor History": "Monitor History",
|
||||||
"clearDataOlderThan": "Keep monitor history data for {0} days.",
|
"clearDataOlderThan": "Keep monitor history data for {0} days.",
|
||||||
"PasswordsDoNotMatch": "Passwords do not match.",
|
"PasswordsDoNotMatch": "Passwords do not match.",
|
||||||
|
|
|
@ -982,13 +982,23 @@
|
||||||
<div class="fixed-bottom-bar p-3">
|
<div class="fixed-bottom-bar p-3">
|
||||||
<button
|
<button
|
||||||
id="monitor-submit-btn"
|
id="monitor-submit-btn"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary me-2"
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="processing"
|
:disabled="processing"
|
||||||
data-testid="save-button"
|
data-testid="save-button"
|
||||||
>
|
>
|
||||||
{{ $t("Save") }}
|
{{ $t("Save") }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="monitor.type === 'http'"
|
||||||
|
id="monitor-debug-btn"
|
||||||
|
class="btn btn-outline-primary"
|
||||||
|
type="button"
|
||||||
|
:disabled="processing"
|
||||||
|
@click.stop="modal.show()"
|
||||||
|
>
|
||||||
|
{{ $t("Debug") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1000,9 +1010,58 @@
|
||||||
<RemoteBrowserDialog ref="remoteBrowserDialog" />
|
<RemoteBrowserDialog ref="remoteBrowserDialog" />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
<div ref="modal" class="modal fade" tabindex="-1">
|
||||||
|
<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";
|
||||||
|
@ -1017,8 +1076,10 @@ 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";
|
||||||
|
const userAgent = `'Uptime-Kuma/${version}'`;
|
||||||
|
|
||||||
const toast = useToast;
|
const toast = useToast();
|
||||||
|
|
||||||
const pushTokenLength = 32;
|
const pushTokenLength = 32;
|
||||||
|
|
||||||
|
@ -1081,6 +1142,7 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
modal: null,
|
||||||
minInterval: MIN_INTERVAL_SECOND,
|
minInterval: MIN_INTERVAL_SECOND,
|
||||||
maxInterval: MAX_INTERVAL_SECOND,
|
maxInterval: MAX_INTERVAL_SECOND,
|
||||||
processing: false,
|
processing: false,
|
||||||
|
@ -1108,6 +1170,53 @@ 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)
|
||||||
|
@ -1464,6 +1573,7 @@ message HealthCheckResponse {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.modal = new Modal(this.$refs.modal);
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
let acceptedStatusCodeOptions = [
|
let acceptedStatusCodeOptions = [
|
||||||
|
@ -1504,6 +1614,14 @@ 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}
|
||||||
|
@ -1792,4 +1910,9 @@ message HealthCheckResponse {
|
||||||
textarea {
|
textarea {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#curl-debug {
|
||||||
|
font-family: monospace;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue