Files
animexe/app/Http/Controllers/Admin/HealthController.php
T
2026-07-14 00:01:48 +03:00

228 lines
8.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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.");
}
}