Files
Infosec/Alert Hub System/app/routes_main.py
T

138 lines
5.4 KiB
Python
Raw Normal View History

2026-09-13 20:09:20 +01:00
"""Frontoffice routes — visible to all authenticated users.
Sections:
1. View Data (everyone)
2. Add/Remove/Edit (admins only)
"""
import json
from datetime import date
from flask import Blueprint, render_template, redirect, url_for, flash, request, abort
from flask_login import current_user
from .models import db, Alert, AppSetting, AuditLog
from .forms import AlertForm
from .auth_utils import admin_required, login_required_full, get_remote_ip
main_bp = Blueprint("main", __name__)
def _parse_days(text: str) -> list[int]:
return sorted({int(x.strip()) for x in text.split(",") if x.strip().isdigit() and int(x.strip()) >= 0}, reverse=True)
# --------------------------------------------------------------------------- #
# Section 1 — View Data
# --------------------------------------------------------------------------- #
@main_bp.route("/")
@login_required_full
def index():
today = date.today()
alerts = Alert.query.filter_by(is_active=True).order_by(Alert.expiration_date.asc()).all()
payload = []
for a in alerts:
days_left = (a.expiration_date - today).days
payload.append({
"id": a.id,
"title": a.title,
"category": a.category,
"description": a.description,
"expiration_date": a.expiration_date.isoformat(),
"days_left": days_left,
"reminder_days": a.reminder_days,
})
return render_template("index.html", alerts=payload, today_iso=today.isoformat())
# --------------------------------------------------------------------------- #
# Section 2 — Add / Remove / Edit
# --------------------------------------------------------------------------- #
@main_bp.route("/alerts")
@login_required_full
def alerts_list():
alerts = Alert.query.order_by(Alert.expiration_date.asc()).all()
return render_template("alerts_list.html", alerts=alerts)
@main_bp.route("/alerts/new", methods=["GET", "POST"])
@admin_required
def alert_new():
default_reminder_days = AppSetting.get("default_reminder_days", "30,14,7,1")
form = AlertForm(reminder_days=default_reminder_days)
if form.validate_on_submit():
days = _parse_days(form.reminder_days.data)
if not days:
flash("Reminder days must contain at least one valid number.", "error")
else:
alert = Alert(
title=form.title.data.strip(),
category=(form.category.data or "").strip() or None,
description=(form.description.data or "").strip() or None,
expiration_date=form.expiration_date.data,
is_active=form.is_active.data,
created_by=current_user.id,
)
alert.reminder_days = days
db.session.add(alert)
db.session.commit()
AuditLog.record("alert.create", user=current_user, details=f"id={alert.id} title={alert.title}", ip=get_remote_ip())
flash("Alert created.", "success")
return redirect(url_for("main.alerts_list"))
return render_template("alert_form.html", form=form, alert=None)
@main_bp.route("/alerts/<int:alert_id>/edit", methods=["GET", "POST"])
@admin_required
def alert_edit(alert_id: int):
alert = db.session.get(Alert, alert_id)
if alert is None:
abort(404)
form = AlertForm(obj=alert)
if request.method == "GET":
form.reminder_days.data = ",".join(str(d) for d in alert.reminder_days)
if form.validate_on_submit():
days = _parse_days(form.reminder_days.data)
if not days:
flash("Reminder days must contain at least one valid number.", "error")
else:
alert.title = form.title.data.strip()
alert.category = (form.category.data or "").strip() or None
alert.description = (form.description.data or "").strip() or None
alert.expiration_date = form.expiration_date.data
alert.is_active = form.is_active.data
# If reminder_days changed OR expiration_date changed, reset sent log
if days != alert.reminder_days:
alert.reminders_sent = []
alert.reminder_days = days
db.session.commit()
AuditLog.record("alert.update", user=current_user, details=f"id={alert.id}", ip=get_remote_ip())
flash("Alert updated.", "success")
return redirect(url_for("main.alerts_list"))
return render_template("alert_form.html", form=form, alert=alert)
@main_bp.route("/alerts/<int:alert_id>/delete", methods=["POST"])
@admin_required
def alert_delete(alert_id: int):
alert = db.session.get(Alert, alert_id)
if alert is None:
abort(404)
title = alert.title
db.session.delete(alert)
db.session.commit()
AuditLog.record("alert.delete", user=current_user, details=f"id={alert_id} title={title}", ip=get_remote_ip())
flash(f"Alert '{title}' deleted.", "success")
return redirect(url_for("main.alerts_list"))
@main_bp.route("/alerts/<int:alert_id>/reset-reminders", methods=["POST"])
@admin_required
def alert_reset_reminders(alert_id: int):
alert = db.session.get(Alert, alert_id)
if alert is None:
abort(404)
alert.reminders_sent = []
db.session.commit()
AuditLog.record("alert.reset_reminders", user=current_user, details=f"id={alert_id}", ip=get_remote_ip())
flash("Reminder send-history cleared. Pending reminders will be re-sent on next scheduler run.", "info")
return redirect(url_for("main.alerts_list"))