"""EasyScreen 后端入口。""" import os from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from .config import get_server_config from .database import Base, _engine from .routers import admin_assets, admin_auth, admin_playlists, admin_screens, client app = FastAPI(title="EasyScreen 电子菜单后台", version="1.0.0") # 开发期允许前端跨域(Vite dev server);生产建议同域部署或收紧来源 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 自动建表 Base.metadata.create_all(bind=_engine) # 媒体文件访问(客户端播放地址 /media/{file}) _upload_dir = os.path.abspath( os.path.join(os.path.dirname(os.path.dirname(__file__)), get_server_config().get("upload_dir", "./uploads")) ) os.makedirs(_upload_dir, exist_ok=True) app.mount("/media", StaticFiles(directory=_upload_dir), name="media") app.include_router(admin_auth.router) app.include_router(admin_screens.router) app.include_router(admin_assets.router) app.include_router(admin_playlists.router) app.include_router(client.router) @app.get("/api/health") def health(): return {"status": "ok"} # ---------------- 管理台静态托管(生产单端口部署) ---------------- # 若存在 web/dist(npm run build 产物),则由本服务直接托管管理台。 # 注意:/api 与 /media 路由已在上方注册,此处 catch-all 仅兜底前端路由。 _WEB_DIST = os.path.abspath( os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "web", "dist") ) @app.get("/{full_path:path}", include_in_schema=False) def serve_web(full_path: str): if not os.path.isdir(_WEB_DIST): return {"message": "EasyScreen API 运行中。管理台未构建:请在 web/ 目录执行 npm run build"} # 优先返回真实文件(JS/CSS/图片等),其余路径回退到 index.html(SPA 路由) file_path = os.path.join(_WEB_DIST, full_path) if full_path and os.path.isfile(file_path): return FileResponse(file_path) return FileResponse(os.path.join(_WEB_DIST, "index.html"))