Begin working on administrative server view changes
Also includes tests for the DatabaseCreation service.
This commit is contained in:
parent
0c513f24d5
commit
580e5ac569
|
@ -34,4 +34,12 @@ interface AllocationRepositoryInterface extends RepositoryInterface
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function assignAllocationsToServer($server, array $ids);
|
public function assignAllocationsToServer($server, array $ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all of the allocations for a specific node.
|
||||||
|
*
|
||||||
|
* @param int $node
|
||||||
|
* @return \Illuminate\Database\Eloquent\Collection
|
||||||
|
*/
|
||||||
|
public function getAllocationsForNode($node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ interface ServerRepositoryInterface extends BaseRepositoryInterface
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @param array $overrides
|
* @param array $overrides
|
||||||
* @param bool $start
|
* @param bool $start
|
||||||
* @return mixed
|
* @return \Psr\Http\Message\ResponseInterface
|
||||||
*/
|
*/
|
||||||
public function create($id, $overrides = [], $start = false);
|
public function create($id, $overrides = [], $start = false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,5 +28,11 @@ use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface;
|
||||||
|
|
||||||
interface NodeRepositoryInterface extends RepositoryInterface, SearchableInterface
|
interface NodeRepositoryInterface extends RepositoryInterface, SearchableInterface
|
||||||
{
|
{
|
||||||
//
|
/**
|
||||||
|
* Return a collection of nodes beloning to a specific location for use on frontend display.
|
||||||
|
*
|
||||||
|
* @param int $location
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getNodesForLocation($location);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||||
*/
|
*/
|
||||||
public function findWithVariables($id);
|
public function findWithVariables($id);
|
||||||
|
|
||||||
|
@ -49,7 +51,30 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter
|
||||||
* default if there is no value defined for the specific server requested.
|
* default if there is no value defined for the specific server requested.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return array
|
* @param bool $returnAsObject
|
||||||
|
* @return array|object
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||||
*/
|
*/
|
||||||
public function getVariablesWithValues($id);
|
public function getVariablesWithValues($id, $returnAsObject = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return enough data to be used for the creation of a server via the daemon.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||||
|
*/
|
||||||
|
public function getDataForCreation($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a server as well as associated databases and their hosts.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||||
|
*/
|
||||||
|
public function getWithDatabases($id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Exceptions;
|
namespace Pterodactyl\Exceptions;
|
||||||
|
|
||||||
use Log;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Auth\AuthenticationException;
|
use Illuminate\Auth\AuthenticationException;
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
@ -21,6 +20,7 @@ class Handler extends ExceptionHandler
|
||||||
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
|
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
|
||||||
\Illuminate\Session\TokenMismatchException::class,
|
\Illuminate\Session\TokenMismatchException::class,
|
||||||
\Illuminate\Validation\ValidationException::class,
|
\Illuminate\Validation\ValidationException::class,
|
||||||
|
\Pterodactyl\Exceptions\Model\DataValidationException::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,20 +28,23 @@ class Handler extends ExceptionHandler
|
||||||
*
|
*
|
||||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||||
*
|
*
|
||||||
* @param \Exception $exception
|
* @param \Exception $exception
|
||||||
* @return void
|
*
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function report(Exception $exception)
|
public function report(Exception $exception)
|
||||||
{
|
{
|
||||||
return parent::report($exception);
|
parent::report($exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render an exception into an HTTP response.
|
* Render an exception into an HTTP response.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param \Exception $exception
|
* @param \Exception $exception
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function render($request, Exception $exception)
|
public function render($request, Exception $exception)
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,8 +28,10 @@ use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||||
use Log;
|
use Log;
|
||||||
use Alert;
|
use Alert;
|
||||||
use Javascript;
|
use Javascript;
|
||||||
|
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
|
||||||
|
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
|
||||||
use Pterodactyl\Http\Requests\Admin\ServerFormRequest;
|
use Pterodactyl\Http\Requests\Admin\ServerFormRequest;
|
||||||
|
@ -38,14 +40,19 @@ use Illuminate\Http\Request;
|
||||||
use GuzzleHttp\Exception\TransferException;
|
use GuzzleHttp\Exception\TransferException;
|
||||||
use Pterodactyl\Exceptions\DisplayException;
|
use Pterodactyl\Exceptions\DisplayException;
|
||||||
use Pterodactyl\Http\Controllers\Controller;
|
use Pterodactyl\Http\Controllers\Controller;
|
||||||
|
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
|
||||||
use Pterodactyl\Repositories\ServerRepository;
|
use Pterodactyl\Repositories\ServerRepository;
|
||||||
use Pterodactyl\Repositories\DatabaseRepository;
|
use Pterodactyl\Repositories\DatabaseRepository;
|
||||||
use Pterodactyl\Exceptions\AutoDeploymentException;
|
|
||||||
use Pterodactyl\Exceptions\DisplayValidationException;
|
use Pterodactyl\Exceptions\DisplayValidationException;
|
||||||
use Pterodactyl\Services\Servers\ServerService;
|
use Pterodactyl\Services\Servers\CreationService;
|
||||||
|
|
||||||
class ServersController extends Controller
|
class ServersController extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $allocationRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Illuminate\Contracts\Config\Repository
|
* @var \Illuminate\Contracts\Config\Repository
|
||||||
*/
|
*/
|
||||||
|
@ -56,18 +63,28 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
protected $databaseRepository;
|
protected $databaseRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $databaseHostRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
|
||||||
*/
|
*/
|
||||||
protected $locationRepository;
|
protected $locationRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $nodeRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||||
*/
|
*/
|
||||||
protected $repository;
|
protected $repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Services\Servers\ServerService
|
* @var \Pterodactyl\Services\Servers\CreationService
|
||||||
*/
|
*/
|
||||||
protected $service;
|
protected $service;
|
||||||
|
|
||||||
|
@ -77,16 +94,22 @@ class ServersController extends Controller
|
||||||
protected $serviceRepository;
|
protected $serviceRepository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
AllocationRepositoryInterface $allocationRepository,
|
||||||
ConfigRepository $config,
|
ConfigRepository $config,
|
||||||
|
CreationService $service,
|
||||||
DatabaseRepositoryInterface $databaseRepository,
|
DatabaseRepositoryInterface $databaseRepository,
|
||||||
|
DatabaseHostRepository $databaseHostRepository,
|
||||||
LocationRepositoryInterface $locationRepository,
|
LocationRepositoryInterface $locationRepository,
|
||||||
ServerService $service,
|
NodeRepositoryInterface $nodeRepository,
|
||||||
ServerRepositoryInterface $repository,
|
ServerRepositoryInterface $repository,
|
||||||
ServiceRepositoryInterface $serviceRepository
|
ServiceRepositoryInterface $serviceRepository
|
||||||
) {
|
) {
|
||||||
|
$this->allocationRepository = $allocationRepository;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->databaseRepository = $databaseRepository;
|
$this->databaseRepository = $databaseRepository;
|
||||||
|
$this->databaseHostRepository = $databaseHostRepository;
|
||||||
$this->locationRepository = $locationRepository;
|
$this->locationRepository = $locationRepository;
|
||||||
|
$this->nodeRepository = $nodeRepository;
|
||||||
$this->repository = $repository;
|
$this->repository = $repository;
|
||||||
$this->service = $service;
|
$this->service = $service;
|
||||||
$this->serviceRepository = $serviceRepository;
|
$this->serviceRepository = $serviceRepository;
|
||||||
|
@ -132,35 +155,25 @@ class ServersController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create server controller method.
|
* Handle POST of server creation form.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Pterodactyl\Http\Requests\Admin\ServerFormRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(ServerFormRequest $request)
|
||||||
{
|
{
|
||||||
$this->service->create($request->all());
|
try {
|
||||||
|
$server = $this->service->create($request->except('_token'));
|
||||||
|
|
||||||
|
return redirect()->route('admin.servers.view', $server->id);
|
||||||
|
} catch (TransferException $ex) {
|
||||||
|
Log::warning($ex);
|
||||||
|
Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash();
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('admin.servers.new')->withInput();
|
return redirect()->route('admin.servers.new')->withInput();
|
||||||
// try {
|
|
||||||
// $repo = new ServerRepository;
|
|
||||||
// $server = $repo->create($request->except('_token'));
|
|
||||||
//
|
|
||||||
// return redirect()->route('admin.servers.view', $server->id);
|
|
||||||
// } catch (DisplayValidationException $ex) {
|
|
||||||
// return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput();
|
|
||||||
// } catch (DisplayException $ex) {
|
|
||||||
// Alert::danger($ex->getMessage())->flash();
|
|
||||||
// } catch (AutoDeploymentException $ex) {
|
|
||||||
// Alert::danger('Auto-Deployment Exception: ' . $ex->getMessage())->flash();
|
|
||||||
// } catch (TransferException $ex) {
|
|
||||||
// Log::warning($ex);
|
|
||||||
// Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash();
|
|
||||||
// } catch (\Exception $ex) {
|
|
||||||
// Log::error($ex);
|
|
||||||
// Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return redirect()->route('admin.servers.new')->withInput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -171,26 +184,7 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function nodes(Request $request)
|
public function nodes(Request $request)
|
||||||
{
|
{
|
||||||
$nodes = Models\Node::with('allocations')->where('location_id', $request->input('location'))->get();
|
return $this->nodeRepository->getNodesForLocation($request->input('location'));
|
||||||
|
|
||||||
return $nodes->map(function ($item) {
|
|
||||||
$filtered = $item->allocations->where('server_id', null)->map(function ($map) {
|
|
||||||
return collect($map)->only(['id', 'ip', 'port']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$item->ports = $filtered->map(function ($map) use ($item) {
|
|
||||||
return [
|
|
||||||
'id' => $map['id'],
|
|
||||||
'text' => $map['ip'] . ':' . $map['port'],
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $item->id,
|
|
||||||
'text' => $item->name,
|
|
||||||
'allocations' => $item->ports,
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -202,7 +196,7 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function viewIndex(Request $request, $id)
|
public function viewIndex(Request $request, $id)
|
||||||
{
|
{
|
||||||
return view('admin.servers.view.index', ['server' => Models\Server::findOrFail($id)]);
|
return view('admin.servers.view.index', ['server' => $this->repository->find($id)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,9 +208,12 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function viewDetails(Request $request, $id)
|
public function viewDetails(Request $request, $id)
|
||||||
{
|
{
|
||||||
$server = Models\Server::where('installed', 1)->findOrFail($id);
|
return view('admin.servers.view.details', [
|
||||||
|
'server' => $this->repository->findFirstWhere([
|
||||||
return view('admin.servers.view.details', ['server' => $server]);
|
['id', '=', $id],
|
||||||
|
['installed', '=', 1],
|
||||||
|
]),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -228,12 +225,17 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function viewBuild(Request $request, $id)
|
public function viewBuild(Request $request, $id)
|
||||||
{
|
{
|
||||||
$server = Models\Server::where('installed', 1)->with('node.allocations')->findOrFail($id);
|
$server = $this->repository->findFirstWhere([
|
||||||
|
['id', '=', $id],
|
||||||
|
['installed', '=', 1],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$allocations = $this->allocationRepository->getAllocationsForNode($server->node_id);
|
||||||
|
|
||||||
return view('admin.servers.view.build', [
|
return view('admin.servers.view.build', [
|
||||||
'server' => $server,
|
'server' => $server,
|
||||||
'assigned' => $server->node->allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
|
'assigned' => $allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
|
||||||
'unassigned' => $server->node->allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
|
'unassigned' => $allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,29 +248,24 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function viewStartup(Request $request, $id)
|
public function viewStartup(Request $request, $id)
|
||||||
{
|
{
|
||||||
$server = Models\Server::where('installed', 1)->with('option.variables', 'variables')->findOrFail($id);
|
$parameters = $this->repository->getVariablesWithValues($id, true);
|
||||||
$server->option->variables->transform(function ($item, $key) use ($server) {
|
if (! $parameters->server->installed) {
|
||||||
$item->server_value = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
return $item;
|
$services = $this->serviceRepository->getWithOptions();
|
||||||
});
|
|
||||||
|
|
||||||
$services = Models\Service::with('options.packs', 'options.variables')->get();
|
|
||||||
Javascript::put([
|
Javascript::put([
|
||||||
'services' => $services->map(function ($item) {
|
'services' => $services->map(function ($item) {
|
||||||
return array_merge($item->toArray(), [
|
return array_merge($item->toArray(), [
|
||||||
'options' => $item->options->keyBy('id')->toArray(),
|
'options' => $item->options->keyBy('id')->toArray(),
|
||||||
]);
|
]);
|
||||||
})->keyBy('id'),
|
})->keyBy('id'),
|
||||||
'server_variables' => $server->variables->mapWithKeys(function ($item) {
|
'server_variables' => $parameters->data,
|
||||||
return ['env_' . $item->variable_id => [
|
|
||||||
'value' => $item->variable_value,
|
|
||||||
]];
|
|
||||||
})->toArray(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return view('admin.servers.view.startup', [
|
return view('admin.servers.view.startup', [
|
||||||
'server' => $server,
|
'server' => $parameters->server,
|
||||||
'services' => $services,
|
'services' => $services,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -282,10 +279,10 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function viewDatabase(Request $request, $id)
|
public function viewDatabase(Request $request, $id)
|
||||||
{
|
{
|
||||||
$server = Models\Server::where('installed', 1)->with('databases.host')->findOrFail($id);
|
$server = $this->repository->getWithDatabases($id);
|
||||||
|
|
||||||
return view('admin.servers.view.database', [
|
return view('admin.servers.view.database', [
|
||||||
'hosts' => Models\DatabaseHost::all(),
|
'hosts' => $this->databaseHostRepository->all(),
|
||||||
'server' => $server,
|
'server' => $server,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -299,7 +296,7 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function viewManage(Request $request, $id)
|
public function viewManage(Request $request, $id)
|
||||||
{
|
{
|
||||||
return view('admin.servers.view.manage', ['server' => Models\Server::findOrFail($id)]);
|
return view('admin.servers.view.manage', ['server' => $this->repository->find($id)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -311,7 +308,7 @@ class ServersController extends Controller
|
||||||
*/
|
*/
|
||||||
public function viewDelete(Request $request, $id)
|
public function viewDelete(Request $request, $id)
|
||||||
{
|
{
|
||||||
return view('admin.servers.view.delete', ['server' => Models\Server::findOrFail($id)]);
|
return view('admin.servers.view.delete', ['server' => $this->repository->find($id)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -78,6 +78,15 @@ class ServerFormRequest extends AdminFormRequest
|
||||||
], function ($input) {
|
], function ($input) {
|
||||||
return ! ($input->auto_deploy);
|
return ! ($input->auto_deploy);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($this->input('pack_id') !== 0) {
|
||||||
|
$validator->sometimes('pack_id', [
|
||||||
|
Rule::exists('packs', 'id')->where(function ($query) {
|
||||||
|
$query->where('selectable', 1);
|
||||||
|
$query->where('option_id', $this->input('option_id'));
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,8 +79,8 @@ class ServerRepository extends BaseRepository implements ServerRepositoryInterfa
|
||||||
array_set($data, $key, $value);
|
array_set($data, $key, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// $this->getHttpClient()->request('POST', '/servers', [
|
return $this->getHttpClient()->request('POST', '/servers', [
|
||||||
// 'json' => $data,
|
'json' => $data,
|
||||||
// ]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,4 +44,12 @@ class AllocationRepository extends EloquentRepository implements AllocationRepos
|
||||||
{
|
{
|
||||||
return $this->getBuilder()->whereIn('id', $ids)->update(['server_id' => $server]);
|
return $this->getBuilder()->whereIn('id', $ids)->update(['server_id' => $server]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getAllocationsForNode($node)
|
||||||
|
{
|
||||||
|
return $this->getBuilder()->where('node_id', $node)->get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,4 +37,31 @@ class NodeRepository extends SearchableRepository implements NodeRepositoryInter
|
||||||
{
|
{
|
||||||
return Node::class;
|
return Node::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getNodesForLocation($location)
|
||||||
|
{
|
||||||
|
$instance = $this->getBuilder()->with('allocations')->where('location_id', $location)->get();
|
||||||
|
|
||||||
|
return $instance->map(function ($item) {
|
||||||
|
$filtered = $item->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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Repositories\Eloquent;
|
namespace Pterodactyl\Repositories\Eloquent;
|
||||||
|
|
||||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||||
use Pterodactyl\Repositories\Eloquent\Attributes\SearchableRepository;
|
use Pterodactyl\Repositories\Eloquent\Attributes\SearchableRepository;
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ class ServerRepository extends SearchableRepository implements ServerRepositoryI
|
||||||
public function findWithVariables($id)
|
public function findWithVariables($id)
|
||||||
{
|
{
|
||||||
$instance = $this->getBuilder()->with('option.variables', 'variables')
|
$instance = $this->getBuilder()->with('option.variables', 'variables')
|
||||||
->where($this->getModel()->getKeyName(), '=', $id)
|
->where($this->getModel()->getKeyName(), '=', $id)
|
||||||
->first($this->getColumns());
|
->first($this->getColumns());
|
||||||
|
|
||||||
if (is_null($instance)) {
|
if (is_null($instance)) {
|
||||||
throw new RecordNotFoundException();
|
throw new RecordNotFoundException();
|
||||||
|
@ -73,10 +73,10 @@ class ServerRepository extends SearchableRepository implements ServerRepositoryI
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getVariablesWithValues($id)
|
public function getVariablesWithValues($id, $returnWithObject = false)
|
||||||
{
|
{
|
||||||
$instance = $this->getBuilder()->with('variables', 'option.variables')
|
$instance = $this->getBuilder()->with('variables', 'option.variables')
|
||||||
->find($id, $this->getColumns());
|
->find($id, $this->getColumns());
|
||||||
|
|
||||||
if (! $instance) {
|
if (! $instance) {
|
||||||
throw new RecordNotFoundException();
|
throw new RecordNotFoundException();
|
||||||
|
@ -89,13 +89,39 @@ class ServerRepository extends SearchableRepository implements ServerRepositoryI
|
||||||
$data[$item->env_variable] = $display ?? $item->default_value;
|
$data[$item->env_variable] = $display ?? $item->default_value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($returnWithObject) {
|
||||||
|
return (object) [
|
||||||
|
'data' => $data,
|
||||||
|
'server' => $instance,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function getDataForCreation($id)
|
public function getDataForCreation($id)
|
||||||
{
|
{
|
||||||
$instance = $this->getBuilder()->with('allocation', 'allocations', 'pack', 'option.service')
|
$instance = $this->getBuilder()->with('allocation', 'allocations', 'pack', 'option.service')
|
||||||
->find($id, $this->getColumns());
|
->find($id, $this->getColumns());
|
||||||
|
|
||||||
|
if (! $instance) {
|
||||||
|
throw new RecordNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getWithDatabases($id)
|
||||||
|
{
|
||||||
|
$instance = $this->getBuilder()->with('databases.host')
|
||||||
|
->where('installed', 1)
|
||||||
|
->find($id, $this->getColumns());
|
||||||
|
|
||||||
if (! $instance) {
|
if (! $instance) {
|
||||||
throw new RecordNotFoundException();
|
throw new RecordNotFoundException();
|
||||||
|
|
|
@ -118,7 +118,7 @@ class CreationService
|
||||||
$this->repository->dropUser($database->username, $database->remote, 'dynamic');
|
$this->repository->dropUser($database->username, $database->remote, 'dynamic');
|
||||||
$this->repository->flush('dynamic');
|
$this->repository->flush('dynamic');
|
||||||
}
|
}
|
||||||
} catch (\Exception $ex) {
|
} catch (\Exception $exTwo) {
|
||||||
// ignore an exception
|
// ignore an exception
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ class CreationService
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->repository->dropUser($database->username, $database->remote, 'dynamic');
|
$this->repository->dropUser($database->username, $database->remote, 'dynamic');
|
||||||
$this->repository->createUser($database->username, $database->remote, $password);
|
$this->repository->createUser($database->username, $database->remote, $password, 'dynamic');
|
||||||
$this->repository->assignUserToDatabase(
|
$this->repository->assignUserToDatabase(
|
||||||
$database->database, $database->username, $database->remote, 'dynamic'
|
$database->database, $database->username, $database->remote, 'dynamic'
|
||||||
);
|
);
|
||||||
|
|
|
@ -73,7 +73,7 @@ class LocationService
|
||||||
/**
|
/**
|
||||||
* Delete a model from the DB.
|
* Delete a model from the DB.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||||
|
|
|
@ -33,34 +33,66 @@ use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
|
||||||
|
|
||||||
class ServerService
|
class CreationService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
||||||
*/
|
*/
|
||||||
protected $allocationRepository;
|
protected $allocationRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $daemonServerRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Database\ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $database;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
|
||||||
*/
|
*/
|
||||||
protected $nodeRepository;
|
protected $nodeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $serverVariableRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
||||||
*/
|
*/
|
||||||
protected $userRepository;
|
protected $userRepository;
|
||||||
|
|
||||||
protected $database;
|
/**
|
||||||
protected $repository;
|
* @var \Pterodactyl\Services\Servers\UsernameGenerationService
|
||||||
|
*/
|
||||||
protected $usernameService;
|
protected $usernameService;
|
||||||
protected $serverVariableRepository;
|
|
||||||
protected $daemonServerRepository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Services\Servers\VariableValidatorService
|
* @var \Pterodactyl\Services\Servers\VariableValidatorService
|
||||||
*/
|
*/
|
||||||
protected $validatorService;
|
protected $validatorService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreationService constructor.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository
|
||||||
|
* @param \Illuminate\Database\ConnectionInterface $database
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface $serverVariableRepository
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $nodeRepository
|
||||||
|
* @param \Pterodactyl\Services\Servers\UsernameGenerationService $usernameService
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
|
||||||
|
* @param \Pterodactyl\Services\Servers\VariableValidatorService $validatorService
|
||||||
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AllocationRepositoryInterface $allocationRepository,
|
AllocationRepositoryInterface $allocationRepository,
|
||||||
ConnectionInterface $database,
|
ConnectionInterface $database,
|
||||||
|
@ -83,9 +115,17 @@ class ServerService
|
||||||
$this->daemonServerRepository = $daemonServerRepository;
|
$this->daemonServerRepository = $daemonServerRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a server on both the panel and daemon.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
|
*/
|
||||||
public function create(array $data)
|
public function create(array $data)
|
||||||
{
|
{
|
||||||
// @todo auto-deployment and packs
|
// @todo auto-deployment
|
||||||
$data['user_id'] = 1;
|
$data['user_id'] = 1;
|
||||||
|
|
||||||
$node = $this->nodeRepository->find($data['node_id']);
|
$node = $this->nodeRepository->find($data['node_id']);
|
||||||
|
@ -141,8 +181,11 @@ class ServerService
|
||||||
$this->serverVariableRepository->insert($records);
|
$this->serverVariableRepository->insert($records);
|
||||||
|
|
||||||
// Create the server on the daemon & commit it to the database.
|
// Create the server on the daemon & commit it to the database.
|
||||||
$this->daemonServerRepository->setNode($server->node_id)->setAccessToken($node->daemonSecret)->create($server->id);
|
$this->daemonServerRepository->setNode($server->node_id)
|
||||||
$this->database->rollBack();
|
->setAccessToken($node->daemonSecret)
|
||||||
|
->create($server->id);
|
||||||
|
|
||||||
|
$this->database->commit();
|
||||||
|
|
||||||
return $server;
|
return $server;
|
||||||
}
|
}
|
|
@ -24,8 +24,8 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Services\Servers;
|
namespace Pterodactyl\Services\Servers;
|
||||||
|
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||||
|
|
||||||
class EnvironmentService
|
class EnvironmentService
|
||||||
{
|
{
|
||||||
|
|
|
@ -143,11 +143,11 @@ class VariableValidatorService
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
throw new DisplayValidationException(json_encode(
|
throw new DisplayValidationException(json_encode(
|
||||||
collect([
|
collect([
|
||||||
'notice' => [
|
'notice' => [
|
||||||
sprintf('There was a validation error with the %s variable.', $item->name),
|
sprintf('There was a validation error with the %s variable.', $item->name),
|
||||||
],
|
],
|
||||||
])->merge($validator->errors()->toArray())
|
])->merge($validator->errors()->toArray())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
@foreach($services as $service)
|
@foreach($services as $service)
|
||||||
<option value="{{ $service->id }}"
|
<option value="{{ $service->id }}"
|
||||||
@if($service->id === $server->service_id)
|
@if($service->id === $server->service_id)
|
||||||
selected="selected"
|
selected
|
||||||
@endif
|
@endif
|
||||||
>{{ $service->name }}</option>
|
>{{ $service->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
@ -125,30 +125,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="row" id="appendVariablesTo">
|
<div class="row" id="appendVariablesTo"></div>
|
||||||
@foreach($server->option->variables as $variable)
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-header with-border">
|
|
||||||
<h3 class="box-title">{{ $variable->name }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="box-body">
|
|
||||||
<input data-action="match-regex" name="env_{{ $variable->id }}" class="form-control" type="text" value="{{ old('env_' . $variable->id, $variable->server_value) }}" />
|
|
||||||
<p class="no-margin small text-muted">{{ $variable->description }}</p>
|
|
||||||
<p class="no-margin">
|
|
||||||
@if($variable->required)<span class="label label-danger">Required</span>@else<span class="label label-default">Optional</span>@endif
|
|
||||||
@if($variable->user_viewable)<span class="label label-success">Visible</span>@else<span class="label label-primary">Hidden</span>@endif
|
|
||||||
@if($variable->user_editable)<span class="label label-success">Editable</span>@else<span class="label label-primary">Locked</span>@endif
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="box-footer">
|
|
||||||
<p class="no-margin text-muted small"><strong>Startup Command Variable:</strong> <code>{{ $variable->env_variable }}</code></p>
|
|
||||||
<p class="no-margin text-muted small"><strong>Input Rules:</strong> <code>{{ $variable->rules }}</code></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -217,7 +194,7 @@
|
||||||
|
|
||||||
$('#appendVariablesTo').html('');
|
$('#appendVariablesTo').html('');
|
||||||
$.each(_.get(objectChain, 'variables', []), function (i, item) {
|
$.each(_.get(objectChain, 'variables', []), function (i, item) {
|
||||||
var setValue = _.get(Pterodactyl.server_variables, 'env_' + item.id + '.value', item.default_value);
|
var setValue = _.get(Pterodactyl.server_variables, item.env_variable, item.default_value);
|
||||||
var isRequired = (item.required === 1) ? '<span class="label label-danger">Required</span> ' : '';
|
var isRequired = (item.required === 1) ? '<span class="label label-danger">Required</span> ' : '';
|
||||||
var dataAppend = ' \
|
var dataAppend = ' \
|
||||||
<div class="col-xs-12"> \
|
<div class="col-xs-12"> \
|
||||||
|
@ -226,7 +203,7 @@
|
||||||
<h3 class="box-title">' + isRequired + item.name + '</h3> \
|
<h3 class="box-title">' + isRequired + item.name + '</h3> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="box-body"> \
|
<div class="box-body"> \
|
||||||
<input data-action="match-regex" name="env_' + item.id + '" class="form-control" type="text" value="' + setValue + '" /> \
|
<input data-action="match-regex" name="environment[' + item.env_variable + ']" class="form-control" type="text" value="' + setValue + '" /> \
|
||||||
<p class="no-margin small text-muted">' + item.description + '</p> \
|
<p class="no-margin small text-muted">' + item.description + '</p> \
|
||||||
</div> \
|
</div> \
|
||||||
<div class="box-footer"> \
|
<div class="box-footer"> \
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Pterodactyl - Panel
|
||||||
|
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\Unit\Services\Database;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Mockery as m;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use phpmock\phpunit\PHPMock;
|
||||||
|
use Illuminate\Database\ConnectionResolver;
|
||||||
|
use Illuminate\Database\ConnectionInterface;
|
||||||
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
|
use Pterodactyl\Services\Database\CreationService;
|
||||||
|
use Pterodactyl\Extensions\DynamicDatabaseConnection;
|
||||||
|
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||||
|
|
||||||
|
class CreationServiceTest extends TestCase
|
||||||
|
{
|
||||||
|
use PHPMock;
|
||||||
|
|
||||||
|
const TEST_DATA = [
|
||||||
|
'server_id' => 1,
|
||||||
|
'database' => 'd1_dbname',
|
||||||
|
'remote' => '%',
|
||||||
|
'username' => 'u1_str_random',
|
||||||
|
'password' => 'enc_password',
|
||||||
|
'database_host_id' => 3,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Database\ConnectionResolver
|
||||||
|
*/
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Database\ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection
|
||||||
|
*/
|
||||||
|
protected $dynamic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||||
|
*/
|
||||||
|
protected $encrypter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Services\Database\CreationService
|
||||||
|
*/
|
||||||
|
protected $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup tests.
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->connection = m::mock(ConnectionResolver::class);
|
||||||
|
$this->database = m::mock(ConnectionInterface::class);
|
||||||
|
$this->dynamic = m::mock(DynamicDatabaseConnection::class);
|
||||||
|
$this->encrypter = m::mock(Encrypter::class);
|
||||||
|
$this->repository = m::mock(DatabaseRepositoryInterface::class);
|
||||||
|
|
||||||
|
$this->getFunctionMock('\\Pterodactyl\\Services\\Database', 'str_random')
|
||||||
|
->expects($this->any())->willReturn('str_random');
|
||||||
|
|
||||||
|
$this->service = new CreationService(
|
||||||
|
$this->database,
|
||||||
|
$this->connection,
|
||||||
|
$this->dynamic,
|
||||||
|
$this->repository,
|
||||||
|
$this->encrypter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a new database can be created that is linked to a specific host.
|
||||||
|
*/
|
||||||
|
public function testCreateANewDatabaseThatIsLinkedToAHost()
|
||||||
|
{
|
||||||
|
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
|
||||||
|
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('createIfNotExists')->with(
|
||||||
|
collect(self::TEST_DATA)->except('remote')->toArray()
|
||||||
|
)->once()->andReturn((object) self::TEST_DATA);
|
||||||
|
|
||||||
|
$this->dynamic->shouldReceive('set')->with('dynamic', self::TEST_DATA['database_host_id'])->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('createDatabase')->with(
|
||||||
|
self::TEST_DATA['database'], 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->encrypter->shouldReceive('decrypt')->with('enc_password')->once()->andReturn('str_random');
|
||||||
|
$this->repository->shouldReceive('createUser')->with(
|
||||||
|
self::TEST_DATA['username'], self::TEST_DATA['remote'], 'str_random', 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('assignUserToDatabase')->with(
|
||||||
|
self::TEST_DATA['database'], self::TEST_DATA['username'], self::TEST_DATA['remote'], 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('flush')->with('dynamic')->once()->andReturnNull();
|
||||||
|
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$response = $this->service->create([
|
||||||
|
'server_id' => 1,
|
||||||
|
'database' => 'dbname',
|
||||||
|
'database_host_id' => 3,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertNotEmpty($response);
|
||||||
|
$this->assertTrue(is_object($response), 'Assert that response is an object.');
|
||||||
|
|
||||||
|
$this->assertEquals(self::TEST_DATA['server_id'], $response->server_id);
|
||||||
|
$this->assertEquals(self::TEST_DATA['database'], $response->database);
|
||||||
|
$this->assertEquals(self::TEST_DATA['remote'], $response->remote);
|
||||||
|
$this->assertEquals(self::TEST_DATA['username'], $response->username);
|
||||||
|
$this->assertEquals(self::TEST_DATA['password'], $response->password);
|
||||||
|
$this->assertEquals(self::TEST_DATA['database_host_id'], $response->database_host_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception before the database is created and returned does not attempt any actions.
|
||||||
|
*
|
||||||
|
* @expectedException \Exception
|
||||||
|
*/
|
||||||
|
public function testExceptionBeforeDatabaseIsCreatedShouldNotAttemptAnyRollBackOperations()
|
||||||
|
{
|
||||||
|
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
|
||||||
|
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('createIfNotExists')->with(
|
||||||
|
collect(self::TEST_DATA)->except('remote')->toArray()
|
||||||
|
)->once()->andThrow(new Exception('Test Message'));
|
||||||
|
|
||||||
|
$this->repository->shouldNotReceive('dropDatabase');
|
||||||
|
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->service->create([
|
||||||
|
'server_id' => 1,
|
||||||
|
'database' => 'dbname',
|
||||||
|
'database_host_id' => 3,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception after database creation attempts to clean up previous operations.
|
||||||
|
*
|
||||||
|
* @expectedException \Exception
|
||||||
|
*/
|
||||||
|
public function testExceptionAfterDatabaseCreationShouldAttemptRollBackOperations()
|
||||||
|
{
|
||||||
|
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
|
||||||
|
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('createIfNotExists')->with(
|
||||||
|
collect(self::TEST_DATA)->except('remote')->toArray()
|
||||||
|
)->once()->andReturn((object) self::TEST_DATA);
|
||||||
|
|
||||||
|
$this->dynamic->shouldReceive('set')->with('dynamic', self::TEST_DATA['database_host_id'])->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('createDatabase')->with(
|
||||||
|
self::TEST_DATA['database'], 'dynamic'
|
||||||
|
)->once()->andThrow(new Exception('Test Message'));
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('dropDatabase')->with(self::TEST_DATA['database'], 'dynamic')->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('dropUser')->with(
|
||||||
|
self::TEST_DATA['username'], self::TEST_DATA['remote'], 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('flush')->with('dynamic')->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->service->create([
|
||||||
|
'server_id' => 1,
|
||||||
|
'database' => 'dbname',
|
||||||
|
'database_host_id' => 3,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception thrown during a rollback operation is silently handled and not returned.
|
||||||
|
*/
|
||||||
|
public function testExceptionThrownDuringRollBackProcessShouldNotBeThrownToCallingFunction()
|
||||||
|
{
|
||||||
|
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
|
||||||
|
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('createIfNotExists')->with(
|
||||||
|
collect(self::TEST_DATA)->except('remote')->toArray()
|
||||||
|
)->once()->andReturn((object) self::TEST_DATA);
|
||||||
|
|
||||||
|
$this->dynamic->shouldReceive('set')->with('dynamic', self::TEST_DATA['database_host_id'])->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('createDatabase')->with(
|
||||||
|
self::TEST_DATA['database'], 'dynamic'
|
||||||
|
)->once()->andThrow(new Exception('Test One'));
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('dropDatabase')->with(self::TEST_DATA['database'], 'dynamic')
|
||||||
|
->once()->andThrow(new Exception('Test Two'));
|
||||||
|
|
||||||
|
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->service->create([
|
||||||
|
'server_id' => 1,
|
||||||
|
'database' => 'dbname',
|
||||||
|
'database_host_id' => 3,
|
||||||
|
]);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$this->assertInstanceOf(Exception::class, $ex);
|
||||||
|
$this->assertEquals('Test One', $ex->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a password can be changed for a given database.
|
||||||
|
*/
|
||||||
|
public function testDatabasePasswordShouldBeChanged()
|
||||||
|
{
|
||||||
|
$this->repository->shouldReceive('find')->with(1)->once()->andReturn((object) self::TEST_DATA);
|
||||||
|
$this->dynamic->shouldReceive('set')->with('dynamic', self::TEST_DATA['database_host_id'])->once()->andReturnNull();
|
||||||
|
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->encrypter->shouldReceive('encrypt')->with('new_password')->once()->andReturn('new_enc_password');
|
||||||
|
$this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf()
|
||||||
|
->shouldReceive('update')->with(1, [
|
||||||
|
'password' => 'new_enc_password',
|
||||||
|
])->andReturn(true);
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('dropUser')->with(
|
||||||
|
self::TEST_DATA['username'], self::TEST_DATA['remote'], 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('createUser')->with(
|
||||||
|
self::TEST_DATA['username'], self::TEST_DATA['remote'], 'new_password', 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('assignUserToDatabase')->with(
|
||||||
|
self::TEST_DATA['database'], self::TEST_DATA['username'], self::TEST_DATA['remote'], 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('flush')->withNoArgs()->once()->andReturnNull();
|
||||||
|
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$response = $this->service->changePassword(1, 'new_password');
|
||||||
|
|
||||||
|
$this->assertTrue($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception thrown while changing a password will attempt a rollback.
|
||||||
|
*
|
||||||
|
* @expectedException \Exception
|
||||||
|
*/
|
||||||
|
public function testExceptionThrownWhileChangingDatabasePasswordShouldRollBack()
|
||||||
|
{
|
||||||
|
$this->repository->shouldReceive('find')->with(1)->once()->andReturn((object) self::TEST_DATA);
|
||||||
|
$this->dynamic->shouldReceive('set')->with('dynamic', self::TEST_DATA['database_host_id'])->once()->andReturnNull();
|
||||||
|
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->encrypter->shouldReceive('encrypt')->with('new_password')->once()->andReturn('new_enc_password');
|
||||||
|
$this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf()
|
||||||
|
->shouldReceive('update')->with(1, [
|
||||||
|
'password' => 'new_enc_password',
|
||||||
|
])->andReturn(true);
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('dropUser')->with(
|
||||||
|
self::TEST_DATA['username'], self::TEST_DATA['remote'], 'dynamic'
|
||||||
|
)->once()->andThrow(new Exception());
|
||||||
|
|
||||||
|
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->service->changePassword(1, 'new_password');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a database can be deleted.
|
||||||
|
*/
|
||||||
|
public function testDatabaseShouldBeDeleted()
|
||||||
|
{
|
||||||
|
$this->repository->shouldReceive('find')->with(1)->once()->andReturn((object) self::TEST_DATA);
|
||||||
|
$this->dynamic->shouldReceive('set')->with('dynamic', self::TEST_DATA['database_host_id'])->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->repository->shouldReceive('dropDatabase')->with(self::TEST_DATA['database'], 'dynamic')->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('dropUser')->with(
|
||||||
|
self::TEST_DATA['username'], self::TEST_DATA['remote'], 'dynamic'
|
||||||
|
)->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('flush')->with('dynamic')->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('delete')->with(1)->once()->andReturn(1);
|
||||||
|
|
||||||
|
$response = $this->service->delete(1);
|
||||||
|
|
||||||
|
$this->assertEquals(1, $response);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue