diff --git a/app/Http/Controllers/Api/Application/ApplicationApiController.php b/app/Http/Controllers/Api/Application/ApplicationApiController.php index 3285a5bf9..3a6473553 100644 --- a/app/Http/Controllers/Api/Application/ApplicationApiController.php +++ b/app/Http/Controllers/Api/Application/ApplicationApiController.php @@ -3,6 +3,7 @@ namespace Pterodactyl\Http\Controllers\Api\Application; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Container\Container; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Extensions\Spatie\Fractalistic\Fractal; @@ -21,17 +22,13 @@ abstract class ApplicationApiController extends Controller /** * ApplicationApiController constructor. - * - * @param \Pterodactyl\Extensions\Spatie\Fractalistic\Fractal $fractal - * @param \Illuminate\Http\Request $request */ - public function __construct(Fractal $fractal, Request $request) + public function __construct() { - $this->fractal = $fractal; - $this->request = $request; + Container::getInstance()->call([$this, 'loadDependencies']); // Parse all of the includes to use on this request. - $includes = collect(explode(',', $request->input('include', '')))->map(function ($value) { + $includes = collect(explode(',', $this->request->input('include', '')))->map(function ($value) { return trim($value); })->filter()->toArray(); @@ -39,6 +36,19 @@ abstract class ApplicationApiController extends Controller $this->fractal->limitRecursion(2); } + /** + * Perform dependency injection of certain classes needed for core functionality + * without littering the constructors of classes that extend this abstract. + * + * @param \Pterodactyl\Extensions\Spatie\Fractalistic\Fractal $fractal + * @param \Illuminate\Http\Request $request + */ + public function loadDependencies(Fractal $fractal, Request $request) + { + $this->fractal = $fractal; + $this->request = $request; + } + /** * Return an instance of an application transformer. * @@ -53,4 +63,14 @@ abstract class ApplicationApiController extends Controller return $transformer; } + + /** + * Return a HTTP/204 response for the API. + * + * @return \Illuminate\Http\Response + */ + protected function returnNoContent(): Response + { + return new Response('', Response::HTTP_NO_CONTENT); + } } diff --git a/app/Http/Controllers/Api/Application/Servers/ServerController.php b/app/Http/Controllers/Api/Application/Servers/ServerController.php index f1d7398a3..98d90f284 100644 --- a/app/Http/Controllers/Api/Application/Servers/ServerController.php +++ b/app/Http/Controllers/Api/Application/Servers/ServerController.php @@ -2,17 +2,22 @@ namespace Pterodactyl\Http\Controllers\Api\Application\Servers; -use Illuminate\Http\Request; +use Illuminate\Http\Response; use Pterodactyl\Models\Server; -use Pterodactyl\Extensions\Spatie\Fractalistic\Fractal; +use Pterodactyl\Services\Servers\ServerDeletionService; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Transformers\Api\Application\ServerTransformer; -use Pterodactyl\Http\Requests\Api\Application\Servers\GetServerRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\GetServersRequest; +use Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; class ServerController extends ApplicationApiController { + /** + * @var \Pterodactyl\Services\Servers\ServerDeletionService + */ + private $deletionService; + /** * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface */ @@ -21,14 +26,14 @@ class ServerController extends ApplicationApiController /** * ServerController constructor. * - * @param \Pterodactyl\Extensions\Spatie\Fractalistic\Fractal $fractal - * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Services\Servers\ServerDeletionService $deletionService * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ - public function __construct(Fractal $fractal, Request $request, ServerRepositoryInterface $repository) + public function __construct(ServerDeletionService $deletionService, ServerRepositoryInterface $repository) { - parent::__construct($fractal, $request); + parent::__construct(); + $this->deletionService = $deletionService; $this->repository = $repository; } @@ -50,14 +55,30 @@ class ServerController extends ApplicationApiController /** * Show a single server transformed for the application API. * - * @param \Pterodactyl\Http\Requests\Api\Application\Servers\GetServerRequest $request - * @param \Pterodactyl\Models\Server $server + * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request + * @param \Pterodactyl\Models\Server $server * @return array */ - public function view(GetServerRequest $request, Server $server): array + public function view(ServerWriteRequest $request, Server $server): array { return $this->fractal->item($server) ->transformWith($this->getTransformer(ServerTransformer::class)) ->toArray(); } + + /** + * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request + * @param \Pterodactyl\Models\Server $server + * @param string $force + * @return \Illuminate\Http\Response + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function delete(ServerWriteRequest $request, Server $server, string $force = ''): Response + { + $this->deletionService->withForce($force === 'force')->handle($server); + + return $this->returnNoContent(); + } } diff --git a/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php new file mode 100644 index 000000000..4379616fa --- /dev/null +++ b/app/Http/Controllers/Api/Application/Servers/ServerManagementController.php @@ -0,0 +1,115 @@ +rebuildService = $rebuildService; + $this->reinstallServerService = $reinstallServerService; + $this->suspensionService = $suspensionService; + } + + /** + * Suspend a server on the Panel. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request + * @param \Pterodactyl\Models\Server $server + * @return \Illuminate\Http\Response + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function suspend(ServerWriteRequest $request, Server $server): Response + { + $this->suspensionService->toggle($server, SuspensionService::ACTION_SUSPEND); + + return $this->returnNoContent(); + } + + /** + * Unsuspend a server on the Panel. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request + * @param \Pterodactyl\Models\Server $server + * @return \Illuminate\Http\Response + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function unsuspend(ServerWriteRequest $request, Server $server): Response + { + $this->suspensionService->toggle($server, SuspensionService::ACTION_UNSUSPEND); + + return $this->returnNoContent(); + } + + /** + * Mark a server as needing to be reinstalled. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request + * @param \Pterodactyl\Models\Server $server + * @return \Illuminate\Http\Response + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function reinstall(ServerWriteRequest $request, Server $server): Response + { + $this->reinstallServerService->reinstall($server); + + return $this->returnNoContent(); + } + + /** + * Mark a server as needing its container rebuilt the next time it is started. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest $request + * @param \Pterodactyl\Models\Server $server + * @return \Illuminate\Http\Response + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function rebuild(ServerWriteRequest $request, Server $server): Response + { + $this->rebuildService->handle($server); + + return $this->returnNoContent(); + } +} diff --git a/app/Http/Requests/Api/Application/Servers/GetServerRequest.php b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php similarity index 86% rename from app/Http/Requests/Api/Application/Servers/GetServerRequest.php rename to app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php index 42428ece7..728b1ce52 100644 --- a/app/Http/Requests/Api/Application/Servers/GetServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php @@ -6,7 +6,7 @@ use Pterodactyl\Models\Server; use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; -class GetServerRequest extends ApplicationApiRequest +class ServerWriteRequest extends ApplicationApiRequest { /** * @var string @@ -16,7 +16,7 @@ class GetServerRequest extends ApplicationApiRequest /** * @var int */ - protected $permission = AdminAcl::READ; + protected $permission = AdminAcl::WRITE; /** * Determine if the requested server exists on the Panel. diff --git a/app/Services/Servers/SuspensionService.php b/app/Services/Servers/SuspensionService.php index 1feb2887d..896cfeb17 100644 --- a/app/Services/Servers/SuspensionService.php +++ b/app/Services/Servers/SuspensionService.php @@ -19,6 +19,9 @@ use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonS class SuspensionService { + const ACTION_SUSPEND = 'suspend'; + const ACTION_UNSUSPEND = 'unsuspend'; + /** * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface */ @@ -70,29 +73,29 @@ class SuspensionService * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function toggle($server, $action = 'suspend') + public function toggle($server, $action = self::ACTION_SUSPEND) { if (! $server instanceof Server) { $server = $this->repository->find($server); } - if (! in_array($action, ['suspend', 'unsuspend'])) { + if (! in_array($action, [self::ACTION_SUSPEND, self::ACTION_UNSUSPEND])) { throw new \InvalidArgumentException(sprintf( - 'Action must be either suspend or unsuspend, %s passed.', + 'Action must be either ' . self::ACTION_SUSPEND . ' or ' . self::ACTION_UNSUSPEND . ', %s passed.', $action )); } if ( - $action === 'suspend' && $server->suspended || - $action === 'unsuspend' && ! $server->suspended + $action === self::ACTION_SUSPEND && $server->suspended || + $action === self::ACTION_UNSUSPEND && ! $server->suspended ) { return true; } $this->database->beginTransaction(); $this->repository->withoutFreshModel()->update($server->id, [ - 'suspended' => $action === 'suspend', + 'suspended' => $action === self::ACTION_SUSPEND, ]); try { diff --git a/routes/api-application.php b/routes/api-application.php index 504b17bc8..f6d2577c0 100644 --- a/routes/api-application.php +++ b/routes/api-application.php @@ -96,5 +96,13 @@ Route::group(['prefix' => '/servers'], function () { }); Route::get('/', 'Servers\ServerController@index')->name('api.application.servers'); - Route::get('/{server}', 'Servers\ServerController@view')->name('api.application.servers'); + Route::get('/{server}', 'Servers\ServerController@view')->name('api.application.servers.view'); + + Route::post('/{server}/suspend', 'Servers\ServerManagementController@suspend')->name('api.application.servers.suspend'); + Route::post('/{server}/unsuspend', 'Servers\ServerManagementController@unsuspend')->name('api.application.servers.unsuspend'); + Route::post('/{server}/reinstall', 'Servers\ServerManagementController@reinstall')->name('api.application.servers.reinstall'); + Route::post('/{server}/rebuild', 'Servers\ServerManagementController@rebuild')->name('api.application.servers.rebuild'); + + Route::delete('/{server}', 'Servers\ServerController@delete'); + Route::delete('/{server}/{force?}', 'Servers\ServerController@delete'); });