'Cloudflare', 'icon' => 'Cloud', 'description' => 'Connect a Cloudflare zone for automatic edge purging when xSpeed clears its cache, plus a dev-mode toggle.', 'custom_panel' => 'CloudflarePanel', ); } public function settings_schema(): array { return array( 'enabled' => array( 'type' => 'bool', 'default' => false, 'label' => 'Enable Cloudflare integration', 'description' => 'Use the credentials below to verify your zone and run purges.', ), 'auth_method' => array( 'type' => 'enum', 'default' => 'token', 'options' => array( 'token', 'key' ), 'option_labels' => array( 'token' => 'API Token', 'key' => 'Global API Key', ), 'label' => 'Authentication', 'description' => 'API Tokens (scoped, recommended) or the legacy Global API Key with your account email.', ), 'api_token' => array( 'type' => 'string', 'default' => '', 'label' => 'API Token', 'description' => 'Create a token at dash.cloudflare.com → My Profile → API Tokens. Needs "Zone → Cache Purge" + "Zone Settings" permissions.', ), 'email' => array( 'type' => 'string', 'default' => '', 'label' => 'Account Email', 'description' => 'Only used when Authentication is set to Global API Key.', ), 'api_key' => array( 'type' => 'string', 'default' => '', 'label' => 'Global API Key', 'description' => 'Found at dash.cloudflare.com → My Profile → API Tokens → Global API Key.', ), 'zone_id' => array( 'type' => 'string', 'default' => '', 'label' => 'Zone ID', 'description' => 'The 32-character hex Zone ID from your domain overview page.', ), 'auto_purge_on_update' => array( 'type' => 'bool', 'default' => true, 'label' => 'Auto-purge Cloudflare on xSpeed purge', 'description' => 'When xSpeed clears its own cache (post save, settings change, manual purge), trigger a Cloudflare purge too.', ), ); } public function rest_routes(): array { $default = parent::rest_routes(); return array_merge( $default, array( array( 'path' => '/verify', 'methods' => 'POST', 'callback' => array( $this, 'rest_verify' ), ), array( 'path' => '/purge', 'methods' => 'POST', 'callback' => array( $this, 'rest_purge' ), ), array( 'path' => '/dev-mode', 'methods' => 'POST', 'callback' => array( $this, 'rest_dev_mode' ), ), ) ); } public function conflicts(): array { return array( array( 'plugin' => 'cloudflare/cloudflare.php', 'feature' => 'cloudflare.purge', 'strategy' => \XSpeed\Conflict_Registry::STRATEGY_WARN, 'reason' => 'The official Cloudflare plugin also auto-purges; keep auto-purge enabled in only one to avoid double API calls.', ), ); } public function boot(): void { $opts = $this->get_settings(); if ( empty( $opts['enabled'] ) ) { return; } if ( ! empty( $opts['auto_purge_on_update'] ) ) { // xSpeed fires this action whenever it purges its own // cache (see Cache::purge_all). Listening here keeps // CF in sync without any new wiring elsewhere. add_action( 'xspeed_after_purge_all', array( $this, 'on_xspeed_purge' ), 10, 0 ); } } public function on_xspeed_purge(): void { $opts = $this->get_settings(); if ( empty( $opts['enabled'] ) || empty( $opts['zone_id'] ) ) { return; } $result = Cloudflare::purge_all( $opts ); if ( ! $result['ok'] && class_exists( '\\XSpeed\\Activity_Log' ) ) { \XSpeed\Activity_Log::record( 'cloudflare_purge_failed', 'Cloudflare auto-purge failed: ' . ( $result['body']['message'] ?? 'unknown error' ), \XSpeed\Activity_Log::WARN ); } } public function rest_verify( \WP_REST_Request $request ) { return rest_ensure_response( Cloudflare::verify( $this->get_settings() ) ); } public function rest_purge( \WP_REST_Request $request ) { $params = $request->get_json_params(); if ( ! is_array( $params ) ) { $params = array(); } $opts = $this->get_settings(); if ( isset( $params['urls'] ) && is_array( $params['urls'] ) && ! empty( $params['urls'] ) ) { return rest_ensure_response( Cloudflare::purge_urls( $opts, $params['urls'] ) ); } return rest_ensure_response( Cloudflare::purge_all( $opts ) ); } public function rest_dev_mode( \WP_REST_Request $request ) { $params = $request->get_json_params(); $on = ! empty( $params['on'] ); return rest_ensure_response( Cloudflare::set_dev_mode( $this->get_settings(), $on ) ); } public function cli_commands(): array { return array( array( 'name' => 'xspeed cf', 'callback' => array( $this, 'cli_handler' ), 'shortdesc' => 'Cloudflare verify / purge / dev-mode helpers.', 'synopsis' => array( array( 'type' => 'positional', 'name' => 'action', 'options' => array( 'verify', 'purge', 'dev-on', 'dev-off' ), 'optional' => false, ), ), ), ); } public function cli_handler( array $args, array $assoc ): void { $opts = $this->get_settings(); $action = $args[0] ?? 'verify'; switch ( $action ) { case 'verify': $res = Cloudflare::verify( $opts ); break; case 'purge': $res = Cloudflare::purge_all( $opts ); break; case 'dev-on': $res = Cloudflare::set_dev_mode( $opts, true ); break; case 'dev-off': $res = Cloudflare::set_dev_mode( $opts, false ); break; default: \WP_CLI::error( "Unknown action: $action" ); return; } \WP_CLI::log( 'HTTP ' . $res['status'] . ' — ' . ( $res['ok'] ? 'ok' : 'failed' ) ); \WP_CLI::log( wp_json_encode( $res['body'] ) ); } }