Files
qihuo/deploy.sh
T
dekun e5a586f903 Restructure into modules/ with single-process CTP and config/ layout.
Move business code under modules/, env template to config/, PM2 single qihuo process, and _legacy shims for old imports.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 14:42:16 +08:00

218 lines
6.7 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 国内期货 · 交易复盘系统 - Ubuntu 一键部署 / 更新
# root 用户 | 目录 /opt/qihuo | 端口 6600 | PM2
#
# 已内置修复(避免重复踩坑):
# - vnpy_ctp 编译:build-essential python3-dev pkg-config
# - CTP 登录崩溃:zh_CN.GB18030 + zh_CN.UTF-8 locale
# - 时区:Asia/Shanghai(与 SimNow 交易时段一致)
# - SimNow 前置:自动探测可用线路并写入 .env
# - PM2 环境变量:LANG/LC_ALL(见 ecosystem.config.cjs
# - .env 缺项补全:SIMNOW_ENV、CTP_AUTO_RECONNECT
set -euo pipefail
APP_DIR="/opt/qihuo"
REPO_URL="https://git.bz121.com/dekun/qihuo.git"
SERVICE_NAME="qihuo"
# SimNow 前置候选(按优先级;部署时自动 nc 探测)
SIMNOW_FRONTS=(
"180.168.146.187:10201:10211"
"180.168.146.187:10202:10212"
"180.168.146.187:10130:10131"
"218.202.237.33:10203:10213"
)
if [ "$(id -u)" -ne 0 ]; then
echo "请使用 root 用户运行: sudo bash deploy.sh"
exit 1
fi
probe_tcp() {
local host="$1" port="$2"
if command -v nc &>/dev/null; then
nc -z -w 3 "$host" "$port" &>/dev/null
return $?
fi
timeout 3 bash -c "echo >/dev/tcp/${host}/${port}" 2>/dev/null
}
ensure_locale_gen() {
local pat="$1"
if [ -f /etc/locale.gen ] && grep -q "^# ${pat}" /etc/locale.gen; then
sed -i "s/^# ${pat}/${pat}/" /etc/locale.gen
fi
}
ensure_env_key() {
local file="$1" key="$2" val="$3"
if [ ! -f "$file" ]; then
return
fi
if grep -q "^${key}=" "$file"; then
return
fi
echo "${key}=${val}" >>"$file"
echo " .env 补全: ${key}=${val}"
}
pick_simnow_front() {
local host td_port md_port
for entry in "${SIMNOW_FRONTS[@]}"; do
IFS=: read -r host td_port md_port <<<"$entry"
if probe_tcp "$host" "$td_port" && probe_tcp "$host" "$md_port"; then
echo "tcp://${host}:${td_port}|tcp://${host}:${md_port}"
return 0
fi
echo " SimNow 前置不可达: ${host} ${td_port}/${md_port}" >&2
done
return 1
}
update_simnow_front_in_env() {
local env_file="$1"
local picked td md
picked="$(pick_simnow_front)" || {
echo "警告: 未能探测到可用 SimNow 前置,请手动编辑 ${env_file}(见 docs/SIMNOW.md"
return 1
}
td="${picked%%|*}"
md="${picked##*|}"
echo "==> SimNow 可用前置: TD=${td} MD=${md}"
if grep -q "^SIMNOW_TD_ADDRESS=" "$env_file"; then
sed -i "s|^SIMNOW_TD_ADDRESS=.*|SIMNOW_TD_ADDRESS=${td}|" "$env_file"
sed -i "s|^SIMNOW_MD_ADDRESS=.*|SIMNOW_MD_ADDRESS=${md}|" "$env_file"
else
echo "SIMNOW_TD_ADDRESS=${td}" >>"$env_file"
echo "SIMNOW_MD_ADDRESS=${md}" >>"$env_file"
fi
}
echo "==> 检查系统依赖..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
need_install() {
if ! command -v "$1" &>/dev/null; then
apt-get install -y "$2"
fi
}
need_install python3 python3
need_install python3-venv python3-venv
need_install git git
echo "==> 安装 vnpy_ctp 编译依赖..."
apt-get install -y build-essential python3-dev pkg-config locales netcat-openbsd
echo "==> 配置时区 Asia/Shanghai..."
if command -v timedatectl &>/dev/null; then
timedatectl set-timezone Asia/Shanghai || true
fi
echo "==> 配置 CTP 所需 localezh_CN.GB18030 等)..."
ensure_locale_gen "zh_CN.GB18030 GB18030"
ensure_locale_gen "zh_CN.UTF-8 UTF-8"
ensure_locale_gen "en_US.UTF-8 UTF-8"
locale-gen zh_CN.GB18030 zh_CN.UTF-8 en_US.UTF-8 2>/dev/null || locale-gen
update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 2>/dev/null || true
export LANG=zh_CN.UTF-8
export LC_ALL=zh_CN.UTF-8
if ! locale -a 2>/dev/null | grep -qi gb18030; then
echo "错误: zh_CN.GB18030 未生成,CTP 连接后会崩溃"
exit 1
fi
echo " locale OK: $(locale -a 2>/dev/null | grep -i gb18030 | head -1)"
if ! command -v pm2 &>/dev/null; then
echo "==> 安装 PM2..."
if ! command -v npm &>/dev/null; then
need_install nodejs nodejs
need_install npm npm
fi
npm install -g pm2
fi
echo "==> 准备应用目录 ${APP_DIR}..."
mkdir -p "$(dirname "$APP_DIR")"
if [ -d "$APP_DIR/.git" ]; then
echo "==> 更新已有仓库..."
cd "$APP_DIR"
git pull origin main || git pull origin master || true
else
if [ -d "$APP_DIR" ] && [ "$(ls -A "$APP_DIR" 2>/dev/null)" ]; then
echo "目录 ${APP_DIR} 已存在且非 git 仓库,请手动处理后重试"
exit 1
fi
git clone "$REPO_URL" "$APP_DIR"
cd "$APP_DIR"
fi
echo "==> Python 虚拟环境与依赖..."
if [ ! -d "$APP_DIR/venv" ]; then
python3 -m venv "$APP_DIR/venv"
fi
# shellcheck disable=SC1091
source "$APP_DIR/venv/bin/activate"
pip install --upgrade pip -q
pip install -r "$APP_DIR/requirements.txt"
python -c "from vnpy_ctp import CtpGateway; print('vnpy_ctp OK')"
echo "==> 配置 config/.env..."
ENV_FILE="$APP_DIR/config/.env"
mkdir -p "$APP_DIR/config"
if [ ! -f "$ENV_FILE" ]; then
cp "$APP_DIR/config/.env.example" "$ENV_FILE"
RAND_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
sed -i "s/change-this-to-a-random-secret-key/${RAND_KEY}/" "$ENV_FILE"
echo " 已生成 config/.env,请编辑 SIMNOW_USER / ADMIN_PASSWORD"
fi
ensure_env_key "$ENV_FILE" "SIMNOW_ENV" "实盘"
ensure_env_key "$ENV_FILE" "CTP_AUTO_RECONNECT" "true"
ensure_env_key "$ENV_FILE" "SIMNOW_BROKER_ID" "9999"
ensure_env_key "$ENV_FILE" "SIMNOW_APP_ID" "simnow_client_test"
ensure_env_key "$ENV_FILE" "SIMNOW_AUTH_CODE" "0000000000000000"
update_simnow_front_in_env "$ENV_FILE" || true
mkdir -p "$APP_DIR/logs"
echo "==> 验证 CTP 环境..."
if grep -q "^SIMNOW_USER=.\+" "$ENV_FILE" 2>/dev/null && \
grep -q "^SIMNOW_PASSWORD=.\+" "$ENV_FILE" 2>/dev/null; then
set +e
python "$APP_DIR/scripts/test_simnow.py"
CTP_TEST=$?
set -e
if [ "$CTP_TEST" -ne 0 ]; then
echo "警告: SimNow 连接测试未通过,请检查 .env 账号与网络(见 docs/SIMNOW.md"
else
echo " SimNow CTP 连接测试通过"
fi
else
echo " 跳过 CTP 测试(未配置 SIMNOW_USER / SIMNOW_PASSWORD"
fi
echo "==> PM2 启动/重启服务..."
cd "$APP_DIR"
if pm2 describe "$SERVICE_NAME" &>/dev/null; then
pm2 restart ecosystem.config.cjs --update-env
else
pm2 start ecosystem.config.cjs
fi
pm2 save
echo ""
echo "=========================================="
echo " 部署完成"
echo " 目录: ${APP_DIR}"
echo " 时区: $(date +%Z) $(date '+%Y-%m-%d %H:%M:%S')"
echo " 端口: 6600"
echo " 访问: http://$(hostname -I 2>/dev/null | awk '{print $1}'):6600"
echo " 日志: pm2 logs ${SERVICE_NAME}"
echo " SimNow 注册: docs/SIMNOW.md"
echo " 开机自启: pm2 startup && pm2 save"
echo "=========================================="