mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-10-31 19:39:20 +08:00 
			
		
		
		
	WIP: Add maintenance status
This commit is contained in:
		| @@ -2,13 +2,14 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); | ||||
| const { parseTimeObject, parseTimeFromTimeObject, utcToLocal, localToUTC } = require("../../src/util"); | ||||
| const { isArray } = require("chart.js/helpers"); | ||||
| const { timeObjectToUTC, timeObjectToLocal } = require("../util-server"); | ||||
| const { R } = require("redbean-node"); | ||||
| const dayjs = require("dayjs"); | ||||
|  | ||||
| class Maintenance extends BeanModel { | ||||
|  | ||||
|     /** | ||||
|      * Return an object that ready to parse to JSON for public | ||||
|      * Only show necessary data to public | ||||
|      * @param {string} timezone If not specified, the timeRange will be in UTC | ||||
|      * @returns {Object} | ||||
|      */ | ||||
|     async toPublicJSON() { | ||||
| @@ -38,6 +39,7 @@ 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(), | ||||
|         }; | ||||
|  | ||||
|         if (!isArray(obj.weekdays)) { | ||||
| @@ -48,9 +50,45 @@ class Maintenance extends BeanModel { | ||||
|             obj.daysOfMonth = []; | ||||
|         } | ||||
|  | ||||
|         // Maintenance Status | ||||
|         if (!obj.active) { | ||||
|             obj.status = "inactive"; | ||||
|         } else if (obj.strategy === "manual" || 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"; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (!obj.status) { | ||||
|                 obj.status = "scheduled"; | ||||
|             } | ||||
|         } else if (obj.timeslotList.length === 0) { | ||||
|             obj.status = "ended"; | ||||
|         } else { | ||||
|             obj.status = "unknown"; | ||||
|         } | ||||
|  | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Only get future or current timeslots only | ||||
|      * @returns {Promise<[]>} | ||||
|      */ | ||||
|     async getTimeslotList() { | ||||
|         return await R.getAll(` | ||||
|             SELECT maintenance_timeslot.* | ||||
|             FROM maintenance_timeslot, maintenance | ||||
|             WHERE maintenance_timeslot.maintenance_id = maintenance.id | ||||
|             AND maintenance.id = ? | ||||
|             AND ${Maintenance.getActiveAndFutureMaintenanceSQLCondition()} | ||||
|         `, [ | ||||
|             this.id | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return an object that ready to parse to JSON | ||||
|      * @param {string} timezone If not specified, the timeRange will be in UTC | ||||
| @@ -111,6 +149,19 @@ class Maintenance extends BeanModel { | ||||
|  | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * SQL conditions for active and future maintenance | ||||
|      * @returns {string} | ||||
|      */ | ||||
|     static getActiveAndFutureMaintenanceSQLCondition() { | ||||
|         return ` | ||||
|             (maintenance_timeslot.end_date >= DATETIME('now') | ||||
|             AND maintenance.active = 1) | ||||
|             OR | ||||
|             (maintenance.strategy = 'manual' AND active = 1) | ||||
|         `; | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = Maintenance; | ||||
|   | ||||
| @@ -1109,10 +1109,10 @@ class Monitor extends BeanModel { | ||||
|             FROM monitor_maintenance mm | ||||
|             JOIN maintenance | ||||
|                 ON mm.maintenance_id = maintenance.id | ||||
|             JOIN maintenance_timeslot | ||||
|                 AND mm.monitor_id = ? | ||||
|             LEFT JOIN maintenance_timeslot | ||||
|                 ON maintenance_timeslot.maintenance_id = maintenance.id | ||||
|             WHERE mm.monitor_id = ? | ||||
|               AND ${activeCondition} | ||||
|             WHERE ${activeCondition} | ||||
|             LIMIT 1`, [ monitorID ]); | ||||
|         return maintenance.count !== 0; | ||||
|     } | ||||
|   | ||||
| @@ -269,6 +269,20 @@ optgroup { | ||||
|         color: white; | ||||
|     } | ||||
|  | ||||
|     .btn-normal { | ||||
|         $bg-color: $dark-header-bg; | ||||
|  | ||||
|         color: $dark-font-color; | ||||
|         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: $dark-font-color2; | ||||
|  | ||||
|   | ||||
| @@ -58,7 +58,7 @@ | ||||
|                             </li> | ||||
|  | ||||
|                             <li> | ||||
|                                 <router-link to="/settings" class="dropdown-item" :class="{ active: $route.path.includes('settings') }"> | ||||
|                                 <router-link to="/settings/general" class="dropdown-item" :class="{ active: $route.path.includes('settings') }"> | ||||
|                                     <font-awesome-icon icon="cog" /> {{ $t("Settings") }} | ||||
|                                 </router-link> | ||||
|                             </li> | ||||
|   | ||||
| @@ -12,10 +12,6 @@ export default { | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|         isActiveMaintenance(endDate) { | ||||
|             return (dayjs.utc(endDate).unix() >= dayjs.utc().unix()); | ||||
|         }, | ||||
|  | ||||
|         toUTC(value) { | ||||
|             return dayjs.tz(value, this.timezone).utc().format(); | ||||
|         }, | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|                     v-for="(item, index) in sortedMaintenanceList" | ||||
|                     :key="index" | ||||
|                     class="item" | ||||
|                     :class="{ 'ended': !$root.isActiveMaintenance(item.end_date) }" | ||||
|                     :class="item.status" | ||||
|                 > | ||||
|                     <div class="left-part"> | ||||
|                         <div | ||||
| @@ -35,7 +35,7 @@ | ||||
|                     <div class="buttons"> | ||||
|                         <router-link v-if="false" :to="maintenanceURL(item.id)" class="btn btn-light">{{ $t("Details") }}</router-link> | ||||
|  | ||||
|                         <button v-if="item.active" class="btn btn-light" @click="pauseDialog"> | ||||
|                         <button v-if="item.active" class="btn btn-normal" @click="pauseDialog"> | ||||
|                             <font-awesome-icon icon="pause" /> {{ $t("Pause") }} | ||||
|                         </button> | ||||
|  | ||||
| @@ -43,7 +43,7 @@ | ||||
|                             <font-awesome-icon icon="play" /> {{ $t("Resume") }} | ||||
|                         </button> | ||||
|  | ||||
|                         <router-link :to="'/maintenance/edit/' + item.id" class="btn btn-secondary"> | ||||
|                         <router-link :to="'/maintenance/edit/' + item.id" class="btn btn-normal"> | ||||
|                             <font-awesome-icon icon="edit" /> {{ $t("Edit") }} | ||||
|                         </router-link> | ||||
|  | ||||
| @@ -90,36 +90,6 @@ export default { | ||||
|             let result = Object.values(this.$root.maintenanceList); | ||||
|  | ||||
|             result.sort((m1, m2) => { | ||||
|  | ||||
|                 if (this.$root.isActiveMaintenance(m1.end_date) !== this.$root.isActiveMaintenance(m2.end_date)) { | ||||
|                     if (!this.$root.isActiveMaintenance(m2.end_date)) { | ||||
|                         return -1; | ||||
|                     } | ||||
|                     if (!this.$root.isActiveMaintenance(m1.end_date)) { | ||||
|                         return 1; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (this.$root.isActiveMaintenance(m1.end_date) && this.$root.isActiveMaintenance(m2.end_date)) { | ||||
|                     if (Date.parse(m1.end_date) < Date.parse(m2.end_date)) { | ||||
|                         return -1; | ||||
|                     } | ||||
|  | ||||
|                     if (Date.parse(m2.end_date) < Date.parse(m1.end_date)) { | ||||
|                         return 1; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (!this.$root.isActiveMaintenance(m1.end_date) && !this.$root.isActiveMaintenance(m2.end_date)) { | ||||
|                     if (Date.parse(m1.end_date) < Date.parse(m2.end_date)) { | ||||
|                         return 1; | ||||
|                     } | ||||
|  | ||||
|                     if (Date.parse(m2.end_date) < Date.parse(m1.end_date)) { | ||||
|                         return -1; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 return m1.title.localeCompare(m2.title); | ||||
|             }); | ||||
|  | ||||
| @@ -173,7 +143,7 @@ export default { | ||||
|         /** | ||||
|          * Pause maintenance | ||||
|          */ | ||||
|         pauseMonitor() { | ||||
|         pauseMaintenance() { | ||||
|             return; | ||||
|             this.$root.getSocket().emit("pauseMaintenance", selectedMaintenanceID, (res) => { | ||||
|                 this.$root.toastRes(res); | ||||
| @@ -211,13 +181,43 @@ export default { | ||||
|             background-color: $highlight-white; | ||||
|         } | ||||
|  | ||||
|         &.under-maintenance { | ||||
|             background-color: rgba(23, 71, 245, 0.16); | ||||
|  | ||||
|             &:hover { | ||||
|                 background-color: rgba(23, 71, 245, 0.3) !important; | ||||
|             } | ||||
|  | ||||
|             .circle { | ||||
|                 background-color: $maintenance; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &.scheduled { | ||||
|             .circle { | ||||
|                 background-color: $primary; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &.inactive { | ||||
|             .circle { | ||||
|                 background-color: $danger; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &.ended { | ||||
|             .left-part { | ||||
|                 opacity: 0.5; | ||||
|                 opacity: 0.3; | ||||
|             } | ||||
|  | ||||
|                 .circle { | ||||
|                     background-color: $dark-font-color; | ||||
|                 } | ||||
|             .circle { | ||||
|                 background-color: $dark-font-color; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &.unknown { | ||||
|             .circle { | ||||
|                 background-color: $dark-font-color; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -230,7 +230,6 @@ export default { | ||||
|                 width: 25px; | ||||
|                 height: 25px; | ||||
|                 border-radius: 50rem; | ||||
|                 background-color: $maintenance; | ||||
|             } | ||||
|  | ||||
|             .info { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user