"""Editable troubleshooting rules API.""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException from app.core.deps import require_admin, require_readonly from app.models.user import User from app.schemas import TroubleshootingRulesOut, TroubleshootingRulesUpdate from app.services.audit import record_audit from app.services.troubleshooting_rules import ( load_rules, match_rule, read_rules_raw, save_rules_raw, ) from app.database import get_db from sqlalchemy.ext.asyncio import AsyncSession router = APIRouter(prefix="/api/rules", tags=["rules"]) @router.get("/troubleshooting", response_model=TroubleshootingRulesOut) async def get_troubleshooting_rules(_: User = Depends(require_readonly)): data = load_rules() # mtime-cached; reloads only when the file changes return TroubleshootingRulesOut( yaml=read_rules_raw(), parsed=data, path_hint="rules/troubleshooting.yaml", ) @router.put("/troubleshooting", response_model=TroubleshootingRulesOut) async def update_troubleshooting_rules( body: TroubleshootingRulesUpdate, db: AsyncSession = Depends(get_db), user: User = Depends(require_admin), ): try: parsed = save_rules_raw(body.yaml) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc except Exception as exc: # noqa: BLE001 raise HTTPException(status_code=400, detail=f"Invalid rules: {exc}") from exc await record_audit( db, user, "rules.troubleshooting.update", "rules", 0, {"bytes": len(body.yaml)}, ) return TroubleshootingRulesOut( yaml=read_rules_raw(), parsed=parsed, path_hint="rules/troubleshooting.yaml", ) @router.post("/troubleshooting/preview") async def preview_rule_match( body: dict, _: User = Depends(require_readonly), ): """Preview which rule and devices match a title + issue (for the Rules UI).""" title = str(body.get("title") or "") issue = str(body.get("issue") or "") matched = match_rule(title, issue) return {"matched": matched.as_dict() if matched else None}