base = rtrim(AI_SEO_CRO_Config::get('scan_api_base') ?: '', '/'); } private function default_headers() { return [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', ]; } private function get_seokey() { return function_exists('ai_seo_cro_get_seokey') ? ai_seo_cro_get_seokey() : ''; } private function request($method, $path, $body = null, $args = []) { if (get_option('ai_seo_cro_scan_service_enabled', '0') !== '1') { return ['success' => false, 'message' => 'Enable the 360 Digital Marketing scan service in 360 SEO > Site Audit before using remote scans and reports.']; } $url = $this->base . '/' . ltrim($path, '/'); $options = array_merge([ 'headers' => $this->default_headers(), 'timeout' => 20, 'redirection' => 0, ], $args); if ($body !== null) { $options['body'] = is_string($body) ? $body : wp_json_encode($body); } if (stripos($method, 'post') === 0) { $resp = wp_remote_post($url, $options); } else { $resp = wp_remote_request($url, array_merge($options, ['method' => strtoupper($method)])); } if (is_wp_error($resp)) { return ['success' => false, 'message' => $resp->get_error_message()]; } $code = wp_remote_retrieve_response_code($resp); $body = wp_remote_retrieve_body($resp); $data = null; $content_type = wp_remote_retrieve_header($resp, 'content-type'); if ($content_type && strpos($content_type, 'application/json') !== false) { $data = json_decode($body, true); } return [ 'success' => $code >= 200 && $code < 300, 'status' => $code, 'body' => $body, 'data' => $data, 'content_type' => $content_type, ]; } public static function include_query_parameters_default() { $preferences = (array) get_option('ai_seo_cro_setup', []); return ($preferences['include_query_parameters'] ?? '0') === '1'; } public function create_scan($projectName, $startUrl, $maxPages = 100, $maxDepth = 6, $includeQueryParameters = null) { $seokey = $this->get_seokey(); $body = [ 'projectName' => $projectName, 'startUrl' => $startUrl, 'maxPages' => (int) $maxPages, 'maxDepth' => (int) $maxDepth, 'includeQueryParameters' => $includeQueryParameters === null ? self::include_query_parameters_default() : (bool) $includeQueryParameters, 'excludePatterns' => function_exists('ai_seo_cro_get_excluded_crawl_patterns') ? ai_seo_cro_get_excluded_crawl_patterns() : [], 'excludeUrls' => function_exists('ai_seo_cro_get_excluded_crawl_patterns') ? ai_seo_cro_get_excluded_crawl_patterns() : [], 'respectRobotsTxt' => true, ]; if ($seokey !== '') { $body['key'] = $seokey; } return $this->request('POST', '/v1/scans', $body); } public function get_scan($scanId) { return $this->request('GET', '/v1/scans/' . rawurlencode($scanId)); } public function delete_scan($scanId) { $headers = $this->default_headers(); unset($headers['Content-Type']); $seokey = $this->get_seokey(); if ($seokey !== '') { $headers['x-seo-key'] = $seokey; } return $this->request( 'DELETE', '/v1/scans/' . rawurlencode($scanId), null, ['headers' => $headers] ); } public function get_report($scanId) { return $this->request('GET', '/v1/scans/' . rawurlencode($scanId) . '/report'); } public function export_scan($scanId, $format = 'html') { $format = sanitize_text_field($format); return $this->request('GET', '/v1/scans/' . rawurlencode($scanId) . '/export?format=' . rawurlencode($format), null, ['headers' => $this->default_headers(), 'timeout' => 60]); } public function list_scans($params = []) { $seokey = $this->get_seokey(); if ($seokey !== '' && empty($params['key'])) { $params['key'] = $seokey; } $query = http_build_query($params); $path = '/v1/scans' . ($query ? '?' . $query : ''); return $this->request('GET', $path); } }