diff --git a/app/Contracts/Repository/AllocationRepositoryInterface.php b/app/Contracts/Repository/AllocationRepositoryInterface.php new file mode 100644 index 000000000..4dca0af88 --- /dev/null +++ b/app/Contracts/Repository/AllocationRepositoryInterface.php @@ -0,0 +1,37 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repository; + +interface AllocationRepositoryInterface extends RepositoryInterface +{ + /** + * Set an array of allocation IDs to be assigned to a specific server. + * + * @param int|null $server + * @param array $ids + * @return int + */ + public function assignAllocationsToServer($server, array $ids); +} diff --git a/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php b/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php new file mode 100644 index 000000000..490f38a44 --- /dev/null +++ b/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php @@ -0,0 +1,81 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repository\Daemon; + +interface BaseRepositoryInterface +{ + /** + * Set the node model to be used for this daemon connection. + * + * @param int $id + * @return $this + */ + public function setNode($id); + + /** + * Return the node model being used. + * + * @return \Pterodactyl\Models\Node + */ + public function getNode(); + + /** + * Set the UUID for the server to be used in the X-Access-Server header for daemon requests. + * + * @param null|string $server + * @return $this + */ + public function setAccessServer($server = null); + + /** + * Return the UUID of the server being used in requests. + * + * @return string + */ + public function getAccessServer(); + + /** + * Set the token to be used in the X-Access-Token header for requests to the daemon. + * + * @param null|string $token + * @return $this + */ + public function setAccessToken($token = null); + + /** + * Return the access token being used for requests. + * + * @return string + */ + public function getAccessToken(); + + /** + * Return an instance of the Guzzle HTTP Client to be used for requests. + * + * @param array $headers + * @return \GuzzleHttp\Client + */ + public function getHttpClient($headers = []); +} diff --git a/app/Contracts/Repository/Daemon/ServerRepositoryInterface.php b/app/Contracts/Repository/Daemon/ServerRepositoryInterface.php new file mode 100644 index 000000000..a4e398bc3 --- /dev/null +++ b/app/Contracts/Repository/Daemon/ServerRepositoryInterface.php @@ -0,0 +1,38 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repository\Daemon; + +interface ServerRepositoryInterface extends BaseRepositoryInterface +{ + /** + * Create a new server on the daemon for the panel. + * + * @param int $id + * @param array $overrides + * @param bool $start + * @return mixed + */ + public function create($id, $overrides = [], $start = false); +} diff --git a/app/Contracts/Repository/NodeRepositoryInterface.php b/app/Contracts/Repository/NodeRepositoryInterface.php new file mode 100644 index 000000000..c72e2fe8e --- /dev/null +++ b/app/Contracts/Repository/NodeRepositoryInterface.php @@ -0,0 +1,32 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repository; + +use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; + +interface NodeRepositoryInterface extends RepositoryInterface, SearchableInterface +{ + // +} diff --git a/app/Contracts/Repository/OptionVariableRepositoryInterface.php b/app/Contracts/Repository/OptionVariableRepositoryInterface.php new file mode 100644 index 000000000..794bfc791 --- /dev/null +++ b/app/Contracts/Repository/OptionVariableRepositoryInterface.php @@ -0,0 +1,30 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repository; + +interface OptionVariableRepositoryInterface extends RepositoryInterface +{ + // +} diff --git a/app/Contracts/Repository/RepositoryInterface.php b/app/Contracts/Repository/RepositoryInterface.php index 2a19810fd..b32260952 100644 --- a/app/Contracts/Repository/RepositoryInterface.php +++ b/app/Contracts/Repository/RepositoryInterface.php @@ -145,4 +145,13 @@ interface RepositoryInterface * @return mixed */ public function all(); + + /** + * Insert a single or multiple records into the database at once skipping + * validation and mass assignment checking. + * + * @param array $data + * @return bool + */ + public function insert(array $data); } diff --git a/app/Contracts/Repository/ServerRepositoryInterface.php b/app/Contracts/Repository/ServerRepositoryInterface.php index 5619e3fdd..99a95ccc5 100644 --- a/app/Contracts/Repository/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/ServerRepositoryInterface.php @@ -35,4 +35,21 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter * @return mixed */ public function getAllServers($paginate); + + /** + * Return a server model and all variables associated with the server. + * + * @param int $id + * @return mixed + */ + public function findWithVariables($id); + + /** + * Return all of the server variables possible and default to the variable + * default if there is no value defined for the specific server requested. + * + * @param int $id + * @return array + */ + public function getVariablesWithValues($id); } diff --git a/app/Contracts/Repository/ServerVariableRepositoryInterface.php b/app/Contracts/Repository/ServerVariableRepositoryInterface.php new file mode 100644 index 000000000..b0ca226cf --- /dev/null +++ b/app/Contracts/Repository/ServerVariableRepositoryInterface.php @@ -0,0 +1,30 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Contracts\Repository; + +interface ServerVariableRepositoryInterface extends RepositoryInterface +{ + // +} diff --git a/app/Exceptions/Services/Servers/RequiredVariableMissingException.php b/app/Exceptions/Services/Servers/RequiredVariableMissingException.php new file mode 100644 index 000000000..f4a1a6317 --- /dev/null +++ b/app/Exceptions/Services/Servers/RequiredVariableMissingException.php @@ -0,0 +1,32 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Exceptions\Services\Servers; + +use Exception; + +class RequiredVariableMissingException extends Exception +{ + // +} diff --git a/app/Http/Controllers/API/User/ServerController.php b/app/Http/Controllers/API/User/ServerController.php index 904935186..f7e652c22 100644 --- a/app/Http/Controllers/API/User/ServerController.php +++ b/app/Http/Controllers/API/User/ServerController.php @@ -28,9 +28,9 @@ use Fractal; use Illuminate\Http\Request; use Pterodactyl\Models\Server; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Repositories\Daemon\PowerRepository; +use Pterodactyl\Repositories\old_Daemon\PowerRepository; use Pterodactyl\Transformers\User\ServerTransformer; -use Pterodactyl\Repositories\Daemon\CommandRepository; +use Pterodactyl\Repositories\old_Daemon\CommandRepository; class ServerController extends Controller { diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 0c27bd59a..e1059d74b 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -32,6 +32,7 @@ use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface; +use Pterodactyl\Http\Requests\Admin\ServerFormRequest; use Pterodactyl\Models; use Illuminate\Http\Request; use GuzzleHttp\Exception\TransferException; @@ -41,6 +42,7 @@ use Pterodactyl\Repositories\ServerRepository; use Pterodactyl\Repositories\DatabaseRepository; use Pterodactyl\Exceptions\AutoDeploymentException; use Pterodactyl\Exceptions\DisplayValidationException; +use Pterodactyl\Services\Servers\ServerService; class ServersController extends Controller { @@ -64,6 +66,11 @@ class ServersController extends Controller */ protected $repository; + /** + * @var \Pterodactyl\Services\Servers\ServerService + */ + protected $service; + /** * @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface */ @@ -73,6 +80,7 @@ class ServersController extends Controller ConfigRepository $config, DatabaseRepositoryInterface $databaseRepository, LocationRepositoryInterface $locationRepository, + ServerService $service, ServerRepositoryInterface $repository, ServiceRepositoryInterface $serviceRepository ) { @@ -80,6 +88,7 @@ class ServersController extends Controller $this->databaseRepository = $databaseRepository; $this->locationRepository = $locationRepository; $this->repository = $repository; + $this->service = $service; $this->serviceRepository = $serviceRepository; } @@ -125,31 +134,33 @@ class ServersController extends Controller /** * Create server controller method. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Response\RedirectResponse + * @param \Illuminate\Http\Request $request */ public function store(Request $request) { - try { - $repo = new ServerRepository; - $server = $repo->create($request->except('_token')); - - return redirect()->route('admin.servers.view', $server->id); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (AutoDeploymentException $ex) { - Alert::danger('Auto-Deployment Exception: ' . $ex->getMessage())->flash(); - } catch (TransferException $ex) { - Log::warning($ex); - Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash(); - } + $this->service->create($request->all()); return redirect()->route('admin.servers.new')->withInput(); + // try { +// $repo = new ServerRepository; +// $server = $repo->create($request->except('_token')); +// +// return redirect()->route('admin.servers.view', $server->id); +// } catch (DisplayValidationException $ex) { +// return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput(); +// } catch (DisplayException $ex) { +// Alert::danger($ex->getMessage())->flash(); +// } catch (AutoDeploymentException $ex) { +// Alert::danger('Auto-Deployment Exception: ' . $ex->getMessage())->flash(); +// } catch (TransferException $ex) { +// Log::warning($ex); +// Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash(); +// } catch (\Exception $ex) { +// Log::error($ex); +// Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash(); +// } +// +// return redirect()->route('admin.servers.new')->withInput(); } /** @@ -310,8 +321,9 @@ class ServersController extends Controller * @param int $id * @return \Illuminate\Http\RedirectResponse */ - public function setDetails(Request $request, $id) + public function setDetails(ServerFormRequest $request, Models\Server $server) { + dd($server); $repo = new ServerRepository; try { $repo->updateDetails($id, array_merge( diff --git a/app/Http/Controllers/Server/AjaxController.php b/app/Http/Controllers/Server/AjaxController.php index c22b0c202..1d824ddf8 100644 --- a/app/Http/Controllers/Server/AjaxController.php +++ b/app/Http/Controllers/Server/AjaxController.php @@ -79,7 +79,7 @@ class AjaxController extends Controller $prevDir['link_show'] = implode('/', $goBack) . '/'; } - $controller = new Repositories\Daemon\FileRepository($uuid); + $controller = new Repositories\old_Daemon\FileRepository($uuid); try { $directoryContents = $controller->returnDirectoryListing($this->directory); @@ -112,7 +112,7 @@ class AjaxController extends Controller $server = Models\Server::byUuid($uuid); $this->authorize('save-files', $server); - $controller = new Repositories\Daemon\FileRepository($uuid); + $controller = new Repositories\old_Daemon\FileRepository($uuid); try { $controller->saveFileContents($request->input('file'), $request->input('contents')); diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index ce6f59595..c15af88ce 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -32,7 +32,7 @@ use Illuminate\Http\Request; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Repositories\ServerRepository; -use Pterodactyl\Repositories\Daemon\FileRepository; +use Pterodactyl\Repositories\old_Daemon\FileRepository; use Pterodactyl\Exceptions\DisplayValidationException; class ServerController extends Controller diff --git a/app/Http/Requests/Admin/ServerFormRequest.php b/app/Http/Requests/Admin/ServerFormRequest.php new file mode 100644 index 000000000..1efe00acb --- /dev/null +++ b/app/Http/Requests/Admin/ServerFormRequest.php @@ -0,0 +1,83 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Requests\Admin; + +use Illuminate\Validation\Rule; +use Pterodactyl\Models\Server; + +class ServerFormRequest extends AdminFormRequest +{ + /** + * Rules to be applied to this request. + * + * @return array + */ + public function rules() + { + if ($this->method() === 'PATCH') { + return Server::getUpdateRulesForId($this->id); + } + + return Server::getCreateRules(); + } + + /** + * Run validation after the rules above have been applied. + * + * @param \Illuminate\Validation\Validator $validator + */ + public function withValidator($validator) + { + $validator->after(function ($validator) { + $validator->sometimes('node_id', 'required|numeric|bail|exists:nodes,id', function ($input) { + return ! ($input->auto_deploy); + }); + + $validator->sometimes('allocation_id', [ + 'required', + 'numeric', + 'bail', + Rule::exists('allocations', 'id')->where(function ($query) { + $query->where('node_id', $this->input('node_id')); + $query->whereNull('server_id'); + }), + ], function ($input) { + return ! ($input->auto_deploy); + }); + + $validator->sometimes('allocation_additional.*', [ + 'sometimes', + 'required', + 'numeric', + Rule::exists('allocations', 'id')->where(function ($query) { + $query->where('node_id', $this->input('node_id')); + $query->whereNull('server_id'); + }), + ], function ($input) { + return ! ($input->auto_deploy); + }); + }); + } +} diff --git a/app/Jobs/SendScheduledTask.php b/app/Jobs/SendScheduledTask.php index 525b670df..8f9889730 100644 --- a/app/Jobs/SendScheduledTask.php +++ b/app/Jobs/SendScheduledTask.php @@ -31,8 +31,8 @@ use Pterodactyl\Models\TaskLog; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; -use Pterodactyl\Repositories\Daemon\PowerRepository; -use Pterodactyl\Repositories\Daemon\CommandRepository; +use Pterodactyl\Repositories\old_Daemon\PowerRepository; +use Pterodactyl\Repositories\old_Daemon\CommandRepository; class SendScheduledTask extends Job implements ShouldQueue { diff --git a/app/Models/Server.php b/app/Models/Server.php index 1a26243e9..5a147c3e9 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -29,13 +29,15 @@ use Cache; use Carbon; use Schema; use Javascript; +use Sofa\Eloquence\Eloquence; +use Sofa\Eloquence\Validable; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; -use Nicolaslopezj\Searchable\SearchableTrait; +use Sofa\Eloquence\Contracts\Validable as ValidableContract; -class Server extends Model +class Server extends Model implements ValidableContract { - use Notifiable, SearchableTrait; + use Eloquence, Notifiable, Validable; /** * The table associated with the model. @@ -65,6 +67,43 @@ class Server extends Model */ protected $guarded = ['id', 'installed', 'created_at', 'updated_at', 'deleted_at']; + protected static $applicationRules = [ + 'owner_id' => 'required', + 'name' => 'required', + 'memory' => 'required', + 'swap' => 'required', + 'io' => 'required', + 'cpu' => 'required', + 'disk' => 'required', + 'service_id' => 'required', + 'option_id' => 'required', + 'pack_id' => 'sometimes', + 'auto_deploy' => 'sometimes', + 'custom_id' => 'sometimes', + 'skip_scripts' => 'sometimes', + ]; + + protected static $dataIntegrityRules = [ + 'owner_id' => 'exists:users,id', + 'name' => 'regex:/^([\w .-]{1,200})$/', + 'node_id' => 'exists:nodes,id', + 'description' => 'nullable|string', + 'memory' => 'numeric|min:0', + 'swap' => 'numeric|min:-1', + 'io' => 'numeric|between:10,1000', + 'cpu' => 'numeric|min:0', + 'disk' => 'numeric|min:0', + 'allocation_id' => 'exists:allocations,id', + 'service_id' => 'exists:services,id', + 'option_id' => 'exists:service_options,id', + 'pack_id' => 'nullable|numeric|min:0', + 'custom_container' => 'nullable|string', + 'startup' => 'nullable|string', + 'auto_deploy' => 'accepted', + 'custom_id' => 'numeric|unique:servers,id', + 'skip_scripts' => 'boolean', + ]; + /** * Cast values to correct type. * diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php index 189ca6eb6..07fc2d28c 100644 --- a/app/Providers/RepositoryServiceProvider.php +++ b/app/Providers/RepositoryServiceProvider.php @@ -25,19 +25,27 @@ namespace Pterodactyl\Providers; use Illuminate\Support\ServiceProvider; +use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface; use Pterodactyl\Contracts\Repository\ApiPermissionRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; +use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; +use Pterodactyl\Contracts\Repository\OptionVariableRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface; +use Pterodactyl\Repositories\Eloquent\AllocationRepository; use Pterodactyl\Repositories\Eloquent\ApiKeyRepository; use Pterodactyl\Repositories\Eloquent\ApiPermissionRepository; use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository; use Pterodactyl\Repositories\Eloquent\DatabaseRepository; use Pterodactyl\Repositories\Eloquent\LocationRepository; +use Pterodactyl\Repositories\Eloquent\NodeRepository; +use Pterodactyl\Repositories\Eloquent\OptionVariableRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository; +use Pterodactyl\Repositories\Eloquent\ServerVariableRepository; use Pterodactyl\Repositories\Eloquent\ServiceRepository; use Pterodactyl\Repositories\Eloquent\UserRepository; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; @@ -49,13 +57,23 @@ class RepositoryServiceProvider extends ServiceProvider */ public function register() { + $this->app->bind(AllocationRepositoryInterface::class, AllocationRepository::class); $this->app->bind(ApiKeyRepositoryInterface::class, ApiKeyRepository::class); $this->app->bind(ApiPermissionRepositoryInterface::class, ApiPermissionRepository::class); - $this->app->bind(DatabaseRepositoryInterface::class, DatabaseRepository::class); $this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class); + $this->app->bind(DatabaseRepositoryInterface::class, DatabaseRepository::class); $this->app->bind(LocationRepositoryInterface::class, LocationRepository::class); + $this->app->bind(NodeRepositoryInterface::class, NodeRepository::class); + $this->app->bind(OptionVariableRepositoryInterface::class, OptionVariableRepository::class); $this->app->bind(ServerRepositoryInterface::class, ServerRepository::class); + $this->app->bind(ServerVariableRepositoryInterface::class, ServerVariableRepository::class); $this->app->bind(ServiceRepositoryInterface::class, ServiceRepository::class); $this->app->bind(UserRepositoryInterface::class, UserRepository::class); + + // Daemon Repositories + $this->app->bind( + \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface::class, + \Pterodactyl\Repositories\Daemon\ServerRepository::class + ); } } diff --git a/app/Repositories/Daemon/BaseRepository.php b/app/Repositories/Daemon/BaseRepository.php new file mode 100644 index 000000000..5f6f92b68 --- /dev/null +++ b/app/Repositories/Daemon/BaseRepository.php @@ -0,0 +1,105 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories\Daemon; + +use GuzzleHttp\Client; +use Illuminate\Foundation\Application; +use Pterodactyl\Contracts\Repository\Daemon\BaseRepositoryInterface; +use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; +use Illuminate\Contracts\Config\Repository as ConfigRepository; + +class BaseRepository implements BaseRepositoryInterface +{ + protected $app; + protected $accessServer; + protected $accessToken; + protected $node; + protected $config; + protected $nodeRepository; + + public function __construct( + Application $app, + ConfigRepository $config, + NodeRepositoryInterface $nodeRepository + ) { + $this->app = $app; + $this->config = $config; + $this->nodeRepository = $nodeRepository; + } + + public function setNode($id) + { + $this->node = $this->nodeRepository->find($id); + + return $this; + } + + public function getNode() + { + return $this->node; + } + + public function setAccessServer($server = null) + { + $this->accessServer = $server; + + return $this; + } + + public function getAccessServer() + { + return $this->accessServer; + } + + public function setAccessToken($token = null) + { + $this->accessToken = $token; + + return $this; + } + + public function getAccessToken() + { + return $this->accessToken; + } + + public function getHttpClient($headers = []) + { + if (! is_null($this->accessServer)) { + $headers[] = ['X-Access-Server' => $this->getAccessServer()]; + } + + if (! is_null($this->accessToken)) { + $headers[] = ['X-Access-Token' => $this->getAccessToken()]; + } + + return new Client([ + 'base_uri' => sprintf('%s://%s:%s/', $this->getNode()->scheme, $this->getNode()->fqdn, $this->getNode()->daemonListen), + 'timeout' => $this->config->get('pterodactyl.guzzle.timeout'), + 'connect_timeout' => $this->config->get('pterodactyl.guzzle.connect_timeout'), + 'headers' => $headers, + ]); + } +} diff --git a/app/Repositories/Daemon/ServerRepository.php b/app/Repositories/Daemon/ServerRepository.php new file mode 100644 index 000000000..2ee011833 --- /dev/null +++ b/app/Repositories/Daemon/ServerRepository.php @@ -0,0 +1,86 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories\Daemon; + +use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface; +use Pterodactyl\Contracts\Repository\ServerRepositoryInterface as DatabaseServerRepositoryInterface; +use Pterodactyl\Services\Servers\EnvironmentService; + +class ServerRepository extends BaseRepository implements ServerRepositoryInterface +{ + const DAEMON_PERMISSIONS = ['s:*']; + + /** + * {@inheritdoc} + */ + public function create($id, $overrides = [], $start = false) + { + $repository = $this->app->make(DatabaseServerRepositoryInterface::class); + $environment = $this->app->make(EnvironmentService::class); + + $server = $repository->getDataForCreation($id); + + $data = [ + 'uuid' => (string) $server->uuid, + 'user' => $server->username, + 'build' => [ + 'default' => [ + 'ip' => $server->allocation->ip, + 'port' => $server->allocation->port, + ], + 'ports' => $server->allocations->groupBy('ip')->map(function ($item) { + return $item->pluck('port'); + })->toArray(), + 'env' => $environment->process($server), + 'memory' => (int) $server->memory, + 'swap' => (int) $server->swap, + 'io' => (int) $server->io, + 'cpu' => (int) $server->cpu, + 'disk' => (int) $server->disk, + 'image' => (int) $server->image, + ], + 'service' => [ + 'type' => $server->option->service->folder, + 'option' => $server->option->tag, + 'pack' => object_get($server, 'pack.uuid'), + 'skip_scripts' => $server->skip_scripts, + ], + 'rebuild' => false, + 'start_on_completion' => $start, + 'keys' => [ + (string) $server->daemonSecret => self::DAEMON_PERMISSIONS, + ], + ]; + + // Loop through overrides. + foreach ($overrides as $key => $value) { + array_set($data, $key, $value); + } + +// $this->getHttpClient()->request('POST', '/servers', [ +// 'json' => $data, +// ]); + } +} diff --git a/app/Repositories/Eloquent/AllocationRepository.php b/app/Repositories/Eloquent/AllocationRepository.php new file mode 100644 index 000000000..21dc85ee4 --- /dev/null +++ b/app/Repositories/Eloquent/AllocationRepository.php @@ -0,0 +1,47 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories\Eloquent; + +use Pterodactyl\Models\Allocation; +use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; + +class AllocationRepository extends EloquentRepository implements AllocationRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function model() + { + return Allocation::class; + } + + /** + * {@inheritdoc} + */ + public function assignAllocationsToServer($server, array $ids) + { + return $this->getBuilder()->whereIn('id', $ids)->update(['server_id' => $server]); + } +} diff --git a/app/Repositories/Eloquent/EloquentRepository.php b/app/Repositories/Eloquent/EloquentRepository.php index 793291bb9..994647df9 100644 --- a/app/Repositories/Eloquent/EloquentRepository.php +++ b/app/Repositories/Eloquent/EloquentRepository.php @@ -160,4 +160,12 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf { return $this->getBuilder()->get($this->getColumns()); } + + /** + * {@inheritdoc} + */ + public function insert(array $data) + { + return $this->getBuilder()->insert($data); + } } diff --git a/app/Repositories/Eloquent/NodeRepository.php b/app/Repositories/Eloquent/NodeRepository.php new file mode 100644 index 000000000..cc2e5303f --- /dev/null +++ b/app/Repositories/Eloquent/NodeRepository.php @@ -0,0 +1,40 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories\Eloquent; + +use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; +use Pterodactyl\Models\Node; +use Pterodactyl\Repositories\Eloquent\Attributes\SearchableRepository; + +class NodeRepository extends SearchableRepository implements NodeRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function model() + { + return Node::class; + } +} diff --git a/app/Repositories/Eloquent/OptionVariableRepository.php b/app/Repositories/Eloquent/OptionVariableRepository.php new file mode 100644 index 000000000..45cc9110f --- /dev/null +++ b/app/Repositories/Eloquent/OptionVariableRepository.php @@ -0,0 +1,39 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories\Eloquent; + +use Pterodactyl\Models\ServiceVariable; +use Pterodactyl\Contracts\Repository\OptionVariableRepositoryInterface; + +class OptionVariableRepository extends EloquentRepository implements OptionVariableRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function model() + { + return ServiceVariable::class; + } +} diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index 4f605b64d..2221ceb05 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -24,6 +24,7 @@ namespace Pterodactyl\Repositories\Eloquent; +use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Models\Server; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Repositories\Eloquent\Attributes\SearchableRepository; @@ -51,4 +52,55 @@ class ServerRepository extends SearchableRepository implements ServerRepositoryI return $instance->paginate($paginate); } + + /** + * {@inheritdoc} + * @return \Illuminate\Database\Eloquent\Model + */ + public function findWithVariables($id) + { + $instance = $this->getBuilder()->with('option.variables', 'variables') + ->where($this->getModel()->getKeyName(), '=', $id) + ->first($this->getColumns()); + + if (is_null($instance)) { + throw new RecordNotFoundException(); + } + + return $instance; + } + + /** + * {@inheritdoc} + */ + public function getVariablesWithValues($id) + { + $instance = $this->getBuilder()->with('variables', 'option.variables') + ->find($id, $this->getColumns()); + + if (! $instance) { + throw new RecordNotFoundException(); + } + + $data = []; + $instance->option->variables->each(function ($item) use (&$data, $instance) { + $display = $instance->variables->where('variable_id', $item->id)->pluck('variable_value')->first(); + + $data[$item->env_variable] = $display ?? $item->default_value; + }); + + return $data; + } + + public function getDataForCreation($id) + { + $instance = $this->getBuilder()->with('allocation', 'allocations', 'pack', 'option.service') + ->find($id, $this->getColumns()); + + if (! $instance) { + throw new RecordNotFoundException(); + } + + return $instance; + } } diff --git a/app/Repositories/Eloquent/ServerVariableRepository.php b/app/Repositories/Eloquent/ServerVariableRepository.php new file mode 100644 index 000000000..e309b88d8 --- /dev/null +++ b/app/Repositories/Eloquent/ServerVariableRepository.php @@ -0,0 +1,39 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Repositories\Eloquent; + +use Pterodactyl\Models\ServerVariable; +use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; + +class ServerVariableRepository extends EloquentRepository implements ServerVariableRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function model() + { + return ServerVariable::class; + } +} diff --git a/app/Repositories/Daemon/CommandRepository.php b/app/Repositories/old_Daemon/CommandRepository.php similarity index 98% rename from app/Repositories/Daemon/CommandRepository.php rename to app/Repositories/old_Daemon/CommandRepository.php index beb9e8530..2f1a41ee8 100644 --- a/app/Repositories/Daemon/CommandRepository.php +++ b/app/Repositories/old_Daemon/CommandRepository.php @@ -22,7 +22,7 @@ * SOFTWARE. */ -namespace Pterodactyl\Repositories\Daemon; +namespace Pterodactyl\Repositories\old_Daemon; use Pterodactyl\Models\User; use Pterodactyl\Models\Server; diff --git a/app/Repositories/Daemon/FileRepository.php b/app/Repositories/old_Daemon/FileRepository.php similarity index 99% rename from app/Repositories/Daemon/FileRepository.php rename to app/Repositories/old_Daemon/FileRepository.php index e789f7469..8254c2273 100644 --- a/app/Repositories/Daemon/FileRepository.php +++ b/app/Repositories/old_Daemon/FileRepository.php @@ -22,7 +22,7 @@ * SOFTWARE. */ -namespace Pterodactyl\Repositories\Daemon; +namespace Pterodactyl\Repositories\old_Daemon; use Exception; use GuzzleHttp\Client; diff --git a/app/Repositories/Daemon/PowerRepository.php b/app/Repositories/old_Daemon/PowerRepository.php similarity index 98% rename from app/Repositories/Daemon/PowerRepository.php rename to app/Repositories/old_Daemon/PowerRepository.php index 925379096..9e0cbc29a 100644 --- a/app/Repositories/Daemon/PowerRepository.php +++ b/app/Repositories/old_Daemon/PowerRepository.php @@ -22,7 +22,7 @@ * SOFTWARE. */ -namespace Pterodactyl\Repositories\Daemon; +namespace Pterodactyl\Repositories\old_Daemon; use Pterodactyl\Models\User; use Pterodactyl\Models\Server; diff --git a/app/Services/Components/UuidService.php b/app/Services/Components/UuidService.php index 468a97f89..27d7e541f 100644 --- a/app/Services/Components/UuidService.php +++ b/app/Services/Components/UuidService.php @@ -37,6 +37,7 @@ class UuidService * @param string $field * @param int $type * @return string + * @deprecated */ public function generate($table = 'users', $field = 'uuid', $type = 4) { @@ -58,6 +59,7 @@ class UuidService * @param string $field * @param null|string $attachedUuid * @return string + * @deprecated */ public function generateShort($table = 'servers', $field = 'uuidShort', $attachedUuid = null) { diff --git a/app/Services/Servers/EnvironmentService.php b/app/Services/Servers/EnvironmentService.php new file mode 100644 index 000000000..0bdb5131d --- /dev/null +++ b/app/Services/Servers/EnvironmentService.php @@ -0,0 +1,106 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Services\Servers; + +use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Models\Server; + +class EnvironmentService +{ + const ENVIRONMENT_CASTS = [ + 'STARTUP' => 'startup', + 'P_SERVER_LOCATION' => 'location.short', + 'P_SERVER_UUID' => 'uuid', + ]; + + /** + * @var array + */ + protected $additional = []; + + /** + * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface + */ + protected $repository; + + /** + * EnvironmentService constructor. + * + * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository + */ + public function __construct(ServerRepositoryInterface $repository) + { + $this->repository = $repository; + } + + /** + * Dynamically configure additional environment variables to be assigned + * with a specific server. + * + * @param string $key + * @param callable $closure + * @return $this + */ + public function setEnvironmentKey($key, callable $closure) + { + $this->additional[] = [$key, $closure]; + + return $this; + } + + /** + * Take all of the environment variables configured for this server and return + * them in an easy to process format. + * + * @param int|\Pterodactyl\Models\Server $server + * @return array + */ + public function process($server) + { + if (! $server instanceof Server) { + if (! is_numeric($server)) { + throw new \InvalidArgumentException( + 'First argument passed to process() must be an instance of \\Pterodactyl\\Models\\Server or numeric.' + ); + } + + $server = $this->repository->find($server); + } + + $variables = $this->repository->getVariablesWithValues($server->id); + + // Process static environment variables defined in this file. + foreach (self::ENVIRONMENT_CASTS as $key => $object) { + $variables[$key] = object_get($server, $object); + } + + // Process dynamically included environment variables. + foreach ($this->additional as $item) { + $variables[$item[0]] = call_user_func($item[1], $server); + } + + return $variables; + } +} diff --git a/app/Services/Servers/ServerService.php b/app/Services/Servers/ServerService.php new file mode 100644 index 000000000..dd7ba9131 --- /dev/null +++ b/app/Services/Servers/ServerService.php @@ -0,0 +1,149 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Services\Servers; + +use Ramsey\Uuid\Uuid; +use Illuminate\Database\ConnectionInterface; +use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; +use Pterodactyl\Contracts\Repository\UserRepositoryInterface; +use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; +use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; +use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; + +class ServerService +{ + /** + * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface + */ + protected $allocationRepository; + + /** + * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface + */ + protected $nodeRepository; + + /** + * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface + */ + protected $userRepository; + + protected $database; + protected $repository; + protected $usernameService; + protected $serverVariableRepository; + protected $daemonServerRepository; + + /** + * @var \Pterodactyl\Services\Servers\VariableValidatorService + */ + protected $validatorService; + + public function __construct( + AllocationRepositoryInterface $allocationRepository, + ConnectionInterface $database, + ServerRepositoryInterface $repository, + DaemonServerRepositoryInterface $daemonServerRepository, + ServerVariableRepositoryInterface $serverVariableRepository, + NodeRepositoryInterface $nodeRepository, + UsernameGenerationService $usernameService, + UserRepositoryInterface $userRepository, + VariableValidatorService $validatorService + ) { + $this->allocationRepository = $allocationRepository; + $this->database = $database; + $this->repository = $repository; + $this->nodeRepository = $nodeRepository; + $this->userRepository = $userRepository; + $this->usernameService = $usernameService; + $this->validatorService = $validatorService; + $this->serverVariableRepository = $serverVariableRepository; + $this->daemonServerRepository = $daemonServerRepository; + } + + public function create(array $data) + { + // @todo auto-deployment and packs + $data['user_id'] = 1; + + $node = $this->nodeRepository->find($data['node_id']); + $validator = $this->validatorService->setAdmin()->setFields($data['environment'])->validate($data['option_id']); + $uniqueShort = bin2hex(random_bytes(4)); + + $this->database->beginTransaction(); + + $server = $this->repository->create([ + 'uuid' => Uuid::uuid4()->toString(), + 'uuidShort' => bin2hex(random_bytes(4)), + 'node_id' => $data['node_id'], + 'name' => $data['name'], + 'description' => $data['description'], + 'skip_scripts' => isset($data['skip_scripts']), + 'suspended' => false, + 'owner_id' => $data['user_id'], + 'memory' => $data['memory'], + 'swap' => $data['swap'], + 'disk' => $data['disk'], + 'io' => $data['io'], + 'cpu' => $data['cpu'], + 'oom_disabled' => isset($data['oom_disabled']), + 'allocation_id' => $data['allocation_id'], + 'service_id' => $data['service_id'], + 'option_id' => $data['option_id'], + 'pack_id' => ($data['pack_id'] == 0) ? null : $data['pack_id'], + 'startup' => $data['startup'], + 'daemonSecret' => bin2hex(random_bytes(18)), + 'image' => $data['docker_image'], + 'username' => $this->usernameService->generate($data['name'], $uniqueShort), + 'sftp_password' => null, + ]); + + // Process allocations and assign them to the server in the database. + $records = [$data['allocation_id']]; + if (isset($data['allocation_additional']) && is_array($data['allocation_additional'])) { + $records = array_merge($records, $data['allocation_additional']); + } + + $this->allocationRepository->assignAllocationsToServer($server->id, $records); + + // Process the passed variables and store them in the database. + $records = []; + foreach ($validator->getResults() as $result) { + $records[] = [ + 'server_id' => $server->id, + 'variable_id' => $result['id'], + 'variable_value' => $result['value'], + ]; + } + + $this->serverVariableRepository->insert($records); + + // Create the server on the daemon & commit it to the database. + $this->daemonServerRepository->setNode($server->node_id)->setAccessToken($node->daemonSecret)->create($server->id); + $this->database->rollBack(); + + return $server; + } +} diff --git a/app/Services/Servers/UsernameGenerationService.php b/app/Services/Servers/UsernameGenerationService.php new file mode 100644 index 000000000..10e3382f7 --- /dev/null +++ b/app/Services/Servers/UsernameGenerationService.php @@ -0,0 +1,55 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Services\Servers; + +class UsernameGenerationService +{ + /** + * Generate a unique username to be used for SFTP connections and identification + * of the server docker container on the host system. + * + * @param string $name + * @param null $identifier + * @return string + */ + public function generate($name, $identifier = null) + { + if (is_null($identifier) || ! ctype_alnum($identifier)) { + $unique = bin2hex(random_bytes(4)); + } else { + if (strlen($identifier) < 8) { + $unique = $identifier . str_random((8 - strlen($identifier))); + } else { + $unique = substr($identifier, 0, 8); + } + } + + // Filter the Server Name + $name = trim(preg_replace('/[^\w]+/', '', $name), '_'); + $name = (strlen($name) < 1) ? str_random(6) : $name; + + return strtolower(substr($name, 0, 6) . '_' . $unique); + } +} diff --git a/app/Services/Servers/VariableValidatorService.php b/app/Services/Servers/VariableValidatorService.php new file mode 100644 index 000000000..9337f38ca --- /dev/null +++ b/app/Services/Servers/VariableValidatorService.php @@ -0,0 +1,173 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Services\Servers; + +use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; +use Pterodactyl\Exceptions\DisplayValidationException; +use Illuminate\Validation\Factory as ValidationFactory; +use Pterodactyl\Contracts\Repository\OptionVariableRepositoryInterface; +use Pterodactyl\Exceptions\Services\Servers\RequiredVariableMissingException; + +class VariableValidatorService +{ + /** + * @var bool + */ + protected $isAdmin = false; + + /** + * @var array + */ + protected $fields = []; + + /** + * @var array + */ + protected $results = []; + + /** + * @var \Pterodactyl\Contracts\Repository\OptionVariableRepositoryInterface + */ + protected $optionVariableRepository; + + /** + * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface + */ + protected $serverRepository; + + /** + * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface + */ + protected $serverVariableRepository; + + /** + * @var \Illuminate\Validation\Factory + */ + protected $validator; + + public function __construct( + OptionVariableRepositoryInterface $optionVariableRepository, + ServerRepositoryInterface $serverRepository, + ServerVariableRepositoryInterface $serverVariableRepository, + ValidationFactory $validator + ) { + $this->optionVariableRepository = $optionVariableRepository; + $this->serverRepository = $serverRepository; + $this->serverVariableRepository = $serverVariableRepository; + $this->validator = $validator; + } + + /** + * Set the fields with populated data to validate. + * + * @param array $fields + * @return $this + */ + public function setFields(array $fields) + { + $this->fields = $fields; + + return $this; + } + + /** + * Set this function to be running at the administrative level. + * + * @return $this + */ + public function setAdmin() + { + $this->isAdmin = true; + + return $this; + } + + /** + * Validate all of the passed data aganist the given service option variables. + * + * @param int $option + * @return $this + */ + public function validate($option) + { + $variables = $this->optionVariableRepository->findWhere([['option_id', '=', $option]]); + if (count($variables) === 0) { + $this->results = []; + + return $this; + } + + $variables->each(function ($item) { + if (! isset($this->fields[$item->env_variable]) && $item->required) { + if ($item->required) { + throw new RequiredVariableMissingException( + sprintf('Required service option variable %s was missing from this request.', $item->env_variable) + ); + } + } + + // Skip doing anything if user is not an admin and variable is not user viewable + // or editable. + if (! $this->isAdmin && (! $item->user_editable || ! $item->user_viewable)) { + return; + } + + $validator = $this->validator->make([ + 'variable_value' => array_key_exists($item->env_variable, $this->fields) ? $this->fields[$item->env_variable] : null, + ], [ + 'variable_value' => $item->rules, + ]); + + if ($validator->fails()) { + throw new DisplayValidationException(json_encode( + collect([ + 'notice' => [ + sprintf('There was a validation error with the %s variable.', $item->name), + ], + ])->merge($validator->errors()->toArray()) + )); + } + + $this->results[] = [ + 'id' => $item->id, + 'key' => $item->env_variable, + 'value' => $this->fields[$item->env_variable], + ]; + }); + + return $this; + } + + /** + * Return the final results after everything has been validated. + * + * @return array + */ + public function getResults() + { + return $this->results; + } +} diff --git a/public/themes/pterodactyl/js/admin/new-server.js b/public/themes/pterodactyl/js/admin/new-server.js index f3de55bee..ecc0b9fb7 100644 --- a/public/themes/pterodactyl/js/admin/new-server.js +++ b/public/themes/pterodactyl/js/admin/new-server.js @@ -179,7 +179,7 @@ $('#pOptionId').on('change', function (event) { var dataAppend = ' \
\ \ - \ + \

' + item.description + '
\ Access in Startup: {{' + item.env_variable + '}}
\ Validation Rules: ' + item.rules + '

\ diff --git a/resources/themes/pterodactyl/admin/servers/new.blade.php b/resources/themes/pterodactyl/admin/servers/new.blade.php index eaf20445f..8a68b197f 100644 --- a/resources/themes/pterodactyl/admin/servers/new.blade.php +++ b/resources/themes/pterodactyl/admin/servers/new.blade.php @@ -83,7 +83,7 @@ @foreach($locations as $location) @endforeach @@ -229,15 +229,10 @@
- - + +

This is the default Docker container that will be used to run this server.

-
- - -

If you would like to use a custom Docker container please enter it here, otherwise leave empty.

-