diff --git a/app/Contracts/Repository/NodeRepositoryInterface.php b/app/Contracts/Repository/NodeRepositoryInterface.php index 0ebcbe3a0..c533032c0 100644 --- a/app/Contracts/Repository/NodeRepositoryInterface.php +++ b/app/Contracts/Repository/NodeRepositoryInterface.php @@ -21,6 +21,14 @@ interface NodeRepositoryInterface extends RepositoryInterface, SearchableInterfa */ public function getUsageStats(Node $node): array; + /** + * Return the usage stats for a single node. + * + * @param \Pterodactyl\Models\Node $node + * @return array + */ + public function getUsageStatsRaw(Node $node): array; + /** * Return all available nodes with a searchable interface. * diff --git a/app/Contracts/Repository/ServerRepositoryInterface.php b/app/Contracts/Repository/ServerRepositoryInterface.php index dc677fba0..9fb712e29 100644 --- a/app/Contracts/Repository/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/ServerRepositoryInterface.php @@ -145,4 +145,11 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter * @return bool */ public function isUniqueUuidCombo(string $uuid, string $short): bool; + + /** + * Get the amount of servers that are suspended + * + * @return int + */ + public function getSuspendedServersCount(): int; } diff --git a/app/Http/Controllers/Admin/StatisticsController.php b/app/Http/Controllers/Admin/StatisticsController.php index 07124e4b6..2327fd88d 100644 --- a/app/Http/Controllers/Admin/StatisticsController.php +++ b/app/Http/Controllers/Admin/StatisticsController.php @@ -11,19 +11,16 @@ use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; -use Pterodactyl\Traits\Controllers\JavascriptStatisticsInjection; +use Pterodactyl\Traits\Controllers\PlainJavascriptInjection; class StatisticsController extends Controller { - use JavascriptStatisticsInjection; + use PlainJavascriptInjection; private $allocationRepository; private $databaseRepository; - private $keyProviderService; - private $eggRepository; private $nodeRepository; @@ -35,7 +32,6 @@ class StatisticsController extends Controller function __construct( AllocationRepositoryInterface $allocationRepository, DatabaseRepositoryInterface $databaseRepository, - DaemonKeyProviderService $keyProviderService, EggRepositoryInterface $eggRepository, NodeRepositoryInterface $nodeRepository, ServerRepositoryInterface $serverRepository, @@ -44,28 +40,33 @@ class StatisticsController extends Controller { $this->allocationRepository = $allocationRepository; $this->databaseRepository = $databaseRepository; - $this->keyProviderService = $keyProviderService; $this->eggRepository = $eggRepository; $this->nodeRepository = $nodeRepository; $this->serverRepository = $serverRepository; $this->userRepository = $userRepository; } - public function index(Request $request) + public function index() { $servers = $this->serverRepository->all(); - $serversCount = count($servers); $nodes = $this->nodeRepository->all(); - $nodesCount = count($nodes); $usersCount = $this->userRepository->count(); $eggsCount = $this->eggRepository->count(); $databasesCount = $this->databaseRepository->count(); - $totalServerRam = DB::table('servers')->sum('memory'); - $totalNodeRam = DB::table('nodes')->sum('memory'); - $totalServerDisk = DB::table('servers')->sum('disk'); - $totalNodeDisk = DB::table('nodes')->sum('disk'); $totalAllocations = $this->allocationRepository->count(); - $suspendedServersCount = $this->serverRepository->getBuilder()->where('suspended', true)->count(); + $suspendedServersCount = $this->serverRepository->getSuspendedServersCount(); + + $totalServerRam = 0; + $totalNodeRam = 0; + $totalServerDisk = 0; + $totalNodeDisk = 0; + foreach ($nodes as $node) { + $stats = $this->nodeRepository->getUsageStatsRaw($node); + $totalServerRam += $stats['memory']['value']; + $totalNodeRam += $stats['memory']['max']; + $totalServerDisk += $stats['disk']['value']; + $totalNodeDisk += $stats['disk']['max']; + } $tokens = []; foreach ($nodes as $node) { @@ -74,7 +75,6 @@ class StatisticsController extends Controller $this->injectJavascript([ 'servers' => $servers, - 'serverCount' => $serversCount, 'suspendedServers' => $suspendedServersCount, 'totalServerRam' => $totalServerRam, 'totalNodeRam' => $totalNodeRam, @@ -83,10 +83,10 @@ class StatisticsController extends Controller 'nodes' => $nodes, 'tokens' => $tokens, ]); - + return view('admin.statistics', [ - 'serversCount' => $serversCount, - 'nodesCount' => $nodesCount, + 'servers' => $servers, + 'nodes' => $nodes, 'usersCount' => $usersCount, 'eggsCount' => $eggsCount, 'totalServerRam' => $totalServerRam, diff --git a/app/Repositories/Eloquent/NodeRepository.php b/app/Repositories/Eloquent/NodeRepository.php index b4d6ba6b0..4f59fddce 100644 --- a/app/Repositories/Eloquent/NodeRepository.php +++ b/app/Repositories/Eloquent/NodeRepository.php @@ -56,6 +56,33 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa })->toArray(); } + /** + * Return the usage stats for a single node. + * + * @param \Pterodactyl\Models\Node $node + * @return array + */ + public function getUsageStatsRaw(Node $node): array + { + $stats = $this->getBuilder()->select( + $this->getBuilder()->raw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') + )->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $node->id)->first(); + + return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])->mapWithKeys(function ($value, $key) use ($node) { + $maxUsage = $node->{$key}; + if ($node->{$key . '_overallocate'} > 0) { + $maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100)); + } + + return [ + $key => [ + 'value' => $value, + 'max' => $maxUsage, + ], + ]; + })->toArray(); + } + /** * Return all available nodes with a searchable interface. * diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index 2fc5f878b..c2c6e1e32 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -328,4 +328,14 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt $this->app->make(SubuserRepository::class)->getBuilder()->select('server_id')->where('user_id', $user) )->pluck('id')->all(); } + + /** + * Get the amount of servers that are suspended + * + * @return int + */ + public function getSuspendedServersCount(): int + { + return $this->getBuilder()->where('suspended', true)->count(); + } } diff --git a/app/Traits/Controllers/JavascriptStatisticsInjection.php b/app/Traits/Controllers/PlainJavascriptInjection.php similarity index 88% rename from app/Traits/Controllers/JavascriptStatisticsInjection.php rename to app/Traits/Controllers/PlainJavascriptInjection.php index 3b4e52d0f..eae53bfbc 100644 --- a/app/Traits/Controllers/JavascriptStatisticsInjection.php +++ b/app/Traits/Controllers/PlainJavascriptInjection.php @@ -10,7 +10,7 @@ namespace Pterodactyl\Traits\Controllers; use JavaScript; -trait JavascriptStatisticsInjection +trait PlainJavascriptInjection { /** diff --git a/public/themes/pterodactyl/js/admin/statistics.js b/public/themes/pterodactyl/js/admin/statistics.js index 59034a8da..7433f3221 100644 --- a/public/themes/pterodactyl/js/admin/statistics.js +++ b/public/themes/pterodactyl/js/admin/statistics.js @@ -28,7 +28,7 @@ let ramChart = new Chart($('#ram_chart'), { } }); -var activeServers = Pterodactyl.serverCount - Pterodactyl.suspendedServers; +var activeServers = Pterodactyl.servers.length - Pterodactyl.suspendedServers; let serversChart = new Chart($('#servers_chart'), { type: 'pie', data: { diff --git a/resources/themes/pterodactyl/admin/statistics.blade.php b/resources/themes/pterodactyl/admin/statistics.blade.php index 036ad937f..529107bb4 100644 --- a/resources/themes/pterodactyl/admin/statistics.blade.php +++ b/resources/themes/pterodactyl/admin/statistics.blade.php @@ -35,7 +35,7 @@
Servers - {{ $serversCount }} + {{ count($servers) }}
@@ -118,7 +118,7 @@
Total Nodes - {{ $nodesCount }} + {{ count($nodes) }}
diff --git a/tests/Unit/Http/Controllers/Admin/StatisticsControllerTest.php b/tests/Unit/Http/Controllers/Admin/StatisticsControllerTest.php new file mode 100644 index 000000000..f3a20f8ac --- /dev/null +++ b/tests/Unit/Http/Controllers/Admin/StatisticsControllerTest.php @@ -0,0 +1,113 @@ +allocationRepository = m::mock(AllocationRepositoryInterface::class); + $this->databaseRepository = m::mock(DatabaseRepositoryInterface::class); + $this->eggRepository = m::mock(EggRepositoryInterface::class); + $this->nodeRepository = m::mock(NodeRepositoryInterface::class); + $this->serverRepository = m::mock(ServerRepositoryInterface::class); + $this->userRepository = m::mock(UserRepositoryInterface::class); + } + + public function testIndexController() + { + $controller = $this->getController(); + + $this->serverRepository->shouldReceive('all')->withNoArgs(); + $this->nodeRepository->shouldReceive('all')->withNoArgs()->andReturn(collect([factory(Node::class)->make(), factory(Node::class)->make()])); + $this->userRepository->shouldReceive('count')->withNoArgs(); + $this->eggRepository->shouldReceive('count')->withNoArgs(); + $this->databaseRepository->shouldReceive('count')->withNoArgs(); + $this->allocationRepository->shouldReceive('count')->withNoArgs(); + $this->serverRepository->shouldReceive('getSuspendedServersCount')->withNoArgs(); + + $this->nodeRepository->shouldReceive('getUsageStatsRaw')->twice()->andReturn([ + 'memory' => [ + 'value' => 1024, + 'max' => 512, + ], + 'disk' => [ + 'value' => 1024, + 'max' => 512, + ] + ]); + + $controller->shouldReceive('injectJavascript')->once(); + + $response = $controller->index(); + + $this->assertIsViewResponse($response); + $this->assertViewNameEquals('admin.statistics', $response); + } + + private function getController() + { + return $this->buildMockedController(StatisticsController::class, [$this->allocationRepository, + $this->databaseRepository, + $this->eggRepository, + $this->nodeRepository, + $this->serverRepository, + $this->userRepository] + ); + } + +} \ No newline at end of file