mirror of
https://github.com/pelican-dev/panel.git
synced 2026-05-04 18:00:48 +03:00
Compare commits
1 Commits
release/v1
...
release/v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7edbc72074 |
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Egg;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UpdateEggIndexCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:egg:update-index';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
$data = file_get_contents('https://raw.githubusercontent.com/pelican-eggs/pelican-eggs.github.io/refs/heads/main/content/pelican.json');
|
||||
$data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (Exception $exception) {
|
||||
$this->error($exception->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$index = [];
|
||||
foreach ($data['nests'] as $nest) {
|
||||
$nestName = $nest['nest_type'];
|
||||
|
||||
$this->info("Nest: $nestName");
|
||||
|
||||
$nestEggs = [];
|
||||
foreach ($nest['Eggs'] as $egg) {
|
||||
$eggName = $egg['egg']['name'];
|
||||
|
||||
$this->comment("Egg: $eggName");
|
||||
|
||||
$nestEggs[$egg['download_url']] = $eggName;
|
||||
}
|
||||
$index[$nestName] = $nestEggs;
|
||||
|
||||
$this->info('');
|
||||
}
|
||||
|
||||
cache()->forever('eggs.index', $index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,6 @@ class QueueWorkerServiceCommand extends Command
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
if (@file_exists('/.dockerenv')) {
|
||||
$result = Process::run('supervisorctl restart queue-worker');
|
||||
if ($result->failed()) {
|
||||
$this->error('Error restarting service: ' . $result->errorOutput());
|
||||
|
||||
return;
|
||||
}
|
||||
$this->line('Queue worker service file updated successfully.');
|
||||
|
||||
return;
|
||||
}
|
||||
$serviceName = $this->option('service-name') ?? $this->ask('Queue worker service name', 'pelican-queue');
|
||||
$path = '/etc/systemd/system/' . $serviceName . '.service';
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ class MakeNodeCommand extends Command
|
||||
{--overallocateCpu= : Enter the amount of cpu to overallocate (% or -1 to overallocate the maximum).}
|
||||
{--uploadSize= : Enter the maximum upload filesize.}
|
||||
{--daemonListeningPort= : Enter the daemon listening port.}
|
||||
{--daemonConnectingPort= : Enter the daemon connecting port.}
|
||||
{--daemonSFTPPort= : Enter the daemon SFTP listening port.}
|
||||
{--daemonSFTPAlias= : Enter the daemon SFTP alias.}
|
||||
{--daemonBase= : Enter the base folder.}';
|
||||
@@ -58,7 +57,6 @@ class MakeNodeCommand extends Command
|
||||
$data['cpu_overallocate'] = $this->option('overallocateCpu') ?? $this->ask(trans('commands.make_node.cpu_overallocate'), '-1');
|
||||
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(trans('commands.make_node.upload_size'), '256');
|
||||
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(trans('commands.make_node.daemonListen'), '8080');
|
||||
$data['daemon_connect'] = $this->option('daemonConnectingPort') ?? $this->ask(trans('commands.make_node.daemonConnect'), '8080');
|
||||
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(trans('commands.make_node.daemonSFTP'), '2022');
|
||||
$data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(trans('commands.make_node.daemonSFTPAlias'), '');
|
||||
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(trans('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
namespace App\Console;
|
||||
|
||||
use App\Console\Commands\Egg\CheckEggUpdatesCommand;
|
||||
use App\Console\Commands\Egg\UpdateEggIndexCommand;
|
||||
use App\Console\Commands\Maintenance\CleanServiceBackupFilesCommand;
|
||||
use App\Console\Commands\Maintenance\PruneImagesCommand;
|
||||
use App\Console\Commands\Maintenance\PruneOrphanedBackupsCommand;
|
||||
use App\Console\Commands\Schedule\ProcessRunnableCommand;
|
||||
use App\Jobs\NodeStatistics;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\Webhook;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
@@ -42,9 +42,9 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->command(CleanServiceBackupFilesCommand::class)->daily();
|
||||
$schedule->command(PruneImagesCommand::class)->daily();
|
||||
$schedule->command(CheckEggUpdatesCommand::class)->hourly();
|
||||
|
||||
$schedule->command(CheckEggUpdatesCommand::class)->daily();
|
||||
$schedule->command(UpdateEggIndexCommand::class)->daily();
|
||||
$schedule->job(new NodeStatistics())->everyFiveSeconds()->withoutOverlapping();
|
||||
|
||||
if (config('backups.prune_age')) {
|
||||
// Every 30 minutes, run the backup pruning command so that any abandoned backups can be deleted.
|
||||
|
||||
@@ -88,7 +88,7 @@ enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
|
||||
|
||||
public function isStartable(): bool
|
||||
{
|
||||
return !in_array($this, [ContainerStatus::Running, ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting, ContainerStatus::Missing]);
|
||||
return !in_array($this, [ContainerStatus::Running, ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting]);
|
||||
}
|
||||
|
||||
public function isRestartable(): bool
|
||||
@@ -97,16 +97,18 @@ enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
|
||||
return true;
|
||||
}
|
||||
|
||||
return !in_array($this, [ContainerStatus::Offline, ContainerStatus::Missing]);
|
||||
return !in_array($this, [ContainerStatus::Offline]);
|
||||
}
|
||||
|
||||
public function isStoppable(): bool
|
||||
{
|
||||
return !in_array($this, [ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting, ContainerStatus::Exited, ContainerStatus::Offline, ContainerStatus::Missing]);
|
||||
return !in_array($this, [ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting, ContainerStatus::Exited, ContainerStatus::Offline]);
|
||||
}
|
||||
|
||||
public function isKillable(): bool
|
||||
{
|
||||
return !in_array($this, [ContainerStatus::Offline, ContainerStatus::Running, ContainerStatus::Exited, ContainerStatus::Missing]);
|
||||
// [ContainerStatus::Restarting, ContainerStatus::Removing, ContainerStatus::Dead, ContainerStatus::Created]
|
||||
|
||||
return !in_array($this, [ContainerStatus::Offline, ContainerStatus::Running, ContainerStatus::Exited]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum HeaderActionPosition: string
|
||||
{
|
||||
case Before = 'before';
|
||||
case After = 'after';
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum HeaderWidgetPosition: string
|
||||
{
|
||||
case Before = 'before';
|
||||
case After = 'after';
|
||||
}
|
||||
@@ -27,16 +27,8 @@ enum ServerState: string implements HasColor, HasIcon, HasLabel
|
||||
};
|
||||
}
|
||||
|
||||
public function getColor(bool $hex = false): string
|
||||
public function getColor(): string
|
||||
{
|
||||
if ($hex) {
|
||||
return match ($this) {
|
||||
self::Normal, self::Installing, self::RestoringBackup => '#2563EB',
|
||||
self::Suspended => '#D97706',
|
||||
self::InstallFailed, self::ReinstallFailed => '#EF4444',
|
||||
};
|
||||
}
|
||||
|
||||
return match ($this) {
|
||||
self::Normal => 'primary',
|
||||
self::Installing => 'primary',
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
use Filament\Support\Contracts\HasColor;
|
||||
use Filament\Support\Contracts\HasIcon;
|
||||
|
||||
enum WebhookType: string implements HasColor, HasIcon, HasLabel
|
||||
{
|
||||
case Regular = 'regular';
|
||||
case Discord = 'discord';
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return trans('admin/webhook.' . $this->value);
|
||||
}
|
||||
|
||||
public function getColor(): ?string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Regular => null,
|
||||
self::Discord => 'blurple',
|
||||
};
|
||||
}
|
||||
|
||||
public function getIcon(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Regular => 'tabler-world-www',
|
||||
self::Discord => 'tabler-brand-discord',
|
||||
};
|
||||
}
|
||||
}
|
||||
11
app/Events/Auth/DirectLogin.php
Normal file
11
app/Events/Auth/DirectLogin.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Events\Event;
|
||||
|
||||
class DirectLogin extends Event
|
||||
{
|
||||
public function __construct(public User $user, public bool $remember) {}
|
||||
}
|
||||
16
app/Events/Auth/FailedPasswordReset.php
Normal file
16
app/Events/Auth/FailedPasswordReset.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events\Auth;
|
||||
|
||||
use App\Events\Event;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class FailedPasswordReset extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(public string $ip, public string $email) {}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Repository;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FileExistsException extends Exception {}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Deployment;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class NoViableNodeException extends DisplayException {}
|
||||
42
app/Extensions/Avatar/AvatarProvider.php
Normal file
42
app/Extensions/Avatar/AvatarProvider.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Avatar;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
abstract class AvatarProvider
|
||||
{
|
||||
/**
|
||||
* @var array<string, static>
|
||||
*/
|
||||
protected static array $providers = [];
|
||||
|
||||
public static function getProvider(string $id): ?self
|
||||
{
|
||||
return Arr::get(static::$providers, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, static>
|
||||
*/
|
||||
public static function getAll(): array
|
||||
{
|
||||
return static::$providers;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
static::$providers[$this->getId()] = $this;
|
||||
}
|
||||
|
||||
abstract public function getId(): string;
|
||||
|
||||
abstract public function get(User $user): ?string;
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return Str::title($this->getId());
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Avatar;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
interface AvatarSchemaInterface
|
||||
{
|
||||
public function getId(): string;
|
||||
|
||||
public function getName(): string;
|
||||
|
||||
public function get(User $user): ?string;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Avatar;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class AvatarService
|
||||
{
|
||||
/** @var AvatarSchemaInterface[] */
|
||||
private array $schemas = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly bool $allowUploadedAvatars,
|
||||
private readonly string $activeSchema,
|
||||
) {}
|
||||
|
||||
public function get(string $id): ?AvatarSchemaInterface
|
||||
{
|
||||
return array_get($this->schemas, $id);
|
||||
}
|
||||
|
||||
public function getActiveSchema(): ?AvatarSchemaInterface
|
||||
{
|
||||
return $this->get($this->activeSchema);
|
||||
}
|
||||
|
||||
public function getAvatarUrl(User $user): ?string
|
||||
{
|
||||
if ($this->allowUploadedAvatars) {
|
||||
$path = "avatars/$user->id.png";
|
||||
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
return Storage::url($path);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getActiveSchema()?->get($user);
|
||||
}
|
||||
|
||||
public function register(AvatarSchemaInterface $schema): void
|
||||
{
|
||||
if (array_key_exists($schema->getId(), $this->schemas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->schemas[$schema->getId()] = $schema;
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function getMappings(): array
|
||||
{
|
||||
return collect($this->schemas)->mapWithKeys(fn ($schema) => [$schema->getId() => $schema->getName()])->all();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Avatar\Schemas;
|
||||
namespace App\Extensions\Avatar\Providers;
|
||||
|
||||
use App\Extensions\Avatar\AvatarSchemaInterface;
|
||||
use App\Extensions\Avatar\AvatarProvider;
|
||||
use App\Models\User;
|
||||
|
||||
class GravatarSchema implements AvatarSchemaInterface
|
||||
class GravatarProvider extends AvatarProvider
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
return 'gravatar';
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Gravatar';
|
||||
}
|
||||
|
||||
public function get(User $user): string
|
||||
{
|
||||
return 'https://gravatar.com/avatar/' . md5($user->email);
|
||||
}
|
||||
|
||||
public static function register(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Avatar\Schemas;
|
||||
namespace App\Extensions\Avatar\Providers;
|
||||
|
||||
use App\Extensions\Avatar\AvatarSchemaInterface;
|
||||
use App\Extensions\Avatar\AvatarProvider;
|
||||
use App\Models\User;
|
||||
|
||||
class UiAvatarsSchema implements AvatarSchemaInterface
|
||||
class UiAvatarsProvider extends AvatarProvider
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
@@ -22,4 +22,9 @@ class UiAvatarsSchema implements AvatarSchemaInterface
|
||||
// UI Avatars is the default of filament so just return null here
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function register(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Captcha;
|
||||
|
||||
use App\Extensions\Captcha\Schemas\CaptchaSchemaInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CaptchaService
|
||||
{
|
||||
/** @var array<string, CaptchaSchemaInterface> */
|
||||
private array $schemas = [];
|
||||
|
||||
/**
|
||||
* @return CaptchaSchemaInterface[]
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->schemas;
|
||||
}
|
||||
|
||||
public function get(string $id): ?CaptchaSchemaInterface
|
||||
{
|
||||
return array_get($this->schemas, $id);
|
||||
}
|
||||
|
||||
public function register(CaptchaSchemaInterface $schema): void
|
||||
{
|
||||
if (array_key_exists($schema->getId(), $this->schemas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
config()->set('captcha.' . Str::lower($schema->getId()), $schema->getConfig());
|
||||
$this->schemas[$schema->getId()] = $schema;
|
||||
}
|
||||
|
||||
/** @return Collection<CaptchaSchemaInterface> */
|
||||
public function getActiveSchemas(): Collection
|
||||
{
|
||||
return collect($this->schemas)
|
||||
->filter(fn (CaptchaSchemaInterface $schema) => $schema->isEnabled());
|
||||
}
|
||||
|
||||
public function getActiveSchema(): ?CaptchaSchemaInterface
|
||||
{
|
||||
return $this->getActiveSchemas()->first();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Captcha\Schemas;
|
||||
namespace App\Extensions\Captcha\Providers;
|
||||
|
||||
use Filament\Forms\Components\Component;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
abstract class BaseSchema
|
||||
abstract class CaptchaProvider
|
||||
{
|
||||
/**
|
||||
* @var array<string, static>
|
||||
*/
|
||||
protected static array $providers = [];
|
||||
|
||||
/**
|
||||
* @return self|static[]
|
||||
*/
|
||||
public static function get(?string $id = null): array|self
|
||||
{
|
||||
return $id ? static::$providers[$id] : static::$providers;
|
||||
}
|
||||
|
||||
protected function __construct(protected Application $app)
|
||||
{
|
||||
if (array_key_exists($this->getId(), static::$providers)) {
|
||||
if (!$this->app->runningUnitTests()) {
|
||||
logger()->warning("Tried to create duplicate Captcha provider with id '{$this->getId()}'");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
config()->set('captcha.' . Str::lower($this->getId()), $this->getConfig());
|
||||
|
||||
static::$providers[$this->getId()] = $this;
|
||||
}
|
||||
|
||||
abstract public function getId(): string;
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return Str::upper($this->getId());
|
||||
}
|
||||
abstract public function getComponent(): Component;
|
||||
|
||||
/**
|
||||
* @return array<string, string|string[]|bool|null>
|
||||
@@ -57,6 +83,34 @@ abstract class BaseSchema
|
||||
];
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return Str::title($this->getId());
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
$id = Str::upper($this->getId());
|
||||
|
||||
return env("CAPTCHA_{$id}_ENABLED", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|bool>
|
||||
*/
|
||||
public function validateResponse(?string $captchaResponse = null): array
|
||||
{
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'validateResponse not defined',
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyDomain(string $hostname, ?string $requestUrl = null): bool
|
||||
{
|
||||
return true;
|
||||
@@ -1,31 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Captcha\Schemas\Turnstile;
|
||||
namespace App\Extensions\Captcha\Providers;
|
||||
|
||||
use App\Extensions\Captcha\Schemas\CaptchaSchemaInterface;
|
||||
use App\Extensions\Captcha\Schemas\BaseSchema;
|
||||
use App\Filament\Components\Forms\Fields\TurnstileCaptcha;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Component as BaseComponent;
|
||||
use Filament\Forms\Components\Component;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class TurnstileSchema extends BaseSchema implements CaptchaSchemaInterface
|
||||
class TurnstileProvider extends CaptchaProvider
|
||||
{
|
||||
public function getId(): string
|
||||
{
|
||||
return 'turnstile';
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
public function getComponent(): Component
|
||||
{
|
||||
return env('CAPTCHA_TURNSTILE_ENABLED', false);
|
||||
}
|
||||
|
||||
public function getFormComponent(): BaseComponent
|
||||
{
|
||||
return Component::make('turnstile');
|
||||
return TurnstileCaptcha::make('turnstile');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +34,7 @@ class TurnstileSchema extends BaseSchema implements CaptchaSchemaInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BaseComponent[]
|
||||
* @return Component[]
|
||||
*/
|
||||
public function getSettingsForm(): array
|
||||
{
|
||||
@@ -57,14 +52,20 @@ class TurnstileSchema extends BaseSchema implements CaptchaSchemaInterface
|
||||
->label(trans('admin/setting.captcha.info_label'))
|
||||
->columnSpan(2)
|
||||
->content(new HtmlString(trans('admin/setting.captcha.info'))),
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
public function getIcon(): string
|
||||
{
|
||||
return 'tabler-brand-cloudflare';
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|bool>
|
||||
*/
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Captcha\Schemas;
|
||||
|
||||
use Filament\Forms\Components\Component;
|
||||
|
||||
interface CaptchaSchemaInterface
|
||||
{
|
||||
public function getId(): string;
|
||||
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* @return array<string, string|string[]|bool|null>
|
||||
*/
|
||||
public function getConfig(): array;
|
||||
|
||||
public function isEnabled(): bool;
|
||||
|
||||
public function getFormComponent(): Component;
|
||||
|
||||
/**
|
||||
* @return Component[]
|
||||
*/
|
||||
public function getSettingsForm(): array;
|
||||
|
||||
public function getIcon(): ?string;
|
||||
|
||||
/**
|
||||
* @return array<string, string|bool>
|
||||
*/
|
||||
public function validateResponse(?string $captchaResponse = null): array;
|
||||
|
||||
public function verifyDomain(string $hostname, ?string $requestUrl = null): bool;
|
||||
}
|
||||
51
app/Extensions/Features/FeatureProvider.php
Normal file
51
app/Extensions/Features/FeatureProvider.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
abstract class FeatureProvider
|
||||
{
|
||||
/**
|
||||
* @var array<string, static>
|
||||
*/
|
||||
protected static array $providers = [];
|
||||
|
||||
/**
|
||||
* @param string[] $id
|
||||
* @return self|static[]
|
||||
*/
|
||||
public static function getProviders(string|array|null $id = null): array|self
|
||||
{
|
||||
if (is_array($id)) {
|
||||
return array_intersect_key(static::$providers, array_flip($id));
|
||||
}
|
||||
|
||||
return $id ? static::$providers[$id] : static::$providers;
|
||||
}
|
||||
|
||||
protected function __construct(protected Application $app)
|
||||
{
|
||||
if (array_key_exists($this->getId(), static::$providers)) {
|
||||
if (!$this->app->runningUnitTests()) {
|
||||
logger()->warning("Tried to create duplicate Feature provider with id '{$this->getId()}'");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static::$providers[$this->getId()] = $this;
|
||||
}
|
||||
|
||||
abstract public function getId(): string;
|
||||
|
||||
/**
|
||||
* A matching subset string (case-insensitive) from the console output
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
abstract public function getListeners(): array;
|
||||
|
||||
abstract public function getAction(): Action;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
use Filament\Actions\Action;
|
||||
|
||||
interface FeatureSchemaInterface
|
||||
{
|
||||
/** @return string[] */
|
||||
public function getListeners(): array;
|
||||
|
||||
public function getId(): string;
|
||||
|
||||
public function getAction(): Action;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
class FeatureService
|
||||
{
|
||||
/** @var FeatureSchemaInterface[] */
|
||||
private array $schemas = [];
|
||||
|
||||
/**
|
||||
* @return FeatureSchemaInterface[]
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->schemas;
|
||||
}
|
||||
|
||||
public function get(string $id): ?FeatureSchemaInterface
|
||||
{
|
||||
return array_get($this->schemas, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ?string[] $features
|
||||
* @return FeatureSchemaInterface[]
|
||||
*/
|
||||
public function getActiveSchemas(?array $features = []): array
|
||||
{
|
||||
return collect($this->schemas)->only($features)->all();
|
||||
}
|
||||
|
||||
public function register(FeatureSchemaInterface $schema): void
|
||||
{
|
||||
if (array_key_exists($schema->getId(), $this->schemas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->schemas[$schema->getId()] = $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ?string[] $features
|
||||
* @return array<string, array<string>>
|
||||
*/
|
||||
public function getMappings(?array $features = []): array
|
||||
{
|
||||
return collect($this->getActiveSchemas($features))
|
||||
->mapWithKeys(fn (FeatureSchemaInterface $schema) => [
|
||||
$schema->getId() => $schema->getListeners(),
|
||||
])->all();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features\Schemas;
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
use App\Extensions\Features\FeatureSchemaInterface;
|
||||
use App\Facades\Activity;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
@@ -15,13 +14,16 @@ use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class GSLTokenSchema implements FeatureSchemaInterface
|
||||
class GSLToken extends FeatureProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
/** @return array<string> */
|
||||
public function getListeners(): array
|
||||
{
|
||||
@@ -33,7 +35,7 @@ class GSLTokenSchema implements FeatureSchemaInterface
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'gsl_token';
|
||||
return 'gsltoken';
|
||||
}
|
||||
|
||||
public function getAction(): Action
|
||||
@@ -42,9 +44,7 @@ class GSLTokenSchema implements FeatureSchemaInterface
|
||||
$server = Filament::getTenant();
|
||||
|
||||
/** @var ServerVariable $serverVariable */
|
||||
$serverVariable = $server->serverVariables()->whereHas('variable', function (Builder $query) {
|
||||
$query->where('env_variable', 'STEAM_ACC');
|
||||
})->first();
|
||||
$serverVariable = $server->serverVariables()->where('env_variable', 'STEAM_ACC')->first();
|
||||
|
||||
return Action::make($this->getId())
|
||||
->requiresConfirmation()
|
||||
@@ -54,7 +54,7 @@ class GSLTokenSchema implements FeatureSchemaInterface
|
||||
->disabledForm(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->form([
|
||||
Placeholder::make('info')
|
||||
->label(new HtmlString(Blade::render('You can either <x-filament::link href="https://steamcommunity.com/dev/managegameservers" target="_blank">generate a new one</x-filament::link> and enter it below or leave the field blank to remove it completely.'))),
|
||||
->label('You can either <x-filament::link href="https://steamcommunity.com/dev/managegameservers" target="_blank">generate a new one</x-filament::link> and enter it below or leave the field blank to remove it completely.'),
|
||||
TextInput::make('gsltoken')
|
||||
->label('GSL Token')
|
||||
->rules([
|
||||
@@ -114,4 +114,9 @@ class GSLTokenSchema implements FeatureSchemaInterface
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features\Schemas;
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
use App\Extensions\Features\FeatureSchemaInterface;
|
||||
use App\Facades\Activity;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
@@ -13,9 +12,15 @@ use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
class JavaVersionSchema implements FeatureSchemaInterface
|
||||
class JavaVersion extends FeatureProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
/** @return array<string> */
|
||||
public function getListeners(): array
|
||||
{
|
||||
@@ -87,4 +92,9 @@ class JavaVersionSchema implements FeatureSchemaInterface
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features\Schemas;
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
use App\Extensions\Features\FeatureSchemaInterface;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonFileRepository;
|
||||
use App\Repositories\Daemon\DaemonPowerRepository;
|
||||
@@ -10,11 +9,17 @@ use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class MinecraftEulaSchema implements FeatureSchemaInterface
|
||||
class MinecraftEula extends FeatureProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
/** @return array<string> */
|
||||
public function getListeners(): array
|
||||
{
|
||||
@@ -58,4 +63,9 @@ class MinecraftEulaSchema implements FeatureSchemaInterface
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features\Schemas;
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
use App\Extensions\Features\FeatureSchemaInterface;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class PIDLimitSchema implements FeatureSchemaInterface
|
||||
class PIDLimit extends FeatureProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
/** @return array<string> */
|
||||
public function getListeners(): array
|
||||
{
|
||||
@@ -63,4 +68,9 @@ class PIDLimitSchema implements FeatureSchemaInterface
|
||||
->modalCancelActionLabel('Close')
|
||||
->action(fn () => null);
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Features\Schemas;
|
||||
namespace App\Extensions\Features;
|
||||
|
||||
use App\Extensions\Features\FeatureSchemaInterface;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class SteamDiskSpaceSchema implements FeatureSchemaInterface
|
||||
class SteamDiskSpace extends FeatureProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
/** @return array<string> */
|
||||
public function getListeners(): array
|
||||
{
|
||||
@@ -51,4 +56,9 @@ class SteamDiskSpaceSchema implements FeatureSchemaInterface
|
||||
->modalCancelActionLabel('Close')
|
||||
->action(fn () => null);
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth;
|
||||
|
||||
use Filament\Forms\Components\Component;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
|
||||
interface OAuthSchemaInterface
|
||||
{
|
||||
public function getId(): string;
|
||||
|
||||
public function getName(): string;
|
||||
|
||||
public function getConfigKey(): string;
|
||||
|
||||
/** @return ?class-string */
|
||||
public function getSocialiteProvider(): ?string;
|
||||
|
||||
/**
|
||||
* @return array<string, string|string[]|bool|null>
|
||||
*/
|
||||
public function getServiceConfig(): array;
|
||||
|
||||
/** @return Component[] */
|
||||
public function getSettingsForm(): array;
|
||||
|
||||
/** @return Step[] */
|
||||
public function getSetupSteps(): array;
|
||||
|
||||
public function getIcon(): ?string;
|
||||
|
||||
public function getHexColor(): ?string;
|
||||
|
||||
public function isEnabled(): bool;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth;
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||
|
||||
class OAuthService
|
||||
{
|
||||
/** @var OAuthSchemaInterface[] */
|
||||
private array $schemas = [];
|
||||
|
||||
/** @return OAuthSchemaInterface[] */
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->schemas;
|
||||
}
|
||||
|
||||
public function get(string $id): ?OAuthSchemaInterface
|
||||
{
|
||||
return array_get($this->schemas, $id);
|
||||
}
|
||||
|
||||
/** @return OAuthSchemaInterface[] */
|
||||
public function getEnabled(): array
|
||||
{
|
||||
return collect($this->schemas)
|
||||
->filter(fn (OAuthSchemaInterface $schema) => $schema->isEnabled())
|
||||
->all();
|
||||
}
|
||||
|
||||
public function register(OAuthSchemaInterface $schema): void
|
||||
{
|
||||
if (array_key_exists($schema->getId(), $this->schemas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
config()->set('services.' . $schema->getId(), array_merge($schema->getServiceConfig(), ['redirect' => '/auth/oauth/callback/' . $schema->getId()]));
|
||||
|
||||
if ($schema->getSocialiteProvider()) {
|
||||
Event::listen(fn (SocialiteWasCalled $event) => $event->extendSocialite($schema->getId(), $schema->getSocialiteProvider()));
|
||||
}
|
||||
|
||||
$this->schemas[$schema->getId()] = $schema;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Schemas;
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\ColorPicker;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Illuminate\Foundation\Application;
|
||||
use SocialiteProviders\Authentik\Provider;
|
||||
|
||||
final class AuthentikSchema extends OAuthSchema
|
||||
final class AuthentikProvider extends OAuthProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'authentik';
|
||||
}
|
||||
|
||||
public function getSocialiteProvider(): string
|
||||
public function getProviderClass(): string
|
||||
{
|
||||
return Provider::class;
|
||||
}
|
||||
@@ -60,4 +66,9 @@ final class AuthentikSchema extends OAuthSchema
|
||||
{
|
||||
return env('OAUTH_AUTHENTIK_DISPLAY_COLOR', '#fd4b2d');
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
38
app/Extensions/OAuth/Providers/CommonProvider.php
Normal file
38
app/Extensions/OAuth/Providers/CommonProvider.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
final class CommonProvider extends OAuthProvider
|
||||
{
|
||||
protected function __construct(protected Application $app, private string $id, private ?string $providerClass, private ?string $icon, private ?string $hexColor)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getProviderClass(): ?string
|
||||
{
|
||||
return $this->providerClass;
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
public function getHexColor(): ?string
|
||||
{
|
||||
return $this->hexColor;
|
||||
}
|
||||
|
||||
public static function register(Application $app, string $id, ?string $providerClass = null, ?string $icon = null, ?string $hexColor = null): static
|
||||
{
|
||||
return new self($app, $id, $providerClass, $icon, $hexColor);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Schemas;
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use SocialiteProviders\Discord\Provider;
|
||||
use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
final class DiscordSchema extends OAuthSchema
|
||||
final class DiscordProvider extends OAuthProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'discord';
|
||||
}
|
||||
|
||||
public function getSocialiteProvider(): string
|
||||
public function getProviderClass(): string
|
||||
{
|
||||
return Provider::class;
|
||||
}
|
||||
@@ -50,4 +56,9 @@ final class DiscordSchema extends OAuthSchema
|
||||
{
|
||||
return '#5865F2';
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Schemas;
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
final class GithubSchema extends OAuthSchema
|
||||
final class GithubProvider extends OAuthProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'github';
|
||||
@@ -49,4 +55,9 @@ final class GithubSchema extends OAuthSchema
|
||||
{
|
||||
return '#4078c0';
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Schemas;
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
final class GitlabSchema extends OAuthSchema
|
||||
final class GitlabProvider extends OAuthProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'gitlab';
|
||||
@@ -62,4 +68,9 @@ final class GitlabSchema extends OAuthSchema
|
||||
{
|
||||
return '#fca326';
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Schemas;
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use App\Extensions\OAuth\OAuthSchemaInterface;
|
||||
use Filament\Forms\Components\Component;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Str;
|
||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||
|
||||
abstract class OAuthSchema implements OAuthSchemaInterface
|
||||
abstract class OAuthProvider
|
||||
{
|
||||
/**
|
||||
* @var array<string, static>
|
||||
*/
|
||||
protected static array $providers = [];
|
||||
|
||||
/**
|
||||
* @return self|static[]
|
||||
*/
|
||||
public static function get(?string $id = null): array|self
|
||||
{
|
||||
return $id ? static::$providers[$id] : static::$providers;
|
||||
}
|
||||
|
||||
protected function __construct(protected Application $app)
|
||||
{
|
||||
if (array_key_exists($this->getId(), static::$providers)) {
|
||||
if (!$this->app->runningUnitTests()) {
|
||||
logger()->warning("Tried to create duplicate OAuth provider with id '{$this->getId()}'");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
config()->set('services.' . $this->getId(), array_merge($this->getServiceConfig(), ['redirect' => '/auth/oauth/callback/' . $this->getId()]));
|
||||
|
||||
if ($this->getProviderClass()) {
|
||||
Event::listen(function (SocialiteWasCalled $event) {
|
||||
$event->extendSocialite($this->getId(), $this->getProviderClass());
|
||||
});
|
||||
}
|
||||
|
||||
static::$providers[$this->getId()] = $this;
|
||||
}
|
||||
|
||||
abstract public function getId(): string;
|
||||
|
||||
public function getSocialiteProvider(): ?string
|
||||
public function getProviderClass(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|string[]|bool|null>
|
||||
*/
|
||||
public function getServiceConfig(): array
|
||||
{
|
||||
$id = Str::upper($this->getId());
|
||||
@@ -73,13 +112,6 @@ abstract class OAuthSchema implements OAuthSchemaInterface
|
||||
return Str::title($this->getId());
|
||||
}
|
||||
|
||||
public function getConfigKey(): string
|
||||
{
|
||||
$id = Str::upper($this->getId());
|
||||
|
||||
return "OAUTH_{$id}_ENABLED";
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
return null;
|
||||
@@ -1,22 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Schemas;
|
||||
namespace App\Extensions\OAuth\Providers;
|
||||
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Wizard\Step;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use SocialiteProviders\Steam\Provider;
|
||||
|
||||
final class SteamSchema extends OAuthSchema
|
||||
final class SteamProvider extends OAuthProvider
|
||||
{
|
||||
public function __construct(protected Application $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'steam';
|
||||
}
|
||||
|
||||
public function getSocialiteProvider(): string
|
||||
public function getProviderClass(): string
|
||||
{
|
||||
return Provider::class;
|
||||
}
|
||||
@@ -67,4 +73,9 @@ final class SteamSchema extends OAuthSchema
|
||||
{
|
||||
return '#00adee';
|
||||
}
|
||||
|
||||
public static function register(Application $app): self
|
||||
{
|
||||
return new self($app);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\OAuth\Schemas;
|
||||
|
||||
final class CommonSchema extends OAuthSchema
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $id,
|
||||
private readonly ?string $name = null,
|
||||
private readonly ?string $configName = null,
|
||||
private readonly ?string $icon = null,
|
||||
private readonly ?string $hexColor = null,
|
||||
) {}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name ?? parent::getName();
|
||||
}
|
||||
|
||||
public function getConfigKey(): string
|
||||
{
|
||||
return $this->configName ?? parent::getConfigKey();
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
public function getHexColor(): ?string
|
||||
{
|
||||
return $this->hexColor;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,14 @@
|
||||
|
||||
namespace App\Filament\Admin\Pages;
|
||||
|
||||
use App\Extensions\Avatar\AvatarService;
|
||||
use App\Extensions\Captcha\CaptchaService;
|
||||
use App\Extensions\OAuth\OAuthService;
|
||||
use App\Extensions\Avatar\AvatarProvider;
|
||||
use App\Extensions\Captcha\Providers\CaptchaProvider;
|
||||
use App\Extensions\OAuth\Providers\OAuthProvider;
|
||||
use App\Models\Backup;
|
||||
use App\Notifications\MailTested;
|
||||
use App\Traits\EnvironmentWriterTrait;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\Actions;
|
||||
use Filament\Forms\Components\Actions\Action as FormAction;
|
||||
use Filament\Forms\Components\Component;
|
||||
@@ -47,23 +44,14 @@ use Illuminate\Support\Str;
|
||||
*/
|
||||
class Settings extends Page implements HasForms
|
||||
{
|
||||
use CanCustomizeHeaderActions, InteractsWithHeaderActions {
|
||||
CanCustomizeHeaderActions::getHeaderActions insteadof InteractsWithHeaderActions;
|
||||
}
|
||||
use CanCustomizeHeaderWidgets;
|
||||
use EnvironmentWriterTrait;
|
||||
use InteractsWithForms;
|
||||
use InteractsWithHeaderActions;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-settings';
|
||||
|
||||
protected static string $view = 'filament.pages.settings';
|
||||
|
||||
protected OAuthService $oauthService;
|
||||
|
||||
protected AvatarService $avatarService;
|
||||
|
||||
protected CaptchaService $captchaService;
|
||||
|
||||
/** @var array<mixed>|null */
|
||||
public ?array $data = [];
|
||||
|
||||
@@ -72,13 +60,6 @@ class Settings extends Page implements HasForms
|
||||
$this->form->fill();
|
||||
}
|
||||
|
||||
public function boot(OAuthService $oauthService, AvatarService $avatarService, CaptchaService $captchaService): void
|
||||
{
|
||||
$this->oauthService = $oauthService;
|
||||
$this->avatarService = $avatarService;
|
||||
$this->captchaService = $captchaService;
|
||||
}
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()->can('view settings');
|
||||
@@ -186,7 +167,7 @@ class Settings extends Page implements HasForms
|
||||
Select::make('FILAMENT_AVATAR_PROVIDER')
|
||||
->label(trans('admin/setting.general.avatar_provider'))
|
||||
->native(false)
|
||||
->options($this->avatarService->getMappings())
|
||||
->options(collect(AvatarProvider::getAll())->mapWithKeys(fn ($provider) => [$provider->getId() => $provider->getName()]))
|
||||
->selectablePlaceholder(false)
|
||||
->default(env('FILAMENT_AVATAR_PROVIDER', config('panel.filament.avatar-provider'))),
|
||||
Toggle::make('FILAMENT_UPLOADABLE_AVATARS')
|
||||
@@ -277,14 +258,15 @@ class Settings extends Page implements HasForms
|
||||
{
|
||||
$formFields = [];
|
||||
|
||||
$captchaSchemas = $this->captchaService->getAll();
|
||||
foreach ($captchaSchemas as $schema) {
|
||||
$id = Str::upper($schema->getId());
|
||||
$captchaProviders = CaptchaProvider::get();
|
||||
foreach ($captchaProviders as $captchaProvider) {
|
||||
$id = Str::upper($captchaProvider->getId());
|
||||
$name = Str::title($captchaProvider->getId());
|
||||
|
||||
$formFields[] = Section::make($schema->getName())
|
||||
$formFields[] = Section::make($name)
|
||||
->columns(5)
|
||||
->icon($schema->getIcon() ?? 'tabler-shield')
|
||||
->collapsed(fn () => !$schema->isEnabled())
|
||||
->icon($captchaProvider->getIcon() ?? 'tabler-shield')
|
||||
->collapsed(fn () => !env("CAPTCHA_{$id}_ENABLED", false))
|
||||
->collapsible()
|
||||
->schema([
|
||||
Hidden::make("CAPTCHA_{$id}_ENABLED")
|
||||
@@ -295,14 +277,21 @@ class Settings extends Page implements HasForms
|
||||
->visible(fn (Get $get) => $get("CAPTCHA_{$id}_ENABLED"))
|
||||
->label(trans('admin/setting.captcha.disable'))
|
||||
->color('danger')
|
||||
->action(fn (Set $set) => $set("CAPTCHA_{$id}_ENABLED", false)),
|
||||
->action(function (Set $set) use ($id) {
|
||||
$set("CAPTCHA_{$id}_ENABLED", false);
|
||||
}),
|
||||
FormAction::make("enable_captcha_$id")
|
||||
->visible(fn (Get $get) => !$get("CAPTCHA_{$id}_ENABLED"))
|
||||
->label(trans('admin/setting.captcha.enable'))
|
||||
->color('success')
|
||||
->action(fn (Set $set) => $set("CAPTCHA_{$id}_ENABLED", true)),
|
||||
->action(function (Set $set) use ($id, $captchaProviders) {
|
||||
foreach ($captchaProviders as $captchaProvider) {
|
||||
$loopId = Str::upper($captchaProvider->getId());
|
||||
$set("CAPTCHA_{$loopId}_ENABLED", $loopId === $id);
|
||||
}
|
||||
}),
|
||||
])->columnSpan(1),
|
||||
Group::make($schema->getSettingsForm())
|
||||
Group::make($captchaProvider->getSettingsForm())
|
||||
->visible(fn (Get $get) => $get("CAPTCHA_{$id}_ENABLED"))
|
||||
->columns(4)
|
||||
->columnSpan(4),
|
||||
@@ -538,37 +527,39 @@ class Settings extends Page implements HasForms
|
||||
{
|
||||
$formFields = [];
|
||||
|
||||
$oauthSchemas = $this->oauthService->getAll();
|
||||
foreach ($oauthSchemas as $schema) {
|
||||
$id = Str::upper($schema->getId());
|
||||
$key = $schema->getConfigKey();
|
||||
$oauthProviders = OAuthProvider::get();
|
||||
foreach ($oauthProviders as $oauthProvider) {
|
||||
$id = Str::upper($oauthProvider->getId());
|
||||
$name = Str::title($oauthProvider->getId());
|
||||
|
||||
$formFields[] = Section::make($schema->getName())
|
||||
$formFields[] = Section::make($name)
|
||||
->columns(5)
|
||||
->icon($schema->getIcon() ?? 'tabler-brand-oauth')
|
||||
->collapsed(fn () => !env($key, false))
|
||||
->icon($oauthProvider->getIcon() ?? 'tabler-brand-oauth')
|
||||
->collapsed(fn () => !env("OAUTH_{$id}_ENABLED", false))
|
||||
->collapsible()
|
||||
->schema([
|
||||
Hidden::make($key)
|
||||
Hidden::make("OAUTH_{$id}_ENABLED")
|
||||
->live()
|
||||
->default(env($key)),
|
||||
->default(env("OAUTH_{$id}_ENABLED")),
|
||||
Actions::make([
|
||||
FormAction::make("disable_oauth_$id")
|
||||
->visible(fn (Get $get) => $get($key))
|
||||
->visible(fn (Get $get) => $get("OAUTH_{$id}_ENABLED"))
|
||||
->label(trans('admin/setting.oauth.disable'))
|
||||
->color('danger')
|
||||
->action(fn (Set $set) => $set($key, false)),
|
||||
->action(function (Set $set) use ($id) {
|
||||
$set("OAUTH_{$id}_ENABLED", false);
|
||||
}),
|
||||
FormAction::make("enable_oauth_$id")
|
||||
->visible(fn (Get $get) => !$get($key))
|
||||
->visible(fn (Get $get) => !$get("OAUTH_{$id}_ENABLED"))
|
||||
->label(trans('admin/setting.oauth.enable'))
|
||||
->color('success')
|
||||
->steps($schema->getSetupSteps())
|
||||
->modalHeading(trans('admin/setting.oauth.enable') . ' ' . $schema->getName())
|
||||
->steps($oauthProvider->getSetupSteps())
|
||||
->modalHeading(trans('admin/setting.oauth.enable') . ' ' . $name)
|
||||
->modalSubmitActionLabel(trans('admin/setting.oauth.enable'))
|
||||
->modalCancelAction(false)
|
||||
->action(function ($data, Set $set) use ($key) {
|
||||
->action(function ($data, Set $set) use ($id) {
|
||||
$data = array_merge([
|
||||
$key => 'true',
|
||||
"OAUTH_{$id}_ENABLED" => 'true',
|
||||
], $data);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
@@ -576,8 +567,8 @@ class Settings extends Page implements HasForms
|
||||
}
|
||||
}),
|
||||
])->columnSpan(1),
|
||||
Group::make($schema->getSettingsForm())
|
||||
->visible(fn (Get $get) => $get($key))
|
||||
Group::make($oauthProvider->getSettingsForm())
|
||||
->visible(fn (Get $get) => $get("OAUTH_{$id}_ENABLED"))
|
||||
->columns(4)
|
||||
->columnSpan(4),
|
||||
]);
|
||||
@@ -639,6 +630,7 @@ class Settings extends Page implements HasForms
|
||||
->onColor('success')
|
||||
->offColor('danger')
|
||||
->live()
|
||||
->columnSpanFull()
|
||||
->formatStateUsing(fn ($state): bool => (bool) $state)
|
||||
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_INSTALL_NOTIFICATION', (bool) $state))
|
||||
->default(env('PANEL_SEND_INSTALL_NOTIFICATION', config('panel.email.send_install_notification'))),
|
||||
@@ -649,6 +641,7 @@ class Settings extends Page implements HasForms
|
||||
->onColor('success')
|
||||
->offColor('danger')
|
||||
->live()
|
||||
->columnSpanFull()
|
||||
->formatStateUsing(fn ($state): bool => (bool) $state)
|
||||
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_REINSTALL_NOTIFICATION', (bool) $state))
|
||||
->default(env('PANEL_SEND_REINSTALL_NOTIFICATION', config('panel.email.send_reinstall_notification'))),
|
||||
@@ -800,8 +793,7 @@ class Settings extends Page implements HasForms
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('save')
|
||||
|
||||
@@ -6,16 +6,11 @@ use App\Filament\Admin\Resources\ApiKeyResource\Pages;
|
||||
use App\Filament\Admin\Resources\UserResource\Pages\EditUser;
|
||||
use App\Filament\Components\Tables\Columns\DateTimeColumn;
|
||||
use App\Models\ApiKey;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use App\Traits\Filament\CanModifyForm;
|
||||
use App\Traits\Filament\CanModifyTable;
|
||||
use Filament\Forms\Components\Fieldset;
|
||||
use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\ToggleButtons;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
@@ -25,11 +20,6 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class ApiKeyResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
use CanModifyForm;
|
||||
use CanModifyTable;
|
||||
|
||||
protected static ?string $model = ApiKey::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-key';
|
||||
@@ -66,7 +56,7 @@ class ApiKeyResource extends Resource
|
||||
return trans('admin/dashboard.advanced');
|
||||
}
|
||||
|
||||
public static function defaultTable(Table $table): Table
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
@@ -89,7 +79,7 @@ class ApiKeyResource extends Resource
|
||||
TextColumn::make('user.username')
|
||||
->label(trans('admin/apikey.table.created_by'))
|
||||
->icon('tabler-user')
|
||||
->url(fn (ApiKey $apiKey) => auth()->user()->can('update', $apiKey->user) ? EditUser::getUrl(['record' => $apiKey->user]) : null),
|
||||
->url(fn (ApiKey $apiKey) => auth()->user()->can('update user', $apiKey->user) ? EditUser::getUrl(['record' => $apiKey->user]) : null),
|
||||
])
|
||||
->actions([
|
||||
DeleteAction::make(),
|
||||
@@ -102,7 +92,7 @@ class ApiKeyResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
public static function defaultForm(Form $form): Form
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
@@ -152,8 +142,7 @@ class ApiKeyResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListApiKeys::route('/'),
|
||||
|
||||
@@ -4,24 +4,16 @@ namespace App\Filament\Admin\Resources\ApiKeyResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\ApiKeyResource;
|
||||
use App\Models\ApiKey;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CreateApiKey extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = ApiKeyResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->getCreateFormAction()->formId('form'),
|
||||
|
||||
@@ -4,22 +4,14 @@ namespace App\Filament\Admin\Resources\ApiKeyResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\ApiKeyResource;
|
||||
use App\Models\ApiKey;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListApiKeys extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = ApiKeyResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make()
|
||||
|
||||
@@ -3,19 +3,12 @@
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource\Pages;
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource\RelationManagers;
|
||||
use App\Models\DatabaseHost;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use App\Traits\Filament\CanModifyForm;
|
||||
use App\Traits\Filament\CanModifyTable;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Set;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
@@ -27,11 +20,6 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class DatabaseHostResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
use CanModifyForm;
|
||||
use CanModifyTable;
|
||||
|
||||
protected static ?string $model = DatabaseHost::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-database';
|
||||
@@ -63,7 +51,7 @@ class DatabaseHostResource extends Resource
|
||||
return trans('admin/dashboard.advanced');
|
||||
}
|
||||
|
||||
public static function defaultTable(Table $table): Table
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
@@ -101,7 +89,7 @@ class DatabaseHostResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
public static function defaultForm(Form $form): Form
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
@@ -162,16 +150,7 @@ class DatabaseHostResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return class-string<RelationManager>[] */
|
||||
public static function getDefaultRelations(): array
|
||||
{
|
||||
return [
|
||||
RelationManagers\DatabasesRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListDatabaseHosts::route('/'),
|
||||
@@ -185,10 +164,8 @@ class DatabaseHostResource extends Resource
|
||||
{
|
||||
$query = parent::getEloquentQuery();
|
||||
|
||||
return $query->where(function (Builder $query) {
|
||||
return $query->whereHas('nodes', function (Builder $query) {
|
||||
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
})->orDoesntHave('nodes');
|
||||
});
|
||||
return $query->whereHas('nodes', function (Builder $query) {
|
||||
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
})->orDoesntHave('nodes');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource;
|
||||
use App\Services\Databases\Hosts\HostCreationService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Forms\Components\Fieldset;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
@@ -28,8 +26,6 @@ use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
class CreateDatabaseHost extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
use HasWizard;
|
||||
|
||||
protected static string $resource = DatabaseHostResource::class;
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource;
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource\RelationManagers\DatabasesRelationManager;
|
||||
use App\Models\DatabaseHost;
|
||||
use App\Services\Databases\Hosts\HostUpdateService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
@@ -18,9 +15,6 @@ use PDOException;
|
||||
|
||||
class EditDatabaseHost extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = DatabaseHostResource::class;
|
||||
|
||||
private HostUpdateService $hostUpdateService;
|
||||
@@ -30,8 +24,7 @@ class EditDatabaseHost extends EditRecord
|
||||
$this->hostUpdateService = $hostUpdateService;
|
||||
}
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make()
|
||||
@@ -46,6 +39,17 @@ class EditDatabaseHost extends EditRecord
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getRelationManagers(): array
|
||||
{
|
||||
if (DatabasesRelationManager::canViewForRecord($this->getRecord(), static::class)) {
|
||||
return [
|
||||
DatabasesRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function handleRecordUpdate(Model $record, array $data): Model
|
||||
{
|
||||
if (!$record instanceof DatabaseHost) {
|
||||
|
||||
@@ -4,22 +4,14 @@ namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource;
|
||||
use App\Models\DatabaseHost;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListDatabaseHosts extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = DatabaseHostResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make()
|
||||
|
||||
@@ -3,25 +3,29 @@
|
||||
namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use App\Filament\Admin\Resources\DatabaseHostResource\RelationManagers\DatabasesRelationManager;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewDatabaseHost extends ViewRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = DatabaseHostResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getRelationManagers(): array
|
||||
{
|
||||
if (DatabasesRelationManager::canViewForRecord($this->getRecord(), static::class)) {
|
||||
return [
|
||||
DatabasesRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,10 +71,10 @@ class DatabasesRelationManager extends RelationManager
|
||||
])
|
||||
->actions([
|
||||
DeleteAction::make()
|
||||
->authorize(fn (Database $database) => auth()->user()->can('delete', $database)),
|
||||
->authorize(fn (Database $database) => auth()->user()->can('delete database', $database)),
|
||||
ViewAction::make()
|
||||
->color('primary')
|
||||
->hidden(fn () => !auth()->user()->can('viewAny', Database::class)),
|
||||
->hidden(fn () => !auth()->user()->can('viewList database')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,11 @@
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\EggResource\Pages;
|
||||
use App\Filament\Admin\Resources\EggResource\RelationManagers;
|
||||
use App\Models\Egg;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Resources\Resource;
|
||||
|
||||
class EggResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
|
||||
protected static ?string $model = Egg::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-eggs';
|
||||
@@ -52,16 +44,7 @@ class EggResource extends Resource
|
||||
return ['name', 'tags', 'uuid', 'id'];
|
||||
}
|
||||
|
||||
/** @return class-string<RelationManager>[] */
|
||||
public static function getDefaultRelations(): array
|
||||
{
|
||||
return [
|
||||
RelationManagers\ServersRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListEggs::route('/'),
|
||||
|
||||
@@ -6,10 +6,6 @@ use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
|
||||
use App\Filament\Admin\Resources\EggResource;
|
||||
use App\Filament\Components\Forms\Fields\CopyFrom;
|
||||
use App\Models\EggVariable;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Fieldset;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
@@ -32,15 +28,11 @@ use Illuminate\Validation\Rules\Unique;
|
||||
|
||||
class CreateEgg extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = EggResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->getCreateFormAction()->formId('form'),
|
||||
|
||||
@@ -4,15 +4,12 @@ namespace App\Filament\Admin\Resources\EggResource\Pages;
|
||||
|
||||
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
|
||||
use App\Filament\Admin\Resources\EggResource;
|
||||
use App\Filament\Admin\Resources\EggResource\RelationManagers\ServersRelationManager;
|
||||
use App\Filament\Components\Actions\ExportEggAction;
|
||||
use App\Filament\Components\Actions\ImportEggAction;
|
||||
use App\Filament\Components\Forms\Fields\CopyFrom;
|
||||
use App\Models\Egg;
|
||||
use App\Models\EggVariable;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Fieldset;
|
||||
@@ -34,9 +31,6 @@ use Illuminate\Validation\Rules\Unique;
|
||||
|
||||
class EditEgg extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = EggResource::class;
|
||||
|
||||
public function form(Form $form): Form
|
||||
@@ -257,8 +251,7 @@ class EditEgg extends EditRecord
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make()
|
||||
@@ -280,4 +273,11 @@ class EditEgg extends EditRecord
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getRelationManagers(): array
|
||||
{
|
||||
return [
|
||||
ServersRelationManager::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ use App\Filament\Components\Tables\Actions\UpdateEggAction;
|
||||
use App\Filament\Components\Tables\Actions\UpdateEggBulkAction;
|
||||
use App\Filament\Components\Tables\Filters\TagsFilter;
|
||||
use App\Models\Egg;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\CreateAction as CreateHeaderAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
@@ -27,9 +23,6 @@ use Illuminate\Support\Str;
|
||||
|
||||
class ListEggs extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = EggResource::class;
|
||||
|
||||
public function table(Table $table): Table
|
||||
@@ -102,8 +95,7 @@ class ListEggs extends ListRecords
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ImportEggHeaderAction::make()
|
||||
|
||||
@@ -38,9 +38,8 @@ class ServersRelationManager extends RelationManager
|
||||
->label(trans('admin/server.docker_image')),
|
||||
SelectColumn::make('allocation.id')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->disabled()
|
||||
->options(fn (Server $server) => $server->allocations->take(1)->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
|
||||
->placeholder('None')
|
||||
->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address])
|
||||
->selectablePlaceholder(false)
|
||||
->sortable(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,6 @@ namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\MountResource\Pages;
|
||||
use App\Models\Mount;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use App\Traits\Filament\CanModifyForm;
|
||||
use App\Traits\Filament\CanModifyTable;
|
||||
use Filament\Forms\Components\Group;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Select;
|
||||
@@ -15,7 +11,6 @@ use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\ToggleButtons;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
@@ -27,11 +22,6 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class MountResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
use CanModifyForm;
|
||||
use CanModifyTable;
|
||||
|
||||
protected static ?string $model = Mount::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-layers-linked';
|
||||
@@ -63,7 +53,7 @@ class MountResource extends Resource
|
||||
return trans('admin/dashboard.advanced');
|
||||
}
|
||||
|
||||
public static function defaultTable(Table $table): Table
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
@@ -86,7 +76,7 @@ class MountResource extends Resource
|
||||
->badge()
|
||||
->icon(fn ($state) => $state ? 'tabler-writing-off' : 'tabler-writing')
|
||||
->color(fn ($state) => $state ? 'success' : 'warning')
|
||||
->formatStateUsing(fn ($state) => $state ? trans('admin/mount.toggles.read_only') : trans('admin/mount.toggles.writable')),
|
||||
->formatStateUsing(fn ($state) => $state ? trans('admin/mount.toggles.read_only') : trans('admin/mount.toggles.writeable')),
|
||||
])
|
||||
->actions([
|
||||
ViewAction::make()
|
||||
@@ -104,7 +94,7 @@ class MountResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
public static function defaultForm(Form $form): Form
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
@@ -172,8 +162,7 @@ class MountResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListMounts::route('/'),
|
||||
@@ -187,10 +176,8 @@ class MountResource extends Resource
|
||||
{
|
||||
$query = parent::getEloquentQuery();
|
||||
|
||||
return $query->where(function (Builder $query) {
|
||||
return $query->whereHas('nodes', function (Builder $query) {
|
||||
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
})->orDoesntHave('nodes');
|
||||
});
|
||||
return $query->whereHas('nodes', function (Builder $query) {
|
||||
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
})->orDoesntHave('nodes');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,17 @@
|
||||
namespace App\Filament\Admin\Resources\MountResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\MountResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CreateMount extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = MountResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->getCreateFormAction()->formId('form'),
|
||||
|
||||
@@ -3,22 +3,14 @@
|
||||
namespace App\Filament\Admin\Resources\MountResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\MountResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditMount extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = MountResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make(),
|
||||
|
||||
@@ -4,22 +4,14 @@ namespace App\Filament\Admin\Resources\MountResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\MountResource;
|
||||
use App\Models\Mount;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListMounts extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = MountResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make()
|
||||
|
||||
@@ -3,22 +3,14 @@
|
||||
namespace App\Filament\Admin\Resources\MountResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\MountResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewMount extends ViewRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = MountResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
|
||||
@@ -5,18 +5,11 @@ namespace App\Filament\Admin\Resources;
|
||||
use App\Filament\Admin\Resources\NodeResource\Pages;
|
||||
use App\Filament\Admin\Resources\NodeResource\RelationManagers;
|
||||
use App\Models\Node;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Resources\Resource;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class NodeResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
|
||||
protected static ?string $model = Node::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-server-2';
|
||||
@@ -48,8 +41,7 @@ class NodeResource extends Resource
|
||||
return (string) static::getEloquentQuery()->count() ?: null;
|
||||
}
|
||||
|
||||
/** @return class-string<RelationManager>[] */
|
||||
public static function getDefaultRelations(): array
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
RelationManagers\AllocationsRelationManager::class,
|
||||
@@ -57,8 +49,7 @@ class NodeResource extends Resource
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListNodes::route('/'),
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace App\Filament\Admin\Resources\NodeResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\NodeResource;
|
||||
use App\Models\Node;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Grid;
|
||||
@@ -23,9 +21,6 @@ use Illuminate\Support\HtmlString;
|
||||
|
||||
class CreateNode extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = NodeResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
@@ -89,14 +84,16 @@ class CreateNode extends CreateRecord
|
||||
return;
|
||||
}
|
||||
|
||||
$ip = get_ip_from_hostname($state);
|
||||
if ($ip) {
|
||||
$validRecords = gethostbynamel($state);
|
||||
if ($validRecords) {
|
||||
$set('dns', true);
|
||||
|
||||
$set('ip', $ip);
|
||||
} else {
|
||||
$set('dns', false);
|
||||
$set('ip', collect($validRecords)->first());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$set('dns', false);
|
||||
})
|
||||
->maxLength(255),
|
||||
|
||||
@@ -127,10 +124,15 @@ class CreateNode extends CreateRecord
|
||||
'lg' => 1,
|
||||
]),
|
||||
|
||||
TextInput::make('daemon_connect')
|
||||
->columnSpan(1)
|
||||
->label(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port') : trans('admin/node.port'))
|
||||
->helperText(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port_help') : trans('admin/node.port_help'))
|
||||
TextInput::make('daemon_listen')
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'sm' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->label(trans('admin/node.port'))
|
||||
->helperText(trans('admin/node.port_help'))
|
||||
->minValue(1)
|
||||
->maxValue(65535)
|
||||
->default(8080)
|
||||
@@ -191,21 +193,7 @@ class CreateNode extends CreateRecord
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
$set('scheme', $state === 'http' ? 'http' : 'https');
|
||||
$set('behind_proxy', $state === 'https_proxy');
|
||||
|
||||
$set('daemon_connect', $state === 'https_proxy' ? 443 : 8080);
|
||||
$set('daemon_listen', 8080);
|
||||
}),
|
||||
|
||||
TextInput::make('daemon_listen')
|
||||
->columnSpan(1)
|
||||
->label(trans('admin/node.listen_port'))
|
||||
->helperText(trans('admin/node.listen_port_help'))
|
||||
->minValue(1)
|
||||
->maxValue(65535)
|
||||
->default(8080)
|
||||
->required()
|
||||
->integer()
|
||||
->visible(fn (Get $get) => $get('connection') === 'https_proxy'),
|
||||
]),
|
||||
Step::make('advanced')
|
||||
->label(trans('admin/node.tabs.advanced_settings'))
|
||||
@@ -421,13 +409,4 @@ class CreateNode extends CreateRecord
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeCreate(array $data): array
|
||||
{
|
||||
if (!$data['behind_proxy']) {
|
||||
$data['daemon_listen'] = $data['daemon_connect'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ use App\Repositories\Daemon\DaemonConfigurationRepository;
|
||||
use App\Services\Helpers\SoftwareVersionService;
|
||||
use App\Services\Nodes\NodeAutoDeployService;
|
||||
use App\Services\Nodes\NodeUpdateService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Exception;
|
||||
use Filament\Actions;
|
||||
use Filament\Forms;
|
||||
@@ -36,9 +34,6 @@ use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
class EditNode extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = NodeResource::class;
|
||||
|
||||
private DaemonConfigurationRepository $daemonConfigurationRepository;
|
||||
@@ -154,14 +149,16 @@ class EditNode extends EditRecord
|
||||
return;
|
||||
}
|
||||
|
||||
$ip = get_ip_from_hostname($state);
|
||||
if ($ip) {
|
||||
$validRecords = gethostbynamel($state);
|
||||
if ($validRecords) {
|
||||
$set('dns', true);
|
||||
|
||||
$set('ip', $ip);
|
||||
} else {
|
||||
$set('dns', false);
|
||||
$set('ip', collect($validRecords)->first());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$set('dns', false);
|
||||
})
|
||||
->maxLength(255),
|
||||
TextInput::make('ip')
|
||||
@@ -184,10 +181,10 @@ class EditNode extends EditRecord
|
||||
false => 'danger',
|
||||
])
|
||||
->columnSpan(1),
|
||||
TextInput::make('daemon_connect')
|
||||
TextInput::make('daemon_listen')
|
||||
->columnSpan(1)
|
||||
->label(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port') : trans('admin/node.port'))
|
||||
->helperText(fn (Get $get) => $get('connection') === 'https_proxy' ? trans('admin/node.connect_port_help') : trans('admin/node.port_help'))
|
||||
->label(trans('admin/node.port'))
|
||||
->helperText(trans('admin/node.port_help'))
|
||||
->minValue(1)
|
||||
->maxValue(65535)
|
||||
->default(8080)
|
||||
@@ -242,20 +239,7 @@ class EditNode extends EditRecord
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
$set('scheme', $state === 'http' ? 'http' : 'https');
|
||||
$set('behind_proxy', $state === 'https_proxy');
|
||||
|
||||
$set('daemon_connect', $state === 'https_proxy' ? 443 : 8080);
|
||||
$set('daemon_listen', 8080);
|
||||
}),
|
||||
TextInput::make('daemon_listen')
|
||||
->columnSpan(1)
|
||||
->label(trans('admin/node.listen_port'))
|
||||
->helperText(trans('admin/node.listen_port_help'))
|
||||
->minValue(1)
|
||||
->maxValue(65535)
|
||||
->default(8080)
|
||||
->required()
|
||||
->integer()
|
||||
->visible(fn (Get $get) => $get('connection') === 'https_proxy'),
|
||||
]),
|
||||
Tab::make('adv')
|
||||
->label(trans('admin/node.tabs.advanced_settings'))
|
||||
@@ -616,10 +600,10 @@ class EditNode extends EditRecord
|
||||
$data['config'] = $node->getYamlConfiguration();
|
||||
|
||||
if (!is_ip($node->fqdn)) {
|
||||
$ip = get_ip_from_hostname($node->fqdn);
|
||||
if ($ip) {
|
||||
$validRecords = gethostbynamel($node->fqdn);
|
||||
if ($validRecords) {
|
||||
$data['dns'] = true;
|
||||
$data['ip'] = $ip;
|
||||
$data['ip'] = collect($validRecords)->first();
|
||||
} else {
|
||||
$data['dns'] = false;
|
||||
}
|
||||
@@ -633,8 +617,7 @@ class EditNode extends EditRecord
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @return array<Actions\Action|Actions\ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make()
|
||||
@@ -644,15 +627,6 @@ class EditNode extends EditRecord
|
||||
];
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeSave(array $data): array
|
||||
{
|
||||
if (!$data['behind_proxy']) {
|
||||
$data['daemon_listen'] = $data['daemon_connect'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function afterSave(): void
|
||||
{
|
||||
$this->fillForm();
|
||||
|
||||
@@ -6,8 +6,6 @@ use App\Filament\Admin\Resources\NodeResource;
|
||||
use App\Filament\Components\Tables\Columns\NodeHealthColumn;
|
||||
use App\Filament\Components\Tables\Filters\TagsFilter;
|
||||
use App\Models\Node;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
@@ -18,9 +16,6 @@ use Filament\Tables\Table;
|
||||
|
||||
class ListNodes extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = NodeResource::class;
|
||||
|
||||
public function table(Table $table): Table
|
||||
@@ -78,8 +73,7 @@ class ListNodes extends ListRecords
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<Actions\Action|Actions\ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make()
|
||||
|
||||
@@ -58,9 +58,6 @@ class AllocationsRelationManager extends RelationManager
|
||||
TextInputColumn::make('ip_alias')
|
||||
->searchable()
|
||||
->label(trans('admin/node.table.alias')),
|
||||
TextInputColumn::make('notes')
|
||||
->label(trans('admin/node.table.allocation_notes'))
|
||||
->placeholder(trans('admin/node.table.no_notes')),
|
||||
SelectColumn::make('ip')
|
||||
->options(fn (Allocation $allocation) => collect($this->getOwnerRecord()->ipAddresses())->merge([$allocation->ip])->mapWithKeys(fn (string $ip) => [$ip => $ip]))
|
||||
->selectablePlaceholder(false)
|
||||
@@ -84,7 +81,8 @@ class AllocationsRelationManager extends RelationManager
|
||||
->label(trans('admin/node.table.alias'))
|
||||
->inlineLabel()
|
||||
->default(null)
|
||||
->helperText(trans('admin/node.alias_help')),
|
||||
->helperText(trans('admin/node.alias_help'))
|
||||
->required(false),
|
||||
TagsInput::make('allocation_ports')
|
||||
->placeholder('27015, 27017-27019')
|
||||
->label(trans('admin/node.ports'))
|
||||
@@ -99,7 +97,7 @@ class AllocationsRelationManager extends RelationManager
|
||||
])
|
||||
->groupedBulkActions([
|
||||
DeleteBulkAction::make()
|
||||
->authorize(fn () => auth()->user()->can('update', $this->getOwnerRecord())),
|
||||
->authorize(fn () => auth()->user()->can('update node')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,8 @@ class NodesRelationManager extends RelationManager
|
||||
->sortable(),
|
||||
SelectColumn::make('allocation.id')
|
||||
->label(trans('admin/node.primary_allocation'))
|
||||
->disabled(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
->options(fn (Server $server) => $server->allocations->take(1)->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
|
||||
->selectablePlaceholder(fn (SelectColumn $select) => !$select->isDisabled())
|
||||
->placeholder('None')
|
||||
->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address])
|
||||
->selectablePlaceholder(false)
|
||||
->sortable(),
|
||||
TextColumn::make('memory')->label(trans('admin/node.memory'))->icon('tabler-device-desktop-analytics'),
|
||||
TextColumn::make('cpu')->label(trans('admin/node.cpu'))->icon('tabler-cpu'),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Admin\Resources\NodeResource\Widgets;
|
||||
|
||||
use App\Models\Node;
|
||||
use Carbon\Carbon;
|
||||
use Filament\Support\RawJs;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
use Illuminate\Support\Number;
|
||||
@@ -15,34 +16,22 @@ class NodeCpuChart extends ChartWidget
|
||||
|
||||
public Node $node;
|
||||
|
||||
/**
|
||||
* @var array<int, array{cpu: string, timestamp: string}>
|
||||
*/
|
||||
protected array $cpuHistory = [];
|
||||
|
||||
protected int $threads = 0;
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$sessionKey = "node_stats.{$this->node->id}";
|
||||
$threads = $this->node->systemInformation()['cpu_count'] ?? 0;
|
||||
|
||||
$data = $this->node->statistics();
|
||||
|
||||
$this->threads = session("{$sessionKey}.threads", $this->node->systemInformation()['cpu_count'] ?? 0);
|
||||
|
||||
$this->cpuHistory = session("{$sessionKey}.cpu_history", []);
|
||||
$this->cpuHistory[] = [
|
||||
'cpu' => round($data['cpu_percent'] * $this->threads, 2),
|
||||
'timestamp' => now(auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
];
|
||||
|
||||
$this->cpuHistory = array_slice($this->cpuHistory, -60);
|
||||
session()->put("{$sessionKey}.cpu_history", $this->cpuHistory);
|
||||
$cpu = collect(cache()->get("nodes.{$this->node->id}.cpu_percent"))
|
||||
->slice(-10)
|
||||
->map(fn ($value, $key) => [
|
||||
'cpu' => round($value * $threads, 2),
|
||||
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
])
|
||||
->all();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'data' => array_column($this->cpuHistory, 'cpu'),
|
||||
'data' => array_column($cpu, 'cpu'),
|
||||
'backgroundColor' => [
|
||||
'rgba(96, 165, 250, 0.3)',
|
||||
],
|
||||
@@ -50,7 +39,7 @@ class NodeCpuChart extends ChartWidget
|
||||
'fill' => true,
|
||||
],
|
||||
],
|
||||
'labels' => array_column($this->cpuHistory, 'timestamp'),
|
||||
'labels' => array_column($cpu, 'timestamp'),
|
||||
'locale' => auth()->user()->language ?? 'en',
|
||||
];
|
||||
}
|
||||
@@ -80,10 +69,10 @@ class NodeCpuChart extends ChartWidget
|
||||
|
||||
public function getHeading(): string
|
||||
{
|
||||
$data = array_slice(end($this->cpuHistory), -60);
|
||||
$threads = $this->node->systemInformation()['cpu_count'] ?? 0;
|
||||
|
||||
$cpu = Number::format($data['cpu'], maxPrecision: 2, locale: auth()->user()->language);
|
||||
$max = Number::format($this->threads * 100, locale: auth()->user()->language);
|
||||
$cpu = Number::format(collect(cache()->get("nodes.{$this->node->id}.cpu_percent"))->last() * $threads, maxPrecision: 2, locale: auth()->user()->language);
|
||||
$max = Number::format($threads * 100, locale: auth()->user()->language);
|
||||
|
||||
return trans('admin/node.cpu_chart', ['cpu' => $cpu, 'max' => $max]);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Admin\Resources\NodeResource\Widgets;
|
||||
|
||||
use App\Models\Node;
|
||||
use Carbon\Carbon;
|
||||
use Filament\Support\RawJs;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
use Illuminate\Support\Number;
|
||||
@@ -15,36 +16,19 @@ class NodeMemoryChart extends ChartWidget
|
||||
|
||||
public Node $node;
|
||||
|
||||
/**
|
||||
* @var array<int, array{memory: string, timestamp: string}>
|
||||
*/
|
||||
protected array $memoryHistory = [];
|
||||
|
||||
protected int $totalMemory = 0;
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$sessionKey = "node_stats.{$this->node->id}";
|
||||
|
||||
$data = $this->node->statistics();
|
||||
|
||||
$this->totalMemory = session("{$sessionKey}.total_memory", $data['memory_total']);
|
||||
|
||||
$this->memoryHistory = session("{$sessionKey}.memory_history", []);
|
||||
$this->memoryHistory[] = [
|
||||
'memory' => round(config('panel.use_binary_prefix')
|
||||
? $data['memory_used'] / 1024 / 1024 / 1024
|
||||
: $data['memory_used'] / 1000 / 1000 / 1000, 2),
|
||||
'timestamp' => now(auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
];
|
||||
|
||||
$this->memoryHistory = array_slice($this->memoryHistory, -60);
|
||||
session()->put("{$sessionKey}.memory_history", $this->memoryHistory);
|
||||
$memUsed = collect(cache()->get("nodes.{$this->node->id}.memory_used"))->slice(-10)
|
||||
->map(fn ($value, $key) => [
|
||||
'memory' => round(config('panel.use_binary_prefix') ? $value / 1024 / 1024 / 1024 : $value / 1000 / 1000 / 1000, 2),
|
||||
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
])
|
||||
->all();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'data' => array_column($this->memoryHistory, 'memory'),
|
||||
'data' => array_column($memUsed, 'memory'),
|
||||
'backgroundColor' => [
|
||||
'rgba(96, 165, 250, 0.3)',
|
||||
],
|
||||
@@ -52,7 +36,7 @@ class NodeMemoryChart extends ChartWidget
|
||||
'fill' => true,
|
||||
],
|
||||
],
|
||||
'labels' => array_column($this->memoryHistory, 'timestamp'),
|
||||
'labels' => array_column($memUsed, 'timestamp'),
|
||||
'locale' => auth()->user()->language ?? 'en',
|
||||
];
|
||||
}
|
||||
@@ -82,15 +66,16 @@ class NodeMemoryChart extends ChartWidget
|
||||
|
||||
public function getHeading(): string
|
||||
{
|
||||
$latestMemoryUsed = array_slice(end($this->memoryHistory), -60);
|
||||
$latestMemoryUsed = collect(cache()->get("nodes.{$this->node->id}.memory_used"))->last();
|
||||
$totalMemory = collect(cache()->get("nodes.{$this->node->id}.memory_total"))->last();
|
||||
|
||||
$used = config('panel.use_binary_prefix')
|
||||
? Number::format($latestMemoryUsed['memory'], maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
||||
: Number::format($latestMemoryUsed['memory'], maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
||||
? Number::format($latestMemoryUsed / 1024 / 1024 / 1024, maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
||||
: Number::format($latestMemoryUsed / 1000 / 1000 / 1000, maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
||||
|
||||
$total = config('panel.use_binary_prefix')
|
||||
? Number::format($this->totalMemory / 1024 / 1024 / 1024, maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
||||
: Number::format($this->totalMemory / 1000 / 1000 / 1000, maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
||||
? Number::format($totalMemory / 1024 / 1024 / 1024, maxPrecision: 2, locale: auth()->user()->language) .' GiB'
|
||||
: Number::format($totalMemory / 1000 / 1000 / 1000, maxPrecision: 2, locale: auth()->user()->language) . ' GB';
|
||||
|
||||
return trans('admin/node.memory_chart', ['used' => $used, 'total' => $total]);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,6 @@ namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\RoleResource\Pages;
|
||||
use App\Models\Role;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use App\Traits\Filament\CanModifyForm;
|
||||
use App\Traits\Filament\CanModifyTable;
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\Component;
|
||||
@@ -18,7 +14,6 @@ use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
@@ -31,11 +26,6 @@ use Spatie\Permission\Contracts\Permission;
|
||||
|
||||
class RoleResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
use CanModifyForm;
|
||||
use CanModifyTable;
|
||||
|
||||
protected static ?string $model = Role::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-users-group';
|
||||
@@ -67,7 +57,7 @@ class RoleResource extends Resource
|
||||
return static::getModel()::count() ?: null;
|
||||
}
|
||||
|
||||
public static function defaultTable(Table $table): Table
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
@@ -107,7 +97,7 @@ class RoleResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
public static function defaultForm(Form $form): Form
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
$permissionSections = [];
|
||||
|
||||
@@ -157,8 +147,6 @@ class RoleResource extends Resource
|
||||
*/
|
||||
private static function makeSection(string $model, array $options): Section
|
||||
{
|
||||
$model = ucwords($model);
|
||||
|
||||
$icon = null;
|
||||
|
||||
if (class_exists('\App\Filament\Admin\Resources\\' . $model . 'Resource')) {
|
||||
@@ -210,8 +198,7 @@ class RoleResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListRoles::route('/'),
|
||||
|
||||
@@ -4,10 +4,6 @@ namespace App\Filament\Admin\Resources\RoleResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\RoleResource;
|
||||
use App\Models\Role;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -18,17 +14,13 @@ use Spatie\Permission\Models\Permission;
|
||||
*/
|
||||
class CreateRole extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
public Collection $permissions;
|
||||
|
||||
protected static string $resource = RoleResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->getCreateFormAction()->formId('form'),
|
||||
|
||||
@@ -4,10 +4,6 @@ namespace App\Filament\Admin\Resources\RoleResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\RoleResource;
|
||||
use App\Models\Role;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -19,9 +15,6 @@ use Spatie\Permission\Models\Permission;
|
||||
*/
|
||||
class EditRole extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = RoleResource::class;
|
||||
|
||||
public Collection $permissions;
|
||||
@@ -52,8 +45,7 @@ class EditRole extends EditRecord
|
||||
$this->record->syncPermissions($permissionModels);
|
||||
}
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make()
|
||||
|
||||
@@ -3,22 +3,14 @@
|
||||
namespace App\Filament\Admin\Resources\RoleResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\RoleResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListRoles extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = RoleResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
|
||||
@@ -3,22 +3,14 @@
|
||||
namespace App\Filament\Admin\Resources\RoleResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\RoleResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewRole extends ViewRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = RoleResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
|
||||
@@ -3,23 +3,15 @@
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\ServerResource\Pages;
|
||||
use App\Filament\Admin\Resources\ServerResource\RelationManagers;
|
||||
use App\Models\Mount;
|
||||
use App\Models\Server;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Resources\Resource;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class ServerResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
|
||||
protected static ?string $model = Server::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-brand-docker';
|
||||
@@ -74,16 +66,7 @@ class ServerResource extends Resource
|
||||
->columnSpanFull();
|
||||
}
|
||||
|
||||
/** @return class-string<RelationManager>[] */
|
||||
public static function getDefaultRelations(): array
|
||||
{
|
||||
return [
|
||||
RelationManagers\AllocationsRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListServers::route('/'),
|
||||
@@ -96,6 +79,8 @@ class ServerResource extends Resource
|
||||
{
|
||||
$query = parent::getEloquentQuery();
|
||||
|
||||
return $query->whereIn('node_id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
return $query->whereHas('node', function (Builder $query) {
|
||||
$query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ use App\Services\Allocations\AssignmentService;
|
||||
use App\Services\Servers\RandomWordService;
|
||||
use App\Services\Servers\ServerCreationService;
|
||||
use App\Services\Users\UserCreationService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Filament\Forms;
|
||||
@@ -47,9 +45,6 @@ use LogicException;
|
||||
|
||||
class CreateServer extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = ServerResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
@@ -128,12 +123,12 @@ class CreateServer extends CreateRecord
|
||||
->live()
|
||||
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id')))
|
||||
->searchable()
|
||||
->required()
|
||||
->preload()
|
||||
->afterStateUpdated(function (Set $set, $state) {
|
||||
$set('allocation_id', null);
|
||||
$this->node = Node::find($state);
|
||||
}),
|
||||
})
|
||||
->required(),
|
||||
|
||||
Select::make('owner_id')
|
||||
->preload()
|
||||
@@ -149,7 +144,6 @@ class CreateServer extends CreateRecord
|
||||
->relationship('user', 'username')
|
||||
->searchable(['username', 'email'])
|
||||
->getOptionLabelFromRecordUsing(fn (User $user) => "$user->username ($user->email)")
|
||||
->createOptionAction(fn (Action $action) => $action->authorize(fn () => auth()->user()->can('create', User::class)))
|
||||
->createOptionForm([
|
||||
TextInput::make('username')
|
||||
->label(trans('admin/user.username'))
|
||||
@@ -194,7 +188,7 @@ class CreateServer extends CreateRecord
|
||||
$set('allocation_additional', null);
|
||||
$set('allocation_additional.needstobeastringhere.extra_allocations', null);
|
||||
})
|
||||
->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address ?? '')
|
||||
->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address)
|
||||
->placeholder(function (Get $get) {
|
||||
$node = Node::find($get('node_id'));
|
||||
|
||||
@@ -211,7 +205,6 @@ class CreateServer extends CreateRecord
|
||||
->where('node_id', $get('node_id'))
|
||||
->whereNull('server_id'),
|
||||
)
|
||||
->createOptionAction(fn (Action $action) => $action->authorize(fn (Get $get) => auth()->user()->can('create', Node::find($get('node_id')))))
|
||||
->createOptionForm(function (Get $get) {
|
||||
$getPage = $get;
|
||||
|
||||
@@ -248,7 +241,9 @@ class CreateServer extends CreateRecord
|
||||
return collect(
|
||||
$assignmentService->handle(Node::find($get('node_id')), $data)
|
||||
)->first();
|
||||
}),
|
||||
})
|
||||
->required(),
|
||||
|
||||
Repeater::make('allocation_additional')
|
||||
->label(trans('admin/server.additional_allocations'))
|
||||
->columnSpan([
|
||||
@@ -268,7 +263,7 @@ class CreateServer extends CreateRecord
|
||||
->prefixIcon('tabler-network')
|
||||
->label('Additional Allocations')
|
||||
->columnSpan(2)
|
||||
->disabled(fn (Get $get) => $get('../../allocation_id') === null || $get('../../node_id') === null)
|
||||
->disabled(fn (Get $get) => $get('../../node_id') === null)
|
||||
->searchable(['ip', 'port', 'ip_alias'])
|
||||
->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address)
|
||||
->placeholder(trans('admin/server.select_additional'))
|
||||
@@ -443,7 +438,6 @@ class CreateServer extends CreateRecord
|
||||
|
||||
$text = TextInput::make('variable_value')
|
||||
->hidden($this->shouldHideComponent(...))
|
||||
->dehydratedWhenHidden()
|
||||
->required(fn (Get $get) => in_array('required', $get('rules')))
|
||||
->rules(
|
||||
fn (Get $get): Closure => function (string $attribute, $value, Closure $fail) use ($get) {
|
||||
@@ -461,7 +455,6 @@ class CreateServer extends CreateRecord
|
||||
|
||||
$select = Select::make('variable_value')
|
||||
->hidden($this->shouldHideComponent(...))
|
||||
->dehydratedWhenHidden()
|
||||
->options($this->getSelectOptionsFromRules(...))
|
||||
->selectablePlaceholder(false);
|
||||
|
||||
@@ -833,9 +826,7 @@ class CreateServer extends CreateRecord
|
||||
|
||||
protected function handleRecordCreation(array $data): Model
|
||||
{
|
||||
if ($allocation_additional = array_get($data, 'allocation_additional')) {
|
||||
$data['allocation_additional'] = collect($allocation_additional)->filter()->all();
|
||||
}
|
||||
$data['allocation_additional'] = collect($data['allocation_additional'])->filter()->all();
|
||||
|
||||
try {
|
||||
return $this->serverCreationService->handle($data);
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Filament\Admin\Resources\ServerResource\Pages;
|
||||
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
|
||||
use App\Enums\SuspendAction;
|
||||
use App\Filament\Admin\Resources\ServerResource;
|
||||
use App\Filament\Admin\Resources\ServerResource\RelationManagers\AllocationsRelationManager;
|
||||
use App\Filament\Components\Forms\Actions\PreviewStartupAction;
|
||||
use App\Filament\Components\Forms\Actions\RotateDatabasePasswordAction;
|
||||
use App\Filament\Server\Pages\Console;
|
||||
@@ -25,8 +26,6 @@ use App\Services\Servers\ServerDeletionService;
|
||||
use App\Services\Servers\SuspensionService;
|
||||
use App\Services\Servers\ToggleInstallService;
|
||||
use App\Services\Servers\TransferServerService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Filament\Actions;
|
||||
@@ -63,9 +62,6 @@ use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
|
||||
|
||||
class EditServer extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = ServerResource::class;
|
||||
|
||||
private DaemonServerRepository $daemonServerRepository;
|
||||
@@ -648,7 +644,6 @@ class EditServer extends EditRecord
|
||||
|
||||
$text = TextInput::make('variable_value')
|
||||
->hidden($this->shouldHideComponent(...))
|
||||
->dehydratedWhenHidden()
|
||||
->required(fn (ServerVariable $serverVariable) => $serverVariable->variable->getRequiredAttribute())
|
||||
->rules([
|
||||
fn (ServerVariable $serverVariable): Closure => function (string $attribute, $value, Closure $fail) use ($serverVariable) {
|
||||
@@ -666,7 +661,6 @@ class EditServer extends EditRecord
|
||||
|
||||
$select = Select::make('variable_value')
|
||||
->hidden($this->shouldHideComponent(...))
|
||||
->dehydratedWhenHidden()
|
||||
->options($this->getSelectOptionsFromRules(...))
|
||||
->selectablePlaceholder(false);
|
||||
|
||||
@@ -692,7 +686,7 @@ class EditServer extends EditRecord
|
||||
ServerResource::getMountCheckboxList($get),
|
||||
]),
|
||||
Tab::make(trans('admin/server.databases'))
|
||||
->hidden(fn () => !auth()->user()->can('viewAny', Database::class))
|
||||
->hidden(fn () => !auth()->user()->can('viewList database'))
|
||||
->icon('tabler-database')
|
||||
->columns(4)
|
||||
->schema([
|
||||
@@ -716,14 +710,14 @@ class EditServer extends EditRecord
|
||||
->hintAction(
|
||||
Action::make('Delete')
|
||||
->label(trans('filament-actions::delete.single.modal.actions.delete.label'))
|
||||
->authorize(fn (Database $database) => auth()->user()->can('delete', $database))
|
||||
->authorize(fn (Database $database) => auth()->user()->can('delete database', $database))
|
||||
->color('danger')
|
||||
->icon('tabler-trash')
|
||||
->requiresConfirmation()
|
||||
->modalIcon('tabler-database-x')
|
||||
->modalHeading(trans('admin/server.delete_db_heading'))
|
||||
->modalSubmitActionLabel(trans('filament-actions::delete.single.label'))
|
||||
->modalDescription(fn (Get $get) => trans('admin/server.delete_db', ['name' => $get('database')]))
|
||||
->modalSubmitActionLabel(fn (Get $get) => 'Delete ' . $get('database') . '?')
|
||||
->modalDescription(fn (Get $get) => trans('admin/server.delete_db') . $get('database') . '?')
|
||||
->action(function (DatabaseManagementService $databaseManagementService, $record) {
|
||||
$databaseManagementService->delete($record);
|
||||
$this->fillForm();
|
||||
@@ -769,7 +763,7 @@ class EditServer extends EditRecord
|
||||
->columnSpan(4),
|
||||
FormActions::make([
|
||||
Action::make('createDatabase')
|
||||
->authorize(fn () => auth()->user()->can('create', Database::class))
|
||||
->authorize(fn () => auth()->user()->can('create database'))
|
||||
->disabled(fn () => DatabaseHost::query()->count() < 1)
|
||||
->label(fn () => DatabaseHost::query()->count() < 1 ? trans('admin/server.no_db_hosts') : trans('admin/server.create_database'))
|
||||
->color(fn () => DatabaseHost::query()->count() < 1 ? 'danger' : 'primary')
|
||||
@@ -857,7 +851,7 @@ class EditServer extends EditRecord
|
||||
} catch (Exception) {
|
||||
Notification::make()
|
||||
->title(trans('admin/server.notifications.reinstall_failed'))
|
||||
->body(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
||||
->body(trans('admin/server.error_connecting', ['node' => $server->node->name]))
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
@@ -906,7 +900,7 @@ class EditServer extends EditRecord
|
||||
Notification::make()
|
||||
->warning()
|
||||
->title(trans('admin/server.notifications.server_suspension'))
|
||||
->body(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
||||
->body(trans('admin/server.error_connecting', ['node' => $server->node->name]))
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
@@ -928,7 +922,7 @@ class EditServer extends EditRecord
|
||||
Notification::make()
|
||||
->warning()
|
||||
->title(trans('admin/server.notifications.server_suspension'))
|
||||
->body(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
||||
->body(trans('admin/server.error_connecting', ['node' => $server->node->name]))
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
@@ -993,7 +987,7 @@ class EditServer extends EditRecord
|
||||
} catch (Exception) {
|
||||
Notification::make()
|
||||
->title(trans('admin/server.notifications.reinstall_failed'))
|
||||
->body(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
||||
->body(trans('admin/server.error_connecting', ['node' => $server->node->name]))
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
@@ -1022,28 +1016,24 @@ class EditServer extends EditRecord
|
||||
->options(fn (Server $server) => Node::whereNot('id', $server->node->id)->pluck('name', 'id')->all()),
|
||||
Select::make('allocation_id')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->disabled(fn (Get $get, Server $server) => !$get('node_id') || !$server->allocation_id)
|
||||
->required(fn (Server $server) => $server->allocation_id)
|
||||
->required()
|
||||
->prefixIcon('tabler-network')
|
||||
->disabled(fn (Get $get) => !$get('node_id'))
|
||||
->options(fn (Get $get) => Allocation::where('node_id', $get('node_id'))->whereNull('server_id')->get()->mapWithKeys(fn (Allocation $allocation) => [$allocation->id => $allocation->address]))
|
||||
->searchable(['ip', 'port', 'ip_alias'])
|
||||
->placeholder(trans('admin/server.select_allocation')),
|
||||
Select::make('allocation_additional')
|
||||
->label(trans('admin/server.additional_allocations'))
|
||||
->disabled(fn (Get $get, Server $server) => !$get('node_id') || $server->allocations->count() <= 1)
|
||||
->multiple()
|
||||
->minItems(fn (Select $select) => $select->getMaxItems())
|
||||
->maxItems(fn (Select $select, Server $server) => $select->isDisabled() ? null : $server->allocations->count() - 1)
|
||||
->prefixIcon('tabler-network')
|
||||
->required(fn (Server $server) => $server->allocations->count() > 1)
|
||||
->disabled(fn (Get $get) => !$get('node_id'))
|
||||
->options(fn (Get $get) => Allocation::where('node_id', $get('node_id'))->whereNull('server_id')->when($get('allocation_id'), fn ($query) => $query->whereNot('id', $get('allocation_id')))->get()->mapWithKeys(fn (Allocation $allocation) => [$allocation->id => $allocation->address]))
|
||||
->searchable(['ip', 'port', 'ip_alias'])
|
||||
->placeholder(trans('admin/server.select_additional')),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<Actions\Action|Actions\ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
/** @var Server $server */
|
||||
$server = $this->getRecord();
|
||||
@@ -1075,7 +1065,7 @@ class EditServer extends EditRecord
|
||||
}
|
||||
})
|
||||
->hidden(fn () => $canForceDelete)
|
||||
->authorize(fn (Server $server) => auth()->user()->can('delete', $server)),
|
||||
->authorize(fn (Server $server) => auth()->user()->can('delete server', $server)),
|
||||
Actions\Action::make('ForceDelete')
|
||||
->color('danger')
|
||||
->label(trans('filament-actions::force-delete.single.label'))
|
||||
@@ -1092,7 +1082,7 @@ class EditServer extends EditRecord
|
||||
}
|
||||
})
|
||||
->visible(fn () => $canForceDelete)
|
||||
->authorize(fn (Server $server) => auth()->user()->can('delete', $server)),
|
||||
->authorize(fn (Server $server) => auth()->user()->can('delete server', $server)),
|
||||
Actions\Action::make('console')
|
||||
->label(trans('admin/server.console'))
|
||||
->icon('tabler-terminal')
|
||||
@@ -1146,6 +1136,13 @@ class EditServer extends EditRecord
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getRelationManagers(): array
|
||||
{
|
||||
return [
|
||||
AllocationsRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
private function shouldHideComponent(ServerVariable $serverVariable, Forms\Components\Component $component): bool
|
||||
{
|
||||
$containsRuleIn = array_first($serverVariable->variable->rules, fn ($value) => str($value)->startsWith('in:'), false);
|
||||
|
||||
@@ -5,8 +5,6 @@ namespace App\Filament\Admin\Resources\ServerResource\Pages;
|
||||
use App\Filament\Server\Pages\Console;
|
||||
use App\Filament\Admin\Resources\ServerResource;
|
||||
use App\Models\Server;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Tables\Actions\Action;
|
||||
@@ -19,9 +17,6 @@ use Filament\Tables\Table;
|
||||
|
||||
class ListServers extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = ServerResource::class;
|
||||
|
||||
public function table(Table $table): Table
|
||||
@@ -73,17 +68,14 @@ class ListServers extends ListRecords
|
||||
->searchable(),
|
||||
SelectColumn::make('allocation_id')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->hidden(fn () => !auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->disabled(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
->hidden(!auth()->user()->can('update server'))
|
||||
->options(fn (Server $server) => $server->allocations->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
|
||||
->selectablePlaceholder(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
->placeholder('None')
|
||||
->selectablePlaceholder(false)
|
||||
->sortable(),
|
||||
TextColumn::make('allocation_id_readonly')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->hidden(fn () => auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->disabled(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
->state(fn (Server $server) => $server->allocation->address ?? 'None'),
|
||||
->hidden(auth()->user()->can('update server'))
|
||||
->state(fn (Server $server) => $server->allocation->address),
|
||||
TextColumn::make('image')->hidden(),
|
||||
TextColumn::make('backups_count')
|
||||
->counts('backups')
|
||||
@@ -109,8 +101,7 @@ class ListServers extends ListRecords
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<Actions\Action|Actions\ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make()
|
||||
|
||||
@@ -12,6 +12,8 @@ use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Forms\Set;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Support\Exceptions\Halt;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Actions\AssociateAction;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
use Filament\Tables\Actions\DissociateAction;
|
||||
@@ -20,6 +22,7 @@ use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\TextInputColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
/**
|
||||
* @method Server getOwnerRecord()
|
||||
@@ -34,6 +37,7 @@ class AllocationsRelationManager extends RelationManager
|
||||
->selectCurrentPageOnly()
|
||||
->recordTitleAttribute('address')
|
||||
->recordTitle(fn (Allocation $allocation) => $allocation->address)
|
||||
->checkIfRecordIsSelectableUsing(fn (Allocation $record) => $record->id !== $this->getOwnerRecord()->allocation_id)
|
||||
->inverseRelationship('server')
|
||||
->heading(trans('admin/server.allocations'))
|
||||
->columns([
|
||||
@@ -43,9 +47,6 @@ class AllocationsRelationManager extends RelationManager
|
||||
->label(trans('admin/server.port')),
|
||||
TextInputColumn::make('ip_alias')
|
||||
->label(trans('admin/server.alias')),
|
||||
TextInputColumn::make('notes')
|
||||
->label(trans('admin/server.notes'))
|
||||
->placeholder(trans('admin/server.no_notes')),
|
||||
IconColumn::make('primary')
|
||||
->icon(fn ($state) => match ($state) {
|
||||
true => 'tabler-star-filled',
|
||||
@@ -55,17 +56,17 @@ class AllocationsRelationManager extends RelationManager
|
||||
true => 'warning',
|
||||
default => 'gray',
|
||||
})
|
||||
->tooltip(fn (Allocation $allocation) => trans('admin/server.' . ($allocation->id === $this->getOwnerRecord()->allocation_id ? 'already' : 'make') . '_primary'))
|
||||
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
|
||||
->default(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id)
|
||||
->label(trans('admin/server.primary')),
|
||||
])
|
||||
->actions([
|
||||
Action::make('make-primary')
|
||||
->label(trans('admin/server.make_primary'))
|
||||
->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords())
|
||||
->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id),
|
||||
DissociateAction::make()
|
||||
->after(function (Allocation $allocation) {
|
||||
$allocation->update(['notes' => null]);
|
||||
$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||
}),
|
||||
->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id),
|
||||
])
|
||||
->headerActions([
|
||||
CreateAction::make()->label(trans('admin/server.create_allocation'))
|
||||
@@ -83,7 +84,8 @@ class AllocationsRelationManager extends RelationManager
|
||||
->label(trans('admin/server.alias'))
|
||||
->inlineLabel()
|
||||
->default(null)
|
||||
->helperText(trans('admin/server.alias_helper')),
|
||||
->helperText(trans('admin/server.alias_helper'))
|
||||
->required(false),
|
||||
TagsInput::make('allocation_ports')
|
||||
->placeholder('27015, 27017-27019')
|
||||
->label(trans('admin/server.ports'))
|
||||
@@ -101,14 +103,22 @@ class AllocationsRelationManager extends RelationManager
|
||||
->preloadRecordSelect()
|
||||
->recordSelectOptionsQuery(fn ($query) => $query->whereBelongsTo($this->getOwnerRecord()->node)->whereNull('server_id'))
|
||||
->recordSelectSearchColumns(['ip', 'port'])
|
||||
->label(trans('admin/server.add_allocation'))
|
||||
->after(fn (array $data) => !$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $data['recordId'][0]])),
|
||||
->label(trans('admin/server.add_allocation')),
|
||||
])
|
||||
->groupedBulkActions([
|
||||
DissociateBulkAction::make()
|
||||
->after(function () {
|
||||
Allocation::whereNull('server_id')->update(['notes' => null]);
|
||||
$this->getOwnerRecord()->allocation_id && $this->getOwnerRecord()->update(['allocation_id' => $this->getOwnerRecord()->allocations()->first()?->id]);
|
||||
->before(function (DissociateBulkAction $action, Collection $records) {
|
||||
$records = $records->filter(function ($allocation) {
|
||||
/** @var Allocation $allocation */
|
||||
return $allocation->id !== $this->getOwnerRecord()->allocation_id;
|
||||
});
|
||||
|
||||
if ($records->isEmpty()) {
|
||||
$action->failureNotificationTitle(trans('admin/server.notifications.dissociate_primary'))->failure();
|
||||
throw new Halt();
|
||||
}
|
||||
|
||||
return $records;
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -6,16 +6,10 @@ use App\Filament\Admin\Resources\UserResource\Pages;
|
||||
use App\Filament\Admin\Resources\UserResource\RelationManagers;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use App\Traits\Filament\CanModifyForm;
|
||||
use App\Traits\Filament\CanModifyTable;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
use Filament\Tables\Actions\EditAction;
|
||||
@@ -28,11 +22,6 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class UserResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
use CanModifyForm;
|
||||
use CanModifyTable;
|
||||
|
||||
protected static ?string $model = User::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-users';
|
||||
@@ -64,7 +53,7 @@ class UserResource extends Resource
|
||||
return static::getModel()::count() ?: null;
|
||||
}
|
||||
|
||||
public static function defaultTable(Table $table): Table
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
@@ -110,7 +99,7 @@ class UserResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
public static function defaultForm(Form $form): Form
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->columns(['default' => 1, 'lg' => 3])
|
||||
@@ -157,16 +146,14 @@ class UserResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return class-string<RelationManager>[] */
|
||||
public static function getDefaultRelations(): array
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
RelationManagers\ServersRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListUsers::route('/'),
|
||||
|
||||
@@ -5,18 +5,11 @@ namespace App\Filament\Admin\Resources\UserResource\Pages;
|
||||
use App\Filament\Admin\Resources\UserResource;
|
||||
use App\Models\Role;
|
||||
use App\Services\Users\UserCreationService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CreateUser extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = UserResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
@@ -28,8 +21,7 @@ class CreateUser extends CreateRecord
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->getCreateFormAction()->formId('form'),
|
||||
|
||||
@@ -5,19 +5,12 @@ namespace App\Filament\Admin\Resources\UserResource\Pages;
|
||||
use App\Filament\Admin\Resources\UserResource;
|
||||
use App\Models\User;
|
||||
use App\Services\Users\UserUpdateService;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class EditUser extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = UserResource::class;
|
||||
|
||||
private UserUpdateService $service;
|
||||
@@ -27,8 +20,7 @@ class EditUser extends EditRecord
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make()
|
||||
|
||||
@@ -3,22 +3,14 @@
|
||||
namespace App\Filament\Admin\Resources\UserResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\UserResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListUsers extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = UserResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
|
||||
@@ -3,22 +3,14 @@
|
||||
namespace App\Filament\Admin\Resources\UserResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\UserResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewUser extends ViewRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = UserResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
|
||||
@@ -70,9 +70,8 @@ class ServersRelationManager extends RelationManager
|
||||
->sortable(),
|
||||
SelectColumn::make('allocation.id')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->disabled()
|
||||
->options(fn (Server $server) => $server->allocations->take(1)->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
|
||||
->placeholder('None')
|
||||
->options(fn (Server $server) => [$server->allocation->id => $server->allocation->address])
|
||||
->selectablePlaceholder(false)
|
||||
->sortable(),
|
||||
TextColumn::make('image')->hidden(),
|
||||
TextColumn::make('databases_count')
|
||||
|
||||
@@ -3,49 +3,21 @@
|
||||
namespace App\Filament\Admin\Resources;
|
||||
|
||||
use App\Filament\Admin\Resources\WebhookResource\Pages;
|
||||
use App\Filament\Admin\Resources\WebhookResource\Pages\EditWebhookConfiguration;
|
||||
use App\Livewire\AlertBanner;
|
||||
use App\Models\WebhookConfiguration;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use App\Traits\Filament\CanModifyForm;
|
||||
use App\Traits\Filament\CanModifyTable;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\ColorPicker;
|
||||
use Filament\Forms\Components\KeyValue;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\ToggleButtons;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Forms\Set;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
use Filament\Tables\Actions\ViewAction;
|
||||
use Filament\Tables\Actions\EditAction;
|
||||
use Filament\Tables\Actions\ReplicateAction;
|
||||
use Filament\Tables\Actions\CreateAction;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
use Filament\Tables\Actions\DeleteBulkAction;
|
||||
use Filament\Tables\Actions\EditAction;
|
||||
use Filament\Tables\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Livewire\Features\SupportEvents\HandlesEvents;
|
||||
use App\Enums\WebhookType;
|
||||
use Filament\Forms\Components\Component;
|
||||
use Livewire\Component as Livewire;
|
||||
|
||||
class WebhookResource extends Resource
|
||||
{
|
||||
use CanCustomizePages;
|
||||
use CanCustomizeRelations;
|
||||
use CanModifyForm;
|
||||
use CanModifyTable;
|
||||
use HandlesEvents;
|
||||
|
||||
protected static ?string $model = WebhookConfiguration::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'tabler-webhook';
|
||||
@@ -77,16 +49,10 @@ class WebhookResource extends Resource
|
||||
return trans('admin/dashboard.advanced');
|
||||
}
|
||||
|
||||
public static function defaultTable(Table $table): Table
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
IconColumn::make('type'),
|
||||
TextColumn::make('endpoint')
|
||||
->label(trans('admin/webhook.table.endpoint'))
|
||||
->formatStateUsing(fn (string $state) => str($state)->after('://'))
|
||||
->limit(60)
|
||||
->wrap(),
|
||||
TextColumn::make('description')
|
||||
->label(trans('admin/webhook.table.description')),
|
||||
TextColumn::make('endpoint')
|
||||
@@ -94,15 +60,9 @@ class WebhookResource extends Resource
|
||||
])
|
||||
->actions([
|
||||
ViewAction::make()
|
||||
->hidden(fn (WebhookConfiguration $record) => static::canEdit($record)),
|
||||
->hidden(fn ($record) => static::canEdit($record)),
|
||||
EditAction::make(),
|
||||
ReplicateAction::make()
|
||||
->iconButton()
|
||||
->tooltip(trans('filament-actions::replicate.single.label'))
|
||||
->modal(false)
|
||||
->excludeAttributes(['created_at', 'updated_at'])
|
||||
->beforeReplicaSaved(fn (WebhookConfiguration $replica) => $replica->description .= ' Copy ' . now()->format('Y-m-d H:i:s'))
|
||||
->successRedirectUrl(fn (WebhookConfiguration $replica) => EditWebhookConfiguration::getUrl(['record' => $replica])),
|
||||
DeleteAction::make(),
|
||||
])
|
||||
->groupedBulkActions([
|
||||
DeleteBulkAction::make(),
|
||||
@@ -112,229 +72,33 @@ class WebhookResource extends Resource
|
||||
->emptyStateHeading(trans('admin/webhook.no_webhooks'))
|
||||
->emptyStateActions([
|
||||
CreateAction::make(),
|
||||
])
|
||||
->persistFiltersInSession()
|
||||
->filters([
|
||||
SelectFilter::make('type')
|
||||
->options(WebhookType::class)
|
||||
->attribute('type'),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function defaultForm(Form $form): Form
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
ToggleButtons::make('type')
|
||||
->live()
|
||||
->inline()
|
||||
->options(WebhookType::class)
|
||||
->default(WebhookType::Regular->value)
|
||||
->afterStateHydrated(function (string $state) {
|
||||
if ($state === WebhookType::Discord->value) {
|
||||
self::sendHelpBanner();
|
||||
}
|
||||
})
|
||||
->afterStateUpdated(function (string $state) {
|
||||
if ($state === WebhookType::Discord->value) {
|
||||
self::sendHelpBanner();
|
||||
}
|
||||
}),
|
||||
TextInput::make('description')
|
||||
->label(trans('admin/webhook.description'))
|
||||
->required(),
|
||||
TextInput::make('endpoint')
|
||||
->label(trans('admin/webhook.endpoint'))
|
||||
->activeUrl()
|
||||
->required()
|
||||
->required(),
|
||||
TextInput::make('description')
|
||||
->label(trans('admin/webhook.description'))
|
||||
->required(),
|
||||
CheckboxList::make('events')
|
||||
->lazy()
|
||||
->options(fn () => WebhookConfiguration::filamentCheckboxList())
|
||||
->searchable()
|
||||
->bulkToggleable()
|
||||
->columns(3)
|
||||
->columnSpanFull()
|
||||
->afterStateUpdated(fn (string $state, Set $set) => $set('type', str($state)->contains('discord.com') ? WebhookType::Discord->value : WebhookType::Regular->value)),
|
||||
Section::make(trans('admin/webhook.regular'))
|
||||
->hidden(fn (Get $get) => $get('type') === WebhookType::Discord->value)
|
||||
->dehydratedWhenHidden()
|
||||
->schema(fn () => self::getRegularFields())
|
||||
->formBefore(),
|
||||
Section::make(trans('admin/webhook.discord'))
|
||||
->hidden(fn (Get $get) => $get('type') === WebhookType::Regular->value)
|
||||
->dehydratedWhenHidden()
|
||||
->afterStateUpdated(fn (Livewire $livewire) => $livewire->dispatch('refresh-widget'))
|
||||
->schema(fn () => self::getDiscordFields())
|
||||
->view('filament.components.webhooksection')
|
||||
->aside()
|
||||
->formBefore(),
|
||||
Section::make(trans('admin/webhook.events'))
|
||||
->collapsible()
|
||||
->collapsed(fn (Get $get) => count($get('events') ?? []))
|
||||
->schema([
|
||||
CheckboxList::make('events')
|
||||
->live()
|
||||
->options(fn () => WebhookConfiguration::filamentCheckboxList())
|
||||
->searchable()
|
||||
->bulkToggleable()
|
||||
->columns(3)
|
||||
->columnSpanFull()
|
||||
->required(),
|
||||
]),
|
||||
->gridDirection('row')
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return Component[] */
|
||||
private static function getRegularFields(): array
|
||||
{
|
||||
return [
|
||||
KeyValue::make('headers')
|
||||
->label(trans('admin/webhook.headers')),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return Component[] */
|
||||
private static function getDiscordFields(): array
|
||||
{
|
||||
return [
|
||||
Section::make(trans('admin/webhook.discord_message.profile'))
|
||||
->collapsible()
|
||||
->schema([
|
||||
TextInput::make('username')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_message.username')),
|
||||
TextInput::make('avatar_url')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_message.avatar_url')),
|
||||
]),
|
||||
Section::make(trans('admin/webhook.discord_message.message'))
|
||||
->collapsible()
|
||||
->schema([
|
||||
TextInput::make('content')
|
||||
->label(trans('admin/webhook.discord_message.message'))
|
||||
->live(debounce: 500)
|
||||
->required(fn (Get $get) => empty($get('embeds'))),
|
||||
TextInput::make('thread_name')
|
||||
->label(trans('admin/webhook.discord_message.forum_thread')),
|
||||
CheckboxList::make('flags')
|
||||
->label('Flags')
|
||||
->options([
|
||||
(1 << 2) => trans('admin/webhook.discord_message.supress_embeds'),
|
||||
(1 << 12) => trans('admin/webhook.discord_message.supress_notifications'),
|
||||
])
|
||||
->descriptions([
|
||||
(1 << 2) => trans('admin/webhook.discord_message.supress_embeds_text'),
|
||||
(1 << 12) => trans('admin/webhook.discord_message.supress_notifications_text'),
|
||||
]),
|
||||
CheckboxList::make('allowed_mentions')
|
||||
->label(trans('admin/webhook.discord_embed.allowed_mentions'))
|
||||
->options([
|
||||
'roles' => trans('admin/webhook.discord_embed.roles'),
|
||||
'users' => trans('admin/webhook.discord_embed.users'),
|
||||
'everyone' => trans('admin/webhook.discord_embed.everyone'),
|
||||
]),
|
||||
]),
|
||||
Repeater::make('embeds')
|
||||
->live(debounce: 500)
|
||||
->itemLabel(fn (array $state) => $state['title'])
|
||||
->addActionLabel(trans('admin/webhook.discord_embed.add_embed'))
|
||||
->required(fn (Get $get) => empty($get('content')))
|
||||
->reorderable()
|
||||
->collapsible()
|
||||
->maxItems(10)
|
||||
->schema([
|
||||
Section::make(trans('admin/webhook.discord_embed.author'))
|
||||
->collapsible()
|
||||
->collapsed()
|
||||
->schema([
|
||||
TextInput::make('author.name')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.author'))
|
||||
->required(fn (Get $get) => filled($get('author.url')) || filled($get('author.icon_url'))),
|
||||
TextInput::make('author.url')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.author_url')),
|
||||
TextInput::make('author.icon_url')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.author_icon_url')),
|
||||
]),
|
||||
Section::make(trans('admin/webhook.discord_embed.body'))
|
||||
->collapsible()
|
||||
->collapsed()
|
||||
->schema([
|
||||
TextInput::make('title')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.title'))
|
||||
->required(fn (Get $get) => $get('description') === null),
|
||||
Textarea::make('description')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.body'))
|
||||
->required(fn (Get $get) => $get('title') === null),
|
||||
ColorPicker::make('color')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.color'))
|
||||
->hex(),
|
||||
TextInput::make('url')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.url')),
|
||||
]),
|
||||
Section::make(trans('admin/webhook.discord_embed.images'))
|
||||
->collapsible()
|
||||
->collapsed()
|
||||
->schema([
|
||||
TextInput::make('image.url')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.image_url')),
|
||||
TextInput::make('thumbnail.url')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.image_thumbnail')),
|
||||
]),
|
||||
Section::make(trans('admin/webhook.discord_embed.footer'))
|
||||
->collapsible()
|
||||
->collapsed()
|
||||
->schema([
|
||||
TextInput::make('footer.text')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.footer')),
|
||||
Checkbox::make('has_timestamp')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.has_timestamp')),
|
||||
TextInput::make('footer.icon_url')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.footer_icon_url')),
|
||||
]),
|
||||
Section::make(trans('admin/webhook.discord_embed.fields'))
|
||||
->collapsible()->collapsed()
|
||||
->schema([
|
||||
Repeater::make('fields')
|
||||
->reorderable()
|
||||
->addActionLabel(trans('admin/webhook.discord_embed.add_field'))
|
||||
->collapsible()
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.field_name'))
|
||||
->required(),
|
||||
Textarea::make('value')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.field_value'))
|
||||
->rows(4)
|
||||
->required(),
|
||||
Checkbox::make('inline')
|
||||
->live(debounce: 500)
|
||||
->label(trans('admin/webhook.discord_embed.inline_field')),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
public static function sendHelpBanner(): void
|
||||
{
|
||||
AlertBanner::make('discord_webhook_help')
|
||||
->title(trans('admin/webhook.help'))
|
||||
->body(trans('admin/webhook.help_text'))
|
||||
->icon('tabler-question-mark')
|
||||
->info()
|
||||
->send();
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
public static function getDefaultPages(): array
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListWebhookConfigurations::route('/'),
|
||||
|
||||
@@ -3,27 +3,17 @@
|
||||
namespace App\Filament\Admin\Resources\WebhookResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\WebhookResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use App\Enums\WebhookType;
|
||||
|
||||
class CreateWebhookConfiguration extends CreateRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = WebhookResource::class;
|
||||
|
||||
protected static bool $canCreateAnother = false;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->getCancelFormAction()->formId('form'),
|
||||
$this->getCreateFormAction()->formId('form'),
|
||||
];
|
||||
}
|
||||
@@ -32,35 +22,4 @@ class CreateWebhookConfiguration extends CreateRecord
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeCreate(array $data): array
|
||||
{
|
||||
if (($data['type'] ?? null) === WebhookType::Discord->value) {
|
||||
$embeds = data_get($data, 'embeds', []);
|
||||
|
||||
foreach ($embeds as &$embed) {
|
||||
$embed['color'] = hexdec(str_replace('#', '', data_get($embed, 'color')));
|
||||
$embed = collect($embed)->filter(fn ($key) => is_array($key) ? array_filter($key, fn ($arr_key) => !empty($arr_key)) : !empty($key))->all();
|
||||
}
|
||||
|
||||
$flags = collect($data['flags'] ?? [])->reduce(fn ($carry, $bit) => $carry | $bit, 0);
|
||||
|
||||
$tmp = collect([
|
||||
'username' => data_get($data, 'username'),
|
||||
'avatar_url' => data_get($data, 'avatar_url'),
|
||||
'content' => data_get($data, 'content'),
|
||||
'image' => data_get($data, 'image'),
|
||||
'thumbnail' => data_get($data, 'thumbnail'),
|
||||
'embeds' => $embeds,
|
||||
'thread_name' => data_get($data, 'thread_name'),
|
||||
'flags' => $flags,
|
||||
'allowed_mentions' => data_get($data, 'allowed_mentions', []),
|
||||
])->filter(fn ($key) => !empty($key))->all();
|
||||
|
||||
unset($data['username'], $data['avatar_url'], $data['content'], $data['image'], $data['thumbnail'], $data['embeds'], $data['thread_name'], $data['flags'], $data['allowed_mentions']);
|
||||
$data['payload'] = $tmp;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,33 +3,17 @@
|
||||
namespace App\Filament\Admin\Resources\WebhookResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\WebhookResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use App\Models\WebhookConfiguration;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
use App\Enums\WebhookType;
|
||||
|
||||
class EditWebhookConfiguration extends EditRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = WebhookResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make(),
|
||||
Action::make('test_now')
|
||||
->label(trans('admin/webhook.test_now'))
|
||||
->color('primary')
|
||||
->disabled(fn (WebhookConfiguration $webhookConfiguration) => count($webhookConfiguration->events) === 0)
|
||||
->action(fn (WebhookConfiguration $webhookConfiguration) => $webhookConfiguration->run())
|
||||
->tooltip(trans('admin/webhook.test_now_help')),
|
||||
$this->getSaveFormAction()->formId('form'),
|
||||
];
|
||||
}
|
||||
@@ -38,89 +22,4 @@ class EditWebhookConfiguration extends EditRecord
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeSave(array $data): array
|
||||
{
|
||||
if (($data['type'] ?? null) === WebhookType::Discord->value) {
|
||||
$embeds = data_get($data, 'embeds', []);
|
||||
|
||||
foreach ($embeds as &$embed) {
|
||||
$embed['color'] = hexdec(str_replace('#', '', data_get($embed, 'color')));
|
||||
$embed = collect($embed)->filter(fn ($key) => is_array($key) ? array_filter($key, fn ($arr_key) => !empty($arr_key)) : !empty($key))->all();
|
||||
}
|
||||
|
||||
$flags = collect($data['flags'] ?? [])->reduce(fn ($carry, $bit) => $carry | $bit, 0);
|
||||
|
||||
$tmp = collect([
|
||||
'username' => data_get($data, 'username'),
|
||||
'avatar_url' => data_get($data, 'avatar_url'),
|
||||
'content' => data_get($data, 'content'),
|
||||
'image' => data_get($data, 'image'),
|
||||
'thumbnail' => data_get($data, 'thumbnail'),
|
||||
'embeds' => $embeds,
|
||||
'thread_name' => data_get($data, 'thread_name'),
|
||||
'flags' => $flags,
|
||||
'allowed_mentions' => data_get($data, 'allowed_mentions', []),
|
||||
])->filter(fn ($key) => !empty($key))->all();
|
||||
|
||||
unset($data['username'], $data['avatar_url'], $data['content'], $data['image'], $data['thumbnail'], $data['embeds'], $data['thread_name'], $data['flags'], $data['allowed_mentions']);
|
||||
|
||||
$data['payload'] = $tmp;
|
||||
}
|
||||
|
||||
if (($data['type'] ?? null) === WebhookType::Regular->value && isset($data['headers']) && is_array($data['headers'])) {
|
||||
$newHeaders = [];
|
||||
foreach ($data['headers'] as $key => $value) {
|
||||
$newKey = str_replace(' ', '-', $key);
|
||||
$newHeaders[$newKey] = $value;
|
||||
}
|
||||
$data['headers'] = $newHeaders;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeFill(array $data): array
|
||||
{
|
||||
if (($data['type'] ?? null) === WebhookType::Discord->value) {
|
||||
$embeds = data_get($data, 'payload.embeds', []);
|
||||
|
||||
foreach ($embeds as &$embed) {
|
||||
$embed['color'] = '#' . dechex(data_get($embed, 'color'));
|
||||
$embed = collect($embed)->filter(fn ($key) => is_array($key) ? array_filter($key, fn ($arr_key) => !empty($arr_key)) : !empty($key))->all();
|
||||
}
|
||||
|
||||
$flags = data_get($data, 'payload.flags');
|
||||
$flags = collect(range(0, PHP_INT_SIZE * 8 - 1))
|
||||
->filter(fn ($i) => ($flags & (1 << $i)) !== 0)
|
||||
->map(fn ($i) => 1 << $i)
|
||||
->values();
|
||||
|
||||
$tmp = collect([
|
||||
'username' => data_get($data, 'payload.username'),
|
||||
'avatar_url' => data_get($data, 'payload.avatar_url'),
|
||||
'content' => data_get($data, 'payload.content'),
|
||||
'image' => data_get($data, 'payload.image'),
|
||||
'thumbnail' => data_get($data, 'payload.thumbnail'),
|
||||
'embeds' => $embeds,
|
||||
'thread_name' => data_get($data, 'payload.thread_name'),
|
||||
'flags' => $flags,
|
||||
'allowed_mentions' => data_get($data, 'payload.allowed_mentions'),
|
||||
])->filter(fn ($key) => !empty($key))->all();
|
||||
|
||||
unset($data['payload'], $data['created_at'], $data['updated_at'], $data['deleted_at']);
|
||||
$data = array_merge($data, $tmp);
|
||||
}
|
||||
|
||||
if (($data['type'] ?? null) === WebhookType::Regular->value) {
|
||||
$data['headers'] = $data['headers'] ?? [];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function afterSave(): void
|
||||
{
|
||||
$this->dispatch('refresh-widget');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,14 @@ namespace App\Filament\Admin\Resources\WebhookResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\WebhookResource;
|
||||
use App\Models\WebhookConfiguration;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListWebhookConfigurations extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = WebhookResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make()
|
||||
|
||||
@@ -3,22 +3,14 @@
|
||||
namespace App\Filament\Admin\Resources\WebhookResource\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\WebhookResource;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewWebhookConfiguration extends ViewRecord
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = WebhookResource::class;
|
||||
|
||||
/** @return array<Action|ActionGroup> */
|
||||
protected function getDefaultHeaderActions(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class CanaryWidget extends FormWidget
|
||||
class CanaryWidget extends Widget
|
||||
{
|
||||
protected static string $view = 'filament.admin.widgets.canary-widget';
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected static ?int $sort = 1;
|
||||
|
||||
public static function canView(): bool
|
||||
@@ -16,28 +18,15 @@ class CanaryWidget extends FormWidget
|
||||
return config('app.version') === 'canary';
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
public function getViewData(): array
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(trans('admin/dashboard.sections.intro-developers.heading'))
|
||||
->icon('tabler-code')
|
||||
->iconColor('primary')
|
||||
->collapsible()
|
||||
->collapsed()
|
||||
->persistCollapsed()
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-developers.content')),
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-developers.extra_note')),
|
||||
])
|
||||
->headerActions([
|
||||
Action::make('issues')
|
||||
->label(trans('admin/dashboard.sections.intro-developers.button_issues'))
|
||||
->icon('tabler-brand-github')
|
||||
->url('https://github.com/pelican-dev/panel/issues', true),
|
||||
]),
|
||||
]);
|
||||
return [
|
||||
'actions' => [
|
||||
CreateAction::make()
|
||||
->label(trans('admin/dashboard.sections.intro-developers.button_issues'))
|
||||
->icon('tabler-brand-github')
|
||||
->url('https://github.com/pelican-dev/panel/issues', true),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Models\WebhookConfiguration;
|
||||
use Filament\Widgets\Widget;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class DiscordPreview extends Widget
|
||||
{
|
||||
protected static string $view = 'filament.admin.widgets.discord-preview';
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected $listeners = [
|
||||
'refresh-widget' => '$refresh',
|
||||
];
|
||||
|
||||
protected static bool $isDiscovered = false; // Without this its shown on every Admin Pages
|
||||
|
||||
protected int|string|array $columnSpan = 1;
|
||||
|
||||
public ?WebhookConfiguration $record = null;
|
||||
|
||||
/** @var string|array<string, mixed>|null */
|
||||
public string|array|null $payload = null;
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* link: callable,
|
||||
* content: mixed,
|
||||
* sender: array{name: string, avatar: string},
|
||||
* embeds: array<int, mixed>,
|
||||
* getTime: mixed
|
||||
* }
|
||||
*/
|
||||
public function getViewData(): array
|
||||
{
|
||||
if (!$this->record || !$this->record->payload) {
|
||||
return [
|
||||
'link' => fn ($href, $child) => $href ? "<a href=\"$href\" target=\"_blank\" class=\"link\">$child</a>" : $child,
|
||||
'content' => null,
|
||||
'sender' => [
|
||||
'name' => 'Pelican',
|
||||
'avatar' => 'https://raw.githubusercontent.com/pelican-dev/panel/refs/heads/main/public/pelican.ico',
|
||||
],
|
||||
'embeds' => [],
|
||||
'getTime' => 'Today at ' . Carbon::now()->format('h:i A'),
|
||||
];
|
||||
}
|
||||
|
||||
$data = $this->getWebhookSampleData();
|
||||
|
||||
if (is_string($this->record->payload)) {
|
||||
$payload = $this->replaceVarsInStringPayload($this->record->payload, $data);
|
||||
} else {
|
||||
$payload = $this->replaceVarsInArrayPayload($this->record->payload, $data);
|
||||
}
|
||||
|
||||
$embeds = data_get($payload, 'embeds', []);
|
||||
foreach ($embeds as &$embed) {
|
||||
if (data_get($embed, 'has_timestamp')) {
|
||||
unset($embed['has_timestamp']);
|
||||
$embed['timestamp'] = 'Today at ' . Carbon::now()->format('h:i A');
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'link' => fn ($href, $child) => $href ? sprintf('<a href="%s" target="_blank" class="link">%s</a>', $href, $child) : $child,
|
||||
'content' => data_get($payload, 'content'),
|
||||
'sender' => [
|
||||
'name' => data_get($payload, 'username', 'Pelican'),
|
||||
'avatar' => data_get($payload, 'avatar_url', 'https://raw.githubusercontent.com/pelican-dev/panel/refs/heads/main/public/pelican.ico'),
|
||||
],
|
||||
'embeds' => $embeds,
|
||||
'getTime' => 'Today at ' . Carbon::now()->format('h:i A'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function replaceVarsInStringPayload(?string $payload, array $data): ?string
|
||||
{
|
||||
if ($payload === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_replace_callback('/{{\s*([\w\.]+)\s*}}/', fn ($m) => data_get($data, $m[1], $m[0]),
|
||||
$payload
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|null $payload
|
||||
* @param array<string, mixed> $data
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function replaceVarsInArrayPayload(?array $payload, array $data): ?array
|
||||
{
|
||||
if ($payload === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($payload as $key => $value) {
|
||||
if (is_string($value)) {
|
||||
$payload[$key] = $this->replaceVarsInStringPayload($value, $data);
|
||||
} elseif (is_array($value)) {
|
||||
$payload[$key] = $this->replaceVarsInArrayPayload($value, $data);
|
||||
}
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getWebhookSampleData(): array
|
||||
{
|
||||
return [
|
||||
'event' => 'updated: server',
|
||||
'id' => 2,
|
||||
'external_id' => 10,
|
||||
'uuid' => '651fgbc1-dee6-4250-814e-10slda13f1e',
|
||||
'uuid_short' => '651fgbc1',
|
||||
'node_id' => 1,
|
||||
'name' => 'Example Server',
|
||||
'description' => 'This is an example server description.',
|
||||
'status' => 'running',
|
||||
'skip_scripts' => false,
|
||||
'owner_id' => 1,
|
||||
'memory' => 512,
|
||||
'swap' => 128,
|
||||
'disk' => 10240,
|
||||
'io' => 500,
|
||||
'cpu' => 500,
|
||||
'threads' => '1, 3, 5',
|
||||
'oom_killer' => false,
|
||||
'allocation_id' => 4,
|
||||
'egg_id' => 2,
|
||||
'startup' => 'This is a example startup command.',
|
||||
'image' => 'Image here',
|
||||
'allocation_limit' => 5,
|
||||
'database_limit' => 1,
|
||||
'backup_limit' => 3,
|
||||
'created_at' => '2025-03-17T15:20:32.000000Z',
|
||||
'updated_at' => '2025-05-12T17:53:12.000000Z',
|
||||
'installed_at' => '2025-04-27T21:06:01.000000Z',
|
||||
'docker_labels' => [],
|
||||
'allocation' => [
|
||||
'id' => 4,
|
||||
'node_id' => 1,
|
||||
'ip' => '192.168.0.3',
|
||||
'ip_alias' => null,
|
||||
'port' => 25567,
|
||||
'server_id' => 2,
|
||||
'notes' => null,
|
||||
'created_at' => '2025-03-17T15:20:09.000000Z',
|
||||
'updated_at' => '2025-03-17T15:20:32.000000Z',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
abstract class FormWidget extends Widget implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected static string $view = 'filament.admin.widgets.form-widget';
|
||||
}
|
||||
@@ -2,34 +2,26 @@
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class HelpWidget extends FormWidget
|
||||
class HelpWidget extends Widget
|
||||
{
|
||||
protected static string $view = 'filament.admin.widgets.help-widget';
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected static ?int $sort = 4;
|
||||
|
||||
public function form(Form $form): Form
|
||||
public function getViewData(): array
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(trans('admin/dashboard.sections.intro-help.heading'))
|
||||
->icon('tabler-question-mark')
|
||||
->iconColor('info')
|
||||
->collapsible()
|
||||
->persistCollapsed()
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-help.content')),
|
||||
])
|
||||
->headerActions([
|
||||
Action::make('docs')
|
||||
->label(trans('admin/dashboard.sections.intro-help.button_docs'))
|
||||
->icon('tabler-speedboat')
|
||||
->url('https://pelican.dev/docs', true),
|
||||
]),
|
||||
]);
|
||||
return [
|
||||
'actions' => [
|
||||
CreateAction::make()
|
||||
->label(trans('admin/dashboard.sections.intro-help.button_docs'))
|
||||
->icon('tabler-speedboat')
|
||||
->url('https://pelican.dev/docs', true),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Filament\Admin\Resources\NodeResource\Pages\CreateNode;
|
||||
use App\Models\Node;
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class NoNodesWidget extends FormWidget
|
||||
class NoNodesWidget extends Widget
|
||||
{
|
||||
protected static string $view = 'filament.admin.widgets.no-nodes-widget';
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected static ?int $sort = 2;
|
||||
|
||||
public static function canView(): bool
|
||||
@@ -18,25 +20,15 @@ class NoNodesWidget extends FormWidget
|
||||
return Node::count() <= 0;
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
public function getViewData(): array
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(trans('admin/dashboard.sections.intro-first-node.heading'))
|
||||
return [
|
||||
'actions' => [
|
||||
CreateAction::make()
|
||||
->label(trans('admin/dashboard.sections.intro-first-node.button_label'))
|
||||
->icon('tabler-server-2')
|
||||
->iconColor('primary')
|
||||
->collapsible()
|
||||
->persistCollapsed()
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-first-node.content')),
|
||||
])
|
||||
->headerActions([
|
||||
Action::make('create-node')
|
||||
->label(trans('admin/dashboard.sections.intro-first-node.button_label'))
|
||||
->icon('tabler-server-2')
|
||||
->url(CreateNode::getUrl()),
|
||||
]),
|
||||
]);
|
||||
->url(CreateNode::getUrl()),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,37 +2,27 @@
|
||||
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class SupportWidget extends FormWidget
|
||||
class SupportWidget extends Widget
|
||||
{
|
||||
protected static string $view = 'filament.admin.widgets.support-widget';
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected static ?int $sort = 3;
|
||||
|
||||
public function form(Form $form): Form
|
||||
public function getViewData(): array
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(trans('admin/dashboard.sections.intro-support.heading'))
|
||||
->icon('tabler-heart-filled')
|
||||
->iconColor('danger')
|
||||
->collapsible()
|
||||
->persistCollapsed()
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-support.content')),
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-support.extra_note')),
|
||||
])
|
||||
->headerActions([
|
||||
Action::make('donate')
|
||||
->label(trans('admin/dashboard.sections.intro-support.button_donate'))
|
||||
->icon('tabler-cash')
|
||||
->url('https://pelican.dev/donate', true)
|
||||
->color('success'),
|
||||
]),
|
||||
]);
|
||||
return [
|
||||
'actions' => [
|
||||
CreateAction::make()
|
||||
->label(trans('admin/dashboard.sections.intro-support.button_donate'))
|
||||
->icon('tabler-cash')
|
||||
->url('https://pelican.dev/donate', true)
|
||||
->color('success'),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
namespace App\Filament\Admin\Widgets;
|
||||
|
||||
use App\Services\Helpers\SoftwareVersionService;
|
||||
use Filament\Forms\Components\Actions\Action;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class UpdateWidget extends FormWidget
|
||||
class UpdateWidget extends Widget
|
||||
{
|
||||
protected static string $view = 'filament.admin.widgets.update-widget';
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected static ?int $sort = 0;
|
||||
|
||||
private SoftwareVersionService $softwareVersionService;
|
||||
@@ -19,34 +21,19 @@ class UpdateWidget extends FormWidget
|
||||
$this->softwareVersionService = $softwareVersionService;
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
public function getViewData(): array
|
||||
{
|
||||
$isLatest = $this->softwareVersionService->isLatestPanel();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
$isLatest
|
||||
? Section::make(trans('admin/dashboard.sections.intro-no-update.heading'))
|
||||
->icon('tabler-checkbox')
|
||||
->iconColor('success')
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-no-update.content', ['version' => $this->softwareVersionService->currentPanelVersion()])),
|
||||
])
|
||||
: Section::make(trans('admin/dashboard.sections.intro-update-available.heading'))
|
||||
->icon('tabler-info-circle')
|
||||
->iconColor('warning')
|
||||
->schema([
|
||||
Placeholder::make('')
|
||||
->content(trans('admin/dashboard.sections.intro-update-available.content', ['latestVersion' => $this->softwareVersionService->latestPanelVersion()])),
|
||||
])
|
||||
->headerActions([
|
||||
Action::make('update')
|
||||
->label(trans('admin/dashboard.sections.intro-update-available.heading'))
|
||||
->icon('tabler-clipboard-text')
|
||||
->url('https://pelican.dev/docs/panel/update', true)
|
||||
->color('warning'),
|
||||
]),
|
||||
]);
|
||||
return [
|
||||
'version' => $this->softwareVersionService->currentPanelVersion(),
|
||||
'latestVersion' => $this->softwareVersionService->latestPanelVersion(),
|
||||
'isLatest' => $this->softwareVersionService->isLatestPanel(),
|
||||
'actions' => [
|
||||
CreateAction::make()
|
||||
->label(trans('admin/dashboard.sections.intro-update-available.heading'))
|
||||
->icon('tabler-clipboard-text')
|
||||
->url('https://pelican.dev/docs/panel/update', true)
|
||||
->color('warning'),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,12 @@ use App\Filament\Server\Pages\Console;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonPowerRepository;
|
||||
use App\Traits\Filament\CanCustomizeHeaderActions;
|
||||
use App\Traits\Filament\CanCustomizeHeaderWidgets;
|
||||
use AymanAlhattami\FilamentContextMenu\Columns\ContextMenuTextColumn;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Components\Tab;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Actions\ActionGroup;
|
||||
use Filament\Tables\Columns\Column;
|
||||
use Filament\Tables\Columns\ColumnGroup;
|
||||
use Filament\Tables\Columns\Layout\Stack;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
@@ -29,9 +25,6 @@ use Livewire\Attributes\On;
|
||||
|
||||
class ListServers extends ListRecords
|
||||
{
|
||||
use CanCustomizeHeaderActions;
|
||||
use CanCustomizeHeaderWidgets;
|
||||
|
||||
protected static string $resource = ServerResource::class;
|
||||
|
||||
public const DANGER_THRESHOLD = 0.9;
|
||||
@@ -45,74 +38,121 @@ class ListServers extends ListRecords
|
||||
$this->daemonPowerRepository = new DaemonPowerRepository();
|
||||
}
|
||||
|
||||
/** @return Stack[] */
|
||||
protected function gridColumns(): array
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return [
|
||||
Stack::make([
|
||||
ServerEntryColumn::make('server_entry')
|
||||
->searchable(['name']),
|
||||
]),
|
||||
];
|
||||
}
|
||||
$baseQuery = auth()->user()->accessibleServers();
|
||||
|
||||
/** @return Column[] */
|
||||
protected function tableColumns(): array
|
||||
{
|
||||
return [
|
||||
TextColumn::make('condition')
|
||||
->label('Status')
|
||||
$menuOptions = function (Server $server) {
|
||||
$status = $server->retrieveStatus();
|
||||
|
||||
return [
|
||||
Action::make('start')
|
||||
->color('primary')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->visible(fn () => $status->isStartable())
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'start'])
|
||||
->icon('tabler-player-play-filled'),
|
||||
Action::make('restart')
|
||||
->color('gray')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->visible(fn () => $status->isRestartable())
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'restart'])
|
||||
->icon('tabler-refresh'),
|
||||
Action::make('stop')
|
||||
->color('danger')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn () => $status->isStoppable())
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'stop'])
|
||||
->icon('tabler-player-stop-filled'),
|
||||
Action::make('kill')
|
||||
->color('danger')
|
||||
->tooltip('This can result in data corruption and/or data loss!')
|
||||
->dispatch('powerAction', ['server' => $server, 'action' => 'kill'])
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn () => $status->isKillable())
|
||||
->icon('tabler-alert-square'),
|
||||
];
|
||||
};
|
||||
|
||||
$viewOne = [
|
||||
ContextMenuTextColumn::make('condition')
|
||||
->label('')
|
||||
->default('unknown')
|
||||
->wrap()
|
||||
->badge()
|
||||
->alignCenter()
|
||||
->tooltip(fn (Server $server) => $server->formatResource('uptime', type: ServerResourceType::Time))
|
||||
->icon(fn (Server $server) => $server->condition->getIcon())
|
||||
->color(fn (Server $server) => $server->condition->getColor()),
|
||||
TextColumn::make('name')
|
||||
->label('Server')
|
||||
->description(fn (Server $server) => $server->description)
|
||||
->grow()
|
||||
->searchable(),
|
||||
TextColumn::make('allocation.address')
|
||||
->color(fn (Server $server) => $server->condition->getColor())
|
||||
->contextMenuActions($menuOptions)
|
||||
->enableContextMenu(fn (Server $server) => !$server->isInConflictState()),
|
||||
];
|
||||
|
||||
$viewTwo = [
|
||||
ContextMenuTextColumn::make('name')
|
||||
->label('')
|
||||
->size('md')
|
||||
->searchable()
|
||||
->contextMenuActions($menuOptions)
|
||||
->enableContextMenu(fn (Server $server) => !$server->isInConflictState()),
|
||||
ContextMenuTextColumn::make('allocation.address')
|
||||
->label('')
|
||||
->badge()
|
||||
->visibleFrom('md')
|
||||
->copyable(request()->isSecure())
|
||||
->state(fn (Server $server) => $server->allocation->address ?? 'None'),
|
||||
->contextMenuActions($menuOptions)
|
||||
->enableContextMenu(fn (Server $server) => !$server->isInConflictState()),
|
||||
];
|
||||
|
||||
$viewThree = [
|
||||
TextColumn::make('cpuUsage')
|
||||
->label('Resources')
|
||||
->label('')
|
||||
->icon('tabler-cpu')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('cpu', limit: true, type: ServerResourceType::Percentage, precision: 0))
|
||||
->state(fn (Server $server) => $server->formatResource('cpu_absolute', type: ServerResourceType::Percentage))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'cpu')),
|
||||
TextColumn::make('memoryUsage')
|
||||
->label('')
|
||||
->icon('tabler-device-desktop-analytics')
|
||||
->icon('tabler-memory')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('memory', limit: true))
|
||||
->state(fn (Server $server) => $server->formatResource('memory_bytes'))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'memory')),
|
||||
TextColumn::make('diskUsage')
|
||||
->label('')
|
||||
->icon('tabler-device-sd-card')
|
||||
->icon('tabler-device-floppy')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('disk', limit: true))
|
||||
->state(fn (Server $server) => $server->formatResource('disk_bytes'))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'disk')),
|
||||
];
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
$baseQuery = auth()->user()->accessibleServers();
|
||||
|
||||
$usingGrid = (auth()->user()->getCustomization()['dashboard_layout'] ?? 'grid') === 'grid';
|
||||
|
||||
return $table
|
||||
->paginated(false)
|
||||
->query(fn () => $baseQuery)
|
||||
->poll('15s')
|
||||
->columns($usingGrid ? $this->gridColumns() : $this->tableColumns())
|
||||
->recordUrl(!$usingGrid ? (fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server)) : null)
|
||||
->actions(!$usingGrid ? ActionGroup::make(static::getPowerActions()) : [])
|
||||
->actionsAlignment(Alignment::Center->value)
|
||||
->contentGrid($usingGrid ? ['default' => 1, 'md' => 2] : null)
|
||||
->columns(
|
||||
(auth()->user()->getCustomization()['dashboard_layout'] ?? 'grid') === 'grid'
|
||||
? [
|
||||
Stack::make([
|
||||
ServerEntryColumn::make('server_entry')
|
||||
->searchable(['name']),
|
||||
]),
|
||||
]
|
||||
: [
|
||||
ColumnGroup::make('Status')
|
||||
->label('Status')
|
||||
->columns($viewOne),
|
||||
ColumnGroup::make('Server')
|
||||
->label('Servers')
|
||||
->columns($viewTwo),
|
||||
ColumnGroup::make('Resources')
|
||||
->label('Resources')
|
||||
->columns($viewThree),
|
||||
]
|
||||
)
|
||||
->recordUrl(fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server))
|
||||
->contentGrid([
|
||||
'default' => 1,
|
||||
'md' => 2,
|
||||
])
|
||||
->emptyStateIcon('tabler-brand-docker')
|
||||
->emptyStateDescription('')
|
||||
->emptyStateHeading(fn () => $this->activeTab === 'my' ? 'You don\'t own any servers!' : 'You don\'t have access to any servers!')
|
||||
@@ -155,33 +195,36 @@ class ListServers extends ListRecords
|
||||
];
|
||||
}
|
||||
|
||||
protected function getResourceColor(Server $server, string $resource): ?string
|
||||
public function getResourceColor(Server $server, string $resource): ?string
|
||||
{
|
||||
$current = null;
|
||||
$limit = null;
|
||||
|
||||
switch ($resource) {
|
||||
case 'cpu':
|
||||
$current = $server->retrieveResources()['cpu_absolute'] ?? 0;
|
||||
$current = $server->resources()['cpu_absolute'] ?? 0;
|
||||
$limit = $server->cpu;
|
||||
if ($server->cpu === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'memory':
|
||||
$current = $server->retrieveResources()['memory_bytes'] ?? 0;
|
||||
$current = $server->resources()['memory_bytes'] ?? 0;
|
||||
$limit = $server->memory * 2 ** 20;
|
||||
if ($server->memory === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'disk':
|
||||
$current = $server->retrieveResources()['disk_bytes'] ?? 0;
|
||||
$current = $server->resources()['disk_bytes'] ?? 0;
|
||||
$limit = $server->disk * 2 ** 20;
|
||||
if ($server->disk === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -195,6 +238,7 @@ class ListServers extends ListRecords
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
#[On('powerAction')]
|
||||
@@ -209,8 +253,6 @@ class ListServers extends ListRecords
|
||||
->success()
|
||||
->send();
|
||||
|
||||
cache()->forget("servers.$server->uuid.status");
|
||||
|
||||
$this->redirect(self::getUrl(['activeTab' => $this->activeTab]));
|
||||
} catch (ConnectionException) {
|
||||
Notification::make()
|
||||
@@ -219,42 +261,4 @@ class ListServers extends ListRecords
|
||||
->send();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Action[]|ActionGroup[] */
|
||||
public static function getPowerActions(): array
|
||||
{
|
||||
return [
|
||||
ActionGroup::make([
|
||||
Action::make('start')
|
||||
->color('primary')
|
||||
->icon('tabler-player-play-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isStartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'start']),
|
||||
Action::make('restart')
|
||||
->color('gray')
|
||||
->icon('tabler-reload')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isRestartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'restart']),
|
||||
Action::make('stop')
|
||||
->color('danger')
|
||||
->icon('tabler-player-stop-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isStoppable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'stop']),
|
||||
Action::make('kill')
|
||||
->color('danger')
|
||||
->icon('tabler-alert-square')
|
||||
->tooltip('This can result in data corruption and/or data loss!')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn (Server $server) => !$server->isInConflictState() & $server->retrieveStatus()->isKillable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'kill']),
|
||||
])
|
||||
->icon(fn (Server $server) => $server->condition->getIcon())
|
||||
->color(fn (Server $server) => $server->condition->getColor())
|
||||
->tooltip(fn (Server $server) => $server->condition->getLabel())
|
||||
->iconSize(IconSize::Large),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Filament\Components\Actions;
|
||||
|
||||
use App\Console\Commands\Egg\UpdateEggIndexCommand;
|
||||
use App\Models\Egg;
|
||||
use App\Services\Eggs\Sharing\EggImporterService;
|
||||
use Closure;
|
||||
@@ -10,16 +9,11 @@ use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Tabs;
|
||||
use Filament\Forms\Components\Tabs\Tab;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Forms\Set;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
|
||||
class ImportEggAction extends Action
|
||||
@@ -103,28 +97,7 @@ class ImportEggAction extends Action
|
||||
Tab::make(trans('admin/egg.import.url'))
|
||||
->icon('tabler-world-upload')
|
||||
->schema([
|
||||
Select::make('github')
|
||||
->label(trans('admin/egg.import.github'))
|
||||
->options(cache('eggs.index'))
|
||||
->selectablePlaceholder(false)
|
||||
->searchable()
|
||||
->preload()
|
||||
->live()
|
||||
->hintIcon('tabler-refresh')
|
||||
->hintIconTooltip(trans('admin/egg.import.refresh'))
|
||||
->hintAction(function () {
|
||||
Artisan::call(UpdateEggIndexCommand::class);
|
||||
})
|
||||
->afterStateUpdated(function ($state, Set $set, Get $get) use ($isMultiple) {
|
||||
if ($state) {
|
||||
$urls = $isMultiple ? $get('urls') : [];
|
||||
$urls[Str::uuid()->toString()] = ['url' => $state];
|
||||
$set('urls', $urls);
|
||||
$set('github', null);
|
||||
}
|
||||
}),
|
||||
Repeater::make('urls')
|
||||
->label('')
|
||||
->itemLabel(fn (array $state) => str($state['url'])->afterLast('/egg-')->before('.json')->headline())
|
||||
->hint(trans('admin/egg.import.url_help'))
|
||||
->addActionLabel(trans('admin/egg.import.add_url'))
|
||||
|
||||
@@ -26,7 +26,7 @@ class RotateDatabasePasswordAction extends Action
|
||||
|
||||
$this->icon('tabler-refresh');
|
||||
|
||||
$this->authorize(fn (Database $database) => auth()->user()->can('update', $database));
|
||||
$this->authorize(fn (Database $database) => auth()->user()->can('update database', $database));
|
||||
|
||||
$this->modalHeading(trans('admin/databasehost.rotate_password'));
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Extensions\Captcha\Schemas\Turnstile;
|
||||
namespace App\Filament\Components\Forms\Fields;
|
||||
|
||||
use App\Rules\ValidTurnstileCaptcha;
|
||||
use Filament\Forms\Components\Field;
|
||||
|
||||
class Component extends Field
|
||||
class TurnstileCaptcha extends Field
|
||||
{
|
||||
protected string $viewIdentifier = 'turnstile';
|
||||
|
||||
@@ -18,6 +19,8 @@ class Component extends Field
|
||||
|
||||
$this->required();
|
||||
|
||||
$this->rule(new Rule());
|
||||
$this->after(function (TurnstileCaptcha $component) {
|
||||
$component->rule(new ValidTurnstileCaptcha());
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user