From a5a43e9db890ce87031d5f7fe64c3d6518e4449e Mon Sep 17 00:00:00 2001 From: xucheng Date: Wed, 7 Jan 2026 23:55:53 +0900 Subject: [PATCH] feat: Add PM2 configuration for deployment, a database initialization script, and update project dependencies. --- README.md | 57 +++++++++++++++++++++++++ create_db.py | 20 +++++++++ deploy.sh | 87 ++++++++++++++++++++++++++++++++++++++ ecosystem.config.js | 22 ++++++++++ ecosystem.config.js.bak | 22 ++++++++++ frontend/package-lock.json | 17 +------- requirements.txt | 5 +++ 7 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 create_db.py create mode 100755 deploy.sh create mode 100644 ecosystem.config.js create mode 100644 ecosystem.config.js.bak diff --git a/README.md b/README.md index 351c9dc..1817a1b 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,63 @@ *注:`IFIND_REFRESH_TOKEN` 为同花顺量化接口的 Refresh Token,需购买相关服务获取。* ``` +## 如何运行 +## 守护进程管理 (PM2) + +本项目推荐使用 [PM2](https://pm2.keymetrics.io/) 来管理和守护后端与前端进程,支持自动重启和日志管理。 + +### 1. 配置 +项目根目录下已提供预置的 `ecosystem.config.js` 配置文件。 + +### 2. 管理命令 +```bash +# 全局安装 PM2 (如果没有) +npm install -g pm2 + +# 启动所有服务 (后端 + 前端) +pm2 start ecosystem.config.js + +# 查看服务状态 +pm2 status + +# 查看日志 +pm2 logs + +# 停止所有服务 +pm2 stop all + +# 重启所有服务 +pm2 restart all +``` + +### 3. 应用说明 +- **datafetch-backend**: Python 后端服务 (Port 8000) +- **datafetch-frontend**: Next.js 前端开发服务 (Port 3000) + +## 自动化部署脚本 + +为了简化部署流程,项目提供了一个自动化脚本 `deploy.sh`,可一键完成从系统依赖安装到服务启动的全过程。 + +### 使用方法 + +```bash +# 赋予执行权限 +chmod +x deploy.sh + +# 运行部署脚本(开发模式) +./deploy.sh + +# 运行部署脚本(生产模式 - 会执行前端 build) +./deploy.sh prod +``` + +### 脚本执行内容 +1. **检查并安装系统级库**:自动安装 `python3-venv`、`pkg-config` 以及 `WeasyPrint` 所需的 `libpango` 等图形库。 +2. **安装全局 Node 工具**:检查并安装 `pm2`。 +3. **设置 Python 环境**:创建 `.venv` 并安装 `requirements.txt` 中的依赖。 +4. **初始化数据库**:运行 `create_db.py` 创建 SQLite 数据库。 +5. **启动服务**:构建前端(如指定 production)并使用 PM2 启动/重启所有服务。 + ## 如何运行 ### 参数说明 diff --git a/create_db.py b/create_db.py new file mode 100644 index 0000000..74f5dec --- /dev/null +++ b/create_db.py @@ -0,0 +1,20 @@ + +import asyncio +import sys +import os + +# Ensure backend module can be found +sys.path.append(os.path.abspath("backend")) + +from app.database import init_db + +async def main(): + print("Initializing database...") + try: + await init_db() + print("Database initialized successfully.") + except Exception as e: + print(f"Error initializing database: {e}") + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..b6814fe --- /dev/null +++ b/deploy.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# 颜色定义 +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' # No Color +INFO="${GREEN}[INFO]${NC}" +ERROR="${RED}[ERROR]${NC}" + +# 错误处理 +set -e +trap 'echo -e "${ERROR} 部署失败,请检查上方错误日志。"' ERR + +echo -e "${INFO} 开始自动化部署脚本..." + +# 1. 检查并安装系统级依赖 +echo -e "${INFO} 1/6 检查并安装系统依赖..." +if command -v apt-get &> /dev/null; then + sudo apt-get update + # 基础构建工具 + sudo apt-get install -y build-essential python3-dev pkg-config + # WeasyPrint 依赖 + sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 libgdk-pixbuf-2.0-0 shared-mime-info + # Python 虚拟环境支持 + sudo apt-get install -y python3-venv python3-pip +else + echo -e "${ERROR} 仅支持 Debian/Ubuntu 系统,请手动安装相关依赖。" + exit 1 +fi + +# 2. 安装 Node.js 依赖 (PM2) +echo -e "${INFO} 2/6 检查并安装全局 Node.js 工具..." +if ! command -v pm2 &> /dev/null; then + echo "安装 PM2..." + sudo npm install -g pm2 +fi + +# 3. 创建并激活 Python 虚拟环境 +echo -e "${INFO} 3/6 设置 Python 虚拟环境..." +if [ ! -d ".venv" ]; then + python3 -m venv .venv +fi +source .venv/bin/activate + +# 4. 安装 Python 项目依赖 +echo -e "${INFO} 4/6 安装 Python 依赖..." +pip install --upgrade pip +pip install -r requirements.txt + +# 5. 初始化数据库 +echo -e "${INFO} 5/6 初始化数据库..." +if [ -f "create_db.py" ]; then + python create_db.py +else + echo -e "${ERROR} 找不到 create_db.py,跳过数据库初始化。" +fi + +# 6. 启动服务 +echo -e "${INFO} 6/6 启动服务..." + +# 如果是生产环境部署前端 +if [ "$1" == "prod" ]; then + echo "构建前端..." + cd frontend + npm install + npm run build + cd .. + # 修改 ecosystem.config.js 指向 build (如果是临时修改建议手动操作,这里演示默认 dev 模式) + echo -e "${INFO} 请确保 ecosystem.config.js 已配置为生产模式启动 (npm start)" +else + # 开发模式 + cd frontend + npm install + cd .. +fi + +# 重启 PM2 +pm2 delete all 2>/dev/null || true +pm2 start ecosystem.config.js + +echo -e "${INFO} ==================================================" +echo -e "${INFO} 部署成功!" +echo -e "${INFO} 后端 API: http://localhost:8000" +echo -e "${INFO} 前端 页面: http://localhost:3000" +echo -e "${INFO} 使用 'pm2 list' 查看服务状态" +echo -e "${INFO} 使用 'pm2 logs' 查看日志" +echo -e "${INFO} ==================================================" diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..3130135 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,22 @@ +module.exports = { + apps: [ + { + name: "datafetch-backend", + script: ".venv/bin/python", + args: "-m uvicorn backend.app.main:app --host 0.0.0.0 --port 8000", + cwd: "./", + env: { + PYTHONPATH: "./backend" + } + }, + { + name: "datafetch-frontend", + script: "npm", + args: "run dev", // 生产环境建议改为 "start" 并先运行 npm run build + cwd: "./frontend", + env: { + NODE_ENV: "development" + } + } + ] +}; diff --git a/ecosystem.config.js.bak b/ecosystem.config.js.bak new file mode 100644 index 0000000..fec5e0e --- /dev/null +++ b/ecosystem.config.js.bak @@ -0,0 +1,22 @@ +module.exports = { + apps: [ + { + name: "datafetch-backend", + script: ".venv/bin/uvicorn", + args: "backend.app.main:app --host 0.0.0.0 --port 8000", + cwd: ".", + env: { + PYTHONPATH: "." + } + }, + { + name: "datafetch-frontend", + script: "npm", + args: "run dev", + cwd: "./frontend", + env: { + NODE_ENV: "development" + } + } + ] +}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 47a0b46..28af12e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -84,7 +84,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2423,7 +2422,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2434,7 +2432,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2490,7 +2487,6 @@ "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.51.0", "@typescript-eslint/types": "8.51.0", @@ -2996,7 +2992,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3359,7 +3354,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4060,7 +4054,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4246,7 +4239,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7478,7 +7470,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7488,7 +7479,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7501,7 +7491,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.69.0.tgz", "integrity": "sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -8350,8 +8339,7 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -8417,7 +8405,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8610,7 +8597,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9050,7 +9036,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.4.tgz", "integrity": "sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/requirements.txt b/requirements.txt index 9014338..02183a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,8 @@ markdown jquants-api-client google-genai PyYAML +fastapi +uvicorn +sqlalchemy +aiosqlite +weasyprint