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'; } }