Passed
Push — master ( 61beee...1576de )
by Darko
10:00
created

AdminPageController::getUserActivityMinutes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 8
rs 10
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;
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
            'recent_activity' => $this->getRecentUserActivity(),
47
        ]));
48
    }
49
50
    /**
51
     * Get recent user activity from the user_activities table
52
     */
53
    protected function getRecentUserActivity(): array
54
    {
55
        $activities = \App\Models\UserActivity::orderBy('created_at', 'desc')
56
            ->limit(10)
57
            ->get();
58
59
        return $activities->map(function ($activity) {
60
            return (object) [
61
                'type' => $activity->activity_type,
62
                'message' => $activity->description,
63
                'icon' => $activity->icon,
64
                'icon_bg' => 'bg-'.$activity->color_class.'-100 dark:bg-'.$activity->color_class.'-900',
65
                'icon_color' => 'text-'.$activity->color_class.'-600 dark:text-'.$activity->color_class.'-400',
66
                'created_at' => $activity->created_at,
67
                'username' => $activity->username,
68
            ];
69
        })->toArray();
70
    }
71
72
    /**
73
     * API endpoint to get recent user activity (for auto-refresh)
74
     */
75
    public function getRecentActivity()
76
    {
77
        $activities = $this->getRecentUserActivity();
78
79
        return response()->json([
80
            'success' => true,
81
            'activities' => array_map(function ($activity) {
82
                return [
83
                    'type' => $activity->type,
84
                    'message' => $activity->message,
85
                    'icon' => $activity->icon,
86
                    'icon_bg' => $activity->icon_bg,
87
                    'icon_color' => $activity->icon_color,
88
                    'created_at' => $activity->created_at->diffForHumans(),
89
                    'username' => $activity->username,
90
                ];
91
            }, $activities),
92
        ]);
93
    }
94
95
    /**
96
     * Get default dashboard statistics
97
     */
98
    protected function getDefaultStats(): array
99
    {
100
        $today = now()->format('Y-m-d');
101
102
        return [
103
            'releases' => \App\Models\Release::count(),
104
            'releases_today' => \App\Models\Release::whereRaw('DATE(adddate) = ?', [$today])->count(),
105
            'users' => \App\Models\User::whereNull('deleted_at')->count(),
106
            'users_today' => \App\Models\User::whereRaw('DATE(created_at) = ?', [$today])->count(),
107
            'groups' => \App\Models\UsenetGroup::count(),
108
            'active_groups' => \App\Models\UsenetGroup::where('active', 1)->count(),
109
            'failed' => \App\Models\DnzbFailure::count(),
110
            'disk_free' => $this->getDiskSpace(),
111
        ];
112
    }
113
114
    /**
115
     * Get disk space information
116
     */
117
    protected function getDiskSpace(): string
118
    {
119
        try {
120
            $bytes = disk_free_space('/');
121
            $units = ['B', 'KB', 'MB', 'GB', 'TB'];
122
123
            for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
124
                $bytes /= 1024;
125
            }
126
127
            return round($bytes, 2).' '.$units[$i];
128
        } catch (\Exception $e) {
129
            return 'N/A';
130
        }
131
    }
132
133
    /**
134
     * Get system metrics (CPU and RAM usage)
135
     */
136
    protected function getSystemMetrics(): array
137
    {
138
        $cpuUsage = $this->getCpuUsage();
139
        $ramUsage = $this->getRamUsage();
140
        $cpuInfo = $this->getCpuInfo();
141
        $loadAverage = $this->getLoadAverage();
142
143
        // Get historical data from database - both hourly (24h) and daily (30d)
144
        $cpuHistory24h = $this->systemMetricsService->getHourlyMetrics('cpu', 24);
145
        $cpuHistory30d = $this->systemMetricsService->getDailyMetrics('cpu', 30);
146
        $ramHistory24h = $this->systemMetricsService->getHourlyMetrics('ram', 24);
147
        $ramHistory30d = $this->systemMetricsService->getDailyMetrics('ram', 30);
148
149
        return [
150
            'cpu' => [
151
                'current' => $cpuUsage,
152
                'label' => 'CPU Usage',
153
                'history_24h' => $cpuHistory24h,
154
                'history_30d' => $cpuHistory30d,
155
                'cores' => $cpuInfo['cores'],
156
                'threads' => $cpuInfo['threads'],
157
                'model' => $cpuInfo['model'],
158
                'load_average' => $loadAverage,
159
            ],
160
            'ram' => [
161
                'used' => $ramUsage['used'],
162
                'total' => $ramUsage['total'],
163
                'percentage' => $ramUsage['percentage'],
164
                'label' => 'RAM Usage',
165
                'history_24h' => $ramHistory24h,
166
                'history_30d' => $ramHistory30d,
167
            ],
168
        ];
169
    }
170
171
    /**
172
     * Get current CPU usage percentage
173
     */
174
    protected function getCpuUsage(): float
175
    {
176
        try {
177
            if (PHP_OS_FAMILY === 'Windows') {
178
                // Windows command
179
                $output = shell_exec('wmic cpu get loadpercentage');
180
                if ($output) {
181
                    preg_match('/\d+/', $output, $matches);
182
183
                    return $matches[0] ?? 0;
184
                }
185
            } else {
186
                // Linux command - get load average and convert to percentage
187
                $load = sys_getloadavg();
188
                if ($load !== false) {
189
                    $cpuCount = $this->getCpuCount();
190
191
                    return round(($load[0] / $cpuCount) * 100, 2);
192
                }
193
            }
194
        } catch (\Exception $e) {
195
            \Log::warning('Could not get CPU usage: '.$e->getMessage());
196
        }
197
198
        return 0;
199
    }
200
201
    /**
202
     * Get number of CPU cores
203
     */
204
    protected function getCpuCount(): int
205
    {
206
        try {
207
            if (PHP_OS_FAMILY === 'Windows') {
208
                $output = shell_exec('wmic cpu get NumberOfLogicalProcessors');
209
                if ($output) {
210
                    preg_match('/\d+/', $output, $matches);
211
212
                    return (int) ($matches[0] ?? 1);
213
                }
214
            } else {
215
                $cpuinfo = file_get_contents('/proc/cpuinfo');
216
                preg_match_all('/^processor/m', $cpuinfo, $matches);
217
218
                return count($matches[0]) ?: 1;
219
            }
220
        } catch (\Exception $e) {
221
            return 1;
222
        }
223
224
        return 1;
225
    }
226
227
    /**
228
     * Get detailed CPU information (cores, threads, model)
229
     */
230
    protected function getCpuInfo(): array
231
    {
232
        $info = [
233
            'cores' => 0,
234
            'threads' => 0,
235
            'model' => 'Unknown',
236
        ];
237
238
        try {
239
            if (PHP_OS_FAMILY === 'Windows') {
240
                // Get number of cores
241
                $coresOutput = shell_exec('wmic cpu get NumberOfCores');
242
                if ($coresOutput) {
243
                    preg_match('/\d+/', $coresOutput, $matches);
244
                    $info['cores'] = (int) ($matches[0] ?? 0);
245
                }
246
247
                // Get number of logical processors (threads)
248
                $threadsOutput = shell_exec('wmic cpu get NumberOfLogicalProcessors');
249
                if ($threadsOutput) {
250
                    preg_match('/\d+/', $threadsOutput, $matches);
251
                    $info['threads'] = (int) ($matches[0] ?? 0);
252
                }
253
254
                // Get CPU model
255
                $modelOutput = shell_exec('wmic cpu get Name');
256
                if ($modelOutput) {
257
                    $lines = explode("\n", trim($modelOutput));
258
                    if (isset($lines[1])) {
259
                        $info['model'] = trim($lines[1]);
260
                    }
261
                }
262
            } else {
263
                // Linux
264
                $cpuinfo = file_get_contents('/proc/cpuinfo');
265
266
                // Get number of physical cores
267
                preg_match_all('/^cpu cores\s*:\s*(\d+)/m', $cpuinfo, $coresMatches);
268
                if (! empty($coresMatches[1])) {
269
                    $info['cores'] = (int) $coresMatches[1][0];
270
                }
271
272
                // Get number of logical processors (threads)
273
                preg_match_all('/^processor/m', $cpuinfo, $processorMatches);
274
                $info['threads'] = count($processorMatches[0]) ?: 0;
275
276
                // Get CPU model
277
                preg_match('/^model name\s*:\s*(.+)$/m', $cpuinfo, $modelMatches);
278
                if (! empty($modelMatches[1])) {
279
                    $info['model'] = trim($modelMatches[1]);
280
                }
281
282
                // If cores is 0, try to get from physical id count
283
                if ($info['cores'] === 0) {
284
                    preg_match_all('/^physical id\s*:\s*(\d+)/m', $cpuinfo, $physicalMatches);
285
                    $uniquePhysical = ! empty($physicalMatches[1]) ? count(array_unique($physicalMatches[1])) : 1;
286
                    $info['cores'] = (int) ($info['threads'] / $uniquePhysical);
287
                }
288
            }
289
        } catch (\Exception $e) {
290
            \Log::warning('Could not get CPU info: '.$e->getMessage());
291
        }
292
293
        return $info;
294
    }
295
296
    /**
297
     * Get system load average
298
     */
299
    protected function getLoadAverage(): array
300
    {
301
        $loadAvg = [
302
            '1min' => 0,
303
            '5min' => 0,
304
            '15min' => 0,
305
        ];
306
307
        try {
308
            if (PHP_OS_FAMILY === 'Windows') {
309
                // Windows doesn't have load average, use CPU queue length instead
310
                $output = shell_exec('wmic path Win32_PerfFormattedData_PerfOS_System get ProcessorQueueLength');
311
                if ($output) {
312
                    preg_match('/\d+/', $output, $matches);
313
                    $queueLength = (int) ($matches[0] ?? 0);
314
                    // Approximate load average
315
                    $loadAvg['1min'] = round($queueLength / 2, 2);
316
                    $loadAvg['5min'] = round($queueLength / 2, 2);
317
                    $loadAvg['15min'] = round($queueLength / 2, 2);
318
                }
319
            } else {
320
                // Linux has native load average
321
                $load = sys_getloadavg();
322
                if ($load !== false) {
323
                    $loadAvg['1min'] = round($load[0], 2);
324
                    $loadAvg['5min'] = round($load[1], 2);
325
                    $loadAvg['15min'] = round($load[2], 2);
326
                }
327
            }
328
        } catch (\Exception $e) {
329
            \Log::warning('Could not get load average: '.$e->getMessage());
330
        }
331
332
        return $loadAvg;
333
    }
334
335
    /**
336
     * Get RAM usage information
337
     */
338
    protected function getRamUsage(): array
339
    {
340
        try {
341
            if (PHP_OS_FAMILY === 'Windows') {
342
                // Windows command
343
                $output = shell_exec('wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /Value');
344
                if ($output) {
345
                    preg_match('/FreePhysicalMemory=(\d+)/', $output, $free);
346
                    preg_match('/TotalVisibleMemorySize=(\d+)/', $output, $total);
347
348
                    if (isset($free[1]) && isset($total[1])) {
349
                        $freeKb = (float) $free[1];
350
                        $totalKb = (float) $total[1];
351
                        $usedKb = $totalKb - $freeKb;
352
353
                        return [
354
                            'used' => round($usedKb / 1024 / 1024, 2),
355
                            'total' => round($totalKb / 1024 / 1024, 2),
356
                            'percentage' => round(($usedKb / $totalKb) * 100, 2),
357
                        ];
358
                    }
359
                }
360
            } else {
361
                // Linux command
362
                $meminfo = file_get_contents('/proc/meminfo');
363
                preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total);
364
                preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available);
365
366
                if (isset($total[1]) && isset($available[1])) {
367
                    $totalKb = (float) $total[1];
368
                    $availableKb = (float) $available[1];
369
                    $usedKb = $totalKb - $availableKb;
370
371
                    return [
372
                        'used' => round($usedKb / 1024 / 1024, 2),
373
                        'total' => round($totalKb / 1024 / 1024, 2),
374
                        'percentage' => round(($usedKb / $totalKb) * 100, 2),
375
                    ];
376
                }
377
            }
378
        } catch (\Exception $e) {
379
            \Log::warning('Could not get RAM usage: '.$e->getMessage());
380
        }
381
382
        return [
383
            'used' => 0,
384
            'total' => 0,
385
            'percentage' => 0,
386
        ];
387
    }
388
389
    /**
390
     * Get minute-to-minute user activity data (API endpoint)
391
     */
392
    public function getUserActivityMinutes()
393
    {
394
        $downloadsPerMinute = $this->userStatsService->getDownloadsPerMinute(60);
395
        $apiHitsPerMinute = $this->userStatsService->getApiHitsPerMinute(60);
396
397
        return response()->json([
398
            'downloads' => $downloadsPerMinute,
399
            'api_hits' => $apiHitsPerMinute,
400
        ]);
401
    }
402
403
    /**
404
     * Get current system metrics (API endpoint)
405
     */
406
    public function getCurrentMetrics()
407
    {
408
        $cpuUsage = $this->getCpuUsage();
409
        $ramUsage = $this->getRamUsage();
410
        $cpuInfo = $this->getCpuInfo();
411
        $loadAverage = $this->getLoadAverage();
412
413
        return response()->json([
414
            'success' => true,
415
            'cpu' => [
416
                'current' => $cpuUsage,
417
                'cores' => $cpuInfo['cores'],
418
                'threads' => $cpuInfo['threads'],
419
                'model' => $cpuInfo['model'],
420
                'load_average' => $loadAverage,
421
            ],
422
            'ram' => [
423
                'used' => $ramUsage['used'],
424
                'total' => $ramUsage['total'],
425
                'percentage' => $ramUsage['percentage'],
426
            ],
427
        ]);
428
    }
429
430
    /**
431
     * Get historical system metrics (API endpoint)
432
     */
433
    public function getHistoricalMetrics()
434
    {
435
        $timeRange = request('range', '24h'); // 24h or 30d
0 ignored issues
show
Bug introduced by
'range' of type string is incompatible with the type list expected by parameter $key of request(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

435
        $timeRange = request(/** @scrutinizer ignore-type */ 'range', '24h'); // 24h or 30d
Loading history...
436
437
        if ($timeRange === '30d') {
438
            $cpuHistory = $this->systemMetricsService->getDailyMetrics('cpu', 30);
439
            $ramHistory = $this->systemMetricsService->getDailyMetrics('ram', 30);
440
        } else {
441
            $cpuHistory = $this->systemMetricsService->getHourlyMetrics('cpu', 24);
442
            $ramHistory = $this->systemMetricsService->getHourlyMetrics('ram', 24);
443
        }
444
445
        return response()->json([
446
            'success' => true,
447
            'cpu_history' => $cpuHistory,
448
            'ram_history' => $ramHistory,
449
            'range' => $timeRange,
450
        ]);
451
    }
452
}
453