goofish / src /app.py
host1syan's picture
Upload 212 files
5378afe verified
"""
新架构的主应用入口
整合所有路由和服务
"""
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from src.api.routes import tasks, logs, settings, prompts, results, login_state, websocket, accounts
from src.api.dependencies import set_process_service
from src.services.task_service import TaskService
from src.services.process_service import ProcessService
from src.services.scheduler_service import SchedulerService
from src.infrastructure.persistence.json_task_repository import JsonTaskRepository
# 全局服务实例
process_service = ProcessService()
scheduler_service = SchedulerService(process_service)
# 设置全局 ProcessService 实例供依赖注入使用
set_process_service(process_service)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理"""
# 启动时
print("正在启动应用...")
# 重置所有任务状态为停止
task_repo = JsonTaskRepository()
task_service = TaskService(task_repo)
tasks_list = await task_service.get_all_tasks()
for task in tasks_list:
if task.is_running:
await task_service.update_task_status(task.id, False)
# 加载定时任务
await scheduler_service.reload_jobs(tasks_list)
scheduler_service.start()
print("应用启动完成")
yield
# 关闭时
print("正在关闭应用...")
scheduler_service.stop()
await process_service.stop_all()
print("应用已关闭")
# 创建 FastAPI 应用
app = FastAPI(
title="闲鱼智能监控机器人",
description="基于AI的闲鱼商品监控系统",
version="2.0.0",
lifespan=lifespan
)
# 注册路由
app.include_router(tasks.router)
app.include_router(logs.router)
app.include_router(settings.router)
app.include_router(prompts.router)
app.include_router(results.router)
app.include_router(login_state.router)
app.include_router(websocket.router)
app.include_router(accounts.router)
# 挂载静态文件
# 旧的静态文件目录(用于截图等)
app.mount("/static", StaticFiles(directory="static"), name="static")
# 挂载 Vue 3 前端构建产物
# 注意:需要在所有 API 路由之后挂载,以避免覆盖 API 路由
import os
if os.path.exists("dist"):
app.mount("/assets", StaticFiles(directory="dist/assets"), name="assets")
# 健康检查端点
@app.get("/health")
async def health_check():
"""健康检查(无需认证)"""
return {"status": "healthy", "message": "服务正常运行"}
# 认证状态检查端点
from fastapi import Request, HTTPException
from fastapi.responses import FileResponse
from pydantic import BaseModel
from src.infrastructure.config.settings import settings
class LoginRequest(BaseModel):
username: str
password: str
@app.post("/auth/status")
async def auth_status(payload: LoginRequest):
"""检查认证状态"""
if payload.username == settings.web_username and payload.password == settings.web_password:
return {"authenticated": True, "username": payload.username}
raise HTTPException(status_code=401, detail="认证失败")
# 主页路由 - 服务 Vue 3 SPA
from fastapi.responses import JSONResponse
@app.get("/")
async def read_root(request: Request):
"""提供 Vue 3 SPA 的主页面"""
if os.path.exists("dist/index.html"):
return FileResponse("dist/index.html")
else:
return JSONResponse(
status_code=500,
content={"error": "前端构建产物不存在,请先运行 cd web-ui && npm run build"}
)
# Catch-all 路由 - 处理所有前端路由(必须放在最后)
@app.get("/{full_path:path}")
async def serve_spa(request: Request, full_path: str):
"""
Catch-all 路由,将所有非 API 请求重定向到 index.html
这样可以支持 Vue Router 的 HTML5 History 模式
"""
# 如果请求的是静态资源(如 favicon.ico),返回 404
if full_path.endswith(('.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.css', '.js', '.json')):
return JSONResponse(status_code=404, content={"error": "资源未找到"})
# 其他所有路径都返回 index.html,让前端路由处理
if os.path.exists("dist/index.html"):
return FileResponse("dist/index.html")
else:
return JSONResponse(
status_code=500,
content={"error": "前端构建产物不存在,请先运行 cd web-ui && npm run build"}
)
if __name__ == "__main__":
import uvicorn
from src.infrastructure.config.settings import settings
print(f"启动新架构应用,端口: {settings.server_port}")
uvicorn.run(app, host="0.0.0.0", port=settings.server_port)