'local', ]); return [ 'ok' => true, 'id' => $id, 'hostname' => $host, 'cf_status' => 'local', 'nameservers' => [], ]; } $client = self::newClient(); $zone = $client->createZone($host); if (!$zone['ok']) { DomainRepository::updateCloudflareMeta($pdo, $id, [ 'cf_status' => 'error', 'cf_error' => $zone['error'] ?? '创建 Zone 失败', ]); return [ 'ok' => false, 'error' => $zone['error'] ?? 'Cloudflare 创建 Zone 失败', 'id' => $id, 'hostname' => $host, ]; } $result = $zone['result']; $ns = is_array($result['name_servers'] ?? null) ? $result['name_servers'] : []; DomainRepository::updateCloudflareMeta($pdo, $id, [ 'cf_zone_id' => (string) ($result['id'] ?? ''), 'cf_nameservers' => json_encode($ns, JSON_UNESCAPED_UNICODE), 'cf_status' => 'pending_ns', 'cf_error' => '', 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return [ 'ok' => true, 'id' => $id, 'hostname' => $host, 'cf_status' => 'pending_ns', 'nameservers' => $ns, ]; } /** * 手动检测 NS 是否生效,生效后配置 A 记录与 SSL * * @return array{ok:bool,status?:string,message?:string,nameservers?:array} */ public static function checkAndProvision(PDO $pdo, $domainId) { $row = DomainRepository::findById($pdo, $domainId); if (!$row) { return ['ok' => false, 'message' => '域名不存在']; } if (!CloudflareConfig::isEnabled()) { return ['ok' => false, 'message' => '未配置 Cloudflare API,无法检测']; } $client = self::newClient(); $zoneId = trim((string) ($row['cf_zone_id'] ?? '')); if ($zoneId === '') { $zone = $client->createZone($row['hostname']); if (!$zone['ok']) { DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'error', 'cf_error' => $zone['error'] ?? '补创建 Zone 失败', 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return ['ok' => false, 'message' => $zone['error'] ?? '补创建 Zone 失败']; } $result = $zone['result']; $zoneId = (string) ($result['id'] ?? ''); $ns = is_array($result['name_servers'] ?? null) ? $result['name_servers'] : []; DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_zone_id' => $zoneId, 'cf_nameservers' => json_encode($ns, JSON_UNESCAPED_UNICODE), 'cf_status' => 'pending_ns', 'cf_error' => '', 'cf_checked_at' => date('Y-m-d H:i:s'), ]); $row = DomainRepository::findById($pdo, $domainId); } $zoneRes = $client->getZone($zoneId); if (!$zoneRes['ok']) { DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'error', 'cf_error' => $zoneRes['error'] ?? '读取 Zone 失败', 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return ['ok' => false, 'message' => $zoneRes['error'] ?? '读取 Zone 失败']; } $zoneData = $zoneRes['result']; $status = strtolower((string) ($zoneData['status'] ?? 'pending')); $ns = CloudflareConfig::parseNameservers($row['cf_nameservers'] ?? ''); if (empty($ns) && is_array($zoneData['name_servers'] ?? null)) { $ns = $zoneData['name_servers']; DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_nameservers' => json_encode($ns, JSON_UNESCAPED_UNICODE), ]); } if ($status !== 'active') { DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'pending_ns', 'cf_checked_at' => date('Y-m-d H:i:s'), 'cf_error' => '', ]); return [ 'ok' => true, 'status' => 'pending_ns', 'message' => 'Nameserver 尚未生效,请到注册商修改为:' . implode('、', $ns), 'nameservers' => $ns, ]; } $serverIp = CloudflareConfig::serverIp(); if ($serverIp === '') { DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'error', 'cf_error' => 'SERVER_IP 未配置,无法添加 DNS 记录', 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return ['ok' => false, 'message' => 'SERVER_IP 未配置,请在 cong.php 中填写服务器公网 IP']; } DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'provisioning', 'cf_error' => '', ]); $host = $row['hostname']; foreach ([$host, 'www.' . $host] as $fqdn) { $dns = $client->upsertARecord($zoneId, $fqdn, $serverIp, true); if (!$dns['ok']) { DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'error', 'cf_error' => 'DNS 记录失败(' . $fqdn . '): ' . ($dns['error'] ?? ''), 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return ['ok' => false, 'message' => $dns['error'] ?? 'DNS 配置失败']; } } $ssl = $client->setSslFlexible($zoneId); if (!$ssl['ok']) { DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'error', 'cf_error' => 'SSL Flexible 失败: ' . ($ssl['error'] ?? ''), 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return ['ok' => false, 'message' => $ssl['error'] ?? 'SSL 配置失败']; } $https = $client->setAlwaysHttps($zoneId); if (!$https['ok']) { DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'error', 'cf_error' => 'Always HTTPS 失败: ' . ($https['error'] ?? ''), 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return ['ok' => false, 'message' => $https['error'] ?? 'Always HTTPS 配置失败']; } DomainRepository::updateCloudflareMeta($pdo, $domainId, [ 'cf_status' => 'ready', 'cf_error' => '', 'cf_checked_at' => date('Y-m-d H:i:s'), ]); return [ 'ok' => true, 'status' => 'ready', 'message' => '域名已生效:A 记录(@/www) 已指向 ' . $serverIp . ',已开启 Flexible SSL 与 Always HTTPS', 'nameservers' => $ns, ]; } /** * 删除本地记录并尝试删除 Cloudflare Zone * * @return array{ok:bool,message?:string,warning?:string} */ public static function deleteWithCloudflare(PDO $pdo, $domainId) { $row = DomainRepository::findById($pdo, $domainId); if (!$row) { return ['ok' => false, 'message' => '域名不存在']; } $warning = ''; $zoneId = trim((string) ($row['cf_zone_id'] ?? '')); if ($zoneId !== '' && CloudflareConfig::isEnabled()) { $del = self::newClient()->deleteZone($zoneId); if (!$del['ok']) { $warning = 'Cloudflare Zone 删除失败:' . ($del['error'] ?? '未知错误'); } } DomainRepository::deleteById($pdo, $domainId); return [ 'ok' => true, 'message' => '域名已删除', 'warning' => $warning, ]; } }