mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-10-29 18:50:39 +00:00
feat(http-requests): add support for methods, body and headers for http
This commit is contained in:
parent
c93f42794f
commit
3f0b85e5a8
8 changed files with 144 additions and 7 deletions
13
db/patch-http-monitor-method-body-and-headers.sql
Normal file
13
db/patch-http-monitor-method-body-and-headers.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD method TEXT default 'GET' not null;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD body TEXT default null;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD headers TEXT default null;
|
||||||
|
|
||||||
|
COMMIT;
|
|
@ -49,6 +49,7 @@ class Database {
|
||||||
"patch-incident-table.sql": true,
|
"patch-incident-table.sql": true,
|
||||||
"patch-group-table.sql": true,
|
"patch-group-table.sql": true,
|
||||||
"patch-monitor-push_token.sql": true,
|
"patch-monitor-push_token.sql": true,
|
||||||
|
"patch-http-monitor-method-body-and-headers.sql": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -53,6 +53,9 @@ class Monitor extends BeanModel {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
url: this.url,
|
url: this.url,
|
||||||
|
method: this.method,
|
||||||
|
body: this.body,
|
||||||
|
headers: this.headers,
|
||||||
hostname: this.hostname,
|
hostname: this.hostname,
|
||||||
port: this.port,
|
port: this.port,
|
||||||
maxretries: this.maxretries,
|
maxretries: this.maxretries,
|
||||||
|
@ -95,6 +98,31 @@ class Monitor extends BeanModel {
|
||||||
return JSON.parse(this.accepted_statuscodes_json);
|
return JSON.parse(this.accepted_statuscodes_json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert header string into an object:
|
||||||
|
* eg:
|
||||||
|
*
|
||||||
|
* Authorization: Basic <credentials>
|
||||||
|
* Content-Type: application/json
|
||||||
|
*
|
||||||
|
* into
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "Authorization": "Basic <credentials>",
|
||||||
|
* "Content-Type": "application/json"
|
||||||
|
* }
|
||||||
|
**/
|
||||||
|
getParsedHeaders() {
|
||||||
|
if (!this.headers || !this.headers.includes(":")) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return Object.fromEntries(this.headers.split("\n").map(header => {
|
||||||
|
const trimmedHeader = header.trim();
|
||||||
|
const firstColonIndex = trimmedHeader.indexOf(":");
|
||||||
|
return [trimmedHeader.slice(0, firstColonIndex), trimmedHeader.slice(firstColonIndex + 1) || ""];
|
||||||
|
}).filter(arr => !!arr[0] && !!arr[1]));
|
||||||
|
}
|
||||||
|
|
||||||
start(io) {
|
start(io) {
|
||||||
let previousBeat = null;
|
let previousBeat = null;
|
||||||
let retries = 0;
|
let retries = 0;
|
||||||
|
@ -136,11 +164,15 @@ class Monitor extends BeanModel {
|
||||||
// Do not do any queries/high loading things before the "bean.ping"
|
// Do not do any queries/high loading things before the "bean.ping"
|
||||||
let startTime = dayjs().valueOf();
|
let startTime = dayjs().valueOf();
|
||||||
|
|
||||||
let res = await axios.get(this.url, {
|
const options = {
|
||||||
|
url: this.url,
|
||||||
|
method: (this.method || "get").toLowerCase(),
|
||||||
|
...(this.body ? { data: JSON.parse(this.body) } : {}),
|
||||||
timeout: this.interval * 1000 * 0.8,
|
timeout: this.interval * 1000 * 0.8,
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
"User-Agent": "Uptime-Kuma/" + version,
|
"User-Agent": "Uptime-Kuma/" + version,
|
||||||
|
...this.getParsedHeaders(),
|
||||||
},
|
},
|
||||||
httpsAgent: new https.Agent({
|
httpsAgent: new https.Agent({
|
||||||
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
|
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
|
||||||
|
@ -150,7 +182,8 @@ class Monitor extends BeanModel {
|
||||||
validateStatus: (status) => {
|
validateStatus: (status) => {
|
||||||
return checkStatusCode(status, this.getAcceptedStatuscodes());
|
return checkStatusCode(status, this.getAcceptedStatuscodes());
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
let res = await axios.request(options);
|
||||||
bean.msg = `${res.status} - ${res.statusText}`;
|
bean.msg = `${res.status} - ${res.statusText}`;
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
bean.ping = dayjs().valueOf() - startTime;
|
||||||
|
|
||||||
|
|
|
@ -505,6 +505,9 @@ exports.entryPage = "dashboard";
|
||||||
bean.name = monitor.name;
|
bean.name = monitor.name;
|
||||||
bean.type = monitor.type;
|
bean.type = monitor.type;
|
||||||
bean.url = monitor.url;
|
bean.url = monitor.url;
|
||||||
|
bean.method = monitor.method;
|
||||||
|
bean.body = monitor.body;
|
||||||
|
bean.headers = monitor.headers;
|
||||||
bean.interval = monitor.interval;
|
bean.interval = monitor.interval;
|
||||||
bean.retryInterval = monitor.retryInterval;
|
bean.retryInterval = monitor.retryInterval;
|
||||||
bean.hostname = monitor.hostname;
|
bean.hostname = monitor.hostname;
|
||||||
|
@ -1028,6 +1031,9 @@ exports.entryPage = "dashboard";
|
||||||
name: monitorListData[i].name,
|
name: monitorListData[i].name,
|
||||||
type: monitorListData[i].type,
|
type: monitorListData[i].type,
|
||||||
url: monitorListData[i].url,
|
url: monitorListData[i].url,
|
||||||
|
method: monitorListData[i].method || "GET",
|
||||||
|
body: monitorListData[i].body,
|
||||||
|
headers: monitorListData[i].headers,
|
||||||
interval: monitorListData[i].interval,
|
interval: monitorListData[i].interval,
|
||||||
retryInterval: retryInterval,
|
retryInterval: retryInterval,
|
||||||
hostname: monitorListData[i].hostname,
|
hostname: monitorListData[i].hostname,
|
||||||
|
|
|
@ -14,6 +14,10 @@ h2 {
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea.form-control {
|
||||||
|
border-radius: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
|
@ -413,4 +417,4 @@ h2 {
|
||||||
|
|
||||||
// Localization
|
// Localization
|
||||||
|
|
||||||
@import "localization.scss";
|
@import "localization.scss";
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__tag {
|
.multiselect__tag {
|
||||||
border-radius: 50rem;
|
border-radius: $border-radius;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: 6px 26px 6px 10px;
|
padding: 6px 26px 6px 10px;
|
||||||
background: $primary !important;
|
background: $primary !important;
|
||||||
|
|
|
@ -186,7 +186,7 @@ export default {
|
||||||
.beat {
|
.beat {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: $primary;
|
background-color: $primary;
|
||||||
border-radius: 50rem;
|
border-radius: $border-radius;
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
background-color: aliceblue;
|
background-color: aliceblue;
|
||||||
|
|
|
@ -44,6 +44,46 @@
|
||||||
<input id="url" v-model="monitor.url" type="url" class="form-control" pattern="https?://.+" required>
|
<input id="url" v-model="monitor.url" type="url" class="form-control" pattern="https?://.+" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Method -->
|
||||||
|
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3">
|
||||||
|
<label for="method" class="form-label">{{ $t("Method") }}</label>
|
||||||
|
<select id="method" v-model="monitor.method" class="form-select">
|
||||||
|
<option value="GET">
|
||||||
|
GET
|
||||||
|
</option>
|
||||||
|
<option value="POST">
|
||||||
|
POST
|
||||||
|
</option>
|
||||||
|
<option value="PUT">
|
||||||
|
PUT
|
||||||
|
</option>
|
||||||
|
<option value="PATCH">
|
||||||
|
PATCH
|
||||||
|
</option>
|
||||||
|
<option value="DELETE">
|
||||||
|
DELETE
|
||||||
|
</option>
|
||||||
|
<option value="HEAD">
|
||||||
|
HEAD
|
||||||
|
</option>
|
||||||
|
<option value="OPTIONS">
|
||||||
|
OPTIONS
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3">
|
||||||
|
<label for="body" class="form-label">{{ $t("Body") }}</label>
|
||||||
|
<textarea id="body" v-model="monitor.body" class="form-control" :placeholder="bodyPlaceholder"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Headers -->
|
||||||
|
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3">
|
||||||
|
<label for="headers" class="form-label">{{ $t("Headers") }}</label>
|
||||||
|
<textarea id="headers" v-model="monitor.headers" class="form-control" :placeholder="headersPlaceholder"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Push URL -->
|
<!-- Push URL -->
|
||||||
<div v-if="monitor.type === 'push' " class="my-3">
|
<div v-if="monitor.type === 'push' " class="my-3">
|
||||||
<label for="push-url" class="form-label">{{ $t("Push URL") }}</label>
|
<label for="push-url" class="form-label">{{ $t("Push URL") }}</label>
|
||||||
|
@ -285,6 +325,18 @@ export default {
|
||||||
|
|
||||||
pushURL() {
|
pushURL() {
|
||||||
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?msg=OK&ping=";
|
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?msg=OK&ping=";
|
||||||
|
},
|
||||||
|
|
||||||
|
bodyPlaceholder() {
|
||||||
|
return `{
|
||||||
|
"id": 124357,
|
||||||
|
"username": "admin",
|
||||||
|
"password": "myAdminPassword"
|
||||||
|
}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
headersPlaceholder() {
|
||||||
|
return "Authorization: Bearer abc123\nContent-Type: application/json";
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -295,7 +347,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
"monitor.interval"(value, oldValue) {
|
"monitor.interval"(value, oldValue) {
|
||||||
// Link interval and retryInerval if they are the same value.
|
// Link interval and retryInterval if they are the same value.
|
||||||
if (this.monitor.retryInterval === oldValue) {
|
if (this.monitor.retryInterval === oldValue) {
|
||||||
this.monitor.retryInterval = value;
|
this.monitor.retryInterval = value;
|
||||||
}
|
}
|
||||||
|
@ -349,6 +401,7 @@ export default {
|
||||||
type: "http",
|
type: "http",
|
||||||
name: "",
|
name: "",
|
||||||
url: "https://",
|
url: "https://",
|
||||||
|
method: "GET",
|
||||||
interval: 60,
|
interval: 60,
|
||||||
retryInterval: this.interval,
|
retryInterval: this.interval,
|
||||||
maxretries: 0,
|
maxretries: 0,
|
||||||
|
@ -383,9 +436,32 @@ export default {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isInputValid() {
|
||||||
|
if (this.monitor.body) {
|
||||||
|
try {
|
||||||
|
JSON.parse(this.monitor.body);
|
||||||
|
} catch (err) {
|
||||||
|
toast.error(this.$t("The request body is not valid json: ") + err.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.monitor.headers) {
|
||||||
|
if (!/^([^:]+:.*)([\s]+[^:]+:.*)+$/g.test(this.monitor.headers)) {
|
||||||
|
toast.error(this.$t("Headers do not have a valid format: \"key: value<new line>key: value<new line>...\""));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
async submit() {
|
async submit() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
|
if (!this.isInputValid()) {
|
||||||
|
this.processing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isAdd) {
|
if (this.isAdd) {
|
||||||
this.$root.add(this.monitor, async (res) => {
|
this.$root.add(this.monitor, async (res) => {
|
||||||
|
|
||||||
|
@ -422,8 +498,12 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
.shadow-box {
|
.shadow-box {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue