130 lines
3.6 KiB
Python
130 lines
3.6 KiB
Python
from contextlib import asynccontextmanager
|
|
from fastapi import FastAPI
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
from app.database import init_db, async_session
|
|
import app.models # noqa: F401 - 注册所有模型
|
|
from app.routers import (
|
|
auth_router,
|
|
conversation_router,
|
|
document_router,
|
|
task_router,
|
|
forum_router,
|
|
graph_router,
|
|
agent_router,
|
|
todo_router,
|
|
reminder_router,
|
|
goal_router,
|
|
schedule_center_router,
|
|
settings_router,
|
|
folder_router,
|
|
skill_router,
|
|
log_router,
|
|
system_router,
|
|
brain_router,
|
|
)
|
|
from app.routers.scheduler import router as scheduler_router
|
|
from app.services.scheduler_service import start_scheduler, stop_scheduler, get_scheduler_status
|
|
from app.services.admin_bootstrap_service import ensure_admin_user, ensure_builtin_skills
|
|
from app.config import settings
|
|
from app.logging_utils import (
|
|
setup_logging,
|
|
request_logging_middleware,
|
|
log_http_exception,
|
|
log_validation_exception,
|
|
log_unhandled_exception,
|
|
persist_system_log,
|
|
)
|
|
import os
|
|
|
|
|
|
INSECURE_SECRET_KEYS = {
|
|
'change-me-in-production',
|
|
'change-me-to-a-random-secret-key',
|
|
'jarvis-secret-key-change-in-production',
|
|
}
|
|
|
|
|
|
def validate_startup_security() -> None:
|
|
if not settings.DEBUG and settings.SECRET_KEY in INSECURE_SECRET_KEYS:
|
|
raise RuntimeError('SECRET_KEY must be changed before running with DEBUG disabled')
|
|
|
|
|
|
async def run_startup() -> None:
|
|
validate_startup_security()
|
|
await init_db()
|
|
async with async_session() as session:
|
|
await ensure_admin_user(session, settings)
|
|
await ensure_builtin_skills(session)
|
|
await persist_system_log(
|
|
message="application_started",
|
|
source="app",
|
|
operation="app.startup",
|
|
details={"version": settings.APP_VERSION},
|
|
)
|
|
start_scheduler()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
# 启动
|
|
setup_logging(settings.DEBUG)
|
|
os.makedirs(settings.DATA_DIR, exist_ok=True)
|
|
os.makedirs(settings.UPLOAD_DIR, exist_ok=True)
|
|
os.makedirs(settings.CHROMA_PERSIST_DIR, exist_ok=True)
|
|
await run_startup()
|
|
yield
|
|
# 关闭
|
|
stop_scheduler()
|
|
|
|
|
|
app = FastAPI(
|
|
title=settings.APP_NAME,
|
|
version=settings.APP_VERSION,
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
# CORS
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.CORS_ORIGINS,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
app.middleware("http")(request_logging_middleware)
|
|
app.add_exception_handler(StarletteHTTPException, log_http_exception)
|
|
app.add_exception_handler(RequestValidationError, log_validation_exception)
|
|
app.add_exception_handler(Exception, log_unhandled_exception)
|
|
|
|
# 注册路由
|
|
app.include_router(auth_router)
|
|
app.include_router(conversation_router)
|
|
app.include_router(document_router)
|
|
app.include_router(task_router)
|
|
app.include_router(forum_router)
|
|
app.include_router(graph_router)
|
|
app.include_router(agent_router)
|
|
app.include_router(todo_router)
|
|
app.include_router(reminder_router)
|
|
app.include_router(goal_router)
|
|
app.include_router(schedule_center_router)
|
|
app.include_router(settings_router)
|
|
app.include_router(folder_router)
|
|
app.include_router(skill_router)
|
|
app.include_router(log_router)
|
|
app.include_router(system_router)
|
|
app.include_router(brain_router)
|
|
app.include_router(scheduler_router)
|
|
|
|
|
|
@app.get("/api/health")
|
|
async def health():
|
|
return {
|
|
"status": "ok",
|
|
"version": settings.APP_VERSION,
|
|
"llm_provider": settings.LLM_PROVIDER,
|
|
"scheduler": get_scheduler_status(),
|
|
}
|