mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-04 13:46:13 +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