Files
Ten31-Portal/backend/ten31portal/main.py
T
Johnny 5 89eddd2ad0 Issue 17: StartOS 0.4.0 packaging
- Dockerfile: multi-stage build (Node frontend + Python backend)
- StartOS manifest, interfaces, backups, version graph
- start.sh: auto-generates session secret, creates first approver on boot
- Backend serves built frontend as SPA in production
- Single data volume for SQLite DB, platform backups cover it
2026-06-07 19:23:26 +00:00

63 lines
1.9 KiB
Python

"""Ten31Portal FastAPI application."""
import os
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import FileResponse
from ten31portal.config import SESSION_SECRET
from ten31portal.db_init import run_migrations
from ten31portal.routers.auth_router import router as auth_router
from ten31portal.routers.audit_router import router as audit_router
from ten31portal.routers.entity_router import router as entity_router
from ten31portal.routers.holding_router import router as holding_router
from ten31portal.routers.position_router import router as position_router
from ten31portal.routers.round_router import router as round_router
from ten31portal.routers.import_router import router as import_router
@asynccontextmanager
async def lifespan(app: FastAPI):
run_migrations()
yield
app = FastAPI(title="Ten31Portal", version="0.1.0", lifespan=lifespan)
app.add_middleware(SessionMiddleware, secret_key=SESSION_SECRET)
app.include_router(auth_router)
app.include_router(audit_router)
app.include_router(entity_router)
app.include_router(holding_router)
app.include_router(position_router)
app.include_router(round_router)
app.include_router(import_router)
@app.get("/api/health")
def health() -> dict[str, str]:
return {"status": "ok"}
# Serve built frontend in production (when static/ dir exists next to the app)
_static_dir = Path(__file__).resolve().parent.parent / "static"
if _static_dir.is_dir():
@app.get("/{path:path}")
async def serve_spa(path: str):
file = _static_dir / path
if file.is_file():
return FileResponse(file)
return FileResponse(_static_dir / "index.html")
def cli() -> None:
import uvicorn
uvicorn.run("ten31portal.main:app", host="0.0.0.0", port=8000, reload=True)
if __name__ == "__main__":
cli()