Merge branch 'develop' into develop
This commit is contained in:
commit
ea778e9345
159 changed files with 3400 additions and 3896 deletions
26
app/Console/Commands/Overrides/SeedCommand.php
Normal file
26
app/Console/Commands/Overrides/SeedCommand.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Console\Commands\Overrides;
|
||||
|
||||
use Pterodactyl\Console\RequiresDatabaseMigrations;
|
||||
use Illuminate\Database\Console\Seeds\SeedCommand as BaseSeedCommand;
|
||||
|
||||
class SeedCommand extends BaseSeedCommand
|
||||
{
|
||||
use RequiresDatabaseMigrations;
|
||||
|
||||
/**
|
||||
* Block someone from running this seed command if they have not completed the migration
|
||||
* process.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->hasCompletedMigrations()) {
|
||||
return $this->showMigrationWarning();
|
||||
}
|
||||
|
||||
return parent::handle();
|
||||
}
|
||||
}
|
23
app/Console/Commands/Overrides/UpCommand.php
Normal file
23
app/Console/Commands/Overrides/UpCommand.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Console\Commands\Overrides;
|
||||
|
||||
use Pterodactyl\Console\RequiresDatabaseMigrations;
|
||||
use Illuminate\Foundation\Console\UpCommand as BaseUpCommand;
|
||||
|
||||
class UpCommand extends BaseUpCommand
|
||||
{
|
||||
use RequiresDatabaseMigrations;
|
||||
|
||||
/**
|
||||
* @return bool|int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->hasCompletedMigrations()) {
|
||||
return $this->showMigrationWarning();
|
||||
}
|
||||
|
||||
return parent::handle();
|
||||
}
|
||||
}
|
61
app/Console/RequiresDatabaseMigrations.php
Normal file
61
app/Console/RequiresDatabaseMigrations.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Console;
|
||||
|
||||
/**
|
||||
* @mixin \Illuminate\Console\Command
|
||||
*/
|
||||
trait RequiresDatabaseMigrations
|
||||
{
|
||||
/**
|
||||
* Checks if the migrations have finished running by comparing the last migration file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasCompletedMigrations(): bool
|
||||
{
|
||||
/** @var \Illuminate\Database\Migrations\Migrator $migrator */
|
||||
$migrator = $this->getLaravel()->make('migrator');
|
||||
|
||||
$files = $migrator->getMigrationFiles(database_path('migrations'));
|
||||
|
||||
if (! $migrator->repositoryExists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_diff(array_keys($files), $migrator->getRepository()->getRan())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw a massive error into the console to hopefully catch the users attention and get
|
||||
* them to properly run the migrations rather than ignoring all of the other previous
|
||||
* errors...
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function showMigrationWarning(): int
|
||||
{
|
||||
$this->getOutput()->writeln("<options=bold>
|
||||
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
|
||||
| |
|
||||
| Your database has not been properly migrated! |
|
||||
| |
|
||||
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |</>
|
||||
|
||||
You must run the following command to finish migrating your database:
|
||||
|
||||
<fg=green;options=bold>php artisan migrate --step --force</>
|
||||
|
||||
You will not be able to use Pterodactyl Panel as expected without fixing your
|
||||
database state by running the command above.
|
||||
");
|
||||
|
||||
$this->getOutput()->error("You must correct the error above before continuing.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -42,18 +42,6 @@ interface DatabaseRepositoryInterface extends RepositoryInterface
|
|||
*/
|
||||
public function getDatabasesForHost(int $host, int $count = 25): LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* Create a new database if it does not already exist on the host with
|
||||
* the provided details.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Database
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException
|
||||
*/
|
||||
public function createIfNotExists(array $data): Database;
|
||||
|
||||
/**
|
||||
* Create a new database on a given connection.
|
||||
*
|
||||
|
|
|
@ -54,15 +54,4 @@ interface NodeRepositoryInterface extends RepositoryInterface
|
|||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getNodesForServerCreation(): Collection;
|
||||
|
||||
/**
|
||||
* Return the IDs of all nodes that exist in the provided locations and have the space
|
||||
* available to support the additional disk and memory provided.
|
||||
*
|
||||
* @param array $locations
|
||||
* @param int $disk
|
||||
* @param int $memory
|
||||
* @return \Illuminate\Support\LazyCollection
|
||||
*/
|
||||
public function getNodesWithResourceUse(array $locations, int $disk, int $memory): LazyCollection;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
|
@ -217,7 +218,9 @@ class Handler extends ExceptionHandler
|
|||
'status' => method_exists($exception, 'getStatusCode')
|
||||
? strval($exception->getStatusCode())
|
||||
: ($exception instanceof ValidationException ? '422' : '500'),
|
||||
'detail' => 'An error was encountered while processing this request.',
|
||||
'detail' => $exception instanceof HttpExceptionInterface
|
||||
? $exception->getMessage()
|
||||
: 'An unexpected error was encountered while processing this request, please try again.',
|
||||
];
|
||||
|
||||
if ($exception instanceof ModelNotFoundException || $exception->getPrevious() instanceof ModelNotFoundException) {
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
namespace Pterodactyl\Http\Controllers\Admin;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\Nest;
|
||||
use Pterodactyl\Models\Mount;
|
||||
use Pterodactyl\Models\Location;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\Mounts\MountUpdateService;
|
||||
use Pterodactyl\Http\Requests\Admin\MountFormRequest;
|
||||
|
@ -37,21 +39,6 @@ class MountController extends Controller
|
|||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Mounts\MountCreationService
|
||||
*/
|
||||
protected $creationService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Mounts\MountDeletionService
|
||||
*/
|
||||
protected $deletionService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Mounts\MountUpdateService
|
||||
*/
|
||||
protected $updateService;
|
||||
|
||||
/**
|
||||
* MountController constructor.
|
||||
*
|
||||
|
@ -59,26 +46,17 @@ class MountController extends Controller
|
|||
* @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $nestRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $locationRepository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\MountRepository $repository
|
||||
* @param \Pterodactyl\Services\Mounts\MountCreationService $creationService
|
||||
* @param \Pterodactyl\Services\Mounts\MountDeletionService $deletionService
|
||||
* @param \Pterodactyl\Services\Mounts\MountUpdateService $updateService
|
||||
*/
|
||||
public function __construct(
|
||||
AlertsMessageBag $alert,
|
||||
NestRepositoryInterface $nestRepository,
|
||||
LocationRepositoryInterface $locationRepository,
|
||||
MountRepository $repository,
|
||||
MountCreationService $creationService,
|
||||
MountDeletionService $deletionService,
|
||||
MountUpdateService $updateService
|
||||
MountRepository $repository
|
||||
) {
|
||||
$this->alert = $alert;
|
||||
$this->nestRepository = $nestRepository;
|
||||
$this->locationRepository = $locationRepository;
|
||||
$this->repository = $repository;
|
||||
$this->creationService = $creationService;
|
||||
$this->deletionService = $deletionService;
|
||||
$this->updateService = $updateService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,11 +81,8 @@ class MountController extends Controller
|
|||
*/
|
||||
public function view($id)
|
||||
{
|
||||
$nests = $this->nestRepository->all();
|
||||
$nests->load('eggs');
|
||||
|
||||
$locations = $this->locationRepository->all();
|
||||
$locations->load('nodes');
|
||||
$nests = Nest::query()->with('eggs')->get();
|
||||
$locations = Location::query()->with('nodes')->get();
|
||||
|
||||
return view('admin.mounts.view', [
|
||||
'mount' => $this->repository->getWithRelations($id),
|
||||
|
@ -126,7 +101,13 @@ class MountController extends Controller
|
|||
*/
|
||||
public function create(MountFormRequest $request)
|
||||
{
|
||||
$mount = $this->creationService->handle($request->normalize());
|
||||
/** @var \Pterodactyl\Models\Mount $mount */
|
||||
$model = (new Mount())->fill($request->validated());
|
||||
$model->forceFill(['uuid' => Uuid::uuid4()->toString()]);
|
||||
|
||||
$model->saveOrFail();
|
||||
$mount = $model->fresh();
|
||||
|
||||
$this->alert->success('Mount was created successfully.')->flash();
|
||||
|
||||
return redirect()->route('admin.mounts.view', $mount->id);
|
||||
|
@ -147,7 +128,8 @@ class MountController extends Controller
|
|||
return $this->delete($mount);
|
||||
}
|
||||
|
||||
$this->updateService->handle($mount->id, $request->normalize());
|
||||
$mount->forceFill($request->validated())->save();
|
||||
|
||||
$this->alert->success('Mount was updated successfully.')->flash();
|
||||
|
||||
return redirect()->route('admin.mounts.view', $mount->id);
|
||||
|
@ -163,15 +145,9 @@ class MountController extends Controller
|
|||
*/
|
||||
public function delete(Mount $mount)
|
||||
{
|
||||
try {
|
||||
$this->deletionService->handle($mount->id);
|
||||
$mount->delete();
|
||||
|
||||
return redirect()->route('admin.mounts');
|
||||
} catch (DisplayException $ex) {
|
||||
$this->alert->danger($ex->getMessage())->flash();
|
||||
}
|
||||
|
||||
return redirect()->route('admin.mounts.view', $mount->id);
|
||||
return redirect()->route('admin.mounts');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,11 +164,12 @@ class MountController extends Controller
|
|||
]);
|
||||
|
||||
$eggs = $validatedData['eggs'] ?? [];
|
||||
if (sizeof($eggs) > 0) {
|
||||
$mount->eggs()->attach(array_map('intval', $eggs));
|
||||
$this->alert->success('Mount was updated successfully.')->flash();
|
||||
if (count($eggs) > 0) {
|
||||
$mount->eggs()->attach($eggs);
|
||||
}
|
||||
|
||||
$this->alert->success('Mount was updated successfully.')->flash();
|
||||
|
||||
return redirect()->route('admin.mounts.view', $mount->id);
|
||||
}
|
||||
|
||||
|
@ -205,16 +182,15 @@ class MountController extends Controller
|
|||
*/
|
||||
public function addNodes(Request $request, Mount $mount)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'nodes' => 'required|exists:nodes,id',
|
||||
]);
|
||||
$data = $request->validate(['nodes' => 'required|exists:nodes,id']);
|
||||
|
||||
$nodes = $validatedData['nodes'] ?? [];
|
||||
if (sizeof($nodes) > 0) {
|
||||
$mount->nodes()->attach(array_map('intval', $nodes));
|
||||
$this->alert->success('Mount was updated successfully.')->flash();
|
||||
$nodes = $data['nodes'] ?? [];
|
||||
if (count($nodes) > 0) {
|
||||
$mount->nodes()->attach($nodes);
|
||||
}
|
||||
|
||||
$this->alert->success('Mount was updated successfully.')->flash();
|
||||
|
||||
return redirect()->route('admin.mounts.view', $mount->id);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Admin\Nests;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Pterodactyl\Models\Egg;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
|
@ -81,14 +75,14 @@ class EggScriptController extends Controller
|
|||
* Handle a request to update the installation script for an Egg.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Admin\Egg\EggScriptFormRequest $request
|
||||
* @param int $egg
|
||||
* @param \Pterodactyl\Models\Egg $egg
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Egg\InvalidCopyFromException
|
||||
*/
|
||||
public function update(EggScriptFormRequest $request, int $egg): RedirectResponse
|
||||
public function update(EggScriptFormRequest $request, Egg $egg): RedirectResponse
|
||||
{
|
||||
$this->installScriptService->handle($egg, $request->normalize());
|
||||
$this->alert->success(trans('admin/nests.eggs.notices.script_updated'))->flash();
|
||||
|
|
|
@ -102,7 +102,7 @@ class EggShareController extends Controller
|
|||
* Update an existing Egg using a new imported file.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest $request
|
||||
* @param int $egg
|
||||
* @param \Pterodactyl\Models\Egg $egg
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
|
@ -110,7 +110,7 @@ class EggShareController extends Controller
|
|||
* @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException
|
||||
* @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException
|
||||
*/
|
||||
public function update(EggImportFormRequest $request, int $egg): RedirectResponse
|
||||
public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse
|
||||
{
|
||||
$this->updateImporterService->handle($egg, $request->file('import_file'));
|
||||
$this->alert->success(trans('admin/nests.eggs.notices.updated_via_import'))->flash();
|
||||
|
|
|
@ -12,7 +12,9 @@ namespace Pterodactyl\Http\Controllers\Admin;
|
|||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\Mount;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\MountServer;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
|
@ -251,7 +253,7 @@ class ServersController extends Controller
|
|||
*/
|
||||
public function reinstallServer(Server $server)
|
||||
{
|
||||
$this->reinstallService->reinstall($server);
|
||||
$this->reinstallService->handle($server);
|
||||
$this->alert->success(trans('admin/server.alerts.server_reinstalled'))->flash();
|
||||
|
||||
return redirect()->route('admin.servers.view.manage', $server->id);
|
||||
|
@ -332,13 +334,18 @@ class ServersController extends Controller
|
|||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function saveStartup(Request $request, Server $server)
|
||||
{
|
||||
$this->startupModificationService->setUserLevel(User::USER_LEVEL_ADMIN);
|
||||
$this->startupModificationService->handle($server, $request->except('_token'));
|
||||
try {
|
||||
$this->startupModificationService
|
||||
->setUserLevel(User::USER_LEVEL_ADMIN)
|
||||
->handle($server, $request->except('_token'));
|
||||
} catch (DataValidationException $exception) {
|
||||
throw new ValidationException($exception->validator);
|
||||
}
|
||||
|
||||
$this->alert->success(trans('admin/server.alerts.startup_changed'))->flash();
|
||||
|
||||
return redirect()->route('admin.servers.view.startup', $server->id);
|
||||
|
@ -356,7 +363,7 @@ class ServersController extends Controller
|
|||
public function newDatabase(StoreServerDatabaseRequest $request, Server $server)
|
||||
{
|
||||
$this->databaseManagementService->create($server, [
|
||||
'database' => $request->input('database'),
|
||||
'database' => DatabaseManagementService::generateUniqueDatabaseName($request->input('database'), $server->id),
|
||||
'remote' => $request->input('remote'),
|
||||
'database_host_id' => $request->input('database_host_id'),
|
||||
'max_connections' => $request->input('max_connections'),
|
||||
|
@ -403,7 +410,7 @@ class ServersController extends Controller
|
|||
['id', '=', $database],
|
||||
]);
|
||||
|
||||
$this->databaseManagementService->delete($database->id);
|
||||
$this->databaseManagementService->delete($database);
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
|
@ -412,12 +419,17 @@ class ServersController extends Controller
|
|||
* Add a mount to a server.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param int $mount_id
|
||||
* @param \Pterodactyl\Models\Mount $mount
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException|\Throwable
|
||||
*/
|
||||
public function addMount(Server $server, int $mount_id)
|
||||
public function addMount(Server $server, Mount $mount)
|
||||
{
|
||||
$server->mounts()->attach($mount_id);
|
||||
$mountServer = new MountServer;
|
||||
$mountServer->mount_id = $mount->id;
|
||||
$mountServer->server_id = $server->id;
|
||||
$mountServer->saveOrFail();
|
||||
|
||||
$data = $this->serverConfigurationStructureService->handle($server);
|
||||
|
||||
|
@ -438,15 +450,15 @@ class ServersController extends Controller
|
|||
* Remove a mount from a server.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param int $mount_id
|
||||
* @param \Pterodactyl\Models\Mount $mount
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws DaemonConnectionException
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function deleteMount(Server $server, int $mount_id)
|
||||
public function deleteMount(Server $server, Mount $mount)
|
||||
{
|
||||
$server->mounts()->detach($mount_id);
|
||||
MountServer::where('mount_id', $mount->id)->where('server_id', $server->id)->delete();
|
||||
|
||||
$data = $this->serverConfigurationStructureService->handle($server);
|
||||
|
||||
|
|
|
@ -86,8 +86,8 @@ class UserController extends Controller
|
|||
{
|
||||
$users = QueryBuilder::for(
|
||||
User::query()->select('users.*')
|
||||
->selectRaw('COUNT(subusers.id) as subuser_of_count')
|
||||
->selectRaw('COUNT(servers.id) as servers_count')
|
||||
->selectRaw('COUNT(DISTINCT(subusers.id)) as subuser_of_count')
|
||||
->selectRaw('COUNT(DISTINCT(servers.id)) as servers_count')
|
||||
->leftJoin('subusers', 'subusers.user_id', '=', 'users.id')
|
||||
->leftJoin('servers', 'servers.owner_id', '=', 'users.id')
|
||||
->groupBy('users.id')
|
||||
|
|
|
@ -110,7 +110,9 @@ class DatabaseController extends ApplicationApiController
|
|||
*/
|
||||
public function store(StoreServerDatabaseRequest $request, Server $server): JsonResponse
|
||||
{
|
||||
$database = $this->databaseManagementService->create($server, $request->validated());
|
||||
$database = $this->databaseManagementService->create($server, array_merge($request->validated(), [
|
||||
'database' => $request->databaseName(),
|
||||
]));
|
||||
|
||||
return $this->fractal->item($database)
|
||||
->transformWith($this->getTransformer(ServerDatabaseTransformer::class))
|
||||
|
@ -133,7 +135,7 @@ class DatabaseController extends ApplicationApiController
|
|||
*/
|
||||
public function delete(ServerDatabaseWriteRequest $request): Response
|
||||
{
|
||||
$this->databaseManagementService->delete($request->getModel(Database::class)->id);
|
||||
$this->databaseManagementService->delete($request->getModel(Database::class));
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ class ServerManagementController extends ApplicationApiController
|
|||
*/
|
||||
public function reinstall(ServerWriteRequest $request, Server $server): Response
|
||||
{
|
||||
$this->reinstallServerService->reinstall($server);
|
||||
$this->reinstallServerService->handle($server);
|
||||
|
||||
return $this->returnNoContent();
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ class DatabaseController extends ClientApiController
|
|||
*/
|
||||
public function delete(DeleteDatabaseRequest $request, Server $server, Database $database): Response
|
||||
{
|
||||
$this->managementService->delete($database->id);
|
||||
$this->managementService->delete($database);
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ class FileController extends ClientApiController
|
|||
{
|
||||
$contents = $this->fileRepository
|
||||
->setServer($server)
|
||||
->getDirectory(urlencode($request->get('directory') ?? '/'));
|
||||
->getDirectory(urlencode(urldecode($request->get('directory') ?? '/')));
|
||||
|
||||
return $this->fractal->collection($contents)
|
||||
->transformWith($this->getTransformer(FileObjectTransformer::class))
|
||||
|
@ -91,7 +91,7 @@ class FileController extends ClientApiController
|
|||
{
|
||||
return new Response(
|
||||
$this->fileRepository->setServer($server)->getContent(
|
||||
urlencode($request->get('file')), config('pterodactyl.files.max_edit_size')
|
||||
urlencode(urldecode($request->get('file'))), config('pterodactyl.files.max_edit_size')
|
||||
),
|
||||
Response::HTTP_OK,
|
||||
['Content-Type' => 'text/plain']
|
||||
|
|
|
@ -120,15 +120,27 @@ class ScheduleController extends ClientApiController
|
|||
*/
|
||||
public function update(UpdateScheduleRequest $request, Server $server, Schedule $schedule)
|
||||
{
|
||||
$this->repository->update($schedule->id, [
|
||||
$active = (bool) $request->input('is_active');
|
||||
|
||||
$data = [
|
||||
'name' => $request->input('name'),
|
||||
'cron_day_of_week' => $request->input('day_of_week'),
|
||||
'cron_day_of_month' => $request->input('day_of_month'),
|
||||
'cron_hour' => $request->input('hour'),
|
||||
'cron_minute' => $request->input('minute'),
|
||||
'is_active' => (bool) $request->input('is_active'),
|
||||
'is_active' => $active,
|
||||
'next_run_at' => $this->getNextRunAt($request),
|
||||
]);
|
||||
];
|
||||
|
||||
// Toggle the processing state of the scheduled task when it is enabled or disabled so that an
|
||||
// invalid state can be reset without manual database intervention.
|
||||
//
|
||||
// @see https://github.com/pterodactyl/panel/issues/2425
|
||||
if ($schedule->is_active !== $active) {
|
||||
$data['is_processing'] = false;
|
||||
}
|
||||
|
||||
$this->repository->update($schedule->id, $data);
|
||||
|
||||
return $this->fractal->item($schedule->refresh())
|
||||
->transformWith($this->getTransformer(ScheduleTransformer::class))
|
||||
|
|
|
@ -69,7 +69,7 @@ class SettingsController extends ClientApiController
|
|||
*/
|
||||
public function reinstall(ReinstallServerRequest $request, Server $server)
|
||||
{
|
||||
$this->reinstallServerService->reinstall($server);
|
||||
$this->reinstallServerService->handle($server);
|
||||
|
||||
return new JsonResponse([], Response::HTTP_ACCEPTED);
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ class StartupController extends ClientApiController
|
|||
'server_id' => $server->id,
|
||||
'variable_id' => $variable->id,
|
||||
], [
|
||||
'variable_value' => $request->input('value'),
|
||||
'variable_value' => $request->input('value') ?? '',
|
||||
]);
|
||||
|
||||
$variable = $variable->refresh();
|
||||
|
|
|
@ -49,11 +49,11 @@ class SubstituteClientApiBindings extends ApiSubstituteBindings
|
|||
return Database::query()->where('id', $id)->firstOrFail();
|
||||
});
|
||||
|
||||
$this->router->model('backup', Backup::class, function ($value) {
|
||||
$this->router->bind('backup', function ($value) {
|
||||
return Backup::query()->where('uuid', $value)->firstOrFail();
|
||||
});
|
||||
|
||||
$this->router->model('user', User::class, function ($value) {
|
||||
$this->router->bind('user', function ($value) {
|
||||
return User::query()->where('uuid', $value)->firstOrFail();
|
||||
});
|
||||
|
||||
|
|
|
@ -19,12 +19,12 @@ class EggFormRequest extends AdminFormRequest
|
|||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:191',
|
||||
'description' => 'nullable|string',
|
||||
'docker_image' => 'required|string|max:255',
|
||||
'docker_image' => 'required|string|max:191',
|
||||
'startup' => 'required|string',
|
||||
'config_from' => 'sometimes|bail|nullable|numeric',
|
||||
'config_stop' => 'required_without:config_from|nullable|string|max:255',
|
||||
'config_stop' => 'required_without:config_from|nullable|string|max:191',
|
||||
'config_startup' => 'required_without:config_from|nullable|json',
|
||||
'config_logs' => 'required_without:config_from|nullable|json',
|
||||
'config_files' => 'required_without:config_from|nullable|json',
|
||||
|
|
|
@ -15,9 +15,9 @@ class EggVariableFormRequest extends AdminFormRequest
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|min:1|max:255',
|
||||
'name' => 'required|string|min:1|max:191',
|
||||
'description' => 'sometimes|nullable|string',
|
||||
'env_variable' => 'required|regex:/^[\w]{1,255}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES,
|
||||
'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES,
|
||||
'options' => 'sometimes|required|array',
|
||||
'rules' => 'bail|required|string',
|
||||
'default_value' => 'present',
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Admin;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class StoreNestFormRequest extends AdminFormRequest
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|min:1|max:255',
|
||||
'name' => 'required|string|min:1|max:191',
|
||||
'description' => 'string|nullable',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class AllocationFormRequest extends AdminFormRequest
|
|||
{
|
||||
return [
|
||||
'allocation_ip' => 'required|string',
|
||||
'allocation_alias' => 'sometimes|nullable|string|max:255',
|
||||
'allocation_alias' => 'sometimes|nullable|string|max:191',
|
||||
'allocation_ports' => 'required|array',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ class AdvancedSettingsFormRequest extends AdminFormRequest
|
|||
{
|
||||
return [
|
||||
'recaptcha:enabled' => 'required|in:true,false',
|
||||
'recaptcha:secret_key' => 'required|string|max:255',
|
||||
'recaptcha:website_key' => 'required|string|max:255',
|
||||
'recaptcha:secret_key' => 'required|string|max:191',
|
||||
'recaptcha:website_key' => 'required|string|max:191',
|
||||
'pterodactyl:guzzle:timeout' => 'required|integer|between:1,60',
|
||||
'pterodactyl:guzzle:connect_timeout' => 'required|integer|between:1,60',
|
||||
'pterodactyl:console:count' => 'required|integer|min:1',
|
||||
|
|
|
@ -16,7 +16,7 @@ class BaseSettingsFormRequest extends AdminFormRequest
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'app:name' => 'required|string|max:255',
|
||||
'app:name' => 'required|string|max:191',
|
||||
'pterodactyl:auth:2fa_required' => 'required|integer|in:0,1,2',
|
||||
'app:locale' => ['required', 'string', Rule::in(array_keys($this->getAvailableLanguages()))],
|
||||
'app:analytics' => 'nullable|string',
|
||||
|
|
|
@ -18,10 +18,10 @@ class MailSettingsFormRequest extends AdminFormRequest
|
|||
'mail:host' => 'required|string',
|
||||
'mail:port' => 'required|integer|between:1,65535',
|
||||
'mail:encryption' => ['present', Rule::in([null, 'tls', 'ssl'])],
|
||||
'mail:username' => 'nullable|string|max:255',
|
||||
'mail:password' => 'nullable|string|max:255',
|
||||
'mail:username' => 'nullable|string|max:191',
|
||||
'mail:password' => 'nullable|string|max:191',
|
||||
'mail:from:address' => 'required|string|email',
|
||||
'mail:from:name' => 'nullable|string|max:255',
|
||||
'mail:from:name' => 'nullable|string|max:191',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class StoreAllocationRequest extends ApplicationApiRequest
|
|||
{
|
||||
return [
|
||||
'ip' => 'required|string',
|
||||
'alias' => 'sometimes|nullable|string|max:255',
|
||||
'alias' => 'sometimes|nullable|string|max:191',
|
||||
'ports' => 'required|array',
|
||||
'ports.*' => 'string',
|
||||
];
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
|
||||
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Pterodactyl\Services\Acl\Api\AdminAcl;
|
||||
use Pterodactyl\Services\Databases\DatabaseManagementService;
|
||||
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
|
||||
|
||||
class StoreServerDatabaseRequest extends ApplicationApiRequest
|
||||
|
@ -26,14 +29,16 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$server = $this->route()->parameter('server');
|
||||
|
||||
return [
|
||||
'database' => [
|
||||
'required',
|
||||
'string',
|
||||
'alpha_dash',
|
||||
'min:1',
|
||||
'max:24',
|
||||
Rule::unique('databases')->where(function (Builder $query) {
|
||||
$query->where('database_host_id', $this->input('host') ?? 0);
|
||||
'max:48',
|
||||
Rule::unique('databases')->where(function (Builder $query) use ($server) {
|
||||
$query->where('server_id', $server->id)->where('database', $this->databaseName());
|
||||
}),
|
||||
],
|
||||
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
|
||||
|
@ -68,4 +73,18 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest
|
|||
'database' => 'Database Name',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database name in the expected format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function databaseName(): string
|
||||
{
|
||||
$server = $this->route()->parameter('server');
|
||||
|
||||
Assert::isInstanceOf($server, Server::class);
|
||||
|
||||
return DatabaseManagementService::generateUniqueDatabaseName($this->input('database'), $server->id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Account;
|
||||
|
||||
use Pterodactyl\Models\ApiKey;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class StoreApiKeyRequest extends ClientApiRequest
|
||||
|
@ -11,9 +12,11 @@ class StoreApiKeyRequest extends ClientApiRequest
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = ApiKey::getRules();
|
||||
|
||||
return [
|
||||
'description' => 'required|string|min:4',
|
||||
'allowed_ips' => 'array',
|
||||
'description' => $rules['memo'],
|
||||
'allowed_ips' => $rules['allowed_ips'],
|
||||
'allowed_ips.*' => 'ip',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class StoreBackupRequest extends ClientApiRequest
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'nullable|string|max:255',
|
||||
'name' => 'nullable|string|max:191',
|
||||
'ignored' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,9 +2,14 @@
|
|||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Pterodactyl\Models\Permission;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use Pterodactyl\Services\Databases\DatabaseManagementService;
|
||||
|
||||
class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
|
@ -21,9 +26,35 @@ class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissions
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$server = $this->route()->parameter('server');
|
||||
|
||||
Assert::isInstanceOf($server, Server::class);
|
||||
|
||||
return [
|
||||
'database' => 'required|alpha_dash|min:3|max:48',
|
||||
'database' => [
|
||||
'required',
|
||||
'alpha_dash',
|
||||
'min:1',
|
||||
'max:48',
|
||||
// Yes, I am aware that you could have the same database name across two unique hosts. However,
|
||||
// I don't really care about that for this validation. We just want to make sure it is unique to
|
||||
// the server itself. No need for complexity.
|
||||
Rule::unique('databases')->where(function (Builder $query) use ($server) {
|
||||
$query->where('server_id', $server->id)
|
||||
->where('database', DatabaseManagementService::generateUniqueDatabaseName($this->input('database'), $server->id));
|
||||
}),
|
||||
],
|
||||
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'database.unique' => 'The database name you have selected is already in use by this server.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class UpdateStartupVariableRequest extends ClientApiRequest
|
|||
{
|
||||
return [
|
||||
'key' => 'required|string',
|
||||
'value' => 'present|string',
|
||||
'value' => 'present',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class StoreSubuserRequest extends SubuserRequest
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email',
|
||||
'email' => 'required|email|between:1,191',
|
||||
'permissions' => 'required|array',
|
||||
'permissions.*' => 'string',
|
||||
];
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
namespace Pterodactyl\Jobs\Schedule;
|
||||
|
||||
use Exception;
|
||||
use Carbon\Carbon;
|
||||
use Pterodactyl\Jobs\Job;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Pterodactyl\Models\Task;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
@ -15,39 +15,25 @@ use Pterodactyl\Repositories\Eloquent\TaskRepository;
|
|||
use Pterodactyl\Services\Backups\InitiateBackupService;
|
||||
use Pterodactyl\Repositories\Wings\DaemonPowerRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonCommandRepository;
|
||||
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
|
||||
|
||||
class RunTaskJob extends Job implements ShouldQueue
|
||||
{
|
||||
use DispatchesJobs, InteractsWithQueue, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $schedule;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @var \Pterodactyl\Models\Task
|
||||
*/
|
||||
public $task;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\TaskRepository
|
||||
*/
|
||||
protected $taskRepository;
|
||||
|
||||
/**
|
||||
* RunTaskJob constructor.
|
||||
*
|
||||
* @param int $task
|
||||
* @param int $schedule
|
||||
* @param \Pterodactyl\Models\Task $task
|
||||
*/
|
||||
public function __construct(int $task, int $schedule)
|
||||
public function __construct(Task $task)
|
||||
{
|
||||
$this->queue = config('pterodactyl.queues.standard');
|
||||
$this->task = $task;
|
||||
$this->schedule = $schedule;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +44,6 @@ class RunTaskJob extends Job implements ShouldQueue
|
|||
* @param \Pterodactyl\Repositories\Wings\DaemonPowerRepository $powerRepository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\TaskRepository $taskRepository
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function handle(
|
||||
|
@ -67,36 +52,32 @@ class RunTaskJob extends Job implements ShouldQueue
|
|||
DaemonPowerRepository $powerRepository,
|
||||
TaskRepository $taskRepository
|
||||
) {
|
||||
$this->taskRepository = $taskRepository;
|
||||
|
||||
$task = $this->taskRepository->getTaskForJobProcess($this->task);
|
||||
$server = $task->getRelation('server');
|
||||
|
||||
// Do not process a task that is not set to active.
|
||||
if (! $task->getRelation('schedule')->is_active) {
|
||||
if (! $this->task->schedule->is_active) {
|
||||
$this->markTaskNotQueued();
|
||||
$this->markScheduleComplete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$server = $this->task->server;
|
||||
// Perform the provided task against the daemon.
|
||||
switch ($task->action) {
|
||||
switch ($this->task->action) {
|
||||
case 'power':
|
||||
$powerRepository->setServer($server)->send($task->payload);
|
||||
$powerRepository->setServer($server)->send($this->task->payload);
|
||||
break;
|
||||
case 'command':
|
||||
$commandRepository->setServer($server)->send($task->payload);
|
||||
$commandRepository->setServer($server)->send($this->task->payload);
|
||||
break;
|
||||
case 'backup':
|
||||
$backupService->setIgnoredFiles(explode(PHP_EOL, $task->payload))->handle($server, null);
|
||||
$backupService->setIgnoredFiles(explode(PHP_EOL, $this->task->payload))->handle($server, null);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.');
|
||||
}
|
||||
|
||||
$this->markTaskNotQueued();
|
||||
$this->queueNextTask($task->sequence_id);
|
||||
$this->queueNextTask();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,23 +93,23 @@ class RunTaskJob extends Job implements ShouldQueue
|
|||
|
||||
/**
|
||||
* Get the next task in the schedule and queue it for running after the defined period of wait time.
|
||||
*
|
||||
* @param int $sequence
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
private function queueNextTask($sequence)
|
||||
private function queueNextTask()
|
||||
{
|
||||
$nextTask = $this->taskRepository->getNextTask($this->schedule, $sequence);
|
||||
/** @var \Pterodactyl\Models\Task|null $nextTask */
|
||||
$nextTask = Task::query()->where('schedule_id', $this->task->schedule_id)
|
||||
->where('sequence_id', $this->task->sequence_id + 1)
|
||||
->first();
|
||||
|
||||
if (is_null($nextTask)) {
|
||||
$this->markScheduleComplete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->taskRepository->update($nextTask->id, ['is_queued' => true]);
|
||||
$this->dispatch((new self($nextTask->id, $this->schedule))->delay($nextTask->time_offset));
|
||||
$nextTask->update(['is_queued' => true]);
|
||||
|
||||
$this->dispatch((new self($nextTask))->delay($nextTask->time_offset));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,13 +117,10 @@ class RunTaskJob extends Job implements ShouldQueue
|
|||
*/
|
||||
private function markScheduleComplete()
|
||||
{
|
||||
Container::getInstance()
|
||||
->make(ScheduleRepositoryInterface::class)
|
||||
->withoutFreshModel()
|
||||
->update($this->schedule, [
|
||||
'is_processing' => false,
|
||||
'last_run_at' => Carbon::now()->toDateTimeString(),
|
||||
]);
|
||||
$this->task->schedule()->update([
|
||||
'is_processing' => false,
|
||||
'last_run_at' => CarbonImmutable::now()->toDateTimeString(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,8 +128,6 @@ class RunTaskJob extends Job implements ShouldQueue
|
|||
*/
|
||||
private function markTaskNotQueued()
|
||||
{
|
||||
Container::getInstance()
|
||||
->make(TaskRepositoryInterface::class)
|
||||
->update($this->task, ['is_queued' => false]);
|
||||
$this->task->update(['is_queued' => false]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class DatabaseHost extends Model
|
|||
* @var array
|
||||
*/
|
||||
public static $validationRules = [
|
||||
'name' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:191',
|
||||
'host' => 'required|string',
|
||||
'port' => 'required|numeric|between:1,65535',
|
||||
'username' => 'required|string|max:32',
|
||||
|
|
|
@ -93,13 +93,13 @@ class Egg extends Model
|
|||
public static $validationRules = [
|
||||
'nest_id' => 'required|bail|numeric|exists:nests,id',
|
||||
'uuid' => 'required|string|size:36',
|
||||
'name' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:191',
|
||||
'description' => 'string|nullable',
|
||||
'author' => 'required|string|email',
|
||||
'docker_image' => 'required|string|max:255',
|
||||
'docker_image' => 'required|string|max:191',
|
||||
'startup' => 'required|nullable|string',
|
||||
'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id',
|
||||
'config_stop' => 'required_without:config_from|nullable|string|max:255',
|
||||
'config_stop' => 'required_without:config_from|nullable|string|max:191',
|
||||
'config_startup' => 'required_without:config_from|nullable|json',
|
||||
'config_logs' => 'required_without:config_from|nullable|json',
|
||||
'config_files' => 'required_without:config_from|nullable|json',
|
||||
|
|
21
app/Models/EggMount.php
Normal file
21
app/Models/EggMount.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
class EggMount extends Model
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'egg_mount';
|
||||
|
||||
/**
|
||||
* @var null
|
||||
*/
|
||||
protected $primaryKey = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $incrementing = false;
|
||||
}
|
|
@ -73,9 +73,9 @@ class EggVariable extends Model
|
|||
*/
|
||||
public static $validationRules = [
|
||||
'egg_id' => 'exists:eggs,id',
|
||||
'name' => 'required|string|between:1,255',
|
||||
'name' => 'required|string|between:1,191',
|
||||
'description' => 'string',
|
||||
'env_variable' => 'required|regex:/^[\w]{1,255}$/|notIn:' . self::RESERVED_ENV_NAMES,
|
||||
'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . self::RESERVED_ENV_NAMES,
|
||||
'default_value' => 'string',
|
||||
'user_viewable' => 'boolean',
|
||||
'user_editable' => 'boolean',
|
||||
|
|
|
@ -41,7 +41,7 @@ class Location extends Model
|
|||
*/
|
||||
public static $validationRules = [
|
||||
'short' => 'required|string|between:1,60|unique:locations,short',
|
||||
'long' => 'string|nullable|between:1,255',
|
||||
'long' => 'string|nullable|between:1,191',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@ use Illuminate\Validation\Rule;
|
|||
use Illuminate\Container\Container;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Illuminate\Database\Eloquent\Model as IlluminateModel;
|
||||
use Pterodactyl\Exceptions\Model\DataValidationException;
|
||||
|
||||
abstract class Model extends IlluminateModel
|
||||
{
|
||||
|
@ -55,7 +56,11 @@ abstract class Model extends IlluminateModel
|
|||
static::$validatorFactory = Container::getInstance()->make(Factory::class);
|
||||
|
||||
static::saving(function (Model $model) {
|
||||
return $model->validate();
|
||||
if (! $model->validate()) {
|
||||
throw new DataValidationException($model->getValidator());
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -147,9 +152,9 @@ abstract class Model extends IlluminateModel
|
|||
}
|
||||
|
||||
return $this->getValidator()->setData(
|
||||
// Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist
|
||||
// for that model. Doing this will return all of the attributes in a format that can
|
||||
// properly be validated.
|
||||
// Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist
|
||||
// for that model. Doing this will return all of the attributes in a format that can
|
||||
// properly be validated.
|
||||
$this->addCastAttributesToArray(
|
||||
$this->getAttributes(), $this->getMutatedAttributes()
|
||||
)
|
||||
|
|
|
@ -56,7 +56,7 @@ class Mount extends Model
|
|||
*/
|
||||
public static $validationRules = [
|
||||
'name' => 'required|string|min:2|max:64|unique:mounts,name',
|
||||
'description' => 'nullable|string|max:255',
|
||||
'description' => 'nullable|string|max:191',
|
||||
'source' => 'required|string',
|
||||
'target' => 'required|string',
|
||||
'read_only' => 'sometimes|boolean',
|
||||
|
|
|
@ -11,6 +11,11 @@ class MountServer extends Model
|
|||
*/
|
||||
protected $table = 'mount_server';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* @var null
|
||||
*/
|
||||
|
|
|
@ -44,7 +44,7 @@ class Nest extends Model
|
|||
*/
|
||||
public static $validationRules = [
|
||||
'author' => 'required|string|email',
|
||||
'name' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:191',
|
||||
'description' => 'nullable|string',
|
||||
];
|
||||
|
||||
|
|
|
@ -219,80 +219,4 @@ class Permission extends Model
|
|||
{
|
||||
return Collection::make(self::$permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of all permissions available for a user.
|
||||
*
|
||||
* @var array
|
||||
* @deprecated
|
||||
*/
|
||||
protected static $deprecatedPermissions = [
|
||||
'power' => [
|
||||
'power-start' => 's:power:start',
|
||||
'power-stop' => 's:power:stop',
|
||||
'power-restart' => 's:power:restart',
|
||||
'power-kill' => 's:power:kill',
|
||||
'send-command' => 's:command',
|
||||
],
|
||||
'subuser' => [
|
||||
'list-subusers' => null,
|
||||
'view-subuser' => null,
|
||||
'edit-subuser' => null,
|
||||
'create-subuser' => null,
|
||||
'delete-subuser' => null,
|
||||
],
|
||||
'server' => [
|
||||
'view-allocations' => null,
|
||||
'edit-allocation' => null,
|
||||
'view-startup' => null,
|
||||
'edit-startup' => null,
|
||||
],
|
||||
'database' => [
|
||||
'view-databases' => null,
|
||||
'reset-db-password' => null,
|
||||
'delete-database' => null,
|
||||
'create-database' => null,
|
||||
],
|
||||
'file' => [
|
||||
'access-sftp' => null,
|
||||
'list-files' => 's:files:get',
|
||||
'edit-files' => 's:files:read',
|
||||
'save-files' => 's:files:post',
|
||||
'move-files' => 's:files:move',
|
||||
'copy-files' => 's:files:copy',
|
||||
'compress-files' => 's:files:compress',
|
||||
'decompress-files' => 's:files:decompress',
|
||||
'create-files' => 's:files:create',
|
||||
'upload-files' => 's:files:upload',
|
||||
'delete-files' => 's:files:delete',
|
||||
'download-files' => 's:files:download',
|
||||
],
|
||||
'task' => [
|
||||
'list-schedules' => null,
|
||||
'view-schedule' => null,
|
||||
'toggle-schedule' => null,
|
||||
'queue-schedule' => null,
|
||||
'edit-schedule' => null,
|
||||
'create-schedule' => null,
|
||||
'delete-schedule' => null,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Return a collection of permissions available.
|
||||
*
|
||||
* @param bool $array
|
||||
* @return array|\Illuminate\Database\Eloquent\Collection
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getPermissions($array = false)
|
||||
{
|
||||
if ($array) {
|
||||
return collect(self::$deprecatedPermissions)->mapWithKeys(function ($item) {
|
||||
return $item;
|
||||
})->all();
|
||||
}
|
||||
|
||||
return collect(self::$deprecatedPermissions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ class Schedule extends Model
|
|||
*/
|
||||
public static $validationRules = [
|
||||
'server_id' => 'required|exists:servers,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:191',
|
||||
'cron_day_of_week' => 'required|string',
|
||||
'cron_day_of_month' => 'required|string',
|
||||
'cron_hour' => 'required|string',
|
||||
|
|
|
@ -15,7 +15,7 @@ use Znck\Eloquent\Traits\BelongsToThrough;
|
|||
* @property string $name
|
||||
* @property string $description
|
||||
* @property bool $skip_scripts
|
||||
* @property int $suspended
|
||||
* @property bool $suspended
|
||||
* @property int $owner_id
|
||||
* @property int $memory
|
||||
* @property int $swap
|
||||
|
@ -103,7 +103,7 @@ class Server extends Model
|
|||
public static $validationRules = [
|
||||
'external_id' => 'sometimes|nullable|string|between:1,191|unique:servers',
|
||||
'owner_id' => 'required|integer|exists:users,id',
|
||||
'name' => 'required|string|min:1|max:255',
|
||||
'name' => 'required|string|min:1|max:191',
|
||||
'node_id' => 'required|exists:nodes,id',
|
||||
'description' => 'string',
|
||||
'memory' => 'required|numeric|min:0',
|
||||
|
@ -118,7 +118,7 @@ class Server extends Model
|
|||
'egg_id' => 'required|exists:eggs,id',
|
||||
'startup' => 'required|string',
|
||||
'skip_scripts' => 'sometimes|boolean',
|
||||
'image' => 'required|string|max:255',
|
||||
'image' => 'required|string|max:191',
|
||||
'installed' => 'in:0,1,2',
|
||||
'database_limit' => 'present|nullable|integer|min:0',
|
||||
'allocation_limit' => 'sometimes|nullable|integer|min:0',
|
||||
|
@ -133,7 +133,7 @@ class Server extends Model
|
|||
protected $casts = [
|
||||
'node_id' => 'integer',
|
||||
'skip_scripts' => 'boolean',
|
||||
'suspended' => 'integer',
|
||||
'suspended' => 'boolean',
|
||||
'owner_id' => 'integer',
|
||||
'memory' => 'integer',
|
||||
'swap' => 'integer',
|
||||
|
|
|
@ -25,7 +25,7 @@ class Setting extends Model
|
|||
* @var array
|
||||
*/
|
||||
public static $validationRules = [
|
||||
'key' => 'required|string|between:1,255',
|
||||
'key' => 'required|string|between:1,191',
|
||||
'value' => 'string',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -137,11 +137,11 @@ class User extends Model implements
|
|||
*/
|
||||
public static $validationRules = [
|
||||
'uuid' => 'required|string|size:36|unique:users,uuid',
|
||||
'email' => 'required|email|unique:users,email',
|
||||
'external_id' => 'sometimes|nullable|string|max:255|unique:users,external_id',
|
||||
'username' => 'required|between:1,255|unique:users,username',
|
||||
'name_first' => 'required|string|between:1,255',
|
||||
'name_last' => 'required|string|between:1,255',
|
||||
'email' => 'required|email|between:1,191|unique:users,email',
|
||||
'external_id' => 'sometimes|nullable|string|max:191|unique:users,external_id',
|
||||
'username' => 'required|between:1,191|unique:users,username',
|
||||
'name_first' => 'required|string|between:1,191',
|
||||
'name_last' => 'required|string|between:1,191',
|
||||
'password' => 'sometimes|nullable|string',
|
||||
'root_admin' => 'boolean',
|
||||
'language' => 'string',
|
||||
|
|
|
@ -93,31 +93,6 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor
|
|||
->paginate($count, $this->getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new database if it does not already exist on the host with
|
||||
* the provided details.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Database
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException
|
||||
*/
|
||||
public function createIfNotExists(array $data): Database
|
||||
{
|
||||
$count = $this->getBuilder()->where([
|
||||
['server_id', '=', array_get($data, 'server_id')],
|
||||
['database_host_id', '=', array_get($data, 'database_host_id')],
|
||||
['database', '=', array_get($data, 'database')],
|
||||
])->count();
|
||||
|
||||
if ($count > 0) {
|
||||
throw new DuplicateDatabaseNameException('A database with those details already exists for the specified server.');
|
||||
}
|
||||
|
||||
return $this->create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new database on a given connection.
|
||||
*
|
||||
|
|
|
@ -171,28 +171,4 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
|
|||
|
||||
return $instance->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IDs of all nodes that exist in the provided locations and have the space
|
||||
* available to support the additional disk and memory provided.
|
||||
*
|
||||
* @param array $locations
|
||||
* @param int $disk
|
||||
* @param int $memory
|
||||
* @return \Illuminate\Support\LazyCollection
|
||||
*/
|
||||
public function getNodesWithResourceUse(array $locations, int $disk, int $memory): LazyCollection
|
||||
{
|
||||
$instance = $this->getBuilder()
|
||||
->select(['nodes.id', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate'])
|
||||
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
||||
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
||||
->where('nodes.public', 1);
|
||||
|
||||
if (! empty($locations)) {
|
||||
$instance->whereIn('nodes.location_id', $locations);
|
||||
}
|
||||
|
||||
return $instance->groupBy('nodes.id')->cursor();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,29 @@
|
|||
namespace Pterodactyl\Services\Databases;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Database;
|
||||
use Pterodactyl\Helpers\Utilities;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Extensions\DynamicDatabaseConnection;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
|
||||
use Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException;
|
||||
use Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException;
|
||||
use Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException;
|
||||
|
||||
class DatabaseManagementService
|
||||
{
|
||||
/**
|
||||
* The regex used to validate that the database name passed through to the function is
|
||||
* in the expected format.
|
||||
*
|
||||
* @see \Pterodactyl\Services\Databases\DatabaseManagementService::generateUniqueDatabaseName()
|
||||
*/
|
||||
private const MATCH_NAME_REGEX = '/^(s[\d]+_)(.*)$/';
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
*/
|
||||
|
@ -31,7 +42,7 @@ class DatabaseManagementService
|
|||
private $encrypter;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
|
||||
* @var \Pterodactyl\Repositories\Eloquent\DatabaseRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
|
@ -50,13 +61,13 @@ class DatabaseManagementService
|
|||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\DatabaseRepository $repository
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
DynamicDatabaseConnection $dynamic,
|
||||
DatabaseRepositoryInterface $repository,
|
||||
DatabaseRepository $repository,
|
||||
Encrypter $encrypter
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
|
@ -65,6 +76,21 @@ class DatabaseManagementService
|
|||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique database name for the given server. This name should be passed through when
|
||||
* calling this handle function for this service, otherwise the database will be created with
|
||||
* whatever name is provided.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $serverId
|
||||
* @return string
|
||||
*/
|
||||
public static function generateUniqueDatabaseName(string $name, int $serverId): string
|
||||
{
|
||||
// Max of 48 characters, including the s123_ that we append to the front.
|
||||
return sprintf('s%d_%s', $serverId, substr($name, 0, 48 - strlen("s{$serverId}_")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set wether or not this class should validate that the server has enough slots
|
||||
* left before creating the new database.
|
||||
|
@ -104,12 +130,15 @@ class DatabaseManagementService
|
|||
}
|
||||
}
|
||||
|
||||
// Max of 48 characters, including the s123_ that we append to the front.
|
||||
$truncatedName = substr($data['database'], 0, 48 - strlen("s{$server->id}_"));
|
||||
// Protect against developer mistakes...
|
||||
if (empty($data['database']) || ! preg_match(self::MATCH_NAME_REGEX, $data['database'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'The database name passed to DatabaseManagementService::handle MUST be prefixed with "s{server_id}_".'
|
||||
);
|
||||
}
|
||||
|
||||
$data = array_merge($data, [
|
||||
'server_id' => $server->id,
|
||||
'database' => $truncatedName,
|
||||
'username' => sprintf('u%d_%s', $server->id, str_random(10)),
|
||||
'password' => $this->encrypter->encrypt(
|
||||
Utilities::randomStringWithSpecialCharacters(24)
|
||||
|
@ -120,7 +149,8 @@ class DatabaseManagementService
|
|||
|
||||
try {
|
||||
return $this->connection->transaction(function () use ($data, &$database) {
|
||||
$database = $this->repository->createIfNotExists($data);
|
||||
$database = $this->createModel($data);
|
||||
|
||||
$this->dynamic->set('dynamic', $data['database_host_id']);
|
||||
|
||||
$this->repository->createDatabase($database->database);
|
||||
|
@ -139,7 +169,7 @@ class DatabaseManagementService
|
|||
$this->repository->dropUser($database->username, $database->remote);
|
||||
$this->repository->flush();
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
} catch (Exception $deletionException) {
|
||||
// Do nothing here. We've already encountered an issue before this point so no
|
||||
// reason to prioritize this error over the initial one.
|
||||
}
|
||||
|
@ -151,20 +181,48 @@ class DatabaseManagementService
|
|||
/**
|
||||
* Delete a database from the given host server.
|
||||
*
|
||||
* @param int $id
|
||||
* @param \Pterodactyl\Models\Database $database
|
||||
* @return bool|null
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete($id)
|
||||
public function delete(Database $database)
|
||||
{
|
||||
$database = $this->repository->find($id);
|
||||
$this->dynamic->set('dynamic', $database->database_host_id);
|
||||
|
||||
$this->repository->dropDatabase($database->database);
|
||||
$this->repository->dropUser($database->username, $database->remote);
|
||||
$this->repository->flush();
|
||||
|
||||
return $this->repository->delete($id);
|
||||
return $database->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the database if there is not an identical match in the DB. While you can technically
|
||||
* have the same name across multiple hosts, for the sake of keeping this logic easy to understand
|
||||
* and avoiding user confusion we will ignore the specific host and just look across all hosts.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Database
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function createModel(array $data): Database
|
||||
{
|
||||
$exists = Database::query()->where('server_id', $data['server_id'])
|
||||
->where('database', $data['database'])
|
||||
->exists();
|
||||
|
||||
if ($exists) {
|
||||
throw new DuplicateDatabaseNameException(
|
||||
'A database with that name already exists for this server.'
|
||||
);
|
||||
}
|
||||
|
||||
$database = (new Database)->forceFill($data);
|
||||
$database->saveOrFail();
|
||||
|
||||
return $database;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,44 +2,27 @@
|
|||
|
||||
namespace Pterodactyl\Services\Databases;
|
||||
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Database;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
|
||||
use Pterodactyl\Models\DatabaseHost;
|
||||
use Pterodactyl\Exceptions\Service\Database\NoSuitableDatabaseHostException;
|
||||
|
||||
class DeployServerDatabaseService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
|
||||
*/
|
||||
private $databaseHostRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
|
||||
*/
|
||||
private $managementService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ServerDatabaseCreationService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $databaseHostRepository
|
||||
* @param \Pterodactyl\Services\Databases\DatabaseManagementService $managementService
|
||||
*/
|
||||
public function __construct(
|
||||
DatabaseRepositoryInterface $repository,
|
||||
DatabaseHostRepositoryInterface $databaseHostRepository,
|
||||
DatabaseManagementService $managementService
|
||||
) {
|
||||
$this->databaseHostRepository = $databaseHostRepository;
|
||||
public function __construct(DatabaseManagementService $managementService)
|
||||
{
|
||||
$this->managementService = $managementService;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,28 +36,26 @@ class DeployServerDatabaseService
|
|||
*/
|
||||
public function handle(Server $server, array $data): Database
|
||||
{
|
||||
$allowRandom = config('pterodactyl.client_features.databases.allow_random');
|
||||
$hosts = $this->databaseHostRepository->setColumns(['id'])->findWhere([
|
||||
['node_id', '=', $server->node_id],
|
||||
]);
|
||||
|
||||
if ($hosts->isEmpty() && ! $allowRandom) {
|
||||
throw new NoSuitableDatabaseHostException;
|
||||
}
|
||||
Assert::notEmpty($data['database'] ?? null);
|
||||
Assert::notEmpty($data['remote'] ?? null);
|
||||
|
||||
$hosts = DatabaseHost::query()->get()->toBase();
|
||||
if ($hosts->isEmpty()) {
|
||||
$hosts = $this->databaseHostRepository->setColumns(['id'])->all();
|
||||
if ($hosts->isEmpty()) {
|
||||
throw new NoSuitableDatabaseHostException;
|
||||
} else {
|
||||
$nodeHosts = $hosts->where('node_id', $server->node_id)->toBase();
|
||||
|
||||
if ($nodeHosts->isEmpty() && ! config('pterodactyl.client_features.databases.allow_random')) {
|
||||
throw new NoSuitableDatabaseHostException;
|
||||
}
|
||||
}
|
||||
|
||||
$host = $hosts->random();
|
||||
|
||||
return $this->managementService->create($server, [
|
||||
'database_host_id' => $host->id,
|
||||
'database' => array_get($data, 'database'),
|
||||
'remote' => array_get($data, 'remote'),
|
||||
'database_host_id' => $nodeHosts->isEmpty()
|
||||
? $hosts->random()->id
|
||||
: $nodeHosts->random()->id,
|
||||
'database' => DatabaseManagementService::generateUniqueDatabaseName($data['database'], $server->id),
|
||||
'remote' => $data['remote'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,12 @@
|
|||
namespace Pterodactyl\Services\Deployment;
|
||||
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
||||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException;
|
||||
|
||||
class FindViableNodesService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
@ -28,16 +24,6 @@ class FindViableNodesService
|
|||
*/
|
||||
protected $memory;
|
||||
|
||||
/**
|
||||
* FindViableNodesService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(NodeRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locations that should be searched through to locate available nodes.
|
||||
*
|
||||
|
@ -46,6 +32,8 @@ class FindViableNodesService
|
|||
*/
|
||||
public function setLocations(array $locations): self
|
||||
{
|
||||
Assert::allInteger($locations, 'An array of location IDs should be provided when calling setLocations.');
|
||||
|
||||
$this->locations = $locations;
|
||||
|
||||
return $this;
|
||||
|
@ -90,32 +78,34 @@ class FindViableNodesService
|
|||
* are tossed out, as are any nodes marked as non-public, meaning automatic
|
||||
* deployments should not be done against them.
|
||||
*
|
||||
* @return int[]
|
||||
* @return \Pterodactyl\Models\Node[]|\Illuminate\Support\Collection
|
||||
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException
|
||||
*/
|
||||
public function handle(): array
|
||||
public function handle()
|
||||
{
|
||||
Assert::integer($this->disk, 'Calls to ' . __METHOD__ . ' must have the disk space set as an integer, received %s');
|
||||
Assert::integer($this->memory, 'Calls to ' . __METHOD__ . ' must have the memory usage set as an integer, received %s');
|
||||
Assert::integer($this->disk, 'Disk space must be an int, got %s');
|
||||
Assert::integer($this->memory, 'Memory usage must be an int, got %s');
|
||||
|
||||
$nodes = $this->repository->getNodesWithResourceUse($this->locations, $this->disk, $this->memory);
|
||||
$viable = [];
|
||||
$query = Node::query()->select('nodes.*')
|
||||
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory')
|
||||
->selectRaw('IFNULL(SUM(servers.disk), 0) as sum_disk')
|
||||
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
||||
->where('nodes.public', 1);
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$memoryLimit = $node->memory * (1 + ($node->memory_overallocate / 100));
|
||||
$diskLimit = $node->disk * (1 + ($node->disk_overallocate / 100));
|
||||
|
||||
if (($node->sum_memory + $this->memory) > $memoryLimit || ($node->sum_disk + $this->disk) > $diskLimit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$viable[] = $node->id;
|
||||
if (! empty($this->locations)) {
|
||||
$query = $query->whereIn('nodes.location_id', $this->locations);
|
||||
}
|
||||
|
||||
if (empty($viable)) {
|
||||
$results = $query->groupBy('nodes.id')
|
||||
->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [ $this->memory ])
|
||||
->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [ $this->disk ])
|
||||
->get()
|
||||
->toBase();
|
||||
|
||||
if ($results->isEmpty()) {
|
||||
throw new NoViableNodeException(trans('exceptions.deployment.no_viable_nodes'));
|
||||
}
|
||||
|
||||
return $viable;
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Eggs\Scripts;
|
||||
|
||||
|
@ -40,12 +33,8 @@ class InstallScriptService
|
|||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Egg\InvalidCopyFromException
|
||||
*/
|
||||
public function handle($egg, array $data)
|
||||
public function handle(Egg $egg, array $data)
|
||||
{
|
||||
if (! $egg instanceof Egg) {
|
||||
$egg = $this->repository->find($egg);
|
||||
}
|
||||
|
||||
if (! is_null(array_get($data, 'copy_script_from'))) {
|
||||
if (! $this->repository->isCopyableScript(array_get($data, 'copy_script_from'), $egg->nest_id)) {
|
||||
throw new InvalidCopyFromException(trans('exceptions.nest.egg.invalid_copy_id'));
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Eggs\Sharing;
|
||||
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Eggs\Sharing;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Pterodactyl\Services\Eggs\Sharing;
|
||||
|
||||
use Pterodactyl\Models\Egg;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||
|
@ -46,7 +47,7 @@ class EggUpdateImporterService
|
|||
/**
|
||||
* Update an existing Egg using an uploaded JSON file.
|
||||
*
|
||||
* @param int $egg
|
||||
* @param \Pterodactyl\Models\Egg $egg
|
||||
* @param \Illuminate\Http\UploadedFile $file
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
|
@ -54,7 +55,7 @@ class EggUpdateImporterService
|
|||
* @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException
|
||||
* @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException
|
||||
*/
|
||||
public function handle(int $egg, UploadedFile $file)
|
||||
public function handle(Egg $egg, UploadedFile $file)
|
||||
{
|
||||
if ($file->getError() !== UPLOAD_ERR_OK || ! $file->isFile()) {
|
||||
throw new InvalidFileUploadException(
|
||||
|
@ -81,7 +82,7 @@ class EggUpdateImporterService
|
|||
}
|
||||
|
||||
$this->connection->beginTransaction();
|
||||
$this->repository->update($egg, [
|
||||
$this->repository->update($egg->id, [
|
||||
'author' => object_get($parsed, 'author'),
|
||||
'name' => object_get($parsed, 'name'),
|
||||
'description' => object_get($parsed, 'description'),
|
||||
|
@ -99,19 +100,19 @@ class EggUpdateImporterService
|
|||
// Update Existing Variables
|
||||
collect($parsed->variables)->each(function ($variable) use ($egg) {
|
||||
$this->variableRepository->withoutFreshModel()->updateOrCreate([
|
||||
'egg_id' => $egg,
|
||||
'egg_id' => $egg->id,
|
||||
'env_variable' => $variable->env_variable,
|
||||
], collect($variable)->except(['egg_id', 'env_variable'])->toArray());
|
||||
});
|
||||
|
||||
$imported = collect($parsed->variables)->pluck('env_variable')->toArray();
|
||||
$existing = $this->variableRepository->setColumns(['id', 'env_variable'])->findWhere([['egg_id', '=', $egg]]);
|
||||
$existing = $this->variableRepository->setColumns(['id', 'env_variable'])->findWhere([['egg_id', '=', $egg->id]]);
|
||||
|
||||
// Delete variables not present in the import.
|
||||
collect($existing)->each(function ($variable) use ($egg, $imported) {
|
||||
if (! in_array($variable->env_variable, $imported)) {
|
||||
$this->variableRepository->deleteWhere([
|
||||
['egg_id', '=', $egg],
|
||||
['egg_id', '=', $egg->id],
|
||||
['env_variable', '=', $variable->env_variable],
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Services\Mounts;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Pterodactyl\Repositories\Eloquent\MountRepository;
|
||||
|
||||
class MountCreationService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\MountRepository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* MountCreationService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\MountRepository $repository
|
||||
*/
|
||||
public function __construct(MountRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mount.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Mount
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
*/
|
||||
public function handle(array $data)
|
||||
{
|
||||
return $this->repository->create(array_merge($data, [
|
||||
'uuid' => Uuid::uuid4()->toString(),
|
||||
]), true, true);
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Services\Mounts;
|
||||
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Mount;
|
||||
use Pterodactyl\Repositories\Eloquent\MountRepository;
|
||||
|
||||
class MountDeletionService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\MountRepository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* MountDeletionService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\MountRepository $repository
|
||||
*/
|
||||
public function __construct(MountRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing location.
|
||||
*
|
||||
* @param int|\Pterodactyl\Models\Mount $mount
|
||||
* @return int|null
|
||||
*/
|
||||
public function handle($mount)
|
||||
{
|
||||
$mount = ($mount instanceof Mount) ? $mount->id : $mount;
|
||||
|
||||
Assert::integerish($mount, 'First argument passed to handle must be numeric or an instance of ' . Mount::class . ', received %s.');
|
||||
|
||||
return $this->repository->delete($mount);
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Services\Mounts;
|
||||
|
||||
use Pterodactyl\Models\Mount;
|
||||
use Pterodactyl\Repositories\Eloquent\MountRepository;
|
||||
|
||||
class MountUpdateService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\MountRepository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* MountUpdateService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\MountRepository $repository
|
||||
*/
|
||||
public function __construct(MountRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing location.
|
||||
*
|
||||
* @param int|\Pterodactyl\Models\Mount $mount
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Mount
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function handle($mount, array $data)
|
||||
{
|
||||
$mount = ($mount instanceof Mount) ? $mount->id : $mount;
|
||||
|
||||
return $this->repository->update($mount, $data);
|
||||
}
|
||||
}
|
|
@ -73,7 +73,7 @@ class ProcessScheduleService
|
|||
$this->taskRepository->update($task->id, ['is_queued' => true]);
|
||||
|
||||
$this->dispatcher->dispatch(
|
||||
(new RunTaskJob($task->id, $schedule->id))->delay($task->time_offset)
|
||||
(new RunTaskJob($task))->delay($task->time_offset)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ namespace Pterodactyl\Services\Servers;
|
|||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\EggVariable;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
|
||||
class EnvironmentService
|
||||
{
|
||||
|
@ -14,28 +12,6 @@ class EnvironmentService
|
|||
*/
|
||||
private $additional = [];
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* EnvironmentService constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(ConfigRepository $config, ServerRepositoryInterface $repository)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically configure additional environment variables to be assigned
|
||||
* with a specific server.
|
||||
|
@ -79,7 +55,7 @@ class EnvironmentService
|
|||
}
|
||||
|
||||
// Process variables set in the configuration file.
|
||||
foreach ($this->config->get('pterodactyl.environment_variables', []) as $key => $object) {
|
||||
foreach (config('pterodactyl.environment_variables', []) as $key => $object) {
|
||||
$variables->put(
|
||||
$key, is_callable($object) ? call_user_func($object, $server) : object_get($server, $object)
|
||||
);
|
||||
|
|
|
@ -19,26 +19,18 @@ class ReinstallServerService
|
|||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ReinstallService constructor.
|
||||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
DaemonServerRepository $daemonServerRepository,
|
||||
ServerRepository $repository
|
||||
DaemonServerRepository $daemonServerRepository
|
||||
) {
|
||||
$this->daemonServerRepository = $daemonServerRepository;
|
||||
$this->connection = $connection;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,16 +41,14 @@ class ReinstallServerService
|
|||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function reinstall(Server $server)
|
||||
public function handle(Server $server)
|
||||
{
|
||||
return $this->connection->transaction(function () use ($server) {
|
||||
$updated = $this->repository->update($server->id, [
|
||||
'installed' => Server::STATUS_INSTALLING,
|
||||
], true, true);
|
||||
$server->forceFill(['installed' => Server::STATUS_INSTALLING])->save();
|
||||
|
||||
$this->daemonServerRepository->setServer($server)->reinstall();
|
||||
|
||||
return $updated;
|
||||
return $server->refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Servers;
|
||||
|
||||
use Pterodactyl\Models\Mount;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
|
||||
class ServerConfigurationStructureService
|
||||
{
|
||||
|
@ -22,22 +14,13 @@ class ServerConfigurationStructureService
|
|||
*/
|
||||
private $environment;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ServerConfigurationStructureService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Services\Servers\EnvironmentService $environment
|
||||
*/
|
||||
public function __construct(
|
||||
ServerRepositoryInterface $repository,
|
||||
EnvironmentService $environment
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
public function __construct(EnvironmentService $environment)
|
||||
{
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
|
@ -50,8 +33,6 @@ class ServerConfigurationStructureService
|
|||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param bool $legacy
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function handle(Server $server, bool $legacy = false): array
|
||||
{
|
||||
|
@ -72,7 +53,7 @@ class ServerConfigurationStructureService
|
|||
{
|
||||
return [
|
||||
'uuid' => $server->uuid,
|
||||
'suspended' => (bool) $server->suspended,
|
||||
'suspended' => $server->suspended,
|
||||
'environment' => $this->environment->handle($server),
|
||||
'invocation' => $server->startup,
|
||||
'skip_egg_scripts' => $server->skip_scripts,
|
||||
|
@ -112,8 +93,6 @@ class ServerConfigurationStructureService
|
|||
*
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
protected function returnLegacyFormat(Server $server)
|
||||
{
|
||||
|
|
|
@ -4,7 +4,9 @@ namespace Pterodactyl\Services\Servers;
|
|||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Illuminate\Support\Arr;
|
||||
use Pterodactyl\Models\Egg;
|
||||
use Pterodactyl\Models\User;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
|
@ -13,7 +15,6 @@ use Pterodactyl\Models\Objects\DeploymentObject;
|
|||
use Pterodactyl\Repositories\Eloquent\EggRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
||||
use Pterodactyl\Services\Deployment\FindViableNodesService;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerVariableRepository;
|
||||
use Pterodactyl\Services\Deployment\AllocationSelectionService;
|
||||
|
@ -21,11 +22,6 @@ use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
|||
|
||||
class ServerCreationService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\AllocationRepository
|
||||
*/
|
||||
private $allocationRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Deployment\AllocationSelectionService
|
||||
*/
|
||||
|
@ -79,7 +75,6 @@ class ServerCreationService
|
|||
/**
|
||||
* CreationService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\AllocationRepository $allocationRepository
|
||||
* @param \Pterodactyl\Services\Deployment\AllocationSelectionService $allocationSelectionService
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
|
||||
|
@ -92,7 +87,6 @@ class ServerCreationService
|
|||
* @param \Pterodactyl\Services\Servers\VariableValidatorService $validatorService
|
||||
*/
|
||||
public function __construct(
|
||||
AllocationRepository $allocationRepository,
|
||||
AllocationSelectionService $allocationSelectionService,
|
||||
ConnectionInterface $connection,
|
||||
DaemonServerRepository $daemonServerRepository,
|
||||
|
@ -105,7 +99,6 @@ class ServerCreationService
|
|||
VariableValidatorService $validatorService
|
||||
) {
|
||||
$this->allocationSelectionService = $allocationSelectionService;
|
||||
$this->allocationRepository = $allocationRepository;
|
||||
$this->configurationStructureService = $configurationStructureService;
|
||||
$this->connection = $connection;
|
||||
$this->findViableNodesService = $findViableNodesService;
|
||||
|
@ -130,15 +123,12 @@ class ServerCreationService
|
|||
* @throws \Throwable
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException
|
||||
*/
|
||||
public function handle(array $data, DeploymentObject $deployment = null): Server
|
||||
{
|
||||
$this->connection->beginTransaction();
|
||||
|
||||
// If a deployment object has been passed we need to get the allocation
|
||||
// that the server should use, and assign the node from that allocation.
|
||||
if ($deployment instanceof DeploymentObject) {
|
||||
|
@ -149,37 +139,42 @@ class ServerCreationService
|
|||
|
||||
// Auto-configure the node based on the selected allocation
|
||||
// if no node was defined.
|
||||
if (is_null(Arr::get($data, 'node_id'))) {
|
||||
$data['node_id'] = $this->getNodeFromAllocation($data['allocation_id']);
|
||||
if (empty($data['node_id'])) {
|
||||
Assert::false(empty($data['allocation_id']), 'Expected a non-empty allocation_id in server creation data.');
|
||||
|
||||
$data['node_id'] = Allocation::query()->findOrFail($data['allocation_id'])->node_id;
|
||||
}
|
||||
|
||||
if (is_null(Arr::get($data, 'nest_id'))) {
|
||||
/** @var \Pterodactyl\Models\Egg $egg */
|
||||
$egg = $this->eggRepository->setColumns(['id', 'nest_id'])->find(Arr::get($data, 'egg_id'));
|
||||
$data['nest_id'] = $egg->nest_id;
|
||||
if (empty($data['nest_id'])) {
|
||||
Assert::false(empty($data['egg_id']), 'Expected a non-empty egg_id in server creation data.');
|
||||
|
||||
$data['nest_id'] = Egg::query()->findOrFail($data['egg_id'])->nest_id;
|
||||
}
|
||||
|
||||
$eggVariableData = $this->validatorService
|
||||
->setUserLevel(User::USER_LEVEL_ADMIN)
|
||||
->handle(Arr::get($data, 'egg_id'), Arr::get($data, 'environment', []));
|
||||
|
||||
// Create the server and assign any additional allocations to it.
|
||||
$server = $this->createModel($data);
|
||||
|
||||
$this->storeAssignedAllocations($server, $data);
|
||||
$this->storeEggVariables($server, $eggVariableData);
|
||||
|
||||
// Due to the design of the Daemon, we need to persist this server to the disk
|
||||
// before we can actually create it on the Daemon.
|
||||
//
|
||||
// If that connection fails out we will attempt to perform a cleanup by just
|
||||
// deleting the server itself from the system.
|
||||
$this->connection->commit();
|
||||
/** @var \Pterodactyl\Models\Server $server */
|
||||
$server = $this->connection->transaction(function () use ($data, $eggVariableData) {
|
||||
// Create the server and assign any additional allocations to it.
|
||||
$server = $this->createModel($data);
|
||||
|
||||
$structure = $this->configurationStructureService->handle($server);
|
||||
$this->storeAssignedAllocations($server, $data);
|
||||
$this->storeEggVariables($server, $eggVariableData);
|
||||
|
||||
return $server;
|
||||
});
|
||||
|
||||
try {
|
||||
$this->daemonServerRepository->setServer($server)->create($structure);
|
||||
$this->daemonServerRepository->setServer($server)->create(
|
||||
$this->configurationStructureService->handle($server)
|
||||
);
|
||||
} catch (DaemonConnectionException $exception) {
|
||||
$this->serverDeletionService->withForce(true)->handle($server);
|
||||
|
||||
|
@ -208,7 +203,7 @@ class ServerCreationService
|
|||
->handle();
|
||||
|
||||
return $this->allocationSelectionService->setDedicated($deployment->isDedicated())
|
||||
->setNodes($nodes)
|
||||
->setNodes($nodes->pluck('id')->toArray())
|
||||
->setPorts($deployment->getPorts())
|
||||
->handle();
|
||||
}
|
||||
|
@ -269,7 +264,7 @@ class ServerCreationService
|
|||
$records = array_merge($records, $data['allocation_additional']);
|
||||
}
|
||||
|
||||
$this->allocationRepository->updateWhereIn('id', $records, [
|
||||
Allocation::query()->whereIn('id', $records)->update([
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
|
@ -295,22 +290,6 @@ class ServerCreationService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node that an allocation belongs to.
|
||||
*
|
||||
* @param int $id
|
||||
* @return int
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
private function getNodeFromAllocation(int $id): int
|
||||
{
|
||||
/** @var \Pterodactyl\Models\Allocation $allocation */
|
||||
$allocation = $this->allocationRepository->setColumns(['id', 'node_id'])->find($id);
|
||||
|
||||
return $allocation->node_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a unique UUID and UUID-Short combo for a server.
|
||||
*
|
||||
|
|
|
@ -3,11 +3,10 @@
|
|||
namespace Pterodactyl\Services\Servers;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Illuminate\Http\Response;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Services\Databases\DatabaseManagementService;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
|
@ -29,50 +28,26 @@ class ServerDeletionService
|
|||
*/
|
||||
private $daemonServerRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\DatabaseRepository
|
||||
*/
|
||||
private $databaseRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
|
||||
*/
|
||||
private $databaseManagementService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private $writer;
|
||||
|
||||
/**
|
||||
* DeletionService constructor.
|
||||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\DatabaseRepository $databaseRepository
|
||||
* @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService
|
||||
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
|
||||
* @param \Psr\Log\LoggerInterface $writer
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
DaemonServerRepository $daemonServerRepository,
|
||||
DatabaseRepository $databaseRepository,
|
||||
DatabaseManagementService $databaseManagementService,
|
||||
ServerRepository $repository,
|
||||
LoggerInterface $writer
|
||||
DatabaseManagementService $databaseManagementService
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
$this->daemonServerRepository = $daemonServerRepository;
|
||||
$this->databaseRepository = $databaseRepository;
|
||||
$this->databaseManagementService = $databaseManagementService;
|
||||
$this->repository = $repository;
|
||||
$this->writer = $writer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,27 +76,39 @@ class ServerDeletionService
|
|||
try {
|
||||
$this->daemonServerRepository->setServer($server)->delete();
|
||||
} catch (DaemonConnectionException $exception) {
|
||||
if ($this->force) {
|
||||
$this->writer->warning($exception);
|
||||
} else {
|
||||
// If there is an error not caused a 404 error and this isn't a forced delete,
|
||||
// go ahead and bail out. We specifically ignore a 404 since that can be assumed
|
||||
// to be a safe error, meaning the server doesn't exist at all on Wings so there
|
||||
// is no reason we need to bail out from that.
|
||||
if (! $this->force && $exception->getStatusCode() !== Response::HTTP_NOT_FOUND) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
Log::warning($exception);
|
||||
}
|
||||
|
||||
$this->connection->transaction(function () use ($server) {
|
||||
$this->databaseRepository->setColumns('id')->findWhere([['server_id', '=', $server->id]])->each(function ($item) {
|
||||
foreach ($server->databases as $database) {
|
||||
try {
|
||||
$this->databaseManagementService->delete($item->id);
|
||||
$this->databaseManagementService->delete($database);
|
||||
} catch (Exception $exception) {
|
||||
if ($this->force) {
|
||||
$this->writer->warning($exception);
|
||||
} else {
|
||||
if (!$this->force) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$this->repository->delete($server->id);
|
||||
// Oh well, just try to delete the database entry we have from the database
|
||||
// so that the server itself can be deleted. This will leave it dangling on
|
||||
// the host instance, but we couldn't delete it anyways so not sure how we would
|
||||
// handle this better anyways.
|
||||
//
|
||||
// @see https://github.com/pterodactyl/panel/issues/2085
|
||||
$database->delete();
|
||||
|
||||
Log::warning($exception);
|
||||
}
|
||||
}
|
||||
|
||||
$server->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
namespace Pterodactyl\Services\Servers;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Pterodactyl\Models\Egg;
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\ServerVariable;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Traits\Services\HasUserLevels;
|
||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface;
|
||||
|
||||
class StartupModificationService
|
||||
{
|
||||
|
@ -19,63 +19,21 @@ class StartupModificationService
|
|||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
|
||||
*/
|
||||
private $eggRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\EnvironmentService
|
||||
*/
|
||||
private $environmentService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface
|
||||
*/
|
||||
private $serverVariableRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\VariableValidatorService
|
||||
*/
|
||||
private $validatorService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
|
||||
*/
|
||||
private $structureService;
|
||||
|
||||
/**
|
||||
* StartupModificationService constructor.
|
||||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $eggRepository
|
||||
* @param \Pterodactyl\Services\Servers\EnvironmentService $environmentService
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $structureService
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface $serverVariableRepository
|
||||
* @param \Pterodactyl\Services\Servers\VariableValidatorService $validatorService
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
EggRepositoryInterface $eggRepository,
|
||||
EnvironmentService $environmentService,
|
||||
ServerRepositoryInterface $repository,
|
||||
ServerConfigurationStructureService $structureService,
|
||||
ServerVariableRepositoryInterface $serverVariableRepository,
|
||||
VariableValidatorService $validatorService
|
||||
) {
|
||||
public function __construct(ConnectionInterface $connection, VariableValidatorService $validatorService)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->eggRepository = $eggRepository;
|
||||
$this->environmentService = $environmentService;
|
||||
$this->repository = $repository;
|
||||
$this->serverVariableRepository = $serverVariableRepository;
|
||||
$this->validatorService = $validatorService;
|
||||
$this->structureService = $structureService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,34 +43,42 @@ class StartupModificationService
|
|||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Server
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function handle(Server $server, array $data): Server
|
||||
{
|
||||
$this->connection->beginTransaction();
|
||||
if (! is_null(array_get($data, 'environment'))) {
|
||||
$this->validatorService->setUserLevel($this->getUserLevel());
|
||||
$results = $this->validatorService->handle(array_get($data, 'egg_id', $server->egg_id), array_get($data, 'environment', []));
|
||||
return $this->connection->transaction(function () use ($server, $data) {
|
||||
if (! empty($data['environment'])) {
|
||||
$egg = $this->isUserLevel(User::USER_LEVEL_ADMIN) ? ($data['egg_id'] ?? $server->egg_id) : $server->egg_id;
|
||||
|
||||
$results->each(function ($result) use ($server) {
|
||||
$this->serverVariableRepository->withoutFreshModel()->updateOrCreate([
|
||||
'server_id' => $server->id,
|
||||
'variable_id' => $result->id,
|
||||
], [
|
||||
'variable_value' => $result->value ?? '',
|
||||
]);
|
||||
});
|
||||
}
|
||||
$results = $this->validatorService
|
||||
->setUserLevel($this->getUserLevel())
|
||||
->handle($egg, $data['environment']);
|
||||
|
||||
if ($this->isUserLevel(User::USER_LEVEL_ADMIN)) {
|
||||
$this->updateAdministrativeSettings($data, $server);
|
||||
}
|
||||
foreach ($results as $result) {
|
||||
ServerVariable::query()->updateOrCreate(
|
||||
[
|
||||
'server_id' => $server->id,
|
||||
'variable_id' => $result->id,
|
||||
],
|
||||
['variable_value' => $result->value ?? '']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->connection->commit();
|
||||
if ($this->isUserLevel(User::USER_LEVEL_ADMIN)) {
|
||||
$this->updateAdministrativeSettings($data, $server);
|
||||
}
|
||||
|
||||
return $server;
|
||||
// Calling ->refresh() rather than ->fresh() here causes it to return the
|
||||
// variables as triplicates for some reason? Not entirely sure, should dig
|
||||
// in more to figure it out, but luckily we have a test case covering this
|
||||
// specific call so we can be assured we're not breaking it _here_ at least.
|
||||
//
|
||||
// TODO(dane): this seems like a red-flag for the code powering the relationship
|
||||
// that should be looked into more.
|
||||
return $server->fresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,28 +86,25 @@ class StartupModificationService
|
|||
*
|
||||
* @param array $data
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
private function updateAdministrativeSettings(array $data, Server &$server)
|
||||
protected function updateAdministrativeSettings(array $data, Server &$server)
|
||||
{
|
||||
if (
|
||||
is_digit(array_get($data, 'egg_id'))
|
||||
&& $data['egg_id'] != $server->egg_id
|
||||
&& is_null(array_get($data, 'nest_id'))
|
||||
) {
|
||||
$egg = $this->eggRepository->setColumns(['id', 'nest_id'])->find($data['egg_id']);
|
||||
$data['nest_id'] = $egg->nest_id;
|
||||
$eggId = Arr::get($data, 'egg_id');
|
||||
|
||||
if (is_digit($eggId) && $server->egg_id !== (int)$eggId) {
|
||||
/** @var \Pterodactyl\Models\Egg $egg */
|
||||
$egg = Egg::query()->findOrFail($data['egg_id']);
|
||||
|
||||
$server = $server->forceFill([
|
||||
'egg_id' => $egg->id,
|
||||
'nest_id' => $egg->nest_id,
|
||||
]);
|
||||
}
|
||||
|
||||
$server = $this->repository->update($server->id, [
|
||||
'installed' => 0,
|
||||
'startup' => array_get($data, 'startup', $server->startup),
|
||||
'nest_id' => array_get($data, 'nest_id', $server->nest_id),
|
||||
'egg_id' => array_get($data, 'egg_id', $server->egg_id),
|
||||
'skip_scripts' => array_get($data, 'skip_scripts') ?? isset($data['skip_scripts']),
|
||||
'image' => array_get($data, 'docker_image', $server->image),
|
||||
]);
|
||||
$server->fill([
|
||||
'startup' => $data['startup'] ?? $server->startup,
|
||||
'skip_scripts' => $data['skip_scripts'] ?? isset($data['skip_scripts']),
|
||||
'image' => $data['docker_image'] ?? $server->image,
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
namespace Pterodactyl\Services\Servers;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
|
||||
class SuspensionService
|
||||
{
|
||||
|
@ -19,16 +17,6 @@ class SuspensionService
|
|||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
private $writer;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
|
||||
*/
|
||||
|
@ -39,25 +27,19 @@ class SuspensionService
|
|||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
* @param \Psr\Log\LoggerInterface $writer
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
DaemonServerRepository $daemonServerRepository,
|
||||
ServerRepositoryInterface $repository,
|
||||
LoggerInterface $writer
|
||||
DaemonServerRepository $daemonServerRepository
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
$this->repository = $repository;
|
||||
$this->writer = $writer;
|
||||
$this->daemonServerRepository = $daemonServerRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends a server on the system.
|
||||
*
|
||||
* @param int|\Pterodactyl\Models\Server $server
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param string $action
|
||||
*
|
||||
* @throws \Throwable
|
||||
|
@ -66,15 +48,16 @@ class SuspensionService
|
|||
{
|
||||
Assert::oneOf($action, [self::ACTION_SUSPEND, self::ACTION_UNSUSPEND]);
|
||||
|
||||
if (
|
||||
$action === self::ACTION_SUSPEND && $server->suspended ||
|
||||
$action === self::ACTION_UNSUSPEND && ! $server->suspended
|
||||
) {
|
||||
$isSuspending = $action === self::ACTION_SUSPEND;
|
||||
// Nothing needs to happen if we're suspending the server and it is already
|
||||
// suspended in the database. Additionally, nothing needs to happen if the server
|
||||
// is not suspended and we try to un-suspend the instance.
|
||||
if ($isSuspending === $server->suspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->connection->transaction(function () use ($action, $server) {
|
||||
$this->repository->withoutFreshModel()->update($server->id, [
|
||||
$server->update([
|
||||
'suspended' => $action === self::ACTION_SUSPEND,
|
||||
]);
|
||||
|
||||
|
|
|
@ -11,32 +11,15 @@ namespace Pterodactyl\Services\Servers;
|
|||
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Pterodactyl\Models\EggVariable;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Pterodactyl\Traits\Services\HasUserLevels;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface;
|
||||
|
||||
class VariableValidatorService
|
||||
{
|
||||
use HasUserLevels;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
|
||||
*/
|
||||
private $optionVariableRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $serverRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface
|
||||
*/
|
||||
private $serverVariableRepository;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
|
@ -45,20 +28,10 @@ class VariableValidatorService
|
|||
/**
|
||||
* VariableValidatorService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $optionVariableRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface $serverVariableRepository
|
||||
* @param \Illuminate\Contracts\Validation\Factory $validator
|
||||
*/
|
||||
public function __construct(
|
||||
EggVariableRepositoryInterface $optionVariableRepository,
|
||||
ServerRepositoryInterface $serverRepository,
|
||||
ServerVariableRepositoryInterface $serverVariableRepository,
|
||||
ValidationFactory $validator
|
||||
) {
|
||||
$this->optionVariableRepository = $optionVariableRepository;
|
||||
$this->serverRepository = $serverRepository;
|
||||
$this->serverVariableRepository = $serverVariableRepository;
|
||||
public function __construct(ValidationFactory $validator)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
|
@ -72,16 +45,18 @@ class VariableValidatorService
|
|||
*/
|
||||
public function handle(int $egg, array $fields = []): Collection
|
||||
{
|
||||
$variables = $this->optionVariableRepository->findWhere([['egg_id', '=', $egg]]);
|
||||
$query = EggVariable::query()->where('egg_id', $egg);
|
||||
if (! $this->isUserLevel(User::USER_LEVEL_ADMIN)) {
|
||||
// Don't attempt to validate variables if they aren't user editable
|
||||
// and we're not running this at an admin level.
|
||||
$query = $query->where('user_editable', true)->where('user_viewable', true);
|
||||
}
|
||||
|
||||
/** @var \Pterodactyl\Models\EggVariable[] $variables */
|
||||
$variables = $query->get();
|
||||
|
||||
$data = $rules = $customAttributes = [];
|
||||
foreach ($variables as $variable) {
|
||||
// Don't attempt to validate variables if they aren't user editable
|
||||
// and we're not running this at an admin level.
|
||||
if (! $variable->user_editable && ! $this->isUserLevel(User::USER_LEVEL_ADMIN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['environment'][$variable->env_variable] = array_get($fields, $variable->env_variable);
|
||||
$rules['environment.' . $variable->env_variable] = $variable->rules;
|
||||
$customAttributes['environment.' . $variable->env_variable] = trans('validation.internal.variable_value', ['env' => $variable->name]);
|
||||
|
@ -92,23 +67,12 @@ class VariableValidatorService
|
|||
throw new ValidationException($validator);
|
||||
}
|
||||
|
||||
$response = $variables->filter(function ($item) {
|
||||
// Skip doing anything if user is not an admin and variable is not user viewable or editable.
|
||||
if (! $this->isUserLevel(User::USER_LEVEL_ADMIN) && (! $item->user_editable || ! $item->user_viewable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})->map(function ($item) use ($fields) {
|
||||
return (object) [
|
||||
return Collection::make($variables)->map(function ($item) use ($fields) {
|
||||
return (object)[
|
||||
'id' => $item->id,
|
||||
'key' => $item->env_variable,
|
||||
'value' => array_get($fields, $item->env_variable),
|
||||
'value' => $fields[$item->env_variable] ?? null,
|
||||
];
|
||||
})->filter(function ($item) {
|
||||
return is_object($item);
|
||||
});
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class ServerTransformer extends BaseClientTransformer
|
|||
'allocations' => $server->allocation_limit,
|
||||
'backups' => $server->backup_limit,
|
||||
],
|
||||
'is_suspended' => $server->suspended !== 0,
|
||||
'is_suspended' => $server->suspended,
|
||||
'is_installing' => $server->installed !== 1,
|
||||
];
|
||||
}
|
||||
|
|
Reference in a new issue