#!/bin/bash # ============================================================ # TPanel v1.3.9 一键安装脚本(super release:合并今日所有修复) # 官网: https://tpanel.cn # 作者: Zhang Pu # 修复: 2026-06-06 - 仪表盘新增 CPU 核心数 + 型号显示 # 负载颜色按核心数判断(1 核机器 1.0 算满,N 核机器 N.0 才算满) # 修复: 2026-06-06 - 修复登录后必须强制刷新才能看到后台的 bug # showLogin() 改用 display 切换两个独立 div,不再 innerHTML 重写 body # 修复: 2026-06-05 - install.sh 自动安装 MariaDB + 配 sudoers # 修复: 2026-06-05 - system.py create_mysql_db 改用 sudo + list 参数 + regex 校验(防 SQL 注入) # 修复: 2026-06-05 - PHP-FPM 改用 TCP 127.0.0.1:9000 # 修复: 2026-06-05 - install.sh 自动安装并启动 PHP-FPM # 修复: 2026-06-05 - systemd ExecStart 传端口参数 # 修复: 2026-06-05 - 创建 /etc/nginx/tpanel 并授权 tpanel 用户 # 修复: 2026-06-05 - 修复解压步骤漏复制根目录 requirements.txt 等文件 # 修复: 2026-06-05 - 修复 zip 魔数校验永远失败的 bug # 修复: 2026-06-04 - 修正下载源 URL # ============================================================set -e PANEL_VERSION="1.3.9" PANEL_PORT=8888 GITHUB_REPO="zhang-pu/tpanel" INSTALL_DIR="/opt/tpanel" LOG_FILE="/tmp/tpanel_install.log" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' log() { echo -e "${GREEN}[$(date +%H:%M:%S)]${NC} $1" | tee -a "$LOG_FILE"; } warn() { echo -e "${YELLOW}[!]${NC} $1" | tee -a "$LOG_FILE"; } err() { echo -e "${RED}[✗]${NC} $1" | tee -a "$LOG_FILE"; } ok() { echo -e "${GREEN}[✓]${NC} $1" | tee -a "$LOG_FILE"; } if [ "$EUID" -ne 0 ]; then err "请用 root 用户运行: sudo bash install.sh"; exit 1 fi # 探测包管理器 if command -v apt-get &>/dev/null; then PKG="apt-get"; PKG_UPDATE="apt-get update"; PKG_INSTALL="apt-get install -y" elif command -v yum &>/dev/null; then PKG="yum"; PKG_UPDATE="yum makecache"; PKG_INSTALL="yum install -y" elif command -v dnf &>/dev/null; then PKG="dnf"; PKG_UPDATE="dnf makecache"; PKG_INSTALL="dnf install -y" else err "不支持的发行版(需要 apt / yum / dnf)"; exit 1 fi log "检测到包管理器: $PKG" read -p "请输入 TPanel 访问域名(留空用 IP): " PANEL_DOMAIN PANEL_DOMAIN=$(echo "$PANEL_DOMAIN" | xargs) # 1/8 依赖 log "[1/8] 安装系统依赖..." $PKG_UPDATE >>"$LOG_FILE" 2>&1 || warn "更新源失败,继续..." DEB_PKGS="nginx python3 python3-pip python3-venv sqlite3 certbot python3-certbot-nginx curl wget unzip php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-mbstring php8.2-xml php8.2-zip php8.2-gd mariadb-server mariadb-client sudo" RPM_PKGS="nginx python3 python3-pip sqlite certbot python3-certbot-nginx curl wget unzip php-fpm php-mysqlnd php-curl php-mbstring php-xml php-zip php-gd mariadb-server mariadb sudo" if [ "$PKG" = "apt-get" ]; then $PKG_INSTALL $DEB_PKGS >>"$LOG_FILE" 2>&1 || { err "依赖安装失败,查看 $LOG_FILE"; exit 1; } else $PKG_INSTALL $RPM_PKGS >>"$LOG_FILE" 2>&1 || { err "依赖安装失败,查看 $LOG_FILE"; exit 1; } fi # 启动 MariaDB(v1.3.8+) if [ "$PKG" = "apt-get" ]; then systemctl enable mariadb >>"$LOG_FILE" 2>&1 || warn "启用 mariadb 失败,继续..." systemctl start mariadb >>"$LOG_FILE" 2>&1 || warn "启动 mariadb 失败,继续..." else systemctl enable mariadb >>"$LOG_FILE" 2>&1 || warn "启用 mariadb 失败,继续..." systemctl start mariadb >>"$LOG_FILE" 2>&1 || warn "启动 mariadb 失败,继续..." fi # 允许 tpanel 用户免密 sudo 跑 mysql(v1.3.8+) cat > /etc/sudoers.d/tpanel-mysql << 'EOF' tpanel ALL=(ALL) NOPASSWD: /usr/bin/mysql EOF chmod 440 /etc/sudoers.d/tpanel-mysql # 启动 PHP-FPM(Debian 8.2 / CentOS php-fpm) if [ "$PKG" = "apt-get" ]; then systemctl enable php8.2-fpm >>"$LOG_FILE" 2>&1 || warn "启用 php8.2-fpm 失败,继续..." # 改用 TCP 127.0.0.1:9000(避免 unix socket 在某些 systemd 环境失效) sed -i 's|^listen = /run/php/php8.2-fpm.sock|listen = 127.0.0.1:9000|' /etc/php/8.2/fpm/pool.d/www.conf 2>/dev/null || warn "修改 PHP-FPM listen 失败" systemctl start php8.2-fpm >>"$LOG_FILE" 2>&1 || warn "启动 php8.2-fpm 失败,继续..." else systemctl enable php-fpm >>"$LOG_FILE" 2>&1 || warn "启用 php-fpm 失败,继续..." # CentOS/RHEL PHP-FPM 默认就是 TCP 9000,跳过 systemctl start php-fpm >>"$LOG_FILE" 2>&1 || warn "启动 php-fpm 失败,继续..." fi ok "依赖安装完成" # 2/8 用户和目录 log "[2/8] 创建 TPanel 用户和目录..." id tpanel &>/dev/null || useradd -r -s /bin/bash -m tpanel mkdir -p "$INSTALL_DIR"/{backend,frontend,logs,backups,sites,ssl} chown -R tpanel:tpanel "$INSTALL_DIR" # 创建 nginx 配置目录并授权 tpanel 用户(config.py 需要写这里) mkdir -p /etc/nginx/tpanel chown tpanel:tpanel /etc/nginx/tpanel chmod 755 /etc/nginx/tpanel ok "目录就绪: $INSTALL_DIR" # 3/8 下载源码 log "[3/8] 下载 TPanel v${PANEL_VERSION} 源码..." TMP_ZIP="/tmp/tpanel-v${PANEL_VERSION}.zip" # 校验文件是否为合法 zip(看前 4 字节魔数 50 4b 03 04) is_valid_zip() { local file="$1" [ -f "$file" ] || return 1 local magic magic=$(od -An -tx1 -N4 "$file" 2>/dev/null | tr -d ' \n') [ "$magic" = "504b0304" ] } download_from_github() { local url="$1" log " 尝试下载: $url" rm -f "$TMP_ZIP" if curl -fsSL --connect-timeout 15 --max-time 300 -o "$TMP_ZIP" "$url" 2>>"$LOG_FILE"; then if is_valid_zip "$TMP_ZIP"; then return 0 else warn " 下载到的东西不是 zip(可能 404 HTML)" return 1 fi else warn " 下载失败 (HTTP 错误或网络不通)" return 1 fi } # ✅ 修正后的下载源(按优先级,只保留真实存在的) URLS=( # 1. 官网镜像(最快,国内用户友好) "https://tpanel.cn/tpanel-v${PANEL_VERSION}-source.zip" # 2. GitHub release 当前版本 "https://github.com/${GITHUB_REPO}/releases/download/v${PANEL_VERSION}/tpanel-v${PANEL_VERSION}-source.zip" # 3. latest 通用 - install.sh 路径 "https://github.com/${GITHUB_REPO}/releases/latest/download/install.sh" # 4. 源码 tag zip "https://github.com/${GITHUB_REPO}/archive/refs/tags/v${PANEL_VERSION}.zip" # 5. main 分支源码 zip "https://github.com/${GITHUB_REPO}/archive/refs/heads/main.zip" ) DOWNLOADED=0 for url in "${URLS[@]}"; do if download_from_github "$url"; then ok " 源码下载成功 ($(ls -lh $TMP_ZIP | awk '{print $5}'))" DOWNLOADED=1 break fi done # 本地兜底 if [ $DOWNLOADED -eq 0 ]; then for local_zip in /tmp/tpanel.zip /tmp/tpanel_v1.3.zip /tmp/tpanel-v1.3.1.zip /tmp/tpanel-v1.3.1-source.zip; do if [ -f "$local_zip" ] && is_valid_zip "$local_zip"; then warn " GitHub 下载失败,使用本地 $local_zip" cp "$local_zip" "$TMP_ZIP" DOWNLOADED=1 ok " 使用本地源码" break fi done fi if [ $DOWNLOADED -eq 0 ]; then err "所有下载源都失败!" echo echo " 手动下载后放到 /tmp/tpanel.zip 再重跑:" echo " wget -O /tmp/tpanel.zip https://tpanel.cn/tpanel-v${PANEL_VERSION}-source.zip" echo " bash install.sh" exit 1 fi # 解压(自适应目录) log " 解压源码..." rm -rf "$INSTALL_DIR"/{backend,frontend} unzip -q -o "$TMP_ZIP" -d /tmp/tpanel_extracted if [ -d /tmp/tpanel_extracted/tpanel-main ]; then EXTRACT_DIR=/tmp/tpanel_extracted/tpanel-main elif [ -d /tmp/tpanel_extracted/tpanel-${PANEL_VERSION} ]; then EXTRACT_DIR=/tmp/tpanel_extracted/tpanel-${PANEL_VERSION} elif [ -d /tmp/tpanel_extracted/backend ]; then EXTRACT_DIR=/tmp/tpanel_extracted else err " 解压目录结构不认识: /tmp/tpanel_extracted/" ls -la /tmp/tpanel_extracted/ exit 1 fi cp -r "$EXTRACT_DIR"/backend "$INSTALL_DIR/" cp -r "$EXTRACT_DIR"/frontend "$INSTALL_DIR/" # 复制根目录的配置文件(如果有) for f in requirements.txt .gitignore README.md SPEC.md; do if [ -f "$EXTRACT_DIR/$f" ]; then cp "$EXTRACT_DIR/$f" "$INSTALL_DIR/" fi done chown -R tpanel:tpanel "$INSTALL_DIR" ok "源码部署完成" # 4/8 venv log "[4/8] 配置 Python 虚拟环境..." sudo -u tpanel python3 -m venv "$INSTALL_DIR/venv" sudo -u tpanel "$INSTALL_DIR/venv/bin/pip" install --upgrade pip >>"$LOG_FILE" 2>&1 sudo -u tpanel "$INSTALL_DIR/venv/bin/pip" install -r "$INSTALL_DIR/requirements.txt" >>"$LOG_FILE" 2>&1 || { err "Python 依赖安装失败,查看 $LOG_FILE"; exit 1; } ok "Python 环境就绪" # 5/8 Nginx log "[5/8] 配置 Nginx..." cat > /etc/nginx/sites-available/tpanel <>"$LOG_FILE" 2>&1 && systemctl reload nginx ok "Nginx 配置完成" # 6/8 systemd log "[6/8] 注册 systemd 服务..." cat > /etc/systemd/system/tpanel.service <>"$LOG_FILE" 2>&1 systemctl restart tpanel sleep 2 if curl -fs http://127.0.0.1:${PANEL_PORT}/api/health >/dev/null 2>&1; then ok "TPanel 启动成功" else warn "健康检查失败,查看: journalctl -u tpanel -n 50" fi # 7/8 防火墙 log "[7/8] 配置防火墙..." if command -v ufw &>/dev/null; then ufw allow 80/tcp >/dev/null 2>&1 || true ufw allow 443/tcp >/dev/null 2>&1 || true ok "ufw 规则已添加" elif command -v firewall-cmd &>/dev/null; then firewall-cmd --permanent --add-service=http >/dev/null 2>&1 || true firewall-cmd --permanent --add-service=https >/dev/null 2>&1 || true firewall-cmd --reload >/dev/null 2>&1 || true ok "firewalld 规则已添加" else warn "未检测到防火墙,跳过" fi # 8/8 SSL if [ -n "$PANEL_DOMAIN" ]; then log "[8/8] 申请 SSL 证书(${PANEL_DOMAIN})..." if certbot --nginx -d "$PANEL_DOMAIN" --non-interactive --agree-tos -m "admin@${PANEL_DOMAIN}" >>"$LOG_FILE" 2>&1; then ok "SSL 证书签发成功" else warn "SSL 签发失败,稍后手动: certbot --nginx -d ${PANEL_DOMAIN}" fi else log "[8/8] 跳过 SSL(无域名)" fi ACCESS_URL="http://${PANEL_DOMAIN:-$(curl -s ifconfig.me 2>/dev/null || echo 'YOUR_SERVER_IP')}" echo echo "============================================================" echo -e "${GREEN} TPanel v${PANEL_VERSION} 安装完成!${NC}" echo "============================================================" echo echo " 访问地址: ${ACCESS_URL}" echo " 默认账号: admin" echo " 默认密码: tpanel.cn" echo echo -e "${YELLOW} ⚠️ 请登录后立即修改密码!${NC}" echo echo -e "${YELLOW} 💡 首次访问请用 Ctrl+Shift+R 硬刷新(跳过浏览器缓存)${NC}" echo "============================================================"