array( 'Authorization' => 'Bearer ' . $api_key, 'Accept' => 'application/json', ), 'timeout' => 20, ) ); if ( is_wp_error( $response ) ) { return new WP_Error( 'aeocas_api_error', $response->get_error_message() ); } $status = wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( 404 === $status ) { return new WP_Error( 'aeocas_no_audit', __( 'No audit found for this site. Request an audit at aeocontent.ai.', 'aeo-content-ai-studio' ) ); } if ( 401 === $status || 403 === $status ) { return new WP_Error( 'aeocas_auth_error', __( 'API key is invalid or does not have read permission.', 'aeo-content-ai-studio' ) ); } if ( 200 !== $status || empty( $body['data'] ) ) { $message = isset( $body['error']['message'] ) ? $body['error']['message'] : __( 'Unexpected API response.', 'aeo-content-ai-studio' ); return new WP_Error( 'aeocas_api_error', $message ); } // The API returns an array when no engine param, or a single object with engine param. // We always use the first audit (or the single object). $audit = is_array( $body['data'] ) && isset( $body['data'][0] ) ? $body['data'][0] : $body['data']; // Cache the result. set_transient( $transient_key, $audit, self::CACHE_TTL ); return $audit; } /** * Clear cached audit data. */ public static function clear_cache() { $slug = self::get_site_slug(); if ( $slug ) { delete_transient( self::TRANSIENT_PREFIX . $slug ); } } /** * AJAX handler for fetching audit data. */ public static function ajax_get_audit() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'aeo-content-ai-studio' ) ), 403 ); } check_ajax_referer( 'aeocas_audit_nonce', 'nonce' ); $force = ! empty( $_POST['refresh'] ) && sanitize_text_field( wp_unslash( $_POST['refresh'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above. $audit = self::get_audit( $force ); if ( is_wp_error( $audit ) ) { wp_send_json_error( array( 'message' => $audit->get_error_message() ) ); } wp_send_json_success( $audit ); } /** * Trigger a re-audit on the platform. * * @return array|WP_Error Response data or error. */ public static function trigger_reaudit() { $api_key = get_option( 'aeocas_site_token', '' ); if ( empty( $api_key ) ) { return new WP_Error( 'aeocas_no_key', __( 'API key is not configured.', 'aeo-content-ai-studio' ) ); } $slug = self::get_site_slug(); if ( empty( $slug ) ) { return new WP_Error( 'aeocas_no_slug', __( 'Could not determine site slug.', 'aeo-content-ai-studio' ) ); } $url = trailingslashit( AEOCAS_PLATFORM_URL ) . 'api/v1/audits/' . $slug . '/reaudit'; $response = wp_remote_post( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json', ), 'body' => '{}', 'timeout' => 20, ) ); if ( is_wp_error( $response ) ) { return new WP_Error( 'aeocas_api_error', $response->get_error_message() ); } $status = wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( $status >= 400 ) { $message = isset( $body['error']['message'] ) ? $body['error']['message'] : ( isset( $body['message'] ) ? $body['message'] : __( 'Failed to trigger re-audit.', 'aeo-content-ai-studio' ) ); return new WP_Error( 'aeocas_reaudit_error', $message ); } // Clear cached audit so next load fetches fresh data. self::clear_cache(); AEOCAS_Activity_Log::log( 'reaudit', 'success', array( 'message' => 'Re-audit triggered.', 'slug' => $slug ) ); return $body; } /** * Get audit job status from platform. * * @return array|WP_Error Status data or error. */ public static function get_audit_status() { $api_key = get_option( 'aeocas_site_token', '' ); if ( empty( $api_key ) ) { return new WP_Error( 'aeocas_no_key', __( 'API key is not configured.', 'aeo-content-ai-studio' ) ); } $slug = self::get_site_slug(); if ( empty( $slug ) ) { return new WP_Error( 'aeocas_no_slug', __( 'Could not determine site slug.', 'aeo-content-ai-studio' ) ); } $url = trailingslashit( AEOCAS_PLATFORM_URL ) . 'api/v1/audits/' . $slug . '/status'; $response = wp_remote_get( $url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Accept' => 'application/json', ), 'timeout' => 15, ) ); if ( is_wp_error( $response ) ) { return new WP_Error( 'aeocas_api_error', $response->get_error_message() ); } $body = json_decode( wp_remote_retrieve_body( $response ), true ); return $body; } /** * AJAX handler for triggering re-audit. */ public static function ajax_reaudit() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'aeo-content-ai-studio' ) ), 403 ); } check_ajax_referer( 'aeocas_audit_nonce', 'nonce' ); $result = self::trigger_reaudit(); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } wp_send_json_success( $result ); } /** * AJAX handler for polling audit status. */ public static function ajax_audit_status() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'aeo-content-ai-studio' ) ), 403 ); } check_ajax_referer( 'aeocas_audit_nonce', 'nonce' ); $result = self::get_audit_status(); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } wp_send_json_success( $result ); } /** * Register AJAX hooks. */ public static function register_ajax() { add_action( 'wp_ajax_aeocas_get_audit', array( __CLASS__, 'ajax_get_audit' ) ); add_action( 'wp_ajax_aeocas_reaudit', array( __CLASS__, 'ajax_reaudit' ) ); add_action( 'wp_ajax_aeocas_audit_status', array( __CLASS__, 'ajax_audit_status' ) ); } }