Files
crypto_monitor/deploy/lib/common.sh
T
dekun 58c9f36a75 Add interactive deploy manager with install, uninstall, and update menus.
Provides curl-friendly manage.sh so production servers can bootstrap without a prior clone, then configure API and risk settings in the browser.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 12:23:29 +08:00

319 lines
7.9 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
# deploy/lib/common.sh — 部署管理器公共函数
set -e
set -u
if [ -n "${BASH_VERSION:-}" ]; then
set -o pipefail
fi
INSTALL_ROOT="${INSTALL_ROOT:-/opt/crypto_monitor}"
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/crypto_monitor.git}"
GIT_BRANCH="${GIT_BRANCH:-main}"
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}"
TZ_NAME="${CM_TZ:-Asia/Shanghai}"
NODE_MAJOR="${NODE_MAJOR:-20}"
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)"
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
PM2_APPS=(
crypto_binance
crypto_gate
crypto_okx
manual-trading-hub
manual-agent-binance
manual-agent-okx
manual-agent-gate
)
CONFIG_PATHS=(
crypto_monitor_binance/.env
crypto_monitor_okx/.env
crypto_monitor_gate/.env
manual_trading_hub/.env
manual_trading_hub/hub_settings.json
)
INSTANCE_DIRS=(
crypto_monitor_binance
crypto_monitor_gate
crypto_monitor_okx
manual_trading_hub
)
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
}
require_ubuntu() {
if [[ ! -f /etc/os-release ]]; then
log "警告: 未检测到 /etc/os-release,跳过 Ubuntu 版本检查"
return 0
fi
# shellcheck source=/dev/null
source /etc/os-release
if [[ "${ID:-}" != "ubuntu" ]]; then
log "警告: 当前系统为 ${ID:-unknown},官方仅测试 Ubuntu 22.04/24.04"
return 0
fi
local ver="${VERSION_ID:-}"
if [[ "${ver}" != "22.04" && "${ver}" != "24.04" ]]; then
log "警告: Ubuntu ${ver} 未在文档中明确测试,继续执行"
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}"
}
confirm_yes() {
local msg="$1"
local ans=""
read -r -p "${msg} [y/N] " ans
[[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]]
}
confirm_uninstall() {
local ans=""
echo "此操作将停止全部 PM2 进程并移走安装目录."
read -r -p "输入 UNINSTALL 确认卸载: " ans
[[ "${ans}" == "UNINSTALL" ]]
}
repo_ready() {
[[ -f "${1}/deploy/setup_env.sh" && -f "${1}/deploy/manage.sh" ]]
}
resolve_repo_root() {
if repo_ready "${INSTALL_ROOT}"; then
echo "${INSTALL_ROOT}"
return 0
fi
if repo_ready "${REPO_ROOT}"; then
echo "${REPO_ROOT}"
return 0
fi
return 1
}
install_log_file() {
local stamp
stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
mkdir -p "${BACKUP_ROOT}/deploy-logs"
echo "${BACKUP_ROOT}/deploy-logs/manage-${stamp}.log"
}
backup_configs_to() {
local src_root="$1"
local dest="$2"
mkdir -p "${dest}"
local rel copied=0
for rel in "${CONFIG_PATHS[@]}"; do
local src="${src_root}/${rel}"
if [[ -f "${src}" ]]; then
mkdir -p "${dest}/$(dirname "${rel}")"
cp -a "${src}" "${dest}/${rel}"
log "backup ${rel}"
copied=$((copied + 1))
fi
done
if [[ "${copied}" -eq 0 ]]; then
log "提示: 未找到可备份的配置文件"
fi
}
install_system_packages() {
step "安装系统依赖"
if ! command -v apt-get >/dev/null 2>&1; then
die "未检测到 apt-get,请手动安装 python3-venv git curl"
fi
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y python3 python3-pip python3-venv curl git ca-certificates
local pyver=""
if command -v python3 >/dev/null 2>&1; then
pyver="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
apt-get install -y "python${pyver}-venv" 2>/dev/null || apt-get install -y python3-venv
fi
}
install_node_pm2() {
step "检查 Node.js 与 PM2"
if command -v pm2 >/dev/null 2>&1; then
log "PM2 已安装: $(pm2 -v)"
return 0
fi
if ! command -v node >/dev/null 2>&1; then
log "安装 Node.js ${NODE_MAJOR}.x ..."
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
apt-get install -y nodejs
fi
log "安装 PM2 ..."
npm install -g pm2
log "PM2: $(pm2 -v)"
}
install_backup_cron_all() {
step "安装三所每日备份 cron"
local dir
for dir in crypto_monitor_binance crypto_monitor_gate crypto_monitor_okx; do
local inst="${REPO_ROOT}/${dir}/scripts/install_backup_cron.sh"
if [[ -f "${inst}" ]]; then
chmod +x "${inst}"
bash "${inst}" || log "警告: ${dir} cron 安装失败"
fi
done
}
remove_backup_cron_all() {
step "移除三所备份 cron"
local tmp removed=0
tmp="$(mktemp)"
if ! crontab -l 2>/dev/null >"${tmp}"; then
rm -f "${tmp}"
return 0
fi
local filtered
filtered="$(grep -vF "backup_data.sh" "${tmp}" || true)"
if [[ "${filtered}" != "$(cat "${tmp}")" ]]; then
printf '%s\n' "${filtered}" | awk '
BEGIN { tz = 0 }
/^CRON_TZ=Asia\/Shanghai$/ {
if (tz++) next
}
{ print }
' | crontab -
removed=1
fi
rm -f "${tmp}"
if [[ "${removed}" -eq 1 ]]; then
log "已移除 backup_data.sh 相关 cron"
fi
}
pm2_save_startup() {
step "PM2 save & startup"
pm2 save 2>/dev/null || true
if pm2 startup systemd -u root --hp /root 2>/dev/null | grep -q "sudo"; then
pm2 startup systemd -u root --hp /root 2>/dev/null | grep "^sudo" | bash || true
else
pm2 startup 2>/dev/null || true
fi
}
pm2_count_online() {
local name online=0
if ! command -v pm2 >/dev/null 2>&1; then
echo "0"
return 0
fi
for name in "${PM2_APPS[@]}"; do
if pm2 pid "${name}" >/dev/null 2>&1; then
online=$((online + 1))
fi
done
echo "${online}"
}
is_deployed() {
local root="$1"
[[ -x "${root}/crypto_monitor_binance/.venv/bin/python" ]] \
|| [[ -x "${root}/manual_trading_hub/.venv/bin/python" ]]
}
check_http() {
local url="$1"
local code
code="$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 5 "${url}" 2>/dev/null || echo "000")"
[[ "${code}" == "200" || "${code}" == "302" || "${code}" == "301" ]]
}
verify_deployment() {
local ip="${1:-$(detect_server_ip)}"
local ok=1
local online total
step "部署验收"
total="${#PM2_APPS[@]}"
online="$(pm2_count_online)"
if [[ "${online}" -eq "${total}" ]]; then
echo " [✓] PM2 进程 ${online}/${total} online"
else
echo " [✗] PM2 进程 ${online}/${total} online"
ok=0
pm2 list 2>/dev/null || true
fi
local checks=(
"中控:http://127.0.0.1:5100/"
"Binance:http://127.0.0.1:5001/"
"Gate:http://127.0.0.1:5000/"
"OKX:http://127.0.0.1:5004/"
)
local item label url
for item in "${checks[@]}"; do
label="${item%%:*}"
url="${item#*:}"
if check_http "${url}"; then
echo " [✓] ${label} 可访问"
else
echo " [✗] ${label} 不可访问 (${url})"
ok=0
fi
done
if [[ "${ok}" -eq 1 ]]; then
echo ""
log "验收通过: 进程都在 + 页面可打开"
return 0
fi
echo ""
log "验收未完全通过,可执行 pm2 logs <进程名> --lines 30 排查"
return 1
}
print_post_install_guide() {
local ip="${1:-$(detect_server_ip)}"
cat <<EOF
═══════════════════════════════════════════
✅ 部署完成
中控: http://${ip}:5100
Binance: http://${ip}:5001
Gate: http://${ip}:5000
OKX: http://${ip}:5004
登录账号: admin
登录密码: admin123
(请尽快在「系统设置」中修改)
下一步(浏览器):
1. 中控 → 系统设置 → 确认账户 URL(有域名时改 flask_url
2. 各所 → env 配置 → 填 API、风控、企业微信
3. 中控 → AI 配置(如需复盘)
4. 各所 env 配置 → 开启实盘下单 → 保存并重启
agent_url 默认本机 127.0.0.1:15200/15201/15202,一般无需修改
═══════════════════════════════════════════
EOF
}