from uuid import UUID from fastapi import FastAPI, HTTPException, Response, status from app.checker import EndpointChecker from app.config import Settings, get_settings from app.logging_config import configure_logging from app.models import Health, Monitor, MonitorCreate, MonitorUpdate, StatusSnapshot from app.store import MonitorStore def create_app( settings: Settings | None = None, store: MonitorStore | None = None, checker: EndpointChecker | None = None, ) -> FastAPI: config = settings or get_settings() configure_logging(config.log_level) state = store or MonitorStore() service = checker or EndpointChecker(config) api = FastAPI(title=config.app_name, version="1.0.0") async def existing(monitor_id: UUID) -> Monitor: monitor = await state.get(monitor_id) if monitor is None: raise HTTPException(status_code=404, detail="monitor not found") return monitor @api.post("/monitors", response_model=Monitor, status_code=201) async def create_monitor(body: MonitorCreate) -> Monitor: return await state.create(body) @api.get("/monitors", response_model=list[Monitor]) async def list_monitors() -> list[Monitor]: return await state.list() @api.get("/monitors/{monitor_id}", response_model=Monitor) async def get_monitor(monitor_id: UUID) -> Monitor: return await existing(monitor_id) @api.put("/monitors/{monitor_id}", response_model=Monitor) async def update_monitor(monitor_id: UUID, body: MonitorUpdate) -> Monitor: monitor = await state.update(monitor_id, body) if monitor is None: raise HTTPException(status_code=404, detail="monitor not found") return monitor @api.delete("/monitors/{monitor_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_monitor(monitor_id: UUID) -> Response: if not await state.delete(monitor_id): raise HTTPException(status_code=404, detail="monitor not found") return Response(status_code=204) @api.post("/monitors/{monitor_id}/check", response_model=StatusSnapshot) async def check_monitor(monitor_id: UUID) -> StatusSnapshot: pending = await state.begin_check(monitor_id) if pending is None: raise HTTPException(status_code=404, detail="monitor not found") monitor, revision = pending result = await service.check(str(monitor.id), str(monitor.url)) await state.finish_check(monitor_id, revision, result) return result @api.get("/monitors/{monitor_id}/status", response_model=StatusSnapshot) async def current_status(monitor_id: UUID) -> StatusSnapshot: return (await existing(monitor_id)).status @api.get("/health/live", response_model=Health) async def liveness() -> Health: return Health(status="ok") @api.get("/health/ready", response_model=Health) async def readiness() -> Health: await state.list() return Health(status="ready") return api app = create_app()