feat(auth): add admin bootstrap and username login

Initialize admin bootstrap settings during startup, persist username support in auth flows, and align frontend auth requests with local API behavior.
This commit is contained in:
2026-03-24 15:07:19 +08:00
parent 6f594631e9
commit a3aa15d339
13 changed files with 787 additions and 27 deletions

View File

@@ -3,7 +3,7 @@ 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
from app.database import init_db, async_session
import app.models # noqa: F401 - 注册所有模型
from app.routers import (
auth_router,
@@ -23,6 +23,7 @@ from app.routers import (
)
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
from app.config import settings
from app.logging_utils import (
setup_logging,
@@ -35,14 +36,23 @@ from app.logging_utils import (
import os
@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)
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 persist_system_log(
message="application_started",
source="app",
@@ -50,6 +60,16 @@ async def lifespan(app: FastAPI):
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()