Initial commit: Animexe Laravel platform
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user