Merge branch 'develop' into feature/server-mounts
This commit is contained in:
commit
295f09ca43
195 changed files with 5395 additions and 5417 deletions
|
@ -3,36 +3,9 @@
|
|||
namespace Pterodactyl\Contracts\Repository;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
|
||||
interface AllocationRepositoryInterface extends RepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Set an array of allocation IDs to be assigned to a specific server.
|
||||
*
|
||||
* @param int|null $server
|
||||
* @param array $ids
|
||||
* @return int
|
||||
*/
|
||||
public function assignAllocationsToServer(int $server = null, array $ids): int;
|
||||
|
||||
/**
|
||||
* Return all of the allocations for a specific node.
|
||||
*
|
||||
* @param int $node
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getAllocationsForNode(int $node): Collection;
|
||||
|
||||
/**
|
||||
* Return all of the allocations for a node in a paginated format.
|
||||
*
|
||||
* @param int $node
|
||||
* @param int $perPage
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
*/
|
||||
public function getPaginatedAllocationsForNode(int $node, int $perPage = 100): LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* Return all of the unique IPs that exist for a given node.
|
||||
*
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Pterodactyl\Contracts\Repository;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
|
@ -107,16 +106,6 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter
|
|||
*/
|
||||
public function getDaemonServiceData(Server $server, bool $refresh = false): array;
|
||||
|
||||
/**
|
||||
* Return a paginated list of servers that a user can access at a given level.
|
||||
*
|
||||
* @param \Pterodactyl\Models\User $user
|
||||
* @param int $level
|
||||
* @param bool|int $paginate
|
||||
* @return \Illuminate\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function filterUserAccessServers(User $user, int $level, $paginate = 25);
|
||||
|
||||
/**
|
||||
* Return a server by UUID.
|
||||
*
|
||||
|
|
|
@ -3,11 +3,10 @@
|
|||
namespace Pterodactyl\Http\Controllers\Api\Application\Nodes;
|
||||
|
||||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
use Pterodactyl\Services\Allocations\AssignmentService;
|
||||
use Pterodactyl\Services\Allocations\AllocationDeletionService;
|
||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||
use Pterodactyl\Transformers\Api\Application\AllocationTransformer;
|
||||
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Allocations\GetAllocationsRequest;
|
||||
|
@ -26,41 +25,32 @@ class AllocationController extends ApplicationApiController
|
|||
*/
|
||||
private $deletionService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* AllocationController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\Allocations\AssignmentService $assignmentService
|
||||
* @param \Pterodactyl\Services\Allocations\AllocationDeletionService $deletionService
|
||||
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
AssignmentService $assignmentService,
|
||||
AllocationDeletionService $deletionService,
|
||||
AllocationRepositoryInterface $repository
|
||||
AllocationDeletionService $deletionService
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
$this->assignmentService = $assignmentService;
|
||||
$this->deletionService = $deletionService;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the allocations that exist for a given node.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Application\Allocations\GetAllocationsRequest $request
|
||||
* @param \Pterodactyl\Models\Node $node
|
||||
* @return array
|
||||
*/
|
||||
public function index(GetAllocationsRequest $request): array
|
||||
public function index(GetAllocationsRequest $request, Node $node): array
|
||||
{
|
||||
$allocations = $this->repository->getPaginatedAllocationsForNode(
|
||||
$request->getModel(Node::class)->id, 50
|
||||
);
|
||||
$allocations = $node->allocations()->paginate(50);
|
||||
|
||||
return $this->fractal->collection($allocations)
|
||||
->transformWith($this->getTransformer(AllocationTransformer::class))
|
||||
|
@ -71,32 +61,35 @@ class AllocationController extends ApplicationApiController
|
|||
* Store new allocations for a given node.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Application\Allocations\StoreAllocationRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param \Pterodactyl\Models\Node $node
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
|
||||
*/
|
||||
public function store(StoreAllocationRequest $request): Response
|
||||
public function store(StoreAllocationRequest $request, Node $node): JsonResponse
|
||||
{
|
||||
$this->assignmentService->handle($request->getModel(Node::class), $request->validated());
|
||||
$this->assignmentService->handle($node, $request->validated());
|
||||
|
||||
return response('', 204);
|
||||
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a specific allocation from the Panel.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Application\Allocations\DeleteAllocationRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param \Pterodactyl\Models\Node $node
|
||||
* @param \Pterodactyl\Models\Allocation $allocation
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException
|
||||
*/
|
||||
public function delete(DeleteAllocationRequest $request): Response
|
||||
public function delete(DeleteAllocationRequest $request, Node $node, Allocation $allocation): JsonResponse
|
||||
{
|
||||
$this->deletionService->handle($request->getModel(Allocation::class));
|
||||
$this->deletionService->handle($allocation);
|
||||
|
||||
return response('', 204);
|
||||
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,40 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
|
|||
|
||||
abstract class ClientApiController extends ApplicationApiController
|
||||
{
|
||||
/**
|
||||
* Returns only the includes which are valid for the given transformer.
|
||||
*
|
||||
* @param \Pterodactyl\Transformers\Api\Client\BaseClientTransformer $transformer
|
||||
* @param array $merge
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getIncludesForTransformer(BaseClientTransformer $transformer, array $merge = [])
|
||||
{
|
||||
$filtered = array_filter($this->parseIncludes(), function ($datum) use ($transformer) {
|
||||
return in_array($datum, $transformer->getAvailableIncludes());
|
||||
});
|
||||
|
||||
return array_merge($filtered, $merge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parsed includes for this request.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function parseIncludes()
|
||||
{
|
||||
$includes = $this->request->query('include') ?? [];
|
||||
|
||||
if (! is_string($includes)) {
|
||||
return $includes;
|
||||
}
|
||||
|
||||
return array_map(function ($item) {
|
||||
return trim($item);
|
||||
}, explode(',', $includes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of an application transformer.
|
||||
*
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
namespace Pterodactyl\Http\Controllers\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Permission;
|
||||
use Spatie\QueryBuilder\QueryBuilder;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Transformers\Api\Client\ServerTransformer;
|
||||
use Pterodactyl\Http\Requests\Api\Client\GetServersRequest;
|
||||
|
@ -36,32 +38,36 @@ class ClientController extends ClientApiController
|
|||
*/
|
||||
public function index(GetServersRequest $request): array
|
||||
{
|
||||
// Check for the filter parameter on the request.
|
||||
switch ($request->input('filter')) {
|
||||
case 'all':
|
||||
$filter = User::FILTER_LEVEL_ALL;
|
||||
break;
|
||||
case 'admin':
|
||||
$filter = User::FILTER_LEVEL_ADMIN;
|
||||
break;
|
||||
case 'owner':
|
||||
$filter = User::FILTER_LEVEL_OWNER;
|
||||
break;
|
||||
case 'subuser-of':
|
||||
default:
|
||||
$filter = User::FILTER_LEVEL_SUBUSER;
|
||||
break;
|
||||
$user = $request->user();
|
||||
$level = $request->getFilterLevel();
|
||||
$transformer = $this->getTransformer(ServerTransformer::class);
|
||||
|
||||
// Start the query builder and ensure we eager load any requested relationships from the request.
|
||||
$builder = Server::query()->with($this->getIncludesForTransformer($transformer, ['node']));
|
||||
|
||||
if ($level === User::FILTER_LEVEL_OWNER) {
|
||||
$builder = $builder->where('owner_id', $request->user()->id);
|
||||
}
|
||||
// If set to all, display all servers they can access, including those they access as an
|
||||
// admin. If set to subuser, only return the servers they can access because they are owner,
|
||||
// or marked as a subuser of the server.
|
||||
elseif (($level === User::FILTER_LEVEL_ALL && ! $user->root_admin) || $level === User::FILTER_LEVEL_SUBUSER) {
|
||||
$builder = $builder->whereIn('id', $user->accessibleServers()->pluck('id')->all());
|
||||
}
|
||||
// If set to admin, only display the servers a user can access because they are an administrator.
|
||||
// This means only servers the user would not have access to if they were not an admin (because they
|
||||
// are not an owner or subuser) are returned.
|
||||
elseif ($level === User::FILTER_LEVEL_ADMIN && $user->root_admin) {
|
||||
$builder = $builder->whereNotIn('id', $user->accessibleServers()->pluck('id')->all());
|
||||
}
|
||||
|
||||
$servers = $this->repository
|
||||
->setSearchTerm($request->input('query'))
|
||||
->filterUserAccessServers(
|
||||
$request->user(), $filter, config('pterodactyl.paginate.frontend.servers')
|
||||
);
|
||||
$builder = QueryBuilder::for($builder)->allowedFilters(
|
||||
'uuid', 'name', 'external_id'
|
||||
);
|
||||
|
||||
return $this->fractal->collection($servers)
|
||||
->transformWith($this->getTransformer(ServerTransformer::class))
|
||||
->toArray();
|
||||
$servers = $builder->paginate(min($request->query('per_page', 50), 100))->appends($request->query());
|
||||
|
||||
return $this->fractal->transformWith($transformer)->collection($servers)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -159,7 +159,7 @@ class FileController extends ClientApiController
|
|||
{
|
||||
$this->fileRepository
|
||||
->setServer($server)
|
||||
->createDirectory($request->input('name'), $request->input('directory', '/'));
|
||||
->createDirectory($request->input('name'), $request->input('root', '/'));
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
||||
use Pterodactyl\Transformers\Api\Client\AllocationTransformer;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\GetNetworkRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\DeleteAllocationRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\UpdateAllocationRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\SetPrimaryAllocationRequest;
|
||||
|
||||
class NetworkAllocationController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\AllocationRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
|
||||
*/
|
||||
private $serverRepository;
|
||||
|
||||
/**
|
||||
* NetworkController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\AllocationRepository $repository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $serverRepository
|
||||
*/
|
||||
public function __construct(
|
||||
AllocationRepository $repository,
|
||||
ServerRepository $serverRepository
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
$this->repository = $repository;
|
||||
$this->serverRepository = $serverRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all of the allocations available to a server and wether or
|
||||
* not they are currently assigned as the primary for this server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Network\GetNetworkRequest $request
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @return array
|
||||
*/
|
||||
public function index(GetNetworkRequest $request, Server $server): array
|
||||
{
|
||||
return $this->fractal->collection($server->allocations)
|
||||
->transformWith($this->getTransformer(AllocationTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the primary allocation for a server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Network\UpdateAllocationRequest $request
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param \Pterodactyl\Models\Allocation $allocation
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(UpdateAllocationRequest $request, Server $server, Allocation $allocation): array
|
||||
{
|
||||
$allocation = $this->repository->update($allocation->id, [
|
||||
'notes' => $request->input('notes'),
|
||||
]);
|
||||
|
||||
return $this->fractal->item($allocation)
|
||||
->transformWith($this->getTransformer(AllocationTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the primary allocation for a server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Network\SetPrimaryAllocationRequest $request
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param \Pterodactyl\Models\Allocation $allocation
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function setPrimary(SetPrimaryAllocationRequest $request, Server $server, Allocation $allocation): array
|
||||
{
|
||||
$this->serverRepository->update($server->id, ['allocation_id' => $allocation->id]);
|
||||
|
||||
return $this->fractal->item($allocation)
|
||||
->transformWith($this->getTransformer(AllocationTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an allocation from a server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Network\DeleteAllocationRequest $request
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param \Pterodactyl\Models\Allocation $allocation
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function delete(DeleteAllocationRequest $request, Server $server, Allocation $allocation)
|
||||
{
|
||||
if ($allocation->id === $server->allocation_id) {
|
||||
throw new DisplayException(
|
||||
'Cannot delete the primary allocation for a server.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->repository->update($allocation->id, ['server_id' => null, 'notes' => null]);
|
||||
|
||||
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
||||
use Pterodactyl\Transformers\Api\Client\AllocationTransformer;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\GetNetworkRequest;
|
||||
|
||||
class NetworkController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\AllocationRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* NetworkController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\AllocationRepository $repository
|
||||
*/
|
||||
public function __construct(AllocationRepository $repository)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all of the allocations available to a server and wether or
|
||||
* not they are currently assigned as the primary for this server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Network\GetNetworkRequest $request
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @return array
|
||||
*/
|
||||
public function index(GetNetworkRequest $request, Server $server): array
|
||||
{
|
||||
$allocations = $this->repository->findWhere([
|
||||
['server_id', '=', $server->id],
|
||||
]);
|
||||
|
||||
return $this->fractal->collection($allocations)
|
||||
->transformWith($this->getTransformer(AllocationTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
}
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Pterodactyl\Http\Controllers\Base;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
|
||||
|
@ -27,15 +25,10 @@ class IndexController extends Controller
|
|||
/**
|
||||
* Returns listing of user's servers.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
public function index()
|
||||
{
|
||||
$servers = $this->repository->setSearchTerm($request->input('query'))->filterUserAccessServers(
|
||||
$request->user(), User::FILTER_LEVEL_ALL, config('pterodactyl.paginate.frontend.servers')
|
||||
);
|
||||
|
||||
return view('templates/base.core', ['servers' => $servers]);
|
||||
return view('templates/base.core');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Middleware\Api\Client\Server;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class AllocationBelongsToServer
|
||||
{
|
||||
/**
|
||||
* Ensure that the allocation found in the URL belongs to the server being queried.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
/** @var \Pterodactyl\Models\Server $server */
|
||||
$server = $request->route()->parameter('server');
|
||||
/** @var \Pterodactyl\Models\Allocation|null $allocation */
|
||||
$allocation = $request->route()->parameter('allocation');
|
||||
|
||||
if ($allocation && $allocation->server_id !== $server->id) {
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -65,7 +65,7 @@ class AuthenticateServerAccess
|
|||
}
|
||||
|
||||
if ($server->suspended) {
|
||||
throw new AccessDeniedHttpException('This server is currenty suspended and the functionality requested is unavailable.');
|
||||
throw new AccessDeniedHttpException('This server is currently suspended and the functionality requested is unavailable.');
|
||||
}
|
||||
|
||||
if (! $server->isInstalled()) {
|
||||
|
|
|
@ -4,12 +4,12 @@ namespace Pterodactyl\Http\Middleware\Api\Client;
|
|||
|
||||
use Closure;
|
||||
use Pterodactyl\Models\Backup;
|
||||
use Pterodactyl\Models\Database;
|
||||
use Illuminate\Container\Container;
|
||||
use Pterodactyl\Contracts\Extensions\HashidsInterface;
|
||||
use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
|
||||
class SubstituteClientApiBindings extends ApiSubstituteBindings
|
||||
{
|
||||
|
@ -43,17 +43,9 @@ class SubstituteClientApiBindings extends ApiSubstituteBindings
|
|||
});
|
||||
|
||||
$this->router->bind('database', function ($value) use ($request) {
|
||||
try {
|
||||
$id = Container::getInstance()->make(HashidsInterface::class)->decodeFirst($value);
|
||||
$id = Container::getInstance()->make(HashidsInterface::class)->decodeFirst($value);
|
||||
|
||||
return Container::getInstance()->make(DatabaseRepositoryInterface::class)->findFirstWhere([
|
||||
['id', '=', $id],
|
||||
]);
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
$request->attributes->set('is_missing_model', true);
|
||||
|
||||
return null;
|
||||
}
|
||||
return Database::query()->where('id', $id)->firstOrFail();
|
||||
});
|
||||
|
||||
$this->router->model('backup', Backup::class, function ($value) {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
|
||||
class GetServersRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
|
@ -11,4 +13,28 @@ class GetServersRequest extends ClientApiRequest
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filtering method for servers when the client base endpoint is requested.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFilterLevel(): int
|
||||
{
|
||||
switch ($this->input('type')) {
|
||||
case 'all':
|
||||
return User::FILTER_LEVEL_ALL;
|
||||
break;
|
||||
case 'admin':
|
||||
return User::FILTER_LEVEL_ADMIN;
|
||||
break;
|
||||
case 'owner':
|
||||
return User::FILTER_LEVEL_OWNER;
|
||||
break;
|
||||
case 'subuser-of':
|
||||
default:
|
||||
return User::FILTER_LEVEL_SUBUSER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
use Pterodactyl\Models\Permission;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class DeleteAllocationRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return Permission::ACTION_ALLOCATION_DELETE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
class SetPrimaryAllocationRequest extends UpdateAllocationRequest
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
use Pterodactyl\Models\Allocation;
|
||||
use Pterodactyl\Models\Permission;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class UpdateAllocationRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return Permission::ACTION_ALLOCATION_UPDATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = Allocation::getRules();
|
||||
|
||||
return [
|
||||
'notes' => array_merge($rules['notes'], ['present']),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\ViewComposers\Server;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ServerDataComposer
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Http\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* ServerDataComposer constructor.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach server data to a view automatically.
|
||||
*
|
||||
* @param \Illuminate\View\View $view
|
||||
*/
|
||||
public function compose(View $view)
|
||||
{
|
||||
$server = $this->request->get('server');
|
||||
|
||||
$view->with('server', $server);
|
||||
$view->with('node', object_get($server, 'node'));
|
||||
$view->with('daemon_token', $this->request->get('server_token'));
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\ViewComposers;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
|
||||
class ServerListComposer
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Http\Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ServerListComposer constructor.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(Request $request, ServerRepositoryInterface $repository)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a list of servers the user can access to the view.
|
||||
*
|
||||
* @param \Illuminate\View\View $view
|
||||
*/
|
||||
public function compose(View $view)
|
||||
{
|
||||
if (! $this->request->user()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$servers = $this->repository
|
||||
->setColumns(['id', 'owner_id', 'uuidShort', 'name', 'description'])
|
||||
->filterUserAccessServers($this->request->user(), User::FILTER_LEVEL_SUBUSER, false);
|
||||
|
||||
$view->with('sidebarServerList', $servers);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace Pterodactyl\Models;
|
|||
* @property string|null $ip_alias
|
||||
* @property int $port
|
||||
* @property int|null $server_id
|
||||
* @property string|null $notes
|
||||
* @property \Carbon\Carbon|null $created_at
|
||||
* @property \Carbon\Carbon|null $updated_at
|
||||
*
|
||||
|
@ -60,6 +61,7 @@ class Allocation extends Model
|
|||
'port' => 'required|numeric|between:1024,65553',
|
||||
'ip_alias' => 'nullable|string',
|
||||
'server_id' => 'nullable|exists:servers,id',
|
||||
'notes' => 'nullable|string|max:256',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,7 +44,9 @@ class Permission extends Model
|
|||
const ACTION_BACKUP_DOWNLOAD = 'backup.download';
|
||||
|
||||
const ACTION_ALLOCATION_READ = 'allocation.read';
|
||||
const ACTION_ALLOCIATION_UPDATE = 'allocation.update';
|
||||
const ACTION_ALLOCATION_CREATE = 'allocation.create';
|
||||
const ACTION_ALLOCATION_UPDATE = 'allocation.update';
|
||||
const ACTION_ALLOCATION_DELETE = 'allocation.delete';
|
||||
|
||||
const ACTION_FILE_READ = 'file.read';
|
||||
const ACTION_FILE_CREATE = 'file.create';
|
||||
|
@ -157,7 +159,9 @@ class Permission extends Model
|
|||
'description' => 'Permissions that control a user\'s ability to modify the port allocations for this server.',
|
||||
'keys' => [
|
||||
'read' => 'Allows a user to view the allocations assigned to this server.',
|
||||
'update' => 'Allows a user to modify the allocations assigned to this server.',
|
||||
'create' => 'Allows a user to assign additional allocations to the server.',
|
||||
'update' => 'Allows a user to change the primary server allocation and attach notes to each allocation.',
|
||||
'delete' => 'Allows a user to delete an allocation from the server.',
|
||||
],
|
||||
],
|
||||
|
||||
|
|
|
@ -17,6 +17,11 @@ class RecoveryToken extends Model
|
|||
*/
|
||||
const UPDATED_AT = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@ use Illuminate\Support\Collection;
|
|||
use Illuminate\Validation\Rules\In;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Pterodactyl\Models\Traits\Searchable;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Pterodactyl\Traits\Helpers\AvailableLanguages;
|
||||
|
@ -260,4 +261,21 @@ class User extends Model implements
|
|||
{
|
||||
return $this->hasMany(RecoveryToken::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the servers that a user can access by way of being the owner of the
|
||||
* server, or because they are assigned as a subuser for that server.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function accessibleServers()
|
||||
{
|
||||
return Server::query()
|
||||
->select('servers.*')
|
||||
->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id')
|
||||
->where(function (Builder $builder) {
|
||||
$builder->where('servers.owner_id', $this->id)->orWhere('subusers.user_id', $this->id);
|
||||
})
|
||||
->groupBy('servers.id');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ namespace Pterodactyl\Providers;
|
|||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Pterodactyl\Http\ViewComposers\AssetComposer;
|
||||
use Pterodactyl\Http\ViewComposers\ServerListComposer;
|
||||
use Pterodactyl\Http\ViewComposers\Server\ServerDataComposer;
|
||||
|
||||
class ViewComposerServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
@ -15,10 +13,5 @@ class ViewComposerServiceProvider extends ServiceProvider
|
|||
public function boot()
|
||||
{
|
||||
$this->app->make('view')->composer('*', AssetComposer::class);
|
||||
|
||||
$this->app->make('view')->composer('server.*', ServerDataComposer::class);
|
||||
|
||||
// Add data to make the sidebar work when viewing a server.
|
||||
$this->app->make('view')->composer(['server.*'], ServerListComposer::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace Pterodactyl\Repositories\Eloquent;
|
|||
use Illuminate\Support\Collection;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||
|
||||
class AllocationRepository extends EloquentRepository implements AllocationRepositoryInterface
|
||||
|
@ -20,41 +19,6 @@ class AllocationRepository extends EloquentRepository implements AllocationRepos
|
|||
return Allocation::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an array of allocation IDs to be assigned to a specific server.
|
||||
*
|
||||
* @param int|null $server
|
||||
* @param array $ids
|
||||
* @return int
|
||||
*/
|
||||
public function assignAllocationsToServer(int $server = null, array $ids): int
|
||||
{
|
||||
return $this->getBuilder()->whereIn('id', $ids)->update(['server_id' => $server]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the allocations for a specific node.
|
||||
*
|
||||
* @param int $node
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getAllocationsForNode(int $node): Collection
|
||||
{
|
||||
return $this->getBuilder()->where('node_id', $node)->get($this->getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the allocations for a node in a paginated format.
|
||||
*
|
||||
* @param int $node
|
||||
* @param int $perPage
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
*/
|
||||
public function getPaginatedAllocationsForNode(int $node, int $perPage = 100): LengthAwarePaginator
|
||||
{
|
||||
return $this->getBuilder()->where('node_id', $node)->paginate($perPage, $this->getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the unique IPs that exist for a given node.
|
||||
*
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
namespace Pterodactyl\Repositories\Eloquent;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Illuminate\Support\Collection;
|
||||
use Pterodactyl\Repositories\Repository;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
|
@ -15,6 +17,53 @@ use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface;
|
|||
|
||||
abstract class EloquentRepository extends Repository implements RepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useRequestFilters = false;
|
||||
|
||||
/**
|
||||
* Determines if the repository function should use filters off the request object
|
||||
* present when returning results. This allows repository methods to be called in API
|
||||
* context's such that we can pass through ?filter[name]=Dane&sort=desc for example.
|
||||
*
|
||||
* @param bool $usingFilters
|
||||
* @return $this
|
||||
*/
|
||||
public function usingRequestFilters($usingFilters = true)
|
||||
{
|
||||
$this->useRequestFilters = $usingFilters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request instance.
|
||||
*
|
||||
* @return \Illuminate\Http\Request
|
||||
*/
|
||||
protected function request()
|
||||
{
|
||||
return $this->app->make(Request::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginate the response data based on the page para.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $instance
|
||||
* @param int $default
|
||||
*
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
*/
|
||||
protected function paginate(Builder $instance, int $default = 50)
|
||||
{
|
||||
if (! $this->useRequestFilters) {
|
||||
return $instance->paginate($default);
|
||||
}
|
||||
|
||||
return $instance->paginate($this->request()->query('per_page', $default));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of the eloquent model bound to this
|
||||
* repository instance.
|
||||
|
@ -236,6 +285,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
* Return all records associated with the given model.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
* @deprecated Just use the model
|
||||
*/
|
||||
public function all(): Collection
|
||||
{
|
||||
|
@ -313,6 +363,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
|
|||
* Get the amount of entries in the database.
|
||||
*
|
||||
* @return int
|
||||
* @deprecated just use the count method off a model
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Pterodactyl\Repositories\Eloquent;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
@ -226,43 +225,6 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a paginated list of servers that a user can access at a given level.
|
||||
*
|
||||
* @param \Pterodactyl\Models\User $user
|
||||
* @param int $level
|
||||
* @param bool|int $paginate
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function filterUserAccessServers(User $user, int $level, $paginate = 25)
|
||||
{
|
||||
$instance = $this->getBuilder()->select($this->getColumns())->with(['user', 'node', 'allocation']);
|
||||
|
||||
// If access level is set to owner, only display servers
|
||||
// that the user owns.
|
||||
if ($level === User::FILTER_LEVEL_OWNER) {
|
||||
$instance->where('owner_id', $user->id);
|
||||
}
|
||||
|
||||
// If set to all, display all servers they can access, including
|
||||
// those they access as an admin. If set to subuser, only return
|
||||
// the servers they can access because they are owner, or marked
|
||||
// as a subuser of the server.
|
||||
elseif (($level === User::FILTER_LEVEL_ALL && ! $user->root_admin) || $level === User::FILTER_LEVEL_SUBUSER) {
|
||||
$instance->whereIn('id', $this->getUserAccessServers($user->id));
|
||||
}
|
||||
|
||||
// If set to admin, only display the servers a user can access
|
||||
// as an administrator (leaves out owned and subuser of).
|
||||
elseif ($level === User::FILTER_LEVEL_ADMIN && $user->root_admin) {
|
||||
$instance->whereNotIn('id', $this->getUserAccessServers($user->id));
|
||||
}
|
||||
|
||||
$instance->search($this->getSearchTerm());
|
||||
|
||||
return $paginate ? $instance->paginate($paginate) : $instance->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a server by UUID.
|
||||
*
|
||||
|
@ -339,20 +301,6 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt
|
|||
return ! $this->getBuilder()->where('uuid', '=', $uuid)->orWhere('uuidShort', '=', $short)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of server IDs that a given user can access based
|
||||
* on owner and subuser permissions.
|
||||
*
|
||||
* @param int $user
|
||||
* @return int[]
|
||||
*/
|
||||
private function getUserAccessServers(int $user): array
|
||||
{
|
||||
return $this->getBuilder()->select('id')->where('owner_id', $user)->union(
|
||||
$this->app->make(SubuserRepository::class)->getBuilder()->select('server_id')->where('user_id', $user)
|
||||
)->pluck('id')->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of servers that are suspended.
|
||||
*
|
||||
|
|
|
@ -270,7 +270,9 @@ class ServerCreationService
|
|||
$records = array_merge($records, $data['allocation_additional']);
|
||||
}
|
||||
|
||||
$this->allocationRepository->assignAllocationsToServer($server->id, $records);
|
||||
$this->allocationRepository->updateWhereIn('id', $records, [
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -39,6 +39,7 @@ class AllocationTransformer extends BaseTransformer
|
|||
'ip' => $allocation->ip,
|
||||
'alias' => $allocation->ip_alias,
|
||||
'port' => $allocation->port,
|
||||
'notes' => $allocation->notes,
|
||||
'assigned' => ! is_null($allocation->server_id),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -24,13 +24,13 @@ class AllocationTransformer extends BaseClientTransformer
|
|||
*/
|
||||
public function transform(Allocation $model)
|
||||
{
|
||||
$model->loadMissing('server');
|
||||
|
||||
return [
|
||||
'id' => $model->id,
|
||||
'ip' => $model->ip,
|
||||
'alias' => $model->ip_alias,
|
||||
'ip_alias' => $model->ip_alias,
|
||||
'port' => $model->port,
|
||||
'default' => $model->getRelation('server')->allocation_id === $model->id,
|
||||
'notes' => $model->notes,
|
||||
'is_default' => $model->server->allocation_id === $model->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,15 @@ namespace Pterodactyl\Transformers\Api\Client;
|
|||
use Pterodactyl\Models\Egg;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Subuser;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
|
||||
class ServerTransformer extends BaseClientTransformer
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $defaultIncludes = ['allocations'];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
@ -41,10 +47,6 @@ class ServerTransformer extends BaseClientTransformer
|
|||
'port' => $server->node->daemonSFTP,
|
||||
],
|
||||
'description' => $server->description,
|
||||
'allocation' => [
|
||||
'ip' => $server->allocation->alias,
|
||||
'port' => $server->allocation->port,
|
||||
],
|
||||
'limits' => [
|
||||
'memory' => $server->memory,
|
||||
'swap' => $server->swap,
|
||||
|
@ -62,6 +64,22 @@ class ServerTransformer extends BaseClientTransformer
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the allocations associated with this server.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @return \League\Fractal\Resource\Collection
|
||||
* @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException
|
||||
*/
|
||||
public function includeAllocations(Server $server)
|
||||
{
|
||||
return $this->collection(
|
||||
$server->allocations,
|
||||
$this->makeTransformer(AllocationTransformer::class),
|
||||
Allocation::RESOURCE_NAME
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the egg associated with this server.
|
||||
*
|
||||
|
|
Reference in a new issue