胖蒜

分享有价值的资源

TePass插件域名授权验证系统上线

前几天看到payjs不给个人用户充值了,就想着把早就想好的TePass插件本地域名授权给做出来,防止哪天我的网站或者接口挂了,用户不能使用,既然用户花钱买了,就算我的网站挂了,用户也应该有资格继续使用。

于是今天就做了出来,要实现的功能很简单,下面讲一下:

一个简单的PHP页面

为了让用户使用简单,不能给用户带来太多的麻烦,我就做了一个简单的PHP页面,用户只需要将页面放到网站的根目录,通过下面的地址访问即可打开:

你的网址/tepass_verify.php

这里将你的网址改为你网址的地址,比如我的https://pangsuan.com,而tepass_verify.php就是授权信息的PHP页面,如果你更改了名称,记得也要做相应更改才能访问。
比如我的就是https://pangsuan.com/tepass_verify.php
当然为了防止任何人都可以更改授权,或者说知道你的授权信息,这里限制了只有ID为1的用户才可以进入页面,修改信息。

页面操作逻辑

ID为1的用户登录网站后,打开页面就会看到验证信息,授权接口等相关情况,如果授权接口断开了,可以通过自己之前保存的授权信息,输入到本地授权信息里面,更新后,TePass插件也能正常使用。

TePass插件授权待更新

目前TePass插件对应的授权验证还未更新,这里只是做了一个域名授权的页面,后面找时间更新TePass插件的,那么到时候就不再依赖在线授权接口了。虽说现在基本也用不到,因为只要激活了插件,就不再验证授权信息了。

tepass_verify.php代码

下面是tepass_verify.php代码,复制之后放在网站根目录即可。

<?php
require_once 'config.inc.php';
/** 载入API支持 */
require_once 'var/Typecho/Common.php';
/** 初始化组件 */
Typecho_Widget::widget('Widget_Init');
/** 程序初始化 */
Typecho_Common::init();

// 用户验证 - 只有用户ID为1才能访问
Typecho_Widget::widget('Widget_User')->to($user);
Typecho_Widget::widget('Widget_Options')->to($options);

if (!$user->hasLogin() || $user->uid != "1") {
    // 未登录或不是ID为1的用户,跳转到登录页面
    echo("<script> top.location.href='/tepass/signin'</script>");
    exit;
}

// 增加安全性检查
session_start();
if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

$current_domain  = trim($_SERVER['HTTP_HOST']);

// 获取远程授权信息
function getRemoteAuthInfo() {
    try {
        $servername = trim($_SERVER['HTTP_HOST']);
        $api_url = 'https://api.pangsuan.com/authority.php?indentify=tepass&domain=' . $servername;
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $api_url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_USERAGENT, 'TePass-Auth-Verifier/1.0');
        
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($http_code != 200) {
            return [
                'status' => 'error',
                'error_type' => 'http_error',
                'http_code' => $http_code,
                'message' => '接口请求失败,HTTP状态码:' . $http_code,
                'timestamp' => time()
            ];
        }
        
        if ($response === false) {
            return [
                'status' => 'error',
                'error_type' => 'curl_error',
                'message' => '接口连接失败:' . $error,
                'timestamp' => time()
            ];
        }
        
        $authority = json_decode($response, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            return [
                'status' => 'error',
                'error_type' => 'json_error',
                'message' => '接口返回数据解析失败:' . json_last_error_msg(),
                'timestamp' => time()
            ];
        }
        
        if (isset($authority["status"]) && $authority["status"] === 'ok') {
            return [
                'status' => 'success',
                'domain' => $_SERVER['HTTP_HOST'] ?? $servername,
                'auth_data' => $authority["key"] ?? '',
                'api_response' => $authority,
                'timestamp' => time()
            ];
        } else {
            return [
                'status' => 'api_error',
                'error_type' => 'api_status',
                'message' => '接口返回状态异常',
                'api_response' => $authority,
                'timestamp' => time()
            ];
        }
    } catch (Exception $e) {
        return [
            'status' => 'error',
            'error_type' => 'exception',
            'message' => '接口请求异常:' . $e->getMessage(),
            'timestamp' => time()
        ];
    }
}

// 获取本地授权信息
function getLocalAuthInfo() {
    try {
        $db = Typecho_Db::get();
        $rstepassValidation = $db->fetchRow($db->select()->from('table.options')->where('name = ?', "tepassValidation")->limit(1));
        
        if ($rstepassValidation && isset($rstepassValidation['value'])) {
            return $rstepassValidation['value'];
        }
        
        return '';
    } catch (Exception $e) {
        return '';
    }
}

// 更新本地授权信息
function updateLocalAuthInfo($auth_data) {
    try {
        $db = Typecho_Db::get();
        
        $existing = $db->fetchRow($db->select()->from('table.options')->where('name = ?', "tepassValidation")->limit(1));
        
        if ($existing) {
            $result = $db->query($db->update('table.options')->rows(array('value' => $auth_data))->where('name = ?', "tepassValidation"));
        } else {
            $result = $db->query($db->insert('table.options')->rows(array(
                'name' => 'tepassValidation',
                'value' => $auth_data,
                'user' => 0
            )));
        }
        
        return $result !== false;
    } catch (Exception $e) {
        return false;
    }
}

// 验证授权格式
function validateAuthFormat($auth_data) {
    if (empty($auth_data)) {
        return false;
    }
    
    if (strlen($auth_data) < 10) {
        return false;
    }
    
    return true;
}

// 验证CSRF令牌
function validateCSRFToken($token) {
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}

// 处理Ajax请求
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
    header('Content-Type: application/json');
    
    // 首先验证用户权限
    if (!$user->hasLogin() || $user->uid != "1") {
        echo json_encode([
            'success' => false,
            'message' => '用户权限不足,请重新登录',
            'type' => 'error',
            'redirect' => '/tepass/signin'
        ]);
        exit;
    }
    
    // 验证CSRF令牌
    $csrf_token = $_POST['csrf_token'] ?? '';
    if (!validateCSRFToken($csrf_token)) {
        echo json_encode([
            'success' => false,
            'message' => '安全令牌验证失败',
            'type' => 'error'
        ]);
        exit;
    }
    
    $action = $_POST['action'] ?? '';
    $response = ['success' => false, 'message' => '未知操作'];
    
    switch ($action) {
        case 'refresh_remote':
            $remote_data = getRemoteAuthInfo();
            $api_status = $remote_data['status'];
            
            if ($api_status === 'success') {
                $response = [
                    'success' => true,
                    'message' => '远程授权信息刷新成功',
                    'type' => 'success',
                    'data' => [
                        'remote_auth' => $remote_data['auth_data'],
                        'api_status' => $api_status,
                        'timestamp' => date('Y-m-d H:i:s', $remote_data['timestamp']),
                        'status_text' => '接口连接正常'
                    ]
                ];
            } else {
                $response = [
                    'success' => false,
                    'message' => '远程授权信息刷新失败:' . ($remote_data['message'] ?? '未知错误'),
                    'type' => 'error',
                    'data' => [
                        'api_status' => $api_status,
                        'error_detail' => $remote_data['message'] ?? '未知错误',
                        'status_text' => '接口连接异常'
                    ]
                ];
            }
            break;
            
        case 'update_local':
            $new_auth = trim($_POST['local_auth'] ?? '');
            
            if (empty($new_auth)) {
                $response = [
                    'success' => false,
                    'message' => '授权信息不能为空',
                    'type' => 'error'
                ];
                break;
            }
            
            if (!validateAuthFormat($new_auth)) {
                $response = [
                    'success' => false,
                    'message' => '授权信息格式不正确,应为至少10位的字符串',
                    'type' => 'error'
                ];
                break;
            }
            
            if (updateLocalAuthInfo($new_auth)) {
                // 获取最新远程信息进行对比
                $remote_data = getRemoteAuthInfo();
                $api_status = $remote_data['status'];
                $remote_auth = ($api_status === 'success') ? $remote_data['auth_data'] : '';
                
                $comparison_result = ($remote_auth === $new_auth) ? 'match' : 'mismatch';
                
                $response = [
                    'success' => true,
                    'message' => '本地授权信息已更新' . ($comparison_result === 'match' ? ',且与远程信息一致!' : ',但与远程信息不一致。'),
                    'type' => 'success',
                    'data' => [
                        'local_auth' => $new_auth,
                        'remote_auth' => $remote_auth,
                        'api_status' => $api_status,
                        'comparison_result' => $comparison_result,
                        'timestamp' => date('Y-m-d H:i:s')
                    ]
                ];
            } else {
                $response = [
                    'success' => false,
                    'message' => '更新失败,数据库错误',
                    'type' => 'error'
                ];
            }
            break;
            
        case 'verify_auth':
            $local_auth = getLocalAuthInfo();
            $remote_data = getRemoteAuthInfo();
            $api_status = $remote_data['status'];
            $remote_auth = ($api_status === 'success') ? $remote_data['auth_data'] : '';
            
            if ($api_status === 'success') {
                if (empty($local_auth)) {
                    $comparison_result = 'no_local';
                    $message = '本地未找到授权信息';
                } else {
                    $comparison_result = ($remote_auth === $local_auth) ? 'match' : 'mismatch';
                    $message = ($remote_auth === $local_auth) ? '授权信息一致' : '授权信息不一致';
                }
                
                $response = [
                    'success' => true,
                    'message' => $message,
                    'type' => $comparison_result === 'match' ? 'success' : 'warning',
                    'data' => [
                        'local_auth' => $local_auth,
                        'remote_auth' => $remote_auth,
                        'api_status' => $api_status,
                        'comparison_result' => $comparison_result,
                        'timestamp' => date('Y-m-d H:i:s')
                    ]
                ];
            } else {
                $response = [
                    'success' => false,
                    'message' => '接口连接失败:' . ($remote_data['message'] ?? '未知错误'),
                    'type' => 'error',
                    'data' => [
                        'local_auth' => $local_auth,
                        'api_status' => $api_status,
                        'comparison_result' => 'api_error'
                    ]
                ];
            }
            break;
            
        default:
            $response = ['success' => false, 'message' => '无效的操作类型', 'type' => 'error'];
    }
    
    echo json_encode($response);
    exit;
}

// 初始化变量 - 仅用于首次页面加载
$comparison_result = "";
$remote_auth = "";
$local_auth = "";
$message = "";
$message_type = "";
$api_status = "unknown";
$api_error_detail = "";
$last_checked = "";
$remote_timestamp = "";

// 页面首次加载时获取授权信息
$local_auth = getLocalAuthInfo();
$remote_data = getRemoteAuthInfo();
$api_status = $remote_data['status'];
$remote_timestamp = isset($remote_data['timestamp']) ? $remote_data['timestamp'] : time();
$last_checked = date('Y-m-d H:i:s');

if ($api_status === 'success') {
    $remote_auth = $remote_data['auth_data'];
    $api_error_detail = "";
    
    if (empty($local_auth)) {
        $comparison_result = 'no_local';
    } else {
        $comparison_result = ($remote_auth === $local_auth) ? 'match' : 'mismatch';
    }
} else {
    $comparison_result = 'api_error';
    $api_error_detail = isset($remote_data['message']) ? $remote_data['message'] : '未知错误';
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TePass插件域名授权验证系统 - 管理员</title>
    <!-- 使用国内可访问的Font Awesome CDN -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css">
    <style>
        :root {
            --primary-color: #3498db;
            --success-color: #27ae60;
            --warning-color: #f39c12;
            --error-color: #e74c3c;
            --info-color: #17a2b8;
            --light-bg: #f8f9fa;
            --dark-text: #2c3e50;
            --border-color: #dee2e6;
            --shadow-color: rgba(0, 0, 0, 0.1);
            --gradient-primary: linear-gradient(135deg, #3498db, #2980b9);
            --gradient-success: linear-gradient(135deg, #27ae60, #219653);
            --gradient-warning: linear-gradient(135deg, #f39c12, #d68910);
            --modal-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
        }
        
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: 'Segoe UI', 'Microsoft YaHei', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
            color: #333;
            line-height: 1.6;
            padding: 20px;
            min-height: 100vh;
        }
        
        .container {
            max-width: 1000px;
            margin: 30px auto;
            background-color: #fff;
            border-radius: 16px;
            box-shadow: 0 10px 30px var(--shadow-color);
            padding: 30px;
            border: 1px solid var(--border-color);
            position: relative;
            overflow: hidden;
        }
        
        .container::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 5px;
            background: var(--gradient-primary);
        }
        
        h1 {
            text-align: center;
            color: var(--dark-text);
            margin-bottom: 15px;
            padding-bottom: 15px;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 15px;
        }
        
        .logo {
            font-size: 2.5rem;
            color: var(--primary-color);
        }
        
        .subtitle {
            text-align: center;
            color: #7f8c8d;
            margin-bottom: 30px;
            font-size: 1.1rem;
        }
        
        .user-info {
            background: var(--gradient-primary);
            color: white;
            padding: 10px 20px;
            border-radius: 8px;
            margin-bottom: 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 4px 6px rgba(52, 152, 219, 0.3);
        }
        
        .user-info span {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .user-info a {
            color: white;
            text-decoration: none;
            font-size: 0.9rem;
            padding: 5px 12px;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 4px;
            transition: background 0.3s;
        }
        
        .user-info a:hover {
            background: rgba(255, 255, 255, 0.3);
        }
        
        .status-panel {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }
        
        .status-box {
            background-color: var(--light-bg);
            padding: 25px;
            border-radius: 12px;
            border-left: 5px solid var(--primary-color);
            transition: all 0.3s ease;
            box-shadow: 0 4px 6px var(--shadow-color);
            position: relative;
            overflow: hidden;
        }
        
        .status-box:hover {
            transform: translateY(-5px);
            box-shadow: 0 6px 12px var(--shadow-color);
        }
        
        .status-box h3 {
            font-size: 1rem;
            color: #555;
            margin-bottom: 15px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .status-value {
            font-size: 1.5rem;
            font-weight: bold;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .status-detail {
            font-size: 0.9rem;
            color: #777;
            margin-top: 5px;
        }
        
        .auth-section {
            margin-bottom: 30px;
            padding: 25px;
            background-color: var(--light-bg);
            border-radius: 12px;
            box-shadow: 0 4px 6px var(--shadow-color);
            border: 1px solid var(--border-color);
        }
        
        .section-title {
            font-size: 1.3rem;
            color: var(--dark-text);
            margin-bottom: 20px;
            padding-bottom: 10px;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .section-icon {
            font-size: 1.5rem;
            color: var(--primary-color);
        }
        
        .input-group {
            margin-bottom: 25px;
        }
        
        label {
            display: block;
            margin-bottom: 10px;
            font-weight: 600;
            color: #555;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .auth-input-container {
            display: flex;
            align-items: center;
            gap: 10px;
            width: 100%;
        }
        
        .auth-input {
            flex: 1;
            padding: 14px 16px;
            border: 2px solid var(--border-color);
            border-radius: 8px;
            font-size: 16px;
            transition: all 0.3s;
            font-family: 'Consolas', 'Monaco', monospace;
            background-color: #fff;
            min-height: 52px;
        }
        
        .auth-input:focus {
            border-color: var(--primary-color);
            outline: none;
            box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
        }
        
        .auth-input[readonly] {
            background-color: #f8f9fa;
            color: #666;
            border-color: #ced4da;
            cursor: not-allowed;
        }
        
        .copy-btn {
            background: var(--gradient-primary);
            color: white;
            border: none;
            border-radius: 8px;
            padding: 14px 20px;
            cursor: pointer;
            font-size: 0.95rem;
            font-weight: 600;
            transition: all 0.3s;
            white-space: nowrap;
            display: flex;
            align-items: center;
            gap: 8px;
            min-height: 52px;
            flex-shrink: 0;
        }
        
        .copy-btn:hover {
            background: linear-gradient(135deg, #2980b9, #1f6394);
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3);
        }
        
        .copy-btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none !important;
            box-shadow: none !important;
        }
        
        .copy-btn.copied {
            background: var(--gradient-success);
        }
        
        .input-hint {
            font-size: 0.9rem;
            color: #777;
            margin-top: 8px;
            padding: 10px;
            background-color: rgba(52, 152, 219, 0.1);
            border-radius: 6px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .input-hint.warning {
            background-color: rgba(231, 76, 60, 0.1);
            color: #c0392b;
        }
        
        .input-hint.success {
            background-color: rgba(39, 174, 96, 0.1);
            color: #27ae60;
        }
        
        .btn-group {
            display: flex;
            gap: 15px;
            margin-top: 25px;
        }
        
        .btn {
            padding: 14px 25px;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 16px;
            font-weight: 600;
            transition: all 0.3s;
            text-align: center;
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            text-decoration: none;
        }
        
        .btn-primary {
            background: var(--gradient-primary);
            color: white;
        }
        
        .btn-primary:hover:not(:disabled) {
            background: linear-gradient(135deg, #2980b9, #1f6394);
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3);
        }
        
        .btn-success {
            background: var(--gradient-success);
            color: white;
        }
        
        .btn-success:hover:not(:disabled) {
            background: linear-gradient(135deg, #219653, #1a7945);
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(39, 174, 96, 0.3);
        }
        
        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none !important;
            box-shadow: none !important;
        }
        
        .message {
            padding: 20px 25px;
            border-radius: 10px;
            margin-bottom: 30px;
            border-left: 5px solid transparent;
            display: flex;
            align-items: center;
            gap: 15px;
            animation: fadeIn 0.5s ease;
        }
        
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(-10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        
        .message-icon {
            font-size: 1.8rem;
            flex-shrink: 0;
        }
        
        .message-content {
            flex-grow: 1;
        }
        
        .message.success {
            background-color: rgba(39, 174, 96, 0.1);
            color: #155724;
            border-left-color: var(--success-color);
        }
        
        .message.error {
            background-color: rgba(231, 76, 60, 0.1);
            color: #721c24;
            border-left-color: var(--error-color);
        }
        
        .message.warning {
            background-color: rgba(243, 156, 18, 0.1);
            color: #856404;
            border-left-color: var(--warning-color);
        }
        
        .message.info {
            background-color: rgba(23, 162, 184, 0.1);
            color: #0c5460;
            border-left-color: var(--info-color);
        }
        
        .security-alert {
            background: linear-gradient(135deg, #fff3cd, #ffeaa7);
            border: 2px solid #ffc107;
            border-radius: 10px;
            padding: 20px;
            margin: 25px 0;
            display: flex;
            align-items: center;
            gap: 15px;
            animation: pulse 2s infinite;
        }
        
        @keyframes pulse {
            0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); }
            70% { box-shadow: 0 0 0 10px rgba(255, 193, 7, 0); }
            100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); }
        }
        
        .security-alert-icon {
            font-size: 2rem;
            color: #856404;
            flex-shrink: 0;
        }
        
        .info-box {
            background-color: var(--light-bg);
            border-left: 5px solid var(--primary-color);
            padding: 25px;
            margin-top: 30px;
            border-radius: 12px;
            box-shadow: 0 4px 6px var(--shadow-color);
        }
        
        .info-box h3 {
            margin-top: 0;
            color: var(--dark-text);
            margin-bottom: 20px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .info-list {
            list-style-type: none;
            padding-left: 0;
        }
        
        .info-list li {
            margin-bottom: 15px;
            padding-left: 30px;
            position: relative;
        }
        
        .info-list li:before {
            content: "✓";
            color: var(--success-color);
            font-size: 1.2rem;
            position: absolute;
            left: 0;
            top: -2px;
            font-weight: bold;
        }
        
        .info-list li.warning:before {
            content: "⚠";
            color: var(--warning-color);
        }
        
        .info-list li.error:before {
            content: "✗";
            color: var(--error-color);
        }
        
        .footer-info {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-top: 30px;
            padding-top: 20px;
            border-top: 1px solid var(--border-color);
            color: #7f8c8d;
            font-size: 0.9rem;
            flex-wrap: wrap;
            gap: 15px;
        }
        
        .last-checked {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .loading-spinner {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: white;
            animation: spin 1s ease-in-out infinite;
        }
        
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        
        .loading-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.5);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 9999;
            opacity: 0;
            visibility: hidden;
            transition: all 0.3s;
        }
        
        .loading-overlay.active {
            opacity: 1;
            visibility: visible;
        }
        
        .loading-content {
            background: white;
            padding: 30px;
            border-radius: 12px;
            text-align: center;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
            min-width: 200px;
        }
        
        .loading-text {
            margin-top: 15px;
            font-weight: 600;
            color: var(--dark-text);
        }
        
        /* 现代化弹窗样式 */
        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.6);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 10000;
            opacity: 0;
            visibility: hidden;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            backdrop-filter: blur(5px);
        }
        
        .modal-overlay.active {
            opacity: 1;
            visibility: visible;
        }
        
        .modal {
            background: white;
            border-radius: 20px;
            width: 90%;
            max-width: 500px;
            overflow: hidden;
            transform: translateY(30px) scale(0.95);
            opacity: 0;
            transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            box-shadow: var(--modal-shadow);
        }
        
        .modal-overlay.active .modal {
            transform: translateY(0) scale(1);
            opacity: 1;
        }
        
        .modal-header {
            background: var(--gradient-primary);
            color: white;
            padding: 25px 30px;
            position: relative;
        }
        
        .modal-header::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            height: 5px;
            background: rgba(255, 255, 255, 0.2);
        }
        
        .modal-title {
            font-size: 1.5rem;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 12px;
        }
        
        .modal-title i {
            font-size: 1.8rem;
        }
        
        .modal-close {
            position: absolute;
            top: 20px;
            right: 20px;
            background: rgba(255, 255, 255, 0.2);
            border: none;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            color: white;
            font-size: 1.2rem;
            cursor: pointer;
            transition: all 0.3s;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        
        .modal-close:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: rotate(90deg);
        }
        
        .modal-body {
            padding: 30px;
        }
        
        .modal-content {
            font-size: 1.1rem;
            line-height: 1.6;
            color: var(--dark-text);
            margin-bottom: 25px;
        }
        
        .modal-content i {
            color: var(--warning-color);
            margin-right: 10px;
            font-size: 1.3rem;
        }
        
        .modal-actions {
            display: flex;
            gap: 15px;
            margin-top: 30px;
        }
        
        .modal-btn {
            flex: 1;
            padding: 16px 20px;
            border: none;
            border-radius: 10px;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
        }
        
        .modal-btn-primary {
            background: var(--gradient-success);
            color: white;
        }
        
        .modal-btn-primary:hover {
            background: linear-gradient(135deg, #219653, #1a7945);
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(39, 174, 96, 0.3);
        }
        
        .modal-btn-secondary {
            background: #f8f9fa;
            color: #6c757d;
            border: 2px solid #dee2e6;
        }
        
        .modal-btn-secondary:hover {
            background: #e9ecef;
            border-color: #ced4da;
            transform: translateY(-2px);
        }
        
        .modal-warning {
            background: linear-gradient(135deg, #fff9e6, #fff3cd);
            border-left: 5px solid var(--warning-color);
            padding: 15px;
            border-radius: 8px;
            margin: 20px 0;
        }
        
        .modal-warning i {
            color: var(--warning-color);
            font-size: 1.2rem;
            margin-right: 10px;
        }
        
        /* 成功提示弹窗 */
        .success-modal .modal-header {
            background: var(--gradient-success);
        }
        
        .success-modal .modal-title i {
            animation: bounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
        }
        
        @keyframes bounce {
            0%, 20%, 50%, 80%, 100% {transform: translateY(0);}
            40% {transform: translateY(-15px);}
            60% {transform: translateY(-7px);}
        }
        
        /* 错误提示弹窗 */
        .error-modal .modal-header {
            background: var(--gradient-warning);
        }
        
        .error-modal .modal-title i {
            animation: shake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97);
        }
        
        @keyframes shake {
            10%, 90% {transform: translate3d(-1px, 0, 0);}
            20%, 80% {transform: translate3d(2px, 0, 0);}
            30%, 50%, 70% {transform: translate3d(-4px, 0, 0);}
            40%, 60% {transform: translate3d(4px, 0, 0);}
        }
        
        /* Toast提示 */
        .toast {
            position: fixed;
            top: 30px;
            right: 30px;
            background: white;
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
            display: flex;
            align-items: center;
            gap: 15px;
            z-index: 10001;
            transform: translateX(100%);
            opacity: 0;
            transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
            max-width: 400px;
        }
        
        .toast.show {
            transform: translateX(0);
            opacity: 1;
        }
        
        .toast-icon {
            font-size: 1.8rem;
            flex-shrink: 0;
        }
        
        .toast-success {
            border-left: 5px solid var(--success-color);
        }
        
        .toast-success .toast-icon {
            color: var(--success-color);
        }
        
        .toast-error {
            border-left: 5px solid var(--error-color);
        }
        
        .toast-error .toast-icon {
            color: var(--error-color);
        }
        
        .toast-warning {
            border-left: 5px solid var(--warning-color);
        }
        
        .toast-warning .toast-icon {
            color: var(--warning-color);
        }
        
        .toast-content {
            flex-grow: 1;
        }
        
        .toast-title {
            font-weight: 600;
            margin-bottom: 5px;
            color: var(--dark-text);
        }
        
        .toast-message {
            font-size: 0.95rem;
            color: #666;
        }
        
        @media (max-width: 768px) {
            .status-panel {
                grid-template-columns: 1fr;
            }
            
            .btn-group {
                flex-direction: column;
            }
            
            .container {
                padding: 20px;
            }
            
            .footer-info {
                flex-direction: column;
                align-items: flex-start;
            }
            
            .auth-input-container {
                flex-direction: column;
                align-items: stretch;
            }
            
            .copy-btn {
                width: 100%;
                justify-content: center;
            }
            
            .modal {
                width: 95%;
                margin: 10px;
            }
            
            .modal-actions {
                flex-direction: column;
            }
            
            .toast {
                left: 20px;
                right: 20px;
                max-width: none;
            }
            
            .user-info {
                flex-direction: column;
                gap: 10px;
                text-align: center;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>
            <i class="fas fa-shield-alt logo"></i>
            TePass插件域名授权验证系统
        </h1>
        <p class="subtitle">安全、可靠的授权验证与管理工具</p>
        
        <!-- 用户信息栏 -->
        <div class="user-info">
            <span>
                <i class="fas fa-user-shield"></i>
                管理员:<?php echo htmlspecialchars($user->screenName); ?> (ID: <?php echo $user->uid; ?>)
            </span>
            <a href="<?php $options->logoutUrl() ?>" title="退出登录"
                <i class="fas fa-sign-out-alt"></i> 退出
            </a>
        </div>
        
        <div class="security-alert">
            <div class="security-alert-icon">
                <i class="fas fa-exclamation-triangle"></i>
            </div>
            <div>
                <strong>重要安全提醒:</strong> 请妥善保管您的授权信息,不要泄露给他人。授权信息是您使用TePass插件的唯一凭证,丢失可能导致服务中断。建议您将授权信息备份到安全的地方。
            </div>
        </div>
        
        <div id="message-container"></div>
        
        <div class="status-panel">
            <div class="status-box api-status" id="api-status-box">
                <h3><i class="fas fa-satellite-dish"></i> 接口状态</h3>
                <div class="status-value">
                    <span id="api-status-icon">
                        <?php if ($api_status === 'success'): ?>
                        <i class="fas fa-check-circle" style="color: var(--success-color);"></i>
                        <span style="color: var(--success-color);">正常</span>
                        <?php elseif ($api_status === 'error' || $api_status === 'api_error'): ?>
                        <i class="fas fa-times-circle" style="color: var(--error-color);"></i>
                        <span style="color: var(--error-color);">异常</span>
                        <?php else: ?>
                        <i class="fas fa-question-circle" style="color: var(--warning-color);"></i>
                        <span style="color: var(--warning-color);">未知</span>
                        <?php endif; ?>
                    </span>
                </div>
                <div class="status-detail" id="api-status-detail">
                    <?php if ($api_error_detail): ?>
                    <i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($api_error_detail); ?>
                    <?php else: ?>
                    <i class="fas <?php echo $api_status === 'success' ? 'fa-wifi' : 'fa-wifi-slash'; ?>"></i>
                    <?php echo $api_status === 'success' ? '授权接口连接正常' : '无法连接授权接口'; ?>
                    <?php endif; ?>
                </div>
            </div>
            
            <div class="status-box auth-status" id="auth-status-box">
                <h3><i class="fas fa-key"></i> 授权状态</h3>
                <div class="status-value">
                    <span id="auth-status-icon">
                        <?php if ($comparison_result === 'match'): ?>
                        <i class="fas fa-check-circle" style="color: var(--success-color);"></i>
                        <span style="color: var(--success-color);">已验证</span>
                        <?php elseif ($comparison_result === 'mismatch'): ?>
                        <i class="fas fa-times-circle" style="color: var(--error-color);"></i>
                        <span style="color: var(--error-color);">不一致</span>
                        <?php elseif ($comparison_result === 'no_local'): ?>
                        <i class="fas fa-exclamation-circle" style="color: var(--error-color);"></i>
                        <span style="color: var(--error-color);">未配置</span>
                        <?php elseif ($comparison_result === 'api_error'): ?>
                        <i class="fas fa-question-circle" style="color: var(--warning-color);"></i>
                        <span style="color: var(--warning-color);">待验证</span>
                        <?php else: ?>
                        <i class="fas fa-sync-alt fa-spin"></i>
                        <span style="color: var(--primary-color);">检查中...</span>
                        <?php endif; ?>
                    </span>
                </div>
                <div class="status-detail" id="auth-status-detail">
                    <?php 
                    if ($comparison_result === 'match') {
                        echo '<i class="fas fa-thumbs-up"></i> 远程与本地授权信息一致';
                    } elseif ($comparison_result === 'mismatch') {
                        echo '<i class="fas fa-exclamation-triangle"></i> 远程与本地授权信息不一致';
                    } elseif ($comparison_result === 'no_local') {
                        echo '<i class="fas fa-database"></i> 本地未找到授权信息';
                    } elseif ($comparison_result === 'api_error') {
                        echo '<i class="fas fa-cloud-slash"></i> 接口异常,无法验证';
                    } else {
                        echo '<i class="fas fa-cog fa-spin"></i> 正在验证授权信息...';
                    }
                    ?>
                </div>
            </div>
            
            <div class="status-box">
                <h3><i class="fas fa-globe"></i> 域名信息</h3>
                <div class="status-value"><?php echo htmlspecialchars($current_domain); ?></div>
                <div class="status-detail">
                    <i class="fas fa-clock"></i> 最后检查: <span id="last-checked"><?php echo $last_checked; ?></span>
                </div>
            </div>
        </div>
        
        <div class="auth-section">
            <h2 class="section-title">
                <span><i class="fas fa-cloud section-icon"></i> 远程授权信息</span>
                <span id="remote-timestamp" style="font-size: 0.9rem; color: var(--success-color);">
                    <?php if ($api_status === 'success'): ?>
                    <i class="fas fa-clock"></i> <?php echo date('H:i:s', $remote_timestamp); ?>
                    <?php endif; ?>
                </span>
            </h2>
            <div class="input-group">
                <label for="remote_auth">
                    <i class="fas fa-download"></i> 从授权接口获取的信息
                </label>
                <div class="auth-input-container">
                    <input 
                        type="text" 
                        id="remote_auth" 
                        class="auth-input" 
                        value="<?php echo htmlspecialchars($remote_auth); ?>" 
                        readonly
                        placeholder="<?php echo $api_status !== 'success' ? '接口连接失败,无法获取远程授权信息' : ''; ?>"
                    >
                    <?php if ($remote_auth): ?>
                    <button type="button" class="copy-btn" id="copy-remote-btn">
                        <i class="fas fa-copy"></i> 复制
                    </button>
                    <?php endif; ?>
                </div>
                <?php if ($api_status !== 'success'): ?>
                <div class="input-hint warning">
                    <i class="fas fa-exclamation-triangle"></i>
                    授权接口连接失败时,请手动输入正确的授权信息保存到本地
                </div>
                <?php endif; ?>
            </div>
            <div class="btn-group">
                <button type="button" id="refresh-remote-btn" class="btn btn-primary">
                    <i class="fas fa-sync-alt"></i> 刷新远程信息
                </button>
            </div>
        </div>
        
        <div class="auth-section">
            <h2 class="section-title">
                <span><i class="fas fa-database section-icon"></i> 本地授权信息</span>
                <span id="local-saved-status" style="font-size: 0.9rem; color: var(--success-color);">
                    <?php if ($local_auth): ?>
                    <i class="fas fa-check"></i> 已保存
                    <?php endif; ?>
                </span>
            </h2>
            <div class="input-group">
                <label for="local_auth">
                    <i class="fas fa-edit"></i> 本地数据库保存的授权信息
                </label>
                <div class="auth-input-container">
                    <input 
                        type="text" 
                        id="local_auth" 
                        class="auth-input" 
                        value="<?php echo htmlspecialchars($local_auth); ?>" 
                        placeholder="请输入正确的授权信息"
                        autocomplete="off"
                    >
                    <?php if ($local_auth): ?>
                    <button type="button" class="copy-btn" id="copy-local-btn">
                        <i class="fas fa-copy"></i> 复制
                    </button>
                    <?php endif; ?>
                </div>
                <div class="input-hint">
                    <i class="fas fa-info-circle"></i>
                    当授权接口断开时,请在此处输入正确的授权信息并保存到本地
                </div>
            </div>
            <div class="btn-group">
                <button type="button" id="update-local-btn" class="btn btn-success">
                    <i class="fas fa-save"></i> 保存到本地
                </button>
            </div>
        </div>
        
        <div class="info-box">
            <h3><i class="fas fa-book"></i> 使用说明与安全注意事项</h3>
            <ul class="info-list">
                <li><strong>访问权限:</strong>只有管理员(用户ID为1)可以访问和操作此系统</li>
                <li><strong>自动验证:</strong>页面加载时会自动获取远程和本地授权信息并进行对比验证</li>
                <li class="warning"><strong>安全提醒:</strong>授权信息是您使用TePass插件的唯一凭证,请勿泄露给他人,并建议做好备份</li>
                <li><strong>无刷新操作:</strong>刷新远程信息和保存本地信息均采用Ajax方式,无需刷新整个页面</li>
                <li><strong>实时状态更新:</strong>所有操作后状态面板会实时更新,显示最新状态</li>
                <li><strong>复制功能:</strong>点击右侧复制按钮可快速复制授权信息</li>
                <li class="error"><strong>注意事项:</strong>请确保输入的授权信息格式正确,授权信息丢失可能导致服务中断</li>
                <li><strong>快捷操作:</strong>使用 Ctrl+R 刷新远程信息,Ctrl+S 保存本地信息</li>
            </ul>
        </div>
        
        <div class="footer-info">
            <div>
                <i class="fas fa-code"></i> TePass插件域名授权验证系统 &copy; <?php echo date('Y'); ?>
            </div>
            <div class="last-checked">
                <i class="fas fa-clock"></i> 最后检查时间: <span id="footer-last-checked"><?php echo $last_checked; ?></span>
            </div>
            <div>
                <i class="fas fa-shield-alt"></i> 当前域名: <?php echo htmlspecialchars($current_domain); ?>
            </div>
        </div>
    </div>
    
    <!-- 加载遮罩层 -->
    <div class="loading-overlay" id="loading-overlay">
        <div class="loading-content">
            <div class="loading-spinner"></div>
            <div class="loading-text" id="loading-text">处理中...</div>
        </div>
    </div>
    
    <!-- 确认保存弹窗 -->
    <div class="modal-overlay" id="confirm-modal">
        <div class="modal">
            <div class="modal-header">
                <div class="modal-title">
                    <i class="fas fa-shield-alt"></i>
                    确认保存授权信息
                </div>
                <button class="modal-close" id="confirm-modal-close">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <div class="modal-body">
                <div class="modal-content">
                    <i class="fas fa-exclamation-circle"></i>
                    您确定要保存授权信息到本地数据库吗?
                </div>
                
                <div class="modal-warning">
                    <i class="fas fa-exclamation-triangle"></i>
                    <strong>重要提醒:</strong>请确保这是正确的授权信息,错误的授权信息可能导致插件无法正常工作。
                </div>
                
                <div class="modal-actions">
                    <button class="modal-btn modal-btn-secondary" id="confirm-cancel">
                        <i class="fas fa-times"></i> 取消
                    </button>
                    <button class="modal-btn modal-btn-primary" id="confirm-save">
                        <i class="fas fa-save"></i> 确认保存
                    </button>
                </div>
            </div>
        </div>
    </div>
    
    <!-- 成功提示弹窗 -->
    <div class="modal-overlay success-modal" id="success-modal">
        <div class="modal">
            <div class="modal-header">
                <div class="modal-title">
                    <i class="fas fa-check-circle"></i>
                    保存成功
                </div>
                <button class="modal-close" id="success-modal-close">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <div class="modal-body">
                <div class="modal-content" id="success-message">
                    授权信息已成功保存到本地数据库!
                </div>
                
                <div class="modal-actions">
                    <button class="modal-btn modal-btn-primary" id="success-ok">
                        <i class="fas fa-check"></i> 确定
                    </button>
                </div>
            </div>
        </div>
    </div>
    
    <!-- 错误提示弹窗 -->
    <div class="modal-overlay error-modal" id="error-modal">
        <div class="modal">
            <div class="modal-header">
                <div class="modal-title">
                    <i class="fas fa-exclamation-triangle"></i>
                    保存失败
                </div>
                <button class="modal-close" id="error-modal-close">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <div class="modal-body">
                <div class="modal-content" id="error-message">
                    授权信息保存失败,请检查后重试。
                </div>
                
                <div class="modal-actions">
                    <button class="modal-btn modal-btn-primary" id="error-ok">
                        <i class="fas fa-redo"></i> 重试
                    </button>
                </div>
            </div>
        </div>
    </div>
    
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const csrfToken = '<?php echo $_SESSION['csrf_token']; ?>';
            const currentDomain = '<?php echo htmlspecialchars($current_domain); ?>';
            
            // 自动聚焦逻辑
            const apiStatus = '<?php echo $api_status; ?>';
            const comparisonResult = '<?php echo $comparison_result; ?>';
            const localAuthInput = document.getElementById('local_auth');
            
            if ((apiStatus !== 'success' || comparisonResult === 'mismatch' || comparisonResult === 'no_local') && localAuthInput) {
                setTimeout(() => {
                    localAuthInput.focus();
                    localAuthInput.select();
                }, 300);
            }
            
            // 键盘快捷键
            document.addEventListener('keydown', function(e) {
                if (e.ctrlKey) {
                    if (e.key === 'r' || e.key === 'R') {
                        e.preventDefault();
                        refreshRemoteAuth();
                    } else if (e.key === 's' || e.key === 'S') {
                        e.preventDefault();
                        showConfirmModal();
                    }
                }
            });
            
            // 复制按钮功能
            document.querySelectorAll('.copy-btn').forEach(button => {
                button.addEventListener('click', function() {
                    const targetId = this.id === 'copy-remote-btn' ? 'remote_auth' : 'local_auth';
                    const input = document.getElementById(targetId);
                    
                    input.select();
                    input.setSelectionRange(0, 99999);
                    
                    try {
                        const successful = document.execCommand('copy');
                        if (successful) {
                            showToast('授权信息已复制到剪贴板!', 'success');
                            
                            // 按钮反馈效果
                            const icon = this.querySelector('i');
                            const originalIconClass = icon.className;
                            const originalText = this.textContent;
                            
                            icon.className = 'fas fa-check';
                            this.textContent = '已复制';
                            this.classList.add('copied');
                            
                            setTimeout(() => {
                                icon.className = originalIconClass;
                                this.textContent = originalText;
                                this.classList.remove('copied');
                            }, 2000);
                        }
                    } catch (err) {
                        console.error('复制失败:', err);
                        showToast('复制失败,请手动选择并复制', 'error');
                    }
                    
                    if (window.getSelection) {
                        window.getSelection().removeAllRanges();
                    }
                });
            });
            
            // 刷新远程信息按钮
            document.getElementById('refresh-remote-btn').addEventListener('click', refreshRemoteAuth);
            
            // 更新本地信息按钮
            document.getElementById('update-local-btn').addEventListener('click', showConfirmModal);
            
            // 弹窗控制
            const confirmModal = document.getElementById('confirm-modal');
            const successModal = document.getElementById('success-modal');
            const errorModal = document.getElementById('error-modal');
            
            // 确认弹窗
            document.getElementById('confirm-modal-close').addEventListener('click', () => hideModal(confirmModal));
            document.getElementById('confirm-cancel').addEventListener('click', () => hideModal(confirmModal));
            document.getElementById('confirm-save').addEventListener('click', updateLocalAuth);
            
            // 成功弹窗
            document.getElementById('success-modal-close').addEventListener('click', () => hideModal(successModal));
            document.getElementById('success-ok').addEventListener('click', () => hideModal(successModal));
            
            // 错误弹窗
            document.getElementById('error-modal-close').addEventListener('click', () => hideModal(errorModal));
            document.getElementById('error-ok').addEventListener('click', () => {
                hideModal(errorModal);
                setTimeout(showConfirmModal, 300);
            });
            
            // 显示弹窗函数
            function showModal(modal) {
                document.body.style.overflow = 'hidden';
                modal.classList.add('active');
            }
            
            // 隐藏弹窗函数
            function hideModal(modal) {
                document.body.style.overflow = '';
                modal.classList.remove('active');
            }
            
            // 显示确认弹窗
            function showConfirmModal() {
                const localAuth = document.getElementById('local_auth').value.trim();
                
                if (!localAuth) {
                    showToast('授权信息不能为空', 'error');
                    document.getElementById('local_auth').focus();
                    return;
                }
                
                if (localAuth.length < 10) {
                    showToast('授权信息格式不正确,应为至少10位的字符串', 'error');
                    document.getElementById('local_auth').focus();
                    document.getElementById('local_auth').select();
                    return;
                }
                
                showModal(confirmModal);
            }
            
            // Toast提示函数
            function showToast(message, type = 'info', title = '') {
                // 移除现有的toast
                const existingToast = document.querySelector('.toast');
                if (existingToast) {
                    existingToast.remove();
                }
                
                // 创建新的toast
                const toast = document.createElement('div');
                toast.className = `toast toast-${type}`;
                
                let icon = 'info-circle';
                if (type === 'success') icon = 'check-circle';
                if (type === 'error') icon = 'exclamation-circle';
                if (type === 'warning') icon = 'exclamation-triangle';
                
                toast.innerHTML = `
                    <div class="toast-icon">
                        <i class="fas fa-${icon}"></i>
                    </div>
                    <div class="toast-content">
                        ${title ? `<div class="toast-title">${title}</div>` : ''}
                        <div class="toast-message">${message}</div>
                    </div>
                `;
                
                document.body.appendChild(toast);
                
                // 触发动画
                setTimeout(() => {
                    toast.classList.add('show');
                }, 10);
                
                // 5秒后自动移除
                setTimeout(() => {
                    toast.classList.remove('show');
                    setTimeout(() => {
                        if (toast.parentNode) {
                            toast.remove();
                        }
                    }, 400);
                }, 5000);
            }
            
            // 显示加载中
            function showLoading(text = '处理中...') {
                const overlay = document.getElementById('loading-overlay');
                const loadingText = document.getElementById('loading-text');
                loadingText.textContent = text;
                overlay.classList.add('active');
            }
            
            // 隐藏加载中
            function hideLoading() {
                const overlay = document.getElementById('loading-overlay');
                overlay.classList.remove('active');
            }
            
            // Ajax请求函数
            function sendAjaxRequest(action, data = {}) {
                return new Promise((resolve, reject) => {
                    showLoading('正在处理...');
                    
                    const xhr = new XMLHttpRequest();
                    xhr.open('POST', '', true);
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                    
                    xhr.onreadystatechange = function() {
                        if (xhr.readyState === 4) {
                            hideLoading();
                            if (xhr.status === 200) {
                                try {
                                    const response = JSON.parse(xhr.responseText);
                                    // 检查是否有重定向(权限不足)
                                    if (response.redirect) {
                                        window.location.href = response.redirect;
                                        return;
                                    }
                                    resolve(response);
                                } catch (e) {
                                    reject(new Error('响应解析失败'));
                                }
                            } else {
                                reject(new Error('请求失败,状态码:' + xhr.status));
                            }
                        }
                    };
                    
                    xhr.onerror = function() {
                        hideLoading();
                        reject(new Error('网络请求失败'));
                    };
                    
                    const params = new URLSearchParams();
                    params.append('action', action);
                    params.append('csrf_token', csrfToken);
                    
                    for (const key in data) {
                        params.append(key, data[key]);
                    }
                    
                    xhr.send(params.toString());
                });
            }
            
            // 刷新远程授权信息
            async function refreshRemoteAuth() {
                const refreshBtn = document.getElementById('refresh-remote-btn');
                const originalContent = refreshBtn.innerHTML;
                
                refreshBtn.disabled = true;
                refreshBtn.innerHTML = '<div class="loading-spinner"></div> 刷新中...';
                
                try {
                    const response = await sendAjaxRequest('refresh_remote');
                    
                    if (response.success) {
                        // 更新远程授权信息
                        document.getElementById('remote_auth').value = response.data.remote_auth;
                        
                        // 更新状态面板
                        updateStatusPanel(response.data);
                        
                        // 显示成功消息
                        showToast(response.message, 'success', '刷新成功');
                        
                        // 启用复制按钮
                        if (response.data.remote_auth && !document.getElementById('copy-remote-btn')) {
                            const inputContainer = document.querySelector('.auth-input-container');
                            const copyBtn = document.createElement('button');
                            copyBtn.type = 'button';
                            copyBtn.className = 'copy-btn';
                            copyBtn.id = 'copy-remote-btn';
                            copyBtn.innerHTML = '<i class="fas fa-copy"></i> 复制';
                            copyBtn.addEventListener('click', function() {
                                const input = document.getElementById('remote_auth');
                                input.select();
                                document.execCommand('copy');
                                showToast('已复制到剪贴板', 'success');
                            });
                            inputContainer.appendChild(copyBtn);
                        }
                    } else {
                        showToast(response.message, 'error', '刷新失败');
                        updateStatusPanel(response.data);
                    }
                } catch (error) {
                    console.error('刷新失败:', error);
                    showToast('刷新失败:' + error.message, 'error', '网络错误');
                } finally {
                    refreshBtn.disabled = false;
                    refreshBtn.innerHTML = originalContent;
                }
            }
            
            // 更新本地授权信息
            async function updateLocalAuth() {
                hideModal(confirmModal);
                
                const localAuth = document.getElementById('local_auth').value.trim();
                const updateBtn = document.getElementById('update-local-btn');
                const originalContent = updateBtn.innerHTML;
                
                updateBtn.disabled = true;
                updateBtn.innerHTML = '<div class="loading-spinner"></div> 保存中...';
                
                try {
                    const response = await sendAjaxRequest('update_local', { local_auth: localAuth });
                    
                    if (response.success) {
                        // 更新本地授权信息显示
                        document.getElementById('local_auth').value = response.data.local_auth;
                        
                        // 更新状态面板
                        updateStatusPanel(response.data);
                        
                        // 显示保存状态
                        const savedStatus = document.getElementById('local-saved-status');
                        savedStatus.innerHTML = '<i class="fas fa-check"></i> 已保存';
                        savedStatus.style.color = 'var(--success-color)';
                        
                        // 显示成功弹窗
                        document.getElementById('success-message').textContent = response.message;
                        showModal(successModal);
                        
                        // 启用复制按钮
                        if (response.data.local_auth && !document.getElementById('copy-local-btn')) {
                            const inputContainer = document.querySelectorAll('.auth-input-container')[1];
                            const copyBtn = document.createElement('button');
                            copyBtn.type = 'button';
                            copyBtn.className = 'copy-btn';
                            copyBtn.id = 'copy-local-btn';
                            copyBtn.innerHTML = '<i class="fas fa-copy"></i> 复制';
                            copyBtn.addEventListener('click', function() {
                                const input = document.getElementById('local_auth');
                                input.select();
                                document.execCommand('copy');
                                showToast('已复制到剪贴板', 'success');
                            });
                            inputContainer.appendChild(copyBtn);
                        }
                    } else {
                        document.getElementById('error-message').textContent = response.message;
                        showModal(errorModal);
                    }
                } catch (error) {
                    console.error('保存失败:', error);
                    document.getElementById('error-message').textContent = '保存失败:' + error.message;
                    showModal(errorModal);
                } finally {
                    updateBtn.disabled = false;
                    updateBtn.innerHTML = originalContent;
                }
            }
            
            // 更新状态面板
            function updateStatusPanel(data) {
                const now = new Date();
                const timestamp = now.toLocaleString('zh-CN');
                
                // 更新最后检查时间
                document.getElementById('last-checked').textContent = timestamp;
                document.getElementById('footer-last-checked').textContent = timestamp;
                
                // 更新API状态
                const apiStatusBox = document.getElementById('api-status-box');
                const apiStatusIcon = document.getElementById('api-status-icon');
                const apiStatusDetail = document.getElementById('api-status-detail');
                
                if (data.api_status === 'success') {
                    apiStatusIcon.innerHTML = '<i class="fas fa-check-circle" style="color: var(--success-color);"></i><span style="color: var(--success-color);">正常</span>';
                    apiStatusDetail.innerHTML = '<i class="fas fa-wifi"></i> 授权接口连接正常';
                    apiStatusBox.style.borderLeftColor = 'var(--success-color)';
                    
                    // 更新远程时间戳
                    const remoteTimestamp = document.getElementById('remote-timestamp');
                    if (remoteTimestamp) {
                        remoteTimestamp.innerHTML = `<i class="fas fa-clock"></i> ${data.timestamp || timestamp.split(' ')[1]}`;
                    }
                } else {
                    apiStatusIcon.innerHTML = '<i class="fas fa-times-circle" style="color: var(--error-color);"></i><span style="color: var(--error-color);">异常</span>';
                    apiStatusDetail.innerHTML = `<i class="fas fa-exclamation-circle"></i> ${data.error_detail || '无法连接授权接口'}`;
                    apiStatusBox.style.borderLeftColor = 'var(--error-color)';
                }
                
                // 更新授权状态
                const authStatusBox = document.getElementById('auth-status-box');
                const authStatusIcon = document.getElementById('auth-status-icon');
                const authStatusDetail = document.getElementById('auth-status-detail');
                
                if (data.comparison_result === 'match') {
                    authStatusIcon.innerHTML = '<i class="fas fa-check-circle" style="color: var(--success-color);"></i><span style="color: var(--success-color);">已验证</span>';
                    authStatusDetail.innerHTML = '<i class="fas fa-thumbs-up"></i> 远程与本地授权信息一致';
                    authStatusBox.style.borderLeftColor = 'var(--success-color)';
                } else if (data.comparison_result === 'mismatch') {
                    authStatusIcon.innerHTML = '<i class="fas fa-times-circle" style="color: var(--error-color);"></i><span style="color: var(--error-color);">不一致</span>';
                    authStatusDetail.innerHTML = '<i class="fas fa-exclamation-triangle"></i> 远程与本地授权信息不一致';
                    authStatusBox.style.borderLeftColor = 'var(--error-color)';
                } else if (data.comparison_result === 'no_local') {
                    authStatusIcon.innerHTML = '<i class="fas fa-exclamation-circle" style="color: var(--error-color);"></i><span style="color: var(--error-color);">未配置</span>';
                    authStatusDetail.innerHTML = '<i class="fas fa-database"></i> 本地未找到授权信息';
                    authStatusBox.style.borderLeftColor = 'var(--error-color)';
                } else if (data.comparison_result === 'api_error') {
                    authStatusIcon.innerHTML = '<i class="fas fa-question-circle" style="color: var(--warning-color);"></i><span style="color: var(--warning-color);">待验证</span>';
                    authStatusDetail.innerHTML = '<i class="fas fa-cloud-slash"></i> 接口异常,无法验证';
                    authStatusBox.style.borderLeftColor = 'var(--warning-color)';
                }
                
                // 更新远程授权信息输入框
                if (data.remote_auth !== undefined) {
                    const remoteInput = document.getElementById('remote_auth');
                    remoteInput.value = data.remote_auth || '';
                    remoteInput.placeholder = data.remote_auth ? '' : '接口连接失败,无法获取远程授权信息';
                }
                
                // 更新本地授权信息输入框
                if (data.local_auth !== undefined) {
                    const localInput = document.getElementById('local_auth');
                    localInput.value = data.local_auth || '';
                }
            }
            
            // 页面加载动画
            const container = document.querySelector('.container');
            container.style.opacity = '0';
            container.style.transform = 'translateY(20px)';
            
            setTimeout(() => {
                container.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
                container.style.opacity = '1';
                container.style.transform = 'translateY(0)';
            }, 100);
            
            // 定期自动验证(每5分钟)
            setInterval(async () => {
                try {
                    const response = await sendAjaxRequest('verify_auth');
                    if (response.success) {
                        updateStatusPanel(response.data);
                    }
                } catch (error) {
                    console.error('自动验证失败:', error);
                }
            }, 5 * 60 * 1000); // 5分钟
        });
    </script>
</body>
</html>

当然我也会发送一份到我的TePass交流群,可以从群文件获取。

本文属原创,转载请注明原文:https://pangsuan.com/p/tepass_verify.html

感觉很棒,欢迎点赞 OR 打赏~

分享到:

评论 (0)

立即登录评论
马上咨询 在线工单