from __future__ import annotations from flask import Response, jsonify, render_template, request from app.blueprints.security import bp from app.blueprints.security.queries import ( format_blocklist, get_ip_history, get_sensitive_path_summary, get_suspicious_events, ) from app.extensions import db from app.models.blocklist_suggestion import BlocklistSuggestion from app.utils.dates import parse_date_range from app.utils.htmx import render_htmx_aware from app.utils.pagination import parse_pagination @bp.route("/security") def security(): from_date, to_date = parse_date_range(request) severity = request.args.get("severity") or "" rule_type = request.args.get("rule_type") or "" return render_htmx_aware( request, full_template="security/index.html", partial_template="security/_content.html", from_date=from_date, to_date=to_date, severity=severity, rule_type=rule_type, ) @bp.get("/api/security/events") def api_security_events(): from_date, to_date = parse_date_range(request) page, per_page = parse_pagination(request) severity = request.args.get("severity") or None rule_type = request.args.get("rule_type") or None rows, total = get_suspicious_events(from_date, to_date, severity, rule_type, page, per_page) return jsonify( data={"rows": rows, "total": total}, meta={"from": from_date.isoformat(), "to": to_date.isoformat(), "page": page, "per_page": per_page}, ) @bp.get("/api/security/sensitive-paths") def api_sensitive_paths(): from_date, to_date = parse_date_range(request) return jsonify(data=get_sensitive_path_summary(from_date, to_date), meta={"from": from_date.isoformat(), "to": to_date.isoformat()}) @bp.get("/api/security/ip/") def api_ip_history(ip: str): history = get_ip_history(ip) if history is None: return render_template("security/_ip_not_found.html", ip=ip), 404 return render_template("security/_ip_history.html", ip_data=history) @bp.get("/api/security/export-blocklist") def api_export_blocklist(): fmt = request.args.get("format", "plain") include_all = request.args.get("all", "false").lower() == "true" query = BlocklistSuggestion.query if not include_all: query = query.filter_by(exported=False) suggestions = query.order_by(BlocklistSuggestion.created_at).all() body = format_blocklist(suggestions, fmt) for s in suggestions: s.exported = True db.session.commit() return Response( body, mimetype="text/plain", headers={"Content-Disposition": "attachment; filename=kavosh-blocklist.txt"}, )