diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2c048c4..10c5af9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -69,6 +69,15 @@ jobs: rm -f .env.example rm -f deploy.sh + # Never ship a local sqlite — would overwrite production data + rm -f database/*.sqlite database/*.sqlite-journal + + # Force the server to re-cache config/routes after deploy + rm -f bootstrap/cache/config.php + rm -f bootstrap/cache/routes-v7.php + rm -f bootstrap/cache/services.php + rm -f bootstrap/cache/packages.php + - name: Deploy via SFTP uses: wlixcc/SFTP-Deploy-Action@v1.2.4 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 08480d4..a1418b0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,6 +16,9 @@ jobs: name: PHP ${{ matrix.php }} + env: + VISITOR_TRACKER_DASHBOARD_ENABLED: false + steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index a7e49f9..931efa7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ /storage/*.key /storage/pail /vendor +/bootstrap/cache/*.php +/database/*.sqlite +/database/*.sqlite-journal Homestead.json Homestead.yaml Thumbs.db diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index d0a6454..e1653b3 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -147,6 +147,12 @@ public function index(): View 'route' => 'tools.diff', 'icon' => 'diff', ], + [ + 'name' => 'Visitor Tracker', + 'description' => 'Server-side visitor analytics for Laravel applications', + 'route' => 'tools.visitor-tracker', + 'icon' => 'chart', + ], ]; return view('home', compact('tools')); @@ -266,4 +272,31 @@ public function diff(): View { return view('tools.diff'); } + + public function visitorTracker(): View + { + $stats = app(\Ghdj\VisitorTracker\Services\StatisticsService::class); + $parser = new \Ghdj\VisitorTracker\Services\UserAgentParser(); + $botDetector = new \Ghdj\VisitorTracker\Services\BotDetector(); + + $userAgent = request()->userAgent(); + $parsedUA = $parser->parse($userAgent); + $isBot = $botDetector->isBot($userAgent); + $botName = $isBot ? $botDetector->getBotName($userAgent) : null; + $botCategory = $isBot ? $botDetector->getBotCategory($userAgent) : null; + + return view('tools.visitor-tracker', [ + 'summary' => $stats->summary(), + 'browsers' => $stats->browserStats(5), + 'platforms' => $stats->platformStats(5), + 'devices' => $stats->deviceStats(), + 'topPages' => $stats->mostVisitedPages(5), + 'userAgent' => $userAgent, + 'parsedUA' => $parsedUA, + 'isBot' => $isBot, + 'botName' => $botName, + 'botCategory' => $botCategory, + 'visitorIp' => request()->ip(), + ]); + } } diff --git a/bootstrap/app.php b/bootstrap/app.php index c3928c5..8e7cb9e 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -12,7 +12,9 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - // + $middleware->web(append: [ + \Ghdj\VisitorTracker\Middleware\TrackVisitor::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/composer.json b/composer.json index 681cfab..4d695fc 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "require": { "php": "^8.2", "doctrine/sql-formatter": "^1.5", + "ghdj/laravel-visitor-tracker": "^1.1", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", "league/commonmark": "^2.8", diff --git a/composer.lock b/composer.lock index 34ee7ce..9c9e109 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6bb720e3c979f64f44a17196c295bcb9", + "content-hash": "0a858fe21191d8c1887ca6ad34dbbee9", "packages": [ { "name": "brick/math", @@ -634,6 +634,83 @@ ], "time": "2023-10-12T05:21:21+00:00" }, + { + "name": "ghdj/laravel-visitor-tracker", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/GhDj/laravel-visitor-tracker.git", + "reference": "b4b28d3a22d46b39798d53dc47f5a3672633a873" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GhDj/laravel-visitor-tracker/zipball/b4b28d3a22d46b39798d53dc47f5a3672633a873", + "reference": "b4b28d3a22d46b39798d53dc47f5a3672633a873", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0|^11.0|^12.0", + "illuminate/database": "^10.0|^11.0|^12.0", + "illuminate/http": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.0", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^7.0|^8.0", + "orchestra/testbench": "^8.0|^9.0|^10.0", + "pestphp/pest": "^2.34|^3.0", + "pestphp/pest-plugin-laravel": "^2.3|^3.0", + "phpstan/phpstan": "^1.10|^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "VisitorTracker": "Ghdj\\VisitorTracker\\Facades\\VisitorTracker" + }, + "providers": [ + "Ghdj\\VisitorTracker\\VisitorTrackerServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Helpers/helpers.php" + ], + "psr-4": { + "Ghdj\\VisitorTracker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "ghdj", + "role": "Developer" + } + ], + "description": "A comprehensive visitor tracking package for Laravel applications with analytics, geolocation, and bot detection - zero external dependencies", + "homepage": "https://github.com/ghdj/laravel-visitor-tracker", + "keywords": [ + "analytics", + "bot-detection", + "geolocation", + "laravel", + "statistics", + "tracking", + "visitor", + "zero-dependency" + ], + "support": { + "issues": "https://github.com/GhDj/laravel-visitor-tracker/issues", + "source": "https://github.com/GhDj/laravel-visitor-tracker/tree/v1.1.0" + }, + "time": "2026-04-28T11:53:44+00:00" + }, { "name": "graham-campbell/result-type", "version": "v1.1.3", diff --git a/config/visitor-tracker.php b/config/visitor-tracker.php new file mode 100644 index 0000000..97c245e --- /dev/null +++ b/config/visitor-tracker.php @@ -0,0 +1,323 @@ + env('VISITOR_TRACKER_ENABLED', true), + + /* + |-------------------------------------------------------------------------- + | Database Tables + |-------------------------------------------------------------------------- + | + | Customize the table names used by the package. + | + */ + 'tables' => [ + 'visitors' => 'visitors', + 'visits' => 'visits', + ], + + /* + |-------------------------------------------------------------------------- + | Cookie Settings + |-------------------------------------------------------------------------- + | + | Settings for the visitor identification cookie. + | + */ + 'cookie' => [ + 'name' => 'visitor_tracker', + 'expiration' => 60 * 24 * 365, // 1 year in minutes + ], + + /* + |-------------------------------------------------------------------------- + | Tracking Exclusions + |-------------------------------------------------------------------------- + | + | Define what should be excluded from tracking. + | + */ + 'exclude' => [ + /* + |---------------------------------------------------------------------- + | Excluded Paths + |---------------------------------------------------------------------- + | + | Path patterns to exclude from tracking. Supports wildcards (*). + | + */ + 'paths' => [ + 'api/*', + 'admin/*', + '_debugbar/*', + 'telescope/*', + 'horizon/*', + 'livewire/*', + 'sanctum/*', + ], + + /* + |---------------------------------------------------------------------- + | Excluded HTTP Methods + |---------------------------------------------------------------------- + | + | HTTP methods to exclude from tracking. + | + */ + 'methods' => [ + 'OPTIONS', + 'HEAD', + ], + + /* + |---------------------------------------------------------------------- + | Excluded Status Codes + |---------------------------------------------------------------------- + | + | Response status codes to exclude from tracking. + | + */ + 'status_codes' => [ + 301, + 302, + 307, + 308, + 404, + 500, + ], + + /* + |---------------------------------------------------------------------- + | Excluded IPs + |---------------------------------------------------------------------- + | + | IP addresses to exclude from tracking. Supports CIDR notation. + | + */ + 'ips' => [ + // '127.0.0.1', + // '192.168.0.0/16', + ], + + /* + |---------------------------------------------------------------------- + | Excluded User Agents + |---------------------------------------------------------------------- + | + | User agent patterns to exclude (case-insensitive partial match). + | + */ + 'user_agents' => [ + // 'curl', + // 'postman', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Bot Detection + |-------------------------------------------------------------------------- + | + | Configuration for detecting and handling bots/crawlers. + | + */ + 'bots' => [ + 'track' => false, // Set to true to track bot visits + 'detect' => true, // Enable bot detection + + // Additional bot patterns (merged with built-in patterns) + 'additional_patterns' => [ + // 'custom-bot', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Agent Parser + |-------------------------------------------------------------------------- + | + | Configuration for the native user agent parser. + | + */ + 'parser' => [ + // Additional browser patterns (merged with built-in patterns) + 'additional_browsers' => [ + // 'CustomBrowser' => '/CustomBrowser\/([0-9.]+)/', + ], + + // Additional platform patterns (merged with built-in patterns) + 'additional_platforms' => [ + // 'CustomOS' => '/CustomOS ([0-9.]+)/', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Geolocation + |-------------------------------------------------------------------------- + | + | Configuration for IP geolocation services. + | Uses Laravel's HTTP client - no external packages required. + | + */ + 'geolocation' => [ + 'enabled' => env('VISITOR_TRACKER_GEOLOCATION', true), + 'provider' => env('VISITOR_TRACKER_GEO_PROVIDER', 'ip-api'), // ip-api, ipinfo, ipapi + 'api_key' => env('VISITOR_TRACKER_GEO_API_KEY'), + 'cache_days' => 7, + 'timeout' => 5, // HTTP request timeout in seconds + ], + + /* + |-------------------------------------------------------------------------- + | Privacy & GDPR + |-------------------------------------------------------------------------- + | + | Privacy-related settings for compliance. + | + */ + 'privacy' => [ + /* + |---------------------------------------------------------------------- + | GDPR Safe Mode + |---------------------------------------------------------------------- + | + | When enabled, the tracker will NOT collect any personal data, + | allowing you to track anonymous aggregate statistics without + | requiring user consent under GDPR. + | + | This mode disables: + | - IP address storage (not even anonymized) + | - User ID association + | - Persistent cookies (uses session-only identification) + | - Full user agent storage + | - Precise geolocation (city, region, coordinates) + | + | Only collected: + | - Page view counts (aggregate) + | - Browser name (Chrome, Firefox, etc.) + | - Platform name (Windows, macOS, etc.) + | - Device type (mobile, desktop, tablet) + | - Country (broad location only) + | - Referrer domain + | + */ + 'gdpr_safe_mode' => env('VISITOR_TRACKER_GDPR_SAFE', false), + + 'anonymize_ip' => env('VISITOR_TRACKER_ANONYMIZE_IP', false), + 'respect_dnt' => env('VISITOR_TRACKER_RESPECT_DNT', true), // Do Not Track header + ], + + /* + |-------------------------------------------------------------------------- + | Data Retention + |-------------------------------------------------------------------------- + | + | How long to keep visitor data. Set to null for indefinite retention. + | + */ + 'retention' => [ + 'days' => env('VISITOR_TRACKER_RETENTION_DAYS', 90), + ], + + /* + |-------------------------------------------------------------------------- + | Queue + |-------------------------------------------------------------------------- + | + | Enable queue for async tracking to improve performance. + | + */ + 'queue' => [ + 'enabled' => env('VISITOR_TRACKER_QUEUE', false), + 'connection' => env('VISITOR_TRACKER_QUEUE_CONNECTION', 'default'), + 'queue' => env('VISITOR_TRACKER_QUEUE_NAME', 'default'), + ], + + /* + |-------------------------------------------------------------------------- + | Cache + |-------------------------------------------------------------------------- + | + | Cache settings for statistics. + | + */ + 'cache' => [ + 'enabled' => true, + 'ttl' => 60, // Cache TTL in minutes + 'prefix' => 'visitor_tracker_', + ], + + /* + |-------------------------------------------------------------------------- + | Online Threshold + |-------------------------------------------------------------------------- + | + | Minutes of inactivity before a visitor is considered offline. + | + */ + 'online_threshold' => 5, + + /* + |-------------------------------------------------------------------------- + | API Routes + |-------------------------------------------------------------------------- + | + | Enable built-in API routes for statistics. + | + */ + 'api' => [ + 'enabled' => false, + 'prefix' => 'api/visitor-tracker', + 'middleware' => ['api', 'auth:sanctum'], + ], + + /* + |-------------------------------------------------------------------------- + | Dashboard + |-------------------------------------------------------------------------- + | + | Enable built-in dashboard routes. Run `php artisan visitor-tracker:install-dashboard` + | to enable the dashboard and publish the necessary files. + | + | IMPORTANT: Always protect your dashboard with at least one of these methods: + | - token: Secret token for sites without authentication + | - middleware: Laravel auth middleware for sites with authentication + | - gate: Laravel Gate for role-based access control + | + */ + 'dashboard' => [ + 'enabled' => env('VISITOR_TRACKER_DASHBOARD_ENABLED', true), + 'prefix' => 'admin/visitor-tracker', + + /* + |---------------------------------------------------------------------- + | Secret Token Authentication + |---------------------------------------------------------------------- + | + | For sites WITHOUT user authentication, use a secret token to protect + | the dashboard. Store this in your .env file (never commit it!). + | + | In local: set VISITOR_TRACKER_TOKEN to require it. + | In production: leave VISITOR_TRACKER_TOKEN unset — Cloudflare Access + | gates dev-tools.online/admin/* at the edge, and we set + | VISITOR_TRACKER_ALLOW_UNPROTECTED=true on the server so the + | package's boot-time guard doesn't trip. + */ + 'token' => env('VISITOR_TRACKER_TOKEN'), + + 'middleware' => ['web'], + + 'gate' => null, + + 'allow_unprotected' => env('VISITOR_TRACKER_ALLOW_UNPROTECTED', false), + ], +]; diff --git a/cron/migrate.php b/cron/migrate.php new file mode 100644 index 0000000..9706754 --- /dev/null +++ b/cron/migrate.php @@ -0,0 +1,34 @@ + + * - Frequency: hourly is plenty (migrations are infrequent and idempotent) + * + * Output (Artisan::output()) is echoed and OVH captures it in the task's + * execution log inside the OVH manager. Migrations are idempotent — running + * this on an empty migration queue is a no-op. + */ + +$root = dirname(__DIR__); +chdir($root); + +require $root.'/vendor/autoload.php'; + +/** @var \Illuminate\Foundation\Application $app */ +$app = require $root.'/bootstrap/app.php'; + +/** @var \Illuminate\Contracts\Console\Kernel $kernel */ +$kernel = $app->make(\Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->call('migrate', ['--force' => true]); + +echo $kernel->output(); + +exit($status); diff --git a/resources/views/tools/visitor-tracker.blade.php b/resources/views/tools/visitor-tracker.blade.php new file mode 100644 index 0000000..1f15265 --- /dev/null +++ b/resources/views/tools/visitor-tracker.blade.php @@ -0,0 +1,299 @@ +@extends('layouts.app') + +@section('title', 'Laravel Visitor Tracker - Server-Side Analytics Demo | Dev Tools') +@section('meta_description', 'Laravel Visitor Tracker - A server-side analytics package for Laravel with bot detection, device parsing, and GDPR compliance. Unblockable by ad blockers.') +@section('meta_keywords', 'laravel visitor tracker, laravel analytics, server-side tracking, bot detection, user agent parser, gdpr analytics, laravel package') + +@push('schema') + +@endpush + +@section('content') +
Server-side analytics for Laravel - Unblockable by ad blockers
+Real data collected by this package running on this site:
+ +{{ number_format($summary['total_visitors']) }}
+Total Visitors
+{{ number_format($summary['total_page_views']) }}
+Page Views
+{{ number_format($summary['online_visitors']) }}
+Online Now
+{{ number_format($summary['today_visitors']) }}
+Today
+This is what the package detects about your current visit:
+ +User Agent:
+{{ $userAgent }}
+No data yet
+ @endforelse +No data yet
+ @endforelse +| Path | +Views | +Unique | +
|---|---|---|
| /{{ $page->path }} | +{{ number_format($page->visits) }} | +{{ number_format($page->unique_visitors) }} | +
Unblockable
+Server-side tracking can't be blocked by ad blockers
+Native Detection
+100+ bot patterns, browser/device parsing - no dependencies
+GDPR Compliant
+GDPR Safe Mode, IP anonymization, DNT support
+Zero Dependencies
+Only uses Laravel's built-in features
+Geolocation
+IP-based location via free APIs (optional)
+Built-in Dashboard
+Tailwind CSS dashboard with token auth
+1. Install via Composer:
+composer require ghdj/laravel-visitor-tracker
+ 2. Publish config and run migrations:
+php artisan vendor:publish --tag="visitor-tracker-config"
+php artisan migrate
+ 3. Add middleware to your routes:
+// In bootstrap/app.php (Laravel 11+)
+->withMiddleware(function (Middleware $middleware) {
+ $middleware->web(append: [
+ \Ghdj\VisitorTracker\Middleware\TrackVisitor::class,
+ ]);
+})
+ 4. Use it:
+use Ghdj\VisitorTracker\Facades\VisitorTracker;
+
+// Get statistics
+$stats = VisitorTracker::stats()->summary();
+$online = VisitorTracker::stats()->onlineVisitors();
+$topPages = VisitorTracker::stats()->mostVisitedPages(10);
+ Laravel Visitor Tracker v1.0.0 - MIT License
++ Documentation + · + Packagist +
+