detector = new AiMiles_Detector();
add_action('init', [$this, 'detect_ai_agent'], 1);
add_action('admin_menu', [$this, 'add_admin_menu']);
add_action('admin_init', [$this, 'register_settings']);
add_action('admin_notices', [$this, 'admin_notices']);
add_filter('plugin_action_links_' . plugin_basename(__FILE__), [$this, 'add_settings_link']);
}
public function admin_notices() {
$api_key = get_option('aimiles_api_key', '');
if (empty($api_key)) {
$settings_url = admin_url('options-general.php?page=aimiles');
echo '
';
}
}
public function detect_ai_agent() {
if (!get_option('aimiles_enabled', true)) {
return;
}
$api_key = get_option('aimiles_api_key', '');
if (empty($api_key)) {
return;
}
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
if (!$this->detector->is_ai_agent($user_agent)) {
return;
}
// Build the original URL the bot was requesting
$protocol = is_ssl() ? 'https' : 'http';
$host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : wp_parse_url(home_url(), PHP_URL_HOST);
$request_uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : '/';
$original_url = $protocol . '://' . $host . $request_uri;
// Proxy through the aiMiles serve API (handles fetching, injection, and logging)
$serve_url = add_query_arg([
'url' => $original_url,
'key' => $api_key,
'ua' => $user_agent,
], 'https://app.aimiles.ai/api/serve');
// Passive IP forwarding: pass the bot's real IP along so it can be
// logged server-side. Recorded only — never checked or enforced.
// Prefer the CDN-restored client IP over REMOTE_ADDR (which is the
// CDN edge when the site sits behind Cloudflare or a proxy).
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$bot_ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP']));
} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$forwarded = sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR']));
$bot_ip = trim(explode(',', $forwarded)[0]);
} else {
$bot_ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
}
$response = wp_remote_get($serve_url, [
'timeout' => 10,
'headers' => [
'X-Bot-IP' => $bot_ip,
],
]);
if (is_wp_error($response)) {
return; // Fail open — let WordPress serve the page normally
}
$status_code = wp_remote_retrieve_response_code($response);
if ($status_code < 200 || $status_code >= 400) {
return; // Fail open
}
$body = wp_remote_retrieve_body($response);
if (empty($body)) {
return; // Fail open
}
// Forward relevant headers from the serve API response
$content_type = wp_remote_retrieve_header($response, 'content-type');
if (!empty($content_type)) {
header('Content-Type: ' . $content_type);
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- proxied HTML from aiMiles serve API
echo $body;
exit;
}
public function add_admin_menu() {
add_options_page(
'aiMiles Settings',
'aiMiles',
'manage_options',
'aimiles',
[$this, 'render_settings_page']
);
}
public function register_settings() {
register_setting('aimiles_settings', 'aimiles_enabled', [
'type' => 'boolean',
'sanitize_callback' => 'rest_sanitize_boolean',
'default' => true,
]);
register_setting('aimiles_settings', 'aimiles_api_key', [
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
]);
}
public function add_settings_link($links) {
$settings_link = 'Settings';
array_unshift($links, $settings_link);
return $links;
}
public function render_settings_page() {
include AIMILES_PLUGIN_DIR . 'admin/settings-page.php';
}
}
function aimiles_init() {
return AiMiles::get_instance();
}
add_action('plugins_loaded', 'aimiles_init');
register_activation_hook(__FILE__, 'aimiles_activate');
function aimiles_activate() {
add_option('aimiles_enabled', true);
add_option('aimiles_api_key', '');
}
register_deactivation_hook(__FILE__, 'aimiles_deactivate');
function aimiles_deactivate() {
// Clean up if needed
}