mirror of
https://github.com/pelican-dev/panel.git
synced 2025-11-08 14:39:35 +01:00
Merge branch 'main' into vehikl/singleton
This commit is contained in:
commit
234cdda1e1
@ -18,6 +18,17 @@ class QueueWorkerServiceCommand extends Command
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
if (@file_exists('/.dockerenv')) {
|
||||
$result = Process::run('supervisorctl restart queue-worker');
|
||||
if ($result->failed()) {
|
||||
$this->error('Error restarting service: ' . $result->errorOutput());
|
||||
|
||||
return;
|
||||
}
|
||||
$this->line('Queue worker service file updated successfully.');
|
||||
|
||||
return;
|
||||
}
|
||||
$serviceName = $this->option('service-name') ?? $this->ask('Queue worker service name', 'pelican-queue');
|
||||
$path = '/etc/systemd/system/' . $serviceName . '.service';
|
||||
|
||||
|
||||
@ -88,7 +88,7 @@ enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
|
||||
|
||||
public function isStartable(): bool
|
||||
{
|
||||
return !in_array($this, [ContainerStatus::Running, ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting]);
|
||||
return !in_array($this, [ContainerStatus::Running, ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting, ContainerStatus::Missing]);
|
||||
}
|
||||
|
||||
public function isRestartable(): bool
|
||||
@ -97,18 +97,16 @@ enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
|
||||
return true;
|
||||
}
|
||||
|
||||
return !in_array($this, [ContainerStatus::Offline]);
|
||||
return !in_array($this, [ContainerStatus::Offline, ContainerStatus::Missing]);
|
||||
}
|
||||
|
||||
public function isStoppable(): bool
|
||||
{
|
||||
return !in_array($this, [ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting, ContainerStatus::Exited, ContainerStatus::Offline]);
|
||||
return !in_array($this, [ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting, ContainerStatus::Exited, ContainerStatus::Offline, ContainerStatus::Missing]);
|
||||
}
|
||||
|
||||
public function isKillable(): bool
|
||||
{
|
||||
// [ContainerStatus::Restarting, ContainerStatus::Removing, ContainerStatus::Dead, ContainerStatus::Created]
|
||||
|
||||
return !in_array($this, [ContainerStatus::Offline, ContainerStatus::Running, ContainerStatus::Exited]);
|
||||
return !in_array($this, [ContainerStatus::Offline, ContainerStatus::Running, ContainerStatus::Exited, ContainerStatus::Missing]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,8 +27,16 @@ enum ServerState: string implements HasColor, HasIcon, HasLabel
|
||||
};
|
||||
}
|
||||
|
||||
public function getColor(): string
|
||||
public function getColor(bool $hex = false): string
|
||||
{
|
||||
if ($hex) {
|
||||
return match ($this) {
|
||||
self::Normal, self::Installing, self::RestoringBackup => '#2563EB',
|
||||
self::Suspended => '#D97706',
|
||||
self::InstallFailed, self::ReinstallFailed => '#EF4444',
|
||||
};
|
||||
}
|
||||
|
||||
return match ($this) {
|
||||
self::Normal => 'primary',
|
||||
self::Installing => 'primary',
|
||||
|
||||
@ -147,6 +147,8 @@ class RoleResource extends Resource
|
||||
*/
|
||||
private static function makeSection(string $model, array $options): Section
|
||||
{
|
||||
$model = ucwords($model);
|
||||
|
||||
$icon = null;
|
||||
|
||||
if (class_exists('\App\Filament\Admin\Resources\\' . $model . 'Resource')) {
|
||||
|
||||
@ -9,12 +9,13 @@ use App\Filament\Server\Pages\Console;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonPowerRepository;
|
||||
use AymanAlhattami\FilamentContextMenu\Columns\ContextMenuTextColumn;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Components\Tab;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Columns\ColumnGroup;
|
||||
use Filament\Tables\Actions\ActionGroup;
|
||||
use Filament\Tables\Columns\Column;
|
||||
use Filament\Tables\Columns\Layout\Stack;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
@ -38,121 +39,73 @@ class ListServers extends ListRecords
|
||||
$this->daemonPowerRepository = new DaemonPowerRepository();
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
/** @return Stack[] */
|
||||
protected function gridColumns(): array
|
||||
{
|
||||
$baseQuery = auth()->user()->accessibleServers();
|
||||
|
||||
$menuOptions = function (Server $server) {
|
||||
$status = $server->retrieveStatus();
|
||||
|
||||
return [
|
||||
Action::make('start')
|
||||
->color('primary')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->visible(fn () => $status->isStartable())
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'start'])
|
||||
->icon('tabler-player-play-filled'),
|
||||
Action::make('restart')
|
||||
->color('gray')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->visible(fn () => $status->isRestartable())
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'restart'])
|
||||
->icon('tabler-refresh'),
|
||||
Action::make('stop')
|
||||
->color('danger')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn () => $status->isStoppable())
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'stop'])
|
||||
->icon('tabler-player-stop-filled'),
|
||||
Action::make('kill')
|
||||
->color('danger')
|
||||
->tooltip('This can result in data corruption and/or data loss!')
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'kill'])
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn () => $status->isKillable())
|
||||
->icon('tabler-alert-square'),
|
||||
Stack::make([
|
||||
ServerEntryColumn::make('server_entry')
|
||||
->searchable(['name']),
|
||||
]),
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
$viewOne = [
|
||||
ContextMenuTextColumn::make('condition')
|
||||
->label('')
|
||||
->default('unknown')
|
||||
->wrap()
|
||||
/** @return Column[] */
|
||||
protected function tableColumns(): array
|
||||
{
|
||||
return [
|
||||
TextColumn::make('condition')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->alignCenter()
|
||||
->tooltip(fn (Server $server) => $server->formatResource('uptime', type: ServerResourceType::Time))
|
||||
->icon(fn (Server $server) => $server->condition->getIcon())
|
||||
->color(fn (Server $server) => $server->condition->getColor())
|
||||
->contextMenuActions($menuOptions)
|
||||
->enableContextMenu(fn (Server $server) => !$server->isInConflictState()),
|
||||
];
|
||||
|
||||
$viewTwo = [
|
||||
ContextMenuTextColumn::make('name')
|
||||
->label('')
|
||||
->size('md')
|
||||
->searchable()
|
||||
->contextMenuActions($menuOptions)
|
||||
->enableContextMenu(fn (Server $server) => !$server->isInConflictState()),
|
||||
ContextMenuTextColumn::make('allocation.address')
|
||||
->color(fn (Server $server) => $server->condition->getColor()),
|
||||
TextColumn::make('name')
|
||||
->label('Server')
|
||||
->description(fn (Server $server) => $server->description)
|
||||
->grow()
|
||||
->searchable(),
|
||||
TextColumn::make('allocation.address')
|
||||
->label('')
|
||||
->badge()
|
||||
->copyable(request()->isSecure())
|
||||
->contextMenuActions($menuOptions)
|
||||
->enableContextMenu(fn (Server $server) => !$server->isInConflictState()),
|
||||
];
|
||||
|
||||
$viewThree = [
|
||||
->visibleFrom('md')
|
||||
->copyable(request()->isSecure()),
|
||||
TextColumn::make('cpuUsage')
|
||||
->label('')
|
||||
->label('Resources')
|
||||
->icon('tabler-cpu')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('cpu', limit: true, type: ServerResourceType::Percentage, precision: 0))
|
||||
->state(fn (Server $server) => $server->formatResource('cpu_absolute', type: ServerResourceType::Percentage))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'cpu')),
|
||||
TextColumn::make('memoryUsage')
|
||||
->label('')
|
||||
->icon('tabler-memory')
|
||||
->icon('tabler-device-desktop-analytics')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('memory', limit: true))
|
||||
->state(fn (Server $server) => $server->formatResource('memory_bytes'))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'memory')),
|
||||
TextColumn::make('diskUsage')
|
||||
->label('')
|
||||
->icon('tabler-device-floppy')
|
||||
->icon('tabler-device-sd-card')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('disk', limit: true))
|
||||
->state(fn (Server $server) => $server->formatResource('disk_bytes'))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'disk')),
|
||||
];
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
$baseQuery = auth()->user()->accessibleServers();
|
||||
|
||||
$usingGrid = (auth()->user()->getCustomization()['dashboard_layout'] ?? 'grid') === 'grid';
|
||||
|
||||
return $table
|
||||
->paginated(false)
|
||||
->query(fn () => $baseQuery)
|
||||
->poll('15s')
|
||||
->columns(
|
||||
(auth()->user()->getCustomization()['dashboard_layout'] ?? 'grid') === 'grid'
|
||||
? [
|
||||
Stack::make([
|
||||
ServerEntryColumn::make('server_entry')
|
||||
->searchable(['name']),
|
||||
]),
|
||||
]
|
||||
: [
|
||||
ColumnGroup::make('Status')
|
||||
->label('Status')
|
||||
->columns($viewOne),
|
||||
ColumnGroup::make('Server')
|
||||
->label('Servers')
|
||||
->columns($viewTwo),
|
||||
ColumnGroup::make('Resources')
|
||||
->label('Resources')
|
||||
->columns($viewThree),
|
||||
]
|
||||
)
|
||||
->recordUrl(fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server))
|
||||
->contentGrid([
|
||||
'default' => 1,
|
||||
'md' => 2,
|
||||
])
|
||||
->columns($usingGrid ? $this->gridColumns() : $this->tableColumns())
|
||||
->recordUrl(!$usingGrid ? (fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server)) : null)
|
||||
->actions(!$usingGrid ? ActionGroup::make(static::getPowerActions()) : [])
|
||||
->actionsAlignment(Alignment::Center->value)
|
||||
->contentGrid($usingGrid ? ['default' => 1, 'md' => 2] : null)
|
||||
->emptyStateIcon('tabler-brand-docker')
|
||||
->emptyStateDescription('')
|
||||
->emptyStateHeading(fn () => $this->activeTab === 'my' ? 'You don\'t own any servers!' : 'You don\'t have access to any servers!')
|
||||
@ -195,36 +148,33 @@ class ListServers extends ListRecords
|
||||
];
|
||||
}
|
||||
|
||||
public function getResourceColor(Server $server, string $resource): ?string
|
||||
protected function getResourceColor(Server $server, string $resource): ?string
|
||||
{
|
||||
$current = null;
|
||||
$limit = null;
|
||||
|
||||
switch ($resource) {
|
||||
case 'cpu':
|
||||
$current = $server->resources()['cpu_absolute'] ?? 0;
|
||||
$current = $server->retrieveResources()['cpu_absolute'] ?? 0;
|
||||
$limit = $server->cpu;
|
||||
if ($server->cpu === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'memory':
|
||||
$current = $server->resources()['memory_bytes'] ?? 0;
|
||||
$current = $server->retrieveResources()['memory_bytes'] ?? 0;
|
||||
$limit = $server->memory * 2 ** 20;
|
||||
if ($server->memory === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'disk':
|
||||
$current = $server->resources()['disk_bytes'] ?? 0;
|
||||
$current = $server->retrieveResources()['disk_bytes'] ?? 0;
|
||||
$limit = $server->disk * 2 ** 20;
|
||||
if ($server->disk === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -238,7 +188,6 @@ class ListServers extends ListRecords
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
#[On('powerAction')]
|
||||
@ -253,6 +202,8 @@ class ListServers extends ListRecords
|
||||
->success()
|
||||
->send();
|
||||
|
||||
cache()->forget("servers.$server->uuid.status");
|
||||
|
||||
$this->redirect(self::getUrl(['activeTab' => $this->activeTab]));
|
||||
} catch (ConnectionException) {
|
||||
Notification::make()
|
||||
@ -261,4 +212,36 @@ class ListServers extends ListRecords
|
||||
->send();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Action[] */
|
||||
public static function getPowerActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('start')
|
||||
->color('primary')
|
||||
->icon('tabler-player-play-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isStartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'start']),
|
||||
Action::make('restart')
|
||||
->color('gray')
|
||||
->icon('tabler-reload')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isRestartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'restart']),
|
||||
Action::make('stop')
|
||||
->color('danger')
|
||||
->icon('tabler-player-stop-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isStoppable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'stop']),
|
||||
Action::make('kill')
|
||||
->color('danger')
|
||||
->icon('tabler-alert-square')
|
||||
->tooltip('This can result in data corruption and/or data loss!')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isKillable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'kill']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\ToggleButtons;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Forms\Set;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Auth\EditProfile as BaseEditProfile;
|
||||
use Filament\Support\Colors\Color;
|
||||
@ -405,27 +406,35 @@ class EditProfile extends BaseEditProfile
|
||||
})
|
||||
->reactive()
|
||||
->default('monospace')
|
||||
->afterStateUpdated(fn ($state, callable $set) => $set('font_preview', $state)),
|
||||
->afterStateUpdated(fn ($state, Set $set) => $set('font_preview', $state)),
|
||||
Placeholder::make('font_preview')
|
||||
->label(trans('profile.font_preview'))
|
||||
->columnSpan(2)
|
||||
->content(function (Get $get) {
|
||||
$fontName = $get('console_font') ?? 'monospace';
|
||||
$fontSize = $get('console_font_size') . 'px';
|
||||
$fontUrl = asset("storage/fonts/{$fontName}.ttf");
|
||||
|
||||
return new HtmlString(<<<HTML
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "CustomPreviewFont";
|
||||
src: url("$fontUrl");
|
||||
}
|
||||
$style = <<<CSS
|
||||
.preview-text {
|
||||
font-family: "CustomPreviewFont";
|
||||
font-family: $fontName;
|
||||
font-size: $fontSize;
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
}
|
||||
CSS;
|
||||
if ($fontName !== 'monospace') {
|
||||
$fontUrl = asset("storage/fonts/$fontName.ttf");
|
||||
$style = <<<CSS
|
||||
@font-face {
|
||||
font-family: $fontName;
|
||||
src: url("$fontUrl");
|
||||
}
|
||||
$style
|
||||
CSS;
|
||||
}
|
||||
|
||||
return new HtmlString(<<<HTML
|
||||
<style>
|
||||
{$style}
|
||||
</style>
|
||||
<span class="preview-text">The quick blue pelican jumps over the lazy pterodactyl. :)</span>
|
||||
HTML);
|
||||
|
||||
@ -14,6 +14,7 @@ use App\Repositories\Daemon\DaemonBackupRepository;
|
||||
use App\Services\Backups\DownloadLinkService;
|
||||
use App\Filament\Components\Tables\Columns\BytesColumn;
|
||||
use App\Filament\Components\Tables\Columns\DateTimeColumn;
|
||||
use App\Services\Backups\DeleteBackupService;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
@ -30,6 +31,7 @@ use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BackupResource extends Resource
|
||||
@ -142,7 +144,7 @@ class BackupResource extends Resource
|
||||
->send();
|
||||
}
|
||||
|
||||
if (!$backup->is_successful && is_null($backup->completed_at)) { //TODO Change to Notifications
|
||||
if (!$backup->is_successful && is_null($backup->completed_at)) {
|
||||
return Notification::make()
|
||||
->danger()
|
||||
->title('Backup Restore Failed')
|
||||
@ -175,9 +177,26 @@ class BackupResource extends Resource
|
||||
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
|
||||
DeleteAction::make('delete')
|
||||
->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')
|
||||
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->delete($request, $server, $backup))
|
||||
->action(function (Backup $backup, DeleteBackupService $deleteBackupService) {
|
||||
try {
|
||||
$deleteBackupService->handle($backup);
|
||||
} catch (ConnectionException) {
|
||||
Notification::make()
|
||||
->title('Could not delete backup')
|
||||
->body('Connection to node failed')
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Activity::event('server:backup.delete')
|
||||
->subject($backup)
|
||||
->property(['name' => $backup->name, 'failed' => !$backup->is_successful])
|
||||
->log();
|
||||
})
|
||||
->visible(fn (Backup $backup) => $backup->status !== BackupStatus::InProgress),
|
||||
]),
|
||||
]);
|
||||
|
||||
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use App\Facades\Activity;
|
||||
use App\Http\Controllers\Api\Client\ClientApiController;
|
||||
use App\Http\Requests\Api\Client\Servers\Settings\DescriptionServerRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Settings\ReinstallServerRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Settings\RenameServerRequest;
|
||||
use App\Http\Requests\Api\Client\Servers\Settings\SetDockerImageRequest;
|
||||
@ -34,24 +35,36 @@ class SettingsController extends ClientApiController
|
||||
public function rename(RenameServerRequest $request, Server $server): JsonResponse
|
||||
{
|
||||
$name = $request->input('name');
|
||||
$description = $request->has('description') ? (string) $request->input('description') : $server->description;
|
||||
|
||||
if ($server->name !== $name) {
|
||||
$server->update(['name' => $name]);
|
||||
|
||||
if ($server->wasChanged('name')) {
|
||||
Activity::event('server:settings.rename')
|
||||
->property(['old' => $server->name, 'new' => $name])
|
||||
->property(['old' => $server->getOriginal('name'), 'new' => $name])
|
||||
->log();
|
||||
$server->name = $name;
|
||||
}
|
||||
|
||||
if ($server->description !== $description && config('panel.editable_server_descriptions')) {
|
||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update server description
|
||||
*/
|
||||
public function description(DescriptionServerRequest $request, Server $server): JsonResponse
|
||||
{
|
||||
if (!config('panel.editable_server_descriptions')) {
|
||||
return new JsonResponse([], Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
$description = $request->input('description');
|
||||
$server->update(['description' => $description ?? '']);
|
||||
|
||||
if ($server->wasChanged('description')) {
|
||||
Activity::event('server:settings.description')
|
||||
->property(['old' => $server->description, 'new' => $description])
|
||||
->property(['old' => $server->getOriginal('description'), 'new' => $description])
|
||||
->log();
|
||||
$server->description = $description;
|
||||
}
|
||||
|
||||
$server->save();
|
||||
|
||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
@ -80,7 +93,7 @@ class SettingsController extends ClientApiController
|
||||
*/
|
||||
public function dockerImage(SetDockerImageRequest $request, Server $server): JsonResponse
|
||||
{
|
||||
if (!in_array($server->image, array_values($server->egg->docker_images))) {
|
||||
if (!in_array($server->image, $server->egg->docker_images)) {
|
||||
throw new BadRequestHttpException('This server\'s Docker image has been manually set by an administrator and cannot be updated.');
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\Client\Servers\Settings;
|
||||
|
||||
use App\Contracts\Http\ClientPermissionsRequest;
|
||||
use App\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use App\Models\Permission;
|
||||
|
||||
class DescriptionServerRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* Returns the permissions string indicating which permission should be used to
|
||||
* validate that the authenticated user has permission to perform this action against
|
||||
* the given resource (server).
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return Permission::ACTION_SETTINGS_DESCRIPTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules to apply when validating this request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'description' => 'string|nullable',
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,6 @@ class RenameServerRequest extends ClientApiRequest implements ClientPermissionsR
|
||||
{
|
||||
return [
|
||||
'name' => Server::getRules()['name'],
|
||||
'description' => 'string|nullable',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,26 +333,6 @@ class Node extends Model implements Validatable
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array-key, mixed>
|
||||
*/
|
||||
public function serverStatuses(): array
|
||||
{
|
||||
$statuses = [];
|
||||
try {
|
||||
$statuses = Http::daemon($this)->connectTimeout(1)->timeout(1)->get('/api/servers')->json() ?? [];
|
||||
} catch (Exception $exception) {
|
||||
report($exception);
|
||||
}
|
||||
|
||||
foreach ($statuses as $status) {
|
||||
$uuid = fluent($status)->get('configuration.uuid');
|
||||
cache()->remember("servers.$uuid.container.status", now()->addMinute(), fn () => fluent($status)->get('state'));
|
||||
}
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
/** @return array{
|
||||
* memory_total: int, memory_used: int,
|
||||
* swap_total: int, swap_used: int,
|
||||
|
||||
@ -97,6 +97,8 @@ class Permission extends Model implements Validatable
|
||||
|
||||
public const ACTION_SETTINGS_RENAME = 'settings.rename';
|
||||
|
||||
public const ACTION_SETTINGS_DESCRIPTION = 'settings.description';
|
||||
|
||||
public const ACTION_SETTINGS_REINSTALL = 'settings.reinstall';
|
||||
|
||||
public const ACTION_ACTIVITY_READ = 'activity.read';
|
||||
@ -176,7 +178,7 @@ class Permission extends Model implements Validatable
|
||||
[
|
||||
'name' => 'settings',
|
||||
'icon' => 'tabler-settings',
|
||||
'permissions' => ['rename', 'reinstall'],
|
||||
'permissions' => ['rename', 'description', 'reinstall'],
|
||||
],
|
||||
[
|
||||
'name' => 'activity',
|
||||
|
||||
@ -435,25 +435,24 @@ class Server extends Model implements Validatable
|
||||
|
||||
public function retrieveStatus(): ContainerStatus
|
||||
{
|
||||
$status = cache()->get("servers.$this->uuid.container.status");
|
||||
return cache()->remember("servers.$this->uuid.status", now()->addSeconds(15), function () {
|
||||
// @phpstan-ignore myCustomRules.forbiddenGlobalFunctions
|
||||
$details = app(DaemonServerRepository::class)->setServer($this)->getDetails();
|
||||
|
||||
if ($status === null) {
|
||||
$this->node->serverStatuses();
|
||||
|
||||
$status = cache()->get("servers.$this->uuid.container.status");
|
||||
}
|
||||
|
||||
return ContainerStatus::tryFrom($status) ?? ContainerStatus::Missing;
|
||||
return ContainerStatus::tryFrom(Arr::get($details, 'state')) ?? ContainerStatus::Missing;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function resources(): array
|
||||
public function retrieveResources(): array
|
||||
{
|
||||
return cache()->remember("resources:$this->uuid", now()->addSeconds(15), function () {
|
||||
return cache()->remember("servers.$this->uuid.resources", now()->addSeconds(15), function () {
|
||||
// @phpstan-ignore myCustomRules.forbiddenGlobalFunctions
|
||||
return Arr::get(app(DaemonServerRepository::class)->setServer($this)->getDetails(), 'utilization', []);
|
||||
$details = app(DaemonServerRepository::class)->setServer($this)->getDetails();
|
||||
|
||||
return Arr::get($details, 'utilization', []);
|
||||
});
|
||||
}
|
||||
|
||||
@ -461,7 +460,7 @@ class Server extends Model implements Validatable
|
||||
{
|
||||
$resourceAmount = $this->{$resourceKey} ?? 0;
|
||||
if (!$limit) {
|
||||
$resourceAmount = $this->resources()[$resourceKey] ?? 0;
|
||||
$resourceAmount = $this->retrieveResources()[$resourceKey] ?? 0;
|
||||
}
|
||||
|
||||
if ($type === ServerResourceType::Time) {
|
||||
@ -494,7 +493,7 @@ class Server extends Model implements Validatable
|
||||
public function condition(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->isSuspended() ? ServerState::Suspended : $this->status ?? $this->retrieveStatus(),
|
||||
get: fn () => $this->status ?? $this->retrieveStatus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ class ServerPanelProvider extends PanelProvider
|
||||
->navigationItems([
|
||||
NavigationItem::make('Open in Admin')
|
||||
->url(fn () => EditServer::getUrl(['record' => Filament::getTenant()], panel: 'admin'))
|
||||
->visible(fn () => auth()->user()->can('view server', Filament::getTenant()))
|
||||
->visible(fn () => auth()->user()->canAccessPanel(Filament::getPanel('admin')) && auth()->user()->can('view server', Filament::getTenant()))
|
||||
->icon('tabler-arrow-back')
|
||||
->sort(99),
|
||||
])
|
||||
|
||||
@ -6,12 +6,11 @@ use App\Extensions\Filesystem\S3Filesystem;
|
||||
use Aws\S3\S3Client;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Models\Backup;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use App\Extensions\Backups\BackupManager;
|
||||
use App\Repositories\Daemon\DaemonBackupRepository;
|
||||
use App\Exceptions\Service\Backup\BackupLockedException;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Exception;
|
||||
|
||||
class DeleteBackupService
|
||||
{
|
||||
@ -48,12 +47,10 @@ class DeleteBackupService
|
||||
$this->connection->transaction(function () use ($backup) {
|
||||
try {
|
||||
$this->daemonBackupRepository->setServer($backup->server)->delete($backup);
|
||||
} catch (ConnectionException $exception) {
|
||||
$previous = $exception->getPrevious();
|
||||
|
||||
} catch (Exception $exception) {
|
||||
// Don't fail the request if the Daemon responds with a 404, just assume the backup
|
||||
// doesn't actually exist and remove its reference from the Panel as well.
|
||||
if (!$previous instanceof ClientException || $previous->getResponse()->getStatusCode() !== Response::HTTP_NOT_FOUND) {
|
||||
if ($exception->getCode() !== Response::HTTP_NOT_FOUND) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,15 +10,14 @@
|
||||
"ext-pdo": "*",
|
||||
"ext-zip": "*",
|
||||
"abdelhamiderrahmouni/filament-monaco-editor": "^0.2.5",
|
||||
"aws/aws-sdk-php": "^3.343",
|
||||
"aymanalhattami/filament-context-menu": "^1.0",
|
||||
"aws/aws-sdk-php": "^3.344",
|
||||
"calebporzio/sushi": "^2.5",
|
||||
"chillerlan/php-qrcode": "^5.0.2",
|
||||
"dedoc/scramble": "^0.12.10",
|
||||
"doctrine/dbal": "~3.6.0",
|
||||
"filament/filament": "^3.3",
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"laravel/framework": "^12.17",
|
||||
"laravel/framework": "^12.18",
|
||||
"laravel/helpers": "^1.7",
|
||||
"laravel/sanctum": "^4.1",
|
||||
"laravel/socialite": "^5.21",
|
||||
|
||||
219
composer.lock
generated
219
composer.lock
generated
@ -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": "9a250ae6162d7d877649172284b56c13",
|
||||
"content-hash": "a006241b5687d547b51a60e6ac50ccae",
|
||||
"packages": [
|
||||
{
|
||||
"name": "abdelhamiderrahmouni/filament-monaco-editor",
|
||||
@ -1020,16 +1020,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.343.23",
|
||||
"version": "3.344.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "010869992129557cfbf2740d94d82ef3b5228462"
|
||||
"reference": "0cf789243c7de82be7d3f7641cab37b5bb5d937d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/010869992129557cfbf2740d94d82ef3b5228462",
|
||||
"reference": "010869992129557cfbf2740d94d82ef3b5228462",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0cf789243c7de82be7d3f7641cab37b5bb5d937d",
|
||||
"reference": "0cf789243c7de82be7d3f7641cab37b5bb5d937d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1111,86 +1111,10 @@
|
||||
"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.343.23"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.344.3"
|
||||
},
|
||||
"time": "2025-06-02T18:04:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "aymanalhattami/filament-context-menu",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aymanalhattami/filament-context-menu.git",
|
||||
"reference": "5118d36303e86891d3037e6e26882d548b880b9c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aymanalhattami/filament-context-menu/zipball/5118d36303e86891d3037e6e26882d548b880b9c",
|
||||
"reference": "5118d36303e86891d3037e6e26882d548b880b9c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"filament/filament": "^3.0",
|
||||
"php": "^8.2",
|
||||
"spatie/laravel-package-tools": "^1.15.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"larastan/larastan": "^2.0",
|
||||
"laravel/pint": "^1.0",
|
||||
"nunomaduro/collision": "^8.0",
|
||||
"orchestra/testbench": "^9.0",
|
||||
"pestphp/pest": "^3.0",
|
||||
"pestphp/pest-plugin-arch": "^3.0",
|
||||
"pestphp/pest-plugin-laravel": "^3.0",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Skeleton": "AymanAlhattami\\FilamentContextMenu\\Facades\\FilamentContextMenu"
|
||||
},
|
||||
"providers": [
|
||||
"AymanAlhattami\\FilamentContextMenu\\FilamentContextMenuServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"AymanAlhattami\\FilamentContextMenu\\": "src/",
|
||||
"AymanAlhattami\\FilamentContextMenu\\Database\\Factories\\": "database/factories/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ayman Alhattami",
|
||||
"email": "ayman.m.alhattami@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "context menu (right click menu) for filament",
|
||||
"homepage": "https://github.com/aymanalhattami/filament-context-menu",
|
||||
"keywords": [
|
||||
"ayman alhattami",
|
||||
"context menu",
|
||||
"filament",
|
||||
"filament admin panel",
|
||||
"filament context menu",
|
||||
"filament_context_menu",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/aymanalhattami/filament-context-menu/issues",
|
||||
"source": "https://github.com/aymanalhattami/filament-context-menu"
|
||||
},
|
||||
"time": "2024-09-22T10:47:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "blade-ui-kit/blade-heroicons",
|
||||
"version": "2.6.0",
|
||||
@ -2634,7 +2558,7 @@
|
||||
},
|
||||
{
|
||||
"name": "filament/actions",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/actions.git",
|
||||
@ -2687,16 +2611,16 @@
|
||||
},
|
||||
{
|
||||
"name": "filament/filament",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/panels.git",
|
||||
"reference": "94ee92244d2a64666fb8c1ea50cb7315ebceb63b"
|
||||
"reference": "8d915ef313835f46f49175396de82feb0166d8a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/panels/zipball/94ee92244d2a64666fb8c1ea50cb7315ebceb63b",
|
||||
"reference": "94ee92244d2a64666fb8c1ea50cb7315ebceb63b",
|
||||
"url": "https://api.github.com/repos/filamentphp/panels/zipball/8d915ef313835f46f49175396de82feb0166d8a8",
|
||||
"reference": "8d915ef313835f46f49175396de82feb0166d8a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2748,20 +2672,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-05-27T18:46:23+00:00"
|
||||
"time": "2025-06-10T16:10:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/forms",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/forms.git",
|
||||
"reference": "d73cdda057a4f5bd409eab9573101e73edb404cc"
|
||||
"reference": "014dd23a7691dc25bb037f26df852cfec5602d01"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/forms/zipball/d73cdda057a4f5bd409eab9573101e73edb404cc",
|
||||
"reference": "d73cdda057a4f5bd409eab9573101e73edb404cc",
|
||||
"url": "https://api.github.com/repos/filamentphp/forms/zipball/014dd23a7691dc25bb037f26df852cfec5602d01",
|
||||
"reference": "014dd23a7691dc25bb037f26df852cfec5602d01",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2804,11 +2728,11 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-06-03T13:40:37+00:00"
|
||||
"time": "2025-06-10T16:10:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/infolists",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/infolists.git",
|
||||
@ -2859,7 +2783,7 @@
|
||||
},
|
||||
{
|
||||
"name": "filament/notifications",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/notifications.git",
|
||||
@ -2911,16 +2835,16 @@
|
||||
},
|
||||
{
|
||||
"name": "filament/support",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/support.git",
|
||||
"reference": "4f9793ad3339301ca53ea6f2c984734f7ac38ce7"
|
||||
"reference": "5c140580d7feeabb4d2b0007c854676ae87be1b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/support/zipball/4f9793ad3339301ca53ea6f2c984734f7ac38ce7",
|
||||
"reference": "4f9793ad3339301ca53ea6f2c984734f7ac38ce7",
|
||||
"url": "https://api.github.com/repos/filamentphp/support/zipball/5c140580d7feeabb4d2b0007c854676ae87be1b3",
|
||||
"reference": "5c140580d7feeabb4d2b0007c854676ae87be1b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2966,20 +2890,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-06-03T06:16:13+00:00"
|
||||
"time": "2025-06-10T16:10:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/tables",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/tables.git",
|
||||
"reference": "1a107a8411549297b97d1142b1f7a5fa7a65e32b"
|
||||
"reference": "3d52c23443f6846774a6a2ce60f6e6173ce20943"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/tables/zipball/1a107a8411549297b97d1142b1f7a5fa7a65e32b",
|
||||
"reference": "1a107a8411549297b97d1142b1f7a5fa7a65e32b",
|
||||
"url": "https://api.github.com/repos/filamentphp/tables/zipball/3d52c23443f6846774a6a2ce60f6e6173ce20943",
|
||||
"reference": "3d52c23443f6846774a6a2ce60f6e6173ce20943",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3018,11 +2942,11 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-06-02T09:43:47+00:00"
|
||||
"time": "2025-06-10T16:10:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/widgets",
|
||||
"version": "v3.3.20",
|
||||
"version": "v3.3.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/widgets.git",
|
||||
@ -3794,16 +3718,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.17.0",
|
||||
"version": "v12.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "8729d084510480fdeec9b6ad198180147d4a7f06"
|
||||
"reference": "7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/8729d084510480fdeec9b6ad198180147d4a7f06",
|
||||
"reference": "8729d084510480fdeec9b6ad198180147d4a7f06",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d",
|
||||
"reference": "7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4005,7 +3929,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-06-03T14:04:18+00:00"
|
||||
"time": "2025-06-10T14:48:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/helpers",
|
||||
@ -6461,16 +6385,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection",
|
||||
"version": "6.2.1",
|
||||
"version": "6.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/Reflection.git",
|
||||
"reference": "ab969af5e53c3bb400eea958b90b87ecc34f4dff"
|
||||
"reference": "d91b3270832785602adcc24ae2d0974ba99a8ff8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/ab969af5e53c3bb400eea958b90b87ecc34f4dff",
|
||||
"reference": "ab969af5e53c3bb400eea958b90b87ecc34f4dff",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/d91b3270832785602adcc24ae2d0974ba99a8ff8",
|
||||
"reference": "d91b3270832785602adcc24ae2d0974ba99a8ff8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6527,9 +6451,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/Reflection/issues",
|
||||
"source": "https://github.com/phpDocumentor/Reflection/tree/6.2.1"
|
||||
"source": "https://github.com/phpDocumentor/Reflection/tree/6.3.0"
|
||||
},
|
||||
"time": "2025-05-30T18:42:55+00:00"
|
||||
"time": "2025-06-06T13:39:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
@ -7998,16 +7922,16 @@
|
||||
},
|
||||
{
|
||||
"name": "secondnetwork/blade-tabler-icons",
|
||||
"version": "v3.33.0",
|
||||
"version": "v3.34.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/secondnetwork/blade-tabler-icons.git",
|
||||
"reference": "523b3ede493ce9f8cbe3a5bdce21769976a80b28"
|
||||
"reference": "e48c0a5a53798d42c7beff760de8cbc7dbbccff4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/secondnetwork/blade-tabler-icons/zipball/523b3ede493ce9f8cbe3a5bdce21769976a80b28",
|
||||
"reference": "523b3ede493ce9f8cbe3a5bdce21769976a80b28",
|
||||
"url": "https://api.github.com/repos/secondnetwork/blade-tabler-icons/zipball/e48c0a5a53798d42c7beff760de8cbc7dbbccff4",
|
||||
"reference": "e48c0a5a53798d42c7beff760de8cbc7dbbccff4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8050,9 +7974,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/secondnetwork/blade-tabler-icons/issues",
|
||||
"source": "https://github.com/secondnetwork/blade-tabler-icons/tree/v3.33.0"
|
||||
"source": "https://github.com/secondnetwork/blade-tabler-icons/tree/v3.34.0"
|
||||
},
|
||||
"time": "2025-05-17T06:28:48+00:00"
|
||||
"time": "2025-06-09T08:41:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "socialiteproviders/authentik",
|
||||
@ -8701,16 +8625,16 @@
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-health",
|
||||
"version": "1.34.2",
|
||||
"version": "1.34.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-health.git",
|
||||
"reference": "e7d9d6e0807a9de46f544c76d54badc19af36ddc"
|
||||
"reference": "d421bc223c7a8c872ad944706d98a74b1056f761"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-health/zipball/e7d9d6e0807a9de46f544c76d54badc19af36ddc",
|
||||
"reference": "e7d9d6e0807a9de46f544c76d54badc19af36ddc",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-health/zipball/d421bc223c7a8c872ad944706d98a74b1056f761",
|
||||
"reference": "d421bc223c7a8c872ad944706d98a74b1056f761",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8782,7 +8706,7 @@
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/spatie/laravel-health/tree/1.34.2"
|
||||
"source": "https://github.com/spatie/laravel-health/tree/1.34.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -8790,7 +8714,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-20T08:31:02+00:00"
|
||||
"time": "2025-06-04T22:04:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-package-tools",
|
||||
@ -12744,16 +12668,16 @@
|
||||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "2.18.0",
|
||||
"version": "2.18.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filp/whoops.git",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e"
|
||||
"reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26",
|
||||
"reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -12803,7 +12727,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/filp/whoops/issues",
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.0"
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -12811,7 +12735,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-15T12:00:00+00:00"
|
||||
"time": "2025-06-03T18:56:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hamcrest/hamcrest-php",
|
||||
@ -12967,16 +12891,16 @@
|
||||
},
|
||||
{
|
||||
"name": "larastan/larastan",
|
||||
"version": "v3.4.0",
|
||||
"version": "v3.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/larastan/larastan.git",
|
||||
"reference": "1042fa0c2ee490bb6da7381f3323f7292ad68222"
|
||||
"reference": "dc20d24871d5a2138b292b0430d94d18da3dbc53"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/1042fa0c2ee490bb6da7381f3323f7292ad68222",
|
||||
"reference": "1042fa0c2ee490bb6da7381f3323f7292ad68222",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/dc20d24871d5a2138b292b0430d94d18da3dbc53",
|
||||
"reference": "dc20d24871d5a2138b292b0430d94d18da3dbc53",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -13044,7 +12968,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/larastan/larastan/issues",
|
||||
"source": "https://github.com/larastan/larastan/tree/v3.4.0"
|
||||
"source": "https://github.com/larastan/larastan/tree/v3.4.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -13052,20 +12976,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-22T09:44:59+00:00"
|
||||
"time": "2025-06-09T21:23:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pail",
|
||||
"version": "v1.2.2",
|
||||
"version": "v1.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/pail.git",
|
||||
"reference": "f31f4980f52be17c4667f3eafe034e6826787db2"
|
||||
"reference": "8cc3d575c1f0e57eeb923f366a37528c50d2385a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/pail/zipball/f31f4980f52be17c4667f3eafe034e6826787db2",
|
||||
"reference": "f31f4980f52be17c4667f3eafe034e6826787db2",
|
||||
"url": "https://api.github.com/repos/laravel/pail/zipball/8cc3d575c1f0e57eeb923f366a37528c50d2385a",
|
||||
"reference": "8cc3d575c1f0e57eeb923f366a37528c50d2385a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -13085,7 +13009,7 @@
|
||||
"orchestra/testbench-core": "^8.13|^9.0|^10.0",
|
||||
"pestphp/pest": "^2.20|^3.0",
|
||||
"pestphp/pest-plugin-type-coverage": "^2.3|^3.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpstan/phpstan": "^1.12.27",
|
||||
"symfony/var-dumper": "^6.3|^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
@ -13121,6 +13045,7 @@
|
||||
"description": "Easily delve into your Laravel application's log files directly from the command line.",
|
||||
"homepage": "https://github.com/laravel/pail",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"laravel",
|
||||
"logs",
|
||||
"php",
|
||||
@ -13130,7 +13055,7 @@
|
||||
"issues": "https://github.com/laravel/pail/issues",
|
||||
"source": "https://github.com/laravel/pail"
|
||||
},
|
||||
"time": "2025-01-28T15:15:15+00:00"
|
||||
"time": "2025-06-05T13:55:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"version": "PLCN_v1",
|
||||
"update_url": "https:\/\/github.com\/pelican-dev\/panel\/raw\/main\/database\/Seeders\/eggs\/rust\/egg-rust.json"
|
||||
},
|
||||
"exported_at": "2025-03-18T12:36:48+00:00",
|
||||
"exported_at": "2025-06-06T11:57:17+00:00",
|
||||
"name": "Rust",
|
||||
"author": "panel@example.com",
|
||||
"uuid": "bace2dfb-209c-452a-9459-7d6f340b07ae",
|
||||
@ -20,7 +20,7 @@
|
||||
"ghcr.io\/parkervcp\/games:rust": "ghcr.io\/parkervcp\/games:rust"
|
||||
},
|
||||
"file_denylist": [],
|
||||
"startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.queryport {{QUERY_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s \"+server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\"\" || printf %s \"+server.levelurl {{MAP_URL}}\" ) {{ADDITIONAL_ARGS}}",
|
||||
"startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.queryport {{QUERY_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{SERVER_HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s \"+server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\"\" || printf %s \"+server.levelurl {{MAP_URL}}\" ) {{ADDITIONAL_ARGS}}",
|
||||
"config": {
|
||||
"files": "{}",
|
||||
"startup": "{\r\n \"done\": \"Server startup complete\"\r\n}",
|
||||
@ -38,7 +38,7 @@
|
||||
{
|
||||
"name": "Server Name",
|
||||
"description": "The name of your server in the public server list.",
|
||||
"env_variable": "HOSTNAME",
|
||||
"env_variable": "SERVER_HOSTNAME",
|
||||
"default_value": "A Rust Server",
|
||||
"user_viewable": true,
|
||||
"user_editable": true,
|
||||
|
||||
@ -16,7 +16,8 @@ return [
|
||||
'startup_update' => 'Allows a user to modify the startup variables for the server.',
|
||||
'startup_docker_image' => 'Allows a user to modify the Docker image used when running the server.',
|
||||
'settings_reinstall' => 'Allows a user to trigger a reinstall of this server.',
|
||||
'settings_rename' => 'Allows a user to rename this server and change the description of it.',
|
||||
'settings_rename' => 'Allows a user to rename this server.',
|
||||
'settings_description' => 'Allows a user to change the description of this server.',
|
||||
'activity_read' => 'Allows a user to view the activity logs for the server.',
|
||||
'websocket_*' => 'Allows a user access to the websocket for this server.',
|
||||
'control_console' => 'Allows a user to send data to the server console.',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -5,7 +5,7 @@
|
||||
$userFontSize = auth()->user()->getCustomization()['console_font_size'] ?? 14;
|
||||
$userRows = auth()->user()->getCustomization()['console_rows'] ?? 30;
|
||||
@endphp
|
||||
@if($userFont)
|
||||
@if($userFont !== "monospace")
|
||||
<link rel="preload" href="{{ asset("storage/fonts/{$userFont}.ttf") }}" as="font" crossorigin>
|
||||
<style>
|
||||
@font-face {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<div class="relative">
|
||||
<div wire:poll.15s class="relative">
|
||||
<a href="{{ \App\Filament\Server\Pages\Console::getUrl(panel: 'server', tenant: $server) }}" wire:navigate>
|
||||
<div
|
||||
class="absolute left-0 top-1 bottom-0 w-1 rounded-lg"
|
||||
style="background-color: {{ $server->condition->getColor(true) }};">
|
||||
@ -44,4 +45,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<x-filament-tables::actions
|
||||
:actions="\App\Filament\App\Resources\ServerResource\Pages\ListServers::getPowerActions()"
|
||||
:alignment="\Filament\Support\Enums\Alignment::Center"
|
||||
:record="$server"
|
||||
/>
|
||||
</div>
|
||||
@ -129,6 +129,7 @@ Route::prefix('/servers/{server:uuid}')->middleware([ServerSubject::class, Authe
|
||||
|
||||
Route::prefix('/settings')->group(function () {
|
||||
Route::post('/rename', [Client\Servers\SettingsController::class, 'rename']);
|
||||
Route::post('/description', [Client\Servers\SettingsController::class, 'description']);
|
||||
Route::post('/reinstall', [Client\Servers\SettingsController::class, 'reinstall']);
|
||||
Route::put('/docker-image', [Client\Servers\SettingsController::class, 'dockerImage']);
|
||||
});
|
||||
|
||||
@ -21,11 +21,9 @@ class SettingsControllerTest extends ClientApiIntegrationTestCase
|
||||
/** @var \App\Models\Server $server */
|
||||
[$user, $server] = $this->generateTestAccount($permissions);
|
||||
$originalName = $server->name;
|
||||
$originalDescription = $server->description;
|
||||
|
||||
$response = $this->actingAs($user)->postJson("/api/client/servers/$server->uuid/settings/rename", [
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
]);
|
||||
|
||||
$response->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
@ -33,18 +31,15 @@ class SettingsControllerTest extends ClientApiIntegrationTestCase
|
||||
|
||||
$server = $server->refresh();
|
||||
$this->assertSame($originalName, $server->name);
|
||||
$this->assertSame($originalDescription, $server->description);
|
||||
|
||||
$this->actingAs($user)
|
||||
->postJson("/api/client/servers/$server->uuid/settings/rename", [
|
||||
'name' => 'Test Server Name',
|
||||
'description' => 'This is a test server.',
|
||||
])
|
||||
->assertStatus(Response::HTTP_NO_CONTENT);
|
||||
|
||||
$server = $server->refresh();
|
||||
$this->assertSame('Test Server Name', $server->name);
|
||||
$this->assertSame('This is a test server.', $server->description);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2,10 +2,8 @@
|
||||
|
||||
namespace App\Tests\Integration\Services\Backups;
|
||||
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use App\Models\Backup;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use App\Extensions\Backups\BackupManager;
|
||||
use App\Extensions\Filesystem\S3Filesystem;
|
||||
use App\Services\Backups\DeleteBackupService;
|
||||
@ -54,7 +52,7 @@ class DeleteBackupServiceTest extends IntegrationTestCase
|
||||
$backup = Backup::factory()->create(['server_id' => $server->id]);
|
||||
|
||||
$mock = $this->mock(DaemonBackupRepository::class);
|
||||
$mock->expects('setServer->delete')->with($backup)->andThrow(new ConnectionException(previous: new ClientException('', new Request('DELETE', '/'), new Response(404))));
|
||||
$mock->expects('setServer->delete')->with($backup)->andThrow(new ConnectionException(code: 404));
|
||||
|
||||
$this->app->make(DeleteBackupService::class)->handle($backup);
|
||||
|
||||
@ -69,7 +67,7 @@ class DeleteBackupServiceTest extends IntegrationTestCase
|
||||
$backup = Backup::factory()->create(['server_id' => $server->id]);
|
||||
|
||||
$mock = $this->mock(DaemonBackupRepository::class);
|
||||
$mock->expects('setServer->delete')->with($backup)->andThrow(new ConnectionException(previous: new ClientException('', new Request('DELETE', '/'), new Response(500))));
|
||||
$mock->expects('setServer->delete')->with($backup)->andThrow(new ConnectionException(code: 500));
|
||||
|
||||
$this->expectException(ConnectionException::class);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user