From 49f1fb608974550d6affd7693a40a982ce96477a Mon Sep 17 00:00:00 2001 From: shijing Date: Fri, 10 Jul 2026 15:29:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=90=8E=E7=AB=AF=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=20dist=20=E5=B9=B6=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=208775=20=E7=AB=AF=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - backend/app/main.py 挂载 dist/ 作为 SPA 静态资源,/assets 走 StaticFiles,其它路径回落 index.html - 前后端 API base 统一切换到 127.0.0.1:8775,避开 8000 占用 - run.bat 自动创建/复用 backend/.venv,用 venv 启动 uvicorn - .gitignore 忽略 backend/.venv/ - 新增 deploy/finai-backend.service Co-Authored-By: Claude Opus 4.7 --- .gitignore | 2 +- backend/.env | 4 +- backend/app/main.py | 34 +++++++++++++++- backend/run.bat | 18 ++++++++- deploy/finai-backend.service | 77 ++++++++++++++++++++++++++++++++++++ frontend/app/README.md | 2 +- frontend/app/app.js | 4 +- frontend/app/index.html | 2 +- frontend/src/stores/app.js | 4 +- frontend/vite.config.js | 2 +- 10 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 deploy/finai-backend.service diff --git a/.gitignore b/.gitignore index bc3622b..0389270 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ backend/.eggs/ backend/wheels/ # 虚拟环境 -# backend/.venv/ +backend/.venv/ backend/venv/ backend/env/ backend/ENV/ diff --git a/backend/.env b/backend/.env index f3a7e94..e9e3ac6 100644 --- a/backend/.env +++ b/backend/.env @@ -1,7 +1,7 @@ APP_NAME=财务大模型智能问答客服系统 ENVIRONMENT=development -# DATABASE_URL=postgresql+psycopg://finai:finai@127.0.0.1:5432/finai -DATABASE_URL=postgresql+psycopg://postgres:zcDsj2026@127.0.0.1:5432/finai +DATABASE_URL=postgresql+psycopg://finance_ai:finance_ai@127.0.0.1:5432/finance_ai +# DATABASE_URL=postgresql+psycopg://postgres:zcDsj2026@127.0.0.1:5432/finai JWT_SECRET_KEY=vWQqGzVAi01hq126fXZdHV4W4S2Tx4BiOvWuwe5m8ubAPPitLpSh-Y4mXC50d7lM JWT_EXPIRE_MINUTES=480 SMS_PROVIDER=tencent diff --git a/backend/app/main.py b/backend/app/main.py index 7370d70..3398471 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,11 +1,17 @@ -from fastapi import FastAPI +from pathlib import Path + +from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import FileResponse +from fastapi.staticfiles import StaticFiles from app.api import api_router from app.config import get_settings from app.db import SessionLocal, init_db from app.services.user_service import ensure_default_admin +FRONTEND_DIST = Path(__file__).resolve().parent.parent / "dist" + def create_app() -> FastAPI: settings = get_settings() @@ -33,7 +39,33 @@ def create_app() -> FastAPI: def health() -> dict: return {"status": "ok", "app": settings.app_name} + _mount_frontend(app) + return app +def _mount_frontend(app: FastAPI) -> None: + if not FRONTEND_DIST.is_dir(): + return + + assets_dir = FRONTEND_DIST / "assets" + if assets_dir.is_dir(): + app.mount("/assets", StaticFiles(directory=assets_dir), name="assets") + + index_file = FRONTEND_DIST / "index.html" + + @app.get("/", include_in_schema=False) + def _spa_root() -> FileResponse: + return FileResponse(index_file) + + @app.get("/{full_path:path}", include_in_schema=False) + def _spa_fallback(full_path: str) -> FileResponse: + if full_path.startswith(("api/", "assets/", "health")): + raise HTTPException(status_code=404) + candidate = FRONTEND_DIST / full_path + if candidate.is_file(): + return FileResponse(candidate) + return FileResponse(index_file) + + app = create_app() diff --git a/backend/run.bat b/backend/run.bat index e3455cd..eb13812 100644 --- a/backend/run.bat +++ b/backend/run.bat @@ -4,8 +4,22 @@ setlocal cd /d "%~dp0" -echo [backend] 启动 uvicorn app.main:app http://127.0.0.1:8000 -python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 +if not exist ".venv\Scripts\python.exe" ( + echo [backend] 未找到 .venv,正在创建虚拟环境... + python -m venv .venv || goto :error + echo [backend] 正在安装依赖... + ".venv\Scripts\python.exe" -m pip install --upgrade pip || goto :error + ".venv\Scripts\python.exe" -m pip install fastapi "uvicorn[standard]" sqlalchemy "psycopg[binary]" pydantic-settings "python-jose[cryptography]" "passlib[bcrypt]" python-multipart httpx tencentcloud-sdk-python-lke || goto :error +) +echo [backend] 启动 uvicorn app.main:app http://127.0.0.1:8775 +".venv\Scripts\python.exe" -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8775 +goto :end + +:error +echo [backend] 启动失败 +exit /b 1 + +:end endlocal pause diff --git a/deploy/finai-backend.service b/deploy/finai-backend.service new file mode 100644 index 0000000..49b6415 --- /dev/null +++ b/deploy/finai-backend.service @@ -0,0 +1,77 @@ +# /etc/systemd/system/finai-backend.service +# +# 财务大模型智能问答客服系统 · FastAPI 后端 +# 代码目录:/home/ubuntu/finai/backend +# 虚拟环境:/home/ubuntu/finai/backend/.venv +# .env :/home/ubuntu/finai/backend/.env (DSN / JWT / ADP 等) +# 监听 :127.0.0.1:8775 (Nginx /api/ 反代到这里) +# +# 部署: +# sudo cp /home/ubuntu/finai/deploy/systemd/finai-backend.service \ +# /etc/systemd/system/finai-backend.service +# sudo systemctl daemon-reload +# sudo systemctl enable --now finai-backend +# +# 常用: +# sudo systemctl status finai-backend +# sudo systemctl restart finai-backend +# journalctl -u finai-backend -f + +[Unit] +Description=Finai Backend (FastAPI) +Documentation=https://finai.ctc-zc.com +After=network-online.target postgresql.service redis-server.service +Wants=network-online.target postgresql.service redis-server.service + +[Service] +Type=simple +User=ubuntu +Group=ubuntu + +WorkingDirectory=/home/ubuntu/finai/backend +EnvironmentFile=/home/ubuntu/finai/backend/.env + +ExecStart=/home/ubuntu/finai/backend/.venv/bin/uvicorn app.main:app \ + --host 127.0.0.1 \ + --port 8775 \ + --workers 2 \ + --proxy-headers \ + --forwarded-allow-ips=127.0.0.1 + +# 收到 SIGTERM 后给 uvicorn 15s 优雅退出(结束在途 SSE 流) +KillSignal=SIGTERM +TimeoutStopSec=15 + +Restart=always +RestartSec=3 +StartLimitBurst=5 +StartLimitIntervalSec=60 + +# 让 stdout/stderr 走 journal(journalctl -u finai-backend 直接看) +StandardOutput=journal +StandardError=journal +SyslogIdentifier=finai-backend + +# ─── 基础加固 ──────────────────────────────────────────────── +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +# 工作目录在 /home/ubuntu,必须放开 /home 保护 +ProtectHome=false +# 精确开放需要写的路径(storage 落图片、SQLite/日志 等) +ReadWritePaths=/home/ubuntu/finai/backend +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictSUIDSGID=true +LockPersonality=true +MemoryDenyWriteExecute=true +RestrictRealtime=true +# 只允许 IPv4/IPv6/UNIX,屏蔽奇葩地址簇 +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX + +# ─── 资源限制 ──────────────────────────────────────────────── +LimitNOFILE=65535 + +[Install] +WantedBy=multi-user.target diff --git a/frontend/app/README.md b/frontend/app/README.md index 0f3b447..4a09bc7 100644 --- a/frontend/app/README.md +++ b/frontend/app/README.md @@ -26,7 +26,7 @@ http://127.0.0.1:5173/index.html ## 登录 -- API 地址:`http://127.0.0.1:8000` +- API 地址:`http://127.0.0.1:8775` - 默认管理员手机号:`13800000000` - Mock 验证码:`123456` diff --git a/frontend/app/app.js b/frontend/app/app.js index 0c2593b..c79c929 100644 --- a/frontend/app/app.js +++ b/frontend/app/app.js @@ -1,5 +1,5 @@ const state = { - apiBase: localStorage.getItem("financeAiApiBase") || "http://127.0.0.1:8000", + apiBase: localStorage.getItem("financeAiApiBase") || "http://127.0.0.1:8775", token: localStorage.getItem("financeAiToken") || "", currentUser: null, currentSessionId: null, @@ -62,7 +62,7 @@ async function request(path, options = {}) { } function setApiBase(value) { - state.apiBase = value.trim() || "http://127.0.0.1:8000"; + state.apiBase = value.trim() || "http://127.0.0.1:8775"; localStorage.setItem("financeAiApiBase", state.apiBase); } diff --git a/frontend/app/index.html b/frontend/app/index.html index 8d14bd4..d51dcaf 100644 --- a/frontend/app/index.html +++ b/frontend/app/index.html @@ -33,7 +33,7 @@