feat: enhance monitor deletion functionality by adding child deletion… (#6314)

Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
Dorian Grasset
2025-11-11 03:52:13 +01:00
committed by GitHub
co-authored by Frank Elsinga
parent 20c6cfcfad
commit 2d8918a1b8
5 changed files with 145 additions and 25 deletions
+49
View File
@@ -1727,6 +1727,55 @@ class Monitor extends BeanModel {
]);
}
/**
* Delete a monitor from the system
* @param {number} monitorID ID of the monitor to delete
* @param {number} userID ID of the user who owns the monitor
* @returns {Promise<void>}
*/
static async deleteMonitor(monitorID, userID) {
const server = UptimeKumaServer.getInstance();
// Stop the monitor if it's running
if (monitorID in server.monitorList) {
await server.monitorList[monitorID].stop();
delete server.monitorList[monitorID];
}
// Delete from database
await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [
monitorID,
userID,
]);
}
/**
* Recursively delete a monitor and all its descendants
* @param {number} monitorID ID of the monitor to delete
* @param {number} userID ID of the user who owns the monitor
* @returns {Promise<void>}
*/
static async deleteMonitorRecursively(monitorID, userID) {
// Check if this monitor is a group
const monitor = await R.findOne("monitor", " id = ? AND user_id = ? ", [
monitorID,
userID,
]);
if (monitor && monitor.type === "group") {
// Get all children and delete them recursively
const children = await Monitor.getChildren(monitorID);
if (children && children.length > 0) {
for (const child of children) {
await Monitor.deleteMonitorRecursively(child.id, userID);
}
}
}
// Delete the monitor itself
await Monitor.deleteMonitor(monitorID, userID);
}
/**
* Checks recursive if parent (ancestors) are active
* @param {number} monitorID ID of the monitor to get
+48 -21
View File
@@ -1047,51 +1047,78 @@ let needSetup = false;
}
});
socket.on("deleteMonitor", async (monitorID, callback) => {
socket.on("deleteMonitor", async (monitorID, deleteChildren, callback) => {
try {
checkLogin(socket);
log.info("manage", `Delete Monitor: ${monitorID} User ID: ${socket.userID}`);
if (monitorID in server.monitorList) {
await server.monitorList[monitorID].stop();
delete server.monitorList[monitorID];
// Backward compatibility: if deleteChildren is omitted, the second parameter is the callback
if (typeof deleteChildren === "function") {
callback = deleteChildren;
deleteChildren = false;
}
checkLogin(socket);
const startTime = Date.now();
// Check if this is a group monitor and unlink children before deletion
// Check if this is a group monitor
const monitor = await R.findOne("monitor", " id = ? AND user_id = ? ", [
monitorID,
socket.userID,
]);
// Log with context about deletion type
if (monitor && monitor.type === "group") {
// Get all children before unlinking them
if (deleteChildren) {
log.info("manage", `Delete Group and Children: ${monitorID} User ID: ${socket.userID}`);
} else {
log.info("manage", `Delete Group (unlink children): ${monitorID} User ID: ${socket.userID}`);
}
} else {
log.info("manage", `Delete Monitor: ${monitorID} User ID: ${socket.userID}`);
}
if (monitor && monitor.type === "group") {
// Get all children before processing
const children = await Monitor.getChildren(monitorID);
// Unlink all children from the group
await Monitor.unlinkAllChildren(monitorID);
if (deleteChildren) {
// Delete all child monitors recursively
if (children && children.length > 0) {
for (const child of children) {
await Monitor.deleteMonitorRecursively(child.id, socket.userID);
await server.sendDeleteMonitorFromList(socket, child.id);
}
}
} else {
// Unlink all children from the group (set parent to null)
await Monitor.unlinkAllChildren(monitorID);
// Notify frontend to update each child monitor's parent to null
if (children && children.length > 0) {
for (const child of children) {
await server.sendUpdateMonitorIntoList(socket, child.id);
// Notify frontend to update each child monitor's parent to null
if (children && children.length > 0) {
for (const child of children) {
await server.sendUpdateMonitorIntoList(socket, child.id);
}
}
}
}
await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [
monitorID,
socket.userID,
]);
// Delete the monitor itself
await Monitor.deleteMonitor(monitorID, socket.userID);
// Fix #2880
apicache.clear();
const endTime = Date.now();
log.info("DB", `Delete Monitor completed in : ${endTime - startTime} ms`);
// Log completion with context about children handling
if (monitor && monitor.type === "group") {
if (deleteChildren) {
log.info("DB", `Delete Monitor completed (group and children deleted) in: ${endTime - startTime} ms`);
} else {
log.info("DB", `Delete Monitor completed (group deleted, children unlinked) in: ${endTime - startTime} ms`);
}
} else {
log.info("DB", `Delete Monitor completed in: ${endTime - startTime} ms`);
}
callback({
ok: true,