宝塔面板 – 反代系统部署

📋 环境要求

  • 面板:宝塔面板 (Linux)

  • Web 服务:Nginx

  • PHP 版本:PHP >= 7.3 (建议 PHP 7.4 或 8.x)

  • 数据库:MySQL (5.6 / 5.7 / 8.0 均可)


第一步:创建网站与数据库

  1. 登录宝塔面板,进入 “网站” -> “添加站点”

  2. 域名:填写您的反代域名(例如 proxy.yourdomain.com)。

  3. 数据库:选择 “MySQL”,设置好数据库名称、用户名和密码(请务必记下这三个信息,稍后要写进代码里)。

  4. PHP版本:选择 7.3 或以上版本。

  5. 点击 “提交”

第二步:配置 SSL 证书(必做,否则无法开启 HTTPS)

  1. 在网站列表中,点击刚才创建的网站的 “未部署” (SSL 这一列)。

  2. 选择 “Let’s Encrypt” 申请免费证书,或者粘贴您自己的证书。

  3. 申请成功后,确保右上角的 “强制HTTPS” 处于关闭状态(因为我们的代码里已经内置了更智能的协议跳转控制)。

第三步:修改 Nginx 配置文件(核心:解决网页白板、无法加载样式)

因为现代网盘(如 OpenList)是前后端分离架构,必须禁止宝塔默认拦截静态文件。

  1. 在网站设置窗口中,点击左侧的 “配置文件”

  2. 在代码中找到以下两段拦截缓存规则,直接将其删除(约在 50~60 行左右):

    # 找到并删除这一段
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
        expires      30d;
        error_log /dev/null;
        access_log /dev/null;
    }
    
    # 找到并删除这一段
    location ~ .*\.(js|css)?$
    {
        expires      12h;
        error_log /dev/null;
        access_log /dev/null;
    }
    1. 删除后,点击 “保存”

    第四步:设置全局伪静态(核心:接管所有路由)

    1. 在网站设置窗口中,点击左侧的 “伪静态”

    2. 复制并粘贴以下规则:

      location / {
          try_files $uri $uri/ /index.php?$query_string;
      }
      1. 点击 “保存”

      第五步:修改并上传代码

      1. 将之前写好的 index.php 代码保存在您的电脑上,并用文本编辑器(如记事本、VSCode)打开。

      2. 找到代码开头的 “数据库配置” 区域,将第一步创建的数据库信息填入:

        // ================= 数据库配置 =================
        const DB_HOST = '127.0.0.1';
        const DB_NAME = '填入宝塔创建的数据库名'; 
        const DB_USER = '填入宝塔创建的数据库用户名'; 
        const DB_PASS = '填入宝塔创建的数据库密码'; 
        const DB_PORT = '3306';
        // ==============================================
         
         

         

        <?php
        // ================= 数据库配置 =================
        const DB_HOST = '127.0.0.1';
        const DB_NAME = 'your_dbname'; // 替换为宝塔创建的数据库名
        const DB_USER = 'your_dbuser'; // 替换为宝塔创建的数据库用户名
        const DB_PASS = 'your_dbpass'; // 替换为宝塔创建的数据库密码
        const DB_PORT = '3306';
        // ==============================================
        
        set_time_limit(0);
        ini_set('memory_limit', '-1');
        
        session_start();
        date_default_timezone_set('Asia/Shanghai');
        
        function getIpLocation($ip) {
            $ip = trim(explode(',', $ip)[0]);
            if (!$ip || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
                return '局域网/保留地址';
            }
            $url = "http://ip-api.com/json/{$ip}?lang=zh-CN";
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 3);
            $res = curl_exec($ch);
            curl_close($ch);
            if ($res) {
                $data = json_decode($res, true);
                if ($data && isset($data['status']) && $data['status'] === 'success') {
                    $country = $data['country'] ?? '';
                    $region = $data['regionName'] ?? '';
                    $city = $data['city'] ?? '';
                    if ($country === '中国') return $region . ' ' . $city;
                    return $country . ' ' . $region . ' ' . $city;
                }
            }
            return '未知';
        }
        
        try {
            $pdo = new PDO("mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=utf8mb4", DB_USER, DB_PASS, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
        } catch (PDOException $e) {
            die("数据库连接失败: " . $e->getMessage());
        }
        
        $pdo->exec("
            CREATE TABLE IF NOT EXISTS `proxy_config` (
                `id` int(11) NOT NULL AUTO_INCREMENT,
                `admin_path` varchar(255) DEFAULT '/admin',
                `user_path` varchar(255) DEFAULT '/user',
                `username` varchar(255) NOT NULL,
                `password` varchar(255) NOT NULL,
                `initialized` tinyint(1) DEFAULT '0',
                `auth_enabled` tinyint(1) DEFAULT '0',
                `target_domain` varchar(255) DEFAULT '',
                `worker_domain` varchar(255) DEFAULT '',
                `http_port` int(11) DEFAULT '0',
                `https_port` int(11) DEFAULT '0',
                `proxy_mode` varchar(50) DEFAULT 'http_only',
                `cache_enabled` tinyint(1) DEFAULT '0',
                `cache_ttl_seconds` int(11) DEFAULT '3600',
                `force_https` tinyint(1) DEFAULT '0',
                `force_http` tinyint(1) DEFAULT '0',
                `require_login` tinyint(1) DEFAULT '1',
                PRIMARY KEY (`id`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
        
            CREATE TABLE IF NOT EXISTS `proxy_users` (
                `id` int(11) NOT NULL AUTO_INCREMENT,
                `username` varchar(255) NOT NULL,
                `account` varchar(255) NOT NULL,
                `password` varchar(255) NOT NULL,
                `qq_number` varchar(50) NOT NULL,
                `is_banned` tinyint(1) DEFAULT '0',
                `auth_days` int(11) DEFAULT '0',
                `auth_start_time` datetime DEFAULT NULL,
                `auth_end_time` datetime DEFAULT NULL,
                `create_time` datetime DEFAULT NULL,
                `login_expire_time` datetime DEFAULT NULL,
                `last_ip` varchar(100) DEFAULT '',
                `last_location` varchar(255) DEFAULT '',
                `session_id` varchar(255) DEFAULT '',
                PRIMARY KEY (`id`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
        ");
        try { $pdo->exec("ALTER TABLE proxy_config ADD COLUMN force_https tinyint(1) DEFAULT '0'"); } catch (Exception $e) {}
        try { $pdo->exec("ALTER TABLE proxy_config ADD COLUMN force_http tinyint(1) DEFAULT '0'"); } catch (Exception $e) {}
        try { $pdo->exec("ALTER TABLE proxy_config ADD COLUMN require_login tinyint(1) DEFAULT '1'"); } catch (Exception $e) {}
        
        $requestUri = $_SERVER['REQUEST_URI'];
        $urlPath = parse_url($requestUri, PHP_URL_PATH);
        $method = $_SERVER['REQUEST_METHOD'];
        $clientIpRaw = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
        $clientIp = trim(explode(',', $clientIpRaw)[0]);
        
        $stmt = $pdo->query("SELECT * FROM proxy_config LIMIT 1");
        $config = $stmt->fetch();
        
        if (!$config || $config['initialized'] == 0) {
            if ($method === 'POST') {
                $adminPath = $_POST['admin_path'] ?? '/admin';
                $userPath = $_POST['user_path'] ?? '/user';
                $username = $_POST['username'] ?? '';
                $password = $_POST['password'] ?? '';
                if ($username && $password) {
                    $encPwd = base64_encode($password);
                    $stmt = $pdo->prepare("INSERT INTO proxy_config (admin_path, user_path, username, password, initialized) VALUES (?, ?, ?, ?, 1)");
                    $stmt->execute([$adminPath, $userPath, $username, $encPwd]);
                    header("Location: " . $adminPath);
                    exit;
                }
                die('请填写完整信息');
            }
            echo getInitHtml();
            exit;
        }
        
        $adminPath = $config['admin_path'] ?: '/admin';
        $userPath = $config['user_path'] ?: '/user';
        
        if (strpos($urlPath, $adminPath) === 0) {
            if (empty($_SESSION['admin_logged_in'])) {
                if ($method === 'POST' && isset($_POST['admin_user'])) {
                    if ($_POST['admin_user'] === $config['username'] && base64_encode($_POST['admin_pwd']) === $config['password']) {
                        $_SESSION['admin_logged_in'] = true;
                        header("Location: " . $adminPath);
                        exit;
                    } else {
                        echo getLoginHtml('账号或密码错误!');
                        exit;
                    }
                }
                echo getLoginHtml();
                exit;
            }
        
            if ($method === 'POST') {
                $action = $_POST['action'] ?? '';
                if ($action === 'update_base') {
                    $newAdminPath = $_POST['new_admin_path'] ?: $config['admin_path'];
                    $newUserPath = $_POST['new_user_path'] ?: $config['user_path'];
                    $newUsername = $_POST['new_username'] ?: $config['username'];
                    $newPassword = !empty($_POST['new_password']) ? base64_encode($_POST['new_password']) : $config['password'];
                    $authEnabled = isset($_POST['auth_enabled']) ? 1 : 0;
                    $requireLogin = isset($_POST['require_login']) ? 1 : 0;
                    $stmt = $pdo->prepare("UPDATE proxy_config SET admin_path=?, user_path=?, username=?, password=?, auth_enabled=?, require_login=? WHERE id=?");
                    $stmt->execute([$newAdminPath, $newUserPath, $newUsername, $newPassword, $authEnabled, $requireLogin, $config['id']]);
                    header("Location: " . $newAdminPath . "#base");
                    exit;
                }
                if ($action === 'update_proxy') {
                    $targetDomain = $_POST['target_domain'] ?? '';
                    $workerDomain = $_POST['worker_domain'] ?? '';
                    $httpPort = intval($_POST['http_port'] ?? 0);
                    $httpsPort = intval($_POST['https_port'] ?? 0);
                    $proxyMode = $_POST['proxy_mode'] ?? 'http_only';
                    $cacheEnabled = isset($_POST['cache_enabled']) ? 1 : 0;
                    
                    $forceProto = $_POST['force_protocol'] ?? '0';
                    $forceHttps = ($forceProto === 'https') ? 1 : 0;
                    $forceHttp = ($forceProto === 'http') ? 1 : 0;
                    
                    $cacheTtl = intval($_POST['cache_ttl'] ?? 3600);
                    $stmt = $pdo->prepare("UPDATE proxy_config SET target_domain=?, worker_domain=?, http_port=?, https_port=?, proxy_mode=?, cache_enabled=?, force_https=?, force_http=?, cache_ttl_seconds=? WHERE id=?");
                    $stmt->execute([$targetDomain, $workerDomain, $httpPort, $httpsPort, $proxyMode, $cacheEnabled, $forceHttps, $forceHttp, $cacheTtl, $config['id']]);
                    header("Location: " . $adminPath . "#proxy");
                    exit;
                }
                if ($action === 'manage_user') {
                    $userId = intval($_POST['user_id'] ?? 0);
                    $operation = $_POST['operation'] ?? '';
                    $authDays = intval($_POST['auth_days'] ?? 0);
                    if ($userId && $operation) {
                        switch ($operation) {
                            case 'ban': $pdo->prepare("UPDATE proxy_users SET is_banned=1 WHERE id=?")->execute([$userId]); break;
                            case 'unban': $pdo->prepare("UPDATE proxy_users SET is_banned=0 WHERE id=?")->execute([$userId]); break;
                            case 'delete': $pdo->prepare("DELETE FROM proxy_users WHERE id=?")->execute([$userId]); break;
                            case 'update_pwd':
                                if (!empty($_POST['new_password'])) $pdo->prepare("UPDATE proxy_users SET password=? WHERE id=?")->execute([base64_encode($_POST['new_password']), $userId]);
                                break;
                            case 'add_auth':
                            case 'reduce_auth':
                                $u = $pdo->prepare("SELECT auth_end_time FROM proxy_users WHERE id=?");
                                $u->execute([$userId]);
                                $user = $u->fetch();
                                if ($user) {
                                    $end = strtotime($user['auth_end_time']);
                                    if ($end < time()) $end = time();
                                    $end = $operation === 'add_auth' ? $end + ($authDays * 86400) : $end - ($authDays * 86400);
                                    $pdo->prepare("UPDATE proxy_users SET auth_end_time=? WHERE id=?")->execute([date('Y-m-d H:i:s', $end), $userId]);
                                }
                                break;
                        }
                    }
                    header("Location: " . $adminPath . "#user-manage");
                    exit;
                }
            }
            $users = $pdo->query("SELECT * FROM proxy_users ORDER BY id DESC")->fetchAll();
            echo getAdminHtml($config, $users);
            exit;
        }
        
        if (strpos($urlPath, $userPath) === 0) {
            $path = str_replace($userPath, '', $urlPath);
            if ($path === '/register' && $method === 'POST') {
                $username = $_POST['username'] ?? '';
                $account = $_POST['account'] ?? '';
                $password = $_POST['password'] ?? '';
                $qqNumber = $_POST['qq_number'] ?? '';
                if (!$username || !$account || !$password || !$qqNumber) die('请填写完整注册信息');
                $chk = $pdo->prepare("SELECT id FROM proxy_users WHERE account=? OR qq_number=?");
                $chk->execute([$account, $qqNumber]);
                if ($chk->fetch()) die('账号或QQ号已存在');
                $now = date('Y-m-d H:i:s');
                $encPwd = base64_encode($password);
                $stmt = $pdo->prepare("INSERT INTO proxy_users (username, account, password, qq_number, is_banned, auth_days, auth_start_time, auth_end_time, create_time, login_expire_time) VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?)");
                $stmt->execute([$username, $account, $encPwd, $qqNumber, $now, $now, $now, $now]);
                header("Location: " . $userPath . "/login");
                exit;
            }
            if ($path === '/login' && $method === 'POST') {
                $account = $_POST['account'] ?? '';
                $password = $_POST['password'] ?? '';
                $stmt = $pdo->prepare("SELECT * FROM proxy_users WHERE account=?");
                $stmt->execute([$account]);
                $user = $stmt->fetch();
                if (!$user) die(getUserLoginHtml('账号不存在'));
                if ($user['is_banned'] == 1) die(getUserLoginHtml('账号已被封禁,无法登录!如需解封请联系管理员QQ:3890053645'));
                if (base64_decode($user['password']) !== $password) die(getUserLoginHtml('密码错误'));
                if ($config['auth_enabled'] == 1 && strtotime($user['auth_end_time']) <= time()) die(getUserLoginHtml('账号授权已到期,无法登录!如需续期请联系管理员QQ:3890053645'));
                
                $sessionId = bin2hex(random_bytes(16));
                $nowMs = time();
                $expireTime = date('Y-m-d H:i:s', $nowMs + 12 * 3600);
                
                $location = getIpLocation($clientIp);
                
                $pdo->prepare("UPDATE proxy_users SET session_id=?, last_ip=?, last_location=?, login_expire_time=? WHERE id=?")->execute([$sessionId, $clientIp, $location, $expireTime, $user['id']]);
                $token = base64_encode("{$account}|{$sessionId}|{$clientIp}|{$nowMs}");
                setcookie('proxy_login', $token, $nowMs + 43200, '/', '', isset($_SERVER['HTTPS']), true);
                header("Location: /");
                exit;
            }
            if ($path === '/find-pwd' && $method === 'POST') {
                $qq = $_POST['qq_number'] ?? '';
                $newPwd = $_POST['new_password'] ?? '';
                $stmt = $pdo->prepare("SELECT * FROM proxy_users WHERE qq_number=?");
                $stmt->execute([$qq]);
                $user = $stmt->fetch();
                if (!$user) die(getUserFindPwdHtml('QQ号未注册'));
                if ($newPwd) {
                    $pdo->prepare("UPDATE proxy_users SET password=? WHERE qq_number=?")->execute([base64_encode($newPwd), $qq]);
                    die(getUserFindPwdHtml("账号:{$user['account']}<br>密码已重置成功,请返回登录"));
                }
                die(getUserFindPwdHtml("你的账号是:{$user['account']}<br>请设置新密码"));
            }
            if ($path === '/register') die(getUserRegisterHtml());
            if ($path === '/find-pwd') die(getUserFindPwdHtml(''));
            die(getUserLoginHtml(''));
        }
        
        if (!isset($config['require_login']) || $config['require_login'] == 1) {
            if (!isset($_COOKIE['proxy_login'])) {
                header("Location: " . $userPath . "/login");
                exit;
            }
        
            $tokenParts = explode('|', base64_decode($_COOKIE['proxy_login']));
            if (count($tokenParts) !== 4) {
                header("Location: " . $userPath . "/login");
                exit;
            }
        
            list($account, $sessionId, $cookieIp, $lastValMs) = $tokenParts;
            $stmt = $pdo->prepare("SELECT * FROM proxy_users WHERE account=?");
            $stmt->execute([$account]);
            $user = $stmt->fetch();
        
            if (!$user) die(getAccessDeniedHtml('账号不存在,请重新登录'));
            if ($user['session_id'] && $sessionId && $user['session_id'] !== $sessionId) die(getAccessDeniedHtml('你的账号已在其他IP登录,当前设备已下线!请重新登录'));
            if ($user['is_banned'] == 1) die(getAccessDeniedHtml('你的账号已被封禁,无法访问该网站!如需解封请联系管理员QQ:3890053645'));
            if (strtotime($user['login_expire_time']) <= time()) die(getAccessDeniedHtml('登录已过期,请重新登录'));
        }
        
        if (empty($config['target_domain']) || empty($config['worker_domain'])) {
            http_response_code(500);
            die('请先在后台配置反代参数');
        }
        
        $isClientHttps = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on') || 
                         (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') || 
                         (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443);
        
        if ($config['force_https'] == 1 && !$isClientHttps) {
            header("Location: https://" . $_SERVER['HTTP_HOST'] . $requestUri);
            exit;
        }
        if ($config['force_http'] == 1 && $isClientHttps) {
            header("Location: http://" . $_SERVER['HTTP_HOST'] . $requestUri);
            exit;
        }
        
        while (ob_get_level()) ob_end_clean();
        
        $protocol = 'http';
        $port = $config['http_port'];
        if ($config['proxy_mode'] === 'https_only' || ($config['proxy_mode'] === 'auto' && $config['https_port'])) {
            $protocol = 'https';
            $port = $config['https_port'];
        }
        $targetUrl = "{$protocol}://{$config['target_domain']}:{$port}{$requestUri}";
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $targetUrl);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        
        $clientHeaders = [];
        foreach (getallheaders() as $k => $v) {
            $lk = strtolower($k);
            if (in_array($lk, ['host', 'cookie', 'accept-encoding'])) continue;
            if ($lk === 'origin' || $lk === 'referer') {
                $v = str_replace($config['worker_domain'], $config['target_domain'], $v);
            }
            $clientHeaders[] = "$k: $v";
        }
        $clientHeaders[] = "Host: " . $config['target_domain'];
        $clientHeaders[] = "X-Forwarded-Host: " . $config['worker_domain'];
        
        $fwdProto = 'http';
        if ($config['force_https'] == 1) $fwdProto = 'https';
        elseif ($config['force_http'] == 1) $fwdProto = 'http';
        else $fwdProto = $isClientHttps ? 'https' : 'http';
        $clientHeaders[] = "X-Forwarded-Proto: " . $fwdProto;
        
        $proxyCookies = [];
        foreach ($_COOKIE as $k => $v) {
            if ($k !== 'proxy_login') $proxyCookies[] = "$k=$v";
        }
        if (!empty($proxyCookies)) $clientHeaders[] = "Cookie: " . implode('; ', $proxyCookies);
        
        curl_setopt($ch, CURLOPT_HTTPHEADER, $clientHeaders);
        
        if ($method !== 'GET' && $method !== 'HEAD' && $method !== 'OPTIONS') {
            curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents('php://input'));
        }
        
        $responseHeaders = [];
        $headersSent = false;
        $isTextContent = false;
        $bodyBuffer = '';
        $httpStatusLine = '';
        
        curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$responseHeaders, &$httpStatusLine) {
            if (empty($httpStatusLine) && preg_match('/^HTTP\/\d(?:\.\d)? \d{3}/i', $header)) {
                $httpStatusLine = trim($header);
            }
            $responseHeaders[] = $header;
            return strlen($header);
        });
        
        curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use (&$headersSent, &$isTextContent, &$bodyBuffer, &$responseHeaders, &$httpStatusLine, $config) {
            if (!$headersSent) {
                $headersSent = true;
        
                if ($httpStatusLine) header($httpStatusLine);
        
                $contentType = '';
                foreach ($responseHeaders as $h) {
                    if (preg_match('/^Content-Type:\s*(.*)/i', $h, $matches)) {
                        $contentType = strtolower(trim($matches[1]));
                        break;
                    }
                }
        
                if (strpos($contentType, 'text/') !== false || strpos($contentType, 'json') !== false || strpos($contentType, 'javascript') !== false || strpos($contentType, 'xml') !== false) {
                    $isTextContent = true;
                } else {
                    header('X-Accel-Buffering: no');
                }
        
                foreach ($responseHeaders as $h) {
                    $hTrim = trim($h);
                    if (empty($hTrim) || preg_match('/^HTTP\//i', $hTrim)) continue;
        
                    $lk = strtolower(explode(':', $hTrim)[0]);
                    if ($isTextContent) {
                        if (in_array($lk, ['content-length', 'transfer-encoding', 'content-encoding', 'content-security-policy', 'x-frame-options'])) continue;
                    } else {
                        if (in_array($lk, ['transfer-encoding'])) continue;
                    }
        
                    $hTrim = str_replace($config['target_domain'], $config['worker_domain'], $hTrim);
                    
                    if ($config['force_https'] == 1) {
                        $hTrim = preg_replace("/http:\/\/" . preg_quote($config['worker_domain'], '/') . "/i", "https://" . $config['worker_domain'], $hTrim);
                    } elseif ($config['force_http'] == 1) {
                        $hTrim = preg_replace("/https:\/\/" . preg_quote($config['worker_domain'], '/') . "/i", "http://" . $config['worker_domain'], $hTrim);
                    }
                    
                    header($hTrim, false);
                }
            }
        
            if ($isTextContent) {
                $bodyBuffer .= $data;
            } else {
                echo $data;
                flush();
            }
        
            return strlen($data);
        });
        
        curl_exec($ch);
        
        if (curl_errno($ch)) {
            http_response_code(502);
            die("Proxy Error: " . curl_error($ch));
        }
        curl_close($ch);
        
        if ($isTextContent && $bodyBuffer !== '') {
            $repProtocol = 'http';
            if ($config['force_https'] == 1) $repProtocol = 'https';
            elseif ($config['force_http'] == 1) $repProtocol = 'http';
            else $repProtocol = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') ? 'https' : 'http';
        
            if ($config['https_port']) $bodyBuffer = str_replace("https://{$config['target_domain']}:{$config['https_port']}", "{$repProtocol}://{$config['worker_domain']}", $bodyBuffer);
            if ($config['http_port']) $bodyBuffer = str_replace("http://{$config['target_domain']}:{$config['http_port']}", "{$repProtocol}://{$config['worker_domain']}", $bodyBuffer);
            $bodyBuffer = str_replace("http://{$config['target_domain']}", "{$repProtocol}://{$config['worker_domain']}", $bodyBuffer);
            $bodyBuffer = str_replace("https://{$config['target_domain']}", "{$repProtocol}://{$config['worker_domain']}", $bodyBuffer);
            echo $bodyBuffer;
        }
        exit;
        
        function getInitHtml() {
            return <<<HTML
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>反代管理系统 - 初始化</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
          <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
            .init-card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
            .card-header { text-align: center; margin-bottom: 2rem; }
            .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
            .card-header p { color: #64748b; font-size: 0.95rem; }
            .form-group { margin-bottom: 1.5rem; }
            .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
            .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
            .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
            .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
            .icon { color: #2563eb; margin-right: 0.5rem; }
          </style>
        </head>
        <body>
          <div class="init-card">
            <div class="card-header">
              <h1><i class="fa fa-cogs icon"></i>反代管理系统</h1>
              <p>首次使用,请完成初始化配置</p>
            </div>
            <form method="POST">
              <div class="form-group">
                <label class="form-label">后台访问路径</label>
                <input type="text" name="admin_path" class="form-input" placeholder="/admin" value="/admin" required>
              </div>
              <div class="form-group">
                <label class="form-label">用户访问路径</label>
                <input type="text" name="user_path" class="form-input" placeholder="/user" value="/user" required>
              </div>
              <div class="form-group">
                <label class="form-label">管理员账号</label>
                <input type="text" name="username" class="form-input" placeholder="请设置管理员账号" required>
              </div>
              <div class="form-group">
                <label class="form-label">管理员密码</label>
                <input type="password" name="password" class="form-input" placeholder="请设置管理员密码" required>
              </div>
              <button type="submit" class="submit-btn"><i class="fa fa-check-circle icon"></i>完成初始化</button>
            </form>
          </div>
        </body>
        </html>
        HTML;
        }
        
        function getLoginHtml($errorMsg = '') {
            $alertHtml = $errorMsg ? '<div class="alert"><i class="fa fa-exclamation-circle"></i> ' . $errorMsg . '</div>' : '';
            return <<<HTML
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>反代管理系统 - 后台登录</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
          <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
            .login-card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1); padding: 2.5rem; max-width: 450px; width: 100%; }
            .card-header { text-align: center; margin-bottom: 2rem; }
            .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
            .card-header p { color: #64748b; font-size: 0.95rem; }
            .alert { background: #fee2e2; color: #dc2626; padding: 1rem; border-radius: 8px; margin-bottom: 1.5rem; text-align: center; font-size: 0.95rem; border-left: 4px solid #dc2626; }
            .icon { color: #2563eb; margin-right: 0.5rem; }
          </style>
        </head>
        <body>
          <div class="login-card">
            <div class="card-header">
              <h1><i class="fa fa-lock icon"></i>管理员登录</h1>
              <p>请输入管理员账号和密码</p>
            </div>
            {$alertHtml}
            <form method="POST">
              <div style="margin-bottom: 1rem;">
                <input type="text" name="admin_user" placeholder="管理员账号" style="width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc;" required>
              </div>
              <div style="margin-bottom: 1.5rem;">
                <input type="password" name="admin_pwd" placeholder="管理员密码" style="width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc;" required>
              </div>
              <button type="submit" style="width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer;">登录后台</button>
            </form>
          </div>
        </body>
        </html>
        HTML;
        }
        
        function getAdminHtml($config, $users) {
            $userTableHtml = '';
            if (count($users) > 0) {
                foreach ($users as $user) {
                    $pwdHtml = '
                        <div class="mb-1">' . base64_decode($user['password']) . '</div>
                        <form method="POST" style="display: flex; gap: 0.2rem;">
                          <input type="hidden" name="action" value="manage_user">
                          <input type="hidden" name="user_id" value="' . $user['id'] . '">
                          <input type="hidden" name="operation" value="update_pwd">
                          <input type="text" name="new_password" placeholder="新密码" class="px-1 py-0.5 text-xs border rounded w-16" required>
                          <button type="submit" class="px-1 py-0.5 text-xs bg-gray-600 text-white rounded">改密</button>
                        </form>
                    ';
                    $statusStr = $user['is_banned'] == 1 ? '<span class="text-red-600">已封禁</span>' : '<span class="text-green-600">正常</span>';
                    $banOp = $user['is_banned'] == 1 ? 'unban' : 'ban';
                    $banBtnCls = $user['is_banned'] == 1 ? 'bg-green-600' : 'bg-red-600';
                    $banBtnTxt = $user['is_banned'] == 1 ? '解禁' : '封禁';
                    $statusActionHtml = '
                        <div class="mb-1">' . $statusStr . '</div>
                        <div style="display: flex; gap: 0.2rem;">
                          <form method="POST">
                            <input type="hidden" name="action" value="manage_user">
                            <input type="hidden" name="user_id" value="' . $user['id'] . '">
                            <input type="hidden" name="operation" value="' . $banOp . '">
                            <button type="submit" class="px-2 py-1 text-xs rounded ' . $banBtnCls . ' text-white">' . $banBtnTxt . '</button>
                          </form>
                          <form method="POST" onsubmit="return confirm(\'确定要删除此用户吗?\');">
                            <input type="hidden" name="action" value="manage_user">
                            <input type="hidden" name="user_id" value="' . $user['id'] . '">
                            <input type="hidden" name="operation" value="delete">
                            <button type="submit" class="px-2 py-1 text-xs bg-red-800 text-white rounded">删除</button>
                          </form>
                        </div>
                    ';
                    $ipHtml = '
                        <div class="text-xs text-gray-600 whitespace-nowrap">' . ($user['last_ip'] ?: '-') . '</div>
                        <div class="text-xs text-blue-600 whitespace-nowrap">' . ($user['last_location'] ?: '未知') . '</div>
                    ';
                    $authOpHtml = '
                        <form method="POST" style="display: flex; gap: 0.2rem; align-items: center;">
                          <input type="hidden" name="action" value="manage_user">
                          <input type="hidden" name="user_id" value="' . $user['id'] . '">
                          <input type="number" name="auth_days" placeholder="天数" class="px-1 py-0.5 text-xs border rounded w-12" required>
                          <button type="submit" name="operation" value="add_auth" class="px-1 py-0.5 text-xs bg-blue-600 text-white rounded">增加</button>
                          <button type="submit" name="operation" value="reduce_auth" class="px-1 py-0.5 text-xs bg-orange-500 text-white rounded">减少</button>
                        </form>
                    ';
                    $userTableHtml .= '
                        <tr class="border-b border-gray-200 hover:bg-gray-50">
                          <td class="px-4 py-3">' . $user['username'] . '</td>
                          <td class="px-4 py-3">' . $user['account'] . '</td>
                          <td class="px-4 py-3">' . $pwdHtml . '</td>
                          <td class="px-4 py-3">' . $user['qq_number'] . '</td>
                          <td class="px-4 py-3">' . $statusActionHtml . '</td>
                          <td class="px-4 py-3">' . $ipHtml . '</td>
                          <td class="px-4 py-3">' . $user['auth_end_time'] . '</td>
                          <td class="px-4 py-3">' . $authOpHtml . '</td>
                        </tr>
                    ';
                }
            } else {
                $userTableHtml = '<tr><td colspan="8" class="px-4 py-3 text-center text-gray-500">暂无用户</td></tr>';
            }
        
            $authCheck = $config['auth_enabled'] == 1 ? 'checked' : '';
            $requireLoginCheck = (!isset($config['require_login']) || $config['require_login'] == 1) ? 'checked' : '';
            $cacheCheck = $config['cache_enabled'] == 1 ? 'checked' : '';
            
            $forceNoneCheck = ($config['force_https'] == 0 && $config['force_http'] == 0) ? 'checked' : '';
            $forceHttpsCheck = $config['force_https'] == 1 ? 'checked' : '';
            $forceHttpCheck = $config['force_http'] == 1 ? 'checked' : '';
        
            $optHttp = $config['proxy_mode'] === 'http_only' ? 'selected' : '';
            $optHttps = $config['proxy_mode'] === 'https_only' ? 'selected' : '';
            $optAuto = $config['proxy_mode'] === 'auto' ? 'selected' : '';
        
            return <<<HTML
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>反代管理系统 - 后台</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
          <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { background: #f8fafc; color: #1e293b; min-height: 100vh; display: flex; }
            .sidebar { width: 250px; background: #1e293b; color: #f8fafc; min-height: 100vh; padding: 1.5rem 0; position: fixed; }
            .sidebar-header { padding: 0 1.5rem 1.5rem; border-bottom: 1px solid #334155; margin-bottom: 1rem; }
            .sidebar-header h2 { font-size: 1.2rem; font-weight: 600; color: #ffffff; display: flex; align-items: center; }
            .sidebar-header h2 i { margin-right: 0.75rem; color: #38bdf8; }
            .sidebar-menu { padding: 0.5rem 0; }
            .menu-item { padding: 0.875rem 1.5rem; display: flex; align-items: center; color: #94a3b8; text-decoration: none; transition: all 0.2s ease; cursor: pointer; border-left: 3px solid transparent; }
            .menu-item.active { background: #334155; color: #ffffff; border-left-color: #38bdf8; }
            .menu-item:hover { background: #273449; color: #e2e8f0; }
            .menu-item i { margin-right: 0.75rem; font-size: 1rem; }
            .main-content { margin-left: 250px; flex: 1; padding: 2rem; }
            .content-header { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid #e2e8f0; }
            .content-header h1 { font-size: 1.75rem; font-weight: 600; color: #0f172a; }
            .content-card { background: #ffffff; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); padding: 2rem; margin-bottom: 2rem; overflow-x: auto; }
            .card-title { font-size: 1.25rem; font-weight: 600; color: #1e293b; margin-bottom: 1.5rem; display: flex; align-items: center; }
            .card-title i { margin-right: 0.75rem; color: #2563eb; }
            .form-group { margin-bottom: 1.5rem; }
            .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
            .form-input, .form-select { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
            .form-input:focus, .form-select:focus { outline: none; border-color: #2563eb; background: #ffffff; }
            .switch-group { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem; }
            .switch-group input { height: 1.25rem; width: 1.25rem; accent-color: #2563eb; }
            .submit-btn { padding: 0.875rem 1.5rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; cursor: pointer; margin-top: 0.5rem; }
            .tab-content { display: none; }
            .tab-content.active { display: block; }
            .user-table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
            .user-table th { padding: 0.75rem 1rem; text-align: left; background: #f1f5f9; font-weight: 500; color: #334155; }
            .user-table td { padding: 0.75rem 1rem; color: #1e293b; }
            @media (max-width: 768px) {
              .sidebar { width: 80px; padding: 1rem 0; }
              .sidebar-header h2 span, .menu-item span { display: none; }
              .menu-item { justify-content: center; padding: 1rem; }
              .menu-item i { margin-right: 0; font-size: 1.2rem; }
              .main-content { margin-left: 80px; padding: 1.5rem 1rem; }
            }
          </style>
        </head>
        <body>
          <div class="sidebar">
            <div class="sidebar-header">
              <h2><i class="fa fa-cogs"></i> <span>反代管理系统</span></h2>
            </div>
            <div class="sidebar-menu">
              <div class="menu-item active" onclick="switchTab('base', event)">
                <i class="fa fa-user-circle"></i><span>基础配置</span>
              </div>
              <div class="menu-item" onclick="switchTab('proxy', event)">
                <i class="fa fa-cloud"></i><span>反代配置</span>
              </div>
              <div class="menu-item" onclick="switchTab('user-manage', event)">
                <i class="fa fa-users"></i><span>用户管理</span>
              </div>
            </div>
          </div>
          <div class="main-content">
            <div class="content-header"><h1>后台管理中心</h1></div>
            <div id="base-tab" class="tab-content active">
              <div class="content-card">
                <h3 class="card-title"><i class="fa fa-user-circle"></i>基础配置</h3>
                <form method="POST">
                  <input type="hidden" name="action" value="update_base">
                  <div class="form-group">
                    <label class="form-label">后台访问路径</label>
                    <input type="text" name="new_admin_path" class="form-input" value="{$config['admin_path']}" required>
                  </div>
                  <div class="form-group">
                    <label class="form-label">用户访问路径(注册/登录)</label>
                    <input type="text" name="new_user_path" class="form-input" value="{$config['user_path']}" required>
                  </div>
                  <div class="form-group">
                    <label class="form-label">管理员账号</label>
                    <input type="text" name="new_username" class="form-input" value="{$config['username']}" required>
                  </div>
                  <div class="form-group">
                    <label class="form-label">管理员密码(留空则不修改)</label>
                    <input type="password" name="new_password" class="form-input" placeholder="留空不修改密码">
                  </div>
                  
                  <div class="switch-group">
                    <input type="checkbox" name="require_login" id="require_login" {$requireLoginCheck}>
                    <label for="require_login" class="form-label mb-0" style="color:#2563eb; font-weight:bold;">开启访问登录限制(勾选后用户必须注册/登录才能访问反代网站,不勾选则直接访问)</label>
                  </div>
                  
                  <div class="switch-group">
                    <input type="checkbox" name="auth_enabled" id="auth_enabled" {$authCheck}>
                    <label for="auth_enabled" class="form-label mb-0">开启授权验证(开启后仅授权用户可登录)</label>
                  </div>
                  <button type="submit" class="submit-btn"><i class="fa fa-save"></i> 保存基础配置</button>
                </form>
              </div>
            </div>
            <div id="proxy-tab" class="tab-content">
              <div class="content-card">
                <h3 class="card-title"><i class="fa fa-cloud"></i>反代配置</h3>
                <form method="POST">
                  <input type="hidden" name="action" value="update_proxy">
                  <div class="form-group">
                    <label class="form-label">源站域名</label>
                    <input type="text" name="target_domain" class="form-input" value="{$config['target_domain']}" placeholder="例如:your-domain.com">
                  </div>
                  <div class="form-group">
                    <label class="form-label">当前代理域名(填你在宝塔绑定的域名)</label>
                    <input type="text" name="worker_domain" class="form-input" value="{$config['worker_domain']}" placeholder="例如:your-proxy.com">
                  </div>
                  <div class="form-group" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
                    <div><label class="form-label">HTTP端口</label><input type="number" name="http_port" class="form-input" value="{$config['http_port']}"></div>
                    <div><label class="form-label">HTTPS端口</label><input type="number" name="https_port" class="form-input" value="{$config['https_port']}"></div>
                  </div>
                  <div class="form-group">
                    <label class="form-label">反代模式</label>
                    <select name="proxy_mode" class="form-select">
                      <option value="http_only" {$optHttp}>仅HTTP</option>
                      <option value="https_only" {$optHttps}>仅HTTPS</option>
                      <option value="auto" {$optAuto}>HTTP+HTTPS自适应</option>
                    </select>
                  </div>
                  
                  <div class="form-group" style="margin-top: 1.5rem; margin-bottom: 1.5rem;">
                    <label class="form-label mb-2">强制协议跳转(二选一或不选)</label>
                    <div style="display: flex; gap: 1.5rem; align-items: center; padding: 0.5rem 0;">
                      <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
                        <input type="radio" name="force_protocol" value="0" style="width: 1.2rem; height: 1.2rem; accent-color: #2563eb;" {$forceNoneCheck}> <span>不强制</span>
                      </label>
                      <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer; color: #dc2626; font-weight: 500;">
                        <input type="radio" name="force_protocol" value="https" style="width: 1.2rem; height: 1.2rem; accent-color: #dc2626;" {$forceHttpsCheck}> <span>强制 HTTPS</span>
                      </label>
                      <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer; color: #2563eb; font-weight: 500;">
                        <input type="radio" name="force_protocol" value="http" style="width: 1.2rem; height: 1.2rem; accent-color: #2563eb;" {$forceHttpCheck}> <span>强制 HTTP</span>
                      </label>
                    </div>
                  </div>
        
                  <div class="switch-group">
                    <input type="checkbox" name="cache_enabled" id="cache_enabled" {$cacheCheck}>
                    <label for="cache_enabled" class="form-label mb-0">开启缓存</label>
                  </div>
                  <div class="form-group">
                    <label class="form-label">缓存时长(秒)</label>
                    <input type="number" name="cache_ttl" class="form-input" value="{$config['cache_ttl_seconds']}">
                  </div>
                  <button type="submit" class="submit-btn"><i class="fa fa-save"></i> 保存反代配置</button>
                </form>
              </div>
            </div>
            <div id="user-manage-tab" class="tab-content">
              <div class="content-card">
                <h3 class="card-title"><i class="fa fa-users"></i>用户管理</h3>
                <table class="user-table text-sm">
                  <thead>
                    <tr class="border-b border-gray-300">
                      <th>用户名</th>
                      <th>账号</th>
                      <th>密码管理</th>
                      <th>QQ号</th>
                      <th>状态管理</th>
                      <th>IP/位置</th>
                      <th>到期时间</th>
                      <th>授权操作</th>
                    </tr>
                  </thead>
                  <tbody>{$userTableHtml}</tbody>
                </table>
              </div>
            </div>
          </div>
          <script>
            if (window.location.hash) {
              let tabId = window.location.hash.substring(1);
              if (tabId.endsWith('-tab')) tabId = tabId.replace('-tab', '');
              
              if(document.getElementById(tabId + '-tab')) {
                document.querySelectorAll('.tab-content').forEach(tab => tab.classList.remove('active'));
                document.querySelectorAll('.menu-item').forEach(item => item.classList.remove('active'));
                document.getElementById(tabId + '-tab').classList.add('active');
                const items = document.querySelectorAll('.menu-item');
                for(let item of items) {
                  if(item.getAttribute('onclick').includes("switchTab('" + tabId + "'")) {
                    item.classList.add('active');
                    break;
                  }
                }
              }
            }
            function switchTab(tabId, event) {
              document.querySelectorAll('.tab-content').forEach(tab => tab.classList.remove('active'));
              document.querySelectorAll('.menu-item').forEach(item => item.classList.remove('active'));
              document.getElementById(tabId + '-tab').classList.add('active');
              if(event && event.currentTarget) event.currentTarget.classList.add('active');
              window.history.replaceState(null, null, '#' + tabId);
            }
          </script>
        </body>
        </html>
        HTML;
        }
        
        function getUserRegisterHtml() {
            return <<<HTML
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>用户注册</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
          <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
            .card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
            .card-header { text-align: center; margin-bottom: 2rem; }
            .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
            .card-header p { color: #64748b; font-size: 0.95rem; }
            .form-group { margin-bottom: 1.5rem; }
            .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
            .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
            .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
            .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
            .link-group { text-align: center; margin-top: 1.5rem; font-size: 0.95rem; }
            .link-group a { color: #2563eb; text-decoration: none; }
            .icon { color: #2563eb; margin-right: 0.5rem; }
          </style>
        </head>
        <body>
          <div class="card">
            <div class="card-header">
              <h1><i class="fa fa-user-plus icon"></i>用户注册</h1>
              <p>填写信息完成注册,即可访问反代服务</p>
            </div>
            <form method="POST">
              <div class="form-group">
                <label class="form-label">用户名</label>
                <input type="text" name="username" class="form-input" placeholder="请输入用户名" required>
              </div>
              <div class="form-group">
                <label class="form-label">登录账号</label>
                <input type="text" name="account" class="form-input" placeholder="请设置登录账号" required>
              </div>
              <div class="form-group">
                <label class="form-label">登录密码</label>
                <input type="password" name="password" class="form-input" placeholder="请设置登录密码" required>
              </div>
              <div class="form-group">
                <label class="form-label">QQ号(用于找回密码)</label>
                <input type="text" name="qq_number" class="form-input" placeholder="请输入QQ号" required>
              </div>
              <button type="submit" class="submit-btn"><i class="fa fa-check-circle icon"></i>完成注册</button>
            </form>
            <div class="link-group">
              已有账号?<a href="./login">立即登录</a> | <a href="./find-pwd">找回密码</a>
            </div>
          </div>
        </body>
        </html>
        HTML;
        }
        
        function getUserLoginHtml($errorMsg = '') {
            $alertHtml = $errorMsg ? '<div class="alert"><i class="fa fa-exclamation-circle"></i> ' . $errorMsg . '</div>' : '';
            return <<<HTML
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>用户登录</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
          <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
            .card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
            .card-header { text-align: center; margin-bottom: 2rem; }
            .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
            .alert { background: #fee2e2; color: #dc2626; padding: 1rem; border-radius: 8px; margin-bottom: 1.5rem; text-align: center; font-size: 0.95rem; border-left: 4px solid #dc2626; }
            .form-group { margin-bottom: 1.5rem; }
            .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
            .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
            .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
            .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
            .link-group { text-align: center; margin-top: 1.5rem; font-size: 0.95rem; }
            .link-group a { color: #2563eb; text-decoration: none; }
            .icon { color: #2563eb; margin-right: 0.5rem; }
          </style>
        </head>
        <body>
          <div class="card">
            <div class="card-header">
              <h1><i class="fa fa-lock icon"></i>用户登录</h1>
              <p>登录后即可访问反代服务</p>
            </div>
            {$alertHtml}
            <form method="POST">
              <div class="form-group">
                <label class="form-label">登录账号</label>
                <input type="text" name="account" class="form-input" placeholder="请输入登录账号" required>
              </div>
              <div class="form-group">
                <label class="form-label">登录密码</label>
                <input type="password" name="password" class="form-input" placeholder="请输入登录密码" required>
              </div>
              <button type="submit" class="submit-btn"><i class="fa fa-sign-in icon"></i>立即登录</button>
            </form>
            <div class="link-group">
              没有账号?<a href="./register">立即注册</a> | <a href="./find-pwd">找回密码</a>
            </div>
          </div>
        </body>
        </html>
        HTML;
        }
        
        function getUserFindPwdHtml($msg = '') {
            $msgHtml = $msg ? '<div class="msg-box"><i class="fa fa-info-circle"></i> ' . $msg . '</div>' : '';
            return <<<HTML
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>找回密码</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
          <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
            .card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
            .card-header { text-align: center; margin-bottom: 2rem; }
            .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
            .msg-box { background: #e0f2fe; color: #0369a1; padding: 1rem; border-radius: 8px; margin-bottom: 1.5rem; text-align: center; font-size: 0.95rem; border-left: 4px solid #0369a1; }
            .form-group { margin-bottom: 1.5rem; }
            .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
            .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
            .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
            .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
            .link-group { text-align: center; margin-top: 1.5rem; font-size: 0.95rem; }
            .link-group a { color: #2563eb; text-decoration: none; }
            .icon { color: #2563eb; margin-right: 0.5rem; }
          </style>
        </head>
        <body>
          <div class="card">
            <div class="card-header">
              <h1><i class="fa fa-key icon"></i>找回密码</h1>
              <p>输入QQ号找回账号并重置密码</p>
            </div>
            {$msgHtml}
            <form method="POST">
              <div class="form-group">
                <label class="form-label">注册时填写的QQ号</label>
                <input type="text" name="qq_number" class="form-input" placeholder="请输入QQ号" required>
              </div>
              <div class="form-group">
                <label class="form-label">新密码(留空仅查询账号)</label>
                <input type="password" name="new_password" class="form-input" placeholder="请设置新密码">
              </div>
              <button type="submit" class="submit-btn"><i class="fa fa-search icon"></i>找回/重置</button>
            </form>
            <div class="link-group">
              返回 <a href="./login">登录</a> | <a href="./register">注册</a>
            </div>
          </div>
        </body>
        </html>
        HTML;
        }
        
        function getAccessDeniedHtml($msg) {
            return <<<HTML
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>访问被拒绝</title>
          <script src="https://cdn.tailwindcss.com"></script>
          <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
          <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
            .denied-card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 3rem 2.5rem; max-width: 500px; width: 100%; text-align: center; }
            .denied-icon { font-size: 4rem; color: #dc2626; margin-bottom: 1.5rem; }
            .denied-title { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 1rem; }
            .denied-msg { font-size: 1.1rem; color: #dc2626; line-height: 1.6; margin-bottom: 2rem; }
            .back-btn { display: inline-block; padding: 0.875rem 2rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border-radius: 8px; text-decoration: none; }
          </style>
        </head>
        <body>
          <div class="denied-card">
            <div class="denied-icon"><i class="fa fa-ban"></i></div>
            <h2 class="denied-title">访问被拒绝</h2>
            <div class="denied-msg">{$msg}</div>
            <a href="./user/login" class="back-btn">返回重新登录</a>
          </div>
        </body>
        </html>
        HTML;
        }

        六步:系统初始化

        1. 在浏览器中访问您的域名(例如 http://proxy.yourdomain.com)。

        2. 此时会自动跳转到初始化安装界面

        3. 按照提示设置:

          • 后台访问路径(例如 /admin,为了安全建议改成不规则的,如 /my_admin_666

          • 用户访问路径(例如 /user

          • 管理员账号(例如 admin

          • 管理员密码(例如 123456

        4. 点击完成初始化,系统会自动创建所有的数据库表结构。

        🎉 部署完成!日常使用说明:

        • 进入后台:访问您设置的后台路径(如 您的域名/admin),输入管理员账号密码即可进入管理界面。

        • 配置反代

          • “基础配置” 里,勾选“开启访问登录限制”即可强制访客登录。

          • “反代配置” 里,填入【源站域名】和【代理域名】(注意不要带 http://,直接填域名)。

          • 强制协议推荐选择 不强制(自适应) 或 强制HTTPS

        • 大文件与视频提示:本系统已经从底层彻底重写了 cURL 传输逻辑(无缓冲流式透传),最大支持无限大小的流媒体拖拽和超大压缩包下载,服务器内存占用永远只有几MB,完全不需要修改宝塔的 PHP 内存限制。


        提示:如果后期更换了域名,只需进入后台,在“反代配置”中把“当前代理域名”修改为新域名即可。

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容