Initial commit
This commit is contained in:
+261
@@ -0,0 +1,261 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models import Alert, DailyStat, InboxStatus, LLMReport, Record, Report
|
||||
|
||||
|
||||
def _pct(pass_count: int, total: int) -> str:
|
||||
return f"{(pass_count / total * 100):.1f}%" if total else "0.0%"
|
||||
|
||||
|
||||
def _as_utc(value: datetime | str | None) -> datetime | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
value = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
except ValueError:
|
||||
return None
|
||||
if value.tzinfo is None:
|
||||
return value.replace(tzinfo=timezone.utc)
|
||||
return value
|
||||
|
||||
|
||||
def report_timestamp(report: Report) -> datetime:
|
||||
return _as_utc(report.date_end or report.date_begin or report.created_at) or datetime.now(timezone.utc)
|
||||
|
||||
|
||||
def _report_date_expr():
|
||||
return func.coalesce(Report.date_end, Report.date_begin, Report.created_at)
|
||||
|
||||
|
||||
def _display_day(value: date) -> str:
|
||||
return value.strftime("%d/%m/%Y")
|
||||
|
||||
|
||||
def report_bounds(session: Session, domain: str | None = None) -> tuple[datetime | None, datetime | None]:
|
||||
stmt = select(func.min(_report_date_expr()), func.max(_report_date_expr()))
|
||||
if domain:
|
||||
stmt = stmt.where(Report.domain == domain)
|
||||
start, end = session.execute(stmt).one()
|
||||
return _as_utc(start), _as_utc(end)
|
||||
|
||||
|
||||
def resolve_date_range(
|
||||
session: Session,
|
||||
*,
|
||||
period: str = "all",
|
||||
domain: str | None = None,
|
||||
date_from: str | None = None,
|
||||
date_to: str | None = None,
|
||||
) -> tuple[datetime | None, datetime | None, str]:
|
||||
first, latest = report_bounds(session, domain)
|
||||
if not latest:
|
||||
return None, None, "No reports"
|
||||
if period == "custom":
|
||||
start = _as_utc(f"{date_from}T00:00:00+00:00") if date_from else first
|
||||
end = _as_utc(f"{date_to}T23:59:59+00:00") if date_to else latest
|
||||
return start, end, "Custom range"
|
||||
if period == "24h":
|
||||
return latest - timedelta(days=1), latest, "Latest 24h"
|
||||
if period == "7d":
|
||||
return latest - timedelta(days=7), latest, "Latest 7 days"
|
||||
if period == "30d":
|
||||
return latest - timedelta(days=30), latest, "Latest 30 days"
|
||||
if period == "365d":
|
||||
return latest - timedelta(days=365), latest, "Latest year"
|
||||
return first, latest, "All imported reports"
|
||||
|
||||
|
||||
def _range_filter(stmt, start: datetime | None, end: datetime | None):
|
||||
expr = _report_date_expr()
|
||||
if start:
|
||||
stmt = stmt.where(expr >= start)
|
||||
if end:
|
||||
stmt = stmt.where(expr <= end)
|
||||
return stmt
|
||||
|
||||
|
||||
def latest_summary(session: Session, domain: str | None = None) -> str:
|
||||
posture_stmt = select(LLMReport).where(LLMReport.report_type == "posture").order_by(LLMReport.created_at.desc(), LLMReport.period_end.desc())
|
||||
if domain:
|
||||
posture_stmt = posture_stmt.where(LLMReport.domain == domain)
|
||||
else:
|
||||
posture_stmt = posture_stmt.where(LLMReport.domain == "__all__")
|
||||
report = session.scalar(posture_stmt.limit(1))
|
||||
if not report:
|
||||
stmt = select(LLMReport).where(LLMReport.report_type == "daily").order_by(LLMReport.period_end.desc(), LLMReport.created_at.desc())
|
||||
if domain:
|
||||
stmt = stmt.where(LLMReport.domain == domain)
|
||||
else:
|
||||
stmt = stmt.where(LLMReport.domain == "__all__")
|
||||
report = session.scalar(stmt.limit(1))
|
||||
if report:
|
||||
return report.plain_text
|
||||
if domain:
|
||||
return "No domain posture digest has been generated yet."
|
||||
return "No portfolio posture digest has been generated yet."
|
||||
|
||||
|
||||
def homepage_summary(
|
||||
session: Session,
|
||||
*,
|
||||
period: str = "all",
|
||||
domain: str | None = None,
|
||||
date_from: str | None = None,
|
||||
date_to: str | None = None,
|
||||
) -> dict:
|
||||
start, end, scope_label = resolve_date_range(session, period=period, domain=domain, date_from=date_from, date_to=date_to)
|
||||
domains = session.scalar(select(func.count(func.distinct(Report.domain)))) or 0
|
||||
reports_stmt = select(func.count(Report.id))
|
||||
records_stmt = select(Record).join(Report)
|
||||
unknown_stmt = select(func.count(func.distinct(Record.source_ip))).join(Report).where(Record.is_known_sender.is_(False))
|
||||
if domain:
|
||||
reports_stmt = reports_stmt.where(Report.domain == domain)
|
||||
records_stmt = records_stmt.where(Report.domain == domain)
|
||||
unknown_stmt = unknown_stmt.where(Report.domain == domain)
|
||||
reports_stmt = _range_filter(reports_stmt, start, end)
|
||||
records_stmt = _range_filter(records_stmt, start, end)
|
||||
unknown_stmt = _range_filter(unknown_stmt, start, end)
|
||||
reports_in_range = session.scalar(reports_stmt) or 0
|
||||
records = session.execute(records_stmt).scalars().all()
|
||||
messages_in_range = sum(row.count for row in records)
|
||||
pass_count = sum(row.count for row in records if row.dmarc_pass)
|
||||
critical = session.scalar(select(func.count(Alert.id)).where(Alert.status == "open", Alert.severity == "critical")) or 0
|
||||
warnings = session.scalar(select(func.count(Alert.id)).where(Alert.status == "open", Alert.severity == "warning")) or 0
|
||||
unknown_sources = session.scalar(unknown_stmt) or 0
|
||||
last_check = session.scalar(select(func.max(InboxStatus.last_success_at)))
|
||||
return {
|
||||
"status": "critical" if critical else "warning" if warnings else "ok",
|
||||
"domains": domains,
|
||||
"reports_today": reports_in_range,
|
||||
"messages_today": messages_in_range,
|
||||
"dmarc_pass_count": pass_count,
|
||||
"dmarc_fail_count": messages_in_range - pass_count,
|
||||
"dmarc_pass_rate": _pct(pass_count, messages_in_range),
|
||||
"dmarc_pass_rate_value": round(pass_count / messages_in_range * 100, 1) if messages_in_range else None,
|
||||
"critical_alerts": critical,
|
||||
"warnings": warnings,
|
||||
"unknown_sources": unknown_sources,
|
||||
"last_check": last_check.isoformat() if last_check else None,
|
||||
"report_day": None,
|
||||
"scope_label": scope_label,
|
||||
"scope_start": start.isoformat() if start else None,
|
||||
"scope_end": end.isoformat() if end else None,
|
||||
"summary": latest_summary(session, domain),
|
||||
}
|
||||
|
||||
|
||||
def domain_metrics(session: Session, domain: str) -> dict:
|
||||
records = session.execute(select(Record).join(Report).where(Report.domain == domain)).scalars().all()
|
||||
total = sum(row.count for row in records)
|
||||
dmarc_pass = sum(row.count for row in records if row.dmarc_pass)
|
||||
spf_aligned = sum(row.count for row in records if row.spf_aligned)
|
||||
dkim_aligned = sum(row.count for row in records if row.dkim_aligned)
|
||||
return {
|
||||
"messages": total,
|
||||
"dmarc_pass": dmarc_pass,
|
||||
"dmarc_fail": total - dmarc_pass,
|
||||
"pass_rate": _pct(dmarc_pass, total),
|
||||
"spf_aligned": spf_aligned,
|
||||
"spf_rate": _pct(spf_aligned, total),
|
||||
"dkim_aligned": dkim_aligned,
|
||||
"dkim_rate": _pct(dkim_aligned, total),
|
||||
"unknown_sources": len({row.source_ip for row in records if not row.is_known_sender}),
|
||||
}
|
||||
|
||||
|
||||
def traffic_distribution(
|
||||
session: Session,
|
||||
*,
|
||||
period: str = "all",
|
||||
domain: str | None = None,
|
||||
date_from: str | None = None,
|
||||
date_to: str | None = None,
|
||||
buckets: int | None = None,
|
||||
) -> list[dict]:
|
||||
start, now, _ = resolve_date_range(session, period=period, domain=domain, date_from=date_from, date_to=date_to)
|
||||
if not start or not now:
|
||||
return []
|
||||
default_buckets = {"24h": 12, "7d": 7, "30d": 10, "365d": 12}.get(period, 14)
|
||||
bucket_count = buckets or default_buckets
|
||||
duration = (now - start).total_seconds()
|
||||
bucket_seconds = max(1, duration / bucket_count)
|
||||
rows = []
|
||||
stmt = select(Record, Report).join(Report).where(_report_date_expr() >= start, _report_date_expr() <= now)
|
||||
if domain:
|
||||
stmt = stmt.where(Report.domain == domain)
|
||||
for record, report in session.execute(stmt).all():
|
||||
created = report_timestamp(report)
|
||||
index = int((created - start).total_seconds() / bucket_seconds)
|
||||
index = min(bucket_count - 1, max(0, index))
|
||||
rows.append((index, record))
|
||||
|
||||
data = []
|
||||
for i in range(bucket_count):
|
||||
bucket_start = start + timedelta(seconds=bucket_seconds * i)
|
||||
bucket_end = start + timedelta(seconds=bucket_seconds * (i + 1))
|
||||
if i == bucket_count - 1:
|
||||
bucket_end = now
|
||||
start_day = bucket_start.date()
|
||||
end_day = bucket_end.date()
|
||||
if start_day == end_day:
|
||||
label = _display_day(start_day)
|
||||
else:
|
||||
label = f"{_display_day(start_day)} to {_display_day(end_day)}"
|
||||
data.append(
|
||||
{
|
||||
"label": label,
|
||||
"date_from": start_day.isoformat(),
|
||||
"date_to": end_day.isoformat(),
|
||||
"valid": 0,
|
||||
"failed": 0,
|
||||
"total": 0,
|
||||
}
|
||||
)
|
||||
for index, record in rows:
|
||||
key = "valid" if record.dmarc_pass else "failed"
|
||||
data[index][key] += record.count
|
||||
data[index]["total"] += record.count
|
||||
max_total = max([item["total"] for item in data] or [0])
|
||||
for item in data:
|
||||
item["height"] = round(item["total"] / max_total * 100) if max_total else 0
|
||||
item["failed_height"] = round(item["failed"] / item["total"] * item["height"]) if item["total"] else 0
|
||||
item["valid_height"] = max(0, item["height"] - item["failed_height"])
|
||||
return data
|
||||
|
||||
|
||||
def domain_homepage_summary(session: Session, domain: str) -> dict:
|
||||
latest = _as_utc(session.scalar(select(func.max(func.coalesce(Report.date_end, Report.date_begin, Report.created_at))).where(Report.domain == domain)))
|
||||
end = latest or datetime.now(timezone.utc)
|
||||
since = end - timedelta(days=1)
|
||||
records = session.execute(
|
||||
select(Record)
|
||||
.join(Report)
|
||||
.where(Report.domain == domain, func.coalesce(Report.date_end, Report.date_begin, Report.created_at) >= since, func.coalesce(Report.date_end, Report.date_begin, Report.created_at) <= end)
|
||||
).scalars().all()
|
||||
total = sum(row.count for row in records)
|
||||
passed = sum(row.count for row in records if row.dmarc_pass)
|
||||
failed = total - passed
|
||||
unknown = len({row.source_ip for row in records if not row.is_known_sender})
|
||||
critical = session.scalar(
|
||||
select(func.count(Alert.id)).where(Alert.status == "open", Alert.domain == domain, Alert.severity == "critical")
|
||||
) or 0
|
||||
warnings = session.scalar(
|
||||
select(func.count(Alert.id)).where(Alert.status == "open", Alert.domain == domain, Alert.severity == "warning")
|
||||
) or 0
|
||||
return {
|
||||
"status": "critical" if critical else "warning" if warnings else "ok",
|
||||
"domain": domain,
|
||||
"messages_24h": total,
|
||||
"pass_rate": _pct(passed, total),
|
||||
"failed": failed,
|
||||
"unknown_sources": unknown,
|
||||
"critical_alerts": critical,
|
||||
"warnings": warnings,
|
||||
"summary": latest_summary(session, domain),
|
||||
}
|
||||
Reference in New Issue
Block a user