prepare("SELECT id FROM users WHERE username = ?"); $stmt->execute([$username]); if ($stmt->fetch()) { return ['success' => false, 'message' => '用户名已被使用']; } // 检查邮箱是否已存在 $stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?"); $stmt->execute([$email]); if ($stmt->fetch()) { return ['success' => false, 'message' => '邮箱已被注册']; } $pdo->beginTransaction(); try { $hashedPassword = password_hash($password, PASSWORD_DEFAULT); $verificationToken = bin2hex(random_bytes(32)); $inviteCodeGen = strtoupper(substr(bin2hex(random_bytes(6)), 0, 8)); // 处理邀请 $invitedBy = null; if ($inviteCode) { $stmt = $pdo->prepare("SELECT id FROM users WHERE invite_code = ?"); $stmt->execute([$inviteCode]); $inviter = $stmt->fetch(); if ($inviter) { $invitedBy = $inviter['id']; } } // 创建用户 $stmt = $pdo->prepare("INSERT INTO users (username, email, password, invite_code, invited_by, verification_token) VALUES (?, ?, ?, ?, ?, ?)"); $stmt->execute([$username, $email, $hashedPassword, $inviteCodeGen, $invitedBy, $verificationToken]); $userId = $pdo->lastInsertId(); // 如果是邀请注册,给邀请人加积分 if ($invitedBy) { // 增加邀请人积分 $stmt = $pdo->prepare("UPDATE users SET credits = credits + ? WHERE id = ?"); $stmt->execute([INVITE_CREDIT, $invitedBy]); // 记录邀请日志 $stmt = $pdo->prepare("INSERT INTO invite_logs (inviter_id, invitee_id, credits_earned) VALUES (?, ?, ?)"); $stmt->execute([$invitedBy, $userId, INVITE_CREDIT]); } $pdo->commit(); // 发送验证邮件 sendVerificationEmail($email, $username, $verificationToken); return ['success' => true, 'message' => '注册成功!请查收验证邮件']; } catch (Exception $e) { $pdo->rollBack(); return ['success' => false, 'message' => '注册失败: ' . $e->getMessage()]; } } /** * 用户登录 */ function loginUser($username, $password) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? OR email = ?"); $stmt->execute([$username, $username]); $user = $stmt->fetch(); if (!$user) { return ['success' => false, 'message' => '用户不存在']; } if (!$user['is_verified']) { return ['success' => false, 'message' => '请先验证邮箱']; } if (!password_verify($password, $user['password'])) { return ['success' => false, 'message' => '密码错误']; } $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username']; $_SESSION['is_admin'] = $user['is_admin']; return ['success' => true, 'message' => '登录成功']; } /** * 发送验证邮件(修复乱码问题) */ function sendVerificationEmail($email, $username, $token) { $mail = new PHPMailer(true); try { $mail->isSMTP(); $mail->Host = SMTP_HOST; $mail->SMTPAuth = true; $mail->Username = SMTP_USERNAME; $mail->Password = SMTP_PASSWORD; $mail->SMTPSecure = SMTP_SECURE; $mail->Port = SMTP_PORT; // 设置字符编码 - 解决乱码问题 $mail->CharSet = 'UTF-8'; $mail->Encoding = 'base64'; $mail->setFrom(SMTP_FROM_EMAIL, SMTP_FROM_NAME); $mail->addAddress($email, $username); $mail->isHTML(true); $verifyLink = SITE_URL . "/verify.php?token=" . $token; $mail->Subject = '验证邮箱 - ' . SITE_NAME; // 邮件正文 - 使用完整的HTML模板 $mail->Body = ' 邮箱验证

📧 ' . SITE_NAME . '

验证您的邮箱地址

您好,' . htmlspecialchars($username) . '

感谢您注册 ' . SITE_NAME . '!请点击下方按钮验证您的邮箱地址:

✅ 验证邮箱

如果按钮无法点击,请复制以下链接到浏览器打开:

⏰ 此链接 24 小时 内有效。
🔒 如果您没有注册 ' . SITE_NAME . ',请忽略并删除此邮件。

'; $mail->AltBody = "欢迎注册 " . SITE_NAME . "!\n\n" . "您好," . $username . ":\n\n" . "请访问以下链接验证您的邮箱地址:\n" . $verifyLink . "\n\n" . "此链接 24 小时内有效。\n\n" . "如果您没有注册 " . SITE_NAME . ",请忽略并删除此邮件。"; $mail->send(); return true; } catch (Exception $e) { error_log("邮件发送失败: " . $e->getMessage()); return false; } } /** * 验证邮箱 */ function verifyEmail($token) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT id, username, email FROM users WHERE verification_token = ? AND is_verified = 0"); $stmt->execute([$token]); $user = $stmt->fetch(); if (!$user) { // 检查token是否已被使用(已验证) $stmt = $pdo->prepare("SELECT id, username FROM users WHERE verification_token = ? AND is_verified = 1"); $stmt->execute([$token]); if ($stmt->fetch()) { return ['success' => false, 'message' => '此链接已被使用,请直接登录。']; } return ['success' => false, 'message' => '无效的验证链接,请检查是否正确复制。']; } // 更新用户状态 $stmt = $pdo->prepare("UPDATE users SET is_verified = 1, verification_token = NULL WHERE id = ?"); $stmt->execute([$user['id']]); return ['success' => true, 'message' => '邮箱验证成功!']; } // ===== 积分相关函数 ===== /** * 获取用户积分 */ function getUserCredits($userId) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT credits FROM users WHERE id = ?"); $stmt->execute([$userId]); $result = $stmt->fetch(); return $result ? $result['credits'] : 0; } /** * 扣减积分 */ function deductCredits($userId, $amount) { $pdo = getDB(); // 检查积分是否足够 $stmt = $pdo->prepare("SELECT credits FROM users WHERE id = ?"); $stmt->execute([$userId]); $user = $stmt->fetch(); if (!$user || $user['credits'] < $amount) { return ['success' => false, 'message' => '剩余的积分不足。']; } $stmt = $pdo->prepare("UPDATE users SET credits = credits - ? WHERE id = ?"); $stmt->execute([$amount, $userId]); return ['success' => true, 'message' => '扣减成功!']; } /** * 添加积分 */ function addCredits($userId, $amount, $reason = '') { $pdo = getDB(); $stmt = $pdo->prepare("UPDATE users SET credits = credits + ? WHERE id = ?"); $stmt->execute([$amount, $userId]); return ['success' => true]; } // ===== 签到相关函数 ===== /** * 签到 */ function signIn($userId) { $pdo = getDB(); $today = date('Y-m-d'); // 检查今天是否已签到 $stmt = $pdo->prepare("SELECT id FROM sign_in_logs WHERE user_id = ? AND sign_in_date = ?"); $stmt->execute([$userId, $today]); if ($stmt->fetch()) { return ['success' => false, 'message' => '今日已经签到过了。']; } $pdo->beginTransaction(); try { // 记录签到 $stmt = $pdo->prepare("INSERT INTO sign_in_logs (user_id, sign_in_date, credits_earned) VALUES (?, ?, ?)"); $stmt->execute([$userId, $today, SIGN_IN_CREDIT]); // 增加积分 $stmt = $pdo->prepare("UPDATE users SET credits = credits + ? WHERE id = ?"); $stmt->execute([SIGN_IN_CREDIT, $userId]); $pdo->commit(); return ['success' => true, 'message' => '签到成功!获得 ' . SIGN_IN_CREDIT . ' 积分', 'credits' => SIGN_IN_CREDIT]; } catch (Exception $e) { $pdo->rollBack(); return ['success' => false, 'message' => '签到失败: ' . $e->getMessage()]; } } /** * 检查今天是否已签到 */ function hasSignedInToday($userId) { $pdo = getDB(); $today = date('Y-m-d'); $stmt = $pdo->prepare("SELECT id FROM sign_in_logs WHERE user_id = ? AND sign_in_date = ?"); $stmt->execute([$userId, $today]); return (bool)$stmt->fetch(); } /** * 获取签到统计 */ function getSignInStats($userId) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT COUNT(*) as total, SUM(credits_earned) as total_credits FROM sign_in_logs WHERE user_id = ?"); $stmt->execute([$userId]); return $stmt->fetch(); } // ===== 域名相关函数 ===== /** * 获取用户域名数量 */ function getUserDomainCount($userId) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM domains WHERE user_id = ? AND status = 1"); $stmt->execute([$userId]); $result = $stmt->fetch(); return $result ? $result['count'] : 0; } /** * 检查用户是否可以添加域名 */ function canAddDomain($userId) { $count = getUserDomainCount($userId); if ($count >= MAX_DOMAINS_PER_USER) { return ['success' => false, 'message' => '您的域名数量已达到最大域名数量限制 (' . MAX_DOMAINS_PER_USER . '个)。']; } $credits = getUserCredits($userId); if ($credits < CREDIT_PER_DOMAIN) { return ['success' => false, 'message' => '积分不足,需要 ' . CREDIT_PER_DOMAIN . ' 积分']; } return ['success' => true]; } /** * 验证子域名是否合法 */ function validateSubdomain($subdomain) { // 不能为空 if (empty($subdomain)) { return ['success' => false, 'message' => '子域名不能为空。']; } // 长度至少3个字符 if (strlen($subdomain) < 3) { return ['success' => false, 'message' => '子域名长度至少 3 个字符。']; } // 不能超过 20 个字符 if (strlen($subdomain) > 20) { return ['success' => false, 'message' => '子域名长度不能超过 20 个字符。']; } // 只能包含小写字母、数字和连字符 if (!preg_match('/^[a-z0-9\-]+$/', $subdomain)) { return ['success' => false, 'message' => '子域名只能包含小写字母、数字和连字符。']; } // 不能以连字符开头或结尾 if (preg_match('/^-|-$/', $subdomain)) { return ['success' => false, 'message' => '子域名不能以连字符开头或结尾。']; } // 不能包含连续的连字符 if (strpos($subdomain, '--') !== false) { return ['success' => false, 'message' => '子域名不能包含连续的连字符。']; } // 不能是保留关键字 $reserved = ['www', 'mail', 'ftp', 'smtp', 'pop3', 'imap', 'admin', 'root', 'test', 'demo', 'api', 'dev', 'prod', 'stage', 'local', 'git', 'store', 'assets', 'image', 'bbs', 'help', 'forum']; if (in_array(strtolower($subdomain), $reserved)) { return ['success' => false, 'message' => '该子域名前缀已被禁用,请使用其他前缀。']; } return ['success' => true]; } /** * 验证记录值格式 */ function validateRecordValue($type, $value) { $type = strtoupper($type); switch ($type) { case 'A': if (!filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return ['success' => false, 'message' => 'A 记录需要有效的 IPv4 地址。']; } break; case 'AAAA': if (!filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return ['success' => false, 'message' => 'AAAA 记录需要有效的 IPv6 地址。']; } break; case 'CNAME': if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/i', $value)) { return ['success' => false, 'message' => 'CNAME 记录需要有效的域名。']; } break; case 'MX': if (!preg_match('/^\d+\s+([a-z0-9\-]+\.)+[a-z]{2,}$/i', $value)) { return ['success' => false, 'message' => 'MX 记录格式: 优先级 域名 (10 mail.example.com)。']; } break; case 'NS': if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/i', $value)) { return ['success' => false, 'message' => 'NS 记录需要有效的域名。']; } break; case 'TXT': if (strlen($value) > 255) { return ['success' => false, 'message' => 'TXT 记录值不能超过 255 个字符。']; } break; case 'SRV': // SRV记录格式: 优先级 权重 端口 目标域名 $parts = preg_split('/\s+/', trim($value)); if (count($parts) != 4) { return ['success' => false, 'message' => 'SRV 记录格式: 优先级 权重 端口 目标域名 (10 5 5060 sip.example.com)。']; } $priority = intval($parts[0]); $weight = intval($parts[1]); $port = intval($parts[2]); $target = strtolower(trim($parts[3])); if ($priority < 0 || $priority > 65535) { return ['success' => false, 'message' => '优先级必须是 0-65535 之间的数字。']; } if ($weight < 0 || $weight > 65535) { return ['success' => false, 'message' => '权重必须是 0-65535 之间的数字。']; } if ($port < 0 || $port > 65535) { return ['success' => false, 'message' => '端口必须是 0-65535 之间的数字。']; } if ($target !== '.' && !preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $target)) { return ['success' => false, 'message' => '无效的目标域名,请输入有效的域名或用 . 表示空。']; } break; default: return ['success' => false, 'message' => '不支持的记录类型。']; } return ['success' => true]; } /** * 获取记录类型列表 */ function getRecordTypes() { return [ 'A' => 'IPv4 地址', 'AAAA' => 'IPv6 地址', 'CNAME' => '域名别名', 'MX' => '邮件交换 (优先级 域名)', 'NS' => '域名服务器', 'TXT' => '文本记录', 'SRV' => '服务定位 (优先级 权重 端口 目标)' ]; } /** * 获取记录类型的示例值 */ function getRecordTypeExample($type) { $examples = [ 'A' => '192.168.1.1', 'AAAA' => '2001:db8::1', 'CNAME' => 'example.com', 'MX' => '10 mail.example.com', 'NS' => 'ns1.example.com', 'TXT' => 'v=spf1 include:example.com ~all', 'SRV' => '10 5 5060 sip.example.com' ]; return $examples[$type] ?? ''; } /** * 获取记录类型的格式说明 */ function getRecordTypeHint($type) { $hints = [ 'A' => 'IPv4 地址,例如: 192.168.1.1', 'AAAA' => 'IPv6 地址,例如: 2001:db8::1', 'CNAME' => '目标域名,例如: example.com', 'MX' => '格式: 优先级 域名,例如: 10 mail.example.com', 'NS' => '域名服务器,例如: ns1.example.com', 'TXT' => '任意文本,例如: v=spf1 include:example.com ~all', 'SRV' => '格式: 优先级 权重 端口 目标域名,例如: 10 5 5060 sip.example.com' ]; return $hints[$type] ?? ''; } /** * 添加域名(扣除积分) */ function addDomain($userId, $mainDomain, $subdomain, $type, $value) { // 检查权限 $canAdd = canAddDomain($userId); if (!$canAdd['success']) { return $canAdd; } // 验证子域名 $subdomainValidation = validateSubdomain($subdomain); if (!$subdomainValidation['success']) { return $subdomainValidation; } // 验证记录值 $valueValidation = validateRecordValue($type, $value); if (!$valueValidation['success']) { return $valueValidation; } // 检查主域名是否支持 $mainDomains = getMainDomains(); if (!in_array($mainDomain, $mainDomains)) { return ['success' => false, 'message' => '不支持的主域名。']; } $pdo = getDB(); // 检查数据库中是否已存在 $stmt = $pdo->prepare("SELECT id FROM domains WHERE main_domain = ? AND subdomain = ? AND status = 1"); $stmt->execute([$mainDomain, $subdomain]); if ($stmt->fetch()) { return ['success' => false, 'message' => '该域名已存在。']; } $pdo->beginTransaction(); try { // 先尝试添加到 Cloudflare $cfResult = addCloudflareRecord($mainDomain, $subdomain, $type, $value); if (!$cfResult['success']) { return ['success' => false, 'message' => '添加失败: ' . $cfResult['message']]; } // 扣减积分 $stmt = $pdo->prepare("UPDATE users SET credits = credits - ? WHERE id = ? AND credits >= ?"); $stmt->execute([CREDIT_PER_DOMAIN, $userId, CREDIT_PER_DOMAIN]); if ($stmt->rowCount() === 0) { throw new Exception('积分不足'); } // 添加域名到数据库 $stmt = $pdo->prepare("INSERT INTO domains (user_id, main_domain, subdomain, record_type, record_value) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$userId, $mainDomain, $subdomain, $type, $value]); $pdo->commit(); return ['success' => true, 'message' => '域名添加成功!消耗 ' . CREDIT_PER_DOMAIN . ' 积分']; } catch (Exception $e) { $pdo->rollBack(); // 如果数据库失败,尝试删除 Cloudflare 记录 deleteCloudflareRecord($mainDomain, $subdomain); return ['success' => false, 'message' => '添加失败: ' . $e->getMessage()]; } } /** * 获取用户的域名列表 */ function getUserDomains($userId) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT * FROM domains WHERE user_id = ? AND status = 1 ORDER BY created_at DESC"); $stmt->execute([$userId]); return $stmt->fetchAll(); } /** * 删除域名 */ function deleteDomain($userId, $domainId) { $pdo = getDB(); // 检查域名是否属于该用户 $stmt = $pdo->prepare("SELECT main_domain, subdomain FROM domains WHERE id = ? AND user_id = ? AND status = 1"); $stmt->execute([$domainId, $userId]); $domain = $stmt->fetch(); if (!$domain) { return ['success' => false, 'message' => '该域名不存在。']; } // 从 Cloudflare 删除 deleteCloudflareRecord($domain['main_domain'], $domain['subdomain']); // 软删除 $stmt = $pdo->prepare("UPDATE domains SET status = 0 WHERE id = ?"); $stmt->execute([$domainId]); return ['success' => true, 'message' => '域名删除成功。']; } // ===== Cloudflare API函数 ===== /** * 获取域名配置 */ function getDomainConfig($mainDomain) { global $DOMAINS_CONFIG; return $DOMAINS_CONFIG[$mainDomain] ?? null; } /** * 添加Cloudflare记录(最终修复SRV - 使用正确的字段名) */ function addCloudflareRecord($mainDomain, $subdomain, $type, $value) { $config = getDomainConfig($mainDomain); if (!$config) { error_log("Cloudflare 错误: 未找到域名配置 - {$mainDomain}"); return ['success' => false, 'message' => '未找到域名配置']; } $fullDomain = strtolower($subdomain) . '.' . strtolower($mainDomain); $type = strtoupper($type); // 检查记录是否已存在 $ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $config['zone_id'] . '/dns_records?name=' . urlencode($fullDomain) . '&type=' . $type); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . $config['api_token'] ]); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { $result = json_decode($response, true); if ($result && $result['success'] && !empty($result['result'])) { error_log("Cloudflare 记录已存在 - {$fullDomain}"); return ['success' => false, 'message' => '该记录已存在。']; } } // 构建请求数据 - 基础字段 $data = [ 'type' => $type, 'name' => $fullDomain, 'ttl' => 300, 'proxied' => false // 必须明确设置为 false ]; // 根据记录类型处理值 switch ($type) { case 'A': if (!filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return ['success' => false, 'message' => '无效的 IPv4 地址。']; } $data['content'] = $value; break; case 'AAAA': if (!filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return ['success' => false, 'message' => '无效的 IPv6 地址。']; } $data['content'] = $value; break; case 'CNAME': $value = strtolower(trim($value)); if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $value)) { return ['success' => false, 'message' => '无效的域名格式。']; } $data['content'] = $value; break; case 'MX': $parts = preg_split('/\s+/', trim($value)); if (count($parts) != 2) { return ['success' => false, 'message' => 'MX 记录格式: 优先级 域名 (例如: 10 mail.example.com)。']; } if (!is_numeric($parts[0]) || intval($parts[0]) < 0 || intval($parts[0]) > 65535) { return ['success' => false, 'message' => '优先级必须是 0-65535 之间的数字。']; } $domain = strtolower(trim($parts[1])); if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $domain)) { return ['success' => false, 'message' => '无效的邮件服务器域名。']; } $data['priority'] = intval($parts[0]); $data['content'] = $domain; break; case 'NS': $value = strtolower(trim($value)); if (!preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $value)) { return ['success' => false, 'message' => '无效的域名服务器地址。']; } $data['content'] = $value; break; case 'TXT': if (strlen($value) > 255) { return ['success' => false, 'message' => 'TXT 记录值不能超过 255 字符。']; } $data['content'] = $value; break; case 'SRV': // ===== SRV记录特殊处理 ===== $parts = preg_split('/\s+/', trim($value)); if (count($parts) != 4) { return ['success' => false, 'message' => 'SRV 记录格式: 优先级 权重 端口 目标域名 (例如: 10 5 5060 sip.example.com)']; } $priority = intval($parts[0]); $weight = intval($parts[1]); $port = intval($parts[2]); $target = strtolower(trim($parts[3])); // 验证数值范围 if ($priority < 0 || $priority > 65535) { return ['success' => false, 'message' => '优先级必须是 0-65535 之间的数字。']; } if ($weight < 0 || $weight > 65535) { return ['success' => false, 'message' => '权重必须是 0-65535 之间的数字。']; } if ($port < 0 || $port > 65535) { return ['success' => false, 'message' => '端口必须是 0-65535 之间的数字。']; } // 验证目标域名 if ($target !== '.' && !preg_match('/^([a-z0-9\-]+\.)+[a-z]{2,}$/', $target)) { return ['success' => false, 'message' => '无效的目标域名,请输入有效的域名或用 . 表示空。']; } // ===== 重要:SRV 记录使用不同的字段名 ===== // Cloudflare SRV API 要求使用这些字段名: // - priority, weight, port, target (不是 content!) $data['priority'] = (int)$priority; $data['weight'] = (int)$weight; $data['port'] = (int)$port; $data['target'] = (string)$target; // 关键:使用 target 而不是 content $data['proxied'] = false; error_log("SRV 数据: priority={$priority}, weight={$weight}, port={$port}, target={$target}"); break; default: return ['success' => false, 'message' => '不支持的记录类型']; } // 记录请求数据用于调试 error_log("Cloudflare 请求数据: " . json_encode($data)); $ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $config['zone_id'] . '/dns_records'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . $config['api_token'] ]); curl_setopt($ch, CURLOPT_TIMEOUT, 30); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); // 记录响应 error_log("Cloudflare 响应: HTTP {$httpCode}"); error_log("Cloudflare 响应内容: " . $response); if ($curlError) { error_log("Cloudflare CURL 错误: " . $curlError); return ['success' => false, 'message' => 'CURL 错误: ' . $curlError]; } if ($httpCode === 200) { $result = json_decode($response, true); if ($result && $result['success']) { error_log("Cloudflare 添加成功: {$fullDomain}"); return ['success' => true, 'message' => '添加成功']; } else { $errorMsg = '未知错误'; if (isset($result['errors']) && !empty($result['errors'])) { $errorMsg = $result['errors'][0]['message'] ?? 'API错误'; if (isset($result['errors'][0]['error_chain'])) { foreach ($result['errors'][0]['error_chain'] as $chain) { if (isset($chain['message'])) { $errorMsg .= ' - ' . $chain['message']; } } } } error_log("Cloudflare API 错误: " . $errorMsg); return ['success' => false, 'message' => 'Cloudflare 错误: ' . $errorMsg]; } } // 解析错误响应 $result = json_decode($response, true); if ($result && isset($result['errors'])) { $errorMsg = ''; foreach ($result['errors'] as $error) { $errorMsg .= $error['message'] . ' '; if (isset($error['error_chain'])) { foreach ($error['error_chain'] as $chain) { if (isset($chain['message'])) { $errorMsg .= '- ' . $chain['message'] . ' '; } } } } return ['success' => false, 'message' => '请求错误: ' . trim($errorMsg)]; } return ['success' => false, 'message' => "HTTP错误: {$httpCode} - " . substr($response, 0, 200)]; } /** * 删除Cloudflare记录(支持SRV) */ function deleteCloudflareRecord($mainDomain, $subdomain) { $config = getDomainConfig($mainDomain); if (!$config) return false; $fullDomain = $subdomain . '.' . $mainDomain; $zoneId = $config['zone_id']; $token = $config['api_token']; // 查询记录ID - 不限定类型,因为SRV记录可能用target字段 $ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $zoneId . '/dns_records?name=' . urlencode($fullDomain)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token ]); $response = curl_exec($ch); $result = json_decode($response, true); curl_close($ch); if (isset($result['result']) && !empty($result['result'])) { $recordId = $result['result'][0]['id']; $ch = curl_init('https://api.cloudflare.com/client/v4/zones/' . $zoneId . '/dns_records/' . $recordId); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token ]); curl_exec($ch); curl_close($ch); return true; } return false; } // ===== 其他辅助函数 ===== /** * 检查是否已登录 */ function isLoggedIn() { return isset($_SESSION['user_id']); } /** * 检查是否是管理员 */ function isAdmin() { return isset($_SESSION['is_admin']) && $_SESSION['is_admin'] == 1; } /** * 获取当前用户ID */ function getCurrentUserId() { return $_SESSION['user_id'] ?? null; } /** * 获取用户信息 */ function getUserInfo($userId) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT id, username, email, credits, invite_code, is_admin, created_at FROM users WHERE id = ?"); $stmt->execute([$userId]); return $stmt->fetch(); } /** * 获取邀请统计 */ function getInviteStats($userId) { $pdo = getDB(); $stmt = $pdo->prepare("SELECT COUNT(*) as count, SUM(credits_earned) as total_credits FROM invite_logs WHERE inviter_id = ?"); $stmt->execute([$userId]); return $stmt->fetch(); } /** * 获取主题 */ function getTheme() { $pdo = getDB(); try { $stmt = $pdo->prepare("SELECT setting_value FROM settings WHERE setting_key = 'theme'"); $stmt->execute(); $result = $stmt->fetch(); return $result ? $result['setting_value'] : 'dark'; } catch (PDOException $e) { return 'dark'; } } /** * 切换主题 */ function toggleTheme() { $pdo = getDB(); $current = getTheme(); $new = $current === 'dark' ? 'light' : 'dark'; $stmt = $pdo->prepare("UPDATE settings SET setting_value = ? WHERE setting_key = 'theme'"); $stmt->execute([$new]); return $new; } /** * 获取所有主域名列表 */ function getMainDomains() { global $DOMAINS_CONFIG; return array_keys($DOMAINS_CONFIG); } ?>