<?php
require_once __DIR__ . '/../base_api.php';

class SiteAPI extends BaseAPI {
    
    /**
     * 确保证书目录中存在 fullchain.pem 文件
     * 宝塔面板在部署证书时需要 fullchain.pem 文件
     * 
     * @param string $cert_hash 证书hash值
     * @return bool 是否成功创建或已存在
     */
    private function ensureFullchainExists($cert_hash) {
        if (empty($cert_hash)) {
            $this->debug_log('ensureFullchainExists: cert_hash为空');
            return false;
        }
        
        // 从证书列表中获取证书详情
        $cert_list = $this->bt_api->GetSSLCertList('0', 1, 100);
        $cert_info = null;
        
        if (is_array($cert_list)) {
            foreach ($cert_list as $cert) {
                if (isset($cert['hash']) && $cert['hash'] == $cert_hash) {
                    $cert_info = $cert;
                    break;
                }
            }
        }
        
        $this->debug_log('ensureFullchainExists: 从列表获取证书信息 - ' . print_r($cert_info, true));
        
        if (!$cert_info || !is_array($cert_info)) {
            $this->debug_log('ensureFullchainExists: 无法获取证书信息');
            return false;
        }
        
        // 获取证书路径
        $cert_path = isset($cert_info['path']) ? $cert_info['path'] : '';
        if (empty($cert_path)) {
            $this->debug_log('ensureFullchainExists: 证书路径为空');
            return false;
        }
        
        // 构建 fullchain.pem 路径
        $fullchain_path = rtrim($cert_path, '/') . '/fullchain.pem';
        
        // 检查 fullchain.pem 是否已存在
        $check_result = $this->bt_api->GetFileContent($fullchain_path);
        if ($check_result && isset($check_result['status']) && $check_result['status'] === true && !empty($check_result['data'])) {
            $this->debug_log('ensureFullchainExists: fullchain.pem 已存在');
            return true;
        }
        
        // fullchain.pem 不存在，需要创建
        $this->debug_log('ensureFullchainExists: fullchain.pem 不存在，开始创建');
        
        // 获取证书内容和根证书
        $cert_content = '';
        $root_content = '';
        
        // 尝试从 cert_info 中获取
        if (isset($cert_info['cert'])) {
            $cert_content = $cert_info['cert'];
        }
        if (isset($cert_info['root'])) {
            $root_content = $cert_info['root'];
        }
        
        // 如果 cert_info 中没有，尝试读取文件
        if (empty($cert_content)) {
            $cert_file_path = rtrim($cert_path, '/') . '/fullchain.pem';
            $cert_file_result = $this->bt_api->GetFileContent($cert_file_path);
            if (!$cert_file_result || !isset($cert_file_result['data'])) {
                // 尝试其他可能的证书文件名
                $possible_cert_files = array('cert.pem', 'certificate.pem', 'domain.crt');
                foreach ($possible_cert_files as $filename) {
                    $file_path = rtrim($cert_path, '/') . '/' . $filename;
                    $file_result = $this->bt_api->GetFileContent($file_path);
                    if ($file_result && isset($file_result['data']) && !empty($file_result['data'])) {
                        $cert_content = $file_result['data'];
                        $this->debug_log('ensureFullchainExists: 从 ' . $filename . ' 读取证书内容');
                        break;
                    }
                }
            } else {
                $cert_content = $cert_file_result['data'];
            }
        }
        
        if (empty($root_content)) {
            // 尝试读取根证书文件
            $possible_root_files = array('ca.pem', 'chain.pem', 'ca-bundle.crt');
            foreach ($possible_root_files as $filename) {
                $file_path = rtrim($cert_path, '/') . '/' . $filename;
                $file_result = $this->bt_api->GetFileContent($file_path);
                if ($file_result && isset($file_result['data']) && !empty($file_result['data'])) {
                    $root_content = $file_result['data'];
                    $this->debug_log('ensureFullchainExists: 从 ' . $filename . ' 读取根证书内容');
                    break;
                }
            }
        }
        
        // 如果没有根证书，只使用证书内容
        if (empty($cert_content)) {
            $this->debug_log('ensureFullchainExists: 无法获取证书内容');
            return false;
        }
        
        // 创建 fullchain.pem 内容（cert + root）
        $fullchain_content = $cert_content;
        if (!empty($root_content)) {
            $fullchain_content .= "\n" . $root_content;
        }
        
        // 先创建文件
        $this->bt_api->CreateFile($fullchain_path);
        
        // 保存 fullchain.pem
        $save_result = $this->bt_api->SaveFileBody($fullchain_path, $fullchain_content, 'utf-8');
        $this->debug_log('ensureFullchainExists: 保存 fullchain.pem 结果 - ' . print_r($save_result, true));
        
        if ($save_result && isset($save_result['status']) && $save_result['status'] === true) {
            $this->debug_log('ensureFullchainExists: fullchain.pem 创建成功');
            return true;
        }
        
        $this->debug_log('ensureFullchainExists: fullchain.pem 创建失败');
        return false;
    }
    
    public function setPassword($username, $password) {
        if (empty($username) || empty($password)) {
            $this->error('用户名和密码不能为空');
        }
        
        $this->debug_log('setPassword开始: siteName=' . $this->site_name . ', username=' . $username);
        
        $result = $this->bt_api->SetHasPwd($this->site_name, $username, $password);
        
        // 调试：记录结果
        $this->debug_log('setPassword API返回: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('设置密码访问', '设置密码访问');
            $this->debug_log('setPassword成功');
            $this->success(array('msg' => '设置成功'));
            return; // 添加return防止继续执行
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '设置失败';
        $this->debug_log('setPassword失败: ' . $error_msg);
        $this->error($error_msg);
    }
    
    public function getPasswordStatus() {
        // 使用宝塔API获取密码保护状态
        $result = $this->bt_api->GetHasPwd($this->site_name);
        
        // 调试：记录结果
        $this->debug_log('getPasswordStatus API调用: siteName=' . $this->site_name);
        $this->debug_log('getPasswordStatus结果: ' . print_r($result, true));
        
        $has_password = false;
        
        // 尝试多种方式判断密码保护状态
        if ($result && is_array($result)) {
            // 方式1: 检查 status 和 data.open
            if (isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
                if (isset($result['data']) && is_array($result['data'])) {
                    if (isset($result['data']['open'])) {
                        $has_password = ($result['data']['open'] === true || $result['data']['open'] === 'true' || $result['data']['open'] === 1 || $result['data']['open'] === '1');
                        $this->debug_log('getPasswordStatus 从data.open获取状态: ' . ($has_password ? 'true' : 'false'));
                    }
                }
            }
            
            // 方式2: 直接检查 open 字段
            if (!$has_password && isset($result['open'])) {
                $has_password = ($result['open'] === true || $result['open'] === 'true' || $result['open'] === 1 || $result['open'] === '1');
                $this->debug_log('getPasswordStatus 从open获取状态: ' . ($has_password ? 'true' : 'false'));
            }
            
            // 方式3: 检查配置文件（备用方案）
            if (!isset($result['status']) || $result['status'] === false) {
                $this->debug_log('getPasswordStatus API调用失败，尝试读取配置文件');
                $config_path = $this->getConfigPath();
                $config_result = $this->bt_api->GetFileContent($config_path);
                
                if ($config_result && isset($config_result['status']) && ($config_result['status'] === true || $config_result['status'] === 'true' || $config_result['status'] === 1)) {
                    $config_content = isset($config_result['data']) ? $config_result['data'] : '';
                    if (!empty($config_content) && strpos($config_content, 'auth_basic') !== false) {
                        $has_password = true;
                        $this->debug_log('getPasswordStatus 从配置文件获取状态: true');
                    }
                }
            }
        }
        
        $this->debug_log('getPasswordStatus 最终结果: ' . ($has_password ? 'true' : 'false'));
        $this->success(array('has_password' => $has_password));
    }
    
    public function closePassword() {
        $this->debug_log('closePassword开始: siteName=' . $this->site_name);
        
        // 使用宝塔API关闭密码访问
        $result = $this->bt_api->CloseHasPwd($this->site_name);
        
        // 调试：记录结果
        $this->debug_log('closePassword API返回: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('关闭密码访问', '关闭密码访问');
            $this->debug_log('closePassword成功');
            $this->success(array('msg' => '关闭成功'));
            return; // 添加return防止继续执行
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '关闭失败';
        $this->debug_log('closePassword失败: ' . $error_msg);
        $this->error($error_msg);
    }
    
    public function getEncryptList() {
        $webid = $this->getWebId();
        
        $result = $this->bt_api->GetDirectoryAuth($webid);
        
        $encrypt_list = array();
        
        if ($result && is_array($result)) {
            $auth_list = null;
            
            // 兼容不同版本的宝塔返回格式
            if (isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
                foreach ($result as $key => $value) {
                    if ($key != 'status' && $key != 'msg' && is_array($value)) {
                        $auth_list = $value;
                        break;
                    }
                }
            } else {
                foreach ($result as $site_name => $value) {
                    if (is_array($value)) {
                        $auth_list = $value;
                        break;
                    }
                }
            }
            
            if ($auth_list && is_array($auth_list)) {
                $i = 1;
                foreach ($auth_list as $auth) {
                    if (!is_array($auth)) continue;
                    
                    $path = isset($auth['site_dir']) ? $auth['site_dir'] : (isset($auth['path']) ? $auth['path'] : '');
                    $name = isset($auth['name']) ? $auth['name'] : '';
                    
                    if (!empty($path)) {
                        // 尝试读取用户文件，提取用户名
                        $username = '';
                        $user_file = '/www/server/pass/' . $this->site_name . '/' . $name . '.pass';
                        $user_file_result = $this->bt_api->GetFileContent($user_file);
                        if ($user_file_result && isset($user_file_result['status']) && ($user_file_result['status'] === true || $user_file_result['status'] === 'true' || $user_file_result['status'] === 1)) {
                            $user_file_content = isset($user_file_result['data']) ? $user_file_result['data'] : '';
                            if (!empty($user_file_content)) {
                                preg_match('/^(\w+):/', $user_file_content, $user_matches);
                                if (isset($user_matches[1])) {
                                    $username = $user_matches[1];
                                }
                            }
                        }

                        $encrypt_list[] = array(
                            'id' => $i++,
                            'path' => $path,
                            'name' => $name,
                            'username' => $username
                        );
                    }
                }
            }
        }
        
        $this->success(array('list' => $encrypt_list));
    }
    
    public function getForbidList() {
        $this->debug_log('getForbidList: 开始获取禁止访问列表');
        
        // 获取Nginx配置文件路径
        $config_path = $this->getConfigPath();
        $this->debug_log('getForbidList: config_path=' . $config_path);
        
        // 读取配置文件
        $config_result = $this->bt_api->GetFileContent($config_path);
        
        if (!$config_result || !isset($config_result['status']) || 
            ($config_result['status'] !== true && $config_result['status'] !== 'true' && $config_result['status'] !== 1)) {
            $this->debug_log('getForbidList: 获取配置文件失败');
            $this->success(array('list' => array()));
            return;
        }
        
        $config_content = isset($config_result['data']) ? $config_result['data'] : '';
        $this->debug_log('getForbidList: 配置文件长度=' . strlen($config_content));
        
        if (empty($config_content)) {
            $this->debug_log('getForbidList: 配置文件为空');
            $this->success(array('list' => array()));
            return;
        }
        
        // 解析配置文件中的禁止访问规则
        $rules = $this->parseForbidRules($config_content);
        
        $this->debug_log('getForbidList: 找到 ' . count($rules) . ' 条规则');
        $this->success(array('list' => $rules));
    }
    
    /**
     * 解析Nginx配置中的禁止访问规则
     */
    private function parseForbidRules($config_content) {
        $rules = array();
        $id = 1;
        
        // 首先尝试匹配带有BEGIN_DENY标记的规则块（宝塔格式）
        if (preg_match_all('/#BEGIN_DENY_(\w+)([\s\S]*?)#END_DENY_\1/i', $config_content, $blocks, PREG_SET_ORDER)) {
            foreach ($blocks as $block) {
                $rule_id = $block[1];  // 例如: 1swq
                $block_content = $block[2];
                
                // 从块中提取名称（#NAME: xxx）
                $rule_name = $rule_id;  // 默认使用规则ID
                if (preg_match('/#NAME:\s*(.+)/i', $block_content, $name_match)) {
                    $rule_name = trim($name_match[1]);
                }
                
                // 从块中提取location指令
                if (preg_match('/location\s+([~\*\^]*)\s*([^\s{]+)\s*\{/i', $block_content, $location_match)) {
                    $modifier = trim($location_match[1]);
                    $path_pattern = trim($location_match[2]);
                    
                    // 跳过默认安全规则
                    if ($this->isDefaultSecurityRule($path_pattern)) {
                        continue;
                    }
                    
                    // 提取显示信息
                    $display_info = $this->extractDisplayInfo($path_pattern);
                    
                    $rule = array(
                        'id' => $id++,
                        'rule_id' => $rule_id,  // 宝塔的规则ID
                        'path' => $path_pattern,  // 原始模式，用于删除时匹配
                        'name' => $rule_name,  // 使用用户输入的名称
                        'ext' => $display_info['ext'],
                        'display_path' => $display_info['display_path'],
                        'display_rule' => $display_info['display_rule']  // 规则显示（如php|jsp）
                    );
                    
                    $rules[] = $rule;
                }
            }
        }
        
        return $rules;
    }
    
    /**
     * 检查是否是默认安全规则
     */
    private function isDefaultSecurityRule($pattern) {
        $default_patterns = array(
            '\\.user\\.ini', '\\.htaccess', '\\.env', '\\.git', '\\.svn',
            '\\.vscode', '\\.claude', '\\.idea', 'node_modules', '\\.well-known',
            '\\.htpasswd', '\\.project', '\\.bashrc', '\\.bash_profile',
            '\\.DS_Store', '\\.gitignore', 'LICENSE', 'README', 'CHANGELOG'
        );
        
        foreach ($default_patterns as $default) {
            if (strpos($pattern, $default) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 从正则表达式模式中提取用户友好的显示信息
     */
    private function extractDisplayInfo($pattern) {
        $info = array(
            'name' => '',
            'ext' => '',
            'display_path' => '',
            'display_rule' => ''
        );
        
        // 例如: ^/aoi.*\.(php|jsp)$
        // 提取路径部分: /aoi
        // 提取扩展名部分: php|jsp
        
        // 先提取扩展名
        if (preg_match('/\\\\?\\.\\(([^\)]+)\\)/', $pattern, $ext_matches)) {
            $info['ext'] = $ext_matches[1];
            $info['display_rule'] = $ext_matches[1];  // php|jsp
        } elseif (preg_match('/\\\\?\\.([a-z0-9]+)\$?/i', $pattern, $ext_matches)) {
            $info['ext'] = $ext_matches[1];
            $info['display_rule'] = $ext_matches[1];
        }
        
        // 提取路径部分
        $clean_path = $pattern;
        
        // 移除开头的 ^
        $clean_path = preg_replace('/^\^/', '', $clean_path);
        
        // 移除扩展名部分及其后面的内容
        $clean_path = preg_replace('/\.\*?\\\\?\\..*$/', '', $clean_path);
        
        // 移除结尾的 $
        $clean_path = preg_replace('/\$$/', '', $clean_path);
        
        // 移除转义符
        $clean_path = str_replace('\\', '', $clean_path);
        
        $info['display_path'] = $clean_path;
        
        return $info;
    }
    
    /**
     * 根据模式生成规则名称
     */
    private function generateRuleNameFromPattern($modifier, $pattern) {
        if ($this->isExtensionPattern($pattern)) {
            return "禁止访问 " . $this->formatExtension($pattern) . " 文件";
        } else {
            // 清理正则表达式符号，使其更易读
            $clean_pattern = str_replace(array('\\', '^', '$', '.*'), '', $pattern);
            return "禁止访问 " . $clean_pattern;
        }
    }

        /**
         * 根据文件名生成规则名称
         */
        private function generateRuleNameFromFilename($filename) {
            if (strpos($filename, '*.') === 0) {
                // 扩展名：*.log -> 禁止访问 *.log 文件
                return "禁止访问 " . $filename . " 文件";
            } elseif (substr($filename, -1) === '/') {
                // 目录：data/ -> 禁止访问 data/ 目录
                return "禁止访问 " . $filename . " 目录";
            } else {
                // 文件：.htaccess -> 禁止访问 .htaccess 文件
                return "禁止访问 " . $filename;
            }
        }

    /**
     * Parse location blocks from forbidden section
     * Task 2.1-2.5: Enhanced parsing logic
     */
    private function parseLocationBlocks($content) {
        $rules = array();
        $id = 1;
        
        // Split into lines for processing
        $lines = explode("\n", $content);
        $currentName = null;
        
        foreach ($lines as $lineNum => $line) {
            $line = trim($line);
            
            // Check for FORBID_ITEM marker (Task 2.4)
            // Format: #FORBID_ITEM /txt/ or #FORBID_ITEM: Custom Name
            if (preg_match('/#FORBID_ITEM[:\s]+(.+)/', $line, $matches)) {
                $itemValue = trim($matches[1]);
                
                // Check if it's a path/pattern or a custom name
                // If it starts with / or *. or contains regex chars, it's a path, not a name
                if (preg_match('/^[\/\*\.]|[\\\\\(\)\[\]\{\}\^\$\|]/', $itemValue)) {
                    // It's a path/pattern, not a custom name
                    $currentName = null;
                } else {
                    // It's a custom name
                    $currentName = $itemValue;
                }
                continue;
            }
            
            // Check for location directive (Task 2.1)
            // Pattern: location [modifier] pattern { ... }
            // Modifiers: ^~, =, ~*, ~, or none
            if (preg_match('/location\s+([\^~\*=]*)\s*([^\s{]+)/', $line, $matches)) {
                $modifier = trim($matches[1]);
                $pattern = trim($matches[2]);
                
                
                $rule = array(
                    'id' => $id++,
                    'path' => '',
                    'name' => $currentName ? $currentName : $this->generateRuleName($modifier, $pattern),
                    'ext' => ''
                );
                
                // Determine if it's a path or extension pattern (Task 2.2, 2.3)
                if ($this->isExtensionPattern($pattern)) {
                    $rule['ext'] = $this->formatExtension($pattern);
                } else {
                    $rule['path'] = $pattern;
                }
                
                $rules[] = $rule;
                $currentName = null; // Reset for next block
            }
        }
        
        return $rules;
    }

    /**
     * Check if pattern is a file extension pattern
     * Task 2.2: Extension pattern detection
     */
    private function isExtensionPattern($pattern) {
        // Check if pattern looks like a file extension pattern
        // Examples: \.(js)$, \.(txt|log)$, \.php$, \\.(js)$
        return preg_match('/\\\\?\\.\\([^\)]+\\)\$?/', $pattern) ||
               preg_match('/\\\\?\\.[a-z0-9]+\$?/i', $pattern);
    }

    /**
     * Format extension pattern to user-friendly format
     * Task 2.2: Extension formatting
     */
    private function formatExtension($pattern) {
        // Convert regex pattern to user-friendly format
        // \.(js)$ -> *.js
        // \.(txt|log)$ -> *.txt, *.log
        // \\.(js)$ -> *.js (handle escaped backslash)

        // Handle patterns with parentheses (multiple extensions)
        if (preg_match('/\\\\?\\.\\(([^\)]+)\\)/', $pattern, $matches)) {
            $exts = explode('|', $matches[1]);
            return '*.' . implode(', *.', $exts);
        }

        // Handle single extension patterns
        if (preg_match('/\\\\?\\.([a-z0-9]+)/i', $pattern, $matches)) {
            return '*.' . $matches[1];
        }

        // Fallback: return original pattern
        return $pattern;
    }

    /**
     * Generate descriptive name when no FORBID_ITEM marker exists
     * Task 2.5: Fallback name generation
     */
    private function generateRuleName($modifier, $pattern) {
        if ($this->isExtensionPattern($pattern)) {
            return "禁止访问 " . $this->formatExtension($pattern) . " 文件";
        } else {
            return "禁止访问 " . $pattern;
        }
    }
    
    /**
     * Validate parsed rules
     * Task 3.1: Data structure validation
     */
    private function validateRules($rules) {
        $validatedRules = array();
        
        foreach ($rules as $rule) {
            // Check that all required fields exist
            if (!isset($rule['id']) || !isset($rule['path']) || !isset($rule['name']) || !isset($rule['ext'])) {
                continue;
            }
            
            // Validate id is positive integer
            if (!is_numeric($rule['id']) || $rule['id'] <= 0) {
                continue;
            }
            
            // Validate name is non-empty
            if (empty($rule['name'])) {
                continue;
            }
            
            // Validate that at least one of path or ext is non-empty
            if (empty($rule['path']) && empty($rule['ext'])) {
                continue;
            }
            
            $validatedRules[] = $rule;
        }
        
        return $validatedRules;
    }

    
    public function addEncrypt($path, $name, $username, $password) {
        if (!$path || !$name || !$username || !$password) {
            $this->error('参数错误');
        }
        
        $webid = $this->getWebId();
        
        // 先检查是否设置了站点级别的密码保护，如果是则先取消
        $pass_status_result = $this->bt_api->CloseHasPwd($this->site_name);
        
        // 获取现有的加密访问列表，检查是否已存在
        $existing_auths = $this->bt_api->GetDirectoryAuth($webid);
        $this->debug_log('addEncrypt: 现有加密列表 - ' . print_r($existing_auths, true));
        
        // 宝塔返回格式: { site_name: [...] }
        $auth_list = array();
        if (is_array($existing_auths)) {
            if (isset($existing_auths[$this->site_name]) && is_array($existing_auths[$this->site_name])) {
                $auth_list = $existing_auths[$this->site_name];
            } elseif (isset($existing_auths['data']) && is_array($existing_auths['data'])) {
                $auth_list = $existing_auths['data'];
            }
        }
        
        // 检查是否存在相同目录或名称的加密
        $existing_name = null;
        foreach ($auth_list as $auth) {
            // 检查目录是否相同
            if (isset($auth['site_dir']) && $auth['site_dir'] === $path) {
                $existing_name = isset($auth['name']) ? $auth['name'] : null;
                $this->debug_log('addEncrypt: 发现相同目录的加密, name=' . $existing_name);
                break;
            }
            // 检查名称是否相同
            if (isset($auth['name']) && $auth['name'] === $name) {
                $existing_name = $name;
                $this->debug_log('addEncrypt: 发现相同名称的加密');
                break;
            }
        }
        
        // 如果存在相同目录或名称的加密，先删除
        if ($existing_name) {
            $this->debug_log('addEncrypt: 先删除已存在的加密, name=' . $existing_name);
            $delete_result = $this->bt_api->DeleteDirectoryAuth($webid, $existing_name);
            $this->debug_log('addEncrypt: 删除结果 - ' . print_r($delete_result, true));
        }
        
        // 调用宝塔API添加加密访问
        // 第五个参数是保护名称
        $result = $this->bt_api->SetDirectoryAuth($webid, $path, $username, $password, $name);
        
        // 调试：记录结果
        $this->debug_log('addEncrypt结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->success(array('msg' => '添加成功'));
        } else {
            $error_msg = '添加失败';
            if (isset($result['msg'])) {
                $error_msg = $result['msg'];
            }
            $this->error($error_msg);
        }
    }
    
    public function addForbid($name, $ext, $path) {
        if (!$name || (!$ext && !$path)) {
            $this->error('参数错误');
        }

        $this->debug_log('addForbid: 开始添加禁止访问规则, name=' . $name . ', ext=' . $ext . ', path=' . $path);

        // 构建要禁止的路径模式
        $location_pattern = '';
        $location_modifier = '~*'; // 默认使用不区分大小写的正则匹配
        
        if (!empty($path)) {
            // 路径模式
            $location_pattern = '^' . preg_quote($path, '/');
        } elseif (!empty($ext)) {
            // 扩展名模式：*.js -> \.(js)$
            $ext = trim($ext, '*. ');
            $location_pattern = '\\.(' . preg_quote($ext, '/') . ')$';
        }

        if (empty($location_pattern)) {
            $this->error('参数错误：路径和扩展名不能同时为空');
        }

        $this->debug_log('addForbid: location_pattern=' . $location_pattern);

        // 获取Nginx配置文件路径
        $config_path = $this->getConfigPath();
        $this->debug_log('addForbid: config_path=' . $config_path);

        // 读取配置文件
        $config_result = $this->bt_api->GetFileContent($config_path);
        
        if (!$config_result || !isset($config_result['status']) || 
            ($config_result['status'] !== true && $config_result['status'] !== 'true' && $config_result['status'] !== 1)) {
            $this->debug_log('addForbid: 获取配置文件失败');
            $this->error('获取配置文件失败');
        }
        
        $config_content = isset($config_result['data']) ? $config_content = is_array($config_result['data']) ? implode("\n", $config_result['data']) : $config_result['data'] : '';
        $this->debug_log('addForbid: 配置文件长度=' . strlen($config_content));
        
        if (empty($config_content)) {
            $this->debug_log('addForbid: 配置文件为空');
            $this->error('配置文件为空');
        }

        // 检查是否已存在相同的规则，如果存在则先删除
        $existing_rules = $this->parseForbidRules($config_content);
        foreach ($existing_rules as $rule) {
            if ($rule['path'] === $location_pattern) {
                $this->debug_log('addForbid: 规则已存在，先删除旧规则, path=' . $location_pattern);
                // 删除旧规则
                $rule_id = isset($rule['rule_id']) ? $rule['rule_id'] : '';
                if (!empty($rule_id)) {
                    $pattern = '/#BEGIN_DENY_' . preg_quote($rule_id, '/') . '[\s\S]*?#END_DENY_' . preg_quote($rule_id, '/') . '\s*\n?/i';
                    $config_content = preg_replace($pattern, '', $config_content);
                }
                break;
            }
        }

        // 生成唯一ID
        $deny_id = substr(md5($location_pattern . time()), 0, 8);

        // 构建新的location块，在注释中保存用户输入的名称
        $new_location_block = "\n    #BEGIN_DENY_{$deny_id}\n";
        $new_location_block .= "    #NAME: {$name}\n";
        $new_location_block .= "    location {$location_modifier} {$location_pattern} {\n";
        $new_location_block .= "        deny all;\n";
        $new_location_block .= "    }\n";
        $new_location_block .= "    #END_DENY_{$deny_id}\n";

        // 查找插入位置：在 #PHP-INFO-START 之前插入
        $insert_marker = '#PHP-INFO-START';
        $insert_pos = strpos($config_content, $insert_marker);
        
        if ($insert_pos === false) {
            // 如果找不到标记，尝试在第一个 location 块之前插入
            $insert_pos = strpos($config_content, 'location');
            if ($insert_pos === false) {
                $this->debug_log('addForbid: 无法找到合适的插入位置');
                $this->error('无法找到合适的插入位置');
            }
        }

        // 插入新的location块
        $new_config_content = substr($config_content, 0, $insert_pos) . $new_location_block . substr($config_content, $insert_pos);

        // 保存修改后的配置文件
        $save_result = $this->bt_api->SaveFileBody($config_path, $new_config_content, 'utf-8');
        $this->debug_log('addForbid: 保存配置文件结果 - ' . print_r($save_result, true));

        if (!$save_result || !isset($save_result['status']) || 
            ($save_result['status'] !== true && $save_result['status'] !== 'true' && $save_result['status'] !== 1)) {
            $this->debug_log('addForbid: 保存配置文件失败');
            $this->error('保存配置文件失败');
        }

        // 重载Nginx配置
        $this->reloadNginx();

        $this->log('添加禁止访问', '添加禁止访问: ' . $name);
        $this->debug_log('addForbid: 添加成功');
        $this->success(array('msg' => '添加成功'));
    }
    
    public function deleteEncrypt($path) {
        if (!$path) {
            $this->error('参数错误');
        }
        
        $webid = $this->getWebId();
        
        // 1. 先获取当前所有的目录认证列表，找到对应的 name
        $auth_list = $this->bt_api->GetDirectoryAuth($webid);
        $auth_name = '';
        
        if ($auth_list && is_array($auth_list)) {
            // 兼容不同版本的宝塔返回格式
            $target_list = null;
            if (isset($auth_list['status'])) {
                 foreach ($auth_list as $key => $value) {
                     if ($key != 'status' && $key != 'msg' && is_array($value)) {
                         $target_list = $value;
                         break;
                     }
                 }
            } else {
                 foreach ($auth_list as $site_name => $value) {
                     if (is_array($value)) {
                         $target_list = $value;
                         break;
                     }
                 }
            }

            if ($target_list) {
                foreach ($target_list as $auth) {
                    $auth_path = isset($auth['site_dir']) ? $auth['site_dir'] : (isset($auth['path']) ? $auth['path'] : '');
                    if ($auth_path == $path) {
                        $auth_name = isset($auth['name']) ? $auth['name'] : '';
                        break;
                    }
                }
            }
        }
        
        if (empty($auth_name)) {
            // 如果没找到对应的 name，尝试用 class.php 生成的默认规则寻找
            $auth_name = 'auth_' . substr(md5($path), 0, 8);
        }
        
        // 2. 调用宝塔API删除指定名称的加密访问
        $result = $this->bt_api->DeleteDirectoryAuth($webid, $auth_name);
        
        // 调试：记录结果
        $this->debug_log('deleteEncrypt结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->success(array('msg' => '删除成功'));
        } else {
            $error_msg = '删除失败';
            if (isset($result['msg'])) {
                $error_msg = $result['msg'];
            }
            $this->error($error_msg);
        }
    }
    
    public function deleteForbid($path) {
        if (!$path) {
            $this->error('参数错误');
        }

        $this->debug_log('deleteForbid: 开始删除禁止访问规则, path=' . $path);

        // 获取Nginx配置文件路径
        $config_path = $this->getConfigPath();
        $this->debug_log('deleteForbid: config_path=' . $config_path);

        // 读取配置文件
        $config_result = $this->bt_api->GetFileContent($config_path);
        
        if (!$config_result || !isset($config_result['status']) || 
            ($config_result['status'] !== true && $config_result['status'] !== 'true' && $config_result['status'] !== 1)) {
            $this->debug_log('deleteForbid: 获取配置文件失败');
            $this->error('获取配置文件失败');
        }
        
        $config_content = isset($config_result['data']) ? $config_result['data'] : '';
        $this->debug_log('deleteForbid: 配置文件长度=' . strlen($config_content));
        
        if (empty($config_content)) {
            $this->debug_log('deleteForbid: 配置文件为空');
            $this->error('配置文件为空');
        }

        // 尝试作为rule_id删除（宝塔格式）
        // 例如: #BEGIN_DENY_1swq ... #END_DENY_1swq
        $pattern = '/#BEGIN_DENY_' . preg_quote($path, '/') . '[\s\S]*?#END_DENY_' . preg_quote($path, '/') . '\s*\n?/i';
        
        if (preg_match($pattern, $config_content)) {
            $new_config_content = preg_replace($pattern, '', $config_content);
            $this->debug_log('deleteForbid: 通过rule_id找到并删除了规则');
        } else {
            // 如果不是rule_id，尝试作为路径模式删除
            $this->debug_log('deleteForbid: 未找到rule_id，尝试作为路径模式删除');
            
            $patterns = array(
                // 匹配带有BEGIN_DENY和END_DENY标记的块
                '/#BEGIN_DENY_\w+\s*\n\s*location\s+[~\*\^]*\s*' . preg_quote($path, '/') . '\s*\{[\s\S]*?\}\s*\n\s*#END_DENY_\w+\s*\n?/i',
                // 匹配普通的location块
                '/\s*location\s+[~\*\^]*\s*' . preg_quote($path, '/') . '\s*\{[\s\S]*?\}\s*\n?/i'
            );

            $found = false;
            $new_config_content = $config_content;

            foreach ($patterns as $p) {
                if (preg_match($p, $new_config_content)) {
                    $new_config_content = preg_replace($p, '', $new_config_content);
                    $found = true;
                    $this->debug_log('deleteForbid: 通过路径模式找到并删除了规则');
                    break;
                }
            }

            if (!$found) {
                $this->debug_log('deleteForbid: 未找到匹配的规则');
                $this->error('未找到要删除的规则');
            }
        }

        // 保存修改后的配置文件
        $save_result = $this->bt_api->SaveFileBody($config_path, $new_config_content, 'utf-8');
        $this->debug_log('deleteForbid: 保存配置文件结果 - ' . print_r($save_result, true));

        if (!$save_result || !isset($save_result['status']) || 
            ($save_result['status'] !== true && $save_result['status'] !== 'true' && $save_result['status'] !== 1)) {
            $this->debug_log('deleteForbid: 保存配置文件失败');
            $this->error('保存配置文件失败');
        }

        // 重载Nginx配置
        $this->reloadNginx();

        $this->log('删除禁止访问', '删除禁止访问: ' . $path);
        $this->debug_log('deleteForbid: 删除成功');
        $this->success(array('msg' => '删除成功'));
    }
    
    public function cleanupDuplicateForbid() {
        $this->debug_log('cleanupDuplicateForbid: 开始清理重复规则');
        
        $config_path = $this->getConfigPath();
        $config_result = $this->bt_api->GetFileContent($config_path);
        
        if (!$config_result || !isset($config_result['status']) || 
            ($config_result['status'] !== true && $config_result['status'] !== 'true' && $config_result['status'] !== 1)) {
            $this->debug_log('cleanupDuplicateForbid: 获取配置文件失败');
            $this->error('获取配置文件失败');
        }
        
        $config_content = is_array($config_result['data']) ? implode("\n", $config_result['data']) : $config_result['data'];
        $this->debug_log('cleanupDuplicateForbid: 配置文件长度=' . strlen($config_content));
        
        if (empty($config_content)) {
            $this->debug_log('cleanupDuplicateForbid: 配置文件为空');
            $this->error('配置文件为空');
        }
        
        // 删除所有 DENY 规则块
        $pattern = '/\s*#BEGIN_DENY_\w+[\s\S]*?#END_DENY_\w+\n*/';
        $new_content = preg_replace($pattern, '', $config_content);
        
        $removed_count = preg_match_all($pattern, $config_content);
        $this->debug_log('cleanupDuplicateForbid: 删除了 ' . $removed_count . ' 条规则');
        
        // 保存
        $save_result = $this->bt_api->SaveFileBody($config_path, $new_content, 'utf-8');
        $this->debug_log('cleanupDuplicateForbid: 保存结果 - ' . print_r($save_result, true));
        
        if (!$save_result || !isset($save_result['status']) || 
            ($save_result['status'] !== true && $save_result['status'] !== 'true' && $save_result['status'] !== 1)) {
            $this->debug_log('cleanupDuplicateForbid: 保存配置文件失败');
            $this->error('保存配置文件失败');
        }
        
        // 重载Nginx
        $this->reloadNginx();
        
        $this->success(array('msg' => '清理完成，已删除 ' . $removed_count . ' 条规则'));
    }

    /**
     * 获取当前已设置的禁止访问路径列表
     */
    private function getCurrentForbidPaths() {
        $config_path = $this->getConfigPath();
        $config_result = $this->bt_api->GetFileContent($config_path);
        
        $paths = array();
        if ($config_result && isset($config_result['status']) && ($config_result['status'] === true || $config_result['status'] === 'true' || $config_result['status'] === 1)) {
            $config_content = isset($config_result['data']) ? $config_result['data'] : '';
            if (!empty($config_content)) {
                // 优先读取带标记的禁止访问项
                if (preg_match('/#FORBIDDEN_START([\s\S]*?)#FORBIDDEN_END/', $config_content, $block)) {
                    preg_match_all('/#FORBID_ITEM\s+([^\r\n]+)/', $block[1], $item_matches);
                    if (isset($item_matches[1]) && count($item_matches[1]) > 0) {
                        foreach ($item_matches[1] as $p) {
                            $paths[] = trim($p);
                        }
                    } else {
                        // 兼容旧版本：解析 location 块
                        preg_match_all('/location\s+(?:(?:\^~|=|~|~\*)\s+)?([^\s]+)\s*\{[^}{]*return\s+403\s*;[^}{]*\}/s', $config_content, $matches);
                        if (isset($matches[1])) {
                            foreach ($matches[1] as $path) {
                                $paths[] = trim($path);
                            }
                        }
                    }
                } else {
                    // 兼容旧版本：全文件解析
                    preg_match_all('/location\s+(?:(?:\^~|=|~|~\*)\s+)?([^\s]+)\s*\{[^}{]*return\s+403\s*;[^}{]*\}/s', $config_content, $matches);
                    if (isset($matches[1])) {
                        foreach ($matches[1] as $path) {
                            $paths[] = trim($path);
                        }
                    }
                }
            }
        }
        return $paths;
    }
    
    /**
     * 保存禁止访问列表到配置文件
     */
    private function saveForbidList($paths) {
        $config_path = $this->getConfigPath();
        $this->debug_log('saveForbidList: config_path=' . $config_path);
        $this->debug_log('saveForbidList: paths=' . print_r($paths, true));
        
        $config_result = $this->bt_api->GetFileContent($config_path);
        
        if (!$config_result || !isset($config_result['status']) || ($config_result['status'] !== true && $config_result['status'] !== 'true' && $config_result['status'] !== 1)) {
            $this->debug_log('saveForbidList: 获取配置文件失败');
            return array('status' => false, 'msg' => '获取配置文件失败');
        }
        
        $config_content = isset($config_result['data']) ? $config_result['data'] : '';
        $this->debug_log('saveForbidList: 原配置文件长度=' . strlen($config_content));
        
        // 移除旧的禁止访问配置
        $config_content = preg_replace('/\s*#FORBIDDEN_START[\s\S]*?#FORBIDDEN_END\s*/', '', $config_content);
        $this->debug_log('saveForbidList: 移除旧配置后长度=' . strlen($config_content));
        
        // 移除所有其他的简单禁止访问规则（兼容旧版本或手动添加的规则）
        $config_content = preg_replace('/location\s+(?:(?:\^~|=|~|~\*)\s+)?(?:[^\s]+)\s*\{[^}{]*return\s+403\s*;[^}{]*\}\s*/s', '', $config_content);
        
        // 添加新的禁止访问配置
        if (!empty($paths)) {
            $forbidden_config = "\n\t#FORBIDDEN_START\n";
            $path_list = is_array($paths) ? $paths : explode(',', $paths);
            $this->debug_log('saveForbidList: 处理' . count($path_list) . '个路径');
            
            foreach ($path_list as $path) {
                $path = trim($path);
                if (!empty($path)) {
                    $forbidden_config .= "\t#FORBID_ITEM " . $path . "\n";
                    if (strpos($path, '*.') === 0) {
                        // 后缀
                        $ext = substr($path, 2);
                        $forbidden_config .= "\tlocation ~* \\.(" . $ext . ")$ {\n";
                        $this->debug_log('saveForbidList: 添加后缀规则: ' . $ext);
                    } elseif (substr($path, -1) == '/') {
                        // 目录
                        $forbidden_config .= "\tlocation ^~ " . $path . " {\n";
                        $this->debug_log('saveForbidList: 添加目录规则: ' . $path);
                    } else {
                        // 文件
                        $forbidden_config .= "\tlocation = " . $path . " {\n";
                        $this->debug_log('saveForbidList: 添加文件规则: ' . $path);
                    }
                    $forbidden_config .= "\t\treturn 403;\n";
                    $forbidden_config .= "\t}\n";
                }
            }
            $forbidden_config .= "\t#FORBIDDEN_END\n";
            
            $this->debug_log('saveForbidList: 生成的配置块长度=' . strlen($forbidden_config));
            $this->debug_log('saveForbidList: 配置块内容=' . $forbidden_config);
            
            // 在server块结束前添加
            // 查找最后一个 } 的位置
            $last_brace_pos = strrpos($config_content, '}');
            if ($last_brace_pos !== false) {
                $config_content = substr($config_content, 0, $last_brace_pos) . $forbidden_config . '}';
                $this->debug_log('saveForbidList: 成功插入配置块');
            } else {
                $this->debug_log('saveForbidList: 未找到server块结束位置');
                return array('status' => false, 'msg' => '配置文件格式错误');
            }
            
            $this->debug_log('saveForbidList: 添加配置后长度=' . strlen($config_content));
        }
        
        // 保存
        $this->debug_log('saveForbidList: 开始保存文件');
        $result = $this->bt_api->setwj($config_content, $config_path);
        $this->debug_log('saveForbidList: 保存结果=' . print_r($result, true));
        
        return $result;
    }

    public function getIndex() {
        $webid = $this->getWebId();
        
        // 调试：输出请求参数
        $this->debug_log('getIndex请求参数: webid=' . $webid);
        $this->debug_log('宝塔API地址: ' . $this->bt_api->BT_PANEL);
        
        // 首先尝试从配置文件中读取默认文档设置
        $config_path = $this->getConfigPath();
        $config_result = $this->bt_api->GetFileContent($config_path);
        
        // 调试：输出配置文件读取结果
        $this->debug_log('getIndex配置文件读取结果: ' . print_r($config_result, true));
        
        if ($config_result && isset($config_result['status']) && ($config_result['status'] === true || $config_result['status'] === 'true' || $config_result['status'] === 1)) {
            $config_content = isset($config_result['data']) ? $config_result['data'] : '';
            if (!empty($config_content)) {
                // 从配置文件中提取默认文档设置
                if (preg_match('/index\s+(.+?);/', $config_content, $matches)) {
                    $index_files = trim($matches[1]);
                    // 将空格分隔的默认文档转换为换行分隔
                    $index_files = str_replace(' ', "\n", $index_files);
                    
                    $this->debug_log('从配置文件中提取默认文档成功: ' . $index_files);
                    $this->success(array('index' => $index_files));
                }
            }
        }
        
        // 如果从配置文件中读取失败，返回默认值
        $default_index = "index.html\nindex.php\nindex.htm\ndefault.html\ndefault.php";
        $this->debug_log('从配置文件中读取失败，返回默认值: ' . $default_index);
        $this->success(array('index' => $default_index));
    }
    
    public function setIndex($index) {
        $webid = $this->getWebId();
        
        // 调试：输出请求参数
        $this->debug_log('setIndex请求参数: webid=' . $webid . ', index=' . $index);
        $this->debug_log('宝塔API地址: ' . $this->bt_api->BT_PANEL);
        
        $result = $this->bt_api->SetIndex($webid, $index);
        
        // 调试：输出响应结果
        $this->debug_log('setIndex响应结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('修改默认文档', '修改默认文档');
            $this->success(array('msg' => '修改成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '修改失败';
        $this->error($error_msg);
    }
    
    public function setRunDir($rundir) {
        $webid = $this->getWebId();
        
        $result = $this->bt_api->SetSiteRunPath($webid, $rundir);
        
        // 调试：记录结果
        $this->debug_log('setRunDir结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('设置运行目录', '设置运行目录: ' . $rundir);
            $this->success(array('msg' => '设置成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '设置失败';
        $this->error($error_msg);
    }
    
    // 获取当前运行目录设置
    public function getRunDir() {
        $webid = $this->getWebId();
        
        // 调试：输出请求参数
        $this->debug_log('getRunDir请求参数: webid=' . $webid);
        $this->debug_log('宝塔API地址: ' . $this->bt_api->BT_PANEL);
        
        // 获取用户的网站根目录路径
        $username = isset($this->user->user) ? $this->user->user : (isset($this->user['user']) ? $this->user['user'] : '');
        
        // 获取系统配置
        global $conf;
        $linux_path = isset($conf['hxi']) && !empty($conf['hxi']) ? $conf['hxi'] : '/www/wwwroot';
        $windows_path = isset($conf['hxo']) && !empty($conf['hxo']) ? $conf['hxo'] : 'D:/wwwroot';
        
        // 获取用户的宝塔服务器操作系统类型
        $bt_info = $this->bt_info;
        $os_type = isset($bt_info['czzt']) ? $bt_info['czzt'] : 'Linux';
        
        // 构建网站根目录路径
        $site_path = ($os_type == 'Linux' ? $linux_path : $windows_path) . '/' . $username;
        
        $this->debug_log('网站根目录路径: ' . $site_path);
        
        // 使用宝塔API获取运行目录
        $result = $this->bt_api->GetDirUserINI($webid, $site_path);
        $this->debug_log('GetDirUserINI结果: ' . print_r($result, true));
        
        if ($result && isset($result['runPath']['runPath'])) {
            $run_dir = $result['runPath']['runPath'];
            $this->debug_log('从宝塔API获取运行目录成功: ' . $run_dir);
            $this->success(array('rundir' => $run_dir));
            return;
        }
        
        // 如果API返回错误或没有运行目录，返回默认值
        $this->debug_log('GetDirUserINI返回异常，返回默认值');
        $this->success(array('rundir' => '/'));
    }
    
    public function setRewrite($rewrite, $content) {
        // 写入调试日志
        $this->debug_log('setRewrite方法被调用，参数: rewrite=' . $rewrite . ', content=' . $content);
        
        $webid = $this->getWebId();
        
        // 调用统一的 bt_api->SetRewrite 方法
        $result = $this->bt_api->SetRewrite($webid, 'nginx', $content);
        
        $this->debug_log('setRewrite结果: ' . print_r($result, true));
        
        if ($result && (isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1) || isset($result[0]['status']) && ($result[0]['status'] === true || $result[0]['status'] === 'true' || $result[0]['status'] === 1))) {
            $this->log('设置伪静态', '设置伪静态');
            $this->success(array('msg' => '设置成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '设置失败';
        $this->error($error_msg);
    }
    
    public function setSSL($type = null, $key = null, $cert = null) {
        $type = $type !== null ? $type : (isset($_POST['type']) ? $_POST['type'] : 0);
        $key = $key !== null ? $key : (isset($_POST['key']) ? $_POST['key'] : '');
        $cert = $cert !== null ? $cert : (isset($_POST['cert']) ? $_POST['cert'] : '');
        
        $webid = $this->getWebId();
        
        if ($type == -1) {
            // 如果type为-1，表示关闭SSL，使用CloseSSLConf接口
            $result = $this->bt_api->CloseSSLConf($this->site_name);
            $action_name = '关闭SSL';
        } else {
            // 否则使用SetSSL接口设置证书
            $result = $this->bt_api->SetSSL($webid, $type, $key, $cert);
            $action_name = 'SSL配置';
        }
        
        // 调试：输出SSL设置结果
        $this->debug_log('setSSL结果 (' . $action_name . '): ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log($action_name, $action_name);
            $this->success(array('msg' => '配置成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : ($type == -1 ? '关闭失败' : '配置失败');
        $this->error($error_msg);
    }

    private function isSystemGiftDomain($domain) {
        $domain = strtolower(trim((string)$domain));
        if ($domain === '') return false;
        $gift = strtolower(trim((string)$this->site_name));
        
        if ($gift !== '' && $domain === $gift) return true;
        if ($gift !== '' && $domain === 'www.' . $gift) return true;
        
        // 额外检查：如果域名包含用户名和主域名，也认为是系统赠送的
        // 从数据库配置获取主域名，避免硬编码
        global $conf;
        $main_domain = isset($conf['main_domain']) && !empty($conf['main_domain']) ? $conf['main_domain'] : 'a116.vip';
        $username = isset($this->user['user']) ? strtolower(trim($this->user['user'])) : '';
        if ($username !== '' && $domain === $username . '.' . $main_domain) return true;
        if ($username !== '' && $domain === 'www.' . $username . '.' . $main_domain) return true;
        
        return false;
    }

    public function getSiteDomains() {
        $webid = $this->getWebId();
        if (empty($webid)) return array();
        
        $result = $this->bt_api->GetSiteDomains($webid);
        
        // 调试：记录结果
        $this->debug_log('getSiteDomains结果: ' . print_r($result, true));
        
        $domains = array();
        
        if ($result && is_array($result)) {
            // 检查是否是宝塔返回的错误响应格式 {"status": false, "msg": "..."}
            if (isset($result['status']) && $result['status'] === false) {
                $this->debug_log('getSiteDomains 获取失败: ' . (isset($result['msg']) ? $result['msg'] : '未知错误'));
                return array();
            }

            foreach ($result as $item) {
                $domain_name = '';
                if (isset($item['name'])) {
                    $domain_name = $item['name'];
                } elseif (is_string($item)) {
                    // 如果直接返回的是字符串数组
                    $domain_name = $item;
                }
                
                // 过滤系统赠送的域名
                if ($domain_name !== '' && !$this->isSystemGiftDomain($domain_name)) {
                    $domains[] = $domain_name;
                }
            }
        }
        
        return $domains;
    }

    public function getSSL() {
        // 获取SSL状态信息
        $result = $this->bt_api->GetSSL($this->site_name);
        
        // 调试：记录结果
        $this->debug_log('getSSL结果: ' . print_r($result, true));
        
        $data = array(
            'status' => false,
            'type' => '未开启',
            'cert_data' => array(),
            'httpToHttps' => false
        );
        
        if ($result && isset($result['status'])) {
            $data['status'] = ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1);
            $data['type'] = isset($result['type']) ? $result['type'] : '宝塔证书';
            $data['cert_data'] = isset($result['cert_data']) ? $result['cert_data'] : array();
            $data['httpToHttps'] = (isset($result['httpTohttps']) && ($result['httpTohttps'] === true || $result['httpTohttps'] === 'true' || $result['httpTohttps'] === 1)) ? true : false;
            if (isset($result['key'])) $data['key'] = $result['key'];
            if (isset($result['csr'])) $data['csr'] = $result['csr'];
        }
        
        // 获取站点绑定的域名
        $site_domains = $this->getSiteDomains();
        $data['site_domains'] = $site_domains;
        
        // 无论SSL是否开启，都尝试获取站点域名，以便在申请证书时显示
        if (empty($data['cert_data']['dns'])) {
            if (!empty($site_domains)) {
                if (!is_array($data['cert_data'])) $data['cert_data'] = array();
                $data['cert_data']['dns'] = $site_domains;
            }
        }
        
        $this->success(array('data' => $data));
    }
    
    public function setHttpToHttps($enable = null) {
        if ($enable === null) {
            $enable = isset($_POST['enable']) ? ($_POST['enable'] == 'true' || $_POST['enable'] == '1') : false;
        }
        
        // 设置强制HTTPS
        if ($enable) {
            $result = $this->bt_api->SetHttpToHttps($this->site_name);
        } else {
            $result = $this->bt_api->CloseHttpToHttps($this->site_name);
        }
        
        // 调试：输出设置强制HTTPS结果
        $this->debug_log('setHttpToHttps结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('SSL证书', $enable ? '开启强制HTTPS' : '关闭强制HTTPS');
            $this->success(array('msg' => '配置成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '设置失败';
        $this->error($error_msg);
    }

    public function getSSLCertList() {
        global $DB;
        
        $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
        
        // 首先尝试从本地数据库获取证书列表
        $local_certs = array();
        try {
            if (isset($DB) && is_object($DB)) {
                $table_check = @$DB->get_row("SHOW TABLES LIKE 'ssl_certificates'");
                if ($table_check) {
                    $sql = "SELECT id, domain, cert_hash, issuer, valid_from, valid_to, status, brand, create_time 
                            FROM ssl_certificates 
                            WHERE user_id = '{$user_id}' AND status = 'active'
                            ORDER BY create_time DESC";
                    $certs = @$DB->get_all($sql);
                    
                    if ($certs && is_array($certs)) {
                        foreach ($certs as $cert) {
                            // 计算剩余天数
                            $expire_days = 0;
                            if (!empty($cert['valid_to'])) {
                                $expire_time = strtotime($cert['valid_to']);
                                $now = time();
                                $expire_days = ceil(($expire_time - $now) / 86400);
                            }
                            
                            // 确定证书品牌显示
                            $brand_display = 'Let\'s Encrypt';
                            if (!empty($cert['brand'])) {
                                $brand_lower = strtolower($cert['brand']);
                                if (strpos($brand_lower, 'r13') !== false) {
                                    $brand_display = 'R13';
                                } elseif (strpos($brand_lower, 'r12') !== false || strpos($brand_lower, 'litessl') !== false) {
                                    $brand_display = 'R12';
                                } else {
                                    $brand_display = $cert['brand'];
                                }
                            }
                            
                            $local_certs[] = array(
                                'id' => $cert['id'],
                                'hash' => $cert['cert_hash'],
                                'domain' => $cert['domain'],
                                'subject' => $cert['domain'],
                                'dns' => array($cert['domain']),
                                'brand' => $brand_display,
                                'issuer' => $cert['issuer'] ?: 'Let\'s Encrypt',
                                'status' => $expire_days > 0 ? 'valid' : 'expired',
                                'expire_date' => $cert['valid_to'] ? date('Y-m-d', strtotime($cert['valid_to'])) : '-',
                                'expire_days' => $expire_days,
                                'verify_method' => '文件验证',
                                'is_local' => true  // 标记为本地证书
                            );
                        }
                    }
                }
            }
        } catch (Exception $e) {
            $this->debug_log('从本地获取证书列表失败: ' . $e->getMessage());
        }
        
        // 记录本地证书数量，后面会合并宝塔的证书
        $this->debug_log('从本地数据库获取到 ' . count($local_certs) . ' 个证书');
        
        // 从宝塔API获取证书
        $this->debug_log('本地没有证书，从宝塔API获取');
        
        // 获取 Let's Encrypt 免费证书（cert_type = '1'）
        $certificates = $this->bt_api->GetSSLCertList('1', 1, 100);
        
        // 调试：输出完整结果
        $this->debug_log('getSSLCertList获取Let\'s Encrypt证书: ' . print_r($certificates, true));
        
        // 如果没有 Let's Encrypt 证书，尝试获取所有证书并过滤出免费证书
        if (empty($certificates)) {
            $this->debug_log('没有Let\'s Encrypt证书，尝试获取所有证书并过滤');
            $all_certificates = $this->bt_api->GetSSLCertList('0', 1, 100);
            
            // 过滤出免费证书（Let's Encrypt 和 LiteSSL）
            $certificates = array();
            if (is_array($all_certificates)) {
                foreach ($all_certificates as $cert) {
                    $is_free = false;
                    
                    // 检查品牌字段
                    if (isset($cert['brand'])) {
                        $brand = strtolower($cert['brand']);
                        if (strpos($brand, 'letsencrypt') !== false || 
                            strpos($brand, 'let\'s encrypt') !== false ||
                            strpos($brand, 'litessl') !== false) {
                            $is_free = true;
                        }
                    }
                    
                    // 检查 issuer 字段
                    if (!$is_free && isset($cert['issuer'])) {
                        $issuer = strtolower($cert['issuer']);
                        if (strpos($issuer, 'letsencrypt') !== false || 
                            strpos($issuer, 'let\'s encrypt') !== false ||
                            strpos($issuer, 'litessl') !== false) {
                            $is_free = true;
                        }
                    }
                    
                    // 检查 info.issuer 字段
                    if (!$is_free && isset($cert['info']['issuer'])) {
                        $issuer = strtolower($cert['info']['issuer']);
                        if (strpos($issuer, 'letsencrypt') !== false || 
                            strpos($issuer, 'let\'s encrypt') !== false ||
                            strpos($issuer, 'litessl') !== false) {
                            $is_free = true;
                        }
                    }
                    
                    // 检查 info.issuer_O 字段
                    if (!$is_free && isset($cert['info']['issuer_O'])) {
                        $issuer = strtolower($cert['info']['issuer_O']);
                        if (strpos($issuer, 'letsencrypt') !== false || 
                            strpos($issuer, 'let\'s encrypt') !== false ||
                            strpos($issuer, 'litessl') !== false) {
                            $is_free = true;
                        }
                    }
                    
                    if ($is_free) {
                        $certificates[] = $cert;
                    }
                }
            }
            
            $this->debug_log('过滤后的免费证书数量: ' . count($certificates));
        }
        
        $user_cert_hashes = $this->getUserCertHashes();
        $user_cert_hash_set = array();
        if (is_array($user_cert_hashes)) {
            foreach ($user_cert_hashes as $h) {
                $h = trim((string)$h);
                if ($h !== '') $user_cert_hash_set[$h] = true;
            }
        }

        $user_domain_set = $this->getUserDomainSet();
        
        // 转换证书数据格式为前端期望的格式
        $formatted_certificates = array();
        if (is_array($certificates)) {
            foreach ($certificates as $cert) {
                $cert_hash = isset($cert['hash']) ? (string)$cert['hash'] : '';
                $cert_hash = trim($cert_hash);
                if ($cert_hash === '') {
                    continue;
                }

                if (!empty($user_cert_hash_set)) {
                    if (!isset($user_cert_hash_set[$cert_hash])) {
                        continue;
                    }
                } else {
                    $dns_list = array();
                    if (isset($cert['dns']) && is_array($cert['dns'])) {
                        $dns_list = $cert['dns'];
                    } elseif (isset($cert['info']['dns']) && is_array($cert['info']['dns'])) {
                        $dns_list = $cert['info']['dns'];
                    } else {
                        $subject = isset($cert['subject']) ? $cert['subject'] : '';
                        if ($subject !== '') $dns_list = array($subject);
                    }

                    $owned = false;
                    foreach ($dns_list as $d) {
                        $d = strtolower(trim((string)$d));
                        $d = preg_replace('/^\*\./', '', $d);
                        if ($d === '') continue;
                        if (isset($user_domain_set[$d])) {
                            $owned = true;
                            break;
                        }
                        foreach ($user_domain_set as $ud => $_v) {
                            if ($ud === '') continue;
                            $suffix1 = '.' . $ud;
                            if (strlen($d) > strlen($suffix1) && substr($d, -strlen($suffix1)) === $suffix1) {
                                $owned = true;
                                break 2;
                            }
                            $suffix2 = '.' . $d;
                            if (strlen($ud) > strlen($suffix2) && substr($ud, -strlen($suffix2)) === $suffix2) {
                                $owned = true;
                                break 2;
                            }
                        }
                    }
                    if (!$owned) {
                        continue;
                    }
                }
                
            // 计算过期天数
            $expire_days = 0;
            $valid_to = isset($cert['not_after']) ? $cert['not_after'] : (isset($cert['info']['notAfter']) ? $cert['info']['notAfter'] : (isset($cert['valid_to']) ? $cert['valid_to'] : ''));
            if ($valid_to) {
                $valid_to_time = strtotime($valid_to);
                $current_time = time();
                $expire_days = floor(($valid_to_time - $current_time) / (24 * 3600));
            }
            
            // 确定证书状态
            $status = 'valid';
            if ($expire_days < 0) {
                $status = 'expired';
            }
            
            // 提取域名（subject字段）
            $domain = isset($cert['subject']) ? $cert['subject'] : (isset($cert['dns']) && is_array($cert['dns']) ? $cert['dns'][0] : (isset($cert['domain']) ? $cert['domain'] : ''));
            
            // 提取品牌信息
            $brand = 'Let\'s Encrypt';
            if (isset($cert['brand'])) {
                $brand = $cert['brand'];
            } elseif (isset($cert['info']['issuer'])) {
                $issuer = $cert['info']['issuer'];
                if (strpos($issuer, 'LiteSSL') !== false) {
                    $brand = 'R12';
                } elseif (strpos($issuer, 'Let\'s Encrypt') !== false) {
                    $brand = 'Let\'s Encrypt';
                } elseif (strpos($issuer, 'TrustAsia') !== false) {
                    $brand = 'R13';
                } else {
                    $brand = $issuer;
                }
            } elseif (isset($cert['issuer'])) {
                $issuer = $cert['issuer'];
                if (strpos($issuer, 'LiteSSL') !== false) {
                    $brand = 'R12';
                } elseif (strpos($issuer, 'Let\'s Encrypt') !== false) {
                    $brand = 'Let\'s Encrypt';
                } elseif (strpos($issuer, 'TrustAsia') !== false) {
                    $brand = 'R13';
                } else {
                    $brand = $issuer;
                }
            } elseif (isset($cert['info']['issuer_O'])) {
                $issuer = $cert['info']['issuer_O'];
                if (strpos($issuer, 'LiteSSL') !== false) {
                    $brand = 'R12';
                } elseif (strpos($issuer, 'Let\'s Encrypt') !== false) {
                    $brand = 'Let\'s Encrypt';
                } elseif (strpos($issuer, 'TrustAsia') !== false) {
                    $brand = 'R13';
                } else {
                    $brand = $issuer;
                }
            }
            
            // 提取验证方式
            $verify_method = '文件验证';
            if (isset($cert['verify_method'])) {
                $verify_method = $cert['verify_method'] == 'dns' ? 'DNS验证' : '文件验证';
            } elseif (isset($cert['auth_info']['auth_type'])) {
                $auth_type = $cert['auth_info']['auth_type'];
                if ($auth_type == 'http') {
                    $verify_method = '文件验证';
                } elseif ($auth_type == 'dns') {
                    $verify_method = 'DNS验证';
                } else {
                    $verify_method = $auth_type;
                }
            }
            
            $formatted_cert = array(
                'id' => isset($cert['id']) ? $cert['id'] : '',
                'hash' => isset($cert['hash']) ? $cert['hash'] : '',
                'domain' => $domain,
                'status' => $status,
                'issuer' => isset($cert['info']['issuer_O']) ? $cert['info']['issuer_O'] : (isset($cert['issuer']) ? $cert['issuer'] : ''),
                'valid_from' => isset($cert['not_before']) ? $cert['not_before'] : (isset($cert['info']['notBefore']) ? $cert['info']['notBefore'] : (isset($cert['valid_from']) ? $cert['valid_from'] : '')),
                'valid_to' => $valid_to,
                'auto_renew' => 1, // 默认为开启自动续期
                'brand' => $brand,
                'expire_days' => $expire_days,
                'verify_method' => $verify_method,
                'expire_date' => $valid_to,
                'location' => '本地'
            );
            $formatted_certificates[] = $formatted_cert;
            }
        }
        
        // 合并本地证书和宝塔证书
        $all_certificates = array_merge($local_certs, $formatted_certificates);
        
        // 去重：根据证书hash去重，优先保留本地证书
        $seen_hashes = array();
        $seen_domain_expire = array(); // 用于根据域名+到期时间去重
        $unique_certificates = array();
        foreach ($all_certificates as $cert) {
            $hash = isset($cert['hash']) ? $cert['hash'] : '';
            $domain = isset($cert['domain']) ? $cert['domain'] : '';
            $expire_date = isset($cert['expire_date']) ? $cert['expire_date'] : '';
            
            // 创建域名+到期时间的唯一标识
            $domain_expire_key = $domain . '_' . $expire_date;
            
            if ($hash === '') {
                // 没有hash的证书，使用域名+到期时间去重
                if (!isset($seen_domain_expire[$domain_expire_key])) {
                    $seen_domain_expire[$domain_expire_key] = true;
                    $unique_certificates[] = $cert;
                }
            } elseif (!isset($seen_hashes[$hash])) {
                // 有hash的证书，先检查hash
                $seen_hashes[$hash] = true;
                // 再检查域名+到期时间
                if (!isset($seen_domain_expire[$domain_expire_key])) {
                    $seen_domain_expire[$domain_expire_key] = true;
                    $unique_certificates[] = $cert;
                }
            }
        }
        
        $this->debug_log('合并后证书数量: ' . count($unique_certificates) . ' (本地:' . count($local_certs) . ', 宝塔:' . count($formatted_certificates) . ')');
        
        // 无论是否有证书，都返回成功
        $this->success(array('certificates' => $unique_certificates));
    }
    
    /**
     * 获取当前用户申请的证书hash列表
     */
    private function getUserCertHashes() {
        global $DB;
        $user_id = $this->user['id'];
        
        try {
            // 检查表是否存在，使用更安全的方式
            $table_check = $DB->get_row("SHOW TABLES LIKE 'user_certs'");
            
            if (!$table_check) {
                // 表不存在，返回空数组
                return array();
            }
            
            // 查询用户申请的证书
            $user_id_escaped = $DB->escape($user_id);
            $result = $DB->query("SELECT cert_hash FROM user_certs WHERE user_id = '{$user_id_escaped}'");
            $hashes = array();
            
            if ($result) {
                while ($row = $DB->fetch($result)) {
                    $hashes[] = $row['cert_hash'];
                }
            }
            
            return $hashes;
        } catch (Exception $e) {
            // 捕获任何异常，返回空数组
            $this->debug_log('getUserCertHashes异常: ' . $e->getMessage());
            return array();
        }
    }

    private function getUserDomainSet() {
        $domains = array();

        if (!empty($this->site_name)) {
            $domains[] = $this->site_name;
        }

        $webid = $this->getWebId();
        if (!empty($webid)) {
            $site_domains = $this->bt_api->GetSiteDomains($webid);
            if (is_array($site_domains)) {
                foreach ($site_domains as $d) {
                    if (is_string($d)) {
                        $d = trim($d);
                        if ($d !== '') $domains[] = $d;
                        continue;
                    }
                    if (is_array($d)) {
                        if (isset($d['name'])) {
                            $name = trim($d['name']);
                            if ($name !== '') $domains[] = $name;
                        } elseif (isset($d['domain'])) {
                            $name = trim($d['domain']);
                            if ($name !== '') $domains[] = $name;
                        }
                    }
                }
            }
        }

        $set = array();
        foreach ($domains as $d) {
            $d = strtolower(trim($d));
            $d = preg_replace('/^\*\./', '', $d);
            if ($d !== '') $set[$d] = true;
        }
        return $set;
    }
    
    /**
     * 解析证书信息
     */
    private function parseCertificateInfo($cert_pem) {
        $info = array(
            'issuer' => '',
            'valid_from' => null,
            'valid_to' => null
        );
        
        try {
            // 使用openssl解析证书
            $cert = openssl_x509_parse($cert_pem);
            if ($cert) {
                // 获取颁发机构
                if (isset($cert['issuer']['O'])) {
                    $info['issuer'] = is_array($cert['issuer']['O']) ? implode(', ', $cert['issuer']['O']) : $cert['issuer']['O'];
                } elseif (isset($cert['issuer']['CN'])) {
                    $info['issuer'] = $cert['issuer']['CN'];
                }
                
                // 获取有效期
                if (isset($cert['validFrom_time_t'])) {
                    $info['valid_from'] = date('Y-m-d H:i:s', $cert['validFrom_time_t']);
                }
                if (isset($cert['validTo_time_t'])) {
                    $info['valid_to'] = date('Y-m-d H:i:s', $cert['validTo_time_t']);
                }
            }
        } catch (Exception $e) {
            $this->debug_log('解析证书信息失败: ' . $e->getMessage());
        }
        
        return $info;
    }
    
    /**
     * 记录用户申请的证书（带完整证书内容）
     */
    private function saveCertificateToLocal($cert_data) {
        global $DB;
        
        // 如果数据库对象不存在，直接返回
        if (!isset($DB) || !is_object($DB)) {
            $this->debug_log('saveCertificateToLocal: 数据库对象不存在');
            return false;
        }
        
        $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
        if (empty($user_id)) {
            $this->debug_log('saveCertificateToLocal: 用户ID不存在');
            return false;
        }
        
        // 使用 try-catch 包裹整个方法
        try {
            // 检查ssl_certificates表是否存在
            $table_check = @$DB->get_row("SHOW TABLES LIKE 'ssl_certificates'");
            
            if (!$table_check) {
                // 表不存在，创建表
                $create_sql = "CREATE TABLE IF NOT EXISTS `ssl_certificates` (
                    `id` int(11) NOT NULL AUTO_INCREMENT,
                    `user_id` int(11) NOT NULL COMMENT '用户ID',
                    `bt_id` int(11) NOT NULL COMMENT 'BT面板ID',
                    `domain` varchar(255) NOT NULL COMMENT '域名',
                    `cert_hash` varchar(255) DEFAULT NULL COMMENT '证书hash',
                    `cert_content` text COMMENT '证书内容(PEM格式)',
                    `key_content` text COMMENT '私钥内容(PEM格式)',
                    `chain_content` text COMMENT '证书链内容',
                    `fullchain_content` text COMMENT '完整证书链(证书+证书链)',
                    `issuer` varchar(255) DEFAULT NULL COMMENT '颁发机构',
                    `valid_from` datetime DEFAULT NULL COMMENT '生效时间',
                    `valid_to` datetime DEFAULT NULL COMMENT '到期时间',
                    `auto_renew` tinyint(1) DEFAULT '1' COMMENT '自动续期开关：0关闭，1开启',
                    `renew_days` int(11) DEFAULT '30' COMMENT '提前续期天数',
                    `status` varchar(20) DEFAULT 'active' COMMENT '证书状态：active,expired,revoked',
                    `brand` varchar(50) DEFAULT 'letsencrypt' COMMENT '证书品牌',
                    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
                    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
                    PRIMARY KEY (`id`),
                    KEY `idx_user_id` (`user_id`),
                    KEY `idx_bt_id` (`bt_id`),
                    KEY `idx_domain` (`domain`),
                    KEY `idx_valid_to` (`valid_to`),
                    KEY `idx_status` (`status`),
                    KEY `idx_cert_hash` (`cert_hash`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='SSL证书管理表'";
                
                $result = @$DB->query($create_sql);
                if (!$result) {
                    $this->debug_log('创建ssl_certificates表失败');
                    return false;
                }
                $this->debug_log('创建ssl_certificates表成功');
            }
            
            // 获取BT ID
            $bt_id = isset($this->user['btid']) ? $this->user['btid'] : (isset($this->user->btid) ? $this->user->btid : 0);
            
            // 准备数据
            $domain = isset($cert_data['domain']) ? daddslashes($cert_data['domain']) : '';
            $cert_hash = isset($cert_data['cert_hash']) ? daddslashes($cert_data['cert_hash']) : '';
            $cert_content = isset($cert_data['cert_content']) ? daddslashes($cert_data['cert_content']) : '';
            $key_content = isset($cert_data['key_content']) ? daddslashes($cert_data['key_content']) : '';
            $chain_content = isset($cert_data['chain_content']) ? daddslashes($cert_data['chain_content']) : '';
            $fullchain_content = isset($cert_data['fullchain_content']) ? daddslashes($cert_data['fullchain_content']) : '';
            $issuer = isset($cert_data['issuer']) ? daddslashes($cert_data['issuer']) : '';
            $valid_from = isset($cert_data['valid_from']) ? daddslashes($cert_data['valid_from']) : null;
            $valid_to = isset($cert_data['valid_to']) ? daddslashes($cert_data['valid_to']) : null;
            $brand = isset($cert_data['brand']) ? daddslashes($cert_data['brand']) : 'letsencrypt';
            
            // 检查是否已存在相同域名的证书
            $user_id_escaped = $DB->escape($user_id);
            $domain_escaped = $DB->escape($domain);
            $exists = @$DB->get_row("SELECT id FROM ssl_certificates WHERE user_id = '{$user_id_escaped}' AND domain = '{$domain_escaped}' AND status = 'active'");
            if ($exists) {
                // 更新现有记录
                $update_sql = "UPDATE ssl_certificates SET 
                    cert_hash = '{$cert_hash}',
                    cert_content = '{$cert_content}',
                    key_content = '{$key_content}',
                    chain_content = '{$chain_content}',
                    fullchain_content = '{$fullchain_content}',
                    issuer = '{$issuer}',
                    valid_from = " . ($valid_from ? "'{$valid_from}'" : "NULL") . ",
                    valid_to = " . ($valid_to ? "'{$valid_to}'" : "NULL") . ",
                    brand = '{$brand}',
                    status = 'active',
                    update_time = NOW()
                    WHERE id = '{$exists['id']}'";
                $result = @$DB->query($update_sql);
                if ($result) {
                    $this->debug_log('更新证书成功: domain=' . $domain);
                    return true;
                }
            } else {
                // 插入新记录
                $insert_sql = "INSERT INTO ssl_certificates 
                    (user_id, bt_id, domain, cert_hash, cert_content, key_content, chain_content, fullchain_content, issuer, valid_from, valid_to, brand, status, create_time, update_time) 
                    VALUES 
                    ('{$user_id}', '{$bt_id}', '{$domain}', '{$cert_hash}', '{$cert_content}', '{$key_content}', '{$chain_content}', '{$fullchain_content}', '{$issuer}', " . ($valid_from ? "'{$valid_from}'" : "NULL") . ", " . ($valid_to ? "'{$valid_to}'" : "NULL") . ", '{$brand}', 'active', NOW(), NOW())";
                $result = @$DB->query($insert_sql);
                if ($result) {
                    $this->debug_log('保存证书成功: domain=' . $domain);
                    return true;
                }
            }
        } catch (Exception $e) {
            $this->debug_log('saveCertificateToLocal异常: ' . $e->getMessage());
        } catch (Throwable $e) {
            $this->debug_log('saveCertificateToLocal错误: ' . $e->getMessage());
        }
        return false;
    }
    
    /**
     * 从本地数据库获取证书列表
     */
    public function getLocalCertList() {
        global $DB;
        
        $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
        if (empty($user_id)) {
            $this->error('用户未登录');
        }
        
        try {
            // 检查表是否存在
            $table_check = @$DB->get_row("SHOW TABLES LIKE 'ssl_certificates'");
            if (!$table_check) {
                // 表不存在，返回空数组
                $this->success(array('code' => 'success', 'certificates' => array()));
                return;
            }
            
            // 获取用户的所有证书
            $sql = "SELECT id, domain, cert_hash, issuer, valid_from, valid_to, status, brand, create_time 
                    FROM ssl_certificates 
                    WHERE user_id = '{$user_id}' AND status = 'active'
                    ORDER BY create_time DESC";
            $certs = @$DB->get_all($sql);
            
            $certificates = array();
            if ($certs && is_array($certs)) {
                foreach ($certs as $cert) {
                    // 计算剩余天数
                    $expire_days = 0;
                    if (!empty($cert['valid_to'])) {
                        $expire_time = strtotime($cert['valid_to']);
                        $now = time();
                        $expire_days = ceil(($expire_time - $now) / 86400);
                    }
                    
                    $certificates[] = array(
                        'id' => $cert['id'],
                        'hash' => $cert['cert_hash'],
                        'subject' => $cert['domain'],
                        'domain' => $cert['domain'],
                        'dns' => array($cert['domain']),
                        'brand' => $cert['brand'] ?: 'Let\'s Encrypt',
                        'issuer' => $cert['issuer'] ?: 'Let\'s Encrypt',
                        'status' => $expire_days > 0 ? 'valid' : 'expired',
                        'expire_date' => $cert['valid_to'] ? date('Y-m-d', strtotime($cert['valid_to'])) : '-',
                        'expire_days' => $expire_days,
                        'valid_from' => $cert['valid_from'],
                        'valid_to' => $cert['valid_to']
                    );
                }
            }
            
            $this->success(array('code' => 'success', 'certificates' => $certificates));
        } catch (Exception $e) {
            $this->debug_log('getLocalCertList异常: ' . $e->getMessage());
            $this->success(array('code' => 'success', 'certificates' => array()));
        }
    }
    
    /**
     * 从本地数据库获取证书内容
     */
    public function getLocalCertContent($cert_id) {
        global $DB;
        
        $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
        if (empty($user_id)) {
            return null;
        }
        
        try {
            $cert_id = intval($cert_id);
            $sql = "SELECT * FROM ssl_certificates WHERE id = '{$cert_id}' AND user_id = '{$user_id}' AND status = 'active'";
            $cert = @$DB->get_row($sql);
            return $cert;
        } catch (Exception $e) {
            $this->debug_log('getLocalCertContent异常: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * 记录用户申请的证书（旧方法，保留兼容）
     */
    private function recordUserCert($cert_hash, $domain = '') {
        global $DB;
        
        // 如果数据库对象不存在，直接返回
        if (!isset($DB) || !is_object($DB)) {
            $this->debug_log('recordUserCert: 数据库对象不存在');
            return;
        }
        
        $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
        if (empty($user_id)) {
            $this->debug_log('recordUserCert: 用户ID不存在');
            return;
        }
        
        // 使用 try-catch 包裹整个方法，防止错误导致页面无响应
        try {
            // 检查表是否存在，使用更安全的方式
            $table_check = @$DB->get_row("SHOW TABLES LIKE 'user_certs'");
            
            if (!$table_check) {
                // 表不存在，创建表
                $create_sql = "CREATE TABLE IF NOT EXISTS `user_certs` (
                    `id` int(11) NOT NULL AUTO_INCREMENT,
                    `user_id` int(11) NOT NULL,
                    `cert_hash` varchar(255) NOT NULL,
                    `domain` varchar(255) DEFAULT NULL,
                    `create_time` datetime DEFAULT NULL,
                    PRIMARY KEY (`id`),
                    KEY `user_id` (`user_id`),
                    KEY `cert_hash` (`cert_hash`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
                
                $result = @$DB->query($create_sql);
                if (!$result) {
                    $this->debug_log('创建user_certs表失败');
                    return; // 失败也不影响主流程
                }
                $this->debug_log('创建user_certs表成功');
            }
            
            // 检查是否已存在
            $user_id_escaped = $DB->escape($user_id);
            $cert_hash_escaped = $DB->escape($cert_hash);
            $exists = @$DB->get_row("SELECT id FROM user_certs WHERE user_id = '{$user_id_escaped}' AND cert_hash = '{$cert_hash_escaped}'");
            if ($exists) {
                $this->debug_log('证书已存在，不重复插入: cert_hash=' . $cert_hash);
                return; // 已存在，不重复插入
            }
            
            // 插入记录
            $create_time = date('Y-m-d H:i:s');
            $insert_sql = "INSERT INTO user_certs (user_id, cert_hash, domain, create_time) VALUES ('{$user_id}', '{$cert_hash}', '{$domain}', '{$create_time}')";
            $result = @$DB->query($insert_sql);
            
            if ($result) {
                $this->debug_log('记录用户证书成功: user_id=' . $user_id . ', cert_hash=' . $cert_hash . ', domain=' . $domain);
            } else {
                $this->debug_log('记录用户证书失败: ' . $insert_sql);
            }
        } catch (Exception $e) {
            // 捕获任何异常，记录日志但不影响主流程
            $this->debug_log('recordUserCert异常: ' . $e->getMessage());
        } catch (Throwable $e) {
            // 捕获所有错误（包括PHP 7+的Error类）
            $this->debug_log('recordUserCert错误: ' . $e->getMessage());
        }
    }
    
    public function applyCertificate($brand = null, $verifyMethod = null, $domains = null) {
        $brand = $brand !== null ? $brand : (isset($_POST['brand']) ? $_POST['brand'] : 'letsencrypt');
        $verifyMethod = $verifyMethod !== null ? $verifyMethod : (isset($_POST['verifyMethod']) ? $_POST['verifyMethod'] : 'file');
        $domains = $domains !== null ? $domains : (isset($_POST['domains']) ? $_POST['domains'] : array());
        
        // 确保domains是数组格式
        if (!is_array($domains)) {
            // 检查是否已经是JSON字符串
            $decoded = json_decode($domains, true);
            if (is_array($decoded)) {
                $domains = $decoded;
            } else {
                // 转换逗号分隔的域名为数组
                $domain_array = explode(',', $domains);
                // 去除域名中的空格和空值
                $domain_array = array_map('trim', $domain_array);
                $domain_array = array_filter($domain_array); // 移除空值
                $domains = $domain_array;
            }
        }
        
        // 验证域名列表
        if (empty($domains) || !is_array($domains) || count($domains) == 0) {
            $this->error('请至少选择一个域名');
        }
        
        // 将域名数组转换为JSON字符串传递给API
        $domains_json = json_encode($domains);
        
        // 获取站点ID（webid）
        $webid = $this->getWebId();
        
        // 调用宝塔API申请证书
        // verifyMethod: 'file' 或 'dns'
        // 对于文件验证，auth_type应该是 'file' 或 'http'
        $auth_type = ($verifyMethod == 'dns') ? 'dns' : 'file';
        
        // 获取站点路径
        $site_path = '/www/wwwroot/' . $this->site_name . '/';
        
        $result = $this->bt_api->ApplyCertificate($this->site_name, $brand, $auth_type, $site_path, $domains_json, $webid);
        
        // 调试：输出申请证书结果
        $this->debug_log('applyCertificate API调用完成');
        $this->debug_log('applyCertificate结果类型: ' . gettype($result));
        $this->debug_log('applyCertificate结果: ' . print_r($result, true));
        
        // 检查申请是否成功 - 使用更宽松的条件判断
        $is_success = false;
        if ($result && is_array($result)) {
            $this->debug_log('applyCertificate 结果是数组');
            if (isset($result['status'])) {
                $this->debug_log('applyCertificate status字段存在，值: ' . var_export($result['status'], true) . ', 类型: ' . gettype($result['status']));
                // 检查 status 字段
                if ($result['status'] === true || 
                    $result['status'] === 'true' || 
                    $result['status'] === 1 || 
                    $result['status'] === '1' ||
                    $result['status'] == true) {  // 使用 == 进行宽松比较
                    $is_success = true;
                    $this->debug_log('applyCertificate status判断为成功');
                } else {
                    $this->debug_log('applyCertificate status判断为失败');
                }
            } elseif (isset($result['cert']) || isset($result['save_path'])) {
                // 如果没有 status 字段，但有 cert 或 save_path 字段，也认为成功
                $is_success = true;
                $this->debug_log('applyCertificate 没有status字段，但有cert或save_path，判断为成功');
            } else {
                $this->debug_log('applyCertificate 没有status、cert或save_path字段');
            }
        } else {
            $this->debug_log('applyCertificate 结果不是数组或为空');
        }
        
        $this->debug_log('applyCertificate 最终成功判断结果: ' . ($is_success ? 'true' : 'false'));
        
        if ($is_success) {
            $this->log('SSL证书', '申请证书：' . implode(',', $domains));
            
            // 获取目标域名
            $target_domain = is_array($domains) ? $domains[0] : $domains;
            
            // 尝试从结果中获取证书数据
            // 宝塔API可能返回不同格式的数据
            $cert_data = null;
            if (isset($result['data']) && is_array($result['data'])) {
                $cert_data = $result['data'];
            } elseif (isset($result['cert']) || isset($result['private_key'])) {
                $cert_data = $result;
            }
            
            // 如果直接从申请结果获取到证书数据，直接使用SetSSL部署
            if ($cert_data && (isset($cert_data['private_key']) || isset($cert_data['key'])) && 
                (isset($cert_data['cert']) || isset($cert_data['csr']))) {
                
                $this->debug_log('从申请结果直接获取证书数据，使用SetSSL部署');
                
                // 获取私钥
                $key = isset($cert_data['private_key']) ? $cert_data['private_key'] : (isset($cert_data['key']) ? $cert_data['key'] : '');
                // 获取证书
                $cert = isset($cert_data['cert']) ? $cert_data['cert'] : (isset($cert_data['csr']) ? $cert_data['csr'] : '');
                
                // 处理数组格式
                if (is_array($key)) $key = implode("\n", $key);
                if (is_array($cert)) $cert = implode("\n", $cert);
                
                if (!empty($key) && !empty($cert)) {
                    // 获取证书链内容
                    $chain = isset($cert_data['root']) ? $cert_data['root'] : (isset($cert_data['chain']) ? $cert_data['chain'] : '');
                    if (is_array($chain)) $chain = implode("\n", $chain);
                    
                    // 构建完整证书链
                    $fullchain = $cert;
                    if (!empty($chain)) {
                        $fullchain = $cert . "\n" . $chain;
                    }
                    
                    // 解析证书信息
                    $cert_info = $this->parseCertificateInfo($cert);
                    
                    // 保存证书到本地数据库
                    $local_cert_data = array(
                        'domain' => $target_domain,
                        'cert_hash' => md5($cert),
                        'cert_content' => $cert,
                        'key_content' => $key,
                        'chain_content' => $chain,
                        'fullchain_content' => $fullchain,
                        'issuer' => isset($cert_info['issuer']) ? $cert_info['issuer'] : '',
                        'valid_from' => isset($cert_info['valid_from']) ? $cert_info['valid_from'] : null,
                        'valid_to' => isset($cert_info['valid_to']) ? $cert_info['valid_to'] : null,
                        'brand' => $brand
                    );
                    $this->saveCertificateToLocal($local_cert_data);
                    
                    // 直接使用SetSSL部署证书
                    $webid = $this->getWebId();
                    $ssl_result = $this->bt_api->SetSSL($webid, 1, $key, $cert);
                    $this->debug_log('SetSSL结果: ' . print_r($ssl_result, true));
                    
                    if ($ssl_result && isset($ssl_result['status']) && ($ssl_result['status'] === true || $ssl_result['status'] === 'true' || $ssl_result['status'] === 1)) {
                        $this->success(array('msg' => '申请证书成功并已部署'));
                        return;
                    } else {
                        $ssl_error = isset($ssl_result['msg']) ? $ssl_result['msg'] : '部署失败';
                        $this->success(array('msg' => '申请证书成功，但部署失败：' . $ssl_error));
                        return;
                    }
                }
            }
            
            // 如果没有直接从申请结果获取到证书数据，尝试保存证书文件并部署
            $this->debug_log('未从申请结果获取到证书数据，尝试保存证书文件');
            
            // 确定证书保存路径
            $cert_dir = '';
            $cert_dir_alt = '';
            if (isset($result['save_path'])) {
                $cert_dir = '/www/server/panel/' . $result['save_path'];
            } else {
                // 宝塔部署证书时查找的路径是 /www/server/panel/vhost/cert/域名/
                // 同时也需要保存到 letsencrypt 目录
                $cert_dir = '/www/server/panel/vhost/cert/' . $target_domain;
                $cert_dir_alt = '/www/server/panel/vhost/letsencrypt/' . $target_domain;
            }
            
            // 保存证书文件
            if (isset($result['cert']) || isset($result['root']) || isset($result['private_key'])) {
                $this->debug_log('证书目录: ' . $cert_dir);
                
                // 需要保存的目录列表
                $cert_dirs = array($cert_dir);
                if (!empty($cert_dir_alt)) {
                    $cert_dirs[] = $cert_dir_alt;
                }
                
                foreach ($cert_dirs as $dir) {
                    $this->debug_log('保存证书到目录: ' . $dir);
                    
                    // 确保目录存在
                    $this->bt_api->CreateDir($dir);
                    
                    // 保存证书文件
                    if (isset($result['cert'])) {
                        $cert_content = is_array($result['cert']) ? implode("\n", $result['cert']) : $result['cert'];
                        $cert_path = $dir . '/cert.pem';
                        $this->bt_api->CreateFile($cert_path);
                        $save_cert = $this->bt_api->SaveFileBody($cert_path, $cert_content, 'utf-8');
                        $this->debug_log('保存 cert.pem 结果: ' . print_r($save_cert, true));
                    }
                    
                    // 保存私钥
                    if (isset($result['private_key'])) {
                        $key_content = is_array($result['private_key']) ? implode("\n", $result['private_key']) : $result['private_key'];
                        $key_path = $dir . '/privkey.pem';
                        $this->bt_api->CreateFile($key_path);
                        $save_key = $this->bt_api->SaveFileBody($key_path, $key_content, 'utf-8');
                        $this->debug_log('保存 privkey.pem 结果: ' . print_r($save_key, true));
                    }
                    
                    // 保存根证书和fullchain
                    if (isset($result['root'])) {
                        $root_content = is_array($result['root']) ? implode("\n", $result['root']) : $result['root'];
                        $root_path = $dir . '/chain.pem';
                        $this->bt_api->CreateFile($root_path);
                        $save_root = $this->bt_api->SaveFileBody($root_path, $root_content, 'utf-8');
                        $this->debug_log('保存 chain.pem 结果: ' . print_r($save_root, true));
                        
                        // 创建 fullchain.pem
                        if (isset($result['cert'])) {
                            $cert_content = is_array($result['cert']) ? implode("\n", $result['cert']) : $result['cert'];
                            $fullchain_content = $cert_content . "\n" . $root_content;
                            $fullchain_path = $dir . '/fullchain.pem';
                            $this->bt_api->CreateFile($fullchain_path);
                            $save_fullchain = $this->bt_api->SaveFileBody($fullchain_path, $fullchain_content, 'utf-8');
                            $this->debug_log('保存 fullchain.pem 结果: ' . print_r($save_fullchain, true));
                        }
                    }
                }
            }
            
            // 申请成功后，获取证书列表以获取证书的hash值
            $list = $this->bt_api->GetSSLCertList('0', 1, 100);
            $cert_hash = '';
            
            if (is_array($list)) {
                // 遍历查找匹配的证书
                foreach ($list as $cert) {
                     $cert_domain = isset($cert['subject']) ? $cert['subject'] : (isset($cert['dns']) && is_array($cert['dns']) ? $cert['dns'][0] : '');
                     if ($cert_domain == $target_domain) {
                         $cert_hash = isset($cert['hash']) ? $cert['hash'] : '';
                         break;
                     }
                }
            }
            
            // 如果没找到，回退到取第一个（作为保底，通常最新申请的在最前面）
            if (empty($cert_hash) && !empty($list) && is_array($list) && isset($list[0]['hash'])) {
                $cert_hash = $list[0]['hash'];
            }
            
            // 使用获取到的hash值部署证书
            $deploy_result = $this->bt_api->DeployCertificate($this->site_name, $cert_hash);
            
            // 记录用户申请的证书（可选，失败不影响主流程）
            if (!empty($cert_hash)) {
                try {
                    $this->recordUserCert($cert_hash, $target_domain);
                    $this->debug_log('已记录用户证书: hash=' . $cert_hash . ', domain=' . $target_domain);
                } catch (Exception $e) {
                    $this->debug_log('记录用户证书失败（不影响主流程）: ' . $e->getMessage());
                } catch (Throwable $e) {
                    $this->debug_log('记录用户证书失败（不影响主流程）: ' . $e->getMessage());
                }
            }
            
            if ($deploy_result && isset($deploy_result['status']) && ($deploy_result['status'] === true || $deploy_result['status'] === 'true' || $deploy_result['status'] === 1)) {
                $this->success(array('msg' => '申请证书成功并已部署'));
                return;
            } else {
                $deploy_error = isset($deploy_result['msg']) ? $deploy_result['msg'] : '未知错误';
                $this->success(array('msg' => '申请证书成功，但部署失败：' . $deploy_error));
                return;
            }
        }
        
        // 如果不成功，返回错误
        $this->debug_log('applyCertificate 进入失败分支');
        
        // 处理错误信息
        $error_msg = '申请证书失败';
        if (isset($result['msg'])) {
            if (is_array($result['msg'])) {
                // 处理数组格式的错误信息
                foreach ($result['msg'] as $msg_item) {
                    if (is_array($msg_item) && isset($msg_item['detail'])) {
                        $error_msg = $msg_item['detail'];
                        break;
                    } elseif (is_string($msg_item) && strpos($msg_item, 'detail') !== false) {
                        // 尝试从JSON字符串中提取detail
                        if (preg_match("/'detail':\s*'([^']+)'/", $msg_item, $matches)) {
                            $error_msg = $matches[1];
                            break;
                        }
                    }
                }
            } else {
                $error_msg = $result['msg'];
            }
        }
        
        $this->debug_log('applyCertificate 错误信息: ' . $error_msg);
        $this->error($error_msg);
    }
    
    public function deployCertificate($cert_hash = null) {
        $cert_hash = $cert_hash !== null ? $cert_hash : (isset($_POST['cert_hash']) ? $_POST['cert_hash'] : '');
        $cert_id = isset($_POST['cert_id']) ? intval($_POST['cert_id']) : 0;
        
        if (empty($cert_hash) && empty($cert_id)) {
            $this->error('证书hash或证书ID不能为空');
        }
        
        // 首先尝试从本地数据库获取证书
        $local_cert = null;
        if (!empty($cert_id)) {
            $local_cert = $this->getLocalCertContent($cert_id);
        } elseif (!empty($cert_hash)) {
            // 通过hash查找本地证书
            global $DB;
            $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
            try {
                $sql = "SELECT * FROM ssl_certificates WHERE user_id = '{$user_id}' AND cert_hash = '{$cert_hash}' AND status = 'active' LIMIT 1";
                $local_cert = @$DB->get_row($sql);
            } catch (Exception $e) {
                $this->debug_log('从本地查找证书失败: ' . $e->getMessage());
            }
        }
        
        // 如果找到本地证书，直接使用本地证书部署
        if ($local_cert && !empty($local_cert['key_content']) && !empty($local_cert['cert_content'])) {
            $this->debug_log('从本地数据库获取到证书，使用本地证书部署');
            
            $webid = $this->getWebId();
            $key = $local_cert['key_content'];
            $cert = $local_cert['cert_content'];
            
            // 使用SetSSL直接部署
            $ssl_result = $this->bt_api->SetSSL($webid, 1, $key, $cert);
            $this->debug_log('SetSSL部署结果: ' . print_r($ssl_result, true));
            
            if ($ssl_result && isset($ssl_result['status']) && ($ssl_result['status'] === true || $ssl_result['status'] === 'true' || $ssl_result['status'] === 1)) {
                $this->log('SSL证书', '部署证书：' . $local_cert['domain']);
                $this->success(array('msg' => '部署成功'));
                return;
            } else {
                $error_msg = isset($ssl_result['msg']) ? $ssl_result['msg'] : '部署失败';
                $this->error('部署失败：' . $error_msg);
                return;
            }
        }
        
        // 本地没有证书，使用宝塔API部署
        $this->debug_log('本地没有证书，使用宝塔API部署');
        
        if (empty($cert_hash)) {
            $this->error('证书hash不能为空');
        }
        
        // 从证书列表获取证书信息
        $cert_list = $this->bt_api->GetSSLCertList('0', 1, 100);
        $cert_info = null;
        
        if (is_array($cert_list)) {
            foreach ($cert_list as $cert) {
                if (isset($cert['hash']) && $cert['hash'] == $cert_hash) {
                    $cert_info = $cert;
                    break;
                }
            }
        }
        
        $this->debug_log('deployCertificate: 证书信息 - ' . print_r($cert_info, true));
        
        if ($cert_info && isset($cert_info['path'])) {
            $ssl_saved_path = $cert_info['path'];
            $domain = isset($cert_info['subject']) ? $cert_info['subject'] : '';
            
            if (!empty($domain)) {
                // 宝塔部署证书时查找的路径
                $cert_path = '/www/server/panel/vhost/cert/' . $domain;
                
                $this->debug_log('deployCertificate: ssl_saved路径=' . $ssl_saved_path);
                $this->debug_log('deployCertificate: cert路径=' . $cert_path);
                
                // 确保目标目录存在
                $this->bt_api->CreateDir($cert_path);
                
                // 复制证书文件
                $files_to_copy = array('fullchain.pem', 'privkey.pem', 'cert.pem', 'chain.pem');
                foreach ($files_to_copy as $filename) {
                    $src_file = rtrim($ssl_saved_path, '/') . '/' . $filename;
                    $dst_file = rtrim($cert_path, '/') . '/' . $filename;
                    
                    // 读取源文件
                    $file_content = $this->bt_api->GetFileContent($src_file);
                    $this->debug_log('deployCertificate: 读取 ' . $src_file . ' 结果 - ' . print_r($file_content, true));
                    
                    // 处理不同的返回格式
                    $content = '';
                    if ($file_content) {
                        if (isset($file_content['status']) && ($file_content['status'] === true || $file_content['status'] === 1 || $file_content['status'] === '1')) {
                            // status为true的情况
                            if (isset($file_content['data'])) {
                                $content = is_array($file_content['data']) ? implode("\n", $file_content['data']) : $file_content['data'];
                            }
                        } elseif (isset($file_content['data']) && !empty($file_content['data'])) {
                            // 没有status但有data的情况
                            $content = is_array($file_content['data']) ? implode("\n", $file_content['data']) : $file_content['data'];
                        } elseif (is_string($file_content) && !empty($file_content)) {
                            // 直接返回字符串的情况
                            $content = $file_content;
                        }
                    }
                    
                    if (!empty($content)) {
                        // 先创建目标文件
                        $this->bt_api->CreateFile($dst_file);
                        // 写入目标文件
                        $save_result = $this->bt_api->SaveFileBody($dst_file, $content, 'utf-8');
                        $this->debug_log('deployCertificate: 复制 ' . $filename . ' 结果 - ' . print_r($save_result, true));
                    } else {
                        $this->debug_log('deployCertificate: 无法读取 ' . $filename . ' 的内容');
                    }
                }
            }
        }
        
        // 在部署前确保 fullchain.pem 存在
        $this->debug_log('deployCertificate: 检查并创建 fullchain.pem');
        $fullchain_result = $this->ensureFullchainExists($cert_hash);
        if (!$fullchain_result) {
            $this->debug_log('deployCertificate: 警告 - fullchain.pem 创建失败，但继续尝试部署');
        }
        
        // 使用标准宝塔API：SetSSLConf部署证书
        $result = $this->bt_api->DeployCertificate($this->site_name, $cert_hash);
        
        // 调试：输出部署结果
        $this->debug_log('deployCertificate结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('SSL证书', '部署证书');
            $this->success(array('msg' => '部署成功'));
        }
        
        // 提取详细的错误信息
        $error_msg = '部署失败';
        $error_details = '';
        
        if (isset($result['msg'])) {
            $error_msg = $result['msg'];
        }
        
        // 检查是否是Nginx配置错误
        if (isset($result['data']) && is_string($result['data'])) {
            $error_details = $result['data'];
        } elseif (isset($result['error'])) {
            $error_details = is_array($result['error']) ? implode("\n", $result['error']) : $result['error'];
        }
        
        // 如果错误信息包含证书文件不存在，给出更明确的提示
        if (strpos($error_msg, 'cannot load certificate') !== false || 
            strpos($error_msg, 'No such file') !== false ||
            strpos($error_details, 'cannot load certificate') !== false) {
            $error_msg = '证书文件不存在，请先上传证书或申请证书';
        }
        
        // 如果错误信息中包含 fullchain，给出更明确的提示
        if (strpos($error_msg, 'fullchain') !== false || strpos($error_details, 'fullchain') !== false) {
            $error_msg = '部署失败：证书文件不完整，缺少 fullchain.pem 文件';
        }
        
        // 返回包含详细信息的错误
        $this->error($error_msg);
    }
    
    public function renewCertificate($domain = null) {
        $domain = $domain !== null ? $domain : (isset($_POST['domain']) ? $_POST['domain'] : '');
        
        // 使用标准宝塔API：Renew_SSL续签证书
        $result = $this->bt_api->RenewCertificate($this->site_name, $domain);
        
        // 调试：记录结果
        $this->debug_log('renewCertificate结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('SSL证书', '续签证书：' . $domain);
            $this->success(array('msg' => '续签成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '续签失败';
        $this->error($error_msg);
    }
    
    public function downloadCertificate($cert_hash = null) {
        $cert_hash = $cert_hash !== null ? $cert_hash : (isset($_POST['cert_hash']) ? $_POST['cert_hash'] : '');
        
        // 使用标准宝塔API：downloadCRT下载证书
        $result = $this->bt_api->DownloadCertificate($cert_hash);
        
        // 调试：记录结果
        $this->debug_log('downloadCertificate结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            // 返回下载链接或文件内容
            if (isset($result['download_url'])) {
                $this->success(array('download_url' => $result['download_url']));
            } elseif (isset($result['data'])) {
                $this->success(array('data' => $result['data']));
            } else {
                $this->success();
            }
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '下载失败';
        $this->error($error_msg);
    }
    
    /**
     * 删除本地证书
     */
    private function deleteLocalCertificate($cert_id) {
        global $DB;
        
        $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
        if (empty($user_id)) {
            $this->error('用户未登录');
        }
        
        try {
            // 检查表是否存在
            $table_check = @$DB->get_row("SHOW TABLES LIKE 'ssl_certificates'");
            if (!$table_check) {
                $this->error('证书不存在');
            }
            
            // 检查证书是否属于当前用户
            $cert_id_escaped = $DB->escape($cert_id);
            $user_id_escaped = $DB->escape($user_id);
            $cert = @$DB->get_row("SELECT * FROM ssl_certificates WHERE id = '{$cert_id_escaped}' AND user_id = '{$user_id_escaped}'");
            if (!$cert) {
                $this->error('证书不存在或无权限删除');
            }
            
            $domain = $cert['domain'];
            $cert_hash = $cert['cert_hash'];
            
            // 软删除：将状态改为revoked
            $cert_id_escaped = $DB->escape($cert_id);
            $result = @$DB->query("UPDATE ssl_certificates SET status = 'revoked', update_time = NOW() WHERE id = '{$cert_id_escaped}'");
            
            if ($result) {
                // 同时尝试删除宝塔中同域名的证书
                $this->debug_log('删除本地证书成功，尝试删除宝塔中同域名的证书: domain=' . $domain);
                $this->deleteBtCertificateByDomain($domain, $cert_hash);
                
                $this->log('SSL证书', '删除本地证书：' . $domain);
                $this->success(array('msg' => '删除成功'));
            } else {
                $this->error('删除失败');
            }
        } catch (Exception $e) {
            $this->debug_log('deleteLocalCertificate异常: ' . $e->getMessage());
            $this->error('删除失败：' . $e->getMessage());
        }
    }
    
    /**
     * 根据域名删除宝塔证书
     */
    private function deleteBtCertificateByDomain($domain, $exclude_hash = '') {
        try {
            // 获取宝塔证书列表
            $cert_list = $this->bt_api->GetSSLCertList('0', 1, 100);
            
            if (!is_array($cert_list)) {
                $this->debug_log('获取宝塔证书列表失败');
                return;
            }
            
            foreach ($cert_list as $bt_cert) {
                $cert_hash = isset($bt_cert['hash']) ? $bt_cert['hash'] : '';
                $cert_domain = '';
                
                // 获取证书域名
                if (isset($bt_cert['subject'])) {
                    $cert_domain = $bt_cert['subject'];
                } elseif (isset($bt_cert['dns']) && is_array($bt_cert['dns']) && count($bt_cert['dns']) > 0) {
                    $cert_domain = $bt_cert['dns'][0];
                } elseif (isset($bt_cert['info']['dns']) && is_array($bt_cert['info']['dns']) && count($bt_cert['info']['dns']) > 0) {
                    $cert_domain = $bt_cert['info']['dns'][0];
                }
                
                // 如果域名匹配且hash不同（避免重复删除同一个证书）
                if ($cert_domain === $domain && $cert_hash !== $exclude_hash && !empty($cert_hash)) {
                    $this->debug_log('找到匹配的宝塔证书，准备删除: hash=' . $cert_hash . ', domain=' . $cert_domain);
                    $result = $this->bt_api->DeleteCertificate($cert_hash);
                    $this->debug_log('删除宝塔证书结果: ' . print_r($result, true));
                }
            }
        } catch (Exception $e) {
            $this->debug_log('deleteBtCertificateByDomain异常: ' . $e->getMessage());
        }
    }
    
    public function deleteCertificate($cert_hash = null) {
        $cert_hash = $cert_hash !== null ? $cert_hash : (isset($_POST['cert_hash']) ? $_POST['cert_hash'] : '');
        $cert_id = isset($_POST['cert_id']) ? intval($_POST['cert_id']) : 0;

        $this->debug_log('deleteCertificate收到的cert_hash: ' . $cert_hash . ', cert_id: ' . $cert_id . ', site_name: ' . $this->site_name);

        // 如果有cert_id，优先删除本地证书
        if (!empty($cert_id)) {
            return $this->deleteLocalCertificate($cert_id);
        }

        if (empty($cert_hash)) {
            $this->error('证书hash不能为空');
        }

        $user_cert_hashes = $this->getUserCertHashes();
        $user_cert_hash_set = array();
        if (is_array($user_cert_hashes)) {
            foreach ($user_cert_hashes as $h) {
                $h = trim((string)$h);
                if ($h !== '') $user_cert_hash_set[$h] = true;
            }
        }
        if (!empty($user_cert_hash_set) && !isset($user_cert_hash_set[$cert_hash])) {
            $this->error('无权限删除该证书');
        }

        if (empty($user_cert_hash_set)) {
            $user_domain_set = $this->getUserDomainSet();
            $cert_list = $this->bt_api->GetSSLCertList('0', 1, 100);
            $dns_list = array();
            if (is_array($cert_list)) {
                foreach ($cert_list as $cert) {
                    if (isset($cert['hash']) && (string)$cert['hash'] === (string)$cert_hash) {
                        if (isset($cert['dns']) && is_array($cert['dns'])) {
                            $dns_list = $cert['dns'];
                        } elseif (isset($cert['info']['dns']) && is_array($cert['info']['dns'])) {
                            $dns_list = $cert['info']['dns'];
                        } elseif (isset($cert['subject'])) {
                            $dns_list = array($cert['subject']);
                        }
                        break;
                    }
                }
            }

            $owned = false;
            foreach ($dns_list as $d) {
                $d = strtolower(trim((string)$d));
                $d = preg_replace('/^\*\./', '', $d);
                if ($d === '') continue;
                if (isset($user_domain_set[$d])) {
                    $owned = true;
                    break;
                }
                foreach ($user_domain_set as $ud => $_v) {
                    if ($ud === '') continue;
                    $suffix1 = '.' . $ud;
                    if (strlen($d) > strlen($suffix1) && substr($d, -strlen($suffix1)) === $suffix1) {
                        $owned = true;
                        break 2;
                    }
                    $suffix2 = '.' . $d;
                    if (strlen($ud) > strlen($suffix2) && substr($ud, -strlen($suffix2)) === $suffix2) {
                        $owned = true;
                        break 2;
                    }
                }
            }
            if (!$owned) {
                $this->error('无权限删除该证书');
            }
        }

        $result = $this->bt_api->DeleteCertificate($cert_hash);
        $this->debug_log('deleteCertificate调用bt_api->DeleteCertificate结果: ' . print_r($result, true));

        if (is_array($result) && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            try {
                global $DB;
                $user_id = isset($this->user['id']) ? $this->user['id'] : (isset($this->user->id) ? $this->user->id : 0);
                if (!empty($user_id) && isset($DB) && is_object($DB)) {
                    @$DB->query("DELETE FROM user_certs WHERE user_id = '" . addslashes($user_id) . "' AND cert_hash = '" . addslashes($cert_hash) . "'");
                }
            } catch (Exception $e) {
            } catch (Throwable $e) {
            }
            $this->log('SSL证书', '删除证书');
            $this->success(array('msg' => isset($result['msg']) ? $result['msg'] : '删除成功'));
        }

        $error_msg = is_array($result) && isset($result['msg']) ? $result['msg'] : '删除失败';
        // 提示用户先关闭SSL再删除
        $this->error('请先在"当前证书"页面关闭SSL证书，然后再点击删除');
    }
    
    public function uploadCertificate($cert_key = null, $cert_pem = null) {
        $cert_key = $cert_key !== null ? $cert_key : (isset($_POST['cert_key']) ? $_POST['cert_key'] : '');
        $cert_pem = $cert_pem !== null ? $cert_pem : (isset($_POST['cert_pem']) ? $_POST['cert_pem'] : '');
        
        $webid = $this->getWebId();
        
        if (empty($webid)) {
            $this->error('无法获取站点ID');
        }
        
        // SetSSL API: type=1表示手动上传，key为密钥内容，cert为证书内容
        $result = $this->bt_api->SetSSL($webid, 1, $cert_key, $cert_pem);
        
        // 调试：记录结果
        $this->debug_log('uploadCertificate SetSSL结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('SSL证书', '上传证书');
            
            // 上传成功后，获取证书列表以获取证书的hash值
            $list = $this->bt_api->GetSSLCertList('0', 1, 100);
            
            // 调试：记录获取列表结果
            $this->debug_log('uploadCertificate GetSSLCertList结果: ' . print_r($list, true));
            
            $cert_hash = '';
            
            if (!empty($list) && is_array($list) && isset($list[0]['hash'])) {
                $cert_hash = $list[0]['hash'];
            }
            
            // 确保 fullchain.pem 存在
            if (!empty($cert_hash)) {
                $this->debug_log('uploadCertificate: 检查并创建 fullchain.pem');
                $fullchain_result = $this->ensureFullchainExists($cert_hash);
                if (!$fullchain_result) {
                    $this->debug_log('uploadCertificate: 警告 - fullchain.pem 创建失败');
                }
            }
            
            // 使用获取到的hash值部署证书
            $deploy_result = $this->bt_api->DeployCertificate($this->site_name, $cert_hash);
            
            // 记录用户上传的证书（可选，失败不影响主流程）
            if (!empty($cert_hash)) {
                try {
                    // 尝试从证书内容中提取域名
                    $domain = $this->site_name;
                    if (preg_match('/CN\s*=\s*([^\s,]+)/', $cert_pem, $matches)) {
                        $domain = $matches[1];
                    }
                    $this->recordUserCert($cert_hash, $domain);
                    $this->debug_log('已记录用户上传的证书: hash=' . $cert_hash . ', domain=' . $domain);
                } catch (Exception $e) {
                    $this->debug_log('记录用户上传的证书失败（不影响主流程）: ' . $e->getMessage());
                } catch (Throwable $e) {
                    $this->debug_log('记录用户上传的证书失败（不影响主流程）: ' . $e->getMessage());
                }
            }
            
            // 调试：记录部署结果
            $this->debug_log('uploadCertificate DeployCertificate结果: ' . print_r($deploy_result, true));
            
            if ($deploy_result && isset($deploy_result['status']) && ($deploy_result['status'] === true || $deploy_result['status'] === 'true' || $deploy_result['status'] === 1)) {
                $this->success(array('msg' => '证书上传并部署成功'));
            } else {
                // 即使部署失败，也返回上传成功
                $deploy_error = isset($deploy_result['msg']) ? $deploy_result['msg'] : '未知错误';
                $this->success(array('msg' => '证书上传成功，但部署失败：' . $deploy_error));
            }
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '上传失败';
        $this->error($error_msg);
    }
    
    public function setHotlink($enable, $domains, $exts, $return_rule = null, $http_status = null) {
        $result = $this->bt_api->SetHotlink($this->site_name, $enable, $domains, $exts);
        
        $this->debug_log('setHotlink结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->log('防盗链设置', '防盗链设置');
            $this->success(array('msg' => '设置成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '设置失败';
        $this->error($error_msg);
    }

    public function getHotlinkStatus() {
        $result = $this->bt_api->GetSecurity($this->site_name);
        
        $this->debug_log('getHotlinkStatus结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $data = $result['data'];
            // 确保返回格式统一
            $this->success(array(
                'status' => (isset($data['status']) && ($data['status'] === true || $data['status'] === 'true' || $data['status'] === 1)),
                'url' => isset($data['url']) ? $data['url'] : '',
                'exts' => isset($data['exts']) ? $data['exts'] : 'jpg,png,gif,js,css',
                'type' => isset($data['type']) ? $data['type'] : 'URI'
            ));
        } else {
            // 如果API调用失败，返回默认值
            $this->success(array(
                'status' => false,
                'url' => '',
                'exts' => 'jpg,png,gif,js,css',
                'type' => 'URI'
            ));
        }
    }
    
    public function getTrafficLimitStatus() {
        $webid = $this->getWebId();
        
        // 使用宝塔API获取流量限制信息
        $result = $this->bt_api->GetTrafficLimit($webid);
        
        // 调试：记录结果
        $this->debug_log('getTrafficLimitStatus结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->success(array('enabled' => ($result['enabled'] === true || $result['enabled'] === 'true' || $result['enabled'] === 1), 'limit' => $result['limit']));
        } else {
            // 如果API调用失败，返回默认值
            $this->success(array('enabled' => false, 'limit' => 10));
        }
    }
    
    public function setTrafficLimit($limit, $enable) {
        $webid = $this->getWebId();
        
        if ($enable == 'true') {
            if (empty($limit) || $limit < 1) {
                $this->error('流量限制值不能小于1MB/秒');
            }
        }
        
        // 使用宝塔API设置流量限制
        $result = $this->bt_api->SetTrafficLimit($webid, $limit, $enable);
        
        // 调试：记录结果
        $this->debug_log('setTrafficLimit结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $action = $enable == 'true' ? '设置' : '取消';
            $this->log('流量限制' . $action, $action . '流量限制: ' . ($enable == 'true' ? $limit . 'MB/秒' : ''));
            $this->success(array('msg' => $action . '成功'));
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : ($enable == 'true' ? '设置失败' : '取消失败');
        $this->error($error_msg);
    }
    
    /**
     * 获取 bt_api 实例（供外部调用）
     * @return bt_api
     */
    public function getBtApi() {
        return $this->bt_api;
    }
    
    /**
     * 公共错误输出方法（供外部调用）
     * @param string $message 错误消息
     */
    public function outputError($message) {
        $this->error($message);
    }
    
    public function querySSLCertificateStatus() {
        $certId = isset($_POST['certId']) ? daddslashes($_POST['certId']) : '';
        $domain = isset($_POST['domain']) ? daddslashes($_POST['domain']) : '';
        
        if (empty($certId) && empty($domain)) {
            $this->error('参数错误：请提供证书ID或域名');
        }
        
        // 调用宝塔API获取证书状态
        $result = $this->bt_api->GetSSLCertificateStatus($certId, $domain);
        
        // 调试：记录结果
        $this->debug_log('querySSLCertificateStatus结果: ' . print_r($result, true));
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            $this->success($result['data']);
        }
        
        $error_msg = isset($result['msg']) ? $result['msg'] : '查询证书状态失败';
        $this->error($error_msg);
    }
}
?>
