from datetime import UTC, datetime, timedelta from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.orm import Session from app.auth.dependencies import get_current_user, require_role from app.db.session import get_db from app.models import AlertRule, Incident, User from app.schemas.core import AlertRuleCreate, AlertRuleRead, AlertRuleUpdate, IncidentRead router = APIRouter(tags=["alerts"]) @router.get("/alerts/rules", response_model=list[AlertRuleRead]) def list_alert_rules(_: User = Depends(get_current_user), db: Session = Depends(get_db)) -> list[AlertRule]: return list(db.scalars(select(AlertRule).order_by(AlertRule.name)).all()) @router.post("/alerts/rules", response_model=AlertRuleRead) def create_alert_rule( payload: AlertRuleCreate, _: User = Depends(require_role("admin")), db: Session = Depends(get_db), ) -> AlertRule: rule = AlertRule(**payload.model_dump()) db.add(rule) db.commit() db.refresh(rule) return rule @router.patch("/alerts/rules/{rule_id}", response_model=AlertRuleRead) def update_alert_rule( rule_id: int, payload: AlertRuleUpdate, _: User = Depends(require_role("admin")), db: Session = Depends(get_db), ) -> AlertRule: rule = db.get(AlertRule, rule_id) if rule is None: raise HTTPException(status_code=404, detail="Alert rule not found") for field, value in payload.model_dump(exclude_unset=True).items(): setattr(rule, field, value) db.commit() db.refresh(rule) return rule @router.delete("/alerts/rules/{rule_id}", status_code=204) def delete_alert_rule( rule_id: int, _: User = Depends(require_role("admin")), db: Session = Depends(get_db), ) -> None: rule = db.get(AlertRule, rule_id) if rule is None: raise HTTPException(status_code=404, detail="Alert rule not found") db.delete(rule) db.commit() @router.get("/incidents", response_model=list[IncidentRead]) def list_incidents(_: User = Depends(get_current_user), db: Session = Depends(get_db)) -> list[Incident]: return list(db.scalars(select(Incident).order_by(Incident.opened_at.desc())).all()) @router.get("/incidents/{incident_id}", response_model=IncidentRead) def get_incident(incident_id: int, _: User = Depends(get_current_user), db: Session = Depends(get_db)) -> Incident: incident = db.get(Incident, incident_id) if incident is None: raise HTTPException(status_code=404, detail="Incident not found") return incident @router.post("/incidents/{incident_id}/acknowledge", response_model=IncidentRead) def acknowledge_incident( incident_id: int, _: User = Depends(require_role("operator")), db: Session = Depends(get_db), ) -> Incident: incident = db.get(Incident, incident_id) if incident is None: raise HTTPException(status_code=404, detail="Incident not found") incident.acknowledged_at = datetime.now(UTC) db.commit() db.refresh(incident) return incident @router.post("/incidents/{incident_id}/silence", response_model=IncidentRead) def silence_incident( incident_id: int, minutes: int = 60, _: User = Depends(require_role("operator")), db: Session = Depends(get_db), ) -> Incident: incident = db.get(Incident, incident_id) if incident is None: raise HTTPException(status_code=404, detail="Incident not found") incident.silenced_until = datetime.now(UTC) + timedelta(minutes=minutes) db.commit() db.refresh(incident) return incident