wip
This commit is contained in:
parent
5a309a0f6d
commit
1ab129b798
5 changed files with 499 additions and 107 deletions
|
@ -22,51 +22,122 @@ def validate_app_data(name, server_id, existing_app_id=None):
|
|||
if query.first():
|
||||
return False, f"Application '{name}' already exists on this server"
|
||||
|
||||
# Find similar app names on this server for suggestion
|
||||
similar_apps = App.query.filter(
|
||||
App.name.ilike(f"%{name}%"),
|
||||
App.server_id == server_id
|
||||
).limit(3).all()
|
||||
|
||||
if similar_apps and (not existing_app_id or not any(app.id == existing_app_id for app in similar_apps)):
|
||||
similar_names = ", ".join([f"'{app.name}'" for app in similar_apps])
|
||||
return False, f"Similar application names found on this server: {similar_names}"
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def validate_port_data(port_number, protocol, description=None):
|
||||
def is_port_in_use(port_number, protocol, server_id, exclude_app_id=None):
|
||||
"""
|
||||
Check if a port+protocol combination is already in use by any application on the server
|
||||
|
||||
Args:
|
||||
port_number: The port number to check
|
||||
protocol: The protocol (TCP, UDP, etc.)
|
||||
server_id: The server ID
|
||||
exclude_app_id: Optional app ID to exclude from the check (for edit operations)
|
||||
|
||||
Returns:
|
||||
Tuple (bool, app_name): Is port in use and by which app
|
||||
"""
|
||||
from sqlalchemy import and_
|
||||
|
||||
# Join App and Port models to find ports used by apps on this server
|
||||
query = db.session.query(Port, App).join(App).filter(
|
||||
Port.port_number == port_number,
|
||||
Port.protocol == protocol,
|
||||
App.server_id == server_id
|
||||
)
|
||||
|
||||
# Exclude the current app if editing
|
||||
if exclude_app_id:
|
||||
query = query.filter(App.id != exclude_app_id)
|
||||
|
||||
result = query.first()
|
||||
|
||||
if result:
|
||||
return True, result.App.name
|
||||
return False, None
|
||||
|
||||
|
||||
def validate_port_data(port_number, protocol, description, server_id=None, app_id=None):
|
||||
"""
|
||||
Validate port data
|
||||
Returns tuple (valid, clean_port_number, error_message)
|
||||
Returns tuple (valid, clean_port, error_message)
|
||||
"""
|
||||
# Clean and validate port number
|
||||
# Existing validation
|
||||
if not port_number:
|
||||
return False, None, "Port number is required"
|
||||
|
||||
try:
|
||||
port = int(port_number)
|
||||
if port < 1 or port > 65535:
|
||||
return False, None, "Port must be between 1 and 65535"
|
||||
except (ValueError, TypeError):
|
||||
return False, None, "Invalid port number"
|
||||
clean_port = int(port_number)
|
||||
except ValueError:
|
||||
return False, None, "Port number must be a valid integer"
|
||||
|
||||
# Validate protocol
|
||||
valid_protocols = ["TCP", "UDP", "SCTP", "OTHER"]
|
||||
if protocol not in valid_protocols:
|
||||
return False, None, f"Protocol must be one of: {', '.join(valid_protocols)}"
|
||||
if clean_port < 1 or clean_port > 65535:
|
||||
return False, None, "Port number must be between 1 and 65535"
|
||||
|
||||
return True, port, None
|
||||
# Add server-side check for conflicts if server_id is provided
|
||||
if server_id:
|
||||
in_use, app_name = is_port_in_use(clean_port, protocol, server_id, app_id)
|
||||
if in_use:
|
||||
return False, clean_port, f"Port {clean_port}/{protocol} is already in use by application '{app_name}'"
|
||||
|
||||
return True, clean_port, None
|
||||
|
||||
|
||||
def process_app_ports(app_id, port_data):
|
||||
def process_app_ports(app_id, port_data, server_id=None):
|
||||
"""
|
||||
Process port data for an application
|
||||
port_data should be a list of tuples (port_number, protocol, description)
|
||||
Returns (success, error_message)
|
||||
"""
|
||||
# Get the app's server_id if not provided
|
||||
if not server_id and app_id:
|
||||
app = App.query.get(app_id)
|
||||
if app:
|
||||
server_id = app.server_id
|
||||
|
||||
# Track the port+protocol combinations we've seen to avoid duplicates
|
||||
seen_ports = set()
|
||||
|
||||
try:
|
||||
for port_number, protocol, description in port_data:
|
||||
# Skip empty port entries
|
||||
if not port_number or not port_number.strip():
|
||||
continue
|
||||
|
||||
port_key = f"{port_number}/{protocol}"
|
||||
|
||||
# Check for duplicates within this form submission
|
||||
if port_key in seen_ports:
|
||||
return False, f"Duplicate port: {port_key} is specified multiple times"
|
||||
|
||||
seen_ports.add(port_key)
|
||||
|
||||
# Validate the port data
|
||||
valid, clean_port, error = validate_port_data(
|
||||
port_number, protocol, description
|
||||
port_number, protocol, description, server_id, app_id
|
||||
)
|
||||
|
||||
if not valid:
|
||||
continue # Skip invalid ports
|
||||
return False, error
|
||||
|
||||
# Check if port already exists
|
||||
# Check if port already exists for this app
|
||||
existing_port = Port.query.filter_by(
|
||||
app_id=app_id, port_number=clean_port
|
||||
app_id=app_id, port_number=clean_port, protocol=protocol
|
||||
).first()
|
||||
|
||||
if existing_port:
|
||||
# Update existing port
|
||||
existing_port.protocol = protocol
|
||||
existing_port.description = description
|
||||
else:
|
||||
# Create new port
|
||||
|
@ -84,46 +155,49 @@ def process_app_ports(app_id, port_data):
|
|||
return False, str(e)
|
||||
|
||||
|
||||
def save_app(name, server_id, documentation, port_data=None, existing_app_id=None):
|
||||
def save_app(name, server_id, documentation, port_data, app_id=None, url=None):
|
||||
"""
|
||||
Save or update an application and its ports
|
||||
Returns tuple (success, app_object_or_none, error_message)
|
||||
Save or update an application
|
||||
Returns (success, app, error_message)
|
||||
"""
|
||||
try:
|
||||
# Validate input data
|
||||
valid, error = validate_app_data(name, server_id, existing_app_id)
|
||||
# Validate application data
|
||||
valid, error = validate_app_data(name, server_id, app_id)
|
||||
if not valid:
|
||||
return False, None, error
|
||||
|
||||
# Create or update app
|
||||
if existing_app_id:
|
||||
if app_id:
|
||||
# Update existing app
|
||||
app = App.query.get(existing_app_id)
|
||||
app = App.query.get(app_id)
|
||||
if not app:
|
||||
return False, None, f"Application with ID {existing_app_id} not found"
|
||||
|
||||
return False, None, "Application not found"
|
||||
app.name = name
|
||||
app.server_id = server_id
|
||||
app.documentation = documentation
|
||||
app.url = url
|
||||
else:
|
||||
# Create new app
|
||||
app = App(name=name, server_id=server_id, documentation=documentation)
|
||||
app = App(
|
||||
name=name,
|
||||
server_id=server_id,
|
||||
documentation=documentation,
|
||||
url=url
|
||||
)
|
||||
db.session.add(app)
|
||||
db.session.flush() # Get the app ID without committing
|
||||
|
||||
# Flush to get the app ID if new
|
||||
db.session.flush()
|
||||
# Remove all existing ports if updating
|
||||
if app_id:
|
||||
Port.query.filter_by(app_id=app_id).delete()
|
||||
|
||||
# Process ports if provided
|
||||
if port_data:
|
||||
success, error = process_app_ports(app.id, port_data)
|
||||
if not success:
|
||||
db.session.rollback()
|
||||
return False, None, f"Error processing ports: {error}"
|
||||
# Process and save ports
|
||||
port_success, port_error = process_app_ports(app.id, port_data, server_id)
|
||||
if not port_success:
|
||||
db.session.rollback()
|
||||
return False, None, port_error
|
||||
|
||||
# Commit all changes
|
||||
db.session.commit()
|
||||
return True, app, None
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return False, None, str(e)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue