diff --git a/app/Console/Commands/Egg/UpdateEggIndexCommand.php b/app/Console/Commands/Egg/UpdateEggIndexCommand.php
new file mode 100644
index 000000000..8ef7646b2
--- /dev/null
+++ b/app/Console/Commands/Egg/UpdateEggIndexCommand.php
@@ -0,0 +1,46 @@
+error($exception->getMessage());
+
+ return 1;
+ }
+
+ $index = [];
+ foreach ($data['nests'] as $nest) {
+ $nestName = $nest['nest_type'];
+
+ $this->info("Nest: $nestName");
+
+ $nestEggs = [];
+ foreach ($nest['Eggs'] as $egg) {
+ $eggName = $egg['egg']['name'];
+
+ $this->comment("Egg: $eggName");
+
+ $nestEggs[$egg['download_url']] = $eggName;
+ }
+ $index[$nestName] = $nestEggs;
+
+ $this->info('');
+ }
+
+ cache()->forever('eggs.index', $index);
+
+ return 0;
+ }
+}
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index 48fbe6b84..8421c1b82 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -3,6 +3,7 @@
namespace App\Console;
use App\Console\Commands\Egg\CheckEggUpdatesCommand;
+use App\Console\Commands\Egg\UpdateEggIndexCommand;
use App\Console\Commands\Maintenance\CleanServiceBackupFilesCommand;
use App\Console\Commands\Maintenance\PruneImagesCommand;
use App\Console\Commands\Maintenance\PruneOrphanedBackupsCommand;
@@ -41,7 +42,9 @@ class Kernel extends ConsoleKernel
$schedule->command(CleanServiceBackupFilesCommand::class)->daily();
$schedule->command(PruneImagesCommand::class)->daily();
- $schedule->command(CheckEggUpdatesCommand::class)->hourly();
+
+ $schedule->command(CheckEggUpdatesCommand::class)->daily();
+ $schedule->command(UpdateEggIndexCommand::class)->daily();
if (config('backups.prune_age')) {
// Every 30 minutes, run the backup pruning command so that any abandoned backups can be deleted.
diff --git a/app/Exceptions/Repository/FileExistsException.php b/app/Exceptions/Repository/FileExistsException.php
new file mode 100644
index 000000000..b71649483
--- /dev/null
+++ b/app/Exceptions/Repository/FileExistsException.php
@@ -0,0 +1,7 @@
+label(trans('admin/server.docker_image')),
SelectColumn::make('allocation.id')
->label(trans('admin/server.primary_allocation'))
- ->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address])
- ->selectablePlaceholder(false)
+ ->disabled()
+ ->options(fn (Server $server) => $server->allocations->take(1)->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
+ ->placeholder('None')
->sortable(),
]);
}
diff --git a/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
index 10a3566ca..020d4eb39 100644
--- a/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
+++ b/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
@@ -58,6 +58,9 @@ class AllocationsRelationManager extends RelationManager
TextInputColumn::make('ip_alias')
->searchable()
->label(trans('admin/node.table.alias')),
+ TextInputColumn::make('notes')
+ ->label(trans('admin/node.table.allocation_notes'))
+ ->placeholder(trans('admin/node.table.no_notes')),
SelectColumn::make('ip')
->options(fn (Allocation $allocation) => collect($this->getOwnerRecord()->ipAddresses())->merge([$allocation->ip])->mapWithKeys(fn (string $ip) => [$ip => $ip]))
->selectablePlaceholder(false)
@@ -81,8 +84,7 @@ class AllocationsRelationManager extends RelationManager
->label(trans('admin/node.table.alias'))
->inlineLabel()
->default(null)
- ->helperText(trans('admin/node.alias_help'))
- ->required(false),
+ ->helperText(trans('admin/node.alias_help')),
TagsInput::make('allocation_ports')
->placeholder('27015, 27017-27019')
->label(trans('admin/node.ports'))
diff --git a/app/Filament/Admin/Resources/NodeResource/RelationManagers/NodesRelationManager.php b/app/Filament/Admin/Resources/NodeResource/RelationManagers/NodesRelationManager.php
index 716c80c6c..b2768a3a2 100644
--- a/app/Filament/Admin/Resources/NodeResource/RelationManagers/NodesRelationManager.php
+++ b/app/Filament/Admin/Resources/NodeResource/RelationManagers/NodesRelationManager.php
@@ -43,8 +43,10 @@ class NodesRelationManager extends RelationManager
->sortable(),
SelectColumn::make('allocation.id')
->label(trans('admin/node.primary_allocation'))
- ->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address])
- ->selectablePlaceholder(false)
+ ->disabled(fn (Server $server) => $server->allocations->count() <= 1)
+ ->options(fn (Server $server) => $server->allocations->take(1)->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
+ ->selectablePlaceholder(fn (SelectColumn $select) => !$select->isDisabled())
+ ->placeholder('None')
->sortable(),
TextColumn::make('memory')->label(trans('admin/node.memory'))->icon('tabler-device-desktop-analytics'),
TextColumn::make('cpu')->label(trans('admin/node.cpu'))->icon('tabler-cpu'),
diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
index c0a5b2efe..878279271 100644
--- a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
+++ b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
@@ -128,12 +128,12 @@ class CreateServer extends CreateRecord
->live()
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id')))
->searchable()
+ ->required()
->preload()
->afterStateUpdated(function (Set $set, $state) {
$set('allocation_id', null);
$this->node = Node::find($state);
- })
- ->required(),
+ }),
Select::make('owner_id')
->preload()
@@ -194,7 +194,7 @@ class CreateServer extends CreateRecord
$set('allocation_additional', null);
$set('allocation_additional.needstobeastringhere.extra_allocations', null);
})
- ->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address)
+ ->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address ?? '')
->placeholder(function (Get $get) {
$node = Node::find($get('node_id'));
@@ -248,9 +248,7 @@ class CreateServer extends CreateRecord
return collect(
$assignmentService->handle(Node::find($get('node_id')), $data)
)->first();
- })
- ->required(),
-
+ }),
Repeater::make('allocation_additional')
->label(trans('admin/server.additional_allocations'))
->columnSpan([
@@ -270,7 +268,7 @@ class CreateServer extends CreateRecord
->prefixIcon('tabler-network')
->label('Additional Allocations')
->columnSpan(2)
- ->disabled(fn (Get $get) => $get('../../node_id') === null)
+ ->disabled(fn (Get $get) => $get('../../allocation_id') === null || $get('../../node_id') === null)
->searchable(['ip', 'port', 'ip_alias'])
->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address)
->placeholder(trans('admin/server.select_additional'))
@@ -833,7 +831,9 @@ class CreateServer extends CreateRecord
protected function handleRecordCreation(array $data): Model
{
- $data['allocation_additional'] = collect($data['allocation_additional'])->filter()->all();
+ if ($allocation_additional = array_get($data, 'allocation_additional')) {
+ $data['allocation_additional'] = collect($allocation_additional)->filter()->all();
+ }
try {
return $this->serverCreationService->handle($data);
diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php b/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php
index 18ba0d732..af6b87861 100644
--- a/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php
+++ b/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php
@@ -1020,17 +1020,20 @@ class EditServer extends EditRecord
->options(fn (Server $server) => Node::whereNot('id', $server->node->id)->pluck('name', 'id')->all()),
Select::make('allocation_id')
->label(trans('admin/server.primary_allocation'))
- ->required()
+ ->disabled(fn (Get $get, Server $server) => !$get('node_id') || !$server->allocation_id)
+ ->required(fn (Server $server) => $server->allocation_id)
->prefixIcon('tabler-network')
- ->disabled(fn (Get $get) => !$get('node_id'))
->options(fn (Get $get) => Allocation::where('node_id', $get('node_id'))->whereNull('server_id')->get()->mapWithKeys(fn (Allocation $allocation) => [$allocation->id => $allocation->address]))
->searchable(['ip', 'port', 'ip_alias'])
->placeholder(trans('admin/server.select_allocation')),
Select::make('allocation_additional')
->label(trans('admin/server.additional_allocations'))
+ ->disabled(fn (Get $get, Server $server) => !$get('node_id') || $server->allocations->count() <= 1)
->multiple()
+ ->minItems(fn (Select $select) => $select->getMaxItems())
+ ->maxItems(fn (Select $select, Server $server) => $select->isDisabled() ? null : $server->allocations->count() - 1)
->prefixIcon('tabler-network')
- ->disabled(fn (Get $get) => !$get('node_id'))
+ ->required(fn (Server $server) => $server->allocations->count() > 1)
->options(fn (Get $get) => Allocation::where('node_id', $get('node_id'))->whereNull('server_id')->when($get('allocation_id'), fn ($query) => $query->whereNot('id', $get('allocation_id')))->get()->mapWithKeys(fn (Allocation $allocation) => [$allocation->id => $allocation->address]))
->searchable(['ip', 'port', 'ip_alias'])
->placeholder(trans('admin/server.select_additional')),
diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/ListServers.php b/app/Filament/Admin/Resources/ServerResource/Pages/ListServers.php
index 14de8fc4b..e1c69a98f 100644
--- a/app/Filament/Admin/Resources/ServerResource/Pages/ListServers.php
+++ b/app/Filament/Admin/Resources/ServerResource/Pages/ListServers.php
@@ -73,14 +73,17 @@ class ListServers extends ListRecords
->searchable(),
SelectColumn::make('allocation_id')
->label(trans('admin/server.primary_allocation'))
- ->hidden(!auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
+ ->hidden(fn () => !auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
+ ->disabled(fn (Server $server) => $server->allocations->count() <= 1)
->options(fn (Server $server) => $server->allocations->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
- ->selectablePlaceholder(false)
+ ->selectablePlaceholder(fn (Server $server) => $server->allocations->count() <= 1)
+ ->placeholder('None')
->sortable(),
TextColumn::make('allocation_id_readonly')
->label(trans('admin/server.primary_allocation'))
- ->hidden(auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
- ->state(fn (Server $server) => $server->allocation->address),
+ ->hidden(fn () => auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
+ ->disabled(fn (Server $server) => $server->allocations->count() <= 1)
+ ->state(fn (Server $server) => $server->allocation->address ?? 'None'),
TextColumn::make('image')->hidden(),
TextColumn::make('backups_count')
->counts('backups')
diff --git a/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
index 14ca71d1d..070812053 100644
--- a/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
+++ b/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
@@ -12,8 +12,6 @@ use Filament\Forms\Components\TextInput;
use Filament\Forms\Get;
use Filament\Forms\Set;
use Filament\Resources\RelationManagers\RelationManager;
-use Filament\Support\Exceptions\Halt;
-use Filament\Tables\Actions\Action;
use Filament\Tables\Actions\AssociateAction;
use Filament\Tables\Actions\CreateAction;
use Filament\Tables\Actions\DissociateAction;
@@ -22,7 +20,6 @@ use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\TextInputColumn;
use Filament\Tables\Table;
-use Illuminate\Database\Eloquent\Collection;
/**
* @method Server getOwnerRecord()
@@ -37,7 +34,6 @@ class AllocationsRelationManager extends RelationManager
->selectCurrentPageOnly()
->recordTitleAttribute('address')
->recordTitle(fn (Allocation $allocation) => $allocation->address)
- ->checkIfRecordIsSelectableUsing(fn (Allocation $record) => $record->id !== $this->getOwnerRecord()->allocation_id)
->inverseRelationship('server')
->heading(trans('admin/server.allocations'))
->columns([
@@ -47,6 +43,9 @@ class AllocationsRelationManager extends RelationManager
->label(trans('admin/server.port')),
TextInputColumn::make('ip_alias')
->label(trans('admin/server.alias')),
+ TextInputColumn::make('notes')
+ ->label(trans('admin/server.notes'))
+ ->placeholder(trans('admin/server.no_notes')),
IconColumn::make('primary')
->icon(fn ($state) => match ($state) {
true => 'tabler-star-filled',
@@ -56,17 +55,17 @@ class AllocationsRelationManager extends RelationManager
true => 'warning',
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())
->default(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id)
->label(trans('admin/server.primary')),
])
->actions([
- Action::make('make-primary')
- ->label(trans('admin/server.make_primary'))
- ->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
- ->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id),
DissociateAction::make()
- ->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id),
+ ->after(function (Allocation $allocation) {
+ $allocation->update(['notes' => null]);
+ $this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
+ }),
])
->headerActions([
CreateAction::make()->label(trans('admin/server.create_allocation'))
@@ -84,8 +83,7 @@ class AllocationsRelationManager extends RelationManager
->label(trans('admin/server.alias'))
->inlineLabel()
->default(null)
- ->helperText(trans('admin/server.alias_helper'))
- ->required(false),
+ ->helperText(trans('admin/server.alias_helper')),
TagsInput::make('allocation_ports')
->placeholder('27015, 27017-27019')
->label(trans('admin/server.ports'))
@@ -103,22 +101,14 @@ class AllocationsRelationManager extends RelationManager
->preloadRecordSelect()
->recordSelectOptionsQuery(fn ($query) => $query->whereBelongsTo($this->getOwnerRecord()->node)->whereNull('server_id'))
->recordSelectSearchColumns(['ip', 'port'])
- ->label(trans('admin/server.add_allocation')),
+ ->label(trans('admin/server.add_allocation'))
+ ->after(fn (array $data) => !$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $data['recordId'][0]])),
])
->groupedBulkActions([
DissociateBulkAction::make()
- ->before(function (DissociateBulkAction $action, Collection $records) {
- $records = $records->filter(function ($allocation) {
- /** @var Allocation $allocation */
- return $allocation->id !== $this->getOwnerRecord()->allocation_id;
- });
-
- if ($records->isEmpty()) {
- $action->failureNotificationTitle(trans('admin/server.notifications.dissociate_primary'))->failure();
- throw new Halt();
- }
-
- return $records;
+ ->after(function () {
+ Allocation::whereNull('server_id')->update(['notes' => null]);
+ $this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
}),
]);
}
diff --git a/app/Filament/Admin/Resources/UserResource/RelationManagers/ServersRelationManager.php b/app/Filament/Admin/Resources/UserResource/RelationManagers/ServersRelationManager.php
index 5f2c7b479..9388a32ac 100644
--- a/app/Filament/Admin/Resources/UserResource/RelationManagers/ServersRelationManager.php
+++ b/app/Filament/Admin/Resources/UserResource/RelationManagers/ServersRelationManager.php
@@ -70,8 +70,9 @@ class ServersRelationManager extends RelationManager
->sortable(),
SelectColumn::make('allocation.id')
->label(trans('admin/server.primary_allocation'))
- ->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address])
- ->selectablePlaceholder(false)
+ ->disabled()
+ ->options(fn (Server $server) => $server->allocations->take(1)->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
+ ->placeholder('None')
->sortable(),
TextColumn::make('image')->hidden(),
TextColumn::make('databases_count')
diff --git a/app/Filament/App/Resources/ServerResource/Pages/ListServers.php b/app/Filament/App/Resources/ServerResource/Pages/ListServers.php
index dd78433a3..1568cbeaa 100644
--- a/app/Filament/App/Resources/ServerResource/Pages/ListServers.php
+++ b/app/Filament/App/Resources/ServerResource/Pages/ListServers.php
@@ -74,7 +74,8 @@ class ListServers extends ListRecords
->label('')
->badge()
->visibleFrom('md')
- ->copyable(request()->isSecure()),
+ ->copyable(request()->isSecure())
+ ->state(fn (Server $server) => $server->allocation->address ?? 'None'),
TextColumn::make('cpuUsage')
->label('Resources')
->icon('tabler-cpu')
diff --git a/app/Filament/Components/Actions/ImportEggAction.php b/app/Filament/Components/Actions/ImportEggAction.php
index c6e3b8b58..615cbe0ba 100644
--- a/app/Filament/Components/Actions/ImportEggAction.php
+++ b/app/Filament/Components/Actions/ImportEggAction.php
@@ -2,6 +2,7 @@
namespace App\Filament\Components\Actions;
+use App\Console\Commands\Egg\UpdateEggIndexCommand;
use App\Models\Egg;
use App\Services\Eggs\Sharing\EggImporterService;
use Closure;
@@ -9,11 +10,16 @@ use Exception;
use Filament\Actions\Action;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Repeater;
+use Filament\Forms\Components\Select;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TextInput;
+use Filament\Forms\Get;
+use Filament\Forms\Set;
use Filament\Notifications\Notification;
use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Artisan;
+use Illuminate\Support\Str;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
class ImportEggAction extends Action
@@ -97,7 +103,28 @@ class ImportEggAction extends Action
Tab::make(trans('admin/egg.import.url'))
->icon('tabler-world-upload')
->schema([
+ Select::make('github')
+ ->label(trans('admin/egg.import.github'))
+ ->options(cache('eggs.index'))
+ ->selectablePlaceholder(false)
+ ->searchable()
+ ->preload()
+ ->live()
+ ->hintIcon('tabler-refresh')
+ ->hintIconTooltip(trans('admin/egg.import.refresh'))
+ ->hintAction(function () {
+ Artisan::call(UpdateEggIndexCommand::class);
+ })
+ ->afterStateUpdated(function ($state, Set $set, Get $get) use ($isMultiple) {
+ if ($state) {
+ $urls = $isMultiple ? $get('urls') : [];
+ $urls[Str::uuid()->toString()] = ['url' => $state];
+ $set('urls', $urls);
+ $set('github', null);
+ }
+ }),
Repeater::make('urls')
+ ->label('')
->itemLabel(fn (array $state) => str($state['url'])->afterLast('/egg-')->before('.json')->headline())
->hint(trans('admin/egg.import.url_help'))
->addActionLabel(trans('admin/egg.import.add_url'))
diff --git a/app/Filament/Components/Tables/Actions/ImportEggAction.php b/app/Filament/Components/Tables/Actions/ImportEggAction.php
index 426ba46b9..b51576535 100644
--- a/app/Filament/Components/Tables/Actions/ImportEggAction.php
+++ b/app/Filament/Components/Tables/Actions/ImportEggAction.php
@@ -8,12 +8,16 @@ use Closure;
use Exception;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Repeater;
+use Filament\Forms\Components\Select;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TextInput;
+use Filament\Forms\Get;
+use Filament\Forms\Set;
use Filament\Notifications\Notification;
use Filament\Tables\Actions\Action;
use Illuminate\Support\Arr;
+use Illuminate\Support\Str;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
class ImportEggAction extends Action
@@ -97,6 +101,21 @@ class ImportEggAction extends Action
Tab::make(trans('admin/egg.import.url'))
->icon('tabler-world-upload')
->schema([
+ Select::make('github')
+ ->label(trans('admin/egg.import.github'))
+ ->options(cache('eggs.index'))
+ ->selectablePlaceholder(false)
+ ->searchable()
+ ->preload()
+ ->live()
+ ->afterStateUpdated(function ($state, Set $set, Get $get) use ($isMultiple) {
+ if ($state) {
+ $urls = $isMultiple ? $get('urls') : [];
+ $urls[Str::uuid()->toString()] = ['url' => $state];
+ $set('urls', $urls);
+ $set('github', null);
+ }
+ }),
Repeater::make('urls')
->itemLabel(fn (array $state) => str($state['url'])->afterLast('/egg-')->before('.json')->headline())
->hint(trans('admin/egg.import.url_help'))
diff --git a/app/Filament/Server/Resources/AllocationResource.php b/app/Filament/Server/Resources/AllocationResource.php
index 26ae815ee..15fd24527 100644
--- a/app/Filament/Server/Resources/AllocationResource.php
+++ b/app/Filament/Server/Resources/AllocationResource.php
@@ -65,11 +65,8 @@ class AllocationResource extends Resource
true => 'warning',
default => 'gray',
})
- ->action(function (Allocation $allocation) use ($server) {
- if (auth()->user()->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server)) {
- return $server->update(['allocation_id' => $allocation->id]);
- }
- })
+ ->tooltip(fn (Allocation $allocation) => ($allocation->id === $server->allocation_id ? 'Already' : 'Make') . ' Primary')
+ ->action(fn (Allocation $allocation) => auth()->user()->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id]))
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
->label('Primary'),
])
@@ -78,7 +75,6 @@ class AllocationResource extends Resource
->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_DELETE, $server))
->label('Delete')
->icon('tabler-trash')
- ->hidden(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
->action(function (Allocation $allocation) {
Allocation::query()->where('id', $allocation->id)->update([
'notes' => null,
@@ -89,7 +85,8 @@ class AllocationResource extends Resource
->subject($allocation)
->property('allocation', $allocation->address)
->log();
- }),
+ })
+ ->after(fn (Allocation $allocation) => $allocation->id === $server->allocation_id && $server->update(['allocation_id' => $server->allocations()->first()?->id])),
]);
}
diff --git a/app/Filament/Server/Resources/AllocationResource/Pages/ListAllocations.php b/app/Filament/Server/Resources/AllocationResource/Pages/ListAllocations.php
index 83e5ab4c7..5d1e14155 100644
--- a/app/Filament/Server/Resources/AllocationResource/Pages/ListAllocations.php
+++ b/app/Filament/Server/Resources/AllocationResource/Pages/ListAllocations.php
@@ -37,6 +37,10 @@ class ListAllocations extends ListRecords
->action(function (FindAssignableAllocationService $service) use ($server) {
$allocation = $service->handle($server);
+ if (!$server->allocation_id) {
+ $server->update(['allocation_id' => $allocation->id]);
+ }
+
Activity::event('server:allocation.create')
->subject($allocation)
->property('allocation', $allocation->address)
diff --git a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php
index d490d1ee8..9fa1de342 100644
--- a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php
+++ b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php
@@ -4,6 +4,7 @@ namespace App\Filament\Server\Resources\FileResource\Pages;
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
use App\Enums\EditorLanguages;
+use App\Exceptions\Repository\FileExistsException;
use App\Facades\Activity;
use App\Filament\Server\Resources\FileResource;
use App\Models\File;
@@ -12,6 +13,7 @@ use App\Models\Server;
use App\Repositories\Daemon\DaemonFileRepository;
use App\Filament\Components\Tables\Columns\BytesColumn;
use App\Filament\Components\Tables\Columns\DateTimeColumn;
+use App\Livewire\AlertBanner;
use App\Traits\Filament\CanCustomizeHeaderActions;
use App\Traits\Filament\CanCustomizeHeaderWidgets;
use Filament\Actions\Action as HeaderAction;
@@ -419,11 +421,22 @@ class ListFiles extends ListRecords
->keyBindings('')
->modalSubmitActionLabel('Create')
->action(function ($data) {
- $this->getDaemonFileRepository()->putContent(join_paths($this->path, $data['name']), $data['editor'] ?? '');
+ $path = join_paths($this->path, $data['name']);
+ try {
+ $this->getDaemonFileRepository()->putContent($path, $data['editor'] ?? '');
- Activity::event('server:file.write')
- ->property('file', join_paths($this->path, $data['name']))
- ->log();
+ Activity::event('server:file.write')
+ ->property('file', join_paths($path, $data['name']))
+ ->log();
+ } catch (FileExistsException) {
+ AlertBanner::make()
+ ->title('' . $path . '
already exists!')
+ ->danger()
+ ->closable()
+ ->send();
+
+ $this->redirect(self::getUrl(['path' => dirname($path)]));
+ }
})
->form([
TextInput::make('name')
@@ -448,11 +461,22 @@ class ListFiles extends ListRecords
->label('New Folder')
->color('gray')
->action(function ($data) {
- $this->getDaemonFileRepository()->createDirectory($data['name'], $this->path);
+ try {
+ $this->getDaemonFileRepository()->createDirectory($data['name'], $this->path);
- Activity::event('server:file.create-directory')
- ->property(['directory' => $this->path, 'name' => $data['name']])
- ->log();
+ Activity::event('server:file.create-directory')
+ ->property(['directory' => $this->path, 'name' => $data['name']])
+ ->log();
+ } catch (FileExistsException) {
+ $path = join_paths($this->path, $data['name']);
+ AlertBanner::make()
+ ->title('' . $path . '
already exists!')
+ ->danger()
+ ->closable()
+ ->send();
+
+ $this->redirect(self::getUrl(['path' => dirname($path)]));
+ }
})
->form([
TextInput::make('name')
diff --git a/app/Filament/Server/Resources/ScheduleResource/RelationManagers/TasksRelationManager.php b/app/Filament/Server/Resources/ScheduleResource/RelationManagers/TasksRelationManager.php
index 5b7b2ffe4..141818a54 100644
--- a/app/Filament/Server/Resources/ScheduleResource/RelationManagers/TasksRelationManager.php
+++ b/app/Filament/Server/Resources/ScheduleResource/RelationManagers/TasksRelationManager.php
@@ -6,6 +6,7 @@ use App\Facades\Activity;
use App\Models\Schedule;
use App\Models\Task;
use Filament\Forms\Components\Field;
+use Filament\Forms\Set;
use Filament\Tables\Actions\DeleteAction;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
@@ -45,10 +46,11 @@ class TasksRelationManager extends RelationManager
Select::make('action')
->required()
->live()
- ->disableOptionWhen(fn (string $value): bool => $value === Task::ACTION_BACKUP && $schedule->server->backup_limit === 0)
+ ->disableOptionWhen(fn (string $value) => $value === Task::ACTION_BACKUP && $schedule->server->backup_limit === 0)
->options($this->getActionOptions())
->selectablePlaceholder(false)
- ->default(Task::ACTION_POWER),
+ ->default(Task::ACTION_POWER)
+ ->afterStateUpdated(fn ($state, Set $set) => $set('payload', $state === Task::ACTION_POWER ? 'restart' : null)),
Textarea::make('payload')
->hidden(fn (Get $get) => $get('action') === Task::ACTION_POWER)
->label(fn (Get $get) => $this->getActionOptions(false)[$get('action')] ?? 'Payload'),
@@ -81,7 +83,8 @@ class TasksRelationManager extends RelationManager
$schedule = $this->getOwnerRecord();
return $table
- ->reorderable('sequence_id', true)
+ ->reorderable('sequence_id')
+ ->defaultSort('sequence_id')
->columns([
TextColumn::make('action')
->state(fn (Task $task) => $this->getActionOptions()[$task->action] ?? $task->action),
diff --git a/app/Filament/Server/Widgets/ServerOverview.php b/app/Filament/Server/Widgets/ServerOverview.php
index 0f7455cb1..510e42e5b 100644
--- a/app/Filament/Server/Widgets/ServerOverview.php
+++ b/app/Filament/Server/Widgets/ServerOverview.php
@@ -23,7 +23,7 @@ class ServerOverview extends StatsOverviewWidget
SmallStatBlock::make('Name', $this->server->name)
->copyOnClick(fn () => request()->isSecure()),
SmallStatBlock::make('Status', $this->status()),
- SmallStatBlock::make('Address', $this->server->allocation->address)
+ SmallStatBlock::make('Address', $this->server?->allocation->address ?? 'None')
->copyOnClick(fn () => request()->isSecure()),
SmallStatBlock::make('CPU', $this->cpuUsage()),
SmallStatBlock::make('Memory', $this->memoryUsage()),
@@ -68,7 +68,7 @@ class ServerOverview extends StatsOverviewWidget
}
$latestMemoryUsed = collect(cache()->get("servers.{$this->server->id}.memory_bytes"))->last(default: 0);
- $totalMemory = collect(cache()->get("servers.{$this->server->id}.memory_limit_bytes"))->last(default: 0);
+ $totalMemory = $this->server->memory * 2 ** 20;
$used = convert_bytes_to_readable($latestMemoryUsed);
$total = convert_bytes_to_readable($totalMemory);
diff --git a/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php b/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php
index c287addb6..2c2ad6e85 100644
--- a/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php
+++ b/app/Http/Controllers/Api/Client/Servers/NetworkAllocationController.php
@@ -137,10 +137,6 @@ class NetworkAllocationController extends ClientApiController
throw new DisplayException('You cannot delete allocations for this server: no allocation limit is set.');
}
- if ($allocation->id === $server->allocation_id) {
- throw new DisplayException('You cannot delete the primary allocation for this server.');
- }
-
Allocation::query()->where('id', $allocation->id)->update([
'notes' => null,
'server_id' => null,
diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php
index 7dbce0288..514f7f60f 100644
--- a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php
+++ b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php
@@ -50,17 +50,18 @@ class ServerTransferController extends Controller
throw new ConflictHttpException('Server is not being transferred.');
}
+ $data = [];
/** @var \App\Models\Server $server */
- $server = $this->connection->transaction(function () use ($server, $transfer) {
- $allocations = array_merge([$transfer->old_allocation], $transfer->old_additional_allocations);
-
- // Remove the old allocations for the server and re-assign the server to the new
- // primary allocation and node.
- Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]);
- $server->update([
- 'allocation_id' => $transfer->new_allocation,
- 'node_id' => $transfer->new_node,
- ]);
+ $server = $this->connection->transaction(function () use ($server, $transfer, $data) {
+ if ($transfer->old_allocation || $transfer->old_additional_allocations) {
+ $allocations = array_merge([$transfer->old_allocation], $transfer->old_additional_allocations);
+ // Remove the old allocations for the server and re-assign the server to the new
+ // primary allocation and node.
+ Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]);
+ $data['allocation_id'] = $transfer->new_allocation;
+ }
+ $data['node_id'] = $transfer->new_node;
+ $server->update($data);
$server = $server->fresh();
$server->transfer->update(['successful' => true]);
@@ -93,8 +94,10 @@ class ServerTransferController extends Controller
$this->connection->transaction(function () use (&$transfer) {
$transfer->forceFill(['successful' => false])->saveOrFail();
- $allocations = array_merge([$transfer->new_allocation], $transfer->new_additional_allocations);
- Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]);
+ if ($transfer->new_allocation || $transfer->new_additional_allocations) {
+ $allocations = array_merge([$transfer->new_allocation], $transfer->new_additional_allocations);
+ Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]);
+ }
});
return new JsonResponse([], Response::HTTP_NO_CONTENT);
diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php
index 1c217dd68..fb94dd739 100644
--- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php
+++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php
@@ -176,6 +176,7 @@ class StoreServerRequest extends ApplicationApiRequest
$object->setDedicated($this->input('deploy.dedicated_ip', false));
$object->setTags($this->input('deploy.tags', $this->input('deploy.locations', [])));
$object->setPorts($this->input('deploy.port_range', []));
+ $object->setNode($this->input('deploy.node_id'));
return $object;
}
diff --git a/app/Livewire/ServerEntry.php b/app/Livewire/ServerEntry.php
index 35b1a3095..958c6bbbc 100644
--- a/app/Livewire/ServerEntry.php
+++ b/app/Livewire/ServerEntry.php
@@ -24,7 +24,7 @@ class ServerEntry extends Component
style="background-color: #D97706;">
-
Network
{{ $server->allocation->address }}
+{{ $server->allocation?->address ?? 'None' }}
Network
{{ $server->allocation->address }}
+{{ $server->allocation?->address ?? 'None' }}