mirror of
https://github.com/pelican-dev/panel.git
synced 2025-11-09 23:59:33 +01:00
Merge branch 'main' into boy132/plugin-system
# Conflicts: # app/Providers/Filament/AdminPanelProvider.php # app/Providers/Filament/AppPanelProvider.php
This commit is contained in:
commit
ce6245005a
130
app/Filament/Admin/Pages/ListLogs.php
Normal file
130
app/Filament/Admin/Pages/ListLogs.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Pages;
|
||||
|
||||
use Boquizo\FilamentLogViewer\Actions\DeleteAction;
|
||||
use Boquizo\FilamentLogViewer\Actions\DownloadAction;
|
||||
use Boquizo\FilamentLogViewer\Actions\ViewLogAction;
|
||||
use Boquizo\FilamentLogViewer\Pages\ListLogs as BaseListLogs;
|
||||
use Boquizo\FilamentLogViewer\Tables\Columns\LevelColumn;
|
||||
use Boquizo\FilamentLogViewer\Tables\Columns\NameColumn;
|
||||
use Boquizo\FilamentLogViewer\Utils\Level;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class ListLogs extends BaseListLogs
|
||||
{
|
||||
protected string $view = 'filament.components.list-logs';
|
||||
|
||||
public function getHeading(): string|null|\Illuminate\Contracts\Support\Htmlable
|
||||
{
|
||||
return trans('admin/log.navigation.panel_logs');
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return parent::table($table)
|
||||
->emptyStateHeading(trans('admin/log.empty_table'))
|
||||
->emptyStateIcon('tabler-check')
|
||||
->columns([
|
||||
NameColumn::make('date'),
|
||||
LevelColumn::make(Level::ALL)
|
||||
->tooltip(trans('admin/log.total_logs')),
|
||||
LevelColumn::make(Level::Error)
|
||||
->tooltip(trans('admin/log.error')),
|
||||
LevelColumn::make(Level::Warning)
|
||||
->tooltip(trans('admin/log.warning')),
|
||||
LevelColumn::make(Level::Notice)
|
||||
->tooltip(trans('admin/log.notice')),
|
||||
LevelColumn::make(Level::Info)
|
||||
->tooltip(trans('admin/log.info')),
|
||||
LevelColumn::make(Level::Debug)
|
||||
->tooltip(trans('admin/log.debug')),
|
||||
])
|
||||
->recordActions([
|
||||
ViewLogAction::make()
|
||||
->icon('tabler-file-description')->iconSize(IconSize::Medium),
|
||||
DownloadAction::make()
|
||||
->icon('tabler-file-download')->iconSize(IconSize::Medium),
|
||||
Action::make('uploadLogs')
|
||||
->button()
|
||||
->hiddenLabel()
|
||||
->icon('tabler-world-upload')->iconSize(IconSize::Medium)
|
||||
->requiresConfirmation()
|
||||
->modalHeading(trans('admin/log.actions.upload_logs'))
|
||||
->modalDescription(fn ($record) => trans('admin/log.actions.upload_logs_description', ['file' => $record['date'], 'url' => 'https://logs.pelican.dev']))
|
||||
->action(function ($record) {
|
||||
$logPath = storage_path('logs/' . $record['date']);
|
||||
|
||||
if (!file_exists($logPath)) {
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.log_not_found'))
|
||||
->body(trans('admin/log.actions.log_not_found_description', ['filename' => $record['date']]))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$lines = file($logPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
$totalLines = count($lines);
|
||||
$uploadLines = $totalLines <= 1000 ? $lines : array_slice($lines, -1000);
|
||||
$content = implode("\n", $uploadLines);
|
||||
|
||||
$logUrl = 'https://logs.pelican.dev';
|
||||
try {
|
||||
$response = Http::timeout(10)->asMultipart()->post($logUrl, [
|
||||
[
|
||||
'name' => 'c',
|
||||
'contents' => $content,
|
||||
],
|
||||
[
|
||||
'name' => 'e',
|
||||
'contents' => '14d',
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->failed()) {
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.failed_to_upload'))
|
||||
->body(trans('admin/log.actions.failed_to_upload_description', ['status' => $response->status()]))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $response->json();
|
||||
$url = $data['url'];
|
||||
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.log_upload'))
|
||||
->body("{$url}")
|
||||
->success()
|
||||
->actions([
|
||||
Action::make('viewLogs')
|
||||
->label(trans('admin/log.actions.view_logs'))
|
||||
->url($url)
|
||||
->openUrlInNewTab(true),
|
||||
])
|
||||
->persistent()
|
||||
->send();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.failed_to_upload'))
|
||||
->body($e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
}),
|
||||
DeleteAction::make()
|
||||
->icon('tabler-trash')->iconSize(IconSize::Medium),
|
||||
]);
|
||||
}
|
||||
}
|
||||
105
app/Filament/Admin/Pages/ViewLogs.php
Normal file
105
app/Filament/Admin/Pages/ViewLogs.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Pages;
|
||||
|
||||
use App\Traits\ResolvesRecordDate;
|
||||
use Boquizo\FilamentLogViewer\Actions\BackAction;
|
||||
use Boquizo\FilamentLogViewer\Actions\DeleteAction;
|
||||
use Boquizo\FilamentLogViewer\Actions\DownloadAction;
|
||||
use Boquizo\FilamentLogViewer\Pages\ViewLog as BaseViewLog;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class ViewLogs extends BaseViewLog
|
||||
{
|
||||
use ResolvesRecordDate;
|
||||
|
||||
public function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make(withTooltip: true)
|
||||
->icon('tabler-trash')->iconSize(IconSize::Medium),
|
||||
DownloadAction::make(withTooltip: true)
|
||||
->icon('tabler-file-download')->iconSize(IconSize::Medium),
|
||||
Action::make('uploadLogs')
|
||||
->button()
|
||||
->hiddenLabel()
|
||||
->icon('tabler-world-upload')->iconSize(IconSize::Medium)
|
||||
->requiresConfirmation()
|
||||
->tooltip(trans('admin/log.actions.upload_tooltip', ['url' => 'logs.pelican.dev']))
|
||||
->modalHeading(trans('admin/log.actions.upload_logs'))
|
||||
->modalDescription(fn () => trans('admin/log.actions.upload_logs_description', ['file' => $this->resolveRecordDate(), 'url' => 'https://logs.pelican.dev']))
|
||||
->action(function () {
|
||||
$logPath = storage_path('logs/' . $this->resolveRecordDate());
|
||||
|
||||
if (!file_exists($logPath)) {
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.log_not_found'))
|
||||
->body(trans('admin/log.actions.log_not_found_description', ['filename' => $this->resolveRecordDate()]))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$lines = file($logPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
$totalLines = count($lines);
|
||||
$uploadLines = $totalLines <= 1000 ? $lines : array_slice($lines, -1000);
|
||||
$content = implode("\n", $uploadLines);
|
||||
|
||||
$logUrl = 'https://logs.pelican.dev';
|
||||
try {
|
||||
$response = Http::timeout(10)->asMultipart()->post($logUrl, [
|
||||
[
|
||||
'name' => 'c',
|
||||
'contents' => $content,
|
||||
],
|
||||
[
|
||||
'name' => 'e',
|
||||
'contents' => '14d',
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->failed()) {
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.failed_to_upload'))
|
||||
->body(trans('admin/log.actions.failed_to_upload_description', ['status' => $response->status()]))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $response->json();
|
||||
$url = $data['url'];
|
||||
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.log_upload'))
|
||||
->body("{$url}")
|
||||
->success()
|
||||
->actions([
|
||||
Action::make('viewLogs')
|
||||
->label(trans('admin/log.actions.view_logs'))
|
||||
->url($url)
|
||||
->openUrlInNewTab(true),
|
||||
])
|
||||
->persistent()
|
||||
->send();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Notification::make()
|
||||
->title(trans('admin/log.actions.failed_to_upload'))
|
||||
->body($e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
}),
|
||||
BackAction::make()
|
||||
->icon('tabler-arrow-left')->iconSize(IconSize::Medium),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -126,7 +126,7 @@ class MountResource extends Resource
|
||||
ToggleButtons::make('read_only')
|
||||
->label(trans('admin/mount.read_only'))
|
||||
->helperText(trans('admin/mount.read_only_help'))
|
||||
->stateCast(new BooleanStateCast(false))
|
||||
->stateCast(new BooleanStateCast(false, true))
|
||||
->options([
|
||||
false => trans('admin/mount.toggles.writable'),
|
||||
true => trans('admin/mount.toggles.read_only'),
|
||||
@ -140,8 +140,7 @@ class MountResource extends Resource
|
||||
true => 'success',
|
||||
])
|
||||
->inline()
|
||||
->default(false)
|
||||
->required(),
|
||||
->default(false),
|
||||
TextInput::make('source')
|
||||
->label(trans('admin/mount.source'))
|
||||
->required()
|
||||
|
||||
@ -4,7 +4,7 @@ namespace App\Filament\Admin\Resources\Nodes\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\Nodes\NodeResource;
|
||||
use App\Models\Node;
|
||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
||||
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||
use App\Services\Helpers\SoftwareVersionService;
|
||||
use App\Services\Nodes\NodeAutoDeployService;
|
||||
use App\Services\Nodes\NodeUpdateService;
|
||||
@ -14,6 +14,8 @@ use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\DeleteAction;
|
||||
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\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
@ -25,6 +27,7 @@ use Filament\Resources\Pages\EditRecord;
|
||||
use Filament\Schemas\Components\Actions;
|
||||
use Filament\Schemas\Components\Fieldset;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
|
||||
use Filament\Schemas\Components\Tabs;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
@ -33,7 +36,10 @@ use Filament\Schemas\Components\Utilities\Set;
|
||||
use Filament\Schemas\Components\View;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Filament\Support\RawJs;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Phiki\Grammar\Grammar;
|
||||
use Throwable;
|
||||
@ -45,13 +51,13 @@ class EditNode extends EditRecord
|
||||
|
||||
protected static string $resource = NodeResource::class;
|
||||
|
||||
private DaemonConfigurationRepository $daemonConfigurationRepository;
|
||||
private DaemonSystemRepository $daemonSystemRepository;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -624,6 +630,154 @@ class EditNode extends EditRecord
|
||||
])->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 {
|
||||
if ($changed) {
|
||||
$this->daemonConfigurationRepository->setNode($node)->update($node);
|
||||
$this->daemonSystemRepository->setNode($node)->update($node);
|
||||
}
|
||||
parent::getSavedNotification()?->send();
|
||||
} catch (ConnectionException) {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Admin\Resources\Nodes\RelationManagers;
|
||||
|
||||
use App\Filament\Admin\Resources\Servers\Pages\CreateServer;
|
||||
use App\Filament\Components\Actions\UpdateNodeAllocations;
|
||||
use App\Models\Allocation;
|
||||
use App\Models\Node;
|
||||
use App\Services\Allocations\AssignmentService;
|
||||
@ -80,7 +81,9 @@ class AllocationsRelationManager extends RelationManager
|
||||
->searchable()
|
||||
->label(trans('admin/node.table.ip')),
|
||||
])
|
||||
->headerActions([
|
||||
->toolbarActions([
|
||||
DeleteBulkAction::make()
|
||||
->authorize(fn () => user()?->can('update', $this->getOwnerRecord())),
|
||||
Action::make('create new allocation')
|
||||
->label(trans('admin/node.create_allocation'))
|
||||
->schema(fn () => [
|
||||
@ -118,9 +121,8 @@ class AllocationsRelationManager extends RelationManager
|
||||
->required(),
|
||||
])
|
||||
->action(fn (array $data, AssignmentService $service) => $service->handle($this->getOwnerRecord(), $data)),
|
||||
])
|
||||
->groupedBulkActions([
|
||||
DeleteBulkAction::make()
|
||||
UpdateNodeAllocations::make()
|
||||
->nodeRecord($this->getOwnerRecord())
|
||||
->authorize(fn () => user()?->can('update', $this->getOwnerRecord())),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -116,6 +116,14 @@ class CreateServer extends CreateRecord
|
||||
->prefixIcon('tabler-server-2')
|
||||
->selectablePlaceholder(false)
|
||||
->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 */
|
||||
$latestNode = user()?->accessibleNodes()->latest()->first();
|
||||
$this->node = $latestNode;
|
||||
@ -829,6 +837,8 @@ class CreateServer extends CreateRecord
|
||||
$data['allocation_additional'] = collect($allocation_additional)->filter()->all();
|
||||
}
|
||||
|
||||
session()->put('last_utilized_node', $data['node_id']);
|
||||
|
||||
try {
|
||||
return $this->serverCreationService->handle($data);
|
||||
} catch (Exception $exception) {
|
||||
|
||||
@ -11,6 +11,7 @@ use Filament\Actions\AssociateAction;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Actions\DissociateAction;
|
||||
use Filament\Actions\DissociateBulkAction;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
@ -60,16 +61,35 @@ class AllocationsRelationManager extends RelationManager
|
||||
->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')),
|
||||
IconColumn::make('is_locked')
|
||||
->label(trans('admin/server.locked'))
|
||||
->tooltip(trans('admin/server.locked_helper'))
|
||||
->trueIcon('tabler-lock')
|
||||
->falseIcon('tabler-lock-open'),
|
||||
])
|
||||
->recordActions([
|
||||
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),
|
||||
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()
|
||||
->after(function (Allocation $allocation) {
|
||||
$allocation->update(['notes' => null]);
|
||||
$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||
$allocation->update([
|
||||
'notes' => null,
|
||||
'is_locked' => false,
|
||||
]);
|
||||
|
||||
if (!$this->getOwnerRecord()->allocation_id) {
|
||||
$this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||
}
|
||||
}),
|
||||
])
|
||||
->headerActions([
|
||||
@ -107,6 +127,8 @@ class AllocationsRelationManager extends RelationManager
|
||||
->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports', CreateServer::retrieveValidPorts($this->getOwnerRecord()->node, $state, $get('allocation_ip'))))
|
||||
->splitKeys(['Tab', ' ', ','])
|
||||
->required(),
|
||||
Hidden::make('is_locked')
|
||||
->default(true),
|
||||
])
|
||||
->action(fn (array $data, AssignmentService $service) => $service->handle($this->getOwnerRecord()->node, $data, $this->getOwnerRecord())),
|
||||
AssociateAction::make()
|
||||
@ -116,13 +138,25 @@ class AllocationsRelationManager extends RelationManager
|
||||
->recordSelectOptionsQuery(fn ($query) => $query->whereBelongsTo($this->getOwnerRecord()->node)->whereNull('server_id'))
|
||||
->recordSelectSearchColumns(['ip', 'port'])
|
||||
->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([
|
||||
DissociateBulkAction::make()
|
||||
->after(function () {
|
||||
Allocation::whereNull('server_id')->update(['notes' => null]);
|
||||
$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||
Allocation::whereNull('server_id')->update([
|
||||
'notes' => null,
|
||||
'is_locked' => false,
|
||||
]);
|
||||
|
||||
if (!$this->getOwnerRecord()->allocation_id) {
|
||||
$this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -81,6 +81,7 @@ class DatabasesRelationManager extends RelationManager
|
||||
ViewAction::make()
|
||||
->color('primary'),
|
||||
DeleteAction::make()
|
||||
->successNotificationTitle(null)
|
||||
->using(function (Database $database, DatabaseManagementService $service) {
|
||||
try {
|
||||
$service->delete($database);
|
||||
|
||||
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]))
|
||||
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
|
||||
->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([
|
||||
DetachAction::make()
|
||||
->visible(fn (Allocation $allocation) => !$allocation->is_locked || user()?->can('update', $allocation->node))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_DELETE, $server))
|
||||
->label(trans('server/network.delete'))
|
||||
->icon('tabler-trash')
|
||||
->action(function (Allocation $allocation) {
|
||||
Allocation::query()->where('id', $allocation->id)->update([
|
||||
Allocation::where('id', $allocation->id)->update([
|
||||
'notes' => null,
|
||||
'is_locked' => false,
|
||||
'server_id' => null,
|
||||
]);
|
||||
|
||||
@ -93,12 +100,12 @@ class AllocationResource extends Resource
|
||||
->after(fn (Allocation $allocation) => $allocation->id === $server->allocation_id && $server->update(['allocation_id' => $server->allocations()->first()?->id])),
|
||||
])
|
||||
->toolbarActions([
|
||||
Action::make('addAllocation')
|
||||
Action::make('add_allocation')
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_CREATE, $server))
|
||||
->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? trans('server/network.limit') : trans('server/network.add'))
|
||||
->hidden(fn () => !config('panel.client_features.allocations.enabled'))
|
||||
->hidden(fn () => !config('panel.client_features.allocations.enabled') || $server->allocation === null)
|
||||
->disabled(fn () => $server->allocations()->count() >= $server->allocation_limit)
|
||||
->color(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'danger' : 'primary')
|
||||
->action(function (FindAssignableAllocationService $service) use ($server) {
|
||||
|
||||
@ -230,6 +230,7 @@ class BackupResource extends Resource
|
||||
->disabled(fn (Backup $backup) => $backup->is_locked && $backup->status !== BackupStatus::Failed)
|
||||
->modalDescription(fn (Backup $backup) => trans('server/backup.actions.delete.description', ['backup' => $backup->name]))
|
||||
->modalSubmitActionLabel(trans('server/backup.actions.delete.title'))
|
||||
->successNotificationTitle(null)
|
||||
->action(function (Backup $backup, DeleteBackupService $deleteBackupService) {
|
||||
try {
|
||||
$deleteBackupService->handle($backup);
|
||||
@ -265,6 +266,7 @@ class BackupResource extends Resource
|
||||
->color(fn () => $server->backups()->count() >= $server->backup_limit ? 'danger' : 'primary')
|
||||
->createAnother(false)
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->successNotificationTitle(null)
|
||||
->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) {
|
||||
$action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? ''));
|
||||
|
||||
|
||||
@ -138,6 +138,7 @@ class DatabaseResource extends Resource
|
||||
ViewAction::make()
|
||||
->modalHeading(fn (Database $database) => trans('server/database.viewing', ['database' => $database->database])),
|
||||
DeleteAction::make()
|
||||
->successNotificationTitle(null)
|
||||
->using(function (Database $database, DatabaseManagementService $service) {
|
||||
try {
|
||||
$service->delete($database);
|
||||
@ -164,6 +165,7 @@ class DatabaseResource extends Resource
|
||||
->disabled(fn () => $server->databases()->count() >= $server->database_limit)
|
||||
->color(fn () => $server->databases()->count() >= $server->database_limit ? 'danger' : 'primary')
|
||||
->createAnother(false)
|
||||
->successNotificationTitle(null)
|
||||
->schema([
|
||||
Grid::make()
|
||||
->columns(2)
|
||||
|
||||
@ -12,8 +12,10 @@ use App\Models\File;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonFileRepository;
|
||||
use App\Services\Nodes\NodeJWTService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
@ -25,7 +27,6 @@ use Filament\Actions\EditAction;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
@ -34,8 +35,6 @@ use Filament\Panel;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Tabs;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
@ -43,7 +42,7 @@ use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Enums\PaginationMode;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Route as RouteFacade;
|
||||
@ -56,6 +55,8 @@ class ListFiles extends ListRecords
|
||||
|
||||
protected static string $resource = FileResource::class;
|
||||
|
||||
protected string $view = 'filament.server.pages.list-files';
|
||||
|
||||
#[Locked]
|
||||
public string $path = '/';
|
||||
|
||||
@ -504,7 +505,7 @@ class ListFiles extends ListRecords
|
||||
->color('primary')
|
||||
->action(function ($data) {
|
||||
try {
|
||||
$this->getDaemonFileRepository()->createDirectory($data['name'], $this->path);
|
||||
$this->createFolder($data['name']);
|
||||
|
||||
Activity::event('server:file.create-directory')
|
||||
->property(['directory' => $this->path, 'name' => $data['name']])
|
||||
@ -528,59 +529,31 @@ class ListFiles extends ListRecords
|
||||
->label(trans('server/file.actions.new_folder.folder_name'))
|
||||
->required(),
|
||||
]),
|
||||
Action::make('upload')
|
||||
Action::make('uploadFile')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->hiddenLabel()->icon('tabler-upload')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.upload.title'))
|
||||
->view('filament.server.pages.file-upload'),
|
||||
Action::make('uploadURL')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->hiddenLabel()->icon('tabler-download')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.upload.from_url'))
|
||||
->modalHeading(trans('server/file.actions.upload.from_url'))
|
||||
->color('success')
|
||||
->action(function ($data) {
|
||||
if (count($data['files']) > 0 && !isset($data['url'])) {
|
||||
/** @var UploadedFile $file */
|
||||
foreach ($data['files'] as $file) {
|
||||
$this->getDaemonFileRepository()->putContent(join_paths($this->path, $file->getClientOriginalName()), $file->getContent());
|
||||
|
||||
Activity::event('server:file.uploaded')
|
||||
->property('directory', $this->path)
|
||||
->property('file', $file->getClientOriginalName())
|
||||
->log();
|
||||
}
|
||||
} elseif ($data['url'] !== null) {
|
||||
$this->getDaemonFileRepository()->pull($data['url'], $this->path);
|
||||
|
||||
Activity::event('server:file.pull')
|
||||
->property('url', $data['url'])
|
||||
->property('directory', $this->path)
|
||||
->log();
|
||||
}
|
||||
|
||||
$this->refreshPage();
|
||||
})
|
||||
->schema([
|
||||
Tabs::make()
|
||||
->contained(false)
|
||||
->schema([
|
||||
Tab::make('files')
|
||||
->label(trans('server/file.actions.upload.from_files'))
|
||||
->live()
|
||||
->schema([
|
||||
FileUpload::make('files')
|
||||
->storeFiles(false)
|
||||
->previewable(false)
|
||||
->preserveFilenames()
|
||||
->maxSize((int) round($server->node->upload_size * (config('panel.use_binary_prefix') ? 1.048576 * 1024 : 1000)))
|
||||
->multiple(),
|
||||
]),
|
||||
Tab::make('url')
|
||||
->label(trans('server/file.actions.upload.url'))
|
||||
->live()
|
||||
->disabled(fn (Get $get) => count($get('files')) > 0)
|
||||
->schema([
|
||||
TextInput::make('url')
|
||||
->label(trans('server/file.actions.upload.url'))
|
||||
->required()
|
||||
->url(),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
Action::make('search')
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ, $server))
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
@ -627,6 +600,81 @@ class ListFiles extends ListRecords
|
||||
};
|
||||
}
|
||||
|
||||
public function getUploadUrl(NodeJWTService $jwtService): string
|
||||
{
|
||||
/** @var Server $server */
|
||||
$server = Filament::getTenant();
|
||||
|
||||
if (!user()?->can(Permission::ACTION_FILE_CREATE, $server)) {
|
||||
abort(403, 'You do not have permission to upload files.');
|
||||
}
|
||||
|
||||
$token = $jwtService
|
||||
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
||||
->setUser(user())
|
||||
->setClaims(['server_uuid' => $server->uuid])
|
||||
->handle($server->node, user()->id . $server->uuid);
|
||||
|
||||
return sprintf(
|
||||
'%s/upload/file?token=%s',
|
||||
$server->node->getConnectionAddress(),
|
||||
$token->toString()
|
||||
);
|
||||
}
|
||||
|
||||
public function getUploadSizeLimit(): int
|
||||
{
|
||||
/** @var Server $server */
|
||||
$server = Filament::getTenant();
|
||||
|
||||
return $server->node->upload_size * 1024 * 1024;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ConnectionException
|
||||
* @throws FileExistsException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function createFolder(string $folderPath): void
|
||||
{
|
||||
/** @var Server $server */
|
||||
$server = Filament::getTenant();
|
||||
|
||||
if (!user()?->can(Permission::ACTION_FILE_CREATE, $server)) {
|
||||
abort(403, 'You do not have permission to create folders.');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->getDaemonFileRepository()->createDirectory($folderPath, $this->path);
|
||||
|
||||
Activity::event('server:file.create-directory')
|
||||
->property(['directory' => $this->path, 'name' => $folderPath])
|
||||
->log();
|
||||
|
||||
} catch (FileExistsException) {
|
||||
// Ignore if the folder already exists.
|
||||
} catch (ConnectionException $e) {
|
||||
Notification::make()
|
||||
->body($e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $files
|
||||
*/
|
||||
public function logUploadedFiles(array $files): void
|
||||
{
|
||||
$filesCollection = collect($files);
|
||||
|
||||
Activity::event('server:files.uploaded')
|
||||
->property('directory', $this->path)
|
||||
->property('files', $filesCollection)
|
||||
->log();
|
||||
}
|
||||
|
||||
private function getDaemonFileRepository(): DaemonFileRepository
|
||||
{
|
||||
/** @var Server $server */
|
||||
|
||||
@ -140,6 +140,7 @@ class UserResource extends Resource
|
||||
DeleteAction::make()
|
||||
->label(trans('server/user.delete'))
|
||||
->hidden(fn (User $user) => user()?->id === $user->id)
|
||||
->successNotificationTitle(null)
|
||||
->action(function (User $user, SubuserDeletionService $subuserDeletionService) use ($server) {
|
||||
$subuser = $server->subusers->where('user_id', $user->id)->first();
|
||||
$subuserDeletionService->handle($subuser, $server);
|
||||
@ -154,6 +155,7 @@ class UserResource extends Resource
|
||||
->hidden(fn (User $user) => user()?->id === $user->id)
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_USER_UPDATE, $server))
|
||||
->modalHeading(fn (User $user) => trans('server/user.editing', ['user' => $user->email]))
|
||||
->successNotificationTitle(null)
|
||||
->action(function (array $data, SubuserUpdateService $subuserUpdateService, User $user) use ($server) {
|
||||
$subuser = $server->subusers->where('user_id', $user->id)->first();
|
||||
|
||||
|
||||
@ -34,7 +34,9 @@ class ProcessWebhook implements ShouldQueue
|
||||
$data = reset($data);
|
||||
}
|
||||
|
||||
if (is_string($data)) {
|
||||
$data = Arr::wrap(json_decode($data, true) ?? []);
|
||||
}
|
||||
$data['event'] = $this->webhookConfiguration->transformClassName($this->eventName);
|
||||
|
||||
if ($this->webhookConfiguration->type === WebhookType::Discord) {
|
||||
@ -55,12 +57,13 @@ class ProcessWebhook implements ShouldQueue
|
||||
}
|
||||
|
||||
try {
|
||||
$customHeaders = $this->webhookConfiguration->headers;
|
||||
$headers = [];
|
||||
foreach ($customHeaders as $key => $value) {
|
||||
|
||||
if ($this->webhookConfiguration->type === WebhookType::Regular) {
|
||||
foreach ($this->webhookConfiguration->headers as $key => $value) {
|
||||
$headers[$key] = $this->webhookConfiguration->replaceVars($data, $value);
|
||||
}
|
||||
|
||||
}
|
||||
Http::withHeaders($headers)->post($this->webhookConfiguration->endpoint, $data)->throw();
|
||||
$successful = now();
|
||||
} catch (Exception $exception) {
|
||||
|
||||
@ -29,6 +29,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
* @property string $address
|
||||
* @property Server|null $server
|
||||
* @property Node $node
|
||||
* @property bool $is_locked
|
||||
*
|
||||
* @method static AllocationFactory factory(...$parameters)
|
||||
* @method static Builder|Allocation newModelQuery()
|
||||
@ -55,6 +56,10 @@ class Allocation extends Model
|
||||
*/
|
||||
public const RESOURCE_NAME = 'allocation';
|
||||
|
||||
protected $attributes = [
|
||||
'is_locked' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* Fields that are not mass assignable.
|
||||
*/
|
||||
@ -68,10 +73,17 @@ class Allocation extends Model
|
||||
'ip_alias' => ['nullable', 'string'],
|
||||
'server_id' => ['nullable', 'exists:servers,id'],
|
||||
'notes' => ['nullable', 'string', 'max:256'],
|
||||
'is_locked' => ['boolean'],
|
||||
];
|
||||
|
||||
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) {
|
||||
throw_if($allocation->server_id, new ServerUsingAllocationException(trans('exceptions.allocations.server_using')));
|
||||
});
|
||||
@ -83,6 +95,7 @@ class Allocation extends Model
|
||||
'node_id' => 'integer',
|
||||
'port' => 'integer',
|
||||
'server_id' => 'integer',
|
||||
'is_locked' => 'bool',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Contracts\Validatable;
|
||||
use App\Exceptions\Service\HasActiveServersException;
|
||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
||||
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||
use App\Traits\HasValidation;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
@ -316,7 +316,7 @@ class Node extends Model implements Validatable
|
||||
{
|
||||
return once(function () {
|
||||
try {
|
||||
return (new DaemonConfigurationRepository())
|
||||
return (new DaemonSystemRepository())
|
||||
->setNode($this)
|
||||
->getSystemInformation();
|
||||
} catch (Exception $exception) {
|
||||
|
||||
@ -171,10 +171,14 @@ class WebhookConfiguration extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed, mixed> $replacement
|
||||
* @param array<mixed, mixed>|object $replacement
|
||||
* */
|
||||
public function replaceVars(array $replacement, string $subject): string
|
||||
public function replaceVars(array|object $replacement, string $subject): string
|
||||
{
|
||||
if (is_object($replacement)) {
|
||||
$replacement = $replacement->toArray();
|
||||
}
|
||||
|
||||
return preg_replace_callback(
|
||||
'/{{(.*?)}}/',
|
||||
function ($matches) use ($replacement) {
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use AchyutN\FilamentLogViewer\FilamentLogViewer;
|
||||
use App\Filament\Admin\Pages\ListLogs;
|
||||
use App\Filament\Admin\Pages\ViewLogs;
|
||||
use Boquizo\FilamentLogViewer\FilamentLogViewerPlugin;
|
||||
use App\Facades\Plugins;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Facades\Filament;
|
||||
@ -36,8 +38,11 @@ class AdminPanelProvider extends PanelProvider
|
||||
->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
|
||||
->discoverWidgets(in: app_path('Filament/Admin/Widgets'), for: 'App\\Filament\\Admin\\Widgets')
|
||||
->plugins([
|
||||
FilamentLogViewer::make()
|
||||
FilamentLogViewerPlugin::make()
|
||||
->authorize(fn () => user()->can('view panelLog'))
|
||||
->listLogs(ListLogs::class)
|
||||
->viewLog(ViewLogs::class)
|
||||
->navigationLabel(fn () => trans('admin/log.navigation.panel_logs'))
|
||||
->navigationGroup(fn () => trans('admin/dashboard.advanced'))
|
||||
->navigationIcon('tabler-file-info'),
|
||||
]);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use AchyutN\FilamentLogViewer\FilamentLogViewer;
|
||||
use Boquizo\FilamentLogViewer\FilamentLogViewerPlugin;
|
||||
use App\Facades\Plugins;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Facades\Filament;
|
||||
@ -27,7 +27,7 @@ class AppPanelProvider extends PanelProvider
|
||||
])
|
||||
->discoverResources(in: app_path('Filament/App/Resources'), for: 'App\\Filament\\App\\Resources')
|
||||
->plugins([
|
||||
FilamentLogViewer::make()
|
||||
FilamentLogViewerPlugin::make()
|
||||
->authorize(false),
|
||||
]);
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ use App\Models\Node;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Http\Client\Response;
|
||||
|
||||
class DaemonConfigurationRepository extends DaemonRepository
|
||||
class DaemonSystemRepository extends DaemonRepository
|
||||
{
|
||||
/**
|
||||
* Returns system information from the daemon instance.
|
||||
@ -30,6 +30,23 @@ class DaemonConfigurationRepository extends DaemonRepository
|
||||
})->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
|
||||
* this instance using a passed-in model. This allows us to change plenty of information
|
||||
@ -85,6 +85,7 @@ class AssignmentService
|
||||
'port' => (int) $unit,
|
||||
'ip_alias' => array_get($data, 'allocation_alias'),
|
||||
'server_id' => $server->id ?? null,
|
||||
'is_locked' => array_get($data, 'is_locked', false),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
@ -98,6 +99,7 @@ class AssignmentService
|
||||
'port' => (int) $port,
|
||||
'ip_alias' => array_get($data, 'allocation_alias'),
|
||||
'server_id' => $server->id ?? null,
|
||||
'is_locked' => array_get($data, 'is_locked', false),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ namespace App\Services\Nodes;
|
||||
|
||||
use App\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
||||
use App\Models\Node;
|
||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
||||
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Str;
|
||||
@ -17,7 +17,7 @@ class NodeUpdateService
|
||||
*/
|
||||
public function __construct(
|
||||
private ConnectionInterface $connection,
|
||||
private DaemonConfigurationRepository $configurationRepository,
|
||||
private DaemonSystemRepository $configurationRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
||||
@ -191,6 +191,7 @@ class ServerCreationService
|
||||
->get()
|
||||
->each(function (Allocation $allocation) use ($server) {
|
||||
$allocation->server_id = $server->id;
|
||||
$allocation->is_locked = true;
|
||||
$allocation->save();
|
||||
});
|
||||
}
|
||||
|
||||
45
app/Traits/ResolvesRecordDate.php
Normal file
45
app/Traits/ResolvesRecordDate.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
trait ResolvesRecordDate
|
||||
{
|
||||
/**
|
||||
* @param mixed|null $record
|
||||
*/
|
||||
protected function resolveRecordDate($record = null): ?string
|
||||
{
|
||||
$r = $record ?? ($this->record ?? null);
|
||||
|
||||
if (is_scalar($r)) {
|
||||
return (string) $r;
|
||||
}
|
||||
|
||||
if (is_array($r)) {
|
||||
return Arr::get($r, 'date') !== null ? (string) Arr::get($r, 'date') : null;
|
||||
}
|
||||
|
||||
if (is_object($r)) {
|
||||
if (method_exists($r, 'getAttribute')) {
|
||||
$val = $r->getAttribute('date');
|
||||
if ($val !== null) {
|
||||
return (string) $val;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($r->date) || property_exists($r, 'date')) {
|
||||
return (string) $r->date;
|
||||
}
|
||||
|
||||
if (method_exists($r, 'toArray')) {
|
||||
$arr = $r->toArray();
|
||||
|
||||
return Arr::get($arr, 'date') !== null ? (string) Arr::get($arr, 'date') : null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -9,13 +9,13 @@
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-zip": "*",
|
||||
"achyutn/filament-log-viewer": "^1.4",
|
||||
"aws/aws-sdk-php": "^3.356",
|
||||
"calebporzio/sushi": "^2.5",
|
||||
"dedoc/scramble": "^0.12.10",
|
||||
"filament/filament": "~4.0",
|
||||
"gboquizosanchez/filament-log-viewer": "^2.1",
|
||||
"guzzlehttp/guzzle": "^7.10",
|
||||
"laravel/framework": "^12.31",
|
||||
"laravel/framework": "^12.37",
|
||||
"laravel/helpers": "^1.7",
|
||||
"laravel/sanctum": "^4.2",
|
||||
"laravel/socialite": "^5.23",
|
||||
|
||||
402
composer.lock
generated
402
composer.lock
generated
@ -4,89 +4,8 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "101c2afb1f31acb872b4bed541397cd2",
|
||||
"content-hash": "fc2037e2f16ad43582ceaf9d41aba799",
|
||||
"packages": [
|
||||
{
|
||||
"name": "achyutn/filament-log-viewer",
|
||||
"version": "v1.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/achyutkneupane/filament-log-viewer.git",
|
||||
"reference": "e285e5cb359d92d17c64e981de13d0e7741f4121"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/achyutkneupane/filament-log-viewer/zipball/e285e5cb359d92d17c64e981de13d0e7741f4121",
|
||||
"reference": "e285e5cb359d92d17c64e981de13d0e7741f4121",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"filament/filament": "^4.0",
|
||||
"phiki/phiki": "^2.0",
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"larastan/larastan": "^3.0",
|
||||
"laravel/pint": "^1.23",
|
||||
"orchestra/testbench": "^10.4",
|
||||
"pestphp/pest": "^3.8",
|
||||
"pestphp/pest-plugin-laravel": "^3.2",
|
||||
"pestphp/pest-plugin-livewire": "^3.0",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"rector/rector": "^2.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"AchyutN\\FilamentLogViewer\\LogViewerProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"AchyutN\\FilamentLogViewer\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Achyut Neupane",
|
||||
"email": "achyutkneupane@gmail.com",
|
||||
"homepage": "https://achyut.com.np",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "A Filament package to view and manage Laravel logs.",
|
||||
"keywords": [
|
||||
"Viewer",
|
||||
"filament",
|
||||
"laravel",
|
||||
"log"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/achyutkneupane/filament-log-viewer/issues",
|
||||
"source": "https://github.com/achyutkneupane/filament-log-viewer/tree/v1.5.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.buymeacoffee.com/achyutn",
|
||||
"type": "buy_me_a_coffee"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/achyutkneupane",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/Achyut",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-10T18:58:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
"version": "1.3.4",
|
||||
@ -209,16 +128,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.359.3",
|
||||
"version": "3.359.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "a32e4c9522f0b61c947fafa1713d3a24b397a757"
|
||||
"reference": "a5be7ed5efd25d70a74275daeff896b896d9c286"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a32e4c9522f0b61c947fafa1713d3a24b397a757",
|
||||
"reference": "a32e4c9522f0b61c947fafa1713d3a24b397a757",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a5be7ed5efd25d70a74275daeff896b896d9c286",
|
||||
"reference": "a5be7ed5efd25d70a74275daeff896b896d9c286",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -300,9 +219,9 @@
|
||||
"support": {
|
||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.359.3"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.359.8"
|
||||
},
|
||||
"time": "2025-10-31T18:15:22+00:00"
|
||||
"time": "2025-11-07T19:48:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "blade-ui-kit/blade-heroicons",
|
||||
@ -2023,6 +1942,70 @@
|
||||
],
|
||||
"time": "2023-10-12T05:21:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "gboquizosanchez/filament-log-viewer",
|
||||
"version": "2.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/gboquizosanchez/filament-log-viewer.git",
|
||||
"reference": "85480879ed0f4da15257393f6c2e0c0ea0892403"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/gboquizosanchez/filament-log-viewer/zipball/85480879ed0f4da15257393f6c2e0c0ea0892403",
|
||||
"reference": "85480879ed0f4da15257393f6c2e0c0ea0892403",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-zip": "*",
|
||||
"owenvoke/blade-fontawesome": "^2.9",
|
||||
"php": "^8.2|^8.3|^8.4",
|
||||
"symfony/polyfill-php83": "^1.33"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.64",
|
||||
"hermes/dependencies": "^1.1",
|
||||
"larastan/larastan": "^2.9",
|
||||
"orchestra/testbench": "^9.1",
|
||||
"pestphp/pest": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Boquizo\\FilamentLogViewer\\FilamentLogViewerServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Boquizo\\FilamentLogViewer\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Germán Boquizo Sánchez",
|
||||
"email": "germanboquizosanchez@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Filament Log Viewer",
|
||||
"homepage": "https://github.com/gboquizosanchez",
|
||||
"keywords": [
|
||||
"filament",
|
||||
"laravel",
|
||||
"log-viewer"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/gboquizosanchez/filament-log-viewer/issues",
|
||||
"source": "https://github.com/gboquizosanchez/filament-log-viewer/tree/2.1.8"
|
||||
},
|
||||
"time": "2025-11-07T21:40:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "graham-campbell/result-type",
|
||||
"version": "v1.1.3",
|
||||
@ -2561,16 +2544,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.36.1",
|
||||
"version": "v12.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8"
|
||||
"reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/cad110d7685fbab990a6bb8184d0cfd847d7c4d8",
|
||||
"reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/3c3c4ad30f5b528b164a7c09aa4ad03118c4c125",
|
||||
"reference": "3c3c4ad30f5b528b164a7c09aa4ad03118c4c125",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2776,7 +2759,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"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",
|
||||
@ -5207,6 +5190,66 @@
|
||||
],
|
||||
"time": "2025-01-30T13:51:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "owenvoke/blade-fontawesome",
|
||||
"version": "v2.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/owenvoke/blade-fontawesome.git",
|
||||
"reference": "94dcd0c78f43f8234b0d9c76c903ecd288b8b0d1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/owenvoke/blade-fontawesome/zipball/94dcd0c78f43f8234b0d9c76c903ecd288b8b0d1",
|
||||
"reference": "94dcd0c78f43f8234b0d9c76c903ecd288b8b0d1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"blade-ui-kit/blade-icons": "^1.5",
|
||||
"illuminate/support": "^10.34|^11.0|^12.0",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.13",
|
||||
"orchestra/testbench": "^8.12|^9.0|^10.0",
|
||||
"pestphp/pest": "^2.26|^3.7",
|
||||
"phpstan/phpstan": "^1.10|^2.1",
|
||||
"symfony/var-dumper": "^6.3|^7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"OwenVoke\\BladeFontAwesome\\BladeFontAwesomeServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OwenVoke\\BladeFontAwesome\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A package to easily make use of Font Awesome in your Laravel Blade views",
|
||||
"support": {
|
||||
"issues": "https://github.com/owenvoke/blade-fontawesome/issues",
|
||||
"source": "https://github.com/owenvoke/blade-fontawesome/tree/v2.9.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://ecologi.com/owenvoke?gift-trees",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/owenvoke",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-28T16:03:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
"version": "v3.1.3",
|
||||
@ -5328,16 +5371,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phiki/phiki",
|
||||
"version": "v2.0.4",
|
||||
"version": "v2.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phikiphp/phiki.git",
|
||||
"reference": "160785c50c01077780ab217e5808f00ab8f05a13"
|
||||
"reference": "36d03e4c103b825f2657db966730d43e2035ff00"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phikiphp/phiki/zipball/160785c50c01077780ab217e5808f00ab8f05a13",
|
||||
"reference": "160785c50c01077780ab217e5808f00ab8f05a13",
|
||||
"url": "https://api.github.com/repos/phikiphp/phiki/zipball/36d03e4c103b825f2657db966730d43e2035ff00",
|
||||
"reference": "36d03e4c103b825f2657db966730d43e2035ff00",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -5383,7 +5426,7 @@
|
||||
"description": "Syntax highlighting using TextMate grammars in PHP.",
|
||||
"support": {
|
||||
"issues": "https://github.com/phikiphp/phiki/issues",
|
||||
"source": "https://github.com/phikiphp/phiki/tree/v2.0.4"
|
||||
"source": "https://github.com/phikiphp/phiki/tree/v2.0.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -5395,7 +5438,7 @@
|
||||
"type": "other"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-20T17:21:02+00:00"
|
||||
"time": "2025-11-04T20:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection",
|
||||
@ -7757,16 +7800,16 @@
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-permission",
|
||||
"version": "6.22.0",
|
||||
"version": "6.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-permission.git",
|
||||
"reference": "8c87966ddc21893bfda54b792047473703992625"
|
||||
"reference": "9e41247bd512b1e6c229afbc1eb528f7565ae3bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-permission/zipball/8c87966ddc21893bfda54b792047473703992625",
|
||||
"reference": "8c87966ddc21893bfda54b792047473703992625",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-permission/zipball/9e41247bd512b1e6c229afbc1eb528f7565ae3bb",
|
||||
"reference": "9e41247bd512b1e6c229afbc1eb528f7565ae3bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -7828,7 +7871,7 @@
|
||||
],
|
||||
"support": {
|
||||
"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": [
|
||||
{
|
||||
@ -7836,7 +7879,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-27T21:58:45+00:00"
|
||||
"time": "2025-11-03T20:16:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-query-builder",
|
||||
@ -8258,16 +8301,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.3.5",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7"
|
||||
"reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7",
|
||||
"reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a",
|
||||
"reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8332,7 +8375,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.5"
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -8352,20 +8395,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-14T15:46:26+00:00"
|
||||
"time": "2025-11-04T01:21:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2"
|
||||
"reference": "84321188c4754e64273b46b406081ad9b18e8614"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2",
|
||||
"reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/84321188c4754e64273b46b406081ad9b18e8614",
|
||||
"reference": "84321188c4754e64273b46b406081ad9b18e8614",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8401,7 +8444,7 @@
|
||||
"description": "Converts CSS selectors to XPath expressions",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/css-selector/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/css-selector/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -8412,12 +8455,16 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:21:43+00:00"
|
||||
"time": "2025-10-29T17:24:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@ -8488,16 +8535,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/error-handler",
|
||||
"version": "v7.3.4",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/error-handler.git",
|
||||
"reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4"
|
||||
"reference": "bbe40bfab84323d99dab491b716ff142410a92a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/99f81bc944ab8e5dae4f21b4ca9972698bbad0e4",
|
||||
"reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/bbe40bfab84323d99dab491b716ff142410a92a8",
|
||||
"reference": "bbe40bfab84323d99dab491b716ff142410a92a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8545,7 +8592,7 @@
|
||||
"description": "Provides tools to manage errors and ease debugging PHP code",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/error-handler/tree/v7.3.4"
|
||||
"source": "https://github.com/symfony/error-handler/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -8565,7 +8612,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T10:12:26+00:00"
|
||||
"time": "2025-10-31T19:12:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
@ -8797,16 +8844,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/html-sanitizer",
|
||||
"version": "v7.3.3",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/html-sanitizer.git",
|
||||
"reference": "8740fc48979f649dee8b8fc51a2698e5c190bf12"
|
||||
"reference": "3855e827adb1b675adcb98ad7f92681e293f2d77"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/8740fc48979f649dee8b8fc51a2698e5c190bf12",
|
||||
"reference": "8740fc48979f649dee8b8fc51a2698e5c190bf12",
|
||||
"url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/3855e827adb1b675adcb98ad7f92681e293f2d77",
|
||||
"reference": "3855e827adb1b675adcb98ad7f92681e293f2d77",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8846,7 +8893,7 @@
|
||||
"sanitizer"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/html-sanitizer/tree/v7.3.3"
|
||||
"source": "https://github.com/symfony/html-sanitizer/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -8866,20 +8913,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-12T10:34:03+00:00"
|
||||
"time": "2025-10-30T13:22:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v7.3.4",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62"
|
||||
"reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62",
|
||||
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de",
|
||||
"reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8946,7 +8993,7 @@
|
||||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.4"
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -8966,7 +9013,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T10:12:26+00:00"
|
||||
"time": "2025-11-05T17:41:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
@ -9048,16 +9095,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v7.3.5",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "ce31218c7cac92eab280762c4375fb70a6f4f897"
|
||||
"reference": "6379e490d6ecfc5c4224ff3a754b90495ecd135c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/ce31218c7cac92eab280762c4375fb70a6f4f897",
|
||||
"reference": "ce31218c7cac92eab280762c4375fb70a6f4f897",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/6379e490d6ecfc5c4224ff3a754b90495ecd135c",
|
||||
"reference": "6379e490d6ecfc5c4224ff3a754b90495ecd135c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -9107,7 +9154,7 @@
|
||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.3.5"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -9127,20 +9174,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-24T21:42:11+00:00"
|
||||
"time": "2025-11-06T11:05:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v7.3.5",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "24fd3f123532e26025f49f1abefcc01a69ef15ab"
|
||||
"reference": "f9a34dc0196677250e3609c2fac9de9e1551a262"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/24fd3f123532e26025f49f1abefcc01a69ef15ab",
|
||||
"reference": "24fd3f123532e26025f49f1abefcc01a69ef15ab",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/f9a34dc0196677250e3609c2fac9de9e1551a262",
|
||||
"reference": "f9a34dc0196677250e3609c2fac9de9e1551a262",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -9225,7 +9272,7 @@
|
||||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.3.5"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -9245,7 +9292,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-28T10:19:01+00:00"
|
||||
"time": "2025-11-06T20:58:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
@ -10454,16 +10501,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v7.3.4",
|
||||
"version": "v7.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
"reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c"
|
||||
"reference": "c97abe725f2a1a858deca629a6488c8fc20c3091"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/8dc648e159e9bac02b703b9fbd937f19ba13d07c",
|
||||
"reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/c97abe725f2a1a858deca629a6488c8fc20c3091",
|
||||
"reference": "c97abe725f2a1a858deca629a6488c8fc20c3091",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -10515,7 +10562,7 @@
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/routing/tree/v7.3.4"
|
||||
"source": "https://github.com/symfony/routing/tree/v7.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -10535,20 +10582,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T10:12:26+00:00"
|
||||
"time": "2025-11-05T07:57:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.6.0",
|
||||
"version": "v3.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
|
||||
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
|
||||
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
|
||||
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -10602,7 +10649,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -10613,12 +10660,16 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-25T09:37:31+00:00"
|
||||
"time": "2025-07-15T11:30:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
@ -10812,16 +10863,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
"version": "v3.6.0",
|
||||
"version": "v3.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation-contracts.git",
|
||||
"reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d"
|
||||
"reference": "65a8bc82080447fae78373aa10f8d13b38338977"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
|
||||
"reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977",
|
||||
"reference": "65a8bc82080447fae78373aa10f8d13b38338977",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -10870,7 +10921,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v3.6.0"
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v3.6.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -10881,12 +10932,16 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-27T08:32:26+00:00"
|
||||
"time": "2025-07-15T13:41:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/uid",
|
||||
@ -12203,16 +12258,16 @@
|
||||
},
|
||||
{
|
||||
"name": "larastan/larastan",
|
||||
"version": "v3.7.2",
|
||||
"version": "v3.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/larastan/larastan.git",
|
||||
"reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae"
|
||||
"reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/a761859a7487bd7d0cb8b662a7538a234d5bb5ae",
|
||||
"reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e",
|
||||
"reference": "d13ef96d652d1b2a8f34f1760ba6bf5b9c98112e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -12226,7 +12281,7 @@
|
||||
"illuminate/pipeline": "^11.44.2 || ^12.4.1",
|
||||
"illuminate/support": "^11.44.2 || ^12.4.1",
|
||||
"php": "^8.2",
|
||||
"phpstan/phpstan": "^2.1.28"
|
||||
"phpstan/phpstan": "^2.1.29"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^13",
|
||||
@ -12239,7 +12294,8 @@
|
||||
"phpunit/phpunit": "^10.5.35 || ^11.5.15"
|
||||
},
|
||||
"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",
|
||||
"extra": {
|
||||
@ -12280,7 +12336,7 @@
|
||||
],
|
||||
"support": {
|
||||
"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": [
|
||||
{
|
||||
@ -12288,7 +12344,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-19T09:03:05+00:00"
|
||||
"time": "2025-10-27T23:09:14+00:00"
|
||||
},
|
||||
{
|
||||
"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');
|
||||
});
|
||||
}
|
||||
};
|
||||
26
lang/en/admin/log.php
Normal file
26
lang/en/admin/log.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'empty_table' => 'Yay! No Errors!',
|
||||
'total_logs' => 'Total Logs',
|
||||
'error' => 'Error',
|
||||
'warning' => 'Warning',
|
||||
'notice' => 'Notice',
|
||||
'info' => 'Info',
|
||||
'debug' => 'Debug',
|
||||
'navigation' => [
|
||||
'panel_logs' => 'Panel Logs',
|
||||
],
|
||||
'actions' => [
|
||||
'upload_logs' => 'Upload Logs?',
|
||||
'upload_logs_description' => 'This will upload :file to :url Are you sure you wish to do this?',
|
||||
'view_logs' => 'View Logs',
|
||||
'log_not_found' => 'Log not found!',
|
||||
'log_not_found_description' => 'Could not find log for :filename',
|
||||
'failed_to_upload' => 'Failed to upload.',
|
||||
'failed_to_upload_description' => 'HTTP Status: :status',
|
||||
'log_upload' => 'Log Uploaded!',
|
||||
'log_upload_action' => 'View Log',
|
||||
'upload_tooltip' => 'Upload to :url',
|
||||
],
|
||||
];
|
||||
@ -10,6 +10,7 @@ return [
|
||||
'basic_settings' => 'Basic Settings',
|
||||
'advanced_settings' => 'Advanced Settings',
|
||||
'config_file' => 'Configuration File',
|
||||
'diagnostics' => 'Diagnostics',
|
||||
],
|
||||
'table' => [
|
||||
'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!',
|
||||
'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_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',
|
||||
'invalid' => 'Invalid',
|
||||
'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.',
|
||||
'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' => [
|
||||
'title' => 'Cloudflare Issue',
|
||||
'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',
|
||||
'alias' => 'Alias',
|
||||
'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',
|
||||
'external_id' => 'External ID',
|
||||
'owner' => 'Owner',
|
||||
|
||||
@ -17,6 +17,11 @@ return [
|
||||
'from_files' => 'Upload Files',
|
||||
'from_url' => 'Upload from URL',
|
||||
'url' => 'URL',
|
||||
'drop_files' => 'Drop files to upload',
|
||||
'success' => 'Files uploaded successfully',
|
||||
'failed' => 'Failed to upload files',
|
||||
'header' => 'Uploading Files',
|
||||
'error' => 'An error occurred while uploading',
|
||||
],
|
||||
'rename' => [
|
||||
'title' => 'Rename',
|
||||
|
||||
@ -12,4 +12,6 @@ return [
|
||||
'primary' => 'Primary',
|
||||
'make' => 'Make',
|
||||
'delete' => 'Delete',
|
||||
'locked' => 'Locked?',
|
||||
'locked_helper' => 'Locked allocations can only be deleted by admins',
|
||||
];
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
"laravel-vite-plugin": "^1.0",
|
||||
"prettier": "^3.4.2",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"vite": "6.2.6"
|
||||
"vite": "7.1.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
|
||||
@ -24,4 +24,6 @@
|
||||
/* Required by widgets */
|
||||
@import '../../vendor/filament/widgets/resources/css/index.css';
|
||||
|
||||
@source '../../vendor/gboquizosanchez/filament-log-viewer/resources/views/**/*.blade.php';
|
||||
|
||||
@variant dark (&:where(.dark, .dark *));
|
||||
|
||||
3
resources/views/filament/components/list-logs.blade.php
Normal file
3
resources/views/filament/components/list-logs.blade.php
Normal file
@ -0,0 +1,3 @@
|
||||
<x-filament-panels::page>
|
||||
{{ $this->table }}
|
||||
</x-filament-panels::page>
|
||||
373
resources/views/filament/server/pages/file-upload.blade.php
Normal file
373
resources/views/filament/server/pages/file-upload.blade.php
Normal file
@ -0,0 +1,373 @@
|
||||
<div
|
||||
x-data="{
|
||||
isUploading: false,
|
||||
uploadQueue: [],
|
||||
currentFileIndex: 0,
|
||||
totalFiles: 0,
|
||||
autoCloseTimer: 1000,
|
||||
|
||||
async extractFilesFromItems(items) {
|
||||
const filesWithPaths = [];
|
||||
const traversePromises = [];
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const entry = items[i].webkitGetAsEntry?.();
|
||||
|
||||
if (entry) {
|
||||
traversePromises.push(this.traverseFileTree(entry, '', filesWithPaths));
|
||||
} else if (items[i].kind === 'file') {
|
||||
const file = items[i].getAsFile();
|
||||
if (file) {
|
||||
filesWithPaths.push({
|
||||
file: file,
|
||||
path: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(traversePromises);
|
||||
return filesWithPaths;
|
||||
},
|
||||
|
||||
async traverseFileTree(entry, path, filesWithPaths) {
|
||||
return new Promise((resolve) => {
|
||||
if (entry.isFile) {
|
||||
entry.file((file) => {
|
||||
filesWithPaths.push({
|
||||
file: file,
|
||||
path: path,
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
} else if (entry.isDirectory) {
|
||||
const reader = entry.createReader();
|
||||
const readEntries = () => {
|
||||
reader.readEntries(async (entries) => {
|
||||
if (entries.length === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const subPromises = entries.map((e) =>
|
||||
this.traverseFileTree(
|
||||
e,
|
||||
path ? `${path}/${entry.name}` : entry.name,
|
||||
filesWithPaths
|
||||
)
|
||||
);
|
||||
|
||||
await Promise.all(subPromises);
|
||||
readEntries();
|
||||
});
|
||||
};
|
||||
readEntries();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async uploadFilesWithFolders(filesWithPaths) {
|
||||
this.isUploading = true;
|
||||
this.uploadQueue = [];
|
||||
this.totalFiles = filesWithPaths.length;
|
||||
this.currentFileIndex = 0;
|
||||
const uploadedFiles = [];
|
||||
|
||||
try {
|
||||
const uploadSizeLimit = await $wire.getUploadSizeLimit();
|
||||
|
||||
for (const {
|
||||
file
|
||||
}
|
||||
of filesWithPaths) {
|
||||
if (file.size > uploadSizeLimit) {
|
||||
new window.FilamentNotification()
|
||||
.title(`File ${file.name} exceeds the upload size limit of ${this.formatBytes(uploadSizeLimit)}`)
|
||||
.danger()
|
||||
.send();
|
||||
this.isUploading = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const folderPaths = new Set();
|
||||
for (const {
|
||||
path
|
||||
}
|
||||
of filesWithPaths) {
|
||||
if (path) {
|
||||
const parts = path.split('/').filter(Boolean);
|
||||
let currentPath = '';
|
||||
for (const part of parts) {
|
||||
currentPath += part + '/';
|
||||
folderPaths.add(currentPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const folderPath of folderPaths) {
|
||||
try {
|
||||
await $wire.createFolder(folderPath.slice(0, -1));
|
||||
} catch (error) {
|
||||
console.warn(`Folder ${folderPath} already exists or failed to create.`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const f of filesWithPaths) {
|
||||
this.uploadQueue.push({
|
||||
file: f.file,
|
||||
name: f.file.name,
|
||||
path: f.path,
|
||||
size: f.file.size,
|
||||
progress: 0,
|
||||
speed: 0,
|
||||
uploadedBytes: 0,
|
||||
status: 'pending',
|
||||
error: null
|
||||
});
|
||||
}
|
||||
|
||||
const maxConcurrent = 3;
|
||||
let activeUploads = [];
|
||||
let completedCount = 0;
|
||||
|
||||
for (let i = 0; i < this.uploadQueue.length; i++) {
|
||||
const uploadPromise = this.uploadFile(i)
|
||||
.then(() => {
|
||||
completedCount++;
|
||||
this.currentFileIndex = completedCount;
|
||||
const item = this.uploadQueue[i];
|
||||
const relativePath = (item.path ? item.path.replace(/^\/+/, '') + '/' : '') + item.name;
|
||||
uploadedFiles.push(relativePath);
|
||||
})
|
||||
.catch(() => {
|
||||
completedCount++;
|
||||
this.currentFileIndex = completedCount;
|
||||
});
|
||||
|
||||
activeUploads.push(uploadPromise);
|
||||
|
||||
if (activeUploads.length >= maxConcurrent) {
|
||||
await Promise.race(activeUploads);
|
||||
activeUploads = activeUploads.filter(p => p.status !== 'fulfilled' && p.status !== 'rejected');
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.allSettled(activeUploads);
|
||||
|
||||
const failed = this.uploadQueue.filter(f => f.status === 'error');
|
||||
await $wire.$refresh();
|
||||
|
||||
if (failed.length === 0) {
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.success') }}')
|
||||
.success()
|
||||
.send();
|
||||
} else if (failed.length === this.totalFiles) {
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.failed') }}')
|
||||
.danger()
|
||||
.send();
|
||||
} else {
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.failed') }}')
|
||||
.danger()
|
||||
.send();
|
||||
}
|
||||
|
||||
if (uploadedFiles.length > 0) {
|
||||
this.$nextTick(() => {
|
||||
if (typeof $wire !== 'undefined' && $wire && typeof $wire.call === 'function') {
|
||||
$wire.call('logUploadedFiles', uploadedFiles);
|
||||
} else if (typeof window.livewire !== 'undefined' && typeof window.livewire.call === 'function') {
|
||||
window.livewire.call('logUploadedFiles', uploadedFiles);
|
||||
} else if (typeof Livewire !== 'undefined' && typeof Livewire.call === 'function') {
|
||||
Livewire.call('logUploadedFiles', uploadedFiles);
|
||||
} else {
|
||||
console.warn('Could not call Livewire method logUploadedFiles; Livewire not found.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.autoCloseTimer) clearTimeout(this.autoCloseTimer);
|
||||
this.autoCloseTimer = setTimeout(() => {
|
||||
this.isUploading = false;
|
||||
this.uploadQueue = [];
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error('Upload error:', error);
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.error') }}')
|
||||
.danger()
|
||||
.send();
|
||||
this.isUploading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async uploadFile(index) {
|
||||
const fileData = this.uploadQueue[index];
|
||||
fileData.status = 'uploading';
|
||||
try {
|
||||
const uploadUrl = await $wire.getUploadUrl();
|
||||
const url = new URL(uploadUrl);
|
||||
let basePath = @js($this->path);
|
||||
|
||||
if (fileData.path && fileData.path.trim() !== '') {
|
||||
basePath = basePath.replace(/\/+$/, '') + '/' + fileData.path.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
url.searchParams.append('directory', basePath);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const formData = new FormData();
|
||||
formData.append('files', fileData.file);
|
||||
|
||||
let lastLoaded = 0;
|
||||
let lastTime = Date.now();
|
||||
|
||||
xhr.upload.addEventListener('progress', (e) => {
|
||||
if (e.lengthComputable) {
|
||||
fileData.uploadedBytes = e.loaded;
|
||||
fileData.progress = Math.round((e.loaded / e.total) * 100);
|
||||
|
||||
const now = Date.now();
|
||||
const timeDiff = (now - lastTime) / 1000;
|
||||
if (timeDiff > 0.1) {
|
||||
const bytesDiff = e.loaded - lastLoaded;
|
||||
fileData.speed = bytesDiff / timeDiff;
|
||||
lastTime = now;
|
||||
lastLoaded = e.loaded;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
fileData.status = 'complete';
|
||||
fileData.progress = 100;
|
||||
resolve();
|
||||
} else {
|
||||
fileData.status = 'error';
|
||||
fileData.error = `Upload failed (${xhr.status})`;
|
||||
reject(new Error(fileData.error));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
fileData.status = 'error';
|
||||
fileData.error = 'Network error';
|
||||
reject(new Error('Network error'));
|
||||
};
|
||||
|
||||
xhr.open('POST', url.toString());
|
||||
xhr.send(formData);
|
||||
});
|
||||
} catch (err) {
|
||||
fileData.status = 'error';
|
||||
fileData.error = 'Failed to get upload token';
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
formatBytes(bytes) {
|
||||
if (bytes === 0) return '0.00 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i];
|
||||
},
|
||||
formatSpeed(bytesPerSecond) {
|
||||
return this.formatBytes(bytesPerSecond) + '/s';
|
||||
},
|
||||
handleEscapeKey(e) {
|
||||
if (e.key === 'Escape' && this.isUploading) {
|
||||
this.isUploading = false;
|
||||
this.uploadQueue = [];
|
||||
}
|
||||
},
|
||||
async handleFileSelect(e) {
|
||||
const files = Array.from(e.target.files);
|
||||
if (files.length > 0) {
|
||||
const filesWithPaths = files.map(f => ({
|
||||
file: f,
|
||||
path: ''
|
||||
}));
|
||||
await this.uploadFilesWithFolders(filesWithPaths);
|
||||
}
|
||||
},
|
||||
triggerBrowse() {
|
||||
this.$refs.fileInput.click();
|
||||
},
|
||||
}"
|
||||
>
|
||||
<x-filament::icon-button
|
||||
iconSize="xl"
|
||||
icon="tabler-upload"
|
||||
color="success"
|
||||
tooltip="{{ trans('server/file.actions.upload.title') }}"
|
||||
@click="triggerBrowse">
|
||||
</x-filament::icon-button>
|
||||
<input type="file" x-ref="fileInput" class="hidden" multiple @change="handleFileSelect">
|
||||
<div
|
||||
x-show="isUploading"
|
||||
x-cloak
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/50 dark:bg-gray-100/20 p-4"
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-white shadow-xl dark:bg-gray-800 max-w-1/2 max-h-[50vh] overflow-hidden flex flex-col">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-center">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||
{{ trans('server/file.actions.upload.header') }} -
|
||||
<span class="text-lg text-gray-600 dark:text-gray-400">
|
||||
<span x-text="currentFileIndex"></span> of <span x-text="totalFiles"></span>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<div class="overflow-hidden">
|
||||
<table class="w-full divide-y divide-gray-200 dark:divide-white/5">
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-white/5 bg-white dark:bg-gray-900">
|
||||
<template x-for="(fileData, index) in uploadQueue" :key="index">
|
||||
<tr class="transition duration-75 hover:bg-gray-50 dark:hover:bg-white/5">
|
||||
<td class="px-4 py-4 sm:px-6">
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<div
|
||||
class="text-sm font-medium leading-6 text-gray-950 dark:text-white truncate max-w-xs"
|
||||
x-text="(fileData.path ? fileData.path + '/' : '') + fileData.name">
|
||||
</div>
|
||||
<div x-show="fileData.status === 'error'"
|
||||
class="text-xs text-danger-600 dark:text-danger-400"
|
||||
x-text="fileData.error"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 sm:px-6">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400"
|
||||
x-text="formatBytes(fileData.size)"></div>
|
||||
</td>
|
||||
<td class="px-4 py-4 sm:px-6">
|
||||
<div x-show="fileData.status === 'uploading' || fileData.status === 'complete'"
|
||||
class="flex justify-between items-center text-sm">
|
||||
<span class="font-medium text-gray-700 dark:text-gray-300"
|
||||
x-text="`${fileData.progress}%`"></span>
|
||||
<span x-show="fileData.status === 'uploading' && fileData.speed > 0"
|
||||
class="text-gray-500 dark:text-gray-400"
|
||||
x-text="formatSpeed(fileData.speed)"></span>
|
||||
</div>
|
||||
<span x-show="fileData.status === 'pending'"
|
||||
class="text-sm text-gray-500 dark:text-gray-400">
|
||||
—
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
441
resources/views/filament/server/pages/list-files.blade.php
Normal file
441
resources/views/filament/server/pages/list-files.blade.php
Normal file
@ -0,0 +1,441 @@
|
||||
<x-filament-panels::page>
|
||||
<div
|
||||
x-data="
|
||||
{
|
||||
isDragging: false,
|
||||
dragCounter: 0,
|
||||
isUploading: false,
|
||||
uploadQueue: [],
|
||||
currentFileIndex: 0,
|
||||
totalFiles: 0,
|
||||
autoCloseTimer: 1000,
|
||||
|
||||
handleDragEnter(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.dragCounter++;
|
||||
this.isDragging = true;
|
||||
},
|
||||
handleDragLeave(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.dragCounter--;
|
||||
if (this.dragCounter === 0) this.isDragging = false;
|
||||
},
|
||||
handleDragOver(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
async handleDrop(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.isDragging = false;
|
||||
this.dragCounter = 0;
|
||||
|
||||
const items = e.dataTransfer.items;
|
||||
const files = e.dataTransfer.files;
|
||||
|
||||
if ((!items || items.length === 0) && (!files || files.length === 0)) return;
|
||||
|
||||
let filesWithPaths = [];
|
||||
|
||||
if (items && items.length > 0 && items[0].webkitGetAsEntry) {
|
||||
filesWithPaths = await this.extractFilesFromItems(items);
|
||||
}
|
||||
|
||||
if (files && files.length > 0 && filesWithPaths.length === 0) {
|
||||
filesWithPaths = Array.from(files).map(f => ({
|
||||
file: f,
|
||||
path: ''
|
||||
}));
|
||||
}
|
||||
|
||||
if (filesWithPaths.length > 0) {
|
||||
await this.uploadFilesWithFolders(filesWithPaths);
|
||||
}
|
||||
},
|
||||
|
||||
async extractFilesFromItems(items) {
|
||||
const filesWithPaths = [];
|
||||
const traversePromises = [];
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const entry = items[i].webkitGetAsEntry?.();
|
||||
|
||||
if (entry) {
|
||||
traversePromises.push(this.traverseFileTree(entry, '', filesWithPaths));
|
||||
} else if (items[i].kind === 'file') {
|
||||
const file = items[i].getAsFile();
|
||||
if (file) {
|
||||
filesWithPaths.push({
|
||||
file: file,
|
||||
path: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(traversePromises);
|
||||
|
||||
return filesWithPaths;
|
||||
},
|
||||
|
||||
async traverseFileTree(entry, path, filesWithPaths) {
|
||||
return new Promise((resolve) => {
|
||||
if (entry.isFile) {
|
||||
entry.file((file) => {
|
||||
filesWithPaths.push({
|
||||
file: file,
|
||||
path: path,
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
} else if (entry.isDirectory) {
|
||||
const reader = entry.createReader();
|
||||
const readEntries = () => {
|
||||
reader.readEntries(async (entries) => {
|
||||
if (entries.length === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const subPromises = entries.map((e) =>
|
||||
this.traverseFileTree(
|
||||
e,
|
||||
path ? `${path}/${entry.name}` : entry.name,
|
||||
filesWithPaths
|
||||
)
|
||||
);
|
||||
|
||||
await Promise.all(subPromises);
|
||||
readEntries();
|
||||
});
|
||||
};
|
||||
readEntries();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
async uploadFilesWithFolders(filesWithPaths) {
|
||||
this.isUploading = true;
|
||||
this.uploadQueue = [];
|
||||
this.totalFiles = filesWithPaths.length;
|
||||
this.currentFileIndex = 0;
|
||||
const uploadedFiles = [];
|
||||
|
||||
try {
|
||||
const uploadSizeLimit = await $wire.getUploadSizeLimit();
|
||||
|
||||
for (const {
|
||||
file
|
||||
}
|
||||
of filesWithPaths) {
|
||||
if (file.size > uploadSizeLimit) {
|
||||
new window.FilamentNotification()
|
||||
.title(`File ${file.name} exceeds the upload limit.`)
|
||||
.danger()
|
||||
.send();
|
||||
this.isUploading = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const folderPaths = new Set();
|
||||
for (const {
|
||||
path
|
||||
}
|
||||
of filesWithPaths) {
|
||||
if (path) {
|
||||
const parts = path.split('/').filter(Boolean);
|
||||
let currentPath = '';
|
||||
for (const part of parts) {
|
||||
currentPath += part + '/';
|
||||
folderPaths.add(currentPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const folderPath of folderPaths) {
|
||||
try {
|
||||
await $wire.createFolder(folderPath.slice(0, -1));
|
||||
} catch (error) {
|
||||
console.warn(`Folder ${folderPath} already exists or failed to create.`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const f of filesWithPaths) {
|
||||
this.uploadQueue.push({
|
||||
file: f.file,
|
||||
name: f.file.name,
|
||||
path: f.path,
|
||||
size: f.file.size,
|
||||
progress: 0,
|
||||
speed: 0,
|
||||
uploadedBytes: 0,
|
||||
status: 'pending',
|
||||
error: null
|
||||
});
|
||||
}
|
||||
|
||||
const maxConcurrent = 3;
|
||||
let activeUploads = [];
|
||||
let completedCount = 0;
|
||||
|
||||
for (let i = 0; i < this.uploadQueue.length; i++) {
|
||||
const uploadPromise = this.uploadFile(i)
|
||||
.then(() => {
|
||||
completedCount++;
|
||||
this.currentFileIndex = completedCount;
|
||||
const item = this.uploadQueue[i];
|
||||
const relativePath = (item.path ? item.path.replace(/^\/+/, '') + '/' : '') + item.name;
|
||||
uploadedFiles.push(relativePath);
|
||||
})
|
||||
.catch(() => {
|
||||
completedCount++;
|
||||
this.currentFileIndex = completedCount;
|
||||
});
|
||||
|
||||
activeUploads.push(uploadPromise);
|
||||
|
||||
if (activeUploads.length >= maxConcurrent) {
|
||||
await Promise.race(activeUploads);
|
||||
activeUploads = activeUploads.filter(p => p.status !== 'fulfilled' && p.status !== 'rejected');
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.allSettled(activeUploads);
|
||||
|
||||
const failed = this.uploadQueue.filter(f => f.status === 'error');
|
||||
await $wire.$refresh();
|
||||
|
||||
if (failed.length === 0) {
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.success') }}')
|
||||
.success()
|
||||
.send();
|
||||
} else if (failed.length === this.totalFiles) {
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.failed') }}')
|
||||
.danger()
|
||||
.send();
|
||||
} else {
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.failed') }}')
|
||||
.danger()
|
||||
.send();
|
||||
}
|
||||
|
||||
if (uploadedFiles.length > 0) {
|
||||
this.$nextTick(() => {
|
||||
if (typeof $wire !== 'undefined' && $wire && typeof $wire.call === 'function') {
|
||||
$wire.call('logUploadedFiles', uploadedFiles);
|
||||
} else if (typeof window.livewire !== 'undefined' && typeof window.livewire.call === 'function') {
|
||||
window.livewire.call('logUploadedFiles', uploadedFiles);
|
||||
} else if (typeof Livewire !== 'undefined' && typeof Livewire.call === 'function') {
|
||||
Livewire.call('logUploadedFiles', uploadedFiles);
|
||||
} else {
|
||||
console.warn('Could not call Livewire method logUploadedFiles; Livewire not found.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.autoCloseTimer) clearTimeout(this.autoCloseTimer);
|
||||
this.autoCloseTimer = setTimeout(() => {
|
||||
this.isUploading = false;
|
||||
this.uploadQueue = [];
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error('Upload error:', error);
|
||||
new window.FilamentNotification()
|
||||
.title('{{ trans('server/file.actions.upload.error') }}')
|
||||
.danger()
|
||||
.send();
|
||||
this.isUploading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async uploadFile(index) {
|
||||
const fileData = this.uploadQueue[index];
|
||||
fileData.status = 'uploading';
|
||||
try {
|
||||
const uploadUrl = await $wire.getUploadUrl();
|
||||
const url = new URL(uploadUrl);
|
||||
let basePath = @js($this->path);
|
||||
|
||||
if (fileData.path && fileData.path.trim() !== '') {
|
||||
basePath = basePath.replace(/\/+$/, '') + '/' + fileData.path.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
url.searchParams.append('directory', basePath);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const formData = new FormData();
|
||||
formData.append('files', fileData.file);
|
||||
|
||||
let lastLoaded = 0;
|
||||
let lastTime = Date.now();
|
||||
|
||||
xhr.upload.addEventListener('progress', (e) => {
|
||||
if (e.lengthComputable) {
|
||||
fileData.uploadedBytes = e.loaded;
|
||||
fileData.progress = Math.round((e.loaded / e.total) * 100);
|
||||
|
||||
const now = Date.now();
|
||||
const timeDiff = (now - lastTime) / 1000;
|
||||
if (timeDiff > 0.1) {
|
||||
const bytesDiff = e.loaded - lastLoaded;
|
||||
fileData.speed = bytesDiff / timeDiff;
|
||||
lastTime = now;
|
||||
lastLoaded = e.loaded;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
fileData.status = 'complete';
|
||||
fileData.progress = 100;
|
||||
resolve();
|
||||
} else {
|
||||
fileData.status = 'error';
|
||||
fileData.error = `Upload failed (${xhr.status})`;
|
||||
reject(new Error(fileData.error));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
fileData.status = 'error';
|
||||
fileData.error = 'Network error';
|
||||
reject(new Error('Network error'));
|
||||
};
|
||||
|
||||
xhr.open('POST', url.toString());
|
||||
xhr.send(formData);
|
||||
});
|
||||
} catch (err) {
|
||||
fileData.status = 'error';
|
||||
fileData.error = 'Failed to get upload token';
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
formatBytes(bytes) {
|
||||
if (bytes === 0) return '0.00 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i];
|
||||
},
|
||||
formatSpeed(bytesPerSecond) {
|
||||
return this.formatBytes(bytesPerSecond) + '/s';
|
||||
},
|
||||
handleEscapeKey(e) {
|
||||
if (e.key === 'Escape' && this.isUploading) {
|
||||
this.isUploading = false;
|
||||
this.uploadQueue = [];
|
||||
}
|
||||
},
|
||||
}"
|
||||
@dragenter.window="handleDragEnter($event)"
|
||||
@dragleave.window="handleDragLeave($event)"
|
||||
@dragover.window="handleDragOver($event)"
|
||||
@drop.window="handleDrop($event)"
|
||||
@keydown.window="handleEscapeKey($event)"
|
||||
class="relative"
|
||||
>
|
||||
<div
|
||||
x-show="isDragging"
|
||||
x-cloak
|
||||
x-transition:enter="transition-[opacity] duration-200 ease-out"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition-[opacity] duration-150 ease-in"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/50 dark:bg-gray-100/20"
|
||||
>
|
||||
<div class="rounded-lg bg-white p-8 shadow-xl dark:bg-gray-800">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon icon-tabler icons-tabler-outline icon-tabler-upload size-12 text-success-500"
|
||||
viewBox="0 0 36 36" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2" />
|
||||
<path d="M7 9l5 -5l5 5" />
|
||||
<path d="M12 4l0 12" />
|
||||
</svg>
|
||||
<p class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||
{{ trans('server/file.actions.upload.drop_files') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
x-show="isUploading"
|
||||
x-cloak
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/50 dark:bg-gray-100/20 p-4"
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-white shadow-xl dark:bg-gray-800 max-w-1/2 max-h-[50vh] overflow-hidden flex flex-col">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-center">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||||
{{ trans('server/file.actions.upload.header') }} -
|
||||
<span class="text-lg text-gray-600 dark:text-gray-400">
|
||||
<span x-text="currentFileIndex"></span> of <span x-text="totalFiles"></span>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<div class="overflow-hidden">
|
||||
<table class="w-full divide-y divide-gray-200 dark:divide-white/5">
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-white/5 bg-white dark:bg-gray-900">
|
||||
<template x-for="(fileData, index) in uploadQueue" :key="index">
|
||||
<tr class="transition duration-75 hover:bg-gray-50 dark:hover:bg-white/5">
|
||||
<td class="px-4 py-4 sm:px-6">
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<div
|
||||
class="text-sm font-medium leading-6 text-gray-950 dark:text-white truncate max-w-xs"
|
||||
x-text="(fileData.path ? fileData.path + '/' : '') + fileData.name">
|
||||
</div>
|
||||
<div x-show="fileData.status === 'error'"
|
||||
class="text-xs text-danger-600 dark:text-danger-400"
|
||||
x-text="fileData.error"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 sm:px-6">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400"
|
||||
x-text="formatBytes(fileData.size)"></div>
|
||||
</td>
|
||||
<td class="px-4 py-4 sm:px-6">
|
||||
<div x-show="fileData.status === 'uploading' || fileData.status === 'complete'"
|
||||
class="flex justify-between items-center text-sm">
|
||||
<span class="font-medium text-gray-700 dark:text-gray-300"
|
||||
x-text="`${fileData.progress}%`"></span>
|
||||
<span x-show="fileData.status === 'uploading' && fileData.speed > 0"
|
||||
class="text-gray-500 dark:text-gray-400"
|
||||
x-text="formatSpeed(fileData.speed)"></span>
|
||||
</div>
|
||||
<span x-show="fileData.status === 'pending'"
|
||||
class="text-sm text-gray-500 dark:text-gray-400">
|
||||
—
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ $this->table }}
|
||||
</div>
|
||||
|
||||
<x-filament-actions::modals />
|
||||
</x-filament-panels::page>
|
||||
277
yarn.lock
277
yarn.lock
@ -182,105 +182,115 @@
|
||||
"@emnapi/runtime" "^1.4.0"
|
||||
"@tybys/wasm-util" "^0.9.0"
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz#d964ee8ce4d18acf9358f96adc408689b6e27fe3"
|
||||
integrity sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==
|
||||
"@rollup/rollup-android-arm-eabi@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.1.tgz#63f6bdc496180079976e655473d5bea99b21f3ff"
|
||||
integrity sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz#9b5e130ecc32a5fc1e96c09ff371743ee71a62d3"
|
||||
integrity sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==
|
||||
"@rollup/rollup-android-arm64@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.1.tgz#177f5e504d2f332edd0ddd3682f91ab72528fb60"
|
||||
integrity sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz#ef439182c739b20b3c4398cfc03e3c1249ac8903"
|
||||
integrity sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==
|
||||
"@rollup/rollup-darwin-arm64@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.1.tgz#ffdbe0cc43c88a35be2821f99cdff4c7a5ee2116"
|
||||
integrity sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz#d7380c1531ab0420ca3be16f17018ef72dd3d504"
|
||||
integrity sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==
|
||||
"@rollup/rollup-darwin-x64@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.1.tgz#27a4852923010abbcd1f028c7e8bd6bf0ccbe755"
|
||||
integrity sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==
|
||||
|
||||
"@rollup/rollup-freebsd-arm64@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz#cbcbd7248823c6b430ce543c59906dd3c6df0936"
|
||||
integrity sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==
|
||||
"@rollup/rollup-freebsd-arm64@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.1.tgz#a02b83018e487674ab445198786bef9b41cad9f0"
|
||||
integrity sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==
|
||||
|
||||
"@rollup/rollup-freebsd-x64@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz#96bf6ff875bab5219c3472c95fa6eb992586a93b"
|
||||
integrity sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==
|
||||
"@rollup/rollup-freebsd-x64@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.1.tgz#fe898a4f0ff7c30f8377c3976ae76b89720c41da"
|
||||
integrity sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz#d80cd62ce6d40f8e611008d8dbf03b5e6bbf009c"
|
||||
integrity sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.1.tgz#be5a731a9f7bd7bc707457a768940b6107a9215e"
|
||||
integrity sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz#75440cfc1e8d0f87a239b4c31dfeaf4719b656b7"
|
||||
integrity sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.1.tgz#30ce6548a9e3591303507c37280300edb0cd1d14"
|
||||
integrity sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz#ac527485ecbb619247fb08253ec8c551a0712e7c"
|
||||
integrity sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==
|
||||
"@rollup/rollup-linux-arm64-gnu@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.1.tgz#ec76f4223335e86cd61b0d596f34e97223f4f711"
|
||||
integrity sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz#74d2b5cb11cf714cd7d1682e7c8b39140e908552"
|
||||
integrity sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==
|
||||
"@rollup/rollup-linux-arm64-musl@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.1.tgz#9d4d87c2988ec8e4bb3cf4516dda7ef6d09dcd3d"
|
||||
integrity sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==
|
||||
|
||||
"@rollup/rollup-linux-loongarch64-gnu@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz#a0a310e51da0b5fea0e944b0abd4be899819aef6"
|
||||
integrity sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==
|
||||
"@rollup/rollup-linux-loong64-gnu@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.1.tgz#584bc6f3c33b30c3dbfdad36ac9c7792e4df5199"
|
||||
integrity sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz#4077e2862b0ac9f61916d6b474d988171bd43b83"
|
||||
integrity sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==
|
||||
"@rollup/rollup-linux-ppc64-gnu@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.1.tgz#3e9a3b095a7d7da6043cb9caa54439d3b598aaf5"
|
||||
integrity sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz#5812a1a7a2f9581cbe12597307cc7ba3321cf2f3"
|
||||
integrity sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.1.tgz#f3c3d6523d246eef4aa1eed265f1ba31b9eef7c8"
|
||||
integrity sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz#973aaaf4adef4531375c36616de4e01647f90039"
|
||||
integrity sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==
|
||||
"@rollup/rollup-linux-riscv64-musl@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.1.tgz#0a8944b4f29a1ba923fb9c2ddb829e621f004988"
|
||||
integrity sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz#9bad59e907ba5bfcf3e9dbd0247dfe583112f70b"
|
||||
integrity sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==
|
||||
"@rollup/rollup-linux-s390x-gnu@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.1.tgz#bcb48f2d509ef6b33ba89f7d76a2f3805be8d4c8"
|
||||
integrity sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz"
|
||||
integrity sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==
|
||||
"@rollup/rollup-linux-x64-gnu@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.1.tgz#ca9045e3b8e8dc0797e55d0229d5c664211bf366"
|
||||
integrity sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz"
|
||||
integrity sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==
|
||||
"@rollup/rollup-linux-x64-musl@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.1.tgz#740876db76078e37bd43cc8584ff1c7f6b382df8"
|
||||
integrity sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz#c5bee19fa670ff5da5f066be6a58b4568e9c650b"
|
||||
integrity sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==
|
||||
"@rollup/rollup-openharmony-arm64@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.1.tgz#3ff19213afe46b806fb6ec105f2664e4027e4cbc"
|
||||
integrity sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz#846e02c17044bd922f6f483a3b4d36aac6e2b921"
|
||||
integrity sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==
|
||||
"@rollup/rollup-win32-arm64-msvc@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.1.tgz#cbba39610831747f8050a306811776534df1030d"
|
||||
integrity sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.40.0":
|
||||
version "4.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz#fd92d31a2931483c25677b9c6698106490cbbc76"
|
||||
integrity sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==
|
||||
"@rollup/rollup-win32-ia32-msvc@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.1.tgz#5453c7ebba95d2bbfcc94c744c05586d587fb640"
|
||||
integrity sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.1.tgz#01e1acb0dacb220d13c8992340f7bc868a564832"
|
||||
integrity sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.53.1":
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.1.tgz#56eeb602545ec03ce84633b331c2e3ece07b99c3"
|
||||
integrity sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==
|
||||
|
||||
"@tailwindcss/forms@^0.5.9":
|
||||
version "0.5.10"
|
||||
@ -410,10 +420,10 @@
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@types/estree@1.0.7":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8"
|
||||
integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==
|
||||
"@types/estree@1.0.8":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||
|
||||
"@xterm/addon-fit@^0.10.0":
|
||||
version "0.10.0"
|
||||
@ -632,6 +642,11 @@ escalade@^3.1.1, escalade@^3.2.0:
|
||||
resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz"
|
||||
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
|
||||
|
||||
fdir@^6.5.0:
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
|
||||
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
|
||||
|
||||
foreground-child@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
|
||||
@ -817,9 +832,9 @@ minipass@^7.1.2:
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
|
||||
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
|
||||
|
||||
nanoid@^3.3.8:
|
||||
nanoid@^3.3.11:
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
|
||||
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
|
||||
|
||||
node-releases@^2.0.19:
|
||||
@ -860,6 +875,11 @@ picomatch@^2.3.1:
|
||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
picomatch@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
|
||||
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||
|
||||
postcss-selector-parser@6.0.10:
|
||||
version "6.0.10"
|
||||
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz"
|
||||
@ -873,12 +893,12 @@ postcss-value-parser@^4.2.0:
|
||||
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.5.3:
|
||||
version "8.5.3"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz"
|
||||
integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==
|
||||
postcss@^8.5.6:
|
||||
version "8.5.6"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
|
||||
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
|
||||
dependencies:
|
||||
nanoid "^3.3.8"
|
||||
nanoid "^3.3.11"
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
@ -897,33 +917,35 @@ require-directory@^2.1.1:
|
||||
resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz"
|
||||
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
|
||||
|
||||
rollup@^4.30.1:
|
||||
version "4.40.0"
|
||||
resolved "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz"
|
||||
integrity sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==
|
||||
rollup@^4.43.0:
|
||||
version "4.53.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.1.tgz#84d1d378584a15dedfcdcff7767a8b9d92d8d3d9"
|
||||
integrity sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==
|
||||
dependencies:
|
||||
"@types/estree" "1.0.7"
|
||||
"@types/estree" "1.0.8"
|
||||
optionalDependencies:
|
||||
"@rollup/rollup-android-arm-eabi" "4.40.0"
|
||||
"@rollup/rollup-android-arm64" "4.40.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.40.0"
|
||||
"@rollup/rollup-darwin-x64" "4.40.0"
|
||||
"@rollup/rollup-freebsd-arm64" "4.40.0"
|
||||
"@rollup/rollup-freebsd-x64" "4.40.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.40.0"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.40.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.40.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.40.0"
|
||||
"@rollup/rollup-linux-loongarch64-gnu" "4.40.0"
|
||||
"@rollup/rollup-linux-powerpc64le-gnu" "4.40.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.40.0"
|
||||
"@rollup/rollup-linux-riscv64-musl" "4.40.0"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.40.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.40.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.40.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.40.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.40.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.40.0"
|
||||
"@rollup/rollup-android-arm-eabi" "4.53.1"
|
||||
"@rollup/rollup-android-arm64" "4.53.1"
|
||||
"@rollup/rollup-darwin-arm64" "4.53.1"
|
||||
"@rollup/rollup-darwin-x64" "4.53.1"
|
||||
"@rollup/rollup-freebsd-arm64" "4.53.1"
|
||||
"@rollup/rollup-freebsd-x64" "4.53.1"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.53.1"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.53.1"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.53.1"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.53.1"
|
||||
"@rollup/rollup-linux-loong64-gnu" "4.53.1"
|
||||
"@rollup/rollup-linux-ppc64-gnu" "4.53.1"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.53.1"
|
||||
"@rollup/rollup-linux-riscv64-musl" "4.53.1"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.53.1"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.53.1"
|
||||
"@rollup/rollup-linux-x64-musl" "4.53.1"
|
||||
"@rollup/rollup-openharmony-arm64" "4.53.1"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.53.1"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.53.1"
|
||||
"@rollup/rollup-win32-x64-gnu" "4.53.1"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.53.1"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rxjs-compat@^6.5.4:
|
||||
@ -1037,6 +1059,14 @@ tapable@^2.2.0:
|
||||
resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"
|
||||
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
|
||||
|
||||
tinyglobby@^0.2.15:
|
||||
version "0.2.15"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
|
||||
integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
|
||||
dependencies:
|
||||
fdir "^6.5.0"
|
||||
picomatch "^4.0.3"
|
||||
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz"
|
||||
@ -1068,14 +1098,17 @@ vite-plugin-full-reload@^1.1.0:
|
||||
picocolors "^1.0.0"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
vite@6.2.6:
|
||||
version "6.2.6"
|
||||
resolved "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz"
|
||||
integrity sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==
|
||||
vite@7.1.11:
|
||||
version "7.1.11"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.11.tgz#4d006746112fee056df64985191e846ebfb6007e"
|
||||
integrity sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==
|
||||
dependencies:
|
||||
esbuild "^0.25.0"
|
||||
postcss "^8.5.3"
|
||||
rollup "^4.30.1"
|
||||
fdir "^6.5.0"
|
||||
picomatch "^4.0.3"
|
||||
postcss "^8.5.6"
|
||||
rollup "^4.43.0"
|
||||
tinyglobby "^0.2.15"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user