pryscraper/routers/monitoring.py
cryptorugmunch 9db3f26f95
Some checks failed
CI / lint (pull_request) Failing after 52s
CI / typecheck (pull_request) Failing after 56s
CI / Secret scan (gitleaks) (pull_request) Successful in 32s
CI / Security audit (bandit) (pull_request) Successful in 34s
CI / test (pull_request) Successful in 1m23s
feat(routers): split remaining 174 api.py routes into per-tag routers
- AST-extract all remaining routes from api.py into routers/*.py
- Group routes by OpenAPI tag (AI, Advanced, Agency, ..., x402)
- Keep existing auth/health/scraping/templates routers; add *_api.py for remaining routes of those tags
- Move shared helpers/models/variables with the routes that need them
- Update tests/test_api.py to import vision helpers from routers.vision
- Regenerate openapi.json (186 paths)
- All 497 tests pass; ruff clean
2026-07-03 03:36:58 +02:00

116 lines
3.7 KiB
Python

"""Pry — Monitoring router (remaining api.py routes).
Auto-extracted from api.py during the router-split refactor.
"""
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Rug Munch Media LLC
from __future__ import annotations
import logging
from datetime import UTC, datetime
from typing import Any
from fastapi import APIRouter, Body
from errors import NotFoundError
logger = logging.getLogger(__name__)
router = APIRouter(tags=["Monitoring"])
@router.post("/v1/monitor", tags=["Monitoring"], summary="Create a scheduled content monitor")
async def create_monitor_endpoint(
name: str = Body(...),
url: str = Body(...),
schedule_cron: str = Body("0 */6 * * *"),
goal: str = Body(""),
webhook: str = Body(""),
use_llm: bool = Body(False),
) -> dict[str, Any]:
"""Create a scheduled monitor that tracks content changes.
Args:
name: Human-readable monitor name
url: Target URL to monitor
schedule_cron: Cron expression (default: every 6 hours)
goal: Natural language goal for meaningful-change detection
webhook: URL to notify on meaningful changes
use_llm: Use LLM for change judging (slower but smarter)
"""
from monitor import create_monitor
monitor = await create_monitor(name, url, schedule_cron, goal, webhook, use_llm)
return {"success": True, "data": monitor}
@router.post("/v1/monitor/run", tags=["Monitoring"], summary="Run a single monitor check")
async def run_monitor_endpoint(
monitor_id: str = Body(...),
) -> dict[str, Any]:
"""Execute a monitor check immediately (outside of schedule)."""
from monitor import run_monitor
result = await run_monitor(monitor_id)
if "error" in result:
raise NotFoundError(result["error"])
return {"success": True, "data": result}
@router.get("/v1/monitors", tags=["Monitoring"], summary="List all monitors")
async def list_monitors_endpoint() -> dict[str, Any]:
"""List all registered monitors."""
from monitor import list_monitors
monitors = await list_monitors()
return {"success": True, "data": {"monitors": monitors, "total": len(monitors)}}
@router.delete("/v1/monitor/{monitor_id}", tags=["Monitoring"], summary="Delete a monitor")
async def delete_monitor_endpoint(monitor_id: str) -> dict[str, Any]:
"""Delete a monitor and its snapshots."""
from monitor import delete_monitor
success = await delete_monitor(monitor_id)
if not success:
raise NotFoundError(f"Monitor not found: {monitor_id}")
return {"success": True, "data": {"monitor_id": monitor_id, "deleted": True}}
@router.post("/v1/monitor/check", tags=["Monitoring"], summary="Run all due monitors")
async def run_due_monitors() -> dict[str, Any]:
"""Check all monitors and run any that are due based on their cron schedule."""
import croniter
from monitor import list_monitors, run_monitor
monitors = await list_monitors()
now = datetime.now(UTC)
results = []
for m in monitors:
last_run = m.get("last_run_at")
last_dt = datetime.fromisoformat(last_run) if last_run else datetime.min.replace(tzinfo=UTC)
try:
cron = croniter.croniter(m["schedule_cron"], last_dt)
next_run = cron.get_next(datetime)
if next_run <= now:
result = await run_monitor(m["id"])
results.append(result)
except Exception as e: # noqa: BLE001
logger.warning(
"monitor_schedule_check_failed", extra={"monitor_id": m["id"], "error": str(e)}
)
return {
"success": True,
"data": {
"total_monitors": len(monitors),
"ran_count": len(results),
"results": results,
},
}
logger = logging.getLogger(__name__)