You've already forked uptime-kuma
mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-11-25 22:41:57 +02:00
feat: enhance monitor deletion functionality by adding child deletion… (#6314)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
@@ -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
|
* Checks recursive if parent (ancestors) are active
|
||||||
* @param {number} monitorID ID of the monitor to get
|
* @param {number} monitorID ID of the monitor to get
|
||||||
|
|||||||
@@ -1047,51 +1047,78 @@ let needSetup = false;
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("deleteMonitor", async (monitorID, callback) => {
|
socket.on("deleteMonitor", async (monitorID, deleteChildren, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
// Backward compatibility: if deleteChildren is omitted, the second parameter is the callback
|
||||||
|
if (typeof deleteChildren === "function") {
|
||||||
log.info("manage", `Delete Monitor: ${monitorID} User ID: ${socket.userID}`);
|
callback = deleteChildren;
|
||||||
|
deleteChildren = false;
|
||||||
if (monitorID in server.monitorList) {
|
|
||||||
await server.monitorList[monitorID].stop();
|
|
||||||
delete server.monitorList[monitorID];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
const startTime = Date.now();
|
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 = ? ", [
|
const monitor = await R.findOne("monitor", " id = ? AND user_id = ? ", [
|
||||||
monitorID,
|
monitorID,
|
||||||
socket.userID,
|
socket.userID,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Log with context about deletion type
|
||||||
if (monitor && monitor.type === "group") {
|
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);
|
const children = await Monitor.getChildren(monitorID);
|
||||||
|
|
||||||
// Unlink all children from the group
|
if (deleteChildren) {
|
||||||
await Monitor.unlinkAllChildren(monitorID);
|
// 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
|
// Notify frontend to update each child monitor's parent to null
|
||||||
if (children && children.length > 0) {
|
if (children && children.length > 0) {
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
await server.sendUpdateMonitorIntoList(socket, child.id);
|
await server.sendUpdateMonitorIntoList(socket, child.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await R.exec("DELETE FROM monitor WHERE id = ? AND user_id = ? ", [
|
// Delete the monitor itself
|
||||||
monitorID,
|
await Monitor.deleteMonitor(monitorID, socket.userID);
|
||||||
socket.userID,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Fix #2880
|
// Fix #2880
|
||||||
apicache.clear();
|
apicache.clear();
|
||||||
|
|
||||||
const endTime = Date.now();
|
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({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|||||||
@@ -596,6 +596,8 @@
|
|||||||
"grpcMethodDescription": "Method name is convert to camelCase format such as sayHello, check, etc.",
|
"grpcMethodDescription": "Method name is convert to camelCase format such as sayHello, check, etc.",
|
||||||
"acceptedStatusCodesDescription": "Select status codes which are considered as a successful response.",
|
"acceptedStatusCodesDescription": "Select status codes which are considered as a successful response.",
|
||||||
"deleteMonitorMsg": "Are you sure want to delete this monitor?",
|
"deleteMonitorMsg": "Are you sure want to delete this monitor?",
|
||||||
|
"deleteGroupMsg": "Are you sure you want to delete this group?",
|
||||||
|
"deleteChildrenMonitors": "Also delete the direct child monitors and its children if it has any | Also delete all {count} direct child monitors and their children if they have any",
|
||||||
"deleteMaintenanceMsg": "Are you sure want to delete this maintenance?",
|
"deleteMaintenanceMsg": "Are you sure want to delete this maintenance?",
|
||||||
"deleteNotificationMsg": "Are you sure want to delete this notification for all monitors?",
|
"deleteNotificationMsg": "Are you sure want to delete this notification for all monitors?",
|
||||||
"dnsPortDescription": "DNS server port. Defaults to 53. You can change the port at any time.",
|
"dnsPortDescription": "DNS server port. Defaults to 53. You can change the port at any time.",
|
||||||
|
|||||||
@@ -602,11 +602,12 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Delete monitor by ID
|
* Delete monitor by ID
|
||||||
* @param {number} monitorID ID of monitor to delete
|
* @param {number} monitorID ID of monitor to delete
|
||||||
|
* @param {boolean} deleteChildren Whether to delete child monitors (for groups)
|
||||||
* @param {socketCB} callback Callback for socket response
|
* @param {socketCB} callback Callback for socket response
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
deleteMonitor(monitorID, callback) {
|
deleteMonitor(monitorID, deleteChildren, callback) {
|
||||||
socket.emit("deleteMonitor", monitorID, callback);
|
socket.emit("deleteMonitor", monitorID, deleteChildren, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -441,7 +441,23 @@
|
|||||||
:no-text="$t('No')"
|
:no-text="$t('No')"
|
||||||
@yes="deleteMonitor"
|
@yes="deleteMonitor"
|
||||||
>
|
>
|
||||||
{{ $t("deleteMonitorMsg") }}
|
<div v-if="monitor && monitor.type === 'group'">
|
||||||
|
<div>{{ $t("deleteGroupMsg") }}</div>
|
||||||
|
<div v-if="hasChildren" class="form-check">
|
||||||
|
<input
|
||||||
|
id="delete-children-checkbox"
|
||||||
|
v-model="deleteChildrenMonitors"
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
>
|
||||||
|
<label class="form-check-label" for="delete-children-checkbox">
|
||||||
|
{{ $t("deleteChildrenMonitors", childrenCount, { count: childrenCount }) }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
{{ $t("deleteMonitorMsg") }}
|
||||||
|
</div>
|
||||||
</Confirm>
|
</Confirm>
|
||||||
|
|
||||||
<Confirm
|
<Confirm
|
||||||
@@ -530,6 +546,7 @@ export default {
|
|||||||
currentExample: "javascript-fetch",
|
currentExample: "javascript-fetch",
|
||||||
code: "",
|
code: "",
|
||||||
},
|
},
|
||||||
|
deleteChildrenMonitors: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -538,6 +555,26 @@ export default {
|
|||||||
return this.$root.monitorList[id];
|
return this.$root.monitorList[id];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the count of children monitors for this group
|
||||||
|
* @returns {number} Number of children monitors
|
||||||
|
*/
|
||||||
|
childrenCount() {
|
||||||
|
if (!this.monitor || this.monitor.type !== "group") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const children = Object.values(this.$root.monitorList).filter(m => m.parent === this.monitor.id);
|
||||||
|
return children.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the monitor is a group and has children
|
||||||
|
* @returns {boolean} True if monitor is a group with children
|
||||||
|
*/
|
||||||
|
hasChildren() {
|
||||||
|
return this.childrenCount > 0;
|
||||||
|
},
|
||||||
|
|
||||||
lastHeartBeat() {
|
lastHeartBeat() {
|
||||||
// Also trigger screenshot refresh here
|
// Also trigger screenshot refresh here
|
||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
@@ -752,7 +789,7 @@ export default {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
deleteMonitor() {
|
deleteMonitor() {
|
||||||
this.$root.deleteMonitor(this.monitor.id, (res) => {
|
this.$root.deleteMonitor(this.monitor.id, this.deleteChildrenMonitors, (res) => {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.$router.push("/dashboard");
|
this.$router.push("/dashboard");
|
||||||
@@ -937,6 +974,10 @@ export default {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../assets/vars.scss";
|
@import "../assets/vars.scss";
|
||||||
|
|
||||||
|
.form-check {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.badge {
|
.badge {
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
|
|||||||
Reference in New Issue
Block a user