/
home
/
altere25
/
.trash
/
wp-to-sanity-companion
/
/home/altere25/.trash/wp-to-sanity-companion
mkdir
upload
Name
Size
Mode
Actions
assets\css\admin.css
7611
0644
edit
dl
rm
assets\js\admin.js
8534
0644
edit
dl
rm
assets\js\claude-connect.js
6502
0644
edit
dl
rm
includes\class-ad-admin.php
26374
0644
edit
dl
rm
includes\class-ad-ai-client.php
8153
0644
edit
dl
rm
includes\class-ad-auth.php
9189
0644
edit
dl
rm
includes\class-ad-cli.php
8669
0644
edit
dl
rm
includes\class-ad-extractor.php
12210
0644
edit
dl
rm
includes\class-ad-mcp.php
6429
0644
edit
dl
rm
includes\class-ad-plugin-core.php
7947
0644
edit
dl
rm
includes\class-ad-progress.php
8022
0644
edit
dl
rm
includes\class-ad-rest.php
31008
0644
edit
dl
rm
includes\class-ad-uninstall.php
1924
0644
edit
dl
rm
includes\class-ad-webhook.php
14854
0644
edit
dl
rm
package.json
220
0644
edit
dl
rm
templates\admin\claude-connect.php
5152
0644
edit
dl
rm
templates\admin\dashboard.php
10457
0644
edit
dl
rm
templates\admin\export.php
16570
0644
edit
dl
rm
templates\admin\log.php
10849
0644
edit
dl
rm
templates\admin\onboarding-v2.php
3827
0644
edit
dl
rm
templates\admin\onboarding.php
10714
0644
edit
dl
rm
templates\admin\progress.php
2443
0644
edit
dl
rm
templates\admin\schema-preview.php
5213
0644
edit
dl
rm
uninstall.php
365
0644
edit
dl
rm
wp-to-sanity-companion.php
4171
0644
edit
dl
rm
Edit:
/home/altere25/.trash/wp-to-sanity-companion/includes\class-ad-rest.php
(31008B)
<?php /** * Altered Digital Migration — REST API * * Registers two parallel namespaces: * - `wp-to-sanity/v1` (back-compat — preserved from v1) * - `altered-digital/v1` (v2 — adds analyze-data, quote, approve) * * Routes: * POST /export — single-post sync (fires webhook) * POST /export/bulk — bulk sync (batched WP_Query for "all") * POST /export/full — full-site queue * GET /status — aggregate stats (admin-locked in v2) * GET /status/post/{id} — per-post last sync (reconciled with D1) * GET /acf-fields — ACF field inventory * POST /connect — Worker /connect handshake (single-tenant) * POST /connect/v2 — Worker /v2/connect handshake (multi-tenant) * GET /connect/test — Worker /health probe * GET /analyze-data — aggregated full-site analysis payload * POST /auth/login — Supabase login (via AD_Auth) * POST /auth/signup — Supabase signup (via AD_Auth) * POST /auth/logout — clear local session * POST /analyze — trigger AI service /analyze * GET /analyze/{id}/status — poll AI service * POST /quote — request AI service quote * POST /approve — AI service approve (capture final 50%) * GET /progress — Worker /v2/migrations (via AD_Progress) * GET /progress/{post_id} — Worker /v2/migrations/{id} * POST /mcp/token — store MCP-scoped Sanity token (via AD_MCP) * GET /mcp/config — fetch existing MCP config (via AD_MCP) * POST /mcp/revoke — revoke the Claude connection (via AD_MCP) * * @package AlteredDigitalMigration */ if (!defined('ABSPATH')) { exit; } class AD_REST { /** @var AD_Plugin_Core */ private $core; /** @var AD_Webhook */ private $webhook; /** @var AD_Extractor */ private $extractor; /** @var AD_Progress */ private $progress; /** @var AD_AI_Client */ private $ai; /** @var AD_Auth */ private $auth; /** @var AD_MCP */ private $mcp; public function __construct( AD_Plugin_Core $core, AD_Webhook $webhook, AD_Extractor $extractor, AD_Progress $progress, AD_AI_Client $ai, AD_Auth $auth, AD_MCP $mcp ) { $this->core = $core; $this->webhook = $webhook; $this->extractor = $extractor; $this->progress = $progress; $this->ai = $ai; $this->auth = $auth; $this->mcp = $mcp; } public function register_hooks(): void { add_action('rest_api_init', [$this, 'register_routes']); } public function register_routes(): void { $this->register_v1_routes(); $this->register_v2_routes(); } // ----------------------------------------------------------------- // v1 back-compat namespace (wp-to-sanity/v1) // ----------------------------------------------------------------- private function register_v1_routes(): void { $ns = 'wp-to-sanity/v1'; register_rest_route($ns, '/export', [ 'methods' => 'POST', 'callback' => [$this, 'handle_export'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/export/bulk', [ 'methods' => 'POST', 'callback' => [$this, 'handle_bulk_export'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/export/full', [ 'methods' => 'POST', 'callback' => [$this, 'handle_full_export'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/status', [ 'methods' => 'GET', 'callback' => [$this, 'handle_status'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/status/post/(?P<id>\d+)', [ 'methods' => 'GET', 'callback' => [$this, 'handle_post_status'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/acf-fields', [ 'methods' => 'GET', 'callback' => [$this, 'handle_acf_fields'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/connect', [ 'methods' => 'POST', 'callback' => [$this, 'handle_connect'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/connect/test', [ 'methods' => 'GET', 'callback' => [$this, 'handle_connect_test'], 'permission_callback' => [$this, 'verify_admin_access'], ]); } // ----------------------------------------------------------------- // v2 namespace (altered-digital/v1) // ----------------------------------------------------------------- private function register_v2_routes(): void { $ns = 'altered-digital/v1'; // Mirror of v1 (same handlers, just re-exposed on the new ns). register_rest_route($ns, '/export', [ 'methods' => 'POST', 'callback' => [$this, 'handle_export'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/export/bulk', [ 'methods' => 'POST', 'callback' => [$this, 'handle_bulk_export'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/status', [ 'methods' => 'GET', 'callback' => [$this, 'handle_status'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/status/post/(?P<id>\d+)', [ 'methods' => 'GET', 'callback' => [$this, 'handle_post_status'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/connect', [ 'methods' => 'POST', 'callback' => [$this, 'handle_connect'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/connect/v2', [ 'methods' => 'POST', 'callback' => [$this, 'handle_connect_v2'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/connect/test', [ 'methods' => 'GET', 'callback' => [$this, 'handle_connect_test'], 'permission_callback' => [$this, 'verify_admin_access'], ]); // v2-only routes. register_rest_route($ns, '/analyze-data', [ 'methods' => 'GET', 'callback' => [$this, 'handle_analyze_data'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/progress', [ 'methods' => 'GET', 'callback' => [$this, 'handle_progress'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/progress/(?P<post_id>\d+)', [ 'methods' => 'GET', 'callback' => [$this, 'handle_progress_post'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/auth/login', [ 'methods' => 'POST', 'callback' => [$this, 'handle_auth_login'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/auth/signup', [ 'methods' => 'POST', 'callback' => [$this, 'handle_auth_signup'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/auth/logout', [ 'methods' => 'POST', 'callback' => [$this, 'handle_auth_logout'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/analyze', [ 'methods' => 'POST', 'callback' => [$this, 'handle_analyze'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/analyze/(?P<id>[a-zA-Z0-9_-]+)/status', [ 'methods' => 'GET', 'callback' => [$this, 'handle_analyze_status'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/quote', [ 'methods' => 'POST', 'callback' => [$this, 'handle_quote'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/checkout', [ 'methods' => 'POST', 'callback' => [$this, 'handle_checkout'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/approve', [ 'methods' => 'POST', 'callback' => [$this, 'handle_approve'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/schema-generate', [ 'methods' => 'POST', 'callback' => [$this, 'handle_schema_generate'], 'permission_callback' => [$this, 'verify_admin_access'], ]); // MCP hand-off (Phase D5). register_rest_route($ns, '/mcp/token', [ 'methods' => 'POST', 'callback' => [$this, 'handle_mcp_token'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/mcp/config', [ 'methods' => 'GET', 'callback' => [$this, 'handle_mcp_config'], 'permission_callback' => [$this, 'verify_admin_access'], ]); register_rest_route($ns, '/mcp/revoke', [ 'methods' => 'POST', 'callback' => [$this, 'handle_mcp_revoke'], 'permission_callback' => [$this, 'verify_admin_access'], ]); } // ----------------------------------------------------------------- // Permission gate // ----------------------------------------------------------------- public function verify_admin_access(WP_REST_Request $request) { if (current_user_can('manage_options')) { return true; } return new WP_Error( 'ad_rest_forbidden', __('You do not have permission to perform this action.', 'altered-digital'), ['status' => rest_authorization_required_code()] ); } // ----------------------------------------------------------------- // Export handlers // ----------------------------------------------------------------- public function handle_export(WP_REST_Request $request): WP_REST_Response { $post_id = (int) $request->get_param('post_id'); if (empty($post_id)) { return new WP_REST_Response(['success' => false, 'error' => 'post_id is required'], 400); } $post = get_post($post_id); if (!$post) { return new WP_REST_Response(['success' => false, 'error' => 'Post not found'], 404); } $this->progress->log_pending($post_id, 'export'); $this->webhook->trigger_export($post); return new WP_REST_Response(['success' => true, 'data' => ['post_id' => $post_id]], 200); } public function handle_bulk_export(WP_REST_Request $request): WP_REST_Response { $post_ids = $request->get_param('post_ids'); $post_type = $request->get_param('post_type') ?? 'post'; $export_all = $request->get_param('export_all') === true || $request->get_param('export_all') === 'true' || $request->get_param('export_all') === 1; if ($export_all && empty($post_ids)) { $batch_size = (int) ($this->core->setting('batch_size', 100)); $query = new WP_Query([ 'post_type' => $post_type, 'post_status' => 'publish', 'posts_per_page' => $batch_size, 'fields' => 'ids', 'no_found_rows' => true, ]); $post_ids = $query->posts; } if (empty($post_ids) || !is_array($post_ids)) { return new WP_REST_Response(['success' => false, 'error' => 'post_ids array is required'], 400); } $queued = 0; foreach ($post_ids as $id) { $post = get_post((int) $id); if ($post && $post->post_type === $post_type) { $this->progress->log_pending((int) $id, 'export'); $this->webhook->trigger_export($post); $queued++; } } return new WP_REST_Response([ 'success' => true, 'data' => ['queued' => $queued, 'count' => $queued], ], 200); } public function handle_full_export(WP_REST_Request $request): WP_REST_Response { $post_types = $this->core->settings()['post_types'] ?? ['post', 'page']; $batch_size = (int) ($this->core->setting('batch_size', 100)); $total = 0; foreach ($post_types as $pt) { $offset = 0; while (true) { $ids = get_posts([ 'post_type' => $pt, 'post_status' => 'publish', 'posts_per_page' => $batch_size, 'offset' => $offset, 'fields' => 'ids', 'no_found_rows' => true, 'orderby' => 'ID', 'order' => 'ASC', ]); if (empty($ids)) { break; } foreach ($ids as $pid) { $post = get_post((int) $pid); if ($post) { $this->progress->log_pending((int) $pid, 'publish'); $this->webhook->trigger_export($post); $total++; } } $offset += $batch_size; } } return new WP_REST_Response([ 'success' => true, 'data' => ['job_id' => 'full-' . wp_generate_uuid4(), 'queued' => $total], ], 200); } // ----------------------------------------------------------------- // Status / progress // ----------------------------------------------------------------- public function handle_status(WP_REST_Request $request): WP_REST_Response { // Local log stats (offline-safe). The dashboard polls /progress // for the D1 view; this endpoint covers the meta-box + fallback. $stats = $this->progress->local_stats(); return new WP_REST_Response([ 'success' => true, 'data' => [ 'migration_stats' => $stats, 'plugin_version' => AD_VERSION, ], ], 200); } public function handle_post_status(WP_REST_Request $request): WP_REST_Response { $post_id = (int) $request->get_param('id'); if (empty($post_id)) { return new WP_REST_Response(['success' => false, 'error' => 'Invalid post ID'], 400); } // Reconcile with D1 first, then read local. $this->progress->fetch_post_history($post_id); $last = $this->progress->get_local_last_sync($post_id); return new WP_REST_Response([ 'success' => true, 'data' => ['last_sync' => $last], ], 200); } public function handle_progress(WP_REST_Request $request): WP_REST_Response { $result = $this->progress->fetch_progress(); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_progress_post(WP_REST_Request $request): WP_REST_Response { $post_id = (int) $request->get_param('post_id'); $result = $this->progress->fetch_post_history($post_id); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_acf_fields(WP_REST_Request $request): WP_REST_Response { if (!function_exists('acf')) { return new WP_REST_Response(['success' => false, 'error' => 'ACF plugin is not active'], 400); } $out = []; foreach (acf_get_field_groups() as $group) { $out[] = [ 'group' => $group, 'fields' => function_exists('acf_get_fields') ? acf_get_fields($group) : [], ]; } return new WP_REST_Response(['success' => true, 'data' => $out], 200); } // ----------------------------------------------------------------- // Analysis (AI service) + builder inventory // ----------------------------------------------------------------- public function handle_analyze_data(WP_REST_Request $request): WP_REST_Response { $payload = $this->extractor->build_analysis_payload(); return new WP_REST_Response(['success' => true, 'data' => $payload], 200); } public function handle_analyze(WP_REST_Request $request): WP_REST_Response { $jwt = $this->auth->access_token(); if (empty($jwt)) { return new WP_REST_Response(['success' => false, 'error' => 'not_authenticated'], 401); } if ($this->auth->is_expired()) { $refresh = $this->auth->refresh(); if (empty($refresh['success'])) { return new WP_REST_Response(['success' => false, 'error' => 'session_expired'], 401); } $jwt = $this->auth->access_token(); } $site_url = home_url(); $payload = $this->extractor->build_analysis_payload(); $result = $this->ai->analyze($site_url, $jwt, $payload); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_analyze_status(WP_REST_Request $request): WP_REST_Response { $id = (string) $request->get_param('id'); $jwt = $this->auth->access_token(); if (empty($jwt)) { return new WP_REST_Response(['success' => false, 'error' => 'not_authenticated'], 401); } $result = $this->ai->analysis_status($id, $jwt); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_quote(WP_REST_Request $request): WP_REST_Response { $analysis_id = (string) $request->get_param('analysis_id'); $add_ons = (array) ($request->get_param('add_ons') ?? []); $jwt = $this->auth->access_token(); if (empty($jwt)) { return new WP_REST_Response(['success' => false, 'error' => 'not_authenticated'], 401); } $result = $this->ai->quote($analysis_id, $add_ons, $jwt); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_approve(WP_REST_Request $request): WP_REST_Response { $quote_id = (string) $request->get_param('quote_id'); $jwt = $this->auth->access_token(); if (empty($jwt)) { return new WP_REST_Response(['success' => false, 'error' => 'not_authenticated'], 401); } $result = $this->ai->approve($quote_id, $jwt); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_checkout(WP_REST_Request $request): WP_REST_Response { $quote_id = (string) $request->get_param('quote_id'); if (empty($quote_id)) { return new WP_REST_Response(['success' => false, 'error' => 'quote_id required'], 400); } $jwt = $this->auth->access_token(); if (empty($jwt)) { return new WP_REST_Response(['success' => false, 'error' => 'not_authenticated'], 401); } $result = $this->ai->start_checkout($quote_id, $jwt); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_schema_generate(WP_REST_Request $request): WP_REST_Response { $jwt = $this->auth->access_token(); if (empty($jwt)) { return new WP_REST_Response(['success' => false, 'error' => 'not_authenticated'], 401); } $payload = $this->extractor->build_analysis_payload(); $result = $this->ai->schema_generate($payload, $jwt); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } // ----------------------------------------------------------------- // MCP hand-off (Phase D5) — Claude/MCP config for migrated Sanity // ----------------------------------------------------------------- public function handle_mcp_token(WP_REST_Request $request): WP_REST_Response { $mcp_token = (string) ($request->get_param('mcp_token') ?? ''); $result = $this->mcp->mint_token($mcp_token); // 404 from Worker (tenant not found) is a real error; 200 only on success. $code = $result['success'] ? 200 : 502; return new WP_REST_Response($result, $code); } public function handle_mcp_config(WP_REST_Request $request): WP_REST_Response { $result = $this->mcp->fetch_config(); // mcp_token_not_set → 404 so the JS can distinguish "mint one" from a hard error. if (!$result['success'] && !empty($result['data']['error']) && $result['data']['error'] === 'mcp_token_not_set') { return new WP_REST_Response($result, 404); } return new WP_REST_Response($result, $result['success'] ? 200 : 502); } public function handle_mcp_revoke(WP_REST_Request $request): WP_REST_Response { $result = $this->mcp->revoke(); return new WP_REST_Response($result, $result['success'] ? 200 : 502); } // ----------------------------------------------------------------- // Auth // ----------------------------------------------------------------- public function handle_auth_login(WP_REST_Request $request): WP_REST_Response { $email = sanitize_email($request->get_param('email') ?? ''); $password = (string) ($request->get_param('password') ?? ''); if (empty($email) || empty($password)) { return new WP_REST_Response(['success' => false, 'error' => 'email and password required'], 400); } $result = $this->auth->login($email, $password); return new WP_REST_Response($result, $result['success'] ? 200 : 401); } public function handle_auth_signup(WP_REST_Request $request): WP_REST_Response { $email = sanitize_email($request->get_param('email') ?? ''); $password = (string) ($request->get_param('password') ?? ''); if (empty($email) || empty($password)) { return new WP_REST_Response(['success' => false, 'error' => 'email and password required'], 400); } $result = $this->auth->signup($email, $password); return new WP_REST_Response($result, $result['success'] ? 200 : 400); } public function handle_auth_logout(WP_REST_Request $request): WP_REST_Response { $this->auth->logout(); return new WP_REST_Response(['success' => true], 200); } // ----------------------------------------------------------------- // Connect handshakes // ----------------------------------------------------------------- /** * Single-tenant /connect (Phase A back-compat). */ public function handle_connect(WP_REST_Request $request): WP_REST_Response { $service_url = esc_url_raw($request->get_param('service_url') ?? ''); $admin_token = sanitize_text_field($request->get_param('admin_token') ?? ''); $webhook_secret = sanitize_text_field($request->get_param('webhook_secret') ?? ''); if (empty($service_url) || empty($admin_token)) { return new WP_REST_Response(['success' => false, 'data' => ['error' => 'service_url and admin_token are required']], 400); } if (empty($webhook_secret)) { $webhook_secret = $this->core->setting('webhook_secret', ''); } $endpoint = rtrim($service_url, '/') . '/connect'; $response = wp_remote_post($endpoint, [ 'timeout' => 20, 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $admin_token, ], 'body' => wp_json_encode(['webhook_secret' => $webhook_secret]), ]); if (is_wp_error($response)) { return new WP_REST_Response(['success' => false, 'data' => ['error' => $response->get_error_message()]], 502); } $code = wp_remote_retrieve_response_code($response); $decoded = json_decode(wp_remote_retrieve_body($response), true); if ($code !== 200 || !is_array($decoded) || empty($decoded['connected'])) { $err = is_array($decoded) && !empty($decoded['error']) ? $decoded['error'] : 'Worker returned ' . intval($code); return new WP_REST_Response(['success' => false, 'data' => ['error' => $err, 'status' => intval($code)]], 502); } $settings = $this->core->settings(); $settings['service_url'] = $service_url; $settings['admin_token'] = $admin_token; $settings['webhook_url'] = rtrim($service_url, '/') . '/webhook/wp'; if (!empty($webhook_secret)) { $settings['webhook_secret'] = $webhook_secret; } $this->core->update_settings($settings); return new WP_REST_Response(['success' => true, 'data' => $decoded], 200); } /** * Multi-tenant /v2/connect. Customer supplies their own Sanity creds; * Worker provisions a tenant + returns webhook_url + webhook_secret. */ public function handle_connect_v2(WP_REST_Request $request): WP_REST_Response { $service_url = esc_url_raw($request->get_param('service_url') ?? ''); $admin_token = sanitize_text_field($request->get_param('admin_token') ?? ''); $account_id = sanitize_text_field($request->get_param('account_id') ?? ''); $sanity_project_id = sanitize_text_field($request->get_param('sanity_project_id') ?? ''); $sanity_api_token = (string) ($request->get_param('sanity_api_token') ?? ''); $sanity_dataset = sanitize_text_field($request->get_param('sanity_dataset') ?? 'production'); $nextjs_revalidate_url = esc_url_raw($request->get_param('nextjs_revalidate_url') ?? ''); if (empty($service_url) || empty($admin_token) || empty($sanity_project_id) || empty($sanity_api_token)) { return new WP_REST_Response([ 'success' => false, 'data' => ['error' => 'service_url, admin_token, sanity_project_id, and sanity_api_token are required'], ], 400); } $endpoint = rtrim($service_url, '/') . '/v2/connect'; $response = wp_remote_post($endpoint, [ 'timeout' => 25, 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $admin_token, ], 'body' => wp_json_encode([ 'account_id' => $account_id, 'wp_site_url' => home_url(), 'sanity_project_id' => $sanity_project_id, 'sanity_api_token' => $sanity_api_token, 'sanity_dataset' => $sanity_dataset, 'nextjs_revalidate_url' => $nextjs_revalidate_url, ]), ]); if (is_wp_error($response)) { return new WP_REST_Response(['success' => false, 'data' => ['error' => $response->get_error_message()]], 502); } $code = wp_remote_retrieve_response_code($response); $decoded = json_decode(wp_remote_retrieve_body($response), true); if ($code !== 200 || !is_array($decoded) || empty($decoded['tenant_id'])) { $err = is_array($decoded) && !empty($decoded['error']) ? $decoded['error'] : 'Worker returned ' . intval($code); return new WP_REST_Response(['success' => false, 'data' => ['error' => $err, 'status' => intval($code)]], 502); } // Persist tenant-scoped config. webhook_url is tenant-scoped via the // signature path; we still store the bare URL and let the Worker // resolve the tenant from the signature. $settings = $this->core->settings(); $settings['service_url'] = $service_url; $settings['admin_token'] = $admin_token; $settings['webhook_url'] = rtrim($service_url, '/') . '/webhook/wp'; if (!empty($decoded['webhook_secret'])) { $settings['webhook_secret'] = $decoded['webhook_secret']; } $settings['tenant_id'] = $decoded['tenant_id']; $this->core->update_settings($settings); return new WP_REST_Response(['success' => true, 'data' => $decoded], 200); } public function handle_connect_test(WP_REST_Request $request): WP_REST_Response { $service_url = $this->core->setting('service_url', '') ?: esc_url_raw($request->get_param('service_url') ?? ''); if (empty($service_url)) { return new WP_REST_Response(['success' => false, 'data' => ['error' => 'No service_url configured. Connect first.']], 400); } $response = wp_remote_get(rtrim($service_url, '/') . '/health', ['timeout' => 15]); if (is_wp_error($response)) { return new WP_REST_Response(['success' => false, 'data' => ['error' => $response->get_error_message()]], 502); } $code = wp_remote_retrieve_response_code($response); $decoded = json_decode(wp_remote_retrieve_body($response), true); if ($code !== 200 || !is_array($decoded)) { return new WP_REST_Response(['success' => false, 'data' => ['error' => 'Worker /health returned ' . intval($code), 'status' => intval($code)]], 502); } return new WP_REST_Response(['success' => true, 'data' => $decoded], 200); } }
Save
cmd:
run