Implement device and IP tracking for users
Added Device and UserIp models and migrations to store device fingerprints and user IPs with expiration. Updated ShowSavingIp Livewire component and view to capture and persist both IP and device fingerprint. Introduced a scheduled command to clean expired user IPs and registered it in the console kernel. Removed the old migration for storing IPs directly on the users table.
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\UserIp;
|
||||
|
||||
class CleanExpiredUserIps extends Command
|
||||
{
|
||||
protected $signature = 'userips:clean';
|
||||
protected $description = 'Elimina las IPs de usuario expiradas (más de 12h)';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$count = UserIp::where('expires_at', '<', now())->delete();
|
||||
$this->info("Se eliminaron {$count} IPs expiradas.");
|
||||
}
|
||||
}
|
||||
@@ -30,11 +30,13 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->command('userips:clean')->hourly();
|
||||
|
||||
// $schedule->exec('nohup php /home/u111286300/domains/sirpremium.com.co/public_html/app/artisan queue:work --sleep=3 --tries=3 > /dev/null 2>&1 &')->everyMinute();
|
||||
|
||||
|
||||
// $schedule->exec('nohup php /var/www/sirpremium/artisan queue:work --sleep=3 --tries=3 > /dev/null 2>&1 &')->everyMinute();
|
||||
$schedule->command('telescope:prune --hours=24')->daily();
|
||||
|
||||
|
||||
$schedule->command('tarea:validarCompra')->everyFiveMinutes();
|
||||
|
||||
|
||||
@@ -100,10 +102,10 @@ class Kernel extends ConsoleKernel
|
||||
//curl_close($curl);
|
||||
//return $response;
|
||||
// if ($err) {
|
||||
//
|
||||
//
|
||||
|
||||
//} else {
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -4,24 +4,53 @@ namespace App\Http\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\UserIp;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Device;
|
||||
|
||||
class ShowSavingIp extends Component
|
||||
{
|
||||
public $ip_address;
|
||||
public $device_fingerprint;
|
||||
|
||||
protected $listeners = ['setIpAddress'];
|
||||
protected $listeners = ['setDeviceData'];
|
||||
|
||||
public function setIpAddress($ip)
|
||||
public function setDeviceData($data)
|
||||
{
|
||||
$this->ip_address = $ip;
|
||||
$this->ip_address = $data['ip'] ?? null;
|
||||
$this->device_fingerprint = $data['fingerprint'] ?? null;
|
||||
|
||||
$user = Auth::user();
|
||||
if ($user) {
|
||||
$user->ip_address = $ip;
|
||||
$user->save();
|
||||
if (!$user) return;
|
||||
|
||||
// Guardar IP pública temporal (12h)
|
||||
if ($this->ip_address) {
|
||||
UserIp::updateOrCreate(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'ip_address' => $this->ip_address,
|
||||
],
|
||||
[
|
||||
'expires_at' => Carbon::now()->addHours(12),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Guardar fingerprint permanente del dispositivo
|
||||
if ($this->device_fingerprint) {
|
||||
Device::updateOrCreate(
|
||||
[
|
||||
'fingerprint' => $this->device_fingerprint,
|
||||
],
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'last_seen_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.show-saving-ip');
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Device extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['user_id', 'fingerprint', 'last_seen_at'];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserIp extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['user_id', 'ip_address', 'expires_at'];
|
||||
|
||||
protected $dates = ['expires_at'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('ip_address')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('ip_address');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('user_ips', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('ip_address');
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('ip_address');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_ips');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('devices', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('user_id')->nullable()->constrained()->onDelete('cascade');
|
||||
$table->string('fingerprint')->unique();
|
||||
$table->timestamp('last_seen_at')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('devices');
|
||||
}
|
||||
};
|
||||
@@ -1,23 +1,35 @@
|
||||
<div
|
||||
x-data="{
|
||||
getIp() {
|
||||
fetch('https://api.ipify.org/?format=json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
$wire.emit('setIpAddress', data.ip);
|
||||
})
|
||||
.catch(err => console.error('Error obteniendo IP:', err));
|
||||
async getData() {
|
||||
try {
|
||||
// Obtener IP pública
|
||||
const ipRes = await fetch('https://api.ipify.org/?format=json');
|
||||
const ipData = await ipRes.json();
|
||||
|
||||
// Cargar librería Fingerprint
|
||||
const fpModule = await import('https://openfpcdn.io/fingerprintjs/v3');
|
||||
const fp = await fpModule.load();
|
||||
const result = await fp.get();
|
||||
|
||||
const fingerprint = result.visitorId;
|
||||
|
||||
// Emitir ambos valores a Livewire
|
||||
$wire.emit('setDeviceData', { ip: ipData.ip, fingerprint });
|
||||
} catch (e) {
|
||||
console.error('Error obteniendo IP/Fingerprint:', e);
|
||||
}
|
||||
}
|
||||
}"
|
||||
x-init="getIp()"
|
||||
x-init="getData()"
|
||||
wire:poll.visible.delay.600000ms
|
||||
class="py-4"
|
||||
>
|
||||
<p class="text-sm text-gray-600">
|
||||
@if($ip_address)
|
||||
Tu IP actual: <strong>{{ $ip_address }}</strong>
|
||||
Tu IP actual: <strong>{{ $ip_address }}</strong><br>
|
||||
ID del dispositivo: <strong>{{ $device_fingerprint }}</strong>
|
||||
@else
|
||||
Obteniendo IP...
|
||||
Obteniendo IP y dispositivo...
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user