From f11dfc8f437cb8e2ee9c77c9450bffc4d5e121af Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 24 Sep 2022 19:18:24 +0800 Subject: [PATCH] [WIP] Add/Edit Maintenance with new UI and recurring --- server/model/maintenance.js | 81 ++++++++++++++++++- .../maintenance-socket-handler.js | 11 ++- src/pages/EditMaintenance.vue | 6 +- src/util.js | 44 +++++++++- src/util.ts | 49 +++++++++++ 5 files changed, 179 insertions(+), 12 deletions(-) diff --git a/server/model/maintenance.js b/server/model/maintenance.js index 2f3e20004..a27a358b1 100644 --- a/server/model/maintenance.js +++ b/server/model/maintenance.js @@ -4,6 +4,8 @@ let timezone = require("dayjs/plugin/timezone"); dayjs.extend(utc); dayjs.extend(timezone); const { BeanModel } = require("redbean-node/dist/bean-model"); +const { parseVueDatePickerTimeFormat, parseTimeFormatFromVueDatePicker } = require("../../src/util"); +const { isArray } = require("chart.js/helpers"); class Maintenance extends BeanModel { @@ -13,15 +15,52 @@ class Maintenance extends BeanModel { * @returns {Object} */ async toPublicJSON() { - return { + + let dateTimeRange = []; + if (this.start_datetime) { + dateTimeRange.push( this.start_datetime); + if (this.end_datetime) { + dateTimeRange.push( this.end_datetime); + } + } + + let dateRange = []; + if (this.start_date) { + dateRange.push( this.start_date); + if (this.end_date) { + dateRange.push( this.end_date); + } + } + + let timeRange = []; + let startTime = parseVueDatePickerTimeFormat(this.start_time); + timeRange.push(startTime); + let endTime = parseVueDatePickerTimeFormat(this.end_time); + timeRange.push(endTime); + + let obj = { id: this.id, title: this.title, description: this.description, - start_date: this.start_date, - end_date: this.end_date, strategy: this.strategy, + intervalDay: this.interval_day, active: !!this.active, + dateTimeRange: dateTimeRange, + dateRange: dateRange, + timeRange: timeRange, + weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [], + daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [], }; + + if (!isArray(obj.weekdays)) { + obj.weekdays = []; + } + + if (!isArray(obj.daysOfMonth)) { + obj.daysOfMonth = []; + } + + return obj; } /** @@ -31,6 +70,42 @@ class Maintenance extends BeanModel { async toJSON() { return this.toPublicJSON(); } + + static jsonToBean(bean, obj) { + if (obj.id) { + bean.id = obj.id; + } + + bean.title = obj.title; + bean.description = obj.description; + bean.strategy = obj.strategy; + bean.interval_day = obj.intervalDay; + bean.active = obj.active; + + if (obj.dateRange[0]) { + bean.start_date = obj.dateRange[0]; + + if (obj.dateRange[1]) { + bean.end_date = obj.dateRange[1]; + } + } + + if (obj.dateTimeRange[0]) { + bean.start_datetime = obj.dateTimeRange[0]; + + if (obj.dateTimeRange[1]) { + bean.end_datetime = obj.dateTimeRange[1]; + } + } + + bean.start_time = parseTimeFormatFromVueDatePicker(obj.timeRange[0]); + bean.end_time = parseTimeFormatFromVueDatePicker(obj.timeRange[1]); + + bean.weekdays = JSON.stringify(obj.weekdays); + bean.days_of_month = JSON.stringify(obj.daysOfMonth); + + return bean; + } } module.exports = Maintenance; diff --git a/server/socket-handlers/maintenance-socket-handler.js b/server/socket-handlers/maintenance-socket-handler.js index 113c336a7..df4a9a35e 100644 --- a/server/socket-handlers/maintenance-socket-handler.js +++ b/server/socket-handlers/maintenance-socket-handler.js @@ -3,6 +3,7 @@ const { log } = require("../../src/util"); const { R } = require("redbean-node"); const apicache = require("../modules/apicache"); const { UptimeKumaServer } = require("../uptime-kuma-server"); +const Maintenance = require("../model/maintenance"); const server = UptimeKumaServer.getInstance(); /** @@ -14,9 +15,10 @@ module.exports.maintenanceSocketHandler = (socket) => { socket.on("addMaintenance", async (maintenance, callback) => { try { checkLogin(socket); - let bean = R.dispense("maintenance"); - bean.import(maintenance); + log.debug("maintenance", maintenance); + + let bean = Maintenance.jsonToBean(R.dispense("maintenance"), maintenance); bean.user_id = socket.userID; let maintenanceID = await R.store(bean); @@ -47,10 +49,7 @@ module.exports.maintenanceSocketHandler = (socket) => { throw new Error("Permission denied."); } - bean.title = maintenance.title; - bean.description = maintenance.description; - bean.start_date = maintenance.start_date; - bean.end_date = maintenance.end_date; + Maintenance.jsonToBean(bean, maintenance); await R.store(bean); diff --git a/src/pages/EditMaintenance.vue b/src/pages/EditMaintenance.vue index 98d5c2b25..3d8ab838d 100644 --- a/src/pages/EditMaintenance.vue +++ b/src/pages/EditMaintenance.vue @@ -366,10 +366,9 @@ export default { description: "", strategy: "single", active: 1, - recurringStartDate: this.$root.date(dayjs()), - recurringEndDate: "", intervalDay: 1, dateTimeRange: [ this.minDate ], + dateRange: [], timeRange: [{ hours: 2, minutes: 0, @@ -426,6 +425,8 @@ export default { return this.processing = false; } + /* + TODO: Temporary disable if (this.maintenance.start_date >= this.maintenance.end_date) { toast.error(this.$t("maintenanceInvalidDate")); return this.processing = false; @@ -438,6 +439,7 @@ export default { this.maintenance.start_date = this.$root.toUTC(this.maintenance.start_date); this.maintenance.end_date = this.$root.toUTC(this.maintenance.end_date); + */ if (this.isAdd) { this.$root.addMaintenance(this.maintenance, async (res) => { diff --git a/src/util.js b/src/util.js index c5fca856f..73f5369d0 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.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.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.parseTimeFormatFromVueDatePicker = exports.parseVueDatePickerTimeFormat = 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.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"); const dayjs = _dayjs; exports.isDev = process.env.NODE_ENV === "development"; @@ -309,3 +309,45 @@ function getMaintenanceRelativeURL(id) { return "/maintenance/" + id; } exports.getMaintenanceRelativeURL = getMaintenanceRelativeURL; +/** + * Parse to Time Object that used in VueDatePicker + * @param {string} time E.g. 12:00 + * @returns object + */ +function parseVueDatePickerTimeFormat(time) { + if (!time) { + return { + hours: 0, + minutes: 0, + }; + } + let array = time.split(":"); + if (array.length < 2) { + throw new Error("parseVueDatePickerTimeFormat: Invalid Time"); + } + let obj = { + hours: parseInt(array[0]), + minutes: parseInt(array[1]), + seconds: 0, + }; + if (array.length >= 3) { + obj.seconds = parseInt(array[2]); + } + return obj; +} +exports.parseVueDatePickerTimeFormat = parseVueDatePickerTimeFormat; +/** + * @returns string e.g. 12:00 + */ +function parseTimeFormatFromVueDatePicker(obj) { + if (!obj) { + return obj; + } + let result = ""; + result += obj.hours.toString().padStart(2, "0") + ":" + obj.minutes.toString().padStart(2, "0"); + if (obj.seconds) { + result += ":" + obj.seconds.toString().padStart(2, "0"); + } + return result; +} +exports.parseTimeFormatFromVueDatePicker = parseTimeFormatFromVueDatePicker; diff --git a/src/util.ts b/src/util.ts index 0dd8a62ab..92da0fd56 100644 --- a/src/util.ts +++ b/src/util.ts @@ -342,3 +342,52 @@ export function getMonitorRelativeURL(id: string) { export function getMaintenanceRelativeURL(id: string) { return "/maintenance/" + id; } + +/** + * Parse to Time Object that used in VueDatePicker + * @param {string} time E.g. 12:00 + * @returns object + */ +export function parseVueDatePickerTimeFormat(time: string) { + if (!time) { + return { + hours: 0, + minutes: 0, + }; + } + + let array = time.split(":"); + + if (array.length < 2) { + throw new Error("parseVueDatePickerTimeFormat: Invalid Time"); + } + + let obj = { + hours: parseInt(array[0]), + minutes: parseInt(array[1]), + seconds: 0, + } + if (array.length >= 3) { + obj.seconds = parseInt(array[2]); + } + return obj; +} + +/** + * @returns string e.g. 12:00 + */ +export function parseTimeFormatFromVueDatePicker(obj : any) { + if (!obj) { + return obj; + } + + let result = ""; + + result += obj.hours.toString().padStart(2, "0") + ":" + obj.minutes.toString().padStart(2, "0") + + if (obj.seconds) { + result += ":" + obj.seconds.toString().padStart(2, "0") + } + + return result; +}