feat: 管理面板(登录认证+触发AI分析+统计+CLI改密码)

This commit is contained in:
Sakurasan
2026-09-01 18:41:32 +08:00
parent bfcc932406
commit 7dba0238a5
7 changed files with 597 additions and 1 deletions
+18 -1
View File
@@ -7,16 +7,23 @@ from contextlib import asynccontextmanager
from dotenv import load_dotenv
from database import init_db
from routes import stock, collections, shares, themes, core_stocks, fuyao, market_dashboard, ai_analysis
from routes import stock, collections, shares, themes, core_stocks, fuyao, market_dashboard, ai_analysis, admin
from services.daily_collector import collector_loop, cache_cleanup_loop
from services.ai_collector import ai_analysis_loop
from services.admin_auth import is_configured, set_password
load_dotenv()
DEFAULT_ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "!auvauv")
@asynccontextmanager
async def lifespan(app: FastAPI):
init_db()
# 首次启动自动设置默认管理密码
if not is_configured():
set_password(DEFAULT_ADMIN_PASSWORD)
print(f"[admin] 已设置默认管理密码,请尽快修改: python3 admin_cli.py")
collector_task = asyncio.create_task(collector_loop())
cache_cleanup_task = asyncio.create_task(cache_cleanup_loop())
ai_analysis_task = asyncio.create_task(ai_analysis_loop())
@@ -50,6 +57,7 @@ app.include_router(core_stocks.router, prefix="/api/core-stocks")
app.include_router(fuyao.router, prefix="/api/v2")
app.include_router(market_dashboard.router, prefix="/api")
app.include_router(ai_analysis.router, prefix="/api")
app.include_router(admin.router, prefix="/api")
# 生产模式:后端同时托管前端静态文件
# catch-all 路由在 API 路由之后注册,所以 API 优先级更高
@@ -58,6 +66,15 @@ dist_path = os.path.join(os.path.dirname(__file__), "dist")
# HTML 不缓存:保证 index.html 永远最新(引用的资源文件名带 hash,可长缓存)
_NO_CACHE_HTML = {"Cache-Control": "no-cache, no-store, must-revalidate"}
# 管理面板:/admin → admin.html(在 SPA catch-all 之前)
ADMIN_HTML = os.path.join(dist_path, "admin.html")
@app.get("/admin")
async def serve_admin():
if os.path.isfile(ADMIN_HTML):
return FileResponse(ADMIN_HTML, media_type="text/html", headers=_NO_CACHE_HTML)
return JSONResponse({"detail": "Admin panel not found"}, status_code=404)
if os.path.isdir(dist_path):
@app.get("/{full_path:path}")
async def serve_spa(full_path: str):