#!/usr/bin/env bash # deploy/lib/common.sh — 比特骆驼自动化对冲系统 部署公共函数 # 目标系统: Ubuntu 22.04 LTS set -e set -u if [ -n "${BASH_VERSION:-}" ]; then set -o pipefail fi INSTALL_ROOT="${INSTALL_ROOT:-/opt/eth_hedge_sim}" GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/eth_hedge_sim.git}" GIT_BRANCH="${GIT_BRANCH:-main}" BACKUP_ROOT="${BACKUP_ROOT:-/root/backups/eth_hedge_sim}" TZ_NAME="${EHS_TZ:-Asia/Shanghai}" NODE_MAJOR="${NODE_MAJOR:-20}" PM2_APP_NAME="${PM2_APP_NAME:-eth-hedge-api}" API_PORT="${API_PORT:-5155}" LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)" REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)" log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; } step() { echo ""; log "==> $*"; } die() { echo "错误: $*" >&2 exit 1 } require_root() { if [[ "$(id -u)" -ne 0 ]]; then die "请使用 root 执行(推荐: sudo -i 后运行)" fi } detect_server_ip() { local ip="" if command -v hostname >/dev/null 2>&1; then ip="$(hostname -I 2>/dev/null | awk '{print $1}')" fi if [[ -z "${ip}" ]]; then ip="127.0.0.1" fi echo "${ip}" } # curl|bash 时 stdin 可能是管道,交互从 /dev/tty 读 cm_read() { local __var="$1" local __prompt="${2:-}" local __line="" if [[ -n "${__prompt}" ]]; then printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}" fi if [[ -r /dev/tty ]]; then IFS= read -r __line /dev/null 2>&1 || return 1 # 能创建 venv 即可;pip 在项目 .venv 内安装 python3 -c "import venv" >/dev/null 2>&1 || return 1 return 0 } node_runtime_ok() { command -v node >/dev/null 2>&1 || return 1 command -v npm >/dev/null 2>&1 || return 1 return 0 } pm2_runtime_ok() { command -v pm2 >/dev/null 2>&1 } print_runtime_probe() { echo "环境检测:" if python_runtime_ok; then echo " [OK] Python3 + venv → $(python3 --version 2>&1)" else echo " [缺] Python3 / venv → 将自动安装" fi if node_runtime_ok; then echo " [OK] Node.js + npm → $(node --version 2>&1) / npm $(npm --version 2>&1)" else echo " [缺] Node.js / npm → 将自动安装 Node ${NODE_MAJOR}" fi if pm2_runtime_ok; then echo " [OK] PM2 → $(pm2 -v 2>&1)" else echo " [缺] PM2 → 将自动安装" fi } install_python_runtime() { step "安装 Python3 环境 (python3 / venv / pip)" export DEBIAN_FRONTEND=noninteractive if ! command -v apt-get >/dev/null 2>&1; then die "非 apt 系统,请手动安装 python3 python3-venv python3-pip" fi apt-get update -qq apt-get install -y python3 python3-venv python3-pip if ! python_runtime_ok; then die "Python 环境安装后仍不可用,请检查 apt 源" fi log "Python 已就绪: $(python3 --version 2>&1)" } install_node_runtime() { step "安装 Node.js ${NODE_MAJOR}" export DEBIAN_FRONTEND=noninteractive if ! command -v apt-get >/dev/null 2>&1; then die "非 apt 系统,请手动安装 Node.js ${NODE_MAJOR}+" fi apt-get update -qq apt-get install -y ca-certificates curl curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - apt-get install -y nodejs if ! node_runtime_ok; then die "Node.js 安装后仍不可用" fi log "Node 已就绪: $(node --version 2>&1)" } install_pm2_runtime() { step "安装 PM2" if ! command -v npm >/dev/null 2>&1; then die "安装 PM2 需要先有 npm" fi npm install -g pm2 if ! pm2_runtime_ok; then die "PM2 安装后仍不可用" fi log "PM2 已就绪: $(pm2 -v 2>&1)" } ensure_base_packages() { local need=() command -v git >/dev/null 2>&1 || need+=(git) command -v curl >/dev/null 2>&1 || need+=(curl) if [[ ${#need[@]} -eq 0 ]]; then log "基础包 git/curl 已存在,跳过" return 0 fi if ! command -v apt-get >/dev/null 2>&1; then die "缺少依赖: ${need[*]},且无 apt-get" fi step "安装基础包: ${need[*]}" export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y ca-certificates "${need[@]}" } ensure_system_deps() { step "环境检测 (Python / Node / PM2)" print_runtime_probe ensure_base_packages if python_runtime_ok; then log "Python 环境已存在,跳过安装" else install_python_runtime fi if node_runtime_ok; then log "Node.js 环境已存在,跳过安装" else install_node_runtime fi if pm2_runtime_ok; then log "PM2 已存在,跳过安装" else install_pm2_runtime fi step "环境确认" print_runtime_probe } pm2_stop_project() { if command -v pm2 >/dev/null 2>&1; then pm2 stop "${PM2_APP_NAME}" 2>/dev/null || true fi } pm2_delete_project() { if command -v pm2 >/dev/null 2>&1; then pm2 delete "${PM2_APP_NAME}" 2>/dev/null || true pm2 save 2>/dev/null || true fi } pm2_save_startup() { if command -v pm2 >/dev/null 2>&1; then pm2 save 2>/dev/null || true pm2 startup systemd -u root --hp /root 2>/dev/null || true fi } verify_health() { local port="${1:-${API_PORT}}" local url="http://127.0.0.1:${port}/health" step "健康检查 ${url}" local i for i in 1 2 3 4 5 6 7 8 9 10; do if curl -fsS "${url}" >/dev/null 2>&1; then log "health OK" curl -fsS "${url}" || true echo "" return 0 fi sleep 1 done log "警告: health 暂未就绪,请稍后检查 pm2 logs ${PM2_APP_NAME}" return 1 } print_post_install_guide() { local ip ip="$(detect_server_ip)" echo "" echo "══════════════════════════════════════" echo " 比特骆驼自动化对冲系统 部署完成" echo " 目录: ${INSTALL_ROOT}" echo " 本机: http://${ip}:${API_PORT}/health" echo " 进程: pm2 status → ${PM2_APP_NAME}" echo " 管理: bash ${INSTALL_ROOT}/deploy/manage.sh" echo " 配置: ${INSTALL_ROOT}/.env" echo "══════════════════════════════════════" echo "" }