mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 22:01:33 +00:00
536 lines
19 KiB
PHP
536 lines
19 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\BasePageController;
|
|
use App\Services\SystemMetricsService;
|
|
use App\Services\UserStatsService;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class AdminPageController extends BasePageController
|
|
{
|
|
protected UserStatsService $userStatsService;
|
|
|
|
protected SystemMetricsService $systemMetricsService;
|
|
|
|
public function __construct(UserStatsService $userStatsService, SystemMetricsService $systemMetricsService)
|
|
{
|
|
parent::__construct();
|
|
$this->userStatsService = $userStatsService;
|
|
$this->systemMetricsService = $systemMetricsService;
|
|
}
|
|
|
|
/**
|
|
* @throws \Exception
|
|
*/
|
|
public function index(): mixed
|
|
{
|
|
$this->setAdminPrefs();
|
|
|
|
// Cache user statistics for 2 minutes
|
|
$userStats = \Illuminate\Support\Facades\Cache::remember('admin_user_stats', 120, function () {
|
|
return [
|
|
'users_by_role' => $this->userStatsService->getUsersByRole(),
|
|
'downloads_per_hour' => $this->userStatsService->getDownloadsPerHour(168), // Last 7 days in hours
|
|
'downloads_per_minute' => $this->userStatsService->getDownloadsPerMinute(60),
|
|
'api_hits_per_hour' => $this->userStatsService->getApiHitsPerHour(168), // Last 7 days in hours
|
|
'api_hits_per_minute' => $this->userStatsService->getApiHitsPerMinute(60),
|
|
'summary' => $this->userStatsService->getSummaryStats(),
|
|
'top_downloaders' => $this->userStatsService->getTopDownloaders(5),
|
|
];
|
|
});
|
|
|
|
return view('admin.dashboard', array_merge($this->viewData, [
|
|
'meta_title' => 'Admin Home',
|
|
'meta_description' => 'Admin home page',
|
|
'userStats' => $userStats,
|
|
'stats' => $this->getDefaultStats(),
|
|
'systemMetrics' => $this->getSystemMetrics(),
|
|
'recent_activity' => $this->getRecentUserActivity(),
|
|
]));
|
|
}
|
|
|
|
/**
|
|
* Get recent user activity from the user_activities table with caching
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function getRecentUserActivity(): array
|
|
{
|
|
return \Illuminate\Support\Facades\Cache::remember('admin_recent_activity', 60, function () {
|
|
$activities = \App\Models\UserActivity::orderBy('created_at', 'desc')
|
|
->limit(10)
|
|
->get();
|
|
|
|
return $activities->map(function ($activity) {
|
|
return (object) [
|
|
'type' => $activity->activity_type,
|
|
'message' => $activity->description,
|
|
'icon' => $activity->icon,
|
|
'icon_bg' => 'bg-'.$activity->color_class.'-100 dark:bg-'.$activity->color_class.'-900',
|
|
'icon_color' => 'text-'.$activity->color_class.'-600 dark:text-'.$activity->color_class.'-400',
|
|
'created_at' => $activity->created_at,
|
|
'username' => $activity->username,
|
|
'metadata' => $activity->metadata,
|
|
];
|
|
})->toArray();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* API endpoint to get recent user activity (for auto-refresh)
|
|
*/
|
|
public function getRecentActivity(): mixed
|
|
{
|
|
$activities = $this->getRecentUserActivity();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'activities' => array_map(function ($activity) {
|
|
return [
|
|
'type' => $activity->type,
|
|
'message' => $activity->message,
|
|
'icon' => $activity->icon,
|
|
'icon_bg' => $activity->icon_bg,
|
|
'icon_color' => $activity->icon_color,
|
|
'created_at' => $activity->created_at->diffForHumans(),
|
|
'username' => $activity->username,
|
|
'metadata' => $activity->metadata,
|
|
];
|
|
}, $activities),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get default dashboard statistics with caching for expensive queries
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function getDefaultStats(): array
|
|
{
|
|
$today = now()->format('Y-m-d');
|
|
|
|
// Cache total releases count for 5 minutes (expensive query on large tables)
|
|
$releasesCount = \Illuminate\Support\Facades\Cache::remember('admin_stats_releases_count', 300, function () {
|
|
return \App\Models\Release::count();
|
|
});
|
|
|
|
// Cache users count for 5 minutes
|
|
$usersCount = \Illuminate\Support\Facades\Cache::remember('admin_stats_users_count', 300, function () {
|
|
return \App\Models\User::whereNull('deleted_at')->count();
|
|
});
|
|
|
|
// Cache groups count for 10 minutes (rarely changes)
|
|
$groupsCount = \Illuminate\Support\Facades\Cache::remember('admin_stats_groups_count', 600, function () {
|
|
return \App\Models\UsenetGroup::count();
|
|
});
|
|
|
|
// Cache active groups count for 10 minutes
|
|
$activeGroupsCount = \Illuminate\Support\Facades\Cache::remember('admin_stats_active_groups_count', 600, function () {
|
|
return \App\Models\UsenetGroup::where('active', 1)->count();
|
|
});
|
|
|
|
// Cache failed count for 5 minutes
|
|
$failedCount = \Illuminate\Support\Facades\Cache::remember('admin_stats_failed_count', 300, function () {
|
|
return \App\Models\DnzbFailure::count();
|
|
});
|
|
|
|
// Cache reported releases count for 5 minutes
|
|
$reportedCount = \Illuminate\Support\Facades\Cache::remember('admin_stats_reported_count', 300, function () {
|
|
return \App\Models\ReleaseReport::where('status', 'pending')->count();
|
|
});
|
|
|
|
// Today's counts can be cached for 1 minute since they change frequently
|
|
$releasesToday = \Illuminate\Support\Facades\Cache::remember('admin_stats_releases_today_'.$today, 60, function () use ($today) {
|
|
return \App\Models\Release::whereRaw('DATE(adddate) = ?', [$today])->count();
|
|
});
|
|
|
|
$usersToday = \Illuminate\Support\Facades\Cache::remember('admin_stats_users_today_'.$today, 60, function () use ($today) {
|
|
return \App\Models\User::whereRaw('DATE(created_at) = ?', [$today])->count();
|
|
});
|
|
|
|
return [
|
|
'releases' => $releasesCount,
|
|
'releases_today' => $releasesToday,
|
|
'users' => $usersCount,
|
|
'users_today' => $usersToday,
|
|
'groups' => $groupsCount,
|
|
'active_groups' => $activeGroupsCount,
|
|
'failed' => $failedCount,
|
|
'reported' => $reportedCount,
|
|
'disk_free' => $this->getDiskSpace(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get disk space information
|
|
*/
|
|
protected function getDiskSpace(): string
|
|
{
|
|
try {
|
|
$bytes = disk_free_space('/');
|
|
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
|
|
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
|
$bytes /= 1024;
|
|
}
|
|
|
|
return round($bytes, 2).' '.$units[$i];
|
|
} catch (\Exception $e) {
|
|
return 'N/A';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get system metrics (CPU and RAM usage) with caching
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function getSystemMetrics(): array
|
|
{
|
|
// Cache CPU info for 1 hour (doesn't change)
|
|
$cpuInfo = \Illuminate\Support\Facades\Cache::remember('admin_cpu_info', 3600, function () {
|
|
return $this->getCpuInfo();
|
|
});
|
|
|
|
// Cache current metrics for 30 seconds
|
|
$cpuUsage = \Illuminate\Support\Facades\Cache::remember('admin_cpu_usage', 30, function () {
|
|
return $this->getCpuUsage();
|
|
});
|
|
|
|
$ramUsage = \Illuminate\Support\Facades\Cache::remember('admin_ram_usage', 30, function () {
|
|
return $this->getRamUsage();
|
|
});
|
|
|
|
$loadAverage = \Illuminate\Support\Facades\Cache::remember('admin_load_average', 30, function () {
|
|
return $this->getLoadAverage();
|
|
});
|
|
|
|
// Cache historical data for 5 minutes (from database)
|
|
$cpuHistory24h = \Illuminate\Support\Facades\Cache::remember('admin_cpu_history_24h', 300, function () {
|
|
return $this->systemMetricsService->getHourlyMetrics('cpu', 24);
|
|
});
|
|
|
|
$cpuHistory30d = \Illuminate\Support\Facades\Cache::remember('admin_cpu_history_30d', 300, function () {
|
|
return $this->systemMetricsService->getDailyMetrics('cpu', 30);
|
|
});
|
|
|
|
$ramHistory24h = \Illuminate\Support\Facades\Cache::remember('admin_ram_history_24h', 300, function () {
|
|
return $this->systemMetricsService->getHourlyMetrics('ram', 24);
|
|
});
|
|
|
|
$ramHistory30d = \Illuminate\Support\Facades\Cache::remember('admin_ram_history_30d', 300, function () {
|
|
return $this->systemMetricsService->getDailyMetrics('ram', 30);
|
|
});
|
|
|
|
return [
|
|
'cpu' => [
|
|
'current' => $cpuUsage,
|
|
'label' => 'CPU Usage',
|
|
'history_24h' => $cpuHistory24h,
|
|
'history_30d' => $cpuHistory30d,
|
|
'cores' => $cpuInfo['cores'],
|
|
'threads' => $cpuInfo['threads'],
|
|
'model' => $cpuInfo['model'],
|
|
'load_average' => $loadAverage,
|
|
],
|
|
'ram' => [
|
|
'used' => $ramUsage['used'],
|
|
'total' => $ramUsage['total'],
|
|
'percentage' => $ramUsage['percentage'],
|
|
'label' => 'RAM Usage',
|
|
'history_24h' => $ramHistory24h,
|
|
'history_30d' => $ramHistory30d,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get current CPU usage percentage
|
|
*/
|
|
protected function getCpuUsage(): float
|
|
{
|
|
try {
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
// Windows command
|
|
$output = shell_exec('wmic cpu get loadpercentage');
|
|
if ($output) {
|
|
preg_match('/\d+/', $output, $matches);
|
|
|
|
return $matches[0] ?? 0;
|
|
}
|
|
} else {
|
|
// Linux command - get load average and convert to percentage
|
|
$load = sys_getloadavg();
|
|
if ($load !== false) {
|
|
$cpuCount = $this->getCpuCount();
|
|
|
|
return round(($load[0] / $cpuCount) * 100, 2);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::warning('Could not get CPU usage: '.$e->getMessage());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get number of CPU cores
|
|
*/
|
|
protected function getCpuCount(): int
|
|
{
|
|
try {
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
$output = shell_exec('wmic cpu get NumberOfLogicalProcessors');
|
|
if ($output) {
|
|
preg_match('/\d+/', $output, $matches);
|
|
|
|
return (int) ($matches[0] ?? 1);
|
|
}
|
|
} else {
|
|
$cpuinfo = file_get_contents('/proc/cpuinfo');
|
|
preg_match_all('/^processor/m', $cpuinfo, $matches);
|
|
|
|
return count($matches[0]) ?: 1;
|
|
}
|
|
} catch (\Exception $e) {
|
|
return 1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Get detailed CPU information (cores, threads, model)
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function getCpuInfo(): array
|
|
{
|
|
$info = [
|
|
'cores' => 0,
|
|
'threads' => 0,
|
|
'model' => 'Unknown',
|
|
];
|
|
|
|
try {
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
// Get number of cores
|
|
$coresOutput = shell_exec('wmic cpu get NumberOfCores');
|
|
if ($coresOutput) {
|
|
preg_match('/\d+/', $coresOutput, $matches);
|
|
$info['cores'] = (int) ($matches[0] ?? 0);
|
|
}
|
|
|
|
// Get number of logical processors (threads)
|
|
$threadsOutput = shell_exec('wmic cpu get NumberOfLogicalProcessors');
|
|
if ($threadsOutput) {
|
|
preg_match('/\d+/', $threadsOutput, $matches);
|
|
$info['threads'] = (int) ($matches[0] ?? 0);
|
|
}
|
|
|
|
// Get CPU model
|
|
$modelOutput = shell_exec('wmic cpu get Name');
|
|
if ($modelOutput) {
|
|
$lines = explode("\n", trim($modelOutput));
|
|
if (isset($lines[1])) {
|
|
$info['model'] = trim($lines[1]);
|
|
}
|
|
}
|
|
} else {
|
|
// Linux
|
|
$cpuinfo = file_get_contents('/proc/cpuinfo');
|
|
|
|
// Get number of physical cores
|
|
preg_match_all('/^cpu cores\s*:\s*(\d+)/m', $cpuinfo, $coresMatches);
|
|
if (! empty($coresMatches[1])) {
|
|
$info['cores'] = (int) $coresMatches[1][0];
|
|
}
|
|
|
|
// Get number of logical processors (threads)
|
|
preg_match_all('/^processor/m', $cpuinfo, $processorMatches);
|
|
$info['threads'] = count($processorMatches[0]) ?: 0;
|
|
|
|
// Get CPU model
|
|
preg_match('/^model name\s*:\s*(.+)$/m', $cpuinfo, $modelMatches);
|
|
if (! empty($modelMatches[1])) {
|
|
$info['model'] = trim($modelMatches[1]);
|
|
}
|
|
|
|
// If cores is 0, try to get from physical id count
|
|
if ($info['cores'] === 0) {
|
|
preg_match_all('/^physical id\s*:\s*(\d+)/m', $cpuinfo, $physicalMatches);
|
|
$uniquePhysical = ! empty($physicalMatches[1]) ? count(array_unique($physicalMatches[1])) : 1;
|
|
$info['cores'] = (int) ($info['threads'] / $uniquePhysical);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::warning('Could not get CPU info: '.$e->getMessage());
|
|
}
|
|
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Get system load average
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function getLoadAverage(): array
|
|
{
|
|
$loadAvg = [
|
|
'1min' => 0,
|
|
'5min' => 0,
|
|
'15min' => 0,
|
|
];
|
|
|
|
try {
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
// Windows doesn't have load average, use CPU queue length instead
|
|
$output = shell_exec('wmic path Win32_PerfFormattedData_PerfOS_System get ProcessorQueueLength');
|
|
if ($output) {
|
|
preg_match('/\d+/', $output, $matches);
|
|
$queueLength = (int) ($matches[0] ?? 0);
|
|
// Approximate load average
|
|
$loadAvg['1min'] = round($queueLength / 2, 2);
|
|
$loadAvg['5min'] = round($queueLength / 2, 2);
|
|
$loadAvg['15min'] = round($queueLength / 2, 2);
|
|
}
|
|
} else {
|
|
// Linux has native load average
|
|
$load = sys_getloadavg();
|
|
if ($load !== false) {
|
|
$loadAvg['1min'] = round($load[0], 2);
|
|
$loadAvg['5min'] = round($load[1], 2);
|
|
$loadAvg['15min'] = round($load[2], 2);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::warning('Could not get load average: '.$e->getMessage());
|
|
}
|
|
|
|
return $loadAvg;
|
|
}
|
|
|
|
/**
|
|
* Get RAM usage information
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function getRamUsage(): array
|
|
{
|
|
try {
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
// Windows command
|
|
$output = shell_exec('wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /Value');
|
|
if ($output) {
|
|
preg_match('/FreePhysicalMemory=(\d+)/', $output, $free);
|
|
preg_match('/TotalVisibleMemorySize=(\d+)/', $output, $total);
|
|
|
|
if (isset($free[1]) && isset($total[1])) {
|
|
$freeKb = (float) $free[1];
|
|
$totalKb = (float) $total[1];
|
|
$usedKb = $totalKb - $freeKb;
|
|
|
|
return [
|
|
'used' => round($usedKb / 1024 / 1024, 2),
|
|
'total' => round($totalKb / 1024 / 1024, 2),
|
|
'percentage' => round(($usedKb / $totalKb) * 100, 2),
|
|
];
|
|
}
|
|
}
|
|
} else {
|
|
// Linux command
|
|
$meminfo = file_get_contents('/proc/meminfo');
|
|
preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total);
|
|
preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available);
|
|
|
|
if (isset($total[1]) && isset($available[1])) {
|
|
$totalKb = (float) $total[1];
|
|
$availableKb = (float) $available[1];
|
|
$usedKb = $totalKb - $availableKb;
|
|
|
|
return [
|
|
'used' => round($usedKb / 1024 / 1024, 2),
|
|
'total' => round($totalKb / 1024 / 1024, 2),
|
|
'percentage' => round(($usedKb / $totalKb) * 100, 2),
|
|
];
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::warning('Could not get RAM usage: '.$e->getMessage());
|
|
}
|
|
|
|
return [
|
|
'used' => 0,
|
|
'total' => 0,
|
|
'percentage' => 0,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get minute-to-minute user activity data (API endpoint)
|
|
*/
|
|
public function getUserActivityMinutes(): mixed
|
|
{
|
|
$downloadsPerMinute = $this->userStatsService->getDownloadsPerMinute(60);
|
|
$apiHitsPerMinute = $this->userStatsService->getApiHitsPerMinute(60);
|
|
|
|
return response()->json([
|
|
'downloads' => $downloadsPerMinute,
|
|
'api_hits' => $apiHitsPerMinute,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get current system metrics (API endpoint)
|
|
*/
|
|
public function getCurrentMetrics(): mixed
|
|
{
|
|
$cpuUsage = $this->getCpuUsage();
|
|
$ramUsage = $this->getRamUsage();
|
|
$cpuInfo = $this->getCpuInfo();
|
|
$loadAverage = $this->getLoadAverage();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'cpu' => [
|
|
'current' => $cpuUsage,
|
|
'cores' => $cpuInfo['cores'],
|
|
'threads' => $cpuInfo['threads'],
|
|
'model' => $cpuInfo['model'],
|
|
'load_average' => $loadAverage,
|
|
],
|
|
'ram' => [
|
|
'used' => $ramUsage['used'],
|
|
'total' => $ramUsage['total'],
|
|
'percentage' => $ramUsage['percentage'],
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get historical system metrics (API endpoint)
|
|
*/
|
|
public function getHistoricalMetrics(): mixed
|
|
{
|
|
$timeRange = request('range', '24h'); // 24h or 30d
|
|
|
|
if ($timeRange === '30d') {
|
|
$cpuHistory = $this->systemMetricsService->getDailyMetrics('cpu', 30);
|
|
$ramHistory = $this->systemMetricsService->getDailyMetrics('ram', 30);
|
|
} else {
|
|
$cpuHistory = $this->systemMetricsService->getHourlyMetrics('cpu', 24);
|
|
$ramHistory = $this->systemMetricsService->getHourlyMetrics('ram', 24);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'cpu_history' => $cpuHistory,
|
|
'ram_history' => $ramHistory,
|
|
'range' => $timeRange,
|
|
]);
|
|
}
|
|
}
|