Initial commit: Animexe Laravel platform

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 00:01:48 +03:00
co-authored by Claude Opus 4.8
commit a63515cfc6
366 changed files with 74773 additions and 0 deletions
@@ -0,0 +1,194 @@
<?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';
}
}