195 lines
7.6 KiB
PHP
195 lines
7.6 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Frontend;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\Analytics\PageView;
|
||
use App\Models\Analytics\WatchEvent;
|
||
use App\Models\Analytics\VisitorSession;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Cache;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
class TrackingController extends Controller
|
||
{
|
||
/**
|
||
* POST /track/pageview
|
||
*/
|
||
public function pageview(Request $request)
|
||
{
|
||
$data = $request->validate([
|
||
'page_type' => 'nullable|string|max:30',
|
||
'anime_id' => 'nullable|integer',
|
||
'episode_id' => 'nullable|integer',
|
||
'referrer' => 'nullable|string|max:500',
|
||
'url' => 'nullable|string|max:500',
|
||
'time_on_page'=> 'nullable|integer|min:0|max:86400',
|
||
]);
|
||
|
||
$ip = $request->ip();
|
||
$ua = $request->userAgent() ?? '';
|
||
$isBot = (bool) $request->attributes->get('is_bot', false);
|
||
$botType= $request->attributes->get('bot_type', null);
|
||
$geo = self::geoIp($ip);
|
||
$sessId = session()->getId();
|
||
|
||
PageView::create([
|
||
'user_id' => auth()->id(),
|
||
'session_id' => $sessId,
|
||
'url' => mb_substr($data['url'] ?? $request->header('Referer', ''), 0, 500),
|
||
'page_type' => $data['page_type'] ?? 'other',
|
||
'anime_id' => $data['anime_id'] ?? null,
|
||
'episode_id' => $data['episode_id'] ?? null,
|
||
'ip' => $ip,
|
||
'country' => $geo['country'] ?? null,
|
||
'city' => $geo['city'] ?? null,
|
||
'device' => self::detectDevice($ua),
|
||
'browser' => self::detectBrowser($ua),
|
||
'referrer' => mb_substr($data['referrer'] ?? '', 0, 500) ?: null,
|
||
'is_bot' => $isBot ? 1 : 0,
|
||
'user_agent' => mb_substr($ua, 0, 500),
|
||
'time_on_page'=> $data['time_on_page'] ?? 0,
|
||
'created_at' => now(),
|
||
]);
|
||
|
||
// Oturum kaydını oluştur / güncelle
|
||
$this->trackSession($sessId, $ip, $ua, $geo, $isBot, $botType, $data);
|
||
|
||
return response()->json(['ok' => true]);
|
||
}
|
||
|
||
/**
|
||
* POST /track/watch
|
||
*/
|
||
public function watch(Request $request)
|
||
{
|
||
$data = $request->validate([
|
||
'anime_id' => 'required|integer',
|
||
'episode_id' => 'nullable|integer',
|
||
'season_number' => 'required|integer|min:1',
|
||
'episode_number' => 'required|integer|min:1',
|
||
'seconds' => 'required|integer|min:0',
|
||
'total' => 'nullable|integer|min:0',
|
||
'percent' => 'nullable|integer|min:0|max:100',
|
||
]);
|
||
|
||
WatchEvent::create([
|
||
'user_id' => auth()->id(),
|
||
'session_id' => session()->getId(),
|
||
'anime_id' => $data['anime_id'],
|
||
'episode_id' => $data['episode_id'] ?? null,
|
||
'season_number' => $data['season_number'],
|
||
'episode_number' => $data['episode_number'],
|
||
'seconds_watched' => $data['seconds'],
|
||
'total_seconds' => $data['total'] ?? 0,
|
||
'percent_complete'=> $data['percent'] ?? 0,
|
||
'created_at' => now(),
|
||
]);
|
||
|
||
// Oturum izleme süresini güncelle
|
||
try {
|
||
DB::table('analytics_sessions')
|
||
->where('session_id', session()->getId())
|
||
->increment('total_seconds', (int)$data['seconds']);
|
||
} catch (\Exception) {}
|
||
|
||
return response()->json(['ok' => true]);
|
||
}
|
||
|
||
/**
|
||
* POST /track/session-end — sayfa kapanırken JS'ten gönderilir
|
||
*/
|
||
public function sessionEnd(Request $request)
|
||
{
|
||
$data = $request->validate([
|
||
'time_on_page' => 'nullable|integer|min:0|max:86400',
|
||
]);
|
||
|
||
try {
|
||
DB::table('analytics_sessions')
|
||
->where('session_id', session()->getId())
|
||
->update([
|
||
'last_seen_at' => now(),
|
||
'total_seconds'=> DB::raw('total_seconds + ' . (int)($data['time_on_page'] ?? 0)),
|
||
]);
|
||
} catch (\Exception) {}
|
||
|
||
return response()->json(['ok' => true]);
|
||
}
|
||
|
||
// ── Private helpers ──────────────────────────────────────────────────────
|
||
|
||
private function trackSession(string $sessId, string $ip, string $ua, array $geo, bool $isBot, ?string $botType, array $data): void
|
||
{
|
||
try {
|
||
$existing = DB::table('analytics_sessions')->where('session_id', $sessId)->first();
|
||
|
||
if ($existing) {
|
||
DB::table('analytics_sessions')
|
||
->where('session_id', $sessId)
|
||
->update([
|
||
'pages_visited' => DB::raw('pages_visited + 1'),
|
||
'last_seen_at' => now(),
|
||
'user_id' => auth()->id() ?? $existing->user_id,
|
||
]);
|
||
} else {
|
||
DB::table('analytics_sessions')->insert([
|
||
'session_id' => $sessId,
|
||
'user_id' => auth()->id(),
|
||
'ip' => $ip,
|
||
'country' => $geo['country'] ?? null,
|
||
'city' => $geo['city'] ?? null,
|
||
'device' => self::detectDevice($ua),
|
||
'browser' => self::detectBrowser($ua),
|
||
'referrer' => mb_substr($data['referrer'] ?? '', 0, 500) ?: null,
|
||
'landing_page' => mb_substr($data['url'] ?? '', 0, 500) ?: null,
|
||
'pages_visited'=> 1,
|
||
'total_seconds'=> 0,
|
||
'is_bot' => $isBot ? 1 : 0,
|
||
'bot_type' => $botType,
|
||
'user_agent' => mb_substr($ua, 0, 500),
|
||
'started_at' => now(),
|
||
'last_seen_at' => now(),
|
||
]);
|
||
}
|
||
} catch (\Exception) {}
|
||
}
|
||
|
||
private static function geoIp(string $ip): array
|
||
{
|
||
if ($ip === '127.0.0.1' || str_starts_with($ip, '192.168.') || str_starts_with($ip, '10.')) {
|
||
return ['country' => 'Yerel', 'city' => 'Localhost'];
|
||
}
|
||
|
||
return Cache::remember("geo_{$ip}", 86400 * 7, function () use ($ip) {
|
||
try {
|
||
$r = Http::timeout(2)->get("http://ip-api.com/json/{$ip}?fields=country,city,status");
|
||
if ($r->ok() && $r->json('status') === 'success') {
|
||
return ['country' => $r->json('country'), 'city' => $r->json('city')];
|
||
}
|
||
} catch (\Exception) {}
|
||
return ['country' => null, 'city' => null];
|
||
});
|
||
}
|
||
|
||
private static function detectDevice(string $ua): string
|
||
{
|
||
$ua = strtolower($ua);
|
||
if (str_contains($ua, 'tablet') || str_contains($ua, 'ipad')) return 'tablet';
|
||
if (str_contains($ua, 'mobile') || str_contains($ua, 'android') || str_contains($ua, 'iphone')) return 'mobile';
|
||
return 'desktop';
|
||
}
|
||
|
||
private static function detectBrowser(string $ua): string
|
||
{
|
||
if (str_contains($ua, 'Edg/')) return 'Edge';
|
||
if (str_contains($ua, 'OPR/') || str_contains($ua, 'Opera')) return 'Opera';
|
||
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, 'MSIE') || str_contains($ua, 'Trident')) return 'IE';
|
||
return 'Other';
|
||
}
|
||
}
|