"""FastAPI application entry point for the web-based OCR order processing system.""" import sys import os from contextlib import asynccontextmanager from pathlib import Path # Ensure app/ is importable _web_dir = Path(__file__).resolve().parent.parent # web/ _project_root = _web_dir.parent # project root if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from .config import get_or_generate_secret # noqa: trigger secret generation from .services.task_manager import TaskManager from .services.db_pool import DBPool from .auth.router import router as auth_router from .routers.files import router as files_router from .routers.processing import router as processing_router from .routers.memory import router as memory_router from .routers.config_api import router as config_router from .routers.barcodes import router as barcodes_router from .routers.sync import router as sync_router from .routers.websocket import router as ws_router from .routers.logs import router as logs_router from .routers.tasks import router as tasks_router from .middleware.logging import LoggingMiddleware # Shared singletons task_manager = TaskManager() db_pool = DBPool() @asynccontextmanager async def lifespan(app: FastAPI): """Initialize shared resources on startup.""" from app.config.settings import ConfigManager ConfigManager() # Initialize DB and cleanup old records from .services.db_schema import init_db, cleanup_old_records, sync_file_relations init_db() cleanup_old_records() # Sync file relations from existing files sync_file_relations() # Wire up DB pool to task manager task_manager.set_db_pool(db_pool) app.state.task_manager = task_manager app.state.db_pool = db_pool yield app = FastAPI( title="益选 OCR 订单处理系统", version="1.0.0", lifespan=lifespan, ) # CORS from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173", "http://127.0.0.1:5173", "http://localhost:8000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # HTTP logging middleware (after CORS, before routes) app.add_middleware(LoggingMiddleware) # Make task_manager and db_pool accessible via request.state @app.middleware("http") async def inject_services(request, call_next): request.state.task_manager = task_manager request.state.db_pool = db_pool return await call_next(request) # Mount routers app.include_router(auth_router) app.include_router(files_router) app.include_router(processing_router) app.include_router(memory_router) app.include_router(config_router) app.include_router(barcodes_router) app.include_router(sync_router) app.include_router(ws_router) app.include_router(logs_router) app.include_router(tasks_router) # Serve Vue SPA static files _static_dir = Path(__file__).resolve().parent / "static" if _static_dir.is_dir(): app.mount("/assets", StaticFiles(directory=str(_static_dir / "assets")), name="assets") @app.get("/{full_path:path}") async def serve_spa(full_path: str): """Catch-all: serve index.html for Vue Router history mode.""" file_path = _static_dir / full_path if file_path.is_file(): return FileResponse(str(file_path)) return FileResponse(str(_static_dir / "index.html"))