Files
SneakyScan/app/web/routes/main.py

324 lines
7.9 KiB
Python

"""
Main web routes for SneakyScanner.
Provides dashboard and scan viewing pages.
"""
import logging
from flask import Blueprint, current_app, redirect, render_template, url_for
from web.auth.decorators import login_required
logger = logging.getLogger(__name__)
bp = Blueprint('main', __name__)
@bp.route('/')
def index():
"""
Root route - redirect to dashboard.
Returns:
Redirect to dashboard
"""
return redirect(url_for('main.dashboard'))
@bp.route('/dashboard')
@login_required
def dashboard():
"""
Dashboard page - shows recent scans and statistics.
Returns:
Rendered dashboard template
"""
import os
# Get list of available config files
configs_dir = '/app/configs'
config_files = []
try:
if os.path.exists(configs_dir):
config_files = [f for f in os.listdir(configs_dir) if f.endswith(('.yaml', '.yml'))]
config_files.sort()
except Exception as e:
logger.error(f"Error listing config files: {e}")
return render_template('dashboard.html', config_files=config_files)
@bp.route('/scans')
@login_required
def scans():
"""
Scans list page - shows all scans with pagination.
Returns:
Rendered scans list template
"""
import os
# Get list of available config files
configs_dir = '/app/configs'
config_files = []
try:
if os.path.exists(configs_dir):
config_files = [f for f in os.listdir(configs_dir) if f.endswith(('.yaml', '.yml'))]
config_files.sort()
except Exception as e:
logger.error(f"Error listing config files: {e}")
return render_template('scans.html', config_files=config_files)
@bp.route('/scans/<int:scan_id>')
@login_required
def scan_detail(scan_id):
"""
Scan detail page - shows full scan results.
Args:
scan_id: Scan ID to display
Returns:
Rendered scan detail template
"""
# TODO: Phase 5 - Implement scan detail page
return render_template('scan_detail.html', scan_id=scan_id)
@bp.route('/scans/<int:scan_id1>/compare/<int:scan_id2>')
@login_required
def compare_scans(scan_id1, scan_id2):
"""
Scan comparison page - shows differences between two scans.
Args:
scan_id1: First (older) scan ID
scan_id2: Second (newer) scan ID
Returns:
Rendered comparison template
"""
return render_template('scan_compare.html', scan_id1=scan_id1, scan_id2=scan_id2)
@bp.route('/schedules')
@login_required
def schedules():
"""
Schedules list page - shows all scheduled scans.
Returns:
Rendered schedules list template
"""
return render_template('schedules.html')
@bp.route('/schedules/create')
@login_required
def create_schedule():
"""
Create new schedule form page.
Returns:
Rendered schedule create template with available config files
"""
import os
# Get list of available config files
configs_dir = '/app/configs'
config_files = []
try:
if os.path.exists(configs_dir):
config_files = [f for f in os.listdir(configs_dir) if f.endswith('.yaml')]
config_files.sort()
except Exception as e:
logger.error(f"Error listing config files: {e}")
return render_template('schedule_create.html', config_files=config_files)
@bp.route('/schedules/<int:schedule_id>/edit')
@login_required
def edit_schedule(schedule_id):
"""
Edit existing schedule form page.
Args:
schedule_id: Schedule ID to edit
Returns:
Rendered schedule edit template
"""
from flask import flash
# Note: Schedule data is loaded via AJAX in the template
# This just renders the page with the schedule_id in the URL
return render_template('schedule_edit.html', schedule_id=schedule_id)
@bp.route('/configs')
@login_required
def configs():
"""
Configuration files list page - shows all config files.
Returns:
Rendered configs list template
"""
return render_template('configs.html')
@bp.route('/configs/upload')
@login_required
def upload_config():
"""
Config upload page - allows CIDR/YAML upload.
Returns:
Rendered config upload template
"""
return render_template('config_upload.html')
@bp.route('/configs/edit/<filename>')
@login_required
def edit_config(filename):
"""
Config edit page - allows editing YAML configuration.
Args:
filename: Config filename to edit
Returns:
Rendered config edit template
"""
from web.services.config_service import ConfigService
from flask import flash, redirect
try:
config_service = ConfigService()
config_data = config_service.get_config(filename)
return render_template(
'config_edit.html',
filename=config_data['filename'],
content=config_data['content']
)
except FileNotFoundError:
flash(f"Config file '{filename}' not found", 'error')
return redirect(url_for('main.configs'))
except Exception as e:
logger.error(f"Error loading config for edit: {e}")
flash(f"Error loading config: {str(e)}", 'error')
return redirect(url_for('main.configs'))
@bp.route('/alerts')
@login_required
def alerts():
"""
Alerts history page - shows all alerts.
Returns:
Rendered alerts template
"""
from flask import request, current_app
from web.models import Alert, AlertRule, Scan
from web.utils.pagination import paginate
# Get query parameters for filtering
page = request.args.get('page', 1, type=int)
per_page = 20
severity = request.args.get('severity')
alert_type = request.args.get('alert_type')
acknowledged = request.args.get('acknowledged')
# Build query
query = current_app.db_session.query(Alert).join(Scan, isouter=True)
# Apply filters
if severity:
query = query.filter(Alert.severity == severity)
if alert_type:
query = query.filter(Alert.alert_type == alert_type)
if acknowledged is not None:
ack_bool = acknowledged == 'true'
query = query.filter(Alert.acknowledged == ack_bool)
# Order by severity and date
query = query.order_by(Alert.severity.desc(), Alert.created_at.desc())
# Paginate using utility function
pagination = paginate(query, page=page, per_page=per_page)
alerts = pagination.items
# Get unique alert types for filter dropdown
try:
alert_types = current_app.db_session.query(Alert.alert_type).distinct().all()
alert_types = [at[0] for at in alert_types] if alert_types else []
except Exception:
alert_types = []
return render_template(
'alerts.html',
alerts=alerts,
pagination=pagination,
current_severity=severity,
current_alert_type=alert_type,
current_acknowledged=acknowledged,
alert_types=alert_types
)
@bp.route('/alerts/rules')
@login_required
def alert_rules():
"""
Alert rules management page.
Returns:
Rendered alert rules template
"""
import os
from flask import current_app
from web.models import AlertRule
# Get all alert rules with error handling
try:
rules = current_app.db_session.query(AlertRule).order_by(
AlertRule.name.nullslast(),
AlertRule.rule_type
).all()
except Exception as e:
logger.error(f"Error fetching alert rules: {e}")
rules = []
# Ensure rules is always a list
if rules is None:
rules = []
# Get list of available config files
configs_dir = '/app/configs'
config_files = []
try:
if os.path.exists(configs_dir):
config_files = [f for f in os.listdir(configs_dir) if f.endswith(('.yaml', '.yml'))]
config_files.sort()
except Exception as e:
logger.error(f"Error listing config files: {e}")
return render_template(
'alert_rules.html',
rules=rules,
config_files=config_files
)