支持 curl 管道打开部署菜单。
服务器可直接拉取 deploy/manage.sh,克隆仓库后选择部署、卸载、更新或退出。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,341 @@
|
||||
#!/usr/bin/env bash
|
||||
# HyperNode-AI 部署管理器 — 一键部署 / 卸载 / 更新
|
||||
#
|
||||
# 新服务器:
|
||||
# curl -fsSL https://git.bz121.com/dekun/HyperNode-AI/raw/branch/main/deploy/manage.sh | bash
|
||||
#
|
||||
# 已安装:
|
||||
# bash /opt/hypernode/deploy/manage.sh
|
||||
#
|
||||
set -e
|
||||
if [[ -n "${BASH_VERSION:-}" ]]; then
|
||||
set -o pipefail
|
||||
fi
|
||||
|
||||
INSTALL_ROOT="${INSTALL_ROOT:-/opt/hypernode}"
|
||||
DATA_DIR="${DATA_DIR:-/var/lib/hypernode}"
|
||||
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/HyperNode-AI.git}"
|
||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||
APP_USER="hypernode"
|
||||
SERVICE_NAME="hypernode"
|
||||
BIND_HOST="0.0.0.0"
|
||||
BIND_PORT="33333"
|
||||
ENV_FILE="${INSTALL_ROOT}/.env"
|
||||
|
||||
_script_src="${BASH_SOURCE[0]:-}"
|
||||
if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then
|
||||
REPO_ROOT="$(cd "$(dirname "${_script_src}")/.." && pwd)"
|
||||
else
|
||||
REPO_ROOT=""
|
||||
fi
|
||||
unset _script_src
|
||||
|
||||
set -u
|
||||
|
||||
log() {
|
||||
printf '\n==> %s\n' "$*"
|
||||
}
|
||||
|
||||
ask() {
|
||||
local value=""
|
||||
if [[ ! -r /dev/tty ]]; then
|
||||
echo "请在终端中运行。通过 curl 管道执行时,也需要当前会话有终端。" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! read -r -p "$1" value </dev/tty; then
|
||||
exit 0
|
||||
fi
|
||||
printf '%s' "${value}"
|
||||
}
|
||||
|
||||
repo_ready() {
|
||||
[[ -f "${1}/app/main.py" && -f "${1}/requirements.txt" && -f "${1}/deploy/manage.sh" ]]
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "请使用 root 执行。可以先 sudo -i,再运行安装命令。" >&2
|
||||
echo "或:curl -fsSL https://git.bz121.com/dekun/HyperNode-AI/raw/branch/main/deploy/manage.sh | sudo bash" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_git() {
|
||||
if command -v git >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
if ! command -v apt-get >/dev/null 2>&1; then
|
||||
echo "未找到 git,且当前系统没有 apt-get。" >&2
|
||||
exit 1
|
||||
fi
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y git ca-certificates curl
|
||||
}
|
||||
|
||||
bootstrap_repo() {
|
||||
if repo_ready "${INSTALL_ROOT}"; then
|
||||
REPO_ROOT="${INSTALL_ROOT}"
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "HyperNode-AI 部署管理器 — 首次自举"
|
||||
echo "将克隆到: ${INSTALL_ROOT}"
|
||||
require_root
|
||||
ensure_git
|
||||
if [[ -e "${INSTALL_ROOT}" ]]; then
|
||||
echo "错误: ${INSTALL_ROOT} 已存在,但不是有效的 HyperNode-AI 目录。" >&2
|
||||
echo "请先处理该目录,或设置 INSTALL_ROOT 指向其他路径。" >&2
|
||||
exit 1
|
||||
fi
|
||||
git clone --branch "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||
exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" </dev/tty
|
||||
}
|
||||
|
||||
install_packages() {
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y python3 python3-venv python3-pip git ca-certificates curl rsync
|
||||
python3 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)'
|
||||
}
|
||||
|
||||
ensure_user() {
|
||||
if ! id -u "${APP_USER}" >/dev/null 2>&1; then
|
||||
useradd --system --home-dir "${INSTALL_ROOT}" --shell /usr/sbin/nologin "${APP_USER}"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_install_tree() {
|
||||
if repo_ready "${INSTALL_ROOT}"; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "${REPO_ROOT}" && "${REPO_ROOT}" != "${INSTALL_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||
mkdir -p "${INSTALL_ROOT}"
|
||||
rsync -a --delete \
|
||||
--exclude '.venv/' \
|
||||
--exclude '.env' \
|
||||
--exclude '__pycache__/' \
|
||||
--exclude '*.db' \
|
||||
"${REPO_ROOT}/" "${INSTALL_ROOT}/"
|
||||
return 0
|
||||
fi
|
||||
ensure_git
|
||||
git clone --branch "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||
}
|
||||
|
||||
write_env() {
|
||||
mkdir -p "${DATA_DIR}"
|
||||
if [[ -f "${ENV_FILE}" ]]; then
|
||||
log "保留已有配置 ${ENV_FILE}"
|
||||
return 0
|
||||
fi
|
||||
log "写入 ${ENV_FILE}"
|
||||
cat >"${ENV_FILE}" <<EOF
|
||||
DATABASE_URL=sqlite:////${DATA_DIR}/hypernode.db
|
||||
YIELD_TIMEZONE=Asia/Shanghai
|
||||
YIELD_HOUR=0
|
||||
YIELD_MINUTE=5
|
||||
DISABLE_SCHEDULER=false
|
||||
EOF
|
||||
}
|
||||
|
||||
install_python_deps() {
|
||||
if [[ ! -x "${INSTALL_ROOT}/.venv/bin/python" ]]; then
|
||||
python3 -m venv "${INSTALL_ROOT}/.venv"
|
||||
fi
|
||||
"${INSTALL_ROOT}/.venv/bin/pip" install --upgrade pip
|
||||
"${INSTALL_ROOT}/.venv/bin/pip" install -r "${INSTALL_ROOT}/requirements.txt"
|
||||
}
|
||||
|
||||
write_service() {
|
||||
cat >"/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
|
||||
[Unit]
|
||||
Description=HyperNode-AI control plane
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${APP_USER}
|
||||
Group=${APP_USER}
|
||||
WorkingDirectory=${INSTALL_ROOT}
|
||||
EnvironmentFile=${ENV_FILE}
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Environment=PYTHONDONTWRITEBYTECODE=1
|
||||
ExecStart=${INSTALL_ROOT}/.venv/bin/uvicorn app.main:app --host ${BIND_HOST} --port ${BIND_PORT} --workers 1
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
TimeoutStopSec=20
|
||||
KillSignal=SIGINT
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
fix_permissions() {
|
||||
mkdir -p "${DATA_DIR}"
|
||||
chown -R "${APP_USER}:${APP_USER}" "${INSTALL_ROOT}" "${DATA_DIR}"
|
||||
chmod 750 "${INSTALL_ROOT}" "${DATA_DIR}"
|
||||
if [[ -f "${ENV_FILE}" ]]; then
|
||||
chmod 640 "${ENV_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
wait_until_ready() {
|
||||
local ready=0
|
||||
local _i
|
||||
for _i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
if curl -fsS "http://127.0.0.1:${BIND_PORT}/health" >/dev/null; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [[ "${ready}" -ne 1 ]]; then
|
||||
echo "服务未在 10 秒内就绪。最近日志:" >&2
|
||||
journalctl -u "${SERVICE_NAME}.service" -n 50 --no-pager >&2 || true
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_deploy() {
|
||||
log "一键部署"
|
||||
install_packages
|
||||
ensure_user
|
||||
ensure_install_tree
|
||||
write_env
|
||||
install_python_deps
|
||||
fix_permissions
|
||||
write_service
|
||||
systemctl enable "${SERVICE_NAME}.service"
|
||||
systemctl restart "${SERVICE_NAME}.service"
|
||||
wait_until_ready
|
||||
log "部署完成"
|
||||
echo "监听地址:${BIND_HOST}:${BIND_PORT}"
|
||||
echo "健康检查:curl http://127.0.0.1:${BIND_PORT}/health"
|
||||
echo "接口文档:http://<服务器 IP>:${BIND_PORT}/docs"
|
||||
echo "程序目录:${INSTALL_ROOT}"
|
||||
echo "数据库:${DATA_DIR}/hypernode.db"
|
||||
echo "日志:journalctl -u ${SERVICE_NAME}.service -f"
|
||||
echo "反代请自行配置,上游地址使用 127.0.0.1:${BIND_PORT}"
|
||||
}
|
||||
|
||||
cmd_update() {
|
||||
if [[ ! -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
|
||||
echo "尚未部署。请先选择「一键部署」。" >&2
|
||||
return 1
|
||||
fi
|
||||
log "更新程序"
|
||||
install_packages
|
||||
if [[ -d "${INSTALL_ROOT}/.git" ]]; then
|
||||
git -C "${INSTALL_ROOT}" pull --ff-only origin "${GIT_BRANCH}"
|
||||
else
|
||||
ensure_install_tree
|
||||
fi
|
||||
install_python_deps
|
||||
fix_permissions
|
||||
write_service
|
||||
systemctl restart "${SERVICE_NAME}.service"
|
||||
wait_until_ready
|
||||
log "更新完成,配置和数据库已保留"
|
||||
}
|
||||
|
||||
cmd_uninstall() {
|
||||
local answer
|
||||
echo "即将停止服务并删除 ${INSTALL_ROOT} 与 systemd 单元。"
|
||||
answer="$(ask "确认卸载请输入 yes:")"
|
||||
if [[ "${answer}" != "yes" ]]; then
|
||||
echo "已取消卸载。"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "卸载服务"
|
||||
systemctl stop "${SERVICE_NAME}.service" >/dev/null 2>&1 || true
|
||||
systemctl disable "${SERVICE_NAME}.service" >/dev/null 2>&1 || true
|
||||
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
systemctl daemon-reload
|
||||
rm -rf "${INSTALL_ROOT}"
|
||||
|
||||
answer="$(ask "同时删除数据库 ${DATA_DIR} ?输入 yes 删除,其他键保留:")"
|
||||
if [[ "${answer}" == "yes" ]]; then
|
||||
rm -rf "${DATA_DIR}"
|
||||
echo "数据库已删除。"
|
||||
else
|
||||
echo "数据库已保留在 ${DATA_DIR}"
|
||||
fi
|
||||
|
||||
if id -u "${APP_USER}" >/dev/null 2>&1; then
|
||||
userdel "${APP_USER}" || echo "系统用户 ${APP_USER} 未删除,可稍后执行 userdel ${APP_USER}"
|
||||
fi
|
||||
log "卸载完成"
|
||||
}
|
||||
|
||||
detect_ip() {
|
||||
hostname -I 2>/dev/null | awk '{print $1}'
|
||||
}
|
||||
|
||||
show_banner() {
|
||||
local ip path
|
||||
ip="$(detect_ip)"
|
||||
path="${INSTALL_ROOT}"
|
||||
[[ -n "${ip}" ]] || ip="?"
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " HyperNode-AI 部署管理器"
|
||||
echo " 仓库: git.bz121.com/dekun/HyperNode-AI"
|
||||
echo " 路径: ${path}"
|
||||
echo " 端口: ${BIND_PORT}"
|
||||
echo " IP: ${ip}"
|
||||
echo "========================================"
|
||||
}
|
||||
|
||||
show_menu() {
|
||||
echo " 1) 一键部署"
|
||||
echo " 2) 一键卸载"
|
||||
echo " 3) 更新"
|
||||
echo " 0) 退出"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main_menu() {
|
||||
local choice
|
||||
bootstrap_repo "$@"
|
||||
require_root
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source /etc/os-release
|
||||
if [[ "${ID:-}" != "ubuntu" ]]; then
|
||||
echo "当前系统是 ${ID:-unknown},这个脚本用于 Ubuntu 22.04。" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${VERSION_ID:-}" != "22.04" ]]; then
|
||||
echo "警告:当前是 Ubuntu ${VERSION_ID:-unknown},脚本按 22.04 编写,将继续。" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
while true; do
|
||||
show_banner
|
||||
show_menu
|
||||
choice="$(ask "请选择 [0-3]: ")"
|
||||
case "${choice}" in
|
||||
1) cmd_deploy || echo "部署失败。" ;;
|
||||
2) cmd_uninstall || echo "卸载失败。" ;;
|
||||
3) cmd_update || echo "更新失败。" ;;
|
||||
0)
|
||||
echo "已退出。"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "无效选项:${choice}" ;;
|
||||
esac
|
||||
ask "按 Enter 返回菜单..." >/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
main_menu "$@"
|
||||
+2
-263
@@ -1,264 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
# HyperNode-AI 交互式部署脚本,适用于 Ubuntu 22.04。
|
||||
# 不安装 Nginx。反代由你自己配置,服务监听 0.0.0.0:33333。
|
||||
#
|
||||
# sudo bash deploy/ubuntu22.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
APP_USER="hypernode"
|
||||
APP_DIR="/opt/hypernode"
|
||||
DATA_DIR="/var/lib/hypernode"
|
||||
ENV_FILE="${APP_DIR}/.env"
|
||||
SERVICE_NAME="hypernode"
|
||||
BIND_HOST="0.0.0.0"
|
||||
BIND_PORT="33333"
|
||||
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "请用 root 运行:sudo bash deploy/ubuntu22.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -t 0 ]]; then
|
||||
echo "请在交互式终端中运行本脚本。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -r /etc/os-release ]]; then
|
||||
echo "无法识别操作系统,这个脚本用于 Ubuntu 22.04。" >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck disable=SC1091
|
||||
source /etc/os-release
|
||||
if [[ "${ID:-}" != "ubuntu" ]]; then
|
||||
echo "当前系统是 ${ID:-unknown},这个脚本用于 Ubuntu 22.04。" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${VERSION_ID:-}" != "22.04" ]]; then
|
||||
echo "警告:当前是 Ubuntu ${VERSION_ID:-unknown},脚本按 22.04 编写,将继续。" >&2
|
||||
fi
|
||||
|
||||
if [[ ! -f "${SOURCE_DIR}/requirements.txt" || ! -f "${SOURCE_DIR}/app/main.py" ]]; then
|
||||
echo "未在 ${SOURCE_DIR} 找到项目文件。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log() {
|
||||
printf '\n==> %s\n' "$*"
|
||||
}
|
||||
|
||||
pause() {
|
||||
read -r -p "按回车返回菜单..." _ || exit 0
|
||||
}
|
||||
|
||||
sync_code() {
|
||||
mkdir -p "${APP_DIR}" "${DATA_DIR}"
|
||||
if [[ "$(realpath "${SOURCE_DIR}")" != "$(realpath "${APP_DIR}")" ]]; then
|
||||
rsync -a --delete \
|
||||
--exclude '.venv/' \
|
||||
--exclude '.env' \
|
||||
--exclude '.git/' \
|
||||
--exclude '__pycache__/' \
|
||||
--exclude '*.db' \
|
||||
--exclude '.pytest_cache/' \
|
||||
"${SOURCE_DIR}/" "${APP_DIR}/"
|
||||
fi
|
||||
}
|
||||
|
||||
write_env() {
|
||||
if [[ -f "${ENV_FILE}" ]]; then
|
||||
log "保留已有配置 ${ENV_FILE}"
|
||||
return
|
||||
fi
|
||||
log "写入 ${ENV_FILE}"
|
||||
cat >"${ENV_FILE}" <<EOF
|
||||
DATABASE_URL=sqlite:////${DATA_DIR}/hypernode.db
|
||||
YIELD_TIMEZONE=Asia/Shanghai
|
||||
YIELD_HOUR=0
|
||||
YIELD_MINUTE=5
|
||||
DISABLE_SCHEDULER=false
|
||||
EOF
|
||||
}
|
||||
|
||||
install_python_deps() {
|
||||
if [[ ! -x "${APP_DIR}/.venv/bin/python" ]]; then
|
||||
python3 -m venv "${APP_DIR}/.venv"
|
||||
fi
|
||||
"${APP_DIR}/.venv/bin/pip" install --upgrade pip
|
||||
"${APP_DIR}/.venv/bin/pip" install -r "${APP_DIR}/requirements.txt"
|
||||
}
|
||||
|
||||
write_service() {
|
||||
cat >"/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
|
||||
[Unit]
|
||||
Description=HyperNode-AI control plane
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${APP_USER}
|
||||
Group=${APP_USER}
|
||||
WorkingDirectory=${APP_DIR}
|
||||
EnvironmentFile=${ENV_FILE}
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Environment=PYTHONDONTWRITEBYTECODE=1
|
||||
ExecStart=${APP_DIR}/.venv/bin/uvicorn app.main:app --host ${BIND_HOST} --port ${BIND_PORT} --workers 1
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
TimeoutStopSec=20
|
||||
KillSignal=SIGINT
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
fix_permissions() {
|
||||
chown -R "${APP_USER}:${APP_USER}" "${APP_DIR}" "${DATA_DIR}"
|
||||
chmod 750 "${APP_DIR}" "${DATA_DIR}"
|
||||
chmod 640 "${ENV_FILE}"
|
||||
}
|
||||
|
||||
wait_until_ready() {
|
||||
local ready=0
|
||||
local _i
|
||||
for _i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
if curl -fsS "http://127.0.0.1:${BIND_PORT}/health" >/dev/null; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [[ "${ready}" -ne 1 ]]; then
|
||||
echo "服务未在 10 秒内就绪。最近日志:" >&2
|
||||
journalctl -u "${SERVICE_NAME}.service" -n 50 --no-pager >&2 || true
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_user() {
|
||||
if ! id -u "${APP_USER}" >/dev/null 2>&1; then
|
||||
useradd --system --home-dir "${APP_DIR}" --shell /usr/sbin/nologin "${APP_USER}"
|
||||
fi
|
||||
}
|
||||
|
||||
install_packages() {
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y python3 python3-venv python3-pip rsync ca-certificates curl
|
||||
python3 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)'
|
||||
}
|
||||
|
||||
cmd_deploy() {
|
||||
log "一键部署"
|
||||
install_packages
|
||||
ensure_user
|
||||
sync_code
|
||||
write_env
|
||||
fix_permissions
|
||||
install_python_deps
|
||||
fix_permissions
|
||||
write_service
|
||||
systemctl enable "${SERVICE_NAME}.service"
|
||||
systemctl restart "${SERVICE_NAME}.service"
|
||||
wait_until_ready
|
||||
log "部署完成"
|
||||
echo "监听地址:${BIND_HOST}:${BIND_PORT}"
|
||||
echo "健康检查:curl http://127.0.0.1:${BIND_PORT}/health"
|
||||
echo "接口文档:http://<服务器 IP>:${BIND_PORT}/docs"
|
||||
echo "程序目录:${APP_DIR}"
|
||||
echo "数据库:${DATA_DIR}/hypernode.db"
|
||||
echo "日志:journalctl -u ${SERVICE_NAME}.service -f"
|
||||
echo "反代请自行配置,上游地址使用 127.0.0.1:${BIND_PORT}"
|
||||
}
|
||||
|
||||
cmd_update() {
|
||||
if [[ ! -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
|
||||
echo "尚未部署。请先选择「一键部署」。" >&2
|
||||
return 1
|
||||
fi
|
||||
log "更新程序"
|
||||
install_packages
|
||||
sync_code
|
||||
install_python_deps
|
||||
fix_permissions
|
||||
write_service
|
||||
systemctl restart "${SERVICE_NAME}.service"
|
||||
wait_until_ready
|
||||
log "更新完成,配置和数据库已保留"
|
||||
}
|
||||
|
||||
cmd_uninstall() {
|
||||
local answer
|
||||
echo "即将停止服务并删除 ${APP_DIR} 与 systemd 单元。"
|
||||
read -r -p "确认卸载请输入 yes:" answer || exit 0
|
||||
if [[ "${answer}" != "yes" ]]; then
|
||||
echo "已取消卸载。"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "卸载服务"
|
||||
systemctl stop "${SERVICE_NAME}.service" || true
|
||||
systemctl disable "${SERVICE_NAME}.service" || true
|
||||
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
systemctl daemon-reload
|
||||
rm -rf "${APP_DIR}"
|
||||
|
||||
read -r -p "同时删除数据库 ${DATA_DIR} ?输入 yes 删除,其他键保留:" answer || exit 0
|
||||
if [[ "${answer}" == "yes" ]]; then
|
||||
rm -rf "${DATA_DIR}"
|
||||
echo "数据库已删除。"
|
||||
else
|
||||
echo "数据库已保留在 ${DATA_DIR}"
|
||||
fi
|
||||
|
||||
if id -u "${APP_USER}" >/dev/null 2>&1; then
|
||||
userdel "${APP_USER}" || echo "系统用户 ${APP_USER} 未删除,可稍后执行 userdel ${APP_USER}"
|
||||
fi
|
||||
log "卸载完成"
|
||||
}
|
||||
|
||||
show_menu() {
|
||||
cat <<EOF
|
||||
|
||||
========================================
|
||||
HyperNode-AI 部署菜单 端口 ${BIND_PORT}
|
||||
========================================
|
||||
1) 一键部署
|
||||
2) 一键卸载
|
||||
3) 更新
|
||||
4) 退出
|
||||
========================================
|
||||
EOF
|
||||
}
|
||||
|
||||
while true; do
|
||||
show_menu
|
||||
read -r -p "请选择 [1-4]: " choice || exit 0
|
||||
case "${choice}" in
|
||||
1)
|
||||
cmd_deploy || echo "部署失败。"
|
||||
pause
|
||||
;;
|
||||
2)
|
||||
cmd_uninstall || echo "卸载失败。"
|
||||
pause
|
||||
;;
|
||||
3)
|
||||
cmd_update || echo "更新失败。"
|
||||
pause
|
||||
;;
|
||||
4)
|
||||
echo "已退出。"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "无效选项:${choice}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# 兼容旧入口,转到 deploy/manage.sh。
|
||||
exec bash "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/manage.sh" "$@"
|
||||
|
||||
Reference in New Issue
Block a user