feat: 支持 Python venv 隔离环境,适配 Node.js 原生部署

## 背景
原方案使用系统 Python + pip --prefix 安装依赖到 data/dep_cache/python3,
系统 Python 环境被污染,且无法隔离不同项目的依赖。

## 改动内容

### 核心:Python venv 支持
- shell/start.sh: 使用 python3 -m venv 创建虚拟环境,替代 --prefix 模式
  - 从 .env 文件读取 PYTHON_VENV_DIR 配置
  - 首次启动自动创建 .venv,已存在则跳过
  - 仅将 .venv/bin 加入 PATH 优先级,不设置 PYTHONHOME/PYTHONPATH(避免破坏 venv 机制)
  - .env 不再强制覆盖,仅首次从 .env.example 复制
  - 启动时自动修复 task/ql 软链接指向当前部署目录

### 后端适配
- back/config/const.ts: 新增 PYTHON_VENV_DIR 常量
- back/config/util.ts: venv 模式下跳过 pip --prefix,直接使用 venv 的 pip3

### 开发模式支持
- shell/dev-env.sh: pnpm start 时自动 source,将 .venv/bin 加入 PATH
- package.json: start:back 加入 source dev-env.sh

### 新增文件
- shell/start-simplify.sh: 精简版启动脚本(跳过系统依赖安装,适用于已预装环境的服务器)
- README-NODE.md: Node.js 原生部署完整文档

### 配置
- .env.example: 新增 PYTHON_VENV_DIR=./.venv 配置项(默认注释状态)

## 兼容性
- Docker 模式不受影响(使用独立的 docker-entrypoint.sh,走原有 --prefix 逻辑)
- 未配置 PYTHON_VENV_DIR 时默认使用系统 Python(向后兼容)
This commit is contained in:
jiandanc
2026-06-07 21:03:51 +08:00
parent ad92e0c2c0
commit 0af546cd1d
8 changed files with 452 additions and 12 deletions
+33 -9
View File
@@ -77,24 +77,48 @@ if [[ $command != "reload" ]]; then
npm install -g pnpm@8.3.1 pm2 ts-node
fi
export PYTHON_SHORT_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
export PNPM_HOME=${QL_DIR}/data/dep_cache/node
export PYTHON_HOME=${QL_DIR}/data/dep_cache/python3
export PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3
# 从 .env 文件读取 PYTHON_VENV_DIR(.env 是给 Node.js 用的,bash 需要手动读取)
if [[ -z "${PYTHON_VENV_DIR:-}" ]] && [[ -f "${QL_DIR}/.env" ]]; then
env_venv_dir=$(grep -E '^PYTHON_VENV_DIR=' "${QL_DIR}/.env" | tail -1 | cut -d'=' -f2 | xargs)
if [[ -n "$env_venv_dir" ]]; then
# 处理相对路径
if [[ "$env_venv_dir" != /* ]]; then
env_venv_dir="${QL_DIR}/${env_venv_dir}"
fi
export PYTHON_VENV_DIR="$env_venv_dir"
fi
fi
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin
export PNPM_HOME=${QL_DIR}/data/dep_cache/node
# ===== Python venv =====
export PYTHON_VENV_DIR="${PYTHON_VENV_DIR:-${QL_DIR}/.venv}"
if [[ ! -f "${PYTHON_VENV_DIR}/bin/python3" ]]; then
echo "正在创建 Python venv: ${PYTHON_VENV_DIR}"
python3 -m venv "${PYTHON_VENV_DIR}"
fi
# 仅将 venv 的 bin 加入 PATH,Python 的 venv 机制自动处理包路径
export PATH=${PYTHON_VENV_DIR}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}
export NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules:${PNPM_HOME}/global/5/node_modules
export PIP_CACHE_DIR=${PYTHON_HOME}/pip
export PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages
export PIP_CACHE_DIR=${PYTHON_VENV_DIR}/pip
mkdir -p "${PIP_CACHE_DIR}"
if [[ $command != "reload" ]]; then
pip3 install --prefix ${PYTHON_HOME} requests
${PYTHON_VENV_DIR}/bin/pip3 install requests
fi
cd ${QL_DIR}
cp -f .env.example .env
# 仅在 .env 不存在时从 .env.example 复制
if [[ ! -f .env ]] && [[ -f .env.example ]]; then
cp -f .env.example .env
fi
chmod 777 ${QL_DIR}/shell/*.sh
# 确保 task/ql 命令指向当前部署目录(覆盖 npm 全局安装可能指向旧路径的软链接)
ln -sf ${QL_DIR}/shell/task.sh /usr/local/bin/task 2>/dev/null || true
ln -sf ${QL_DIR}/shell/update.sh /usr/local/bin/ql 2>/dev/null || true
. ${QL_DIR}/shell/share.sh
. ${QL_DIR}/shell/env.sh