feat(01-01): create device registry, show store, FastAPI app, API routes, and frontend shell
- DeviceRegistry with load/save/add/remove/instantiate and _DEVICE_CLASSES dict
- ShowStore with save/load/list_ids using aiofiles for async I/O
- FastAPI app with lifespan hook (startup: load registry/store, shutdown: save registry)
- API routes: GET/POST /api/devices/, DELETE /api/devices/{id}, GET/POST /api/shows/, GET /api/shows/{id}
- WebSocket stub at /ws with ConnectionManager and ack echo
- Frontend shell: DAW layout with header, sidebar (DEVICES + ANIMATIONS), TIMELINE, TRANSPORT
- Terminal aesthetic: dark theme, cyan accents, monospace fonts, no drop shadows
- .gitignore for runtime data (devices.json, shows/, __pycache__, .venv)
This commit is contained in:
43
lightsync/main.py
Normal file
43
lightsync/main.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from lightsync.devices.registry import DeviceRegistry
|
||||
from lightsync.store.show_store import ShowStore
|
||||
|
||||
# Module-level containers populated in lifespan
|
||||
registry: DeviceRegistry | None = None
|
||||
show_store: ShowStore | None = None
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
global registry, show_store
|
||||
# Startup
|
||||
registry = DeviceRegistry(Path("devices.json"))
|
||||
await registry.load()
|
||||
show_store = ShowStore(Path("shows"))
|
||||
show_store.ensure_dir()
|
||||
yield
|
||||
# Shutdown
|
||||
await registry.save()
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
app = FastAPI(title="LightSync", lifespan=lifespan)
|
||||
|
||||
# API routes (registered before static mount — see Pattern 7)
|
||||
from lightsync.api import shows, devices, ws
|
||||
app.include_router(shows.router, prefix="/api/shows")
|
||||
app.include_router(devices.router, prefix="/api/devices")
|
||||
app.include_router(ws.router)
|
||||
|
||||
# Static file serving — MUST be last (catches all unmatched paths)
|
||||
frontend_dir = Path(__file__).parent / "frontend"
|
||||
app.mount("/", StaticFiles(directory=frontend_dir, html=True), name="frontend")
|
||||
|
||||
return app
|
||||
|
||||
|
||||
app = create_app()
|
||||
Reference in New Issue
Block a user