Add server-rendered HTML5 shell with SSE
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-16 21:10:39 +03:00
parent 523a756f5c
commit 567b517699
3 changed files with 335 additions and 1 deletions
+52 -1
View File
@@ -31,10 +31,11 @@ from collaboration import (
)
from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import PlainTextResponse, Response
from fastapi.responses import PlainTextResponse, Response, StreamingResponse
from neo4j import AsyncGraphDatabase
from pydantic import BaseModel, Field
from api_server.html5 import render_html5_editor, render_html5_index, render_html5_status
from impact_engine import object_impact, routine_impact
from incremental_indexer import rebuild_changed_file
from integration_topology import IntegrationKind, build_integration_topology
@@ -1567,6 +1568,56 @@ async def api_health() -> dict[str, str]:
return {"status": "ok"}
@app.get("/html5")
async def html5_index() -> Response:
return Response(
render_html5_index(_project_summaries()),
media_type="text/html; charset=utf-8",
)
@app.get("/html5/projects/{project_id}/editor")
async def html5_project_editor(project_id: str, q: str = "") -> Response:
try:
snapshot = _project_snapshot_or_404(project_id)
html = render_html5_editor(
project_id=project_id,
projects=_project_summaries(),
snapshot=snapshot,
q=q,
)
except HTTPException as error:
html = render_html5_editor(
project_id=project_id,
projects=_project_summaries(),
snapshot=None,
error=str(error.detail),
q=q,
)
return Response(html, media_type="text/html; charset=utf-8")
@app.get("/html5/projects/{project_id}/events")
async def html5_project_events(project_id: str, once: bool = False) -> StreamingResponse:
def stream_status():
while True:
try:
snapshot = _project_snapshot_or_404(project_id)
fragment = render_html5_status(project_id, snapshot)
except HTTPException as error:
fragment = f'<span>project: {project_id}</span><span>error: {error.detail}</span>'
yield f"event: status\ndata: {fragment}\n\n"
if once:
break
time.sleep(5)
return StreamingResponse(
stream_status(),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
)
@app.get("/version")
async def version() -> dict[str, str]:
return {"name": "sfera", "version": "0.1.0"}