97 lines
3.8 KiB
Python
97 lines
3.8 KiB
Python
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, HTTPException, Request, Response, status
|
|
|
|
from .checker import EndpointChecker, TargetBlockedError
|
|
from .models import CheckResult, Health, Monitor, MonitorCreate, MonitorUpdate, StatusView
|
|
from .store import Conflict, MonitorStore, NotFound
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def store(request: Request) -> MonitorStore:
|
|
return request.app.state.store
|
|
|
|
|
|
def checker(request: Request) -> EndpointChecker:
|
|
return request.app.state.checker
|
|
|
|
|
|
@router.get("/healthz", response_model=Health, tags=["operations"])
|
|
async def health() -> Health:
|
|
return Health(status="ok")
|
|
|
|
|
|
@router.get("/readyz", response_model=Health, tags=["operations"])
|
|
async def ready(request: Request) -> Health:
|
|
if not hasattr(request.app.state, "store"):
|
|
raise HTTPException(status_code=503, detail="store unavailable")
|
|
return Health(status="ready")
|
|
|
|
|
|
@router.post("/v1/monitors", response_model=Monitor, status_code=201, tags=["monitors"])
|
|
async def create(data: MonitorCreate, request: Request) -> Monitor:
|
|
try:
|
|
return await store(request).create(data)
|
|
except Conflict as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/v1/monitors", response_model=list[Monitor], tags=["monitors"])
|
|
async def list_monitors(request: Request) -> list[Monitor]:
|
|
return await store(request).list()
|
|
|
|
|
|
@router.get("/v1/monitors/{monitor_id}", response_model=Monitor, tags=["monitors"])
|
|
async def get_monitor(monitor_id: UUID, request: Request) -> Monitor:
|
|
try:
|
|
return await store(request).get(monitor_id)
|
|
except NotFound as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@router.patch("/v1/monitors/{monitor_id}", response_model=Monitor, tags=["monitors"])
|
|
async def update_monitor(monitor_id: UUID, data: MonitorUpdate, request: Request) -> Monitor:
|
|
try:
|
|
return await store(request).update(monitor_id, data)
|
|
except NotFound as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except Conflict as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
|
|
|
|
@router.delete("/v1/monitors/{monitor_id}", status_code=204, tags=["monitors"])
|
|
async def delete_monitor(monitor_id: UUID, request: Request) -> Response:
|
|
try:
|
|
await store(request).delete(monitor_id)
|
|
except NotFound as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
|
|
|
|
@router.post("/v1/monitors/{monitor_id}/check", response_model=CheckResult, tags=["checks"])
|
|
async def check_monitor(monitor_id: UUID, request: Request) -> CheckResult:
|
|
try:
|
|
item = await store(request).get(monitor_id)
|
|
result = await checker(request).check(str(item.url), str(monitor_id))
|
|
await store(request).record_check(monitor_id, result)
|
|
return result
|
|
except NotFound as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except TargetBlockedError as exc:
|
|
result = CheckResult(status="error", checked_at=__import__("datetime").datetime.now(__import__("datetime").UTC), latency_ms=0, final_url=None, detail=str(exc))
|
|
try:
|
|
await store(request).record_check(monitor_id, result)
|
|
except NotFound as missing:
|
|
raise HTTPException(status_code=404, detail=str(missing)) from missing
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/v1/monitors/{monitor_id}/status", response_model=StatusView, tags=["checks"])
|
|
async def monitor_status(monitor_id: UUID, request: Request) -> StatusView:
|
|
try:
|
|
item = await store(request).get(monitor_id)
|
|
except NotFound as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return StatusView(id=item.id, status=item.status, last_check=item.last_check)
|