feat: Add PM2 configuration for deployment, a database initialization script, and update project dependencies.

This commit is contained in:
xucheng 2026-01-07 23:55:53 +09:00
parent daf5808f05
commit a5a43e9db8
7 changed files with 214 additions and 16 deletions

View File

@ -36,6 +36,63 @@
*注:`IFIND_REFRESH_TOKEN` 为同花顺量化接口的 Refresh Token需购买相关服务获取。* *注:`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 启动/重启所有服务。
## 如何运行 ## 如何运行
### 参数说明 ### 参数说明

20
create_db.py Normal file
View File

@ -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())

87
deploy.sh Executable file
View File

@ -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} =================================================="

22
ecosystem.config.js Normal file
View File

@ -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"
}
}
]
};

22
ecosystem.config.js.bak Normal file
View File

@ -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"
}
}
]
};

View File

@ -84,7 +84,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@ -2423,7 +2422,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
} }
@ -2434,7 +2432,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
@ -2490,7 +2487,6 @@
"integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==", "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.51.0", "@typescript-eslint/scope-manager": "8.51.0",
"@typescript-eslint/types": "8.51.0", "@typescript-eslint/types": "8.51.0",
@ -2996,7 +2992,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -3359,7 +3354,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759", "caniuse-lite": "^1.0.30001759",
@ -4060,7 +4054,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@ -4246,7 +4239,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@rtsao/scc": "^1.1.0", "@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9", "array-includes": "^3.1.9",
@ -7478,7 +7470,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -7488,7 +7479,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
@ -7501,7 +7491,6 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.69.0.tgz", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.69.0.tgz",
"integrity": "sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==", "integrity": "sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
@ -8350,8 +8339,7 @@
"version": "4.1.18", "version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/tailwindcss-animate": { "node_modules/tailwindcss-animate": {
"version": "1.0.7", "version": "1.0.7",
@ -8417,7 +8405,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -8610,7 +8597,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -9050,7 +9036,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.4.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.4.tgz",
"integrity": "sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==", "integrity": "sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }

View File

@ -7,3 +7,8 @@ markdown
jquants-api-client jquants-api-client
google-genai google-genai
PyYAML PyYAML
fastapi
uvicorn
sqlalchemy
aiosqlite
weasyprint