fixed deletion

This commit is contained in:
pika 2025-04-03 12:51:31 +02:00
parent e9d1f985ae
commit 78ce15e82d
2 changed files with 94 additions and 58 deletions

View file

@ -409,15 +409,27 @@ def app_edit(app_id):
@bp.route("/app/<int:app_id>/delete", methods=["POST"]) @bp.route("/app/<int:app_id>/delete", methods=["POST"])
@login_required @login_required
def app_delete(app_id): def app_delete(app_id):
"""Delete an application""" """Delete an application and all its ports"""
app = App.query.get_or_404(app_id) app = App.query.get_or_404(app_id)
app_name = app.name
server_id = app.server_id server_id = app.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.delete(app)
db.session.commit() db.session.commit()
flash("Application deleted successfully", "success") 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)) 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"]) @bp.route("/settings", methods=["GET", "POST"])

View file

@ -1,15 +1,15 @@
// Application Name Validation // Application Name Validation
function validateAppName() { function validateAppName() {
const nameField = document.getElementById('app-name'); const nameField = document.getElementById("app-name");
const serverField = document.getElementById('server-id'); const serverField = document.getElementById("server-id");
const feedbackElement = document.getElementById('name-feedback'); const feedbackElement = document.getElementById("name-feedback");
const submitButton = document.querySelector('button[type="submit"]'); const submitButton = document.querySelector('button[type="submit"]');
if (!nameField || !serverField) return; if (!nameField || !serverField) return;
const name = nameField.value.trim(); const name = nameField.value.trim();
const serverId = serverField.value; const serverId = serverField.value;
const appId = document.getElementById('app-id')?.value; const appId = document.getElementById("app-id")?.value;
if (!name || !serverId) { if (!name || !serverId) {
clearFeedback(feedbackElement); clearFeedback(feedbackElement);
@ -19,9 +19,11 @@ function validateAppName() {
// Debounce to avoid too many requests // Debounce to avoid too many requests
clearTimeout(nameField.timer); clearTimeout(nameField.timer);
nameField.timer = setTimeout(() => { nameField.timer = setTimeout(() => {
fetch(`/api/validate/app-name?name=${encodeURIComponent(name)}&server_id=${serverId}${appId ? '&app_id=' + appId : ''}`) fetch(
.then(response => response.json()) `/api/validate/app-name?name=${encodeURIComponent(name)}&server_id=${serverId}${appId ? "&app_id=" + appId : ""}`,
.then(data => { )
.then((response) => response.json())
.then((data) => {
if (!data.valid) { if (!data.valid) {
showError(feedbackElement, data.message); showError(feedbackElement, data.message);
if (data.edit_url) { if (data.edit_url) {
@ -32,10 +34,10 @@ function validateAppName() {
showWarning(feedbackElement, data.warning); showWarning(feedbackElement, data.warning);
if (data.similar_apps && data.similar_apps.length > 0) { if (data.similar_apps && data.similar_apps.length > 0) {
feedbackElement.innerHTML += '<ul class="mb-0 mt-1">'; feedbackElement.innerHTML += '<ul class="mb-0 mt-1">';
data.similar_apps.forEach(app => { data.similar_apps.forEach((app) => {
feedbackElement.innerHTML += `<li><a href="/dashboard/app/${app.id}/edit" class="alert-link">${app.name}</a></li>`; feedbackElement.innerHTML += `<li><a href="/dashboard/app/${app.id}/edit" class="alert-link">${app.name}</a></li>`;
}); });
feedbackElement.innerHTML += '</ul>'; feedbackElement.innerHTML += "</ul>";
} }
submitButton.disabled = false; submitButton.disabled = false;
} else { } else {
@ -43,8 +45,8 @@ function validateAppName() {
submitButton.disabled = false; submitButton.disabled = false;
} }
}) })
.catch(error => { .catch((error) => {
console.error('Validation error:', error); console.error("Validation error:", error);
clearFeedback(feedbackElement); clearFeedback(feedbackElement);
}); });
}, 300); }, 300);
@ -52,11 +54,14 @@ function validateAppName() {
// Port Validation // Port Validation
function validatePort(portField, protocolField) { function validatePort(portField, protocolField) {
const serverField = document.getElementById('server-id'); const serverField = document.getElementById("server-id");
// Create feedback element if it doesn't exist // Create feedback element if it doesn't exist
if (!portField.nextElementSibling || !portField.nextElementSibling.classList.contains('feedback')) { if (
const feedback = document.createElement('div'); !portField.nextElementSibling ||
feedback.className = 'feedback'; !portField.nextElementSibling.classList.contains("feedback")
) {
const feedback = document.createElement("div");
feedback.className = "feedback";
portField.parentNode.insertBefore(feedback, portField.nextSibling); portField.parentNode.insertBefore(feedback, portField.nextSibling);
} }
@ -68,56 +73,69 @@ function validatePort(portField, protocolField) {
const port = portField.value.trim(); const port = portField.value.trim();
const protocol = protocolField.value; const protocol = protocolField.value;
const serverId = serverField.value; const serverId = serverField.value;
const appId = document.getElementById('app-id')?.value; const appId = document.getElementById("app-id")?.value;
if (!port || !serverId) { if (!port || !serverId) {
clearFeedback(feedbackElement); clearFeedback(feedbackElement);
portField.classList.remove('is-invalid'); portField.classList.remove("is-invalid");
return; return;
} }
// Check for duplicate ports within the form first // Check for duplicate ports within the form first
const allPortFields = document.querySelectorAll('input[name="port_numbers[]"]'); const allPortFields = document.querySelectorAll(
const allProtocolFields = document.querySelectorAll('select[name="protocols[]"]'); 'input[name="port_numbers[]"]',
);
const allProtocolFields = document.querySelectorAll(
'select[name="protocols[]"]',
);
let duplicateFound = false; let duplicateFound = false;
allPortFields.forEach((field, index) => { allPortFields.forEach((field, index) => {
if (field !== portField && if (
field !== portField &&
field.value === port && field.value === port &&
allProtocolFields[index].value === protocol) { allProtocolFields[index].value === protocol
) {
duplicateFound = true; duplicateFound = true;
} }
}); });
if (duplicateFound) { if (duplicateFound) {
portField.classList.add('is-invalid'); portField.classList.add("is-invalid");
showError(feedbackElement, `Duplicate port ${port}/${protocol} in your form`); clearFeedback(feedbackElement);
showError(
feedbackElement,
`Duplicate port ${port}/${protocol} in your form`,
);
return; return;
} }
// Debounce to avoid too many requests // Debounce to avoid too many requests
clearTimeout(portField.timer); clearTimeout(portField.timer);
portField.timer = setTimeout(() => { portField.timer = setTimeout(() => {
fetch(`/api/validate/app-port?port_number=${encodeURIComponent(port)}&protocol=${protocol}&server_id=${serverId}${appId ? '&app_id=' + appId : ''}`) fetch(
.then(response => response.json()) `/api/validate/app-port?port_number=${encodeURIComponent(port)}&protocol=${protocol}&server_id=${serverId}${appId ? "&app_id=" + appId : ""}`,
.then(data => { )
.then((response) => response.json())
.then((data) => {
clearFeedback(feedbackElement);
if (!data.valid) { if (!data.valid) {
portField.classList.add('is-invalid'); portField.classList.add("is-invalid");
showError(feedbackElement, data.message); showError(feedbackElement, data.message);
if (data.edit_url) { if (data.edit_url) {
feedbackElement.innerHTML += ` <a href="${data.edit_url}" class="alert-link">Edit conflicting app</a>`; feedbackElement.innerHTML += ` <a href="${data.edit_url}" class="alert-link">Edit conflicting app</a>`;
} }
} else { } else {
portField.classList.remove('is-invalid'); portField.classList.remove("is-invalid");
portField.classList.add('is-valid'); portField.classList.add("is-valid");
showSuccess(feedbackElement, "Port available"); showSuccess(feedbackElement, "Port available");
} }
}) })
.catch(error => { .catch((error) => {
console.error('Validation error:', error); console.error("Validation error:", error);
clearFeedback(feedbackElement); clearFeedback(feedbackElement);
portField.classList.remove('is-invalid'); portField.classList.remove("is-invalid");
portField.classList.remove('is-valid'); portField.classList.remove("is-valid");
}); });
}, 300); }, 300);
} }
@ -125,38 +143,38 @@ function validatePort(portField, protocolField) {
// Helper functions for feedback display // Helper functions for feedback display
function showError(element, message) { function showError(element, message) {
if (!element) return; if (!element) return;
element.className = 'invalid-feedback d-block'; element.className = "invalid-feedback d-block";
element.textContent = message; element.textContent = message;
} }
function showWarning(element, message) { function showWarning(element, message) {
if (!element) return; if (!element) return;
element.className = 'text-warning d-block'; element.className = "text-warning d-block";
element.textContent = message; element.textContent = message;
} }
function showSuccess(element, message) { function showSuccess(element, message) {
if (!element) return; if (!element) return;
element.className = 'valid-feedback d-block'; element.className = "valid-feedback d-block";
element.textContent = message; element.textContent = message;
} }
function clearFeedback(element) { function clearFeedback(element) {
if (!element) return; if (!element) return;
element.className = ''; element.className = "";
element.textContent = ''; element.textContent = "";
} }
// Set up event listeners when the document is loaded // Set up event listeners when the document is loaded
document.addEventListener('DOMContentLoaded', function () { document.addEventListener("DOMContentLoaded", function () {
const nameField = document.getElementById('app-name'); const nameField = document.getElementById("app-name");
const serverField = document.getElementById('server-id'); const serverField = document.getElementById("server-id");
if (nameField) { if (nameField) {
nameField.addEventListener('input', validateAppName); nameField.addEventListener("input", validateAppName);
if (serverField) { if (serverField) {
serverField.addEventListener('change', validateAppName); serverField.addEventListener("change", validateAppName);
} }
} }
@ -164,9 +182,9 @@ document.addEventListener('DOMContentLoaded', function () {
setupPortValidation(); setupPortValidation();
// Make sure new port rows get validation too // Make sure new port rows get validation too
const addPortButton = document.getElementById('add-port-btn'); const addPortButton = document.getElementById("add-port-btn");
if (addPortButton) { if (addPortButton) {
addPortButton.addEventListener('click', function () { addPortButton.addEventListener("click", function () {
// Wait a moment for the new row to be added // Wait a moment for the new row to be added
setTimeout(setupPortValidation, 100); setTimeout(setupPortValidation, 100);
}); });
@ -175,26 +193,31 @@ document.addEventListener('DOMContentLoaded', function () {
function setupPortValidation() { function setupPortValidation() {
const portFields = document.querySelectorAll('input[name="port_numbers[]"]'); 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) => { portFields.forEach((field, index) => {
if (!field.hasValidationSetup) { if (!field.hasValidationSetup) {
const protocolField = protocolFields[index]; const protocolField = protocolFields[index];
field.addEventListener('input', function () { field.addEventListener("input", function () {
validatePort(field, protocolField); validatePort(field, protocolField);
}); });
if (protocolField) { if (protocolField) {
protocolField.addEventListener('change', function () { protocolField.addEventListener("change", function () {
validatePort(field, protocolField); validatePort(field, protocolField);
}); });
} }
// Add a div for feedback if it doesn't exist // Add a div for feedback if it doesn't exist
if (!field.nextElementSibling || !field.nextElementSibling.classList.contains('feedback')) { if (
const feedback = document.createElement('div'); !field.nextElementSibling ||
feedback.className = 'feedback'; !field.nextElementSibling.classList.contains("feedback")
) {
const feedback = document.createElement("div");
feedback.className = "feedback";
field.parentNode.insertBefore(feedback, field.nextSibling); field.parentNode.insertBefore(feedback, field.nextSibling);
} }
@ -202,3 +225,4 @@ function setupPortValidation() {
} }
}); });
} }