Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
417 changes: 417 additions & 0 deletions app/Filament/Admin/Resources/Eggs/EggResource.php

Large diffs are not rendered by default.

407 changes: 1 addition & 406 deletions app/Filament/Admin/Resources/Eggs/Pages/EditEgg.php

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions app/Filament/Admin/Resources/Eggs/Pages/ListEggs.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ public function table(Table $table): Table
{
return $table
->searchable(true)
->recordUrl(fn (Egg $egg) => user()?->can('update egg', $egg)
? EggResource::getUrl('edit', ['record' => $egg])
: EggResource::getUrl('view', ['record' => $egg])
)
->defaultPaginationPageOption(25)
->columns([
TextColumn::make('id')
Expand Down
22 changes: 22 additions & 0 deletions app/Filament/Admin/Resources/Eggs/Pages/ViewEgg.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Filament\Admin\Resources\Eggs\Pages;

use App\Filament\Admin\Resources\Eggs\EggResource;
use App\Traits\Filament\CanCustomizeHeaderActions;
use App\Traits\Filament\CanCustomizeHeaderWidgets;
use Filament\Resources\Pages\ViewRecord;
use Filament\Schemas\Schema;

class ViewEgg extends ViewRecord
{
use CanCustomizeHeaderActions;
use CanCustomizeHeaderWidgets;

protected static string $resource = EggResource::class;

public function form(Schema $schema): Schema
{
return EggResource::schema($schema);
}
}
766 changes: 765 additions & 1 deletion app/Filament/Admin/Resources/Nodes/NodeResource.php

Large diffs are not rendered by default.

776 changes: 19 additions & 757 deletions app/Filament/Admin/Resources/Nodes/Pages/EditNode.php

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions app/Filament/Admin/Resources/Nodes/Pages/ListNodes.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class ListNodes extends ListRecords
public function table(Table $table): Table
{
return $table
->recordUrl(fn (Node $node) => user()?->can('update node', $node)
? NodeResource::getUrl('edit', ['record' => $node])
: NodeResource::getUrl('view', ['record' => $node])
)
->searchable(false)
->checkIfRecordIsSelectableUsing(fn (Node $node) => $node->servers_count <= 0)
->columns([
Expand Down
48 changes: 48 additions & 0 deletions app/Filament/Admin/Resources/Nodes/Pages/ViewNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace App\Filament\Admin\Resources\Nodes\Pages;

use App\Filament\Admin\Resources\Nodes\NodeResource;
use App\Filament\Admin\Resources\Nodes\RelationManagers\AllocationsRelationManager;
use App\Filament\Admin\Resources\Nodes\RelationManagers\ServersRelationManager;
use App\Traits\Filament\CanCustomizeHeaderActions;
use App\Traits\Filament\CanCustomizeHeaderWidgets;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ViewRecord;
use Filament\Schemas\Schema;

class ViewNode extends ViewRecord
{
use CanCustomizeHeaderActions;
use CanCustomizeHeaderWidgets;

protected static string $resource = NodeResource::class;

public function form(Schema $schema): Schema
{
return NodeResource::schema($schema);
}

public function getRelationManagers(): array
{
return [
AllocationsRelationManager::class,
ServersRelationManager::class,
];
}

protected function getSavedNotification(): ?Notification
{
return null;
}

protected function getColumnSpan(): ?int
{
return null;
}

protected function getColumnStart(): ?int
{
return null;
}
}
1,083 changes: 8 additions & 1,075 deletions app/Filament/Admin/Resources/Servers/Pages/EditServer.php

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions app/Filament/Admin/Resources/Servers/Pages/ListServers.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Filament\Actions\ActionGroup;
use Filament\Actions\CreateAction;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Support\Enums\IconSize;
use Filament\Tables\Columns\SelectColumn;
Expand All @@ -29,6 +30,10 @@ class ListServers extends ListRecords
public function table(Table $table): Table
{
return $table
->recordUrl(fn (Server $server) => user()?->can('update server', $server)
? ServerResource::getUrl('edit', ['record' => $server])
: ServerResource::getUrl('view', ['record' => $server])
)
->searchable(false)
->defaultGroup('node.name')
->groups([
Expand Down Expand Up @@ -91,14 +96,21 @@ public function table(Table $table): Table
->sortable(),
])
->recordActions([
Action::make('View')
->label(trans('admin/server.view'))
Action::make('Console')
->label(trans('admin/server.console'))
->iconButton()
->icon('tabler-terminal')
->iconSize(IconSize::Large)
->url(fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server))
->authorize(fn (Server $server) => user()?->canAccessTenant($server)),
EditAction::make(),
EditAction::make()
->icon('tabler-pencil')
->hiddenLabel()
->iconSize(IconSize::Large),
ViewAction::make()
->hiddenLabel()
->icon('tabler-eye')
->iconSize(IconSize::ExtraLarge),
])
->emptyStateIcon('tabler-brand-docker')
->searchable()
Expand Down
110 changes: 110 additions & 0 deletions app/Filament/Admin/Resources/Servers/Pages/ViewServer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace App\Filament\Admin\Resources\Servers\Pages;

use App\Filament\Admin\Resources\Servers\RelationManagers\AllocationsRelationManager;
use App\Filament\Admin\Resources\Servers\RelationManagers\DatabasesRelationManager;
use App\Filament\Admin\Resources\Servers\ServerResource;
use App\Filament\Server\Pages\Console;
use App\Models\Server;
use App\Services\Servers\ServerDeletionService;
use App\Traits\Filament\CanCustomizeHeaderActions;
use App\Traits\Filament\CanCustomizeHeaderWidgets;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Actions\EditAction;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ViewRecord;
use Filament\Schemas\Schema;
use Filament\Support\Enums\IconSize;
use Illuminate\Http\Client\ConnectionException;
use Predis\Connection\ConnectionException as PredisConnectionException;

class ViewServer extends ViewRecord
{
use CanCustomizeHeaderActions;
use CanCustomizeHeaderWidgets;

protected static string $resource = ServerResource::class;

/**
* @throws \Random\RandomException
* @throws \Exception
*/
public function form(Schema $schema): Schema
{
return ServerResource::schema($schema);
}

public function getRelationManagers(): array
{
return [
AllocationsRelationManager::class,
DatabasesRelationManager::class,
];
}

/** @return array<Action|ActionGroup> */
protected function getDefaultHeaderActions(): array
{
/** @var Server $server */
$server = $this->getRecord();

$canForceDelete = cache()->get("servers.$server->uuid.canForceDelete", false);

return [
Action::make('console')
->label(trans('admin/server.console'))
->icon('tabler-terminal')
->iconButton()->iconSize(IconSize::ExtraLarge)
->url(fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server)),
Action::make('Delete')
->color('danger')
->label(trans('filament-actions::delete.single.label'))
->modalHeading(trans('filament-actions::delete.single.modal.heading', ['label' => $server->name]))
->modalSubmitActionLabel(trans('filament-actions::delete.single.label'))
->requiresConfirmation()
->action(function (Server $server, ServerDeletionService $service) {
try {
$service->handle($server);

return redirect(ListServers::getUrl(panel: 'admin'));
} catch (ConnectionException|PredisConnectionException) {
cache()->put("servers.$server->uuid.canForceDelete", true, now()->addMinutes(5));

return Notification::make()
->title(trans('admin/server.notifications.error_server_delete'))
->body(trans('admin/server.notifications.error_server_delete_body'))
->color('warning')
->icon('tabler-database')
->warning()
->send();
}
})
->hidden(fn () => $canForceDelete)
->authorize(fn (Server $server) => user()?->can('delete server', $server))
->icon('tabler-trash')
->iconButton()->iconSize(IconSize::ExtraLarge),
Action::make('ForceDelete')
->color('danger')
->label(trans('filament-actions::force-delete.single.label'))
->modalHeading(trans('filament-actions::force-delete.single.modal.heading', ['label' => $server->name]))
->modalSubmitActionLabel(trans('filament-actions::force-delete.single.label'))
->requiresConfirmation()
->action(function (Server $server, ServerDeletionService $service) {
try {
$service->withForce()->handle($server);

return redirect(ListServers::getUrl(panel: 'admin'));
} catch (ConnectionException|PredisConnectionException) {
return cache()->forget("servers.$server->uuid.canForceDelete");
}
Comment on lines +94 to +101
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Silent failure on ForceDelete connection error.

When ForceDelete fails due to a connection exception, the cache flag is cleared but no notification is shown to the user. This could leave the user confused about why the action didn't complete.

 ->action(function (Server $server, ServerDeletionService $service) {
     try {
         $service->withForce()->handle($server);

         return redirect(ListServers::getUrl(panel: 'admin'));
     } catch (ConnectionException|PredisConnectionException) {
-        return cache()->forget("servers.$server->uuid.canForceDelete");
+        cache()->forget("servers.$server->uuid.canForceDelete");
+
+        return Notification::make()
+            ->title(trans('admin/server.notifications.error_server_delete'))
+            ->body(trans('admin/server.notifications.error_server_delete_body'))
+            ->color('danger')
+            ->icon('tabler-database')
+            ->danger()
+            ->send();
     }
 })
🤖 Prompt for AI Agents
In app/Filament/Admin/Resources/Servers/Pages/ViewServer.php around lines 94 to
101, the catch block for ConnectionException|PredisConnectionException only
clears the cache flag and silently fails; update the catch to both clear the
cache and surface a user-facing notification (e.g., Filament/Toast/Notification)
indicating the force-delete failed due to a connection error, then return an
appropriate redirect or back response so the user sees the notification; keep
the existing cache()->forget call and include a concise, user-friendly message
and optional logging of the exception for diagnostics.

})
->visible(fn () => $canForceDelete)
->authorize(fn (Server $server) => user()?->can('delete server', $server)),
EditAction::make()
->icon('tabler-pencil')
->iconButton()->iconSize(IconSize::ExtraLarge),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,14 @@ public function table(Table $table): Table
default => 'gray',
})
->tooltip(fn (Allocation $allocation) => trans('admin/server.' . ($allocation->id === $this->getOwnerRecord()->allocation_id ? 'already' : 'make') . '_primary'))
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
->action(function (Allocation $allocation) {
if (!user()?->can('update server', $this->getOwnerRecord())) {
return;
}

$this->getOwnerRecord()->update(['allocation_id' => $allocation->id]);
$this->deselectAllTableRecords();
})
->default(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id)
->label(trans('admin/server.primary')),
IconColumn::make('is_locked')
Expand All @@ -71,15 +78,24 @@ public function table(Table $table): Table
->recordActions([
Action::make('make-primary')
->label(trans('admin/server.make_primary'))
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
->action(function (Allocation $allocation) {
if (!user()?->can('update server', $this->getOwnerRecord())) {
return;
}
$this->getOwnerRecord()->update(['allocation_id' => $allocation->id]);
$this->deselectAllTableRecords();
})
->disabled(fn () => !user()?->can('update server', $this->getOwnerRecord()))
->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id),
Action::make('lock')
->label(trans('admin/server.lock'))
->action(fn (Allocation $allocation) => $allocation->update(['is_locked' => true]) && $this->deselectAllTableRecords())
->disabled(fn () => !user()?->can('update server', $this->getOwnerRecord()))
->hidden(fn (Allocation $allocation) => $allocation->is_locked),
Action::make('unlock')
->label(trans('admin/server.unlock'))
->action(fn (Allocation $allocation) => $allocation->update(['is_locked' => false]) && $this->deselectAllTableRecords())
->disabled(fn () => !user()?->can('update server', $this->getOwnerRecord()))
->visible(fn (Allocation $allocation) => $allocation->is_locked),
DissociateAction::make()
->after(function (Allocation $allocation) {
Expand Down
Loading
Loading