diff --git a/app/Http/Controllers/Admin/MountController.php b/app/Http/Controllers/Admin/MountController.php new file mode 100644 index 000000000..26cc6d369 --- /dev/null +++ b/app/Http/Controllers/Admin/MountController.php @@ -0,0 +1,248 @@ +alert = $alert; + $this->nestRepository = $nestRepository; + $this->locationRepository = $locationRepository; + $this->repository = $repository; + $this->creationService = $creationService; + $this->deletionService = $deletionService; + $this->updateService = $updateService; + } + + /** + * Return the mount overview page. + * + * @return \Illuminate\View\View + */ + public function index() + { + return view('admin.mounts.index', [ + 'mounts' => $this->repository->getAllWithDetails(), + ]); + } + + /** + * Return the mount view page. + * + * @param string $id + * @return \Illuminate\View\View + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function view($id) + { + $nests = $this->nestRepository->all(); + $nests->load('eggs'); + + $locations = $this->locationRepository->all(); + $locations->load('nodes'); + + return view('admin.mounts.view', [ + 'mount' => $this->repository->getWithRelations($id), + 'nests' => $nests, + 'locations' => $locations, + ]); + } + + /** + * Handle request to create new mount. + * + * @param \Pterodactyl\Http\Requests\Admin\MountFormRequest $request + * @return \Illuminate\Http\RedirectResponse + * + * @throws \Throwable + */ + public function create(MountFormRequest $request) + { + $mount = $this->creationService->handle($request->normalize()); + $this->alert->success('Mount was created successfully.')->flash(); + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Handle request to update or delete location. + * + * @param \Pterodactyl\Http\Requests\Admin\MountFormRequest $request + * @param \Pterodactyl\Models\Mount $mount + * @return \Illuminate\Http\RedirectResponse + * + * @throws \Throwable + */ + public function update(MountFormRequest $request, Mount $mount) + { + if ($request->input('action') === 'delete') { + return $this->delete($mount); + } + + $this->updateService->handle($mount->id, $request->normalize()); + $this->alert->success('Mount was updated successfully.')->flash(); + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Delete a location from the system. + * + * @param \Pterodactyl\Models\Mount $mount + * @return \Illuminate\Http\RedirectResponse + * + * @throws \Exception + */ + public function delete(Mount $mount) + { + try { + $this->deletionService->handle($mount->id); + + return redirect()->route('admin.mounts'); + } catch (DisplayException $ex) { + $this->alert->danger($ex->getMessage())->flash(); + } + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Adds eggs to the mount's many to many relation. + * + * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Models\Mount $mount + * @return \Illuminate\Http\RedirectResponse + */ + public function addEggs(Request $request, Mount $mount) + { + $validatedData = $request->validate([ + 'eggs' => 'required|exists:eggs,id', + ]); + + $eggs = $validatedData['eggs'] ?? []; + if (sizeof($eggs) > 0) { + $mount->eggs()->attach(array_map('intval', $eggs)); + $this->alert->success('Mount was updated successfully.')->flash(); + } + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Adds nodes to the mount's many to many relation. + * + * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Models\Mount $mount + * @return \Illuminate\Http\RedirectResponse + */ + public function addNodes(Request $request, Mount $mount) + { + $validatedData = $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(); + } + + return redirect()->route('admin.mounts.view', $mount->id); + } + + /** + * Deletes an egg from the mount's many to many relation. + * + * @param \Pterodactyl\Models\Mount $mount + * @param int $egg_id + * @return \Illuminate\Http\Response + */ + public function deleteEgg(Mount $mount, int $egg_id) + { + $mount->eggs()->detach($egg_id); + + return response('', 204); + } + + /** + * Deletes an node from the mount's many to many relation. + * + * @param \Pterodactyl\Models\Mount $mount + * @param int $node_id + * @return \Illuminate\Http\Response + */ + public function deleteNode(Mount $mount, int $node_id) + { + $mount->nodes()->detach($node_id); + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Admin/Servers/ServerViewController.php b/app/Http/Controllers/Admin/Servers/ServerViewController.php index 25e198592..67531fa5f 100644 --- a/app/Http/Controllers/Admin/Servers/ServerViewController.php +++ b/app/Http/Controllers/Admin/Servers/ServerViewController.php @@ -11,6 +11,7 @@ use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Repositories\Eloquent\NestRepository; use Pterodactyl\Repositories\Eloquent\NodeRepository; +use Pterodactyl\Repositories\Eloquent\MountRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Traits\Controllers\JavascriptInjection; use Pterodactyl\Repositories\Eloquent\LocationRepository; @@ -35,6 +36,11 @@ class ServerViewController extends Controller */ private $repository; + /** + * @var \Pterodactyl\Repositories\Eloquent\MountRepository + */ + protected $mountRepository; + /** * @var \Pterodactyl\Repositories\Eloquent\NestRepository */ @@ -53,27 +59,30 @@ class ServerViewController extends Controller /** * ServerViewController constructor. * + * @param \Illuminate\Contracts\View\Factory $view * @param \Pterodactyl\Repositories\Eloquent\DatabaseHostRepository $databaseHostRepository - * @param \Pterodactyl\Repositories\Eloquent\NestRepository $nestRepository * @param \Pterodactyl\Repositories\Eloquent\LocationRepository $locationRepository + * @param \Pterodactyl\Repositories\Eloquent\MountRepository $mountRepository + * @param \Pterodactyl\Repositories\Eloquent\NestRepository $nestRepository * @param \Pterodactyl\Repositories\Eloquent\NodeRepository $nodeRepository * @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository - * @param \Illuminate\Contracts\View\Factory $view */ public function __construct( + Factory $view, DatabaseHostRepository $databaseHostRepository, - NestRepository $nestRepository, LocationRepository $locationRepository, + MountRepository $mountRepository, + NestRepository $nestRepository, NodeRepository $nodeRepository, - ServerRepository $repository, - Factory $view + ServerRepository $repository ) { $this->view = $view; $this->databaseHostRepository = $databaseHostRepository; - $this->repository = $repository; + $this->locationRepository = $locationRepository; + $this->mountRepository = $mountRepository; $this->nestRepository = $nestRepository; $this->nodeRepository = $nodeRepository; - $this->locationRepository = $locationRepository; + $this->repository = $repository; } /** @@ -160,6 +169,23 @@ class ServerViewController extends Controller ]); } + /** + * Returns all of the mounts that exist for the server. + * + * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Models\Server $server + * @return \Illuminate\Contracts\View\View + */ + public function mounts(Request $request, Server $server) + { + $server->load('mounts'); + + return $this->view->make('admin.servers.view.mounts', [ + 'mounts' => $this->mountRepository->getMountListForServer($server), + 'server' => $server, + ]); + } + /** * Returns the base server management page, or an exception if the server * is in a state that cannot be recovered from. @@ -169,7 +195,6 @@ class ServerViewController extends Controller * @return \Illuminate\Contracts\View\View * * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function manage(Request $request, Server $server) { diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index e163804cb..f914013bb 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -9,17 +9,21 @@ namespace Pterodactyl\Http\Controllers\Admin; +use Illuminate\Support\Arr; use Illuminate\Http\Request; use Pterodactyl\Models\User; use Pterodactyl\Models\Server; use Prologue\Alerts\AlertsMessageBag; +use GuzzleHttp\Exception\RequestException; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; use Illuminate\Validation\ValidationException; use Pterodactyl\Services\Servers\SuspensionService; +use Pterodactyl\Repositories\Eloquent\MountRepository; use Pterodactyl\Services\Servers\ServerDeletionService; use Pterodactyl\Services\Servers\ReinstallServerService; use Pterodactyl\Exceptions\Model\DataValidationException; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Services\Servers\BuildModificationService; use Pterodactyl\Services\Databases\DatabasePasswordService; use Pterodactyl\Services\Servers\DetailsModificationService; @@ -31,6 +35,8 @@ use Illuminate\Contracts\Config\Repository as ConfigRepository; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; +use Pterodactyl\Services\Servers\ServerConfigurationStructureService; use Pterodactyl\Http\Requests\Admin\Servers\Databases\StoreServerDatabaseRequest; class ServersController extends Controller @@ -55,6 +61,11 @@ class ServersController extends Controller */ protected $config; + /** + * @var \Pterodactyl\Repositories\Wings\DaemonServerRepository + */ + private $daemonServerRepository; + /** * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface */ @@ -85,6 +96,11 @@ class ServersController extends Controller */ protected $detailsModificationService; + /** + * @var \Pterodactyl\Repositories\Eloquent\MountRepository + */ + protected $mountRepository; + /** * @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface */ @@ -100,6 +116,11 @@ class ServersController extends Controller */ protected $repository; + /** + * @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService + */ + private $serverConfigurationStructureService; + /** * @var \Pterodactyl\Services\Servers\StartupModificationService */ @@ -117,6 +138,7 @@ class ServersController extends Controller * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository * @param \Pterodactyl\Services\Servers\BuildModificationService $buildModificationService * @param \Illuminate\Contracts\Config\Repository $config + * @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository * @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService * @param \Pterodactyl\Services\Databases\DatabasePasswordService $databasePasswordService * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $databaseRepository @@ -125,7 +147,9 @@ class ServersController extends Controller * @param \Pterodactyl\Services\Servers\DetailsModificationService $detailsModificationService * @param \Pterodactyl\Services\Servers\ReinstallServerService $reinstallService * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository + * @param \Pterodactyl\Repositories\Eloquent\MountRepository $mountRepository * @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $nestRepository + * @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $serverConfigurationStructureService * @param \Pterodactyl\Services\Servers\StartupModificationService $startupModificationService * @param \Pterodactyl\Services\Servers\SuspensionService $suspensionService */ @@ -134,6 +158,7 @@ class ServersController extends Controller AllocationRepositoryInterface $allocationRepository, BuildModificationService $buildModificationService, ConfigRepository $config, + DaemonServerRepository $daemonServerRepository, DatabaseManagementService $databaseManagementService, DatabasePasswordService $databasePasswordService, DatabaseRepositoryInterface $databaseRepository, @@ -142,7 +167,9 @@ class ServersController extends Controller DetailsModificationService $detailsModificationService, ReinstallServerService $reinstallService, ServerRepositoryInterface $repository, + MountRepository $mountRepository, NestRepositoryInterface $nestRepository, + ServerConfigurationStructureService $serverConfigurationStructureService, StartupModificationService $startupModificationService, SuspensionService $suspensionService ) { @@ -150,6 +177,7 @@ class ServersController extends Controller $this->allocationRepository = $allocationRepository; $this->buildModificationService = $buildModificationService; $this->config = $config; + $this->daemonServerRepository = $daemonServerRepository; $this->databaseHostRepository = $databaseHostRepository; $this->databaseManagementService = $databaseManagementService; $this->databasePasswordService = $databasePasswordService; @@ -159,6 +187,8 @@ class ServersController extends Controller $this->nestRepository = $nestRepository; $this->reinstallService = $reinstallService; $this->repository = $repository; + $this->mountRepository = $mountRepository; + $this->serverConfigurationStructureService = $serverConfigurationStructureService; $this->startupModificationService = $startupModificationService; $this->suspensionService = $suspensionService; } @@ -170,7 +200,6 @@ class ServersController extends Controller * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\RedirectResponse * - * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ @@ -378,4 +407,59 @@ class ServersController extends Controller return response('', 204); } + + /** + * Add a mount to a server. + * + * @param Server $server + * @param int $mount_id + * @return \Illuminate\Http\RedirectResponse + */ + public function addMount(Server $server, int $mount_id) + { + $server->mounts()->attach($mount_id); + + $data = $this->serverConfigurationStructureService->handle($server); + + try { + $this->daemonServerRepository + ->setServer($server) + ->update(Arr::only($data, ['mounts'])); + } catch (RequestException $exception) { + throw new DaemonConnectionException($exception); + } + + $this->alert->success('Mount was added successfully.')->flash(); + + return redirect()->route('admin.servers.view.mounts', $server->id); + } + + /** + * Remove a mount from a server. + * + * @param Server $server + * @param int $mount_id + * @return \Illuminate\Http\RedirectResponse + * + * @throws DaemonConnectionException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function deleteMount(Server $server, int $mount_id) + { + $server->mounts()->detach($mount_id); + + $data = $this->serverConfigurationStructureService->handle($server); + + try { + $this->daemonServerRepository + ->setServer($server) + ->update(Arr::only($data, ['mounts'])); + } catch (RequestException $exception) { + throw new DaemonConnectionException($exception); + } + + $this->alert->success('Mount was removed successfully.')->flash(); + + return redirect()->route('admin.servers.view.mounts', $server->id); + } } diff --git a/app/Http/Requests/Admin/MountFormRequest.php b/app/Http/Requests/Admin/MountFormRequest.php new file mode 100644 index 000000000..b6647b16b --- /dev/null +++ b/app/Http/Requests/Admin/MountFormRequest.php @@ -0,0 +1,29 @@ +. + * + * This software is licensed under the terms of the MIT license. + * https://opensource.org/licenses/MIT + */ + +namespace Pterodactyl\Http\Requests\Admin; + +use Pterodactyl\Models\Mount; + +class MountFormRequest extends AdminFormRequest +{ + /** + * Setup the validation rules to use for these requests. + * + * @return array + */ + public function rules() + { + if ($this->method() === 'PATCH') { + return Mount::getRulesForUpdate($this->route()->parameter('mount')->id); + } + + return Mount::getRules(); + } +} diff --git a/app/Models/Mount.php b/app/Models/Mount.php new file mode 100644 index 000000000..ac0b5da9a --- /dev/null +++ b/app/Models/Mount.php @@ -0,0 +1,108 @@ + 'int', + 'uuid' => 'string', + 'name' => 'string', + 'description' => 'string', + 'source' => 'string', + 'target' => 'string', + 'read_only' => 'bool', + 'user_mountable' => 'bool', + ]; + + /** + * Rules verifying that the data being stored matches the expectations of the database. + * + * @var string + */ + public static $validationRules = [ + // 'uuid' => 'required|string|size:36|unique:mounts,uuid', + 'name' => 'required|string|min:2|max:64|unique:mounts,name', + 'description' => 'nullable|string|max:255', + 'source' => 'required|string', + 'target' => 'required|string', + 'read_only' => 'sometimes|boolean', + 'user_mountable' => 'sometimes|boolean', + ]; + + /** + * Disable timestamps on this model. + * + * @var bool + */ + public $timestamps = false; + + /** + * Returns all eggs that have this mount assigned. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function eggs() + { + return $this->belongsToMany(Egg::class); + } + + /** + * Returns all nodes that have this mount assigned. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function nodes() + { + return $this->belongsToMany(Node::class); + } + + /** + * Returns all servers that have this mount assigned. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function servers() + { + return $this->belongsToMany(Server::class); + } +} diff --git a/app/Models/Server.php b/app/Models/Server.php index d8428cb86..8f15bfcf1 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -53,6 +53,7 @@ use Znck\Eloquent\Traits\BelongsToThrough; * @property \Pterodactyl\Models\DaemonKey[]|\Illuminate\Database\Eloquent\Collection $keys * @property \Pterodactyl\Models\ServerTransfer $transfer * @property \Pterodactyl\Models\Backup[]|\Illuminate\Database\Eloquent\Collection $backups + * @property \Pterodactyl\Models\Mount[]|\Illuminate\Database\Eloquent\Collection $mounts */ class Server extends Model { @@ -351,4 +352,14 @@ class Server extends Model { return $this->hasMany(Backup::class); } + + /** + * Returns all mounts that have this server has mounted. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function mounts() + { + return $this->belongsToMany(Mount::class); + } } diff --git a/app/Repositories/Eloquent/MountRepository.php b/app/Repositories/Eloquent/MountRepository.php new file mode 100644 index 000000000..c75ba2aa6 --- /dev/null +++ b/app/Repositories/Eloquent/MountRepository.php @@ -0,0 +1,70 @@ +getBuilder()->withCount('eggs', 'nodes')->get($this->getColumns()); + } + + /** + * Return all of the mounts and their respective relations. + * + * @param string $id + * @return mixed + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function getWithRelations(string $id): Mount + { + try { + return $this->getBuilder()->with('eggs', 'nodes')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException; + } + } + + /** + * Return mounts available to a server (ignoring if they are or are not mounted). + * + * @param Server $server + * @return \Illuminate\Support\Collection + */ + public function getMountListForServer(Server $server): Collection + { + return $this->getBuilder() + ->whereHas('eggs', function ($q) use ($server) { + $q->where('id', '=', $server->egg_id); + }) + ->whereHas('nodes', function ($q) use ($server) { + $q->where('id', '=', $server->node_id); + }) + ->get($this->getColumns()); + } +} diff --git a/app/Services/Mounts/MountCreationService.php b/app/Services/Mounts/MountCreationService.php new file mode 100644 index 000000000..923ba9abb --- /dev/null +++ b/app/Services/Mounts/MountCreationService.php @@ -0,0 +1,40 @@ +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); + } +} diff --git a/app/Services/Mounts/MountDeletionService.php b/app/Services/Mounts/MountDeletionService.php new file mode 100644 index 000000000..0850e685b --- /dev/null +++ b/app/Services/Mounts/MountDeletionService.php @@ -0,0 +1,40 @@ +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); + } +} diff --git a/app/Services/Mounts/MountUpdateService.php b/app/Services/Mounts/MountUpdateService.php new file mode 100644 index 000000000..a66f1ea91 --- /dev/null +++ b/app/Services/Mounts/MountUpdateService.php @@ -0,0 +1,41 @@ +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); + } +} diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php index 69950b230..fea2eaac0 100644 --- a/app/Services/Servers/ServerConfigurationStructureService.php +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -71,6 +71,17 @@ class ServerConfigurationStructureService */ protected function returnCurrentFormat(Server $server) { + $mounts = $server->mounts; + foreach ($mounts as $mount) { + unset($mount->id); + unset($mount->uuid); + unset($mount->name); + unset($mount->description); + $mount->read_only = $mount->read_only == 1; + unset($mount->user_mountable); + unset($mount->pivot); + } + return [ 'uuid' => $server->uuid, 'suspended' => (bool) $server->suspended, @@ -101,6 +112,7 @@ class ServerConfigurationStructureService ], 'mappings' => $server->getAllocationMappings(), ], + 'mounts' => $mounts, ]; } diff --git a/database/migrations/2020_05_20_234655_add_mounts_table.php b/database/migrations/2020_05_20_234655_add_mounts_table.php new file mode 100644 index 000000000..09846a0a5 --- /dev/null +++ b/database/migrations/2020_05_20_234655_add_mounts_table.php @@ -0,0 +1,53 @@ +increments('id')->unique(); + $table->char('uuid', 36)->unique(); + $table->string('name')->unique(); + $table->text('description')->nullable(); + $table->string('source'); + $table->string('target'); + $table->tinyInteger('read_only')->unsigned(); + $table->tinyInteger('user_mountable')->unsigned(); + }); + + Schema::create('egg_mount', function (Blueprint $table) { + $table->integer('egg_id'); + $table->integer('mount_id'); + + $table->unique(['egg_id', 'mount_id']); + }); + + Schema::create('mount_node', function (Blueprint $table) { + $table->integer('node_id'); + $table->integer('mount_id'); + + $table->unique(['node_id', 'mount_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('mount_node'); + Schema::dropIfExists('egg_mount'); + Schema::dropIfExists('mounts'); + } +} diff --git a/database/migrations/2020_05_21_192756_add_mount_server_table.php b/database/migrations/2020_05_21_192756_add_mount_server_table.php new file mode 100644 index 000000000..682bd578d --- /dev/null +++ b/database/migrations/2020_05_21_192756_add_mount_server_table.php @@ -0,0 +1,33 @@ +integer('server_id'); + $table->integer('mount_id'); + + $table->unique(['server_id', 'mount_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('mount_server'); + } +} diff --git a/resources/views/admin/mounts/index.blade.php b/resources/views/admin/mounts/index.blade.php new file mode 100644 index 000000000..da554cd04 --- /dev/null +++ b/resources/views/admin/mounts/index.blade.php @@ -0,0 +1,149 @@ +{{-- Pterodactyl - Panel --}} +{{-- Copyright (c) 2015 - 2017 Dane Everitt --}} + +{{-- This software is licensed under the terms of the MIT license. --}} +{{-- https://opensource.org/licenses/MIT --}} + +@extends('layouts.admin') + +@section('title') + Mounts +@endsection + +@section('content-header') +

MountsSoonTM

+ +@endsection + +@section('content') +
+
+
+
+

Mount List

+ +
+ +
+
+ +
+ + + + + + + + + + + + + @foreach ($mounts as $mount) + + + + + + + + + + @endforeach + +
IDNameSourceTargetEggsNodesServers
{{ $mount->id }}{{ $mount->name }}{{ $mount->source }}{{ $mount->target }}{{ $mount->eggs_count }}{{ $mount->nodes_count }}{{ $mount->servers_count }}
+
+
+
+
+ + +@endsection diff --git a/resources/views/admin/mounts/view.blade.php b/resources/views/admin/mounts/view.blade.php new file mode 100644 index 000000000..f53007f3f --- /dev/null +++ b/resources/views/admin/mounts/view.blade.php @@ -0,0 +1,319 @@ +{{-- Pterodactyl - Panel --}} +{{-- Copyright (c) 2015 - 2017 Dane Everitt --}} + +{{-- This software is licensed under the terms of the MIT license. --}} +{{-- https://opensource.org/licenses/MIT --}} + +@extends('layouts.admin') + +@section('title') + Mounts → View → {{ $mount->id }} +@endsection + +@section('content-header') +

{{ $mount->name }}{{ str_limit($mount->description, 75) }}

+ +@endsection + +@section('content') +
+
+
+
+

Mount Details

+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + +
+
+ read_only) checked @endif> + +
+ +
+ read_only) checked @endif> + +
+
+
+ +
+ + +
+
+ user_mountable) checked @endif> + +
+ +
+ user_mountable) checked @endif> + +
+
+
+
+
+ + +
+
+
+ +
+
+
+

Eggs

+ +
+ +
+
+ +
+ + + + + + + + @foreach ($mount->eggs as $egg) + + + + + + @endforeach +
IDName
{{ $egg->id }}{{ $egg->name }} + +
+
+
+ +
+
+

Nodes

+ +
+ +
+
+ +
+ + + + + + + + + @foreach ($mount->nodes as $node) + + + + + + + @endforeach +
IDNameFQDN
{{ $node->id }}{{ $node->name }}{{ $node->fqdn }} + +
+
+
+
+
+ + + + +@endsection + +@section('footer-scripts') + @parent + + +@endsection diff --git a/resources/views/admin/servers/partials/navigation.blade.php b/resources/views/admin/servers/partials/navigation.blade.php index 32316e611..0474787de 100644 --- a/resources/views/admin/servers/partials/navigation.blade.php +++ b/resources/views/admin/servers/partials/navigation.blade.php @@ -21,6 +21,9 @@
  • Database
  • +
  • + Mounts +
  • @endif
  • Manage diff --git a/resources/views/admin/servers/view/database.blade.php b/resources/views/admin/servers/view/database.blade.php index f861008eb..93916584c 100644 --- a/resources/views/admin/servers/view/database.blade.php +++ b/resources/views/admin/servers/view/database.blade.php @@ -37,7 +37,7 @@ Username Connections From Host - Max Conenctions + Max Connections @foreach($server->databases as $database) diff --git a/resources/views/admin/servers/view/mounts.blade.php b/resources/views/admin/servers/view/mounts.blade.php new file mode 100644 index 000000000..a34823169 --- /dev/null +++ b/resources/views/admin/servers/view/mounts.blade.php @@ -0,0 +1,78 @@ +@extends('layouts.admin') + +@section('title') + Server — {{ $server->name }}: Mounts +@endsection + +@section('content-header') +

    {{ $server->name }}Manage server mounts.

    + +@endsection + +@section('content') + @include('admin.servers.partials.navigation') + +
    +
    +
    +
    +

    Available Mounts

    +
    + +
    + + + + + + + + + + + @foreach ($mounts as $mount) + + + + + + + @if (! in_array($mount->id, $server->mounts->pluck('id')->toArray())) + + + + @else + + + + @endif + + @endforeach +
    IDNameSourceTargetStatus
    {{ $mount->id }}{{ $mount->name }}{{ $mount->source }}{{ $mount->target }} + Unmounted + +
    + {!! csrf_field() !!} + + +
    +
    + Mounted + +
    + @method('DELETE') + {!! csrf_field() !!} + + +
    +
    +
    +
    +
    +
    +@endsection diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index 373515e84..42a2bc434 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -117,6 +117,11 @@
  • SERVICE MANAGEMENT
  • +
  • + + Mounts + +
  • Nests diff --git a/routes/admin.php b/routes/admin.php index 7a1c9fa75..32ce93c83 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -113,6 +113,7 @@ Route::group(['prefix' => 'servers'], function () { Route::get('/view/{server}/build', 'Servers\ServerViewController@build')->name('admin.servers.view.build'); Route::get('/view/{server}/startup', 'Servers\ServerViewController@startup')->name('admin.servers.view.startup'); Route::get('/view/{server}/database', 'Servers\ServerViewController@database')->name('admin.servers.view.database'); + Route::get('/view/{server}/mounts', 'Servers\ServerViewController@mounts')->name('admin.servers.view.mounts'); }); Route::get('/view/{server}/manage', 'Servers\ServerViewController@manage')->name('admin.servers.view.manage'); @@ -122,6 +123,7 @@ Route::group(['prefix' => 'servers'], function () { Route::post('/view/{server}/build', 'ServersController@updateBuild'); Route::post('/view/{server}/startup', 'ServersController@saveStartup'); Route::post('/view/{server}/database', 'ServersController@newDatabase'); + Route::post('/view/{server}/mounts/{mount}', 'ServersController@addMount')->name('admin.servers.view.mounts.toggle'); Route::post('/view/{server}/manage/toggle', 'ServersController@toggleInstall')->name('admin.servers.view.manage.toggle'); Route::post('/view/{server}/manage/suspension', 'ServersController@manageSuspension')->name('admin.servers.view.manage.suspension'); Route::post('/view/{server}/manage/reinstall', 'ServersController@reinstallServer')->name('admin.servers.view.manage.reinstall'); @@ -132,6 +134,7 @@ Route::group(['prefix' => 'servers'], function () { Route::patch('/view/{server}/database', 'ServersController@resetDatabasePassword'); Route::delete('/view/{server}/database/{database}/delete', 'ServersController@deleteDatabase')->name('admin.servers.view.database.delete'); + Route::delete('/view/{server}/mounts/{mount}', 'ServersController@deleteMount'); }); /* @@ -165,6 +168,28 @@ Route::group(['prefix' => 'nodes'], function () { Route::delete('/view/{node}/allocations', 'NodesController@allocationRemoveMultiple')->name('admin.nodes.view.allocation.removeMultiple'); }); +/* +|-------------------------------------------------------------------------- +| Mount Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /admin/mounts +| +*/ +Route::group(['prefix' => 'mounts'], function () { + Route::get('/', 'MountController@index')->name('admin.mounts'); + Route::get('/view/{mount}', 'MountController@view')->name('admin.mounts.view'); + + Route::post('/', 'MountController@create'); + Route::post('/{mount}/eggs', 'MountController@addEggs')->name('admin.mounts.eggs'); + Route::post('/{mount}/nodes', 'MountController@addNodes')->name('admin.mounts.nodes'); + + Route::patch('/view/{mount}', 'MountController@update'); + + Route::delete('/{mount}/eggs/{egg_id}', 'MountController@deleteEgg'); + Route::delete('/{mount}/nodes/{node_id}', 'MountController@deleteNode'); +}); + /* |-------------------------------------------------------------------------- | Nest Controller Routes