bulid 63
This commit is contained in:
144
ui_builder/integration.py
Normal file
144
ui_builder/integration.py
Normal file
@@ -0,0 +1,144 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from .auth import EditorAuthManager, create_editor_auth_router
|
||||
from .manager import UIBuilderManager
|
||||
from .router import DataProvider, create_ui_builder_router
|
||||
|
||||
|
||||
def _seed_config(source: Path, destination: Path) -> None:
|
||||
if destination.exists():
|
||||
return
|
||||
if source.exists():
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(source, destination)
|
||||
|
||||
|
||||
def install_ui_builder(
|
||||
app: FastAPI,
|
||||
*,
|
||||
settings_dir: Path,
|
||||
data_providers: dict[str, DataProvider],
|
||||
source_labels: dict[str, str] | None = None,
|
||||
api_prefix: str = "/api/ui-builder",
|
||||
assets_url: str = "/ui-builder-assets",
|
||||
editor_url: str = "/editor",
|
||||
runtime_url: str = "/",
|
||||
settings_filename: str = "ui_builder.json",
|
||||
draft_filename: str = "ui_builder_draft.json",
|
||||
published_filename: str = "ui_builder_published.json",
|
||||
custom_script_urls: list[str] | None = None,
|
||||
custom_style_urls: list[str] | None = None,
|
||||
) -> UIBuilderManager:
|
||||
"""Install published runtime and PIN-protected editor."""
|
||||
|
||||
settings_dir = Path(settings_dir)
|
||||
legacy = settings_dir / Path(settings_filename).name
|
||||
draft_file = settings_dir / Path(draft_filename).name
|
||||
published_file = settings_dir / Path(published_filename).name
|
||||
|
||||
_seed_config(legacy, draft_file)
|
||||
_seed_config(legacy, published_file)
|
||||
|
||||
draft_manager = UIBuilderManager(settings_dir, filename=draft_file.name)
|
||||
published_manager = UIBuilderManager(settings_dir, filename=published_file.name)
|
||||
auth = EditorAuthManager(settings_dir)
|
||||
|
||||
assets_dir = Path(__file__).resolve().parent / "static"
|
||||
app.mount(
|
||||
assets_url,
|
||||
StaticFiles(directory=assets_dir),
|
||||
name="ui-builder-assets",
|
||||
)
|
||||
|
||||
editor_api = f"{api_prefix.rstrip('/')}/editor"
|
||||
runtime_api = f"{api_prefix.rstrip('/')}/runtime"
|
||||
auth_api = f"{api_prefix.rstrip('/')}/auth"
|
||||
|
||||
app.include_router(
|
||||
create_editor_auth_router(
|
||||
auth,
|
||||
prefix=auth_api,
|
||||
editor_url=editor_url,
|
||||
)
|
||||
)
|
||||
app.include_router(
|
||||
create_ui_builder_router(
|
||||
draft_manager,
|
||||
data_providers,
|
||||
source_labels=source_labels,
|
||||
prefix=editor_api,
|
||||
access_dependency=auth.require_editor,
|
||||
publish_manager=published_manager,
|
||||
)
|
||||
)
|
||||
app.include_router(
|
||||
create_ui_builder_router(
|
||||
published_manager,
|
||||
data_providers,
|
||||
source_labels=source_labels,
|
||||
prefix=runtime_api,
|
||||
read_only=True,
|
||||
)
|
||||
)
|
||||
|
||||
template = (assets_dir / "index.html").read_text(encoding="utf-8")
|
||||
asset_digest = hashlib.sha256()
|
||||
for asset_name in ("app.js", "styles.css"):
|
||||
asset_path = assets_dir / asset_name
|
||||
if asset_path.exists():
|
||||
asset_digest.update(asset_path.read_bytes())
|
||||
asset_version = asset_digest.hexdigest()[:12]
|
||||
custom_script_urls = custom_script_urls or []
|
||||
custom_style_urls = custom_style_urls or []
|
||||
|
||||
def render_page(mode: str) -> HTMLResponse:
|
||||
page_api = editor_api if mode == "editor" else runtime_api
|
||||
html = (
|
||||
template.replace("__UI_BUILDER_ASSETS__", assets_url.rstrip("/"))
|
||||
.replace("__UI_BUILDER_ASSET_VERSION__", asset_version)
|
||||
.replace("__UI_BUILDER_API__", page_api)
|
||||
.replace("__UI_BUILDER_AUTH_API__", auth_api)
|
||||
.replace("__UI_BUILDER_EDITOR__", editor_url)
|
||||
.replace("__UI_BUILDER_RUNTIME__", runtime_url)
|
||||
.replace("__UI_BUILDER_MODE__", mode)
|
||||
.replace("__UI_BUILDER_EDITOR_HOTKEY__", "Ctrl+Shift+E")
|
||||
.replace(
|
||||
"__UI_BUILDER_CUSTOM_STYLES__",
|
||||
"\n".join(
|
||||
f'<link rel="stylesheet" href="{url}">'
|
||||
for url in custom_style_urls
|
||||
),
|
||||
)
|
||||
.replace(
|
||||
"__UI_BUILDER_CUSTOM_SCRIPTS__",
|
||||
"\n".join(
|
||||
f'<script src="{url}"></script>'
|
||||
for url in custom_script_urls
|
||||
),
|
||||
)
|
||||
)
|
||||
return HTMLResponse(html)
|
||||
|
||||
@app.get(editor_url, include_in_schema=False)
|
||||
async def ui_builder_editor(request: Request):
|
||||
if not auth.is_request_authenticated(request):
|
||||
return RedirectResponse(runtime_url, status_code=302)
|
||||
return render_page("editor")
|
||||
|
||||
@app.get(runtime_url, include_in_schema=False)
|
||||
async def ui_builder_runtime() -> HTMLResponse:
|
||||
return render_page("runtime")
|
||||
|
||||
app.state.ui_builder_manager = draft_manager
|
||||
app.state.ui_builder_draft_manager = draft_manager
|
||||
app.state.ui_builder_published_manager = published_manager
|
||||
app.state.ui_builder_auth = auth
|
||||
return draft_manager
|
||||
Reference in New Issue
Block a user