diff --git a/honeypot-fields.php b/honeypot-fields.php index e95ea1e..f250f49 100644 --- a/honeypot-fields.php +++ b/honeypot-fields.php @@ -160,14 +160,63 @@ class SmartHoneypotAPIClient { public static function defaults(): array { return [ - 'enabled' => false, - 'api_url' => '', - 'api_token' => '', - 'last_sync' => 0, - 'sent_total' => 0, + 'enabled' => false, + 'api_url' => '', + 'api_token' => '', + 'last_sync' => 0, + 'sent_total' => 0, + 'connection_ok' => null, // null=untested, true=ok, false=failed + 'last_verified' => 0, + 'last_error' => '', ]; } + /** + * Tests reachability (health endpoint) and auth (submit with bad payload). + * Returns ['ok' => bool, 'message' => string] + */ + public static function test_connection(): array { + $s = self::settings(); + + if (empty($s['api_url'])) { + return ['ok' => false, 'message' => 'No API URL configured.']; + } + + $base = trailingslashit(esc_url_raw($s['api_url'])); + $headers = ['Content-Type' => 'application/json']; + if (!empty($s['api_token'])) { + $headers['Authorization'] = 'Bearer ' . $s['api_token']; + } + + // Step 1 — reachability + $health = wp_remote_get($base . 'api/v1/health', ['timeout' => 8]); + if (is_wp_error($health)) { + return ['ok' => false, 'message' => 'Cannot reach API: ' . $health->get_error_message()]; + } + $code = wp_remote_retrieve_response_code($health); + if ($code !== 200) { + return ['ok' => false, 'message' => "API returned HTTP {$code}. Verify the URL is correct."]; + } + + // Step 2 — token validation: POST with an intentionally invalid payload. + // Auth passes → 400 (bad payload). Wrong/missing token → 403. + $auth = wp_remote_post($base . 'api/v1/submit', [ + 'timeout' => 8, + 'headers' => $headers, + 'body' => wp_json_encode(['site_hash' => 'connectivity_test', 'blocks' => []]), + ]); + if (is_wp_error($auth)) { + return ['ok' => false, 'message' => 'Token check request failed: ' . $auth->get_error_message()]; + } + $auth_code = wp_remote_retrieve_response_code($auth); + if ($auth_code === 403) { + return ['ok' => false, 'message' => 'API reachable but token rejected (HTTP 403). Check the token matches API_TOKEN in Docker.']; + } + + // 400 = auth passed, payload correctly rejected — exactly what we expect + return ['ok' => true, 'message' => 'Connection verified. API is reachable and token is accepted.']; + } + public static function settings(): array { return wp_parse_args(get_option(self::OPT_SETTINGS, []), self::defaults()); } @@ -311,19 +360,40 @@ class SmartHoneypotAdmin { } if ($_POST['hp_action'] === 'save_api_settings') { - $current = SmartHoneypotAPIClient::settings(); + $current = SmartHoneypotAPIClient::settings(); + $new_url = esc_url_raw(trim($_POST['hp_api_url'] ?? '')); + $new_token = sanitize_text_field($_POST['hp_api_token'] ?? ''); + $url_changed = $new_url !== $current['api_url']; + $tok_changed = $new_token !== $current['api_token']; + $new = [ - 'enabled' => !empty($_POST['hp_api_enabled']), - 'api_url' => esc_url_raw(trim($_POST['hp_api_url'] ?? '')), - 'api_token' => sanitize_text_field($_POST['hp_api_token'] ?? ''), - 'last_sync' => $current['last_sync'], - 'sent_total' => $current['sent_total'], + 'enabled' => !empty($_POST['hp_api_enabled']), + 'api_url' => $new_url, + 'api_token' => $new_token, + 'last_sync' => $current['last_sync'], + 'sent_total' => $current['sent_total'], + // Reset verification if URL or token changed + 'connection_ok' => ($url_changed || $tok_changed) ? null : $current['connection_ok'], + 'last_verified' => ($url_changed || $tok_changed) ? 0 : $current['last_verified'], + 'last_error' => ($url_changed || $tok_changed) ? '' : $current['last_error'], ]; update_option(SmartHoneypotAPIClient::OPT_SETTINGS, $new); wp_redirect(add_query_arg(['page' => self::MENU_SLUG, 'tab' => 'settings', 'saved' => 1], admin_url('admin.php'))); exit; } + if ($_POST['hp_action'] === 'test_connection') { + $result = SmartHoneypotAPIClient::test_connection(); + $s = SmartHoneypotAPIClient::settings(); + $s['connection_ok'] = $result['ok']; + $s['last_verified'] = time(); + $s['last_error'] = $result['ok'] ? '' : $result['message']; + update_option(SmartHoneypotAPIClient::OPT_SETTINGS, $s); + set_transient('hp_conn_result', $result, 60); + wp_redirect(add_query_arg(['page' => self::MENU_SLUG, 'tab' => 'settings', 'tested' => 1], admin_url('admin.php'))); + exit; + } + if ($_POST['hp_action'] === 'flush_queue') { SmartHoneypotAPIClient::flush(); wp_redirect(add_query_arg(['page' => self::MENU_SLUG, 'tab' => 'settings', 'flushed' => 1], admin_url('admin.php'))); @@ -350,6 +420,13 @@ class SmartHoneypotAdmin {
Queue flushed to central API.
= esc_html($res['message']) ?>