Passed
Push — master ( 4f5b3b...1c5d75 )
by Darko
09:48
created

AdminPageController::index()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
c 0
b 0
f 0
dl 0
loc 21
rs 9.7666
cc 1
nc 1
nop 0
1
<?php
2
3
namespace App\Http\Controllers\Admin;
4
5
use App\Http\Controllers\BasePageController;
6
use App\Services\SystemMetricsService;
0 ignored issues
show
Bug introduced by
The type App\Services\SystemMetricsService was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use App\Services\UserStatsService;
8
9
class AdminPageController extends BasePageController
10
{
11
    protected UserStatsService $userStatsService;
12
13
    protected SystemMetricsService $systemMetricsService;
14
15
    public function __construct(UserStatsService $userStatsService, SystemMetricsService $systemMetricsService)
16
    {
17
        parent::__construct();
18
        $this->userStatsService = $userStatsService;
19
        $this->systemMetricsService = $systemMetricsService;
20
    }
21
22
    /**
23
     * @throws \Exception
24
     */
25
    public function index()
26
    {
27
        $this->setAdminPrefs();
28
29
        // Get user statistics
30
        $userStats = [
31
            'users_by_role' => $this->userStatsService->getUsersByRole(),
32
            'downloads_per_hour' => $this->userStatsService->getDownloadsPerHour(168), // Last 7 days in hours
33
            'downloads_per_minute' => $this->userStatsService->getDownloadsPerMinute(60),
34
            'api_hits_per_hour' => $this->userStatsService->getApiHitsPerHour(168), // Last 7 days in hours
35
            'api_hits_per_minute' => $this->userStatsService->getApiHitsPerMinute(60),
36
            'summary' => $this->userStatsService->getSummaryStats(),
37
            'top_downloaders' => $this->userStatsService->getTopDownloaders(5),
38
        ];
39
40
        return view('admin.dashboard', array_merge($this->viewData, [
41
            'meta_title' => 'Admin Home',
42
            'meta_description' => 'Admin home page',
43
            'userStats' => $userStats,
44
            'stats' => $this->getDefaultStats(),
45
            'systemMetrics' => $this->getSystemMetrics(),
46
        ]));
47
    }
48
49
    /**
50
     * Get default dashboard statistics
51
     */
52
    protected function getDefaultStats(): array
53
    {
54
        $today = now()->format('Y-m-d');
55
56
        return [
57
            'releases' => \App\Models\Release::count(),
58
            'releases_today' => \App\Models\Release::whereRaw('DATE(adddate) = ?', [$today])->count(),
59
            'users' => \App\Models\User::whereNull('deleted_at')->count(),
60
            'users_today' => \App\Models\User::whereRaw('DATE(created_at) = ?', [$today])->count(),
61
            'groups' => \App\Models\UsenetGroup::count(),
62
            'active_groups' => \App\Models\UsenetGroup::where('active', 1)->count(),
63
            'failed' => \App\Models\DnzbFailure::count(),
64
            'disk_free' => $this->getDiskSpace(),
65
        ];
66
    }
67
68
    /**
69
     * Get disk space information
70
     */
71
    protected function getDiskSpace(): string
72
    {
73
        try {
74
            $bytes = disk_free_space('/');
75
            $units = ['B', 'KB', 'MB', 'GB', 'TB'];
76
77
            for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
78
                $bytes /= 1024;
79
            }
80
81
            return round($bytes, 2).' '.$units[$i];
82
        } catch (\Exception $e) {
83
            return 'N/A';
84
        }
85
    }
86
87
    /**
88
     * Get system metrics (CPU and RAM usage)
89
     */
90
    protected function getSystemMetrics(): array
91
    {
92
        $cpuUsage = $this->getCpuUsage();
93
        $ramUsage = $this->getRamUsage();
94
        $cpuInfo = $this->getCpuInfo();
95
        $loadAverage = $this->getLoadAverage();
96
97
        // Get historical data from database - both hourly (24h) and daily (30d)
98
        $cpuHistory24h = $this->systemMetricsService->getHourlyMetrics('cpu', 24);
99
        $cpuHistory30d = $this->systemMetricsService->getDailyMetrics('cpu', 30);
100
        $ramHistory24h = $this->systemMetricsService->getHourlyMetrics('ram', 24);
101
        $ramHistory30d = $this->systemMetricsService->getDailyMetrics('ram', 30);
102
103
        return [
104
            'cpu' => [
105
                'current' => $cpuUsage,
106
                'label' => 'CPU Usage',
107
                'history_24h' => $cpuHistory24h,
108
                'history_30d' => $cpuHistory30d,
109
                'cores' => $cpuInfo['cores'],
110
                'threads' => $cpuInfo['threads'],
111
                'model' => $cpuInfo['model'],
112
                'load_average' => $loadAverage,
113
            ],
114
            'ram' => [
115
                'used' => $ramUsage['used'],
116
                'total' => $ramUsage['total'],
117
                'percentage' => $ramUsage['percentage'],
118
                'label' => 'RAM Usage',
119
                'history_24h' => $ramHistory24h,
120
                'history_30d' => $ramHistory30d,
121
            ],
122
        ];
123
    }
124
125
    /**
126
     * Get current CPU usage percentage
127
     */
128
    protected function getCpuUsage(): float
129
    {
130
        try {
131
            if (PHP_OS_FAMILY === 'Windows') {
132
                // Windows command
133
                $output = shell_exec('wmic cpu get loadpercentage');
134
                if ($output) {
135
                    preg_match('/\d+/', $output, $matches);
136
137
                    return $matches[0] ?? 0;
138
                }
139
            } else {
140
                // Linux command - get load average and convert to percentage
141
                $load = sys_getloadavg();
142
                if ($load !== false) {
143
                    $cpuCount = $this->getCpuCount();
144
145
                    return round(($load[0] / $cpuCount) * 100, 2);
146
                }
147
            }
148
        } catch (\Exception $e) {
149
            \Log::warning('Could not get CPU usage: '.$e->getMessage());
150
        }
151
152
        return 0;
153
    }
154
155
    /**
156
     * Get number of CPU cores
157
     */
158
    protected function getCpuCount(): int
159
    {
160
        try {
161
            if (PHP_OS_FAMILY === 'Windows') {
162
                $output = shell_exec('wmic cpu get NumberOfLogicalProcessors');
163
                if ($output) {
164
                    preg_match('/\d+/', $output, $matches);
165
166
                    return (int) ($matches[0] ?? 1);
167
                }
168
            } else {
169
                $cpuinfo = file_get_contents('/proc/cpuinfo');
170
                preg_match_all('/^processor/m', $cpuinfo, $matches);
171
172
                return count($matches[0]) ?: 1;
173
            }
174
        } catch (\Exception $e) {
175
            return 1;
176
        }
177
178
        return 1;
179
    }
180
181
    /**
182
     * Get detailed CPU information (cores, threads, model)
183
     */
184
    protected function getCpuInfo(): array
185
    {
186
        $info = [
187
            'cores' => 0,
188
            'threads' => 0,
189
            'model' => 'Unknown',
190
        ];
191
192
        try {
193
            if (PHP_OS_FAMILY === 'Windows') {
194
                // Get number of cores
195
                $coresOutput = shell_exec('wmic cpu get NumberOfCores');
196
                if ($coresOutput) {
197
                    preg_match('/\d+/', $coresOutput, $matches);
198
                    $info['cores'] = (int) ($matches[0] ?? 0);
199
                }
200
201
                // Get number of logical processors (threads)
202
                $threadsOutput = shell_exec('wmic cpu get NumberOfLogicalProcessors');
203
                if ($threadsOutput) {
204
                    preg_match('/\d+/', $threadsOutput, $matches);
205
                    $info['threads'] = (int) ($matches[0] ?? 0);
206
                }
207
208
                // Get CPU model
209
                $modelOutput = shell_exec('wmic cpu get Name');
210
                if ($modelOutput) {
211
                    $lines = explode("\n", trim($modelOutput));
212
                    if (isset($lines[1])) {
213
                        $info['model'] = trim($lines[1]);
214
                    }
215
                }
216
            } else {
217
                // Linux
218
                $cpuinfo = file_get_contents('/proc/cpuinfo');
219
220
                // Get number of physical cores
221
                preg_match_all('/^cpu cores\s*:\s*(\d+)/m', $cpuinfo, $coresMatches);
222
                if (! empty($coresMatches[1])) {
223
                    $info['cores'] = (int) $coresMatches[1][0];
224
                }
225
226
                // Get number of logical processors (threads)
227
                preg_match_all('/^processor/m', $cpuinfo, $processorMatches);
228
                $info['threads'] = count($processorMatches[0]) ?: 0;
229
230
                // Get CPU model
231
                preg_match('/^model name\s*:\s*(.+)$/m', $cpuinfo, $modelMatches);
232
                if (! empty($modelMatches[1])) {
233
                    $info['model'] = trim($modelMatches[1]);
234
                }
235
236
                // If cores is 0, try to get from physical id count
237
                if ($info['cores'] === 0) {
238
                    preg_match_all('/^physical id\s*:\s*(\d+)/m', $cpuinfo, $physicalMatches);
239
                    $uniquePhysical = ! empty($physicalMatches[1]) ? count(array_unique($physicalMatches[1])) : 1;
240
                    $info['cores'] = (int) ($info['threads'] / $uniquePhysical);
241
                }
242
            }
243
        } catch (\Exception $e) {
244
            \Log::warning('Could not get CPU info: '.$e->getMessage());
245
        }
246
247
        return $info;
248
    }
249
250
    /**
251
     * Get system load average
252
     */
253
    protected function getLoadAverage(): array
254
    {
255
        $loadAvg = [
256
            '1min' => 0,
257
            '5min' => 0,
258
            '15min' => 0,
259
        ];
260
261
        try {
262
            if (PHP_OS_FAMILY === 'Windows') {
263
                // Windows doesn't have load average, use CPU queue length instead
264
                $output = shell_exec('wmic path Win32_PerfFormattedData_PerfOS_System get ProcessorQueueLength');
265
                if ($output) {
266
                    preg_match('/\d+/', $output, $matches);
267
                    $queueLength = (int) ($matches[0] ?? 0);
268
                    // Approximate load average
269
                    $loadAvg['1min'] = round($queueLength / 2, 2);
270
                    $loadAvg['5min'] = round($queueLength / 2, 2);
271
                    $loadAvg['15min'] = round($queueLength / 2, 2);
272
                }
273
            } else {
274
                // Linux has native load average
275
                $load = sys_getloadavg();
276
                if ($load !== false) {
277
                    $loadAvg['1min'] = round($load[0], 2);
278
                    $loadAvg['5min'] = round($load[1], 2);
279
                    $loadAvg['15min'] = round($load[2], 2);
280
                }
281
            }
282
        } catch (\Exception $e) {
283
            \Log::warning('Could not get load average: '.$e->getMessage());
284
        }
285
286
        return $loadAvg;
287
    }
288
289
    /**
290
     * Get RAM usage information
291
     */
292
    protected function getRamUsage(): array
293
    {
294
        try {
295
            if (PHP_OS_FAMILY === 'Windows') {
296
                // Windows command
297
                $output = shell_exec('wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /Value');
298
                if ($output) {
299
                    preg_match('/FreePhysicalMemory=(\d+)/', $output, $free);
300
                    preg_match('/TotalVisibleMemorySize=(\d+)/', $output, $total);
301
302
                    if (isset($free[1]) && isset($total[1])) {
303
                        $freeKb = (float) $free[1];
304
                        $totalKb = (float) $total[1];
305
                        $usedKb = $totalKb - $freeKb;
306
307
                        return [
308
                            'used' => round($usedKb / 1024 / 1024, 2),
309
                            'total' => round($totalKb / 1024 / 1024, 2),
310
                            'percentage' => round(($usedKb / $totalKb) * 100, 2),
311
                        ];
312
                    }
313
                }
314
            } else {
315
                // Linux command
316
                $meminfo = file_get_contents('/proc/meminfo');
317
                preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total);
318
                preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available);
319
320
                if (isset($total[1]) && isset($available[1])) {
321
                    $totalKb = (float) $total[1];
322
                    $availableKb = (float) $available[1];
323
                    $usedKb = $totalKb - $availableKb;
324
325
                    return [
326
                        'used' => round($usedKb / 1024 / 1024, 2),
327
                        'total' => round($totalKb / 1024 / 1024, 2),
328
                        'percentage' => round(($usedKb / $totalKb) * 100, 2),
329
                    ];
330
                }
331
            }
332
        } catch (\Exception $e) {
333
            \Log::warning('Could not get RAM usage: '.$e->getMessage());
334
        }
335
336
        return [
337
            'used' => 0,
338
            'total' => 0,
339
            'percentage' => 0,
340
        ];
341
    }
342
343
    /**
344
     * Get minute-to-minute user activity data (API endpoint)
345
     */
346
    public function getUserActivityMinutes()
347
    {
348
        $downloadsPerMinute = $this->userStatsService->getDownloadsPerMinute(60);
349
        $apiHitsPerMinute = $this->userStatsService->getApiHitsPerMinute(60);
350
351
        return response()->json([
352
            'downloads' => $downloadsPerMinute,
353
            'api_hits' => $apiHitsPerMinute,
354
        ]);
355
    }
356
}
357