From 5fe0fdd0225b9830b7e85f59854c436fb514f2a8 Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Mon, 11 May 2026 09:10:58 -0700 Subject: [PATCH] feat: add POST /api/diagnose and GET/PATCH /api/settings endpoints --- app/rest.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/app/rest.py b/app/rest.py index 9da8a62..98d8461 100644 --- a/app/rest.py +++ b/app/rest.py @@ -39,8 +39,10 @@ from app.services.search import ( stats_summary as _stats, format_results, ) +from app.services.diagnose import diagnose as _diagnose DB_PATH = Path(os.environ.get("TURNSTONE_DB", Path(__file__).parent.parent / "data" / "turnstone.db")) +PREFS_PATH = DB_PATH.parent / "preferences.json" DIST_DIR = Path(__file__).parent.parent / "web" / "dist" SOURCE_HOST = os.environ.get("TURNSTONE_SOURCE_HOST", "unknown") BUNDLE_ENDPOINT = os.environ.get("TURNSTONE_BUNDLE_ENDPOINT", "") @@ -50,7 +52,7 @@ app = FastAPI(title="Turnstone API", version="0.1.0", docs_url="/turnstone/docs" app.add_middleware( CORSMiddleware, allow_origins=["*"], - allow_methods=["GET", "POST", "DELETE"], + allow_methods=["GET", "POST", "DELETE", "PATCH"], allow_headers=["*"], ) @@ -60,6 +62,29 @@ def _startup() -> None: ensure_schema(DB_PATH) +def _load_prefs() -> dict[str, str]: + if PREFS_PATH.exists(): + try: + return json.loads(PREFS_PATH.read_text()) + except (json.JSONDecodeError, OSError): + pass + return {"entry_point_style": "topbar"} + + +def _save_prefs(data: dict[str, str]) -> None: + PREFS_PATH.write_text(json.dumps(data)) + + +class DiagnoseRequest(BaseModel): + query: str + since: str | None = None + until: str | None = None + + +class SettingsBody(BaseModel): + entry_point_style: str + + class IncidentCreate(BaseModel): label: str issue_type: str = "" @@ -167,6 +192,38 @@ def diagnose( } +@router.post("/api/diagnose") +def diagnose_post(body: DiagnoseRequest) -> dict: + if not body.query.strip(): + return { + "summary": { + "total": 0, "window_start": None, "window_end": None, + "time_detected": False, "by_severity": {}, "by_source": {}, + }, + "entries": [], + } + result = _diagnose(DB_PATH, query=body.query, since=body.since, until=body.until) + return { + "summary": result["summary"], + "entries": [dataclasses.asdict(r) for r in result["entries"]], + } + + +@router.get("/api/settings") +def get_settings() -> dict: + return _load_prefs() + + +@router.patch("/api/settings") +def patch_settings(body: SettingsBody) -> dict: + if body.entry_point_style not in ("topbar", "fab"): + raise HTTPException(status_code=422, detail="entry_point_style must be 'topbar' or 'fab'") + prefs = _load_prefs() + prefs["entry_point_style"] = body.entry_point_style + _save_prefs(prefs) + return prefs + + @router.get("/api/sources") def list_sources() -> dict: return {"sources": _list_sources(DB_PATH)}