From e3278277b7bee90dd4e6f84861a5850d16f9b853 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 17:11:59 -0500 Subject: [PATCH 01/13] Add php 8.4 --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2cbc4feb17..9ea22983b3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.2, 8.3] + php: [8.2, 8.3, 8.4] database: ["mysql:8"] services: database: From 553b6a176c98d261fc66aa9092b4f648e30f1884 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 19:44:18 -0500 Subject: [PATCH 02/13] Update ide helper --- composer.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index 54f2ef37f7..a4acae1916 100644 --- a/composer.lock +++ b/composer.lock @@ -10873,16 +10873,16 @@ "packages-dev": [ { "name": "barryvdh/laravel-ide-helper", - "version": "v3.2.2", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "07e3bd8796f3d1414801a03d3783f9d3ec9efc08" + "reference": "b7675670f75914bf34afdea52a6c2fe3781f7c44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/07e3bd8796f3d1414801a03d3783f9d3ec9efc08", - "reference": "07e3bd8796f3d1414801a03d3783f9d3ec9efc08", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/b7675670f75914bf34afdea52a6c2fe3781f7c44", + "reference": "b7675670f75914bf34afdea52a6c2fe3781f7c44", "shasum": "" }, "require": { @@ -10913,13 +10913,13 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - }, "laravel": { "providers": [ "Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider" ] + }, + "branch-alias": { + "dev-master": "3.2-dev" } }, "autoload": { @@ -10951,7 +10951,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", - "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.2.2" + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.3.0" }, "funding": [ { @@ -10963,7 +10963,7 @@ "type": "github" } ], - "time": "2024-10-29T14:00:16+00:00" + "time": "2024-12-18T08:24:19+00:00" }, { "name": "barryvdh/reflection-docblock", From 297f5e1382916db60d9daa23929ea149da5621ea Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 19:45:23 -0500 Subject: [PATCH 03/13] Add php 8.4 --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9ea22983b3..00f5b28a68 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -86,7 +86,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.2, 8.3] + php: [8.2, 8.3, 8.4] database: ["mariadb:10.6", "mariadb:10.11", "mariadb:11.4"] services: database: @@ -159,7 +159,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.2, 8.3] + php: [8.2, 8.3, 8.4] env: APP_ENV: testing APP_DEBUG: "false" From a4d6813d2ed7635c8b0638705e0c88ef68a27c3c Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 19:46:23 -0500 Subject: [PATCH 04/13] Update laravel sanctum --- composer.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index a4acae1916..65a99b1e8e 100644 --- a/composer.lock +++ b/composer.lock @@ -3278,16 +3278,16 @@ }, { "name": "laravel/sanctum", - "version": "v4.0.3", + "version": "v4.0.7", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "54aea9d13743ae8a6cdd3c28dbef128a17adecab" + "reference": "698064236a46df016e64a7eb059b1414e0b281df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/54aea9d13743ae8a6cdd3c28dbef128a17adecab", - "reference": "54aea9d13743ae8a6cdd3c28dbef128a17adecab", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/698064236a46df016e64a7eb059b1414e0b281df", + "reference": "698064236a46df016e64a7eb059b1414e0b281df", "shasum": "" }, "require": { @@ -3338,7 +3338,7 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2024-09-27T14:55:41+00:00" + "time": "2024-12-11T16:40:21+00:00" }, { "name": "laravel/serializable-closure", From 2afeb9170f89c87543e03497268c812ebf7244b8 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 20:01:42 -0500 Subject: [PATCH 05/13] Update laravel framework --- composer.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index 65a99b1e8e..ff0f8dca1e 100644 --- a/composer.lock +++ b/composer.lock @@ -2953,16 +2953,16 @@ }, { "name": "laravel/framework", - "version": "v11.31.0", + "version": "v11.33.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "365090ed2c68244e3141cdb5e247cdf3dfba2c40" + "reference": "6b9832751cf8eed18b3c73df5071f78f0682aa5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/365090ed2c68244e3141cdb5e247cdf3dfba2c40", - "reference": "365090ed2c68244e3141cdb5e247cdf3dfba2c40", + "url": "https://api.github.com/repos/laravel/framework/zipball/6b9832751cf8eed18b3c73df5071f78f0682aa5d", + "reference": "6b9832751cf8eed18b3c73df5071f78f0682aa5d", "shasum": "" }, "require": { @@ -2982,7 +2982,7 @@ "guzzlehttp/guzzle": "^7.8", "guzzlehttp/uri-template": "^1.0", "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", - "laravel/serializable-closure": "^1.3", + "laravel/serializable-closure": "^1.3|^2.0", "league/commonmark": "^2.2.1", "league/flysystem": "^3.8.0", "monolog/monolog": "^3.0", @@ -3065,9 +3065,9 @@ "league/flysystem-path-prefixing": "^3.3", "league/flysystem-read-only": "^3.3", "league/flysystem-sftp-v3": "^3.0", - "mockery/mockery": "^1.6", + "mockery/mockery": "^1.6.10", "nyholm/psr7": "^1.2", - "orchestra/testbench-core": "^9.5", + "orchestra/testbench-core": "^9.6", "pda/pheanstalk": "^5.0", "phpstan/phpstan": "^1.11.5", "phpunit/phpunit": "^10.5|^11.0", @@ -3158,7 +3158,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-11-12T15:36:15+00:00" + "time": "2024-11-19T22:47:13+00:00" }, { "name": "laravel/helpers", From d72f7fdf47909e1f9078224375c5e291e08d34c6 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 21:15:13 -0500 Subject: [PATCH 06/13] Hash rounds were increased --- tests/Integration/Api/Client/TwoFactorControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Api/Client/TwoFactorControllerTest.php b/tests/Integration/Api/Client/TwoFactorControllerTest.php index 24d8a26843..2b2c38c998 100644 --- a/tests/Integration/Api/Client/TwoFactorControllerTest.php +++ b/tests/Integration/Api/Client/TwoFactorControllerTest.php @@ -100,7 +100,7 @@ public function testTwoFactorCanBeEnabledOnAccount(): void $tokens = RecoveryToken::query()->where('user_id', $user->id)->get(); $this->assertCount(10, $tokens); - $this->assertStringStartsWith('$2y$10$', $tokens[0]->token); + $this->assertStringStartsWith('$2y$', $tokens[0]->token); // Ensure the recovery tokens that were created include a "created_at" timestamp value on them. $this->assertNotNull($tokens[0]->created_at); From b210693c9ed169c6199e9f6f1c28e1d1f3bc2946 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 22:31:53 -0500 Subject: [PATCH 07/13] This is always false --- app/Http/Requests/Api/Application/ApplicationApiRequest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Http/Requests/Api/Application/ApplicationApiRequest.php b/app/Http/Requests/Api/Application/ApplicationApiRequest.php index b8feb094dd..6344e2b178 100644 --- a/app/Http/Requests/Api/Application/ApplicationApiRequest.php +++ b/app/Http/Requests/Api/Application/ApplicationApiRequest.php @@ -38,9 +38,6 @@ public function authorize(): bool } $token = $this->user()->currentAccessToken(); - if ($token instanceof TransientToken) { - return true; - } /** @var ApiKey $token */ if ($token->key_type === ApiKey::TYPE_ACCOUNT) { From 8519d79d6546530674f745b6dc17c92c549a83d0 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 22:34:19 -0500 Subject: [PATCH 08/13] Extend model now --- app/Models/ApiKey.php | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 299ff7d374..44ae63ea68 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -4,6 +4,7 @@ use App\Services\Acl\Api\AdminAcl; use Illuminate\Support\Str; +use Laravel\Sanctum\PersonalAccessToken; use Webmozart\Assert\Assert; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -47,7 +48,7 @@ * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUserId($value) */ -class ApiKey extends Model +class ApiKey extends PersonalAccessToken { /** * The resource name for this model when it is transformed into an @@ -148,16 +149,6 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } - /** - * Required for support with Laravel Sanctum. - * - * @see \Laravel\Sanctum\Guard::supportsTokens() - */ - public function tokenable(): BelongsTo - { - return $this->user(); - } - /** * Returns the permission for the given resource. */ @@ -195,11 +186,14 @@ public static function getPermissionList(): array /** * Finds the model matching the provided token. + * + * @param string $token */ - public static function findToken(string $token): ?self + public static function findToken($token): ?self { $identifier = substr($token, 0, self::IDENTIFIER_LENGTH); + /** @var static|null $model */ $model = static::where('identifier', $identifier)->first(); if (!is_null($model) && $model->token === substr($token, strlen($identifier))) { return $model; From 5859ea0ce41a4867936e43d8d7395a251ce90d71 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 22:34:27 -0500 Subject: [PATCH 09/13] This does nothing --- app/Models/ApiKey.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 44ae63ea68..66f515a730 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -171,11 +171,6 @@ public function getPermission(string $resource): int private static array $customResourceNames = []; - public static function registerCustomResourceName(string $resourceName): void - { - $customResourceNames[] = $resourceName; - } - /** * Returns a list of all possible permission keys. */ From ae404bccb20391d48038f31e416e569f680de147 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Fri, 3 Jan 2025 22:34:50 -0500 Subject: [PATCH 10/13] Move model validation methods to trait --- app/Models/ApiKey.php | 3 + app/Models/Model.php | 131 +----------------------------------- app/Traits/Validation.php | 137 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 129 deletions(-) create mode 100644 app/Traits/Validation.php diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 66f515a730..98e82fc853 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Services\Acl\Api\AdminAcl; +use App\Traits\Validation; use Illuminate\Support\Str; use Laravel\Sanctum\PersonalAccessToken; use Webmozart\Assert\Assert; @@ -50,6 +51,8 @@ */ class ApiKey extends PersonalAccessToken { + use Validation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. diff --git a/app/Models/Model.php b/app/Models/Model.php index ca36fbefec..f2f92649b8 100644 --- a/app/Models/Model.php +++ b/app/Models/Model.php @@ -2,53 +2,14 @@ namespace App\Models; -use Illuminate\Support\Arr; -use Illuminate\Support\Str; -use Illuminate\Validation\Rule; -use Illuminate\Container\Container; -use Illuminate\Validation\ValidationException; +use App\Traits\Validation; use Illuminate\Database\Eloquent\Factories\HasFactory; -use App\Exceptions\Model\DataValidationException; use Illuminate\Database\Eloquent\Model as IlluminateModel; -use Illuminate\Validation\Factory as ValidationFactory; -use Illuminate\Validation\Validator; abstract class Model extends IlluminateModel { use HasFactory; - - /** - * Determines if the model should undergo data validation before it is saved - * to the database. - */ - protected bool $skipValidation = false; - - protected static ValidationFactory $validatorFactory; - - public static array $validationRules = []; - - /** - * Listen for the model saving event and fire off the validation - * function before it is saved. - * - * @throws \Illuminate\Contracts\Container\BindingResolutionException - */ - protected static function boot(): void - { - parent::boot(); - - static::$validatorFactory = Container::getInstance()->make(ValidationFactory::class); - - static::saving(function (Model $model) { - try { - $model->validate(); - } catch (ValidationException $exception) { - throw new DataValidationException($exception->validator, $model); - } - - return true; - }); - } + use Validation; /** * Returns the model key to use for route model binding. By default, we'll @@ -63,92 +24,4 @@ public function getRouteKeyName(): string { return 'uuid'; } - - /** - * Returns the validator instance used by this model. - */ - public function getValidator(): Validator - { - $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); - - return static::$validatorFactory->make([], $rules); - } - - /** - * Returns the rules associated with this model. - */ - public static function getRules(): array - { - $rules = static::$validationRules; - foreach ($rules as &$rule) { - $rule = is_array($rule) ? $rule : explode('|', $rule); - } - - return $rules; - } - - /** - * Returns the rules for a specific field. If the field is not found an empty - * array is returned. - */ - public static function getRulesForField(string $field): array - { - return Arr::get(static::getRules(), $field) ?? []; - } - - /** - * Returns the rules associated with the model, specifically for updating the given model - * rather than just creating it. - */ - public static function getRulesForUpdate(self $model): array - { - [$id, $column] = [$model->getKey(), $model->getKeyName()]; - - $rules = static::getRules(); - foreach ($rules as $key => &$data) { - // For each rule in a given field, iterate over it and confirm if the rule - // is one for a unique field. If that is the case, append the ID of the current - // working model, so we don't run into errors due to the way that field validation - // works. - foreach ($data as &$datum) { - if (!is_string($datum) || !Str::startsWith($datum, 'unique')) { - continue; - } - - [, $args] = explode(':', $datum); - $args = explode(',', $args); - - $datum = Rule::unique($args[0], $args[1] ?? $key)->ignore($id ?? $model, $column); - } - } - - return $rules; - } - - /** - * Determines if the model is in a valid state or not. - * - * @throws \Illuminate\Validation\ValidationException - */ - public function validate(): void - { - if ($this->skipValidation) { - return; - } - - $validator = $this->getValidator(); - $validator->setData( - // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist - // for that model. Doing this will return all the attributes in a format that can - // properly be validated. - $this->addCastAttributesToArray( - $this->getAttributes(), - $this->getMutatedAttributes() - ) - ); - - if (!$validator->passes()) { - throw new ValidationException($validator); - } - } } diff --git a/app/Traits/Validation.php b/app/Traits/Validation.php new file mode 100644 index 0000000000..221cb9f19f --- /dev/null +++ b/app/Traits/Validation.php @@ -0,0 +1,137 @@ +make(ValidationFactory::class); + + static::saving(function (Model $model) { + try { + $model->validate(); + } catch (ValidationException $exception) { + throw new DataValidationException($exception->validator, $model); + } + + return true; + }); + } + + /** + * Returns the validator instance used by this model. + */ + public function getValidator(): Validator + { + $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); + + return static::$validatorFactory->make([], $rules); + } + + /** + * Returns the rules associated with this model. + */ + public static function getRules(): array + { + $rules = static::$validationRules; + foreach ($rules as &$rule) { + $rule = is_array($rule) ? $rule : explode('|', $rule); + } + + return $rules; + } + + /** + * Returns the rules for a specific field. If the field is not found an empty + * array is returned. + */ + public static function getRulesForField(string $field): array + { + return Arr::get(static::getRules(), $field) ?? []; + } + + /** + * Returns the rules associated with the model, specifically for updating the given model + * rather than just creating it. + */ + public static function getRulesForUpdate(self $model): array + { + [$id, $column] = [$model->getKey(), $model->getKeyName()]; + + $rules = static::getRules(); + foreach ($rules as $key => &$data) { + // For each rule in a given field, iterate over it and confirm if the rule + // is one for a unique field. If that is the case, append the ID of the current + // working model, so we don't run into errors due to the way that field validation + // works. + foreach ($data as &$datum) { + if (!is_string($datum) || !Str::startsWith($datum, 'unique')) { + continue; + } + + [, $args] = explode(':', $datum); + $args = explode(',', $args); + + $datum = Rule::unique($args[0], $args[1] ?? $key)->ignore($id ?? $model, $column); + } + } + + return $rules; + } + + /** + * Determines if the model is in a valid state or not. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function validate(): void + { + if ($this->skipValidation) { + return; + } + + $validator = $this->getValidator(); + $validator->setData( + // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist + // for that model. Doing this will return all the attributes in a format that can + // properly be validated. + $this->addCastAttributesToArray( + $this->getAttributes(), + $this->getMutatedAttributes() + ) + ); + + if (!$validator->passes()) { + throw new ValidationException($validator); + } + } +} From 881c333d01ad684a282bf6d8c0276e13e833edce Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Sat, 4 Jan 2025 16:41:57 -0500 Subject: [PATCH 11/13] Remove base model --- app/Contracts/Validatable.php | 16 +++++ .../Api/Application/ApplicationApiRequest.php | 1 - app/Models/ActivityLog.php | 8 ++- app/Models/Allocation.php | 16 ++--- app/Models/ApiKey.php | 19 +++--- app/Models/AuditLog.php | 11 ++- app/Models/Backup.php | 10 ++- app/Models/Database.php | 19 +++--- app/Models/DatabaseHost.php | 19 +++--- app/Models/Egg.php | 19 +++--- app/Models/EggVariable.php | 14 ++-- app/Models/File.php | 1 + app/Models/Model.php | 27 -------- app/Models/Mount.php | 19 ++---- app/Models/Node.php | 18 ++--- app/Models/Permission.php | 15 ++--- app/Models/RecoveryToken.php | 7 +- app/Models/Schedule.php | 19 +++--- app/Models/Server.php | 18 ++--- app/Models/ServerTransfer.php | 12 ++-- app/Models/ServerVariable.php | 9 ++- app/Models/Subuser.php | 13 ++-- app/Models/Task.php | 19 +++--- app/Models/User.php | 37 ++++------ app/Models/UserSSHKey.php | 5 ++ app/Observers/ValidationObserver.php | 20 ++++++ .../{Validation.php => HasValidation.php} | 67 +++++++------------ 27 files changed, 218 insertions(+), 240 deletions(-) create mode 100644 app/Contracts/Validatable.php delete mode 100644 app/Models/Model.php create mode 100644 app/Observers/ValidationObserver.php rename app/Traits/{Validation.php => HasValidation.php} (66%) diff --git a/app/Contracts/Validatable.php b/app/Contracts/Validatable.php new file mode 100644 index 0000000000..dedf3cbc65 --- /dev/null +++ b/app/Contracts/Validatable.php @@ -0,0 +1,16 @@ +whereMorphedTo('actor', $actor); } diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index b8bbeb571a..702e8b5b81 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -3,7 +3,10 @@ namespace App\Models; use App\Exceptions\Service\Allocation\ServerUsingAllocationException; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; /** @@ -40,17 +43,15 @@ */ class Allocation extends Model { + use HasFactory; + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'allocation'; - /** - * The table associated with the model. - */ - protected $table = 'allocations'; - /** * Fields that are not mass assignable. */ @@ -81,11 +82,6 @@ protected function casts(): array ]; } - public function getRouteKeyName(): string - { - return $this->getKeyName(); - } - /** * Accessor to automatically provide the IP alias if defined. */ diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 98e82fc853..74dab586c4 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -3,7 +3,8 @@ namespace App\Models; use App\Services\Acl\Api\AdminAcl; -use App\Traits\Validation; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Support\Str; use Laravel\Sanctum\PersonalAccessToken; use Webmozart\Assert\Assert; @@ -51,7 +52,8 @@ */ class ApiKey extends PersonalAccessToken { - use Validation; + use HasFactory; + use HasValidation; /** * The resource name for this model when it is transformed into an @@ -79,11 +81,6 @@ class ApiKey extends PersonalAccessToken */ public const KEY_LENGTH = 32; - /** - * The table associated with the model. - */ - protected $table = 'api_keys'; - /** * Fields that are mass assignable. */ @@ -152,6 +149,12 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } + public function tokenable() + { + // @phpstan-ignore-next-line + return $this->user(); + } + /** * Returns the permission for the given resource. */ @@ -185,7 +188,7 @@ public static function getPermissionList(): array /** * Finds the model matching the provided token. * - * @param string $token + * @param string $token */ public static function findToken($token): ?self { diff --git a/app/Models/AuditLog.php b/app/Models/AuditLog.php index d091773eae..6a06743947 100644 --- a/app/Models/AuditLog.php +++ b/app/Models/AuditLog.php @@ -2,6 +2,10 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Ramsey\Uuid\Uuid; use Illuminate\Http\Request; use Illuminate\Container\Container; @@ -10,8 +14,11 @@ /** * @deprecated — this class will be dropped in a future version, use the activity log */ -class AuditLog extends Model +class AuditLog extends Model implements Validatable { + use HasFactory; + use HasValidation; + public const UPDATED_AT = null; public static array $validationRules = [ @@ -24,8 +31,6 @@ class AuditLog extends Model 'metadata' => 'array', ]; - protected $table = 'audit_logs'; - protected $guarded = [ 'id', 'created_at', diff --git a/app/Models/Backup.php b/app/Models/Backup.php index d6e4be65a2..63ff9be2b6 100644 --- a/app/Models/Backup.php +++ b/app/Models/Backup.php @@ -2,7 +2,11 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -25,8 +29,10 @@ * @property \App\Models\Server $server * @property \App\Models\AuditLog[] $audits */ -class Backup extends Model +class Backup extends Model implements Validatable { + use HasFactory; + use HasValidation; use SoftDeletes; public const RESOURCE_NAME = 'backup'; @@ -35,8 +41,6 @@ class Backup extends Model public const ADAPTER_AWS_S3 = 's3'; - protected $table = 'backups'; - protected $attributes = [ 'is_successful' => false, 'is_locked' => false, diff --git a/app/Models/Database.php b/app/Models/Database.php index 795fa28aff..b7aaac0b74 100644 --- a/app/Models/Database.php +++ b/app/Models/Database.php @@ -2,7 +2,11 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\DB; @@ -21,8 +25,11 @@ * @property \App\Models\Server $server * @property \App\Models\DatabaseHost $host */ -class Database extends Model +class Database extends Model implements Validatable { + use HasFactory; + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. Also used as name for api key permissions. @@ -31,11 +38,6 @@ class Database extends Model public const DEFAULT_CONNECTION_NAME = 'dynamic'; - /** - * The table associated with the model. - */ - protected $table = 'databases'; - /** * The attributes excluded from the model's JSON form. */ @@ -68,11 +70,6 @@ protected function casts(): array ]; } - public function getRouteKeyName(): string - { - return $this->getKeyName(); - } - /** * Gets the host database server associated with a database. */ diff --git a/app/Models/DatabaseHost.php b/app/Models/DatabaseHost.php index 1163959d25..e76192cffc 100644 --- a/app/Models/DatabaseHost.php +++ b/app/Models/DatabaseHost.php @@ -2,6 +2,10 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -21,19 +25,17 @@ * @property \Illuminate\Database\Eloquent\Collection|\App\Models\Database[] $databases * @property int|null $databases_count */ -class DatabaseHost extends Model +class DatabaseHost extends Model implements Validatable { + use HasFactory; + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'database_host'; - /** - * The table associated with the model. - */ - protected $table = 'database_hosts'; - /** * The attributes excluded from the model's JSON form. */ @@ -70,11 +72,6 @@ protected function casts(): array ]; } - public function getRouteKeyName(): string - { - return 'id'; - } - public function nodes(): BelongsToMany { return $this->belongsToMany(Node::class); diff --git a/app/Models/Egg.php b/app/Models/Egg.php index 5ebc165754..1862d80667 100644 --- a/app/Models/Egg.php +++ b/app/Models/Egg.php @@ -2,8 +2,12 @@ namespace App\Models; +use App\Contracts\Validatable; use App\Exceptions\Service\Egg\HasChildrenException; use App\Exceptions\Service\HasActiveServersException; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Str; @@ -49,8 +53,11 @@ * @property \App\Models\Egg|null $scriptFrom * @property \App\Models\Egg|null $configFrom */ -class Egg extends Model +class Egg extends Model implements Validatable { + use HasFactory; + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. Also used as name for api key permissions. @@ -75,11 +82,6 @@ class Egg extends Model public const FEATURE_FASTDL = 'fastdl'; - /** - * The table associated with the model. - */ - protected $table = 'eggs'; - /** * Fields that are not mass assignable. */ @@ -167,11 +169,6 @@ protected static function booted(): void }); } - public function getRouteKeyName(): string - { - return 'id'; - } - /** * Returns the install script for the egg; if egg is copying from another * it will return the copied script. diff --git a/app/Models/EggVariable.php b/app/Models/EggVariable.php index 2d3a4fb2c5..f7076adba2 100644 --- a/app/Models/EggVariable.php +++ b/app/Models/EggVariable.php @@ -2,6 +2,10 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -26,8 +30,11 @@ * using the server relationship. * @property string|null $server_value */ -class EggVariable extends Model +class EggVariable extends Model implements Validatable { + use HasFactory; + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. @@ -39,11 +46,6 @@ class EggVariable extends Model */ public const RESERVED_ENV_NAMES = 'SERVER_MEMORY,SERVER_IP,SERVER_PORT,ENV,HOME,USER,STARTUP,SERVER_UUID,UUID'; - /** - * The table associated with the model. - */ - protected $table = 'egg_variables'; - /** * Fields that are not mass assignable. */ diff --git a/app/Models/File.php b/app/Models/File.php index 9f9f6f2aa4..aef1b2093f 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -7,6 +7,7 @@ use Exception; use Filament\Notifications\Notification; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; use Sushi\Sushi; /** diff --git a/app/Models/Model.php b/app/Models/Model.php deleted file mode 100644 index f2f92649b8..0000000000 --- a/app/Models/Model.php +++ /dev/null @@ -1,27 +0,0 @@ -belongsToMany(Server::class); } - - public function getRouteKeyName(): string - { - return 'id'; - } } diff --git a/app/Models/Node.php b/app/Models/Node.php index 011abb2527..792a216e45 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -2,9 +2,13 @@ namespace App\Models; +use App\Contracts\Validatable; use App\Exceptions\Service\HasActiveServersException; use App\Repositories\Daemon\DaemonConfigurationRepository; +use App\Traits\HasValidation; use Exception; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; @@ -47,8 +51,10 @@ * @property \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations * @property int|null $allocations_count */ -class Node extends Model +class Node extends Model implements Validatable { + use HasFactory; + use HasValidation; use Notifiable; /** @@ -61,11 +67,6 @@ class Node extends Model public const DAEMON_TOKEN_LENGTH = 64; - /** - * The table associated with the model. - */ - protected $table = 'nodes'; - /** * The attributes excluded from the model's JSON form. */ @@ -146,11 +147,6 @@ protected function casts(): array public int $servers_sum_cpu = 0; - public function getRouteKeyName(): string - { - return 'id'; - } - protected static function booted(): void { static::creating(function (self $node) { diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 8874d4c991..e59d0d2eb8 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -2,10 +2,15 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; -class Permission extends Model +class Permission extends Model implements Validatable { + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. @@ -95,16 +100,8 @@ class Permission extends Model public const ACTION_ACTIVITY_READ = 'settings.activity'; - /** - * Should timestamps be used on this model. - */ public $timestamps = false; - /** - * The table associated with the model. - */ - protected $table = 'permissions'; - /** * Fields that are not mass assignable. */ diff --git a/app/Models/RecoveryToken.php b/app/Models/RecoveryToken.php index c4fe8c8ae1..809c735d4a 100644 --- a/app/Models/RecoveryToken.php +++ b/app/Models/RecoveryToken.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; /** @@ -11,8 +14,10 @@ * @property \Carbon\CarbonImmutable $created_at * @property \App\Models\User $user */ -class RecoveryToken extends Model +class RecoveryToken extends Model implements Validatable { + use HasValidation; + /** * There are no updates to this model, only inserts and deletes. */ diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php index d6d19a8e14..ba85428b8e 100644 --- a/app/Models/Schedule.php +++ b/app/Models/Schedule.php @@ -2,7 +2,11 @@ namespace App\Models; +use App\Contracts\Validatable; use App\Helpers\Utilities; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -25,19 +29,17 @@ * @property \App\Models\Server $server * @property \App\Models\Task[]|\Illuminate\Support\Collection $tasks */ -class Schedule extends Model +class Schedule extends Model implements Validatable { + use HasFactory; + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. */ public const RESOURCE_NAME = 'server_schedule'; - /** - * The table associated with the model. - */ - protected $table = 'schedules'; - /** * Always return the tasks associated with this schedule. */ @@ -101,11 +103,6 @@ protected function casts(): array ]; } - public function getRouteKeyName(): string - { - return $this->getKeyName(); - } - /** * Returns the schedule's execution crontab entry as a string. * diff --git a/app/Models/Server.php b/app/Models/Server.php index 62dfe09454..2d4b4d326a 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -2,12 +2,16 @@ namespace App\Models; +use App\Contracts\Validatable; use App\Enums\ContainerStatus; use App\Enums\ServerResourceType; use App\Enums\ServerState; use App\Repositories\Daemon\DaemonServerRepository; +use App\Traits\HasValidation; use Carbon\CarbonInterface; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Http\Client\ConnectionException; use Illuminate\Notifications\Notifiable; @@ -121,8 +125,10 @@ * @method static \Illuminate\Database\Eloquent\Builder|Server wherePorts($value) * @method static \Illuminate\Database\Eloquent\Builder|Server whereUuidShort($value) */ -class Server extends Model +class Server extends Model implements Validatable { + use HasFactory; + use HasValidation; use Notifiable; /** @@ -131,11 +137,6 @@ class Server extends Model */ public const RESOURCE_NAME = 'server'; - /** - * The table associated with the model. - */ - protected $table = 'servers'; - /** * Default values when creating the model. We want to switch to disabling OOM killer * on server instances unless the user specifies otherwise in the request. @@ -360,11 +361,6 @@ public function activity(): MorphToMany return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects'); } - public function getRouteKeyName(): string - { - return 'id'; - } - public function resolveRouteBinding($value, $field = null): ?self { return match ($field) { diff --git a/app/Models/ServerTransfer.php b/app/Models/ServerTransfer.php index abfef3eb67..ce8b29f2f6 100644 --- a/app/Models/ServerTransfer.php +++ b/app/Models/ServerTransfer.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -22,19 +25,16 @@ * @property \App\Models\Node $oldNode * @property \App\Models\Node $newNode */ -class ServerTransfer extends Model +class ServerTransfer extends Model implements Validatable { + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. */ public const RESOURCE_NAME = 'server_transfer'; - /** - * The table associated with the model. - */ - protected $table = 'server_transfers'; - /** * Fields that are not mass assignable. */ diff --git a/app/Models/ServerVariable.php b/app/Models/ServerVariable.php index 0c7eeffc56..6721b87ed6 100644 --- a/app/Models/ServerVariable.php +++ b/app/Models/ServerVariable.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; /** @@ -14,16 +17,16 @@ * @property \App\Models\EggVariable $variable * @property \App\Models\Server $server */ -class ServerVariable extends Model +class ServerVariable extends Model implements Validatable { + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. */ public const RESOURCE_NAME = 'server_variable'; - protected $table = 'server_variables'; - protected $guarded = ['id', 'created_at', 'updated_at']; public static array $validationRules = [ diff --git a/app/Models/Subuser.php b/app/Models/Subuser.php index 566c8a7412..0276b8bc95 100644 --- a/app/Models/Subuser.php +++ b/app/Models/Subuser.php @@ -2,6 +2,10 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -16,8 +20,10 @@ * @property \App\Models\User $user * @property \App\Models\Server $server */ -class Subuser extends Model +class Subuser extends Model implements Validatable { + use HasFactory; + use HasValidation; use Notifiable; /** @@ -26,11 +32,6 @@ class Subuser extends Model */ public const RESOURCE_NAME = 'server_subuser'; - /** - * The table associated with the model. - */ - protected $table = 'subusers'; - /** * Fields that are not mass assignable. */ diff --git a/app/Models/Task.php b/app/Models/Task.php index 202a3165ab..69ad93336c 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -2,6 +2,10 @@ namespace App\Models; +use App\Contracts\Validatable; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -19,8 +23,11 @@ * @property \App\Models\Schedule $schedule * @property \App\Models\Server $server */ -class Task extends Model +class Task extends Model implements Validatable { + use HasFactory; + use HasValidation; + /** * The resource name for this model when it is transformed into an * API representation using fractal. @@ -38,11 +45,6 @@ class Task extends Model public const ACTION_DELETE_FILES = 'delete_files'; - /** - * The table associated with the model. - */ - protected $table = 'tasks'; - /** * Relationships to be updated when this model is updated. */ @@ -92,11 +94,6 @@ protected function casts(): array ]; } - public function getRouteKeyName(): string - { - return $this->getKeyName(); - } - /** * Return the schedule that a task belongs to. */ diff --git a/app/Models/User.php b/app/Models/User.php index 482add4f6f..45842973d0 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,15 +2,19 @@ namespace App\Models; +use App\Contracts\Validatable; use App\Exceptions\DisplayException; use App\Rules\Username; use App\Facades\Activity; +use App\Traits\HasValidation; use DateTimeZone; use Filament\Models\Contracts\FilamentUser; use Filament\Models\Contracts\HasAvatar; use Filament\Models\Contracts\HasName; use Filament\Models\Contracts\HasTenants; use Filament\Panel; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -29,7 +33,6 @@ use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use App\Notifications\SendPasswordReset as ResetPasswordNotification; use Filament\Facades\Filament; -use Illuminate\Database\Eloquent\Model as IlluminateModel; use Spatie\Permission\Traits\HasRoles; /** @@ -89,14 +92,16 @@ * @method static Builder|User whereUsername($value) * @method static Builder|User whereUuid($value) */ -class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, FilamentUser, HasAvatar, HasName, HasTenants +class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, FilamentUser, HasAvatar, HasName, HasTenants, Validatable { use Authenticatable; - use Authorizable {can as protected canned; } + use Authorizable { can as protected canned; } use AvailableLanguages; use CanResetPassword; use HasAccessTokens; + use HasFactory; use HasRoles; + use HasValidation { getRules as getValidationRules; } use Notifiable; public const USER_LEVEL_USER = 0; @@ -109,16 +114,6 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac */ public const RESOURCE_NAME = 'user'; - /** - * Level of servers to display when using access() on a user. - */ - protected string $accessLevel = 'all'; - - /** - * The table associated with the model. - */ - protected $table = 'users'; - /** * A list of mass-assignable variables. */ @@ -189,9 +184,8 @@ protected function casts(): array protected static function booted(): void { static::creating(function (self $user) { - $user->uuid = Str::uuid()->toString(); - - $user->timezone = env('APP_TIMEZONE', 'UTC'); + $user->uuid ??= Str::uuid()->toString(); + $user->timezone ??= config('app.timezone', 'UTC'); return true; }); @@ -203,18 +197,13 @@ protected static function booted(): void }); } - public function getRouteKeyName(): string - { - return 'id'; - } - /** * Implement language verification by overriding Eloquence's gather * rules function. */ public static function getRules(): array { - $rules = parent::getRules(); + $rules = self::getValidationRules(); $rules['language'][] = new In(array_keys((new self())->getAvailableLanguages())); $rules['timezone'][] = new In(array_values(DateTimeZone::listIdentifiers())); @@ -402,7 +391,7 @@ public function getFilamentAvatarUrl(): ?string return 'https://gravatar.com/avatar/' . md5(strtolower($this->email)); } - public function canTarget(IlluminateModel $user): bool + public function canTarget(Model $user): bool { if ($this->isRootAdmin()) { return true; @@ -416,7 +405,7 @@ public function getTenants(Panel $panel): array|Collection return $this->accessibleServers()->get(); } - public function canAccessTenant(IlluminateModel $tenant): bool + public function canAccessTenant(Model $tenant): bool { if ($tenant instanceof Server) { if ($this->isRootAdmin() || $tenant->owner_id === $this->id) { diff --git a/app/Models/UserSSHKey.php b/app/Models/UserSSHKey.php index f5cf02e4a5..e07b52e3b5 100644 --- a/app/Models/UserSSHKey.php +++ b/app/Models/UserSSHKey.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Traits\HasValidation; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -36,6 +39,8 @@ */ class UserSSHKey extends Model { + use HasFactory; + use HasValidation; use SoftDeletes; public const RESOURCE_NAME = 'ssh_key'; diff --git a/app/Observers/ValidationObserver.php b/app/Observers/ValidationObserver.php new file mode 100644 index 0000000000..f5537569c3 --- /dev/null +++ b/app/Observers/ValidationObserver.php @@ -0,0 +1,20 @@ +validate(); + } catch (ValidationException $exception) { + throw new DataValidationException($exception->validator, $model); + } + } +} diff --git a/app/Traits/Validation.php b/app/Traits/HasValidation.php similarity index 66% rename from app/Traits/Validation.php rename to app/Traits/HasValidation.php index 221cb9f19f..ecce82c55f 100644 --- a/app/Traits/Validation.php +++ b/app/Traits/HasValidation.php @@ -2,9 +2,10 @@ namespace App\Traits; -use App\Exceptions\Model\DataValidationException; -use App\Models\Model; +use App\Observers\ValidationObserver; use Illuminate\Container\Container; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Str; use Illuminate\Validation\Factory as ValidationFactory; @@ -12,41 +13,9 @@ use Illuminate\Validation\ValidationException; use Illuminate\Validation\Validator; -trait Validation +#[ObservedBy([ValidationObserver::class])] +trait HasValidation { - /** - * Determines if the model should undergo data validation before it is saved - * to the database. - */ - protected bool $skipValidation = false; - - protected static ValidationFactory $validatorFactory; - - public static array $validationRules = []; - - /** - * Listen for the model saving event and fire off the validation - * function before it is saved. - * - * @throws \Illuminate\Contracts\Container\BindingResolutionException - */ - protected static function boot(): void - { - parent::boot(); - - static::$validatorFactory = Container::getInstance()->make(ValidationFactory::class); - - static::saving(function (Model $model) { - try { - $model->validate(); - } catch (ValidationException $exception) { - throw new DataValidationException($exception->validator, $model); - } - - return true; - }); - } - /** * Returns the validator instance used by this model. */ @@ -54,7 +23,9 @@ public function getValidator(): Validator { $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); - return static::$validatorFactory->make([], $rules); + $validatorFactory = Container::getInstance()->make(ValidationFactory::class); + + return $validatorFactory->make([], $rules); } /** @@ -115,23 +86,37 @@ public static function getRulesForUpdate(self $model): array */ public function validate(): void { - if ($this->skipValidation) { + if (isset($this->skipValidation)) { return; } + // $data = $this->addCastAttributesToArray( + // $this->getAttributes(), + // $this->getMutatedAttributes() + // ); + // $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); + // $validator = \Illuminate\Support\Facades\Validator::make($data, $rules); + + // dump('a'); + $validator = $this->getValidator(); + // dump('b'); $validator->setData( - // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist - // for that model. Doing this will return all the attributes in a format that can - // properly be validated. + // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist + // for that model. Doing this will return all the attributes in a format that can + // properly be validated. $this->addCastAttributesToArray( $this->getAttributes(), $this->getMutatedAttributes() ) ); + // dump('c'); + if (!$validator->passes()) { throw new ValidationException($validator); } + + // dump('d'); } } From 9bd2b42ac5b01de11f599194921d0294a6fd0725 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Sat, 4 Jan 2025 16:42:07 -0500 Subject: [PATCH 12/13] Backup routes were previously referenced by uuids --- routes/api-client.php | 10 +++++----- routes/api-remote.php | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/routes/api-client.php b/routes/api-client.php index ed1190af64..0feceeb788 100644 --- a/routes/api-client.php +++ b/routes/api-client.php @@ -115,11 +115,11 @@ Route::prefix('/backups')->group(function () { Route::get('/', [Client\Servers\BackupController::class, 'index']); Route::post('/', [Client\Servers\BackupController::class, 'store']); - Route::get('/{backup}', [Client\Servers\BackupController::class, 'view']); - Route::get('/{backup}/download', [Client\Servers\BackupController::class, 'download']); - Route::post('/{backup}/lock', [Client\Servers\BackupController::class, 'toggleLock']); - Route::post('/{backup}/restore', [Client\Servers\BackupController::class, 'restore']); - Route::delete('/{backup}', [Client\Servers\BackupController::class, 'delete']); + Route::get('/{backup:uuid}', [Client\Servers\BackupController::class, 'view']); + Route::get('/{backup:uuid}/download', [Client\Servers\BackupController::class, 'download']); + Route::post('/{backup:uuid}/lock', [Client\Servers\BackupController::class, 'toggleLock']); + Route::post('/{backup:uuid}/restore', [Client\Servers\BackupController::class, 'restore']); + Route::delete('/{backup:uuid}', [Client\Servers\BackupController::class, 'delete']); }); Route::prefix('/startup')->group(function () { diff --git a/routes/api-remote.php b/routes/api-remote.php index d2c9a893d8..df69d1c309 100644 --- a/routes/api-remote.php +++ b/routes/api-remote.php @@ -24,7 +24,7 @@ }); Route::prefix('/backups')->group(function () { - Route::get('/{backup}', Remote\Backups\BackupRemoteUploadController::class); - Route::post('/{backup}', [Remote\Backups\BackupStatusController::class, 'index']); - Route::post('/{backup}/restore', [Remote\Backups\BackupStatusController::class, 'restore']); + Route::get('/{backup:uuid}', Remote\Backups\BackupRemoteUploadController::class); + Route::post('/{backup:uuid}', [Remote\Backups\BackupStatusController::class, 'index']); + Route::post('/{backup:uuid}/restore', [Remote\Backups\BackupStatusController::class, 'restore']); }); From 6665a78f6dab897cd3262209ffa9e5ca077c76de Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Sat, 4 Jan 2025 16:50:33 -0500 Subject: [PATCH 13/13] Remove commented code --- app/Traits/HasValidation.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/app/Traits/HasValidation.php b/app/Traits/HasValidation.php index ecce82c55f..8cb3617be2 100644 --- a/app/Traits/HasValidation.php +++ b/app/Traits/HasValidation.php @@ -90,17 +90,7 @@ public function validate(): void return; } - // $data = $this->addCastAttributesToArray( - // $this->getAttributes(), - // $this->getMutatedAttributes() - // ); - // $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); - // $validator = \Illuminate\Support\Facades\Validator::make($data, $rules); - - // dump('a'); - $validator = $this->getValidator(); - // dump('b'); $validator->setData( // Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist // for that model. Doing this will return all the attributes in a format that can @@ -111,12 +101,8 @@ public function validate(): void ) ); - // dump('c'); - if (!$validator->passes()) { throw new ValidationException($validator); } - - // dump('d'); } }