Add PHP 8.4 Support (#858)

* Add php 8.4

* Update ide helper

* Add php 8.4

* Update laravel sanctum

* Update laravel framework

* Hash rounds were increased

* This is always false

* Extend model now

* This does nothing

* Move model validation methods to trait

* Remove base model

* Backup routes were previously referenced by uuids

* Remove commented code

* Upgrade laravel/framework

* Fix migration

* Update ide helper

* Update sanctum

* Add version to composer

* Add this back in, fixed

* Make this protected to be safer
This commit is contained in:
Lance Pioch 2025-01-30 16:39:00 -05:00 committed by GitHub
parent 20125dbc6f
commit 635cc6a029
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 245 additions and 275 deletions

View File

@ -13,7 +13,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [8.2, 8.3] php: [8.2, 8.3, 8.4]
database: ["mysql:8"] database: ["mysql:8"]
services: services:
database: database:
@ -86,7 +86,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [8.2, 8.3] php: [8.2, 8.3, 8.4]
database: ["mariadb:10.6", "mariadb:10.11", "mariadb:11.4"] database: ["mariadb:10.6", "mariadb:10.11", "mariadb:11.4"]
services: services:
database: database:
@ -159,7 +159,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [8.2, 8.3] php: [8.2, 8.3, 8.4]
env: env:
APP_ENV: testing APP_ENV: testing
APP_DEBUG: "false" APP_DEBUG: "false"

View File

@ -0,0 +1,16 @@
<?php
namespace App\Contracts;
use Illuminate\Validation\Validator;
interface Validatable
{
public function getValidator(): Validator;
public static function getRules(): array;
public static function getRulesForField(string $field): array;
public function validate(): void;
}

View File

@ -4,7 +4,6 @@ namespace App\Http\Requests\Api\Application;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
use App\Models\ApiKey; use App\Models\ApiKey;
use Laravel\Sanctum\TransientToken;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Services\Acl\Api\AdminAcl; use App\Services\Acl\Api\AdminAcl;
@ -38,9 +37,6 @@ abstract class ApplicationApiRequest extends FormRequest
} }
$token = $this->user()->currentAccessToken(); $token = $this->user()->currentAccessToken();
if ($token instanceof TransientToken) {
return true;
}
/** @var ApiKey $token */ /** @var ApiKey $token */
if ($token->key_type === ApiKey::TYPE_ACCOUNT) { if ($token->key_type === ApiKey::TYPE_ACCOUNT) {

View File

@ -2,6 +2,7 @@
namespace App\Models; namespace App\Models;
use App\Traits\HasValidation;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use App\Events\ActivityLogged; use App\Events\ActivityLogged;
@ -12,7 +13,7 @@ use Illuminate\Database\Eloquent\MassPrunable;
use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Model as IlluminateModel; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str; use Illuminate\Support\Str;
/** /**
@ -28,12 +29,12 @@ use Illuminate\Support\Str;
* @property int|null $api_key_id * @property int|null $api_key_id
* @property \Illuminate\Support\Collection|null $properties * @property \Illuminate\Support\Collection|null $properties
* @property \Carbon\Carbon $timestamp * @property \Carbon\Carbon $timestamp
* @property IlluminateModel|\Eloquent $actor * @property Model|\Eloquent $actor
* @property \Illuminate\Database\Eloquent\Collection|\App\Models\ActivityLogSubject[] $subjects * @property \Illuminate\Database\Eloquent\Collection|\App\Models\ActivityLogSubject[] $subjects
* @property int|null $subjects_count * @property int|null $subjects_count
* @property \App\Models\ApiKey|null $apiKey * @property \App\Models\ApiKey|null $apiKey
* *
* @method static Builder|ActivityLog forActor(\Illuminate\Database\Eloquent\Model $actor) * @method static Builder|ActivityLog forActor(Model $actor)
* @method static Builder|ActivityLog forEvent(string $action) * @method static Builder|ActivityLog forEvent(string $action)
* @method static Builder|ActivityLog newModelQuery() * @method static Builder|ActivityLog newModelQuery()
* @method static Builder|ActivityLog newQuery() * @method static Builder|ActivityLog newQuery()
@ -51,6 +52,7 @@ use Illuminate\Support\Str;
*/ */
class ActivityLog extends Model implements HasIcon, HasLabel class ActivityLog extends Model implements HasIcon, HasLabel
{ {
use HasValidation;
use MassPrunable; use MassPrunable;
public const RESOURCE_NAME = 'activity_log'; public const RESOURCE_NAME = 'activity_log';
@ -109,7 +111,7 @@ class ActivityLog extends Model implements HasIcon, HasLabel
/** /**
* Scopes a query to only return results where the actor is a given model. * Scopes a query to only return results where the actor is a given model.
*/ */
public function scopeForActor(Builder $builder, IlluminateModel $actor): Builder public function scopeForActor(Builder $builder, Model $actor): Builder
{ {
return $builder->whereMorphedTo('actor', $actor); return $builder->whereMorphedTo('actor', $actor);
} }

View File

@ -3,7 +3,10 @@
namespace App\Models; namespace App\Models;
use App\Exceptions\Service\Allocation\ServerUsingAllocationException; 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\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** /**
@ -40,17 +43,15 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
*/ */
class Allocation extends Model class Allocation extends Model
{ {
use HasFactory;
use HasValidation;
/** /**
* The resource name for this model when it is transformed into an * The resource name for this model when it is transformed into an
* API representation using fractal. Also used as name for api key permissions. * API representation using fractal. Also used as name for api key permissions.
*/ */
public const RESOURCE_NAME = 'allocation'; public const RESOURCE_NAME = 'allocation';
/**
* The table associated with the model.
*/
protected $table = 'allocations';
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */
@ -81,11 +82,6 @@ class Allocation extends Model
]; ];
} }
public function getRouteKeyName(): string
{
return $this->getKeyName();
}
/** /**
* Accessor to automatically provide the IP alias if defined. * Accessor to automatically provide the IP alias if defined.
*/ */

View File

@ -3,7 +3,10 @@
namespace App\Models; namespace App\Models;
use App\Services\Acl\Api\AdminAcl; use App\Services\Acl\Api\AdminAcl;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Laravel\Sanctum\PersonalAccessToken;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -47,8 +50,11 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUserId($value) * @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereUserId($value)
*/ */
class ApiKey extends Model class ApiKey extends PersonalAccessToken
{ {
use HasFactory;
use HasValidation;
/** /**
* The resource name for this model when it is transformed into an * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
@ -75,11 +81,6 @@ class ApiKey extends Model
*/ */
public const KEY_LENGTH = 32; public const KEY_LENGTH = 32;
/**
* The table associated with the model.
*/
protected $table = 'api_keys';
/** /**
* Fields that are mass assignable. * Fields that are mass assignable.
*/ */
@ -148,13 +149,9 @@ class ApiKey extends Model
return $this->belongsTo(User::class); return $this->belongsTo(User::class);
} }
/** public function tokenable()
* Required for support with Laravel Sanctum.
*
* @see \Laravel\Sanctum\Guard::supportsTokens()
*/
public function tokenable(): BelongsTo
{ {
// @phpstan-ignore-next-line
return $this->user(); return $this->user();
} }
@ -178,11 +175,11 @@ class ApiKey extends Model
Role::RESOURCE_NAME, Role::RESOURCE_NAME,
]; ];
private static array $customResourceNames = []; protected static array $customResourceNames = [];
public static function registerCustomResourceName(string $resourceName): void public static function registerCustomResourceName(string $resourceName): void
{ {
$customResourceNames[] = $resourceName; static::$customResourceNames[] = $resourceName;
} }
/** /**
@ -195,11 +192,14 @@ class ApiKey extends Model
/** /**
* Finds the model matching the provided token. * 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); $identifier = substr($token, 0, self::IDENTIFIER_LENGTH);
/** @var static|null $model */
$model = static::where('identifier', $identifier)->first(); $model = static::where('identifier', $identifier)->first();
if (!is_null($model) && $model->token === substr($token, strlen($identifier))) { if (!is_null($model) && $model->token === substr($token, strlen($identifier))) {
return $model; return $model;

View File

@ -2,6 +2,10 @@
namespace App\Models; 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 Ramsey\Uuid\Uuid;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Container\Container; use Illuminate\Container\Container;
@ -10,8 +14,11 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** /**
* @deprecated this class will be dropped in a future version, use the activity log * @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 const UPDATED_AT = null;
public static array $validationRules = [ public static array $validationRules = [
@ -24,8 +31,6 @@ class AuditLog extends Model
'metadata' => 'array', 'metadata' => 'array',
]; ];
protected $table = 'audit_logs';
protected $guarded = [ protected $guarded = [
'id', 'id',
'created_at', 'created_at',

View File

@ -2,6 +2,10 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Eloquent\BackupQueryBuilder; use App\Eloquent\BackupQueryBuilder;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -25,8 +29,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property \App\Models\Server $server * @property \App\Models\Server $server
* @property \App\Models\AuditLog[] $audits * @property \App\Models\AuditLog[] $audits
*/ */
class Backup extends Model class Backup extends Model implements Validatable
{ {
use HasFactory;
use HasValidation;
use SoftDeletes; use SoftDeletes;
public const RESOURCE_NAME = 'backup'; public const RESOURCE_NAME = 'backup';
@ -35,8 +41,6 @@ class Backup extends Model
public const ADAPTER_AWS_S3 = 's3'; public const ADAPTER_AWS_S3 = 's3';
protected $table = 'backups';
protected $attributes = [ protected $attributes = [
'is_successful' => false, 'is_successful' => false,
'is_locked' => false, 'is_locked' => false,

View File

@ -2,7 +2,11 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Casts\Attribute; 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\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -21,8 +25,11 @@ use Illuminate\Support\Facades\DB;
* @property \App\Models\Server $server * @property \App\Models\Server $server
* @property \App\Models\DatabaseHost $host * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. Also used as name for api key permissions. * 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'; public const DEFAULT_CONNECTION_NAME = 'dynamic';
/**
* The table associated with the model.
*/
protected $table = 'databases';
/** /**
* The attributes excluded from the model's JSON form. * The attributes excluded from the model's JSON form.
*/ */
@ -68,11 +70,6 @@ class Database extends Model
]; ];
} }
public function getRouteKeyName(): string
{
return $this->getKeyName();
}
/** /**
* Gets the host database server associated with a database. * Gets the host database server associated with a database.
*/ */

View File

@ -2,6 +2,10 @@
namespace App\Models; 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\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@ -21,19 +25,17 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property \Illuminate\Database\Eloquent\Collection|\App\Models\Database[] $databases * @property \Illuminate\Database\Eloquent\Collection|\App\Models\Database[] $databases
* @property int|null $databases_count * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. Also used as name for api key permissions. * API representation using fractal. Also used as name for api key permissions.
*/ */
public const RESOURCE_NAME = 'database_host'; 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. * The attributes excluded from the model's JSON form.
*/ */
@ -70,11 +72,6 @@ class DatabaseHost extends Model
]; ];
} }
public function getRouteKeyName(): string
{
return 'id';
}
public function nodes(): BelongsToMany public function nodes(): BelongsToMany
{ {
return $this->belongsToMany(Node::class); return $this->belongsToMany(Node::class);

View File

@ -2,8 +2,12 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Exceptions\Service\Egg\HasChildrenException; use App\Exceptions\Service\Egg\HasChildrenException;
use App\Exceptions\Service\HasActiveServersException; 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\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -49,8 +53,11 @@ use Illuminate\Support\Str;
* @property \App\Models\Egg|null $scriptFrom * @property \App\Models\Egg|null $scriptFrom
* @property \App\Models\Egg|null $configFrom * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. Also used as name for api key permissions. * 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'; public const FEATURE_FASTDL = 'fastdl';
/**
* The table associated with the model.
*/
protected $table = 'eggs';
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */
@ -167,11 +169,6 @@ class Egg extends Model
}); });
} }
public function getRouteKeyName(): string
{
return 'id';
}
/** /**
* Returns the install script for the egg; if egg is copying from another * Returns the install script for the egg; if egg is copying from another
* it will return the copied script. * it will return the copied script.

View File

@ -2,6 +2,10 @@
namespace App\Models; 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\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@ -26,8 +30,11 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* using the server relationship. * using the server relationship.
* @property string|null $server_value * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
@ -39,11 +46,6 @@ class EggVariable extends Model
*/ */
public const RESERVED_ENV_NAMES = 'P_SERVER_UUID,P_SERVER_ALLOCATION_LIMIT,SERVER_MEMORY,SERVER_IP,SERVER_PORT,ENV,HOME,USER,STARTUP,MODIFIED_STARTUP,SERVER_UUID,UUID,INTERNAL_IP'; public const RESERVED_ENV_NAMES = 'P_SERVER_UUID,P_SERVER_ALLOCATION_LIMIT,SERVER_MEMORY,SERVER_IP,SERVER_PORT,ENV,HOME,USER,STARTUP,MODIFIED_STARTUP,SERVER_UUID,UUID,INTERNAL_IP';
/**
* The table associated with the model.
*/
protected $table = 'egg_variables';
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */

View File

@ -7,6 +7,7 @@ use App\Repositories\Daemon\DaemonFileRepository;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Sushi\Sushi; use Sushi\Sushi;
/** /**

View File

@ -2,6 +2,9 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rules\NotIn; use Illuminate\Validation\Rules\NotIn;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@ -18,19 +21,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
* @property \App\Models\Node[]|\Illuminate\Database\Eloquent\Collection $nodes * @property \App\Models\Node[]|\Illuminate\Database\Eloquent\Collection $nodes
* @property \App\Models\Server[]|\Illuminate\Database\Eloquent\Collection $servers * @property \App\Models\Server[]|\Illuminate\Database\Eloquent\Collection $servers
*/ */
class Mount extends Model class Mount extends Model implements Validatable
{ {
use HasValidation { getRules as getValidationRules; }
/** /**
* The resource name for this model when it is transformed into an * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
*/ */
public const RESOURCE_NAME = 'mount'; public const RESOURCE_NAME = 'mount';
/**
* The table associated with the model.
*/
protected $table = 'mounts';
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */
@ -54,7 +54,7 @@ class Mount extends Model
*/ */
public static function getRules(): array public static function getRules(): array
{ {
$rules = parent::getRules(); $rules = self::getValidationRules();
$rules['source'][] = new NotIn(Mount::$invalidSourcePaths); $rules['source'][] = new NotIn(Mount::$invalidSourcePaths);
$rules['target'][] = new NotIn(Mount::$invalidTargetPaths); $rules['target'][] = new NotIn(Mount::$invalidTargetPaths);
@ -115,9 +115,4 @@ class Mount extends Model
{ {
return $this->belongsToMany(Server::class); return $this->belongsToMany(Server::class);
} }
public function getRouteKeyName(): string
{
return 'id';
}
} }

View File

@ -2,9 +2,13 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Exceptions\Service\HasActiveServersException; use App\Exceptions\Service\HasActiveServersException;
use App\Repositories\Daemon\DaemonConfigurationRepository; use App\Repositories\Daemon\DaemonConfigurationRepository;
use App\Traits\HasValidation;
use Exception; use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasManyThrough;
@ -47,8 +51,10 @@ use Symfony\Component\Yaml\Yaml;
* @property \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations * @property \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations
* @property int|null $allocations_count * @property int|null $allocations_count
*/ */
class Node extends Model class Node extends Model implements Validatable
{ {
use HasFactory;
use HasValidation;
use Notifiable; use Notifiable;
/** /**
@ -61,11 +67,6 @@ class Node extends Model
public const DAEMON_TOKEN_LENGTH = 64; public const DAEMON_TOKEN_LENGTH = 64;
/**
* The table associated with the model.
*/
protected $table = 'nodes';
/** /**
* The attributes excluded from the model's JSON form. * The attributes excluded from the model's JSON form.
*/ */
@ -146,11 +147,6 @@ class Node extends Model
public int $servers_sum_cpu = 0; public int $servers_sum_cpu = 0;
public function getRouteKeyName(): string
{
return 'id';
}
protected static function booted(): void protected static function booted(): void
{ {
static::creating(function (self $node) { static::creating(function (self $node) {

View File

@ -2,10 +2,15 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection; 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 * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
@ -95,16 +100,8 @@ class Permission extends Model
public const ACTION_ACTIVITY_READ = 'activity.read'; public const ACTION_ACTIVITY_READ = 'activity.read';
/**
* Should timestamps be used on this model.
*/
public $timestamps = false; public $timestamps = false;
/**
* The table associated with the model.
*/
protected $table = 'permissions';
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */

View File

@ -2,6 +2,9 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** /**
@ -11,8 +14,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property \Carbon\CarbonImmutable $created_at * @property \Carbon\CarbonImmutable $created_at
* @property \App\Models\User $user * @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. * There are no updates to this model, only inserts and deletes.
*/ */

View File

@ -2,7 +2,11 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Helpers\Utilities; 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\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -25,19 +29,17 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property \App\Models\Server $server * @property \App\Models\Server $server
* @property \App\Models\Task[]|\Illuminate\Support\Collection $tasks * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
*/ */
public const RESOURCE_NAME = 'server_schedule'; public const RESOURCE_NAME = 'server_schedule';
/**
* The table associated with the model.
*/
protected $table = 'schedules';
/** /**
* Always return the tasks associated with this schedule. * Always return the tasks associated with this schedule.
*/ */
@ -101,11 +103,6 @@ class Schedule extends Model
]; ];
} }
public function getRouteKeyName(): string
{
return $this->getKeyName();
}
/** /**
* Returns the schedule's execution crontab entry as a string. * Returns the schedule's execution crontab entry as a string.
* *

View File

@ -2,12 +2,16 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Enums\ContainerStatus; use App\Enums\ContainerStatus;
use App\Enums\ServerResourceType; use App\Enums\ServerResourceType;
use App\Enums\ServerState; use App\Enums\ServerState;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
use App\Traits\HasValidation;
use Carbon\CarbonInterface; use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Casts\Attribute; 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\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Client\ConnectionException;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
@ -121,8 +125,10 @@ use App\Services\Subusers\SubuserDeletionService;
* @method static \Illuminate\Database\Eloquent\Builder|Server wherePorts($value) * @method static \Illuminate\Database\Eloquent\Builder|Server wherePorts($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server whereUuidShort($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; use Notifiable;
/** /**
@ -131,11 +137,6 @@ class Server extends Model
*/ */
public const RESOURCE_NAME = 'server'; 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 * 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. * on server instances unless the user specifies otherwise in the request.
@ -363,11 +364,6 @@ class Server extends Model
return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects'); return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects');
} }
public function getRouteKeyName(): string
{
return 'id';
}
public function resolveRouteBinding($value, $field = null): ?self public function resolveRouteBinding($value, $field = null): ?self
{ {
return match ($field) { return match ($field) {

View File

@ -2,6 +2,9 @@
namespace App\Models; 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\HasOne;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -22,19 +25,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property \App\Models\Node $oldNode * @property \App\Models\Node $oldNode
* @property \App\Models\Node $newNode * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
*/ */
public const RESOURCE_NAME = 'server_transfer'; public const RESOURCE_NAME = 'server_transfer';
/**
* The table associated with the model.
*/
protected $table = 'server_transfers';
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */

View File

@ -2,6 +2,9 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** /**
@ -14,16 +17,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property \App\Models\EggVariable $variable * @property \App\Models\EggVariable $variable
* @property \App\Models\Server $server * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
*/ */
public const RESOURCE_NAME = 'server_variable'; public const RESOURCE_NAME = 'server_variable';
protected $table = 'server_variables';
protected $guarded = ['id', 'created_at', 'updated_at']; protected $guarded = ['id', 'created_at', 'updated_at'];
public static array $validationRules = [ public static array $validationRules = [

View File

@ -2,6 +2,10 @@
namespace App\Models; 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\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -16,8 +20,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property \App\Models\User $user * @property \App\Models\User $user
* @property \App\Models\Server $server * @property \App\Models\Server $server
*/ */
class Subuser extends Model class Subuser extends Model implements Validatable
{ {
use HasFactory;
use HasValidation;
use Notifiable; use Notifiable;
/** /**
@ -26,11 +32,6 @@ class Subuser extends Model
*/ */
public const RESOURCE_NAME = 'server_subuser'; public const RESOURCE_NAME = 'server_subuser';
/**
* The table associated with the model.
*/
protected $table = 'subusers';
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */

View File

@ -2,6 +2,10 @@
namespace App\Models; 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\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -19,8 +23,11 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property \App\Models\Schedule $schedule * @property \App\Models\Schedule $schedule
* @property \App\Models\Server $server * @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 * The resource name for this model when it is transformed into an
* API representation using fractal. * API representation using fractal.
@ -38,11 +45,6 @@ class Task extends Model
public const ACTION_DELETE_FILES = 'delete_files'; 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. * Relationships to be updated when this model is updated.
*/ */
@ -92,11 +94,6 @@ class Task extends Model
]; ];
} }
public function getRouteKeyName(): string
{
return $this->getKeyName();
}
/** /**
* Return the schedule that a task belongs to. * Return the schedule that a task belongs to.
*/ */

View File

@ -2,15 +2,19 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable;
use App\Exceptions\DisplayException; use App\Exceptions\DisplayException;
use App\Rules\Username; use App\Rules\Username;
use App\Facades\Activity; use App\Facades\Activity;
use App\Traits\HasValidation;
use DateTimeZone; use DateTimeZone;
use Filament\Models\Contracts\FilamentUser; use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasAvatar; use Filament\Models\Contracts\HasAvatar;
use Filament\Models\Contracts\HasName; use Filament\Models\Contracts\HasName;
use Filament\Models\Contracts\HasTenants; use Filament\Models\Contracts\HasTenants;
use Filament\Panel; use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -28,7 +32,6 @@ use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use App\Notifications\SendPasswordReset as ResetPasswordNotification; use App\Notifications\SendPasswordReset as ResetPasswordNotification;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model as IlluminateModel;
use ResourceBundle; use ResourceBundle;
use Spatie\Permission\Traits\HasRoles; use Spatie\Permission\Traits\HasRoles;
@ -85,13 +88,15 @@ use Spatie\Permission\Traits\HasRoles;
* @method static Builder|User whereUsername($value) * @method static Builder|User whereUsername($value)
* @method static Builder|User whereUuid($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 Authenticatable;
use Authorizable {can as protected canned; } use Authorizable { can as protected canned; }
use CanResetPassword; use CanResetPassword;
use HasAccessTokens; use HasAccessTokens;
use HasFactory;
use HasRoles; use HasRoles;
use HasValidation { getRules as getValidationRules; }
use Notifiable; use Notifiable;
public const USER_LEVEL_USER = 0; public const USER_LEVEL_USER = 0;
@ -104,16 +109,6 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
*/ */
public const RESOURCE_NAME = 'user'; 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. * A list of mass-assignable variables.
*/ */
@ -191,18 +186,13 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
}); });
} }
public function getRouteKeyName(): string
{
return 'id';
}
/** /**
* Implement language verification by overriding Eloquence's gather * Implement language verification by overriding Eloquence's gather
* rules function. * rules function.
*/ */
public static function getRules(): array public static function getRules(): array
{ {
$rules = parent::getRules(); $rules = self::getValidationRules();
$rules['language'][] = new In(array_values(array_filter(ResourceBundle::getLocales(''), fn ($lang) => preg_match('/^[a-z]{2}$/', $lang)))); $rules['language'][] = new In(array_values(array_filter(ResourceBundle::getLocales(''), fn ($lang) => preg_match('/^[a-z]{2}$/', $lang))));
$rules['timezone'][] = new In(DateTimeZone::listIdentifiers()); $rules['timezone'][] = new In(DateTimeZone::listIdentifiers());
@ -407,7 +397,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
return 'https://gravatar.com/avatar/' . md5(strtolower($this->email)); return 'https://gravatar.com/avatar/' . md5(strtolower($this->email));
} }
public function canTarget(IlluminateModel $user): bool public function canTarget(Model $user): bool
{ {
if ($this->isRootAdmin()) { if ($this->isRootAdmin()) {
return true; return true;
@ -421,7 +411,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
return $this->accessibleServers()->get(); return $this->accessibleServers()->get();
} }
public function canAccessTenant(IlluminateModel $tenant): bool public function canAccessTenant(Model $tenant): bool
{ {
if ($tenant instanceof Server) { if ($tenant instanceof Server) {
if ($this->canned('view server', $tenant) || $tenant->owner_id === $this->id) { if ($this->canned('view server', $tenant) || $tenant->owner_id === $this->id) {

View File

@ -2,6 +2,9 @@
namespace App\Models; 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\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -36,6 +39,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
*/ */
class UserSSHKey extends Model class UserSSHKey extends Model
{ {
use HasFactory;
use HasValidation;
use SoftDeletes; use SoftDeletes;
public const RESOURCE_NAME = 'ssh_key'; public const RESOURCE_NAME = 'ssh_key';

View File

@ -0,0 +1,20 @@
<?php
namespace App\Observers;
use App\Contracts\Validatable as HasValidationContract;
use App\Exceptions\Model\DataValidationException;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\ValidationException;
class ValidationObserver
{
public function saving(Model&HasValidationContract $model): void
{
try {
$model->validate();
} catch (ValidationException $exception) {
throw new DataValidationException($exception->validator, $model);
}
}
}

View File

@ -1,69 +1,21 @@
<?php <?php
namespace App\Models; namespace App\Traits;
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\Arr;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Container\Container;
use Illuminate\Validation\ValidationException;
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\Factory as ValidationFactory;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
abstract class Model extends IlluminateModel #[ObservedBy([ValidationObserver::class])]
trait HasValidation
{ {
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;
});
}
/**
* Returns the model key to use for route model binding. By default, we'll
* assume every model uses a UUID field for this. If the model does not have
* a UUID and is using a different key it should be specified on the model
* itself.
*
* You may also optionally override this on a per-route basis by declaring
* the key name in the URL definition, like "{user:id}".
*/
public function getRouteKeyName(): string
{
return 'uuid';
}
/** /**
* Returns the validator instance used by this model. * Returns the validator instance used by this model.
*/ */
@ -71,7 +23,9 @@ abstract class Model extends IlluminateModel
{ {
$rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules(); $rules = $this->exists ? static::getRulesForUpdate($this) : static::getRules();
return static::$validatorFactory->make([], $rules); $validatorFactory = Container::getInstance()->make(ValidationFactory::class);
return $validatorFactory->make([], $rules);
} }
/** /**
@ -132,7 +86,7 @@ abstract class Model extends IlluminateModel
*/ */
public function validate(): void public function validate(): void
{ {
if ($this->skipValidation) { if (isset($this->skipValidation)) {
return; return;
} }

View File

@ -2,7 +2,7 @@
"name": "pelican-dev/panel", "name": "pelican-dev/panel",
"description": "The free, open-source game management panel. Supporting Minecraft, Spigot, BungeeCord, and SRCDS servers.", "description": "The free, open-source game management panel. Supporting Minecraft, Spigot, BungeeCord, and SRCDS servers.",
"require": { "require": {
"php": "^8.2 || ^8.3", "php": "^8.2 || ^8.3 || ^8.4",
"ext-intl": "*", "ext-intl": "*",
"ext-json": "*", "ext-json": "*",
"ext-mbstring": "*", "ext-mbstring": "*",

45
composer.lock generated
View File

@ -2951,16 +2951,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v11.37.0", "version": "v11.40.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "6cb103d2024b087eae207654b3f4b26646119ba5" "reference": "599a28196d284fee158cc10086fd56ac625ad7a3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/6cb103d2024b087eae207654b3f4b26646119ba5", "url": "https://api.github.com/repos/laravel/framework/zipball/599a28196d284fee158cc10086fd56ac625ad7a3",
"reference": "6cb103d2024b087eae207654b3f4b26646119ba5", "reference": "599a28196d284fee158cc10086fd56ac625ad7a3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2986,7 +2986,7 @@
"league/flysystem-local": "^3.25.1", "league/flysystem-local": "^3.25.1",
"league/uri": "^7.5.1", "league/uri": "^7.5.1",
"monolog/monolog": "^3.0", "monolog/monolog": "^3.0",
"nesbot/carbon": "^2.72.2|^3.4", "nesbot/carbon": "^2.72.6|^3.8.4",
"nunomaduro/termwind": "^2.0", "nunomaduro/termwind": "^2.0",
"php": "^8.2", "php": "^8.2",
"psr/container": "^1.1.1|^2.0.1", "psr/container": "^1.1.1|^2.0.1",
@ -3061,6 +3061,7 @@
"fakerphp/faker": "^1.24", "fakerphp/faker": "^1.24",
"guzzlehttp/promises": "^2.0.3", "guzzlehttp/promises": "^2.0.3",
"guzzlehttp/psr7": "^2.4", "guzzlehttp/psr7": "^2.4",
"laravel/pint": "^1.18",
"league/flysystem-aws-s3-v3": "^3.25.1", "league/flysystem-aws-s3-v3": "^3.25.1",
"league/flysystem-ftp": "^3.25.1", "league/flysystem-ftp": "^3.25.1",
"league/flysystem-path-prefixing": "^3.25.1", "league/flysystem-path-prefixing": "^3.25.1",
@ -3161,7 +3162,7 @@
"issues": "https://github.com/laravel/framework/issues", "issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework" "source": "https://github.com/laravel/framework"
}, },
"time": "2025-01-02T20:10:21+00:00" "time": "2025-01-24T16:17:42+00:00"
}, },
{ {
"name": "laravel/helpers", "name": "laravel/helpers",
@ -3281,16 +3282,16 @@
}, },
{ {
"name": "laravel/sanctum", "name": "laravel/sanctum",
"version": "v4.0.3", "version": "v4.0.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/sanctum.git", "url": "https://github.com/laravel/sanctum.git",
"reference": "54aea9d13743ae8a6cdd3c28dbef128a17adecab" "reference": "698064236a46df016e64a7eb059b1414e0b281df"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/sanctum/zipball/54aea9d13743ae8a6cdd3c28dbef128a17adecab", "url": "https://api.github.com/repos/laravel/sanctum/zipball/698064236a46df016e64a7eb059b1414e0b281df",
"reference": "54aea9d13743ae8a6cdd3c28dbef128a17adecab", "reference": "698064236a46df016e64a7eb059b1414e0b281df",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3341,7 +3342,7 @@
"issues": "https://github.com/laravel/sanctum/issues", "issues": "https://github.com/laravel/sanctum/issues",
"source": "https://github.com/laravel/sanctum" "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", "name": "laravel/serializable-closure",
@ -10881,16 +10882,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "barryvdh/laravel-ide-helper", "name": "barryvdh/laravel-ide-helper",
"version": "v3.2.2", "version": "v3.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git", "url": "https://github.com/barryvdh/laravel-ide-helper.git",
"reference": "07e3bd8796f3d1414801a03d3783f9d3ec9efc08" "reference": "b7675670f75914bf34afdea52a6c2fe3781f7c44"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/07e3bd8796f3d1414801a03d3783f9d3ec9efc08", "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/b7675670f75914bf34afdea52a6c2fe3781f7c44",
"reference": "07e3bd8796f3d1414801a03d3783f9d3ec9efc08", "reference": "b7675670f75914bf34afdea52a6c2fe3781f7c44",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -10921,13 +10922,13 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": {
"dev-master": "3.2-dev"
},
"laravel": { "laravel": {
"providers": [ "providers": [
"Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider" "Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
] ]
},
"branch-alias": {
"dev-master": "3.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -10959,7 +10960,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues", "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": [ "funding": [
{ {
@ -10971,7 +10972,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-10-29T14:00:16+00:00" "time": "2024-12-18T08:24:19+00:00"
}, },
{ {
"name": "barryvdh/reflection-docblock", "name": "barryvdh/reflection-docblock",
@ -13969,7 +13970,7 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": {}, "stability-flags": [],
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
@ -13980,6 +13981,6 @@
"ext-pdo": "*", "ext-pdo": "*",
"ext-zip": "*" "ext-zip": "*"
}, },
"platform-dev": {}, "platform-dev": [],
"plugin-api-version": "2.6.0" "plugin-api-version": "2.6.0"
} }

View File

@ -25,7 +25,7 @@ return new class extends Migration
$table->json('permissions'); $table->json('permissions');
}); });
foreach (ApiKey::query() as $apiKey) { foreach (ApiKey::all() as $apiKey) {
$permissions = [ $permissions = [
Server::RESOURCE_NAME => intval($apiKey->r_servers ?? 0), Server::RESOURCE_NAME => intval($apiKey->r_servers ?? 0),
Node::RESOURCE_NAME => intval($apiKey->r_nodes ?? 0), Node::RESOURCE_NAME => intval($apiKey->r_nodes ?? 0),

View File

@ -115,11 +115,11 @@ Route::prefix('/servers/{server:uuid}')->middleware([ServerSubject::class, Authe
Route::prefix('/backups')->group(function () { Route::prefix('/backups')->group(function () {
Route::get('/', [Client\Servers\BackupController::class, 'index']); Route::get('/', [Client\Servers\BackupController::class, 'index']);
Route::post('/', [Client\Servers\BackupController::class, 'store']); Route::post('/', [Client\Servers\BackupController::class, 'store']);
Route::get('/{backup}', [Client\Servers\BackupController::class, 'view']); Route::get('/{backup:uuid}', [Client\Servers\BackupController::class, 'view']);
Route::get('/{backup}/download', [Client\Servers\BackupController::class, 'download']); Route::get('/{backup:uuid}/download', [Client\Servers\BackupController::class, 'download']);
Route::post('/{backup}/lock', [Client\Servers\BackupController::class, 'toggleLock']); Route::post('/{backup:uuid}/lock', [Client\Servers\BackupController::class, 'toggleLock']);
Route::post('/{backup}/restore', [Client\Servers\BackupController::class, 'restore']); Route::post('/{backup:uuid}/restore', [Client\Servers\BackupController::class, 'restore']);
Route::delete('/{backup}', [Client\Servers\BackupController::class, 'delete']); Route::delete('/{backup:uuid}', [Client\Servers\BackupController::class, 'delete']);
}); });
Route::prefix('/startup')->group(function () { Route::prefix('/startup')->group(function () {

View File

@ -24,7 +24,7 @@ Route::prefix('/servers/{server:uuid}')->group(function () {
}); });
Route::prefix('/backups')->group(function () { Route::prefix('/backups')->group(function () {
Route::get('/{backup}', Remote\Backups\BackupRemoteUploadController::class); Route::get('/{backup:uuid}', Remote\Backups\BackupRemoteUploadController::class);
Route::post('/{backup}', [Remote\Backups\BackupStatusController::class, 'index']); Route::post('/{backup:uuid}', [Remote\Backups\BackupStatusController::class, 'index']);
Route::post('/{backup}/restore', [Remote\Backups\BackupStatusController::class, 'restore']); Route::post('/{backup:uuid}/restore', [Remote\Backups\BackupStatusController::class, 'restore']);
}); });

View File

@ -100,7 +100,7 @@ class TwoFactorControllerTest extends ClientApiIntegrationTestCase
$tokens = RecoveryToken::query()->where('user_id', $user->id)->get(); $tokens = RecoveryToken::query()->where('user_id', $user->id)->get();
$this->assertCount(10, $tokens); $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. // Ensure the recovery tokens that were created include a "created_at" timestamp value on them.
$this->assertNotNull($tokens[0]->created_at); $this->assertNotNull($tokens[0]->created_at);