Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.

Commit 5002f88

Browse files
KONFeatureclaude
andcommitted
refactor: implement cache-friendly JS configuration injection
- Created dedicated endpoint (/frak-config.js) for serving configuration - Removed inline JavaScript injection from every page - Added smart caching with ETag validation and 304 responses - Implemented cache busting via version parameter (config hash) - Added optional JavaScript minification for production - Integrated with popular caching plugins (WP Rocket, W3TC, etc.) - Added proper cache headers including CORS support - Config now cacheable by browsers, CDNs, and caching plugins Benefits: - Better performance with separate cacheable config file - Reduced page size (no inline JS) - Cache-friendly for aggressive caching strategies - Only config endpoint needs clearing on changes - Easy debugging via direct endpoint access 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 62773b7 commit 5002f88

4 files changed

Lines changed: 284 additions & 9 deletions

File tree

admin/class-frak-admin.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ private function save_settings() {
204204
update_option('frak_floating_button_position', $floating_button_position);
205205
update_option('frak_modal_language', $modal_language);
206206
update_option('frak_modal_i18n', json_encode($modal_i18n));
207+
208+
// Update config last modified timestamp for cache busting
209+
if (class_exists('Frak_Config_Endpoint')) {
210+
Frak_Config_Endpoint::update_last_modified();
211+
}
207212

208213
}
209214

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
<?php
2+
/**
3+
* Frak Config Endpoint
4+
*
5+
* Handles dynamic JavaScript configuration delivery with proper caching
6+
*/
7+
class Frak_Config_Endpoint {
8+
9+
private static $instance = null;
10+
11+
public static function instance() {
12+
if (null === self::$instance) {
13+
self::$instance = new self();
14+
}
15+
return self::$instance;
16+
}
17+
18+
private function __construct() {
19+
// Register the endpoint
20+
add_action('init', array($this, 'register_endpoint'));
21+
add_action('template_redirect', array($this, 'handle_endpoint'));
22+
23+
// Flush rewrite rules on activation
24+
register_activation_hook(FRAK_PLUGIN_FILE, array($this, 'flush_rewrite_rules'));
25+
register_deactivation_hook(FRAK_PLUGIN_FILE, array($this, 'flush_rewrite_rules'));
26+
}
27+
28+
/**
29+
* Register the custom endpoint
30+
*/
31+
public function register_endpoint() {
32+
add_rewrite_rule('^frak-config\.js$', 'index.php?frak_config=1', 'top');
33+
add_rewrite_tag('%frak_config%', '([^&]+)');
34+
}
35+
36+
/**
37+
* Handle the endpoint request
38+
*/
39+
public function handle_endpoint() {
40+
global $wp_query;
41+
42+
if (!isset($wp_query->query_vars['frak_config'])) {
43+
return;
44+
}
45+
46+
// Get the configuration
47+
$config = get_option('frak_custom_config', '');
48+
49+
if (empty($config)) {
50+
// Generate default config if none exists
51+
$admin = Frak_Admin::instance();
52+
$config = $this->get_compiled_config();
53+
}
54+
55+
// Generate ETag based on config content
56+
$etag = md5($config);
57+
$last_modified = get_option('frak_config_last_modified', time());
58+
59+
// Set proper headers
60+
$this->set_cache_headers($etag, $last_modified);
61+
62+
// Check if client has valid cached version
63+
if ($this->is_not_modified($etag)) {
64+
status_header(304);
65+
exit;
66+
}
67+
68+
// Output the JavaScript
69+
header('Content-Type: application/javascript; charset=utf-8');
70+
71+
// Apply minification if enabled
72+
if (get_option('frak_minify_config', 0) && !defined('SCRIPT_DEBUG')) {
73+
$config = $this->minify_javascript($config);
74+
} else {
75+
// Add header comment for non-minified version
76+
$config = "/* Frak Configuration - Generated: " . date('Y-m-d H:i:s') . " */\n" . $config;
77+
}
78+
79+
echo $config;
80+
exit;
81+
}
82+
83+
/**
84+
* Get compiled configuration from individual options
85+
*/
86+
private function get_compiled_config() {
87+
$app_name = get_option('frak_app_name', get_bloginfo('name'));
88+
$logo_url = get_option('frak_logo_url', '');
89+
$modal_language = get_option('frak_modal_language', 'default');
90+
$floating_button_position = get_option('frak_floating_button_position', 'right');
91+
$modal_i18n = get_option('frak_modal_i18n', '{}');
92+
93+
// Handle language setting
94+
$lang_code = $modal_language === 'default' ? 'undefined' : "'{$modal_language}'";
95+
96+
return <<<JS
97+
let logoUrl = '{$logo_url}';
98+
const lang = {$lang_code};
99+
100+
let i18n = {};
101+
try {
102+
i18n = JSON.parse('{$modal_i18n}'.replace(
103+
/&amp;|&lt;|&gt;|&#39;|&quot;/g,
104+
tag =>
105+
({
106+
'&amp;': '&',
107+
'&lt;': '<',
108+
'&gt;': '>',
109+
'&#39;': "'",
110+
'&quot;': '"'
111+
}[tag] || tag)
112+
)) || {};
113+
} catch (error) {
114+
console.error('Error parsing i18n customizations:', error);
115+
}
116+
117+
window.FrakSetup = {
118+
config: {
119+
walletUrl: 'https://wallet.frak.id',
120+
metadata: {
121+
name: '{$app_name}',
122+
lang,
123+
logoUrl
124+
},
125+
customizations: { i18n },
126+
domain: window.location.host
127+
},
128+
modalConfig: {
129+
login: {
130+
allowSso: true,
131+
ssoMetadata: {
132+
logoUrl,
133+
homepageLink: window.location.host
134+
}
135+
}
136+
},
137+
modalShareConfig: {
138+
link: window.location.href
139+
},
140+
modalWalletConfig: {
141+
metadata: {
142+
position: '{$floating_button_position}'
143+
}
144+
},
145+
};
146+
JS;
147+
}
148+
149+
/**
150+
* Set proper cache headers
151+
*/
152+
private function set_cache_headers($etag, $last_modified) {
153+
// Allow caching for 1 hour, but validate with ETag
154+
header('Cache-Control: public, max-age=3600, must-revalidate');
155+
header('ETag: "' . $etag . '"');
156+
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $last_modified) . ' GMT');
157+
header('X-Content-Type-Options: nosniff');
158+
159+
// Add CORS headers if needed
160+
$allowed_origin = get_option('frak_cors_origin', '*');
161+
header('Access-Control-Allow-Origin: ' . $allowed_origin);
162+
}
163+
164+
/**
165+
* Check if client has valid cached version
166+
*/
167+
private function is_not_modified($etag) {
168+
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false;
169+
170+
if ($if_none_match && $if_none_match === '"' . $etag . '"') {
171+
return true;
172+
}
173+
174+
return false;
175+
}
176+
177+
/**
178+
* Flush rewrite rules
179+
*/
180+
public function flush_rewrite_rules() {
181+
$this->register_endpoint();
182+
flush_rewrite_rules();
183+
}
184+
185+
/**
186+
* Get config URL with version parameter
187+
*/
188+
public static function get_config_url() {
189+
$config = get_option('frak_custom_config', '');
190+
$version = substr(md5($config), 0, 8);
191+
192+
return home_url('/frak-config.js?v=' . $version);
193+
}
194+
195+
/**
196+
* Update last modified timestamp when config changes
197+
*/
198+
public static function update_last_modified() {
199+
update_option('frak_config_last_modified', time());
200+
201+
// Clear caches from popular caching plugins
202+
self::clear_external_caches();
203+
}
204+
205+
/**
206+
* Simple JavaScript minification
207+
*/
208+
private function minify_javascript($js) {
209+
// Remove comments
210+
$js = preg_replace('/\/\*[\s\S]*?\*\/|\/\/.*$/m', '', $js);
211+
212+
// Remove unnecessary whitespace
213+
$js = preg_replace('/\s+/', ' ', $js);
214+
215+
// Remove whitespace around operators
216+
$js = preg_replace('/\s*([{}:;,=+\-*\/])\s*/', '$1', $js);
217+
218+
return trim($js);
219+
}
220+
221+
/**
222+
* Clear caches from popular caching plugins
223+
*/
224+
private static function clear_external_caches() {
225+
// WP Rocket
226+
if (function_exists('rocket_clean_domain')) {
227+
rocket_clean_domain();
228+
}
229+
230+
// W3 Total Cache
231+
if (function_exists('w3tc_flush_all')) {
232+
w3tc_flush_all();
233+
}
234+
235+
// WP Super Cache
236+
if (function_exists('wp_cache_clear_cache')) {
237+
wp_cache_clear_cache();
238+
}
239+
240+
// WP Fastest Cache
241+
if (class_exists('WpFastestCache') && method_exists('WpFastestCache', 'deleteCache')) {
242+
$wpfc = new WpFastestCache();
243+
$wpfc->deleteCache(true);
244+
}
245+
246+
// LiteSpeed Cache
247+
if (class_exists('LiteSpeed\Purge')) {
248+
LiteSpeed\Purge::purge_all();
249+
}
250+
251+
// Autoptimize
252+
if (class_exists('autoptimizeCache') && method_exists('autoptimizeCache', 'clearall')) {
253+
autoptimizeCache::clearall();
254+
}
255+
256+
// Clear WordPress object cache
257+
wp_cache_flush();
258+
}
259+
}

includes/class-frak-frontend.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,26 @@ private function __construct() {
1717
}
1818

1919
public function enqueue_scripts() {
20-
// Load the configuration
21-
$custom_config = get_option('frak_custom_config', '');
20+
// Check if we have a configuration
21+
$has_config = !empty(get_option('frak_custom_config', '')) ||
22+
!empty(get_option('frak_app_name', ''));
2223

23-
if (!empty($custom_config)) {
24-
// Inject the configuration inline instead of using a separate file
25-
wp_register_script('frak-config', false);
26-
wp_enqueue_script('frak-config');
27-
wp_add_inline_script('frak-config', $custom_config, 'before');
24+
if ($has_config) {
25+
// Load configuration from endpoint with cache busting
26+
wp_enqueue_script(
27+
'frak-config',
28+
Frak_Config_Endpoint::get_config_url(),
29+
array(),
30+
null,
31+
false // Load in head for early initialization
32+
);
2833
}
2934

3035
// Load the Frak SDK
3136
wp_enqueue_script(
3237
'frak-sdk',
3338
'https://cdn.jsdelivr.net/npm/@frak-labs/components@latest/cdn/components.js',
34-
array('frak-config'),
39+
$has_config ? array('frak-config') : array(),
3540
null,
3641
array('strategy' => 'defer')
3742
);

includes/class-frak-plugin.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ private function define_constants() {
2424
}
2525

2626
private function includes() {
27+
require_once FRAK_PLUGIN_DIR . 'includes/class-frak-webhook-helper.php';
2728
require_once FRAK_PLUGIN_DIR . 'admin/class-frak-admin.php';
2829
require_once FRAK_PLUGIN_DIR . 'includes/class-frak-frontend.php';
30+
require_once FRAK_PLUGIN_DIR . 'includes/class-frak-config-endpoint.php';
2931

3032
if (class_exists('WooCommerce')) {
3133
require_once FRAK_PLUGIN_DIR . 'includes/class-frak-woocommerce.php';
@@ -40,6 +42,9 @@ private function init_hooks() {
4042
}
4143

4244
public function init() {
45+
// Initialize config endpoint for all contexts
46+
Frak_Config_Endpoint::instance();
47+
4348
if (is_admin()) {
4449
Frak_Admin::instance();
4550
} else {
@@ -52,7 +57,8 @@ public function init() {
5257
}
5358

5459
public function activate() {
55-
// Activation logic if needed
60+
// Flush rewrite rules for config endpoint
61+
Frak_Config_Endpoint::instance()->flush_rewrite_rules();
5662
}
5763

5864
public function deactivate() {

0 commit comments

Comments
 (0)