mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-05 07:52:34 +00:00
- 2.x - sites settings
- tags - source-control soft deletes
This commit is contained in:
@ -49,6 +49,16 @@ public function getSecondSubNavigation(): array
|
||||
]));
|
||||
}
|
||||
|
||||
if (Settings::canAccess()) {
|
||||
$items[] = NavigationItem::make(Settings::getNavigationLabel())
|
||||
->icon('heroicon-o-wrench-screwdriver')
|
||||
->isActiveWhen(fn () => request()->routeIs(Settings::getRouteName()))
|
||||
->url(Settings::getUrl(parameters: [
|
||||
'server' => $this->server,
|
||||
'site' => $this->site,
|
||||
]));
|
||||
}
|
||||
|
||||
return [
|
||||
NavigationGroup::make()
|
||||
->items($items),
|
||||
|
77
app/Web/Pages/Servers/Sites/Settings.php
Normal file
77
app/Web/Pages/Servers/Sites/Settings.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Web\Pages\Servers\Sites;
|
||||
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use App\Web\Fields\CodeEditorField;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
class Settings extends Page
|
||||
{
|
||||
protected static ?string $slug = 'servers/{server}/sites/{site}/settings';
|
||||
|
||||
protected static ?string $title = 'Settings';
|
||||
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()?->can('update', [static::getSiteFromRoute(), static::getServerFromRoute()]) ?? false;
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
[Widgets\SiteDetails::class, ['site' => $this->site]],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->vhostAction(),
|
||||
$this->deleteAction(),
|
||||
];
|
||||
}
|
||||
|
||||
private function deleteAction(): Action
|
||||
{
|
||||
return DeleteAction::make()
|
||||
->icon('heroicon-o-trash')
|
||||
->record($this->server)
|
||||
->modalHeading('Delete Site')
|
||||
->modalDescription('Once your site is deleted, all of its resources and data will be permanently deleted and can\'t be restored');
|
||||
}
|
||||
|
||||
private function vhostAction(): Action
|
||||
{
|
||||
return Action::make('vhost')
|
||||
->color('gray')
|
||||
->icon('si-nginx')
|
||||
->label('VHost')
|
||||
->modalSubmitActionLabel('Save')
|
||||
->form([
|
||||
CodeEditorField::make('vhost')
|
||||
->formatStateUsing(function () {
|
||||
/** @var Webserver $handler */
|
||||
$handler = $this->server->webserver()->handler();
|
||||
|
||||
return $handler->getVhost($this->site);
|
||||
})
|
||||
->rules(['required']),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
/** @var Webserver $handler */
|
||||
$handler = $this->server->webserver()->handler();
|
||||
$handler->updateVHost($this->site, false, $data['vhost']);
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('VHost updated!')
|
||||
->send();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -68,6 +68,12 @@ public function getWidgets(): array
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->site->isReady()) {
|
||||
if (in_array(SiteFeature::DEPLOYMENT, $this->site->type()->supportedFeatures())) {
|
||||
$widgets[] = [Widgets\DeploymentsList::class, ['site' => $this->site]];
|
||||
}
|
||||
}
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
@ -118,6 +124,8 @@ private function deployAction(): Action
|
||||
->success()
|
||||
->title('Deployment started!')
|
||||
->send();
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
84
app/Web/Pages/Servers/Sites/Widgets/DeploymentsList.php
Normal file
84
app/Web/Pages/Servers/Sites/Widgets/DeploymentsList.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Web\Pages\Servers\Sites\Widgets;
|
||||
|
||||
use App\Models\Deployment;
|
||||
use App\Models\Site;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
class DeploymentsList extends Widget
|
||||
{
|
||||
public Site $site;
|
||||
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Deployment::query()->where('site_id', $this->site->id);
|
||||
}
|
||||
|
||||
protected function applySortingToTableQuery(Builder $query): Builder
|
||||
{
|
||||
return $query->latest('created_at');
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
TextColumn::make('commit_data')
|
||||
->label('Commit')
|
||||
->url(fn (Deployment $record) => $record->commit_data['url'] ?? '#')
|
||||
->openUrlInNewTab()
|
||||
->formatStateUsing(fn (Deployment $record) => $record->commit_data['message'] ?? 'No message')
|
||||
->tooltip(fn (Deployment $record) => $record->commit_data['message'] ?? 'No message')
|
||||
->limit(50)
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->formatStateUsing(fn (Deployment $record) => $record->created_at_by_timezone)
|
||||
->sortable(),
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(fn (Deployment $record) => Deployment::$statusColors[$record->status])
|
||||
->searchable()
|
||||
->sortable(),
|
||||
];
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query($this->getTableQuery())
|
||||
->columns($this->getTableColumns())
|
||||
->heading('Deployments')
|
||||
->actions([
|
||||
Action::make('view')
|
||||
->hiddenLabel()
|
||||
->tooltip('View')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn ($record) => auth()->user()->can('view', $record->log))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (Deployment $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => $record->log?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close'),
|
||||
Action::make('download')
|
||||
->hiddenLabel()
|
||||
->tooltip('Download')
|
||||
->color('gray')
|
||||
->icon('heroicon-o-archive-box-arrow-down')
|
||||
->authorize(fn ($record) => auth()->user()->can('view', $record->log))
|
||||
->action(fn (Deployment $record) => $record->log?->download()),
|
||||
]);
|
||||
}
|
||||
}
|
182
app/Web/Pages/Servers/Sites/Widgets/SiteDetails.php
Normal file
182
app/Web/Pages/Servers/Sites/Widgets/SiteDetails.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace App\Web\Pages\Servers\Sites\Widgets;
|
||||
|
||||
use App\Actions\Site\UpdateAliases;
|
||||
use App\Actions\Site\UpdatePHPVersion;
|
||||
use App\Actions\Site\UpdateSourceControl;
|
||||
use App\Models\Site;
|
||||
use App\Models\SourceControl;
|
||||
use App\Web\Pages\Settings\SourceControls\Actions\Create;
|
||||
use App\Web\Pages\Settings\Tags\Actions\EditTags;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Infolists\Components\Actions\Action;
|
||||
use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Infolists\Concerns\InteractsWithInfolists;
|
||||
use Filament\Infolists\Contracts\HasInfolists;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class SiteDetails extends Widget implements HasForms, HasInfolists
|
||||
{
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected static string $view = 'web.components.infolist';
|
||||
|
||||
public Site $site;
|
||||
|
||||
public function infolist(Infolist $infolist): Infolist
|
||||
{
|
||||
return $infolist
|
||||
->schema([
|
||||
Section::make()
|
||||
->heading('Site Details')
|
||||
->description('More details about your site')
|
||||
->columns(1)
|
||||
->schema([
|
||||
TextEntry::make('id')
|
||||
->label('ID')
|
||||
->inlineLabel()
|
||||
->hintIcon('heroicon-o-information-circle')
|
||||
->hintIconTooltip('Site unique identifier to use in the API'),
|
||||
TextEntry::make('created_at')
|
||||
->label('Created At')
|
||||
->formatStateUsing(fn ($record) => $record->created_at_by_timezone)
|
||||
->inlineLabel(),
|
||||
TextEntry::make('type')
|
||||
->extraAttributes(['class' => 'capitalize'])
|
||||
->inlineLabel(),
|
||||
TextEntry::make('tags.*')
|
||||
->default('No tags')
|
||||
->formatStateUsing(fn ($state) => is_object($state) ? $state->name : $state)
|
||||
->inlineLabel()
|
||||
->badge()
|
||||
->color(fn ($state) => is_object($state) ? $state->color : 'gray')
|
||||
->icon(fn ($state) => is_object($state) ? 'heroicon-o-tag' : '')
|
||||
->suffixAction(
|
||||
EditTags::infolist($this->site)
|
||||
),
|
||||
TextEntry::make('php_version')
|
||||
->label('PHP Version')
|
||||
->inlineLabel()
|
||||
->suffixAction(
|
||||
Action::make('edit_php_version')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->tooltip('Change')
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Update PHP Version')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form([
|
||||
Select::make('version')
|
||||
->label('Version')
|
||||
->selectablePlaceholder(false)
|
||||
->rules(UpdatePHPVersion::rules($this->site)['version'])
|
||||
->default($this->site->php_version)
|
||||
->options(
|
||||
collect($this->site->server->installedPHPVersions())
|
||||
->mapWithKeys(fn ($version) => [$version => $version])
|
||||
),
|
||||
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
app(UpdatePHPVersion::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('PHP version updated!')
|
||||
->send();
|
||||
});
|
||||
})
|
||||
),
|
||||
TextEntry::make('aliases.*')
|
||||
->inlineLabel()
|
||||
->badge()
|
||||
->default('No aliases')
|
||||
->color(fn ($state) => $state == 'No aliases' ? 'gray' : 'primary')
|
||||
->suffixAction(
|
||||
Action::make('edit_aliases')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->tooltip('Change')
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Update Aliases')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form([
|
||||
TagsInput::make('aliases')
|
||||
->splitKeys(['Enter', 'Tab', ' ', ','])
|
||||
->placeholder('Type and press enter to add an alias')
|
||||
->default($this->site->aliases)
|
||||
->nestedRecursiveRules(UpdateAliases::rules()['aliases.*']),
|
||||
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
app(UpdateAliases::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Aliases updated!')
|
||||
->send();
|
||||
});
|
||||
})
|
||||
),
|
||||
TextEntry::make('source_control_id')
|
||||
->label('Source Control')
|
||||
->formatStateUsing(fn (Site $record) => $record->sourceControl?->profile)
|
||||
->inlineLabel()
|
||||
->suffixAction(
|
||||
Action::make('edit_source_control')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->tooltip('Change')
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Update Source Control')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form([
|
||||
Select::make('source_control')
|
||||
->label('Source Control')
|
||||
->rules(UpdateSourceControl::rules()['source_control'])
|
||||
->options(
|
||||
SourceControl::getByProjectId(auth()->user()->current_project_id)
|
||||
->pluck('profile', 'id')
|
||||
)
|
||||
->default($this->site->source_control_id)
|
||||
->suffixAction(
|
||||
\Filament\Forms\Components\Actions\Action::make('connect')
|
||||
->form(Create::form())
|
||||
->modalHeading('Connect to a source control')
|
||||
->modalSubmitActionLabel('Connect')
|
||||
->icon('heroicon-o-wifi')
|
||||
->tooltip('Connect to a source control')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->authorize(fn () => auth()->user()->can('create', SourceControl::class))
|
||||
->action(fn (array $data) => Create::action($data))
|
||||
)
|
||||
->placeholder('Select source control'),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
app(UpdateSourceControl::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Source control updated!')
|
||||
->send();
|
||||
});
|
||||
})
|
||||
),
|
||||
]),
|
||||
])
|
||||
->record($this->site);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\Web\Pages\Servers\Sites\Settings;
|
||||
use App\Web\Pages\Servers\Sites\View;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
@ -25,10 +26,15 @@ protected function getTableQuery(): Builder
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
TextColumn::make('id')
|
||||
TextColumn::make('domain')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('domain')
|
||||
TextColumn::make('tags')
|
||||
->label('Tags')
|
||||
->badge()
|
||||
->icon('heroicon-o-tag')
|
||||
->formatStateUsing(fn ($state) => $state->name)
|
||||
->color(fn ($state) => $state->color)
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
@ -52,7 +58,7 @@ public function getTable(): Table
|
||||
->label('Settings')
|
||||
->icon('heroicon-o-cog-6-tooth')
|
||||
->authorize(fn (Site $record) => auth()->user()->can('update', [$record, $this->server]))
|
||||
->url(fn (Site $record) => '/'),
|
||||
->url(fn (Site $record) => Settings::getUrl(parameters: ['server' => $this->server, 'site' => $record])),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user