Files
gh-christianlouis-dmarq/backend/app/api/api_v1/endpoints/health.py
T
2026-05-23 11:37:10 +02:00

119 lines
3.9 KiB
Python

from fastapi import APIRouter, Depends
from sqlalchemy import func, text
from sqlalchemy.orm import Session
from app.api.api_v1.endpoints.setup import setup_status
from app.core.database import get_db
from app.core.security import require_admin_auth
from app.models.mail_source import MailSource
from app.models.mail_source_import import MailSourceImport
from app.models.report import DMARCReport
from app.services.runtime_status import get_scheduler_status
router = APIRouter()
@router.get("/health", status_code=200)
async def health_check():
"""
Health check endpoint to verify API status.
For Milestone 1, this simply returns status information without checking a database.
"""
return {
"status": "ok",
"version": "0.1.0",
"service": "dmarq",
"is_setup_complete": setup_status["is_setup_complete"],
}
def _iso(value):
return value.isoformat() if value else None
@router.get("/health/operations", status_code=200)
async def operations_health(
db: Session = Depends(get_db),
_auth: dict = Depends(require_admin_auth),
):
"""Return operational health details for the web health page."""
database = {"ok": True, "detail": "Connected"}
try:
db.execute(text("SELECT 1"))
except Exception as exc: # pylint: disable=broad-exception-caught
database = {"ok": False, "detail": str(exc)}
enabled_sources = 0
total_sources = 0
report_count = 0
latest_report = None
latest_import = None
latest_successful_import = None
if database["ok"]:
enabled_sources = (
db.query(func.count(MailSource.id))
.filter(MailSource.enabled == True) # noqa: E712
.scalar()
)
total_sources = db.query(func.count(MailSource.id)).scalar()
report_count = db.query(func.count(DMARCReport.id)).scalar()
latest_report = db.query(func.max(DMARCReport.processed_at)).scalar()
latest_import = (
db.query(MailSourceImport)
.order_by(MailSourceImport.finished_at.desc(), MailSourceImport.id.desc())
.first()
)
latest_successful_import = (
db.query(MailSourceImport)
.filter(MailSourceImport.status.in_(["success", "warning"]))
.order_by(MailSourceImport.finished_at.desc(), MailSourceImport.id.desc())
.first()
)
scheduler = get_scheduler_status()
status = "ok"
checks = []
if not database["ok"]:
status = "degraded"
checks.append("Database connectivity failed.")
if total_sources and enabled_sources == 0:
status = "degraded"
checks.append("All mail sources are disabled.")
if scheduler.get("last_error"):
status = "degraded"
checks.append("The scheduler reported a recent error.")
return {
"status": status,
"service": "dmarq",
"database": database,
"scheduler": {
**scheduler,
"enabled_sources": int(enabled_sources or 0),
"total_sources": int(total_sources or 0),
},
"imports": {
"latest": {
"status": latest_import.status,
"trigger": latest_import.trigger,
"reports_found": latest_import.reports_found,
"finished_at": _iso(latest_import.finished_at),
}
if latest_import
else None,
"latest_successful": {
"status": latest_successful_import.status,
"trigger": latest_successful_import.trigger,
"reports_found": latest_successful_import.reports_found,
"finished_at": _iso(latest_successful_import.finished_at),
}
if latest_successful_import
else None,
},
"reports": {
"count": int(report_count or 0),
"latest_processed_at": _iso(latest_report),
},
"checks": checks,
}