Replace node repository
This commit is contained in:
parent
7266c66ebf
commit
aeb7590a6d
14 changed files with 101 additions and 250 deletions
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Contracts\Repository;
|
|
||||||
|
|
||||||
use Pterodactyl\Models\Node;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
interface NodeRepositoryInterface extends RepositoryInterface
|
|
||||||
{
|
|
||||||
public const THRESHOLD_PERCENTAGE_LOW = 75;
|
|
||||||
public const THRESHOLD_PERCENTAGE_MEDIUM = 90;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
public function getUsageStats(Node $node): array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
public function getUsageStatsRaw(Node $node): array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a single node with location and server information.
|
|
||||||
*/
|
|
||||||
public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach a paginated set of allocations to a node mode including
|
|
||||||
* any servers that are also attached to those allocations.
|
|
||||||
*/
|
|
||||||
public function loadNodeAllocations(Node $node, bool $refresh = false): Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a collection of nodes for all locations to use in server creation UI.
|
|
||||||
*/
|
|
||||||
public function getNodesForServerCreation(): Collection;
|
|
||||||
}
|
|
|
@ -9,7 +9,6 @@ use Illuminate\Support\Collection;
|
||||||
use Pterodactyl\Models\Allocation;
|
use Pterodactyl\Models\Allocation;
|
||||||
use Pterodactyl\Http\Controllers\Controller;
|
use Pterodactyl\Http\Controllers\Controller;
|
||||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||||
use Pterodactyl\Services\Helpers\SoftwareVersionService;
|
use Pterodactyl\Services\Helpers\SoftwareVersionService;
|
||||||
|
@ -26,7 +25,6 @@ class NodeViewController extends Controller
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AllocationRepository $allocationRepository,
|
private AllocationRepository $allocationRepository,
|
||||||
private LocationRepository $locationRepository,
|
private LocationRepository $locationRepository,
|
||||||
private NodeRepository $repository,
|
|
||||||
private ServerRepository $serverRepository,
|
private ServerRepository $serverRepository,
|
||||||
private SoftwareVersionService $versionService,
|
private SoftwareVersionService $versionService,
|
||||||
private ViewFactory $view
|
private ViewFactory $view
|
||||||
|
@ -38,11 +36,37 @@ class NodeViewController extends Controller
|
||||||
*/
|
*/
|
||||||
public function index(Request $request, Node $node): View
|
public function index(Request $request, Node $node): View
|
||||||
{
|
{
|
||||||
$node = $this->repository->loadLocationAndServerCount($node);
|
$node->load('location')->loadCount('servers');
|
||||||
|
|
||||||
|
$stats = Node::query()
|
||||||
|
->selectRaw('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();
|
||||||
|
|
||||||
|
$usageStats = Collection::make(['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));
|
||||||
|
}
|
||||||
|
|
||||||
|
$percent = ($value / $maxUsage) * 100;
|
||||||
|
|
||||||
|
return [
|
||||||
|
$key => [
|
||||||
|
'value' => number_format($value),
|
||||||
|
'max' => number_format($maxUsage),
|
||||||
|
'percent' => $percent,
|
||||||
|
'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
|
||||||
return $this->view->make('admin.nodes.view.index', [
|
return $this->view->make('admin.nodes.view.index', [
|
||||||
'node' => $node,
|
'node' => $node,
|
||||||
'stats' => $this->repository->getUsageStats($node),
|
'stats' => $usageStats,
|
||||||
'version' => $this->versionService,
|
'version' => $this->versionService,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +95,15 @@ class NodeViewController extends Controller
|
||||||
*/
|
*/
|
||||||
public function allocations(Request $request, Node $node): View
|
public function allocations(Request $request, Node $node): View
|
||||||
{
|
{
|
||||||
$node = $this->repository->loadNodeAllocations($node);
|
$node->setRelation(
|
||||||
|
'allocations',
|
||||||
|
$node->allocations()
|
||||||
|
->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL')
|
||||||
|
->orderByRaw('INET_ATON(ip) ASC')
|
||||||
|
->orderBy('port')
|
||||||
|
->with('server:id,name')
|
||||||
|
->paginate(50)
|
||||||
|
);
|
||||||
|
|
||||||
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
|
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ use Pterodactyl\Services\Nodes\NodeDeletionService;
|
||||||
use Pterodactyl\Services\Allocations\AssignmentService;
|
use Pterodactyl\Services\Allocations\AssignmentService;
|
||||||
use Pterodactyl\Services\Helpers\SoftwareVersionService;
|
use Pterodactyl\Services\Helpers\SoftwareVersionService;
|
||||||
use Pterodactyl\Http\Requests\Admin\Node\NodeFormRequest;
|
use Pterodactyl\Http\Requests\Admin\Node\NodeFormRequest;
|
||||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||||
use Pterodactyl\Http\Requests\Admin\Node\AllocationFormRequest;
|
use Pterodactyl\Http\Requests\Admin\Node\AllocationFormRequest;
|
||||||
use Pterodactyl\Services\Allocations\AllocationDeletionService;
|
use Pterodactyl\Services\Allocations\AllocationDeletionService;
|
||||||
|
@ -40,7 +39,6 @@ class NodesController extends Controller
|
||||||
protected NodeCreationService $creationService,
|
protected NodeCreationService $creationService,
|
||||||
protected NodeDeletionService $deletionService,
|
protected NodeDeletionService $deletionService,
|
||||||
protected LocationRepositoryInterface $locationRepository,
|
protected LocationRepositoryInterface $locationRepository,
|
||||||
protected NodeRepositoryInterface $repository,
|
|
||||||
protected ServerRepositoryInterface $serverRepository,
|
protected ServerRepositoryInterface $serverRepository,
|
||||||
protected NodeUpdateService $updateService,
|
protected NodeUpdateService $updateService,
|
||||||
protected SoftwareVersionService $versionService,
|
protected SoftwareVersionService $versionService,
|
||||||
|
|
|
@ -11,7 +11,6 @@ use Prologue\Alerts\AlertsMessageBag;
|
||||||
use Illuminate\View\Factory as ViewFactory;
|
use Illuminate\View\Factory as ViewFactory;
|
||||||
use Pterodactyl\Http\Controllers\Controller;
|
use Pterodactyl\Http\Controllers\Controller;
|
||||||
use Pterodactyl\Repositories\Eloquent\NestRepository;
|
use Pterodactyl\Repositories\Eloquent\NestRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Pterodactyl\Http\Requests\Admin\ServerFormRequest;
|
use Pterodactyl\Http\Requests\Admin\ServerFormRequest;
|
||||||
use Pterodactyl\Services\Servers\ServerCreationService;
|
use Pterodactyl\Services\Servers\ServerCreationService;
|
||||||
|
|
||||||
|
@ -23,7 +22,6 @@ class CreateServerController extends Controller
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AlertsMessageBag $alert,
|
private AlertsMessageBag $alert,
|
||||||
private NestRepository $nestRepository,
|
private NestRepository $nestRepository,
|
||||||
private NodeRepository $nodeRepository,
|
|
||||||
private ServerCreationService $creationService,
|
private ServerCreationService $creationService,
|
||||||
private ViewFactory $view
|
private ViewFactory $view
|
||||||
) {
|
) {
|
||||||
|
@ -46,7 +44,7 @@ class CreateServerController extends Controller
|
||||||
$nests = $this->nestRepository->getWithEggs();
|
$nests = $this->nestRepository->getWithEggs();
|
||||||
|
|
||||||
JavaScript::put([
|
JavaScript::put([
|
||||||
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
|
'nodeData' => Node::getForServerCreation(),
|
||||||
'nests' => $nests->map(function ($item) {
|
'nests' => $nests->map(function ($item) {
|
||||||
return array_merge($item->toArray(), [
|
return array_merge($item->toArray(), [
|
||||||
'eggs' => $item->eggs->keyBy('id')->toArray(),
|
'eggs' => $item->eggs->keyBy('id')->toArray(),
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
namespace Pterodactyl\Http\Controllers\Admin\Servers;
|
namespace Pterodactyl\Http\Controllers\Admin\Servers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Pterodactyl\Models\Node;
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
use Prologue\Alerts\AlertsMessageBag;
|
use Prologue\Alerts\AlertsMessageBag;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Pterodactyl\Models\ServerTransfer;
|
use Pterodactyl\Models\ServerTransfer;
|
||||||
use Pterodactyl\Http\Controllers\Controller;
|
use Pterodactyl\Http\Controllers\Controller;
|
||||||
use Pterodactyl\Services\Servers\TransferService;
|
use Pterodactyl\Services\Servers\TransferService;
|
||||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
||||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ class ServerTransferController extends Controller
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AlertsMessageBag $alert,
|
private AlertsMessageBag $alert,
|
||||||
private AllocationRepositoryInterface $allocationRepository,
|
private AllocationRepositoryInterface $allocationRepository,
|
||||||
private NodeRepository $nodeRepository,
|
|
||||||
private TransferService $transferService,
|
private TransferService $transferService,
|
||||||
private DaemonConfigurationRepository $daemonConfigurationRepository
|
private DaemonConfigurationRepository $daemonConfigurationRepository
|
||||||
) {
|
) {
|
||||||
|
@ -45,7 +44,13 @@ class ServerTransferController extends Controller
|
||||||
$additional_allocations = array_map('intval', $validatedData['allocation_additional'] ?? []);
|
$additional_allocations = array_map('intval', $validatedData['allocation_additional'] ?? []);
|
||||||
|
|
||||||
// Check if the node is viable for the transfer.
|
// Check if the node is viable for the transfer.
|
||||||
$node = $this->nodeRepository->getNodeWithResourceUsage($node_id);
|
$node = Node::query()
|
||||||
|
->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemonListen', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate'])
|
||||||
|
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
||||||
|
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
||||||
|
->where('nodes.id', $node_id)
|
||||||
|
->first();
|
||||||
|
|
||||||
if ($node->isViable($server->memory, $server->disk)) {
|
if ($node->isViable($server->memory, $server->disk)) {
|
||||||
// Check if the selected daemon is online.
|
// Check if the selected daemon is online.
|
||||||
$this->daemonConfigurationRepository->setNode($node)->getSystemInformation();
|
$this->daemonConfigurationRepository->setNode($node)->getSystemInformation();
|
||||||
|
|
|
@ -6,13 +6,13 @@ use JavaScript;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Pterodactyl\Models\Nest;
|
use Pterodactyl\Models\Nest;
|
||||||
|
use Pterodactyl\Models\Node;
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
use Pterodactyl\Exceptions\DisplayException;
|
use Pterodactyl\Exceptions\DisplayException;
|
||||||
use Pterodactyl\Http\Controllers\Controller;
|
use Pterodactyl\Http\Controllers\Controller;
|
||||||
use Pterodactyl\Services\Servers\EnvironmentService;
|
use Pterodactyl\Services\Servers\EnvironmentService;
|
||||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||||
use Pterodactyl\Repositories\Eloquent\NestRepository;
|
use Pterodactyl\Repositories\Eloquent\NestRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Pterodactyl\Repositories\Eloquent\MountRepository;
|
use Pterodactyl\Repositories\Eloquent\MountRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||||
|
@ -31,7 +31,6 @@ class ServerViewController extends Controller
|
||||||
private LocationRepository $locationRepository,
|
private LocationRepository $locationRepository,
|
||||||
private MountRepository $mountRepository,
|
private MountRepository $mountRepository,
|
||||||
private NestRepository $nestRepository,
|
private NestRepository $nestRepository,
|
||||||
private NodeRepository $nodeRepository,
|
|
||||||
private ServerRepository $repository,
|
private ServerRepository $repository,
|
||||||
private EnvironmentService $environmentService,
|
private EnvironmentService $environmentService,
|
||||||
private ViewFactory $view
|
private ViewFactory $view
|
||||||
|
@ -128,14 +127,14 @@ class ServerViewController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the panel doesn't have at least 2 nodes configured.
|
// Check if the panel doesn't have at least 2 nodes configured.
|
||||||
$nodes = $this->nodeRepository->all();
|
$nodeCount = Node::query()->count();
|
||||||
$canTransfer = false;
|
$canTransfer = false;
|
||||||
if (count($nodes) >= 2) {
|
if ($nodeCount >= 2) {
|
||||||
$canTransfer = true;
|
$canTransfer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JavaScript::put([
|
JavaScript::put([
|
||||||
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
|
'nodeData' => Node::getForServerCreation(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->view->make('admin.servers.view.manage', [
|
return $this->view->make('admin.servers.view.manage', [
|
||||||
|
|
|
@ -4,10 +4,9 @@ namespace Pterodactyl\Http\Middleware\Api\Daemon;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Pterodactyl\Models\Node;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
|
||||||
|
@ -23,14 +22,14 @@ class DaemonAuthenticate
|
||||||
/**
|
/**
|
||||||
* DaemonAuthenticate constructor.
|
* DaemonAuthenticate constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(private Encrypter $encrypter, private NodeRepository $repository)
|
public function __construct(private Encrypter $encrypter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a request from the daemon can be properly attributed back to a single node instance.
|
* Check if a request from the daemon can be properly attributed back to a single node instance.
|
||||||
*
|
*
|
||||||
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
|
* @throws HttpException
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): mixed
|
public function handle(Request $request, Closure $next): mixed
|
||||||
{
|
{
|
||||||
|
@ -43,24 +42,18 @@ class DaemonAuthenticate
|
||||||
}
|
}
|
||||||
|
|
||||||
$parts = explode('.', $bearer);
|
$parts = explode('.', $bearer);
|
||||||
// Ensure that all of the correct parts are provided in the header.
|
// Ensure that all the correct parts are provided in the header.
|
||||||
if (count($parts) !== 2 || empty($parts[0]) || empty($parts[1])) {
|
if (count($parts) !== 2 || empty($parts[0]) || empty($parts[1])) {
|
||||||
throw new BadRequestHttpException('The Authorization header provided was not in a valid format.');
|
throw new BadRequestHttpException('The Authorization header provided was not in a valid format.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
/** @var Node $node */
|
||||||
/** @var \Pterodactyl\Models\Node $node */
|
$node = Node::query()->where('daemon_token_id', $parts[0])->firstOrFail();
|
||||||
$node = $this->repository->findFirstWhere([
|
|
||||||
'daemon_token_id' => $parts[0],
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (hash_equals((string) $this->encrypter->decrypt($node->daemon_token), $parts[1])) {
|
if (hash_equals((string) $this->encrypter->decrypt($node->daemon_token), $parts[1])) {
|
||||||
$request->attributes->set('node', $node);
|
$request->attributes->set('node', $node);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
|
||||||
} catch (RecordNotFoundException $exception) {
|
|
||||||
// Do nothing, we don't want to expose a node not existing at all.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AccessDeniedHttpException('You are not authorized to access this resource.');
|
throw new AccessDeniedHttpException('You are not authorized to access this resource.');
|
||||||
|
|
|
@ -225,4 +225,26 @@ class Node extends Model
|
||||||
|
|
||||||
return ($this->sum_memory + $memory) <= $memoryLimit && ($this->sum_disk + $disk) <= $diskLimit;
|
return ($this->sum_memory + $memory) <= $memoryLimit && ($this->sum_disk + $disk) <= $diskLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getForServerCreation()
|
||||||
|
{
|
||||||
|
return self::with('allocations')->get()->map(function (Node $item) {
|
||||||
|
$filtered = $item->getRelation('allocations')->where('server_id', null)->map(function ($map) {
|
||||||
|
return collect($map)->only(['id', 'ip', 'port']);
|
||||||
|
});
|
||||||
|
|
||||||
|
$item->ports = $filtered->map(function ($map) {
|
||||||
|
return [
|
||||||
|
'id' => $map['id'],
|
||||||
|
'text' => sprintf('%s:%s', $map['ip'], $map['port']),
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $item->id,
|
||||||
|
'text' => $item->name,
|
||||||
|
'allocations' => $item->ports,
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ namespace Pterodactyl\Providers;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Pterodactyl\Repositories\Eloquent\EggRepository;
|
use Pterodactyl\Repositories\Eloquent\EggRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\NestRepository;
|
use Pterodactyl\Repositories\Eloquent\NestRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Pterodactyl\Repositories\Eloquent\TaskRepository;
|
use Pterodactyl\Repositories\Eloquent\TaskRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\UserRepository;
|
use Pterodactyl\Repositories\Eloquent\UserRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
|
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
|
||||||
|
@ -20,7 +19,6 @@ use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
||||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||||
use Pterodactyl\Repositories\Eloquent\EggVariableRepository;
|
use Pterodactyl\Repositories\Eloquent\EggVariableRepository;
|
||||||
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||||
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
|
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
|
||||||
|
@ -54,7 +52,6 @@ class RepositoryServiceProvider extends ServiceProvider
|
||||||
$this->app->bind(EggVariableRepositoryInterface::class, EggVariableRepository::class);
|
$this->app->bind(EggVariableRepositoryInterface::class, EggVariableRepository::class);
|
||||||
$this->app->bind(LocationRepositoryInterface::class, LocationRepository::class);
|
$this->app->bind(LocationRepositoryInterface::class, LocationRepository::class);
|
||||||
$this->app->bind(NestRepositoryInterface::class, NestRepository::class);
|
$this->app->bind(NestRepositoryInterface::class, NestRepository::class);
|
||||||
$this->app->bind(NodeRepositoryInterface::class, NodeRepository::class);
|
|
||||||
$this->app->bind(ScheduleRepositoryInterface::class, ScheduleRepository::class);
|
$this->app->bind(ScheduleRepositoryInterface::class, ScheduleRepository::class);
|
||||||
$this->app->bind(ServerRepositoryInterface::class, ServerRepository::class);
|
$this->app->bind(ServerRepositoryInterface::class, ServerRepository::class);
|
||||||
$this->app->bind(ServerVariableRepositoryInterface::class, ServerVariableRepository::class);
|
$this->app->bind(ServerVariableRepositoryInterface::class, ServerVariableRepository::class);
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
namespace Pterodactyl\Repositories\Eloquent;
|
namespace Pterodactyl\Repositories\Eloquent;
|
||||||
|
|
||||||
use Pterodactyl\Models\Node;
|
use Pterodactyl\Models\Node;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
|
|
||||||
class NodeRepository extends EloquentRepository implements NodeRepositoryInterface
|
class NodeRepository
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Return the model backing this repository.
|
* Return the model backing this repository.
|
||||||
|
@ -15,138 +13,4 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
|
||||||
{
|
{
|
||||||
return Node::class;
|
return Node::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
public function getUsageStats(Node $node): array
|
|
||||||
{
|
|
||||||
$stats = $this->getBuilder()
|
|
||||||
->selectRaw('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 Collection::make(['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));
|
|
||||||
}
|
|
||||||
|
|
||||||
$percent = ($value / $maxUsage) * 100;
|
|
||||||
|
|
||||||
return [
|
|
||||||
$key => [
|
|
||||||
'value' => number_format($value),
|
|
||||||
'max' => number_format($maxUsage),
|
|
||||||
'percent' => $percent,
|
|
||||||
'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
})
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
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 a single node with location and server information.
|
|
||||||
*/
|
|
||||||
public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node
|
|
||||||
{
|
|
||||||
if (!$node->relationLoaded('location') || $refresh) {
|
|
||||||
$node->load('location');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is quite ugly and can probably be improved down the road.
|
|
||||||
// And by probably, I mean it should.
|
|
||||||
if (is_null($node->servers_count) || $refresh) {
|
|
||||||
$node->load('servers');
|
|
||||||
$node->setRelation('servers_count', count($node->getRelation('servers')));
|
|
||||||
unset($node->servers);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach a paginated set of allocations to a node mode including
|
|
||||||
* any servers that are also attached to those allocations.
|
|
||||||
*/
|
|
||||||
public function loadNodeAllocations(Node $node, bool $refresh = false): Node
|
|
||||||
{
|
|
||||||
$node->setRelation(
|
|
||||||
'allocations',
|
|
||||||
$node->allocations()
|
|
||||||
->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL')
|
|
||||||
->orderByRaw('INET_ATON(ip) ASC')
|
|
||||||
->orderBy('port')
|
|
||||||
->with('server:id,name')
|
|
||||||
->paginate(50)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a collection of nodes for all locations to use in server creation UI.
|
|
||||||
*/
|
|
||||||
public function getNodesForServerCreation(): Collection
|
|
||||||
{
|
|
||||||
return $this->getBuilder()->with('allocations')->get()->map(function (Node $item) {
|
|
||||||
$filtered = $item->getRelation('allocations')->where('server_id', null)->map(function ($map) {
|
|
||||||
return collect($map)->only(['id', 'ip', 'port']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$item->ports = $filtered->map(function ($map) {
|
|
||||||
return [
|
|
||||||
'id' => $map['id'],
|
|
||||||
'text' => sprintf('%s:%s', $map['ip'], $map['port']),
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $item->id,
|
|
||||||
'text' => $item->name,
|
|
||||||
'allocations' => $item->ports,
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a node with the given id with the Node's resource usage.
|
|
||||||
*/
|
|
||||||
public function getNodeWithResourceUsage(int $node_id): Node
|
|
||||||
{
|
|
||||||
$instance = $this->getBuilder()
|
|
||||||
->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemonListen', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate'])
|
|
||||||
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
|
||||||
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
|
||||||
->where('nodes.id', $node_id);
|
|
||||||
|
|
||||||
return $instance->first();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,39 +2,26 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Services\Locations;
|
namespace Pterodactyl\Services\Locations;
|
||||||
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
use Pterodactyl\Models\Location;
|
use Pterodactyl\Models\Location;
|
||||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
use Pterodactyl\Exceptions\Service\Location\HasActiveNodesException;
|
use Pterodactyl\Exceptions\Service\Location\HasActiveNodesException;
|
||||||
|
|
||||||
class LocationDeletionService
|
class LocationDeletionService
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* LocationDeletionService constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
protected LocationRepositoryInterface $repository,
|
|
||||||
protected NodeRepositoryInterface $nodeRepository
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete an existing location.
|
* Delete an existing location.
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException
|
* @throws HasActiveNodesException
|
||||||
*/
|
*/
|
||||||
public function handle(Location|int $location): ?int
|
public function handle(Location|int $location): ?int
|
||||||
{
|
{
|
||||||
$location = ($location instanceof Location) ? $location->id : $location;
|
/** @var Location $location */
|
||||||
|
$location = ($location instanceof Location) ? $location : Location::query()->findOrFail($location);
|
||||||
|
|
||||||
Assert::integerish($location, 'First argument passed to handle must be numeric or an instance of ' . Location::class . ', received %s.');
|
$count = $location->nodes()->count();
|
||||||
|
|
||||||
$count = $this->nodeRepository->findCountWhere([['location_id', '=', $location]]);
|
|
||||||
if ($count > 0) {
|
if ($count > 0) {
|
||||||
throw new HasActiveNodesException(trans('exceptions.locations.has_nodes'));
|
throw new HasActiveNodesException(trans('exceptions.locations.has_nodes'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->repository->delete($location);
|
return $location->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,19 @@ use Ramsey\Uuid\Uuid;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Pterodactyl\Models\Node;
|
use Pterodactyl\Models\Node;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
|
|
||||||
class NodeCreationService
|
class NodeCreationService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* NodeCreationService constructor.
|
* NodeCreationService constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(private Encrypter $encrypter, protected NodeRepositoryInterface $repository)
|
public function __construct(private Encrypter $encrypter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new node on the panel.
|
* Create a new node on the panel.
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
*/
|
*/
|
||||||
public function handle(array $data): Node
|
public function handle(array $data): Node
|
||||||
{
|
{
|
||||||
|
@ -28,6 +26,9 @@ class NodeCreationService
|
||||||
$data['daemon_token'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH));
|
$data['daemon_token'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH));
|
||||||
$data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH);
|
$data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH);
|
||||||
|
|
||||||
return $this->repository->create($data, true, true);
|
/** @var Node $node */
|
||||||
|
$node = Node::query()->create($data);
|
||||||
|
|
||||||
|
return $node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,38 +4,33 @@ namespace Pterodactyl\Services\Nodes;
|
||||||
|
|
||||||
use Pterodactyl\Models\Node;
|
use Pterodactyl\Models\Node;
|
||||||
use Illuminate\Contracts\Translation\Translator;
|
use Illuminate\Contracts\Translation\Translator;
|
||||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use Pterodactyl\Exceptions\Service\HasActiveServersException;
|
use Pterodactyl\Exceptions\Service\HasActiveServersException;
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
|
||||||
|
|
||||||
class NodeDeletionService
|
class NodeDeletionService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* NodeDeletionService constructor.
|
* NodeDeletionService constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(protected Translator $translator)
|
||||||
protected NodeRepositoryInterface $repository,
|
{
|
||||||
protected ServerRepositoryInterface $serverRepository,
|
|
||||||
protected Translator $translator
|
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a node from the panel if no servers are attached to it.
|
* Delete a node from the panel if no servers are attached to it.
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
|
* @throws HasActiveServersException
|
||||||
*/
|
*/
|
||||||
public function handle(int|Node $node): int
|
public function handle(int|Node $node): int
|
||||||
{
|
{
|
||||||
if ($node instanceof Node) {
|
if (is_int($node)) {
|
||||||
$node = $node->id;
|
$node = Node::query()->findOrFail($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
$servers = $this->serverRepository->setColumns('id')->findCountWhere([['node_id', '=', $node]]);
|
$servers = $node->servers()->count();
|
||||||
if ($servers > 0) {
|
if ($servers > 0) {
|
||||||
throw new HasActiveServersException($this->translator->get('exceptions.node.servers_attached'));
|
throw new HasActiveServersException($this->translator->get('exceptions.node.servers_attached'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->repository->delete($node);
|
return $node->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use Pterodactyl\Models\Node;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
||||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||||
use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
||||||
|
@ -20,8 +19,7 @@ class NodeUpdateService
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ConnectionInterface $connection,
|
private ConnectionInterface $connection,
|
||||||
private DaemonConfigurationRepository $configurationRepository,
|
private DaemonConfigurationRepository $configurationRepository,
|
||||||
private Encrypter $encrypter,
|
private Encrypter $encrypter
|
||||||
private NodeRepository $repository
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +37,7 @@ class NodeUpdateService
|
||||||
|
|
||||||
[$updated, $exception] = $this->connection->transaction(function () use ($data, $node) {
|
[$updated, $exception] = $this->connection->transaction(function () use ($data, $node) {
|
||||||
/** @var \Pterodactyl\Models\Node $updated */
|
/** @var \Pterodactyl\Models\Node $updated */
|
||||||
$updated = $this->repository->withFreshModel()->update($node->id, $data, true, true);
|
$updated = (clone $node)->update($data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If we're changing the FQDN for the node, use the newly provided FQDN for the connection
|
// If we're changing the FQDN for the node, use the newly provided FQDN for the connection
|
||||||
|
|
Loading…
Reference in a new issue