mirror of
https://github.com/pelican-dev/panel.git
synced 2025-11-09 20:19:26 +01:00
Merge remote-tracking branch 'JoanFo1456/main' into charles/drag&drop
This commit is contained in:
commit
993f3004f2
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,6 +21,7 @@ yarn-error.log
|
|||||||
/.idea
|
/.idea
|
||||||
/.nova
|
/.nova
|
||||||
/.vscode
|
/.vscode
|
||||||
|
/.ddev
|
||||||
|
|
||||||
public/assets/manifest.json
|
public/assets/manifest.json
|
||||||
/database/*.sqlite*
|
/database/*.sqlite*
|
||||||
|
|||||||
@ -4,7 +4,7 @@ namespace App\Filament\Admin\Resources\Nodes\Pages;
|
|||||||
|
|
||||||
use App\Filament\Admin\Resources\Nodes\NodeResource;
|
use App\Filament\Admin\Resources\Nodes\NodeResource;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||||
use App\Services\Helpers\SoftwareVersionService;
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
use App\Services\Nodes\NodeAutoDeployService;
|
use App\Services\Nodes\NodeAutoDeployService;
|
||||||
use App\Services\Nodes\NodeUpdateService;
|
use App\Services\Nodes\NodeUpdateService;
|
||||||
@ -14,6 +14,8 @@ use Exception;
|
|||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Actions\DeleteAction;
|
use Filament\Actions\DeleteAction;
|
||||||
use Filament\Forms\Components\Hidden;
|
use Filament\Forms\Components\Hidden;
|
||||||
|
use Filament\Forms\Components\Slider;
|
||||||
|
use Filament\Forms\Components\Slider\Enums\PipsMode;
|
||||||
use Filament\Forms\Components\TagsInput;
|
use Filament\Forms\Components\TagsInput;
|
||||||
use Filament\Forms\Components\Textarea;
|
use Filament\Forms\Components\Textarea;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
@ -25,6 +27,7 @@ use Filament\Resources\Pages\EditRecord;
|
|||||||
use Filament\Schemas\Components\Actions;
|
use Filament\Schemas\Components\Actions;
|
||||||
use Filament\Schemas\Components\Fieldset;
|
use Filament\Schemas\Components\Fieldset;
|
||||||
use Filament\Schemas\Components\Grid;
|
use Filament\Schemas\Components\Grid;
|
||||||
|
use Filament\Schemas\Components\Section;
|
||||||
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
|
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
|
||||||
use Filament\Schemas\Components\Tabs;
|
use Filament\Schemas\Components\Tabs;
|
||||||
use Filament\Schemas\Components\Tabs\Tab;
|
use Filament\Schemas\Components\Tabs\Tab;
|
||||||
@ -33,7 +36,10 @@ use Filament\Schemas\Components\Utilities\Set;
|
|||||||
use Filament\Schemas\Components\View;
|
use Filament\Schemas\Components\View;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Support\Enums\Alignment;
|
use Filament\Support\Enums\Alignment;
|
||||||
|
use Filament\Support\Enums\IconSize;
|
||||||
|
use Filament\Support\RawJs;
|
||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\HtmlString;
|
use Illuminate\Support\HtmlString;
|
||||||
use Phiki\Grammar\Grammar;
|
use Phiki\Grammar\Grammar;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@ -45,13 +51,13 @@ class EditNode extends EditRecord
|
|||||||
|
|
||||||
protected static string $resource = NodeResource::class;
|
protected static string $resource = NodeResource::class;
|
||||||
|
|
||||||
private DaemonConfigurationRepository $daemonConfigurationRepository;
|
private DaemonSystemRepository $daemonSystemRepository;
|
||||||
|
|
||||||
private NodeUpdateService $nodeUpdateService;
|
private NodeUpdateService $nodeUpdateService;
|
||||||
|
|
||||||
public function boot(DaemonConfigurationRepository $daemonConfigurationRepository, NodeUpdateService $nodeUpdateService): void
|
public function boot(DaemonSystemRepository $daemonSystemRepository, NodeUpdateService $nodeUpdateService): void
|
||||||
{
|
{
|
||||||
$this->daemonConfigurationRepository = $daemonConfigurationRepository;
|
$this->daemonSystemRepository = $daemonSystemRepository;
|
||||||
$this->nodeUpdateService = $nodeUpdateService;
|
$this->nodeUpdateService = $nodeUpdateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,6 +630,154 @@ class EditNode extends EditRecord
|
|||||||
])->fullWidth(),
|
])->fullWidth(),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
Tab::make('diagnostics')
|
||||||
|
->label(trans('admin/node.tabs.diagnostics'))
|
||||||
|
->icon('tabler-heart-search')
|
||||||
|
->schema([
|
||||||
|
Section::make('diag')
|
||||||
|
->heading(trans('admin/node.tabs.diagnostics'))
|
||||||
|
->columnSpanFull()
|
||||||
|
->columns(4)
|
||||||
|
->disabled(fn (Get $get) => $get('pulled'))
|
||||||
|
->headerActions([
|
||||||
|
Action::make('pull')
|
||||||
|
->label(trans('admin/node.diagnostics.pull'))
|
||||||
|
->icon('tabler-cloud-download')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||||
|
->hidden(fn (Get $get) => $get('pulled'))
|
||||||
|
->action(function (Get $get, Set $set, Node $node) {
|
||||||
|
$includeEndpoints = $get('include_endpoints') ?? true;
|
||||||
|
$includeLogs = $get('include_logs') ?? true;
|
||||||
|
$logLines = $get('log_lines') ?? 200;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->daemonSystemRepository->setNode($node)->getDiagnostics($logLines, $includeEndpoints, $includeLogs);
|
||||||
|
|
||||||
|
if ($response->status() === 404) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.404'))
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$set('pulled', true);
|
||||||
|
$set('uploaded', false);
|
||||||
|
$set('log', $response->body());
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.logs_pulled'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
} catch (ConnectionException $e) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.error_connecting', ['node' => $node->name]))
|
||||||
|
->body($e->getMessage())
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Action::make('upload')
|
||||||
|
->label(trans('admin/node.diagnostics.upload'))
|
||||||
|
->visible(fn (Get $get) => $get('pulled') ?? false)
|
||||||
|
->icon('tabler-cloud-upload')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||||
|
->action(function (Get $get, Set $set) {
|
||||||
|
try {
|
||||||
|
$response = Http::asMultipart()->post('https://logs.pelican.dev', [
|
||||||
|
[
|
||||||
|
'name' => 'c',
|
||||||
|
'contents' => $get('log'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'e',
|
||||||
|
'contents' => '14d',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($response->failed()) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.upload_failed'))
|
||||||
|
->body(fn () => $response->status() . ' - ' . $response->body())
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $response->json();
|
||||||
|
$url = $data['url'];
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.logs_uploaded'))
|
||||||
|
->body("{$url}")
|
||||||
|
->success()
|
||||||
|
->actions([
|
||||||
|
Action::make('viewLogs')
|
||||||
|
->label(trans('admin/node.diagnostics.view_logs'))
|
||||||
|
->url($url)
|
||||||
|
->openUrlInNewTab(true),
|
||||||
|
])
|
||||||
|
->persistent()
|
||||||
|
->send();
|
||||||
|
$set('log', $url);
|
||||||
|
$set('pulled', false);
|
||||||
|
$set('uploaded', true);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.upload_failed'))
|
||||||
|
->body($e->getMessage())
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Action::make('clear')
|
||||||
|
->label(trans('admin/node.diagnostics.clear'))
|
||||||
|
->visible(fn (Get $get) => $get('pulled') ?? false)
|
||||||
|
->icon('tabler-trash')->iconButton()->iconSize(IconSize::ExtraLarge)->color('danger')
|
||||||
|
->action(function (Get $get, Set $set) {
|
||||||
|
$set('pulled', false);
|
||||||
|
$set('uploaded', false);
|
||||||
|
$set('log', null);
|
||||||
|
$this->refresh();
|
||||||
|
}
|
||||||
|
),
|
||||||
|
])
|
||||||
|
->schema([
|
||||||
|
ToggleButtons::make('include_endpoints')
|
||||||
|
->hintIcon('tabler-question-mark')->inline()
|
||||||
|
->hintIconTooltip(trans('admin/node.diagnostics.include_endpoints_hint'))
|
||||||
|
->formatStateUsing(fn () => 1)
|
||||||
|
->boolean(),
|
||||||
|
ToggleButtons::make('include_logs')
|
||||||
|
->live()
|
||||||
|
->hintIcon('tabler-question-mark')->inline()
|
||||||
|
->hintIconTooltip(trans('admin/node.diagnostics.include_logs_hint'))
|
||||||
|
->formatStateUsing(fn () => 1)
|
||||||
|
->boolean(),
|
||||||
|
Slider::make('log_lines')
|
||||||
|
->columnSpan(2)
|
||||||
|
->hiddenLabel()
|
||||||
|
->live()
|
||||||
|
->tooltips(RawJs::make(<<<'JS'
|
||||||
|
`${$value} lines`
|
||||||
|
JS))
|
||||||
|
->visible(fn (Get $get) => $get('include_logs'))
|
||||||
|
->range(minValue: 100, maxValue: 500)
|
||||||
|
->pips(PipsMode::Steps, density: 10)
|
||||||
|
->step(50)
|
||||||
|
->formatStateUsing(fn () => 200)
|
||||||
|
->fillTrack(),
|
||||||
|
Hidden::make('pulled'),
|
||||||
|
Hidden::make('uploaded'),
|
||||||
|
]),
|
||||||
|
Textarea::make('log')
|
||||||
|
->hiddenLabel()
|
||||||
|
->columnSpanFull()
|
||||||
|
->rows(35)
|
||||||
|
->visible(fn (Get $get) => ($get('pulled') ?? false) || ($get('uploaded') ?? false)),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -681,7 +835,7 @@ class EditNode extends EditRecord
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if ($changed) {
|
if ($changed) {
|
||||||
$this->daemonConfigurationRepository->setNode($node)->update($node);
|
$this->daemonSystemRepository->setNode($node)->update($node);
|
||||||
}
|
}
|
||||||
parent::getSavedNotification()?->send();
|
parent::getSavedNotification()?->send();
|
||||||
} catch (ConnectionException) {
|
} catch (ConnectionException) {
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Admin\Resources\Nodes\RelationManagers;
|
namespace App\Filament\Admin\Resources\Nodes\RelationManagers;
|
||||||
|
|
||||||
use App\Filament\Admin\Resources\Servers\Pages\CreateServer;
|
use App\Filament\Admin\Resources\Servers\Pages\CreateServer;
|
||||||
|
use App\Filament\Components\Actions\UpdateNodeAllocations;
|
||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Services\Allocations\AssignmentService;
|
use App\Services\Allocations\AssignmentService;
|
||||||
@ -80,7 +81,9 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->searchable()
|
->searchable()
|
||||||
->label(trans('admin/node.table.ip')),
|
->label(trans('admin/node.table.ip')),
|
||||||
])
|
])
|
||||||
->headerActions([
|
->toolbarActions([
|
||||||
|
DeleteBulkAction::make()
|
||||||
|
->authorize(fn () => user()?->can('update', $this->getOwnerRecord())),
|
||||||
Action::make('create new allocation')
|
Action::make('create new allocation')
|
||||||
->label(trans('admin/node.create_allocation'))
|
->label(trans('admin/node.create_allocation'))
|
||||||
->schema(fn () => [
|
->schema(fn () => [
|
||||||
@ -118,9 +121,8 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->required(),
|
->required(),
|
||||||
])
|
])
|
||||||
->action(fn (array $data, AssignmentService $service) => $service->handle($this->getOwnerRecord(), $data)),
|
->action(fn (array $data, AssignmentService $service) => $service->handle($this->getOwnerRecord(), $data)),
|
||||||
])
|
UpdateNodeAllocations::make()
|
||||||
->groupedBulkActions([
|
->nodeRecord($this->getOwnerRecord())
|
||||||
DeleteBulkAction::make()
|
|
||||||
->authorize(fn () => user()?->can('update', $this->getOwnerRecord())),
|
->authorize(fn () => user()?->can('update', $this->getOwnerRecord())),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,6 +116,14 @@ class CreateServer extends CreateRecord
|
|||||||
->prefixIcon('tabler-server-2')
|
->prefixIcon('tabler-server-2')
|
||||||
->selectablePlaceholder(false)
|
->selectablePlaceholder(false)
|
||||||
->default(function () {
|
->default(function () {
|
||||||
|
$lastUsedNode = session()->get('last_utilized_node');
|
||||||
|
|
||||||
|
if ($lastUsedNode && user()?->accessibleNodes()->where('id', $lastUsedNode)->exists()) {
|
||||||
|
$this->node = Node::find($lastUsedNode);
|
||||||
|
|
||||||
|
return $this->node?->id;
|
||||||
|
}
|
||||||
|
|
||||||
/** @var ?Node $latestNode */
|
/** @var ?Node $latestNode */
|
||||||
$latestNode = user()?->accessibleNodes()->latest()->first();
|
$latestNode = user()?->accessibleNodes()->latest()->first();
|
||||||
$this->node = $latestNode;
|
$this->node = $latestNode;
|
||||||
@ -829,6 +837,8 @@ class CreateServer extends CreateRecord
|
|||||||
$data['allocation_additional'] = collect($allocation_additional)->filter()->all();
|
$data['allocation_additional'] = collect($allocation_additional)->filter()->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session()->put('last_utilized_node', $data['node_id']);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return $this->serverCreationService->handle($data);
|
return $this->serverCreationService->handle($data);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
|
|||||||
@ -60,16 +60,35 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
|
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
|
||||||
->default(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id)
|
->default(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id)
|
||||||
->label(trans('admin/server.primary')),
|
->label(trans('admin/server.primary')),
|
||||||
|
IconColumn::make('is_locked')
|
||||||
|
->label(trans('admin/server.locked'))
|
||||||
|
->tooltip(trans('admin/server.locked_helper'))
|
||||||
|
->trueIcon('tabler-lock')
|
||||||
|
->falseIcon('tabler-lock-open'),
|
||||||
])
|
])
|
||||||
->recordActions([
|
->recordActions([
|
||||||
Action::make('make-primary')
|
Action::make('make-primary')
|
||||||
->label(trans('admin/server.make_primary'))
|
->label(trans('admin/server.make_primary'))
|
||||||
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
|
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
|
||||||
->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id),
|
->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())
|
||||||
|
->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())
|
||||||
|
->visible(fn (Allocation $allocation) => $allocation->is_locked),
|
||||||
DissociateAction::make()
|
DissociateAction::make()
|
||||||
->after(function (Allocation $allocation) {
|
->after(function (Allocation $allocation) {
|
||||||
$allocation->update(['notes' => null]);
|
$allocation->update([
|
||||||
$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
'notes' => null,
|
||||||
|
'is_locked' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$this->getOwnerRecord()->allocation_id) {
|
||||||
|
$this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
->headerActions([
|
->headerActions([
|
||||||
@ -116,13 +135,25 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->recordSelectOptionsQuery(fn ($query) => $query->whereBelongsTo($this->getOwnerRecord()->node)->whereNull('server_id'))
|
->recordSelectOptionsQuery(fn ($query) => $query->whereBelongsTo($this->getOwnerRecord()->node)->whereNull('server_id'))
|
||||||
->recordSelectSearchColumns(['ip', 'port'])
|
->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]])),
|
->after(function (array $data) {
|
||||||
|
Allocation::whereIn('id', array_values(array_unique($data['recordId'])))->update(['is_locked' => true]);
|
||||||
|
|
||||||
|
if (!$this->getOwnerRecord()->allocation_id) {
|
||||||
|
$this->getOwnerRecord()->update(['allocation_id' => $data['recordId'][0]]);
|
||||||
|
}
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
->groupedBulkActions([
|
->groupedBulkActions([
|
||||||
DissociateBulkAction::make()
|
DissociateBulkAction::make()
|
||||||
->after(function () {
|
->after(function () {
|
||||||
Allocation::whereNull('server_id')->update(['notes' => null]);
|
Allocation::whereNull('server_id')->update([
|
||||||
$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
'notes' => null,
|
||||||
|
'is_locked' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$this->getOwnerRecord()->allocation_id) {
|
||||||
|
$this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,10 +10,17 @@ class ServerResource extends Resource
|
|||||||
{
|
{
|
||||||
protected static ?string $model = Server::class;
|
protected static ?string $model = Server::class;
|
||||||
|
|
||||||
|
protected static string|\BackedEnum|null $navigationIcon = 'tabler-brand-docker';
|
||||||
|
|
||||||
protected static ?string $slug = '/';
|
protected static ?string $slug = '/';
|
||||||
|
|
||||||
protected static bool $shouldRegisterNavigation = false;
|
protected static bool $shouldRegisterNavigation = false;
|
||||||
|
|
||||||
|
public static function getNavigationBadge(): ?string
|
||||||
|
{
|
||||||
|
return (string) user()?->directAccessibleServers()->where('owner_id', user()?->id)->count();
|
||||||
|
}
|
||||||
|
|
||||||
public static function canAccess(): bool
|
public static function canAccess(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -25,4 +32,10 @@ class ServerResource extends Resource
|
|||||||
'index' => ListServers::route('/'),
|
'index' => ListServers::route('/'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function embedServerList(bool $condition = true): void
|
||||||
|
{
|
||||||
|
static::$slug = $condition ? null : '/';
|
||||||
|
static::$shouldRegisterNavigation = $condition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
106
app/Filament/Components/Actions/UpdateNodeAllocations.php
Normal file
106
app/Filament/Components/Actions/UpdateNodeAllocations.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Components\Actions;
|
||||||
|
|
||||||
|
use App\Models\Allocation;
|
||||||
|
use App\Models\Node;
|
||||||
|
use Exception;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
|
||||||
|
class UpdateNodeAllocations extends Action
|
||||||
|
{
|
||||||
|
public static function getDefaultName(): ?string
|
||||||
|
{
|
||||||
|
return 'bulk_update_ip';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->label(trans('admin/node.bulk_update_ip'));
|
||||||
|
|
||||||
|
$this->icon('tabler-replace');
|
||||||
|
|
||||||
|
$this->color('warning');
|
||||||
|
|
||||||
|
$this->requiresConfirmation();
|
||||||
|
|
||||||
|
$this->modalHeading(trans('admin/node.bulk_update_ip'));
|
||||||
|
|
||||||
|
$this->modalDescription(trans('admin/node.bulk_update_ip_description'));
|
||||||
|
|
||||||
|
$this->modalIconColor('warning');
|
||||||
|
|
||||||
|
$this->modalSubmitActionLabel(trans('admin/node.update_ip'));
|
||||||
|
|
||||||
|
$this->schema(function () {
|
||||||
|
/** @var Node $node */
|
||||||
|
$node = $this->record;
|
||||||
|
|
||||||
|
$currentIps = Allocation::where('node_id', $node->id)
|
||||||
|
->pluck('ip')
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
return [
|
||||||
|
Select::make('old_ip')
|
||||||
|
->label(trans('admin/node.old_ip'))
|
||||||
|
->options(array_combine($currentIps, $currentIps))
|
||||||
|
->selectablePlaceholder(false)
|
||||||
|
->required()
|
||||||
|
->live(),
|
||||||
|
Select::make('new_ip')
|
||||||
|
->label(trans('admin/node.new_ip'))
|
||||||
|
->options(fn () => array_combine($node->ipAddresses(), $node->ipAddresses()) ?: [])
|
||||||
|
->required()
|
||||||
|
->different('old_ip'),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->action(function (array $data) {
|
||||||
|
/** @var Node $node */
|
||||||
|
$node = $this->record;
|
||||||
|
$allocations = Allocation::where('node_id', $node->id)->where('ip', $data['old_ip'])->get();
|
||||||
|
|
||||||
|
if ($allocations->count() === 0) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.no_allocations_to_update'))
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$updated = 0;
|
||||||
|
$failed = 0;
|
||||||
|
|
||||||
|
foreach ($allocations as $allocation) {
|
||||||
|
try {
|
||||||
|
$allocation->update(['ip' => $data['new_ip']]);
|
||||||
|
$updated++;
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
$failed++;
|
||||||
|
report($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.ip_updated', ['count' => $updated, 'total' => $allocations->count()]))
|
||||||
|
->body($failed > 0 ? trans('admin/node.ip_update_failed', ['count' => $failed]) : null)
|
||||||
|
->status($failed > 0 ? 'warning' : 'success')
|
||||||
|
->persistent()
|
||||||
|
->send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nodeRecord(Node $node): static
|
||||||
|
{
|
||||||
|
$this->record = $node;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -73,15 +73,22 @@ class AllocationResource extends Resource
|
|||||||
->action(fn (Allocation $allocation) => user()?->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id]))
|
->action(fn (Allocation $allocation) => user()?->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id]))
|
||||||
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
|
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
|
||||||
->label(trans('server/network.primary')),
|
->label(trans('server/network.primary')),
|
||||||
|
IconColumn::make('is_locked')
|
||||||
|
->label(trans('server/network.locked'))
|
||||||
|
->tooltip(trans('server/network.locked_helper'))
|
||||||
|
->trueIcon('tabler-lock')
|
||||||
|
->falseIcon('tabler-lock-open'),
|
||||||
])
|
])
|
||||||
->recordActions([
|
->recordActions([
|
||||||
DetachAction::make()
|
DetachAction::make()
|
||||||
|
->visible(fn (Allocation $allocation) => !$allocation->is_locked || user()?->can('update', $allocation->node))
|
||||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_DELETE, $server))
|
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_DELETE, $server))
|
||||||
->label(trans('server/network.delete'))
|
->label(trans('server/network.delete'))
|
||||||
->icon('tabler-trash')
|
->icon('tabler-trash')
|
||||||
->action(function (Allocation $allocation) {
|
->action(function (Allocation $allocation) {
|
||||||
Allocation::query()->where('id', $allocation->id)->update([
|
Allocation::where('id', $allocation->id)->update([
|
||||||
'notes' => null,
|
'notes' => null,
|
||||||
|
'is_locked' => false,
|
||||||
'server_id' => null,
|
'server_id' => null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -93,7 +100,7 @@ class AllocationResource extends Resource
|
|||||||
->after(fn (Allocation $allocation) => $allocation->id === $server->allocation_id && $server->update(['allocation_id' => $server->allocations()->first()?->id])),
|
->after(fn (Allocation $allocation) => $allocation->id === $server->allocation_id && $server->update(['allocation_id' => $server->allocations()->first()?->id])),
|
||||||
])
|
])
|
||||||
->toolbarActions([
|
->toolbarActions([
|
||||||
Action::make('addAllocation')
|
Action::make('add_allocation')
|
||||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||||
->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network')
|
->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network')
|
||||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_CREATE, $server))
|
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_CREATE, $server))
|
||||||
|
|||||||
@ -7,11 +7,13 @@ use App\Exceptions\Repository\FileNotEditableException;
|
|||||||
use App\Facades\Activity;
|
use App\Facades\Activity;
|
||||||
use App\Filament\Server\Resources\Files\FileResource;
|
use App\Filament\Server\Resources\Files\FileResource;
|
||||||
use App\Livewire\AlertBanner;
|
use App\Livewire\AlertBanner;
|
||||||
|
use App\Models\File;
|
||||||
use App\Models\Permission;
|
use App\Models\Permission;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Repositories\Daemon\DaemonFileRepository;
|
use App\Repositories\Daemon\DaemonFileRepository;
|
||||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||||
|
use Closure;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Facades\Filament;
|
use Filament\Facades\Filament;
|
||||||
use Filament\Forms\Components\CodeEditor;
|
use Filament\Forms\Components\CodeEditor;
|
||||||
@ -215,13 +217,15 @@ class EditFiles extends Page
|
|||||||
|
|
||||||
$this->previousUrl = url()->previous();
|
$this->previousUrl = url()->previous();
|
||||||
|
|
||||||
if (str($path)->endsWith('.pelicanignore')) {
|
foreach (File::getSpecialFiles() as $fileName => $data) {
|
||||||
AlertBanner::make('.pelicanignore_info')
|
if ($data['check'] instanceof Closure && $data['check']($path)) {
|
||||||
->title(trans('server/file.alerts.pelicanignore.title'))
|
AlertBanner::make($fileName . '_info')
|
||||||
->body(trans('server/file.alerts.pelicanignore.body'))
|
->title($data['title'])
|
||||||
->info()
|
->body($data['body'])
|
||||||
->closable()
|
->info()
|
||||||
->send();
|
->closable()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
* @property string $address
|
* @property string $address
|
||||||
* @property Server|null $server
|
* @property Server|null $server
|
||||||
* @property Node $node
|
* @property Node $node
|
||||||
|
* @property bool $is_locked
|
||||||
*
|
*
|
||||||
* @method static AllocationFactory factory(...$parameters)
|
* @method static AllocationFactory factory(...$parameters)
|
||||||
* @method static Builder|Allocation newModelQuery()
|
* @method static Builder|Allocation newModelQuery()
|
||||||
@ -55,6 +56,10 @@ class Allocation extends Model
|
|||||||
*/
|
*/
|
||||||
public const RESOURCE_NAME = 'allocation';
|
public const RESOURCE_NAME = 'allocation';
|
||||||
|
|
||||||
|
protected $attributes = [
|
||||||
|
'is_locked' => false,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fields that are not mass assignable.
|
* Fields that are not mass assignable.
|
||||||
*/
|
*/
|
||||||
@ -68,10 +73,17 @@ class Allocation extends Model
|
|||||||
'ip_alias' => ['nullable', 'string'],
|
'ip_alias' => ['nullable', 'string'],
|
||||||
'server_id' => ['nullable', 'exists:servers,id'],
|
'server_id' => ['nullable', 'exists:servers,id'],
|
||||||
'notes' => ['nullable', 'string', 'max:256'],
|
'notes' => ['nullable', 'string', 'max:256'],
|
||||||
|
'is_locked' => ['boolean'],
|
||||||
];
|
];
|
||||||
|
|
||||||
protected static function booted(): void
|
protected static function booted(): void
|
||||||
{
|
{
|
||||||
|
static::updating(function (self $allocation) {
|
||||||
|
if (is_null($allocation->server_id)) {
|
||||||
|
$allocation->is_locked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
static::deleting(function (self $allocation) {
|
static::deleting(function (self $allocation) {
|
||||||
throw_if($allocation->server_id, new ServerUsingAllocationException(trans('exceptions.allocations.server_using')));
|
throw_if($allocation->server_id, new ServerUsingAllocationException(trans('exceptions.allocations.server_using')));
|
||||||
});
|
});
|
||||||
@ -83,6 +95,7 @@ class Allocation extends Model
|
|||||||
'node_id' => 'integer',
|
'node_id' => 'integer',
|
||||||
'port' => 'integer',
|
'port' => 'integer',
|
||||||
'server_id' => 'integer',
|
'server_id' => 'integer',
|
||||||
|
'is_locked' => 'bool',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use App\Livewire\AlertBanner;
|
use App\Livewire\AlertBanner;
|
||||||
use App\Repositories\Daemon\DaemonFileRepository;
|
use App\Repositories\Daemon\DaemonFileRepository;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Closure;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -54,6 +55,32 @@ class File extends Model
|
|||||||
|
|
||||||
protected static ?string $searchTerm;
|
protected static ?string $searchTerm;
|
||||||
|
|
||||||
|
/** @var array<string, array<string, string|Closure|null>> */
|
||||||
|
protected static array $customSpecialFiles = [];
|
||||||
|
|
||||||
|
public static function registerSpecialFile(string $fileName, string|Closure $bannerTitle, string|Closure|null $bannerBody = null, ?Closure $nameCheck = null): void
|
||||||
|
{
|
||||||
|
static::$customSpecialFiles[$fileName] = [
|
||||||
|
'title' => $bannerTitle,
|
||||||
|
'body' => $bannerBody,
|
||||||
|
'check' => $nameCheck ?? fn (string $path) => str($path)->endsWith($fileName),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<string, array<string, string|Closure|null>> */
|
||||||
|
public static function getSpecialFiles(): array
|
||||||
|
{
|
||||||
|
$specialFiles = [
|
||||||
|
'.pelicanignore' => [
|
||||||
|
'title' => fn () => trans('server/file.alerts.pelicanignore.title'),
|
||||||
|
'body' => fn () => trans('server/file.alerts.pelicanignore.body'),
|
||||||
|
'check' => fn (string $path) => str($path)->endsWith('.pelicanignore'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return array_merge($specialFiles, static::$customSpecialFiles);
|
||||||
|
}
|
||||||
|
|
||||||
public static function get(Server $server, string $path = '/', ?string $searchTerm = null): Builder
|
public static function get(Server $server, string $path = '/', ?string $searchTerm = null): Builder
|
||||||
{
|
{
|
||||||
self::$server = $server;
|
self::$server = $server;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Contracts\Validatable;
|
use App\Contracts\Validatable;
|
||||||
use App\Exceptions\Service\HasActiveServersException;
|
use App\Exceptions\Service\HasActiveServersException;
|
||||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||||
use App\Traits\HasValidation;
|
use App\Traits\HasValidation;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
@ -316,7 +316,7 @@ class Node extends Model implements Validatable
|
|||||||
{
|
{
|
||||||
return once(function () {
|
return once(function () {
|
||||||
try {
|
try {
|
||||||
return (new DaemonConfigurationRepository())
|
return (new DaemonSystemRepository())
|
||||||
->setNode($this)
|
->setNode($this)
|
||||||
->getSystemInformation();
|
->getSystemInformation();
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
|
|||||||
@ -287,7 +287,8 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
|||||||
->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id')
|
->leftJoin('subusers', 'subusers.server_id', '=', 'servers.id')
|
||||||
->where(function (Builder $builder) {
|
->where(function (Builder $builder) {
|
||||||
$builder->where('servers.owner_id', $this->id)->orWhere('subusers.user_id', $this->id);
|
$builder->where('servers.owner_id', $this->id)->orWhere('subusers.user_id', $this->id);
|
||||||
});
|
})
|
||||||
|
->distinct('servers.id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function accessibleNodes(): Builder
|
public function accessibleNodes(): Builder
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use App\Models\Node;
|
|||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Http\Client\Response;
|
use Illuminate\Http\Client\Response;
|
||||||
|
|
||||||
class DaemonConfigurationRepository extends DaemonRepository
|
class DaemonSystemRepository extends DaemonRepository
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns system information from the daemon instance.
|
* Returns system information from the daemon instance.
|
||||||
@ -30,6 +30,23 @@ class DaemonConfigurationRepository extends DaemonRepository
|
|||||||
})->json();
|
})->json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve diagnostics from the daemon for the current node.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @throws ConnectionException
|
||||||
|
*/
|
||||||
|
public function getDiagnostics(int $lines, bool $includeEndpoints, bool $includeLogs): Response
|
||||||
|
{
|
||||||
|
return $this->getHttpClient()
|
||||||
|
->timeout(5)
|
||||||
|
->get('/api/diagnostics', [
|
||||||
|
'log_lines' => $lines,
|
||||||
|
'include_endpoints' => $includeEndpoints ? 'true' : 'false',
|
||||||
|
'include_logs' => $includeLogs ? 'true' : 'false',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the configuration information for a daemon. Updates the information for
|
* Updates the configuration information for a daemon. Updates the information for
|
||||||
* this instance using a passed-in model. This allows us to change plenty of information
|
* this instance using a passed-in model. This allows us to change plenty of information
|
||||||
@ -4,7 +4,7 @@ namespace App\Services\Nodes;
|
|||||||
|
|
||||||
use App\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
use App\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -17,7 +17,7 @@ class NodeUpdateService
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ConnectionInterface $connection,
|
private ConnectionInterface $connection,
|
||||||
private DaemonConfigurationRepository $configurationRepository,
|
private DaemonSystemRepository $configurationRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -191,6 +191,7 @@ class ServerCreationService
|
|||||||
->get()
|
->get()
|
||||||
->each(function (Allocation $allocation) use ($server) {
|
->each(function (Allocation $allocation) use ($server) {
|
||||||
$allocation->server_id = $server->id;
|
$allocation->server_id = $server->id;
|
||||||
|
$allocation->is_locked = true;
|
||||||
$allocation->save();
|
$allocation->save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
"dedoc/scramble": "^0.12.10",
|
"dedoc/scramble": "^0.12.10",
|
||||||
"filament/filament": "~4.0",
|
"filament/filament": "~4.0",
|
||||||
"guzzlehttp/guzzle": "^7.10",
|
"guzzlehttp/guzzle": "^7.10",
|
||||||
"laravel/framework": "^12.31",
|
"laravel/framework": "^12.37",
|
||||||
"laravel/helpers": "^1.7",
|
"laravel/helpers": "^1.7",
|
||||||
"laravel/sanctum": "^4.2",
|
"laravel/sanctum": "^4.2",
|
||||||
"laravel/socialite": "^5.23",
|
"laravel/socialite": "^5.23",
|
||||||
|
|||||||
53
composer.lock
generated
53
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "101c2afb1f31acb872b4bed541397cd2",
|
"content-hash": "c8143eccd2736bd88b35d8fe6c8de289",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "achyutn/filament-log-viewer",
|
"name": "achyutn/filament-log-viewer",
|
||||||
@ -209,16 +209,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "aws/aws-sdk-php",
|
"name": "aws/aws-sdk-php",
|
||||||
"version": "3.359.3",
|
"version": "3.359.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||||
"reference": "a32e4c9522f0b61c947fafa1713d3a24b397a757"
|
"reference": "510cb4b7e2fa3ea09ad2154e7a13fe7675c36b30"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a32e4c9522f0b61c947fafa1713d3a24b397a757",
|
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/510cb4b7e2fa3ea09ad2154e7a13fe7675c36b30",
|
||||||
"reference": "a32e4c9522f0b61c947fafa1713d3a24b397a757",
|
"reference": "510cb4b7e2fa3ea09ad2154e7a13fe7675c36b30",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -300,9 +300,9 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.359.3"
|
"source": "https://github.com/aws/aws-sdk-php/tree/3.359.4"
|
||||||
},
|
},
|
||||||
"time": "2025-10-31T18:15:22+00:00"
|
"time": "2025-11-03T19:18:23+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "blade-ui-kit/blade-heroicons",
|
"name": "blade-ui-kit/blade-heroicons",
|
||||||
@ -2561,16 +2561,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/framework",
|
"name": "laravel/framework",
|
||||||
"version": "v12.36.1",
|
"version": "v12.37.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/laravel/framework.git",
|
"url": "https://github.com/laravel/framework.git",
|
||||||
"reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8"
|
"reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/laravel/framework/zipball/cad110d7685fbab990a6bb8184d0cfd847d7c4d8",
|
"url": "https://api.github.com/repos/laravel/framework/zipball/3c3c4ad30f5b528b164a7c09aa4ad03118c4c125",
|
||||||
"reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8",
|
"reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -2776,7 +2776,7 @@
|
|||||||
"issues": "https://github.com/laravel/framework/issues",
|
"issues": "https://github.com/laravel/framework/issues",
|
||||||
"source": "https://github.com/laravel/framework"
|
"source": "https://github.com/laravel/framework"
|
||||||
},
|
},
|
||||||
"time": "2025-10-29T14:20:57+00:00"
|
"time": "2025-11-04T15:39:33+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/helpers",
|
"name": "laravel/helpers",
|
||||||
@ -7757,16 +7757,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "spatie/laravel-permission",
|
"name": "spatie/laravel-permission",
|
||||||
"version": "6.22.0",
|
"version": "6.23.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/spatie/laravel-permission.git",
|
"url": "https://github.com/spatie/laravel-permission.git",
|
||||||
"reference": "8c87966ddc21893bfda54b792047473703992625"
|
"reference": "9e41247bd512b1e6c229afbc1eb528f7565ae3bb"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/spatie/laravel-permission/zipball/8c87966ddc21893bfda54b792047473703992625",
|
"url": "https://api.github.com/repos/spatie/laravel-permission/zipball/9e41247bd512b1e6c229afbc1eb528f7565ae3bb",
|
||||||
"reference": "8c87966ddc21893bfda54b792047473703992625",
|
"reference": "9e41247bd512b1e6c229afbc1eb528f7565ae3bb",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -7828,7 +7828,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/spatie/laravel-permission/issues",
|
"issues": "https://github.com/spatie/laravel-permission/issues",
|
||||||
"source": "https://github.com/spatie/laravel-permission/tree/6.22.0"
|
"source": "https://github.com/spatie/laravel-permission/tree/6.23.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -7836,7 +7836,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-10-27T21:58:45+00:00"
|
"time": "2025-11-03T20:16:13+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "spatie/laravel-query-builder",
|
"name": "spatie/laravel-query-builder",
|
||||||
@ -12203,16 +12203,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "larastan/larastan",
|
"name": "larastan/larastan",
|
||||||
"version": "v3.7.2",
|
"version": "v3.8.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/larastan/larastan.git",
|
"url": "https://github.com/larastan/larastan.git",
|
||||||
"reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae"
|
"reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/a761859a7487bd7d0cb8b662a7538a234d5bb5ae",
|
"url": "https://api.github.com/repos/larastan/larastan/zipball/d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e",
|
||||||
"reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae",
|
"reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -12226,7 +12226,7 @@
|
|||||||
"illuminate/pipeline": "^11.44.2 || ^12.4.1",
|
"illuminate/pipeline": "^11.44.2 || ^12.4.1",
|
||||||
"illuminate/support": "^11.44.2 || ^12.4.1",
|
"illuminate/support": "^11.44.2 || ^12.4.1",
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"phpstan/phpstan": "^2.1.28"
|
"phpstan/phpstan": "^2.1.29"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/coding-standard": "^13",
|
"doctrine/coding-standard": "^13",
|
||||||
@ -12239,7 +12239,8 @@
|
|||||||
"phpunit/phpunit": "^10.5.35 || ^11.5.15"
|
"phpunit/phpunit": "^10.5.35 || ^11.5.15"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench"
|
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
|
||||||
|
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
|
||||||
},
|
},
|
||||||
"type": "phpstan-extension",
|
"type": "phpstan-extension",
|
||||||
"extra": {
|
"extra": {
|
||||||
@ -12280,7 +12281,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/larastan/larastan/issues",
|
"issues": "https://github.com/larastan/larastan/issues",
|
||||||
"source": "https://github.com/larastan/larastan/tree/v3.7.2"
|
"source": "https://github.com/larastan/larastan/tree/v3.8.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -12288,7 +12289,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-09-19T09:03:05+00:00"
|
"time": "2025-10-27T23:09:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/pail",
|
"name": "laravel/pail",
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('allocations', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_locked')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('allocations', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_locked');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -10,6 +10,7 @@ return [
|
|||||||
'basic_settings' => 'Basic Settings',
|
'basic_settings' => 'Basic Settings',
|
||||||
'advanced_settings' => 'Advanced Settings',
|
'advanced_settings' => 'Advanced Settings',
|
||||||
'config_file' => 'Configuration File',
|
'config_file' => 'Configuration File',
|
||||||
|
'diagnostics' => 'Diagnostics',
|
||||||
],
|
],
|
||||||
'table' => [
|
'table' => [
|
||||||
'health' => 'Health',
|
'health' => 'Health',
|
||||||
@ -43,7 +44,7 @@ return [
|
|||||||
'error' => 'This is the domain name that points to your node\'s IP Address. If you\'ve already set up this, you can verify it by checking the next field!',
|
'error' => 'This is the domain name that points to your node\'s IP Address. If you\'ve already set up this, you can verify it by checking the next field!',
|
||||||
'fqdn_help' => 'Your panel is currently secured via an SSL certificate and that means your nodes require one too. You must use a domain name, because you cannot get SSL certificates for IP Addresses.',
|
'fqdn_help' => 'Your panel is currently secured via an SSL certificate and that means your nodes require one too. You must use a domain name, because you cannot get SSL certificates for IP Addresses.',
|
||||||
'dns' => 'DNS Record Check',
|
'dns' => 'DNS Record Check',
|
||||||
'dns_help' => 'This lets you know if you DNS record is pointing to the correct IP address.',
|
'dns_help' => 'This lets you know if your DNS record is pointing to the correct IP address.',
|
||||||
'valid' => 'Valid',
|
'valid' => 'Valid',
|
||||||
'invalid' => 'Invalid',
|
'invalid' => 'Invalid',
|
||||||
'port' => 'Port',
|
'port' => 'Port',
|
||||||
@ -117,8 +118,35 @@ return [
|
|||||||
'error_connecting_description' => 'The configuration could not be automatically updated on Wings, you will need to manually update the configuration file.',
|
'error_connecting_description' => 'The configuration could not be automatically updated on Wings, you will need to manually update the configuration file.',
|
||||||
'allocation' => 'Allocation',
|
'allocation' => 'Allocation',
|
||||||
|
|
||||||
|
'diagnostics' => [
|
||||||
|
'header' => 'Node Diagnostics',
|
||||||
|
'include_endpoints' => 'Include Endpoints',
|
||||||
|
'include_endpoints_hint' => 'Including endpoints will show panel urls within the logs and NOT obscure them.',
|
||||||
|
'include_logs' => 'Include Logs',
|
||||||
|
'include_logs_hint' => 'Including logs will show recent logs and help track down possible issues.',
|
||||||
|
'run_diagnostics' => 'Run Diagnostics',
|
||||||
|
'upload_to_pelican' => 'Upload Logs',
|
||||||
|
'logs_pulled' => 'Logs Pulled!',
|
||||||
|
'logs_uploaded' => 'Logs Uploaded',
|
||||||
|
'upload_failed' => 'Logs Upload Failed',
|
||||||
|
'view_logs' => 'View Logs',
|
||||||
|
'pull' => 'Pull',
|
||||||
|
'upload' => 'Upload',
|
||||||
|
'clear' => 'Clear',
|
||||||
|
'404' => 'The requested diagnostic report could not be found. Make sure wings is up to date and try again.',
|
||||||
|
],
|
||||||
|
|
||||||
'cloudflare_issue' => [
|
'cloudflare_issue' => [
|
||||||
'title' => 'Cloudflare Issue',
|
'title' => 'Cloudflare Issue',
|
||||||
'body' => 'Your Node is not accessible by Cloudflare',
|
'body' => 'Your Node is not accessible by Cloudflare',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'bulk_update_ip' => 'Update IPs',
|
||||||
|
'bulk_update_ip_description' => 'Replace an old IP address with a new one for allocations. This is useful when a node\'s IP address changes',
|
||||||
|
'update_ip' => 'Update IP',
|
||||||
|
'old_ip' => 'Old IP Address',
|
||||||
|
'new_ip' => 'New IP Address',
|
||||||
|
'no_allocations_to_update' => 'No allocations with the selected old IP address were found',
|
||||||
|
'ip_updated' => 'Successfully updated :count of :total allocation(s)',
|
||||||
|
'ip_update_failed' => ':count allocation(s) failed to update',
|
||||||
];
|
];
|
||||||
|
|||||||
@ -13,6 +13,10 @@ return [
|
|||||||
'ports' => 'Ports',
|
'ports' => 'Ports',
|
||||||
'alias' => 'Alias',
|
'alias' => 'Alias',
|
||||||
'alias_helper' => 'Optional display name to help you remember what these are.',
|
'alias_helper' => 'Optional display name to help you remember what these are.',
|
||||||
|
'locked' => 'Locked?',
|
||||||
|
'locked_helper' => 'Users won\'t be able to delete locked allocations',
|
||||||
|
'lock' => 'Lock',
|
||||||
|
'unlock' => 'Unlock',
|
||||||
'name' => 'Name',
|
'name' => 'Name',
|
||||||
'external_id' => 'External ID',
|
'external_id' => 'External ID',
|
||||||
'owner' => 'Owner',
|
'owner' => 'Owner',
|
||||||
|
|||||||
@ -12,4 +12,6 @@ return [
|
|||||||
'primary' => 'Primary',
|
'primary' => 'Primary',
|
||||||
'make' => 'Make',
|
'make' => 'Make',
|
||||||
'delete' => 'Delete',
|
'delete' => 'Delete',
|
||||||
|
'locked' => 'Locked?',
|
||||||
|
'locked_helper' => 'Locked allocations can only be deleted by admins',
|
||||||
];
|
];
|
||||||
|
|||||||
@ -22,7 +22,10 @@
|
|||||||
"></div>
|
"></div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="flex items-center mb-5 gap-2">
|
<div @class([
|
||||||
|
'flex items-center gap-2',
|
||||||
|
'mb-5' => !$server->description,
|
||||||
|
])>
|
||||||
<x-filament::icon-button
|
<x-filament::icon-button
|
||||||
:icon="$server->condition->getIcon()"
|
:icon="$server->condition->getIcon()"
|
||||||
:color="$server->condition->getColor()"
|
:color="$server->condition->getColor()"
|
||||||
@ -45,6 +48,12 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if ($server->description)
|
||||||
|
<div class="text-left mb-1 ml-4 pl-4">
|
||||||
|
<p class="text-base text-gray-400">{{ Str::limit($server->description, 40, preserveWords: true) }}</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="flex justify-between text-center items-center gap-4">
|
<div class="flex justify-between text-center items-center gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.cpu') }}</p>
|
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.cpu') }}</p>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user