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". # 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/ # 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
@ -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 # 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/.env ./.env \
&& ln -s /pelican-data/database/database.sqlite ./database/database.sqlite \ && 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 # Create necessary directories
&& mkdir -p /pelican-data /var/run/supervisord /etc/supercronic \ && mkdir -p /pelican-data /var/run/supervisord /etc/supercronic \
# Finally allow www-data write permissions where necessary # Finally allow www-data write permissions where necessary
&& chown -R www-data:www-data /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \ && 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 && chmod -R u+rwX,g+rwX,o-rwx /pelican-data ./storage ./bootstrap/cache /var/run/supervisord
# Configure Supervisor # Configure Supervisor

View File

@ -1,10 +1,10 @@
# ================================ # ================================
# Stage 0: Build PHP Base Image # 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/ 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 public function run(): Result
{ {
$all = Node::query()->count(); $all = Node::all();
if ($all === 0) { if ($all->isEmpty()) {
$result = Result::make() $result = Result::make()
->notificationMessage(trans('admin/health.results.nodeversions.no_nodes_created')) ->notificationMessage(trans('admin/health.results.nodeversions.no_nodes_created'))
->shortSummary(trans('admin/health.results.nodeversions.no_nodes')); ->shortSummary(trans('admin/health.results.nodeversions.no_nodes'));
@ -25,16 +25,18 @@ class NodeVersionsCheck extends Check
return $result; return $result;
} }
$latestVersion = $this->versionService->latestWingsVersion(); $outdated = $all
->filter(fn (Node $node) => !isset($node->systemInformation()['exception']) && !$this->versionService->isLatestWings($node->systemInformation()['version']))
$outdated = Node::query()->get()
->filter(fn (Node $node) => !isset($node->systemInformation()['exception']) && $node->systemInformation()['version'] !== $latestVersion)
->count(); ->count();
$all = $all->count();
$latestVersion = $this->versionService->latestWingsVersion();
$result = Result::make() $result = Result::make()
->meta([ ->meta([
'all' => $all, 'all' => $all,
'outdated' => $outdated, '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])); ->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; return static::getModel()::count() ?: null;
} }
public static function getNavigationGroup(): ?string
{
return trans('admin/dashboard.server');
}
public static function getNavigationLabel(): string public static function getNavigationLabel(): string
{ {
return trans('admin/egg.nav_title'); return trans('admin/egg.nav_title');

View File

@ -30,11 +30,6 @@ class NodeResource extends Resource
return trans('admin/node.model_label_plural'); return trans('admin/node.model_label_plural');
} }
public static function getNavigationGroup(): ?string
{
return trans('admin/dashboard.server');
}
public static function getNavigationBadge(): ?string public static function getNavigationBadge(): ?string
{ {
return static::getModel()::count() ?: null; 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")) $cpu = collect(cache()->get("nodes.{$this->node->id}.cpu_percent"))
->slice(-10) ->slice(-10)
->map(fn ($value, $key) => [ ->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'), 'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
]) ])
->all(); ->all();

View File

@ -20,7 +20,7 @@ class NodeMemoryChart extends ChartWidget
{ {
$memUsed = collect(cache()->get("nodes.{$this->node->id}.memory_used"))->slice(-10) $memUsed = collect(cache()->get("nodes.{$this->node->id}.memory_used"))->slice(-10)
->map(fn ($value, $key) => [ ->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'), 'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
]) ])
->all(); ->all();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -130,8 +130,18 @@ class EditProfile extends BaseEditProfile
FileUpload::make('avatar') FileUpload::make('avatar')
->visible(fn () => config('panel.filament.avatar-provider') === 'local') ->visible(fn () => config('panel.filament.avatar-provider') === 'local')
->avatar() ->avatar()
->acceptedFileTypes(['image/png'])
->directory('avatars') ->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')) 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\Components\TextInput;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
class Startup extends ServerFormPage class Startup extends ServerFormPage
@ -100,7 +101,7 @@ class Startup extends ServerFormPage
->schema([ ->schema([
Repeater::make('server_variables') Repeater::make('server_variables')
->label('') ->label('')
->relationship('viewableServerVariables') ->relationship('serverVariables', fn (Builder $query) => $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort'))
->grid() ->grid()
->disabled(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server)) ->disabled(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server))
->reorderable(false)->addable(false)->deletable(false) ->reorderable(false)->addable(false)->deletable(false)

View File

@ -30,8 +30,7 @@ class ActivityResource extends Resource
/** @var Server $server */ /** @var Server $server */
$server = Filament::getTenant(); $server = Filament::getTenant();
return $server->activity() return ActivityLog::whereHas('subjects', fn (Builder $query) => $query->where('subject_id', $server->id))
->getQuery()
->whereNotIn('activity_logs.event', ActivityLog::DISABLED_EVENTS) ->whereNotIn('activity_logs.event', ActivityLog::DISABLED_EVENTS)
->when(config('activity.hide_admin_activity'), function (Builder $builder) use ($server) { ->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 // 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; namespace App\Filament\Server\Resources\BackupResource\Pages;
use App\Enums\BackupStatus;
use App\Enums\ServerState; use App\Enums\ServerState;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Server\Resources\BackupResource; use App\Filament\Server\Resources\BackupResource;
@ -70,13 +71,14 @@ class ListBackups extends ListRecords
->label('Created') ->label('Created')
->since() ->since()
->sortable(), ->sortable(),
IconColumn::make('is_successful') TextColumn::make('status')
->label('Successful') ->label('Status')
->boolean(), ->badge(),
IconColumn::make('is_locked') IconColumn::make('is_locked')
->visibleFrom('md') ->visibleFrom('md')
->label('Lock Status') ->label('Lock Status')
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock-open' : 'tabler-lock'), ->trueIcon('tabler-lock')
->falseIcon('tabler-lock-open'),
]) ])
->actions([ ->actions([
ActionGroup::make([ ActionGroup::make([
@ -84,12 +86,14 @@ class ListBackups extends ListRecords
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open') ->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open')
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server))
->label(fn (Backup $backup) => !$backup->is_locked ? 'Lock' : 'Unlock') ->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') Action::make('download')
->color('primary') ->color('primary')
->icon('tabler-download') ->icon('tabler-download')
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) ->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') Action::make('restore')
->color('success') ->color('success')
->icon('tabler-folder-up') ->icon('tabler-folder-up')
@ -138,12 +142,14 @@ class ListBackups extends ListRecords
return Notification::make() return Notification::make()
->title('Restoring Backup') ->title('Restoring Backup')
->send(); ->send();
}), })
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
DeleteAction::make('delete') 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 . '?') ->modalDescription(fn (Backup $backup) => 'Do you wish to delete, ' . $backup->name . '?')
->modalSubmitActionLabel('Delete Backup') ->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.') ->body($backup->name . ' created.')
->success() ->success()
->send(); ->send();
} catch (HttpException $e) { } catch (HttpException $e) {
return Notification::make() return Notification::make()
->danger() ->danger()

View File

@ -37,7 +37,7 @@ class StartupController extends ClientApiController
$startup = $this->startupCommandService->handle($server); $startup = $this->startupCommandService->handle($server);
return $this->fractal->collection( 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)) ->transformWith($this->getTransformer(EggVariableTransformer::class))
->addMeta([ ->addMeta([

View File

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

View File

@ -11,7 +11,7 @@ class UpdateServerDetailsRequest extends ServerWriteRequest
*/ */
public function rules(): array 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 [ return [
'external_id' => $rules['external_id'], 'external_id' => $rules['external_id'],

View File

@ -17,12 +17,12 @@ class UpdateServerStartupRequest extends ApplicationApiRequest
*/ */
public function rules(): array 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 [ return [
'startup' => 'sometimes|string', 'startup' => 'sometimes|string',
'environment' => 'present|array', 'environment' => 'present|array',
'egg' => $data['egg_id'], 'egg' => $rules['egg_id'],
'image' => 'sometimes|string', 'image' => 'sometimes|string',
'skip_scripts' => 'present|boolean', 'skip_scripts' => 'present|boolean',
]; ];

View File

@ -7,6 +7,8 @@ use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Eloquent\BackupQueryBuilder; use App\Eloquent\BackupQueryBuilder;
use App\Enums\BackupStatus;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -23,6 +25,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int $bytes * @property int $bytes
* @property string|null $upload_id * @property string|null $upload_id
* @property \Carbon\CarbonImmutable|null $completed_at * @property \Carbon\CarbonImmutable|null $completed_at
* @property BackupStatus $status
* @property \Carbon\CarbonImmutable $created_at * @property \Carbon\CarbonImmutable $created_at
* @property \Carbon\CarbonImmutable $updated_at * @property \Carbon\CarbonImmutable $updated_at
* @property \Carbon\CarbonImmutable|null $deleted_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 public function server(): BelongsTo
{ {
return $this->belongsTo(Server::class); return $this->belongsTo(Server::class);

View File

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

View File

@ -310,15 +310,6 @@ class Server extends Model implements Validatable
return $this->hasMany(ServerVariable::class); 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. * Gets information for the node associated with this server.
*/ */

View File

@ -2,6 +2,7 @@
namespace App\Notifications; namespace App\Notifications;
use App\Filament\Server\Pages\Console;
use App\Models\Server; use App\Models\Server;
use App\Models\User; use App\Models\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -27,6 +28,6 @@ class AddedToServer extends Notification implements ShouldQueue
->greeting('Hello ' . $notifiable->username . '!') ->greeting('Hello ' . $notifiable->username . '!')
->line('You have been added as a subuser for the following server, allowing you certain control over the server.') ->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) ->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 . '.') ->greeting('Hello ' . $notifiable->username . '.')
->line('You have been removed as a subuser for the following server.') ->line('You have been removed as a subuser for the following server.')
->line('Server Name: ' . $this->server->name) ->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; namespace App\Notifications;
use App\Filament\Server\Pages\Console;
use App\Models\User; use App\Models\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use App\Models\Server; use App\Models\Server;
use App\Filament\App\Resources\ServerResource\Pages\ListServers;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@ -28,6 +28,6 @@ class ServerInstalled extends Notification implements ShouldQueue
->greeting('Hello ' . $notifiable->username . '.') ->greeting('Hello ' . $notifiable->username . '.')
->line('Your server has finished installing and is now ready for you to use.') ->line('Your server has finished installing and is now ready for you to use.')
->line('Server Name: ' . $this->server->name) ->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), ->sort(24),
]) ])
->navigationGroups([ ->navigationGroups([
NavigationGroup::make(trans('admin/dashboard.server'))
->collapsible(false),
NavigationGroup::make(trans('admin/dashboard.user'))
->collapsible(false),
NavigationGroup::make(trans('admin/dashboard.advanced')), NavigationGroup::make(trans('admin/dashboard.advanced')),
]) ])
->sidebarCollapsibleOnDesktop() ->sidebarCollapsibleOnDesktop()

View File

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

View File

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

View File

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