diff --git a/honeypot-fields.php b/honeypot-fields.php index f250f49..efe0874 100644 --- a/honeypot-fields.php +++ b/honeypot-fields.php @@ -217,6 +217,85 @@ class SmartHoneypotAPIClient { return ['ok' => true, 'message' => 'Connection verified. API is reachable and token is accepted.']; } + /** + * Sends one batch of existing local log records to the central API. + * Picks up from where it left off using the hp_history_last_id option. + * + * Returns ['ok', 'sent', 'remaining', 'has_more', 'message'] + */ + public static function send_history_batch(int $batch_size = 50): array { + $s = self::settings(); + if (empty($s['api_url'])) { + return ['ok' => false, 'message' => 'No API URL configured.']; + } + + global $wpdb; + $table = SmartHoneypotDB::table(); + $last_id = (int) get_option('hp_history_last_id', 0); + $total = SmartHoneypotDB::count(); + + $rows = $wpdb->get_results( + $wpdb->prepare( + "SELECT * FROM {$table} WHERE id > %d ORDER BY id ASC LIMIT %d", + $last_id, $batch_size + ), + ARRAY_A + ); + + if (empty($rows)) { + return ['ok' => true, 'sent' => 0, 'remaining' => 0, 'has_more' => false, + 'message' => 'All records have already been sent.']; + } + + $blocks = array_map(fn($r) => [ + 'ip' => $r['ip_address'], + 'form_type' => $r['form_type'], + 'reason' => $r['reason'], + 'user_agent' => $r['user_agent'], + 'blocked_at' => $r['blocked_at'], + ], $rows); + + $headers = ['Content-Type' => 'application/json']; + if (!empty($s['api_token'])) { + $headers['Authorization'] = 'Bearer ' . $s['api_token']; + } + + $response = wp_remote_post( + trailingslashit(esc_url_raw($s['api_url'])) . 'api/v1/submit', + [ + 'timeout' => 30, + 'headers' => $headers, + 'body' => wp_json_encode([ + 'site_hash' => hash('sha256', home_url()), + 'blocks' => $blocks, + ]), + ] + ); + + if (is_wp_error($response)) { + return ['ok' => false, 'message' => 'Request failed: ' . $response->get_error_message()]; + } + $code = wp_remote_retrieve_response_code($response); + if ($code !== 200) { + return ['ok' => false, 'message' => "API returned HTTP {$code}. Check connection settings."]; + } + + $new_last_id = (int) end($rows)['id']; + $sent_total = (int) get_option('hp_history_total_sent', 0) + count($rows); + update_option('hp_history_last_id', $new_last_id); + update_option('hp_history_total_sent', $sent_total); + + $remaining = max(0, $total - $sent_total); + + return [ + 'ok' => true, + 'sent' => count($rows), + 'remaining' => $remaining, + 'has_more' => $remaining > 0, + 'message' => sprintf('Sent %d records. %d remaining.', count($rows), $remaining), + ]; + } + public static function settings(): array { return wp_parse_args(get_option(self::OPT_SETTINGS, []), self::defaults()); } @@ -399,6 +478,20 @@ class SmartHoneypotAdmin { wp_redirect(add_query_arg(['page' => self::MENU_SLUG, 'tab' => 'settings', 'flushed' => 1], admin_url('admin.php'))); exit; } + + if ($_POST['hp_action'] === 'send_history') { + $result = SmartHoneypotAPIClient::send_history_batch(); + set_transient('hp_history_result', $result, 60); + wp_redirect(add_query_arg(['page' => self::MENU_SLUG, 'tab' => 'settings', 'history' => 1], admin_url('admin.php'))); + exit; + } + + if ($_POST['hp_action'] === 'reset_history') { + delete_option('hp_history_last_id'); + delete_option('hp_history_total_sent'); + wp_redirect(add_query_arg(['page' => self::MENU_SLUG, 'tab' => 'settings', 'history_reset' => 1], admin_url('admin.php'))); + exit; + } } public static function render_page() { @@ -427,6 +520,22 @@ class SmartHoneypotAdmin {
= esc_html($res['message']) ?>
= esc_html($res['message']) ?> + + Click "Send Next Batch" to continue. + +
+History sync progress has been reset. You can re-send from the beginning.