- Schedules web UI (/settings/crons): list, add, edit, pause/resume, delete jobs - cron task type: full orchestrator tool loop on a schedule, result → notification channel - parse_schedule: monthly/yearly formats (monthly:DD:HH:MM, yearly:MM:DD:HH:MM) - HA inbound webhook tools toggle: orchestrator loop vs. direct LLM, configurable in UI - ae_db_query/describe/show_view: SELECT-only Aether MariaDB access (admin, per-user creds) - /settings/integrations: admin-only page for Aether DB credentials - Schedules nav link added to all settings pages - pymysql added to requirements - Docs updated: HELP.md, MASTER.md, CLAUDE.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
77 lines
2.2 KiB
Python
77 lines
2.2 KiB
Python
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,
|
|
)
|