mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-23 14:54:05 +00:00
feat: different monitor timezones from user timezone (#5201)
This commit is contained in:
parent
ed6087e233
commit
a6610340a5
7 changed files with 70 additions and 14 deletions
12
db/knex_migrations/2024-10-16-0000-timezones.js
Normal file
12
db/knex_migrations/2024-10-16-0000-timezones.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
exports.up = function (knex) {
|
||||||
|
return knex.schema
|
||||||
|
.alterTable("monitor", function (table) {
|
||||||
|
table.text("timezone").notNullable().defaultTo("auto");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function (knex) {
|
||||||
|
return knex.schema.alterTable("monitor", function (table) {
|
||||||
|
table.dropColumn("timezone");
|
||||||
|
});
|
||||||
|
};
|
|
@ -154,6 +154,7 @@ class Monitor extends BeanModel {
|
||||||
jsonPathOperator: this.jsonPathOperator,
|
jsonPathOperator: this.jsonPathOperator,
|
||||||
snmpVersion: this.snmpVersion,
|
snmpVersion: this.snmpVersion,
|
||||||
conditions: JSON.parse(this.conditions),
|
conditions: JSON.parse(this.conditions),
|
||||||
|
timezone: this.timezone,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeSensitiveData) {
|
if (includeSensitiveData) {
|
||||||
|
|
|
@ -869,6 +869,7 @@ let needSetup = false;
|
||||||
bean.jsonPathOperator = monitor.jsonPathOperator;
|
bean.jsonPathOperator = monitor.jsonPathOperator;
|
||||||
bean.timeout = monitor.timeout;
|
bean.timeout = monitor.timeout;
|
||||||
bean.conditions = JSON.stringify(monitor.conditions);
|
bean.conditions = JSON.stringify(monitor.conditions);
|
||||||
|
bean.timezone = monitor.timezone;
|
||||||
|
|
||||||
bean.validate();
|
bean.validate();
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,11 @@ export default {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
/** Monitor Timezone */
|
||||||
|
monitorTimezone: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -251,7 +256,7 @@ export default {
|
||||||
},
|
},
|
||||||
// push datapoint to chartData
|
// push datapoint to chartData
|
||||||
pushDatapoint(datapoint, avgPingData, minPingData, maxPingData, downData, colorData) {
|
pushDatapoint(datapoint, avgPingData, minPingData, maxPingData, downData, colorData) {
|
||||||
const x = this.$root.unixToDateTime(datapoint.timestamp);
|
const x = this.$root.unixToDateTime(datapoint.timestamp, this.monitorTimezone);
|
||||||
|
|
||||||
// Show ping values if it was up in this period
|
// Show ping values if it was up in this period
|
||||||
avgPingData.push({
|
avgPingData.push({
|
||||||
|
@ -306,7 +311,7 @@ export default {
|
||||||
let heartbeatList = (this.monitorId in this.$root.heartbeatList && this.$root.heartbeatList[this.monitorId]) || [];
|
let heartbeatList = (this.monitorId in this.$root.heartbeatList && this.$root.heartbeatList[this.monitorId]) || [];
|
||||||
|
|
||||||
for (const beat of heartbeatList) {
|
for (const beat of heartbeatList) {
|
||||||
const beatTime = this.$root.toDayjs(beat.time);
|
const beatTime = this.$root.toDayjs(beat.time, this.monitorTimezone);
|
||||||
const x = beatTime.format("YYYY-MM-DD HH:mm:ss");
|
const x = beatTime.format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
|
||||||
// Insert empty datapoint to separate big gaps
|
// Insert empty datapoint to separate big gaps
|
||||||
|
@ -407,7 +412,7 @@ export default {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const beatTime = this.$root.unixToDayjs(datapoint.timestamp);
|
const beatTime = this.$root.unixToDayjs(datapoint.timestamp, this.monitorTimezone);
|
||||||
|
|
||||||
// Insert empty datapoint to separate big gaps
|
// Insert empty datapoint to separate big gaps
|
||||||
if (lastHeartbeatTime && monitorInterval) {
|
if (lastHeartbeatTime && monitorInterval) {
|
||||||
|
@ -427,7 +432,7 @@ export default {
|
||||||
|
|
||||||
const gapX = [
|
const gapX = [
|
||||||
lastHeartbeatTime.subtract(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss"),
|
lastHeartbeatTime.subtract(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss"),
|
||||||
this.$root.unixToDateTime(datapoint.timestamp + 60),
|
this.$root.unixToDateTime(datapoint.timestamp + 60, this.monitorTimezone),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const x of gapX) {
|
for (const x of gapX) {
|
||||||
|
|
|
@ -44,28 +44,31 @@ export default {
|
||||||
/**
|
/**
|
||||||
* Converts a Unix timestamp to a formatted date and time string.
|
* Converts a Unix timestamp to a formatted date and time string.
|
||||||
* @param {number} value - The Unix timestamp to convert.
|
* @param {number} value - The Unix timestamp to convert.
|
||||||
|
* @param {string} timezone - The timezone to use for the conversion.
|
||||||
* @returns {string} The formatted date and time string.
|
* @returns {string} The formatted date and time string.
|
||||||
*/
|
*/
|
||||||
unixToDateTime(value) {
|
unixToDateTime(value, timezone) {
|
||||||
return dayjs.unix(value).tz(this.timezone).format("YYYY-MM-DD HH:mm:ss");
|
return dayjs.unix(value).tz(timezone === "auto" ? this.timezone : timezone).format("YYYY-MM-DD HH:mm:ss");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a Unix timestamp to a dayjs object.
|
* Converts a Unix timestamp to a dayjs object.
|
||||||
* @param {number} value - The Unix timestamp to convert.
|
* @param {number} value - The Unix timestamp to convert.
|
||||||
|
* @param {string} timezone - The timezone to use for the conversion.
|
||||||
* @returns {dayjs.Dayjs} The dayjs object representing the given timestamp.
|
* @returns {dayjs.Dayjs} The dayjs object representing the given timestamp.
|
||||||
*/
|
*/
|
||||||
unixToDayjs(value) {
|
unixToDayjs(value, timezone) {
|
||||||
return dayjs.unix(value).tz(this.timezone);
|
return dayjs.unix(value).tz(timezone === "auto" ? this.timezone : timezone);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given value to a dayjs object.
|
* Converts the given value to a dayjs object.
|
||||||
* @param {string} value - the value to be converted
|
* @param {string} value - The value to be converted.
|
||||||
* @returns {dayjs.Dayjs} a dayjs object in the timezone of this instance
|
* @param {string} timezone - The timezone to use for the conversion.
|
||||||
|
* @returns {dayjs.Dayjs} The dayjs object in the timezone of this instance.
|
||||||
*/
|
*/
|
||||||
toDayjs(value) {
|
toDayjs(value, timezone) {
|
||||||
return dayjs.utc(value).tz(this.timezone);
|
return dayjs.utc(value).tz(timezone === "auto" ? this.timezone : timezone);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -184,7 +184,7 @@
|
||||||
<div v-if="showPingChartBox" class="shadow-box big-padding text-center ping-chart-wrapper">
|
<div v-if="showPingChartBox" class="shadow-box big-padding text-center ping-chart-wrapper">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<PingChart :monitor-id="monitor.id" />
|
<PingChart :monitor-id="monitor.id" :monitor-timezone="monitor.timezone" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 class="mb-2">{{ $t("General") }}</h2>
|
<h2 class="mb-2">{{ $t("General") }}</h2>
|
||||||
|
|
||||||
|
<!-- Monitor Type -->
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="type" class="form-label">{{ $t("Monitor Type") }}</label>
|
<label for="type" class="form-label">{{ $t("Monitor Type") }}</label>
|
||||||
<select id="type" v-model="monitor.type" class="form-select" data-testid="monitor-type-select">
|
<select id="type" v-model="monitor.type" class="form-select" data-testid="monitor-type-select">
|
||||||
|
@ -563,6 +564,23 @@
|
||||||
<input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="0" step="1">
|
<input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="0" step="1">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Monitor Timezone -->
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="timezone" class="form-label">{{ $t("Monitor Timezone") }}</label>
|
||||||
|
<select id="timezone" v-model="monitor.timezone" class="form-select">
|
||||||
|
<option value="auto">
|
||||||
|
{{ $t("Default") }}: {{ getUserTimezone }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="(timezone, index) in timezoneList"
|
||||||
|
:key="index"
|
||||||
|
:value="timezone.value"
|
||||||
|
>
|
||||||
|
{{ timezone.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
|
<h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
|
||||||
|
|
||||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' " class="my-3 form-check" :title="monitor.ignoreTls ? $t('ignoredTLSError') : ''">
|
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' " class="my-3 form-check" :title="monitor.ignoreTls ? $t('ignoredTLSError') : ''">
|
||||||
|
@ -1077,6 +1095,8 @@ 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 { version } from "../../package.json";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { timezoneList } from "../util-frontend";
|
||||||
const userAgent = `'Uptime-Kuma/${version}'`;
|
const userAgent = `'Uptime-Kuma/${version}'`;
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
@ -1122,7 +1142,8 @@ const monitorDefaults = {
|
||||||
kafkaProducerAllowAutoTopicCreation: false,
|
kafkaProducerAllowAutoTopicCreation: false,
|
||||||
gamedigGivenPortOnly: true,
|
gamedigGivenPortOnly: true,
|
||||||
remote_browser: null,
|
remote_browser: null,
|
||||||
conditions: []
|
conditions: [],
|
||||||
|
timezone: "auto"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -1165,6 +1186,7 @@ export default {
|
||||||
},
|
},
|
||||||
draftGroupName: null,
|
draftGroupName: null,
|
||||||
remoteBrowsersEnabled: false,
|
remoteBrowsersEnabled: false,
|
||||||
|
timezoneList: timezoneList(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1217,6 +1239,18 @@ export default {
|
||||||
return command.join(" ");
|
return command.join(" ");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getUserTimezone() {
|
||||||
|
let timezone = localStorage.timezone;
|
||||||
|
if (timezone === null || timezone === "auto") {
|
||||||
|
timezone = this.guessTimezone;
|
||||||
|
}
|
||||||
|
return timezone;
|
||||||
|
},
|
||||||
|
|
||||||
|
guessTimezone() {
|
||||||
|
return dayjs.tz.guess();
|
||||||
|
},
|
||||||
|
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue