From bdbe9b2ffd0e1740e9c4cc7b7bc958892a380818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Janczak?= Date: Mon, 1 Jun 2026 13:05:48 +0200 Subject: [PATCH] v1.2.0: Added counter buffering (write-back caching), CDN trust headers toggle, full UI localization, dynamic Filament routes, and high-concurrency fixes --- README.md | 13 ++++ config/filament-short-url.php | 38 +++++++++++ resources/lang/en/default.php | 31 +++++++++ resources/lang/pl/default.php | 31 +++++++++ resources/views/qr-designer.blade.php | 30 ++++---- routes/web.php | 2 +- .../Commands/SyncBufferedCountersCommand.php | 68 +++++++++++++++++++ .../Pages/ShortUrlSettingsPage.php | 16 +++++ .../Pages/ViewShortUrlLogs.php | 23 ++++--- .../ShortUrlResource/Schemas/ShortUrlForm.php | 10 +-- .../Tables/ShortUrlsTable.php | 20 +----- src/FilamentShortUrlServiceProvider.php | 2 + .../ShortUrlRedirectController.php | 9 ++- src/Models/ShortUrl.php | 32 ++++++++- src/Services/ClientIpExtractor.php | 68 ++++++++++--------- src/Services/ShortUrlSettingsManager.php | 12 ++++ 16 files changed, 323 insertions(+), 82 deletions(-) create mode 100644 src/Console/Commands/SyncBufferedCountersCommand.php diff --git a/README.md b/README.md index 11429d5..fbaf351 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,18 @@ Sends server-side `short_url_visit` hits using the **GA4 Measurement Protocol AP * **GA4 API Secret**: Create this secret in Google Analytics under `Admin -> Data Streams -> Measurement Protocol API secrets`. * **Firebase App ID / Measurement ID**: The target analytics stream identifier. +### 4. Counter Buffering (Write-back Caching) +For extremely high-traffic applications, direct database writes for click counts can cause row-locking bottlenecks. +* **Buffer Click Counts**: Toggling this option buffers total and unique visit count increments in the application cache. +* **Cron Synchronization**: When enabled, you must schedule the synchronization command to run periodically (e.g., every minute) to flush counts to the database: + ```bash + php artisan short-url:sync-counters + ``` + In your scheduler (`routes/console.php` or `app/Console/Kernel.php`): + ```php + $schedule->command('short-url:sync-counters')->everyMinute(); + ``` + --- ## Configuration Reference (.env) @@ -162,6 +174,7 @@ You can also pre-configure all parameters via your `.env` file: | `SHORT_URL_CACHE_TTL` | `cache_ttl` | `3600` | Redirection model caching TTL (set to `0` to disable). | | `GA4_API_SECRET` | `ga4.api_secret` | `null` | Google Analytics 4 Measurement Protocol API Secret. | | `FIREBASE_APP_ID` | `ga4.firebase_app_id` | `null` | Google Analytics 4 Firebase App ID (or uses Measurement ID). | +| `SHORT_URL_COUNTER_BUFFERING` | `counter_buffering.enabled` | `false` | Buffer click counts in cache (flushed via console command). | --- diff --git a/config/filament-short-url.php b/config/filament-short-url.php index f3e6699..08f2109 100644 --- a/config/filament-short-url.php +++ b/config/filament-short-url.php @@ -132,4 +132,42 @@ return [ 'gradient_type' => 'linear', ], + /* + |-------------------------------------------------------------------------- + | Counter Buffering (Write-back Caching) + |-------------------------------------------------------------------------- + | For high-traffic applications, direct database writes on every visit + | can cause locks. Enable this to buffer total/unique visit counts in + | cache, then sync them to the database periodically via a scheduled task. + */ + 'counter_buffering' => [ + 'enabled' => (bool) env('SHORT_URL_COUNTER_BUFFERING', false), + 'cache_key_prefix' => 'filament-short-url:buffer:', + ], + + /* + |-------------------------------------------------------------------------- + | Trust CDN/Proxy Headers + |-------------------------------------------------------------------------- + | If your application sits behind a CDN (like Cloudflare, AWS CloudFront) + | or a reverse proxy, set this to true to parse real visitor IP addresses + | and country codes from proxy headers. Only enable this if you are + | actually behind a proxy to prevent client IP spoofing! + | + */ + 'trust_cdn_headers' => (bool) env('SHORT_URL_TRUST_CDN_HEADERS', false), + + /* + |-------------------------------------------------------------------------- + | Redirect Route Middleware + |-------------------------------------------------------------------------- + | The middleware list applied to the short URL redirect route. + | By default, standard web middleware and rate limiting are applied. + | + */ + 'middleware' => [ + 'web', + 'throttle:120,1', + ], + ]; diff --git a/resources/lang/en/default.php b/resources/lang/en/default.php index 8a6200e..34eacad 100644 --- a/resources/lang/en/default.php +++ b/resources/lang/en/default.php @@ -180,4 +180,35 @@ return [ 'settings_ga4_api_secret_helper' => 'Generate this in GA4 → Admin → Data Streams → your stream → Measurement Protocol API secrets.', 'settings_ga4_firebase_app_id' => 'Firebase App ID (optional)', 'settings_ga4_firebase_app_id_helper' => 'Required only if tracking a Firebase / app stream. Leave empty for standard web GA4 streams.', + + 'settings_section_buffering' => 'Visit Counters Buffering', + 'settings_buffering_enabled' => 'Buffer Visit Counts in Cache', + 'settings_buffering_helper' => 'When enabled, visit count increments are temporarily buffered in the application cache and must be flushed periodically to the database via "php artisan short-url:sync-counters". This prevents row-locking issues and performance degradation under high-traffic spikes.', + + // CDN Trust Settings + 'settings_trust_cdn_headers' => 'Trust CDN & Proxy Headers', + 'settings_trust_cdn_headers_helper' => 'Enable this if your app sits behind a CDN (like Cloudflare, AWS CloudFront) or a reverse proxy. This allows extracting the real client IP and country code from CDN headers. Warning: only enable this if you are actually behind a proxy, otherwise client IP addresses can be spoofed!', + + // Stats View & Export Localization + 'stats_filter_visited_from' => 'Visited From', + 'stats_filter_visited_until' => 'Visited Until', + 'stats_action_export' => 'Export CSV', + 'stats_csv_time' => 'Time', + 'stats_csv_ip' => 'IP Address', + 'stats_csv_country' => 'Country', + 'stats_csv_device' => 'Device', + 'stats_csv_browser' => 'Browser', + 'stats_csv_os' => 'OS', + 'stats_csv_referer' => 'Referer', + + // Bulk Actions Localization + 'action_enable_selected' => 'Enable selected', + 'action_disable_selected' => 'Disable selected', + + // Form Sections Localization + 'form_section_options' => 'Options', + 'form_section_notes' => 'Internal Notes', + 'form_section_tracking' => 'Visit Tracking', + 'form_section_tracked_fields' => 'Tracked Fields', + 'form_section_analytics' => 'Third-Party Analytics', ]; diff --git a/resources/lang/pl/default.php b/resources/lang/pl/default.php index 4da7f5b..79ed21b 100644 --- a/resources/lang/pl/default.php +++ b/resources/lang/pl/default.php @@ -181,4 +181,35 @@ return [ 'settings_ga4_api_secret_helper' => 'Wygeneruj w GA4 → Admin → Strumienie danych → twój strumień → Klucze API Measurement Protocol.', 'settings_ga4_firebase_app_id' => 'Firebase App ID (opcjonalne)', 'settings_ga4_firebase_app_id_helper' => 'Wymagane tylko przy śledzeniu strumienia Firebase/aplikacji. Zostaw puste dla standardowych strumieni GA4.', + + 'settings_section_buffering' => 'Buforowanie liczników wizyt', + 'settings_buffering_enabled' => 'Buforuj kliknięcia w pamięci podręcznej (Cache)', + 'settings_buffering_helper' => 'Po włączeniu, zliczenia kliknięć będą buforowane w pamięci podręcznej aplikacji i muszą być okresowo synchronizowane z bazą danych za pomocą komendy "php artisan short-url:sync-counters". Zapobiega to blokowaniu wierszy bazy danych i spadkom wydajności przy masowym ruchu.', + + // CDN Trust Settings + 'settings_trust_cdn_headers' => 'Ufaj nagłówkom CDN i proxy', + 'settings_trust_cdn_headers_helper' => 'Włącz tę opcję, jeśli Twoja aplikacja działa za CDN (np. Cloudflare, AWS CloudFront) lub reverse proxy. Pozwala to na pobieranie prawdziwego adresu IP klienta i kodu kraju z nagłówków CDN. Uwaga: włączaj tylko wtedy, gdy faktycznie korzystasz z proxy, w przeciwnym razie adresy IP mogą być sfałszowane!', + + // Stats View & Export Localization + 'stats_filter_visited_from' => 'Odwiedzono od', + 'stats_filter_visited_until' => 'Odwiedzono do', + 'stats_action_export' => 'Eksportuj CSV', + 'stats_csv_time' => 'Czas', + 'stats_csv_ip' => 'Adres IP', + 'stats_csv_country' => 'Kraj', + 'stats_csv_device' => 'Urządzenie', + 'stats_csv_browser' => 'Przeglądarka', + 'stats_csv_os' => 'System operacyjny', + 'stats_csv_referer' => 'Źródło (Referer)', + + // Bulk Actions Localization + 'action_enable_selected' => 'Włącz zaznaczone', + 'action_disable_selected' => 'Wyłącz zaznaczone', + + // Form Sections Localization + 'form_section_options' => 'Opcje', + 'form_section_notes' => 'Wewnętrzne notatki', + 'form_section_tracking' => 'Śledzenie wizyt', + 'form_section_tracked_fields' => 'Śledzone pola', + 'form_section_analytics' => 'Zewnętrzne systemy analityczne', ]; diff --git a/resources/views/qr-designer.blade.php b/resources/views/qr-designer.blade.php index 9280189..7b43f53 100644 --- a/resources/views/qr-designer.blade.php +++ b/resources/views/qr-designer.blade.php @@ -254,7 +254,7 @@ {{-- Size & Margin --}}
-
+
{{ __('filament-short-url::default.qr_label_size') }} @@ -288,7 +288,7 @@ {{ __('filament-short-url::default.qr_label_foreground_color') }} {{-- Mode radio --}} -
+
-
+
{{ __('filament-short-url::default.qr_label_color') }} -
+
@@ -381,8 +381,8 @@ maxlength="7" placeholder="#ffffff" />
-