mirror of
https://github.com/pelican-dev/panel.git
synced 2025-12-08 18:30:15 +01:00
Add server icons (#1906)
Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
This commit is contained in:
parent
a195b56f93
commit
65bb99e2b0
@ -29,6 +29,7 @@ use Exception;
|
|||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Actions\ActionGroup;
|
use Filament\Actions\ActionGroup;
|
||||||
use Filament\Forms\Components\CodeEditor;
|
use Filament\Forms\Components\CodeEditor;
|
||||||
|
use Filament\Forms\Components\FileUpload;
|
||||||
use Filament\Forms\Components\Hidden;
|
use Filament\Forms\Components\Hidden;
|
||||||
use Filament\Forms\Components\KeyValue;
|
use Filament\Forms\Components\KeyValue;
|
||||||
use Filament\Forms\Components\Repeater;
|
use Filament\Forms\Components\Repeater;
|
||||||
@ -38,12 +39,14 @@ use Filament\Forms\Components\Textarea;
|
|||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Forms\Components\ToggleButtons;
|
use Filament\Forms\Components\ToggleButtons;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Resources\Pages\EditRecord;
|
use Filament\Resources\Pages\EditRecord;
|
||||||
use Filament\Schemas\Components\Actions;
|
use Filament\Schemas\Components\Actions;
|
||||||
use Filament\Schemas\Components\Component;
|
use Filament\Schemas\Components\Component;
|
||||||
use Filament\Schemas\Components\Fieldset;
|
use Filament\Schemas\Components\Fieldset;
|
||||||
use Filament\Schemas\Components\Grid;
|
use Filament\Schemas\Components\Grid;
|
||||||
|
use Filament\Schemas\Components\Image;
|
||||||
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
|
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
|
||||||
use Filament\Schemas\Components\Tabs;
|
use Filament\Schemas\Components\Tabs;
|
||||||
use Filament\Schemas\Components\Tabs\Tab;
|
use Filament\Schemas\Components\Tabs\Tab;
|
||||||
@ -51,6 +54,7 @@ use Filament\Schemas\Components\Utilities\Get;
|
|||||||
use Filament\Schemas\Components\Utilities\Set;
|
use Filament\Schemas\Components\Utilities\Set;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Support\Enums\Alignment;
|
use Filament\Support\Enums\Alignment;
|
||||||
|
use Filament\Support\Enums\IconSize;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
@ -94,90 +98,265 @@ class EditServer extends EditRecord
|
|||||||
->label(trans('admin/server.tabs.information'))
|
->label(trans('admin/server.tabs.information'))
|
||||||
->icon('tabler-info-circle')
|
->icon('tabler-info-circle')
|
||||||
->schema([
|
->schema([
|
||||||
TextInput::make('name')
|
Grid::make()
|
||||||
->prefixIcon('tabler-server')
|
->columns(2)
|
||||||
->label(trans('admin/server.name'))
|
->columnStart(1)
|
||||||
->suffixAction(Action::make('random')
|
->schema([
|
||||||
->icon('tabler-dice-' . random_int(1, 6))
|
Image::make('', 'icon')
|
||||||
->action(function (Set $set, Get $get) {
|
->hidden(fn ($record) => !$record->icon && !$record->egg->image)
|
||||||
$egg = Egg::find($get('egg_id'));
|
->url(fn ($record) => $record->icon ?: $record->egg->image)
|
||||||
$prefix = $egg ? str($egg->name)->lower()->kebab() . '-' : '';
|
->tooltip(fn ($record) => $record->icon ? '' : trans('server/setting.server_info.icon.tooltip'))
|
||||||
|
->columnSpan(2)
|
||||||
$word = (new RandomWordService())->word();
|
->alignJustify(),
|
||||||
|
Action::make('uploadIcon')
|
||||||
$set('name', $prefix . $word);
|
->iconButton()->iconSize(IconSize::Large)
|
||||||
}))
|
->icon('tabler-photo-up')
|
||||||
->columnSpan([
|
->modal()
|
||||||
'default' => 2,
|
->modalSubmitActionLabel(trans('server/setting.server_info.icon.upload'))
|
||||||
'sm' => 1,
|
|
||||||
'md' => 2,
|
|
||||||
'lg' => 3,
|
|
||||||
])
|
|
||||||
->required()
|
|
||||||
->maxLength(255),
|
|
||||||
Select::make('owner_id')
|
|
||||||
->prefixIcon('tabler-user')
|
|
||||||
->label(trans('admin/server.owner'))
|
|
||||||
->columnSpan([
|
|
||||||
'default' => 2,
|
|
||||||
'sm' => 1,
|
|
||||||
'md' => 2,
|
|
||||||
'lg' => 2,
|
|
||||||
])
|
|
||||||
->relationship('user', 'username')
|
|
||||||
->searchable(['username', 'email'])
|
|
||||||
->getOptionLabelFromRecordUsing(fn (User $user) => "$user->username ($user->email)")
|
|
||||||
->preload()
|
|
||||||
->required(),
|
|
||||||
ToggleButtons::make('condition')
|
|
||||||
->label(trans('admin/server.server_status'))
|
|
||||||
->formatStateUsing(fn (Server $server) => $server->condition)
|
|
||||||
->options(fn ($state) => [$state->value => $state->getLabel()])
|
|
||||||
->colors(fn ($state) => [$state->value => $state->getColor()])
|
|
||||||
->icons(fn ($state) => [$state->value => $state->getIcon()])
|
|
||||||
->stateCast(new ServerConditionStateCast())
|
|
||||||
->columnSpan([
|
|
||||||
'default' => 2,
|
|
||||||
'sm' => 1,
|
|
||||||
'md' => 1,
|
|
||||||
'lg' => 1,
|
|
||||||
])
|
|
||||||
->hintAction(
|
|
||||||
Action::make('view_install_log')
|
|
||||||
->label(trans('admin/server.view_install_log'))
|
|
||||||
//->visible(fn (Server $server) => $server->isFailedInstall())
|
|
||||||
->modalHeading('')
|
|
||||||
->modalSubmitAction(false)
|
|
||||||
->modalFooterActionsAlignment(Alignment::Right)
|
|
||||||
->modalCancelActionLabel(trans('filament::components/modal.actions.close.label'))
|
|
||||||
->schema([
|
->schema([
|
||||||
CodeEditor::make('logs')
|
Tabs::make()->tabs([
|
||||||
->hiddenLabel()
|
Tab::make(trans('admin/egg.import.url'))
|
||||||
->formatStateUsing(function (Server $server, DaemonServerRepository $serverRepository) {
|
->schema([
|
||||||
try {
|
Hidden::make('base64Image'),
|
||||||
$logs = $serverRepository->setServer($server)->getInstallLogs();
|
TextInput::make('image_url')
|
||||||
|
->label(trans('admin/egg.import.image_url'))
|
||||||
|
->reactive()
|
||||||
|
->autocomplete(false)
|
||||||
|
->debounce(500)
|
||||||
|
->afterStateUpdated(function ($state, Set $set) {
|
||||||
|
if (!$state) {
|
||||||
|
$set('image_url_error', null);
|
||||||
|
|
||||||
return mb_convert_encoding($logs, 'UTF-8', ['UTF-8', 'UTF-16', 'ISO-8859-1', 'Windows-1252', 'ASCII']);
|
return;
|
||||||
} catch (ConnectionException) {
|
}
|
||||||
Notification::make()
|
|
||||||
->title(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
|
||||||
->body(trans('admin/server.notifications.log_failed'))
|
|
||||||
->color('warning')
|
|
||||||
->warning()
|
|
||||||
->send();
|
|
||||||
} catch (Exception) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
try {
|
||||||
}),
|
if (!in_array(parse_url($state, PHP_URL_SCHEME), ['http', 'https'], true)) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filter_var($state, FILTER_VALIDATE_URL)) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedExtensions = [
|
||||||
|
'png' => 'image/png',
|
||||||
|
'jpg' => 'image/jpeg',
|
||||||
|
'jpeg' => 'image/jpeg',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'webp' => 'image/webp',
|
||||||
|
'svg' => 'image/svg+xml',
|
||||||
|
];
|
||||||
|
|
||||||
|
$extension = strtolower(pathinfo(parse_url($state, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
if (!array_key_exists($extension, $allowedExtensions)) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', array_keys($allowedExtensions))]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = parse_url($state, PHP_URL_HOST);
|
||||||
|
$ip = gethostbyname($host);
|
||||||
|
|
||||||
|
if (
|
||||||
|
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
|
||||||
|
) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.no_local_ip'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = stream_context_create([
|
||||||
|
'http' => ['timeout' => 3],
|
||||||
|
'https' => [
|
||||||
|
'timeout' => 3,
|
||||||
|
'verify_peer' => true,
|
||||||
|
'verify_peer_name' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$imageContent = @file_get_contents($state, false, $context, 0, 262144); //256KB
|
||||||
|
|
||||||
|
if (!$imageContent) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.image_error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$mimeType = $allowedExtensions[$extension];
|
||||||
|
$base64 = 'data:' . $mimeType . ';base64,' . base64_encode($imageContent);
|
||||||
|
|
||||||
|
$set('base64Image', $base64);
|
||||||
|
$set('image_url_error', null);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$set('image_url_error', $e->getMessage());
|
||||||
|
$set('base64Image', null);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TextEntry::make('image_url_error')
|
||||||
|
->hiddenLabel()
|
||||||
|
->visible(fn (Get $get) => $get('image_url_error') !== null)
|
||||||
|
->afterStateHydrated(fn (Get $get) => $get('image_url_error')),
|
||||||
|
Image::make(fn (Get $get) => $get('image_url'), '')
|
||||||
|
->imageSize(150)
|
||||||
|
->visible(fn (Get $get) => $get('image_url') && !$get('image_url_error'))
|
||||||
|
->alignCenter(),
|
||||||
|
]),
|
||||||
|
Tab::make(trans('admin/egg.import.file'))
|
||||||
|
->schema([
|
||||||
|
FileUpload::make('image')
|
||||||
|
->hiddenLabel()
|
||||||
|
->previewable()
|
||||||
|
->openable(false)
|
||||||
|
->downloadable(false)
|
||||||
|
->maxSize(256)
|
||||||
|
->maxFiles(1)
|
||||||
|
->columnSpanFull()
|
||||||
|
->alignCenter()
|
||||||
|
->imageEditor()
|
||||||
|
->image()
|
||||||
|
->saveUploadedFileUsing(function ($file, Set $set) {
|
||||||
|
$base64 = "data:{$file->getMimeType()};base64,". base64_encode(file_get_contents($file->getRealPath()));
|
||||||
|
$set('base64Image', $base64);
|
||||||
|
|
||||||
|
return $base64;
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
),
|
->action(function (array $data, $record): void {
|
||||||
|
$base64 = $data['base64Image'] ?? null;
|
||||||
|
|
||||||
|
if (empty($base64) && !empty($data['image'])) {
|
||||||
|
$base64 = $data['image'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($base64)) {
|
||||||
|
$record->update([
|
||||||
|
'icon' => $base64,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('server/setting.server_info.icon.updated'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$record->refresh();
|
||||||
|
} else {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/egg.import.no_image'))
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Action::make('deleteIcon')
|
||||||
|
->visible(fn ($record) => $record->icon)
|
||||||
|
->label('')
|
||||||
|
->icon('tabler-trash')
|
||||||
|
->iconButton()->iconSize(IconSize::Large)
|
||||||
|
->color('danger')
|
||||||
|
->action(function ($record) {
|
||||||
|
$record->update([
|
||||||
|
'icon' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('server/setting.server_info.icon.deleted'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$record->refresh();
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
Grid::make()
|
||||||
|
->columns(3)
|
||||||
|
->columnStart(2)
|
||||||
|
->columnSpan([
|
||||||
|
'default' => 2,
|
||||||
|
'sm' => 2,
|
||||||
|
'md' => 3,
|
||||||
|
'lg' => 5,
|
||||||
|
])
|
||||||
|
->schema([
|
||||||
|
TextInput::make('name')
|
||||||
|
->prefixIcon('tabler-server')
|
||||||
|
->label(trans('admin/server.name'))
|
||||||
|
->suffixAction(Action::make('random')
|
||||||
|
->icon('tabler-dice-' . random_int(1, 6))
|
||||||
|
->action(function (Set $set, Get $get) {
|
||||||
|
$egg = Egg::find($get('egg_id'));
|
||||||
|
$prefix = $egg ? str($egg->name)->lower()->kebab() . '-' : '';
|
||||||
|
|
||||||
|
$word = (new RandomWordService())->word();
|
||||||
|
|
||||||
|
$set('name', $prefix . $word);
|
||||||
|
}))
|
||||||
|
->columnSpan([
|
||||||
|
'default' => 2,
|
||||||
|
'sm' => 1,
|
||||||
|
'md' => 2,
|
||||||
|
'lg' => 3,
|
||||||
|
])
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
Select::make('owner_id')
|
||||||
|
->prefixIcon('tabler-user')
|
||||||
|
->label(trans('admin/server.owner'))
|
||||||
|
->columnSpan([
|
||||||
|
'default' => 2,
|
||||||
|
'sm' => 1,
|
||||||
|
'md' => 2,
|
||||||
|
'lg' => 2,
|
||||||
|
])
|
||||||
|
->relationship('user', 'username')
|
||||||
|
->searchable(['username', 'email'])
|
||||||
|
->getOptionLabelFromRecordUsing(fn (User $user) => "$user->username ($user->email)")
|
||||||
|
->preload()
|
||||||
|
->required(),
|
||||||
|
ToggleButtons::make('condition')
|
||||||
|
->label(trans('admin/server.server_status'))
|
||||||
|
->formatStateUsing(fn (Server $server) => $server->condition)
|
||||||
|
->options(fn ($state) => [$state->value => $state->getLabel()])
|
||||||
|
->colors(fn ($state) => [$state->value => $state->getColor()])
|
||||||
|
->icons(fn ($state) => [$state->value => $state->getIcon()])
|
||||||
|
->stateCast(new ServerConditionStateCast())
|
||||||
|
->columnSpan([
|
||||||
|
'default' => 2,
|
||||||
|
'sm' => 1,
|
||||||
|
'md' => 1,
|
||||||
|
'lg' => 1,
|
||||||
|
])
|
||||||
|
->hintAction(
|
||||||
|
Action::make('view_install_log')
|
||||||
|
->label(trans('admin/server.view_install_log'))
|
||||||
|
//->visible(fn (Server $server) => $server->isFailedInstall())
|
||||||
|
->modalHeading('')
|
||||||
|
->modalSubmitAction(false)
|
||||||
|
->modalFooterActionsAlignment(Alignment::Right)
|
||||||
|
->modalCancelActionLabel(trans('filament::components/modal.actions.close.label'))
|
||||||
|
->schema([
|
||||||
|
CodeEditor::make('logs')
|
||||||
|
->hiddenLabel()
|
||||||
|
->formatStateUsing(function (Server $server, DaemonServerRepository $serverRepository) {
|
||||||
|
try {
|
||||||
|
$logs = $serverRepository->setServer($server)->getInstallLogs();
|
||||||
|
|
||||||
|
return mb_convert_encoding($logs, 'UTF-8', ['UTF-8', 'UTF-16', 'ISO-8859-1', 'Windows-1252', 'ASCII']);
|
||||||
|
} catch (ConnectionException) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
||||||
|
->body(trans('admin/server.notifications.log_failed'))
|
||||||
|
->color('warning')
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
} catch (Exception) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
),
|
||||||
|
]),
|
||||||
Textarea::make('description')
|
Textarea::make('description')
|
||||||
->label(trans('admin/server.description'))
|
->label(trans('admin/server.description'))
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
|
|
||||||
TextInput::make('uuid')
|
TextInput::make('uuid')
|
||||||
->label(trans('admin/server.uuid'))
|
->label(trans('admin/server.uuid'))
|
||||||
->copyable()
|
->copyable()
|
||||||
|
|||||||
@ -20,6 +20,7 @@ use Filament\Schemas\Components\Tabs\Tab;
|
|||||||
use Filament\Support\Enums\Alignment;
|
use Filament\Support\Enums\Alignment;
|
||||||
use Filament\Support\Enums\IconSize;
|
use Filament\Support\Enums\IconSize;
|
||||||
use Filament\Tables\Columns\Column;
|
use Filament\Tables\Columns\Column;
|
||||||
|
use Filament\Tables\Columns\ImageColumn;
|
||||||
use Filament\Tables\Columns\Layout\Stack;
|
use Filament\Tables\Columns\Layout\Stack;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Filters\SelectFilter;
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
@ -62,6 +63,10 @@ class ListServers extends ListRecords
|
|||||||
protected function tableColumns(): array
|
protected function tableColumns(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
ImageColumn::make('icon')
|
||||||
|
->label('')
|
||||||
|
->imageSize(46)
|
||||||
|
->state(fn (Server $server) => $server->icon ?: $server->egg->image),
|
||||||
TextColumn::make('condition')
|
TextColumn::make('condition')
|
||||||
->label(trans('server/dashboard.status'))
|
->label(trans('server/dashboard.status'))
|
||||||
->badge()
|
->badge()
|
||||||
|
|||||||
@ -8,14 +8,23 @@ use App\Models\Server;
|
|||||||
use App\Services\Servers\ReinstallServerService;
|
use App\Services\Servers\ReinstallServerService;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Forms\Components\FileUpload;
|
||||||
|
use Filament\Forms\Components\Hidden;
|
||||||
use Filament\Forms\Components\Textarea;
|
use Filament\Forms\Components\Textarea;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Infolists\Components\TextEntry;
|
use Filament\Infolists\Components\TextEntry;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Schemas\Components\Fieldset;
|
use Filament\Schemas\Components\Fieldset;
|
||||||
|
use Filament\Schemas\Components\Grid;
|
||||||
|
use Filament\Schemas\Components\Image;
|
||||||
use Filament\Schemas\Components\Section;
|
use Filament\Schemas\Components\Section;
|
||||||
|
use Filament\Schemas\Components\Tabs;
|
||||||
|
use Filament\Schemas\Components\Tabs\Tab;
|
||||||
|
use Filament\Schemas\Components\Utilities\Get;
|
||||||
|
use Filament\Schemas\Components\Utilities\Set;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Support\Enums\Alignment;
|
use Filament\Support\Enums\Alignment;
|
||||||
|
use Filament\Support\Enums\IconSize;
|
||||||
|
|
||||||
class Settings extends ServerFormPage
|
class Settings extends ServerFormPage
|
||||||
{
|
{
|
||||||
@ -29,51 +38,208 @@ class Settings extends ServerFormPage
|
|||||||
public function form(Schema $schema): Schema
|
public function form(Schema $schema): Schema
|
||||||
{
|
{
|
||||||
return parent::form($schema)
|
return parent::form($schema)
|
||||||
->columns(4)
|
|
||||||
->components([
|
->components([
|
||||||
Section::make(trans('server/setting.server_info.title'))
|
Section::make(trans('server/setting.server_info.title'))
|
||||||
->columnSpanFull()
|
->columnSpanFull()
|
||||||
->columns([
|
->columns([
|
||||||
'default' => 1,
|
'default' => 1,
|
||||||
'sm' => 2,
|
'sm' => 1,
|
||||||
'md' => 4,
|
'md' => 4,
|
||||||
'lg' => 6,
|
'lg' => 6,
|
||||||
])
|
])
|
||||||
->schema([
|
->schema([
|
||||||
Fieldset::make()
|
Fieldset::make()
|
||||||
->label(trans('server/setting.server_info.information'))
|
->label(trans('server/setting.server_info.information'))
|
||||||
->columnSpan([
|
->columnSpanFull()
|
||||||
'default' => 1,
|
|
||||||
'sm' => 2,
|
|
||||||
'md' => 2,
|
|
||||||
'lg' => 6,
|
|
||||||
])
|
|
||||||
->schema([
|
->schema([
|
||||||
TextInput::make('name')
|
Grid::make()
|
||||||
->label(trans('server/setting.server_info.name'))
|
->columns(2)
|
||||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_RENAME, $server))
|
->columnSpan(5)
|
||||||
->required()
|
->schema([
|
||||||
->columnSpan([
|
TextInput::make('name')
|
||||||
'default' => 1,
|
->columnStart(1)
|
||||||
'sm' => 2,
|
->columnSpanFull()
|
||||||
'md' => 2,
|
->label(trans('server/setting.server_info.name'))
|
||||||
'lg' => 6,
|
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_RENAME, $server))
|
||||||
])
|
->required()
|
||||||
->live(onBlur: true)
|
->live(onBlur: true)
|
||||||
->afterStateUpdated(fn ($state, Server $server) => $this->updateName($state, $server)),
|
->afterStateUpdated(fn ($state, Server $server) => $this->updateName($state, $server)),
|
||||||
Textarea::make('description')
|
Textarea::make('description')
|
||||||
->label(trans('server/setting.server_info.description'))
|
->columnStart(1)
|
||||||
->hidden(!config('panel.editable_server_descriptions'))
|
->columnSpanFull()
|
||||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_DESCRIPTION, $server))
|
->label(trans('server/setting.server_info.description'))
|
||||||
->columnSpan([
|
->hidden(!config('panel.editable_server_descriptions'))
|
||||||
'default' => 1,
|
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_DESCRIPTION, $server))
|
||||||
'sm' => 2,
|
->autosize()
|
||||||
'md' => 2,
|
->live(onBlur: true)
|
||||||
'lg' => 6,
|
->afterStateUpdated(fn ($state, Server $server) => $this->updateDescription($state ?? '', $server)),
|
||||||
])
|
]),
|
||||||
->autosize()
|
Grid::make()
|
||||||
->live(onBlur: true)
|
->columns(2)
|
||||||
->afterStateUpdated(fn ($state, Server $server) => $this->updateDescription($state ?? '', $server)),
|
->columnStart(6)
|
||||||
|
->schema([
|
||||||
|
Image::make('', 'icon')
|
||||||
|
->hidden(fn ($record) => !$record->icon && !$record->egg->image)
|
||||||
|
->url(fn ($record) => $record->icon ?: $record->egg->image)
|
||||||
|
->tooltip(fn ($record) => $record->icon ? '' : trans('server/setting.server_info.icon.tooltip'))
|
||||||
|
->columnSpan(2)
|
||||||
|
->alignJustify(),
|
||||||
|
Action::make('uploadIcon')
|
||||||
|
->iconButton()->iconSize(IconSize::Large)
|
||||||
|
->icon('tabler-photo-up')
|
||||||
|
->modal()
|
||||||
|
->modalSubmitActionLabel(trans('server/setting.server_info.icon.upload'))
|
||||||
|
->schema([
|
||||||
|
Tabs::make()->tabs([
|
||||||
|
Tab::make(trans('admin/egg.import.url'))
|
||||||
|
->schema([
|
||||||
|
Hidden::make('base64Image'),
|
||||||
|
TextInput::make('image_url')
|
||||||
|
->label(trans('admin/egg.import.image_url'))
|
||||||
|
->reactive()
|
||||||
|
->autocomplete(false)
|
||||||
|
->debounce(500)
|
||||||
|
->afterStateUpdated(function ($state, Set $set) {
|
||||||
|
if (!$state) {
|
||||||
|
$set('image_url_error', null);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!in_array(parse_url($state, PHP_URL_SCHEME), ['http', 'https'], true)) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filter_var($state, FILTER_VALIDATE_URL)) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.invalid_url'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedExtensions = [
|
||||||
|
'png' => 'image/png',
|
||||||
|
'jpg' => 'image/jpeg',
|
||||||
|
'jpeg' => 'image/jpeg',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'webp' => 'image/webp',
|
||||||
|
'svg' => 'image/svg+xml',
|
||||||
|
];
|
||||||
|
|
||||||
|
$extension = strtolower(pathinfo(parse_url($state, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
if (!array_key_exists($extension, $allowedExtensions)) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.unsupported_format', ['format' => implode(', ', array_keys($allowedExtensions))]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = parse_url($state, PHP_URL_HOST);
|
||||||
|
$ip = gethostbyname($host);
|
||||||
|
|
||||||
|
if (
|
||||||
|
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false
|
||||||
|
) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.no_local_ip'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = stream_context_create([
|
||||||
|
'http' => ['timeout' => 3],
|
||||||
|
'https' => [
|
||||||
|
'timeout' => 3,
|
||||||
|
'verify_peer' => true,
|
||||||
|
'verify_peer_name' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$imageContent = @file_get_contents($state, false, $context, 0, 262144); //256KB
|
||||||
|
|
||||||
|
if (!$imageContent) {
|
||||||
|
throw new \Exception(trans('admin/egg.import.image_error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$mimeType = $allowedExtensions[$extension];
|
||||||
|
$base64 = 'data:' . $mimeType . ';base64,' . base64_encode($imageContent);
|
||||||
|
|
||||||
|
$set('base64Image', $base64);
|
||||||
|
$set('image_url_error', null);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$set('image_url_error', $e->getMessage());
|
||||||
|
$set('base64Image', null);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
TextEntry::make('image_url_error')
|
||||||
|
->hiddenLabel()
|
||||||
|
->visible(fn (Get $get) => $get('image_url_error') !== null)
|
||||||
|
->afterStateHydrated(fn (Get $get) => $get('image_url_error')),
|
||||||
|
Image::make(fn (Get $get) => $get('image_url'), '')
|
||||||
|
->imageSize(150)
|
||||||
|
->visible(fn (Get $get) => $get('image_url') && !$get('image_url_error'))
|
||||||
|
->alignCenter(),
|
||||||
|
]),
|
||||||
|
Tab::make(trans('admin/egg.import.file'))
|
||||||
|
->schema([
|
||||||
|
FileUpload::make('image')
|
||||||
|
->hiddenLabel()
|
||||||
|
->previewable()
|
||||||
|
->openable(false)
|
||||||
|
->downloadable(false)
|
||||||
|
->maxSize(256)
|
||||||
|
->maxFiles(1)
|
||||||
|
->columnSpanFull()
|
||||||
|
->alignCenter()
|
||||||
|
->imageEditor()
|
||||||
|
->image()
|
||||||
|
->saveUploadedFileUsing(function ($file, Set $set) {
|
||||||
|
$base64 = "data:{$file->getMimeType()};base64,". base64_encode(file_get_contents($file->getRealPath()));
|
||||||
|
$set('base64Image', $base64);
|
||||||
|
|
||||||
|
return $base64;
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->action(function (array $data, $record): void {
|
||||||
|
$base64 = $data['base64Image'] ?? null;
|
||||||
|
|
||||||
|
if (empty($base64) && !empty($data['image'])) {
|
||||||
|
$base64 = $data['image'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($base64)) {
|
||||||
|
$record->update([
|
||||||
|
'icon' => $base64,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('server/setting.server_info.icon.updated'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$record->refresh();
|
||||||
|
} else {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/egg.import.no_image'))
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Action::make('deleteIcon')
|
||||||
|
->visible(fn ($record) => $record->icon)
|
||||||
|
->label('')
|
||||||
|
->icon('tabler-trash')
|
||||||
|
->iconButton()->iconSize(IconSize::Large)
|
||||||
|
->color('danger')
|
||||||
|
->action(function ($record) {
|
||||||
|
$record->update([
|
||||||
|
'icon' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('server/setting.server_info.icon.deleted'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$record->refresh();
|
||||||
|
}),
|
||||||
|
]),
|
||||||
TextInput::make('uuid')
|
TextInput::make('uuid')
|
||||||
->label(trans('server/setting.server_info.uuid'))
|
->label(trans('server/setting.server_info.uuid'))
|
||||||
->columnSpan([
|
->columnSpan([
|
||||||
@ -97,14 +263,14 @@ class Settings extends ServerFormPage
|
|||||||
->label(trans('server/setting.server_info.limits.title'))
|
->label(trans('server/setting.server_info.limits.title'))
|
||||||
->columnSpan([
|
->columnSpan([
|
||||||
'default' => 1,
|
'default' => 1,
|
||||||
'sm' => 2,
|
'sm' => 1,
|
||||||
'md' => 2,
|
'md' => 4,
|
||||||
'lg' => 6,
|
'lg' => 6,
|
||||||
])
|
])
|
||||||
->columns([
|
->columns([
|
||||||
'default' => 1,
|
'default' => 1,
|
||||||
'sm' => 1,
|
'sm' => 1,
|
||||||
'md' => 1,
|
'md' => 2,
|
||||||
'lg' => 3,
|
'lg' => 3,
|
||||||
])
|
])
|
||||||
->schema([
|
->schema([
|
||||||
|
|||||||
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Filament\Server\Pages\Console;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Filament\Support\Facades\FilamentView;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@ -12,68 +14,23 @@ class ServerEntry extends Component
|
|||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
{
|
{
|
||||||
return view('livewire.server-entry');
|
return view('livewire.server-entry', ['component' => $this]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function placeholder(): string
|
public function placeholder(): View
|
||||||
{
|
{
|
||||||
return <<<'HTML'
|
return view('livewire.server-entry-placeholder', ['server' => $this->server, 'component' => $this]);
|
||||||
<div class="relative cursor-pointer" x-on:click="window.location.href = '{{ \App\Filament\Server\Pages\Console::getUrl(panel: 'server', tenant: $server) }}'">
|
}
|
||||||
<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">
|
public function redirectUrl(?bool $shouldOpenUrlInNewTab = false): string
|
||||||
@if($server->egg->image)
|
{
|
||||||
<div style="
|
$url = Console::getUrl(panel: 'server', tenant: $this->server);
|
||||||
position: absolute;
|
$target = $shouldOpenUrlInNewTab ? '_blank' : '_self';
|
||||||
inset: 0;
|
|
||||||
background: url('{{ $server->egg->image }}') right no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
opacity: 0.20;
|
|
||||||
max-width: 680px;
|
|
||||||
max-height: 140px;
|
|
||||||
"></div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div class="flex items-center mb-5 gap-2">
|
if (!$shouldOpenUrlInNewTab && FilamentView::hasSpaMode($url)) {
|
||||||
<x-filament::loading-indicator class="h-6 w-6" />
|
return sprintf("Livewire.navigate('%s')", $url);
|
||||||
<h2 class="text-xl font-bold">
|
}
|
||||||
{{ $server->name }}
|
|
||||||
<span class="dark:text-gray-400">
|
|
||||||
({{ trans('server/dashboard.loading') }})
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-between text-center items-center gap-4">
|
return sprintf("window.open('%s', '%s')", $url, $target);
|
||||||
<div>
|
|
||||||
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.cpu') }}</p>
|
|
||||||
<p class="text-md font-semibold">{{ format_number(0, precision: 2) . '%' }}</p>
|
|
||||||
<hr class="p-0.5">
|
|
||||||
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::CPULimit) }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.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(\App\Enums\ServerResourceType::MemoryLimit) }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.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(\App\Enums\ServerResourceType::DiskLimit) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:block">
|
|
||||||
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.network') }}</p>
|
|
||||||
<hr class="p-0.5">
|
|
||||||
<p class="text-md font-semibold">{{ $server->allocation?->address ?? trans('server/dashboard.none') }} </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
HTML;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use App\Services\Subusers\SubuserDeletionService;
|
|||||||
use App\Traits\HasValidation;
|
use App\Traits\HasValidation;
|
||||||
use Carbon\CarbonInterface;
|
use Carbon\CarbonInterface;
|
||||||
use Database\Factories\ServerFactory;
|
use Database\Factories\ServerFactory;
|
||||||
|
use Filament\Models\Contracts\HasAvatar;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
@ -55,6 +56,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||||||
* @property int $egg_id
|
* @property int $egg_id
|
||||||
* @property string $startup
|
* @property string $startup
|
||||||
* @property string $image
|
* @property string $image
|
||||||
|
* @property string|null $icon
|
||||||
* @property int|null $allocation_limit
|
* @property int|null $allocation_limit
|
||||||
* @property int|null $database_limit
|
* @property int|null $database_limit
|
||||||
* @property int|null $backup_limit
|
* @property int|null $backup_limit
|
||||||
@ -70,7 +72,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||||||
* @property int|null $backups_count
|
* @property int|null $backups_count
|
||||||
* @property Collection|Database[] $databases
|
* @property Collection|Database[] $databases
|
||||||
* @property int|null $databases_count
|
* @property int|null $databases_count
|
||||||
* @property Egg|null $egg
|
* @property Egg $egg
|
||||||
* @property Collection|Mount[] $mounts
|
* @property Collection|Mount[] $mounts
|
||||||
* @property int|null $mounts_count
|
* @property int|null $mounts_count
|
||||||
* @property Node $node
|
* @property Node $node
|
||||||
@ -129,7 +131,7 @@ use Psr\Http\Message\ResponseInterface;
|
|||||||
* @method static Builder|Server wherePorts($value)
|
* @method static Builder|Server wherePorts($value)
|
||||||
* @method static Builder|Server whereUuidShort($value)
|
* @method static Builder|Server whereUuidShort($value)
|
||||||
*/
|
*/
|
||||||
class Server extends Model implements Validatable
|
class Server extends Model implements HasAvatar, Validatable
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use HasValidation;
|
use HasValidation;
|
||||||
@ -181,6 +183,7 @@ class Server extends Model implements Validatable
|
|||||||
'startup' => ['required', 'string'],
|
'startup' => ['required', 'string'],
|
||||||
'skip_scripts' => ['sometimes', 'boolean'],
|
'skip_scripts' => ['sometimes', 'boolean'],
|
||||||
'image' => ['required', 'string', 'max:255'],
|
'image' => ['required', 'string', 'max:255'],
|
||||||
|
'icon' => ['sometimes', 'nullable', 'string'],
|
||||||
'database_limit' => ['present', 'nullable', 'integer', 'min:0'],
|
'database_limit' => ['present', 'nullable', 'integer', 'min:0'],
|
||||||
'allocation_limit' => ['sometimes', 'nullable', 'integer', 'min:0'],
|
'allocation_limit' => ['sometimes', 'nullable', 'integer', 'min:0'],
|
||||||
'backup_limit' => ['present', 'nullable', 'integer', 'min:0'],
|
'backup_limit' => ['present', 'nullable', 'integer', 'min:0'],
|
||||||
@ -513,4 +516,10 @@ class Server extends Model implements Validatable
|
|||||||
get: fn () => $this->status ?? $this->retrieveStatus(),
|
get: fn () => $this->status ?? $this->retrieveStatus(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFilamentAvatarUrl(): ?string
|
||||||
|
{
|
||||||
|
return $this->icon ?? $this->egg->image;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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('servers', function (Blueprint $table) {
|
||||||
|
$table->longText('icon')->nullable()->after('image');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('icon');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -14,6 +14,12 @@ return [
|
|||||||
'uuid' => 'Server UUID',
|
'uuid' => 'Server UUID',
|
||||||
'uuid_short' => 'Server ID',
|
'uuid_short' => 'Server ID',
|
||||||
'node_name' => 'Node Name',
|
'node_name' => 'Node Name',
|
||||||
|
'icon' => [
|
||||||
|
'upload' => 'Upload Icon',
|
||||||
|
'tooltip' => 'Using Egg Icon',
|
||||||
|
'updated' => 'Server icon updated',
|
||||||
|
'deleted' => 'Server icon deleted',
|
||||||
|
],
|
||||||
'limits' => [
|
'limits' => [
|
||||||
'title' => 'Limits',
|
'title' => 'Limits',
|
||||||
'unlimited' => 'Unlimited',
|
'unlimited' => 'Unlimited',
|
||||||
|
|||||||
69
resources/views/livewire/server-entry-placeholder.blade.php
Normal file
69
resources/views/livewire/server-entry-placeholder.blade.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
@php
|
||||||
|
$backgroundImage = $server->icon ?? $server->egg->image;
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="relative cursor-pointer"
|
||||||
|
x-on:click="{{ $component->redirectUrl() }}"
|
||||||
|
x-on:auxclick.prevent="if ($event.button === 1) {{ $component->redirectUrl(true) }}">
|
||||||
|
<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">
|
||||||
|
@if($backgroundImage)
|
||||||
|
<div style="
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: url('{{ $backgroundImage }}') right no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
opacity: 0.20;
|
||||||
|
max-width: 680px;
|
||||||
|
max-height: 140px;
|
||||||
|
"></div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div @class([
|
||||||
|
'flex items-center gap-2',
|
||||||
|
'mb-5' => !$server->description,
|
||||||
|
])>
|
||||||
|
|
||||||
|
<x-filament::loading-indicator class="h-6 w-6" />
|
||||||
|
<h2 class="text-xl font-bold">
|
||||||
|
{{ $server->name }}
|
||||||
|
<span class="dark:text-gray-400">({{ trans('server/dashboard.loading') }})</span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($server->description)
|
||||||
|
<div class="text-left mb-1 ml-4 pl-4">
|
||||||
|
<p class="text-base dark:text-gray-400">{{ Str::limit($server->description, 40, preserveWords: true) }}</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex justify-between text-center items-center gap-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.cpu') }}</p>
|
||||||
|
<p class="text-md font-semibold">{{ format_number(0, precision: 2) . '%' }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::CPULimit) }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.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(\App\Enums\ServerResourceType::MemoryLimit) }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.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(\App\Enums\ServerResourceType::DiskLimit) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:block">
|
||||||
|
<p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.network') }}</p>
|
||||||
|
<hr class="p-0.5">
|
||||||
|
<p class="text-md font-semibold">{{ $server->allocation?->address ?? trans('server/dashboard.none') }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@ -1,25 +1,27 @@
|
|||||||
@php
|
@php
|
||||||
$actiongroup = \App\Filament\App\Resources\Servers\Pages\ListServers::getPowerActionGroup()->record($server);
|
$actiongroup = \App\Filament\App\Resources\Servers\Pages\ListServers::getPowerActionGroup()->record($server);
|
||||||
|
$backgroundImage = $server->icon ?? $server->egg->image;
|
||||||
@endphp
|
@endphp
|
||||||
<div wire:poll.15s
|
<div wire:poll.15s
|
||||||
class="relative cursor-pointer"
|
class="relative cursor-pointer"
|
||||||
x-on:click="window.location.href = '{{ \App\Filament\Server\Pages\Console::getUrl(panel: 'server', tenant: $server) }}'">
|
x-on:click="{{ $component->redirectUrl() }}"
|
||||||
|
x-on:auxclick.prevent="if ($event.button === 1) {{ $component->redirectUrl(true) }}">
|
||||||
|
|
||||||
<div class="absolute left-0 top-1 bottom-0 w-1 rounded-lg"
|
<div class="absolute left-0 top-1 bottom-0 w-1 rounded-lg"
|
||||||
style="background-color: {{ $server->condition->getColor(true) }};">
|
style="background-color: {{ $server->condition->getColor(true) }};">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 dark:bg-gray-800 dark:text-white rounded-lg overflow-hidden p-3">
|
<div class="flex-1 dark:bg-gray-800 dark:text-white rounded-lg overflow-hidden p-3">
|
||||||
@if($server->egg->image)
|
@if($backgroundImage)
|
||||||
<div style="
|
<div style="
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: url('{{ $server->egg->image }}') right no-repeat;
|
background: url('{{ $backgroundImage }}') right no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
opacity: 0.20;
|
opacity: 0.20;
|
||||||
max-width: 680px;
|
max-width: 680px;
|
||||||
max-height: 140px;
|
max-height: 140px;
|
||||||
"></div>
|
"></div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div @class([
|
<div @class([
|
||||||
@ -49,9 +51,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if ($server->description)
|
@if ($server->description)
|
||||||
<div class="text-left mb-1 ml-4 pl-4">
|
<div class="text-left mb-1 ml-4 pl-4">
|
||||||
<p class="text-base text-gray-400">{{ Str::limit($server->description, 40, preserveWords: true) }}</p>
|
<p class="text-base dark:text-gray-400">{{ Str::limit($server->description, 40, preserveWords: true) }}</p>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="flex justify-between text-center items-center gap-4">
|
<div class="flex justify-between text-center items-center gap-4">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user