$expect
* @return T
*
* @noinspection PhpDocSignatureInspection
diff --git a/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php b/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php
index aedd5109ba..435a6d86bf 100644
--- a/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php
+++ b/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php
@@ -12,7 +12,7 @@ class StoreDatabaseHostRequest extends ApplicationApiRequest
protected int $permission = AdminAcl::WRITE;
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
return $rules ?? DatabaseHost::getRules();
}
diff --git a/app/Http/Requests/Api/Application/DatabaseHosts/UpdateDatabaseHostRequest.php b/app/Http/Requests/Api/Application/DatabaseHosts/UpdateDatabaseHostRequest.php
index 111560bbfd..f14b9b90ff 100644
--- a/app/Http/Requests/Api/Application/DatabaseHosts/UpdateDatabaseHostRequest.php
+++ b/app/Http/Requests/Api/Application/DatabaseHosts/UpdateDatabaseHostRequest.php
@@ -6,11 +6,11 @@
class UpdateDatabaseHostRequest extends StoreDatabaseHostRequest
{
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
/** @var DatabaseHost $databaseHost */
$databaseHost = $this->route()->parameter('database_host');
- return $rules ?? DatabaseHost::getRulesForUpdate($databaseHost->id);
+ return $rules ?? DatabaseHost::getRulesForUpdate($databaseHost);
}
}
diff --git a/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php b/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php
index 7f76ec4201..05e7d0a235 100644
--- a/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php
+++ b/app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php
@@ -9,11 +9,11 @@ class UpdateMountRequest extends StoreMountRequest
/**
* Apply validation rules to this request.
*/
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
/** @var Mount $mount */
$mount = $this->route()->parameter('mount');
- return Mount::getRulesForUpdate($mount->id);
+ return Mount::getRulesForUpdate($mount);
}
}
diff --git a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php
index 913204b836..eae3242335 100644
--- a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php
+++ b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php
@@ -15,7 +15,7 @@ class StoreNodeRequest extends ApplicationApiRequest
/**
* Validation rules to apply to this request.
*/
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
return collect($rules ?? Node::getRules())->only([
'public',
diff --git a/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php
index ab636dff02..5907860a2b 100644
--- a/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php
+++ b/app/Http/Requests/Api/Application/Nodes/UpdateNodeRequest.php
@@ -10,11 +10,11 @@ class UpdateNodeRequest extends StoreNodeRequest
* Apply validation rules to this request. Uses the parent class rules()
* function but passes in the rules for updating rather than creating.
*/
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
/** @var Node $node */
$node = $this->route()->parameter('node');
- return parent::rules(Node::getRulesForUpdate($node->id));
+ return parent::rules(Node::getRulesForUpdate($node));
}
}
diff --git a/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php b/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php
index 7968449a73..4cf01400ec 100644
--- a/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php
+++ b/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php
@@ -11,7 +11,7 @@ class StoreRoleRequest extends ApplicationApiRequest
protected int $permission = AdminAcl::WRITE;
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
return [
'name' => 'required|string',
diff --git a/app/Http/Requests/Api/Application/Users/AssignUserRolesRequest.php b/app/Http/Requests/Api/Application/Users/AssignUserRolesRequest.php
index c0af95102b..61e191c203 100644
--- a/app/Http/Requests/Api/Application/Users/AssignUserRolesRequest.php
+++ b/app/Http/Requests/Api/Application/Users/AssignUserRolesRequest.php
@@ -7,7 +7,7 @@ class AssignUserRolesRequest extends StoreUserRequest
/**
* Return the validation rules for this request.
*/
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
return [
'roles' => 'array',
diff --git a/app/Http/Requests/Api/Application/Users/StoreUserRequest.php b/app/Http/Requests/Api/Application/Users/StoreUserRequest.php
index 43603639c4..cf1b6c244e 100644
--- a/app/Http/Requests/Api/Application/Users/StoreUserRequest.php
+++ b/app/Http/Requests/Api/Application/Users/StoreUserRequest.php
@@ -15,7 +15,7 @@ class StoreUserRequest extends ApplicationApiRequest
/**
* Return the validation rules for this request.
*/
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
$rules = $rules ?? User::getRules();
diff --git a/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php b/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php
index ecccbdd5aa..9ec5067a50 100644
--- a/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php
+++ b/app/Http/Requests/Api/Application/Users/UpdateUserRequest.php
@@ -9,10 +9,10 @@ class UpdateUserRequest extends StoreUserRequest
/**
* Return the validation rules for this request.
*/
- public function rules(array $rules = null): array
+ public function rules(?array $rules = null): array
{
- $userId = $this->parameter('user', User::class)->id;
+ $user = $this->parameter('user', User::class);
- return parent::rules(User::getRulesForUpdate($userId));
+ return parent::rules(User::getRulesForUpdate($user));
}
}
diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php
index bd68fb8908..349db06168 100644
--- a/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php
+++ b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php
@@ -49,7 +49,7 @@ public function authorize(): bool
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
- protected function validatePermissionsCanBeAssigned(array $permissions)
+ protected function validatePermissionsCanBeAssigned(array $permissions): void
{
$user = $this->user();
/** @var \App\Models\Server $server */
diff --git a/app/Jobs/NodeStatistics.php b/app/Jobs/NodeStatistics.php
index 19fae9b9db..9546f0ba0d 100644
--- a/app/Jobs/NodeStatistics.php
+++ b/app/Jobs/NodeStatistics.php
@@ -42,5 +42,4 @@ public function handle(): void
}
}
}
-
}
diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php
index 6a660e5c05..158950c45d 100644
--- a/app/Jobs/Schedule/RunTaskJob.php
+++ b/app/Jobs/Schedule/RunTaskJob.php
@@ -90,7 +90,7 @@ public function handle(
/**
* Handle a failure while sending the action to the daemon or otherwise processing the job.
*/
- public function failed(\Exception $exception = null)
+ public function failed(?\Exception $exception = null): void
{
$this->markTaskNotQueued();
$this->markScheduleComplete();
@@ -99,7 +99,7 @@ public function failed(\Exception $exception = null)
/**
* Get the next task in the schedule and queue it for running after the defined period of wait time.
*/
- private function queueNextTask()
+ private function queueNextTask(): void
{
/** @var \App\Models\Task|null $nextTask */
$nextTask = Task::query()->where('schedule_id', $this->task->schedule_id)
@@ -121,7 +121,7 @@ private function queueNextTask()
/**
* Marks the parent schedule as being complete.
*/
- private function markScheduleComplete()
+ private function markScheduleComplete(): void
{
$this->task->schedule()->update([
'is_processing' => false,
@@ -132,7 +132,7 @@ private function markScheduleComplete()
/**
* Mark a specific task as no longer being queued.
*/
- private function markTaskNotQueued()
+ private function markTaskNotQueued(): void
{
$this->task->update(['is_queued' => false]);
}
diff --git a/app/Livewire/NodeSystemInformation.php b/app/Livewire/NodeSystemInformation.php
index b291902510..55f71f7ce5 100644
--- a/app/Livewire/NodeSystemInformation.php
+++ b/app/Livewire/NodeSystemInformation.php
@@ -3,19 +3,21 @@
namespace App\Livewire;
use App\Models\Node;
+use Illuminate\View\View;
use Livewire\Component;
class NodeSystemInformation extends Component
{
public Node $node;
+
public string $sizeClasses;
- public function render()
+ public function render(): View
{
return view('livewire.node-system-information');
}
- public function placeholder()
+ public function placeholder(): string
{
return <<<'HTML'
diff --git a/app/Models/ActivityLog.php b/app/Models/ActivityLog.php
index bb6149c142..20fb579e06 100644
--- a/app/Models/ActivityLog.php
+++ b/app/Models/ActivityLog.php
@@ -123,7 +123,7 @@ public function scopeForActor(Builder $builder, IlluminateModel $actor): Builder
*
* @see https://laravel.com/docs/9.x/eloquent#pruning-models
*/
- public function prunable()
+ public function prunable(): Builder
{
if (is_null(config('activity.prune_days'))) {
throw new \LogicException('Cannot prune activity logs: no "prune_days" configuration value is set.');
@@ -136,7 +136,7 @@ public function prunable()
* Boots the model event listeners. This will trigger an activity log event every
* time a new model is inserted which can then be captured and worked with as needed.
*/
- protected static function boot()
+ protected static function boot(): void
{
parent::boot();
@@ -149,7 +149,7 @@ protected static function boot()
});
}
- public function htmlable()
+ public function htmlable(): string
{
$user = $this->actor;
if (!$user instanceof User) {
diff --git a/app/Models/ActivityLogSubject.php b/app/Models/ActivityLogSubject.php
index 037d344f05..f6a7291fae 100644
--- a/app/Models/ActivityLogSubject.php
+++ b/app/Models/ActivityLogSubject.php
@@ -3,8 +3,9 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
-use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Database\Eloquent\SoftDeletingScope;
/**
* \App\Models\ActivityLogSubject.
@@ -25,6 +26,7 @@
class ActivityLogSubject extends Pivot
{
public $incrementing = true;
+
public $timestamps = false;
protected $table = 'activity_log_subjects';
@@ -36,15 +38,8 @@ public function activityLog(): BelongsTo
return $this->belongsTo(ActivityLog::class);
}
- public function subject()
+ public function subject(): MorphTo
{
- $morph = $this->morphTo();
-
- if (in_array(SoftDeletes::class, class_uses_recursive($morph::class))) {
- /** @var self|Backup|UserSSHKey $morph - cannot use traits in doc blocks */
- return $morph->withTrashed();
- }
-
- return $morph;
+ return $this->morphTo()->withoutGlobalScope(SoftDeletingScope::class);
}
}
diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php
index fbc81a1824..edf8ebb439 100644
--- a/app/Models/ApiKey.php
+++ b/app/Models/ApiKey.php
@@ -63,6 +63,7 @@ class ApiKey extends Model
* API representation using fractal.
*/
public const RESOURCE_NAME = 'api_key';
+
/**
* Maximum number of Api keys that a user can have.
*/
@@ -71,12 +72,22 @@ class ApiKey extends Model
* Different API keys that can exist on the system.
*/
public const TYPE_NONE = 0;
+
public const TYPE_ACCOUNT = 1;
+
public const TYPE_APPLICATION = 2;
+
+ /* @deprecated */
+ public const TYPE_DAEMON_USER = 3;
+
+ /* @deprecated */
+ public const TYPE_DAEMON_APPLICATION = 4;
+
/**
* The length of API key identifiers.
*/
public const IDENTIFIER_LENGTH = 16;
+
/**
* The length of the actual API key that is encrypted and stored
* in the database.
diff --git a/app/Models/Backup.php b/app/Models/Backup.php
index 3f2a931d1e..d6e4be65a2 100644
--- a/app/Models/Backup.php
+++ b/app/Models/Backup.php
@@ -32,6 +32,7 @@ class Backup extends Model
public const RESOURCE_NAME = 'backup';
public const ADAPTER_DAEMON = 'wings';
+
public const ADAPTER_AWS_S3 = 's3';
protected $table = 'backups';
diff --git a/app/Models/Egg.php b/app/Models/Egg.php
index e8c672064f..001be85e3f 100644
--- a/app/Models/Egg.php
+++ b/app/Models/Egg.php
@@ -70,6 +70,7 @@ class Egg extends Model
* than leaving it null.
*/
public const FEATURE_EULA_POPUP = 'eula';
+
public const FEATURE_FASTDL = 'fastdl';
/**
diff --git a/app/Models/Filters/AdminServerFilter.php b/app/Models/Filters/AdminServerFilter.php
index d3c1eea606..e90f17e081 100644
--- a/app/Models/Filters/AdminServerFilter.php
+++ b/app/Models/Filters/AdminServerFilter.php
@@ -11,9 +11,9 @@ class AdminServerFilter implements Filter
* A multi-column filter for the servers table that allows an administrative user to search
* across UUID, name, owner username, and owner email.
*
- * @param string $value
+ * @param string $value
*/
- public function __invoke(Builder $query, $value, string $property)
+ public function __invoke(Builder $query, $value, string $property): void
{
if ($query->getQuery()->from !== 'servers') {
throw new \BadMethodCallException('Cannot use the AdminServerFilter against a non-server model.');
diff --git a/app/Models/Filters/MultiFieldServerFilter.php b/app/Models/Filters/MultiFieldServerFilter.php
index da3f91f6d5..89bb15f49c 100644
--- a/app/Models/Filters/MultiFieldServerFilter.php
+++ b/app/Models/Filters/MultiFieldServerFilter.php
@@ -19,9 +19,9 @@ class MultiFieldServerFilter implements Filter
* search across multiple columns. This allows us to provide a very generic search ability for
* the frontend.
*
- * @param string $value
+ * @param string $value
*/
- public function __invoke(Builder $query, $value, string $property)
+ public function __invoke(Builder $query, $value, string $property): void
{
if ($query->getQuery()->from !== 'servers') {
throw new \BadMethodCallException('Cannot use the MultiFieldServerFilter against a non-server model.');
diff --git a/app/Models/Model.php b/app/Models/Model.php
index 536edf9b57..20bc2bf334 100644
--- a/app/Models/Model.php
+++ b/app/Models/Model.php
@@ -38,7 +38,7 @@ abstract class Model extends IlluminateModel
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
- protected static function boot()
+ protected static function boot(): void
{
parent::boot();
@@ -69,7 +69,7 @@ public function getRouteKeyName(): string
return 'uuid';
}
- protected function asDateTime($value)
+ protected function asDateTime($value): Carbon
{
$timezone = auth()->user()?->timezone ?? config('app.timezone', 'UTC');
@@ -135,11 +135,9 @@ public static function getRulesForField(string $field): array
* Returns the rules associated with the model, specifically for updating the given model
* rather than just creating it.
*/
- public static function getRulesForUpdate($model, string $column = 'id'): array
+ public static function getRulesForUpdate(self $model): array
{
- if ($model instanceof Model) {
- [$id, $column] = [$model->getKey(), $model->getKeyName()];
- }
+ [$id, $column] = [$model->getKey(), $model->getKeyName()];
$rules = static::getRules();
foreach ($rules as $key => &$data) {
diff --git a/app/Models/Mount.php b/app/Models/Mount.php
index 2236038059..60cb2b436c 100644
--- a/app/Models/Mount.php
+++ b/app/Models/Mount.php
@@ -70,7 +70,7 @@ public static function getRules(): array
/**
* Blacklisted source paths.
*/
- public static $invalidSourcePaths = [
+ public static array $invalidSourcePaths = [
'/etc/pelican',
'/var/lib/pelican/volumes',
'/srv/daemon-data',
@@ -79,7 +79,7 @@ public static function getRules(): array
/**
* Blacklisted target paths.
*/
- public static $invalidTargetPaths = [
+ public static array $invalidTargetPaths = [
'/home/container',
];
diff --git a/app/Models/Node.php b/app/Models/Node.php
index 0a9613196d..64f44e3af0 100644
--- a/app/Models/Node.php
+++ b/app/Models/Node.php
@@ -8,6 +8,7 @@
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
@@ -53,6 +54,7 @@ class Node extends Model
public const RESOURCE_NAME = 'node';
public const DAEMON_TOKEN_ID_LENGTH = 16;
+
public const DAEMON_TOKEN_LENGTH = 64;
/**
@@ -135,7 +137,9 @@ protected function casts(): array
}
public int $servers_sum_memory = 0;
+
public int $servers_sum_disk = 0;
+
public int $servers_sum_cpu = 0;
public function getRouteKeyName(): string
@@ -268,7 +272,7 @@ public function isViable(int $memory, int $disk, int $cpu): bool
return true;
}
- public static function getForServerCreation()
+ public static function getForServerCreation(): Collection
{
return self::with('allocations')->get()->map(function (Node $item) {
$filtered = $item->getRelation('allocations')->where('server_id', null)->map(function ($map) {
@@ -294,6 +298,7 @@ public function systemInformation(): array
{
return once(function () {
try {
+ // @phpstan-ignore-next-line
return resolve(DaemonConfigurationRepository::class)
->setNode($this)
->getSystemInformation(connectTimeout: 3);
@@ -330,7 +335,7 @@ public function serverStatuses(): array
return $statuses;
}
- public function statistics()
+ public function statistics(): array
{
$default = [
'memory_total' => 0,
diff --git a/app/Models/Objects/DeploymentObject.php b/app/Models/Objects/DeploymentObject.php
index b7a4ebaf3f..692ca74873 100644
--- a/app/Models/Objects/DeploymentObject.php
+++ b/app/Models/Objects/DeploymentObject.php
@@ -45,5 +45,4 @@ public function setTags(array $tags): self
return $this;
}
-
}
diff --git a/app/Models/Permission.php b/app/Models/Permission.php
index ab9fef2607..29cbd826d3 100644
--- a/app/Models/Permission.php
+++ b/app/Models/Permission.php
@@ -16,51 +16,81 @@ class Permission extends Model
* Constants defining different permissions available.
*/
public const ACTION_WEBSOCKET_CONNECT = 'websocket.connect';
+
public const ACTION_CONTROL_CONSOLE = 'control.console';
+
public const ACTION_CONTROL_START = 'control.start';
+
public const ACTION_CONTROL_STOP = 'control.stop';
+
public const ACTION_CONTROL_RESTART = 'control.restart';
public const ACTION_DATABASE_READ = 'database.read';
+
public const ACTION_DATABASE_CREATE = 'database.create';
+
public const ACTION_DATABASE_UPDATE = 'database.update';
+
public const ACTION_DATABASE_DELETE = 'database.delete';
+
public const ACTION_DATABASE_VIEW_PASSWORD = 'database.view_password';
public const ACTION_SCHEDULE_READ = 'schedule.read';
+
public const ACTION_SCHEDULE_CREATE = 'schedule.create';
+
public const ACTION_SCHEDULE_UPDATE = 'schedule.update';
+
public const ACTION_SCHEDULE_DELETE = 'schedule.delete';
public const ACTION_USER_READ = 'user.read';
+
public const ACTION_USER_CREATE = 'user.create';
+
public const ACTION_USER_UPDATE = 'user.update';
+
public const ACTION_USER_DELETE = 'user.delete';
public const ACTION_BACKUP_READ = 'backup.read';
+
public const ACTION_BACKUP_CREATE = 'backup.create';
+
public const ACTION_BACKUP_DELETE = 'backup.delete';
+
public const ACTION_BACKUP_DOWNLOAD = 'backup.download';
+
public const ACTION_BACKUP_RESTORE = 'backup.restore';
public const ACTION_ALLOCATION_READ = 'allocation.read';
+
public const ACTION_ALLOCATION_CREATE = 'allocation.create';
+
public const ACTION_ALLOCATION_UPDATE = 'allocation.update';
+
public const ACTION_ALLOCATION_DELETE = 'allocation.delete';
public const ACTION_FILE_READ = 'file.read';
+
public const ACTION_FILE_READ_CONTENT = 'file.read-content';
+
public const ACTION_FILE_CREATE = 'file.create';
+
public const ACTION_FILE_UPDATE = 'file.update';
+
public const ACTION_FILE_DELETE = 'file.delete';
+
public const ACTION_FILE_ARCHIVE = 'file.archive';
+
public const ACTION_FILE_SFTP = 'file.sftp';
public const ACTION_STARTUP_READ = 'startup.read';
+
public const ACTION_STARTUP_UPDATE = 'startup.update';
+
public const ACTION_STARTUP_DOCKER_IMAGE = 'startup.docker-image';
public const ACTION_SETTINGS_RENAME = 'settings.rename';
+
public const ACTION_SETTINGS_REINSTALL = 'settings.reinstall';
public const ACTION_ACTIVITY_READ = 'activity.read';
diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php
index ae7b9baecf..d6d19a8e14 100644
--- a/app/Models/Schedule.php
+++ b/app/Models/Schedule.php
@@ -2,8 +2,7 @@
namespace App\Models;
-use Cron\CronExpression;
-use Carbon\CarbonImmutable;
+use App\Helpers\Utilities;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -112,13 +111,9 @@ public function getRouteKeyName(): string
*
* @throws \Exception
*/
- public function getNextRunDate(): CarbonImmutable
+ public function getNextRunDate(): string
{
- $formatted = sprintf('%s %s %s %s %s', $this->cron_minute, $this->cron_hour, $this->cron_day_of_month, $this->cron_month, $this->cron_day_of_week);
-
- return CarbonImmutable::createFromTimestamp(
- (new CronExpression($formatted))->getNextRunDate()->getTimestamp()
- );
+ return Utilities::getScheduleNextRunDate($this->cron_minute, $this->cron_hour, $this->cron_day_of_month, $this->cron_month, $this->cron_day_of_week)->toDateTimeString();
}
/**
diff --git a/app/Models/Server.php b/app/Models/Server.php
index a0c341c6d4..67b7190324 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -71,6 +71,7 @@
* @property \App\Models\User $user
* @property \Illuminate\Database\Eloquent\Collection|\App\Models\EggVariable[] $variables
* @property int|null $variables_count
+ *
* @method static \Database\Factories\ServerFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Builder|Server newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Server newQuery()
@@ -101,6 +102,7 @@
* @method static \Illuminate\Database\Eloquent\Builder|Server whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server whereUuid($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server whereuuid_short($value)
+ *
* @property array|null $docker_labels
* @property string|null $ports
* @property-read mixed $condition
@@ -108,10 +110,12 @@
* @property-read int|null $egg_variables_count
* @property-read \Illuminate\Database\Eloquent\Collection $serverVariables
* @property-read int|null $server_variables_count
+ *
* @method static \Illuminate\Database\Eloquent\Builder|Server whereDockerLabels($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server whereInstalledAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server wherePorts($value)
* @method static \Illuminate\Database\Eloquent\Builder|Server whereUuidShort($value)
+ *
* @mixin \Eloquent
*/
class Server extends Model
@@ -363,7 +367,7 @@ public function resolveChildRouteBinding($childType, $value, $field)
*
* @throws ServerStateConflictException
*/
- public function validateCurrentState()
+ public function validateCurrentState(): void
{
if (
$this->isSuspended() ||
@@ -382,7 +386,7 @@ public function validateCurrentState()
* sure the server can be transferred and is not currently being transferred
* or installed.
*/
- public function validateTransferState()
+ public function validateTransferState(): void
{
if (
!$this->isInstalled() ||
diff --git a/app/Models/Task.php b/app/Models/Task.php
index 254d38ece8..202a3165ab 100644
--- a/app/Models/Task.php
+++ b/app/Models/Task.php
@@ -31,8 +31,11 @@ class Task extends Model
* The default actions that can exist for a task
*/
public const ACTION_POWER = 'power';
+
public const ACTION_COMMAND = 'command';
+
public const ACTION_BACKUP = 'backup';
+
public const ACTION_DELETE_FILES = 'delete_files';
/**
diff --git a/app/Models/User.php b/app/Models/User.php
index 395f4994fa..45545db348 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -99,6 +99,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
use Notifiable;
public const USER_LEVEL_USER = 0;
+
public const USER_LEVEL_ADMIN = 1;
/**
@@ -233,9 +234,9 @@ public function toReactObject(): array
/**
* Send the password reset notification.
*
- * @param string $token
+ * @param string $token
*/
- public function sendPasswordResetNotification($token)
+ public function sendPasswordResetNotification($token): void
{
Activity::event('auth:reset-password')
->withRequestMetadata()
@@ -248,7 +249,7 @@ public function sendPasswordResetNotification($token)
/**
* Store the username as a lowercase string.
*/
- public function setUsernameAttribute(string $value)
+ public function setUsernameAttribute(string $value): void
{
$this->attributes['username'] = mb_strtolower($value);
}
diff --git a/app/PHPStan/ForbiddenGlobalFunctionsRule.php b/app/PHPStan/ForbiddenGlobalFunctionsRule.php
new file mode 100644
index 0000000000..545cc60fb7
--- /dev/null
+++ b/app/PHPStan/ForbiddenGlobalFunctionsRule.php
@@ -0,0 +1,38 @@
+forbiddenFunctions = $forbiddenFunctions;
+ }
+
+ public function getNodeType(): string
+ {
+ return FuncCall::class;
+ }
+
+ public function processNode(Node $node, Scope $scope): array
+ {
+ /** @var FuncCall $node */
+ if ($node->name instanceof Node\Name) {
+ $functionName = (string) $node->name;
+ if (in_array($functionName, $this->forbiddenFunctions, true)) {
+ return [
+ sprintf('Usage of global function "%s" is forbidden.', $functionName),
+ ];
+ }
+ }
+
+ return [];
+ }
+}
diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php
index dbc2ae4028..dfa72c9b98 100644
--- a/app/Policies/ServerPolicy.php
+++ b/app/Policies/ServerPolicy.php
@@ -41,7 +41,7 @@ public function before(User $user, string $ability, string|Server $server): ?boo
* not call the before() function if there isn't a function matching the
* policy permission.
*/
- public function __call(string $name, mixed $arguments)
+ public function __call(string $name, mixed $arguments): void
{
// do nothing
}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 20a6b0dd51..4ead65cbbd 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -7,13 +7,13 @@
use App\Models\ApiKey;
use App\Models\Node;
use App\Models\User;
-use App\Services\Helpers\SoftwareVersionService;
use Dedoc\Scramble\Scramble;
use Dedoc\Scramble\Support\Generator\OpenApi;
use Dedoc\Scramble\Support\Generator\SecurityScheme;
use Filament\Support\Colors\Color;
use Filament\Support\Facades\FilamentColor;
use Illuminate\Database\Eloquent\Relations\Relation;
+use Illuminate\Foundation\Application;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Event;
@@ -30,11 +30,11 @@ class AppServiceProvider extends ServiceProvider
/**
* Bootstrap any application services.
*/
- public function boot(): void
+ public function boot(Application $app): void
{
- $versionData = app(SoftwareVersionService::class)->versionData();
- View::share('appVersion', $versionData['version'] ?? 'undefined');
- View::share('appIsGit', $versionData['is_git'] ?? false);
+ // TODO: remove when old admin area gets yeeted
+ View::share('appVersion', config('app.version'));
+ View::share('appIsGit', false);
Paginator::useBootstrap();
@@ -65,7 +65,7 @@ public function boot(): void
->asJson()
->withToken($node->daemon_token)
->withHeaders($headers)
- ->withOptions(['verify' => (bool) app()->environment('production')])
+ ->withOptions(['verify' => (bool) $app->environment('production')])
->timeout(config('panel.guzzle.timeout'))
->connectTimeout(config('panel.guzzle.connect_timeout'))
->baseUrl($node->getConnectionAddress())
diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php
index 0aef2f030a..f6b7c568b0 100644
--- a/app/Providers/Filament/AdminPanelProvider.php
+++ b/app/Providers/Filament/AdminPanelProvider.php
@@ -9,6 +9,7 @@
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Panel;
use Filament\PanelProvider;
+use Filament\Support\Enums\MaxWidth;
use Filament\Support\Facades\FilamentAsset;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
@@ -21,7 +22,7 @@
class AdminPanelProvider extends PanelProvider
{
- public function boot()
+ public function boot(): void
{
FilamentAsset::registerCssVariables([
'sidebar-width' => '16rem !important',
@@ -43,6 +44,8 @@ public function panel(Panel $panel): Panel
->brandLogo(config('app.logo'))
->brandLogoHeight('2rem')
->profile(EditProfile::class, false)
+ ->maxContentWidth(MaxWidth::ScreenTwoExtraLarge)
+ ->spa()
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->discoverClusters(in: app_path('Filament/Clusters'), for: 'App\\Filament\\Clusters')
diff --git a/app/Repositories/Daemon/DaemonBackupRepository.php b/app/Repositories/Daemon/DaemonBackupRepository.php
index bbe852e809..ff34c2acd2 100644
--- a/app/Repositories/Daemon/DaemonBackupRepository.php
+++ b/app/Repositories/Daemon/DaemonBackupRepository.php
@@ -2,6 +2,7 @@
namespace App\Repositories\Daemon;
+use Illuminate\Http\Client\Response;
use Webmozart\Assert\Assert;
use App\Models\Backup;
use App\Models\Server;
@@ -27,7 +28,7 @@ public function setBackupAdapter(string $adapter): self
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function backup(Backup $backup)
+ public function backup(Backup $backup): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -50,7 +51,7 @@ public function backup(Backup $backup)
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function restore(Backup $backup, string $url = null, bool $truncate = false)
+ public function restore(Backup $backup, ?string $url = null, bool $truncate = false): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -73,7 +74,7 @@ public function restore(Backup $backup, string $url = null, bool $truncate = fal
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function delete(Backup $backup)
+ public function delete(Backup $backup): Response
{
Assert::isInstanceOf($this->server, Server::class);
diff --git a/app/Repositories/Daemon/DaemonConfigurationRepository.php b/app/Repositories/Daemon/DaemonConfigurationRepository.php
index 52cda56dd4..910e95e58b 100644
--- a/app/Repositories/Daemon/DaemonConfigurationRepository.php
+++ b/app/Repositories/Daemon/DaemonConfigurationRepository.php
@@ -5,6 +5,7 @@
use App\Models\Node;
use GuzzleHttp\Exception\TransferException;
use App\Exceptions\Http\Connection\DaemonConnectionException;
+use Illuminate\Http\Client\Response;
class DaemonConfigurationRepository extends DaemonRepository
{
@@ -13,7 +14,7 @@ class DaemonConfigurationRepository extends DaemonRepository
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function getSystemInformation(?int $version = null, $connectTimeout = 5): array
+ public function getSystemInformation(?int $version = null, int $connectTimeout = 5): array
{
try {
$response = $this
@@ -34,7 +35,7 @@ public function getSystemInformation(?int $version = null, $connectTimeout = 5):
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function update(Node $node)
+ public function update(Node $node): Response
{
try {
return $this->getHttpClient()->post(
diff --git a/app/Repositories/Daemon/DaemonFileRepository.php b/app/Repositories/Daemon/DaemonFileRepository.php
index 7667f482e8..a65013fb30 100644
--- a/app/Repositories/Daemon/DaemonFileRepository.php
+++ b/app/Repositories/Daemon/DaemonFileRepository.php
@@ -3,6 +3,7 @@
namespace App\Repositories\Daemon;
use Carbon\CarbonInterval;
+use Illuminate\Http\Client\Response;
use Webmozart\Assert\Assert;
use App\Models\Server;
use GuzzleHttp\Exception\ClientException;
@@ -15,13 +16,13 @@ class DaemonFileRepository extends DaemonRepository
/**
* Return the contents of a given file.
*
- * @param int|null $notLargerThan the maximum content length in bytes
+ * @param int|null $notLargerThan the maximum content length in bytes
*
* @throws \GuzzleHttp\Exception\TransferException
* @throws \App\Exceptions\Http\Server\FileSizeTooLargeException
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function getContent(string $path, int $notLargerThan = null): string
+ public function getContent(string $path, ?int $notLargerThan = null): string
{
Assert::isInstanceOf($this->server, Server::class);
@@ -48,7 +49,7 @@ public function getContent(string $path, int $notLargerThan = null): string
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function putContent(string $path, string $content)
+ public function putContent(string $path, string $content): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -88,7 +89,7 @@ public function getDirectory(string $path): array
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function createDirectory(string $name, string $path)
+ public function createDirectory(string $name, string $path): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -110,7 +111,7 @@ public function createDirectory(string $name, string $path)
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function renameFiles(?string $root, array $files)
+ public function renameFiles(?string $root, array $files): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -132,7 +133,7 @@ public function renameFiles(?string $root, array $files)
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function copyFile(string $location)
+ public function copyFile(string $location): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -153,7 +154,7 @@ public function copyFile(string $location)
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function deleteFiles(?string $root, array $files)
+ public function deleteFiles(?string $root, array $files): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -203,7 +204,7 @@ public function compressFiles(?string $root, array $files): array
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function decompressFile(?string $root, string $file)
+ public function decompressFile(?string $root, string $file): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -229,7 +230,7 @@ public function decompressFile(?string $root, string $file)
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function chmodFiles(?string $root, array $files)
+ public function chmodFiles(?string $root, array $files): Response
{
Assert::isInstanceOf($this->server, Server::class);
@@ -251,7 +252,7 @@ public function chmodFiles(?string $root, array $files)
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function pull(string $url, ?string $directory, array $params = [])
+ public function pull(string $url, ?string $directory, array $params = []): Response
{
Assert::isInstanceOf($this->server, Server::class);
diff --git a/app/Repositories/Daemon/DaemonPowerRepository.php b/app/Repositories/Daemon/DaemonPowerRepository.php
index ca3e03c442..3b701be342 100644
--- a/app/Repositories/Daemon/DaemonPowerRepository.php
+++ b/app/Repositories/Daemon/DaemonPowerRepository.php
@@ -2,6 +2,7 @@
namespace App\Repositories\Daemon;
+use Illuminate\Http\Client\Response;
use Webmozart\Assert\Assert;
use App\Models\Server;
use GuzzleHttp\Exception\TransferException;
@@ -14,7 +15,7 @@ class DaemonPowerRepository extends DaemonRepository
*
* @throws \App\Exceptions\Http\Connection\DaemonConnectionException
*/
- public function send(string $action)
+ public function send(string $action): Response
{
Assert::isInstanceOf($this->server, Server::class);
diff --git a/app/Rules/Username.php b/app/Rules/Username.php
index 591df389c2..f888da1037 100644
--- a/app/Rules/Username.php
+++ b/app/Rules/Username.php
@@ -17,8 +17,8 @@ class Username implements Rule
*
* Allowed characters: a-z0-9_-.
*
- * @param string $attribute
- * @param mixed $value
+ * @param string $attribute
+ * @param mixed $value
*/
public function passes($attribute, $value): bool
{
diff --git a/app/Services/Acl/Api/AdminAcl.php b/app/Services/Acl/Api/AdminAcl.php
index dd58e317c6..90ec6480ae 100644
--- a/app/Services/Acl/Api/AdminAcl.php
+++ b/app/Services/Acl/Api/AdminAcl.php
@@ -17,7 +17,9 @@ class AdminAcl
* implements a read/write/none permissions scheme for all endpoints.
*/
public const NONE = 0;
+
public const READ = 1;
+
public const WRITE = 2;
/**
@@ -25,13 +27,21 @@ class AdminAcl
* set for each key. These are stored in the database as r_{resource}.
*/
public const RESOURCE_SERVERS = 'servers';
+
public const RESOURCE_NODES = 'nodes';
+
public const RESOURCE_ALLOCATIONS = 'allocations';
+
public const RESOURCE_USERS = 'users';
+
public const RESOURCE_EGGS = 'eggs';
+
public const RESOURCE_DATABASE_HOSTS = 'database_hosts';
+
public const RESOURCE_SERVER_DATABASES = 'server_databases';
+
public const RESOURCE_MOUNTS = 'mounts';
+
public const RESOURCE_ROLES = 'roles';
/**
diff --git a/app/Services/Activity/ActivityLogBatchService.php b/app/Services/Activity/ActivityLogBatchService.php
index cb2e91b5c4..df237286fb 100644
--- a/app/Services/Activity/ActivityLogBatchService.php
+++ b/app/Services/Activity/ActivityLogBatchService.php
@@ -7,6 +7,7 @@
class ActivityLogBatchService
{
protected int $transaction = 0;
+
protected ?string $uuid = null;
/**
diff --git a/app/Services/Activity/ActivityLogService.php b/app/Services/Activity/ActivityLogService.php
index 37b7ae43b5..7de267a3e7 100644
--- a/app/Services/Activity/ActivityLogService.php
+++ b/app/Services/Activity/ActivityLogService.php
@@ -64,7 +64,7 @@ public function description(?string $description): self
*
* @template T extends \Illuminate\Database\Eloquent\Model|\Illuminate\Contracts\Auth\Authenticatable
*
- * @param T|T[]|null $subjects
+ * @param T|T[]|null $subjects
*/
public function subject(...$subjects): self
{
@@ -100,8 +100,8 @@ public function actor(Model $actor): self
/**
* Sets a custom property on the activity log instance.
*
- * @param string|array $key
- * @param mixed $value
+ * @param string|array $key
+ * @param mixed $value
*/
public function property($key, $value = null): self
{
@@ -130,7 +130,7 @@ public function withRequestMetadata(): self
* performing this action it will be logged to the disk but will not interrupt
* the code flow.
*/
- public function log(string $description = null): ActivityLog
+ public function log(?string $description = null): ActivityLog
{
$activity = $this->getActivity();
@@ -168,7 +168,7 @@ public function clone(): self
*
* @throws \Throwable
*/
- public function transaction(\Closure $callback)
+ public function transaction(\Closure $callback): mixed
{
return $this->connection->transaction(function () use ($callback) {
$response = $callback($this);
diff --git a/app/Services/Allocations/AssignmentService.php b/app/Services/Allocations/AssignmentService.php
index 33ed4aa748..23f28e1224 100644
--- a/app/Services/Allocations/AssignmentService.php
+++ b/app/Services/Allocations/AssignmentService.php
@@ -16,10 +16,15 @@
class AssignmentService
{
public const CIDR_MAX_BITS = 25;
+
public const CIDR_MIN_BITS = 32;
+
public const PORT_FLOOR = 1024;
+
public const PORT_CEIL = 65535;
+
public const PORT_RANGE_LIMIT = 1000;
+
public const PORT_RANGE_REGEX = '/^(\d{4,5})-(\d{4,5})$/';
/**
@@ -38,7 +43,7 @@ public function __construct(protected ConnectionInterface $connection)
* @throws \App\Exceptions\Service\Allocation\PortOutOfRangeException
* @throws \App\Exceptions\Service\Allocation\TooManyPortsInRangeException
*/
- public function handle(Node $node, array $data, Server $server = null): array
+ public function handle(Node $node, array $data, ?Server $server = null): array
{
$explode = explode('/', $data['allocation_ip']);
if (count($explode) !== 1) {
diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php
index 447c575359..fa15db88ac 100644
--- a/app/Services/Backups/InitiateBackupService.php
+++ b/app/Services/Backups/InitiateBackupService.php
@@ -43,7 +43,7 @@ public function setIsLocked(bool $isLocked): self
/**
* Sets the files to be ignored by this backup.
*
- * @param string[]|null $ignored
+ * @param string[]|null $ignored
*/
public function setIgnoredFiles(?array $ignored): self
{
@@ -70,7 +70,7 @@ public function setIgnoredFiles(?array $ignored): self
* @throws \App\Exceptions\Service\Backup\TooManyBackupsException
* @throws \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException
*/
- public function handle(Server $server, string $name = null, bool $override = false): Backup
+ public function handle(Server $server, ?string $name = null, bool $override = false): Backup
{
$limit = config('backups.throttles.limit');
$period = config('backups.throttles.period');
diff --git a/app/Services/Deployment/FindViableNodesService.php b/app/Services/Deployment/FindViableNodesService.php
index 4cffc71d28..d95c08c640 100644
--- a/app/Services/Deployment/FindViableNodesService.php
+++ b/app/Services/Deployment/FindViableNodesService.php
@@ -17,7 +17,7 @@ class FindViableNodesService
* are tossed out, as are any nodes marked as non-public, meaning automatic
* deployments should not be done against them.
*/
- public function handle(int $memory = 0, int $disk = 0, int $cpu = 0, $tags = []): Collection
+ public function handle(int $memory = 0, int $disk = 0, int $cpu = 0, array $tags = []): Collection
{
$nodes = Node::query()
->withSum('servers', 'memory')
diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php
index dc54525a6e..e539a7f282 100644
--- a/app/Services/Eggs/Sharing/EggImporterService.php
+++ b/app/Services/Eggs/Sharing/EggImporterService.php
@@ -34,7 +34,7 @@ public function __construct(protected ConnectionInterface $connection)
*
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
*/
- public function fromFile(UploadedFile $file, Egg $egg = null): Egg
+ public function fromFile(UploadedFile $file, ?Egg $egg = null): Egg
{
$parsed = $this->parseFile($file);
@@ -75,7 +75,7 @@ public function fromFile(UploadedFile $file, Egg $egg = null): Egg
*
* @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable
*/
- public function fromUrl(string $url, Egg $egg = null): Egg
+ public function fromUrl(string $url, ?Egg $egg = null): Egg
{
$info = pathinfo($url);
$tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed();
diff --git a/app/Services/Files/DeleteFilesService.php b/app/Services/Files/DeleteFilesService.php
index 2b1be5fda1..1692923edd 100644
--- a/app/Services/Files/DeleteFilesService.php
+++ b/app/Services/Files/DeleteFilesService.php
@@ -19,6 +19,7 @@ public function __construct(
/**
* Deletes the given files.
+ *
* @throws DaemonConnectionException
*/
public function handle(Server $server, array $files): void
diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php
index 43b8c97364..7ac714ed51 100644
--- a/app/Services/Servers/ServerConfigurationStructureService.php
+++ b/app/Services/Servers/ServerConfigurationStructureService.php
@@ -93,5 +93,4 @@ protected function returnFormat(Server $server): array
return $response;
}
-
}
diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php
index 7fd3e8b67a..18eb790e53 100644
--- a/app/Services/Servers/ServerCreationService.php
+++ b/app/Services/Servers/ServerCreationService.php
@@ -45,7 +45,7 @@ public function __construct(
* @throws \Illuminate\Validation\ValidationException
* @throws \App\Exceptions\Service\Deployment\NoViableAllocationException
*/
- public function handle(array $data, DeploymentObject $deployment = null): Server
+ public function handle(array $data, ?DeploymentObject $deployment = null): Server
{
if (!isset($data['oom_killer']) && isset($data['oom_disabled'])) {
$data['oom_killer'] = !$data['oom_disabled'];
diff --git a/app/Services/Servers/SuspensionService.php b/app/Services/Servers/SuspensionService.php
index aef02b7b2e..0209db3fc5 100644
--- a/app/Services/Servers/SuspensionService.php
+++ b/app/Services/Servers/SuspensionService.php
@@ -12,6 +12,7 @@
class SuspensionService
{
public const ACTION_SUSPEND = 'suspend';
+
public const ACTION_UNSUSPEND = 'unsuspend';
/**
@@ -27,7 +28,7 @@ public function __construct(
*
* @throws \Throwable
*/
- public function toggle(Server $server, string $action = self::ACTION_SUSPEND)
+ public function toggle(Server $server, string $action = self::ACTION_SUSPEND): void
{
Assert::oneOf($action, [self::ACTION_SUSPEND, self::ACTION_UNSUSPEND]);
@@ -36,7 +37,9 @@ public function toggle(Server $server, string $action = self::ACTION_SUSPEND)
// suspended in the database. Additionally, nothing needs to happen if the server
// is not suspended, and we try to un-suspend the instance.
if ($isSuspending === $server->isSuspended()) {
- return Notification::make()->danger()->title('Failed!')->body('Server is already suspended!')->send();
+ Notification::make()->danger()->title('Failed!')->body('Server is already suspended!')->send();
+
+ return;
}
// Check if the server is currently being transferred.
diff --git a/app/Services/Servers/TransferServerService.php b/app/Services/Servers/TransferServerService.php
index 4312369dd7..8a7bcd3ea1 100644
--- a/app/Services/Servers/TransferServerService.php
+++ b/app/Services/Servers/TransferServerService.php
@@ -106,7 +106,7 @@ public function handle(Server $server, array $data): bool
/**
* Assigns the specified allocations to the specified server.
*/
- private function assignAllocationsToServer(Server $server, int $node_id, int $allocation_id, array $additional_allocations)
+ private function assignAllocationsToServer(Server $server, int $node_id, int $allocation_id, array $additional_allocations): void
{
$allocations = $additional_allocations;
$allocations[] = $allocation_id;
diff --git a/app/Services/Users/ToggleTwoFactorService.php b/app/Services/Users/ToggleTwoFactorService.php
index 64518e39bf..ca5df2dcbd 100644
--- a/app/Services/Users/ToggleTwoFactorService.php
+++ b/app/Services/Users/ToggleTwoFactorService.php
@@ -30,7 +30,7 @@ public function __construct(
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
* @throws \App\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid
*/
- public function handle(User $user, string $token, bool $toggleState = null): array
+ public function handle(User $user, string $token, ?bool $toggleState = null): array
{
$isValidToken = $this->google2FA->verifyKey($user->totp_secret, $token, config()->get('panel.auth.2fa.window'));
diff --git a/app/Traits/CheckMigrationsTrait.php b/app/Traits/CheckMigrationsTrait.php
index ee2de8f3a8..1f849e8aa9 100644
--- a/app/Traits/CheckMigrationsTrait.php
+++ b/app/Traits/CheckMigrationsTrait.php
@@ -12,7 +12,7 @@ trait CheckMigrationsTrait
protected function hasCompletedMigrations(): bool
{
/** @var Migrator $migrator */
- $migrator = app()->make('migrator');
+ $migrator = app()->make('migrator'); // @phpstan-ignore-line
$files = $migrator->getMigrationFiles(database_path('migrations'));
diff --git a/app/Traits/Controllers/PlainJavascriptInjection.php b/app/Traits/Controllers/PlainJavascriptInjection.php
index 4b5169296b..e77686776c 100644
--- a/app/Traits/Controllers/PlainJavascriptInjection.php
+++ b/app/Traits/Controllers/PlainJavascriptInjection.php
@@ -9,7 +9,7 @@ trait PlainJavascriptInjection
/**
* Injects statistics into javascript.
*/
- public function injectJavascript($data)
+ public function injectJavascript($data): void
{
\JavaScript::put($data);
}
diff --git a/app/Traits/EnvironmentWriterTrait.php b/app/Traits/EnvironmentWriterTrait.php
index 460d8000e1..cb5bef0476 100644
--- a/app/Traits/EnvironmentWriterTrait.php
+++ b/app/Traits/EnvironmentWriterTrait.php
@@ -22,6 +22,7 @@ public function escapeEnvironmentValue(string $value): string
/**
* Update the .env file for the application using the passed in values.
+ *
* @throws Exception
*/
public function writeToEnvironment(array $values = []): void
diff --git a/app/Traits/Helpers/AvailableLanguages.php b/app/Traits/Helpers/AvailableLanguages.php
index 53b45c5a60..aafffbbab1 100644
--- a/app/Traits/Helpers/AvailableLanguages.php
+++ b/app/Traits/Helpers/AvailableLanguages.php
@@ -51,6 +51,7 @@ public function isLanguageTranslated(string $countryCode = 'en'): bool
*/
private function getFilesystemInstance(): Filesystem
{
+ // @phpstan-ignore-next-line
return $this->filesystem = $this->filesystem ?: app()->make(Filesystem::class);
}
}
diff --git a/app/Transformers/Api/Application/BaseTransformer.php b/app/Transformers/Api/Application/BaseTransformer.php
index 62ee4ceeb6..ec4bb9d3cd 100644
--- a/app/Transformers/Api/Application/BaseTransformer.php
+++ b/app/Transformers/Api/Application/BaseTransformer.php
@@ -54,6 +54,7 @@ public function setRequest(Request $request): self
*/
public static function fromRequest(Request $request): self
{
+ // @phpstan-ignore-next-line
return app(static::class)->setRequest($request);
}
@@ -89,8 +90,7 @@ protected function authorize(string $resource): bool
*
* @template T of \App\Transformers\Api\Application\BaseTransformer
*
- * @param class-string $abstract
- *
+ * @param class-string $abstract
* @return T
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
diff --git a/app/Transformers/Api/Application/EggVariableTransformer.php b/app/Transformers/Api/Application/EggVariableTransformer.php
index 381b3102e1..d70299787b 100644
--- a/app/Transformers/Api/Application/EggVariableTransformer.php
+++ b/app/Transformers/Api/Application/EggVariableTransformer.php
@@ -15,7 +15,7 @@ public function getResourceName(): string
return Egg::RESOURCE_NAME;
}
- public function transform(EggVariable $model)
+ public function transform(EggVariable $model): array
{
return $model->toArray();
}
diff --git a/app/Transformers/Api/Application/MountTransformer.php b/app/Transformers/Api/Application/MountTransformer.php
index c658f30fa2..18dbe36dcd 100644
--- a/app/Transformers/Api/Application/MountTransformer.php
+++ b/app/Transformers/Api/Application/MountTransformer.php
@@ -22,7 +22,7 @@ public function getResourceName(): string
return Mount::RESOURCE_NAME;
}
- public function transform(Mount $model)
+ public function transform(Mount $model): array
{
return $model->toArray();
}
diff --git a/app/Transformers/Api/Application/ServerTransformer.php b/app/Transformers/Api/Application/ServerTransformer.php
index 90836cc248..b81baf2794 100644
--- a/app/Transformers/Api/Application/ServerTransformer.php
+++ b/app/Transformers/Api/Application/ServerTransformer.php
@@ -30,7 +30,7 @@ class ServerTransformer extends BaseTransformer
/**
* Perform dependency injection.
*/
- public function handle(EnvironmentService $environmentService)
+ public function handle(EnvironmentService $environmentService): void
{
$this->environmentService = $environmentService;
}
diff --git a/app/Transformers/Api/Client/ActivityLogTransformer.php b/app/Transformers/Api/Client/ActivityLogTransformer.php
index 58d5f13ae5..a54cb1f304 100644
--- a/app/Transformers/Api/Client/ActivityLogTransformer.php
+++ b/app/Transformers/Api/Client/ActivityLogTransformer.php
@@ -6,6 +6,7 @@
use App\Models\User;
use App\Models\ActivityLog;
use Illuminate\Database\Eloquent\Model;
+use League\Fractal\Resource\ResourceAbstract;
class ActivityLogTransformer extends BaseClientTransformer
{
@@ -34,7 +35,7 @@ public function transform(ActivityLog $model): array
];
}
- public function includeActor(ActivityLog $model)
+ public function includeActor(ActivityLog $model): ResourceAbstract
{
if (!$model->actor instanceof User) {
return $this->null();
@@ -111,7 +112,7 @@ protected function hasAdditionalMetadata(ActivityLog $model): bool
* Determines if the user can view the IP address in the output either because they are the
* actor that performed the action, or because they are an administrator on the Panel.
*/
- protected function canViewIP(Model $actor = null): bool
+ protected function canViewIP(?Model $actor = null): bool
{
return $actor?->is($this->request->user()) || $this->request->user()->isRootAdmin();
}
diff --git a/app/Transformers/Api/Client/BaseClientTransformer.php b/app/Transformers/Api/Client/BaseClientTransformer.php
index c490d1bfdc..9ee56cfaf1 100644
--- a/app/Transformers/Api/Client/BaseClientTransformer.php
+++ b/app/Transformers/Api/Client/BaseClientTransformer.php
@@ -24,7 +24,7 @@ public function getUser(): User
*
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
*/
- protected function authorize(string $ability, Server $server = null): bool
+ protected function authorize(string $ability, ?Server $server = null): bool
{
Assert::isInstanceOf($server, Server::class);
diff --git a/compose.yml b/compose.yml
index 23d2f2a397..6dab7145e3 100644
--- a/compose.yml
+++ b/compose.yml
@@ -5,9 +5,7 @@ x-common:
ADMIN_EMAIL: "USEYOUROWNEMAILHERE@example.com"
APP_DEBUG: "false"
- APP_ENVIRONMENT_ONLY: "false"
APP_ENV: "production"
- SESSION_DRIVER: "file"
mail:
&mail-environment
diff --git a/config/logging.php b/config/logging.php
new file mode 100644
index 0000000000..492c0a890c
--- /dev/null
+++ b/config/logging.php
@@ -0,0 +1,7 @@
+ env('LOG_CHANNEL', 'daily'),
+
+];
diff --git a/config/mail.php b/config/mail.php
index c6a0657b31..22b1b4e763 100644
--- a/config/mail.php
+++ b/config/mail.php
@@ -2,6 +2,13 @@
return [
+ 'default' => env('MAIL_MAILER', 'log'),
+
+ 'from' => [
+ 'address' => env('MAIL_FROM_ADDRESS', 'no-reply@example.com'),
+ 'name' => env('MAIL_FROM_NAME', 'Pelican Admin'),
+ ],
+
'mailers' => [
'mailgun' => [
'transport' => 'mailgun',
diff --git a/config/session.php b/config/session.php
new file mode 100644
index 0000000000..cf4a46ff83
--- /dev/null
+++ b/config/session.php
@@ -0,0 +1,9 @@
+ env('SESSION_DRIVER', 'file'),
+
+ 'cookie' => env('SESSION_COOKIE', 'pelican_session'),
+
+];
diff --git a/config/trustedproxy.php b/config/trustedproxy.php
new file mode 100644
index 0000000000..7e0166af8e
--- /dev/null
+++ b/config/trustedproxy.php
@@ -0,0 +1,28 @@
+getClientIp()
+ * always gets the originating client IP, no matter
+ * how many proxies that client's request has
+ * subsequently passed through.
+ */
+ 'proxies' => in_array(env('TRUSTED_PROXIES', []), ['*', '**']) ?
+ env('TRUSTED_PROXIES') : explode(',', env('TRUSTED_PROXIES') ?? ''),
+];
diff --git a/database/Seeders/DatabaseSeeder.php b/database/Seeders/DatabaseSeeder.php
index 2f7f6694e1..b2e7c20e41 100644
--- a/database/Seeders/DatabaseSeeder.php
+++ b/database/Seeders/DatabaseSeeder.php
@@ -10,7 +10,7 @@ class DatabaseSeeder extends Seeder
/**
* Run the database seeds.
*/
- public function run()
+ public function run(): void
{
$this->call(EggSeeder::class);
diff --git a/database/Seeders/EggSeeder.php b/database/Seeders/EggSeeder.php
index dcbc4f25c1..d6a3743b7b 100644
--- a/database/Seeders/EggSeeder.php
+++ b/database/Seeders/EggSeeder.php
@@ -34,7 +34,7 @@ public function __construct(
/**
* Run the egg seeder.
*/
- public function run()
+ public function run(): void
{
foreach (static::$imports as $import) {
/* @noinspection PhpParamsInspection */
@@ -45,7 +45,7 @@ public function run()
/**
* Loop through the list of egg files and import them.
*/
- protected function parseEggFiles($name)
+ protected function parseEggFiles($name): void
{
$files = new \DirectoryIterator(database_path('Seeders/eggs/' . kebab_case($name)));
diff --git a/phpstan.neon b/phpstan.neon
index 58b6c8e913..e5c462230c 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,20 +1,20 @@
includes:
- vendor/larastan/larastan/extension.neon
+rules:
+ - App\PHPStan\ForbiddenGlobalFunctionsRule
+
parameters:
paths:
- - app/
+ - app
- # Level 9 is the highest level
- level: 5
+ level: 6
ignoreErrors:
# Prologue\Alerts defines its methods from its configuration file dynamically
- '#^Call to an undefined method Prologue\\Alerts\\AlertsMessageBag::(danger|success|info|warning)\(\)\.$#'
-# excludePaths:
-# - ./*/*/FileToBeExcluded.php
-#
-# checkMissingIterableValueType: false
-
+ - '#no value type specified in iterable#'
+ - '#Unable to resolve the template type#'
+ - '#does not specify its types#'
diff --git a/pint.json b/pint.json
index 4d7ccf1135..99fa80d3a1 100644
--- a/pint.json
+++ b/pint.json
@@ -1,12 +1,8 @@
{
"preset": "laravel",
"rules": {
- "class_attributes_separation": false,
"concat_space": false,
"not_operator_with_successor_space": false,
- "nullable_type_declaration_for_default_null_value": false,
- "ordered_imports": false,
- "phpdoc_align": false,
- "phpdoc_separation": false
+ "ordered_imports": false
}
}
diff --git a/tests/Assertions/AssertsActivityLogged.php b/tests/Assertions/AssertsActivityLogged.php
index db352bc975..23a3461b1f 100644
--- a/tests/Assertions/AssertsActivityLogged.php
+++ b/tests/Assertions/AssertsActivityLogged.php
@@ -11,7 +11,7 @@
trait AssertsActivityLogged
{
/**
- * @param \Illuminate\Database\Eloquent\Model|array $subjects
+ * @param \Illuminate\Database\Eloquent\Model|array $subjects
*/
public function assertActivityFor(string $event, ?Model $actor, ...$subjects): void
{
diff --git a/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php b/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php
index e909bb41ec..d6dee00da0 100644
--- a/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php
+++ b/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php
@@ -47,7 +47,7 @@ protected function createTestResponse($response, $request): \Illuminate\Testing\
/**
* Returns a link to the specific resource using the client API.
*/
- protected function link(mixed $model, string $append = null): string
+ protected function link(mixed $model, ?string $append = null): string
{
switch (get_class($model)) {
case Server::class:
@@ -76,7 +76,7 @@ protected function link(mixed $model, string $append = null): string
* Asserts that the data passed through matches the output of the data from the transformer. This
* will remove the "relationships" key when performing the comparison.
*/
- protected function assertJsonTransformedWith(array $data, Model|EloquentModel $model)
+ protected function assertJsonTransformedWith(array $data, Model|EloquentModel $model): void
{
$reflect = new \ReflectionClass($model);
$transformer = sprintf('\\App\\Transformers\\Api\\Client\\%sTransformer', $reflect->getShortName());
diff --git a/tests/Integration/Api/Client/Server/PowerControllerTest.php b/tests/Integration/Api/Client/Server/PowerControllerTest.php
index 5cadb37729..94d3e7326c 100644
--- a/tests/Integration/Api/Client/Server/PowerControllerTest.php
+++ b/tests/Integration/Api/Client/Server/PowerControllerTest.php
@@ -14,7 +14,7 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
* an error in response. This checks against the specific permission needed to send
* the command to the server.
*
- * @param string[] $permissions
+ * @param string[] $permissions
*
* @dataProvider invalidPermissionDataProvider
*/
diff --git a/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php b/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php
index 031eedce70..3236266dcd 100644
--- a/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php
+++ b/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php
@@ -226,7 +226,7 @@ protected function getUsername(bool $long = false): string
/**
* Sets the authorization header for the rest of the test.
*/
- protected function setAuthorization(Node $node = null): void
+ protected function setAuthorization(?Node $node = null): void
{
$node = $node ?? $this->server->node;
diff --git a/tests/Traits/Http/RequestMockHelpers.php b/tests/Traits/Http/RequestMockHelpers.php
index 22f60e74f6..46a71e26a7 100644
--- a/tests/Traits/Http/RequestMockHelpers.php
+++ b/tests/Traits/Http/RequestMockHelpers.php
@@ -27,7 +27,7 @@ public function setRequestMockClass(string $class): void
/**
* Configure the user model that the request mock should return with.
*/
- public function setRequestUserModel(User $user = null): void
+ public function setRequestUserModel(?User $user = null): void
{
$this->request->shouldReceive('user')->andReturn($user);
}
@@ -80,7 +80,7 @@ protected function buildRequestMock(): void
*
* @deprecated
*/
- protected function setRequestUser(User $user = null): User
+ protected function setRequestUser(?User $user = null): User
{
$user = $user instanceof User ? $user : User::factory()->make();
$this->request->shouldReceive('user')->withNoArgs()->andReturn($user);
diff --git a/tests/Traits/Integration/CreatesTestModels.php b/tests/Traits/Integration/CreatesTestModels.php
index 12d6459347..0e28cb40e7 100644
--- a/tests/Traits/Integration/CreatesTestModels.php
+++ b/tests/Traits/Integration/CreatesTestModels.php
@@ -65,8 +65,7 @@ public function createServerModel(array $attributes = []): Server
* Generates a user and a server for that user. If an array of permissions is passed it
* is assumed that the user is actually a subuser of the server.
*
- * @param string[] $permissions
- *
+ * @param string[] $permissions
* @return array{\App\Models\User, \App\Models\Server}
*/
public function generateTestAccount(array $permissions = []): array