Merge branch 'master' into feature/5934

This commit is contained in:
Mário Garrido 2025-07-18 09:15:11 +01:00 committed by GitHub
commit 38037f5673
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 70 additions and 12 deletions

View file

@ -0,0 +1,34 @@
// Add column last_start_date to maintenance table
exports.up = async function (knex) {
await knex.schema
.alterTable("maintenance", function (table) {
table.datetime("last_start_date");
});
// Perform migration for recurring-interval strategy
const recurringMaintenances = await knex("maintenance").where({
strategy: "recurring-interval",
cron: "* * * * *"
}).select("id", "start_time");
// eslint-disable-next-line camelcase
const maintenanceUpdates = recurringMaintenances.map(async ({ start_time, id }) => {
// eslint-disable-next-line camelcase
const [ hourStr, minuteStr ] = start_time.split(":");
const hour = parseInt(hourStr, 10);
const minute = parseInt(minuteStr, 10);
const cron = `${minute} ${hour} * * *`;
await knex("maintenance")
.where({ id })
.update({ cron });
});
await Promise.all(maintenanceUpdates);
};
exports.down = function (knex) {
return knex.schema.alterTable("maintenance", function (table) {
table.dropColumn("last_start_date");
});
};

View file

@ -239,6 +239,8 @@ class Maintenance extends BeanModel {
apicache.clear(); apicache.clear();
}); });
} else if (this.cron != null) { } else if (this.cron != null) {
let current = dayjs();
// Here should be cron or recurring // Here should be cron or recurring
try { try {
this.beanMeta.status = "scheduled"; this.beanMeta.status = "scheduled";
@ -258,6 +260,10 @@ class Maintenance extends BeanModel {
this.beanMeta.status = "scheduled"; this.beanMeta.status = "scheduled";
UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id); UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id);
}, duration); }, duration);
// Set last start date to current time
this.last_start_date = current.toISOString();
R.store(this);
}; };
// Create Cron // Create Cron
@ -268,9 +274,25 @@ class Maintenance extends BeanModel {
const startDateTime = startDate.hour(hour).minute(minute); const startDateTime = startDate.hour(hour).minute(minute);
this.beanMeta.job = new Cron(this.cron, { this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(), timezone: await this.getTimezone(),
interval: this.interval_day * 24 * 60 * 60,
startAt: startDateTime.toISOString(), startAt: startDateTime.toISOString(),
}, startEvent); }, () => {
if (!this.lastStartDate || this.interval_day === 1) {
return startEvent();
}
// If last start date is set, it means the maintenance has been started before
let lastStartDate = dayjs(this.lastStartDate)
.subtract(1.1, "hour"); // Subtract 1.1 hour to avoid issues with timezone differences
// Check if the interval is enough
if (current.diff(lastStartDate, "day") < this.interval_day) {
log.debug("maintenance", "Maintenance id: " + this.id + " is still in the window, skipping start event");
return;
}
log.debug("maintenance", "Maintenance id: " + this.id + " is not in the window, starting event");
return startEvent();
});
} else { } else {
this.beanMeta.job = new Cron(this.cron, { this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(), timezone: await this.getTimezone(),
@ -279,7 +301,6 @@ class Maintenance extends BeanModel {
// Continue if the maintenance is still in the window // Continue if the maintenance is still in the window
let runningTimeslot = this.getRunningTimeslot(); let runningTimeslot = this.getRunningTimeslot();
let current = dayjs();
if (runningTimeslot) { if (runningTimeslot) {
let duration = dayjs(runningTimeslot.endDate).diff(current, "second") * 1000; let duration = dayjs(runningTimeslot.endDate).diff(current, "second") * 1000;
@ -423,8 +444,11 @@ class Maintenance extends BeanModel {
} else if (!this.strategy.startsWith("recurring-")) { } else if (!this.strategy.startsWith("recurring-")) {
this.cron = ""; this.cron = "";
} else if (this.strategy === "recurring-interval") { } else if (this.strategy === "recurring-interval") {
// For intervals, the pattern is calculated in the run function as the interval-option is set // For intervals, the pattern is used to check if the execution should be started
this.cron = "* * * * *"; let array = this.start_time.split(":");
let hour = parseInt(array[0]);
let minute = parseInt(array[1]);
this.cron = `${minute} ${hour} * * *`;
this.duration = this.calcDuration(); this.duration = this.calcDuration();
log.debug("maintenance", "Cron: " + this.cron); log.debug("maintenance", "Cron: " + this.cron);
log.debug("maintenance", "Duration: " + this.duration); log.debug("maintenance", "Duration: " + this.duration);

View file

@ -1298,7 +1298,7 @@ class Monitor extends BeanModel {
/** /**
* Send a notification about a monitor * Send a notification about a monitor
* @param {boolean} isFirstBeat Is this beat the first of this monitor? * @param {boolean} isFirstBeat Is this beat the first of this monitor?
* @param {Monitor} monitor The monitor to send a notificaton about * @param {Monitor} monitor The monitor to send a notification about
* @param {Bean} bean Status information about monitor * @param {Bean} bean Status information about monitor
* @returns {void} * @returns {void}
*/ */

View file

@ -15,7 +15,7 @@ class PromoSMS extends NotificationProvider {
notification.promosmsAllowLongSMS = false; notification.promosmsAllowLongSMS = false;
} }
//TODO: Add option for enabling special characters. It will decrese message max length from 160 to 70 chars. //TODO: Add option for enabling special characters. It will decrease message max length from 160 to 70 chars.
//Lets remove non ascii char //Lets remove non ascii char
let cleanMsg = msg.replace(/[^\x00-\x7F]/g, ""); let cleanMsg = msg.replace(/[^\x00-\x7F]/g, "");

View file

@ -27,7 +27,7 @@ module.exports.apiKeySocketHandler = (socket) => {
log.debug("apikeys", "Added API Key"); log.debug("apikeys", "Added API Key");
log.debug("apikeys", key); log.debug("apikeys", key);
// Append key ID and prefix to start of key seperated by _, used to get // Append key ID and prefix to start of key separated by _, used to get
// correct hash when validating key. // correct hash when validating key.
let formattedKey = "uk" + bean.id + "_" + clearKey; let formattedKey = "uk" + bean.id + "_" + clearKey;
await sendAPIKeyList(socket); await sendAPIKeyList(socket);

View file

@ -582,7 +582,7 @@ class UptimeCalculator {
let totalPing = 0; let totalPing = 0;
let endTimestamp; let endTimestamp;
// Get the eariest timestamp of the required period based on the type // Get the earliest timestamp of the required period based on the type
switch (type) { switch (type) {
case "day": case "day":
endTimestamp = key - 86400 * (num - 1); endTimestamp = key - 86400 * (num - 1);
@ -710,7 +710,7 @@ class UptimeCalculator {
let endTimestamp; let endTimestamp;
// Get the eariest timestamp of the required period based on the type // Get the earliest timestamp of the required period based on the type
switch (type) { switch (type) {
case "day": case "day":
endTimestamp = key - 86400 * (num - 1); endTimestamp = key - 86400 * (num - 1);

View file

@ -18,7 +18,7 @@
{{ $t("matrixDesc1") }} {{ $t("matrixDesc1") }}
</p> </p>
<i18n-t tag="p" keypath="matrixDesc2" style="margin-top: 8px;"> <i18n-t tag="p" keypath="matrixDesc2" style="margin-top: 8px;">
<code>curl -XPOST -d '{"type": "m.login.password", "identifier": {"user": "botusername", "type": "m.id.user"}, "password": "passwordforuser"}' "https://home.server/_matrix/client/v3/login"</code>. <code>curl -XPOST --json '{"type": "m.login.password", "identifier": {"user": "botusername", "type": "m.id.user"}, "password": "passwordforuser"}' "https://home.server/_matrix/client/v3/login"</code>.
</i18n-t> </i18n-t>
</div> </div>
</template> </template>