mirror of
https://github.com/pelican-dev/panel.git
synced 2025-06-22 22:11:09 +02:00
Merge branch 'main' into vehikl/singleton
This commit is contained in:
commit
2ba50e616c
@ -24,6 +24,7 @@ class MakeNodeCommand extends Command
|
|||||||
{--overallocateCpu= : Enter the amount of cpu to overallocate (% or -1 to overallocate the maximum).}
|
{--overallocateCpu= : Enter the amount of cpu to overallocate (% or -1 to overallocate the maximum).}
|
||||||
{--uploadSize= : Enter the maximum upload filesize.}
|
{--uploadSize= : Enter the maximum upload filesize.}
|
||||||
{--daemonListeningPort= : Enter the daemon listening port.}
|
{--daemonListeningPort= : Enter the daemon listening port.}
|
||||||
|
{--daemonConnectingPort= : Enter the daemon connecting port.}
|
||||||
{--daemonSFTPPort= : Enter the daemon SFTP listening port.}
|
{--daemonSFTPPort= : Enter the daemon SFTP listening port.}
|
||||||
{--daemonSFTPAlias= : Enter the daemon SFTP alias.}
|
{--daemonSFTPAlias= : Enter the daemon SFTP alias.}
|
||||||
{--daemonBase= : Enter the base folder.}';
|
{--daemonBase= : Enter the base folder.}';
|
||||||
@ -57,6 +58,7 @@ class MakeNodeCommand extends Command
|
|||||||
$data['cpu_overallocate'] = $this->option('overallocateCpu') ?? $this->ask(trans('commands.make_node.cpu_overallocate'), '-1');
|
$data['cpu_overallocate'] = $this->option('overallocateCpu') ?? $this->ask(trans('commands.make_node.cpu_overallocate'), '-1');
|
||||||
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(trans('commands.make_node.upload_size'), '256');
|
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(trans('commands.make_node.upload_size'), '256');
|
||||||
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(trans('commands.make_node.daemonListen'), '8080');
|
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(trans('commands.make_node.daemonListen'), '8080');
|
||||||
|
$data['daemon_connect'] = $this->option('daemonConnectingPort') ?? $this->ask(trans('commands.make_node.daemonConnect'), '8080');
|
||||||
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(trans('commands.make_node.daemonSFTP'), '2022');
|
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(trans('commands.make_node.daemonSFTP'), '2022');
|
||||||
$data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(trans('commands.make_node.daemonSFTPAlias'), '');
|
$data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(trans('commands.make_node.daemonSFTPAlias'), '');
|
||||||
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(trans('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
|
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(trans('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
|
||||||
|
@ -7,7 +7,6 @@ use App\Console\Commands\Maintenance\CleanServiceBackupFilesCommand;
|
|||||||
use App\Console\Commands\Maintenance\PruneImagesCommand;
|
use App\Console\Commands\Maintenance\PruneImagesCommand;
|
||||||
use App\Console\Commands\Maintenance\PruneOrphanedBackupsCommand;
|
use App\Console\Commands\Maintenance\PruneOrphanedBackupsCommand;
|
||||||
use App\Console\Commands\Schedule\ProcessRunnableCommand;
|
use App\Console\Commands\Schedule\ProcessRunnableCommand;
|
||||||
use App\Jobs\NodeStatistics;
|
|
||||||
use App\Models\ActivityLog;
|
use App\Models\ActivityLog;
|
||||||
use App\Models\Webhook;
|
use App\Models\Webhook;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
@ -44,8 +43,6 @@ class Kernel extends ConsoleKernel
|
|||||||
$schedule->command(PruneImagesCommand::class)->daily();
|
$schedule->command(PruneImagesCommand::class)->daily();
|
||||||
$schedule->command(CheckEggUpdatesCommand::class)->hourly();
|
$schedule->command(CheckEggUpdatesCommand::class)->hourly();
|
||||||
|
|
||||||
$schedule->job(new NodeStatistics())->everyFiveSeconds()->withoutOverlapping();
|
|
||||||
|
|
||||||
if (config('backups.prune_age')) {
|
if (config('backups.prune_age')) {
|
||||||
// Every 30 minutes, run the backup pruning command so that any abandoned backups can be deleted.
|
// Every 30 minutes, run the backup pruning command so that any abandoned backups can be deleted.
|
||||||
$schedule->command(PruneOrphanedBackupsCommand::class)->everyThirtyMinutes();
|
$schedule->command(PruneOrphanedBackupsCommand::class)->everyThirtyMinutes();
|
||||||
|
@ -635,7 +635,6 @@ class Settings extends Page implements HasForms
|
|||||||
->onColor('success')
|
->onColor('success')
|
||||||
->offColor('danger')
|
->offColor('danger')
|
||||||
->live()
|
->live()
|
||||||
->columnSpanFull()
|
|
||||||
->formatStateUsing(fn ($state): bool => (bool) $state)
|
->formatStateUsing(fn ($state): bool => (bool) $state)
|
||||||
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_INSTALL_NOTIFICATION', (bool) $state))
|
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_INSTALL_NOTIFICATION', (bool) $state))
|
||||||
->default(env('PANEL_SEND_INSTALL_NOTIFICATION', config('panel.email.send_install_notification'))),
|
->default(env('PANEL_SEND_INSTALL_NOTIFICATION', config('panel.email.send_install_notification'))),
|
||||||
@ -646,7 +645,6 @@ class Settings extends Page implements HasForms
|
|||||||
->onColor('success')
|
->onColor('success')
|
||||||
->offColor('danger')
|
->offColor('danger')
|
||||||
->live()
|
->live()
|
||||||
->columnSpanFull()
|
|
||||||
->formatStateUsing(fn ($state): bool => (bool) $state)
|
->formatStateUsing(fn ($state): bool => (bool) $state)
|
||||||
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_REINSTALL_NOTIFICATION', (bool) $state))
|
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_REINSTALL_NOTIFICATION', (bool) $state))
|
||||||
->default(env('PANEL_SEND_REINSTALL_NOTIFICATION', config('panel.email.send_reinstall_notification'))),
|
->default(env('PANEL_SEND_REINSTALL_NOTIFICATION', config('panel.email.send_reinstall_notification'))),
|
||||||
|
@ -124,15 +124,10 @@ class CreateNode extends CreateRecord
|
|||||||
'lg' => 1,
|
'lg' => 1,
|
||||||
]),
|
]),
|
||||||
|
|
||||||
TextInput::make('daemon_listen')
|
TextInput::make('daemon_connect')
|
||||||
->columnSpan([
|
->columnSpan(1)
|
||||||
'default' => 1,
|
->label(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port') : trans('admin/node.port'))
|
||||||
'sm' => 1,
|
->helperText(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port_help') : trans('admin/node.port_help'))
|
||||||
'md' => 1,
|
|
||||||
'lg' => 1,
|
|
||||||
])
|
|
||||||
->label(trans('admin/node.port'))
|
|
||||||
->helperText(trans('admin/node.port_help'))
|
|
||||||
->minValue(1)
|
->minValue(1)
|
||||||
->maxValue(65535)
|
->maxValue(65535)
|
||||||
->default(8080)
|
->default(8080)
|
||||||
@ -193,7 +188,21 @@ class CreateNode extends CreateRecord
|
|||||||
->afterStateUpdated(function ($state, Set $set) {
|
->afterStateUpdated(function ($state, Set $set) {
|
||||||
$set('scheme', $state === 'http' ? 'http' : 'https');
|
$set('scheme', $state === 'http' ? 'http' : 'https');
|
||||||
$set('behind_proxy', $state === 'https_proxy');
|
$set('behind_proxy', $state === 'https_proxy');
|
||||||
|
|
||||||
|
$set('daemon_connect', $state === 'https_proxy' ? 443 : 8080);
|
||||||
|
$set('daemon_listen', 8080);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
TextInput::make('daemon_listen')
|
||||||
|
->columnSpan(1)
|
||||||
|
->label(trans('admin/node.listen_port'))
|
||||||
|
->helperText(trans('admin/node.listen_port_help'))
|
||||||
|
->minValue(1)
|
||||||
|
->maxValue(65535)
|
||||||
|
->default(8080)
|
||||||
|
->required()
|
||||||
|
->integer()
|
||||||
|
->visible(fn (Get $get) => $get('connection') === 'https_proxy'),
|
||||||
]),
|
]),
|
||||||
Step::make('advanced')
|
Step::make('advanced')
|
||||||
->label(trans('admin/node.tabs.advanced_settings'))
|
->label(trans('admin/node.tabs.advanced_settings'))
|
||||||
@ -409,4 +418,13 @@ class CreateNode extends CreateRecord
|
|||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function mutateFormDataBeforeCreate(array $data): array
|
||||||
|
{
|
||||||
|
if (!$data['behind_proxy']) {
|
||||||
|
$data['daemon_listen'] = $data['daemon_connect'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,10 +181,10 @@ class EditNode extends EditRecord
|
|||||||
false => 'danger',
|
false => 'danger',
|
||||||
])
|
])
|
||||||
->columnSpan(1),
|
->columnSpan(1),
|
||||||
TextInput::make('daemon_listen')
|
TextInput::make('daemon_connect')
|
||||||
->columnSpan(1)
|
->columnSpan(1)
|
||||||
->label(trans('admin/node.port'))
|
->label(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port') : trans('admin/node.port'))
|
||||||
->helperText(trans('admin/node.port_help'))
|
->helperText(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port_help') : trans('admin/node.port_help'))
|
||||||
->minValue(1)
|
->minValue(1)
|
||||||
->maxValue(65535)
|
->maxValue(65535)
|
||||||
->default(8080)
|
->default(8080)
|
||||||
@ -239,7 +239,20 @@ class EditNode extends EditRecord
|
|||||||
->afterStateUpdated(function ($state, Set $set) {
|
->afterStateUpdated(function ($state, Set $set) {
|
||||||
$set('scheme', $state === 'http' ? 'http' : 'https');
|
$set('scheme', $state === 'http' ? 'http' : 'https');
|
||||||
$set('behind_proxy', $state === 'https_proxy');
|
$set('behind_proxy', $state === 'https_proxy');
|
||||||
|
|
||||||
|
$set('daemon_connect', $state === 'https_proxy' ? 443 : 8080);
|
||||||
|
$set('daemon_listen', 8080);
|
||||||
}),
|
}),
|
||||||
|
TextInput::make('daemon_listen')
|
||||||
|
->columnSpan(1)
|
||||||
|
->label(trans('admin/node.listen_port'))
|
||||||
|
->helperText(trans('admin/node.listen_port_help'))
|
||||||
|
->minValue(1)
|
||||||
|
->maxValue(65535)
|
||||||
|
->default(8080)
|
||||||
|
->required()
|
||||||
|
->integer()
|
||||||
|
->visible(fn (Get $get) => $get('connection') === 'https_proxy'),
|
||||||
]),
|
]),
|
||||||
Tab::make('adv')
|
Tab::make('adv')
|
||||||
->label(trans('admin/node.tabs.advanced_settings'))
|
->label(trans('admin/node.tabs.advanced_settings'))
|
||||||
@ -627,6 +640,15 @@ class EditNode extends EditRecord
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function mutateFormDataBeforeSave(array $data): array
|
||||||
|
{
|
||||||
|
if (!$data['behind_proxy']) {
|
||||||
|
$data['daemon_listen'] = $data['daemon_connect'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
protected function afterSave(): void
|
protected function afterSave(): void
|
||||||
{
|
{
|
||||||
$this->fillForm();
|
$this->fillForm();
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Filament\Admin\Resources\NodeResource\Widgets;
|
namespace App\Filament\Admin\Resources\NodeResource\Widgets;
|
||||||
|
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Carbon\Carbon;
|
|
||||||
use Filament\Support\RawJs;
|
use Filament\Support\RawJs;
|
||||||
use Filament\Widgets\ChartWidget;
|
use Filament\Widgets\ChartWidget;
|
||||||
use Illuminate\Support\Number;
|
use Illuminate\Support\Number;
|
||||||
@ -16,22 +15,34 @@ class NodeCpuChart extends ChartWidget
|
|||||||
|
|
||||||
public Node $node;
|
public Node $node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, array{cpu: string, timestamp: string}>
|
||||||
|
*/
|
||||||
|
protected array $cpuHistory = [];
|
||||||
|
|
||||||
|
protected int $threads = 0;
|
||||||
|
|
||||||
protected function getData(): array
|
protected function getData(): array
|
||||||
{
|
{
|
||||||
$threads = $this->node->systemInformation()['cpu_count'] ?? 0;
|
$sessionKey = "node_stats.{$this->node->id}";
|
||||||
|
|
||||||
$cpu = collect(cache()->get("nodes.{$this->node->id}.cpu_percent"))
|
$data = $this->node->statistics();
|
||||||
->slice(-10)
|
|
||||||
->map(fn ($value, $key) => [
|
$this->threads = session("{$sessionKey}.threads", $this->node->systemInformation()['cpu_count'] ?? 0);
|
||||||
'cpu' => round($value * $threads, 2),
|
|
||||||
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
$this->cpuHistory = session("{$sessionKey}.cpu_history", []);
|
||||||
])
|
$this->cpuHistory[] = [
|
||||||
->all();
|
'cpu' => round($data['cpu_percent'] * $this->threads, 2),
|
||||||
|
'timestamp' => now(auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->cpuHistory = array_slice($this->cpuHistory, -60);
|
||||||
|
session()->put("{$sessionKey}.cpu_history", $this->cpuHistory);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'datasets' => [
|
'datasets' => [
|
||||||
[
|
[
|
||||||
'data' => array_column($cpu, 'cpu'),
|
'data' => array_column($this->cpuHistory, 'cpu'),
|
||||||
'backgroundColor' => [
|
'backgroundColor' => [
|
||||||
'rgba(96, 165, 250, 0.3)',
|
'rgba(96, 165, 250, 0.3)',
|
||||||
],
|
],
|
||||||
@ -39,7 +50,7 @@ class NodeCpuChart extends ChartWidget
|
|||||||
'fill' => true,
|
'fill' => true,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'labels' => array_column($cpu, 'timestamp'),
|
'labels' => array_column($this->cpuHistory, 'timestamp'),
|
||||||
'locale' => auth()->user()->language ?? 'en',
|
'locale' => auth()->user()->language ?? 'en',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -69,10 +80,10 @@ class NodeCpuChart extends ChartWidget
|
|||||||
|
|
||||||
public function getHeading(): string
|
public function getHeading(): string
|
||||||
{
|
{
|
||||||
$threads = $this->node->systemInformation()['cpu_count'] ?? 0;
|
$data = array_slice(end($this->cpuHistory), -60);
|
||||||
|
|
||||||
$cpu = Number::format(collect(cache()->get("nodes.{$this->node->id}.cpu_percent"))->last() * $threads, maxPrecision: 2, locale: auth()->user()->language);
|
$cpu = Number::format($data['cpu'], maxPrecision: 2, locale: auth()->user()->language);
|
||||||
$max = Number::format($threads * 100, locale: auth()->user()->language);
|
$max = Number::format($this->threads * 100, locale: auth()->user()->language);
|
||||||
|
|
||||||
return trans('admin/node.cpu_chart', ['cpu' => $cpu, 'max' => $max]);
|
return trans('admin/node.cpu_chart', ['cpu' => $cpu, 'max' => $max]);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Filament\Admin\Resources\NodeResource\Widgets;
|
namespace App\Filament\Admin\Resources\NodeResource\Widgets;
|
||||||
|
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Carbon\Carbon;
|
|
||||||
use Filament\Support\RawJs;
|
use Filament\Support\RawJs;
|
||||||
use Filament\Widgets\ChartWidget;
|
use Filament\Widgets\ChartWidget;
|
||||||
use Illuminate\Support\Number;
|
use Illuminate\Support\Number;
|
||||||
@ -16,19 +15,36 @@ class NodeMemoryChart extends ChartWidget
|
|||||||
|
|
||||||
public Node $node;
|
public Node $node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, array{memory: string, timestamp: string}>
|
||||||
|
*/
|
||||||
|
protected array $memoryHistory = [];
|
||||||
|
|
||||||
|
protected int $totalMemory = 0;
|
||||||
|
|
||||||
protected function getData(): array
|
protected function getData(): array
|
||||||
{
|
{
|
||||||
$memUsed = collect(cache()->get("nodes.{$this->node->id}.memory_used"))->slice(-10)
|
$sessionKey = "node_stats.{$this->node->id}";
|
||||||
->map(fn ($value, $key) => [
|
|
||||||
'memory' => round(config('panel.use_binary_prefix') ? $value / 1024 / 1024 / 1024 : $value / 1000 / 1000 / 1000, 2),
|
$data = $this->node->statistics();
|
||||||
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
|
||||||
])
|
$this->totalMemory = session("{$sessionKey}.total_memory", $data['memory_total']);
|
||||||
->all();
|
|
||||||
|
$this->memoryHistory = session("{$sessionKey}.memory_history", []);
|
||||||
|
$this->memoryHistory[] = [
|
||||||
|
'memory' => round(config('panel.use_binary_prefix')
|
||||||
|
? $data['memory_used'] / 1024 / 1024 / 1024
|
||||||
|
: $data['memory_used'] / 1000 / 1000 / 1000, 2),
|
||||||
|
'timestamp' => now(auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->memoryHistory = array_slice($this->memoryHistory, -60);
|
||||||
|
session()->put("{$sessionKey}.memory_history", $this->memoryHistory);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'datasets' => [
|
'datasets' => [
|
||||||
[
|
[
|
||||||
'data' => array_column($memUsed, 'memory'),
|
'data' => array_column($this->memoryHistory, 'memory'),
|
||||||
'backgroundColor' => [
|
'backgroundColor' => [
|
||||||
'rgba(96, 165, 250, 0.3)',
|
'rgba(96, 165, 250, 0.3)',
|
||||||
],
|
],
|
||||||
@ -36,7 +52,7 @@ class NodeMemoryChart extends ChartWidget
|
|||||||
'fill' => true,
|
'fill' => true,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'labels' => array_column($memUsed, 'timestamp'),
|
'labels' => array_column($this->memoryHistory, 'timestamp'),
|
||||||
'locale' => auth()->user()->language ?? 'en',
|
'locale' => auth()->user()->language ?? 'en',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -66,16 +82,15 @@ class NodeMemoryChart extends ChartWidget
|
|||||||
|
|
||||||
public function getHeading(): string
|
public function getHeading(): string
|
||||||
{
|
{
|
||||||
$latestMemoryUsed = collect(cache()->get("nodes.{$this->node->id}.memory_used"))->last();
|
$latestMemoryUsed = array_slice(end($this->memoryHistory), -60);
|
||||||
$totalMemory = collect(cache()->get("nodes.{$this->node->id}.memory_total"))->last();
|
|
||||||
|
|
||||||
$used = config('panel.use_binary_prefix')
|
$used = config('panel.use_binary_prefix')
|
||||||
? Number::format($latestMemoryUsed / 1024 / 1024 / 1024, maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
? Number::format($latestMemoryUsed['memory'], maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
||||||
: Number::format($latestMemoryUsed / 1000 / 1000 / 1000, maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
: Number::format($latestMemoryUsed['memory'], maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
||||||
|
|
||||||
$total = config('panel.use_binary_prefix')
|
$total = config('panel.use_binary_prefix')
|
||||||
? Number::format($totalMemory / 1024 / 1024 / 1024, maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
? Number::format($this->totalMemory / 1024 / 1024 / 1024, maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
||||||
: Number::format($totalMemory / 1000 / 1000 / 1000, maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
: Number::format($this->totalMemory / 1000 / 1000 / 1000, maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
||||||
|
|
||||||
return trans('admin/node.memory_chart', ['used' => $used, 'total' => $total]);
|
return trans('admin/node.memory_chart', ['used' => $used, 'total' => $total]);
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,5 @@ use Filament\Tables\Columns\Column;
|
|||||||
|
|
||||||
class ServerEntryColumn extends Column
|
class ServerEntryColumn extends Column
|
||||||
{
|
{
|
||||||
protected string $view = 'tables.columns.server-entry-column';
|
protected string $view = 'livewire.columns.server-entry-column';
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ class AllocationResource extends Resource
|
|||||||
|
|
||||||
Activity::event('server:allocation.delete')
|
Activity::event('server:allocation.delete')
|
||||||
->subject($allocation)
|
->subject($allocation)
|
||||||
->property('allocation', $allocation->toString())
|
->property('allocation', $allocation->address)
|
||||||
->log();
|
->log();
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
@ -32,7 +32,7 @@ class ListAllocations extends ListRecords
|
|||||||
|
|
||||||
Activity::event('server:allocation.create')
|
Activity::event('server:allocation.create')
|
||||||
->subject($allocation)
|
->subject($allocation)
|
||||||
->property('allocation', $allocation->toString())
|
->property('allocation', $allocation->address)
|
||||||
->log();
|
->log();
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
@ -314,9 +314,9 @@ class ListFiles extends ListRecords
|
|||||||
->label('')
|
->label('')
|
||||||
->icon('tabler-trash')
|
->icon('tabler-trash')
|
||||||
->requiresConfirmation()
|
->requiresConfirmation()
|
||||||
->modalDescription(fn (File $file) => $file->name)
|
->modalHeading(fn (File $file) => trans('filament-actions::delete.single.modal.heading', ['label' => $file->name . ' ' . ($file->is_directory ? 'folder' : 'file')]))
|
||||||
->modalHeading('Delete file?')
|
|
||||||
->action(function (File $file) {
|
->action(function (File $file) {
|
||||||
|
$this->deselectAllTableRecords();
|
||||||
$this->getDaemonFileRepository()->deleteFiles($this->path, [$file->name]);
|
$this->getDaemonFileRepository()->deleteFiles($this->path, [$file->name]);
|
||||||
|
|
||||||
Activity::event('server:file.delete')
|
Activity::event('server:file.delete')
|
||||||
|
@ -62,7 +62,7 @@ class NetworkAllocationController extends ClientApiController
|
|||||||
if ($original !== $allocation->notes) {
|
if ($original !== $allocation->notes) {
|
||||||
Activity::event('server:allocation.notes')
|
Activity::event('server:allocation.notes')
|
||||||
->subject($allocation)
|
->subject($allocation)
|
||||||
->property(['allocation' => $allocation->toString(), 'old' => $original, 'new' => $allocation->notes])
|
->property(['allocation' => $allocation->address, 'old' => $original, 'new' => $allocation->notes])
|
||||||
->log();
|
->log();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ class NetworkAllocationController extends ClientApiController
|
|||||||
|
|
||||||
Activity::event('server:allocation.primary')
|
Activity::event('server:allocation.primary')
|
||||||
->subject($allocation)
|
->subject($allocation)
|
||||||
->property('allocation', $allocation->toString())
|
->property('allocation', $allocation->address)
|
||||||
->log();
|
->log();
|
||||||
|
|
||||||
return $this->fractal->item($allocation)
|
return $this->fractal->item($allocation)
|
||||||
@ -114,7 +114,7 @@ class NetworkAllocationController extends ClientApiController
|
|||||||
|
|
||||||
Activity::event('server:allocation.create')
|
Activity::event('server:allocation.create')
|
||||||
->subject($allocation)
|
->subject($allocation)
|
||||||
->property('allocation', $allocation->toString())
|
->property('allocation', $allocation->address)
|
||||||
->log();
|
->log();
|
||||||
|
|
||||||
return $this->fractal->item($allocation)
|
return $this->fractal->item($allocation)
|
||||||
@ -148,7 +148,7 @@ class NetworkAllocationController extends ClientApiController
|
|||||||
|
|
||||||
Activity::event('server:allocation.delete')
|
Activity::event('server:allocation.delete')
|
||||||
->subject($allocation)
|
->subject($allocation)
|
||||||
->property('allocation', $allocation->toString())
|
->property('allocation', $allocation->address)
|
||||||
->log();
|
->log();
|
||||||
|
|
||||||
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs;
|
|
||||||
|
|
||||||
use App\Models\Node;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class NodeStatistics implements ShouldBeUnique, ShouldQueue
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
foreach (Node::all() as $node) {
|
|
||||||
$stats = $node->statistics();
|
|
||||||
$timestamp = now()->getTimestamp();
|
|
||||||
|
|
||||||
foreach ($stats as $key => $value) {
|
|
||||||
$cacheKey = "nodes.{$node->id}.$key";
|
|
||||||
$data = cache()->get($cacheKey, []);
|
|
||||||
|
|
||||||
// Add current timestamp and value to the data array
|
|
||||||
$data[$timestamp] = $value;
|
|
||||||
|
|
||||||
// Update the cache with the new data, expires in 1 minute
|
|
||||||
cache()->put($cacheKey, $data, now()->addMinute());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
64
app/Livewire/ServerEntry.php
Normal file
64
app/Livewire/ServerEntry.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class ServerEntry extends Component
|
||||||
|
{
|
||||||
|
public Server $server;
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('livewire.server-entry');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function placeholder(): string
|
||||||
|
{
|
||||||
|
return <<<'HTML'
|
||||||
|
<div class="relative">
|
||||||
|
<div
|
||||||
|
class="absolute left-0 top-1 bottom-0 w-1 rounded-lg"
|
||||||
|
style="background-color: #D97706;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 dark:bg-gray-800 dark:text-white rounded-lg overflow-hidden p-3">
|
||||||
|
<div class="flex items-center mb-5 gap-2">
|
||||||
|
<x-filament::loading-indicator class="h-5 w-5" />
|
||||||
|
<h2 class="text-xl font-bold">
|
||||||
|
{{ $server->name }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between text-center">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">CPU</p>
|
||||||
|
<p class="text-md font-semibold">{{ Number::format(0, precision: 2, locale: auth()->user()->language ?? 'en') . '%' }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('cpu', type: \App\Enums\ServerResourceType::Percentage, limit: true) }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">Memory</p>
|
||||||
|
<p class="text-md font-semibold">{{ convert_bytes_to_readable(0, decimals: 2) }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('memory', limit: true) }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">Disk</p>
|
||||||
|
<p class="text-md font-semibold">{{ convert_bytes_to_readable(0, decimals: 2) }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('disk', limit: true) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:block">
|
||||||
|
<p class="text-sm dark:text-gray-400">Network</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-md font-semibold">{{ $server->allocation->address }} </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
}
|
@ -121,11 +121,6 @@ class Allocation extends Model
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toString(): string
|
|
||||||
{
|
|
||||||
return $this->address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets information for the server associated with this allocation.
|
* Gets information for the server associated with this allocation.
|
||||||
*/
|
*/
|
||||||
|
@ -38,6 +38,7 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
* @property string $daemon_token_id
|
* @property string $daemon_token_id
|
||||||
* @property string $daemon_token
|
* @property string $daemon_token
|
||||||
* @property int $daemon_listen
|
* @property int $daemon_listen
|
||||||
|
* @property int $daemon_connect
|
||||||
* @property int $daemon_sftp
|
* @property int $daemon_sftp
|
||||||
* @property string|null $daemon_sftp_alias
|
* @property string|null $daemon_sftp_alias
|
||||||
* @property string $daemon_base
|
* @property string $daemon_base
|
||||||
@ -83,7 +84,7 @@ class Node extends Model implements Validatable
|
|||||||
'memory', 'memory_overallocate', 'disk',
|
'memory', 'memory_overallocate', 'disk',
|
||||||
'disk_overallocate', 'cpu', 'cpu_overallocate',
|
'disk_overallocate', 'cpu', 'cpu_overallocate',
|
||||||
'upload_size', 'daemon_base',
|
'upload_size', 'daemon_base',
|
||||||
'daemon_sftp', 'daemon_sftp_alias', 'daemon_listen',
|
'daemon_sftp', 'daemon_sftp_alias', 'daemon_listen', 'daemon_connect',
|
||||||
'description', 'maintenance_mode', 'tags',
|
'description', 'maintenance_mode', 'tags',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -105,6 +106,7 @@ class Node extends Model implements Validatable
|
|||||||
'daemon_sftp' => ['required', 'numeric', 'between:1,65535'],
|
'daemon_sftp' => ['required', 'numeric', 'between:1,65535'],
|
||||||
'daemon_sftp_alias' => ['nullable', 'string'],
|
'daemon_sftp_alias' => ['nullable', 'string'],
|
||||||
'daemon_listen' => ['required', 'numeric', 'between:1,65535'],
|
'daemon_listen' => ['required', 'numeric', 'between:1,65535'],
|
||||||
|
'daemon_connect' => ['required', 'numeric', 'between:1,65535'],
|
||||||
'maintenance_mode' => ['boolean'],
|
'maintenance_mode' => ['boolean'],
|
||||||
'upload_size' => ['int', 'between:1,1024'],
|
'upload_size' => ['int', 'between:1,1024'],
|
||||||
'tags' => ['array'],
|
'tags' => ['array'],
|
||||||
@ -125,6 +127,7 @@ class Node extends Model implements Validatable
|
|||||||
'daemon_base' => '/var/lib/pelican/volumes',
|
'daemon_base' => '/var/lib/pelican/volumes',
|
||||||
'daemon_sftp' => 2022,
|
'daemon_sftp' => 2022,
|
||||||
'daemon_listen' => 8080,
|
'daemon_listen' => 8080,
|
||||||
|
'daemon_connect' => 8080,
|
||||||
'maintenance_mode' => false,
|
'maintenance_mode' => false,
|
||||||
'tags' => '[]',
|
'tags' => '[]',
|
||||||
];
|
];
|
||||||
@ -136,6 +139,7 @@ class Node extends Model implements Validatable
|
|||||||
'disk' => 'integer',
|
'disk' => 'integer',
|
||||||
'cpu' => 'integer',
|
'cpu' => 'integer',
|
||||||
'daemon_listen' => 'integer',
|
'daemon_listen' => 'integer',
|
||||||
|
'daemon_connect' => 'integer',
|
||||||
'daemon_sftp' => 'integer',
|
'daemon_sftp' => 'integer',
|
||||||
'daemon_token' => 'encrypted',
|
'daemon_token' => 'encrypted',
|
||||||
'behind_proxy' => 'boolean',
|
'behind_proxy' => 'boolean',
|
||||||
@ -171,7 +175,7 @@ class Node extends Model implements Validatable
|
|||||||
*/
|
*/
|
||||||
public function getConnectionAddress(): string
|
public function getConnectionAddress(): string
|
||||||
{
|
{
|
||||||
return "$this->scheme://$this->fqdn:$this->daemon_listen";
|
return "$this->scheme://$this->fqdn:$this->daemon_connect";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +19,7 @@ class DaemonServerRepository extends DaemonRepository
|
|||||||
public function getDetails(): array
|
public function getDetails(): array
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->getHttpClient()->get("/api/servers/{$this->server->uuid}")->throw()->json();
|
return $this->getHttpClient()->connectTimeout(1)->timeout(1)->get("/api/servers/{$this->server->uuid}")->throw()->json();
|
||||||
} catch (RequestException $exception) {
|
} catch (RequestException $exception) {
|
||||||
$cfId = $exception->response->header('Cf-Ray');
|
$cfId = $exception->response->header('Cf-Ray');
|
||||||
$cfCache = $exception->response->header('Cf-Cache-Status');
|
$cfCache = $exception->response->header('Cf-Cache-Status');
|
||||||
|
@ -47,7 +47,7 @@ class TransferServerService
|
|||||||
|
|
||||||
// Check if the node is viable for the transfer.
|
// Check if the node is viable for the transfer.
|
||||||
$node = Node::query()
|
$node = Node::query()
|
||||||
->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemon_listen', 'nodes.memory', 'nodes.disk', 'nodes.cpu', 'nodes.memory_overallocate', 'nodes.disk_overallocate', 'nodes.cpu_overallocate'])
|
->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemon_connect', 'nodes.memory', 'nodes.disk', 'nodes.cpu', 'nodes.memory_overallocate', 'nodes.disk_overallocate', 'nodes.cpu_overallocate'])
|
||||||
->withSum('servers', 'disk')
|
->withSum('servers', 'disk')
|
||||||
->withSum('servers', 'memory')
|
->withSum('servers', 'memory')
|
||||||
->withSum('servers', 'cpu')
|
->withSum('servers', 'cpu')
|
||||||
|
@ -2,54 +2,20 @@
|
|||||||
|
|
||||||
namespace App\Traits;
|
namespace App\Traits;
|
||||||
|
|
||||||
use Exception;
|
use Illuminate\Support\Env;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
trait EnvironmentWriterTrait
|
trait EnvironmentWriterTrait
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Escapes an environment value by looking for any characters that could
|
|
||||||
* reasonably cause environment parsing issues. Those values are then wrapped
|
|
||||||
* in quotes before being returned.
|
|
||||||
*/
|
|
||||||
public function escapeEnvironmentValue(string $value): string
|
|
||||||
{
|
|
||||||
if (!preg_match('/^\"(.*)\"$/', $value) && preg_match('/([^\w.\-+\/])+/', $value)) {
|
|
||||||
return sprintf('"%s"', addcslashes($value, '\\"'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the .env file for the application using the passed in values.
|
* Update the .env file for the application using the passed in values.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $values
|
* @param array<string, mixed> $values
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
public function writeToEnvironment(array $values = []): void
|
public function writeToEnvironment(array $values = []): void
|
||||||
{
|
{
|
||||||
$path = base_path('.env');
|
Env::writeVariables($values, base_path('.env'), true);
|
||||||
if (!file_exists($path)) {
|
|
||||||
throw new Exception('Cannot locate .env file, was this software installed correctly?');
|
|
||||||
}
|
|
||||||
|
|
||||||
$saveContents = file_get_contents($path);
|
|
||||||
if ($saveContents === false) {
|
|
||||||
$saveContents = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
collect($values)->each(function ($value, $key) use (&$saveContents) {
|
|
||||||
$key = strtoupper($key);
|
|
||||||
$saveValue = sprintf('%s=%s', $key, $this->escapeEnvironmentValue($value ?? ''));
|
|
||||||
|
|
||||||
if (preg_match_all('/^' . $key . '=(.*)$/m', $saveContents) < 1) {
|
|
||||||
$saveContents = $saveContents . PHP_EOL . $saveValue;
|
|
||||||
} else {
|
|
||||||
$saveContents = preg_replace('/^' . $key . '=(.*)$/m', $saveValue, $saveContents);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
file_put_contents($path, $saveContents);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
"doctrine/dbal": "~3.6.0",
|
"doctrine/dbal": "~3.6.0",
|
||||||
"filament/filament": "^3.3",
|
"filament/filament": "^3.3",
|
||||||
"guzzlehttp/guzzle": "^7.9",
|
"guzzlehttp/guzzle": "^7.9",
|
||||||
"laravel/framework": "^12.16",
|
"laravel/framework": "^12.17",
|
||||||
"laravel/helpers": "^1.7",
|
"laravel/helpers": "^1.7",
|
||||||
"laravel/sanctum": "^4.1",
|
"laravel/sanctum": "^4.1",
|
||||||
"laravel/socialite": "^5.20",
|
"laravel/socialite": "^5.21",
|
||||||
"laravel/tinker": "^2.10.1",
|
"laravel/tinker": "^2.10.1",
|
||||||
"laravel/ui": "^4.6",
|
"laravel/ui": "^4.6",
|
||||||
"lcobucci/jwt": "~4.3.0",
|
"lcobucci/jwt": "~4.3.0",
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"spatie/laravel-data": "^4.15",
|
"spatie/laravel-data": "^4.15",
|
||||||
"spatie/laravel-fractal": "^6.3",
|
"spatie/laravel-fractal": "^6.3",
|
||||||
"spatie/laravel-health": "^1.34",
|
"spatie/laravel-health": "^1.34",
|
||||||
"spatie/laravel-permission": "^6.18",
|
"spatie/laravel-permission": "^6.19",
|
||||||
"spatie/laravel-query-builder": "^6.3",
|
"spatie/laravel-query-builder": "^6.3",
|
||||||
"spatie/temporary-directory": "^2.3",
|
"spatie/temporary-directory": "^2.3",
|
||||||
"symfony/http-client": "^7.2",
|
"symfony/http-client": "^7.2",
|
||||||
|
480
composer.lock
generated
480
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,7 @@ class NodeFactory extends Factory
|
|||||||
'daemon_token_id' => Str::random(Node::DAEMON_TOKEN_ID_LENGTH),
|
'daemon_token_id' => Str::random(Node::DAEMON_TOKEN_ID_LENGTH),
|
||||||
'daemon_token' => Str::random(Node::DAEMON_TOKEN_LENGTH),
|
'daemon_token' => Str::random(Node::DAEMON_TOKEN_LENGTH),
|
||||||
'daemon_listen' => 8080,
|
'daemon_listen' => 8080,
|
||||||
|
'daemon_connect' => 8080,
|
||||||
'daemon_sftp' => 2022,
|
'daemon_sftp' => 2022,
|
||||||
'daemon_base' => '/var/lib/panel/volumes',
|
'daemon_base' => '/var/lib/panel/volumes',
|
||||||
'maintenance_mode' => false,
|
'maintenance_mode' => false,
|
||||||
|
@ -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('nodes', function (Blueprint $table) {
|
||||||
|
$table->smallInteger('daemon_connect')->unsigned()->default(8080);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('nodes', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('daemon_connect');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -45,6 +45,10 @@ return [
|
|||||||
'port' => 'Port',
|
'port' => 'Port',
|
||||||
'ports' => 'Ports',
|
'ports' => 'Ports',
|
||||||
'port_help' => 'If you are running the daemon behind Cloudflare you should set the daemon port to 8443 to allow websocket proxying over SSL.',
|
'port_help' => 'If you are running the daemon behind Cloudflare you should set the daemon port to 8443 to allow websocket proxying over SSL.',
|
||||||
|
'connect_port' => 'Connection Port',
|
||||||
|
'connect_port_help' => 'Connections to wings will use this port. If you are using a reverse proxy this can differ from the listen port. When using Cloudflare proxy you should use 8443.',
|
||||||
|
'listen_port' => 'Listening Port',
|
||||||
|
'listen_port_help' => 'Wings will listen on this port.',
|
||||||
'display_name' => 'Display Name',
|
'display_name' => 'Display Name',
|
||||||
'ssl' => 'Communicate over SSL',
|
'ssl' => 'Communicate over SSL',
|
||||||
'panel_on_ssl' => 'Your Panel is using a secure SSL connection,<br>so your Daemon must too.',
|
'panel_on_ssl' => 'Your Panel is using a secure SSL connection,<br>so your Daemon must too.',
|
||||||
|
@ -36,6 +36,7 @@ return [
|
|||||||
'cpu_overallocate' => 'Enter the amount of cpu to over allocate by, -1 will disable checking and 0 will prevent creating new server',
|
'cpu_overallocate' => 'Enter the amount of cpu to over allocate by, -1 will disable checking and 0 will prevent creating new server',
|
||||||
'upload_size' => "'Enter the maximum filesize upload",
|
'upload_size' => "'Enter the maximum filesize upload",
|
||||||
'daemonListen' => 'Enter the daemon listening port',
|
'daemonListen' => 'Enter the daemon listening port',
|
||||||
|
'daemonConnect' => 'Enter the daemon connecting port (can be same as listen port)',
|
||||||
'daemonSFTP' => 'Enter the daemon SFTP listening port',
|
'daemonSFTP' => 'Enter the daemon SFTP listening port',
|
||||||
'daemonSFTPAlias' => 'Enter the daemon SFTP alias (can be empty)',
|
'daemonSFTPAlias' => 'Enter the daemon SFTP alias (can be empty)',
|
||||||
'daemonBase' => 'Enter the base folder',
|
'daemonBase' => 'Enter the base folder',
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
@php
|
||||||
|
/** @var \App\Models\Server $server */
|
||||||
|
$server = $getRecord();
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="w-full">
|
||||||
|
@livewire('server-entry', ['server' => $server, 'lazy' => true], key($server->id))
|
||||||
|
</div>
|
47
resources/views/livewire/server-entry.blade.php
Normal file
47
resources/views/livewire/server-entry.blade.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<div class="relative">
|
||||||
|
<div
|
||||||
|
class="absolute left-0 top-1 bottom-0 w-1 rounded-lg"
|
||||||
|
style="background-color: {{ $server->condition->getColor(true) }};">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 dark:bg-gray-800 dark:text-white rounded-lg overflow-hidden p-3">
|
||||||
|
<div class="flex items-center mb-5 gap-2">
|
||||||
|
<x-filament::icon-button
|
||||||
|
:icon="$server->condition->getIcon()"
|
||||||
|
:color="$server->condition->getColor()"
|
||||||
|
:tooltip="$server->condition->getLabel()"
|
||||||
|
size="xl"
|
||||||
|
/>
|
||||||
|
<h2 class="text-xl font-bold">
|
||||||
|
{{ $server->name }}
|
||||||
|
<span class="dark:text-gray-400">({{ $server->formatResource('uptime', type: \App\Enums\ServerResourceType::Time) }})</span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between text-center">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">CPU</p>
|
||||||
|
<p class="text-md font-semibold">{{ $server->formatResource('cpu_absolute', type: \App\Enums\ServerResourceType::Percentage) }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('cpu', type: \App\Enums\ServerResourceType::Percentage, limit: true) }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">Memory</p>
|
||||||
|
<p class="text-md font-semibold">{{ $server->formatResource('memory_bytes') }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('memory', limit: true) }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">Disk</p>
|
||||||
|
<p class="text-md font-semibold">{{ $server->formatResource('disk_bytes') }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('disk', limit: true) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:block">
|
||||||
|
<p class="text-sm dark:text-gray-400">Network</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-md font-semibold">{{ $server->allocation->address }} </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,63 +0,0 @@
|
|||||||
@php
|
|
||||||
use App\Enums\ServerResourceType;
|
|
||||||
|
|
||||||
/** @var \App\Models\Server $server */
|
|
||||||
$server = $getRecord();
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
hr {
|
|
||||||
border-color: #9ca3af;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<div class="w-full">
|
|
||||||
<div class="relative">
|
|
||||||
<div
|
|
||||||
class="absolute left-0 top-1 bottom-0 w-1 rounded-lg"
|
|
||||||
style="background-color: {{ $server->condition->getColor(true) }};">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-1 bg-gray-800 dark:text-white rounded-lg overflow-hidden p-3">
|
|
||||||
<div class="flex items-center mb-5 gap-2">
|
|
||||||
<x-filament::icon-button
|
|
||||||
:icon="$server->condition->getIcon()"
|
|
||||||
:color="$server->condition->getColor()"
|
|
||||||
:tooltip="$server->condition->getLabel()"
|
|
||||||
size="xl"
|
|
||||||
/>
|
|
||||||
<h2 class="text-xl font-bold">
|
|
||||||
{{ $server->name }}
|
|
||||||
<span class="dark:text-gray-400">({{ $server->formatResource('uptime', type: ServerResourceType::Time) }})</span>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-between text-center">
|
|
||||||
<div>
|
|
||||||
<p class="text-sm dark:text-gray-400">CPU</p>
|
|
||||||
<p class="text-md font-semibold">{{ $server->formatResource('cpu_absolute', type: ServerResourceType::Percentage) }}</p>
|
|
||||||
<hr class="p-0.5">
|
|
||||||
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('cpu', type: ServerResourceType::Percentage, limit: true) }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="text-sm dark:text-gray-400">Memory</p>
|
|
||||||
<p class="text-md font-semibold">{{ $server->formatResource('memory_bytes') }}</p>
|
|
||||||
<hr class="p-0.5">
|
|
||||||
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('memory', limit: true) }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="text-sm dark:text-gray-400">Disk</p>
|
|
||||||
<p class="text-md font-semibold">{{ $server->formatResource('disk_bytes') }}</p>
|
|
||||||
<hr class="p-0.5">
|
|
||||||
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('disk', limit: true) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:block">
|
|
||||||
<p class="text-sm dark:text-gray-400">Network</p>
|
|
||||||
<hr class="p-0.5">
|
|
||||||
<p class="text-md font-semibold">{{ $server->allocation->address }} </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Tests\Unit\Helpers;
|
|
||||||
|
|
||||||
use App\Tests\TestCase;
|
|
||||||
use App\Traits\EnvironmentWriterTrait;
|
|
||||||
use PHPUnit\Framework\Attributes\DataProvider;
|
|
||||||
|
|
||||||
class EnvironmentWriterTraitTest extends TestCase
|
|
||||||
{
|
|
||||||
#[DataProvider('variableDataProvider')]
|
|
||||||
public function test_variable_is_escaped_properly($input, $expected): void
|
|
||||||
{
|
|
||||||
$output = (new FooClass())->escapeEnvironmentValue($input);
|
|
||||||
|
|
||||||
$this->assertSame($expected, $output);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function variableDataProvider(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['foo', 'foo'],
|
|
||||||
['abc123', 'abc123'],
|
|
||||||
['val"ue', '"val\"ue"'],
|
|
||||||
['val\'ue', '"val\'ue"'],
|
|
||||||
['my test value', '"my test value"'],
|
|
||||||
['mysql_p@assword', '"mysql_p@assword"'],
|
|
||||||
['mysql_p#assword', '"mysql_p#assword"'],
|
|
||||||
['mysql p@$$word', '"mysql p@$$word"'],
|
|
||||||
['mysql p%word', '"mysql p%word"'],
|
|
||||||
['mysql p#word', '"mysql p#word"'],
|
|
||||||
['abc_@#test', '"abc_@#test"'],
|
|
||||||
['test 123 $$$', '"test 123 $$$"'],
|
|
||||||
['#password%', '"#password%"'],
|
|
||||||
['$pass ', '"$pass "'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FooClass
|
|
||||||
{
|
|
||||||
use EnvironmentWriterTrait;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user