Files
QZMusic-Web/install.sh

254 lines
8.2 KiB
Bash
Raw Permalink Normal View History

#!/bin/bash
# QZMusic-Web 一键安装脚本
# 从 Gitea 仓库获取并自动部署,安装后以 systemd 服务对外暴露 1219 端口
# 使用方法:
# bash <(curl -sL http://171.80.3.149:4321/miao-moe/QZMusic-Web/raw/branch/master/install.sh)
# 或直接在项目目录运行: ./install.sh
set -e
echo "=========================================="
echo " QZMusic-Web 一键安装 (systemd)"
echo "=========================================="
echo ""
# ========= 配置 =========
REPO_URL="http://171.80.3.149:4321/miao-moe/QZMusic-Web.git"
INSTALL_DIR="/opt/QZMusic-Web"
PORT=1219
SERVICE_NAME="qzmusic-web"
BRANCH="master"
# ========= 颜色 =========
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ========= 权限 =========
SUDO=""
if [ "$EUID" -ne 0 ]; then
SUDO="sudo"
log_warn "当前非 root将使用 sudo 执行系统级操作。"
fi
# ========= Node.js 检查 =========
if ! command -v node &> /dev/null; then
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"
exit 1
fi
log_info "Node.js 版本: $(node -v)"
log_info "npm 版本: $(npm -v)"
echo ""
# ========= 克隆/拉取代码 =========
if [ -d "$INSTALL_DIR" ]; then
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_error "目录 $INSTALL_DIR 已存在但不是 git 仓库,为避免误删数据,已中止安装。"
echo "请手动处理该目录后重新执行脚本,或临时删除它:"
echo " sudo rm -rf $INSTALL_DIR"
exit 1
fi
else
log_info "正在创建安装目录: $INSTALL_DIR"
$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
fi
cd "$INSTALL_DIR"
echo ""
# ========= 安装依赖 =========
log_info "正在安装依赖 (使用淘宝镜像加速)..."
npm install --registry=https://registry.npmmirror.com --loglevel=error
if [ $? -ne 0 ]; then
log_error "依赖安装失败!"
exit 1
fi
log_info "依赖安装成功!"
echo ""
# ========= 构建 =========
log_info "正在构建项目..."
npm run build
if [ $? -ne 0 ]; then
log_error "构建失败!"
exit 1
fi
log_info "项目构建成功!"
echo ""
# ========= 下载官方音源插件 (v3) =========
log_info "正在下载官方音源插件到 dist/plugins/..."
PLUGIN_DIR="$INSTALL_DIR/dist/plugins"
mkdir -p "$PLUGIN_DIR"
FILE_SERVER="http://171.80.3.149:5244"
SHARE_CODE="music"
PLUGIN_FILES=(
"zq_wy_v3.js"
"zq_tx_v3-fix1.js"
"zq_kw_v3-fix1.js"
"zq_kg.js"
"zq_mg_v3.js"
)
DOWNLOADED=0
for fname in "${PLUGIN_FILES[@]}"; do
target="$PLUGIN_DIR/$fname"
# 通过 fs/get API 获取 raw_url (JSON 中 data.raw_url)
api_resp=$(curl -s -X POST "$FILE_SERVER/api/fs/get" \
-H "Content-Type: application/json" \
-d "{\"path\":\"/@s/c6VNt7hG/音源/QZ-Music_v2/官方/v3/$fname\",\"password\":\"$SHARE_CODE\"}" \
--max-time 30 2>/dev/null || echo "")
raw_url=""
if [ -n "$api_resp" ]; then
# 使用 python3 解析 JSON 取 data.raw_url若失败则回退 sd 直链
raw_url=$(echo "$api_resp" | python3 -c "import sys,json;
try:
d=json.load(sys.stdin)
print(d.get('data',{}).get('raw_url',''))
except:
print('')" 2>/dev/null)
fi
if [ -z "$raw_url" ]; then
raw_url="$FILE_SERVER/sd/c6VNt7hG/音源/QZ-Music_v2/官方/v3/$fname?pwd=$SHARE_CODE"
fi
if curl -sL "$raw_url" --max-time 120 -o "$target" -w '%{http_code}' 2>/dev/null | grep -qE "^2"; then
DOWNLOADED=$((DOWNLOADED + 1))
size=$(wc -c < "$target" 2>/dev/null | tr -d ' ')
log_info "$fname ($size bytes)"
else
log_warn "$fname 下载失败"
fi
done
log_info "音源插件下载完成: $DOWNLOADED/${#PLUGIN_FILES[@]}"
echo ""
# ========= 部署 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。"
fi
# ========= 云服务器安全组提醒 =========
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 "")
echo ""
echo "=========================================="
echo " 🎉 安装完成!"
echo "=========================================="
echo ""
echo "📂 安装目录: $INSTALL_DIR"
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
echo ""
echo "管理命令:"
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"
echo ""