78 lines
2.8 KiB
PHP
78 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use App\Models\UserActivityLog;
|
|
use Illuminate\Http\Request;
|
|
|
|
class ActivityLogger
|
|
{
|
|
public static function log(
|
|
string $action,
|
|
?int $userId = null,
|
|
?string $subjectType = null,
|
|
?int $subjectId = null,
|
|
?array $meta = null,
|
|
?Request $request = null
|
|
): void {
|
|
try {
|
|
$req = $request ?? request();
|
|
$ip = $req->ip();
|
|
$ua = $req->userAgent() ?? '';
|
|
$isBot = (bool) $req->attributes->get('is_bot', false);
|
|
$geo = cache()->remember("geo_{$ip}", 3600, fn() => self::geoIp($ip));
|
|
|
|
UserActivityLog::create([
|
|
'user_id' => $userId ?? auth()->id(),
|
|
'session_id' => session()->getId(),
|
|
'action' => $action,
|
|
'subject_type' => $subjectType,
|
|
'subject_id' => $subjectId,
|
|
'ip' => $ip,
|
|
'country' => $geo['country'] ?? null,
|
|
'city' => $geo['city'] ?? null,
|
|
'device' => self::device($ua),
|
|
'browser' => self::browser($ua),
|
|
'user_agent' => mb_substr($ua, 0, 500),
|
|
'is_bot' => $isBot,
|
|
'meta' => $meta,
|
|
'created_at' => now(),
|
|
]);
|
|
} catch (\Throwable) {
|
|
// never break the app
|
|
}
|
|
}
|
|
|
|
private static function geoIp(string $ip): array
|
|
{
|
|
try {
|
|
if (in_array($ip, ['127.0.0.1', '::1']) || str_starts_with($ip, '192.168.') || str_starts_with($ip, '10.')) {
|
|
return ['country' => 'Local', 'city' => null];
|
|
}
|
|
$r = \Illuminate\Support\Facades\Http::timeout(2)->get("http://ip-api.com/json/{$ip}?fields=country,city,status");
|
|
$d = $r->json();
|
|
return ($d['status'] ?? '') === 'success' ? ['country' => $d['country'], 'city' => $d['city']] : [];
|
|
} catch (\Throwable) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
private static function device(string $ua): string
|
|
{
|
|
$ua = strtolower($ua);
|
|
if (str_contains($ua, 'mobile') || str_contains($ua, 'android') || str_contains($ua, 'iphone')) return 'mobile';
|
|
if (str_contains($ua, 'tablet') || str_contains($ua, 'ipad')) return 'tablet';
|
|
return 'desktop';
|
|
}
|
|
|
|
private static function browser(string $ua): string
|
|
{
|
|
if (str_contains($ua, 'Chrome')) return 'Chrome';
|
|
if (str_contains($ua, 'Firefox')) return 'Firefox';
|
|
if (str_contains($ua, 'Safari')) return 'Safari';
|
|
if (str_contains($ua, 'Edge')) return 'Edge';
|
|
if (str_contains($ua, 'Opera')) return 'Opera';
|
|
return 'Other';
|
|
}
|
|
}
|