diff --git a/server/client.js b/server/client.js index dcc778df2..bed2ba034 100644 --- a/server/client.js +++ b/server/client.js @@ -125,7 +125,7 @@ async function sendInfo(socket) { latestVersion: checkVersion.latestVersion, primaryBaseURL: await setting("primaryBaseURL"), serverTimezone: await server.getTimezone(), - serverTimezoneOffset: dayjs().format("Z"), + serverTimezoneOffset: server.getTimezoneOffset(), }); } diff --git a/server/model/maintenance.js b/server/model/maintenance.js index 2ab2a5bb4..4910d2a03 100644 --- a/server/model/maintenance.js +++ b/server/model/maintenance.js @@ -39,9 +39,15 @@ class Maintenance extends BeanModel { timeRange: timeRange, weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [], daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [], - timeslotList: await this.getTimeslotList(), + timeslotList: [], }; + const timeslotList = await this.getTimeslotList(); + + for (let timeslot of timeslotList) { + obj.timeslotList.push(await timeslot.toPublicJSON()); + } + if (!isArray(obj.weekdays)) { obj.weekdays = []; } @@ -53,7 +59,9 @@ class Maintenance extends BeanModel { // Maintenance Status if (!obj.active) { obj.status = "inactive"; - } else if (obj.strategy === "manual" || obj.timeslotList.length > 0) { + } else if (obj.strategy === "manual") { + obj.status = "under-maintenance"; + } else if (obj.timeslotList.length > 0) { for (let timeslot of obj.timeslotList) { if (dayjs.utc(timeslot.start_date) <= dayjs.utc() && dayjs.utc(timeslot.end_date) >= dayjs.utc()) { obj.status = "under-maintenance"; @@ -78,7 +86,7 @@ class Maintenance extends BeanModel { * @returns {Promise<[]>} */ async getTimeslotList() { - return await R.getAll(` + return R.convertToBeans("maintenance_timeslot", await R.getAll(` SELECT maintenance_timeslot.* FROM maintenance_timeslot, maintenance WHERE maintenance_timeslot.maintenance_id = maintenance.id @@ -86,7 +94,7 @@ class Maintenance extends BeanModel { AND ${Maintenance.getActiveAndFutureMaintenanceSQLCondition()} `, [ this.id - ]); + ])); } /** @@ -156,10 +164,10 @@ class Maintenance extends BeanModel { */ static getActiveAndFutureMaintenanceSQLCondition() { return ` - (maintenance_timeslot.end_date >= DATETIME('now') + ((maintenance_timeslot.end_date >= DATETIME('now') AND maintenance.active = 1) OR - (maintenance.strategy = 'manual' AND active = 1) + (maintenance.strategy = 'manual' AND active = 1)) `; } } diff --git a/server/model/maintenance_timeslot.js b/server/model/maintenance_timeslot.js index 4db3a1dbb..f06806acb 100644 --- a/server/model/maintenance_timeslot.js +++ b/server/model/maintenance_timeslot.js @@ -1,16 +1,28 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); const { R } = require("redbean-node"); const dayjs = require("dayjs"); -const { log } = require("../../src/util"); +const { log, utcToLocal, SQL_DATETIME_FORMAT_WITHOUT_SECOND } = require("../../src/util"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); class MaintenanceTimeslot extends BeanModel { async toPublicJSON() { + const serverTimezoneOffset = await UptimeKumaServer.getInstance().getTimezoneOffset(); + const obj = { + id: this.id, + startDate: this.start_date, + endDate: this.end_date, + startDateServerTimezone: utcToLocal(this.start_date, SQL_DATETIME_FORMAT_WITHOUT_SECOND), + endDateServerTimezone: utcToLocal(this.end_date, SQL_DATETIME_FORMAT_WITHOUT_SECOND), + serverTimezoneOffset, + }; + + return obj; } async toJSON() { - + return await this.toPublicJSON(); } /** diff --git a/server/socket-handlers/maintenance-socket-handler.js b/server/socket-handlers/maintenance-socket-handler.js index 49527f23d..5294050ca 100644 --- a/server/socket-handlers/maintenance-socket-handler.js +++ b/server/socket-handlers/maintenance-socket-handler.js @@ -258,4 +258,54 @@ module.exports.maintenanceSocketHandler = (socket) => { }); } }); + + socket.on("pauseMaintenance", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Pause Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + await R.exec("UPDATE maintenance SET active = 0 WHERE id = ? ", [ + maintenanceID, + ]); + + callback({ + ok: true, + msg: "Paused Successfully.", + }); + + await server.sendMaintenanceList(socket); + + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("resumeMaintenance", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Resume Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + await R.exec("UPDATE maintenance SET active = 1 WHERE id = ? ", [ + maintenanceID, + ]); + + callback({ + ok: true, + msg: "Resume Successfully", + }); + + await server.sendMaintenanceList(socket); + + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); }; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 155831592..667f6b6aa 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -204,6 +204,10 @@ class UptimeKumaServer { } } + async getTimezoneOffset() { + return dayjs().format("Z"); + } + async setTimezone(timezone) { await Settings.set("serverTimezone", timezone, "general"); process.env.TZ = timezone; diff --git a/src/assets/app.scss b/src/assets/app.scss index 7eb959315..7da76fff0 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -101,11 +101,6 @@ optgroup { } } -// Override Bootstrap -.btn-group > .btn:hover { - z-index: initial; -} - .btn { padding-left: 20px; padding-right: 20px; @@ -125,6 +120,19 @@ optgroup { } } +.btn-normal { + $bg-color: #F5F5F5; + + background-color: $bg-color; + border-color: $bg-color; + + &:hover { + $hover-color: darken($bg-color, 3%); + background-color: $hover-color; + border-color: $hover-color; + } +} + .btn-warning { color: white; diff --git a/src/languages/en.js b/src/languages/en.js index 835fa248d..a1c9b560c 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -631,4 +631,10 @@ export default { lastDay3: "3rd Last Day of Month", lastDay4: "4th Last Day of Month", "No Maintenance": "No Maintenance", + pauseMaintenanceMsg: "Are you sure want to pause?", + "maintenanceStatus-under-maintenance": "Under Maintenance", + "maintenanceStatus-inactive": "Inactive", + "maintenanceStatus-scheduled": "Scheduled", + "maintenanceStatus-ended": "Ended", + "maintenanceStatus-unknown": "Unknown", }; diff --git a/src/pages/ManageMaintenance.vue b/src/pages/ManageMaintenance.vue index 725e94b0f..af9c0ba5f 100644 --- a/src/pages/ManageMaintenance.vue +++ b/src/pages/ManageMaintenance.vue @@ -28,7 +28,18 @@ >
{{ item.title }}
-
{{ item.description }}
+
{{ item.description }}
+
+ {{ $t("maintenanceStatus-" + item.status) }} +
+ +
+ {{ $t("Manual") }} +
+
+ {{ item.timeslotList[0].startDateServerTimezone }} - {{ item.timeslotList[0].endDateServerTimezone }} + (UTC{{ item.timeslotList[0].serverTimezoneOffset }}) +
@@ -36,11 +47,11 @@ {{ $t("Details") }}
- - @@ -149,7 +160,8 @@ export default { /** * Show dialog to confirm pause */ - pauseDialog() { + pauseDialog(maintenanceID) { + this.selectedMaintenanceID = maintenanceID; this.$refs.confirmPause.show(); }, @@ -157,8 +169,7 @@ export default { * Pause maintenance */ pauseMaintenance() { - return; - this.$root.getSocket().emit("pauseMaintenance", selectedMaintenanceID, (res) => { + this.$root.getSocket().emit("pauseMaintenance", this.selectedMaintenanceID, (res) => { this.$root.toastRes(res); }); }, @@ -166,9 +177,8 @@ export default { /** * Resume maintenance */ - resumeMaintenance() { - return; - this.$root.getSocket().emit("resumeMaintenance", selectedMaintenanceID, (res) => { + resumeMaintenance(id) { + this.$root.getSocket().emit("resumeMaintenance", id, (res) => { this.$root.toastRes(res); }); }, @@ -189,6 +199,7 @@ export default { justify-content: space-between; padding: 10px; min-height: 90px; + margin-bottom: 5px; &:hover { background-color: $highlight-white; @@ -251,9 +262,27 @@ export default { font-size: 20px; } - .slug { + .status { font-size: 14px; } + + .timeslot { + margin-top: 5px; + display: inline-block; + font-size: 14px; + background-color: rgba(255, 255, 255, 0.5); + border-radius: 20px; + padding: 0 10px; + + .to { + margin: 0 6px; + } + + .dark & { + color: white; + background-color: rgba(255, 255, 255, 0.1); + } + } } } diff --git a/src/util.js b/src/util.js index 2213d4d0e..9cdecc17a 100644 --- a/src/util.js +++ b/src/util.js @@ -7,7 +7,7 @@ // Backend uses the compiled file util.js // Frontend uses util.ts Object.defineProperty(exports, "__esModule", { value: true }); -exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; +exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; const dayjs = require("dayjs"); exports.isDev = process.env.NODE_ENV === "development"; exports.appName = "Uptime Kuma"; @@ -21,6 +21,7 @@ exports.STATUS_PAGE_PARTIAL_DOWN = 2; exports.STATUS_PAGE_MAINTENANCE = 3; exports.SQL_DATE_FORMAT = "YYYY-MM-DD"; exports.SQL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss"; +exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm"; /** Flip the status of s */ function flipStatus(s) { if (s === exports.UP) { @@ -366,11 +367,11 @@ exports.utcToISODateTime = utcToISODateTime; /** * For SQL_DATETIME_FORMAT */ -function utcToLocal(input) { - return dayjs.utc(input).local().format(exports.SQL_DATETIME_FORMAT); +function utcToLocal(input, format = exports.SQL_DATETIME_FORMAT) { + return dayjs.utc(input).local().format(format); } exports.utcToLocal = utcToLocal; -function localToUTC(input) { - return dayjs(input).utc().format(exports.SQL_DATETIME_FORMAT); +function localToUTC(input, format = exports.SQL_DATETIME_FORMAT) { + return dayjs(input).utc().format(format); } exports.localToUTC = localToUTC; diff --git a/src/util.ts b/src/util.ts index 966383f8f..5d8acfcd6 100644 --- a/src/util.ts +++ b/src/util.ts @@ -25,6 +25,7 @@ export const STATUS_PAGE_MAINTENANCE = 3; export const SQL_DATE_FORMAT = "YYYY-MM-DD"; export const SQL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss"; +export const SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm"; /** Flip the status of s */ export function flipStatus(s: number) { @@ -412,10 +413,10 @@ export function utcToISODateTime(input : string) { /** * For SQL_DATETIME_FORMAT */ -export function utcToLocal(input : string) { - return dayjs.utc(input).local().format(SQL_DATETIME_FORMAT); +export function utcToLocal(input : string, format = SQL_DATETIME_FORMAT) { + return dayjs.utc(input).local().format(format); } -export function localToUTC(input : string) { - return dayjs(input).utc().format(SQL_DATETIME_FORMAT); +export function localToUTC(input : string, format = SQL_DATETIME_FORMAT) { + return dayjs(input).utc().format(format); }