import logging from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.staticfiles import StaticFiles import uvicorn logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(name)s: %(message)s") from config import settings from auth_middleware import SessionAuthMiddleware from routers import chat, google_chat, nextcloud_talk, homeassistant, files, distill, auth, orchestrator from routers import ui, onboarding, settings, tools_settings, help, auth_google, local_llm, push, audit, usage, crons @asynccontextmanager async def lifespan(app: FastAPI): import scheduler scheduler.start() yield scheduler.stop() from llm_client import cleanup await cleanup() app = FastAPI(title="Cortex Dispatcher", lifespan=lifespan) app.add_middleware(SessionAuthMiddleware) # API routers app.include_router(chat.router) app.include_router(google_chat.router) app.include_router(nextcloud_talk.router) app.include_router(homeassistant.router) app.include_router(files.router) app.include_router(distill.router) app.include_router(auth.router) app.include_router(orchestrator.router) app.include_router(push.router) app.include_router(audit.router) app.include_router(usage.router) # Static files — must be mounted BEFORE ui.router so /static/* is matched first. # ui.router has a wildcard /{username}/{persona} that would otherwise catch /static/style.css etc. app.mount("/static", StaticFiles(directory="static"), name="static") # Google OAuth — must be before ui.router (wildcard /{user}/{persona} would swallow it) app.include_router(auth_google.router) # Onboarding (invite tokens + persona creation — before ui.router) app.include_router(onboarding.router) # Account settings app.include_router(settings.router) app.include_router(tools_settings.router) app.include_router(local_llm.router) app.include_router(crons.router) # Help page app.include_router(help.router) # UI router (login + /{user}/{persona} — must be last to avoid swallowing API paths) app.include_router(ui.router) @app.get("/health") async def health() -> dict: return {"status": "ok"} if __name__ == "__main__": uvicorn.run( "main:app", host=settings.host, port=settings.port, reload=True, )