Changeset 3349092
- Timestamp:
- 08/24/2025 12:57:41 AM (7 months ago)
- Location:
- rocketfront-connect/trunk
- Files:
-
- 2 edited
-
functions.php (modified) (3 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
rocketfront-connect/trunk/functions.php
r3225218 r3349092 4 4 Plugin URI: https://www.kodeala.com 5 5 Description: Automate AWS CloudFront CDN clearing and cache management by seamlessly integrating the WP Rocket plugin with C3 Cloudfront Cache Controller. 6 Version: 1.0. 06 Version: 1.0.1 7 7 Author: Kodeala 8 Tags: WP Rocket, CloudFront, C3 Cloudfront Cache Controller, CDN, RocketFront9 8 License: GPL-2.0-or-later 10 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 12 11 13 12 Requires at least: 5.0 14 Tested up to: 6. 7.113 Tested up to: 6.4.3 15 14 16 15 Requires WP Rocket: Yes … … 21 20 22 21 require_once( ABSPATH . 'wp-admin/includes/plugin.php' ); 23 use Aws\CloudFront\CloudFrontClient; 24 function kodealarocketfront_initialize_cloudfront_wprocket_connection() { 25 //Check if Cloudfront plugin is active 26 if (is_plugin_active('c3-cloudfront-clear-cache/c3-cloudfront-clear-cache.php')) { 27 //Fetch Autoload.php from cloudfront plugin. 28 require_once WP_PLUGIN_DIR . '/c3-cloudfront-clear-cache/vendor/autoload.php'; 29 30 $region = get_option('kodealarocketfront_selected_region', 'us-east-1'); 31 32 $c3_settings = get_option('c3_settings'); 33 $cloudfront = null; 34 35 if (is_array($c3_settings) && !empty($c3_settings['access_key']) && !empty($c3_settings['secret_key'])) { 36 $cloudfront = new CloudFrontClient([ 37 'version' => 'latest', 38 'region' => esc_html(sanitize_text_field($region)), 22 23 function kodealarocketfront_try_load_aws_sdk(): bool { 24 if ( class_exists('\Aws\CloudFront\CloudFrontClient') ) return true; 25 26 $candidates = [ 27 WP_PLUGIN_DIR . '/c3-cloudfront-clear-cache/vendor/autoload.php', 28 WP_PLUGIN_DIR . '/c3-cloudfront-clear-cache/aws/aws-autoloader.php', 29 ABSPATH . 'vendor/autoload.php', 30 ]; 31 foreach ( $candidates as $path ) { 32 if ( is_readable( $path ) ) { 33 @include_once $path; 34 if ( class_exists('\Aws\CloudFront\CloudFrontClient') ) return true; 35 } 36 } 37 return false; 38 } 39 40 function kodealarocketfront_get_c3_creds(): array { 41 $opt = get_option('c3_settings', []); 42 $dist = $opt['distribution_id'] ?? ( defined('C3_DISTRIBUTION_ID') ? C3_DISTRIBUTION_ID : '' ); 43 $key = $opt['access_key'] ?? ( defined('AWS_ACCESS_KEY_ID') ? AWS_ACCESS_KEY_ID : '' ); 44 $secret = $opt['secret_key'] ?? ( defined('AWS_SECRET_ACCESS_KEY') ? AWS_SECRET_ACCESS_KEY : '' ); 45 $token = defined('AWS_SESSION_TOKEN') ? AWS_SESSION_TOKEN : ''; 46 47 return [ 48 'distribution_id' => trim((string) $dist), 49 'key' => trim((string) $key), 50 'secret' => trim((string) $secret), 51 'token' => trim((string) $token), 52 ]; 53 } 54 55 function kodealarocketfront_cf_http_invalidate(array $creds, array $paths): bool { 56 if ( empty($creds['distribution_id']) || empty($creds['key']) || empty($creds['secret']) ) { 57 return false; 58 } 59 60 $service = 'cloudfront'; 61 $region = 'us-east-1'; 62 $host = 'cloudfront.amazonaws.com'; 63 $now = gmdate('Ymd\THis\Z'); 64 $dateStamp = gmdate('Ymd'); 65 66 $itemsXml = ''; 67 foreach ($paths as $p) { 68 $p = (string) $p; 69 if ($p === '') continue; 70 $p = (strpos($p, '/') === 0) ? $p : '/' . $p; 71 $itemsXml .= '<Path>' . esc_html($p) . '</Path>'; 72 } 73 if ($itemsXml === '') $itemsXml = '<Path>/*</Path>'; 74 75 $callerRef = (string) (microtime(true) * 1000000); 76 $body = <<<XML 77 <InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/"> 78 <Paths> 79 <Quantity>1</Quantity> 80 <Items> 81 {$itemsXml} 82 </Items> 83 </Paths> 84 <CallerReference>{$callerRef}</CallerReference> 85 </InvalidationBatch> 86 XML; 87 88 $uriPath = '/2020-05-31/distribution/' . rawurlencode($creds['distribution_id']) . '/invalidation'; 89 $canonicalURI = $uriPath; 90 $canonicalQuery = ''; 91 $contentSha256 = hash('sha256', $body); 92 $headers = [ 93 'content-type' => 'application/xml', 94 'host' => $host, 95 'x-amz-content-sha256'=> $contentSha256, 96 'x-amz-date' => $now, 97 ]; 98 if (!empty($creds['token'])) { 99 $headers['x-amz-security-token'] = $creds['token']; 100 } 101 102 ksort($headers); 103 $canonicalHeaders = ''; 104 $signedHeaderKeys = []; 105 foreach ($headers as $k => $v) { 106 $canonicalHeaders .= strtolower($k) . ':' . trim($v) . "\n"; 107 $signedHeaderKeys[] = strtolower($k); 108 } 109 $signedHeaders = implode(';', $signedHeaderKeys); 110 111 $canonicalRequest = "POST\n{$canonicalURI}\n{$canonicalQuery}\n{$canonicalHeaders}\n{$signedHeaders}\n{$contentSha256}"; 112 $hashedCanonical = hash('sha256', $canonicalRequest); 113 114 $scope = "{$dateStamp}/{$region}/{$service}/aws4_request"; 115 $stringToSign = "AWS4-HMAC-SHA256\n{$now}\n{$scope}\n{$hashedCanonical}"; 116 117 $kDate = hash_hmac('sha256', $dateStamp, 'AWS4' . $creds['secret'], true); 118 $kRegion = hash_hmac('sha256', $region, $kDate, true); 119 $kService = hash_hmac('sha256', $service, $kRegion, true); 120 $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true); 121 122 $signature = hash_hmac('sha256', $stringToSign, $kSigning); 123 124 $authHeader = sprintf( 125 'AWS4-HMAC-SHA256 Credential=%s/%s, SignedHeaders=%s, Signature=%s', 126 $creds['key'], 127 $scope, 128 $signedHeaders, 129 $signature 130 ); 131 132 $wp_headers = [ 133 'Authorization' => $authHeader, 134 'Content-Type' => $headers['content-type'], 135 'X-Amz-Date' => $headers['x-amz-date'], 136 'X-Amz-Content-Sha256' => $headers['x-amz-content-sha256'], 137 'Host' => $headers['host'], 138 ]; 139 if (!empty($creds['token'])) { 140 $wp_headers['X-Amz-Security-Token'] = $creds['token']; 141 } 142 143 $resp = wp_remote_request( 144 "https://{$host}{$uriPath}", 145 [ 146 'method' => 'POST', 147 'timeout' => 20, 148 'headers' => $wp_headers, 149 'body' => $body, 150 ] 151 ); 152 153 if ( is_wp_error($resp) ) { 154 error_log('[RocketFront] HTTP invalidation error: ' . $resp->get_error_message()); 155 return false; 156 } 157 $code = wp_remote_retrieve_response_code($resp); 158 if ( $code < 200 || $code >= 300 ) { 159 error_log('[RocketFront] HTTP invalidation failed with status ' . $code . ' body: ' . wp_remote_retrieve_body($resp)); 160 return false; 161 } 162 return true; 163 } 164 165 function kodealarocketfront_invalidate(array $paths = ['/*']): bool { 166 static $done = false; 167 if ( $done ) return true; 168 $done = true; 169 170 $creds = kodealarocketfront_get_c3_creds(); 171 if ( empty($creds['distribution_id']) ) { 172 error_log('[RocketFront] Missing C3 distribution id – skipping invalidation.'); 173 return false; 174 } 175 176 if ( kodealarocketfront_try_load_aws_sdk() && class_exists('\Aws\CloudFront\CloudFrontClient') ) { 177 try { 178 $client = new \Aws\CloudFront\CloudFrontClient([ 179 'version' => 'latest', 180 'region' => 'us-east-1', 39 181 'credentials' => [ 40 'key' => esc_html(sanitize_text_field($c3_settings['access_key'])),41 'secret' => esc_html(sanitize_text_field($c3_settings['secret_key'])),182 'key' => $creds['key'], 183 'secret' => $creds['secret'], 42 184 ], 43 185 ]); 44 } 45 function kodealarocketfront_invalidate_cloudfront_cache($cloudfront) { 46 if (defined('KODEALAROCKETFRONT_CACHE_INVALIDATED')) { 47 return; 48 } 49 define('KODEALAROCKETFRONT_CACHE_INVALIDATED', true); 50 51 $c3_settings = get_option('c3_settings'); 52 $result = $cloudfront->createInvalidation([ 53 'DistributionId' => esc_html(sanitize_text_field($c3_settings['distribution_id'])), 54 'InvalidationBatch' => [ 55 'CallerReference' => time(), 186 $client->createInvalidation([ 187 'DistributionId' => $creds['distribution_id'], 188 'InvalidationBatch'=> [ 189 'CallerReference' => (string) (microtime(true) * 1000000), 56 190 'Paths' => [ 57 'Quantity' => 1,58 'Items' => ['/*'],191 'Quantity' => count($paths), 192 'Items' => $paths, 59 193 ], 60 194 ], 61 195 ]); 196 return true; 197 } catch (\Throwable $e) { 198 error_log('[RocketFront] SDK invalidation error: ' . $e->getMessage() . ' – falling back to HTTP.'); 62 199 } 63 64 if (!empty($cloudfront)) { 65 add_action('after_rocket_clean_domain', function () use ($cloudfront) { 66 kodealarocketfront_invalidate_cloudfront_cache($cloudfront); 67 }); 68 add_action('after_rocket_clean_minify', function () use ($cloudfront) { 69 kodealarocketfront_invalidate_cloudfront_cache($cloudfront); 70 }); 71 add_action('after_rocket_clean_cache', function () use ($cloudfront) { 72 kodealarocketfront_invalidate_cloudfront_cache($cloudfront); 73 }); 74 } 75 76 function kodealarocketfront_add_region_dropdown() { 77 add_options_page( 78 'CloudFront Regions', 79 'CloudFront Regions', 80 'manage_options', 81 'kodealarocketfront-region-settings', 82 'kodealarocketfront_region_settings_page' 83 ); 84 } 85 add_action('admin_menu', 'kodealarocketfront_add_region_dropdown'); 86 87 function kodealarocketfront_region_settings_page() { 88 if (!isset($_POST['kodealarocketfront_nonce_field']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['kodealarocketfront_nonce_field'])), 'kodealarocketfront_nonce_action')) { 89 $selected_region = isset($_POST['kodealarocketfront_selected_region']) ? sanitize_text_field(wp_unslash($_POST['kodealarocketfront_selected_region'])) : ''; 90 if (!empty($selected_region)) { 91 update_option('kodealarocketfront_selected_region', $selected_region); 92 } 93 } 94 $regions = [ 95 'us-east-1' => 'US East (N. Virginia)', 96 'us-east-2' => 'US East (Ohio)', 97 'us-west-1' => 'US West (N. California)', 98 'us-west-2' => 'US West (Oregon)', 99 'ap-south-1' => 'Asia Pacific (Mumbai)', 100 'ap-northeast-3' => 'Asia Pacific (Osaka)', 101 'ap-northeast-2' => 'Asia Pacific (Seoul)', 102 'ap-southeast-1' => 'Asia Pacific (Singapore)', 103 'ap-southeast-2' => 'Asia Pacific (Sydney)', 104 'ap-northeast-1' => 'Asia Pacific (Tokyo)', 105 'ca-central-1' => 'Canada (Central)', 106 'eu-central-1' => 'Europe (Frankfurt)', 107 'eu-west-1' => 'Europe (Ireland)', 108 'eu-west-2' => 'Europe (London)', 109 'eu-west-3' => 'Europe (Paris)', 110 'eu-north-1' => 'Europe (Stockholm)', 111 'sa-east-1' => 'South America (São Paulo)', 112 'af-south-1' => 'Africa (Cape Town)', 113 'ap-east-1' => 'Asia Pacific (Hong Kong)', 114 'ap-south-2' => 'Asia Pacific (Hyderabad)', 115 'ap-southeast-3' => 'Asia Pacific (Jakarta)', 116 'ap-southeast-4' => 'Asia Pacific (Melbourne)', 117 'ca-west-1' => 'Canada (Calgary)', 118 'eu-south-1' => 'Europe (Milan)', 119 'eu-south-2' => 'Europe (Spain)', 120 'eu-central-2' => 'Europe (Zurich)', 121 'me-south-1' => 'Middle East (Bahrain)', 122 'me-central-1' => 'Middle East (UAE)', 123 'il-central-1' => 'Israel (Tel Aviv)' 124 ]; 125 126 $region = get_option('kodealarocketfront_selected_region', 'us-east-1'); 127 ?> 128 <div class="wrap"> 129 <h1>Region Settings</h1> 130 <form method="post"> 131 <label for="kodealarocketfront_selected_region">Select CloudFront Region:</label> 132 <select name="kodealarocketfront_selected_region" id="kodealarocketfront_selected_region"> 133 <?php foreach ($regions as $region_code => $region_name){ 134 $selected = ($region == $region_code) ? 'selected' : ''; 135 ?> 136 <option value="<?php echo esc_html(sanitize_text_field($region_code)); ?>" <?php echo esc_html(sanitize_text_field($selected)); ?>><?php echo esc_html(sanitize_text_field($region_name)); ?></option> 137 <?php }; ?> 138 </select> 139 <?php 140 wp_nonce_field('kodealarocketfront_nonce_action', 'kodealarocketfront_nonce_field', true, false); 141 submit_button('Save Changes'); 142 ?> 143 </form> 144 </div> 145 <?php 146 } 147 } 148 } 149 add_action('plugins_loaded', 'kodealarocketfront_initialize_cloudfront_wprocket_connection'); 150 ?> 200 } 201 202 return kodealarocketfront_cf_http_invalidate($creds, $paths); 203 } 204 205 function kodealarocketfront_boot() { 206 if ( ! function_exists('is_plugin_active') ) { 207 @include_once ABSPATH . 'wp-admin/includes/plugin.php'; 208 } 209 if ( ! is_plugin_active('c3-cloudfront-clear-cache/c3-cloudfront-clear-cache.php') ) { 210 return; 211 } 212 213 $callback = function() { kodealarocketfront_invalidate(['/*']); }; 214 215 add_action('after_rocket_clean_domain', $callback); 216 add_action('after_rocket_clean_minify', $callback); 217 add_action('after_rocket_clean_cache', $callback); 218 219 add_action('after_rocket_clean_files', $callback); 220 } 221 add_action('plugins_loaded', 'kodealarocketfront_boot'); -
rocketfront-connect/trunk/readme.txt
r3129792 r3349092 17 17 3. Configure the plugin settings under the CloudFront Regions on the settings pages. 18 18 == Changelog == 19 v1.0.1 20 Fixed Fatal Error due to file removed from C3 Cloudfront Cache Controller 21 19 22 v1.0 20 23 Initial creation of plugin
Note: See TracChangeset
for help on using the changeset viewer.