支持 Ubuntu 一键部署与 PM2,并增加系统设置改密。
默认账号改为 admin/admin123,去掉 Windows 部署说明,与客户端同套部署流程。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# crypto_okx_hub 环境一键部署(Ubuntu / root /opt)
|
||||
|
||||
在 **`/opt/crypto_okx_hub`** 下以 **root** 部署期权多机中控,使用 **PM2** 常驻.
|
||||
|
||||
仓库: [https://git.bz121.com/dekun/crypto_okx_hub.git](https://git.bz121.com/dekun/crypto_okx_hub.git)
|
||||
|
||||
完整步骤见根目录 **[部署文档.md](../部署文档.md)**.
|
||||
|
||||
---
|
||||
|
||||
## 一键部署管理器(推荐)
|
||||
|
||||
新服务器**无需先 clone**,一条命令进入菜单:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.bz121.com/dekun/crypto_okx_hub/raw/branch/main/deploy/manage.sh | bash
|
||||
```
|
||||
|
||||
已安装机器:
|
||||
|
||||
```bash
|
||||
bash /opt/crypto_okx_hub/deploy/manage.sh
|
||||
```
|
||||
|
||||
### 菜单
|
||||
|
||||
| 选项 | 功能 |
|
||||
|------|------|
|
||||
| **1) 一键部署** | 环境识别 + 系统依赖 + Node/PM2 + venv + 启动 `crypto_okx_hub` |
|
||||
| **2) 一键卸载** | 备份 `.env` / `hub.db` → 停 PM2 → 移走目录 |
|
||||
| **3) 更新** | 快速更新 / 依赖更新 |
|
||||
| **4) 仅检测** | 识别 OS / Python / Node / PM2 / 项目状态(不安装) |
|
||||
| **0) 退出** | |
|
||||
|
||||
### 部署完成后
|
||||
|
||||
- 访问: `http://<服务器IP>:5100/login` (端口以 `.env` 的 `APP_PORT` 为准)
|
||||
- 默认账号: **admin** / **admin123**(可在页面「系统设置」修改)
|
||||
- 路径: `/opt/crypto_okx_hub`
|
||||
- 日志: `pm2 logs crypto_okx_hub --lines 100`
|
||||
|
||||
---
|
||||
|
||||
## 脚本结构
|
||||
|
||||
```
|
||||
deploy/
|
||||
├── manage.sh # 入口(自举 + 菜单)
|
||||
├── lib/
|
||||
│ ├── common.sh # 公共函数 / 环境识别 / 验收
|
||||
│ ├── install.sh # 一键部署
|
||||
│ ├── uninstall.sh # 一键卸载
|
||||
│ ├── update.sh # 更新子菜单
|
||||
│ └── doctor.sh # 仅检测
|
||||
├── setup_env.sh # venv + pip(被 install 调用)
|
||||
└── pull_and_restart.sh # 快速更新
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
- **Ubuntu 22.04 / 24.04**,用户 **root**
|
||||
- 能 `git clone` [crypto_okx_hub](https://git.bz121.com/dekun/crypto_okx_hub.git) 到 `/opt/crypto_okx_hub`
|
||||
|
||||
---
|
||||
|
||||
## 分步安装(仍可用)
|
||||
|
||||
```bash
|
||||
cd /opt
|
||||
git clone https://git.bz121.com/dekun/crypto_okx_hub.git crypto_okx_hub
|
||||
cd /opt/crypto_okx_hub
|
||||
bash deploy/setup_env.sh --install-system-deps
|
||||
pm2 start ecosystem.config.cjs
|
||||
pm2 save && pm2 startup
|
||||
```
|
||||
|
||||
`setup_env.sh` 常用参数:
|
||||
|
||||
```bash
|
||||
bash deploy/setup_env.sh --recreate-venv # 重建虚拟环境
|
||||
bash deploy/setup_env.sh --skip-pm2 # 不尝试安装 pm2
|
||||
bash deploy/setup_env.sh --skip-env-copy # 不复制 .env.example
|
||||
```
|
||||
|
||||
若脚本报 `pipefail` 错误(CRLF 行尾),先转 LF:
|
||||
|
||||
```bash
|
||||
sed -i 's/\r$//' deploy/manage.sh deploy/lib/*.sh deploy/*.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 环境变量(可选)
|
||||
|
||||
```bash
|
||||
INSTALL_ROOT=/opt/crypto_okx_hub
|
||||
GIT_URL=https://git.bz121.com/dekun/crypto_okx_hub.git
|
||||
GIT_BRANCH=main
|
||||
BACKUP_ROOT=/root/backups
|
||||
APP_PORT=5100
|
||||
```
|
||||
@@ -0,0 +1,360 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy/lib/common.sh — crypto_okx_hub 部署公共函数 (Ubuntu / root / PM2)
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
INSTALL_ROOT="${INSTALL_ROOT:-/opt/crypto_okx_hub}"
|
||||
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/crypto_okx_hub.git}"
|
||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}"
|
||||
TZ_NAME="${CM_TZ:-Asia/Shanghai}"
|
||||
NODE_MAJOR="${NODE_MAJOR:-20}"
|
||||
APP_PORT="${APP_PORT:-5100}"
|
||||
PM2_APP_NAME="${PM2_APP_NAME:-crypto_okx_hub}"
|
||||
|
||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)"
|
||||
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
||||
|
||||
PM2_APPS=(
|
||||
crypto_okx_hub
|
||||
)
|
||||
PM2_APPS_LEGACY=()
|
||||
|
||||
CONFIG_PATHS=(
|
||||
.env
|
||||
hub.db
|
||||
)
|
||||
|
||||
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}"
|
||||
}
|
||||
|
||||
cm_read() {
|
||||
local __var="$1"
|
||||
local __prompt="$2"
|
||||
local __val=""
|
||||
if [[ -r /dev/tty ]]; then
|
||||
IFS= read -r -p "${__prompt}" __val </dev/tty || true
|
||||
else
|
||||
IFS= read -r -p "${__prompt}" __val || true
|
||||
fi
|
||||
printf -v "${__var}" '%s' "${__val}"
|
||||
}
|
||||
|
||||
confirm_uninstall() {
|
||||
local ans=""
|
||||
echo "此操作将停止 crypto_okx_hub PM2 进程并移走安装目录."
|
||||
cm_read ans "输入 UNINSTALL 确认卸载: "
|
||||
[[ "${ans}" == "UNINSTALL" ]]
|
||||
}
|
||||
|
||||
repo_ready() {
|
||||
[[ -f "${1}/deploy/setup_env.sh" && -f "${1}/deploy/manage.sh" && -f "${1}/app.py" ]]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
is_deployed() {
|
||||
local root="$1"
|
||||
[[ -f "${root}/.env" && -x "${root}/.venv/bin/python" && -f "${root}/ecosystem.config.cjs" ]]
|
||||
}
|
||||
|
||||
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)"
|
||||
}
|
||||
|
||||
pip_progress_bar_arg() {
|
||||
echo "on"
|
||||
}
|
||||
|
||||
pip_upgrade_tools() {
|
||||
local pip_bin="$1"
|
||||
local bar
|
||||
bar="$(pip_progress_bar_arg)"
|
||||
echo " 升级 pip ..."
|
||||
"${pip_bin}" install -U pip setuptools wheel \
|
||||
--disable-pip-version-check \
|
||||
--progress-bar "${bar}"
|
||||
}
|
||||
|
||||
pip_install_requirements() {
|
||||
local pip_bin="$1"
|
||||
local req_file="$2"
|
||||
local label="${3:-依赖}"
|
||||
local bar
|
||||
bar="$(pip_progress_bar_arg)"
|
||||
echo " 安装${label} (下方为 pip 进度) ..."
|
||||
[[ -f "${req_file}" ]] || die "缺少依赖文件: ${req_file}"
|
||||
"${pip_bin}" install -r "${req_file}" \
|
||||
--disable-pip-version-check \
|
||||
--retries 5 \
|
||||
--progress-bar "${bar}"
|
||||
echo " ${label}安装完成"
|
||||
}
|
||||
|
||||
pm2_app_exists() {
|
||||
local name="$1"
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
pm2 describe "${name}" 2>/dev/null | grep -q "status"
|
||||
return $?
|
||||
fi
|
||||
pm2 jlist 2>/dev/null | python3 -c '
|
||||
import json,sys
|
||||
name=sys.argv[1]
|
||||
try:
|
||||
apps=json.load(sys.stdin)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
sys.exit(0 if any(isinstance(a,dict) and a.get("name")==name for a in (apps or [])) else 1)
|
||||
' "${name}"
|
||||
}
|
||||
|
||||
pm2_stop_project_apps() {
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
log "未安装 pm2,跳过"
|
||||
return 0
|
||||
fi
|
||||
local name
|
||||
for name in "${PM2_APPS[@]}" "${PM2_APPS_LEGACY[@]}"; do
|
||||
if pm2_app_exists "${name}"; then
|
||||
log "pm2 stop ${name}"
|
||||
pm2 stop "${name}" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
pm2_delete_project_apps() {
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
local name deleted=0
|
||||
for name in "${PM2_APPS[@]}" "${PM2_APPS_LEGACY[@]}"; do
|
||||
if pm2_app_exists "${name}"; then
|
||||
log "pm2 delete ${name}"
|
||||
pm2 delete "${name}" 2>/dev/null || true
|
||||
deleted=$((deleted + 1))
|
||||
fi
|
||||
done
|
||||
if [[ "${deleted}" -gt 0 ]]; then
|
||||
pm2 save 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
pm2_start_app() {
|
||||
step "启动 PM2: ${PM2_APP_NAME}"
|
||||
cd "${REPO_ROOT}"
|
||||
[[ -f "${REPO_ROOT}/ecosystem.config.cjs" ]] || die "缺少 ecosystem.config.cjs"
|
||||
[[ -x "${REPO_ROOT}/.venv/bin/python" ]] || die "缺少 .venv,请先运行 setup_env.sh"
|
||||
if pm2_app_exists "${PM2_APP_NAME}"; then
|
||||
log "已存在,执行 restart --update-env"
|
||||
if ! pm2 restart "${PM2_APP_NAME}" --update-env; then
|
||||
pm2 start ecosystem.config.cjs
|
||||
fi
|
||||
else
|
||||
log "首次启动: pm2 start ecosystem.config.cjs"
|
||||
pm2 start ecosystem.config.cjs
|
||||
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
|
||||
}
|
||||
|
||||
read_app_port() {
|
||||
local env_file="${REPO_ROOT}/.env"
|
||||
local port="${APP_PORT}"
|
||||
if [[ -f "${env_file}" ]]; then
|
||||
local line
|
||||
line="$(grep -E '^[[:space:]]*APP_PORT=' "${env_file}" | tail -n1 || true)"
|
||||
if [[ -n "${line}" ]]; then
|
||||
port="${line#*=}"
|
||||
port="$(echo "${port}" | tr -d '"' | tr -d "'" | xargs)"
|
||||
fi
|
||||
fi
|
||||
echo "${port:-5100}"
|
||||
}
|
||||
|
||||
verify_deployment() {
|
||||
local ip="${1:-$(detect_server_ip)}"
|
||||
local port
|
||||
port="$(read_app_port)"
|
||||
step "验收"
|
||||
if command -v pm2 >/dev/null 2>&1 && pm2_app_exists "${PM2_APP_NAME}"; then
|
||||
log "PM2: ${PM2_APP_NAME} 已注册"
|
||||
else
|
||||
log "警告: 未检测到 PM2 进程 ${PM2_APP_NAME}"
|
||||
return 1
|
||||
fi
|
||||
local code
|
||||
code="$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 2 --max-time 3 "http://127.0.0.1:${port}/login" 2>/dev/null || echo "000")"
|
||||
log "HTTP /login -> ${code}"
|
||||
log "访问: http://${ip}:${port}/login"
|
||||
return 0
|
||||
}
|
||||
|
||||
print_post_install_guide() {
|
||||
local ip="${1:-$(detect_server_ip)}"
|
||||
local port
|
||||
port="$(read_app_port)"
|
||||
echo ""
|
||||
echo "======== 部署完成 ========"
|
||||
echo " 中控: http://${ip}:${port}/login"
|
||||
echo " 账号: 见 .env 的 HUB_USERNAME / HUB_PASSWORD"
|
||||
echo " 路径: ${REPO_ROOT}"
|
||||
echo " 日志: pm2 logs ${PM2_APP_NAME} --lines 100"
|
||||
echo " 菜单: bash ${REPO_ROOT}/deploy/manage.sh"
|
||||
echo " 客户端: https://git.bz121.com/dekun/crypto_okx.git"
|
||||
echo "=========================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
doctor_report() {
|
||||
step "环境检测"
|
||||
require_ubuntu || true
|
||||
log "用户: $(id -un) uid=$(id -u)"
|
||||
log "路径候选: INSTALL_ROOT=${INSTALL_ROOT}"
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
log "Python: $(python3 --version 2>&1)"
|
||||
else
|
||||
log "Python: 未安装"
|
||||
fi
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
log "Node: $(node -v)"
|
||||
else
|
||||
log "Node: 未安装"
|
||||
fi
|
||||
if command -v pm2 >/dev/null 2>&1; then
|
||||
log "PM2: $(pm2 -v)"
|
||||
else
|
||||
log "PM2: 未安装"
|
||||
fi
|
||||
local root=""
|
||||
if root="$(resolve_repo_root 2>/dev/null)"; then
|
||||
log "仓库: ${root} (就绪)"
|
||||
REPO_ROOT="${root}"
|
||||
if [[ -x "${root}/.venv/bin/python" ]]; then
|
||||
log "venv: 存在"
|
||||
else
|
||||
log "venv: 缺失"
|
||||
fi
|
||||
if [[ -f "${root}/.env" ]]; then
|
||||
log ".env: 存在"
|
||||
else
|
||||
log ".env: 缺失"
|
||||
fi
|
||||
if command -v pm2 >/dev/null 2>&1 && pm2_app_exists "${PM2_APP_NAME}"; then
|
||||
log "PM2 进程: ${PM2_APP_NAME} 已注册"
|
||||
else
|
||||
log "PM2 进程: 未注册 ${PM2_APP_NAME}"
|
||||
fi
|
||||
else
|
||||
log "仓库: 未安装到 ${INSTALL_ROOT}"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy/lib/doctor.sh — 仅检测,不安装
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=common.sh
|
||||
source "${LIB_DIR}/common.sh"
|
||||
|
||||
main_doctor() {
|
||||
local root=""
|
||||
if root="$(resolve_repo_root 2>/dev/null)"; then
|
||||
REPO_ROOT="${root}"
|
||||
fi
|
||||
doctor_report
|
||||
echo ""
|
||||
log "检测完成(未修改系统).需要安装请返回菜单选 1."
|
||||
}
|
||||
|
||||
main_doctor "$@"
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy/lib/install.sh — crypto_okx_hub 一键部署
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=common.sh
|
||||
source "${LIB_DIR}/common.sh"
|
||||
|
||||
install_fresh() {
|
||||
step "克隆仓库"
|
||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||
die "目录已存在: ${INSTALL_ROOT},请选修复环境"
|
||||
fi
|
||||
mkdir -p "$(dirname "${INSTALL_ROOT}")"
|
||||
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||
}
|
||||
|
||||
install_repair() {
|
||||
step "修复环境(保留 .env 与 hub.db)"
|
||||
bash "${REPO_ROOT}/deploy/setup_env.sh" --install-system-deps --recreate-venv
|
||||
}
|
||||
|
||||
run_install_pipeline() {
|
||||
step "环境部署 setup_env.sh"
|
||||
bash "${REPO_ROOT}/deploy/setup_env.sh" --install-system-deps
|
||||
|
||||
pm2_start_app
|
||||
pm2_save_startup
|
||||
|
||||
verify_deployment "$(detect_server_ip)" || true
|
||||
print_post_install_guide "$(detect_server_ip)"
|
||||
}
|
||||
|
||||
handle_existing_install() {
|
||||
echo ""
|
||||
echo "检测到已部署安装: ${INSTALL_ROOT}"
|
||||
echo " a) 取消"
|
||||
echo " b) 修复环境(重建 venv,保留 .env 与数据库)"
|
||||
echo " c) 重新启动 PM2(不改依赖)"
|
||||
local choice=""
|
||||
cm_read choice "请选择 [a/b/c]: "
|
||||
case "${choice}" in
|
||||
b|B)
|
||||
install_repair
|
||||
if command -v pm2 >/dev/null 2>&1; then
|
||||
pm2_start_app
|
||||
pm2_save_startup
|
||||
fi
|
||||
verify_deployment "$(detect_server_ip)" || true
|
||||
print_post_install_guide "$(detect_server_ip)"
|
||||
;;
|
||||
c|C)
|
||||
pm2_start_app
|
||||
pm2_save_startup
|
||||
verify_deployment "$(detect_server_ip)" || true
|
||||
print_post_install_guide "$(detect_server_ip)"
|
||||
;;
|
||||
*)
|
||||
log "已取消"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ensure_repo_ready() {
|
||||
if repo_ready "${INSTALL_ROOT}"; then
|
||||
REPO_ROOT="${INSTALL_ROOT}"
|
||||
elif [[ -n "${REPO_ROOT:-}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||
:
|
||||
else
|
||||
REPO_ROOT=""
|
||||
fi
|
||||
|
||||
if [[ -z "${REPO_ROOT}" ]]; then
|
||||
install_system_packages
|
||||
install_node_pm2
|
||||
install_fresh
|
||||
REPO_ROOT="${INSTALL_ROOT}"
|
||||
else
|
||||
install_system_packages
|
||||
install_node_pm2
|
||||
fi
|
||||
}
|
||||
|
||||
main_install() {
|
||||
require_root
|
||||
require_ubuntu
|
||||
|
||||
if repo_ready "${INSTALL_ROOT}"; then
|
||||
REPO_ROOT="${INSTALL_ROOT}"
|
||||
elif repo_ready "${REPO_ROOT}"; then
|
||||
:
|
||||
else
|
||||
REPO_ROOT=""
|
||||
fi
|
||||
|
||||
if [[ -n "${REPO_ROOT}" ]] && is_deployed "${REPO_ROOT}"; then
|
||||
handle_existing_install
|
||||
return 0
|
||||
fi
|
||||
|
||||
ensure_repo_ready
|
||||
run_install_pipeline
|
||||
}
|
||||
|
||||
main_install "$@"
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy/lib/uninstall.sh — crypto_okx_hub 一键卸载
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=common.sh
|
||||
source "${LIB_DIR}/common.sh"
|
||||
|
||||
main_uninstall() {
|
||||
require_root
|
||||
|
||||
local root=""
|
||||
if ! root="$(resolve_repo_root)"; then
|
||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||
root="${INSTALL_ROOT}"
|
||||
else
|
||||
die "未找到安装目录 ${INSTALL_ROOT}"
|
||||
fi
|
||||
fi
|
||||
REPO_ROOT="${root}"
|
||||
|
||||
if ! confirm_uninstall; then
|
||||
log "已取消卸载"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local stamp backup_dir removed_dir
|
||||
stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
|
||||
backup_dir="${BACKUP_ROOT}/crypto_okx_hub-pre-uninstall-${stamp}"
|
||||
removed_dir="${INSTALL_ROOT}.removed.${stamp}"
|
||||
|
||||
step "备份配置到 ${backup_dir}"
|
||||
backup_configs_to "${REPO_ROOT}" "${backup_dir}"
|
||||
{
|
||||
echo "created_at=${stamp}"
|
||||
echo "install_root=${INSTALL_ROOT}"
|
||||
echo "removed_dir=${removed_dir}"
|
||||
} >"${backup_dir}/uninstall.manifest"
|
||||
|
||||
step "停止并移除本项目 PM2 进程(不影响其它 PM2)"
|
||||
pm2_stop_project_apps
|
||||
pm2_delete_project_apps
|
||||
|
||||
step "移走安装目录"
|
||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||
mv "${INSTALL_ROOT}" "${removed_dir}"
|
||||
log "已移动: ${INSTALL_ROOT} -> ${removed_dir}"
|
||||
else
|
||||
log "安装目录不存在,跳过"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "卸载完成."
|
||||
echo " 配置备份: ${backup_dir}"
|
||||
echo " 旧目录: ${removed_dir}"
|
||||
echo ""
|
||||
echo "回滚示例:"
|
||||
echo " mv ${removed_dir} ${INSTALL_ROOT}"
|
||||
echo " bash ${INSTALL_ROOT}/deploy/manage.sh # 选 1 修复环境"
|
||||
}
|
||||
|
||||
main_uninstall "$@"
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy/lib/update.sh — crypto_okx_hub 更新
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=common.sh
|
||||
source "${LIB_DIR}/common.sh"
|
||||
|
||||
update_quick() {
|
||||
step "快速更新"
|
||||
bash "${REPO_ROOT}/deploy/pull_and_restart.sh"
|
||||
# shellcheck source=common.sh
|
||||
source "${LIB_DIR}/common.sh"
|
||||
verify_deployment "$(detect_server_ip)" || true
|
||||
}
|
||||
|
||||
update_deps() {
|
||||
step "依赖更新"
|
||||
if [[ -x "${REPO_ROOT}/.venv/bin/pip" ]]; then
|
||||
pip_install_requirements "${REPO_ROOT}/.venv/bin/pip" "${REPO_ROOT}/requirements.txt" "crypto_okx_hub 依赖"
|
||||
else
|
||||
bash "${REPO_ROOT}/deploy/setup_env.sh" --install-system-deps
|
||||
fi
|
||||
update_quick
|
||||
}
|
||||
|
||||
show_update_menu() {
|
||||
while true; do
|
||||
echo ""
|
||||
echo " 更新选项:"
|
||||
echo " 3-1) 快速更新(git pull + pm2 restart)"
|
||||
echo " 3-2) 依赖更新(含 pip install)"
|
||||
echo " 0) 返回主菜单"
|
||||
local choice=""
|
||||
cm_read choice "请选择 [0/3-1/3-2]: "
|
||||
case "${choice}" in
|
||||
3-1|31|1) update_quick; break ;;
|
||||
3-2|32|2) update_deps; break ;;
|
||||
0) break ;;
|
||||
*) echo "无效选项" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
main_update() {
|
||||
require_root
|
||||
if ! REPO_ROOT="$(resolve_repo_root)"; then
|
||||
die "未找到安装目录 ${INSTALL_ROOT},请先执行「1) 一键部署」"
|
||||
fi
|
||||
if ! repo_ready "${REPO_ROOT}"; then
|
||||
die "未找到安装,请先执行「1) 一键部署」"
|
||||
fi
|
||||
show_update_menu
|
||||
}
|
||||
|
||||
main_update "$@"
|
||||
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env bash
|
||||
# crypto_okx_hub 部署管理器 — 一键部署 / 卸载 / 更新 (Ubuntu / root / PM2)
|
||||
#
|
||||
# 新服务器(免克隆):
|
||||
# curl -fsSL https://git.bz121.com/dekun/crypto_okx_hub/raw/branch/main/deploy/manage.sh | bash
|
||||
#
|
||||
# 已安装:
|
||||
# bash /opt/crypto_okx_hub/deploy/manage.sh
|
||||
#
|
||||
set -e
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
INSTALL_ROOT="${INSTALL_ROOT:-/opt/crypto_okx_hub}"
|
||||
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/crypto_okx_hub.git}"
|
||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||
|
||||
_script_src="${BASH_SOURCE[0]:-}"
|
||||
if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then
|
||||
DEPLOY_DIR="$(cd "$(dirname "${_script_src}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
||||
LIB_DIR="${DEPLOY_DIR}/lib"
|
||||
else
|
||||
DEPLOY_DIR=""
|
||||
REPO_ROOT=""
|
||||
LIB_DIR=""
|
||||
fi
|
||||
unset _script_src
|
||||
|
||||
set -u
|
||||
|
||||
repo_ready() {
|
||||
[[ -f "${1}/deploy/setup_env.sh" && -f "${1}/deploy/manage.sh" && -f "${1}/app.py" ]]
|
||||
}
|
||||
|
||||
sync_repo_if_present() {
|
||||
local root="$1"
|
||||
if [[ -d "${root}/.git" ]] && command -v git >/dev/null 2>&1; then
|
||||
git -C "${root}" pull -q --ff-only 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
bootstrap_repo() {
|
||||
if repo_ready "${INSTALL_ROOT}"; then
|
||||
REPO_ROOT="${INSTALL_ROOT}"
|
||||
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
||||
LIB_DIR="${DEPLOY_DIR}/lib"
|
||||
sync_repo_if_present "${REPO_ROOT}"
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
||||
LIB_DIR="${DEPLOY_DIR}/lib"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "crypto_okx_hub 部署管理器 — 首次自举"
|
||||
echo "将克隆到: ${INSTALL_ROOT}"
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "错误: 请使用 root 执行" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y git ca-certificates curl
|
||||
else
|
||||
echo "错误: 未找到 git" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||
echo "错误: ${INSTALL_ROOT} 已存在但不是有效仓库" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$(dirname "${INSTALL_ROOT}")"
|
||||
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||
exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" </dev/tty
|
||||
}
|
||||
|
||||
show_banner() {
|
||||
local ip path
|
||||
path="${INSTALL_ROOT}"
|
||||
if repo_ready "${REPO_ROOT:-}"; then
|
||||
path="${REPO_ROOT}"
|
||||
fi
|
||||
if [[ -f "${LIB_DIR}/common.sh" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${LIB_DIR}/common.sh"
|
||||
ip="$(detect_server_ip)"
|
||||
else
|
||||
ip="?"
|
||||
fi
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════╗"
|
||||
echo "║ crypto_okx_hub 部署管理器 ║"
|
||||
echo "║ 仓库: git.bz121.com/dekun/crypto_okx_hub"
|
||||
echo "║ 路径: ${path}"
|
||||
echo "║ IP: ${ip}"
|
||||
echo "╚══════════════════════════════════════╝"
|
||||
echo ""
|
||||
}
|
||||
|
||||
show_menu() {
|
||||
echo " 1) 一键部署(检测环境 + 依赖 + PM2)"
|
||||
echo " 2) 一键卸载"
|
||||
echo " 3) 更新"
|
||||
echo " 4) 仅检测环境/依赖(不安装)"
|
||||
echo " 0) 退出"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main_menu() {
|
||||
bootstrap_repo
|
||||
# shellcheck source=lib/common.sh
|
||||
source "${LIB_DIR}/common.sh"
|
||||
REPO_ROOT="$(resolve_repo_root || echo "${INSTALL_ROOT}")"
|
||||
|
||||
while true; do
|
||||
show_banner
|
||||
show_menu
|
||||
local choice=""
|
||||
cm_read choice "请选择 [0-4]: "
|
||||
case "${choice}" in
|
||||
1) bash "${LIB_DIR}/install.sh" ;;
|
||||
2) bash "${LIB_DIR}/uninstall.sh" ;;
|
||||
3) bash "${LIB_DIR}/update.sh" ;;
|
||||
4) bash "${LIB_DIR}/doctor.sh" ;;
|
||||
0) echo "再见."; exit 0 ;;
|
||||
*) echo "无效选项,请输入 0-4" ;;
|
||||
esac
|
||||
echo ""
|
||||
cm_read _ "按 Enter 返回菜单..."
|
||||
done
|
||||
}
|
||||
|
||||
main_menu "$@"
|
||||
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
# 服务器上拉代码并重启 PM2.
|
||||
# 用法(/opt/crypto_okx_hub 下 root):
|
||||
# bash deploy/pull_and_restart.sh
|
||||
# bash deploy/pull_and_restart.sh --dry-run
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
REPO="${REPO:-/opt/crypto_okx_hub}"
|
||||
PM2_APP_NAME="${PM2_APP_NAME:-crypto_okx_hub}"
|
||||
DRY=0
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY=1
|
||||
echo "(dry-run mode)"
|
||||
fi
|
||||
|
||||
pm2_has_app() {
|
||||
local name="$1"
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
pm2 jlist 2>/dev/null | python3 -c '
|
||||
import json,sys
|
||||
name=sys.argv[1]
|
||||
try:
|
||||
apps=json.load(sys.stdin)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
sys.exit(0 if any(isinstance(a,dict) and a.get("name")==name for a in (apps or [])) else 1)
|
||||
' "${name}"
|
||||
return $?
|
||||
fi
|
||||
pm2 describe "${name}" 2>/dev/null | grep -qE 'status[[:space:]]*[|:]'
|
||||
}
|
||||
|
||||
read_port() {
|
||||
local env_file="${REPO}/.env"
|
||||
local port="5100"
|
||||
if [[ -f "${env_file}" ]]; then
|
||||
local line
|
||||
line="$(grep -E '^[[:space:]]*APP_PORT=' "${env_file}" | tail -n1 || true)"
|
||||
if [[ -n "${line}" ]]; then
|
||||
port="${line#*=}"
|
||||
port="$(echo "${port}" | tr -d '"' | tr -d "'" | xargs)"
|
||||
fi
|
||||
fi
|
||||
echo "${port:-5100}"
|
||||
}
|
||||
|
||||
wait_http_ready() {
|
||||
local port="$1"
|
||||
local url="http://127.0.0.1:${port}/login"
|
||||
local i code
|
||||
echo ">>> 等待 HTTP 就绪 ${url} (最多 30s)"
|
||||
for i in $(seq 1 30); do
|
||||
code="$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 2 --max-time 3 "${url}" 2>/dev/null || echo "000")"
|
||||
if [[ "${code}" == "200" || "${code}" == "302" || "${code}" == "301" || "${code}" == "401" || "${code}" == "403" ]]; then
|
||||
echo ">>> HTTP OK (${code}) after ${i}s"
|
||||
return 0
|
||||
fi
|
||||
if ! pm2_has_app "${PM2_APP_NAME}"; then
|
||||
echo ">>> PM2 进程已消失" >&2
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo ">>> HTTP 仍未就绪 (最后 code=${code:-000})" >&2
|
||||
echo ">>> 请查看: pm2 logs ${PM2_APP_NAME} --lines 50" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
cd "${REPO}"
|
||||
echo ">>> git pull"
|
||||
git pull
|
||||
|
||||
if [[ "${DRY}" -eq 1 ]]; then
|
||||
echo "(dry-run, skip pm2 restart)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
echo "warn: 未安装 pm2" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f ecosystem.config.cjs ]]; then
|
||||
echo "error: 缺少 ecosystem.config.cjs" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if pm2_has_app "${PM2_APP_NAME}"; then
|
||||
echo ">>> pm2 restart ${PM2_APP_NAME} --update-env"
|
||||
pm2 restart "${PM2_APP_NAME}" --update-env
|
||||
else
|
||||
echo ">>> 未注册 ${PM2_APP_NAME},执行 pm2 start ecosystem.config.cjs"
|
||||
pm2 start ecosystem.config.cjs
|
||||
fi
|
||||
|
||||
port="$(read_port)"
|
||||
wait_http_ready "${port}" || true
|
||||
|
||||
echo "done"
|
||||
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env bash
|
||||
# crypto_okx_hub 一键环境部署(Ubuntu / root /opt/crypto_okx_hub)
|
||||
#
|
||||
# 用法:
|
||||
# bash deploy/setup_env.sh
|
||||
# bash deploy/setup_env.sh --skip-pm2
|
||||
# bash deploy/setup_env.sh --recreate-venv
|
||||
# bash deploy/setup_env.sh --install-system-deps
|
||||
#
|
||||
set -e
|
||||
set -u
|
||||
if [ -n "${BASH_VERSION:-}" ]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
||||
REQ_FILE="${REPO_ROOT}/requirements.txt"
|
||||
# shellcheck source=lib/common.sh
|
||||
source "${DEPLOY_DIR}/lib/common.sh"
|
||||
|
||||
SKIP_PM2=0
|
||||
SKIP_ENV_COPY=0
|
||||
RECREATE_VENV=0
|
||||
INSTALL_APT_DEPS=0
|
||||
PY=""
|
||||
|
||||
usage() {
|
||||
sed -n '2,10p' "$0" | sed 's/^# \?//'
|
||||
exit "${1:-0}"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--skip-pm2) SKIP_PM2=1; shift ;;
|
||||
--skip-env-copy) SKIP_ENV_COPY=1; shift ;;
|
||||
--recreate-venv) RECREATE_VENV=1; shift ;;
|
||||
--install-system-deps) INSTALL_APT_DEPS=1; shift ;;
|
||||
-h|--help) usage 0 ;;
|
||||
*) echo "未知参数: $1" >&2; usage 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
find_python() {
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
echo python3
|
||||
return
|
||||
fi
|
||||
if command -v python >/dev/null 2>&1; then
|
||||
echo python
|
||||
return
|
||||
fi
|
||||
echo "未找到 python3/python,请先安装 Python 3.10+" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_python_version() {
|
||||
local py="$1"
|
||||
local ver
|
||||
ver="$("${py}" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
|
||||
local major minor
|
||||
major="${ver%%.*}"
|
||||
minor="${ver#*.}"
|
||||
if [[ "${major}" -lt 3 ]] || [[ "${major}" -eq 3 && "${minor}" -lt 10 ]]; then
|
||||
echo "需要 Python 3.10+,当前: ${ver}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Python: $("${py}" --version 2>&1)"
|
||||
}
|
||||
|
||||
python_minor_version() {
|
||||
local py="$1"
|
||||
"${py}" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")'
|
||||
}
|
||||
|
||||
check_venv_available() {
|
||||
local py="$1"
|
||||
local tmp
|
||||
tmp="$(mktemp -d 2>/dev/null || mktemp -d -t cmvenv)"
|
||||
if "${py}" -m venv "${tmp}" >/dev/null 2>&1 && [[ -x "${tmp}/bin/python" ]]; then
|
||||
rm -rf "${tmp}"
|
||||
return 0
|
||||
fi
|
||||
rm -rf "${tmp}" 2>/dev/null || true
|
||||
return 1
|
||||
}
|
||||
|
||||
install_debian_venv_packages() {
|
||||
local py="$1"
|
||||
local ver
|
||||
ver="$(python_minor_version "${py}")"
|
||||
if ! command -v apt-get >/dev/null 2>&1; then
|
||||
echo " 未检测到 apt-get,请手动安装 python${ver}-venv 与 python3-pip" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo " 需要 root 安装系统包,请执行:" >&2
|
||||
echo " sudo apt update && sudo apt install -y python${ver}-venv python3-pip curl" >&2
|
||||
echo " 或: sudo bash deploy/setup_env.sh --install-system-deps" >&2
|
||||
return 1
|
||||
fi
|
||||
step "安装系统依赖 (python${ver}-venv) ..."
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
if ! apt-get install -y "python${ver}-venv" python3-pip curl ca-certificates git; then
|
||||
apt-get install -y python3-venv python3-pip curl ca-certificates git
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_venv_prereqs() {
|
||||
local py="$1"
|
||||
if check_venv_available "${py}"; then
|
||||
return 0
|
||||
fi
|
||||
echo " 当前 Python 无法创建 venv(缺少 ensurepip,常见于未安装 python*-venv)" >&2
|
||||
if [[ "${INSTALL_APT_DEPS}" -eq 1 ]] || [[ "$(id -u)" -eq 0 ]]; then
|
||||
install_debian_venv_packages "${py}" || exit 1
|
||||
if check_venv_available "${py}"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
local ver
|
||||
ver="$(python_minor_version "${py}")"
|
||||
echo "请安装后重试:" >&2
|
||||
echo " apt update && apt install -y python${ver}-venv python3-pip" >&2
|
||||
echo " bash deploy/setup_env.sh" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
create_project_venv() {
|
||||
local py="$1"
|
||||
if [[ "${RECREATE_VENV}" -eq 1 && -d .venv ]]; then
|
||||
echo " 删除旧 venv ..."
|
||||
rm -rf .venv
|
||||
fi
|
||||
if [[ -d .venv && ! -x .venv/bin/python ]]; then
|
||||
echo " 清理未完成的 venv ..."
|
||||
rm -rf .venv
|
||||
fi
|
||||
if [[ -x .venv/bin/python ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo " 创建 venv ..."
|
||||
if ! "${py}" -m venv .venv; then
|
||||
rm -rf .venv 2>/dev/null || true
|
||||
echo " venv 创建失败" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_flask_secret() {
|
||||
local env_file="${REPO_ROOT}/.env"
|
||||
[[ -f "${env_file}" ]] || return 0
|
||||
local line val
|
||||
line="$(grep -E '^[[:space:]]*FLASK_SECRET_KEY=' "${env_file}" | tail -n1 || true)"
|
||||
val="${line#*=}"
|
||||
val="$(echo "${val}" | tr -d '"' | tr -d "'" | xargs)"
|
||||
if [[ -z "${val}" || "${val}" == "change-me-to-long-random" || "${val}" == "hub-dev-secret" ]]; then
|
||||
local secret
|
||||
secret="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))' 2>/dev/null || openssl rand -hex 24)"
|
||||
if grep -qE '^[[:space:]]*FLASK_SECRET_KEY=' "${env_file}"; then
|
||||
sed -i "s|^[[:space:]]*FLASK_SECRET_KEY=.*|FLASK_SECRET_KEY=${secret}|" "${env_file}"
|
||||
else
|
||||
echo "FLASK_SECRET_KEY=${secret}" >>"${env_file}"
|
||||
fi
|
||||
echo " 已生成 FLASK_SECRET_KEY"
|
||||
else
|
||||
echo " 保留已有 FLASK_SECRET_KEY"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_app() {
|
||||
step "crypto_okx_hub"
|
||||
cd "${REPO_ROOT}"
|
||||
create_project_venv "${PY}"
|
||||
pip_upgrade_tools ".venv/bin/pip"
|
||||
pip_install_requirements ".venv/bin/pip" "${REQ_FILE}" "crypto_okx_hub 依赖"
|
||||
if [[ "${SKIP_ENV_COPY}" -eq 0 ]]; then
|
||||
if [[ -f .env.example && ! -f .env ]]; then
|
||||
cp -n .env.example .env 2>/dev/null || cp .env.example .env
|
||||
echo " 已复制 .env.example -> .env"
|
||||
elif [[ -f .env ]]; then
|
||||
echo " 保留已有 .env"
|
||||
else
|
||||
echo " 无 .env.example,请手动配置 .env"
|
||||
fi
|
||||
fi
|
||||
ensure_flask_secret
|
||||
echo " 完成: ${REPO_ROOT}/.venv/bin/python"
|
||||
}
|
||||
|
||||
install_pm2_optional() {
|
||||
if [[ "${SKIP_PM2}" -eq 1 ]]; then
|
||||
return
|
||||
fi
|
||||
step "PM2(可选)"
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo " 未检测到 Node.js,跳过.安装后执行: npm install -g pm2"
|
||||
return
|
||||
fi
|
||||
if command -v pm2 >/dev/null 2>&1; then
|
||||
echo " PM2 已安装: $(pm2 -v)"
|
||||
return
|
||||
fi
|
||||
echo " 正在安装 pm2 ..."
|
||||
npm install -g pm2
|
||||
}
|
||||
|
||||
echo "crypto_okx_hub 环境部署"
|
||||
echo "仓库根目录: ${REPO_ROOT}"
|
||||
echo "远端仓库: ${GIT_URL}"
|
||||
|
||||
[[ -f "${REQ_FILE}" ]] || { echo "缺少 ${REQ_FILE}" >&2; exit 1; }
|
||||
|
||||
PY="$(find_python)"
|
||||
check_python_version "${PY}"
|
||||
ensure_venv_prereqs "${PY}"
|
||||
|
||||
setup_app
|
||||
install_pm2_optional
|
||||
|
||||
echo ""
|
||||
echo "部署完成.下一步:"
|
||||
echo " 1. 编辑 ${REPO_ROOT}/.env (默认账号 admin / admin123)"
|
||||
echo " 2. pm2 start ecosystem.config.cjs"
|
||||
echo " 或: bash deploy/manage.sh # 选 1"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user