2026-06-04 14:05:06 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
|
|
# QZMusic-Web 一键安装脚本
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# 从 Gitea 仓库获取并自动部署,安装后以 systemd 服务对外暴露 1219 端口
|
|
|
|
|
|
# 使用方法:
|
|
|
|
|
|
# bash <(curl -sL http://171.80.3.149:4321/miao-moe/QZMusic-Web/raw/branch/master/install.sh)
|
|
|
|
|
|
# 或直接在项目目录运行: ./install.sh
|
2026-06-04 14:05:06 +00:00
|
|
|
|
|
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
|
|
echo "=========================================="
|
2026-06-13 17:11:28 +00:00
|
|
|
|
echo " QZMusic-Web 一键安装 (systemd)"
|
2026-06-04 14:05:06 +00:00
|
|
|
|
echo "=========================================="
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 配置 =========
|
2026-06-04 14:05:06 +00:00
|
|
|
|
REPO_URL="http://171.80.3.149:4321/miao-moe/QZMusic-Web.git"
|
|
|
|
|
|
INSTALL_DIR="/opt/QZMusic-Web"
|
2026-06-13 17:11:28 +00:00
|
|
|
|
PORT=1219
|
|
|
|
|
|
SERVICE_NAME="qzmusic-web"
|
|
|
|
|
|
BRANCH="master"
|
2026-06-04 14:05:06 +00:00
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 颜色 =========
|
2026-06-04 14:05:06 +00:00
|
|
|
|
GREEN='\033[0;32m'
|
|
|
|
|
|
YELLOW='\033[1;33m'
|
|
|
|
|
|
RED='\033[0;31m'
|
2026-06-13 17:11:28 +00:00
|
|
|
|
NC='\033[0m'
|
2026-06-04 14:05:06 +00:00
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
|
|
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
|
|
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
2026-06-04 14:05:06 +00:00
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 权限 =========
|
|
|
|
|
|
SUDO=""
|
2026-06-04 14:05:06 +00:00
|
|
|
|
if [ "$EUID" -ne 0 ]; then
|
2026-06-13 17:11:28 +00:00
|
|
|
|
SUDO="sudo"
|
|
|
|
|
|
log_warn "当前非 root,将使用 sudo 执行系统级操作。"
|
2026-06-04 14:05:06 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= Node.js 检查 =========
|
2026-06-04 14:05:06 +00:00
|
|
|
|
if ! command -v node &> /dev/null; then
|
2026-06-13 17:11:28 +00:00
|
|
|
|
log_error "Node.js 未安装!请先安装 Node.js (推荐 v18+)"
|
|
|
|
|
|
echo " Ubuntu/Debian: curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs"
|
|
|
|
|
|
echo " CentOS/RHEL: curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - && sudo yum install -y nodejs"
|
2026-06-04 14:05:06 +00:00
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
log_info "Node.js 版本: $(node -v)"
|
|
|
|
|
|
log_info "npm 版本: $(npm -v)"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 克隆/拉取代码 =========
|
2026-06-04 14:05:06 +00:00
|
|
|
|
if [ -d "$INSTALL_DIR" ]; then
|
2026-06-13 17:11:28 +00:00
|
|
|
|
if [ -d "$INSTALL_DIR/.git" ]; then
|
|
|
|
|
|
log_info "检测到已有 git 仓库,正在更新到最新版本..."
|
|
|
|
|
|
cd "$INSTALL_DIR"
|
|
|
|
|
|
git fetch origin "$BRANCH"
|
|
|
|
|
|
git reset --hard "origin/$BRANCH"
|
|
|
|
|
|
if [ $? -ne 0 ]; then
|
|
|
|
|
|
log_error "更新失败!"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
else
|
|
|
|
|
|
log_warn "目录 $INSTALL_DIR 已存在但不是 git 仓库,将清理后重新克隆..."
|
|
|
|
|
|
$SUDO rm -rf "$INSTALL_DIR"
|
|
|
|
|
|
log_info "正在从仓库获取代码 ($REPO_URL)..."
|
|
|
|
|
|
$SUDO git clone -b "$BRANCH" "$REPO_URL" "$INSTALL_DIR"
|
|
|
|
|
|
$SUDO chown -R "$(whoami)" "$INSTALL_DIR" 2>/dev/null || true
|
2026-06-04 14:05:06 +00:00
|
|
|
|
fi
|
|
|
|
|
|
else
|
|
|
|
|
|
log_info "正在创建安装目录: $INSTALL_DIR"
|
2026-06-13 17:11:28 +00:00
|
|
|
|
$SUDO mkdir -p "$(dirname "$INSTALL_DIR")"
|
|
|
|
|
|
log_info "正在从仓库获取代码 ($REPO_URL)..."
|
|
|
|
|
|
$SUDO git clone -b "$BRANCH" "$REPO_URL" "$INSTALL_DIR"
|
|
|
|
|
|
$SUDO chown -R "$(whoami)" "$INSTALL_DIR" 2>/dev/null || true
|
2026-06-04 14:05:06 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
cd "$INSTALL_DIR"
|
2026-06-13 17:11:28 +00:00
|
|
|
|
echo ""
|
2026-06-04 14:05:06 +00:00
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 安装依赖 =========
|
|
|
|
|
|
log_info "正在安装依赖 (使用淘宝镜像加速)..."
|
|
|
|
|
|
npm install --registry=https://registry.npmmirror.com --loglevel=error
|
2026-06-04 14:05:06 +00:00
|
|
|
|
if [ $? -ne 0 ]; then
|
|
|
|
|
|
log_error "依赖安装失败!"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
log_info "依赖安装成功!"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 构建 =========
|
2026-06-04 14:05:06 +00:00
|
|
|
|
log_info "正在构建项目..."
|
|
|
|
|
|
npm run build
|
|
|
|
|
|
if [ $? -ne 0 ]; then
|
|
|
|
|
|
log_error "构建失败!"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
log_info "项目构建成功!"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 部署 systemd 服务 =========
|
|
|
|
|
|
log_info "正在部署 systemd 服务: $SERVICE_NAME"
|
|
|
|
|
|
|
|
|
|
|
|
SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service"
|
|
|
|
|
|
|
|
|
|
|
|
# 停止旧的监听(以防之前有进程占端口)
|
|
|
|
|
|
if command -v lsof &> /dev/null; then
|
|
|
|
|
|
OLD_PID=$(lsof -ti:$PORT 2>/dev/null || true)
|
|
|
|
|
|
if [ -n "$OLD_PID" ]; then
|
|
|
|
|
|
log_warn "端口 $PORT 已被占用 (PID: $OLD_PID),正在清理..."
|
|
|
|
|
|
$SUDO kill $OLD_PID 2>/dev/null || true
|
|
|
|
|
|
sleep 2
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 如果之前有同名服务,先停掉
|
|
|
|
|
|
if systemctl list-unit-files 2>/dev/null | grep -q "$SERVICE_NAME.service"; then
|
|
|
|
|
|
$SUDO systemctl stop "$SERVICE_NAME" 2>/dev/null || true
|
|
|
|
|
|
$SUDO systemctl disable "$SERVICE_NAME" 2>/dev/null || true
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 写入服务文件
|
|
|
|
|
|
$SUDO tee "$SERVICE_FILE" > /dev/null <<EOF
|
|
|
|
|
|
[Unit]
|
|
|
|
|
|
Description=QZMusic Web Server
|
|
|
|
|
|
After=network.target
|
|
|
|
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
|
|
Type=simple
|
|
|
|
|
|
WorkingDirectory=$INSTALL_DIR
|
|
|
|
|
|
ExecStart=$(command -v node) $INSTALL_DIR/server.cjs
|
|
|
|
|
|
Restart=always
|
|
|
|
|
|
RestartSec=3
|
|
|
|
|
|
Environment=NODE_ENV=production
|
|
|
|
|
|
Environment=QZMUSIC_PORT=$PORT
|
|
|
|
|
|
Environment=QZMUSIC_HOST=0.0.0.0
|
|
|
|
|
|
StandardOutput=journal
|
|
|
|
|
|
StandardError=journal
|
|
|
|
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
|
|
# 重载 & 启动
|
|
|
|
|
|
$SUDO systemctl daemon-reload
|
|
|
|
|
|
$SUDO systemctl enable "$SERVICE_NAME"
|
|
|
|
|
|
$SUDO systemctl restart "$SERVICE_NAME"
|
|
|
|
|
|
sleep 2
|
|
|
|
|
|
|
|
|
|
|
|
# ========= 防火墙开放 =========
|
|
|
|
|
|
log_info "正在开放防火墙端口 $PORT..."
|
|
|
|
|
|
if command -v firewall-cmd &> /dev/null && systemctl is-active --quiet firewalld 2>/dev/null; then
|
|
|
|
|
|
$SUDO firewall-cmd --permanent --add-port=${PORT}/tcp >/dev/null
|
|
|
|
|
|
$SUDO firewall-cmd --reload >/dev/null
|
|
|
|
|
|
log_info "firewalld: 已放行 $PORT/tcp"
|
|
|
|
|
|
elif command -v ufw &> /dev/null && systemctl is-active --quiet ufw 2>/dev/null; then
|
|
|
|
|
|
$SUDO ufw allow ${PORT}/tcp >/dev/null
|
|
|
|
|
|
log_info "ufw: 已放行 $PORT/tcp"
|
|
|
|
|
|
else
|
|
|
|
|
|
log_warn "未检测到活动的 firewalld/ufw,跳过防火墙配置。如有其他防火墙请手动放行 $PORT/tcp。"
|
2026-06-04 14:05:06 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2026-06-13 17:11:28 +00:00
|
|
|
|
# ========= 云服务器安全组提醒 =========
|
|
|
|
|
|
log_info "已处理系统防火墙。如果你的机器是云服务器,请记得在云厂商控制台的『安全组』也放行 $PORT/tcp。"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
# ========= 验证 =========
|
|
|
|
|
|
log_info "等待服务启动..."
|
|
|
|
|
|
for i in 1 2 3 4 5; do
|
|
|
|
|
|
if curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:$PORT/" 2>/dev/null | grep -q "200"; then
|
|
|
|
|
|
break
|
|
|
|
|
|
fi
|
|
|
|
|
|
sleep 1
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:$PORT/" 2>/dev/null || echo "000")
|
|
|
|
|
|
STATUS=$($SUDO systemctl is-active "$SERVICE_NAME" 2>/dev/null || echo "unknown")
|
|
|
|
|
|
|
|
|
|
|
|
# 获取本机公网/局域网 IP
|
|
|
|
|
|
LAN_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
|
|
|
|
|
WAN_IP=$(curl -s --max-time 5 https://ifconfig.me 2>/dev/null || curl -s --max-time 5 https://api.ipify.org 2>/dev/null || echo "")
|
|
|
|
|
|
|
2026-06-04 14:05:06 +00:00
|
|
|
|
echo ""
|
|
|
|
|
|
echo "=========================================="
|
|
|
|
|
|
echo " 🎉 安装完成!"
|
|
|
|
|
|
echo "=========================================="
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "📂 安装目录: $INSTALL_DIR"
|
2026-06-13 17:11:28 +00:00
|
|
|
|
echo "🖥 服务状态: $STATUS"
|
|
|
|
|
|
echo "🌐 本地访问: http://127.0.0.1:$PORT (HTTP $HTTP_CODE)"
|
|
|
|
|
|
if [ -n "$LAN_IP" ]; then
|
|
|
|
|
|
echo "🏠 局域网: http://$LAN_IP:$PORT"
|
|
|
|
|
|
fi
|
|
|
|
|
|
if [ -n "$WAN_IP" ]; then
|
|
|
|
|
|
echo "🌍 公网: http://$WAN_IP:$PORT"
|
|
|
|
|
|
fi
|
2026-06-04 14:05:06 +00:00
|
|
|
|
echo ""
|
|
|
|
|
|
echo "管理命令:"
|
2026-06-13 17:11:28 +00:00
|
|
|
|
echo " 查看日志: sudo journalctl -u $SERVICE_NAME -f --since \"5 min ago\""
|
|
|
|
|
|
echo " 启动服务: sudo systemctl start $SERVICE_NAME"
|
|
|
|
|
|
echo " 停止服务: sudo systemctl stop $SERVICE_NAME"
|
|
|
|
|
|
echo " 重启服务: sudo systemctl restart $SERVICE_NAME"
|
|
|
|
|
|
echo " 开机自启: sudo systemctl enable $SERVICE_NAME"
|
|
|
|
|
|
echo " 更新代码: cd $INSTALL_DIR && git pull && npm run build && sudo systemctl restart $SERVICE_NAME"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "卸载/清除所有部署: bash $INSTALL_DIR/uninstall.sh"
|
2026-06-04 14:05:06 +00:00
|
|
|
|
echo ""
|