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,227 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Anime;
use App\Models\Episode;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class HealthController extends Controller
{
public function index()
{
// 1. Duplike anime — aynı MAL ID'ye sahip birden fazla anime
$malDuplicates = DB::table('animes')
->whereNotNull('mal_id')
->where('mal_id', '>', 0)
->select('mal_id', DB::raw('COUNT(*) as cnt'))
->groupBy('mal_id')
->having('cnt', '>', 1)
->get()
->map(function ($row) {
$animes = Anime::where('mal_id', $row->mal_id)
->withCount('episodes')
->get(['id', 'title', 'slug', 'mal_id', 'created_at']);
return ['mal_id' => $row->mal_id, 'animes' => $animes];
});
// 2. Karışık kaynak — aynı anime içinde hem animecix hem anizium bölüm var
$mixedSources = DB::table('episodes')
->whereIn('source', ['anizium', 'animecix'])
->whereNotNull('anime_id')
->select('anime_id', 'source', DB::raw('COUNT(*) as cnt'))
->groupBy('anime_id', 'source')
->get()
->groupBy('anime_id')
->filter(fn($group) => $group->pluck('source')->unique()->count() > 1)
->map(function ($group) {
$anime = Anime::find($group->first()->anime_id, ['id', 'title', 'slug']);
if (!$anime) return null;
$sources = $group->mapWithKeys(fn($r) => [$r->source => $r->cnt]);
return ['anime' => $anime, 'sources' => $sources];
})
->filter()
->values();
// 3. Eksik bölümler — episode_count > gerçek bölüm sayısı
$missingEpisodes = Anime::whereNotNull('episode_count')
->where('episode_count', '>', 0)
->withCount('episodes')
->get(['id', 'title', 'slug', 'episode_count'])
->filter(fn($a) => $a->episodes_count < $a->episode_count)
->map(fn($a) => [
'id' => $a->id,
'title' => $a->title,
'slug' => $a->slug,
'expected' => $a->episode_count,
'actual' => $a->episodes_count,
'missing' => $a->episode_count - $a->episodes_count,
])
->sortByDesc('missing')
->values();
// 4. Harici CDN bölümler — BunnyCDN'e taşınmamış, Anizium CDN'de kalan
$externalCount = Episode::whereNull('video_url')
->where(function ($q) {
$q->where('m3u8_url', 'like', '%aniziumserver%')
->orWhere('m3u8_url', 'like', '%anizium%');
})
->count();
$externalSample = Episode::whereNull('video_url')
->where(function ($q) {
$q->where('m3u8_url', 'like', '%aniziumserver%')
->orWhere('m3u8_url', 'like', '%anizium%');
})
->with('anime:id,title,slug')
->select('id', 'anime_id', 'season_id', 'episode_number', 'm3u8_url', 'source')
->orderByDesc('id')
->limit(100)
->get();
// 5. Sıfır bölümlü animeler
$zeroEpisodeAnimes = Anime::whereDoesntHave('episodes')
->get(['id', 'title', 'slug', 'created_at']);
return view('admin.health.index', compact(
'malDuplicates',
'mixedSources',
'missingEpisodes',
'externalCount',
'externalSample',
'zeroEpisodeAnimes'
));
}
// ── Sistem Temizliği ─────────────────────────────────────────────────────
/**
* Depolama istatistiklerini döndür inode tüketimini gösterir.
*/
public function storageStats()
{
$dirs = [
'seg_cache' => storage_path('app/seg_cache'),
'cache_data' => storage_path('framework/cache/data'),
'sessions' => storage_path('framework/sessions'),
'views' => storage_path('framework/views'),
'logs' => storage_path('logs'),
'app_public' => storage_path('app/public'),
];
$stats = [];
foreach ($dirs as $key => $path) {
if (!is_dir($path)) {
$stats[$key] = ['count' => 0, 'size' => 0, 'path' => $path];
continue;
}
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS));
$count = 0;
$size = 0;
foreach ($files as $f) {
$count++;
$size += $f->getSize();
}
$stats[$key] = ['count' => $count, 'size' => $size, 'path' => $path];
}
return response()->json(['stats' => $stats, 'total_files' => array_sum(array_column($stats, 'count'))]);
}
/**
* Belirtilen depolama dizinini temizle.
*/
public function cleanupStorage(Request $request)
{
$target = $request->input('target');
$allowed = [
'seg_cache' => storage_path('app/seg_cache'),
'cache_data' => storage_path('framework/cache/data'),
'sessions' => storage_path('framework/sessions'),
'views' => storage_path('framework/views'),
'old_logs' => storage_path('logs'),
];
if (!array_key_exists($target, $allowed)) {
return response()->json(['error' => 'Geçersiz hedef.'], 422);
}
$path = $allowed[$target];
$deleted = 0;
if (!is_dir($path)) {
return response()->json(['ok' => true, 'deleted' => 0, 'message' => 'Dizin yok.']);
}
if ($target === 'old_logs') {
// Logları tamamen silme — sadece 7 günden eskilerini sil
foreach (glob($path . '/*.log') ?: [] as $f) {
if (filemtime($f) < time() - 604800) { // 7 gün
@unlink($f);
$deleted++;
}
}
// Laravel her gün yeni log açar, bugünküne dokunma
} else {
// Diğer dizinler: tümünü temizle
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $f) {
if ($f->isFile()) {
@unlink($f->getRealPath());
$deleted++;
} elseif ($f->isDir()) {
@rmdir($f->getRealPath());
}
}
}
// Laravel cache'i PHP seviyesinde de temizle
if ($target === 'cache_data') {
try { \Illuminate\Support\Facades\Cache::flush(); } catch (\Throwable) {}
}
return response()->json([
'ok' => true,
'deleted' => $deleted,
'message' => "{$deleted} dosya silindi.",
]);
}
/**
* Session driver bilgisi + önerisi.
*/
public function sessionInfo()
{
$driver = config('session.driver', 'file');
$sessionPath = storage_path('framework/sessions');
$sessionCount = is_dir($sessionPath) ? count(glob($sessionPath . '/*') ?: []) : 0;
return response()->json([
'driver' => $driver,
'session_files' => $sessionCount,
'recommendation'=> $driver === 'file'
? 'SESSION_DRIVER=database veya cookie kullanmanız önerilir (inode tasarrufu).'
: 'Session sürücüsü inode-dostu.',
]);
}
public function deleteAnime(Request $request, Anime $anime)
{
$title = $anime->title;
$anime->delete();
return back()->with('success', "\"$title\" silindi.");
}
public function deleteSourceEpisodes(Request $request, Anime $anime)
{
$source = $request->validate(['source' => 'required|in:anizium,animecix'])['source'];
$count = Episode::where('anime_id', $anime->id)->where('source', $source)->count();
Episode::where('anime_id', $anime->id)->where('source', $source)->delete();
return back()->with('success', "$anime->title$source kaynağından $count bölüm silindi.");
}
}