From 78ce15e82df21c72b261f26089198f5bf1a3ba42 Mon Sep 17 00:00:00 2001 From: pika Date: Thu, 3 Apr 2025 12:51:31 +0200 Subject: [PATCH] fixed deletion --- app/routes/dashboard.py | 24 +++++-- app/static/js/validation.js | 128 +++++++++++++++++++++--------------- 2 files changed, 94 insertions(+), 58 deletions(-) diff --git a/app/routes/dashboard.py b/app/routes/dashboard.py index c8590c5..ea3e7c4 100644 --- a/app/routes/dashboard.py +++ b/app/routes/dashboard.py @@ -409,15 +409,27 @@ def app_edit(app_id): @bp.route("/app//delete", methods=["POST"]) @login_required def app_delete(app_id): - """Delete an application""" + """Delete an application and all its ports""" app = App.query.get_or_404(app_id) + app_name = app.name server_id = app.server_id - db.session.delete(app) - db.session.commit() - - flash("Application deleted successfully", "success") - return redirect(url_for("dashboard.server_view", server_id=server_id)) + try: + # First explicitly delete all associated ports + Port.query.filter_by(app_id=app_id).delete() + + # Then delete the application + db.session.delete(app) + db.session.commit() + + flash(f"Application '{app_name}' has been deleted", "success") + + # Redirect back to server view + return redirect(url_for("dashboard.server_view", server_id=server_id)) + except Exception as e: + db.session.rollback() + flash(f"Error deleting application: {str(e)}", "danger") + return redirect(url_for("dashboard.app_view", app_id=app_id)) @bp.route("/settings", methods=["GET", "POST"]) diff --git a/app/static/js/validation.js b/app/static/js/validation.js index 07a68a4..6ea3c19 100644 --- a/app/static/js/validation.js +++ b/app/static/js/validation.js @@ -1,15 +1,15 @@ // Application Name Validation function validateAppName() { - const nameField = document.getElementById('app-name'); - const serverField = document.getElementById('server-id'); - const feedbackElement = document.getElementById('name-feedback'); + const nameField = document.getElementById("app-name"); + const serverField = document.getElementById("server-id"); + const feedbackElement = document.getElementById("name-feedback"); const submitButton = document.querySelector('button[type="submit"]'); if (!nameField || !serverField) return; const name = nameField.value.trim(); const serverId = serverField.value; - const appId = document.getElementById('app-id')?.value; + const appId = document.getElementById("app-id")?.value; if (!name || !serverId) { clearFeedback(feedbackElement); @@ -19,9 +19,11 @@ function validateAppName() { // Debounce to avoid too many requests clearTimeout(nameField.timer); nameField.timer = setTimeout(() => { - fetch(`/api/validate/app-name?name=${encodeURIComponent(name)}&server_id=${serverId}${appId ? '&app_id=' + appId : ''}`) - .then(response => response.json()) - .then(data => { + fetch( + `/api/validate/app-name?name=${encodeURIComponent(name)}&server_id=${serverId}${appId ? "&app_id=" + appId : ""}`, + ) + .then((response) => response.json()) + .then((data) => { if (!data.valid) { showError(feedbackElement, data.message); if (data.edit_url) { @@ -32,10 +34,10 @@ function validateAppName() { showWarning(feedbackElement, data.warning); if (data.similar_apps && data.similar_apps.length > 0) { feedbackElement.innerHTML += ''; + feedbackElement.innerHTML += ""; } submitButton.disabled = false; } else { @@ -43,8 +45,8 @@ function validateAppName() { submitButton.disabled = false; } }) - .catch(error => { - console.error('Validation error:', error); + .catch((error) => { + console.error("Validation error:", error); clearFeedback(feedbackElement); }); }, 300); @@ -52,11 +54,14 @@ function validateAppName() { // Port Validation function validatePort(portField, protocolField) { - const serverField = document.getElementById('server-id'); + const serverField = document.getElementById("server-id"); // Create feedback element if it doesn't exist - if (!portField.nextElementSibling || !portField.nextElementSibling.classList.contains('feedback')) { - const feedback = document.createElement('div'); - feedback.className = 'feedback'; + if ( + !portField.nextElementSibling || + !portField.nextElementSibling.classList.contains("feedback") + ) { + const feedback = document.createElement("div"); + feedback.className = "feedback"; portField.parentNode.insertBefore(feedback, portField.nextSibling); } @@ -68,56 +73,69 @@ function validatePort(portField, protocolField) { const port = portField.value.trim(); const protocol = protocolField.value; const serverId = serverField.value; - const appId = document.getElementById('app-id')?.value; + const appId = document.getElementById("app-id")?.value; if (!port || !serverId) { clearFeedback(feedbackElement); - portField.classList.remove('is-invalid'); + portField.classList.remove("is-invalid"); return; } // Check for duplicate ports within the form first - const allPortFields = document.querySelectorAll('input[name="port_numbers[]"]'); - const allProtocolFields = document.querySelectorAll('select[name="protocols[]"]'); + const allPortFields = document.querySelectorAll( + 'input[name="port_numbers[]"]', + ); + const allProtocolFields = document.querySelectorAll( + 'select[name="protocols[]"]', + ); let duplicateFound = false; allPortFields.forEach((field, index) => { - if (field !== portField && + if ( + field !== portField && field.value === port && - allProtocolFields[index].value === protocol) { + allProtocolFields[index].value === protocol + ) { duplicateFound = true; } }); if (duplicateFound) { - portField.classList.add('is-invalid'); - showError(feedbackElement, `Duplicate port ${port}/${protocol} in your form`); + portField.classList.add("is-invalid"); + clearFeedback(feedbackElement); + showError( + feedbackElement, + `Duplicate port ${port}/${protocol} in your form`, + ); return; } // Debounce to avoid too many requests clearTimeout(portField.timer); portField.timer = setTimeout(() => { - fetch(`/api/validate/app-port?port_number=${encodeURIComponent(port)}&protocol=${protocol}&server_id=${serverId}${appId ? '&app_id=' + appId : ''}`) - .then(response => response.json()) - .then(data => { + fetch( + `/api/validate/app-port?port_number=${encodeURIComponent(port)}&protocol=${protocol}&server_id=${serverId}${appId ? "&app_id=" + appId : ""}`, + ) + .then((response) => response.json()) + .then((data) => { + clearFeedback(feedbackElement); if (!data.valid) { - portField.classList.add('is-invalid'); + portField.classList.add("is-invalid"); showError(feedbackElement, data.message); if (data.edit_url) { feedbackElement.innerHTML += ` Edit conflicting app`; } } else { - portField.classList.remove('is-invalid'); - portField.classList.add('is-valid'); + portField.classList.remove("is-invalid"); + portField.classList.add("is-valid"); showSuccess(feedbackElement, "Port available"); } }) - .catch(error => { - console.error('Validation error:', error); + .catch((error) => { + console.error("Validation error:", error); clearFeedback(feedbackElement); - portField.classList.remove('is-invalid'); - portField.classList.remove('is-valid'); + portField.classList.remove("is-invalid"); + portField.classList.remove("is-valid"); }); }, 300); } @@ -125,38 +143,38 @@ function validatePort(portField, protocolField) { // Helper functions for feedback display function showError(element, message) { if (!element) return; - element.className = 'invalid-feedback d-block'; + element.className = "invalid-feedback d-block"; element.textContent = message; } function showWarning(element, message) { if (!element) return; - element.className = 'text-warning d-block'; + element.className = "text-warning d-block"; element.textContent = message; } function showSuccess(element, message) { if (!element) return; - element.className = 'valid-feedback d-block'; + element.className = "valid-feedback d-block"; element.textContent = message; } function clearFeedback(element) { if (!element) return; - element.className = ''; - element.textContent = ''; + element.className = ""; + element.textContent = ""; } // Set up event listeners when the document is loaded -document.addEventListener('DOMContentLoaded', function () { - const nameField = document.getElementById('app-name'); - const serverField = document.getElementById('server-id'); +document.addEventListener("DOMContentLoaded", function () { + const nameField = document.getElementById("app-name"); + const serverField = document.getElementById("server-id"); if (nameField) { - nameField.addEventListener('input', validateAppName); + nameField.addEventListener("input", validateAppName); if (serverField) { - serverField.addEventListener('change', validateAppName); + serverField.addEventListener("change", validateAppName); } } @@ -164,9 +182,9 @@ document.addEventListener('DOMContentLoaded', function () { setupPortValidation(); // Make sure new port rows get validation too - const addPortButton = document.getElementById('add-port-btn'); + const addPortButton = document.getElementById("add-port-btn"); if (addPortButton) { - addPortButton.addEventListener('click', function () { + addPortButton.addEventListener("click", function () { // Wait a moment for the new row to be added setTimeout(setupPortValidation, 100); }); @@ -175,30 +193,36 @@ document.addEventListener('DOMContentLoaded', function () { function setupPortValidation() { const portFields = document.querySelectorAll('input[name="port_numbers[]"]'); - const protocolFields = document.querySelectorAll('select[name="protocols[]"]'); + const protocolFields = document.querySelectorAll( + 'select[name="protocols[]"]', + ); portFields.forEach((field, index) => { if (!field.hasValidationSetup) { const protocolField = protocolFields[index]; - field.addEventListener('input', function () { + field.addEventListener("input", function () { validatePort(field, protocolField); }); if (protocolField) { - protocolField.addEventListener('change', function () { + protocolField.addEventListener("change", function () { validatePort(field, protocolField); }); } // Add a div for feedback if it doesn't exist - if (!field.nextElementSibling || !field.nextElementSibling.classList.contains('feedback')) { - const feedback = document.createElement('div'); - feedback.className = 'feedback'; + if ( + !field.nextElementSibling || + !field.nextElementSibling.classList.contains("feedback") + ) { + const feedback = document.createElement("div"); + feedback.className = "feedback"; field.parentNode.insertBefore(feedback, field.nextSibling); } field.hasValidationSetup = true; } }); -} \ No newline at end of file +} +