diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 1805a831a3..2011d19485 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -66,12 +66,30 @@ public function __construct( * * @throws DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws ValidationException */ public function setDetails(Request $request, Server $server): RedirectResponse { - $this->detailsModificationService->handle($server, $request->only([ - 'owner_id', 'external_id', 'name', 'description', - ])); + if ($request->input('external_id')) { + $externalId = $request->input('external_id'); + $exists = Server::where('external_id', $externalId) + ->where('id', '!=', $server->id) + ->exists(); + + if ($exists) { + throw ValidationException::withMessages([ + 'external_id' => 'The external identifier must be unique among all servers.', + ]); + } + } + + try { + $this->detailsModificationService->handle($server, $request->only([ + 'owner_id', 'external_id', 'name', 'description', + ])); + } catch (DataValidationException $exception) { + throw new ValidationException($exception->getValidator()); + } $this->alert->success(trans('admin/server.alerts.details_updated'))->flash(); diff --git a/app/Http/Requests/Admin/ServerFormRequest.php b/app/Http/Requests/Admin/ServerFormRequest.php index bf461dc956..08e5d56b15 100644 --- a/app/Http/Requests/Admin/ServerFormRequest.php +++ b/app/Http/Requests/Admin/ServerFormRequest.php @@ -53,6 +53,21 @@ public function withValidator(Validator $validator): void ], function ($input) { return !$input->auto_deploy; }); + + if ($this->input('external_id')) { + $server = $this->route('server'); + $externalId = $this->input('external_id'); + + $query = Server::where('external_id', $externalId); + + if ($server) { + $query->where('id', '!=', $server->id); + } + + if ($query->exists()) { + $validator->errors()->add('external_id', 'The external identifier must be unique among all servers.'); + } + } }); } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 0350fdf3f1..effa8f6b97 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Pterodactyl\Exceptions\Http\Server\ServerStateConflictException; +use Illuminate\Validation\Rule; /** * \Pterodactyl\Models\Server. @@ -217,6 +218,35 @@ public function isSuspended(): bool return $this->status === self::STATUS_SUSPENDED; } + public static function getRulesForUpdate($model, string $column = 'id'): array + { + if ($model instanceof Model) { + [$id, $column] = [$model->getKey(), $model->getKeyName()]; + } + + $rules = static::getRules(); + foreach ($rules as $key => &$data) { + foreach ($data as &$datum) { + if (!is_string($datum) || !str_starts_with($datum, 'unique')) { + continue; + } + + [, $args] = explode(':', $datum); + $args = explode(',', $args); + + if ($key === 'external_id') { + $datum = Rule::unique($args[0], $args[1] ?? $key) + ->ignore($id ?? $model, $column) + ->whereNotNull('external_id'); + } else { + $datum = Rule::unique($args[0], $args[1] ?? $key)->ignore($id ?? $model, $column); + } + } + } + + return $rules; + } + /** * Gets the user who owns the server. *