mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-05 16:02:34 +00:00
Add phpstan level 7(#544)
This commit is contained in:
@ -39,7 +39,7 @@ public function logoutAction(): Action
|
||||
->label('Logout')
|
||||
->color('danger')
|
||||
->link()
|
||||
->action(function () {
|
||||
->action(function (): void {
|
||||
Filament::auth()->logout();
|
||||
|
||||
session()->forget('login.id');
|
||||
@ -96,16 +96,22 @@ public function authenticate(): ?LoginResponse
|
||||
return $loginResponse;
|
||||
}
|
||||
|
||||
private function confirmTwoFactor(): ?LoginResponse
|
||||
private function confirmTwoFactor(): LoginResponse
|
||||
{
|
||||
$request = TwoFactorLoginRequest::createFrom(request())->merge([
|
||||
'code' => $this->data['code'],
|
||||
'recovery_code' => $this->data['recovery_code'],
|
||||
'code' => $this->data['code'] ?? null,
|
||||
'recovery_code' => $this->data['recovery_code'] ?? null,
|
||||
]);
|
||||
|
||||
/** @var ?User $user */
|
||||
$user = $request->challengedUser();
|
||||
|
||||
if (! $user) {
|
||||
$this->redirect(Filament::getLoginUrl());
|
||||
|
||||
return app(LoginResponse::class);
|
||||
}
|
||||
|
||||
if ($code = $request->validRecoveryCode()) {
|
||||
$user->replaceRecoveryCode($code);
|
||||
} elseif (! $request->hasValidCode()) {
|
||||
@ -141,7 +147,7 @@ private function initTwoFactor(): void
|
||||
PanelsRenderHook::AUTH_LOGIN_FORM_BEFORE,
|
||||
fn (): string => Blade::render(
|
||||
<<<BLADE
|
||||
<x-slot name="subheading">{$this->logoutAction()->render()}</x-slot>
|
||||
<x-slot name="subheading">{$this->logoutAction()->render()->render()}</x-slot>
|
||||
BLADE
|
||||
),
|
||||
);
|
||||
|
@ -42,18 +42,17 @@ protected function getHeaderActions(): array
|
||||
{
|
||||
$form = [
|
||||
Select::make('server')
|
||||
->options(function () {
|
||||
return auth()->user()?->currentProject?->servers?->pluck('name', 'id') ?? [];
|
||||
})
|
||||
->options(fn () => auth()->user()?->currentProject?->servers?->pluck('name', 'id') ?? [])
|
||||
->rules(fn (Get $get) => ExecuteScript::rules($get())['server'])
|
||||
->searchable()
|
||||
->reactive(),
|
||||
Select::make('user')
|
||||
->rules(fn (Get $get) => ExecuteScript::rules($get())['user'])
|
||||
->native(false)
|
||||
->options(function (Get $get) {
|
||||
->options(function (Get $get): array {
|
||||
$users = ['root'];
|
||||
|
||||
/** @var ?Server $server */
|
||||
$server = Server::query()->find($get('server'));
|
||||
if ($server) {
|
||||
$users = $server->getSshUsers();
|
||||
@ -74,7 +73,7 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-bolt')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->form($form)
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
app(ExecuteScript::class)->execute($this->script, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -40,6 +40,9 @@ public function getWidgets(): array
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
Action::make('read-the-docs')
|
||||
->label('Read the Docs')
|
||||
@ -62,9 +65,9 @@ protected function getHeaderActions(): array
|
||||
->label('Is Global (Accessible in all projects)'),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
app(CreateScript::class)->create(auth()->user(), $data);
|
||||
->action(function (array $data) use ($user): void {
|
||||
run_action($this, function () use ($data, $user): void {
|
||||
app(CreateScript::class)->create($user, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
|
@ -14,15 +14,25 @@
|
||||
|
||||
class ScriptExecutionsList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public Script $script;
|
||||
|
||||
/**
|
||||
* @return Builder<ScriptExecution>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return ScriptExecution::query()->where('script_id', $this->script->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<ScriptExecution> $query
|
||||
* @return Builder<ScriptExecution>
|
||||
*/
|
||||
protected function applyDefaultSortingToTableQuery(Builder $query): Builder
|
||||
{
|
||||
return $query->latest('created_at');
|
||||
@ -32,13 +42,11 @@ protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
TextColumn::make('server')
|
||||
->formatStateUsing(function (ScriptExecution $record) {
|
||||
return $record->getServer()?->name ?? 'Unknown';
|
||||
})
|
||||
->url(function (ScriptExecution $record) {
|
||||
->formatStateUsing(fn (ScriptExecution $record) => $record->getServer()->name ?? 'Unknown')
|
||||
->url(function (ScriptExecution $record): ?string {
|
||||
$server = $record->getServer();
|
||||
|
||||
return $server ? View::getUrl(['server' => $server]) : null;
|
||||
return $server instanceof \App\Models\Server ? View::getUrl(['server' => $server]) : null;
|
||||
})
|
||||
->searchable()
|
||||
->sortable(),
|
||||
@ -66,14 +74,12 @@ public function table(Table $table): Table
|
||||
->hiddenLabel()
|
||||
->tooltip('Logs')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn (ScriptExecution $record) => auth()->user()->can('view', $record->serverLog))
|
||||
->authorize(fn (ScriptExecution $record) => auth()->user()?->can('view', $record->serverLog))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (ScriptExecution $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => $record->serverLog?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalContent(fn (ScriptExecution $record) => view('components.console-view', [
|
||||
'slot' => $record->serverLog?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]))
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close'),
|
||||
]);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\Script\EditScript;
|
||||
use App\Models\Script;
|
||||
use App\Models\User;
|
||||
use App\Web\Fields\CodeEditorField;
|
||||
use App\Web\Pages\Scripts\Executions;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
@ -18,11 +19,20 @@
|
||||
|
||||
class ScriptsList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Script>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Script::getByProjectId(auth()->user()->current_project_id, auth()->user()->id);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return Script::getByProjectId($user->current_project_id, $user->id);
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
@ -34,10 +44,8 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('id')
|
||||
->label('Global')
|
||||
->badge()
|
||||
->color(fn ($record) => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(function (Script $record) {
|
||||
return $record->project_id ? 'No' : 'Yes';
|
||||
}),
|
||||
->color(fn ($record): string => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(fn (Script $record): string => $record->project_id ? 'No' : 'Yes'),
|
||||
TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->formatStateUsing(fn (Script $record) => $record->created_at_by_timezone)
|
||||
@ -48,22 +56,23 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
->columns($this->getTableColumns())
|
||||
->recordUrl(fn (Script $record) => Executions::getUrl(['script' => $record]))
|
||||
->recordUrl(fn (Script $record): string => Executions::getUrl(['script' => $record]))
|
||||
->actions([
|
||||
EditAction::make('edit')
|
||||
->label('Edit')
|
||||
->modalHeading('Edit Script')
|
||||
->mutateRecordDataUsing(function (array $data, Script $record) {
|
||||
return [
|
||||
'name' => $record->name,
|
||||
'content' => $record->content,
|
||||
'global' => $record->project_id === null,
|
||||
];
|
||||
})
|
||||
->mutateRecordDataUsing(fn (array $data, Script $record): array => [
|
||||
'name' => $record->name,
|
||||
'content' => $record->content,
|
||||
'global' => $record->project_id === null,
|
||||
])
|
||||
->form([
|
||||
TextInput::make('name')
|
||||
->rules(EditScript::rules()['name']),
|
||||
@ -73,17 +82,17 @@ public function table(Table $table): Table
|
||||
Checkbox::make('global')
|
||||
->label('Is Global (Accessible in all projects)'),
|
||||
])
|
||||
->authorize(fn (Script $record) => auth()->user()->can('update', $record))
|
||||
->using(function (array $data, Script $record) {
|
||||
app(EditScript::class)->edit($record, auth()->user(), $data);
|
||||
->authorize(fn (Script $record) => $user->can('update', $record))
|
||||
->using(function (array $data, Script $record) use ($user): void {
|
||||
app(EditScript::class)->edit($record, $user, $data);
|
||||
$this->dispatch('$refresh');
|
||||
})
|
||||
->modalWidth(MaxWidth::ThreeExtraLarge),
|
||||
DeleteAction::make('delete')
|
||||
->label('Delete')
|
||||
->modalHeading('Delete Script')
|
||||
->authorize(fn (Script $record) => auth()->user()->can('delete', $record))
|
||||
->using(function (array $data, Script $record) {
|
||||
->authorize(fn (Script $record) => $user->can('delete', $record))
|
||||
->using(function (array $data, Script $record): void {
|
||||
$record->delete();
|
||||
}),
|
||||
]);
|
||||
|
@ -9,6 +9,9 @@ class Index extends Page
|
||||
{
|
||||
protected ?string $live = '';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = [];
|
||||
|
||||
protected static ?string $slug = 'servers/{server}/console';
|
||||
|
@ -17,6 +17,9 @@ class Index extends Page
|
||||
|
||||
protected static ?string $title = 'Cron Jobs';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
@ -64,11 +67,11 @@ protected function getHeaderActions(): array
|
||||
TextInput::make('custom')
|
||||
->label('Custom Frequency (Cron)')
|
||||
->rules(fn (callable $get) => CreateCronJob::rules($get(), $this->server)['custom'])
|
||||
->visible(fn (callable $get) => $get('frequency') === 'custom')
|
||||
->visible(fn (callable $get): bool => $get('frequency') === 'custom')
|
||||
->placeholder('0 * * * *'),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(CreateCronJob::class)->create($this->server, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -18,8 +18,14 @@ class CronJobsList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<CronJob>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return CronJob::query()->where('server_id', $this->server->id);
|
||||
@ -34,7 +40,7 @@ protected function getTableColumns(): array
|
||||
->searchable()
|
||||
->copyable(),
|
||||
TextColumn::make('frequency')
|
||||
->formatStateUsing(fn (CronJob $cronJob) => $cronJob->frequencyLabel())
|
||||
->formatStateUsing(fn (CronJob $cronJob): string => $cronJob->frequencyLabel())
|
||||
->searchable()
|
||||
->sortable()
|
||||
->copyable(),
|
||||
@ -52,6 +58,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -62,10 +71,10 @@ public function table(Table $table): Table
|
||||
->tooltip('Enable')
|
||||
->icon('heroicon-o-play')
|
||||
->requiresConfirmation()
|
||||
->authorize(fn (CronJob $record) => auth()->user()->can('update', [$record, $this->server]))
|
||||
->visible(fn (CronJob $record) => $record->isDisabled())
|
||||
->action(function (CronJob $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->authorize(fn (CronJob $record) => $user->can('update', [$record, $this->server]))
|
||||
->visible(fn (CronJob $record): bool => $record->isDisabled())
|
||||
->action(function (CronJob $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
app(EnableCronJob::class)->enable($this->server, $record);
|
||||
});
|
||||
}),
|
||||
@ -74,10 +83,10 @@ public function table(Table $table): Table
|
||||
->tooltip('Disable')
|
||||
->icon('heroicon-o-stop')
|
||||
->requiresConfirmation()
|
||||
->authorize(fn (CronJob $record) => auth()->user()->can('update', [$record, $this->server]))
|
||||
->visible(fn (CronJob $record) => $record->isEnabled())
|
||||
->action(function (CronJob $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->authorize(fn (CronJob $record) => $user->can('update', [$record, $this->server]))
|
||||
->visible(fn (CronJob $record): bool => $record->isEnabled())
|
||||
->action(function (CronJob $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
app(DisableCronJob::class)->disable($this->server, $record);
|
||||
});
|
||||
}),
|
||||
@ -87,8 +96,8 @@ public function table(Table $table): Table
|
||||
->color('danger')
|
||||
->hiddenLabel()
|
||||
->requiresConfirmation()
|
||||
->authorize(fn (CronJob $record) => auth()->user()->can('delete', $record))
|
||||
->action(function (CronJob $record) {
|
||||
->authorize(fn (CronJob $record) => $user->can('delete', $record))
|
||||
->action(function (CronJob $record): void {
|
||||
try {
|
||||
app(DeleteCronJob::class)->delete($this->server, $record);
|
||||
} catch (\Exception $e) {
|
||||
|
@ -29,11 +29,14 @@ public function mount(): void
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
Action::make('create')
|
||||
->icon('heroicon-o-plus')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->authorize(fn () => auth()->user()?->can('create', [Backup::class, $this->server]))
|
||||
->authorize(fn () => $user->can('create', [Backup::class, $this->server]))
|
||||
->form([
|
||||
Select::make('database')
|
||||
->label('Database')
|
||||
@ -52,7 +55,7 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-wifi')
|
||||
->tooltip('Connect to a new storage provider')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->authorize(fn () => auth()->user()->can('create', StorageProvider::class))
|
||||
->authorize(fn () => $user->can('create', StorageProvider::class))
|
||||
->action(fn (array $data) => Create::action($data))
|
||||
),
|
||||
Select::make('interval')
|
||||
@ -63,7 +66,7 @@ protected function getHeaderActions(): array
|
||||
TextInput::make('custom_interval')
|
||||
->label('Custom Interval (Cron)')
|
||||
->rules(fn (callable $get) => ManageBackup::rules($this->server, $get())['custom_interval'])
|
||||
->visible(fn (callable $get) => $get('interval') === 'custom')
|
||||
->visible(fn (callable $get): bool => $get('interval') === 'custom')
|
||||
->placeholder('0 * * * *'),
|
||||
TextInput::make('keep')
|
||||
->label('Backups to Keep')
|
||||
@ -71,8 +74,8 @@ protected function getHeaderActions(): array
|
||||
->helperText('How many backups to keep before deleting the oldest one'),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(ManageBackup::class)->create($this->server, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -40,7 +40,7 @@ public static function getCharsetInput(Server $server): Select
|
||||
|
||||
return $service->type_data['defaultCharset'] ?? null;
|
||||
})
|
||||
->options(function () use ($server) {
|
||||
->options(function () use ($server): array {
|
||||
$service = $server->defaultService('database');
|
||||
$charsets = $service->type_data['charsets'] ?? [];
|
||||
|
||||
@ -49,7 +49,7 @@ public static function getCharsetInput(Server $server): Select
|
||||
array_keys($charsets)
|
||||
);
|
||||
})
|
||||
->afterStateUpdated(function (Get $get, Set $set, $state) use ($server) {
|
||||
->afterStateUpdated(function (Get $get, Set $set, $state) use ($server): void {
|
||||
$service = $server->defaultService('database');
|
||||
$charsets = $service->type_data['charsets'] ?? [];
|
||||
$set('collation', $charsets[$state]['default'] ?? null);
|
||||
@ -70,7 +70,7 @@ public static function getCollationInput(Server $server): Select
|
||||
|
||||
return $charsets[$charset]['default'] ?? null;
|
||||
})
|
||||
->options(function (Get $get) use ($server) {
|
||||
->options(function (Get $get) use ($server): array {
|
||||
$service = $server->defaultService('database');
|
||||
$collations = $service->type_data['charsets'][$get('charset')]['list'] ?? [];
|
||||
|
||||
@ -102,24 +102,24 @@ protected function getHeaderActions(): array
|
||||
TextInput::make('username')
|
||||
->label('Username')
|
||||
->rules(fn (callable $get) => CreateDatabase::rules($this->server, $get())['username'])
|
||||
->hidden(fn (callable $get) => $get('user') !== true),
|
||||
->hidden(fn (callable $get): bool => $get('user') !== true),
|
||||
TextInput::make('password')
|
||||
->label('Password')
|
||||
->rules(fn (callable $get) => CreateDatabase::rules($this->server, $get())['password'])
|
||||
->hidden(fn (callable $get) => $get('user') !== true),
|
||||
->hidden(fn (callable $get): bool => $get('user') !== true),
|
||||
Checkbox::make('remote')
|
||||
->label('Remote')
|
||||
->default(false)
|
||||
->hidden(fn (callable $get) => $get('user') !== true)
|
||||
->hidden(fn (callable $get): bool => $get('user') !== true)
|
||||
->reactive(),
|
||||
TextInput::make('host')
|
||||
->label('Host')
|
||||
->rules(fn (callable $get) => CreateDatabase::rules($this->server, $get())['host'])
|
||||
->hidden(fn (callable $get) => $get('remote') !== true),
|
||||
->hidden(fn (callable $get): bool => $get('remote') !== true),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(CreateDatabase::class)->create($this->server, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -5,9 +5,9 @@
|
||||
use App\Models\Backup;
|
||||
use App\Models\Database;
|
||||
use App\Models\DatabaseUser;
|
||||
use App\Web\Pages\Servers\Databases\Backups as Backups;
|
||||
use App\Web\Pages\Servers\Databases\Backups;
|
||||
use App\Web\Pages\Servers\Databases\Index as Databases;
|
||||
use App\Web\Pages\Servers\Databases\Users as Users;
|
||||
use App\Web\Pages\Servers\Databases\Users;
|
||||
use Filament\Navigation\NavigationGroup;
|
||||
use Filament\Navigation\NavigationItem;
|
||||
|
||||
@ -15,23 +15,26 @@ trait Navigation
|
||||
{
|
||||
public function getSecondSubNavigation(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$items = [];
|
||||
|
||||
if (auth()->user()->can('viewAny', [Database::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Database::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(Databases::getNavigationLabel())
|
||||
->icon('heroicon-o-circle-stack')
|
||||
->isActiveWhen(fn () => request()->routeIs(Databases::getRouteName()))
|
||||
->url(Databases::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [DatabaseUser::class, $this->server])) {
|
||||
if ($user->can('viewAny', [DatabaseUser::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(Users::getNavigationLabel())
|
||||
->icon('heroicon-o-users')
|
||||
->isActiveWhen(fn () => request()->routeIs(Users::getRouteName()))
|
||||
->url(Users::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [Backup::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Backup::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(Backups::getNavigationLabel())
|
||||
->icon('heroicon-o-cloud')
|
||||
->isActiveWhen(fn () => request()->routeIs(Backups::getRouteName()))
|
||||
|
@ -47,11 +47,11 @@ protected function getHeaderActions(): array
|
||||
TextInput::make('host')
|
||||
->label('Host')
|
||||
->rules(fn (callable $get) => CreateDatabase::rules($this->server, $get())['host'])
|
||||
->hidden(fn (callable $get) => $get('remote') !== true),
|
||||
->hidden(fn (callable $get): bool => $get('remote') !== true),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(CreateDatabaseUser::class)->create($this->server, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -20,13 +20,22 @@ class BackupFilesList extends Widget
|
||||
{
|
||||
public Backup $backup;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<BackupFile>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return BackupFile::query()->where('backup_id', $this->backup->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
@ -38,7 +47,7 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('restored_to')
|
||||
->searchable(),
|
||||
TextColumn::make('restored_at')
|
||||
->formatStateUsing(fn (BackupFile $record) => $record->getDateTimeByTimezone($record->restored_at))
|
||||
->formatStateUsing(fn (BackupFile $record): string => $record->getDateTimeByTimezone($record->restored_at))
|
||||
->sortable(),
|
||||
TextColumn::make('status')
|
||||
->badge()
|
||||
@ -48,6 +57,10 @@ protected function getTableColumns(): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<BackupFile> $query
|
||||
* @return Builder<BackupFile>
|
||||
*/
|
||||
protected function applyDefaultSortingToTableQuery(Builder $query): Builder
|
||||
{
|
||||
return $query->latest('created_at');
|
||||
@ -55,6 +68,9 @@ protected function applyDefaultSortingToTableQuery(Builder $query): Builder
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -63,19 +79,17 @@ public function table(Table $table): Table
|
||||
Action::make('download')
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-arrow-down-tray')
|
||||
->visible(fn (BackupFile $record) => $record->isAvailable() && $record->isLocal())
|
||||
->visible(fn (BackupFile $record): bool => $record->isAvailable() && $record->isLocal())
|
||||
->tooltip('Download')
|
||||
->action(function (BackupFile $record) {
|
||||
return app(ManageBackupFile::class)->download($record);
|
||||
})
|
||||
->authorize(fn (BackupFile $record) => auth()->user()->can('view', $record)),
|
||||
->action(fn (BackupFile $record) => app(ManageBackupFile::class)->download($record))
|
||||
->authorize(fn (BackupFile $record) => $user->can('view', $record)),
|
||||
Action::make('restore')
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->modalHeading('Restore Backup')
|
||||
->tooltip('Restore Backup')
|
||||
->disabled(fn (BackupFile $record) => ! $record->isAvailable())
|
||||
->authorize(fn (BackupFile $record) => auth()->user()->can('update', $record->backup))
|
||||
->disabled(fn (BackupFile $record): bool => ! $record->isAvailable())
|
||||
->authorize(fn (BackupFile $record) => $user->can('update', $record->backup))
|
||||
->form([
|
||||
Select::make('database')
|
||||
->label('Restore to')
|
||||
@ -84,8 +98,8 @@ public function table(Table $table): Table
|
||||
->native(false),
|
||||
])
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->action(function (BackupFile $record, array $data) {
|
||||
run_action($this, function () use ($record, $data) {
|
||||
->action(function (BackupFile $record, array $data): void {
|
||||
run_action($this, function () use ($record, $data): void {
|
||||
$this->validate();
|
||||
|
||||
/** @var Database $database */
|
||||
@ -108,11 +122,11 @@ public function table(Table $table): Table
|
||||
->icon('heroicon-o-trash')
|
||||
->modalHeading('Delete Backup File')
|
||||
->color('danger')
|
||||
->disabled(fn (BackupFile $record) => ! $record->isAvailable())
|
||||
->disabled(fn (BackupFile $record): bool => ! $record->isAvailable())
|
||||
->tooltip('Delete')
|
||||
->authorize(fn (BackupFile $record) => auth()->user()->can('delete', $record))
|
||||
->authorize(fn (BackupFile $record) => $user->can('delete', $record))
|
||||
->requiresConfirmation()
|
||||
->action(function (BackupFile $record) {
|
||||
->action(function (BackupFile $record): void {
|
||||
app(ManageBackupFile::class)->delete($record);
|
||||
$this->dispatch('$refresh');
|
||||
}),
|
||||
|
@ -21,8 +21,14 @@ class BackupsList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Backup>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Backup::query()->where('server_id', $this->server->id);
|
||||
@ -56,6 +62,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -65,8 +74,8 @@ public function table(Table $table): Table
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-pencil')
|
||||
->tooltip('Edit Configuration')
|
||||
->disabled(fn (Backup $record) => ! in_array($record->status, ['running', 'failed']))
|
||||
->authorize(fn (Backup $record) => auth()->user()->can('update', $record))
|
||||
->disabled(fn (Backup $record): bool => ! in_array($record->status, ['running', 'failed']))
|
||||
->authorize(fn (Backup $record) => $user->can('update', $record))
|
||||
->modelLabel('Edit Backup')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->modalSubmitActionLabel('Update')
|
||||
@ -80,7 +89,7 @@ public function table(Table $table): Table
|
||||
TextInput::make('custom_interval')
|
||||
->label('Custom Interval (Cron)')
|
||||
->rules(fn (callable $get) => ManageBackup::rules($this->server, $get())['custom_interval'])
|
||||
->visible(fn (callable $get) => $get('interval') === 'custom')
|
||||
->visible(fn (callable $get): bool => $get('interval') === 'custom')
|
||||
->default(fn (Backup $record) => $record->isCustomInterval() ? $record->interval : '')
|
||||
->placeholder('0 * * * *'),
|
||||
TextInput::make('keep')
|
||||
@ -89,8 +98,8 @@ public function table(Table $table): Table
|
||||
->rules(fn (callable $get) => ManageBackup::rules($this->server, $get())['keep'])
|
||||
->helperText('How many backups to keep before deleting the oldest one'),
|
||||
])
|
||||
->action(function (Backup $backup, array $data) {
|
||||
run_action($this, function () use ($data, $backup) {
|
||||
->action(function (Backup $backup, array $data): void {
|
||||
run_action($this, function () use ($data, $backup): void {
|
||||
app(ManageBackup::class)->update($backup, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
@ -107,8 +116,8 @@ public function table(Table $table): Table
|
||||
->modalHeading('Backup Files')
|
||||
->color('gray')
|
||||
->tooltip('Show backup files')
|
||||
->disabled(fn (Backup $record) => ! in_array($record->status, ['running', 'failed']))
|
||||
->authorize(fn (Backup $record) => auth()->user()->can('viewAny', [BackupFile::class, $record]))
|
||||
->disabled(fn (Backup $record): bool => ! in_array($record->status, ['running', 'failed']))
|
||||
->authorize(fn (Backup $record) => $user->can('viewAny', [BackupFile::class, $record]))
|
||||
->modalContent(fn (Backup $record) => view('components.dynamic-widget', [
|
||||
'widget' => BackupFilesList::class,
|
||||
'params' => [
|
||||
@ -124,7 +133,7 @@ public function table(Table $table): Table
|
||||
->label('Run Backup')
|
||||
->icon('heroicon-o-play')
|
||||
->color('primary')
|
||||
->action(function (Backup $record) {
|
||||
->action(function (Backup $record): void {
|
||||
app(RunBackup::class)->run($record);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
@ -134,12 +143,12 @@ public function table(Table $table): Table
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-trash')
|
||||
->modalHeading('Delete Backup & Files')
|
||||
->disabled(fn (Backup $record) => ! in_array($record->status, ['running', 'failed']))
|
||||
->disabled(fn (Backup $record): bool => ! in_array($record->status, ['running', 'failed']))
|
||||
->color('danger')
|
||||
->tooltip('Delete')
|
||||
->authorize(fn (Backup $record) => auth()->user()->can('delete', $record))
|
||||
->authorize(fn (Backup $record) => $user->can('delete', $record))
|
||||
->requiresConfirmation()
|
||||
->action(function (Backup $record) {
|
||||
->action(function (Backup $record): void {
|
||||
app(ManageBackup::class)->delete($record);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -20,8 +20,14 @@ class DatabaseUsersList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<DatabaseUser>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return DatabaseUser::query()->where('server_id', $this->server->id);
|
||||
@ -59,6 +65,9 @@ public function table(Table $table): Table
|
||||
|
||||
private function passwordAction(): Action
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return Action::make('password')
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-key')
|
||||
@ -66,14 +75,14 @@ private function passwordAction(): Action
|
||||
->modalHeading('Database user\'s password')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->tooltip('Show the password')
|
||||
->authorize(fn ($record) => auth()->user()->can('view', $record))
|
||||
->authorize(fn ($record) => $user->can('view', $record))
|
||||
->form([
|
||||
TextInput::make('password')
|
||||
->label('Password')
|
||||
->default(fn (DatabaseUser $record) => $record->password)
|
||||
->disabled(),
|
||||
])
|
||||
->action(function (DatabaseUser $record, array $data) {
|
||||
->action(function (DatabaseUser $record, array $data): void {
|
||||
//
|
||||
})
|
||||
->modalSubmitAction(false)
|
||||
@ -82,6 +91,9 @@ private function passwordAction(): Action
|
||||
|
||||
private function linkAction(): Action
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return Action::make('link')
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-link')
|
||||
@ -89,16 +101,16 @@ private function linkAction(): Action
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->tooltip('Link user')
|
||||
->modalSubmitActionLabel('Save')
|
||||
->authorize(fn ($record) => auth()->user()->can('update', $record))
|
||||
->authorize(fn ($record) => $user->can('update', $record))
|
||||
->form([
|
||||
CheckboxList::make('databases')
|
||||
->label('Databases')
|
||||
->options($this->server->databases()->pluck('name', 'name')->toArray())
|
||||
->rules(fn (callable $get) => LinkUser::rules($this->server, $get()))
|
||||
->rules(fn (callable $get): array => LinkUser::rules($this->server, $get()))
|
||||
->default(fn (DatabaseUser $record) => $record->databases),
|
||||
])
|
||||
->action(function (DatabaseUser $record, array $data) {
|
||||
run_action($this, function () use ($record, $data) {
|
||||
->action(function (DatabaseUser $record, array $data): void {
|
||||
run_action($this, function () use ($record, $data): void {
|
||||
app(LinkUser::class)->link($record, $data);
|
||||
|
||||
Notification::make()
|
||||
@ -111,16 +123,19 @@ private function linkAction(): Action
|
||||
|
||||
private function deleteAction(): Action
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return Action::make('delete')
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-trash')
|
||||
->modalHeading('Delete Database User')
|
||||
->color('danger')
|
||||
->tooltip('Delete')
|
||||
->authorize(fn ($record) => auth()->user()->can('delete', $record))
|
||||
->authorize(fn ($record) => $user->can('delete', $record))
|
||||
->requiresConfirmation()
|
||||
->action(function (DatabaseUser $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->action(function (DatabaseUser $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
app(DeleteDatabaseUser::class)->delete($this->server, $record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
|
@ -15,8 +15,14 @@ class DatabasesList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Database>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Database::query()->where('server_id', $this->server->id);
|
||||
@ -47,6 +53,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -58,10 +67,10 @@ public function table(Table $table): Table
|
||||
->modalHeading('Delete Database')
|
||||
->color('danger')
|
||||
->tooltip('Delete')
|
||||
->authorize(fn ($record) => auth()->user()->can('delete', $record))
|
||||
->authorize(fn ($record) => $user->can('delete', $record))
|
||||
->requiresConfirmation()
|
||||
->action(function (Database $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->action(function (Database $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
app(DeleteDatabase::class)->delete($this->server, $record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
|
@ -10,6 +10,9 @@ class Index extends Page
|
||||
|
||||
protected static ?string $title = 'File Manager';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
|
@ -30,6 +30,9 @@ class FilesList extends Widget
|
||||
|
||||
public string $path;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
@ -43,6 +46,9 @@ public function mount(): void
|
||||
$this->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
protected function getTableHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
@ -63,6 +69,9 @@ protected function getTableHeaderActions(): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<File>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return File::query()
|
||||
@ -72,6 +81,8 @@ protected function getTableQuery(): Builder
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
auth()->user();
|
||||
|
||||
return $table
|
||||
->query($this->getTableQuery())
|
||||
->headerActions($this->getTableHeaderActions())
|
||||
@ -79,7 +90,7 @@ public function table(Table $table): Table
|
||||
->columns([
|
||||
IconColumn::make('type')
|
||||
->sortable()
|
||||
->icon(fn (File $file) => $this->getIcon($file)),
|
||||
->icon(fn (File $file): string => $this->getIcon($file)),
|
||||
TextColumn::make('name')
|
||||
->sortable(),
|
||||
TextColumn::make('size')
|
||||
@ -93,7 +104,7 @@ public function table(Table $table): Table
|
||||
TextColumn::make('permissions')
|
||||
->sortable(),
|
||||
])
|
||||
->recordUrl(function (File $file) {
|
||||
->recordUrl(function (File $file): string {
|
||||
if ($file->type === 'directory') {
|
||||
return Index::getUrl([
|
||||
'server' => $this->server->id,
|
||||
@ -134,9 +145,12 @@ public function changeUser(string $user): void
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
try {
|
||||
app(FetchFiles::class)->fetch(
|
||||
auth()->user(),
|
||||
$user,
|
||||
$this->server,
|
||||
[
|
||||
'user' => $this->serverUser,
|
||||
@ -172,7 +186,7 @@ protected function homeAction(): Action
|
||||
->label('Home')
|
||||
->size(ActionSize::Small)
|
||||
->icon('heroicon-o-home')
|
||||
->action(function () {
|
||||
->action(function (): void {
|
||||
$this->path = home_path($this->serverUser);
|
||||
$this->refresh();
|
||||
});
|
||||
@ -211,8 +225,8 @@ protected function newFileAction(): Action
|
||||
return Action::make('new-file')
|
||||
->label('New File')
|
||||
->icon('heroicon-o-document-text')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
$this->server->os()->write(
|
||||
$this->path.'/'.$data['name'],
|
||||
str_replace("\r\n", "\n", $data['content']),
|
||||
@ -221,13 +235,11 @@ protected function newFileAction(): Action
|
||||
$this->refresh();
|
||||
});
|
||||
})
|
||||
->form(function () {
|
||||
return [
|
||||
TextInput::make('name')
|
||||
->placeholder('file-name.txt'),
|
||||
CodeEditorField::make('content'),
|
||||
];
|
||||
})
|
||||
->form(fn (): array => [
|
||||
TextInput::make('name')
|
||||
->placeholder('file-name.txt'),
|
||||
CodeEditorField::make('content'),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->modalHeading('New File')
|
||||
->modalWidth('4xl');
|
||||
@ -238,8 +250,8 @@ protected function newDirectoryAction(): Action
|
||||
return Action::make('new-directory')
|
||||
->label('New Directory')
|
||||
->icon('heroicon-o-folder')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
$this->server->os()->mkdir(
|
||||
$this->path.'/'.$data['name'],
|
||||
$this->serverUser
|
||||
@ -247,12 +259,10 @@ protected function newDirectoryAction(): Action
|
||||
$this->refresh();
|
||||
});
|
||||
})
|
||||
->form(function () {
|
||||
return [
|
||||
TextInput::make('name')
|
||||
->placeholder('directory name'),
|
||||
];
|
||||
})
|
||||
->form(fn (): array => [
|
||||
TextInput::make('name')
|
||||
->placeholder('directory name'),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->modalHeading('New Directory')
|
||||
->modalWidth('lg');
|
||||
@ -263,11 +273,11 @@ protected function uploadAction(): Action
|
||||
return Action::make('upload')
|
||||
->label('Upload File')
|
||||
->icon('heroicon-o-arrow-up-on-square')
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
//
|
||||
})
|
||||
->after(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->after(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
foreach ($data['file'] as $file) {
|
||||
$this->server->ssh()->upload(
|
||||
Storage::disk('tmp')->path($file),
|
||||
@ -278,14 +288,12 @@ protected function uploadAction(): Action
|
||||
$this->refresh();
|
||||
});
|
||||
})
|
||||
->form(function () {
|
||||
return [
|
||||
FileUpload::make('file')
|
||||
->disk('tmp')
|
||||
->multiple()
|
||||
->preserveFilenames(),
|
||||
];
|
||||
})
|
||||
->form(fn (): array => [
|
||||
FileUpload::make('file')
|
||||
->disk('tmp')
|
||||
->multiple()
|
||||
->preserveFilenames(),
|
||||
])
|
||||
->modalSubmitActionLabel('Upload to Server')
|
||||
->modalHeading('Upload File')
|
||||
->modalWidth('xl');
|
||||
@ -297,8 +305,8 @@ protected function extractAction(): Action
|
||||
->tooltip('Extract')
|
||||
->icon('heroicon-o-archive-box')
|
||||
->hiddenLabel()
|
||||
->visible(fn (File $file) => $file->isExtractable())
|
||||
->action(function (File $file) {
|
||||
->visible(fn (File $file): bool => $file->isExtractable())
|
||||
->action(function (File $file): void {
|
||||
$file->server->os()->extract($file->getFilePath(), $file->path, $file->server_user);
|
||||
$this->refresh();
|
||||
});
|
||||
@ -310,7 +318,7 @@ protected function downloadAction(): Action
|
||||
->tooltip('Download')
|
||||
->icon('heroicon-o-arrow-down-tray')
|
||||
->hiddenLabel()
|
||||
->visible(fn (File $file) => $file->type === 'file')
|
||||
->visible(fn (File $file): bool => $file->type === 'file')
|
||||
->action(function (File $file) {
|
||||
$file->server->ssh($file->server_user)->download(
|
||||
Storage::disk('tmp')->path($file->name),
|
||||
@ -327,8 +335,8 @@ protected function editAction(): Action
|
||||
->tooltip('Edit')
|
||||
->icon('heroicon-o-pencil')
|
||||
->hiddenLabel()
|
||||
->visible(fn (File $file) => $file->type === 'file')
|
||||
->action(function (File $file, array $data) {
|
||||
->visible(fn (File $file): bool => $file->type === 'file')
|
||||
->action(function (File $file, array $data): void {
|
||||
$file->server->os()->write(
|
||||
$file->getFilePath(),
|
||||
str_replace("\r\n", "\n", $data['content']),
|
||||
@ -336,19 +344,17 @@ protected function editAction(): Action
|
||||
);
|
||||
$this->refresh();
|
||||
})
|
||||
->form(function (File $file) {
|
||||
return [
|
||||
CodeEditorField::make('content')
|
||||
->formatStateUsing(function () use ($file) {
|
||||
$file->server->ssh($file->server_user)->download(
|
||||
Storage::disk('tmp')->path($file->name),
|
||||
$file->getFilePath()
|
||||
);
|
||||
->form(fn (File $file): array => [
|
||||
CodeEditorField::make('content')
|
||||
->formatStateUsing(function () use ($file) {
|
||||
$file->server->ssh($file->server_user)->download(
|
||||
Storage::disk('tmp')->path($file->name),
|
||||
$file->getFilePath()
|
||||
);
|
||||
|
||||
return Storage::disk('tmp')->get(basename($file->getFilePath()));
|
||||
}),
|
||||
];
|
||||
})
|
||||
return Storage::disk('tmp')->get(basename($file->getFilePath()));
|
||||
}),
|
||||
])
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Edit')
|
||||
->modalWidth('4xl');
|
||||
@ -362,9 +368,9 @@ protected function deleteAction(): Action
|
||||
->color('danger')
|
||||
->hiddenLabel()
|
||||
->requiresConfirmation()
|
||||
->visible(fn (File $file) => $file->name !== '..')
|
||||
->action(function (File $file) {
|
||||
run_action($this, function () use ($file) {
|
||||
->visible(fn (File $file): bool => $file->name !== '..')
|
||||
->action(function (File $file): void {
|
||||
run_action($this, function () use ($file): void {
|
||||
$file->delete();
|
||||
});
|
||||
});
|
||||
|
@ -20,6 +20,9 @@ class Index extends Page
|
||||
|
||||
protected static ?string $title = 'Firewall';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
@ -34,6 +37,9 @@ public function getWidgets(): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function getFirewallForm(?FirewallRule $record = null): array
|
||||
{
|
||||
return [
|
||||
@ -68,7 +74,7 @@ public static function getFirewallForm(?FirewallRule $record = null): array
|
||||
->helperText('Allow connections from any source, regardless of their IP address or subnet mask.')
|
||||
->live(),
|
||||
TextInput::make('source')
|
||||
->hidden(fn (Get $get) => $get('source_any') == true)
|
||||
->hidden(fn (Get $get): bool => $get('source_any') == true)
|
||||
->label('Source')
|
||||
->helperText('The IP address of the source of the connection.')
|
||||
->rules(ManageRule::rules()['source'])
|
||||
@ -78,13 +84,13 @@ public static function getFirewallForm(?FirewallRule $record = null): array
|
||||
->icon('heroicon-o-globe-alt')
|
||||
->color('primary')
|
||||
->tooltip('Use My IP')
|
||||
->action(function ($set) {
|
||||
->action(function ($set): void {
|
||||
$ip = Request::ip();
|
||||
$set('source', $ip);
|
||||
})
|
||||
),
|
||||
TextInput::make('mask')
|
||||
->hidden(fn (Get $get) => $get('source_any') == true)
|
||||
->hidden(fn (Get $get): bool => $get('source_any') == true)
|
||||
->label('Mask')
|
||||
->default($record->mask ?? null)
|
||||
->helperText('The subnet mask of the source of the connection. Leave blank for a single IP address.')
|
||||
@ -110,8 +116,8 @@ protected function getHeaderActions(): array
|
||||
->modalDescription('Add a new rule to the firewall')
|
||||
->modalSubmitActionLabel('Create')
|
||||
->form(self::getFirewallForm())
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(ManageRule::class)->create($this->server, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -19,8 +19,14 @@ class RulesList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<FirewallRule>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return FirewallRule::query()->where('server_id', $this->server->id);
|
||||
@ -36,7 +42,7 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('type')
|
||||
->sortable()
|
||||
->badge()
|
||||
->color(fn ($state) => $state === 'allow' ? 'success' : 'warning')
|
||||
->color(fn ($state): string => $state === 'allow' ? 'success' : 'warning')
|
||||
->label('Type')
|
||||
->formatStateUsing(fn ($state) => Str::upper($state)),
|
||||
TextColumn::make('id')
|
||||
@ -62,12 +68,15 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(fn (FirewallRule $record) => $record->getStatusColor()),
|
||||
->color(fn (FirewallRule $record): string => $record->getStatusColor()),
|
||||
];
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -81,10 +90,10 @@ public function table(Table $table): Table
|
||||
->modalHeading('Edit Firewall Rule')
|
||||
->modalDescription('Edit the associated servers firewall rule.')
|
||||
->modalSubmitActionLabel('Update')
|
||||
->authorize(fn (FirewallRule $record) => auth()->user()->can('update', $record))
|
||||
->form(fn ($record) => Index::getFirewallForm($record))
|
||||
->action(function (FirewallRule $record, array $data) {
|
||||
run_action($this, function () use ($record, $data) {
|
||||
->authorize(fn (FirewallRule $record) => $user->can('update', $record))
|
||||
->form(fn ($record): array => Index::getFirewallForm($record))
|
||||
->action(function (FirewallRule $record, array $data): void {
|
||||
run_action($this, function () use ($record, $data): void {
|
||||
app(ManageRule::class)->update($record, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
@ -101,8 +110,8 @@ public function table(Table $table): Table
|
||||
->color('danger')
|
||||
->hiddenLabel()
|
||||
->requiresConfirmation()
|
||||
->authorize(fn (FirewallRule $record) => auth()->user()->can('delete', $record))
|
||||
->action(function (FirewallRule $record) {
|
||||
->authorize(fn (FirewallRule $record) => $user->can('delete', $record))
|
||||
->action(function (FirewallRule $record): void {
|
||||
try {
|
||||
app(ManageRule::class)->delete($record);
|
||||
} catch (\Exception $e) {
|
||||
|
@ -7,7 +7,9 @@
|
||||
use App\Enums\PHP;
|
||||
use App\Enums\ServerProvider;
|
||||
use App\Enums\Webserver;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use App\Web\Components\Page;
|
||||
use App\Web\Fields\AlertField;
|
||||
use App\Web\Fields\ProviderField;
|
||||
@ -37,7 +39,10 @@ public static function getNavigationItemActiveRoutePattern(): string
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('viewAny', [Server::class, auth()->user()->currentProject]);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$this->authorize('viewAny', [Server::class, $user->currentProject]);
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
@ -53,7 +58,11 @@ protected function getHeaderActions(): array
|
||||
'public_key' => get_public_key_content(),
|
||||
]);
|
||||
|
||||
$project = auth()->user()->currentProject;
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var Project $project */
|
||||
$project = $user->currentProject;
|
||||
|
||||
return [
|
||||
\Filament\Actions\Action::make('read-the-docs')
|
||||
@ -65,7 +74,7 @@ protected function getHeaderActions(): array
|
||||
\Filament\Actions\Action::make('create')
|
||||
->label('Create a Server')
|
||||
->icon('heroicon-o-plus')
|
||||
->authorize('create', [Server::class, auth()->user()->currentProject])
|
||||
->authorize('create', [Server::class, $user->currentProject])
|
||||
->modalWidth(MaxWidth::FiveExtraLarge)
|
||||
->slideOver()
|
||||
->form([
|
||||
@ -74,7 +83,7 @@ protected function getHeaderActions(): array
|
||||
->default(ServerProvider::CUSTOM)
|
||||
->live()
|
||||
->reactive()
|
||||
->afterStateUpdated(function (callable $set) {
|
||||
->afterStateUpdated(function (callable $set): void {
|
||||
$set('server_provider', null);
|
||||
$set('region', null);
|
||||
$set('plan', null);
|
||||
@ -83,16 +92,14 @@ protected function getHeaderActions(): array
|
||||
AlertField::make('alert')
|
||||
->warning()
|
||||
->message(__('servers.create.public_key_warning'))
|
||||
->visible(fn ($get) => $get('provider') === ServerProvider::CUSTOM),
|
||||
->visible(fn ($get): bool => $get('provider') === ServerProvider::CUSTOM),
|
||||
Select::make('server_provider')
|
||||
->visible(fn ($get) => $get('provider') !== ServerProvider::CUSTOM)
|
||||
->visible(fn ($get): bool => $get('provider') !== ServerProvider::CUSTOM)
|
||||
->label('Server provider connection')
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['server_provider'])
|
||||
->options(function ($get) {
|
||||
return \App\Models\ServerProvider::getByProjectId(auth()->user()->current_project_id)
|
||||
->where('provider', $get('provider'))
|
||||
->pluck('profile', 'id');
|
||||
})
|
||||
->options(fn ($get) => \App\Models\ServerProvider::getByProjectId($project->id)
|
||||
->where('provider', $get('provider'))
|
||||
->pluck('profile', 'id'))
|
||||
->suffixAction(
|
||||
Action::make('connect')
|
||||
->form(Create::form())
|
||||
@ -101,7 +108,7 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-wifi')
|
||||
->tooltip('Connect to a new server provider')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->authorize(fn () => auth()->user()->can('create', \App\Models\ServerProvider::class))
|
||||
->authorize(fn () => $user->can('create', \App\Models\ServerProvider::class))
|
||||
->action(fn (array $data) => Create::action($data))
|
||||
)
|
||||
->placeholder('Select profile')
|
||||
@ -109,7 +116,7 @@ protected function getHeaderActions(): array
|
||||
->live()
|
||||
->reactive()
|
||||
->selectablePlaceholder(false)
|
||||
->visible(fn ($get) => $get('provider') !== ServerProvider::CUSTOM),
|
||||
->visible(fn ($get): bool => $get('provider') !== ServerProvider::CUSTOM),
|
||||
Grid::make()
|
||||
->schema([
|
||||
Select::make('region')
|
||||
@ -117,7 +124,7 @@ protected function getHeaderActions(): array
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['region'] ?? [])
|
||||
->live()
|
||||
->reactive()
|
||||
->options(function ($get) {
|
||||
->options(function ($get): array {
|
||||
if (! $get('server_provider')) {
|
||||
return [];
|
||||
}
|
||||
@ -125,14 +132,14 @@ protected function getHeaderActions(): array
|
||||
return \App\Models\ServerProvider::regions($get('server_provider'));
|
||||
})
|
||||
->loadingMessage('Loading regions...')
|
||||
->disabled(fn ($get) => ! $get('server_provider'))
|
||||
->placeholder(fn ($get) => $get('server_provider') ? 'Select region' : 'Select connection first')
|
||||
->disabled(fn ($get): bool => ! $get('server_provider'))
|
||||
->placeholder(fn ($get): string => $get('server_provider') ? 'Select region' : 'Select connection first')
|
||||
->searchable(),
|
||||
Select::make('plan')
|
||||
->label('Plan')
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['plan'] ?? [])
|
||||
->reactive()
|
||||
->options(function ($get) {
|
||||
->options(function ($get): array {
|
||||
if (! $get('server_provider') || ! $get('region')) {
|
||||
return [];
|
||||
}
|
||||
@ -140,11 +147,11 @@ protected function getHeaderActions(): array
|
||||
return \App\Models\ServerProvider::plans($get('server_provider'), $get('region'));
|
||||
})
|
||||
->loadingMessage('Loading plans...')
|
||||
->disabled(fn ($get) => ! $get('region'))
|
||||
->placeholder(fn ($get) => $get('region') ? 'Select plan' : 'Select plan first')
|
||||
->disabled(fn ($get): bool => ! $get('region'))
|
||||
->placeholder(fn ($get): string => $get('region') ? 'Select plan' : 'Select plan first')
|
||||
->searchable(),
|
||||
])
|
||||
->visible(fn ($get) => $get('provider') !== ServerProvider::CUSTOM),
|
||||
->visible(fn ($get): bool => $get('provider') !== ServerProvider::CUSTOM),
|
||||
TextInput::make('public_key')
|
||||
->label('Public Key')
|
||||
->default($publicKey)
|
||||
@ -152,7 +159,7 @@ protected function getHeaderActions(): array
|
||||
Action::make('copy')
|
||||
->icon('heroicon-o-clipboard-document-list')
|
||||
->tooltip('Copy')
|
||||
->action(function ($livewire, $state) {
|
||||
->action(function ($livewire, string $state): void {
|
||||
$livewire->js(
|
||||
'window.navigator.clipboard.writeText("'.$state.'");'
|
||||
);
|
||||
@ -164,7 +171,7 @@ protected function getHeaderActions(): array
|
||||
)
|
||||
->helperText('Run this command on your server as root user')
|
||||
->disabled()
|
||||
->visible(fn ($get) => $get('provider') === ServerProvider::CUSTOM),
|
||||
->visible(fn ($get): bool => $get('provider') === ServerProvider::CUSTOM),
|
||||
Grid::make()
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
@ -175,7 +182,7 @@ protected function getHeaderActions(): array
|
||||
->native(false)
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['os'])
|
||||
->options(
|
||||
collect(config('core.operating_systems'))
|
||||
collect((array) config('core.operating_systems'))
|
||||
->mapWithKeys(fn ($value) => [$value => $value])
|
||||
),
|
||||
]),
|
||||
@ -188,7 +195,7 @@ protected function getHeaderActions(): array
|
||||
->label('SSH Port')
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['port']),
|
||||
])
|
||||
->visible(fn ($get) => $get('provider') === ServerProvider::CUSTOM),
|
||||
->visible(fn ($get): bool => $get('provider') === ServerProvider::CUSTOM),
|
||||
Fieldset::make('Services')
|
||||
->columns(1)
|
||||
->schema([
|
||||
@ -204,7 +211,7 @@ protected function getHeaderActions(): array
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['webserver'] ?? [])
|
||||
->default(Webserver::NONE)
|
||||
->options(
|
||||
collect(config('core.webservers'))->mapWithKeys(fn ($value) => [$value => $value])
|
||||
collect((array) config('core.webservers'))->mapWithKeys(fn ($value) => [$value => $value])
|
||||
),
|
||||
Select::make('database')
|
||||
->label('Database')
|
||||
@ -213,7 +220,7 @@ protected function getHeaderActions(): array
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['database'] ?? [])
|
||||
->default(Database::NONE)
|
||||
->options(
|
||||
collect(config('core.databases_name'))
|
||||
collect((array) config('core.databases_name'))
|
||||
->mapWithKeys(fn ($value, $key) => [
|
||||
$key => $value.' '.config('core.databases_version')[$key],
|
||||
])
|
||||
@ -225,16 +232,16 @@ protected function getHeaderActions(): array
|
||||
->rules(fn ($get) => CreateServerAction::rules($project, $get())['php'] ?? [])
|
||||
->default(PHP::NONE)
|
||||
->options(
|
||||
collect(config('core.php_versions'))
|
||||
collect((array) config('core.php_versions'))
|
||||
->mapWithKeys(fn ($value) => [$value => $value])
|
||||
),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
$server = app(CreateServerAction::class)->create(auth()->user(), auth()->user()->currentProject, $data);
|
||||
->action(function (array $data) use ($user, $project): void {
|
||||
run_action($this, function () use ($data, $user, $project): void {
|
||||
$server = app(CreateServerAction::class)->create($user, $project, $data);
|
||||
|
||||
$this->redirect(View::getUrl(['server' => $server]));
|
||||
});
|
||||
|
@ -44,7 +44,7 @@ protected function getHeaderActions(): array
|
||||
->rules(fn (callable $get) => CreateServerLog::rules()['path']),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
app(CreateServerLog::class)->create($this->server, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -12,9 +12,12 @@ trait Navigation
|
||||
{
|
||||
public function getSecondSubNavigation(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$items = [];
|
||||
|
||||
if (auth()->user()->can('viewAny', [ServerLog::class, $this->server])) {
|
||||
if ($user->can('viewAny', [ServerLog::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(Index::getNavigationLabel())
|
||||
->icon('heroicon-o-square-3-stack-3d')
|
||||
->isActiveWhen(fn () => request()->routeIs(Index::getRouteName()))
|
||||
|
@ -25,14 +25,20 @@ class LogsList extends Widget
|
||||
|
||||
public bool $remote = false;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<ServerLog>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return ServerLog::query()
|
||||
->where('server_id', $this->server->id)
|
||||
->where(function (Builder $query) {
|
||||
if ($this->site) {
|
||||
->where(function (Builder $query): void {
|
||||
if ($this->site instanceof \App\Models\Site) {
|
||||
$query->where('site_id', $this->site->id);
|
||||
}
|
||||
})
|
||||
@ -53,6 +59,10 @@ protected function getTableColumns(): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<ServerLog> $query
|
||||
* @return Builder<ServerLog>
|
||||
*/
|
||||
protected function applyDefaultSortingToTableQuery(Builder $query): Builder
|
||||
{
|
||||
return $query->latest('created_at');
|
||||
@ -63,6 +73,9 @@ protected function applyDefaultSortingToTableQuery(Builder $query): Builder
|
||||
*/
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -73,31 +86,27 @@ public function table(Table $table): Table
|
||||
DatePicker::make('created_from'),
|
||||
DatePicker::make('created_until'),
|
||||
])
|
||||
->query(function (Builder $query, array $data): Builder {
|
||||
return $query
|
||||
->when(
|
||||
$data['created_from'],
|
||||
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date),
|
||||
)
|
||||
->when(
|
||||
$data['created_until'],
|
||||
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date),
|
||||
);
|
||||
}),
|
||||
->query(fn (Builder $query, array $data): Builder => $query
|
||||
->when(
|
||||
$data['created_from'],
|
||||
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date),
|
||||
)
|
||||
->when(
|
||||
$data['created_until'],
|
||||
fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date),
|
||||
)),
|
||||
])
|
||||
->actions([
|
||||
Action::make('view')
|
||||
->hiddenLabel()
|
||||
->tooltip('View')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn ($record) => auth()->user()->can('view', $record))
|
||||
->authorize(fn ($record) => $user->can('view', $record))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (ServerLog $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => $record->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalContent(fn (ServerLog $record) => view('components.console-view', [
|
||||
'slot' => $record->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]))
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close'),
|
||||
Action::make('download')
|
||||
@ -105,19 +114,19 @@ public function table(Table $table): Table
|
||||
->tooltip('Download')
|
||||
->color('gray')
|
||||
->icon('heroicon-o-archive-box-arrow-down')
|
||||
->authorize(fn ($record) => auth()->user()->can('view', $record))
|
||||
->action(fn (ServerLog $record) => $record->download()),
|
||||
->authorize(fn ($record) => $user->can('view', $record))
|
||||
->action(fn (ServerLog $record): \Symfony\Component\HttpFoundation\StreamedResponse => $record->download()),
|
||||
DeleteAction::make()
|
||||
->hiddenLabel()
|
||||
->tooltip('Delete')
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->authorize(fn ($record) => auth()->user()->can('delete', $record)),
|
||||
->authorize(fn ($record) => $user->can('delete', $record)),
|
||||
])
|
||||
->bulkActions([
|
||||
DeleteBulkAction::make()
|
||||
->requiresConfirmation()
|
||||
->authorize(auth()->user()->can('deleteMany', [ServerLog::class, $this->server])),
|
||||
->authorize($user->can('deleteMany', [ServerLog::class, $this->server])),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ protected function getHeaderActions(): array
|
||||
->default($this->server->monitoring()?->type_data['data_retention'] ?? 30),
|
||||
])
|
||||
->modalSubmitActionLabel('Save')
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
app(UpdateMetricSettings::class)->update($this->server, $data);
|
||||
|
||||
Notification::make()
|
||||
|
@ -20,19 +20,25 @@ class FilterForm extends Widget implements HasForms
|
||||
|
||||
protected static string $view = 'components.form';
|
||||
|
||||
/**
|
||||
* @var ?array<string, mixed>
|
||||
*/
|
||||
public ?array $data = [
|
||||
'period' => '1h',
|
||||
'from' => null,
|
||||
'to' => null,
|
||||
];
|
||||
|
||||
public function updated($name, $value): void
|
||||
public function updated(string $name, mixed $value): void
|
||||
{
|
||||
if ($value !== 'custom') {
|
||||
$this->dispatch('updateFilters', filters: $this->data);
|
||||
}
|
||||
|
||||
if ($value === 'custom' && $this->data['from'] && $this->data['to']) {
|
||||
$from = $this->data['from'] ?? null;
|
||||
$to = $this->data['to'] ?? null;
|
||||
|
||||
if ($value === 'custom' && $from && $to) {
|
||||
$this->dispatch('updateFilters', filters: $this->data);
|
||||
}
|
||||
}
|
||||
@ -61,12 +67,12 @@ public function form(Form $form): Form
|
||||
->rules(fn (Get $get) => GetMetrics::rules($get())['period']),
|
||||
DatePicker::make('from')
|
||||
->reactive()
|
||||
->visible(fn (Get $get) => $get('period') === 'custom')
|
||||
->visible(fn (Get $get): bool => $get('period') === 'custom')
|
||||
->maxDate(fn (Get $get) => now())
|
||||
->rules(fn (Get $get) => GetMetrics::rules($get())['from']),
|
||||
DatePicker::make('to')
|
||||
->reactive()
|
||||
->visible(fn (Get $get) => $get('period') === 'custom')
|
||||
->visible(fn (Get $get): bool => $get('period') === 'custom')
|
||||
->minDate(fn (Get $get) => $get('from') ?: now())
|
||||
->maxDate(now())
|
||||
->rules(fn (Get $get) => GetMetrics::rules($get())['to']),
|
||||
|
@ -20,6 +20,9 @@ class MetricDetails extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
@ -14,10 +14,16 @@ class Metrics extends BaseWidget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public array $filters = [];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $filters
|
||||
*/
|
||||
#[On('updateFilters')]
|
||||
public function updateFilters(array $filters): void
|
||||
{
|
||||
@ -34,13 +40,13 @@ protected function getStats(): array
|
||||
$metrics = app(GetMetrics::class)->filter($this->server, $this->filters);
|
||||
|
||||
return [
|
||||
Stat::make('CPU Load', $lastMetric?->load ?? 0)
|
||||
Stat::make('CPU Load', $lastMetric->load ?? 0)
|
||||
->color('success')
|
||||
->chart($metrics->pluck('load')->toArray()),
|
||||
Stat::make('Memory Usage', Number::fileSize($lastMetric?->memory_used_in_bytes ?? 0, 2))
|
||||
Stat::make('Memory Usage', Number::fileSize($lastMetric->memory_used_in_bytes ?? 0, 2))
|
||||
->color('warning')
|
||||
->chart($metrics->pluck('memory_used')->toArray()),
|
||||
Stat::make('Disk Usage', Number::fileSize($lastMetric?->disk_used_in_bytes ?? 0, 2))
|
||||
Stat::make('Disk Usage', Number::fileSize($lastMetric->disk_used_in_bytes ?? 0, 2))
|
||||
->color('primary')
|
||||
->chart($metrics->pluck('disk_used')->toArray()),
|
||||
];
|
||||
|
@ -43,15 +43,15 @@ protected function getHeaderActions(): array
|
||||
->form([
|
||||
Select::make('version')
|
||||
->options(
|
||||
collect(config('core.nodejs_versions'))
|
||||
->filter(fn ($version) => ! in_array($version, array_merge($installedNodeVersions, [NodeJS::NONE])))
|
||||
collect((array) config('core.nodejs_versions'))
|
||||
->filter(fn ($version): bool => ! in_array($version, array_merge($installedNodeVersions, [NodeJS::NONE])))
|
||||
->mapWithKeys(fn ($version) => [$version => $version])
|
||||
->toArray()
|
||||
)
|
||||
->rules(InstallNewNodeJsVersion::rules($this->server)['version']),
|
||||
])
|
||||
->modalSubmitActionLabel('Install')
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
app(InstallNewNodeJsVersion::class)->install($this->server, $data);
|
||||
|
||||
Notification::make()
|
||||
|
@ -19,8 +19,14 @@ class NodeJSList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Service>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Service::query()->where('type', 'nodejs')->where('server_id', $this->server->id);
|
||||
@ -39,8 +45,8 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('is_default')
|
||||
->label('Default Cli')
|
||||
->badge()
|
||||
->color(fn (Service $service) => $service->is_default ? 'primary' : 'gray')
|
||||
->state(fn (Service $service) => $service->is_default ? 'Yes' : 'No')
|
||||
->color(fn (Service $service): string => $service->is_default ? 'primary' : 'gray')
|
||||
->state(fn (Service $service): string => $service->is_default ? 'Yes' : 'No')
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->label('Installed At')
|
||||
@ -71,7 +77,7 @@ private function defaultNodeJsCliAction(): Action
|
||||
->authorize(fn (Service $nodejs) => auth()->user()?->can('update', $nodejs))
|
||||
->label('Make Default CLI')
|
||||
->hidden(fn (Service $service) => $service->is_default)
|
||||
->action(function (Service $service) {
|
||||
->action(function (Service $service): void {
|
||||
try {
|
||||
app(ChangeDefaultCli::class)->change($this->server, ['version' => $service->version]);
|
||||
|
||||
@ -99,7 +105,7 @@ private function uninstallAction(): Action
|
||||
->label('Uninstall')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(function (Service $service) {
|
||||
->action(function (Service $service): void {
|
||||
try {
|
||||
app(Uninstall::class)->uninstall($service);
|
||||
} catch (Exception $e) {
|
||||
|
@ -43,15 +43,15 @@ protected function getHeaderActions(): array
|
||||
->form([
|
||||
Select::make('version')
|
||||
->options(
|
||||
collect(config('core.php_versions'))
|
||||
->filter(fn ($version) => ! in_array($version, array_merge($installedPHPs, [PHP::NONE])))
|
||||
collect((array) config('core.php_versions'))
|
||||
->filter(fn ($version): bool => ! in_array($version, array_merge($installedPHPs, [PHP::NONE])))
|
||||
->mapWithKeys(fn ($version) => [$version => $version])
|
||||
->toArray()
|
||||
)
|
||||
->rules(InstallNewPHP::rules($this->server)['version']),
|
||||
])
|
||||
->modalSubmitActionLabel('Install')
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
app(InstallNewPHP::class)->install($this->server, $data);
|
||||
|
||||
Notification::make()
|
||||
|
@ -29,8 +29,14 @@ class PHPList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Service>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Service::query()->where('type', 'php')->where('server_id', $this->server->id);
|
||||
@ -49,8 +55,8 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('is_default')
|
||||
->label('Default Cli')
|
||||
->badge()
|
||||
->color(fn (Service $service) => $service->is_default ? 'primary' : 'gray')
|
||||
->state(fn (Service $service) => $service->is_default ? 'Yes' : 'No')
|
||||
->color(fn (Service $service): string => $service->is_default ? 'primary' : 'gray')
|
||||
->state(fn (Service $service): string => $service->is_default ? 'Yes' : 'No')
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->label('Installed At')
|
||||
@ -93,12 +99,12 @@ private function installExtensionAction(): Action
|
||||
->rules(InstallPHPExtension::rules($this->server)['version']),
|
||||
Select::make('extension')
|
||||
->options(
|
||||
collect(config('core.php_extensions'))
|
||||
collect((array) config('core.php_extensions'))
|
||||
->mapWithKeys(fn ($extension) => [$extension => $extension])
|
||||
)
|
||||
->rules(InstallPHPExtension::rules($this->server)['extension']),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
$this->validate();
|
||||
|
||||
try {
|
||||
@ -132,7 +138,7 @@ private function defaultPHPCliAction(): Action
|
||||
->authorize(fn (Service $php) => auth()->user()?->can('update', $php))
|
||||
->label('Make Default CLI')
|
||||
->hidden(fn (Service $service) => $service->is_default)
|
||||
->action(function (Service $service) {
|
||||
->action(function (Service $service): void {
|
||||
try {
|
||||
app(ChangeDefaultCli::class)->change($this->server, ['version' => $service->version]);
|
||||
|
||||
@ -176,7 +182,7 @@ private function editPHPIniAction(string $type): Action
|
||||
'version' => $service->version,
|
||||
])),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
$this->validate();
|
||||
|
||||
try {
|
||||
@ -205,7 +211,7 @@ private function restartFPMAction(): Action
|
||||
return Action::make('restart-fpm')
|
||||
->authorize(fn (Service $php) => auth()->user()?->can('update', $php))
|
||||
->label('Restart PHP FPM')
|
||||
->action(function (Service $service) {
|
||||
->action(function (Service $service): void {
|
||||
try {
|
||||
app(Manage::class)->restart($service);
|
||||
} catch (Exception $e) {
|
||||
@ -228,7 +234,7 @@ private function uninstallAction(): Action
|
||||
->label('Uninstall')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(function (Service $service) {
|
||||
->action(function (Service $service): void {
|
||||
try {
|
||||
app(Uninstall::class)->uninstall($service);
|
||||
} catch (Exception $e) {
|
||||
|
@ -37,100 +37,103 @@ abstract class Page extends BasePage
|
||||
|
||||
public function getSubNavigation(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$items = [];
|
||||
|
||||
if (auth()->user()->can('view', $this->server)) {
|
||||
if ($user->can('view', $this->server)) {
|
||||
$items[] = NavigationItem::make(ServerView::getNavigationLabel())
|
||||
->icon('heroicon-o-chart-pie')
|
||||
->isActiveWhen(fn () => request()->routeIs(ServerView::getRouteName()))
|
||||
->url(ServerView::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [Site::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Site::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(SitesIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-cursor-arrow-ripple')
|
||||
->isActiveWhen(fn () => request()->routeIs(SitesIndex::getRouteName().'*'))
|
||||
->url(SitesIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [Database::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Database::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(DatabasesIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-circle-stack')
|
||||
->isActiveWhen(fn () => request()->routeIs(DatabasesIndex::getRouteName().'*'))
|
||||
->url(DatabasesIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('manage', $this->server)) {
|
||||
if ($user->can('manage', $this->server)) {
|
||||
$items[] = NavigationItem::make(FileManagerIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-folder')
|
||||
->isActiveWhen(fn () => request()->routeIs(FileManagerIndex::getRouteName().'*'))
|
||||
->url(FileManagerIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [Service::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Service::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(PHPIndex::getNavigationLabel())
|
||||
->icon('icon-php-alt')
|
||||
->isActiveWhen(fn () => request()->routeIs(PHPIndex::getRouteName().'*'))
|
||||
->url(PHPIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [Service::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Service::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(NodeJsIndex::getNavigationLabel())
|
||||
->icon('icon-nodejs-alt')
|
||||
->isActiveWhen(fn () => request()->routeIs(NodeJsIndex::getRouteName().'*'))
|
||||
->url(NodeJsIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [FirewallRule::class, $this->server])) {
|
||||
if ($user->can('viewAny', [FirewallRule::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(FirewallIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-fire')
|
||||
->isActiveWhen(fn () => request()->routeIs(FirewallIndex::getRouteName().'*'))
|
||||
->url(FirewallIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [CronJob::class, $this->server])) {
|
||||
if ($user->can('viewAny', [CronJob::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(CronJobsIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-clock')
|
||||
->isActiveWhen(fn () => request()->routeIs(CronJobsIndex::getRouteName().'*'))
|
||||
->url(CronJobsIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAnyServer', [SshKey::class, $this->server])) {
|
||||
if ($user->can('viewAnyServer', [SshKey::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(SshKeysIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-key')
|
||||
->isActiveWhen(fn () => request()->routeIs(SshKeysIndex::getRouteName().'*'))
|
||||
->url(SshKeysIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [Service::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Service::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(ServicesIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-cog-6-tooth')
|
||||
->isActiveWhen(fn () => request()->routeIs(ServicesIndex::getRouteName().'*'))
|
||||
->url(ServicesIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [Metric::class, $this->server])) {
|
||||
if ($user->can('viewAny', [Metric::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(MetricsIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-chart-bar')
|
||||
->isActiveWhen(fn () => request()->routeIs(MetricsIndex::getRouteName().'*'))
|
||||
->url(MetricsIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('manage', $this->server)) {
|
||||
if ($user->can('manage', $this->server)) {
|
||||
$items[] = NavigationItem::make(ConsoleIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-command-line')
|
||||
->isActiveWhen(fn () => request()->routeIs(ConsoleIndex::getRouteName().'*'))
|
||||
->url(ConsoleIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [ServerLog::class, $this->server])) {
|
||||
if ($user->can('viewAny', [ServerLog::class, $this->server])) {
|
||||
$items[] = NavigationItem::make(LogsIndex::getNavigationLabel())
|
||||
->icon('heroicon-o-square-3-stack-3d')
|
||||
->isActiveWhen(fn () => request()->routeIs(LogsIndex::getRouteName().'*'))
|
||||
->url(LogsIndex::getUrl(parameters: ['server' => $this->server]));
|
||||
}
|
||||
|
||||
if (auth()->user()->can('update', $this->server)) {
|
||||
if ($user->can('update', $this->server)) {
|
||||
$items[] = NavigationItem::make(ServerSettings::getNavigationLabel())
|
||||
->icon('heroicon-o-wrench-screwdriver')
|
||||
->isActiveWhen(fn () => request()->routeIs(ServerSettings::getRouteName().'*'))
|
||||
|
@ -34,10 +34,13 @@ public function getWidgets(): array
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
Action::make('deploy')
|
||||
->label('Deploy a Key')
|
||||
->authorize(fn () => auth()->user()?->can('createServer', [SshKey::class, $this->server]))
|
||||
->authorize(fn () => $user->can('createServer', [SshKey::class, $this->server]))
|
||||
->icon('heroicon-o-rocket-launch')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->form([
|
||||
@ -50,25 +53,25 @@ protected function getHeaderActions(): array
|
||||
->default('existing'),
|
||||
Select::make('key_id')
|
||||
->label('Key')
|
||||
->options(auth()->user()->sshKeys()->pluck('name', 'id')->toArray())
|
||||
->visible(fn ($get) => $get('type') === 'existing')
|
||||
->rules(DeployKeyToServer::rules(auth()->user(), $this->server)['key_id']),
|
||||
->options($user->sshKeys()->pluck('name', 'id')->toArray())
|
||||
->visible(fn ($get): bool => $get('type') === 'existing')
|
||||
->rules(DeployKeyToServer::rules($user, $this->server)['key_id']),
|
||||
TextInput::make('name')
|
||||
->label('Name')
|
||||
->visible(fn ($get) => $get('type') === 'new')
|
||||
->visible(fn ($get): bool => $get('type') === 'new')
|
||||
->rules(CreateSshKey::rules()['name']),
|
||||
Textarea::make('public_key')
|
||||
->label('Public Key')
|
||||
->visible(fn ($get) => $get('type') === 'new')
|
||||
->visible(fn ($get): bool => $get('type') === 'new')
|
||||
->rules(CreateSshKey::rules()['public_key']),
|
||||
])
|
||||
->modalSubmitActionLabel('Deploy')
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data) use ($user): void {
|
||||
$this->validate();
|
||||
|
||||
try {
|
||||
if (! isset($data['key_id'])) {
|
||||
$data['key_id'] = app(CreateSshKey::class)->create(auth()->user(), $data)->id;
|
||||
$data['key_id'] = app(CreateSshKey::class)->create($user, $data)->id;
|
||||
}
|
||||
|
||||
app(DeployKeyToServer::class)->deploy($this->server, $data);
|
||||
|
@ -17,8 +17,14 @@ class SshKeysList extends TableWidget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<SshKey>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return SshKey::withTrashed()
|
||||
@ -44,6 +50,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -51,8 +60,8 @@ public function table(Table $table): Table
|
||||
->actions([
|
||||
DeleteAction::make('delete')
|
||||
->hiddenLabel()
|
||||
->authorize(fn (SshKey $record) => auth()->user()->can('deleteServer', [SshKey::class, $this->server]))
|
||||
->using(function (SshKey $record) {
|
||||
->authorize(fn (SshKey $record) => $user->can('deleteServer', [SshKey::class, $this->server]))
|
||||
->using(function (SshKey $record): void {
|
||||
try {
|
||||
app(DeleteKeyFromServer::class)->delete($this->server, $record);
|
||||
} catch (Exception $e) {
|
||||
|
@ -57,13 +57,13 @@ protected function getHeaderActions(): array
|
||||
return [];
|
||||
}
|
||||
|
||||
return collect(config("core.service_versions.{$get('name')}"))
|
||||
return collect((array) config("core.service_versions.{$get('name')}"))
|
||||
->mapWithKeys(fn ($version) => [$version => $version]);
|
||||
})
|
||||
->rules(fn ($get) => Install::rules($get())['version'])
|
||||
->reactive(),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
$this->validate();
|
||||
|
||||
try {
|
||||
|
@ -21,8 +21,14 @@ class ServicesList extends TableWidget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Service>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Service::query()->where('server_id', $this->server->id);
|
||||
@ -33,7 +39,7 @@ protected function getTableColumns(): array
|
||||
return [
|
||||
IconColumn::make('id')
|
||||
->label('Service')
|
||||
->icon(fn (Service $record) => 'icon-'.$record->name)
|
||||
->icon(fn (Service $record): string => 'icon-'.$record->name)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->sortable(),
|
||||
@ -78,7 +84,7 @@ private function serviceAction(string $type, string $icon): Action
|
||||
->authorize(fn (Service $service) => auth()->user()?->can($type, $service))
|
||||
->label(ucfirst($type).' Service')
|
||||
->icon($icon)
|
||||
->action(function (Service $service) use ($type) {
|
||||
->action(function (Service $service) use ($type): void {
|
||||
try {
|
||||
app(Manage::class)->$type($service);
|
||||
} catch (Exception $e) {
|
||||
@ -102,7 +108,7 @@ private function uninstallAction(): Action
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(function (Service $service) {
|
||||
->action(function (Service $service): void {
|
||||
try {
|
||||
app(Uninstall::class)->uninstall($service);
|
||||
|
||||
|
@ -17,11 +17,17 @@ class Settings extends Page
|
||||
|
||||
protected static ?string $title = 'Settings';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('update', [$this->server, auth()->user()->currentProject]);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$this->authorize('update', [$this->server, $user->currentProject]);
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
@ -46,7 +52,7 @@ protected function getHeaderActions(): array
|
||||
->modalHeading('Delete Server')
|
||||
->modalDescription('Once your server is deleted, all of its resources and data will be permanently deleted and can\'t be restored')
|
||||
->authorize('delete', $this->server)
|
||||
->action(function () {
|
||||
->action(function (): void {
|
||||
try {
|
||||
$this->server->delete();
|
||||
|
||||
@ -68,7 +74,7 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->label('Reboot')
|
||||
->requiresConfirmation()
|
||||
->action(function () {
|
||||
->action(function (): void {
|
||||
app(RebootServer::class)->reboot($this->server);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -7,6 +7,7 @@
|
||||
use App\Enums\SiteType;
|
||||
use App\Models\Site;
|
||||
use App\Models\SourceControl;
|
||||
use App\Models\User;
|
||||
use App\Web\Pages\Settings\SourceControls\Actions\Create;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
@ -41,6 +42,9 @@ public function getWidgets(): array
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
Action::make('read-the-docs')
|
||||
->label('Read the Docs')
|
||||
@ -51,17 +55,17 @@ protected function getHeaderActions(): array
|
||||
Action::make('create')
|
||||
->label('Create a Site')
|
||||
->icon('heroicon-o-plus')
|
||||
->authorize(fn () => auth()->user()?->can('create', [Site::class, $this->server]))
|
||||
->authorize(fn () => $user->can('create', [Site::class, $this->server]))
|
||||
->modalWidth(MaxWidth::FiveExtraLarge)
|
||||
->slideOver()
|
||||
->form([
|
||||
Select::make('type')
|
||||
->label('Site Type')
|
||||
->options(
|
||||
collect(config('core.site_types'))->mapWithKeys(fn ($type) => [$type => $type])
|
||||
collect((array) config('core.site_types'))->mapWithKeys(fn ($type) => [$type => $type])
|
||||
)
|
||||
->reactive()
|
||||
->afterStateUpdated(function (?string $state, Set $set) {
|
||||
->afterStateUpdated(function (?string $state, Set $set): void {
|
||||
if ($state === SiteType::LARAVEL) {
|
||||
$set('web_directory', 'public');
|
||||
} else {
|
||||
@ -78,12 +82,12 @@ protected function getHeaderActions(): array
|
||||
Select::make('php_version')
|
||||
->label('PHP Version')
|
||||
->options(collect($this->server->installedPHPVersions())->mapWithKeys(fn ($version) => [$version => $version]))
|
||||
->visible(fn (Get $get) => isset(CreateSite::rules($this->server, $get())['php_version']))
|
||||
->visible(fn (Get $get): bool => isset(CreateSite::rules($this->server, $get())['php_version']))
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['php_version']),
|
||||
TextInput::make('web_directory')
|
||||
->placeholder('For / leave empty')
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['web_directory'])
|
||||
->visible(fn (Get $get) => isset(CreateSite::rules($this->server, $get())['web_directory']))
|
||||
->visible(fn (Get $get): bool => isset(CreateSite::rules($this->server, $get())['web_directory']))
|
||||
->helperText(
|
||||
sprintf(
|
||||
'The relative path of your website from /home/%s/your-domain/',
|
||||
@ -94,7 +98,7 @@ protected function getHeaderActions(): array
|
||||
->label('Source Control')
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['source_control'])
|
||||
->options(
|
||||
SourceControl::getByProjectId(auth()->user()->current_project_id)
|
||||
SourceControl::getByProjectId($user->current_project_id)
|
||||
->pluck('profile', 'id')
|
||||
)
|
||||
->suffixAction(
|
||||
@ -105,24 +109,24 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-wifi')
|
||||
->tooltip('Connect to a source control')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->authorize(fn () => auth()->user()->can('create', SourceControl::class))
|
||||
->authorize(fn () => $user->can('create', SourceControl::class))
|
||||
->action(fn (array $data) => Create::action($data))
|
||||
)
|
||||
->placeholder('Select source control')
|
||||
->live()
|
||||
->visible(fn (Get $get) => isset(CreateSite::rules($this->server, $get())['source_control'])),
|
||||
->visible(fn (Get $get): bool => isset(CreateSite::rules($this->server, $get())['source_control'])),
|
||||
TextInput::make('repository')
|
||||
->placeholder('organization/repository')
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['repository'])
|
||||
->visible(fn (Get $get) => isset(CreateSite::rules($this->server, $get())['repository'])),
|
||||
->visible(fn (Get $get): bool => isset(CreateSite::rules($this->server, $get())['repository'])),
|
||||
TextInput::make('branch')
|
||||
->placeholder('main')
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['branch'])
|
||||
->visible(fn (Get $get) => isset(CreateSite::rules($this->server, $get())['branch'])),
|
||||
->visible(fn (Get $get): bool => isset(CreateSite::rules($this->server, $get())['branch'])),
|
||||
Checkbox::make('composer')
|
||||
->label('Run `composer install --no-dev`')
|
||||
->default(false)
|
||||
->visible(fn (Get $get) => isset(CreateSite::rules($this->server, $get())['composer'])),
|
||||
->visible(fn (Get $get): bool => isset(CreateSite::rules($this->server, $get())['composer'])),
|
||||
// PHPMyAdmin
|
||||
Select::make('version')
|
||||
->label('PHPMyAdmin Version')
|
||||
@ -130,7 +134,7 @@ protected function getHeaderActions(): array
|
||||
->options([
|
||||
'5.2.1' => '5.2.1',
|
||||
])
|
||||
->visible(fn (Get $get) => $get('type') === SiteType::PHPMYADMIN)
|
||||
->visible(fn (Get $get): bool => $get('type') === SiteType::PHPMYADMIN)
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['version']),
|
||||
// WordPress
|
||||
$this->wordpressFields(),
|
||||
@ -142,7 +146,7 @@ protected function getHeaderActions(): array
|
||||
collect(LoadBalancerMethod::all())
|
||||
->mapWithKeys(fn ($method) => [$method => $method])
|
||||
)
|
||||
->visible(fn (Get $get) => $get('type') === SiteType::LOAD_BALANCER)
|
||||
->visible(fn (Get $get): bool => $get('type') === SiteType::LOAD_BALANCER)
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['method'] ?? []),
|
||||
// User
|
||||
TextInput::make('user')
|
||||
@ -153,7 +157,7 @@ protected function getHeaderActions(): array
|
||||
)
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['user']),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
$this->authorize('create', [Site::class, $this->server]);
|
||||
|
||||
$this->validate();
|
||||
@ -178,9 +182,12 @@ protected function getHeaderActions(): array
|
||||
|
||||
private function wordpressFields(): Component
|
||||
{
|
||||
/** @var User */
|
||||
$user = auth()->user();
|
||||
|
||||
return Grid::make()
|
||||
->columns(3)
|
||||
->visible(fn (Get $get) => $get('type') === SiteType::WORDPRESS)
|
||||
->visible(fn (Get $get): bool => $get('type') === SiteType::WORDPRESS)
|
||||
->schema([
|
||||
TextInput::make('title')
|
||||
->label('Site Title')
|
||||
@ -188,7 +195,7 @@ private function wordpressFields(): Component
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['title']),
|
||||
TextInput::make('email')
|
||||
->label('WP Admin Email')
|
||||
->default(auth()->user()?->email)
|
||||
->default($user->email)
|
||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['email']),
|
||||
TextInput::make('username')
|
||||
->label('WP Admin Username')
|
||||
|
@ -20,6 +20,7 @@ abstract class Page extends BasePage implements HasSecondSubNav
|
||||
|
||||
public function getSecondSubNavigation(): array
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$items = [];
|
||||
|
||||
@ -99,7 +100,10 @@ protected static function getSiteFromRoute(): ?Site
|
||||
}
|
||||
|
||||
if ($site) {
|
||||
return Site::query()->find($site);
|
||||
/** @var Site $site */
|
||||
$site = Site::query()->findOrFail($site);
|
||||
|
||||
return $site;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -67,8 +67,8 @@ protected function getHeaderActions(): array
|
||||
->default(false),
|
||||
]),
|
||||
])
|
||||
->using(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->using(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(CreateQueue::class)->create($this->site, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -27,8 +27,14 @@ class QueuesList extends Widget
|
||||
{
|
||||
public Site $site;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Queue>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Queue::query()->where('site_id', $this->site->id);
|
||||
@ -75,12 +81,15 @@ public function table(Table $table): Table
|
||||
|
||||
private function operationAction(string $type, string $icon): Action
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return Action::make($type)
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('update', [$record, $this->site, $this->site->server]))
|
||||
->authorize(fn (Queue $record) => $user->can('update', [$record, $this->site, $this->site->server]))
|
||||
->label(ucfirst($type).' queue')
|
||||
->icon($icon)
|
||||
->action(function (Queue $record) use ($type) {
|
||||
run_action($this, function () use ($record, $type) {
|
||||
->action(function (Queue $record) use ($type): void {
|
||||
run_action($this, function () use ($record, $type): void {
|
||||
app(ManageQueue::class)->$type($record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
@ -89,27 +98,31 @@ private function operationAction(string $type, string $icon): Action
|
||||
|
||||
private function logsAction(): Action
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return Action::make('logs')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('view', [$record, $this->site, $this->site->server]))
|
||||
->authorize(fn (Queue $record) => $user->can('view', [$record, $this->site, $this->site->server]))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (Queue $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => app(GetQueueLogs::class)->getLogs($record),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalContent(fn (Queue $record) => view('components.console-view', [
|
||||
'slot' => app(GetQueueLogs::class)->getLogs($record),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]))
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close');
|
||||
}
|
||||
|
||||
private function editAction(): Action
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return EditAction::make('edit')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('update', [$record, $this->site, $this->site->server]))
|
||||
->authorize(fn (Queue $record) => $user->can('update', [$record, $this->site, $this->site->server]))
|
||||
->modalWidth(MaxWidth::ExtraLarge)
|
||||
->fillForm(fn (Queue $record) => [
|
||||
->fillForm(fn (Queue $record): array => [
|
||||
'command' => $record->command,
|
||||
'user' => $record->user,
|
||||
'numprocs' => $record->numprocs,
|
||||
@ -138,8 +151,8 @@ private function editAction(): Action
|
||||
->default(false),
|
||||
]),
|
||||
])
|
||||
->using(function (Queue $record, array $data) {
|
||||
run_action($this, function () use ($record, $data) {
|
||||
->using(function (Queue $record, array $data): void {
|
||||
run_action($this, function () use ($record, $data): void {
|
||||
app(EditQueue::class)->edit($record, $data);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
@ -148,11 +161,14 @@ private function editAction(): Action
|
||||
|
||||
private function deleteAction(): Action
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return DeleteAction::make('delete')
|
||||
->icon('heroicon-o-trash')
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (Queue $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->authorize(fn (Queue $record) => $user->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (Queue $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
app(DeleteQueue::class)->delete($record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
|
@ -47,11 +47,11 @@ protected function getHeaderActions(): array
|
||||
->openUrlInNewTab(),
|
||||
Action::make('force-ssl')
|
||||
->label('Force SSL')
|
||||
->tooltip(fn () => $this->site->force_ssl ? 'Disable force SSL' : 'Enable force SSL')
|
||||
->icon(fn () => $this->site->force_ssl ? 'icon-force-ssl-enabled' : 'icon-force-ssl-disabled')
|
||||
->tooltip(fn (): string => $this->site->force_ssl ? 'Disable force SSL' : 'Enable force SSL')
|
||||
->icon(fn (): string => $this->site->force_ssl ? 'icon-force-ssl-enabled' : 'icon-force-ssl-disabled')
|
||||
->requiresConfirmation()
|
||||
->modalSubmitActionLabel(fn () => $this->site->force_ssl ? 'Disable' : 'Enable')
|
||||
->action(function () {
|
||||
->modalSubmitActionLabel(fn (): string => $this->site->force_ssl ? 'Disable' : 'Enable')
|
||||
->action(function (): void {
|
||||
$this->site->update([
|
||||
'force_ssl' => ! $this->site->force_ssl,
|
||||
]);
|
||||
@ -72,34 +72,34 @@ protected function getHeaderActions(): array
|
||||
->message('Let\'s Encrypt has rate limits. Read more about them <a href="https://letsencrypt.org/docs/rate-limits/" target="_blank" class="underline">here</a>.'),
|
||||
Select::make('type')
|
||||
->options(
|
||||
collect(config('core.ssl_types'))->mapWithKeys(fn ($type) => [$type => $type])
|
||||
collect((array) config('core.ssl_types'))->mapWithKeys(fn ($type) => [$type => $type])
|
||||
)
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['type'])
|
||||
->reactive(),
|
||||
TextInput::make('email')
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['email'] ?? [])
|
||||
->visible(fn (Get $get) => $get('type') === SslType::LETSENCRYPT)
|
||||
->visible(fn (Get $get): bool => $get('type') === SslType::LETSENCRYPT)
|
||||
->helperText('Email address to provide to Certbot.'),
|
||||
Textarea::make('certificate')
|
||||
->rows(5)
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['certificate'])
|
||||
->visible(fn (Get $get) => $get('type') === SslType::CUSTOM),
|
||||
->visible(fn (Get $get): bool => $get('type') === SslType::CUSTOM),
|
||||
Textarea::make('private')
|
||||
->label('Private Key')
|
||||
->rows(5)
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['private'])
|
||||
->visible(fn (Get $get) => $get('type') === SslType::CUSTOM),
|
||||
->visible(fn (Get $get): bool => $get('type') === SslType::CUSTOM),
|
||||
DatePicker::make('expires_at')
|
||||
->format('Y-m-d')
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['expires_at'])
|
||||
->visible(fn (Get $get) => $get('type') === SslType::CUSTOM),
|
||||
->visible(fn (Get $get): bool => $get('type') === SslType::CUSTOM),
|
||||
Checkbox::make('aliases')
|
||||
->label("Set SSL for site's aliases as well"),
|
||||
])
|
||||
->createAnother(false)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->using(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->using(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(CreateSSL::class)->create($this->site, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -20,8 +20,14 @@ class SslsList extends Widget
|
||||
{
|
||||
public Site $site;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Ssl>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Ssl::query()->where('site_id', $this->site->id);
|
||||
@ -29,10 +35,12 @@ protected function getTableQuery(): Builder
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
auth()->user();
|
||||
|
||||
return [
|
||||
IconColumn::make('is_active')
|
||||
->color(fn (Ssl $record) => $record->is_active ? 'green' : 'gray')
|
||||
->icon(fn (Ssl $record) => $record->is_active ? 'heroicon-o-lock-closed' : 'heroicon-o-lock-open'),
|
||||
->color(fn (Ssl $record): string => $record->is_active ? 'green' : 'gray')
|
||||
->icon(fn (Ssl $record): string => $record->is_active ? 'heroicon-o-lock-closed' : 'heroicon-o-lock-open'),
|
||||
TextColumn::make('type')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
@ -40,7 +48,7 @@ protected function getTableColumns(): array
|
||||
->formatStateUsing(fn (Ssl $record) => $record->created_at_by_timezone)
|
||||
->sortable(),
|
||||
TextColumn::make('expires_at')
|
||||
->formatStateUsing(fn (Ssl $record) => $record->getDateTimeByTimezone($record->expires_at))
|
||||
->formatStateUsing(fn (Ssl $record): string => $record->getDateTimeByTimezone($record->expires_at))
|
||||
->sortable(),
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
@ -53,6 +61,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -60,15 +71,15 @@ public function table(Table $table): Table
|
||||
->actions([
|
||||
Action::make('activate-ssl')
|
||||
->hiddenLabel()
|
||||
->visible(fn (Ssl $record) => ! $record->is_active)
|
||||
->visible(fn (Ssl $record): bool => ! $record->is_active)
|
||||
->tooltip('Activate SSL')
|
||||
->icon('heroicon-o-lock-closed')
|
||||
->authorize(fn (Ssl $record) => auth()->user()->can('update', [$record->site, $this->site->server]))
|
||||
->authorize(fn (Ssl $record) => $user->can('update', [$record->site, $this->site->server]))
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Activate SSL')
|
||||
->modalSubmitActionLabel('Activate')
|
||||
->action(function (Ssl $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->action(function (Ssl $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
app(ActivateSSL::class)->activate($record);
|
||||
|
||||
Notification::make()
|
||||
@ -81,23 +92,21 @@ public function table(Table $table): Table
|
||||
->hiddenLabel()
|
||||
->tooltip('Logs')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn (Ssl $record) => auth()->user()->can('view', [$record, $this->site, $this->site->server]))
|
||||
->authorize(fn (Ssl $record) => $user->can('view', [$record, $this->site, $this->site->server]))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (Ssl $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => $record->log?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalContent(fn (Ssl $record) => view('components.console-view', [
|
||||
'slot' => $record->log?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]))
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close'),
|
||||
DeleteAction::make('delete')
|
||||
->hiddenLabel()
|
||||
->tooltip('Delete')
|
||||
->icon('heroicon-o-trash')
|
||||
->authorize(fn (Ssl $record) => auth()->user()->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (Ssl $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->authorize(fn (Ssl $record) => $user->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (Ssl $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
app(DeleteSSL::class)->delete($record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace App\Web\Pages\Servers\Sites;
|
||||
|
||||
use App\Actions\Site\DeleteSite;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use App\Web\Fields\CodeEditorField;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\DeleteAction;
|
||||
@ -15,6 +14,9 @@ class Settings extends Page
|
||||
|
||||
protected static ?string $title = 'Settings';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
@ -44,8 +46,8 @@ private function deleteAction(): Action
|
||||
->record($this->site)
|
||||
->modalHeading('Delete Site')
|
||||
->modalDescription('Once your site is deleted, all of its resources and data will be permanently deleted and can\'t be restored')
|
||||
->using(function () {
|
||||
run_action($this, function () {
|
||||
->using(function (): void {
|
||||
run_action($this, function (): void {
|
||||
app(DeleteSite::class)->delete($this->site);
|
||||
|
||||
$this->redirect(Index::getUrl(['server' => $this->server]));
|
||||
@ -62,19 +64,12 @@ private function vhostAction(): Action
|
||||
->modalSubmitActionLabel('Save')
|
||||
->form([
|
||||
CodeEditorField::make('vhost')
|
||||
->formatStateUsing(function () {
|
||||
/** @var Webserver $handler */
|
||||
$handler = $this->server->webserver()->handler();
|
||||
|
||||
return $handler->getVhost($this->site);
|
||||
})
|
||||
->formatStateUsing(fn (): string => $this->site->webserver()->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, $data['vhost']);
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
$this->site->webserver()->updateVHost($this->site, $data['vhost']);
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('VHost updated!')
|
||||
|
@ -114,7 +114,7 @@ private function deployAction(): Action
|
||||
{
|
||||
return Action::make('deploy')
|
||||
->icon('heroicon-o-rocket-launch')
|
||||
->action(function () {
|
||||
->action(function (): void {
|
||||
if (! $this->site->deploymentScript?->content) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
@ -123,7 +123,7 @@ private function deployAction(): Action
|
||||
|
||||
return;
|
||||
}
|
||||
run_action($this, function () {
|
||||
run_action($this, function (): void {
|
||||
app(Deploy::class)->run($this->site);
|
||||
|
||||
Notification::make()
|
||||
@ -139,12 +139,12 @@ private function deployAction(): Action
|
||||
private function autoDeploymentAction(): Action
|
||||
{
|
||||
return Action::make('auto-deployment')
|
||||
->label(fn () => $this->site->isAutoDeployment() ? 'Disable Auto Deployment' : 'Enable Auto Deployment')
|
||||
->modalHeading(fn () => $this->site->isAutoDeployment() ? 'Disable Auto Deployment' : 'Enable Auto Deployment')
|
||||
->modalIconColor(fn () => $this->site->isAutoDeployment() ? 'red' : 'green')
|
||||
->label(fn (): string => $this->site->isAutoDeployment() ? 'Disable Auto Deployment' : 'Enable Auto Deployment')
|
||||
->modalHeading(fn (): string => $this->site->isAutoDeployment() ? 'Disable Auto Deployment' : 'Enable Auto Deployment')
|
||||
->modalIconColor(fn (): string => $this->site->isAutoDeployment() ? 'red' : 'green')
|
||||
->requiresConfirmation()
|
||||
->action(function () {
|
||||
run_action($this, function () {
|
||||
->action(function (): void {
|
||||
run_action($this, function (): void {
|
||||
$this->site->isAutoDeployment()
|
||||
? $this->site->disableAutoDeployment()
|
||||
: $this->site->enableAutoDeployment();
|
||||
@ -168,8 +168,8 @@ private function deploymentScriptAction(): Action
|
||||
->default($this->site->deploymentScript?->content)
|
||||
->rules(UpdateDeploymentScript::rules()['script']),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(UpdateDeploymentScript::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
@ -188,15 +188,13 @@ private function dotEnvAction(): Action
|
||||
->modalHeading('Update .env file')
|
||||
->form([
|
||||
CodeEditorField::make('env')
|
||||
->formatStateUsing(function () {
|
||||
return $this->site->getEnv();
|
||||
})
|
||||
->formatStateUsing(fn (): string => $this->site->getEnv())
|
||||
->rules([
|
||||
'env' => 'required',
|
||||
]),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(UpdateEnv::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
@ -219,8 +217,8 @@ private function branchAction(): Action
|
||||
->default($this->site->branch)
|
||||
->rules(UpdateBranch::rules()['branch']),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(UpdateBranch::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
|
@ -25,18 +25,31 @@ class Commands extends Widget
|
||||
{
|
||||
public Site $site;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Command>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Command::query()->where('site_id', $this->site->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<Command> $query
|
||||
* @return Builder<Command>
|
||||
*/
|
||||
protected function applySortingToTableQuery(Builder $query): Builder
|
||||
{
|
||||
return $query->latest('created_at');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
@ -56,14 +69,17 @@ protected function getTableColumns(): array
|
||||
|
||||
protected function getTableHeaderActions(): array
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
Action::make('new-command')
|
||||
->label('Create a Command')
|
||||
->modalDescription('The command will be executed inside the site\'s directory')
|
||||
->icon('heroicon-o-plus')
|
||||
->authorize(fn () => auth()->user()->can('create', [Command::class, $this->site, $this->site->server]))
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->authorize(fn () => $user->can('create', [Command::class, $this->site, $this->site->server]))
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(CreateCommand::class)->create($this->site, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
@ -91,6 +107,9 @@ protected function getTableHeaderActions(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->query($this->getTableQuery())
|
||||
->headerActions($this->getTableHeaderActions())
|
||||
@ -118,10 +137,8 @@ public function table(Table $table): Table
|
||||
|
||||
return $form;
|
||||
})
|
||||
->authorize(fn (Command $record) => auth()->user()->can('update', [$record->site, $record->site->server]))
|
||||
->action(function (array $data, Command $record) {
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
->authorize(fn (Command $record) => $user->can('update', [$record->site, $record->site->server]))
|
||||
->action(function (array $data, Command $record) use ($user): void {
|
||||
app(ExecuteCommand::class)->execute($record, $user, $data);
|
||||
$this->dispatch('$refresh');
|
||||
}),
|
||||
@ -130,24 +147,20 @@ public function table(Table $table): Table
|
||||
->tooltip('Last Log')
|
||||
->icon('heroicon-o-eye')
|
||||
->modalHeading('View Last Execution Log')
|
||||
->modalContent(function (Command $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => $record->lastExecution?->serverLog?->getContent() ?? 'Not executed yet',
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalContent(fn (Command $record) => view('components.console-view', [
|
||||
'slot' => $record->lastExecution?->serverLog?->getContent() ?? 'Not executed yet',
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]))
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close'),
|
||||
EditAction::make('edit')
|
||||
->hiddenLabel()
|
||||
->tooltip('Edit')
|
||||
->modalHeading('Edit Command')
|
||||
->mutateRecordDataUsing(function (array $data, Command $record) {
|
||||
return [
|
||||
'name' => $record->name,
|
||||
'command' => $record->command,
|
||||
];
|
||||
})
|
||||
->mutateRecordDataUsing(fn (array $data, Command $record): array => [
|
||||
'name' => $record->name,
|
||||
'command' => $record->command,
|
||||
])
|
||||
->form([
|
||||
TextInput::make('name')
|
||||
->rules(EditCommand::rules()['name']),
|
||||
@ -156,8 +169,8 @@ public function table(Table $table): Table
|
||||
->helperText('You can use variables like ${VARIABLE_NAME} in the command. The variables will be asked when executing the command'),
|
||||
|
||||
])
|
||||
->authorize(fn (Command $record) => auth()->user()->can('update', [$record, $this->site, $this->site->server]))
|
||||
->using(function (array $data, Command $record) {
|
||||
->authorize(fn (Command $record) => $user->can('update', [$record, $this->site, $this->site->server]))
|
||||
->using(function (array $data, Command $record): void {
|
||||
app(EditCommand::class)->edit($record, $data);
|
||||
$this->dispatch('$refresh');
|
||||
})
|
||||
@ -167,8 +180,8 @@ public function table(Table $table): Table
|
||||
->hiddenLabel()
|
||||
->tooltip('Delete')
|
||||
->modalHeading('Delete Command')
|
||||
->authorize(fn (Command $record) => auth()->user()->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (array $data, Command $record) {
|
||||
->authorize(fn (Command $record) => $user->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (array $data, Command $record): void {
|
||||
$record->delete();
|
||||
}),
|
||||
]);
|
||||
|
@ -15,13 +15,23 @@ class DeploymentsList extends Widget
|
||||
{
|
||||
public Site $site;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Deployment>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Deployment::query()->where('site_id', $this->site->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<Deployment> $query
|
||||
* @return Builder<Deployment>
|
||||
*/
|
||||
protected function applySortingToTableQuery(Builder $query): Builder
|
||||
{
|
||||
return $query->latest('created_at');
|
||||
@ -53,6 +63,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->query($this->getTableQuery())
|
||||
->columns($this->getTableColumns())
|
||||
@ -62,14 +75,12 @@ public function table(Table $table): Table
|
||||
->hiddenLabel()
|
||||
->tooltip('View')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn ($record) => auth()->user()->can('view', $record->log))
|
||||
->authorize(fn ($record) => $user->can('view', $record->log))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (Deployment $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => $record->log?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalContent(fn (Deployment $record) => view('components.console-view', [
|
||||
'slot' => $record->log?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]))
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close'),
|
||||
Action::make('download')
|
||||
@ -77,7 +88,7 @@ public function table(Table $table): Table
|
||||
->tooltip('Download')
|
||||
->color('gray')
|
||||
->icon('heroicon-o-archive-box-arrow-down')
|
||||
->authorize(fn ($record) => auth()->user()->can('view', $record->log))
|
||||
->authorize(fn ($record) => $user->can('view', $record->log))
|
||||
->action(fn (Deployment $record) => $record->log?->download()),
|
||||
]);
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ class Installing extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
@ -5,6 +5,7 @@
|
||||
use App\Actions\Site\UpdateLoadBalancer;
|
||||
use App\Enums\LoadBalancerMethod;
|
||||
use App\Models\LoadBalancerServer;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
@ -29,12 +30,15 @@ class LoadBalancerServers extends Widget implements HasForms
|
||||
|
||||
public string $method;
|
||||
|
||||
/**
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
public array $servers = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->setLoadBalancerServers();
|
||||
if (empty($this->servers)) {
|
||||
if ($this->servers === []) {
|
||||
$this->servers = [
|
||||
[
|
||||
'server' => null,
|
||||
@ -50,14 +54,12 @@ public function mount(): void
|
||||
#[On('load-balancer-updated')]
|
||||
public function setLoadBalancerServers(): void
|
||||
{
|
||||
$this->servers = $this->site->loadBalancerServers->map(function (LoadBalancerServer $server) {
|
||||
return [
|
||||
'server' => $server->ip,
|
||||
'port' => $server->port,
|
||||
'weight' => $server->weight,
|
||||
'backup' => $server->backup,
|
||||
];
|
||||
})->toArray();
|
||||
$this->servers = $this->site->loadBalancerServers->map(fn (LoadBalancerServer $server): array => [
|
||||
'server' => $server->ip,
|
||||
'port' => $server->port,
|
||||
'weight' => $server->weight,
|
||||
'backup' => $server->backup,
|
||||
])->toArray();
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
@ -84,14 +86,14 @@ public function form(Form $form): Form
|
||||
->searchable()
|
||||
->required()
|
||||
->rules(UpdateLoadBalancer::rules($this->site)['servers.*.server'])
|
||||
->options(function () {
|
||||
return $this->site->project->servers()
|
||||
->where('id', '!=', $this->site->server_id)
|
||||
->get()
|
||||
->mapWithKeys(function ($server) {
|
||||
return [$server->local_ip => $server->name.' ('.$server->local_ip.')'];
|
||||
});
|
||||
}),
|
||||
->options(fn () => $this->site->project->servers()
|
||||
->where('id', '!=', $this->site->server_id)
|
||||
->whereNotNull('local_ip')
|
||||
->get()
|
||||
->mapWithKeys(function ($server): array {
|
||||
/** @var Server $server */
|
||||
return $server->local_ip ? [$server->local_ip => $server->name.' ('.$server->local_ip.')'] : [];
|
||||
})),
|
||||
TextInput::make('port')
|
||||
->default(80)
|
||||
->required()
|
||||
@ -124,7 +126,7 @@ public function save(): void
|
||||
|
||||
$this->validate();
|
||||
|
||||
run_action($this, function () {
|
||||
run_action($this, function (): void {
|
||||
app(UpdateLoadBalancer::class)->update($this->site, [
|
||||
'method' => $this->method,
|
||||
'servers' => $this->servers,
|
||||
|
@ -7,6 +7,7 @@
|
||||
use App\Actions\Site\UpdateSourceControl;
|
||||
use App\Models\Site;
|
||||
use App\Models\SourceControl;
|
||||
use App\Models\User;
|
||||
use App\Web\Pages\Settings\SourceControls\Actions\Create;
|
||||
use App\Web\Pages\Settings\Tags\Actions\EditTags;
|
||||
use Filament\Forms\Components\Select;
|
||||
@ -28,6 +29,9 @@ class SiteDetails extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
@ -38,6 +42,9 @@ class SiteDetails extends Widget implements HasForms, HasInfolists
|
||||
|
||||
public function infolist(Infolist $infolist): Infolist
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $infolist
|
||||
->schema([
|
||||
Section::make()
|
||||
@ -59,15 +66,15 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->inlineLabel(),
|
||||
TextEntry::make('type')
|
||||
->extraAttributes(['class' => 'capitalize'])
|
||||
->icon(fn ($state) => 'icon-'.$state)
|
||||
->icon(fn ($state): string => 'icon-'.$state)
|
||||
->inlineLabel(),
|
||||
TextEntry::make('tags.*')
|
||||
->default('No tags')
|
||||
->formatStateUsing(fn ($state) => is_object($state) ? $state->name : $state)
|
||||
->formatStateUsing(fn ($state) => is_object($state) && isset($state->name) ? $state->name : $state)
|
||||
->inlineLabel()
|
||||
->badge()
|
||||
->color(fn ($state) => is_object($state) ? $state->color : 'gray')
|
||||
->icon(fn ($state) => is_object($state) ? 'heroicon-o-tag' : '')
|
||||
->color(fn ($state) => is_object($state) && isset($state->color) ? $state->color : 'gray')
|
||||
->icon(fn ($state): string => is_object($state) ? 'heroicon-o-tag' : '')
|
||||
->suffixAction(
|
||||
EditTags::infolist($this->site)
|
||||
),
|
||||
@ -93,8 +100,8 @@ public function infolist(Infolist $infolist): Infolist
|
||||
),
|
||||
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(UpdatePHPVersion::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
@ -108,7 +115,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->inlineLabel()
|
||||
->badge()
|
||||
->default('No aliases')
|
||||
->color(fn ($state) => $state == 'No aliases' ? 'gray' : 'primary')
|
||||
->color(fn ($state): string => $state == 'No aliases' ? 'gray' : 'primary')
|
||||
->suffixAction(
|
||||
Action::make('edit_aliases')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
@ -124,8 +131,8 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->nestedRecursiveRules(UpdateAliases::rules()['aliases.*']),
|
||||
|
||||
])
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(UpdateAliases::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
@ -152,7 +159,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->label('Source Control')
|
||||
->rules(UpdateSourceControl::rules()['source_control'])
|
||||
->options(
|
||||
SourceControl::getByProjectId(auth()->user()->current_project_id)
|
||||
SourceControl::getByProjectId($user->current_project_id)
|
||||
->pluck('profile', 'id')
|
||||
)
|
||||
->default($this->site->source_control_id)
|
||||
@ -164,13 +171,13 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->icon('heroicon-o-wifi')
|
||||
->tooltip('Connect to a source control')
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->authorize(fn () => auth()->user()->can('create', SourceControl::class))
|
||||
->authorize(fn () => $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) {
|
||||
->action(function (array $data): void {
|
||||
run_action($this, function () use ($data): void {
|
||||
app(UpdateSourceControl::class)->update($this->site, $data);
|
||||
|
||||
Notification::make()
|
||||
|
@ -18,6 +18,9 @@ class SiteSummary extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
@ -44,9 +47,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
TextEntry::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(static function ($state): string {
|
||||
return Site::$statusColors[$state];
|
||||
}),
|
||||
->color(static fn ($state): string => Site::$statusColors[$state]),
|
||||
])
|
||||
->columns(3),
|
||||
])
|
||||
|
@ -17,6 +17,9 @@ class SitesList extends Widget
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
/**
|
||||
* @return Builder<Site>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Site::query()->where('server_id', $this->server->id);
|
||||
@ -26,7 +29,7 @@ protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('type')
|
||||
->icon(fn (Site $record) => 'icon-'.$record->type)
|
||||
->icon(fn (Site $record): string => 'icon-'.$record->type)
|
||||
->tooltip(fn (Site $record) => $record->type)
|
||||
->width(24),
|
||||
TextColumn::make('domain')
|
||||
@ -54,17 +57,20 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
->columns($this->getTableColumns())
|
||||
->recordUrl(fn (Site $record) => View::getUrl(parameters: ['server' => $this->server, 'site' => $record]))
|
||||
->recordUrl(fn (Site $record): string => View::getUrl(parameters: ['server' => $this->server, 'site' => $record]))
|
||||
->actions([
|
||||
Action::make('settings')
|
||||
->label('Settings')
|
||||
->icon('heroicon-o-cog-6-tooth')
|
||||
->authorize(fn (Site $record) => auth()->user()->can('update', [$record, $this->server]))
|
||||
->url(fn (Site $record) => Settings::getUrl(parameters: ['server' => $this->server, 'site' => $record])),
|
||||
->authorize(fn (Site $record) => $user->can('update', [$record, $this->server]))
|
||||
->url(fn (Site $record): string => Settings::getUrl(parameters: ['server' => $this->server, 'site' => $record])),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,10 @@ class View extends Page
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('view', [$this->server, auth()->user()->currentProject]);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$this->authorize('view', [$this->server, $user->currentProject]);
|
||||
$this->previousStatus = $this->server->status;
|
||||
}
|
||||
|
||||
@ -36,6 +39,9 @@ public function refresh(): void
|
||||
|
||||
public function getWidgets(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$widgets = [];
|
||||
|
||||
if ($this->server->isInstalling()) {
|
||||
@ -44,7 +50,7 @@ public function getWidgets(): array
|
||||
$widgets[] = [ServerStats::class, ['server' => $this->server]];
|
||||
}
|
||||
|
||||
if (auth()->user()->can('viewAny', [ServerLog::class, $this->server])) {
|
||||
if ($user->can('viewAny', [ServerLog::class, $this->server])) {
|
||||
$widgets[] = [
|
||||
LogsList::class, [
|
||||
'server' => $this->server,
|
||||
|
@ -18,6 +18,9 @@ class Installing extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
@ -21,6 +21,9 @@ class ServerDetails extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
@ -55,7 +58,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
Action::make('check-update')
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->tooltip('Check Now')
|
||||
->action(function (Server $record) {
|
||||
->action(function (Server $record): void {
|
||||
$record->checkForUpdates();
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
@ -63,7 +66,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
Notification::make()
|
||||
->info()
|
||||
->title('Available updates:')
|
||||
->body($record->updates)
|
||||
->body((string) $record->updates)
|
||||
->send();
|
||||
})
|
||||
),
|
||||
@ -75,7 +78,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->icon('heroicon-o-check-circle')
|
||||
->tooltip('Update Now')
|
||||
->requiresConfirmation()
|
||||
->action(function (Server $record) {
|
||||
->action(function (Server $record): void {
|
||||
app(Update::class)->update($record);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
@ -91,11 +94,11 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->inlineLabel(),
|
||||
TextEntry::make('tags.*')
|
||||
->default('No tags')
|
||||
->formatStateUsing(fn ($state) => is_object($state) ? $state->name : $state)
|
||||
->formatStateUsing(fn ($state) => is_object($state) && isset($state->name) ? $state->name : $state)
|
||||
->inlineLabel()
|
||||
->badge()
|
||||
->color(fn ($state) => is_object($state) ? $state->color : 'gray')
|
||||
->icon(fn ($state) => is_object($state) ? 'heroicon-o-tag' : '')
|
||||
->color(fn ($state) => is_object($state) && isset($state->color) ? $state->color : 'gray')
|
||||
->icon(fn ($state): string => is_object($state) ? 'heroicon-o-tag' : '')
|
||||
->suffixAction(
|
||||
EditTags::infolist($this->server)
|
||||
),
|
||||
|
@ -16,17 +16,17 @@ protected function getStats(): array
|
||||
{
|
||||
$stats = [];
|
||||
|
||||
if ($this->server->webserver()) {
|
||||
if ($this->server->webserver() instanceof \App\Models\Service) {
|
||||
$stats[] = Stat::make('Sites', $this->server->sites()->count())
|
||||
->icon('heroicon-o-cursor-arrow-ripple');
|
||||
}
|
||||
|
||||
if ($this->server->database()) {
|
||||
if ($this->server->database() instanceof \App\Models\Service) {
|
||||
$stats[] = Stat::make('Databases', $this->server->databases()->count())
|
||||
->icon('heroicon-o-circle-stack');
|
||||
}
|
||||
|
||||
if ($this->server->defaultService('php')) {
|
||||
if ($this->server->defaultService('php') instanceof \App\Models\Service) {
|
||||
$stats[] = Stat::make('PHP Version', $this->server->defaultService('php')->version)
|
||||
->icon('heroicon-o-command-line');
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ class ServerSummary extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
@ -39,7 +42,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->schema([
|
||||
TextEntry::make('name')
|
||||
->label('Name')
|
||||
->url(fn (Server $record) => View::getUrl(parameters: ['server' => $record])),
|
||||
->url(fn (Server $record): string => View::getUrl(parameters: ['server' => $record])),
|
||||
TextEntry::make('ip')
|
||||
->label('IP Address')
|
||||
->icon('heroicon-o-clipboard-document')
|
||||
@ -48,14 +51,12 @@ public function infolist(Infolist $infolist): Infolist
|
||||
TextEntry::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(static function ($state): string {
|
||||
return Server::$statusColors[$state];
|
||||
})
|
||||
->color(static fn ($state): string => Server::$statusColors[$state])
|
||||
->suffixAction(
|
||||
Action::make('check-status')
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->tooltip('Check Connection')
|
||||
->action(function (Server $record) {
|
||||
->action(function (Server $record): void {
|
||||
$previousStatus = $record->status;
|
||||
|
||||
$record = $record->checkConnection();
|
||||
|
@ -14,16 +14,22 @@
|
||||
|
||||
class ServersList extends Widget
|
||||
{
|
||||
/**
|
||||
* @return Builder<Server>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Server::query()->where('project_id', auth()->user()->current_project_id);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return Server::query()->where('project_id', $user->current_project_id);
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (Server $record) => 'icon-'.$record->provider)
|
||||
->icon(fn (Server $record): string => 'icon-'.$record->provider)
|
||||
->tooltip(fn (Server $record) => $record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
@ -53,17 +59,20 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
->columns($this->getTableColumns())
|
||||
->recordUrl(fn (Server $record) => View::getUrl(parameters: ['server' => $record]))
|
||||
->recordUrl(fn (Server $record): string => View::getUrl(parameters: ['server' => $record]))
|
||||
->actions([
|
||||
Action::make('settings')
|
||||
->label('Settings')
|
||||
->icon('heroicon-o-cog-6-tooth')
|
||||
->authorize(fn ($record) => auth()->user()->can('update', $record))
|
||||
->url(fn (Server $record) => Settings::getUrl(parameters: ['server' => $record])),
|
||||
->authorize(fn ($record) => $user->can('update', $record))
|
||||
->url(fn (Server $record): string => Settings::getUrl(parameters: ['server' => $record])),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,9 @@ class UpdateServerInfo extends Widget implements HasForms
|
||||
|
||||
public string $ip;
|
||||
|
||||
public ?string $local_ip;
|
||||
public ?string $local_ip = null;
|
||||
|
||||
public string $port;
|
||||
public int $port;
|
||||
|
||||
public function mount(Server $server): void
|
||||
{
|
||||
|
@ -24,11 +24,17 @@ class Index extends Page
|
||||
|
||||
public string $token = '';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()?->can('viewAny', PersonalAccessToken::class) ?? false;
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->can('viewAny', PersonalAccessToken::class);
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
@ -47,6 +53,9 @@ public function unmountAction(bool $shouldCancelParentActions = true, bool $shou
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
Action::make('read-the-docs')
|
||||
->label('Read the Docs')
|
||||
@ -59,8 +68,8 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-plus')
|
||||
->modalHeading('Create a new Key')
|
||||
->modalSubmitActionLabel('Create')
|
||||
->form(function () {
|
||||
if ($this->token) {
|
||||
->form(function (): array {
|
||||
if ($this->token !== '' && $this->token !== '0') {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -76,8 +85,8 @@ protected function getHeaderActions(): array
|
||||
->required(),
|
||||
];
|
||||
})
|
||||
->infolist(function () {
|
||||
if ($this->token) {
|
||||
->infolist(function (): array {
|
||||
if ($this->token !== '' && $this->token !== '0') {
|
||||
return [
|
||||
TextEntry::make('token')
|
||||
->state($this->token)
|
||||
@ -91,12 +100,12 @@ protected function getHeaderActions(): array
|
||||
})
|
||||
->authorize('create', PersonalAccessToken::class)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data) use ($user): void {
|
||||
$permissions = ['read'];
|
||||
if ($data['permission'] === 'write') {
|
||||
$permissions[] = 'write';
|
||||
}
|
||||
$token = auth()->user()->createToken($data['name'], $permissions);
|
||||
$token = $user->createToken($data['name'], $permissions);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
||||
@ -105,11 +114,11 @@ protected function getHeaderActions(): array
|
||||
$this->halt();
|
||||
})
|
||||
->modalSubmitAction(function () {
|
||||
if ($this->token) {
|
||||
if ($this->token !== '' && $this->token !== '0') {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
->closeModalByClickingAway(fn () => ! $this->token),
|
||||
->closeModalByClickingAway(fn (): bool => $this->token === '' || $this->token === '0'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,23 @@
|
||||
|
||||
class ApiKeysList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<PersonalAccessToken>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return auth()->user()->tokens()->getQuery();
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var Builder<PersonalAccessToken> $query */
|
||||
$query = $user->tokens()->getQuery();
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
@ -35,7 +47,7 @@ protected function getTableColumns(): array
|
||||
->sortable(),
|
||||
TextColumn::make('last_used_at')
|
||||
->label('Last Used At')
|
||||
->formatStateUsing(fn (PersonalAccessToken $record) => $record->getDateTimeByTimezone($record->last_used_at))
|
||||
->formatStateUsing(fn (PersonalAccessToken $record): string => $record->getDateTimeByTimezone($record->last_used_at))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
];
|
||||
@ -43,6 +55,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -50,15 +65,15 @@ public function table(Table $table): Table
|
||||
->actions([
|
||||
DeleteAction::make('delete')
|
||||
->modalHeading('Delete Token')
|
||||
->authorize(fn (PersonalAccessToken $record) => auth()->user()->can('delete', $record))
|
||||
->using(function (array $data, PersonalAccessToken $record) {
|
||||
->authorize(fn (PersonalAccessToken $record) => $user->can('delete', $record))
|
||||
->using(function (array $data, PersonalAccessToken $record): void {
|
||||
$record->delete();
|
||||
}),
|
||||
])
|
||||
->bulkActions([
|
||||
DeleteBulkAction::make()
|
||||
->requiresConfirmation()
|
||||
->authorize(auth()->user()->can('deleteMany', PersonalAccessToken::class)),
|
||||
->authorize($user->can('deleteMany', PersonalAccessToken::class)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,15 @@
|
||||
|
||||
class Create
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
Select::make('provider')
|
||||
->options(
|
||||
collect(config('core.notification_channels_providers'))
|
||||
collect((array) config('core.notification_channels_providers'))
|
||||
->mapWithKeys(fn ($provider) => [$provider => $provider])
|
||||
)
|
||||
->live()
|
||||
@ -47,12 +50,16 @@ public static function form(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function action(array $data): void
|
||||
{
|
||||
try {
|
||||
app(AddChannel::class)->add(auth()->user(), $data);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
app(AddChannel::class)->add($user, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -10,6 +10,9 @@
|
||||
|
||||
class Edit
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
@ -20,8 +23,13 @@ public static function form(): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public static function action(NotificationChannel $channel, array $data): void
|
||||
{
|
||||
app(EditChannel::class)->edit($channel, auth()->user(), $data);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
app(EditChannel::class)->edit($channel, $user, $data);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ protected function getHeaderActions(): array
|
||||
->form(Actions\Create::form())
|
||||
->authorize('create', NotificationChannel::class)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
try {
|
||||
Actions\Create::action($data);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Web\Pages\Settings\NotificationChannels\Widgets;
|
||||
|
||||
use App\Models\NotificationChannel;
|
||||
use App\Models\User;
|
||||
use App\Web\Pages\Settings\NotificationChannels\Actions\Edit;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
@ -15,18 +16,27 @@
|
||||
|
||||
class NotificationChannelsList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<NotificationChannel>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return NotificationChannel::getByProjectId(auth()->user()->current_project_id);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return NotificationChannel::getByProjectId($user->current_project_id);
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (NotificationChannel $record) => 'icon-'.$record->provider)
|
||||
->icon(fn (NotificationChannel $record): string => 'icon-'.$record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('label')
|
||||
->default(fn (NotificationChannel $record) => $record->label)
|
||||
@ -35,10 +45,8 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('id')
|
||||
->label('Global')
|
||||
->badge()
|
||||
->color(fn (NotificationChannel $record) => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(function (NotificationChannel $record) {
|
||||
return $record->project_id ? 'No' : 'Yes';
|
||||
}),
|
||||
->color(fn (NotificationChannel $record): string => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(fn (NotificationChannel $record): string => $record->project_id ? 'No' : 'Yes'),
|
||||
TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->formatStateUsing(fn (NotificationChannel $record) => $record->created_at_by_timezone)
|
||||
@ -49,6 +57,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -56,20 +67,18 @@ public function table(Table $table): Table
|
||||
->actions([
|
||||
EditAction::make('edit')
|
||||
->modalHeading('Edit Notification Channel')
|
||||
->mutateRecordDataUsing(function (array $data, NotificationChannel $record) {
|
||||
return [
|
||||
'label' => $record->label,
|
||||
'global' => ! $record->project_id,
|
||||
];
|
||||
})
|
||||
->mutateRecordDataUsing(fn (array $data, NotificationChannel $record): array => [
|
||||
'label' => $record->label,
|
||||
'global' => ! $record->project_id,
|
||||
])
|
||||
->form(Edit::form())
|
||||
->authorize(fn (NotificationChannel $record) => auth()->user()->can('update', $record))
|
||||
->authorize(fn (NotificationChannel $record) => $user->can('update', $record))
|
||||
->using(fn (array $data, NotificationChannel $record) => Edit::action($record, $data))
|
||||
->modalWidth(MaxWidth::Medium),
|
||||
DeleteAction::make('delete')
|
||||
->modalHeading('Delete Notification Channel')
|
||||
->authorize(fn (NotificationChannel $record) => auth()->user()->can('delete', $record))
|
||||
->using(function (array $data, NotificationChannel $record) {
|
||||
->authorize(fn (NotificationChannel $record) => $user->can('delete', $record))
|
||||
->using(function (array $data, NotificationChannel $record): void {
|
||||
$record->delete();
|
||||
}),
|
||||
]);
|
||||
|
@ -54,7 +54,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->label('Password')
|
||||
->required(),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
self::logoutOtherBrowserSessions($data['password']);
|
||||
})
|
||||
->modalWidth('2xl'),
|
||||
@ -62,6 +62,9 @@ public function infolist(Infolist $infolist): Infolist
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
private function getDynamicSchema(): array
|
||||
{
|
||||
$sections = [];
|
||||
@ -71,8 +74,8 @@ private function getDynamicSchema(): array
|
||||
->schema([
|
||||
TextEntry::make('device')
|
||||
->hiddenLabel()
|
||||
->icon($session->device['desktop'] ? 'heroicon-o-computer-desktop' : 'heroicon-o-device-phone-mobile')
|
||||
->state($session->device['platform'].' - '.$session->device['browser']),
|
||||
->icon(isset($session->device['desktop']) ? 'heroicon-o-computer-desktop' : 'heroicon-o-device-phone-mobile')
|
||||
->state(($session->device['platform'] ?? 'Platform').' - '.($session->device['browser'] ?? 'Browser')),
|
||||
TextEntry::make('browser')
|
||||
->hiddenLabel()
|
||||
->icon('heroicon-o-map-pin')
|
||||
@ -92,15 +95,32 @@ private function getDynamicSchema(): array
|
||||
return $sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, object{
|
||||
* device: array{
|
||||
* browser: string,
|
||||
* desktop: bool,
|
||||
* mobile: bool,
|
||||
* tablet: bool,
|
||||
* platform: string
|
||||
* },
|
||||
* ip_address: string|null,
|
||||
* is_current_device: bool,
|
||||
* last_active: string
|
||||
* }>
|
||||
*/
|
||||
private function getSessions(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
if (config(key: 'session.driver') !== 'database') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return collect(
|
||||
value: DB::connection(config(key: 'session.connection'))->table(table: config(key: 'session.table', default: 'sessions'))
|
||||
->where(column: 'user_id', operator: Auth::user()->getAuthIdentifier())
|
||||
->where(column: 'user_id', operator: $user->getAuthIdentifier())
|
||||
->latest(column: 'last_activity')
|
||||
->get()
|
||||
)->map(callback: function ($session): object {
|
||||
@ -121,17 +141,20 @@ private function getSessions(): array
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
private function createAgent(mixed $session)
|
||||
private function createAgent(mixed $session): Agent
|
||||
{
|
||||
return tap(
|
||||
value: new Agent,
|
||||
callback: fn ($agent) => $agent->setUserAgent(userAgent: $session->user_agent)
|
||||
callback: fn ($agent): string => $agent->setUserAgent(userAgent: $session->user_agent)
|
||||
);
|
||||
}
|
||||
|
||||
private function logoutOtherBrowserSessions($password): void
|
||||
private function logoutOtherBrowserSessions(string $password): void
|
||||
{
|
||||
if (! Hash::check($password, Auth::user()->password)) {
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
if (! Hash::check($password, $user->password)) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title('The password you entered was incorrect. Please try again.')
|
||||
@ -143,7 +166,7 @@ private function logoutOtherBrowserSessions($password): void
|
||||
Auth::guard()->logoutOtherDevices($password);
|
||||
|
||||
request()->session()->put([
|
||||
'password_hash_'.Auth::getDefaultDriver() => Auth::user()->getAuthPassword(),
|
||||
'password_hash_'.Auth::getDefaultDriver() => $user->getAuthPassword(),
|
||||
]);
|
||||
|
||||
$this->deleteOtherSessionRecords();
|
||||
@ -156,12 +179,15 @@ private function logoutOtherBrowserSessions($password): void
|
||||
|
||||
private function deleteOtherSessionRecords(): void
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
if (config(key: 'session.driver') !== 'database') {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::connection(config(key: 'session.connection'))->table(table: config(key: 'session.table', default: 'sessions'))
|
||||
->where('user_id', Auth::user()->getAuthIdentifier())
|
||||
->where('user_id', $user->getAuthIdentifier())
|
||||
->where('id', '!=', request()->session()->getId())
|
||||
->delete();
|
||||
}
|
||||
|
@ -29,14 +29,19 @@ class ProfileInformation extends Widget implements HasForms
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->name = auth()->user()->name;
|
||||
$this->email = auth()->user()->email;
|
||||
$this->timezone = auth()->user()->timezone;
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
$this->name = $user->name;
|
||||
$this->email = $user->email;
|
||||
$this->timezone = $user->timezone;
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
$rules = UpdateUserProfileInformation::rules(auth()->user());
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$rules = UpdateUserProfileInformation::rules($user);
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
@ -69,9 +74,12 @@ public function form(Form $form): Form
|
||||
|
||||
public function submit(): void
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$this->validate();
|
||||
|
||||
app(UpdateUserProfileInformation::class)->update(auth()->user(), $this->all());
|
||||
app(UpdateUserProfileInformation::class)->update($user, $this->all());
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
|
@ -22,6 +22,9 @@ class TwoFactor extends Widget implements HasForms, HasInfolists
|
||||
use InteractsWithForms;
|
||||
use InteractsWithInfolists;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
@ -34,13 +37,19 @@ class TwoFactor extends Widget implements HasForms, HasInfolists
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
if (auth()->user()->two_factor_secret) {
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user->two_factor_secret) {
|
||||
$this->enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function infolist(Infolist $infolist): Infolist
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $infolist->schema([
|
||||
Section::make()
|
||||
->heading('Two Factor Authentication')
|
||||
@ -53,12 +62,12 @@ public function infolist(Infolist $infolist): Infolist
|
||||
ViewEntry::make('qr_code')
|
||||
->hiddenLabel()
|
||||
->view('components.container', [
|
||||
'content' => $this->enabled ? auth()->user()->twoFactorQrCodeSvg() : null,
|
||||
'content' => $this->enabled ? $user->twoFactorQrCodeSvg() : null,
|
||||
])
|
||||
->visible($this->enabled && $this->showCodes),
|
||||
TextEntry::make('qr_code_manual')
|
||||
->label('If you are unable to scan the QR code, please use the 2FA secret instead.')
|
||||
->state($this->enabled ? decrypt(auth()->user()->two_factor_secret) : null)
|
||||
->state($this->enabled ? decrypt($user->two_factor_secret) : null)
|
||||
->copyable()
|
||||
->visible($this->enabled && $this->showCodes),
|
||||
TextEntry::make('recovery_codes_text')
|
||||
@ -70,7 +79,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
->hiddenLabel()
|
||||
->extraAttributes(['class' => 'rounded-lg border border-gray-100 p-2 dark:border-gray-700'])
|
||||
->view('components.container', [
|
||||
'content' => $this->enabled ? implode('</br>', json_decode(decrypt(auth()->user()->two_factor_recovery_codes), true)) : null,
|
||||
'content' => $this->enabled ? implode('</br>', json_decode((string) decrypt($user->two_factor_recovery_codes), true)) : null,
|
||||
])
|
||||
->visible($this->enabled),
|
||||
])
|
||||
@ -78,7 +87,7 @@ public function infolist(Infolist $infolist): Infolist
|
||||
Action::make('two-factor')
|
||||
->color($this->enabled ? 'danger' : 'primary')
|
||||
->label($this->enabled ? 'Disable' : 'Enable')
|
||||
->action(function () {
|
||||
->action(function (): void {
|
||||
if ($this->enabled) {
|
||||
$this->disableTwoFactor();
|
||||
} else {
|
||||
@ -96,7 +105,10 @@ public function infolist(Infolist $infolist): Infolist
|
||||
|
||||
public function enableTwoFactor(): void
|
||||
{
|
||||
app(EnableTwoFactorAuthentication::class)(auth()->user());
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
app(EnableTwoFactorAuthentication::class)($user);
|
||||
|
||||
$this->enabled = true;
|
||||
$this->showCodes = true;
|
||||
@ -111,7 +123,10 @@ public function enableTwoFactor(): void
|
||||
|
||||
public function disableTwoFactor(): void
|
||||
{
|
||||
app(DisableTwoFactorAuthentication::class)(auth()->user());
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
app(DisableTwoFactorAuthentication::class)($user);
|
||||
|
||||
$this->enabled = false;
|
||||
$this->showCodes = false;
|
||||
@ -126,7 +141,10 @@ public function disableTwoFactor(): void
|
||||
|
||||
public function regenerateRecoveryCodes(): void
|
||||
{
|
||||
app(GenerateNewRecoveryCodes::class)(auth()->user());
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
app(GenerateNewRecoveryCodes::class)($user);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
|
@ -29,7 +29,10 @@ public static function getNavigationItemActiveRoutePattern(): string
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()?->can('viewAny', Project::class) ?? false;
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->can('viewAny', Project::class);
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
@ -47,15 +50,13 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-plus')
|
||||
->authorize('create', Project::class)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->form(function (Form $form) {
|
||||
return $form->schema([
|
||||
TextInput::make('name')
|
||||
->name('name')
|
||||
->rules(CreateProject::rules()['name']),
|
||||
])->columns(1);
|
||||
})
|
||||
->action(function (array $data) {
|
||||
app(CreateProject::class)->create(auth()->user(), $data);
|
||||
->form(fn (Form $form): \Filament\Forms\Form => $form->schema([
|
||||
TextInput::make('name')
|
||||
->name('name')
|
||||
->rules(CreateProject::rules()['name']),
|
||||
])->columns(1))
|
||||
->action(function (array $data): void {
|
||||
app(CreateProject::class)->create($this->getUser(), $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
}),
|
||||
|
@ -62,9 +62,9 @@ protected function getHeaderActions(): array
|
||||
->modalHeading('Delete Project')
|
||||
->modalDescription('Are you sure you want to delete this project? This action will delete all associated data and cannot be undone.')
|
||||
->requiresConfirmation()
|
||||
->action(function (Project $record) {
|
||||
->action(function (Project $record): void {
|
||||
try {
|
||||
app(DeleteProject::class)->delete(auth()->user(), $record);
|
||||
app(DeleteProject::class)->delete($this->getUser(), $record);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
|
@ -21,7 +21,7 @@ class AddUser extends Widget implements HasForms
|
||||
|
||||
public Project $project;
|
||||
|
||||
public ?int $user;
|
||||
public ?int $user = null;
|
||||
|
||||
public function mount(Project $project): void
|
||||
{
|
||||
@ -38,7 +38,7 @@ public function form(Form $form): Form
|
||||
Select::make('user')
|
||||
->name('user')
|
||||
->options(fn () => User::query()
|
||||
->whereNotExists(function ($query) {
|
||||
->whereNotExists(function ($query): void {
|
||||
$query->select('user_id')
|
||||
->from('user_project')
|
||||
->whereColumn('users.id', 'user_project.user_id')
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
class ProjectUsersList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $listeners = ['userAdded' => '$refresh'];
|
||||
|
||||
public Project $project;
|
||||
@ -20,9 +23,12 @@ public function mount(Project $project): void
|
||||
$this->project = $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<User>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return User::query()->whereHas('projects', function (Builder $query) {
|
||||
return User::query()->whereHas('projects', function (Builder $query): void {
|
||||
$query->where('project_id', $this->project->id);
|
||||
});
|
||||
}
|
||||
@ -46,10 +52,8 @@ public function table(Table $table): Table
|
||||
Tables\Actions\DeleteAction::make()
|
||||
->label('Remove')
|
||||
->modalHeading('Remove user from project')
|
||||
->visible(function ($record) {
|
||||
return $this->authorize('update', $this->project)->allowed() && $record->id !== auth()->id();
|
||||
})
|
||||
->using(function ($record) {
|
||||
->visible(fn ($record): bool => $this->authorize('update', $this->project)->allowed() && $record->id !== auth()->id())
|
||||
->using(function ($record): void {
|
||||
$this->project->users()->detach($record);
|
||||
}),
|
||||
])
|
||||
|
@ -12,8 +12,14 @@
|
||||
|
||||
class ProjectsList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Project>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Project::query();
|
||||
@ -35,17 +41,20 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
->columns($this->getTableColumns())
|
||||
->recordUrl(fn (Project $record) => Settings::getUrl(['project' => $record]))
|
||||
->recordUrl(fn (Project $record): string => Settings::getUrl(['project' => $record]))
|
||||
->actions([
|
||||
Action::make('settings')
|
||||
->label('Settings')
|
||||
->icon('heroicon-o-cog-6-tooth')
|
||||
->authorize(fn ($record) => auth()->user()->can('update', $record))
|
||||
->url(fn (Project $record) => Settings::getUrl(['project' => $record])),
|
||||
->authorize(fn ($record) => $user->can('update', $record))
|
||||
->url(fn (Project $record): string => Settings::getUrl(['project' => $record])),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -10,22 +10,31 @@ class SelectProject extends Widget
|
||||
{
|
||||
protected static string $view = 'widgets.select-project';
|
||||
|
||||
public ?Project $currentProject;
|
||||
public ?Project $currentProject = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Project>
|
||||
*/
|
||||
public Collection $projects;
|
||||
|
||||
public int|string|null $project;
|
||||
public int|string|null $project = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->currentProject = auth()->user()->currentProject;
|
||||
$this->projects = auth()->user()->allProjects()->get();
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$this->currentProject = $user->currentProject;
|
||||
$this->projects = $user->allProjects()->get();
|
||||
}
|
||||
|
||||
public function updateProject(Project $project): void
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$this->authorize('view', $project);
|
||||
auth()->user()->update(['current_project_id' => $project->id]);
|
||||
$user->update(['current_project_id' => $project->id]);
|
||||
|
||||
$this->redirect('/');
|
||||
}
|
||||
|
@ -24,7 +24,10 @@ class Index extends Page
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()?->can('viewAny', SshKey::class) ?? false;
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->can('viewAny', SshKey::class);
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
@ -36,6 +39,9 @@ public function getWidgets(): array
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
CreateAction::make('add')
|
||||
->label('Add Key')
|
||||
@ -53,8 +59,8 @@ protected function getHeaderActions(): array
|
||||
])
|
||||
->authorize('create', SshKey::class)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->using(function (array $data) {
|
||||
app(CreateSshKey::class)->create(auth()->user(), $data);
|
||||
->using(function (array $data) use ($user): void {
|
||||
app(CreateSshKey::class)->create($user, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
}),
|
||||
|
@ -11,8 +11,14 @@
|
||||
|
||||
class SshKeysList extends TableWidget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<SshKey>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return SshKey::query()->where('user_id', auth()->id());
|
||||
@ -35,6 +41,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -42,9 +51,9 @@ public function table(Table $table): Table
|
||||
->actions([
|
||||
DeleteAction::make('delete')
|
||||
->requiresConfirmation()
|
||||
->authorize(fn (SshKey $record) => auth()->user()->can('delete', $record))
|
||||
->action(function (SshKey $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
->authorize(fn (SshKey $record) => $user->can('delete', $record))
|
||||
->action(function (SshKey $record): void {
|
||||
run_action($this, function () use ($record): void {
|
||||
$record->delete();
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\ServerProvider\CreateServerProvider;
|
||||
use App\Enums\ServerProvider;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Select;
|
||||
@ -13,13 +14,16 @@
|
||||
|
||||
class Create
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
Select::make('provider')
|
||||
->options(
|
||||
collect(config('core.server_providers'))
|
||||
->filter(fn ($provider) => $provider != ServerProvider::CUSTOM)
|
||||
collect((array) config('core.server_providers'))
|
||||
->filter(fn ($provider): bool => $provider != ServerProvider::CUSTOM)
|
||||
->mapWithKeys(fn ($provider) => [$provider => $provider])
|
||||
)
|
||||
->live()
|
||||
@ -30,15 +34,15 @@ public static function form(): array
|
||||
TextInput::make('token')
|
||||
->label('API Key')
|
||||
->validationAttribute('API Key')
|
||||
->visible(fn ($get) => isset(CreateServerProvider::rules($get())['token']))
|
||||
->visible(fn ($get): bool => isset(CreateServerProvider::rules($get())['token']))
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['token']),
|
||||
TextInput::make('key')
|
||||
->label('Access Key')
|
||||
->visible(fn ($get) => isset(CreateServerProvider::rules($get())['key']))
|
||||
->visible(fn ($get): bool => isset(CreateServerProvider::rules($get())['key']))
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['key']),
|
||||
TextInput::make('secret')
|
||||
->label('Secret')
|
||||
->visible(fn ($get) => isset(CreateServerProvider::rules($get())['secret']))
|
||||
->visible(fn ($get): bool => isset(CreateServerProvider::rules($get())['secret']))
|
||||
->rules(fn (Get $get) => CreateServerProvider::rules($get())['secret']),
|
||||
Checkbox::make('global')
|
||||
->label('Is Global (Accessible in all projects)'),
|
||||
@ -46,12 +50,17 @@ public static function form(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function action(array $data): void
|
||||
{
|
||||
try {
|
||||
app(CreateServerProvider::class)->create(auth()->user(), auth()->user()->currentProject, $data);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
app(CreateServerProvider::class)->create($user, $user->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -4,11 +4,15 @@
|
||||
|
||||
use App\Actions\ServerProvider\EditServerProvider;
|
||||
use App\Models\ServerProvider;
|
||||
use App\Models\User;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
|
||||
class Edit
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
@ -20,8 +24,14 @@ public static function form(): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public static function action(ServerProvider $provider, array $data): void
|
||||
{
|
||||
app(EditServerProvider::class)->edit($provider, auth()->user()->currentProject, $data);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
app(EditServerProvider::class)->edit($provider, $user->currentProject, $data);
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ protected function getHeaderActions(): array
|
||||
->form(Actions\Create::form())
|
||||
->authorize('create', ServerProvider::class)
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->using(function (array $data) {
|
||||
->using(function (array $data): void {
|
||||
Actions\Create::action($data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\ServerProvider\DeleteServerProvider;
|
||||
use App\Models\ServerProvider;
|
||||
use App\Models\User;
|
||||
use App\Web\Pages\Settings\ServerProviders\Actions\Edit;
|
||||
use Exception;
|
||||
use Filament\Notifications\Notification;
|
||||
@ -18,18 +19,27 @@
|
||||
|
||||
class ServerProvidersList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<ServerProvider>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return ServerProvider::getByProjectId(auth()->user()->current_project_id);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return ServerProvider::getByProjectId($user->current_project_id);
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (ServerProvider $record) => 'icon-'.$record->provider)
|
||||
->icon(fn (ServerProvider $record): string => 'icon-'.$record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->default(fn ($record) => $record->profile)
|
||||
@ -39,10 +49,8 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('id')
|
||||
->label('Global')
|
||||
->badge()
|
||||
->color(fn ($record) => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(function (ServerProvider $record) {
|
||||
return $record->project_id ? 'No' : 'Yes';
|
||||
}),
|
||||
->color(fn ($record): string => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(fn (ServerProvider $record): string => $record->project_id ? 'No' : 'Yes'),
|
||||
TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->formatStateUsing(fn ($record) => $record->created_at_by_timezone)
|
||||
@ -53,6 +61,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -61,21 +72,19 @@ public function table(Table $table): Table
|
||||
EditAction::make('edit')
|
||||
->label('Edit')
|
||||
->modalHeading('Edit Server Provider')
|
||||
->mutateRecordDataUsing(function (array $data, ServerProvider $record) {
|
||||
return [
|
||||
'name' => $record->profile,
|
||||
'global' => $record->project_id === null,
|
||||
];
|
||||
})
|
||||
->mutateRecordDataUsing(fn (array $data, ServerProvider $record): array => [
|
||||
'name' => $record->profile,
|
||||
'global' => $record->project_id === null,
|
||||
])
|
||||
->form(Edit::form())
|
||||
->authorize(fn (ServerProvider $record) => auth()->user()->can('update', $record))
|
||||
->authorize(fn (ServerProvider $record) => $user->can('update', $record))
|
||||
->using(fn (array $data, ServerProvider $record) => Edit::action($record, $data))
|
||||
->modalWidth(MaxWidth::Medium),
|
||||
DeleteAction::make('delete')
|
||||
->label('Delete')
|
||||
->modalHeading('Delete Server Provider')
|
||||
->authorize(fn (ServerProvider $record) => auth()->user()->can('delete', $record))
|
||||
->using(function (array $data, ServerProvider $record) {
|
||||
->authorize(fn (ServerProvider $record) => $user->can('delete', $record))
|
||||
->using(function (array $data, ServerProvider $record): void {
|
||||
try {
|
||||
app(DeleteServerProvider::class)->delete($record);
|
||||
} catch (Exception $e) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\SourceControl\ConnectSourceControl;
|
||||
use App\Enums\SourceControl;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Select;
|
||||
@ -13,12 +14,15 @@
|
||||
|
||||
class Create
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
Select::make('provider')
|
||||
->options(
|
||||
collect(config('core.source_control_providers'))
|
||||
collect((array) config('core.source_control_providers'))
|
||||
->mapWithKeys(fn ($provider) => [$provider => $provider])
|
||||
)
|
||||
->live()
|
||||
@ -29,21 +33,21 @@ public static function form(): array
|
||||
TextInput::make('token')
|
||||
->label('API Key')
|
||||
->validationAttribute('API Key')
|
||||
->visible(fn ($get) => in_array($get('provider'), [
|
||||
->visible(fn ($get): bool => in_array($get('provider'), [
|
||||
SourceControl::GITHUB,
|
||||
SourceControl::GITLAB,
|
||||
]))
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['token']),
|
||||
TextInput::make('url')
|
||||
->label('URL (optional)')
|
||||
->visible(fn ($get) => $get('provider') == SourceControl::GITLAB)
|
||||
->visible(fn ($get): bool => $get('provider') == SourceControl::GITLAB)
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['url'])
|
||||
->helperText('If you run a self-managed gitlab enter the url here, leave empty to use gitlab.com'),
|
||||
TextInput::make('username')
|
||||
->visible(fn ($get) => $get('provider') == SourceControl::BITBUCKET)
|
||||
->visible(fn ($get): bool => $get('provider') == SourceControl::BITBUCKET)
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['username']),
|
||||
TextInput::make('password')
|
||||
->visible(fn ($get) => $get('provider') == SourceControl::BITBUCKET)
|
||||
->visible(fn ($get): bool => $get('provider') == SourceControl::BITBUCKET)
|
||||
->rules(fn (Get $get) => ConnectSourceControl::rules($get())['password']),
|
||||
Checkbox::make('global')
|
||||
->label('Is Global (Accessible in all projects)'),
|
||||
@ -51,12 +55,17 @@ public static function form(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function action(array $data): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
try {
|
||||
app(ConnectSourceControl::class)->connect(auth()->user(), auth()->user()->currentProject, $data);
|
||||
app(ConnectSourceControl::class)->connect($user->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\SourceControl\EditSourceControl;
|
||||
use App\Models\SourceControl;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
@ -12,6 +13,9 @@
|
||||
|
||||
class Edit
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(SourceControl $sourceControl): array
|
||||
{
|
||||
return [
|
||||
@ -39,12 +43,17 @@ public static function form(SourceControl $sourceControl): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function action(SourceControl $sourceControl, array $data): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
try {
|
||||
app(EditSourceControl::class)->edit($sourceControl, auth()->user()->currentProject, $data);
|
||||
app(EditSourceControl::class)->edit($sourceControl, $user->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -42,7 +42,7 @@ protected function getHeaderActions(): array
|
||||
->form(Actions\Create::form())
|
||||
->authorize('create', SourceControl::class)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
Actions\Create::action($data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\SourceControl\DeleteSourceControl;
|
||||
use App\Models\SourceControl;
|
||||
use App\Models\User;
|
||||
use App\Web\Pages\Settings\SourceControls\Actions\Edit;
|
||||
use Exception;
|
||||
use Filament\Notifications\Notification;
|
||||
@ -18,18 +19,27 @@
|
||||
|
||||
class SourceControlsList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<SourceControl>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return SourceControl::getByProjectId(auth()->user()->current_project_id);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return SourceControl::getByProjectId($user->current_project_id);
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (SourceControl $record) => 'icon-'.$record->provider)
|
||||
->icon(fn (SourceControl $record): string => 'icon-'.$record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->default(fn (SourceControl $record) => $record->profile)
|
||||
@ -39,10 +49,8 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('id')
|
||||
->label('Global')
|
||||
->badge()
|
||||
->color(fn (SourceControl $record) => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(function (SourceControl $record) {
|
||||
return $record->project_id ? 'No' : 'Yes';
|
||||
}),
|
||||
->color(fn (SourceControl $record): string => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(fn (SourceControl $record): string => $record->project_id ? 'No' : 'Yes'),
|
||||
TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->formatStateUsing(fn (SourceControl $record) => $record->created_at_by_timezone)
|
||||
@ -53,6 +61,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -61,18 +72,16 @@ public function table(Table $table): Table
|
||||
EditAction::make('edit')
|
||||
->label('Edit')
|
||||
->modalHeading('Edit Source Control')
|
||||
->fillForm(function (array $data, SourceControl $record) {
|
||||
return [
|
||||
'provider' => $record->provider,
|
||||
'name' => $record->profile,
|
||||
'token' => $record->provider_data['token'] ?? null,
|
||||
'username' => $record->provider_data['username'] ?? null,
|
||||
'password' => $record->provider_data['password'] ?? null,
|
||||
'global' => $record->project_id === null,
|
||||
];
|
||||
})
|
||||
->form(fn (SourceControl $record) => Edit::form($record))
|
||||
->authorize(fn (SourceControl $record) => auth()->user()->can('update', $record))
|
||||
->fillForm(fn (array $data, SourceControl $record): array => [
|
||||
'provider' => $record->provider,
|
||||
'name' => $record->profile,
|
||||
'token' => $record->provider_data['token'] ?? null,
|
||||
'username' => $record->provider_data['username'] ?? null,
|
||||
'password' => $record->provider_data['password'] ?? null,
|
||||
'global' => $record->project_id === null,
|
||||
])
|
||||
->form(fn (SourceControl $record): array => Edit::form($record))
|
||||
->authorize(fn (SourceControl $record) => $user->can('update', $record))
|
||||
->using(fn (array $data, SourceControl $record) => Edit::action($record, $data))
|
||||
->modalWidth(MaxWidth::Medium),
|
||||
Action::make('delete')
|
||||
@ -81,8 +90,8 @@ public function table(Table $table): Table
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Delete Source Control')
|
||||
->authorize(fn (SourceControl $record) => auth()->user()->can('delete', $record))
|
||||
->action(function (array $data, SourceControl $record) {
|
||||
->authorize(fn (SourceControl $record) => $user->can('delete', $record))
|
||||
->action(function (array $data, SourceControl $record): void {
|
||||
try {
|
||||
app(DeleteSourceControl::class)->delete($record);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\StorageProvider\CreateStorageProvider;
|
||||
use App\Enums\StorageProvider;
|
||||
use App\Models\User;
|
||||
use App\Web\Components\Link;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
@ -14,12 +15,15 @@
|
||||
|
||||
class Create
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
Select::make('provider')
|
||||
->options(
|
||||
collect(config('core.storage_providers'))
|
||||
collect((array) config('core.storage_providers'))
|
||||
->mapWithKeys(fn ($provider) => [$provider => $provider])
|
||||
)
|
||||
->live()
|
||||
@ -31,62 +35,58 @@ public static function form(): array
|
||||
TextInput::make('token')
|
||||
->label('API Token')
|
||||
->validationAttribute('API Token')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::DROPBOX)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::DROPBOX)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['token']),
|
||||
Grid::make()
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::FTP)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::FTP)
|
||||
->schema([
|
||||
TextInput::make('host')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::FTP)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::FTP)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['host']),
|
||||
TextInput::make('port')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::FTP)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::FTP)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['port']),
|
||||
TextInput::make('username')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::FTP)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::FTP)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['username']),
|
||||
TextInput::make('password')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::FTP)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::FTP)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['password']),
|
||||
Checkbox::make('ssl')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::FTP)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::FTP)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['ssl']),
|
||||
Checkbox::make('passive')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::FTP)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::FTP)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['passive']),
|
||||
]),
|
||||
TextInput::make('api_url')
|
||||
->label('API URL')
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::S3)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::S3)
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['api_url'])
|
||||
->helperText('Required if you are using an S3 compatible provider like Cloudflare R2'),
|
||||
TextInput::make('path')
|
||||
->visible(fn ($get) => in_array($get('provider'), [
|
||||
->visible(fn ($get): bool => in_array($get('provider'), [
|
||||
StorageProvider::S3,
|
||||
StorageProvider::FTP,
|
||||
StorageProvider::LOCAL,
|
||||
]))
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['path'])
|
||||
->helperText(function ($get) {
|
||||
return match ($get('provider')) {
|
||||
StorageProvider::LOCAL => 'The absolute path on your server that the database exists. like `/home/vito/db-backups`',
|
||||
default => '',
|
||||
};
|
||||
->helperText(fn ($get): string => match ($get('provider')) {
|
||||
StorageProvider::LOCAL => 'The absolute path on your server that the database exists. like `/home/vito/db-backups`',
|
||||
default => '',
|
||||
}),
|
||||
Grid::make()
|
||||
->visible(fn ($get) => $get('provider') == StorageProvider::S3)
|
||||
->visible(fn ($get): bool => $get('provider') == StorageProvider::S3)
|
||||
->schema([
|
||||
TextInput::make('key')
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['key'])
|
||||
->helperText(function ($get) {
|
||||
return match ($get('provider')) {
|
||||
StorageProvider::S3 => new Link(
|
||||
href: 'https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html',
|
||||
text: 'How to generate?',
|
||||
external: true
|
||||
),
|
||||
default => '',
|
||||
};
|
||||
->helperText(fn ($get): Link|string => match ($get('provider')) {
|
||||
StorageProvider::S3 => new Link(
|
||||
href: 'https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html',
|
||||
text: 'How to generate?',
|
||||
external: true
|
||||
),
|
||||
default => '',
|
||||
}),
|
||||
TextInput::make('secret')
|
||||
->rules(fn ($get) => CreateStorageProvider::rules($get())['secret']),
|
||||
@ -101,12 +101,17 @@ public static function form(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function action(array $data): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
try {
|
||||
app(CreateStorageProvider::class)->create(auth()->user(), auth()->user()->currentProject, $data);
|
||||
app(CreateStorageProvider::class)->create($user, $user->currentProject, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -4,11 +4,15 @@
|
||||
|
||||
use App\Actions\StorageProvider\EditStorageProvider;
|
||||
use App\Models\StorageProvider;
|
||||
use App\Models\User;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
|
||||
class Edit
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
@ -20,8 +24,14 @@ public static function form(): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public static function action(StorageProvider $provider, array $data): void
|
||||
{
|
||||
app(EditStorageProvider::class)->edit($provider, auth()->user()->currentProject, $data);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
app(EditStorageProvider::class)->edit($provider, $user->currentProject, $data);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ protected function getHeaderActions(): array
|
||||
->form(Actions\Create::form())
|
||||
->authorize('create', StorageProvider::class)
|
||||
->modalWidth(MaxWidth::ExtraLarge)
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
Actions\Create::action($data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
use App\Actions\StorageProvider\DeleteStorageProvider;
|
||||
use App\Models\StorageProvider;
|
||||
use App\Models\User;
|
||||
use App\Web\Pages\Settings\StorageProviders\Actions\Edit;
|
||||
use Exception;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
@ -17,18 +19,27 @@
|
||||
|
||||
class StorageProvidersList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<StorageProvider>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return StorageProvider::getByProjectId(auth()->user()->current_project_id);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return StorageProvider::getByProjectId($user->current_project_id);
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (StorageProvider $record) => 'icon-'.$record->provider)
|
||||
->icon(fn (StorageProvider $record): string => 'icon-'.$record->provider)
|
||||
->tooltip(fn (StorageProvider $record) => $record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
@ -39,10 +50,8 @@ protected function getTableColumns(): array
|
||||
TextColumn::make('id')
|
||||
->label('Global')
|
||||
->badge()
|
||||
->color(fn ($record) => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(function (StorageProvider $record) {
|
||||
return $record->project_id ? 'No' : 'Yes';
|
||||
}),
|
||||
->color(fn ($record): string => $record->project_id ? 'gray' : 'success')
|
||||
->formatStateUsing(fn (StorageProvider $record): string => $record->project_id ? 'No' : 'Yes'),
|
||||
TextColumn::make('created_at')
|
||||
->label('Created At')
|
||||
->formatStateUsing(fn ($record) => $record->created_at_by_timezone)
|
||||
@ -53,6 +62,9 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
@ -61,24 +73,22 @@ public function table(Table $table): Table
|
||||
EditAction::make('edit')
|
||||
->label('Edit')
|
||||
->modalHeading('Edit Storage Provider')
|
||||
->mutateRecordDataUsing(function (array $data, StorageProvider $record) {
|
||||
return [
|
||||
'name' => $record->profile,
|
||||
'global' => $record->project_id === null,
|
||||
];
|
||||
})
|
||||
->mutateRecordDataUsing(fn (array $data, StorageProvider $record): array => [
|
||||
'name' => $record->profile,
|
||||
'global' => $record->project_id === null,
|
||||
])
|
||||
->form(Edit::form())
|
||||
->authorize(fn (StorageProvider $record) => auth()->user()->can('update', $record))
|
||||
->authorize(fn (StorageProvider $record) => $user->can('update', $record))
|
||||
->using(fn (array $data, StorageProvider $record) => Edit::action($record, $data))
|
||||
->modalWidth(MaxWidth::Medium),
|
||||
DeleteAction::make('delete')
|
||||
->label('Delete')
|
||||
->modalHeading('Delete Storage Provider')
|
||||
->authorize(fn (StorageProvider $record) => auth()->user()->can('delete', $record))
|
||||
->using(function (array $data, StorageProvider $record) {
|
||||
->authorize(fn (StorageProvider $record) => $user->can('delete', $record))
|
||||
->using(function (array $data, StorageProvider $record): void {
|
||||
try {
|
||||
app(DeleteStorageProvider::class)->delete($record);
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->danger()
|
||||
->title($e->getMessage())
|
||||
|
@ -12,6 +12,9 @@
|
||||
|
||||
class Create
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
@ -19,10 +22,10 @@ public static function form(): array
|
||||
->rules(fn ($get) => CreateTag::rules()['name']),
|
||||
Select::make('color')
|
||||
->prefixIcon('heroicon-s-tag')
|
||||
->prefixIconColor(fn (Get $get) => $get('color'))
|
||||
->prefixIconColor(fn (Get $get): mixed => $get('color'))
|
||||
->searchable()
|
||||
->options(
|
||||
collect(config('core.tag_colors'))
|
||||
collect((array) config('core.tag_colors'))
|
||||
->mapWithKeys(fn ($color) => [$color => $color])
|
||||
)
|
||||
->reactive()
|
||||
@ -31,12 +34,17 @@ public static function form(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function action(array $data): Tag
|
||||
{
|
||||
try {
|
||||
return app(CreateTag::class)->create(auth()->user(), $data);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return app(CreateTag::class)->create($user, $data);
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
|
@ -9,6 +9,9 @@
|
||||
|
||||
class Edit
|
||||
{
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public static function form(): array
|
||||
{
|
||||
return [
|
||||
@ -17,13 +20,16 @@ public static function form(): array
|
||||
Select::make('color')
|
||||
->searchable()
|
||||
->options(
|
||||
collect(config('core.tag_colors'))
|
||||
collect((array) config('core.tag_colors'))
|
||||
->mapWithKeys(fn ($color) => [$color => $color])
|
||||
)
|
||||
->rules(fn ($get) => EditTag::rules()['color']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public static function action(Tag $tag, array $data): void
|
||||
{
|
||||
app(EditTag::class)->edit($tag, $data);
|
||||
|
@ -5,6 +5,8 @@
|
||||
use App\Actions\Tag\SyncTags;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Filament\Forms\Components\Actions\Action as FormAction;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Infolists\Components\Actions\Action as InfolistAction;
|
||||
@ -26,8 +28,8 @@ public static function infolist(mixed $taggable): InfolistAction
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Edit Tags')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form(static::form($taggable))
|
||||
->action(static::action($taggable));
|
||||
->form(self::form($taggable))
|
||||
->action(self::action($taggable));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,22 +44,23 @@ public static function table(mixed $taggable): TableAction
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Edit Tags')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form(static::form($taggable))
|
||||
->action(static::action($taggable));
|
||||
->form(self::form($taggable))
|
||||
->action(self::action($taggable));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Site|Server $taggable
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
private static function form(mixed $taggable): array
|
||||
private static function form(Site|Server $taggable): array
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
Select::make('tags')
|
||||
->default($taggable->tags()->pluck('tags.id')->toArray())
|
||||
->options(function () {
|
||||
return auth()->user()->currentProject->tags()->pluck('name', 'id')->toArray();
|
||||
})
|
||||
->nestedRecursiveRules(SyncTags::rules(auth()->user()->currentProject->id)['tags.*'])
|
||||
->options(fn () => $user->currentProject->tags()->pluck('name', 'id')->toArray())
|
||||
->nestedRecursiveRules(SyncTags::rules($user->currentProject->id)['tags.*'])
|
||||
->suffixAction(
|
||||
FormAction::make('create_tag')
|
||||
->icon('heroicon-o-plus')
|
||||
@ -66,7 +69,7 @@ private static function form(mixed $taggable): array
|
||||
->modalHeading('Create Tag')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form(Create::form())
|
||||
->action(function (array $data) {
|
||||
->action(function (array $data): void {
|
||||
Create::action($data);
|
||||
}),
|
||||
)
|
||||
@ -77,12 +80,12 @@ private static function form(mixed $taggable): array
|
||||
/**
|
||||
* @param Site|Server $taggable
|
||||
*/
|
||||
private static function action(mixed $taggable): \Closure
|
||||
private static function action(mixed $taggable): Closure
|
||||
{
|
||||
return function (array $data) use ($taggable) {
|
||||
app(SyncTags::class)->sync(auth()->user(), [
|
||||
return function (array $data) use ($taggable): void {
|
||||
app(SyncTags::class)->sync([
|
||||
'taggable_id' => $taggable->id,
|
||||
'taggable_type' => get_class($taggable),
|
||||
'taggable_type' => $taggable::class,
|
||||
'tags' => $data['tags'],
|
||||
]);
|
||||
|
||||
|
@ -25,6 +25,9 @@ public static function canAccess(): bool
|
||||
return auth()->user()?->can('viewAny', Tag::class) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, class-string>>
|
||||
*/
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
@ -44,7 +47,7 @@ protected function getHeaderActions(): array
|
||||
->form(Actions\Create::form())
|
||||
->authorize('create', Tag::class)
|
||||
->modalWidth(MaxWidth::ExtraLarge)
|
||||
->using(function (array $data) {
|
||||
->using(function (array $data): void {
|
||||
Actions\Create::action($data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\Tag\DeleteTag;
|
||||
use App\Models\Tag;
|
||||
use App\Models\User;
|
||||
use App\Web\Pages\Settings\Tags\Actions\Edit;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Tables\Actions\Action;
|
||||
@ -17,11 +18,20 @@
|
||||
|
||||
class TagsList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
/**
|
||||
* @return Builder<Tag>
|
||||
*/
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Tag::getByProjectId(auth()->user()->current_project_id);
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return Tag::getByProjectId($user->current_project_id);
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
@ -53,25 +63,29 @@ public function table(Table $table): Table
|
||||
|
||||
private function editAction(): Action
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return EditAction::make('edit')
|
||||
->fillForm(function (Tag $record) {
|
||||
return [
|
||||
'name' => $record->name,
|
||||
'color' => $record->color,
|
||||
'global' => $record->project_id === null,
|
||||
];
|
||||
})
|
||||
->fillForm(fn (Tag $record): array => [
|
||||
'name' => $record->name,
|
||||
'color' => $record->color,
|
||||
'global' => $record->project_id === null,
|
||||
])
|
||||
->form(Edit::form())
|
||||
->authorize(fn (Tag $record) => auth()->user()->can('update', $record))
|
||||
->authorize(fn (Tag $record) => $user->can('update', $record))
|
||||
->using(fn (array $data, Tag $record) => Edit::action($record, $data))
|
||||
->modalWidth(MaxWidth::Medium);
|
||||
}
|
||||
|
||||
private function deleteAction(): Action
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return DeleteAction::make('delete')
|
||||
->authorize(fn (Tag $record) => auth()->user()->can('delete', $record))
|
||||
->using(function (Tag $record) {
|
||||
->authorize(fn (Tag $record) => $user->can('delete', $record))
|
||||
->using(function (Tag $record): void {
|
||||
app(DeleteTag::class)->delete($record);
|
||||
});
|
||||
}
|
||||
|
@ -28,6 +28,9 @@ public static function canAccess(): bool
|
||||
return auth()->user()?->can('viewAny', User::class) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, class-string>>
|
||||
*/
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
@ -48,7 +51,7 @@ protected function getHeaderActions(): array
|
||||
|
||||
return $user;
|
||||
})
|
||||
->form(function (Form $form) {
|
||||
->form(function (Form $form): \Filament\Forms\Form {
|
||||
$rules = CreateUser::rules();
|
||||
|
||||
return $form
|
||||
@ -61,7 +64,7 @@ protected function getHeaderActions(): array
|
||||
->rules($rules['password']),
|
||||
Select::make('role')
|
||||
->rules($rules['role'])
|
||||
->options(collect(config('core.user_roles'))->mapWithKeys(fn ($role) => [$role => $role])),
|
||||
->options(collect((array) config('core.user_roles'))->mapWithKeys(fn ($role) => [$role => $role])),
|
||||
])
|
||||
->columns(1);
|
||||
})
|
||||
|
@ -18,17 +18,14 @@
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class UsersList extends Widget
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return User::query();
|
||||
}
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
@ -50,56 +47,55 @@ protected function getTableColumns(): array
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $table
|
||||
->heading(null)
|
||||
->query($this->getTableQuery())
|
||||
->query(User::query())
|
||||
->columns($this->getTableColumns())
|
||||
->actions([
|
||||
EditAction::make('edit')
|
||||
->authorize(fn ($record) => auth()->user()->can('update', $record))
|
||||
->using(function ($record, array $data) {
|
||||
->authorize(fn ($record) => $user->can('update', $record))
|
||||
->using(function ($record, array $data): void {
|
||||
app(UpdateUser::class)->update($record, $data);
|
||||
})
|
||||
->form(function (Form $form, $record) {
|
||||
return $form
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->rules(UpdateUser::rules($record)['name']),
|
||||
TextInput::make('email')
|
||||
->rules(UpdateUser::rules($record)['email']),
|
||||
Select::make('timezone')
|
||||
->searchable()
|
||||
->options(
|
||||
collect(timezone_identifiers_list())
|
||||
->mapWithKeys(fn ($timezone) => [$timezone => $timezone])
|
||||
)
|
||||
->rules(UpdateUser::rules($record)['timezone']),
|
||||
Select::make('role')
|
||||
->options(
|
||||
collect(config('core.user_roles'))
|
||||
->mapWithKeys(fn ($role) => [$role => $role])
|
||||
)
|
||||
->rules(UpdateUser::rules($record)['role']),
|
||||
])
|
||||
->columns(1);
|
||||
})
|
||||
->form(fn (Form $form, $record): \Filament\Forms\Form => $form
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->rules(UpdateUser::rules($record)['name']),
|
||||
TextInput::make('email')
|
||||
->rules(UpdateUser::rules($record)['email']),
|
||||
Select::make('timezone')
|
||||
->searchable()
|
||||
->options(
|
||||
collect(timezone_identifiers_list())
|
||||
->mapWithKeys(fn ($timezone) => [$timezone => $timezone])
|
||||
)
|
||||
->rules(UpdateUser::rules($record)['timezone']),
|
||||
Select::make('role')
|
||||
->options(
|
||||
collect((array) config('core.user_roles'))
|
||||
->mapWithKeys(fn ($role) => [$role => $role])
|
||||
)
|
||||
->rules(UpdateUser::rules($record)['role']),
|
||||
])
|
||||
->columns(1))
|
||||
->modalWidth(MaxWidth::Large),
|
||||
Action::make('update-projects')
|
||||
->label('Projects')
|
||||
->icon('heroicon-o-rectangle-stack')
|
||||
->authorize(fn ($record) => auth()->user()->can('update', $record))
|
||||
->form(function (Form $form, $record) {
|
||||
return $form
|
||||
->schema([
|
||||
CheckboxList::make('projects')
|
||||
->options(Project::query()->pluck('name', 'id')->toArray())
|
||||
->searchable()
|
||||
->default($record->projects->pluck('id')->toArray())
|
||||
->rules(UpdateProjects::rules()['projects.*']),
|
||||
])
|
||||
->columns(1);
|
||||
})
|
||||
->action(function ($record, array $data) {
|
||||
->authorize(fn ($record) => $user->can('update', $record))
|
||||
->form(fn (Form $form, $record): \Filament\Forms\Form => $form
|
||||
->schema([
|
||||
CheckboxList::make('projects')
|
||||
->options(Project::query()->pluck('name', 'id')->toArray())
|
||||
->searchable()
|
||||
->default($record->projects->pluck('id')->toArray())
|
||||
->rules(UpdateProjects::rules()['projects.*']),
|
||||
])
|
||||
->columns(1))
|
||||
->action(function ($record, array $data): void {
|
||||
app(UpdateProjects::class)->update($record, $data);
|
||||
Notification::make()
|
||||
->title('Projects Updated')
|
||||
@ -109,7 +105,7 @@ public function table(Table $table): Table
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalWidth(MaxWidth::Large),
|
||||
DeleteAction::make('delete')
|
||||
->authorize(fn (User $record) => auth()->user()->can('delete', $record)),
|
||||
->authorize(fn (User $record) => $user->can('delete', $record)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user