diff --git a/app/Http/Middleware/RecordUserActivity.php b/app/Http/Middleware/RecordUserActivity.php new file mode 100644 index 00000000..a8f8dc1f --- /dev/null +++ b/app/Http/Middleware/RecordUserActivity.php @@ -0,0 +1,41 @@ +ip(); + $position = Location::get($whoIsIp); + + dd($position); + + // Perform action + if (Auth::id() !== null) { + UserActivity::create([ + 'url' => config('app.url').'/'.$request->path(), + 'user_id' => Auth::id(), + 'ip' => $whoIsIp, + 'country' => $position->countryName, + 'location' => $position->countryName, + ]); + } + + return $response; + } +} diff --git a/app/Models/UserActivity.php b/app/Models/UserActivity.php new file mode 100644 index 00000000..46e06f74 --- /dev/null +++ b/app/Models/UserActivity.php @@ -0,0 +1,16 @@ + 'datetime', + ]; +} diff --git a/composer.json b/composer.json index fe213890..dba3c2a8 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", "outhebox/blade-flags": "^1.5", + "stevebauman/location": "^7.6", "swisnl/filament-backgrounds": "^2.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 2164e300..42f42e98 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": "872a298c0c1cebe00cac20f0cb2539f7", + "content-hash": "8f50a22045a498496eb4e857574e3649", "packages": [ { "name": "agencetwogether/hookshelper", @@ -949,6 +949,78 @@ }, "time": "2026-01-21T12:59:34+00:00" }, + { + "name": "composer/ca-bundle", + "version": "1.5.10", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/961a5e4056dd2e4a2eedcac7576075947c28bf63", + "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.5.10" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-12-08T15:06:51+00:00" + }, { "name": "danharrin/date-format-converter", "version": "v0.3.1", @@ -1985,6 +2057,64 @@ ], "time": "2025-12-03T09:33:47+00:00" }, + { + "name": "geoip2/geoip2", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/maxmind/GeoIP2-php.git", + "reference": "49fceddd694295e76e970a32848e03bb19e56b42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maxmind/GeoIP2-php/zipball/49fceddd694295e76e970a32848e03bb19e56b42", + "reference": "49fceddd694295e76e970a32848e03bb19e56b42", + "shasum": "" + }, + "require": { + "ext-json": "*", + "maxmind-db/reader": "^1.13.0", + "maxmind/web-service-common": "~0.11", + "php": ">=8.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.*", + "phpstan/phpstan": "*", + "phpunit/phpunit": "^10.0", + "squizlabs/php_codesniffer": "4.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "GeoIp2\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https://www.maxmind.com/" + } + ], + "description": "MaxMind GeoIP2 PHP API", + "homepage": "https://github.com/maxmind/GeoIP2-php", + "keywords": [ + "IP", + "geoip", + "geoip2", + "geolocation", + "maxmind" + ], + "support": { + "issues": "https://github.com/maxmind/GeoIP2-php/issues", + "source": "https://github.com/maxmind/GeoIP2-php/tree/v3.3.0" + }, + "time": "2025-11-20T18:50:15+00:00" + }, { "name": "graham-campbell/result-type", "version": "v1.1.4", @@ -4170,6 +4300,121 @@ }, "time": "2025-07-25T09:04:22+00:00" }, + { + "name": "maxmind-db/reader", + "version": "v1.13.1", + "source": { + "type": "git", + "url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git", + "reference": "2194f58d0f024ce923e685cdf92af3daf9951908" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/2194f58d0f024ce923e685cdf92af3daf9951908", + "reference": "2194f58d0f024ce923e685cdf92af3daf9951908", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "conflict": { + "ext-maxminddb": "<1.11.1 || >=2.0.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.*", + "phpstan/phpstan": "*", + "phpunit/phpunit": ">=8.0.0,<10.0.0", + "squizlabs/php_codesniffer": "4.*" + }, + "suggest": { + "ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder", + "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups", + "maxmind-db/reader-ext": "C extension for significantly faster IP lookups (install via PIE: pie install maxmind-db/reader-ext)" + }, + "type": "library", + "autoload": { + "psr-4": { + "MaxMind\\Db\\": "src/MaxMind/Db" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory J. Oschwald", + "email": "goschwald@maxmind.com", + "homepage": "https://www.maxmind.com/" + } + ], + "description": "MaxMind DB Reader API", + "homepage": "https://github.com/maxmind/MaxMind-DB-Reader-php", + "keywords": [ + "database", + "geoip", + "geoip2", + "geolocation", + "maxmind" + ], + "support": { + "issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues", + "source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.13.1" + }, + "time": "2025-11-21T22:24:26+00:00" + }, + { + "name": "maxmind/web-service-common", + "version": "v0.11.1", + "source": { + "type": "git", + "url": "https://github.com/maxmind/web-service-common-php.git", + "reference": "c309236b5a5555b96cf560089ec3cead12d845d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maxmind/web-service-common-php/zipball/c309236b5a5555b96cf560089ec3cead12d845d2", + "reference": "c309236b5a5555b96cf560089ec3cead12d845d2", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0.3", + "ext-curl": "*", + "ext-json": "*", + "php": ">=8.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.*", + "phpstan/phpstan": "*", + "phpunit/phpunit": "^10.0", + "squizlabs/php_codesniffer": "4.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "MaxMind\\Exception\\": "src/Exception", + "MaxMind\\WebService\\": "src/WebService" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Gregory Oschwald", + "email": "goschwald@maxmind.com" + } + ], + "description": "Internal MaxMind Web Service API", + "homepage": "https://github.com/maxmind/web-service-common-php", + "support": { + "issues": "https://github.com/maxmind/web-service-common-php/issues", + "source": "https://github.com/maxmind/web-service-common-php/tree/v0.11.1" + }, + "time": "2026-01-13T17:56:03+00:00" + }, { "name": "mobiledetect/mobiledetectlib", "version": "2.8.45", @@ -6348,6 +6593,75 @@ ], "time": "2026-02-01T09:30:04+00:00" }, + { + "name": "stevebauman/location", + "version": "v7.6.2", + "source": { + "type": "git", + "url": "https://github.com/stevebauman/location.git", + "reference": "2f7686b685b924f193d6a6b82074861b8cc5aa3a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stevebauman/location/zipball/2f7686b685b924f193d6a6b82074861b8cc5aa3a", + "reference": "2f7686b685b924f193d6a6b82074861b8cc5aa3a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "geoip2/geoip2": "^2.0|^3.0", + "guzzlehttp/guzzle": "^7.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "php": ">=8.1" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", + "pestphp/pest": "^1.0|^2.0|^3.7" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Location": "Stevebauman\\Location\\Facades\\Location" + }, + "providers": [ + "Stevebauman\\Location\\LocationServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Stevebauman\\Location\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steve Bauman", + "email": "steven_bauman@outlook.com" + } + ], + "description": "Retrieve a user's location by their IP Address", + "keywords": [ + "IP", + "geo", + "geo-location", + "geoip", + "laravel", + "location", + "php" + ], + "support": { + "issues": "https://github.com/stevebauman/location/issues", + "source": "https://github.com/stevebauman/location/tree/v7.6.2" + }, + "time": "2026-02-11T16:06:17+00:00" + }, { "name": "swisnl/filament-backgrounds", "version": "2.0.2", diff --git a/config/location.php b/config/location.php new file mode 100644 index 00000000..9f96276d --- /dev/null +++ b/config/location.php @@ -0,0 +1,143 @@ + Stevebauman\Location\Drivers\IpApi::class, + + /* + |-------------------------------------------------------------------------- + | Driver Fallbacks + |-------------------------------------------------------------------------- + | + | The drivers you want to use to retrieve the user's location + | if the above selected driver is unavailable. + | + | These will be called upon in order (first to last). + | + */ + + 'fallbacks' => [ + Stevebauman\Location\Drivers\Ip2locationio::class, + Stevebauman\Location\Drivers\IpInfo::class, + Stevebauman\Location\Drivers\GeoPlugin::class, + Stevebauman\Location\Drivers\MaxMind::class, + ], + + /* + |-------------------------------------------------------------------------- + | Position + |-------------------------------------------------------------------------- + | + | Here you may configure the position instance that is created + | and returned from the above drivers. The instance you + | create must extend the built-in Position class. + | + */ + + 'position' => Stevebauman\Location\Position::class, + + /* + |-------------------------------------------------------------------------- + | HTTP Client Options + |-------------------------------------------------------------------------- + | + | Here you may configure the options used by the underlying + | Laravel HTTP client. This will be used in drivers that + | request info via HTTP requests through API services. + | + */ + + 'http' => [ + 'timeout' => 3, + 'connect_timeout' => 3, + ], + + /* + |-------------------------------------------------------------------------- + | Localhost Testing + |-------------------------------------------------------------------------- + | + | If your running your website locally and want to test different + | IP addresses to see location detection, set 'enabled' to true. + | + | The testing IP address is a Google host in the United-States. + | + */ + + 'testing' => [ + 'ip' => '66.102.0.0', + 'enabled' => env('LOCATION_TESTING', true), + ], + + /* + |-------------------------------------------------------------------------- + | MaxMind Configuration + |-------------------------------------------------------------------------- + | + | If web service is enabled, you must fill in your user ID and license key. + | + | If web service is disabled, it will try and retrieve the user's location + | from the MaxMind database file located in the local path below. + | + | The MaxMind database file can be either City (default) or Country (smaller). + | + */ + + 'maxmind' => [ + 'license_key' => env('MAXMIND_LICENSE_KEY'), + + 'web' => [ + 'enabled' => false, + 'user_id' => env('MAXMIND_USER_ID'), + 'locales' => ['en'], + 'options' => ['host' => 'geoip.maxmind.com'], + ], + + 'local' => [ + 'type' => 'city', + 'path' => database_path('maxmind/GeoLite2-City.mmdb'), + 'url' => sprintf('https://download.maxmind.com/app/geoip_download_by_token?edition_id=GeoLite2-City&license_key=%s&suffix=tar.gz', env('MAXMIND_LICENSE_KEY')), + ], + ], + + 'ip_api' => [ + 'token' => env('IP_API_TOKEN'), + ], + + 'ipinfo' => [ + 'token' => env('IPINFO_TOKEN'), + ], + + 'ipdata' => [ + 'token' => env('IPDATA_TOKEN'), + ], + + 'ip2locationio' => [ + 'token' => env('IP2LOCATIONIO_TOKEN'), + ], + + /* + |-------------------------------------------------------------------------- + | Kloudend ~ ipapi.co Configuration + |-------------------------------------------------------------------------- + | + | The configuration for the Kloudend driver. + | + */ + + 'kloudend' => [ + + 'token' => env('KLOUDEND_TOKEN'), + + ], + +]; diff --git a/database/migrations/2026_02_11_171641_create_user_activities_table.php b/database/migrations/2026_02_11_171641_create_user_activities_table.php new file mode 100644 index 00000000..73b276dc --- /dev/null +++ b/database/migrations/2026_02_11_171641_create_user_activities_table.php @@ -0,0 +1,34 @@ +id(); + + $table->unsignedInteger('user_id')->nullable(); + $table->string('url'); + $table->string('ip'); + $table->string('country'); + $table->string('location'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_activities'); + } +};