mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 10:53:33 +08:00
* feat(web): add FastAPI & React web connect * feat: add FastAPI package * feat: system base struct * feat: frontend base struct * feat: 基础分析接口,历史记录接口 * fix: 使用正确的 FastAPI 方法定义,避免线程阻塞 * fix: 对接历史报告页、详情页 * fix: 修复接口问题 * fix: 网络配置 允许公网访问 跨域配置 * fix: 页面打包 & 提供 Server 静态访问 * fix: 优化dock栏样式 * fix: 优化图标样式 * fix: 修改部分配色 * fix: 删除垃圾文档 * fix: 删除垃圾代码 * fix: 驼峰转换工具 * fix: 历史记录列表滚动加载(分页) * feat: 显示分析中任务 * fix: 调整布局 * fix: 优化 Market Sentiment 组件动效 * fix: 历史列表组件bug * fix: FastAPI 日志配置 * feat: 新闻历史接口 * feat: 资讯列表 * feat: 优化布局 * fix: 修复页面元素变宽问题 * fix: 中文标题 * fix: 任务列表状态显示问题 * fix: 抽取日志配置 * fix: 优化报告价格显示 * fix: 修复编译错误 * fix: FastAPI 启动提取到 main.py * fix: 补充新web-ui启动文档 * fix: 默认发送通知 * fix: FastAPI 模式适配 docker * fix: FastAPI 模式文档 * Update api/v1/endpoints/stocks.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: package-lock.json * fix: 修改错别字 * fix: 更新文档说明 * fix: 更新文档说明 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
165 lines
5.1 KiB
Python
165 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
===================================
|
||
FastAPI 应用工厂模块
|
||
===================================
|
||
|
||
职责:
|
||
1. 创建和配置 FastAPI 应用实例
|
||
2. 配置 CORS 中间件
|
||
3. 注册路由和异常处理器
|
||
4. 托管前端静态文件(生产模式)
|
||
|
||
使用方式:
|
||
from api.app import create_app
|
||
app = create_app()
|
||
"""
|
||
|
||
import os
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
|
||
from fastapi import FastAPI, Request
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
from fastapi.staticfiles import StaticFiles
|
||
from fastapi.responses import FileResponse
|
||
|
||
from api.v1 import api_v1_router
|
||
from api.middlewares.error_handler import add_error_handlers
|
||
from api.v1.schemas.common import RootResponse, HealthResponse
|
||
|
||
|
||
def create_app(static_dir: Optional[Path] = None) -> FastAPI:
|
||
"""
|
||
创建并配置 FastAPI 应用实例
|
||
|
||
Args:
|
||
static_dir: 静态文件目录路径(可选,默认为项目根目录下的 static)
|
||
|
||
Returns:
|
||
配置完成的 FastAPI 应用实例
|
||
"""
|
||
# 默认静态文件目录
|
||
if static_dir is None:
|
||
static_dir = Path(__file__).parent.parent / "static"
|
||
|
||
# 创建 FastAPI 实例
|
||
app = FastAPI(
|
||
title="Daily Stock Analysis API",
|
||
description=(
|
||
"A股/港股/美股自选股智能分析系统 API\n\n"
|
||
"## 功能模块\n"
|
||
"- 股票分析:触发 AI 智能分析\n"
|
||
"- 历史记录:查询历史分析报告\n"
|
||
"- 股票数据:获取行情数据\n\n"
|
||
"## 认证方式\n"
|
||
"当前版本暂无认证要求"
|
||
),
|
||
version="1.0.0",
|
||
)
|
||
|
||
# ============================================================
|
||
# CORS 配置
|
||
# ============================================================
|
||
|
||
allowed_origins = [
|
||
"http://localhost:5173",
|
||
"http://127.0.0.1:5173",
|
||
"http://localhost:3000",
|
||
"http://127.0.0.1:3000",
|
||
]
|
||
|
||
# 从环境变量添加额外的允许来源
|
||
extra_origins = os.environ.get("CORS_ORIGINS", "")
|
||
if extra_origins:
|
||
allowed_origins.extend([o.strip() for o in extra_origins.split(",") if o.strip()])
|
||
|
||
# 允许所有来源(开发/演示用)
|
||
if os.environ.get("CORS_ALLOW_ALL", "").lower() == "true":
|
||
allowed_origins = ["*"]
|
||
|
||
app.add_middleware(
|
||
CORSMiddleware,
|
||
allow_origins=allowed_origins,
|
||
allow_credentials=True,
|
||
allow_methods=["*"],
|
||
allow_headers=["*"],
|
||
)
|
||
|
||
# ============================================================
|
||
# 注册路由
|
||
# ============================================================
|
||
|
||
app.include_router(api_v1_router)
|
||
add_error_handlers(app)
|
||
|
||
# ============================================================
|
||
# 根路由和健康检查
|
||
# ============================================================
|
||
|
||
has_frontend = static_dir.exists() and (static_dir / "index.html").exists()
|
||
|
||
if has_frontend:
|
||
@app.get("/", include_in_schema=False)
|
||
async def root():
|
||
"""根路由 - 返回前端页面"""
|
||
return FileResponse(static_dir / "index.html")
|
||
else:
|
||
@app.get(
|
||
"/",
|
||
response_model=RootResponse,
|
||
tags=["Health"],
|
||
summary="API 根路由",
|
||
description="返回 API 运行状态信息"
|
||
)
|
||
async def root() -> RootResponse:
|
||
"""根路由 - API 状态信息"""
|
||
return RootResponse(
|
||
message="Daily Stock Analysis API is running",
|
||
version="1.0.0"
|
||
)
|
||
|
||
@app.get(
|
||
"/api/health",
|
||
response_model=HealthResponse,
|
||
tags=["Health"],
|
||
summary="健康检查",
|
||
description="用于负载均衡器或监控系统检查服务状态"
|
||
)
|
||
async def health_check() -> HealthResponse:
|
||
"""健康检查接口"""
|
||
return HealthResponse(
|
||
status="ok",
|
||
timestamp=datetime.now().isoformat()
|
||
)
|
||
|
||
# ============================================================
|
||
# 静态文件托管(前端 SPA)
|
||
# ============================================================
|
||
|
||
if has_frontend:
|
||
# 挂载静态资源目录
|
||
assets_dir = static_dir / "assets"
|
||
if assets_dir.exists():
|
||
app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")
|
||
|
||
# SPA 路由回退
|
||
@app.get("/{full_path:path}", include_in_schema=False)
|
||
async def serve_spa(request: Request, full_path: str):
|
||
"""SPA 路由回退 - 非 API 路由返回 index.html"""
|
||
if full_path.startswith("api/"):
|
||
return None
|
||
|
||
file_path = static_dir / full_path
|
||
if file_path.exists() and file_path.is_file():
|
||
return FileResponse(file_path)
|
||
|
||
return FileResponse(static_dir / "index.html")
|
||
|
||
return app
|
||
|
||
|
||
# 默认应用实例(供 uvicorn 直接使用)
|
||
app = create_app()
|