Merge branch 'main' into fix/UserServerRelationManager

This commit is contained in:
notCharles 2025-04-22 15:51:29 -04:00
commit 74699c8a74
33 changed files with 208 additions and 157 deletions

View File

@ -4,11 +4,11 @@
# For those who want to build this Dockerfile themselves, uncomment lines 6-12 and replace "localhost:5000/base-php:$TARGETARCH" on lines 17 and 67 with "base".
# FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine as base
# FROM --platform=$TARGETOS/$TARGETARCH php:8.4-fpm-alpine as base
# ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
# RUN install-php-extensions bcmath gd intl zip opcache pcntl posix pdo_mysql
# RUN install-php-extensions bcmath gd intl zip opcache pcntl posix pdo_mysql pdo_pgsql
# RUN rm /usr/local/bin/install-php-extensions
@ -85,10 +85,13 @@ RUN chown root:www-data ./ \
# Symlink to env/database path, as www-data won't be able to write to webroot
&& ln -s /pelican-data/.env ./.env \
&& ln -s /pelican-data/database/database.sqlite ./database/database.sqlite \
&& mkdir -p /pelican-data/storage \
&& ln -sf /var/www/html/storage/app/public /var/www/html/public/storage \
&& ln -s /pelican-data/storage /var/www/html/storage/app/public/avatars \
# Create necessary directories
&& mkdir -p /pelican-data /var/run/supervisord /etc/supercronic \
# Finally allow www-data write permissions where necessary
&& chown -R www-data:www-data /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \
# Finally allow www-data write permissions where necessary
&& chown -R www-data:www-data /pelican-data ./storage ./bootstrap/cache /var/run/supervisord /var/www/html/public/storage \
&& chmod -R u+rwX,g+rwX,o-rwx /pelican-data ./storage ./bootstrap/cache /var/run/supervisord
# Configure Supervisor

View File

@ -1,10 +1,10 @@
# ================================
# Stage 0: Build PHP Base Image
# ================================
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
FROM --platform=$TARGETOS/$TARGETARCH php:8.4-fpm-alpine
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN install-php-extensions bcmath gd intl zip opcache pcntl posix pdo_mysql
RUN install-php-extensions bcmath gd intl zip opcache pcntl posix pdo_mysql pdo_pgsql
RUN rm /usr/local/bin/install-php-extensions
RUN rm /usr/local/bin/install-php-extensions

View File

@ -14,9 +14,9 @@ class NodeVersionsCheck extends Check
public function run(): Result
{
$all = Node::query()->count();
$all = Node::all();
if ($all === 0) {
if ($all->isEmpty()) {
$result = Result::make()
->notificationMessage(trans('admin/health.results.nodeversions.no_nodes_created'))
->shortSummary(trans('admin/health.results.nodeversions.no_nodes'));
@ -25,16 +25,18 @@ class NodeVersionsCheck extends Check
return $result;
}
$latestVersion = $this->versionService->latestWingsVersion();
$outdated = Node::query()->get()
->filter(fn (Node $node) => !isset($node->systemInformation()['exception']) && $node->systemInformation()['version'] !== $latestVersion)
$outdated = $all
->filter(fn (Node $node) => !isset($node->systemInformation()['exception']) && !$this->versionService->isLatestWings($node->systemInformation()['version']))
->count();
$all = $all->count();
$latestVersion = $this->versionService->latestWingsVersion();
$result = Result::make()
->meta([
'all' => $all,
'outdated' => $outdated,
'latestVersion' => $latestVersion,
])
->shortSummary($outdated === 0 ? trans('admin/health.results.nodeversions.all_up_to_date') : trans('admin/health.results.nodeversions.outdated', ['outdated' => $outdated, 'all' => $all]));

View File

@ -0,0 +1,37 @@
<?php
namespace App\Enums;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
enum BackupStatus: string implements HasColor, HasIcon, HasLabel
{
case InProgress = 'in_progress';
case Successful = 'successful';
case Failed = 'failed';
public function getIcon(): string
{
return match ($this) {
self::InProgress => 'tabler-circle-dashed',
self::Successful => 'tabler-circle-check',
self::Failed => 'tabler-circle-x',
};
}
public function getColor(): string
{
return match ($this) {
self::InProgress => 'primary',
self::Successful => 'success',
self::Failed => 'danger',
};
}
public function getLabel(): string
{
return str($this->value)->headline();
}
}

View File

@ -19,11 +19,6 @@ class EggResource extends Resource
return static::getModel()::count() ?: null;
}
public static function getNavigationGroup(): ?string
{
return trans('admin/dashboard.server');
}
public static function getNavigationLabel(): string
{
return trans('admin/egg.nav_title');

View File

@ -30,11 +30,6 @@ class NodeResource extends Resource
return trans('admin/node.model_label_plural');
}
public static function getNavigationGroup(): ?string
{
return trans('admin/dashboard.server');
}
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;

View File

@ -23,7 +23,7 @@ class NodeCpuChart extends ChartWidget
$cpu = collect(cache()->get("nodes.{$this->node->id}.cpu_percent"))
->slice(-10)
->map(fn ($value, $key) => [
'cpu' => Number::format($value * $threads, maxPrecision: 2),
'cpu' => round($value * $threads, 2),
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
])
->all();

View File

@ -20,7 +20,7 @@ class NodeMemoryChart extends ChartWidget
{
$memUsed = collect(cache()->get("nodes.{$this->node->id}.memory_used"))->slice(-10)
->map(fn ($value, $key) => [
'memory' => Number::format(config('panel.use_binary_prefix') ? $value / 1024 / 1024 / 1024 : $value / 1000 / 1000 / 1000, maxPrecision: 2),
'memory' => round(config('panel.use_binary_prefix') ? $value / 1024 / 1024 / 1024 : $value / 1000 / 1000 / 1000, 2),
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
])
->all();

View File

@ -4,7 +4,6 @@ namespace App\Filament\Admin\Resources\NodeResource\Widgets;
use App\Models\Node;
use Filament\Widgets\ChartWidget;
use Illuminate\Support\Number;
class NodeStorageChart extends ChartWidget
{
@ -46,8 +45,8 @@ class NodeStorageChart extends ChartWidget
$unused = $total - $used;
$used = Number::format($used, maxPrecision: 2);
$unused = Number::format($unused, maxPrecision: 2);
$used = round($used, 2);
$unused = round($unused, 2);
return [
'datasets' => [

View File

@ -48,7 +48,7 @@ class RoleResource extends Resource
public static function getNavigationGroup(): ?string
{
return trans('admin/dashboard.user');
return trans('admin/dashboard.advanced');
}
public static function getNavigationBadge(): ?string

View File

@ -29,11 +29,6 @@ class ServerResource extends Resource
return trans('admin/server.model_label_plural');
}
public static function getNavigationGroup(): ?string
{
return trans('admin/dashboard.server');
}
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;

View File

@ -426,7 +426,7 @@ class CreateServer extends CreateRecord
Repeater::make('server_variables')
->label('')
->relationship('serverVariables')
->relationship('serverVariables', fn (Builder $query) => $query->orderByPowerJoins('variable.sort'))
->saveRelationshipsBeforeChildrenUsing(null)
->saveRelationshipsUsing(null)
->grid(2)
@ -792,6 +792,7 @@ class CreateServer extends CreateRecord
]),
KeyValue::make('docker_labels')
->live()
->label('Container Labels')
->keyLabel(trans('admin/server.title'))
->valueLabel(trans('admin/server.description'))

View File

@ -486,6 +486,7 @@ class EditServer extends EditRecord
]),
KeyValue::make('docker_labels')
->live()
->label(trans('admin/server.container_labels'))
->keyLabel(trans('admin/server.title'))
->valueLabel(trans('admin/server.description'))
@ -595,9 +596,7 @@ class EditServer extends EditRecord
]);
}
return $query
->join('egg_variables', 'server_variables.variable_id', '=', 'egg_variables.id')
->orderBy('egg_variables.sort');
return $query->orderByPowerJoins('variable.sort');
})
->grid()
->mutateRelationshipDataBeforeSaveUsing(function (array &$data): array {

View File

@ -43,11 +43,6 @@ class UserResource extends Resource
return trans('admin/user.model_label_plural');
}
public static function getNavigationGroup(): ?string
{
return trans('admin/dashboard.user');
}
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;

View File

@ -130,8 +130,18 @@ class EditProfile extends BaseEditProfile
FileUpload::make('avatar')
->visible(fn () => config('panel.filament.avatar-provider') === 'local')
->avatar()
->acceptedFileTypes(['image/png'])
->directory('avatars')
->getUploadedFileNameForStorageUsing(fn () => $this->getUser()->id . '.png'),
->getUploadedFileNameForStorageUsing(fn () => $this->getUser()->id . '.png')
->hintAction(function (FileUpload $fileUpload) {
$path = $fileUpload->getDirectory() . '/' . $this->getUser()->id . '.png';
return Action::make('remove_avatar')
->icon('tabler-photo-minus')
->iconButton()
->hidden(fn () => !$fileUpload->getDisk()->exists($path))
->action(fn () => $fileUpload->getDisk()->delete($path));
}),
]),
Tab::make(trans('profile.tabs.oauth'))

View File

@ -18,6 +18,7 @@ use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Validator;
class Startup extends ServerFormPage
@ -100,7 +101,7 @@ class Startup extends ServerFormPage
->schema([
Repeater::make('server_variables')
->label('')
->relationship('viewableServerVariables')
->relationship('serverVariables', fn (Builder $query) => $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort'))
->grid()
->disabled(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server))
->reorderable(false)->addable(false)->deletable(false)

View File

@ -30,8 +30,7 @@ class ActivityResource extends Resource
/** @var Server $server */
$server = Filament::getTenant();
return $server->activity()
->getQuery()
return ActivityLog::whereHas('subjects', fn (Builder $query) => $query->where('subject_id', $server->id))
->whereNotIn('activity_logs.event', ActivityLog::DISABLED_EVENTS)
->when(config('activity.hide_admin_activity'), function (Builder $builder) use ($server) {
// We could do this with a query and a lot of joins, but that gets pretty

View File

@ -2,6 +2,7 @@
namespace App\Filament\Server\Resources\BackupResource\Pages;
use App\Enums\BackupStatus;
use App\Enums\ServerState;
use App\Facades\Activity;
use App\Filament\Server\Resources\BackupResource;
@ -70,13 +71,14 @@ class ListBackups extends ListRecords
->label('Created')
->since()
->sortable(),
IconColumn::make('is_successful')
->label('Successful')
->boolean(),
TextColumn::make('status')
->label('Status')
->badge(),
IconColumn::make('is_locked')
->visibleFrom('md')
->label('Lock Status')
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock-open' : 'tabler-lock'),
->trueIcon('tabler-lock')
->falseIcon('tabler-lock-open'),
])
->actions([
ActionGroup::make([
@ -84,12 +86,14 @@ class ListBackups extends ListRecords
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open')
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server))
->label(fn (Backup $backup) => !$backup->is_locked ? 'Lock' : 'Unlock')
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup)),
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup))
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
Action::make('download')
->color('primary')
->icon('tabler-download')
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server))
->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true),
->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true)
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
Action::make('restore')
->color('success')
->icon('tabler-folder-up')
@ -138,12 +142,14 @@ class ListBackups extends ListRecords
return Notification::make()
->title('Restoring Backup')
->send();
}),
})
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
DeleteAction::make('delete')
->disabled(fn (Backup $backup): bool => $backup->is_locked)
->disabled(fn (Backup $backup) => $backup->is_locked)
->modalDescription(fn (Backup $backup) => 'Do you wish to delete, ' . $backup->name . '?')
->modalSubmitActionLabel('Delete Backup')
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->delete($request, $server, $backup)),
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->delete($request, $server, $backup))
->visible(fn (Backup $backup) => $backup->status !== BackupStatus::InProgress),
]),
]);
}
@ -180,7 +186,6 @@ class ListBackups extends ListRecords
->body($backup->name . ' created.')
->success()
->send();
} catch (HttpException $e) {
return Notification::make()
->danger()

View File

@ -37,7 +37,7 @@ class StartupController extends ClientApiController
$startup = $this->startupCommandService->handle($server);
return $this->fractal->collection(
$server->variables()->orderBy('sort')->where('user_viewable', true)->get()
$server->variables()->where('user_viewable', true)->orderBy('sort')->get()
)
->transformWith($this->getTransformer(EggVariableTransformer::class))
->addMeta([

View File

@ -12,7 +12,7 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
*/
public function rules(): array
{
$rules = Server::getRulesForUpdate($this->parameter('server', Server::class));
$rules = $this->route() ? Server::getRulesForUpdate($this->parameter('server', Server::class)) : Server::getRules();
return [
'allocation' => $rules['allocation_id'],
@ -26,13 +26,17 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
'limits.threads' => $this->requiredToOptional('threads', $rules['threads'], true),
'limits.disk' => $this->requiredToOptional('disk', $rules['disk'], true),
// Legacy rules to maintain backwards compatable API support without requiring
// a major version bump.
// Deprecated - use limits.memory
'memory' => $this->requiredToOptional('memory', $rules['memory']),
// Deprecated - use limits.swap
'swap' => $this->requiredToOptional('swap', $rules['swap']),
// Deprecated - use limits.io
'io' => $this->requiredToOptional('io', $rules['io']),
// Deprecated - use limits.cpu
'cpu' => $this->requiredToOptional('cpu', $rules['cpu']),
// Deprecated - use limits.threads
'threads' => $this->requiredToOptional('threads', $rules['threads']),
// Deprecated - use limits.disk
'disk' => $this->requiredToOptional('disk', $rules['disk']),
'add_allocations' => 'bail|array',

View File

@ -11,7 +11,7 @@ class UpdateServerDetailsRequest extends ServerWriteRequest
*/
public function rules(): array
{
$rules = Server::getRulesForUpdate($this->parameter('server', Server::class));
$rules = $this->route() ? Server::getRulesForUpdate($this->parameter('server', Server::class)) : Server::getRules();
return [
'external_id' => $rules['external_id'],

View File

@ -17,12 +17,12 @@ class UpdateServerStartupRequest extends ApplicationApiRequest
*/
public function rules(): array
{
$data = Server::getRulesForUpdate($this->parameter('server', Server::class));
$rules = $this->route() ? Server::getRulesForUpdate($this->parameter('server', Server::class)) : Server::getRules();
return [
'startup' => 'sometimes|string',
'environment' => 'present|array',
'egg' => $data['egg_id'],
'egg' => $rules['egg_id'],
'image' => 'sometimes|string',
'skip_scripts' => 'present|boolean',
];

View File

@ -7,6 +7,8 @@ use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Eloquent\BackupQueryBuilder;
use App\Enums\BackupStatus;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -23,6 +25,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int $bytes
* @property string|null $upload_id
* @property \Carbon\CarbonImmutable|null $completed_at
* @property BackupStatus $status
* @property \Carbon\CarbonImmutable $created_at
* @property \Carbon\CarbonImmutable $updated_at
* @property \Carbon\CarbonImmutable|null $deleted_at
@ -79,6 +82,13 @@ class Backup extends Model implements Validatable
];
}
protected function status(): Attribute
{
return Attribute::make(
get: fn () => !$this->completed_at ? BackupStatus::InProgress : ($this->is_successful ? BackupStatus::Successful : BackupStatus::Failed),
);
}
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);

View File

@ -33,6 +33,8 @@ class File extends Model
protected $keyType = 'string';
protected int $sushiInsertChunkSize = 100;
public const ARCHIVE_MIMES = [
'application/vnd.rar', // .rar
'application/x-rar-compressed', // .rar (2)
@ -167,7 +169,7 @@ class File extends Model
throw new Exception($contents['error']);
}
return array_map(function ($file) {
$rows = array_map(function ($file) {
return [
'name' => $file['name'],
'created_at' => Carbon::parse($file['created'])->timezone('UTC'),
@ -181,6 +183,14 @@ class File extends Model
'mime_type' => $file['mime'],
];
}, $contents);
$rowCount = count($rows);
$limit = 999;
if ($rowCount > $limit) {
$this->sushiInsertChunkSize = min(floor($limit / count($this->getSchema())), $rowCount);
}
return $rows;
} catch (Exception $exception) {
report($exception);

View File

@ -310,15 +310,6 @@ class Server extends Model implements Validatable
return $this->hasMany(ServerVariable::class);
}
/** @deprecated use serverVariables */
public function viewableServerVariables(): HasMany
{
return $this->serverVariables()
->join('egg_variables', 'egg_variables.id', '=', 'server_variables.variable_id')
->orderBy('egg_variables.sort')
->where('egg_variables.user_viewable', true);
}
/**
* Gets information for the node associated with this server.
*/

View File

@ -2,6 +2,7 @@
namespace App\Notifications;
use App\Filament\Server\Pages\Console;
use App\Models\Server;
use App\Models\User;
use Illuminate\Bus\Queueable;
@ -27,6 +28,6 @@ class AddedToServer extends Notification implements ShouldQueue
->greeting('Hello ' . $notifiable->username . '!')
->line('You have been added as a subuser for the following server, allowing you certain control over the server.')
->line('Server Name: ' . $this->server->name)
->action('Visit Server', url('/server/' . $this->server->uuid_short));
->action('Visit Server', Console::getUrl(panel: 'server', tenant: $this->server));
}
}

View File

@ -28,6 +28,6 @@ class RemovedFromServer extends Notification implements ShouldQueue
->greeting('Hello ' . $notifiable->username . '.')
->line('You have been removed as a subuser for the following server.')
->line('Server Name: ' . $this->server->name)
->action('Visit Panel', config('app.url'));
->action('Visit Panel', url(''));
}
}

View File

@ -2,10 +2,10 @@
namespace App\Notifications;
use App\Filament\Server\Pages\Console;
use App\Models\User;
use Illuminate\Bus\Queueable;
use App\Models\Server;
use App\Filament\App\Resources\ServerResource\Pages\ListServers;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
@ -28,6 +28,6 @@ class ServerInstalled extends Notification implements ShouldQueue
->greeting('Hello ' . $notifiable->username . '.')
->line('Your server has finished installing and is now ready for you to use.')
->line('Server Name: ' . $this->server->name)
->action('Login and Begin Using', ListServers::getUrl());
->action('Login and Begin Using', Console::getUrl(panel: 'server', tenant: $this->server));
}
}

View File

@ -53,10 +53,6 @@ class AdminPanelProvider extends PanelProvider
->sort(24),
])
->navigationGroups([
NavigationGroup::make(trans('admin/dashboard.server'))
->collapsible(false),
NavigationGroup::make(trans('admin/dashboard.user'))
->collapsible(false),
NavigationGroup::make(trans('admin/dashboard.advanced')),
])
->sidebarCollapsibleOnDesktop()

View File

@ -16,7 +16,7 @@
"doctrine/dbal": "~3.6.0",
"filament/filament": "^3.3",
"guzzlehttp/guzzle": "^7.9",
"laravel/framework": "^12.8",
"laravel/framework": "^12.9",
"laravel/helpers": "^1.7",
"laravel/sanctum": "^4.0.2",
"laravel/socialite": "^5.19",

146
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1a12d9151f0ded4224c11d94f6ef755c",
"content-hash": "f2cbc3561aca38dd0e3247834434ce01",
"packages": [
{
"name": "abdelhamiderrahmouni/filament-monaco-editor",
@ -1020,16 +1020,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.342.23",
"version": "3.342.27",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "a5e9ba23ffecf1f71d8cfb177ef5cb3fbe2ecf05"
"reference": "4c18299000c34ab4903100fe68721d6f26c7cdf2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a5e9ba23ffecf1f71d8cfb177ef5cb3fbe2ecf05",
"reference": "a5e9ba23ffecf1f71d8cfb177ef5cb3fbe2ecf05",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4c18299000c34ab4903100fe68721d6f26c7cdf2",
"reference": "4c18299000c34ab4903100fe68721d6f26c7cdf2",
"shasum": ""
},
"require": {
@ -1111,9 +1111,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.342.23"
"source": "https://github.com/aws/aws-sdk-php/tree/3.342.27"
},
"time": "2025-04-08T18:35:41+00:00"
"time": "2025-04-14T18:08:09+00:00"
},
{
"name": "blade-ui-kit/blade-heroicons",
@ -1758,16 +1758,16 @@
},
{
"name": "dedoc/scramble",
"version": "v0.12.17",
"version": "v0.12.18",
"source": {
"type": "git",
"url": "https://github.com/dedoc/scramble.git",
"reference": "f71afa07e485548f9c3743b69f7859bb4d899600"
"reference": "a107fd82242abd765a9a22652bfa13f65e77e209"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dedoc/scramble/zipball/f71afa07e485548f9c3743b69f7859bb4d899600",
"reference": "f71afa07e485548f9c3743b69f7859bb4d899600",
"url": "https://api.github.com/repos/dedoc/scramble/zipball/a107fd82242abd765a9a22652bfa13f65e77e209",
"reference": "a107fd82242abd765a9a22652bfa13f65e77e209",
"shasum": ""
},
"require": {
@ -1822,7 +1822,7 @@
],
"support": {
"issues": "https://github.com/dedoc/scramble/issues",
"source": "https://github.com/dedoc/scramble/tree/v0.12.17"
"source": "https://github.com/dedoc/scramble/tree/v0.12.18"
},
"funding": [
{
@ -1830,7 +1830,7 @@
"type": "github"
}
],
"time": "2025-04-04T06:45:52+00:00"
"time": "2025-04-14T14:01:50+00:00"
},
{
"name": "dflydev/dot-access-data",
@ -2986,16 +2986,16 @@
},
{
"name": "firebase/php-jwt",
"version": "v6.11.0",
"version": "v6.11.1",
"source": {
"type": "git",
"url": "https://github.com/firebase/php-jwt.git",
"reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712"
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/8f718f4dfc9c5d5f0c994cdfd103921b43592712",
"reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
"shasum": ""
},
"require": {
@ -3043,9 +3043,9 @@
],
"support": {
"issues": "https://github.com/firebase/php-jwt/issues",
"source": "https://github.com/firebase/php-jwt/tree/v6.11.0"
"source": "https://github.com/firebase/php-jwt/tree/v6.11.1"
},
"time": "2025-01-23T05:11:06+00:00"
"time": "2025-04-09T20:32:01+00:00"
},
{
"name": "fruitcake/php-cors",
@ -3714,16 +3714,16 @@
},
{
"name": "laravel/framework",
"version": "v12.8.1",
"version": "v12.9.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "d1ea3566f6e0cad34834c6d18db0bf995438eb87"
"reference": "3db59aa0f382c349c78a92f3e5b5522e00e3301b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/d1ea3566f6e0cad34834c6d18db0bf995438eb87",
"reference": "d1ea3566f6e0cad34834c6d18db0bf995438eb87",
"url": "https://api.github.com/repos/laravel/framework/zipball/3db59aa0f382c349c78a92f3e5b5522e00e3301b",
"reference": "3db59aa0f382c349c78a92f3e5b5522e00e3301b",
"shasum": ""
},
"require": {
@ -3832,7 +3832,7 @@
"league/flysystem-sftp-v3": "^3.25.1",
"mockery/mockery": "^1.6.10",
"orchestra/testbench-core": "^10.0.0",
"pda/pheanstalk": "^5.0.6",
"pda/pheanstalk": "^5.0.6|^7.0.0",
"php-http/discovery": "^1.15",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
@ -3925,7 +3925,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-04-08T19:58:59+00:00"
"time": "2025-04-16T15:44:19+00:00"
},
{
"name": "laravel/helpers",
@ -5400,16 +5400,16 @@
},
{
"name": "livewire/livewire",
"version": "v3.6.2",
"version": "v3.6.3",
"source": {
"type": "git",
"url": "https://github.com/livewire/livewire.git",
"reference": "8f8914731f5eb43b6bb145d87c8d5a9edfc89313"
"reference": "56aa1bb63a46e06181c56fa64717a7287e19115e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/livewire/livewire/zipball/8f8914731f5eb43b6bb145d87c8d5a9edfc89313",
"reference": "8f8914731f5eb43b6bb145d87c8d5a9edfc89313",
"url": "https://api.github.com/repos/livewire/livewire/zipball/56aa1bb63a46e06181c56fa64717a7287e19115e",
"reference": "56aa1bb63a46e06181c56fa64717a7287e19115e",
"shasum": ""
},
"require": {
@ -5464,7 +5464,7 @@
"description": "A front-end framework for Laravel.",
"support": {
"issues": "https://github.com/livewire/livewire/issues",
"source": "https://github.com/livewire/livewire/tree/v3.6.2"
"source": "https://github.com/livewire/livewire/tree/v3.6.3"
},
"funding": [
{
@ -5472,7 +5472,7 @@
"type": "github"
}
],
"time": "2025-03-12T20:24:15+00:00"
"time": "2025-04-12T22:26:52+00:00"
},
{
"name": "masterminds/html5",
@ -6501,16 +6501,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "5.6.1",
"version": "5.6.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8"
"reference": "92dde6a5919e34835c506ac8c523ef095a95ed62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8",
"reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62",
"reference": "92dde6a5919e34835c506ac8c523ef095a95ed62",
"shasum": ""
},
"require": {
@ -6559,9 +6559,9 @@
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.1"
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2"
},
"time": "2024-12-07T09:39:29+00:00"
"time": "2025-04-13T19:20:35+00:00"
},
{
"name": "phpdocumentor/type-resolver",
@ -8464,16 +8464,16 @@
},
{
"name": "spatie/laravel-data",
"version": "4.14.1",
"version": "4.15.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-data.git",
"reference": "edd61b4dca5acdcfd1e3b7f2c19b75e83730f87c"
"reference": "cb97afe6c0dadeb2e76ea1b7220cd04ed33dcca9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-data/zipball/edd61b4dca5acdcfd1e3b7f2c19b75e83730f87c",
"reference": "edd61b4dca5acdcfd1e3b7f2c19b75e83730f87c",
"url": "https://api.github.com/repos/spatie/laravel-data/zipball/cb97afe6c0dadeb2e76ea1b7220cd04ed33dcca9",
"reference": "cb97afe6c0dadeb2e76ea1b7220cd04ed33dcca9",
"shasum": ""
},
"require": {
@ -8486,6 +8486,7 @@
"require-dev": {
"fakerphp/faker": "^1.14",
"friendsofphp/php-cs-fixer": "^3.0",
"inertiajs/inertia-laravel": "^2.0",
"livewire/livewire": "^3.0",
"mockery/mockery": "^1.6",
"nesbot/carbon": "^2.63|^3.0",
@ -8534,7 +8535,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-data/issues",
"source": "https://github.com/spatie/laravel-data/tree/4.14.1"
"source": "https://github.com/spatie/laravel-data/tree/4.15.1"
},
"funding": [
{
@ -8542,7 +8543,7 @@
"type": "github"
}
],
"time": "2025-03-17T13:54:28+00:00"
"time": "2025-04-10T06:06:27+00:00"
},
{
"name": "spatie/laravel-fractal",
@ -8720,16 +8721,16 @@
},
{
"name": "spatie/laravel-package-tools",
"version": "1.92.0",
"version": "1.92.4",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-package-tools.git",
"reference": "dd46cd0ed74015db28822d88ad2e667f4496a6f6"
"reference": "d20b1969f836d210459b78683d85c9cd5c5f508c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/dd46cd0ed74015db28822d88ad2e667f4496a6f6",
"reference": "dd46cd0ed74015db28822d88ad2e667f4496a6f6",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/d20b1969f836d210459b78683d85c9cd5c5f508c",
"reference": "d20b1969f836d210459b78683d85c9cd5c5f508c",
"shasum": ""
},
"require": {
@ -8740,6 +8741,7 @@
"mockery/mockery": "^1.5",
"orchestra/testbench": "^7.7|^8.0|^9.0|^10.0",
"pestphp/pest": "^1.23|^2.1|^3.1",
"phpunit/php-code-coverage": "^9.0|^10.0|^11.0",
"phpunit/phpunit": "^9.5.24|^10.5|^11.5",
"spatie/pest-plugin-test-time": "^1.1|^2.2"
},
@ -8768,7 +8770,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-package-tools/issues",
"source": "https://github.com/spatie/laravel-package-tools/tree/1.92.0"
"source": "https://github.com/spatie/laravel-package-tools/tree/1.92.4"
},
"funding": [
{
@ -8776,20 +8778,20 @@
"type": "github"
}
],
"time": "2025-03-27T08:34:10+00:00"
"time": "2025-04-11T15:27:14+00:00"
},
{
"name": "spatie/laravel-permission",
"version": "6.16.0",
"version": "6.17.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-permission.git",
"reference": "4fa03c06509e037a4d42c131d0f181e3e4bbd483"
"reference": "02ada8f638b643713fa2fb543384738e27346ddb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-permission/zipball/4fa03c06509e037a4d42c131d0f181e3e4bbd483",
"reference": "4fa03c06509e037a4d42c131d0f181e3e4bbd483",
"url": "https://api.github.com/repos/spatie/laravel-permission/zipball/02ada8f638b643713fa2fb543384738e27346ddb",
"reference": "02ada8f638b643713fa2fb543384738e27346ddb",
"shasum": ""
},
"require": {
@ -8851,7 +8853,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-permission/issues",
"source": "https://github.com/spatie/laravel-permission/tree/6.16.0"
"source": "https://github.com/spatie/laravel-permission/tree/6.17.0"
},
"funding": [
{
@ -8859,20 +8861,20 @@
"type": "github"
}
],
"time": "2025-02-28T20:29:57+00:00"
"time": "2025-04-08T15:06:14+00:00"
},
{
"name": "spatie/laravel-query-builder",
"version": "6.3.1",
"version": "6.3.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-query-builder.git",
"reference": "465d9b7364590c9ae3ee3738ff8a293e685dd588"
"reference": "3675f7bace346dc5243f58fa7c531e36471200f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/465d9b7364590c9ae3ee3738ff8a293e685dd588",
"reference": "465d9b7364590c9ae3ee3738ff8a293e685dd588",
"url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/3675f7bace346dc5243f58fa7c531e36471200f4",
"reference": "3675f7bace346dc5243f58fa7c531e36471200f4",
"shasum": ""
},
"require": {
@ -8932,7 +8934,7 @@
"type": "custom"
}
],
"time": "2025-02-19T07:14:53+00:00"
"time": "2025-04-16T07:30:03+00:00"
},
{
"name": "spatie/php-structure-discoverer",
@ -13061,16 +13063,16 @@
},
{
"name": "laravel/pint",
"version": "v1.21.2",
"version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "370772e7d9e9da087678a0edf2b11b6960e40558"
"reference": "7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/370772e7d9e9da087678a0edf2b11b6960e40558",
"reference": "370772e7d9e9da087678a0edf2b11b6960e40558",
"url": "https://api.github.com/repos/laravel/pint/zipball/7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36",
"reference": "7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36",
"shasum": ""
},
"require": {
@ -13081,9 +13083,9 @@
"php": "^8.2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.72.0",
"friendsofphp/php-cs-fixer": "^3.75.0",
"illuminate/view": "^11.44.2",
"larastan/larastan": "^3.2.0",
"larastan/larastan": "^3.3.1",
"laravel-zero/framework": "^11.36.1",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.3",
@ -13123,7 +13125,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2025-03-14T22:31:42+00:00"
"time": "2025-04-08T22:11:45+00:00"
},
{
"name": "laravel/sail",
@ -13814,16 +13816,16 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.11",
"version": "2.1.12",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30"
"reference": "96dde49e967c0c22812bcfa7bda4ff82c09f3b0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30",
"reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/96dde49e967c0c22812bcfa7bda4ff82c09f3b0c",
"reference": "96dde49e967c0c22812bcfa7bda4ff82c09f3b0c",
"shasum": ""
},
"require": {
@ -13868,7 +13870,7 @@
"type": "github"
}
],
"time": "2025-03-24T13:45:00+00:00"
"time": "2025-04-16T13:19:18+00:00"
},
{
"name": "phpunit/php-code-coverage",

View File

@ -4,8 +4,6 @@ return [
'heading' => 'Welcome to Pelican!',
'version' => 'Version: :version',
'advanced' => 'Advanced',
'server' => 'Server',
'user' => 'User',
'sections' => [
'intro-developers' => [
'heading' => 'Information for Developers',

View File

@ -42,6 +42,9 @@ abstract class TestCase extends BaseTestCase
*/
protected function tearDown(): void
{
restore_exception_handler();
restore_error_handler();
parent::tearDown();
Carbon::setTestNow();