bulid 63
This commit is contained in:
131
ui_builder/router.py
Normal file
131
ui_builder/router.py
Normal file
@@ -0,0 +1,131 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
from collections.abc import Awaitable, Callable
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from .manager import UIBuilderManager
|
||||
|
||||
DataProvider = Callable[[], dict[str, Any] | Awaitable[dict[str, Any]]]
|
||||
|
||||
|
||||
class ConfigPayload(BaseModel):
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
version: int = 20
|
||||
project_name: str
|
||||
data_source: str
|
||||
canvas: dict[str, Any] = Field(default_factory=dict)
|
||||
tabs: list[dict[str, Any]] = Field(default_factory=list)
|
||||
components: list[dict[str, Any]] = Field(default_factory=list)
|
||||
triggers: list[dict[str, Any]] = Field(default_factory=list)
|
||||
shortcut_sequences: list[dict[str, Any]] = Field(default_factory=list)
|
||||
prematch_groups: list[dict[str, Any]] = Field(default_factory=list)
|
||||
prematch_buttons: list[dict[str, Any]] = Field(default_factory=list)
|
||||
quick_panel_selectors: list[dict[str, Any]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BackupPayload(BaseModel):
|
||||
name: str
|
||||
|
||||
|
||||
def create_ui_builder_router(
|
||||
manager: UIBuilderManager,
|
||||
data_providers: dict[str, DataProvider],
|
||||
*,
|
||||
source_labels: dict[str, str] | None = None,
|
||||
prefix: str = "/api/ui-builder",
|
||||
access_dependency: Callable[..., Any] | None = None,
|
||||
read_only: bool = False,
|
||||
publish_manager: UIBuilderManager | None = None,
|
||||
) -> APIRouter:
|
||||
dependencies = [Depends(access_dependency)] if access_dependency else []
|
||||
router = APIRouter(
|
||||
prefix=prefix,
|
||||
tags=["UI Builder"],
|
||||
dependencies=dependencies,
|
||||
)
|
||||
source_labels = source_labels or {}
|
||||
|
||||
@router.get("/config")
|
||||
async def get_config() -> dict[str, Any]:
|
||||
return manager.load()
|
||||
|
||||
if not read_only:
|
||||
@router.post("/config")
|
||||
async def save_config(payload: ConfigPayload) -> dict[str, Any]:
|
||||
saved = manager.save(payload.model_dump())
|
||||
return {"ok": True, "config": saved}
|
||||
|
||||
@router.post("/import")
|
||||
async def import_config(payload: ConfigPayload) -> dict[str, Any]:
|
||||
saved = manager.import_config(payload.model_dump())
|
||||
return {"ok": True, "config": saved}
|
||||
|
||||
@router.get("/export")
|
||||
async def export_config() -> JSONResponse:
|
||||
return JSONResponse(
|
||||
manager.load(),
|
||||
headers={
|
||||
"Content-Disposition":
|
||||
'attachment; filename="ui_builder_draft.json"'
|
||||
},
|
||||
)
|
||||
|
||||
@router.get("/backups")
|
||||
async def list_backups() -> dict[str, Any]:
|
||||
return {"items": manager.list_backups()}
|
||||
|
||||
@router.post("/backups/restore")
|
||||
async def restore_backup(payload: BackupPayload) -> dict[str, Any]:
|
||||
try:
|
||||
restored = manager.restore_backup(payload.name)
|
||||
except (FileNotFoundError, ValueError, OSError):
|
||||
raise HTTPException(status_code=404, detail="Backup not found")
|
||||
return {"ok": True, "config": restored}
|
||||
|
||||
if publish_manager is not None:
|
||||
@router.post("/publish")
|
||||
async def publish_config() -> dict[str, Any]:
|
||||
published = publish_manager.save(
|
||||
manager.load(),
|
||||
create_backup=True,
|
||||
)
|
||||
return {
|
||||
"ok": True,
|
||||
"config": published,
|
||||
"published_at": datetime.now().isoformat(timespec="seconds"),
|
||||
}
|
||||
|
||||
@router.get("/sources")
|
||||
async def list_sources() -> dict[str, Any]:
|
||||
return {
|
||||
"items": [
|
||||
{"id": key, "label": source_labels.get(key, key)}
|
||||
for key in data_providers
|
||||
]
|
||||
}
|
||||
|
||||
@router.get("/data/{source}")
|
||||
async def get_data(source: str) -> dict[str, Any]:
|
||||
provider = data_providers.get(source)
|
||||
if provider is None:
|
||||
raise HTTPException(status_code=404, detail="Unknown data source")
|
||||
try:
|
||||
result = provider()
|
||||
if inspect.isawaitable(result):
|
||||
result = await result
|
||||
if not isinstance(result, dict):
|
||||
raise TypeError("Data provider must return a dict")
|
||||
return result
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
||||
return router
|
||||
Reference in New Issue
Block a user