PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.0.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.0.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / api / class-ai-insights-endpoint.php

class-ai-insights-endpoint.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 2.0.0, at includes/api/class-ai-insights-endpoint.php

188 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AI Insights API endpoints.
4 *
5 * Admin REST surface for the AI Insights screen (#248 P1 trio):
6 *
7 * GET /ai-insights/traffic — AI referral/crawler dashboard summary
8 * GET /ai-insights/auto-ai — auto-optimization settings + last run
9 * POST /ai-insights/auto-ai — save auto-optimization settings
10 *
11 * Brand visibility used to live here too (`/ai-insights/brand*`). Those routes
12 * were removed in 1.30.0: the v2 rebuild moved the feature to
13 * `/thinkrank/v1/brand-visibility/*` with a queued runner, and the v1 routes
14 * outlived their only UI consumer while still being able to start a
15 * synchronous batch of paid AI probes in a single request (#301).
16 *
17 * @package ThinkRank
18 * @subpackage API
19 * @since 1.27.0
20 */
21
22 declare(strict_types=1);
23
24 namespace ThinkRank\API;
25
26 use ThinkRank\Core\Settings;
27 use ThinkRank\SEO\Ai_Traffic_Tracker;
28 use ThinkRank\SEO\Auto_Ai_Optimizer;
29 use WP_REST_Controller;
30 use WP_REST_Request;
31 use WP_REST_Response;
32
33 // Prevent direct access
34 if (!defined('ABSPATH')) {
35 exit;
36 }
37
38 /**
39 * REST controller for AI traffic and auto AI settings.
40 */
41 class Ai_Insights_Endpoint extends WP_REST_Controller {
42
43 /**
44 * API namespace
45 *
46 * @var string
47 */
48 protected $namespace = 'thinkrank/v1';
49
50 /**
51 * API resource base
52 *
53 * @var string
54 */
55 protected $rest_base = 'ai-insights';
56
57 /**
58 * Register routes.
59 *
60 * @return void
61 */
62 public function register_routes(): void {
63 register_rest_route($this->namespace, '/' . $this->rest_base . '/traffic', [
64 'methods' => 'GET',
65 'callback' => [$this, 'get_traffic'],
66 'permission_callback' => [$this, 'check_admin_permissions'],
67 'args' => [
68 'days' => [
69 'required' => false,
70 'type' => 'integer',
71 'default' => 30,
72 'minimum' => 1,
73 'maximum' => 180,
74 ],
75 ],
76 ]);
77
78 register_rest_route($this->namespace, '/' . $this->rest_base . '/auto-ai', [
79 [
80 'methods' => 'GET',
81 'callback' => [$this, 'get_auto_ai'],
82 'permission_callback' => [$this, 'check_admin_permissions'],
83 ],
84 [
85 'methods' => 'POST',
86 'callback' => [$this, 'save_auto_ai'],
87 'permission_callback' => [$this, 'check_admin_permissions'],
88 'args' => [
89 'enabled' => [
90 'required' => false,
91 'type' => 'boolean',
92 ],
93 'post_types' => [
94 'required' => false,
95 'type' => 'array',
96 'items' => ['type' => 'string'],
97 'sanitize_callback' => static function ($value) {
98 if (!is_array($value)) {
99 return [];
100 }
101 // Only real, public post types survive.
102 return array_values(array_filter(
103 array_map('sanitize_key', $value),
104 static fn($t) => post_type_exists($t)
105 ));
106 },
107 ],
108 ],
109 ],
110 ]);
111 }
112
113 /**
114 * Admin permission gate (matches the other admin-only endpoints).
115 *
116 * @return bool
117 */
118 public function check_admin_permissions(): bool {
119 return current_user_can('manage_options');
120 }
121
122 /**
123 * AI traffic dashboard summary.
124 *
125 * @param WP_REST_Request $request Request.
126 * @return WP_REST_Response
127 */
128 public function get_traffic(WP_REST_Request $request): WP_REST_Response {
129 $tracker = new Ai_Traffic_Tracker();
130
131 return new WP_REST_Response([
132 'success' => true,
133 'data' => $tracker->summary((int) $request->get_param('days')),
134 ], 200);
135 }
136
137 /**
138 * Auto AI settings + last run outcome.
139 *
140 * @return WP_REST_Response
141 */
142 public function get_auto_ai(): WP_REST_Response {
143 $settings = Settings::instance();
144
145 return new WP_REST_Response([
146 'success' => true,
147 'data' => [
148 'enabled' => (bool) $settings->get('auto_ai_meta_enabled', false),
149 'post_types' => (array) $settings->get('auto_ai_meta_post_types', ['post']),
150 'last_run' => get_option(Auto_Ai_Optimizer::LAST_RUN_OPTION, null),
151 // Pro-gated: the UI locks the toggle and shows the upsell.
152 'available' => \ThinkRank\Core\Plan_Config::can('auto_ai_meta', 'ai_visibility'),
153 ],
154 ], 200);
155 }
156
157 /**
158 * Save auto AI settings.
159 *
160 * @param WP_REST_Request $request Request.
161 * @return WP_REST_Response
162 */
163 public function save_auto_ai(WP_REST_Request $request): WP_REST_Response {
164 $settings = Settings::instance();
165
166 if (null !== $request->get_param('enabled')) {
167 $enabled = rest_sanitize_boolean($request->get_param('enabled'));
168
169 // Pro capability. Turning it OFF always works — a lapsed licence
170 // must never trap the setting in the on position.
171 if ($enabled && !\ThinkRank\Core\Plan_Config::can('auto_ai_meta', 'ai_visibility')) {
172 return new WP_REST_Response([
173 'success' => false,
174 'message' => __('Automatic AI metadata is a ThinkRank Pro feature.', 'thinkrank'),
175 'data' => ['requires_pro' => true],
176 ], 403);
177 }
178
179 $settings->set('auto_ai_meta_enabled', $enabled);
180 }
181 if (null !== $request->get_param('post_types')) {
182 $settings->set('auto_ai_meta_post_types', (array) $request->get_param('post_types'));
183 }
184
185 return $this->get_auto_ai();
186 }
187 }
188