wip
This commit is contained in:
parent
30e9c9328e
commit
254593d260
20 changed files with 156 additions and 65 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,13 @@
|
|||
from flask import Blueprint, jsonify, request, abort, current_app, render_template, redirect, url_for
|
||||
from flask import (
|
||||
Blueprint,
|
||||
jsonify,
|
||||
request,
|
||||
abort,
|
||||
current_app,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import login_required
|
||||
from app.core.models import Subnet, Server, App, Port
|
||||
from app.core.extensions import db
|
||||
|
@ -277,59 +286,77 @@ def suggest_port(server_id):
|
|||
def add_app_port(app_id):
|
||||
"""Add a port to an application"""
|
||||
app = App.query.get_or_404(app_id)
|
||||
|
||||
|
||||
try:
|
||||
port_number = request.form.get("port_number")
|
||||
protocol = request.form.get("protocol", "TCP")
|
||||
description = request.form.get("description", "")
|
||||
|
||||
|
||||
# Validate port data
|
||||
valid, clean_port, error = validate_port_data(port_number, protocol, description)
|
||||
|
||||
valid, clean_port, error = validate_port_data(
|
||||
port_number, protocol, description
|
||||
)
|
||||
|
||||
if not valid:
|
||||
flash(error, "danger")
|
||||
return redirect(url_for("dashboard.app_view", app_id=app_id)) if not request.is_xhr else jsonify({"success": False, "error": error}), 400
|
||||
|
||||
return (
|
||||
redirect(url_for("dashboard.app_view", app_id=app_id))
|
||||
if not request.is_xhr
|
||||
else jsonify({"success": False, "error": error})
|
||||
), 400
|
||||
|
||||
# Check if port already exists
|
||||
existing_port = Port.query.filter_by(app_id=app_id, port_number=clean_port).first()
|
||||
existing_port = Port.query.filter_by(
|
||||
app_id=app_id, port_number=clean_port
|
||||
).first()
|
||||
if existing_port:
|
||||
error_msg = f"Port {clean_port} already exists for this application"
|
||||
flash(error_msg, "warning")
|
||||
return redirect(url_for("dashboard.app_view", app_id=app_id)) if not request.is_xhr else jsonify({"success": False, "error": error_msg}), 400
|
||||
|
||||
return (
|
||||
redirect(url_for("dashboard.app_view", app_id=app_id))
|
||||
if not request.is_xhr
|
||||
else jsonify({"success": False, "error": error_msg})
|
||||
), 400
|
||||
|
||||
# Create new port
|
||||
new_port = Port(
|
||||
app_id=app_id,
|
||||
port_number=clean_port,
|
||||
protocol=protocol,
|
||||
description=description
|
||||
description=description,
|
||||
)
|
||||
db.session.add(new_port)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
success_msg = f"Port {clean_port}/{protocol} added successfully"
|
||||
flash(success_msg, "success")
|
||||
|
||||
|
||||
# If it's a regular form submission (not AJAX), redirect
|
||||
if not request.is_xhr and not request.is_json:
|
||||
return redirect(url_for("dashboard.app_view", app_id=app_id))
|
||||
|
||||
|
||||
# Otherwise return JSON for API/AJAX calls
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": success_msg,
|
||||
"port": {
|
||||
"id": new_port.id,
|
||||
"number": new_port.port_number,
|
||||
"protocol": new_port.protocol,
|
||||
"description": new_port.description
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"message": success_msg,
|
||||
"port": {
|
||||
"id": new_port.id,
|
||||
"number": new_port.port_number,
|
||||
"protocol": new_port.protocol,
|
||||
"description": new_port.description,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash(f"Error: {str(e)}", "danger")
|
||||
return redirect(url_for("dashboard.app_view", app_id=app_id)) if not request.is_xhr else jsonify({"success": False, "error": str(e)}), 500
|
||||
return (
|
||||
redirect(url_for("dashboard.app_view", app_id=app_id))
|
||||
if not request.is_xhr
|
||||
else jsonify({"success": False, "error": str(e)})
|
||||
), 500
|
||||
|
||||
|
||||
@bp.route("/app/<int:app_id>/ports", methods=["GET"])
|
||||
|
@ -362,19 +389,21 @@ def delete_app_port(app_id, port_id):
|
|||
"""Delete a port from an application"""
|
||||
app = App.query.get_or_404(app_id)
|
||||
port = Port.query.get_or_404(port_id)
|
||||
|
||||
|
||||
if port.app_id != app.id:
|
||||
flash("Port does not belong to this application", "danger")
|
||||
return redirect(url_for("dashboard.app_view", app_id=app_id))
|
||||
|
||||
|
||||
try:
|
||||
db.session.delete(port)
|
||||
db.session.commit()
|
||||
flash(f"Port {port.port_number}/{port.protocol} deleted successfully", "success")
|
||||
flash(
|
||||
f"Port {port.port_number}/{port.protocol} deleted successfully", "success"
|
||||
)
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash(f"Error deleting port: {str(e)}", "danger")
|
||||
|
||||
|
||||
return redirect(url_for("dashboard.app_view", app_id=app_id))
|
||||
|
||||
|
||||
|
|
|
@ -218,52 +218,65 @@ def server_delete(server_id):
|
|||
|
||||
|
||||
@bp.route("/app/new", methods=["GET", "POST"])
|
||||
@bp.route("/app/new/<int:server_id>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def app_new():
|
||||
"""Create a new application with comprehensive error handling"""
|
||||
# Get all servers for dropdown
|
||||
def app_new(server_id=None):
|
||||
"""Create a new application"""
|
||||
servers = Server.query.all()
|
||||
|
||||
if not servers:
|
||||
flash("You need to create a server before adding applications", "warning")
|
||||
return redirect(url_for("dashboard.server_new"))
|
||||
|
||||
if request.method == "POST":
|
||||
# Get form data
|
||||
name = request.form.get("name", "").strip()
|
||||
# Handle form submission
|
||||
name = request.form.get("name")
|
||||
server_id = request.form.get("server_id")
|
||||
documentation = request.form.get("documentation", "")
|
||||
|
||||
# Process port data from form
|
||||
port_data = []
|
||||
port_numbers = request.form.getlist("port_numbers[]")
|
||||
protocols = request.form.getlist("protocols[]")
|
||||
descriptions = request.form.getlist("port_descriptions[]")
|
||||
if not name or not server_id:
|
||||
flash("Name and server are required", "danger")
|
||||
return render_template(
|
||||
"dashboard/app_form.html",
|
||||
title="New Application",
|
||||
edit_mode=False,
|
||||
servers=servers,
|
||||
selected_server_id=server_id,
|
||||
)
|
||||
|
||||
for i in range(len(port_numbers)):
|
||||
if port_numbers[i] and port_numbers[i].strip():
|
||||
protocol = protocols[i] if i < len(protocols) else "TCP"
|
||||
description = descriptions[i] if i < len(descriptions) else ""
|
||||
port_data.append((port_numbers[i], protocol, description))
|
||||
# Create the app
|
||||
app = App(name=name, server_id=server_id, documentation=documentation)
|
||||
|
||||
# Save application
|
||||
from app.utils.app_utils import save_app
|
||||
try:
|
||||
db.session.add(app)
|
||||
db.session.commit()
|
||||
|
||||
success, app, error = save_app(name, server_id, documentation, port_data)
|
||||
# Process port numbers if any
|
||||
port_numbers = request.form.getlist("port_numbers[]")
|
||||
protocols = request.form.getlist("protocols[]")
|
||||
descriptions = request.form.getlist("port_descriptions[]")
|
||||
|
||||
if success:
|
||||
flash("Application created successfully", "success")
|
||||
for i, port_number in enumerate(port_numbers):
|
||||
if port_number and port_number.isdigit():
|
||||
port = Port(
|
||||
app_id=app.id,
|
||||
port_number=int(port_number),
|
||||
protocol=protocols[i] if i < len(protocols) else "TCP",
|
||||
description=descriptions[i] if i < len(descriptions) else "",
|
||||
)
|
||||
db.session.add(port)
|
||||
|
||||
db.session.commit()
|
||||
flash(f"Application '{name}' created successfully", "success")
|
||||
return redirect(url_for("dashboard.app_view", app_id=app.id))
|
||||
else:
|
||||
flash(error, "danger")
|
||||
|
||||
# For GET requests or failed POSTs
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash(f"Error creating application: {str(e)}", "danger")
|
||||
|
||||
# GET request - render the form
|
||||
return render_template(
|
||||
"dashboard/app_form.html",
|
||||
title="Create New Application",
|
||||
title="New Application",
|
||||
edit_mode=False,
|
||||
dashboard_link=url_for("dashboard.dashboard_home"),
|
||||
servers=servers,
|
||||
selected_server_id=server_id, # This will pre-select the server
|
||||
)
|
||||
|
||||
|
||||
|
|
Binary file not shown.
|
@ -58,7 +58,8 @@
|
|||
<select class="form-select" name="server_id" required>
|
||||
<option value="">Select a server</option>
|
||||
{% for server in servers %}
|
||||
<option value="{{ server.id }}" {% if app and server.id==app.server_id %}selected{% endif %}>
|
||||
<option value="{{ server.id }}" {% if app and server.id==app.server_id %}selected {% elif selected_server_id
|
||||
and server.id|string==selected_server_id|string %}selected {% endif %}>
|
||||
{{ server.hostname }} ({{ server.ip_address }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-xl">
|
||||
<div class="page-header d-print-none">
|
||||
|
@ -12,13 +14,16 @@
|
|||
{{ app.name }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-auto ms-auto">
|
||||
<div class="col-auto ms-auto d-print-none">
|
||||
<div class="btn-list">
|
||||
<a href="{{ url_for('dashboard.app_edit', app_id=app.id) }}" class="btn btn-primary">
|
||||
<span class="ti ti-edit me-2"></span>Edit Application
|
||||
<a href="{{ url_for('dashboard.app_edit', app_id=app.id) }}" class="btn btn-primary d-none d-sm-inline-block">
|
||||
<span class="ti ti-edit"></span>
|
||||
Edit Application
|
||||
</a>
|
||||
<button class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteAppModal">
|
||||
<span class="ti ti-trash me-2"></span>Delete
|
||||
<button class="btn btn-danger d-none d-sm-inline-block" data-bs-toggle="modal"
|
||||
data-bs-target="#deleteAppModal">
|
||||
<span class="ti ti-trash"></span>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -180,7 +185,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirmation Modal for Port Deletion -->
|
||||
<!-- Delete Port Modal -->
|
||||
<div class="modal modal-blur fade" id="deletePortModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-sm modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
|
@ -342,7 +347,9 @@
|
|||
border-left-color: #d73a49;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
// Store app ID for JavaScript use
|
||||
const appId = {{ app.id }};
|
||||
|
@ -365,6 +372,9 @@
|
|||
.then(response => response.json())
|
||||
.then(data => {
|
||||
document.querySelector('#addPortForm input[name="port_number"]').value = data.port;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching random port:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Binary file not shown.
38
app/utils/validators.py
Normal file
38
app/utils/validators.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
def validate_app_data(name, server_id, existing_app_id=None):
|
||||
"""
|
||||
Validate application data, checking for required fields and uniqueness
|
||||
|
||||
Args:
|
||||
name: The application name
|
||||
server_id: The server ID
|
||||
existing_app_id: The ID of the application being edited (if any)
|
||||
|
||||
Returns:
|
||||
tuple: (valid, error_message)
|
||||
"""
|
||||
# Check for required fields
|
||||
if not name or not name.strip():
|
||||
return False, "Application name is required"
|
||||
|
||||
if not server_id:
|
||||
return False, "Server is required"
|
||||
|
||||
# Check name length
|
||||
if len(name) < 2:
|
||||
return False, "Application name must be at least 2 characters"
|
||||
|
||||
# Check for uniqueness of name on the server
|
||||
from app.models import App
|
||||
|
||||
query = App.query.filter(App.name == name, App.server_id == server_id)
|
||||
|
||||
# If editing an existing app, exclude it from the uniqueness check
|
||||
if existing_app_id:
|
||||
query = query.filter(App.id != existing_app_id)
|
||||
|
||||
existing_app = query.first()
|
||||
|
||||
if existing_app:
|
||||
return False, f"Application '{name}' already exists on this server"
|
||||
|
||||
return True, None
|
BIN
instance/app.db
BIN
instance/app.db
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue