diff --git a/.env.example b/.env.example index 5bfd4a9..37e3570 100755 --- a/.env.example +++ b/.env.example @@ -14,8 +14,8 @@ DB_DATABASE=vito DB_USERNAME=root DB_PASSWORD= -BROADCAST_DRIVER=log -CACHE_DRIVER=file +BROADCAST_DRIVER=null +CACHE_DRIVER=redis FILESYSTEM_DRIVER=local QUEUE_CONNECTION=sync SESSION_DRIVER=database diff --git a/app/Actions/Database/CreateDatabaseUser.php b/app/Actions/Database/CreateDatabaseUser.php index f8416b2..0521c45 100755 --- a/app/Actions/Database/CreateDatabaseUser.php +++ b/app/Actions/Database/CreateDatabaseUser.php @@ -13,7 +13,7 @@ class CreateDatabaseUser /** * @throws ValidationException */ - public function create(Server $server, array $input): DatabaseUser + public function create(Server $server, array $input, array $links = []): DatabaseUser { $this->validate($server, $input); @@ -22,6 +22,7 @@ public function create(Server $server, array $input): DatabaseUser 'username' => $input['username'], 'password' => $input['password'], 'host' => isset($input['remote']) && $input['remote'] ? $input['host'] : 'localhost', + 'databases' => $links, ]); $databaseUser->save(); $databaseUser->createOnServer(); diff --git a/app/Actions/FirewallRule/CreateRule.php b/app/Actions/FirewallRule/CreateRule.php index 06e708c..26f7691 100755 --- a/app/Actions/FirewallRule/CreateRule.php +++ b/app/Actions/FirewallRule/CreateRule.php @@ -21,7 +21,7 @@ public function create(Server $server, array $input): FirewallRule 'protocol' => $input['protocol'], 'port' => $input['port'], 'source' => $input['source'], - 'mask' => $input['mask'], + 'mask' => $input['mask'] ?? null, 'status' => FirewallRuleStatus::CREATING, ]); $rule->save(); @@ -49,14 +49,12 @@ private function validate(Server $server, array $input): void 'numeric', 'min:1', 'max:65535', - Rule::unique('firewall_rules', 'port')->where('server_id', $server->id), ], 'source' => [ 'required', 'ip', ], 'mask' => [ - 'required', 'numeric', ], ])->validateWithBag('createRule'); diff --git a/app/Actions/PHP/UninstallPHP.php b/app/Actions/PHP/UninstallPHP.php index b655791..2596545 100755 --- a/app/Actions/PHP/UninstallPHP.php +++ b/app/Actions/PHP/UninstallPHP.php @@ -3,6 +3,7 @@ namespace App\Actions\PHP; use App\Models\Server; +use App\Models\Service; use Illuminate\Validation\ValidationException; class UninstallPHP @@ -11,6 +12,7 @@ public function uninstall(Server $server, string $version): void { $this->validate($server, $version); + /** @var Service $php */ $php = $server->services()->where('type', 'php')->where('version', $version)->first(); $php->uninstall(); diff --git a/app/Actions/PHP/UpdatePHPIni.php b/app/Actions/PHP/UpdatePHPIni.php index 09849f2..41f2ceb 100755 --- a/app/Actions/PHP/UpdatePHPIni.php +++ b/app/Actions/PHP/UpdatePHPIni.php @@ -29,6 +29,8 @@ public function update(Service $service, string $ini): void 'ini' => __("Couldn't update php.ini file!"), ]); } + + $service->restart(); } private function deleteTempFile(string $name): void diff --git a/app/Actions/Server/CreateServer.php b/app/Actions/Server/CreateServer.php index 082b672..2bf6455 100755 --- a/app/Actions/Server/CreateServer.php +++ b/app/Actions/Server/CreateServer.php @@ -36,8 +36,8 @@ public function create(User $creator, array $input): Server 'provider' => $input['provider'], 'authentication' => [ 'user' => config('core.ssh_user'), - 'pass' => Str::random(10), - 'root_pass' => Str::random(10), + 'pass' => Str::random(15), + 'root_pass' => Str::random(15), ], 'progress' => 0, 'progress_step' => 'Initializing', @@ -77,8 +77,7 @@ public function create(User $creator, array $input): Server $server->progress_step = __('Installation will begin in 3 minutes!'); $server->save(); dispatch(new ContinueInstallation($server)) - ->delay(now()->addMinutes(3)) - ->onQueue('default'); + ->delay(now()->addMinutes(2)); } DB::commit(); diff --git a/app/Actions/Site/UpdateEnv.php b/app/Actions/Site/UpdateEnv.php index a41dcee..9df9566 100755 --- a/app/Actions/Site/UpdateEnv.php +++ b/app/Actions/Site/UpdateEnv.php @@ -6,7 +6,7 @@ class UpdateEnv { - public function handle(Site $site, array $input): void + public function update(Site $site, array $input): void { $typeData = $site->type_data; $typeData['env'] = $input['env']; diff --git a/app/Actions/SourceControl/ConnectSourceControl.php b/app/Actions/SourceControl/ConnectSourceControl.php index 4e7a881..875ba44 100644 --- a/app/Actions/SourceControl/ConnectSourceControl.php +++ b/app/Actions/SourceControl/ConnectSourceControl.php @@ -3,33 +3,48 @@ namespace App\Actions\SourceControl; use App\Models\SourceControl; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; class ConnectSourceControl { - public function connect(string $provider, array $input): void + public function connect(array $input): void { - $sourceControl = SourceControl::query() - ->where('provider', $provider) - ->first(); - if (! $sourceControl) { - $sourceControl = new SourceControl([ - 'provider' => $provider, - ]); - } + $this->validate($input); + $sourceControl = new SourceControl([ + 'provider' => $input['provider'], + 'profile' => $input['name'], + 'access_token' => $input['token'] + ]); - if (! $input['token']) { - $sourceControl->delete(); - - return; - } - - $sourceControl->access_token = $input['token']; if (! $sourceControl->provider()->connect()) { throw ValidationException::withMessages([ - 'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $provider]), + 'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $sourceControl->provider] + ), ]); } + $sourceControl->save(); } + + /** + * @throws ValidationException + */ + private function validate(array $input): void + { + $rules = [ + 'provider' => [ + 'required', + Rule::in(\App\Enums\SourceControl::getValues()) + ], + 'name' => [ + 'required', + ], + 'token' => [ + 'required' + ] + ]; + Validator::make($input, $rules)->validate(); + } } diff --git a/app/Contracts/Firewall.php b/app/Contracts/Firewall.php index e711160..150bf87 100755 --- a/app/Contracts/Firewall.php +++ b/app/Contracts/Firewall.php @@ -4,7 +4,7 @@ interface Firewall { - public function addRule(string $type, string $protocol, int $port, string $source, string $mask): void; + public function addRule(string $type, string $protocol, int $port, string $source, ?string $mask): void; - public function removeRule(string $type, string $protocol, int $port, string $source, string $mask): void; + public function removeRule(string $type, string $protocol, int $port, string $source, ?string $mask): void; } diff --git a/app/Contracts/SSHCommand.php b/app/Contracts/SSHCommand.php index 8766305..f6c36a6 100755 --- a/app/Contracts/SSHCommand.php +++ b/app/Contracts/SSHCommand.php @@ -4,7 +4,7 @@ interface SSHCommand { - public function file(string $os): string; + public function file(): string; - public function content(string $os): string; + public function content(): string; } diff --git a/app/Contracts/SourceControlProvider.php b/app/Contracts/SourceControlProvider.php index 62608a1..a2fd825 100755 --- a/app/Contracts/SourceControlProvider.php +++ b/app/Contracts/SourceControlProvider.php @@ -8,11 +8,13 @@ public function connect(): bool; public function getRepo(string $repo = null): mixed; - public function fullRepoUrl(string $repo): string; + public function fullRepoUrl(string $repo, string $key): string; public function deployHook(string $repo, array $events, string $secret): array; public function destroyHook(string $repo, string $hookId): void; public function getLastCommit(string $repo, string $branch): ?array; + + public function deployKey(string $title, string $repo, string $key): void; } diff --git a/app/Events/Broadcast.php b/app/Events/Broadcast.php index 987bc59..31541cd 100644 --- a/app/Events/Broadcast.php +++ b/app/Events/Broadcast.php @@ -3,23 +3,14 @@ namespace App\Events; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PrivateChannel; -use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class Broadcast implements ShouldBroadcast +class Broadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public function __construct(public string $type, public array $data) { } - - public function broadcastOn(): array - { - return [ - new PrivateChannel('app'), - ]; - } } diff --git a/app/Exceptions/FailedToDeployGitKey.php b/app/Exceptions/FailedToDeployGitKey.php new file mode 100644 index 0000000..930bb28 --- /dev/null +++ b/app/Exceptions/FailedToDeployGitKey.php @@ -0,0 +1,10 @@ +connection = null; $this->log = null; @@ -38,8 +42,9 @@ public function init(Server $server, string $asUser = null, bool $defaultKeys = $this->user = $asUser; $this->asUser = $asUser; } - $this->publicKey = $this->server->sshKey($defaultKeys)['public_key_path']; - $this->privateKey = $this->server->sshKey($defaultKeys)['private_key_path']; + $this->privateKey = PublicKeyLoader::loadPrivateKey( + file_get_contents($this->server->sshKey()['private_key_path']) + ); return $this; } @@ -57,29 +62,30 @@ public function setLog(string $logType, $siteId = null): void /** * @throws Throwable */ - public function connect(): void + public function connect(bool $sftp = false): void { - $defaultTimeout = ini_get('default_socket_timeout'); - ini_set('default_socket_timeout', 7); - try { - if (! ($this->connection = ssh2_connect($this->server->ip, $this->server->port))) { - throw new SSHConnectionError('Cannot connect to the server'); + if ($sftp) { + $this->connection = new SFTP($this->server->ip, $this->server->port); + } else { + $this->connection = new SSH2($this->server->ip, $this->server->port); } - if (! ssh2_auth_pubkey_file($this->connection, $this->user, $this->publicKey, $this->privateKey)) { - throw new SSHAuthenticationError('Authentication failed'); + $login = $this->connection->login($this->user, $this->privateKey); + + if (! $login) { + throw new SSHAuthenticationError("Error authenticating"); } + + Log::info("Login status", [ + 'status' => $login + ]); } catch (Throwable $e) { - ini_set('default_socket_timeout', $defaultTimeout); - if ($this->server->status == 'ready') { - $this->server->status = 'disconnected'; - $this->server->save(); - } + Log::error("Error connecting", [ + "msg" => $e->getMessage() + ]); throw $e; } - - ini_set('default_socket_timeout', $defaultTimeout); } /** @@ -114,31 +120,17 @@ public function exec(string|array|SSHCommand $commands, string $log = '', int $s */ public function upload(string $local, string $remote): void { + $this->log = null; + + Log::info("Starting to upload"); if (! $this->connection) { - $this->connect(); + $this->connect(true); } - - $sftp = @ssh2_sftp($this->connection); - if (! $sftp) { - throw new Exception('Could not initialize SFTP'); - } - - $stream = @fopen("ssh2.sftp://$sftp$remote", 'w'); - - if (! $stream) { - throw new Exception("Could not open file: $remote"); - } - - $data_to_send = @file_get_contents($local); - if ($data_to_send === false) { - throw new Exception("Could not open local file: $local."); - } - - if (@fwrite($stream, $data_to_send) === false) { - throw new Exception("Could not send data from file: $local."); - } - - @fclose($stream); + Log::info("Uploading"); + $uploaded = $this->connection->put($remote, $local, SFTP::SOURCE_LOCAL_FILE); + Log::info("Upload finished", [ + 'status' => $uploaded + ]); } /** @@ -152,31 +144,30 @@ protected function executeCommand(string|SSHCommand $command): string $commandContent = $command; } + Log::info("command", [ + "asUser" => $this->asUser, + "content" => $commandContent + ]); + if ($this->asUser) { $commandContent = 'sudo su - '.$this->asUser.' -c '.'"'.addslashes($commandContent).'"'; } - if (! ($stream = ssh2_exec($this->connection, $commandContent, 'vt102', [], 100, 30))) { - throw new Exception('SSH command failed'); - } + Log::info("Running command", [ + "cmd" => $commandContent + ]); - $data = ''; - try { - stream_set_blocking($stream, true); - while ($buf = fread($stream, 1024)) { - $data .= $buf; - $this->log?->write($buf); - } - fclose($stream); - } catch (Throwable) { - $data = 'Error reading data'; - } + $output = $this->connection->exec($commandContent); - if (Str::contains($data, 'VITO_SSH_ERROR')) { + Log::info("Command executed"); + + $this->log?->write($output); + + if (Str::contains($output, 'VITO_SSH_ERROR')) { throw new Exception('SSH command failed with an error'); } - return $data; + return $output; } /** @@ -185,11 +176,7 @@ protected function executeCommand(string|SSHCommand $command): string public function disconnect(): void { if ($this->connection) { - try { - ssh2_disconnect($this->connection); - } catch (Exception) { - // - } + $this->connection->disconnect(); $this->connection = null; } } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index c34cdcf..52e5ffb 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -2,6 +2,7 @@ namespace App\Http; +use App\Http\Middleware\ServerIsReadyMiddleware; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel @@ -63,5 +64,6 @@ class Kernel extends HttpKernel 'signed' => \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'server-is-ready' => ServerIsReadyMiddleware::class ]; } diff --git a/app/Http/Livewire/Application/Env.php b/app/Http/Livewire/Application/Env.php new file mode 100644 index 0000000..dad9442 --- /dev/null +++ b/app/Http/Livewire/Application/Env.php @@ -0,0 +1,37 @@ +env = $this->site->env; + } + + public function save(): void + { + app(UpdateEnv::class)->update($this->site, $this->all()); + + session()->flash('status', 'updating-env'); + + $this->emit(Deploy::class, '$refresh'); + } + + public function render(): View + { + return view('livewire.application.env'); + } +} diff --git a/app/Http/Livewire/Broadcast.php b/app/Http/Livewire/Broadcast.php new file mode 100644 index 0000000..f8972f1 --- /dev/null +++ b/app/Http/Livewire/Broadcast.php @@ -0,0 +1,21 @@ +emit('broadcast', $event); + } + + return view('livewire.broadcast'); + } +} diff --git a/app/Http/Livewire/Databases/DatabaseList.php b/app/Http/Livewire/Databases/DatabaseList.php index 5beb528..3ca2d7b 100644 --- a/app/Http/Livewire/Databases/DatabaseList.php +++ b/app/Http/Livewire/Databases/DatabaseList.php @@ -32,10 +32,10 @@ class DatabaseList extends Component public function create(): void { - app(CreateDatabase::class)->create($this->server, $this->all()); + $database = app(CreateDatabase::class)->create($this->server, $this->all()); if ($this->all()['user']) { - app(CreateDatabaseUser::class)->create($this->server, $this->all()); + app(CreateDatabaseUser::class)->create($this->server, $this->all(), [$database->name]); } $this->refreshComponent([]); @@ -45,6 +45,7 @@ public function create(): void public function delete(): void { + /** @var Database $database */ $database = Database::query()->findOrFail($this->deleteId); $database->deleteFromServer(); diff --git a/app/Http/Livewire/Databases/DatabaseUserList.php b/app/Http/Livewire/Databases/DatabaseUserList.php index 763a8de..782c1c0 100644 --- a/app/Http/Livewire/Databases/DatabaseUserList.php +++ b/app/Http/Livewire/Databases/DatabaseUserList.php @@ -43,6 +43,7 @@ public function create(): void public function delete(): void { + /** @var DatabaseUser $databaseUser */ $databaseUser = DatabaseUser::query()->findOrFail($this->deleteId); $databaseUser->deleteFromServer(); @@ -56,6 +57,7 @@ public function delete(): void public function viewPassword(int $id): void { + /** @var DatabaseUser $databaseUser */ $databaseUser = DatabaseUser::query()->findOrFail($id); $this->viewPassword = $databaseUser->password; @@ -65,6 +67,7 @@ public function viewPassword(int $id): void public function showLink(int $id): void { + /** @var DatabaseUser $databaseUser */ $databaseUser = DatabaseUser::query()->findOrFail($id); $this->linkId = $id; @@ -75,6 +78,7 @@ public function showLink(int $id): void public function link(): void { + /** @var DatabaseUser $databaseUser */ $databaseUser = DatabaseUser::query()->findOrFail($this->linkId); app(LinkUser::class)->link($databaseUser, $this->link); diff --git a/app/Http/Livewire/Firewall/CreateFirewallRule.php b/app/Http/Livewire/Firewall/CreateFirewallRule.php index cd4f235..cc9986d 100644 --- a/app/Http/Livewire/Firewall/CreateFirewallRule.php +++ b/app/Http/Livewire/Firewall/CreateFirewallRule.php @@ -22,7 +22,7 @@ class CreateFirewallRule extends Component public string $source = '0.0.0.0'; - public string $mask = '0'; + public string $mask = ''; public function create(): void { diff --git a/app/Http/Livewire/Firewall/FirewallRulesList.php b/app/Http/Livewire/Firewall/FirewallRulesList.php index 080f9ba..59c0ce7 100644 --- a/app/Http/Livewire/Firewall/FirewallRulesList.php +++ b/app/Http/Livewire/Firewall/FirewallRulesList.php @@ -18,6 +18,7 @@ class FirewallRulesList extends Component public function delete(): void { + /** @var FirewallRule $rule */ $rule = FirewallRule::query()->findOrFail($this->deleteId); $rule->removeFromServer(); diff --git a/app/Http/Livewire/Php/InstalledVersions.php b/app/Http/Livewire/Php/InstalledVersions.php index 52aac21..a792781 100644 --- a/app/Http/Livewire/Php/InstalledVersions.php +++ b/app/Http/Livewire/Php/InstalledVersions.php @@ -6,7 +6,7 @@ use App\Actions\PHP\UpdatePHPIni; use App\Models\Server; use App\Models\Service; -use App\SSHCommands\GetPHPIniCommand; +use App\SSHCommands\PHP\GetPHPIniCommand; use App\Traits\RefreshComponentOnBroadcast; use Illuminate\Contracts\View\View; use Livewire\Component; diff --git a/app/Http/Livewire/Servers/ServerStatus.php b/app/Http/Livewire/Servers/ServerStatus.php new file mode 100644 index 0000000..695cb21 --- /dev/null +++ b/app/Http/Livewire/Servers/ServerStatus.php @@ -0,0 +1,20 @@ +url = request()->input('redirect') ?? null; + $this->token = SourceControl::query() ->where('provider', \App\Enums\SourceControl::BITBUCKET) ->first()?->access_token ?? ''; @@ -23,6 +27,10 @@ public function connect(): void app(ConnectSourceControl::class)->connect(\App\Enums\SourceControl::BITBUCKET, $this->all()); session()->flash('status', 'bitbucket-updated'); + + if ($this->url) { + $this->redirect($this->url); + } } public function render(): View diff --git a/app/Http/Livewire/SourceControls/Connect.php b/app/Http/Livewire/SourceControls/Connect.php new file mode 100644 index 0000000..4073fe7 --- /dev/null +++ b/app/Http/Livewire/SourceControls/Connect.php @@ -0,0 +1,38 @@ +connect($this->all()); + + $this->emitTo(SourceControlsList::class, '$refresh'); + + $this->dispatchBrowserEvent('connected', true); + } + + public function render(): View + { + if (request()->query('provider')) { + $this->provider = request()->query('provider'); + } + + return view('livewire.source-controls.connect', [ + 'open' => ! is_null(request()->query('provider')), + ]); + } +} diff --git a/app/Http/Livewire/SourceControls/Github.php b/app/Http/Livewire/SourceControls/Github.php index 6e8949d..1e8b0ab 100644 --- a/app/Http/Livewire/SourceControls/Github.php +++ b/app/Http/Livewire/SourceControls/Github.php @@ -11,8 +11,12 @@ class Github extends Component { public string $token; + public ?string $url; + public function mount(): void { + $this->url = request()->input('redirect') ?? null; + $this->token = SourceControl::query() ->where('provider', \App\Enums\SourceControl::GITHUB) ->first()?->access_token ?? ''; @@ -20,9 +24,13 @@ public function mount(): void public function connect(): void { - app(ConnectSourceControl::class)->connect(\App\Enums\SourceControl::GITHUB, $this->all()); + app(ConnectSourceControl::class)->connect(\App\Enums\SourceControl::GITHUB, array_merge($this->all())); session()->flash('status', 'github-updated'); + + if ($this->url) { + $this->redirect($this->url); + } } public function render(): View diff --git a/app/Http/Livewire/SourceControls/Gitlab.php b/app/Http/Livewire/SourceControls/Gitlab.php index 6a90b72..da05db7 100644 --- a/app/Http/Livewire/SourceControls/Gitlab.php +++ b/app/Http/Livewire/SourceControls/Gitlab.php @@ -11,8 +11,12 @@ class Gitlab extends Component { public string $token; + public ?string $url; + public function mount(): void { + $this->url = request()->input('redirect') ?? null; + $this->token = SourceControl::query() ->where('provider', \App\Enums\SourceControl::GITLAB) ->first()?->access_token ?? ''; @@ -23,6 +27,10 @@ public function connect(): void app(ConnectSourceControl::class)->connect(\App\Enums\SourceControl::GITLAB, $this->all()); session()->flash('status', 'gitlab-updated'); + + if ($this->url) { + $this->redirect($this->url); + } } public function render(): View diff --git a/app/Http/Livewire/SourceControls/SourceControlsList.php b/app/Http/Livewire/SourceControls/SourceControlsList.php new file mode 100644 index 0000000..d739123 --- /dev/null +++ b/app/Http/Livewire/SourceControls/SourceControlsList.php @@ -0,0 +1,37 @@ +findOrFail($this->deleteId); + + $provider->delete(); + + $this->refreshComponent([]); + + $this->dispatchBrowserEvent('confirmed', true); + } + + public function render(): View + { + return view('livewire.source-controls.source-controls-list', [ + 'sourceControls' => SourceControl::query()->latest()->get(), + ]); + } +} diff --git a/app/Http/Middleware/ServerIsReadyMiddleware.php b/app/Http/Middleware/ServerIsReadyMiddleware.php new file mode 100644 index 0000000..c9e6220 --- /dev/null +++ b/app/Http/Middleware/ServerIsReadyMiddleware.php @@ -0,0 +1,22 @@ +route('server'); + + if (! $server->isReady()) { + return redirect()->route('servers.show', ['server' => $server]); + } + + return $next($request); + } +} diff --git a/app/Jobs/CronJob/AddToServer.php b/app/Jobs/CronJob/AddToServer.php index b2a5326..d1e28c4 100644 --- a/app/Jobs/CronJob/AddToServer.php +++ b/app/Jobs/CronJob/AddToServer.php @@ -6,7 +6,7 @@ use App\Events\Broadcast; use App\Jobs\Job; use App\Models\CronJob; -use App\SSHCommands\UpdateCronJobsCommand; +use App\SSHCommands\CronJob\UpdateCronJobsCommand; use Throwable; class AddToServer extends Job diff --git a/app/Jobs/CronJob/RemoveFromServer.php b/app/Jobs/CronJob/RemoveFromServer.php index 88d1fa0..dce0992 100644 --- a/app/Jobs/CronJob/RemoveFromServer.php +++ b/app/Jobs/CronJob/RemoveFromServer.php @@ -5,7 +5,7 @@ use App\Events\Broadcast; use App\Jobs\Job; use App\Models\CronJob; -use App\SSHCommands\UpdateCronJobsCommand; +use App\SSHCommands\CronJob\UpdateCronJobsCommand; use Throwable; class RemoveFromServer extends Job diff --git a/app/Jobs/DatabaseUser/CreateOnServer.php b/app/Jobs/DatabaseUser/CreateOnServer.php index 0c2e669..a76839f 100644 --- a/app/Jobs/DatabaseUser/CreateOnServer.php +++ b/app/Jobs/DatabaseUser/CreateOnServer.php @@ -25,6 +25,11 @@ public function handle(): void ); $this->databaseUser->status = DatabaseUserStatus::READY; $this->databaseUser->save(); + + if (count($this->databaseUser->databases) > 0) { + (new LinkUser($this->databaseUser))->handle(); + } + event( new Broadcast('create-database-user-finished', [ 'id' => $this->databaseUser->id, diff --git a/app/Jobs/Installation/Initialize.php b/app/Jobs/Installation/Initialize.php index f427c30..fadc89a 100755 --- a/app/Jobs/Installation/Initialize.php +++ b/app/Jobs/Installation/Initialize.php @@ -3,8 +3,8 @@ namespace App\Jobs\Installation; use App\Models\Server; -use App\SSHCommands\CreateUserCommand; -use App\SSHCommands\GetPublicKeyCommand; +use App\SSHCommands\System\CreateUserCommand; +use App\SSHCommands\System\GetPublicKeyCommand; use Throwable; class Initialize extends InstallationJob @@ -13,13 +13,10 @@ class Initialize extends InstallationJob protected ?string $asUser; - protected bool $defaultKeys; - - public function __construct(Server $server, string $asUser = null, bool $defaultKeys = false) + public function __construct(Server $server, string $asUser = null) { $this->server = $server->refresh(); $this->asUser = $asUser; - $this->defaultKeys = $defaultKeys; } /** @@ -38,7 +35,7 @@ public function handle(): void protected function authentication(): void { $this->server - ->ssh($this->asUser ?? $this->server->ssh_user, $this->defaultKeys) + ->ssh($this->asUser ?? $this->server->ssh_user) ->exec( new CreateUserCommand( $this->server->authentication['user'], diff --git a/app/Jobs/Installation/InstallCertbot.php b/app/Jobs/Installation/InstallCertbot.php index c4c647e..c9dc3ea 100755 --- a/app/Jobs/Installation/InstallCertbot.php +++ b/app/Jobs/Installation/InstallCertbot.php @@ -3,7 +3,7 @@ namespace App\Jobs\Installation; use App\Models\Server; -use App\SSHCommands\InstallCertbotCommand; +use App\SSHCommands\SSL\InstallCertbotCommand; use Throwable; class InstallCertbot extends InstallationJob diff --git a/app/Jobs/Installation/InstallComposer.php b/app/Jobs/Installation/InstallComposer.php index cb15851..f864559 100755 --- a/app/Jobs/Installation/InstallComposer.php +++ b/app/Jobs/Installation/InstallComposer.php @@ -3,7 +3,7 @@ namespace App\Jobs\Installation; use App\Models\Server; -use App\SSHCommands\InstallComposerCommand; +use App\SSHCommands\PHP\InstallComposerCommand; use Throwable; class InstallComposer extends InstallationJob diff --git a/app/Jobs/Installation/InstallMariadb.php b/app/Jobs/Installation/InstallMariadb.php index 219f576..1ad4955 100755 --- a/app/Jobs/Installation/InstallMariadb.php +++ b/app/Jobs/Installation/InstallMariadb.php @@ -5,8 +5,8 @@ use App\Enums\ServiceStatus; use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\InstallMariadbCommand; -use App\SSHCommands\ServiceStatusCommand; +use App\SSHCommands\Database\InstallMariadbCommand; +use App\SSHCommands\Service\ServiceStatusCommand; use Throwable; class InstallMariadb extends InstallationJob diff --git a/app/Jobs/Installation/InstallMysql.php b/app/Jobs/Installation/InstallMysql.php index 586b28e..4627de0 100755 --- a/app/Jobs/Installation/InstallMysql.php +++ b/app/Jobs/Installation/InstallMysql.php @@ -5,8 +5,8 @@ use App\Enums\ServiceStatus; use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\InstallMysqlCommand; -use App\SSHCommands\ServiceStatusCommand; +use App\SSHCommands\Database\InstallMysqlCommand; +use App\SSHCommands\Service\ServiceStatusCommand; use Throwable; class InstallMysql extends InstallationJob diff --git a/app/Jobs/Installation/InstallNginx.php b/app/Jobs/Installation/InstallNginx.php index 0d3244c..142feef 100755 --- a/app/Jobs/Installation/InstallNginx.php +++ b/app/Jobs/Installation/InstallNginx.php @@ -5,8 +5,8 @@ use App\Enums\ServiceStatus; use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\InstallNginxCommand; -use App\SSHCommands\ServiceStatusCommand; +use App\SSHCommands\Nginx\InstallNginxCommand; +use App\SSHCommands\Service\ServiceStatusCommand; use Throwable; class InstallNginx extends InstallationJob diff --git a/app/Jobs/Installation/InstallNodejs.php b/app/Jobs/Installation/InstallNodejs.php index 9d65895..c30edde 100755 --- a/app/Jobs/Installation/InstallNodejs.php +++ b/app/Jobs/Installation/InstallNodejs.php @@ -3,7 +3,7 @@ namespace App\Jobs\Installation; use App\Models\Server; -use App\SSHCommands\InstallNodejsCommand; +use App\SSHCommands\Installation\InstallNodejsCommand; use Throwable; class InstallNodejs extends InstallationJob diff --git a/app/Jobs/Installation/InstallPHP.php b/app/Jobs/Installation/InstallPHP.php index 1702858..ed9dcff 100755 --- a/app/Jobs/Installation/InstallPHP.php +++ b/app/Jobs/Installation/InstallPHP.php @@ -5,8 +5,8 @@ use App\Enums\ServiceStatus; use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\InstallPHPCommand; -use App\SSHCommands\ServiceStatusCommand; +use App\SSHCommands\PHP\InstallPHPCommand; +use App\SSHCommands\Service\ServiceStatusCommand; use Throwable; class InstallPHP extends InstallationJob diff --git a/app/Jobs/Installation/InstallPHPMyAdmin.php b/app/Jobs/Installation/InstallPHPMyAdmin.php index 9f759d7..9082771 100644 --- a/app/Jobs/Installation/InstallPHPMyAdmin.php +++ b/app/Jobs/Installation/InstallPHPMyAdmin.php @@ -6,8 +6,8 @@ use App\Jobs\Job; use App\Models\FirewallRule; use App\Models\Service; -use App\SSHCommands\CreateNginxPHPMyAdminVHostCommand; -use App\SSHCommands\DownloadPHPMyAdminCommand; +use App\SSHCommands\PHPMyAdmin\CreateNginxPHPMyAdminVHostCommand; +use App\SSHCommands\PHPMyAdmin\DownloadPHPMyAdminCommand; use Illuminate\Support\Facades\File; use Illuminate\Support\Str; use Throwable; @@ -76,7 +76,7 @@ private function downloadSource(): void */ private function setUpVHost(): void { - $vhost = File::get(base_path('system/command-templates/nginx/phpmyadmin-vhost.conf')); + $vhost = File::get(resource_path('commands/webserver/nginx/phpmyadmin-vhost.conf')); $vhost = Str::replace('__php_version__', $this->service->server->defaultService('php')->version, $vhost); $this->service->server->ssh()->exec( new CreateNginxPHPMyAdminVHostCommand($vhost), diff --git a/app/Jobs/Installation/InstallRedis.php b/app/Jobs/Installation/InstallRedis.php index ccec2e7..e70333d 100755 --- a/app/Jobs/Installation/InstallRedis.php +++ b/app/Jobs/Installation/InstallRedis.php @@ -5,8 +5,8 @@ use App\Enums\ServiceStatus; use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\InstallRedisCommand; -use App\SSHCommands\ServiceStatusCommand; +use App\SSHCommands\Installation\InstallRedisCommand; +use App\SSHCommands\Service\ServiceStatusCommand; use Throwable; class InstallRedis extends InstallationJob diff --git a/app/Jobs/Installation/InstallRequirements.php b/app/Jobs/Installation/InstallRequirements.php index bb8f782..7e0a88f 100755 --- a/app/Jobs/Installation/InstallRequirements.php +++ b/app/Jobs/Installation/InstallRequirements.php @@ -3,7 +3,7 @@ namespace App\Jobs\Installation; use App\Models\Server; -use App\SSHCommands\InstallRequirementsCommand; +use App\SSHCommands\Installation\InstallRequirementsCommand; use Throwable; class InstallRequirements extends InstallationJob diff --git a/app/Jobs/Installation/InstallSupervisor.php b/app/Jobs/Installation/InstallSupervisor.php index 694d2e2..28df41c 100755 --- a/app/Jobs/Installation/InstallSupervisor.php +++ b/app/Jobs/Installation/InstallSupervisor.php @@ -5,8 +5,8 @@ use App\Enums\ServiceStatus; use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\InstallSupervisorCommand; -use App\SSHCommands\ServiceStatusCommand; +use App\SSHCommands\Service\ServiceStatusCommand; +use App\SSHCommands\Supervisor\InstallSupervisorCommand; use Throwable; class InstallSupervisor extends InstallationJob diff --git a/app/Jobs/Installation/InstallUfw.php b/app/Jobs/Installation/InstallUfw.php index 3ec1309..67e5dbd 100755 --- a/app/Jobs/Installation/InstallUfw.php +++ b/app/Jobs/Installation/InstallUfw.php @@ -5,8 +5,8 @@ use App\Enums\ServiceStatus; use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\InstallUfwCommand; -use App\SSHCommands\ServiceStatusCommand; +use App\SSHCommands\Firewall\InstallUfwCommand; +use App\SSHCommands\Service\ServiceStatusCommand; use Throwable; class InstallUfw extends InstallationJob diff --git a/app/Jobs/Installation/UninstallPHP.php b/app/Jobs/Installation/UninstallPHP.php index d160b40..65a0688 100755 --- a/app/Jobs/Installation/UninstallPHP.php +++ b/app/Jobs/Installation/UninstallPHP.php @@ -4,7 +4,7 @@ use App\Exceptions\InstallationFailed; use App\Models\Service; -use App\SSHCommands\UninstallPHPCommand; +use App\SSHCommands\PHP\UninstallPHPCommand; use Throwable; class UninstallPHP extends InstallationJob diff --git a/app/Jobs/Installation/UninstallPHPMyAdmin.php b/app/Jobs/Installation/UninstallPHPMyAdmin.php index c750f3c..6d38b6e 100644 --- a/app/Jobs/Installation/UninstallPHPMyAdmin.php +++ b/app/Jobs/Installation/UninstallPHPMyAdmin.php @@ -5,7 +5,7 @@ use App\Jobs\Job; use App\Models\FirewallRule; use App\Models\Service; -use App\SSHCommands\DeleteNginxPHPMyAdminVHost; +use App\SSHCommands\PHPMyAdmin\DeleteNginxPHPMyAdminVHost; use Exception; use Throwable; diff --git a/app/Jobs/Installation/Upgrade.php b/app/Jobs/Installation/Upgrade.php index 5d9cc51..0c977aa 100755 --- a/app/Jobs/Installation/Upgrade.php +++ b/app/Jobs/Installation/Upgrade.php @@ -3,7 +3,7 @@ namespace App\Jobs\Installation; use App\Models\Server; -use App\SSHCommands\UpgradeCommand; +use App\SSHCommands\System\UpgradeCommand; use Throwable; class Upgrade extends InstallationJob diff --git a/app/Jobs/PHP/InstallPHPExtension.php b/app/Jobs/PHP/InstallPHPExtension.php index 1044e20..444389b 100755 --- a/app/Jobs/PHP/InstallPHPExtension.php +++ b/app/Jobs/PHP/InstallPHPExtension.php @@ -6,7 +6,7 @@ use App\Exceptions\ProcessFailed; use App\Jobs\Job; use App\Models\Service; -use App\SSHCommands\InstallPHPExtensionCommand; +use App\SSHCommands\PHP\InstallPHPExtensionCommand; use Illuminate\Support\Str; use Throwable; diff --git a/app/Jobs/PHP/SetDefaultCli.php b/app/Jobs/PHP/SetDefaultCli.php index 5dae951..7cc9eba 100644 --- a/app/Jobs/PHP/SetDefaultCli.php +++ b/app/Jobs/PHP/SetDefaultCli.php @@ -6,7 +6,7 @@ use App\Events\Broadcast; use App\Jobs\Job; use App\Models\Service; -use App\SSHCommands\ChangeDefaultPHPCommand; +use App\SSHCommands\PHP\ChangeDefaultPHPCommand; use Throwable; class SetDefaultCli extends Job diff --git a/app/Jobs/PHP/UpdatePHPSettings.php b/app/Jobs/PHP/UpdatePHPSettings.php deleted file mode 100755 index 44fe4af..0000000 --- a/app/Jobs/PHP/UpdatePHPSettings.php +++ /dev/null @@ -1,58 +0,0 @@ -service = $service; - $this->settings = $settings; - } - - /** - * Execute the job. - * - * @throws Throwable - */ - public function handle(): void - { - $commands = []; - foreach ($this->settings as $key => $value) { - $commands[] = new UpdatePHPSettingsCommand( - $this->service->version, - $key, - $value.' '.config('core.php_settings_unit')[$key] - ); - } - $this->service->server->ssh()->exec($commands, 'update-php-settings'); - $typeData = $this->service->type_data; - $typeData['settings'] = $this->settings; - $this->service->type_data = $typeData; - $this->service->save(); - event( - new Broadcast('update-php-settings-finished', [ - 'service' => $this->service, - ]) - ); - } - - public function failed(): void - { - event( - new Broadcast('update-php-settings-failed', [ - 'service' => $this->service, - ]) - ); - } -} diff --git a/app/Jobs/Server/RebootServer.php b/app/Jobs/Server/RebootServer.php index dbc8f5d..5e55cf9 100644 --- a/app/Jobs/Server/RebootServer.php +++ b/app/Jobs/Server/RebootServer.php @@ -5,7 +5,7 @@ use App\Events\Broadcast; use App\Jobs\Job; use App\Models\Server; -use App\SSHCommands\RebootCommand; +use App\SSHCommands\System\RebootCommand; use Throwable; class RebootServer extends Job diff --git a/app/Jobs/Service/Manage.php b/app/Jobs/Service/Manage.php index d4e7672..c6a7a43 100644 --- a/app/Jobs/Service/Manage.php +++ b/app/Jobs/Service/Manage.php @@ -5,9 +5,9 @@ use App\Events\Broadcast; use App\Jobs\Job; use App\Models\Service; -use App\SSHCommands\RestartServiceCommand; -use App\SSHCommands\StartServiceCommand; -use App\SSHCommands\StopServiceCommand; +use App\SSHCommands\Service\RestartServiceCommand; +use App\SSHCommands\Service\StartServiceCommand; +use App\SSHCommands\Service\StopServiceCommand; use Exception; use Throwable; diff --git a/app/Jobs/Site/CloneRepository.php b/app/Jobs/Site/CloneRepository.php index db4cba3..4898ddf 100755 --- a/app/Jobs/Site/CloneRepository.php +++ b/app/Jobs/Site/CloneRepository.php @@ -4,7 +4,7 @@ use App\Jobs\Job; use App\Models\Site; -use App\SSHCommands\CloneRepositoryCommand; +use App\SSHCommands\Website\CloneRepositoryCommand; use Throwable; class CloneRepository extends Job @@ -25,7 +25,8 @@ public function handle(): void new CloneRepositoryCommand( $this->site->full_repository_url, $this->site->path, - $this->site->branch + $this->site->branch, + $this->site->ssh_key_name ), 'clone-repository', $this->site->id diff --git a/app/Jobs/Site/ComposerInstall.php b/app/Jobs/Site/ComposerInstall.php index 795d472..e71deef 100755 --- a/app/Jobs/Site/ComposerInstall.php +++ b/app/Jobs/Site/ComposerInstall.php @@ -5,7 +5,7 @@ use App\Exceptions\ComposerInstallFailed; use App\Jobs\Job; use App\Models\Site; -use App\SSHCommands\ComposerInstallCommand; +use App\SSHCommands\Website\ComposerInstallCommand; use Throwable; class ComposerInstall extends Job diff --git a/app/Jobs/Site/Deploy.php b/app/Jobs/Site/Deploy.php index 8dcc8ac..7773dd2 100644 --- a/app/Jobs/Site/Deploy.php +++ b/app/Jobs/Site/Deploy.php @@ -7,7 +7,7 @@ use App\Helpers\SSH; use App\Jobs\Job; use App\Models\Deployment; -use App\SSHCommands\RunScript; +use App\SSHCommands\System\RunScript; use Throwable; class Deploy extends Job diff --git a/app/Jobs/Site/DeployEnv.php b/app/Jobs/Site/DeployEnv.php index 72809ec..c4cd10a 100644 --- a/app/Jobs/Site/DeployEnv.php +++ b/app/Jobs/Site/DeployEnv.php @@ -5,7 +5,7 @@ use App\Events\Broadcast; use App\Jobs\Job; use App\Models\Site; -use App\SSHCommands\EditFileCommand; +use App\SSHCommands\System\EditFileCommand; use Throwable; class DeployEnv extends Job @@ -26,7 +26,9 @@ public function handle(): void new EditFileCommand( $this->site->path.'/.env', $this->site->env - ) + ), + 'update-env', + $this->site->id ); event( new Broadcast('deploy-site-env-finished', [ diff --git a/app/Jobs/Site/DeployKey.php b/app/Jobs/Site/DeployKey.php new file mode 100755 index 0000000..9521fd1 --- /dev/null +++ b/app/Jobs/Site/DeployKey.php @@ -0,0 +1,42 @@ +site = $site; + } + + /** + * @throws Throwable + */ + public function handle(): void + { + $this->site->server->ssh()->exec( + new GenerateSshKeyCommand($this->site->ssh_key_name), + 'generate-ssh-key', + $this->site->id + ); + $this->site->ssh_key = $this->site->server->ssh()->exec( + new ReadSshKeyCommand($this->site->ssh_key_name), + 'read-public-key', + $this->site->id + ); + $this->site->save(); + $this->site->sourceControl()->provider()->deployKey( + $this->site->domain.'-key-' . $this->site->id, + $this->site->repository, + $this->site->ssh_key + ); + } +} diff --git a/app/Jobs/Site/InstallWordpress.php b/app/Jobs/Site/InstallWordpress.php index 593f823..e70080d 100755 --- a/app/Jobs/Site/InstallWordpress.php +++ b/app/Jobs/Site/InstallWordpress.php @@ -7,7 +7,7 @@ use App\Models\Database; use App\Models\DatabaseUser; use App\Models\Site; -use App\SSHCommands\InstallWordpressCommand; +use App\SSHCommands\Wordpress\InstallWordpressCommand; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; use Throwable; diff --git a/app/Jobs/Site/UpdateBranch.php b/app/Jobs/Site/UpdateBranch.php index a9063ef..f2606e6 100644 --- a/app/Jobs/Site/UpdateBranch.php +++ b/app/Jobs/Site/UpdateBranch.php @@ -5,7 +5,7 @@ use App\Events\Broadcast; use App\Jobs\Job; use App\Models\Site; -use App\SSHCommands\UpdateBranchCommand; +use App\SSHCommands\Website\UpdateBranchCommand; use Throwable; class UpdateBranch extends Job diff --git a/app/Jobs/Site/UpdateSourceControlsRemote.php b/app/Jobs/Site/UpdateSourceControlsRemote.php deleted file mode 100644 index a000ff5..0000000 --- a/app/Jobs/Site/UpdateSourceControlsRemote.php +++ /dev/null @@ -1,41 +0,0 @@ -sourceControl = $sourceControl; - } - - /** - * Execute the job. - * - * @throws Throwable - */ - public function handle(): void - { - $sites = Site::query() - ->where('user_id', $this->sourceControl->user_id) - ->where('source_control', $this->sourceControl->provider) - ->get(); - foreach ($sites as $site) { - $site->server->ssh()->exec( - 'cd '.$site->path.' && git remote set-url origin '.$site->full_repository_url - ); - } - } -} diff --git a/app/Jobs/SshKey/DeleteSshKeyFromServer.php b/app/Jobs/SshKey/DeleteSshKeyFromServer.php index 06883df..26fbeb4 100644 --- a/app/Jobs/SshKey/DeleteSshKeyFromServer.php +++ b/app/Jobs/SshKey/DeleteSshKeyFromServer.php @@ -6,7 +6,7 @@ use App\Jobs\Job; use App\Models\Server; use App\Models\SshKey; -use App\SSHCommands\DeleteSshKeyCommand; +use App\SSHCommands\System\DeleteSshKeyCommand; use Throwable; class DeleteSshKeyFromServer extends Job diff --git a/app/Jobs/SshKey/DeploySshKeyToServer.php b/app/Jobs/SshKey/DeploySshKeyToServer.php index 8a58816..b5d5b67 100644 --- a/app/Jobs/SshKey/DeploySshKeyToServer.php +++ b/app/Jobs/SshKey/DeploySshKeyToServer.php @@ -7,7 +7,7 @@ use App\Jobs\Job; use App\Models\Server; use App\Models\SshKey; -use App\SSHCommands\DeploySshKeyCommand; +use App\SSHCommands\System\DeploySshKeyCommand; use Throwable; class DeploySshKeyToServer extends Job diff --git a/app/Listeners/BroadcastListener.php b/app/Listeners/BroadcastListener.php new file mode 100644 index 0000000..fa66776 --- /dev/null +++ b/app/Listeners/BroadcastListener.php @@ -0,0 +1,21 @@ + $event->type, + 'data' => $event->data + ], now()->addMinutes(5)); + } +} diff --git a/app/Models/FirewallRule.php b/app/Models/FirewallRule.php index 0169d18..ef5a60e 100755 --- a/app/Models/FirewallRule.php +++ b/app/Models/FirewallRule.php @@ -15,7 +15,7 @@ * @property string $real_protocol * @property int $port * @property string $source - * @property string $mask + * @property ?string $mask * @property string $note * @property string $status * @property Server $server diff --git a/app/Models/Server.php b/app/Models/Server.php index 2be36d3..74b21f3 100755 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Contracts\ServerType; +use App\Enums\ServerStatus; use App\Facades\SSH; use App\Jobs\Installation\Upgrade; use App\Jobs\Server\CheckConnection; @@ -232,9 +233,9 @@ public function install(): void // $this->team->notify(new ServerInstallationStarted($this)); } - public function ssh(string $user = null, bool $defaultKeys = false): \App\Helpers\SSH|SSHFake + public function ssh(string $user = null): \App\Helpers\SSH|SSHFake { - return SSH::init($this, $user, $defaultKeys); + return SSH::init($this, $user); } public function installedPHPVersions(): array @@ -323,7 +324,7 @@ public function getSshUserAttribute(string $value): string return config('core.ssh_user'); } - public function sshKey(bool $default = false): array + public function sshKey(): array { if (app()->environment() == 'testing') { return [ @@ -333,14 +334,6 @@ public function sshKey(bool $default = false): array ]; } - if ($default) { - return [ - 'public_key' => Str::replace("\n", '', File::get(storage_path(config('core.ssh_public_key_name')))), - 'public_key_path' => storage_path(config('core.ssh_public_key_name')), - 'private_key_path' => storage_path(config('core.ssh_private_key_name')), - ]; - } - return [ 'public_key' => Str::replace("\n", '', Storage::disk(config('core.key_pairs_disk'))->get($this->id.'.pub')), 'public_key_path' => Storage::disk(config('core.key_pairs_disk'))->path($this->id.'.pub'), @@ -385,4 +378,9 @@ public function getHostnameAttribute(): string { return Str::of($this->name)->slug(); } + + public function isReady(): bool + { + return $this->status == ServerStatus::READY; + } } diff --git a/app/Models/Site.php b/app/Models/Site.php index e895e8e..2018450 100755 --- a/app/Models/Site.php +++ b/app/Models/Site.php @@ -14,6 +14,7 @@ use App\Jobs\Site\UpdateBranch; use Exception; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; @@ -32,7 +33,9 @@ * @property string $path * @property string $php_version * @property string $source_control + * @property int $source_control_id * @property string $repository + * @property string $ssh_key * @property string $branch * @property string $status * @property int $port @@ -52,6 +55,7 @@ * @property string $aliases_string * @property string $deployment_script_text * @property string $env + * @property string $ssh_key_name */ class Site extends AbstractModel { @@ -67,7 +71,9 @@ class Site extends AbstractModel 'path', 'php_version', 'source_control', + 'source_control_id', 'repository', + 'ssh_key', 'branch', 'status', 'port', @@ -81,6 +87,7 @@ class Site extends AbstractModel 'progress' => 'integer', 'auto_deployment' => 'boolean', 'aliases' => 'array', + 'source_control_id' => 'integer', ]; protected $appends = [ @@ -151,22 +158,21 @@ public function ssls(): HasMany /** * @throws SourceControlIsNotConnected */ - public function sourceControl(): SourceControl|HasOne|null + public function sourceControl(): SourceControl|HasOne|null|Model { - if (! $this->source_control) { + $sourceControl = null; + + if (! $this->source_control && ! $this->source_control_id) { return null; } - if ($this->source_control == 'custom') { - return new SourceControl([ - 'user_id' => $this->id, - 'provider' => 'custom', - 'token' => '', - 'connected' => true, - ]); + if ($this->source_control) { + $sourceControl = SourceControl::query()->where('provider', $this->source_control)->first(); } - $sourceControl = SourceControl::query()->where('provider', $this->source_control)->first(); + if ($this->source_control_id) { + $sourceControl = SourceControl::query()->find($this->source_control_id); + } if (! $sourceControl) { throw new SourceControlIsNotConnected($this->source_control); @@ -180,7 +186,7 @@ public function sourceControl(): SourceControl|HasOne|null */ public function getFullRepositoryUrlAttribute() { - return $this->sourceControl()->provider()->fullRepoUrl($this->repository); + return $this->sourceControl()->provider()->fullRepoUrl($this->repository, $this->ssh_key_name); } public function getAliasesStringAttribute(): string @@ -394,4 +400,9 @@ public function updateBranch(string $branch): void { dispatch(new UpdateBranch($this, $branch))->onConnection('ssh'); } + + public function getSshKeyNameAttribute(): string + { + return str('site_'.$this->id)->toString(); + } } diff --git a/app/Models/SourceControl.php b/app/Models/SourceControl.php index 16d7fd4..f0e8975 100755 --- a/app/Models/SourceControl.php +++ b/app/Models/SourceControl.php @@ -7,6 +7,8 @@ /** * @property string $provider + * @property ?string $profile + * @property ?string $url * @property string $access_token */ class SourceControl extends AbstractModel @@ -15,6 +17,8 @@ class SourceControl extends AbstractModel protected $fillable = [ 'provider', + 'profile', + 'url', 'access_token', ]; diff --git a/app/NotificationChannels/Discord.php b/app/NotificationChannels/Discord.php index b806d8d..bb0e6be 100644 --- a/app/NotificationChannels/Discord.php +++ b/app/NotificationChannels/Discord.php @@ -43,7 +43,7 @@ public function sendMessage(string $subject, string $text): void Http::post($data['webhook_url'], [ 'content' => '*'.$subject.'*'."\n".$text, ]); - })->onQueue('default'); + }); } private function checkConnection(string $subject, string $text): bool diff --git a/app/NotificationChannels/Slack.php b/app/NotificationChannels/Slack.php index 5b1d68f..b6c3193 100644 --- a/app/NotificationChannels/Slack.php +++ b/app/NotificationChannels/Slack.php @@ -43,7 +43,7 @@ public function sendMessage(string $subject, string $text): void Http::post($data['webhook_url'], [ 'text' => '*'.$subject.'*'."\n".$text, ]); - })->onQueue('default'); + }); } private function checkConnection(string $subject, string $text): bool diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 2d65aac..1b03e38 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use App\Events\Broadcast; +use App\Listeners\BroadcastListener; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -18,6 +20,9 @@ class EventServiceProvider extends ServiceProvider Registered::class => [ SendEmailVerificationNotification::class, ], + Broadcast::class => [ + BroadcastListener::class, + ], ]; /** diff --git a/app/SSHCommands/ChangeDefaultPHPCommand.php b/app/SSHCommands/ChangeDefaultPHPCommand.php deleted file mode 100755 index ff60fad..0000000 --- a/app/SSHCommands/ChangeDefaultPHPCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -version = $version; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/change-default-php.sh')); - } - - public function content(string $os): string - { - return Str::replace('__version__', $this->version, $this->file($os)); - } -} diff --git a/app/SSHCommands/ChangeNginxPHPVersionCommand.php b/app/SSHCommands/ChangeNginxPHPVersionCommand.php deleted file mode 100755 index 77907f3..0000000 --- a/app/SSHCommands/ChangeNginxPHPVersionCommand.php +++ /dev/null @@ -1,47 +0,0 @@ -domain = $domain; - $this->oldVersion = $oldVersion; - $this->newVersion = $newVersion; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/webserver/nginx/change-php-version.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__domain__', $this->domain, $this->file($os)); - $command = Str::replace('__old_version__', $this->oldVersion, $command); - - return Str::replace('__new_version__', $this->newVersion, $command); - } -} diff --git a/app/SSHCommands/CloneRepositoryCommand.php b/app/SSHCommands/CloneRepositoryCommand.php deleted file mode 100755 index a5c5d93..0000000 --- a/app/SSHCommands/CloneRepositoryCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -repository = $repository; - $this->path = $path; - $this->branch = $branch; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/clone-repository.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__repo__', $this->repository, $this->file($os)); - $command = Str::replace('__host__', get_hostname_from_repo($this->repository), $command); - $command = Str::replace('__branch__', $this->branch, $command); - - return Str::replace('__path__', $this->path, $command); - } -} diff --git a/app/SSHCommands/ComposerInstallCommand.php b/app/SSHCommands/ComposerInstallCommand.php deleted file mode 100755 index daad0ae..0000000 --- a/app/SSHCommands/ComposerInstallCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -path = $path; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/composer-install.sh')); - } - - public function content(string $os): string - { - return Str::replace('__path__', $this->path, $this->file($os)); - } -} diff --git a/app/SSHCommands/CreateCustomSSLCommand.php b/app/SSHCommands/CreateCustomSSLCommand.php deleted file mode 100755 index fc7a140..0000000 --- a/app/SSHCommands/CreateCustomSSLCommand.php +++ /dev/null @@ -1,43 +0,0 @@ -path = $path; - $this->certificate = $certificate; - $this->pk = $pk; - $this->certificatePath = $certificatePath; - $this->pkPath = $pkPath; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/create-custom-ssl.sh')); - } - - public function content(string $os): string - { - $content = $this->file($os); - $content = str_replace('__path__', $this->path, $content); - $content = str_replace('__certificate__', $this->certificate, $content); - $content = str_replace('__pk__', $this->pk, $content); - $content = str_replace('__certificate_path__', $this->certificatePath, $content); - - return str_replace('__pk_path__', $this->pkPath, $content); - } -} diff --git a/app/SSHCommands/CreateLetsencryptSSLCommand.php b/app/SSHCommands/CreateLetsencryptSSLCommand.php deleted file mode 100755 index a306397..0000000 --- a/app/SSHCommands/CreateLetsencryptSSLCommand.php +++ /dev/null @@ -1,35 +0,0 @@ -email = $email; - $this->domain = $domain; - $this->webDirectory = $webDirectory; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/create-letsencrypt-ssl.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__email__', $this->email, $this->file($os)); - $command = Str::replace('__web_directory__', $this->webDirectory, $command); - - return Str::replace('__domain__', $this->domain, $command); - } -} diff --git a/app/SSHCommands/CreateNginxPHPMyAdminVHostCommand.php b/app/SSHCommands/CreateNginxPHPMyAdminVHostCommand.php deleted file mode 100755 index ef91428..0000000 --- a/app/SSHCommands/CreateNginxPHPMyAdminVHostCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -vhost = $vhost; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/webserver/nginx/create-phpmyadmin-vhost.sh')); - } - - public function content(string $os): string - { - return Str::replace('__vhost__', $this->vhost, $this->file($os)); - } -} diff --git a/app/SSHCommands/CreateNginxVHostCommand.php b/app/SSHCommands/CreateNginxVHostCommand.php deleted file mode 100755 index 8b51299..0000000 --- a/app/SSHCommands/CreateNginxVHostCommand.php +++ /dev/null @@ -1,50 +0,0 @@ -domain = $domain; - $this->path = $path; - $this->vhost = $vhost; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/webserver/nginx/create-vhost.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__path__', $this->path, $this->file($os)); - $command = Str::replace('__domain__', $this->domain, $command); - - return Str::replace('__vhost__', $this->vhost, $command); - } -} diff --git a/app/SSHCommands/CreateUserCommand.php b/app/SSHCommands/CreateUserCommand.php deleted file mode 100755 index 293bafc..0000000 --- a/app/SSHCommands/CreateUserCommand.php +++ /dev/null @@ -1,39 +0,0 @@ -user = $user; - $this->password = $password; - $this->key = $key; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/create-user.sh')); - } - - public function content(string $os): string - { - $command = $this->file($os); - $command = Str::replace('__user__', $this->user, $command); - $command = Str::replace('__key__', $this->key, $command); - - return Str::replace('__password__', $this->password, $command); - } -} diff --git a/app/SSHCommands/CronJob/UpdateCronJobsCommand.php b/app/SSHCommands/CronJob/UpdateCronJobsCommand.php new file mode 100755 index 0000000..c121217 --- /dev/null +++ b/app/SSHCommands/CronJob/UpdateCronJobsCommand.php @@ -0,0 +1,26 @@ +file()) + ->replace('__user__', $this->user) + ->replace('__data__', $this->data) + ->toString(); + } +} diff --git a/app/SSHCommands/Database/BackupDatabaseCommand.php b/app/SSHCommands/Database/BackupDatabaseCommand.php index bbd25b1..3fdf4cf 100644 --- a/app/SSHCommands/Database/BackupDatabaseCommand.php +++ b/app/SSHCommands/Database/BackupDatabaseCommand.php @@ -4,33 +4,23 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class BackupDatabaseCommand extends Command { - protected $provider; - - protected $database; - - protected $fileName; - - public function __construct($provider, $database, $fileName) + public function __construct(protected string $provider, protected string $database, protected string $fileName) { - $this->provider = $provider; - $this->database = $database; - $this->fileName = $fileName; } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/backup.sh')); + return File::get(resource_path(sprintf("commands/database/%s/backup.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - $command = $this->file($os); - $command = Str::replace('__database__', $this->database, $command); - - return Str::replace('__file__', $this->fileName, $command); + return str($this->file()) + ->replace('__database__', $this->database) + ->replace('__file__', $this->fileName) + ->toString(); } } diff --git a/app/SSHCommands/Database/CreateCommand.php b/app/SSHCommands/Database/CreateCommand.php index 38a4ce0..4ec61e2 100755 --- a/app/SSHCommands/Database/CreateCommand.php +++ b/app/SSHCommands/Database/CreateCommand.php @@ -4,33 +4,22 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class CreateCommand extends Command { - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $name; - - public function __construct($provider, $name) + public function __construct(protected string $provider, protected string $name) { - $this->provider = $provider; - $this->name = $name; } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/create.sh')); + return File::get(resource_path(sprintf("commands/database/%s/create.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - return Str::replace('__name__', $this->name, $this->file($os)); + return str($this->file()) + ->replace('__name__', $this->name) + ->toString(); } } diff --git a/app/SSHCommands/Database/CreateUserCommand.php b/app/SSHCommands/Database/CreateUserCommand.php index 5cc1b36..584a2f4 100755 --- a/app/SSHCommands/Database/CreateUserCommand.php +++ b/app/SSHCommands/Database/CreateUserCommand.php @@ -4,48 +4,28 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class CreateUserCommand extends Command { - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $username; - - /** - * @var string - */ - protected $password; - - /** - * @var string - */ - protected $host; - - public function __construct($provider, $username, $password, $host) - { - $this->provider = $provider; - $this->username = $username; - $this->password = $password; - $this->host = $host; + public function __construct( + protected string $provider, + protected string $username, + protected string $password, + protected string $host + ) { } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/create-user.sh')); + return File::get(resource_path(sprintf("commands/database/%s/create-user.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - $command = Str::replace('__username__', $this->username, $this->file($os)); - $command = Str::replace('__password__', $this->password, $command); - - return Str::replace('__host__', $this->host, $command); + return str($this->file()) + ->replace('__username__', $this->username) + ->replace('__password__', $this->password) + ->replace('__host__', $this->host) + ->toString(); } } diff --git a/app/SSHCommands/Database/DeleteCommand.php b/app/SSHCommands/Database/DeleteCommand.php index 80926ee..fdd998f 100755 --- a/app/SSHCommands/Database/DeleteCommand.php +++ b/app/SSHCommands/Database/DeleteCommand.php @@ -4,33 +4,22 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class DeleteCommand extends Command { - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $name; - - public function __construct($provider, $name) + public function __construct(protected string $provider, protected string $name) { - $this->provider = $provider; - $this->name = $name; } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/delete.sh')); + return File::get(resource_path(sprintf("commands/database/%s/delete.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - return Str::replace('__name__', $this->name, $this->file($os)); + return str($this->file()) + ->replace('__name__', $this->name) + ->toString(); } } diff --git a/app/SSHCommands/Database/DeleteUserCommand.php b/app/SSHCommands/Database/DeleteUserCommand.php index 585b905..6947c22 100755 --- a/app/SSHCommands/Database/DeleteUserCommand.php +++ b/app/SSHCommands/Database/DeleteUserCommand.php @@ -4,41 +4,23 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class DeleteUserCommand extends Command { - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $username; - - /** - * @var string - */ - protected $host; - - public function __construct($provider, $username, $host) + public function __construct(protected string $provider, protected string $username, protected string $host) { - $this->provider = $provider; - $this->username = $username; - $this->host = $host; } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/delete-user.sh')); + return File::get(resource_path(sprintf("commands/database/%s/delete-user.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - $command = Str::replace('__username__', $this->username, $this->file($os)); - - return Str::replace('__host__', $this->host, $command); + return str($this->file()) + ->replace('__username__', $this->username) + ->replace('__host__', $this->host) + ->toString(); } } diff --git a/app/SSHCommands/Database/InstallMariadbCommand.php b/app/SSHCommands/Database/InstallMariadbCommand.php new file mode 100755 index 0000000..42a19ff --- /dev/null +++ b/app/SSHCommands/Database/InstallMariadbCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/Database/InstallMysqlCommand.php b/app/SSHCommands/Database/InstallMysqlCommand.php new file mode 100755 index 0000000..3060ec7 --- /dev/null +++ b/app/SSHCommands/Database/InstallMysqlCommand.php @@ -0,0 +1,27 @@ +version == '8.0') { + return File::get(resource_path('commands/database/install-mysql-8.sh')); + } + + return File::get(resource_path('commands/database/install-mysql.sh')); + } + + public function content(): string + { + return $this->file(); + } +} diff --git a/app/SSHCommands/Database/LinkCommand.php b/app/SSHCommands/Database/LinkCommand.php index ee66a46..0af52ad 100755 --- a/app/SSHCommands/Database/LinkCommand.php +++ b/app/SSHCommands/Database/LinkCommand.php @@ -4,45 +4,28 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class LinkCommand extends Command { - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $username; - - protected $host; - - /** - * @var string - */ - protected $database; - - public function __construct($provider, $username, $host, $database) - { - $this->provider = $provider; - $this->username = $username; - $this->host = $host; - $this->database = $database; + public function __construct( + protected string $provider, + protected string $username, + protected string $host, + protected string $database + ) { } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/link.sh')); + return File::get(resource_path(sprintf("commands/database/%s/link.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - $command = Str::replace('__username__', $this->username, $this->file($os)); - $command = Str::replace('__host__', $this->host, $command); - - return Str::replace('__database__', $this->database, $command); + return str($this->file()) + ->replace('__username__', $this->username) + ->replace('__host__', $this->host) + ->replace('__database__', $this->database) + ->toString(); } } diff --git a/app/SSHCommands/Database/RestoreDatabaseCommand.php b/app/SSHCommands/Database/RestoreDatabaseCommand.php index 831a8a1..989ba33 100644 --- a/app/SSHCommands/Database/RestoreDatabaseCommand.php +++ b/app/SSHCommands/Database/RestoreDatabaseCommand.php @@ -4,33 +4,23 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class RestoreDatabaseCommand extends Command { - protected $provider; - - protected $database; - - protected $fileName; - - public function __construct($provider, $database, $fileName) + public function __construct(protected string $provider, protected string $database, protected string $fileName) { - $this->provider = $provider; - $this->database = $database; - $this->fileName = $fileName; } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/restore.sh')); + return File::get(resource_path(sprintf("commands/database/%s/restore.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - $command = $this->file($os); - $command = Str::replace('__database__', $this->database, $command); - - return Str::replace('__file__', $this->fileName, $command); + return str($this->file()) + ->replace('__database__', $this->database) + ->replace('__file__', $this->fileName) + ->toString(); } } diff --git a/app/SSHCommands/Database/UnlinkCommand.php b/app/SSHCommands/Database/UnlinkCommand.php index 7c049fc..cc3a75d 100755 --- a/app/SSHCommands/Database/UnlinkCommand.php +++ b/app/SSHCommands/Database/UnlinkCommand.php @@ -4,38 +4,26 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class UnlinkCommand extends Command { - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $username; - - protected $host; - - public function __construct($provider, $username, $host) - { - $this->provider = $provider; - $this->username = $username; - $this->host = $host; + public function __construct( + protected string $provider, + protected string $username, + protected string $host + ) { } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/database/'.$this->provider.'/unlink.sh')); + return File::get(resource_path(sprintf("commands/database/%s/unlink.sh", $this->provider))); } - public function content(string $os): string + public function content(): string { - $command = Str::replace('__username__', $this->username, $this->file($os)); - - return Str::replace('__host__', $this->host, $command); + return str($this->file()) + ->replace('__username__', $this->username) + ->replace('__host__', $this->host) + ->toString(); } } diff --git a/app/SSHCommands/DeleteNginxPHPMyAdminVHost.php b/app/SSHCommands/DeleteNginxPHPMyAdminVHost.php deleted file mode 100755 index 73cdb29..0000000 --- a/app/SSHCommands/DeleteNginxPHPMyAdminVHost.php +++ /dev/null @@ -1,30 +0,0 @@ -path = $path; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/webserver/nginx/delete-phpmyadmin-vhost.sh')); - } - - public function content(string $os): string - { - return Str::replace('__path__', $this->path, $this->file($os)); - } -} diff --git a/app/SSHCommands/DeleteNginxSiteCommand.php b/app/SSHCommands/DeleteNginxSiteCommand.php deleted file mode 100755 index c2dddb3..0000000 --- a/app/SSHCommands/DeleteNginxSiteCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -domain = $domain; - $this->path = $path; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/webserver/nginx/delete-site.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__domain__', $this->domain, $this->file($os)); - - return Str::replace('__path__', $this->path, $command); - } -} diff --git a/app/SSHCommands/DeleteSshKeyCommand.php b/app/SSHCommands/DeleteSshKeyCommand.php deleted file mode 100755 index 966b295..0000000 --- a/app/SSHCommands/DeleteSshKeyCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -key = $key; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/delete-ssh-key.sh')); - } - - public function content(string $os): string - { - return Str::replace('__key__', Str::replace('/', '\/', $this->key), $this->file($os)); - } -} diff --git a/app/SSHCommands/DeploySshKeyCommand.php b/app/SSHCommands/DeploySshKeyCommand.php deleted file mode 100755 index c00798f..0000000 --- a/app/SSHCommands/DeploySshKeyCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -key = $key; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/deploy-ssh-key.sh')); - } - - public function content(string $os): string - { - return Str::replace('__key__', addslashes($this->key), $this->file($os)); - } -} diff --git a/app/SSHCommands/DownloadPHPMyAdminCommand.php b/app/SSHCommands/DownloadPHPMyAdminCommand.php deleted file mode 100644 index 117d56d..0000000 --- a/app/SSHCommands/DownloadPHPMyAdminCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/EditFileCommand.php b/app/SSHCommands/EditFileCommand.php deleted file mode 100644 index 7687ba9..0000000 --- a/app/SSHCommands/EditFileCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -path = $path; - $this->content = $content; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/edit-file.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__path__', $this->path, $this->file($os)); - - return Str::replace('__content__', addslashes($this->content), $command); - } -} diff --git a/app/SSHCommands/Firewall/AddRuleCommand.php b/app/SSHCommands/Firewall/AddRuleCommand.php index ac6c321..6f37abc 100755 --- a/app/SSHCommands/Firewall/AddRuleCommand.php +++ b/app/SSHCommands/Firewall/AddRuleCommand.php @@ -9,48 +9,18 @@ class AddRuleCommand extends Command { use CommandContent; - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $type; - - /** - * @var string - */ - protected $protocol; - - /** - * @var string - */ - protected $port; - - /** - * @var string - */ - protected $source; - - /** - * @var string - */ - protected $mask; - - public function __construct($provider, $type, $protocol, $port, $source, $mask) - { - $this->provider = $provider; - $this->type = $type; - $this->protocol = $protocol; - $this->port = $port; - $this->source = $source; - $this->mask = $mask; + public function __construct( + protected string $provider, + protected string $type, + protected string $protocol, + protected string $port, + protected string $source, + protected ?string $mask = null + ) { } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/firewall/'.$this->provider.'/add-rule.sh')); + return File::get(resource_path(sprintf("commands/firewall/%s/add-rule.sh", $this->provider))); } } diff --git a/app/SSHCommands/Firewall/CommandContent.php b/app/SSHCommands/Firewall/CommandContent.php index 5b7633c..86ea4bd 100755 --- a/app/SSHCommands/Firewall/CommandContent.php +++ b/app/SSHCommands/Firewall/CommandContent.php @@ -2,17 +2,16 @@ namespace App\SSHCommands\Firewall; -use Illuminate\Support\Str; - trait CommandContent { - public function content(string $os): string + public function content(): string { - $command = Str::replace('__type__', $this->type, $this->file($os)); - $command = Str::replace('__protocol__', $this->protocol, $command); - $command = Str::replace('__source__', $this->source, $command); - $command = Str::replace('__mask__', $this->mask, $command); - - return Str::replace('__port__', $this->port, $command); + return str($this->file()) + ->replace('__type__', $this->type) + ->replace('__protocol__', $this->protocol) + ->replace('__source__', $this->source) + ->replace('__mask__', $this->mask || $this->mask == 0 ? '/'.$this->mask : '') + ->replace('__port__', $this->port) + ->toString(); } } diff --git a/app/SSHCommands/Firewall/InstallUfwCommand.php b/app/SSHCommands/Firewall/InstallUfwCommand.php new file mode 100755 index 0000000..14cce4a --- /dev/null +++ b/app/SSHCommands/Firewall/InstallUfwCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/Firewall/RemoveRuleCommand.php b/app/SSHCommands/Firewall/RemoveRuleCommand.php index 4024b59..763a449 100755 --- a/app/SSHCommands/Firewall/RemoveRuleCommand.php +++ b/app/SSHCommands/Firewall/RemoveRuleCommand.php @@ -9,48 +9,18 @@ class RemoveRuleCommand extends Command { use CommandContent; - /** - * @var string - */ - protected $provider; - - /** - * @var string - */ - protected $type; - - /** - * @var string - */ - protected $protocol; - - /** - * @var string - */ - protected $port; - - /** - * @var string - */ - protected $source; - - /** - * @var string - */ - protected $mask; - - public function __construct($provider, $type, $protocol, $port, $source, $mask) - { - $this->provider = $provider; - $this->type = $type; - $this->protocol = $protocol; - $this->port = $port; - $this->source = $source; - $this->mask = $mask; + public function __construct( + protected string $provider, + protected string $type, + protected string $protocol, + protected string $port, + protected string $source, + protected ?string $mask = null + ) { } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/firewall/'.$this->provider.'/remove-rule.sh')); + return File::get(resource_path(sprintf("commands/firewall/%s/remove-rule.sh", $this->provider))); } } diff --git a/app/SSHCommands/GetPHPIniCommand.php b/app/SSHCommands/GetPHPIniCommand.php deleted file mode 100755 index 567fbfd..0000000 --- a/app/SSHCommands/GetPHPIniCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -version = $version; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/get-php-ini.sh')); - } - - public function content(string $os): string - { - return Str::replace('__version__', $this->version, $this->file($os)); - } -} diff --git a/app/SSHCommands/GetPublicKeyCommand.php b/app/SSHCommands/GetPublicKeyCommand.php deleted file mode 100755 index 00ce95d..0000000 --- a/app/SSHCommands/GetPublicKeyCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallCertbotCommand.php b/app/SSHCommands/InstallCertbotCommand.php deleted file mode 100755 index 6ff17df..0000000 --- a/app/SSHCommands/InstallCertbotCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallComposerCommand.php b/app/SSHCommands/InstallComposerCommand.php deleted file mode 100755 index 502da17..0000000 --- a/app/SSHCommands/InstallComposerCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallDependenciesCommand.php b/app/SSHCommands/InstallDependenciesCommand.php deleted file mode 100755 index 4dac42f..0000000 --- a/app/SSHCommands/InstallDependenciesCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallMariadbCommand.php b/app/SSHCommands/InstallMariadbCommand.php deleted file mode 100755 index 347caee..0000000 --- a/app/SSHCommands/InstallMariadbCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallMysqlCommand.php b/app/SSHCommands/InstallMysqlCommand.php deleted file mode 100755 index d291687..0000000 --- a/app/SSHCommands/InstallMysqlCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -version = $version; - } - - public function file(string $os): string - { - if ($this->version == '8.0') { - return File::get(base_path('system/commands/ubuntu/install-mysql-8.sh')); - } - - return File::get(base_path('system/commands/ubuntu/install-mysql.sh')); - } - - public function content(string $os): string - { - return $this->file($os); - } -} diff --git a/app/SSHCommands/InstallNginxCommand.php b/app/SSHCommands/InstallNginxCommand.php deleted file mode 100755 index 0e82a31..0000000 --- a/app/SSHCommands/InstallNginxCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -config(), $this->file($os)); - } - - /** - * @return string - */ - protected function config() - { - $config = File::get(base_path('system/command-templates/nginx/nginx.conf')); - - return Str::replace('__user__', config('core.ssh_user'), $config); - } -} diff --git a/app/SSHCommands/InstallNodejsCommand.php b/app/SSHCommands/InstallNodejsCommand.php deleted file mode 100755 index b6c1411..0000000 --- a/app/SSHCommands/InstallNodejsCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallPHPCommand.php b/app/SSHCommands/InstallPHPCommand.php deleted file mode 100755 index e1e3745..0000000 --- a/app/SSHCommands/InstallPHPCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -version = $version; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/install-php.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__version__', $this->version, $this->file($os)); - - return Str::replace('__user__', config('core.ssh_user'), $command); - } -} diff --git a/app/SSHCommands/InstallPHPExtensionCommand.php b/app/SSHCommands/InstallPHPExtensionCommand.php deleted file mode 100755 index a8f76df..0000000 --- a/app/SSHCommands/InstallPHPExtensionCommand.php +++ /dev/null @@ -1,37 +0,0 @@ -version = $version; - $this->name = $name; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/install-php-extension.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__version__', $this->version, $this->file($os)); - - return Str::replace('__name__', $this->name, $command); - } -} diff --git a/app/SSHCommands/InstallRedisCommand.php b/app/SSHCommands/InstallRedisCommand.php deleted file mode 100755 index 94579f5..0000000 --- a/app/SSHCommands/InstallRedisCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallRequirementsCommand.php b/app/SSHCommands/InstallRequirementsCommand.php deleted file mode 100755 index 5002ac6..0000000 --- a/app/SSHCommands/InstallRequirementsCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -email = $email; - $this->name = $name; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/install-requirements.sh')); - } - - public function content(string $os): string - { - return $this->file($os); - } -} diff --git a/app/SSHCommands/InstallSupervisorCommand.php b/app/SSHCommands/InstallSupervisorCommand.php deleted file mode 100755 index 8098300..0000000 --- a/app/SSHCommands/InstallSupervisorCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallUfwCommand.php b/app/SSHCommands/InstallUfwCommand.php deleted file mode 100755 index 5d302a7..0000000 --- a/app/SSHCommands/InstallUfwCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/InstallWordpressCommand.php b/app/SSHCommands/InstallWordpressCommand.php deleted file mode 100755 index 2e2bfec..0000000 --- a/app/SSHCommands/InstallWordpressCommand.php +++ /dev/null @@ -1,71 +0,0 @@ -path = $path; - $this->domain = $domain; - $this->dbName = $dbName; - $this->dbUser = $dbUser; - $this->dbPass = $dbPass; - $this->dbHost = $dbHost; - $this->dbPrefix = $dbPrefix; - $this->username = $username; - $this->password = $password; - $this->email = $email; - $this->title = $title; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/wordpress/install.sh')); - } - - public function content(string $os): string - { - $command = $this->file($os); - - $command = str_replace('__path__', $this->path, $command); - $command = str_replace('__domain__', $this->domain, $command); - $command = str_replace('__db_name__', $this->dbName, $command); - $command = str_replace('__db_user__', $this->dbUser, $command); - $command = str_replace('__db_pass__', $this->dbPass, $command); - $command = str_replace('__db_host__', $this->dbHost, $command); - $command = str_replace('__db_prefix__', $this->dbPrefix, $command); - $command = str_replace('__username__', $this->username, $command); - $command = str_replace('__password__', $this->password, $command); - $command = str_replace('__title__', $this->title, $command); - - return str_replace('__email__', $this->email, $command); - } -} diff --git a/app/SSHCommands/Installation/InstallNodejsCommand.php b/app/SSHCommands/Installation/InstallNodejsCommand.php new file mode 100755 index 0000000..9173fde --- /dev/null +++ b/app/SSHCommands/Installation/InstallNodejsCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/Installation/InstallRedisCommand.php b/app/SSHCommands/Installation/InstallRedisCommand.php new file mode 100755 index 0000000..11b7f95 --- /dev/null +++ b/app/SSHCommands/Installation/InstallRedisCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/Installation/InstallRequirementsCommand.php b/app/SSHCommands/Installation/InstallRequirementsCommand.php new file mode 100755 index 0000000..a0f57b4 --- /dev/null +++ b/app/SSHCommands/Installation/InstallRequirementsCommand.php @@ -0,0 +1,26 @@ +file()) + ->replace('__email__', $this->email) + ->replace('__name__', $this->name) + ->toString(); + } +} diff --git a/app/SSHCommands/ManageServiceCommand.php b/app/SSHCommands/ManageServiceCommand.php deleted file mode 100755 index 5eff68e..0000000 --- a/app/SSHCommands/ManageServiceCommand.php +++ /dev/null @@ -1,40 +0,0 @@ -unit = $unit; - $this->action = $action; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/manage-service.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__service__', $this->unit, $this->file($os)); - - return Str::replace('__action__', $this->action, $command); - } -} diff --git a/app/SSHCommands/Nginx/ChangeNginxPHPVersionCommand.php b/app/SSHCommands/Nginx/ChangeNginxPHPVersionCommand.php new file mode 100755 index 0000000..be03101 --- /dev/null +++ b/app/SSHCommands/Nginx/ChangeNginxPHPVersionCommand.php @@ -0,0 +1,27 @@ +file()) + ->replace('__domain__', $this->domain) + ->replace('__old_version__', $this->oldVersion) + ->replace('__new_version__', $this->newVersion) + ->toString(); + } +} diff --git a/app/SSHCommands/Nginx/CreateNginxVHostCommand.php b/app/SSHCommands/Nginx/CreateNginxVHostCommand.php new file mode 100755 index 0000000..b13eb53 --- /dev/null +++ b/app/SSHCommands/Nginx/CreateNginxVHostCommand.php @@ -0,0 +1,30 @@ +file()) + ->replace('__domain__', $this->domain) + ->replace('__path__', $this->path) + ->replace('__vhost__', $this->vhost) + ->toString(); + } +} diff --git a/app/SSHCommands/Nginx/DeleteNginxSiteCommand.php b/app/SSHCommands/Nginx/DeleteNginxSiteCommand.php new file mode 100755 index 0000000..d4dcc3e --- /dev/null +++ b/app/SSHCommands/Nginx/DeleteNginxSiteCommand.php @@ -0,0 +1,26 @@ +file()) + ->replace('__domain__', $this->domain) + ->replace('__path__', $this->path) + ->toString(); + } +} diff --git a/app/SSHCommands/Nginx/InstallNginxCommand.php b/app/SSHCommands/Nginx/InstallNginxCommand.php new file mode 100755 index 0000000..c30e00f --- /dev/null +++ b/app/SSHCommands/Nginx/InstallNginxCommand.php @@ -0,0 +1,32 @@ +file()) + ->replace('__config__', $this->config()) + ->toString(); + } + + protected function config(): string + { + $config = File::get(resource_path('commands/webserver/nginx/nginx.conf')); + + /** TODO: change user to server user */ + return str($config) + ->replace('__user__', config('core.ssh_user')) + ->toString(); + } +} diff --git a/app/SSHCommands/Nginx/UpdateNginxRedirectsCommand.php b/app/SSHCommands/Nginx/UpdateNginxRedirectsCommand.php new file mode 100755 index 0000000..bd8c25d --- /dev/null +++ b/app/SSHCommands/Nginx/UpdateNginxRedirectsCommand.php @@ -0,0 +1,28 @@ +file()) + ->replace('__redirects__', addslashes($this->redirects)) + ->replace('__domain__', $this->domain) + ->toString(); + } +} diff --git a/app/SSHCommands/Nginx/UpdateNginxVHostCommand.php b/app/SSHCommands/Nginx/UpdateNginxVHostCommand.php new file mode 100755 index 0000000..f0f321a --- /dev/null +++ b/app/SSHCommands/Nginx/UpdateNginxVHostCommand.php @@ -0,0 +1,30 @@ +file()) + ->replace('__domain__', $this->domain) + ->replace('__path__', $this->path) + ->replace('__vhost__', $this->vhost) + ->toString(); + } +} diff --git a/app/SSHCommands/PHP/ChangeDefaultPHPCommand.php b/app/SSHCommands/PHP/ChangeDefaultPHPCommand.php new file mode 100755 index 0000000..df9fc8a --- /dev/null +++ b/app/SSHCommands/PHP/ChangeDefaultPHPCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__version__', $this->version) + ->toString(); + } +} diff --git a/app/SSHCommands/PHP/GetPHPIniCommand.php b/app/SSHCommands/PHP/GetPHPIniCommand.php new file mode 100755 index 0000000..ef7858d --- /dev/null +++ b/app/SSHCommands/PHP/GetPHPIniCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__version__', $this->version) + ->toString(); + } +} diff --git a/app/SSHCommands/PHP/InstallComposerCommand.php b/app/SSHCommands/PHP/InstallComposerCommand.php new file mode 100755 index 0000000..60cc39a --- /dev/null +++ b/app/SSHCommands/PHP/InstallComposerCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/PHP/InstallPHPCommand.php b/app/SSHCommands/PHP/InstallPHPCommand.php new file mode 100755 index 0000000..d0c9e31 --- /dev/null +++ b/app/SSHCommands/PHP/InstallPHPCommand.php @@ -0,0 +1,28 @@ +file()) + ->replace('__version__', $this->version) + ->replace('__user__', config('core.ssh_user')) + ->toString(); + } +} diff --git a/app/SSHCommands/PHP/InstallPHPExtensionCommand.php b/app/SSHCommands/PHP/InstallPHPExtensionCommand.php new file mode 100755 index 0000000..331cf58 --- /dev/null +++ b/app/SSHCommands/PHP/InstallPHPExtensionCommand.php @@ -0,0 +1,26 @@ +file()) + ->replace('__version__', $this->version) + ->replace('__name__', $this->name) + ->toString(); + } +} diff --git a/app/SSHCommands/PHP/UninstallPHPCommand.php b/app/SSHCommands/PHP/UninstallPHPCommand.php new file mode 100755 index 0000000..efd4911 --- /dev/null +++ b/app/SSHCommands/PHP/UninstallPHPCommand.php @@ -0,0 +1,28 @@ +file()) + ->replace('__version__', $this->version) + ->replace('__user__', config('core.ssh_user')) + ->toString(); + } +} diff --git a/app/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommand.php b/app/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommand.php new file mode 100755 index 0000000..508134e --- /dev/null +++ b/app/SSHCommands/PHPMyAdmin/CreateNginxPHPMyAdminVHostCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__vhost__', $this->vhost) + ->toString(); + } +} diff --git a/app/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHost.php b/app/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHost.php new file mode 100755 index 0000000..3d6ad71 --- /dev/null +++ b/app/SSHCommands/PHPMyAdmin/DeleteNginxPHPMyAdminVHost.php @@ -0,0 +1,25 @@ +file()) + ->replace('__path__', $this->path) + ->toString(); + } +} diff --git a/app/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommand.php b/app/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommand.php new file mode 100644 index 0000000..15963de --- /dev/null +++ b/app/SSHCommands/PHPMyAdmin/DownloadPHPMyAdminCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/ProcessManager/Supervisor/CreateWorkerCommand.php b/app/SSHCommands/ProcessManager/Supervisor/CreateWorkerCommand.php deleted file mode 100644 index b813374..0000000 --- a/app/SSHCommands/ProcessManager/Supervisor/CreateWorkerCommand.php +++ /dev/null @@ -1,33 +0,0 @@ -id = $id; - $this->config = $config; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/process-manager/supervisor/create-worker.sh')); - } - - public function content(string $os): string - { - $command = $this->file($os); - $command = Str::replace('__id__', $this->id, $command); - - return Str::replace('__config__', $this->config, $command); - } -} diff --git a/app/SSHCommands/ProcessManager/Supervisor/DeleteWorkerCommand.php b/app/SSHCommands/ProcessManager/Supervisor/DeleteWorkerCommand.php deleted file mode 100644 index 95d68f6..0000000 --- a/app/SSHCommands/ProcessManager/Supervisor/DeleteWorkerCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -id = $id; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/process-manager/supervisor/delete-worker.sh')); - } - - public function content(string $os): string - { - $command = $this->file($os); - - return Str::replace('__id__', $this->id, $command); - } -} diff --git a/app/SSHCommands/ProcessManager/Supervisor/RestartWorkerCommand.php b/app/SSHCommands/ProcessManager/Supervisor/RestartWorkerCommand.php deleted file mode 100644 index 5a0b247..0000000 --- a/app/SSHCommands/ProcessManager/Supervisor/RestartWorkerCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -id = $id; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/process-manager/supervisor/restart-worker.sh')); - } - - public function content(string $os): string - { - $command = $this->file($os); - - return Str::replace('__id__', $this->id, $command); - } -} diff --git a/app/SSHCommands/ProcessManager/Supervisor/StartWorkerCommand.php b/app/SSHCommands/ProcessManager/Supervisor/StartWorkerCommand.php deleted file mode 100644 index fdbd3dd..0000000 --- a/app/SSHCommands/ProcessManager/Supervisor/StartWorkerCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -id = $id; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/process-manager/supervisor/start-worker.sh')); - } - - public function content(string $os): string - { - $command = $this->file($os); - - return Str::replace('__id__', $this->id, $command); - } -} diff --git a/app/SSHCommands/ProcessManager/Supervisor/StopWorkerCommand.php b/app/SSHCommands/ProcessManager/Supervisor/StopWorkerCommand.php deleted file mode 100644 index 7bf46ba..0000000 --- a/app/SSHCommands/ProcessManager/Supervisor/StopWorkerCommand.php +++ /dev/null @@ -1,29 +0,0 @@ -id = $id; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/process-manager/supervisor/stop-worker.sh')); - } - - public function content(string $os): string - { - $command = $this->file($os); - - return Str::replace('__id__', $this->id, $command); - } -} diff --git a/app/SSHCommands/RebootCommand.php b/app/SSHCommands/RebootCommand.php deleted file mode 100644 index 49c4549..0000000 --- a/app/SSHCommands/RebootCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/RemoveSSLCommand.php b/app/SSHCommands/RemoveSSLCommand.php deleted file mode 100755 index f5f491a..0000000 --- a/app/SSHCommands/RemoveSSLCommand.php +++ /dev/null @@ -1,23 +0,0 @@ -path = $path; - } - - public function file(string $os): string - { - return ''; - } - - public function content(string $os): string - { - return 'sudo rm -rf '.$this->path.'*'."\n"; - } -} diff --git a/app/SSHCommands/RestartServiceCommand.php b/app/SSHCommands/RestartServiceCommand.php deleted file mode 100644 index 2472efe..0000000 --- a/app/SSHCommands/RestartServiceCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -unit = $unit; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/restart-service.sh')); - } - - public function content(string $os): string - { - return Str::replace('__service__', $this->unit, $this->file($os)); - } -} diff --git a/app/SSHCommands/RunScript.php b/app/SSHCommands/RunScript.php deleted file mode 100644 index fad9a87..0000000 --- a/app/SSHCommands/RunScript.php +++ /dev/null @@ -1,31 +0,0 @@ -path = $path; - $this->script = $script; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/run-script.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__path__', $this->path, $this->file($os)); - - return Str::replace('__script__', make_bash_script($this->script), $command); - } -} diff --git a/app/SSHCommands/SSL/CreateCustomSSLCommand.php b/app/SSHCommands/SSL/CreateCustomSSLCommand.php new file mode 100755 index 0000000..86732b9 --- /dev/null +++ b/app/SSHCommands/SSL/CreateCustomSSLCommand.php @@ -0,0 +1,34 @@ +file()) + ->replace('__path__', $this->path) + ->replace('__certificate__', $this->certificate) + ->replace('__pk__', $this->pk) + ->replace('__certificate_path__', $this->certificatePath) + ->replace('__pk_path__', $this->pkPath) + ->toString(); + } +} diff --git a/app/SSHCommands/SSL/CreateLetsencryptSSLCommand.php b/app/SSHCommands/SSL/CreateLetsencryptSSLCommand.php new file mode 100755 index 0000000..8072b6c --- /dev/null +++ b/app/SSHCommands/SSL/CreateLetsencryptSSLCommand.php @@ -0,0 +1,27 @@ +file()) + ->replace('__email__', $this->email) + ->replace('__web_directory__', $this->webDirectory) + ->replace('__domain__', $this->domain) + ->toString(); + } +} diff --git a/app/SSHCommands/SSL/InstallCertbotCommand.php b/app/SSHCommands/SSL/InstallCertbotCommand.php new file mode 100755 index 0000000..b13e8f5 --- /dev/null +++ b/app/SSHCommands/SSL/InstallCertbotCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/SSL/RemoveSSLCommand.php b/app/SSHCommands/SSL/RemoveSSLCommand.php new file mode 100755 index 0000000..b76a307 --- /dev/null +++ b/app/SSHCommands/SSL/RemoveSSLCommand.php @@ -0,0 +1,22 @@ +path.'*'."\n"; + } +} diff --git a/app/SSHCommands/Service/RestartServiceCommand.php b/app/SSHCommands/Service/RestartServiceCommand.php new file mode 100644 index 0000000..14a5ab3 --- /dev/null +++ b/app/SSHCommands/Service/RestartServiceCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__service__', $this->unit) + ->toString(); + } +} diff --git a/app/SSHCommands/Service/ServiceStatusCommand.php b/app/SSHCommands/Service/ServiceStatusCommand.php new file mode 100755 index 0000000..1434546 --- /dev/null +++ b/app/SSHCommands/Service/ServiceStatusCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__service__', $this->unit) + ->toString(); + } +} diff --git a/app/SSHCommands/Service/StartServiceCommand.php b/app/SSHCommands/Service/StartServiceCommand.php new file mode 100644 index 0000000..f4b616b --- /dev/null +++ b/app/SSHCommands/Service/StartServiceCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__service__', $this->unit) + ->toString(); + } +} diff --git a/app/SSHCommands/Service/StopServiceCommand.php b/app/SSHCommands/Service/StopServiceCommand.php new file mode 100644 index 0000000..b9a99bb --- /dev/null +++ b/app/SSHCommands/Service/StopServiceCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__service__', $this->unit) + ->toString(); + } +} diff --git a/app/SSHCommands/ServiceStatusCommand.php b/app/SSHCommands/ServiceStatusCommand.php deleted file mode 100755 index c16a46a..0000000 --- a/app/SSHCommands/ServiceStatusCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -unit = $unit; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/service-status.sh')); - } - - public function content(string $os): string - { - return Str::replace('__service__', $this->unit, $this->file($os)); - } -} diff --git a/app/SSHCommands/StartServiceCommand.php b/app/SSHCommands/StartServiceCommand.php deleted file mode 100644 index 4224bc5..0000000 --- a/app/SSHCommands/StartServiceCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -unit = $unit; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/start-service.sh')); - } - - public function content(string $os): string - { - return Str::replace('__service__', $this->unit, $this->file($os)); - } -} diff --git a/app/SSHCommands/StopServiceCommand.php b/app/SSHCommands/StopServiceCommand.php deleted file mode 100644 index c7a422b..0000000 --- a/app/SSHCommands/StopServiceCommand.php +++ /dev/null @@ -1,32 +0,0 @@ -unit = $unit; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/stop-service.sh')); - } - - public function content(string $os): string - { - return Str::replace('__service__', $this->unit, $this->file($os)); - } -} diff --git a/app/SSHCommands/Storage/DownloadFromDropboxCommand.php b/app/SSHCommands/Storage/DownloadFromDropboxCommand.php index b6857ee..e6ba695 100644 --- a/app/SSHCommands/Storage/DownloadFromDropboxCommand.php +++ b/app/SSHCommands/Storage/DownloadFromDropboxCommand.php @@ -4,34 +4,24 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class DownloadFromDropboxCommand extends Command { - protected $src; - - protected $dest; - - protected $token; - - public function __construct($src, $dest, $token) + public function __construct(protected string $src, protected string $dest, protected string $token) { - $this->src = $src; - $this->dest = $dest; - $this->token = $token; } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/common/storage/download-from-dropbox.sh')); + return File::get(resource_path('commands/storage/download-from-dropbox.sh')); } - public function content(string $os): string + public function content(): string { - $command = $this->file($os); - $command = Str::replace('__src__', $this->src, $command); - $command = Str::replace('__dest__', $this->dest, $command); - - return Str::replace('__token__', $this->token, $command); + return str($this->file()) + ->replace('__src__', $this->src) + ->replace('__dest__', $this->dest) + ->replace('__token__', $this->token) + ->toString(); } } diff --git a/app/SSHCommands/Storage/UploadToDropboxCommand.php b/app/SSHCommands/Storage/UploadToDropboxCommand.php index 8025e97..29498f3 100644 --- a/app/SSHCommands/Storage/UploadToDropboxCommand.php +++ b/app/SSHCommands/Storage/UploadToDropboxCommand.php @@ -4,34 +4,24 @@ use App\SSHCommands\Command; use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; class UploadToDropboxCommand extends Command { - protected $src; - - protected $dest; - - protected $token; - - public function __construct($src, $dest, $token) + public function __construct(protected string $src, protected string $dest, protected string $token) { - $this->src = $src; - $this->dest = $dest; - $this->token = $token; } - public function file(string $os): string + public function file(): string { - return File::get(base_path('system/commands/common/storage/upload-to-dropbox.sh')); + return File::get(resource_path('commands/storage/upload-to-dropbox.sh')); } - public function content(string $os): string + public function content(): string { - $command = $this->file($os); - $command = Str::replace('__src__', $this->src, $command); - $command = Str::replace('__dest__', $this->dest, $command); - - return Str::replace('__token__', $this->token, $command); + return str($this->file()) + ->replace('__src__', $this->src) + ->replace('__dest__', $this->dest) + ->replace('__token__', $this->token) + ->toString(); } } diff --git a/app/SSHCommands/Supervisor/CreateWorkerCommand.php b/app/SSHCommands/Supervisor/CreateWorkerCommand.php new file mode 100644 index 0000000..197c7a9 --- /dev/null +++ b/app/SSHCommands/Supervisor/CreateWorkerCommand.php @@ -0,0 +1,26 @@ +file()) + ->replace('__id__', $this->id) + ->replace('__config__', $this->config) + ->toString(); + } +} diff --git a/app/SSHCommands/Supervisor/DeleteWorkerCommand.php b/app/SSHCommands/Supervisor/DeleteWorkerCommand.php new file mode 100644 index 0000000..0171772 --- /dev/null +++ b/app/SSHCommands/Supervisor/DeleteWorkerCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__id__', $this->id) + ->toString(); + } +} diff --git a/app/SSHCommands/Supervisor/InstallSupervisorCommand.php b/app/SSHCommands/Supervisor/InstallSupervisorCommand.php new file mode 100755 index 0000000..8808741 --- /dev/null +++ b/app/SSHCommands/Supervisor/InstallSupervisorCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/Supervisor/RestartWorkerCommand.php b/app/SSHCommands/Supervisor/RestartWorkerCommand.php new file mode 100644 index 0000000..12a1006 --- /dev/null +++ b/app/SSHCommands/Supervisor/RestartWorkerCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__id__', $this->id) + ->toString(); + } +} diff --git a/app/SSHCommands/Supervisor/StartWorkerCommand.php b/app/SSHCommands/Supervisor/StartWorkerCommand.php new file mode 100644 index 0000000..f2732bf --- /dev/null +++ b/app/SSHCommands/Supervisor/StartWorkerCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__id__', $this->id) + ->toString(); + } +} diff --git a/app/SSHCommands/Supervisor/StopWorkerCommand.php b/app/SSHCommands/Supervisor/StopWorkerCommand.php new file mode 100644 index 0000000..32125e0 --- /dev/null +++ b/app/SSHCommands/Supervisor/StopWorkerCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__id__', $this->id) + ->toString(); + } +} diff --git a/app/SSHCommands/System/CreateUserCommand.php b/app/SSHCommands/System/CreateUserCommand.php new file mode 100755 index 0000000..adc7e39 --- /dev/null +++ b/app/SSHCommands/System/CreateUserCommand.php @@ -0,0 +1,27 @@ +file()) + ->replace('__user__', $this->user) + ->replace('__key__', $this->key) + ->replace('__password__', $this->password) + ->toString(); + } +} diff --git a/app/SSHCommands/System/DeleteSshKeyCommand.php b/app/SSHCommands/System/DeleteSshKeyCommand.php new file mode 100755 index 0000000..6eb46ca --- /dev/null +++ b/app/SSHCommands/System/DeleteSshKeyCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__key__', str($this->key)->replace('/', '\/')) + ->toString(); + } +} diff --git a/app/SSHCommands/System/DeploySshKeyCommand.php b/app/SSHCommands/System/DeploySshKeyCommand.php new file mode 100755 index 0000000..fd228d1 --- /dev/null +++ b/app/SSHCommands/System/DeploySshKeyCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__key__', addslashes($this->key)) + ->toString(); + } +} diff --git a/app/SSHCommands/System/EditFileCommand.php b/app/SSHCommands/System/EditFileCommand.php new file mode 100644 index 0000000..267ae80 --- /dev/null +++ b/app/SSHCommands/System/EditFileCommand.php @@ -0,0 +1,26 @@ +file()) + ->replace('__path__', $this->path) + ->replace('__content__', $this->content) + ->toString(); + } +} diff --git a/app/SSHCommands/System/GenerateSshKeyCommand.php b/app/SSHCommands/System/GenerateSshKeyCommand.php new file mode 100755 index 0000000..29077c6 --- /dev/null +++ b/app/SSHCommands/System/GenerateSshKeyCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__name__', $this->name) + ->toString(); + } +} diff --git a/app/SSHCommands/System/GetPublicKeyCommand.php b/app/SSHCommands/System/GetPublicKeyCommand.php new file mode 100755 index 0000000..2ebad7f --- /dev/null +++ b/app/SSHCommands/System/GetPublicKeyCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/System/ReadFileCommand.php b/app/SSHCommands/System/ReadFileCommand.php new file mode 100644 index 0000000..943dd3a --- /dev/null +++ b/app/SSHCommands/System/ReadFileCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__path__', $this->path) + ->toString(); + } +} diff --git a/app/SSHCommands/System/ReadSshKeyCommand.php b/app/SSHCommands/System/ReadSshKeyCommand.php new file mode 100755 index 0000000..7a89529 --- /dev/null +++ b/app/SSHCommands/System/ReadSshKeyCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__name__', $this->name) + ->toString(); + } +} diff --git a/app/SSHCommands/System/RebootCommand.php b/app/SSHCommands/System/RebootCommand.php new file mode 100644 index 0000000..0e0a7da --- /dev/null +++ b/app/SSHCommands/System/RebootCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/System/RunScript.php b/app/SSHCommands/System/RunScript.php new file mode 100644 index 0000000..3ec539e --- /dev/null +++ b/app/SSHCommands/System/RunScript.php @@ -0,0 +1,26 @@ +file()) + ->replace('__path__', $this->path) + ->replace('__script__', make_bash_script($this->script)) + ->toString(); + } +} diff --git a/app/SSHCommands/System/UpgradeCommand.php b/app/SSHCommands/System/UpgradeCommand.php new file mode 100755 index 0000000..20607ad --- /dev/null +++ b/app/SSHCommands/System/UpgradeCommand.php @@ -0,0 +1,19 @@ +file(); + } +} diff --git a/app/SSHCommands/UninstallPHPCommand.php b/app/SSHCommands/UninstallPHPCommand.php deleted file mode 100755 index fdba407..0000000 --- a/app/SSHCommands/UninstallPHPCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -version = $version; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/uninstall-php.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__version__', $this->version, $this->file($os)); - - return Str::replace('__user__', config('core.ssh_user'), $command); - } -} diff --git a/app/SSHCommands/UpdateBranchCommand.php b/app/SSHCommands/UpdateBranchCommand.php deleted file mode 100644 index d38f768..0000000 --- a/app/SSHCommands/UpdateBranchCommand.php +++ /dev/null @@ -1,31 +0,0 @@ -path = $path; - $this->branch = $branch; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/update-branch.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__path__', $this->path, $this->file($os)); - - return Str::replace('__branch__', $this->branch, $command); - } -} diff --git a/app/SSHCommands/UpdateCronJobsCommand.php b/app/SSHCommands/UpdateCronJobsCommand.php deleted file mode 100755 index 540eda9..0000000 --- a/app/SSHCommands/UpdateCronJobsCommand.php +++ /dev/null @@ -1,34 +0,0 @@ -user = $user; - $this->data = $data; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/common/update-cron-jobs.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__user__', $this->user, $this->file($os)); - - return Str::replace('__data__', $this->data, $command); - } -} diff --git a/app/SSHCommands/UpdateNginxRedirectsCommand.php b/app/SSHCommands/UpdateNginxRedirectsCommand.php deleted file mode 100755 index c17d551..0000000 --- a/app/SSHCommands/UpdateNginxRedirectsCommand.php +++ /dev/null @@ -1,43 +0,0 @@ -domain = $domain; - $this->redirects = $redirects; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/webserver/nginx/update-redirects.sh')); - } - - public function content(string $os): string - { - info($this->redirects); - $command = Str::replace('__redirects__', addslashes($this->redirects), $this->file($os)); - - return Str::replace('__domain__', $this->domain, $command); - } -} diff --git a/app/SSHCommands/UpdateNginxVHostCommand.php b/app/SSHCommands/UpdateNginxVHostCommand.php deleted file mode 100755 index dd4d842..0000000 --- a/app/SSHCommands/UpdateNginxVHostCommand.php +++ /dev/null @@ -1,50 +0,0 @@ -domain = $domain; - $this->path = $path; - $this->vhost = $vhost; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/webserver/nginx/update-vhost.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__path__', $this->path, $this->file($os)); - $command = Str::replace('__domain__', $this->domain, $command); - - return Str::replace('__vhost__', $this->vhost, $command); - } -} diff --git a/app/SSHCommands/UpdatePHPSettingsCommand.php b/app/SSHCommands/UpdatePHPSettingsCommand.php deleted file mode 100755 index c13b1f9..0000000 --- a/app/SSHCommands/UpdatePHPSettingsCommand.php +++ /dev/null @@ -1,41 +0,0 @@ -version = $version; - $this->variable = $variable; - $this->value = $value; - } - - public function file(string $os): string - { - return File::get(base_path('system/commands/ubuntu/update-php-settings.sh')); - } - - public function content(string $os): string - { - $command = Str::replace('__version__', $this->version, $this->file($os)); - $command = Str::replace('__variable__', $this->variable, $command); - - return Str::replace('__value__', $this->value, $command); - } -} diff --git a/app/SSHCommands/UpdateWordpressCommand.php b/app/SSHCommands/UpdateWordpressCommand.php deleted file mode 100755 index 2a0351e..0000000 --- a/app/SSHCommands/UpdateWordpressCommand.php +++ /dev/null @@ -1,50 +0,0 @@ -path = $path; - $this->url = $url; - $this->username = $username; - $this->password = $password; - $this->email = $email; - $this->title = $title; - } - - public function file(string $os): string - { - return ''; - } - - public function content(string $os): string - { - $command = ''; - if ($this->title) { - $command .= 'wp --path='.$this->path.' option update blogname "'.addslashes($this->title).'"'."\n"; - } - if ($this->url) { - $command .= 'wp --path='.$this->path.' option update siteurl "'.addslashes($this->url).'"'."\n"; - $command .= 'wp --path='.$this->path.' option update home "'.addslashes($this->url).'"'."\n"; - } - - return $command; - } -} diff --git a/app/SSHCommands/UpgradeCommand.php b/app/SSHCommands/UpgradeCommand.php deleted file mode 100755 index 030e5d0..0000000 --- a/app/SSHCommands/UpgradeCommand.php +++ /dev/null @@ -1,18 +0,0 @@ -file($os); - } -} diff --git a/app/SSHCommands/Website/CloneRepositoryCommand.php b/app/SSHCommands/Website/CloneRepositoryCommand.php new file mode 100755 index 0000000..2b28b78 --- /dev/null +++ b/app/SSHCommands/Website/CloneRepositoryCommand.php @@ -0,0 +1,33 @@ +file()) + ->replace('__repo__', $this->repository) + ->replace('__host__', str($this->repository)->after('@')->before('-')) + ->replace('__branch__', $this->branch) + ->replace('__path__', $this->path) + ->replace('__key__', $this->privateKeyPath) + ->toString(); + } +} diff --git a/app/SSHCommands/Website/ComposerInstallCommand.php b/app/SSHCommands/Website/ComposerInstallCommand.php new file mode 100755 index 0000000..182536e --- /dev/null +++ b/app/SSHCommands/Website/ComposerInstallCommand.php @@ -0,0 +1,25 @@ +file()) + ->replace('__path__', $this->path) + ->toString(); + } +} diff --git a/app/SSHCommands/Website/UpdateBranchCommand.php b/app/SSHCommands/Website/UpdateBranchCommand.php new file mode 100644 index 0000000..652ae77 --- /dev/null +++ b/app/SSHCommands/Website/UpdateBranchCommand.php @@ -0,0 +1,26 @@ +file()) + ->replace('__path__', $this->path) + ->replace('__branch__', $this->branch) + ->toString(); + } +} diff --git a/app/SSHCommands/Wordpress/InstallWordpressCommand.php b/app/SSHCommands/Wordpress/InstallWordpressCommand.php new file mode 100755 index 0000000..3432860 --- /dev/null +++ b/app/SSHCommands/Wordpress/InstallWordpressCommand.php @@ -0,0 +1,46 @@ +file()) + ->replace('__path__', $this->path) + ->replace('__domain__', $this->domain) + ->replace('__db_name__', $this->dbName) + ->replace('__db_user__', $this->dbUser) + ->replace('__db_pass__', $this->dbPass) + ->replace('__db_host__', $this->dbHost) + ->replace('__db_prefix__', $this->dbPrefix) + ->replace('__username__', $this->username) + ->replace('__password__', $this->password) + ->replace('__title__', $this->title) + ->replace('__email__', $this->email) + ->toString(); + } +} diff --git a/app/SSHCommands/Wordpress/UpdateWordpressCommand.php b/app/SSHCommands/Wordpress/UpdateWordpressCommand.php new file mode 100755 index 0000000..db0bb38 --- /dev/null +++ b/app/SSHCommands/Wordpress/UpdateWordpressCommand.php @@ -0,0 +1,37 @@ +title) { + $command .= 'wp --path='.$this->path.' option update blogname "'.addslashes($this->title).'"'."\n"; + } + if ($this->url) { + $command .= 'wp --path='.$this->path.' option update siteurl "'.addslashes($this->url).'"'."\n"; + $command .= 'wp --path='.$this->path.' option update home "'.addslashes($this->url).'"'."\n"; + } + + return $command; + } +} diff --git a/app/ServerProviders/AWS.php b/app/ServerProviders/AWS.php index 7a9e75f..0e5c6ac 100755 --- a/app/ServerProviders/AWS.php +++ b/app/ServerProviders/AWS.php @@ -20,7 +20,6 @@ class AWS extends AbstractProvider public function createValidationRules(array $input): array { $rules = [ - 'size' => 'required|numeric|min:15|max:16000', 'os' => 'required|in:'.implode(',', OperatingSystem::getValues()), ]; // plans @@ -36,6 +35,8 @@ public function createValidationRules(array $input): array } $rules['region'] = 'required|in:'.implode(',', $regions); + Log::info("AWS Creation Rules", $rules); + return $rules; } @@ -60,7 +61,6 @@ public function data(array $input): array return [ 'plan' => $input['plan'], 'region' => $input['region'], - 'size' => $input['size'], ]; } diff --git a/app/ServerProviders/Custom.php b/app/ServerProviders/Custom.php index 265aae1..dee211d 100755 --- a/app/ServerProviders/Custom.php +++ b/app/ServerProviders/Custom.php @@ -3,6 +3,8 @@ namespace App\ServerProviders; use App\ValidationRules\RestrictedIPAddressesRule; +use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Storage; use Illuminate\Validation\Rule; class Custom extends AbstractProvider @@ -57,7 +59,14 @@ public function regions(): array public function create(): void { - $this->generateKeyPair(); + File::copy( + storage_path(config('core.ssh_private_key_name')), + Storage::disk(config('core.key_pairs_disk'))->path($this->server->id) + ); + File::copy( + storage_path(config('core.ssh_public_key_name')), + Storage::disk(config('core.key_pairs_disk'))->path($this->server->id.'.pub') + ); } public function isRunning(): bool diff --git a/app/ServerProviders/Linode.php b/app/ServerProviders/Linode.php index dbdbafb..b56d450 100644 --- a/app/ServerProviders/Linode.php +++ b/app/ServerProviders/Linode.php @@ -6,6 +6,7 @@ use App\Exceptions\ServerProviderError; use App\Notifications\FailedToDeleteServerFromProvider; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Log; class Linode extends AbstractProvider { @@ -103,6 +104,7 @@ public function create(): void if (count($errors) > 0) { $msg = $errors[0]['reason']; } + Log::error("Linode error", $errors); throw new ServerProviderError($msg); } $this->server->ip = $create->json()['ipv4'][0]; diff --git a/app/ServerTypes/Regular.php b/app/ServerTypes/Regular.php index 6d5e9d5..42a0482 100755 --- a/app/ServerTypes/Regular.php +++ b/app/ServerTypes/Regular.php @@ -53,7 +53,7 @@ public function createServices(array $input): void public function install(): void { $jobs = [ - new Initialize($this->server, $this->server->ssh_user, $this->server->provider === 'custom'), + new Initialize($this->server, $this->server->ssh_user), $this->progress(15, 'Installing Updates'), new Upgrade($this->server), $this->progress(25, 'Installing Requirements'), diff --git a/app/ServiceHandlers/Firewall/Ufw.php b/app/ServiceHandlers/Firewall/Ufw.php index 30dea50..d6dd8e7 100755 --- a/app/ServiceHandlers/Firewall/Ufw.php +++ b/app/ServiceHandlers/Firewall/Ufw.php @@ -11,7 +11,7 @@ class Ufw extends AbstractFirewall /** * @throws Throwable */ - public function addRule(string $type, string $protocol, int $port, string $source, string $mask): void + public function addRule(string $type, string $protocol, int $port, string $source, ?string $mask): void { $this->service->server->ssh()->exec( new AddRuleCommand('ufw', $type, $protocol, $port, $source, $mask), @@ -22,7 +22,7 @@ public function addRule(string $type, string $protocol, int $port, string $sourc /** * @throws Throwable */ - public function removeRule(string $type, string $protocol, int $port, string $source, string $mask): void + public function removeRule(string $type, string $protocol, int $port, string $source, ?string $mask): void { $this->service->server->ssh()->exec( new RemoveRuleCommand('ufw', $type, $protocol, $port, $source, $mask), diff --git a/app/ServiceHandlers/PHP.php b/app/ServiceHandlers/PHP.php index 471debd..fb337b6 100644 --- a/app/ServiceHandlers/PHP.php +++ b/app/ServiceHandlers/PHP.php @@ -5,7 +5,6 @@ use App\Enums\ServiceStatus; use App\Jobs\PHP\InstallPHPExtension; use App\Jobs\PHP\SetDefaultCli; -use App\Jobs\PHP\UpdatePHPSettings; use App\Models\Service; class PHP @@ -28,9 +27,4 @@ public function installExtension($name): void { dispatch(new InstallPHPExtension($this->service, $name))->onConnection('ssh-long'); } - - public function updateSettings(array $settings): void - { - dispatch(new UpdatePHPSettings($this->service, $settings))->onConnection('ssh-long'); - } } diff --git a/app/ServiceHandlers/ProcessManager/Supervisor.php b/app/ServiceHandlers/ProcessManager/Supervisor.php index 3501085..091a53a 100644 --- a/app/ServiceHandlers/ProcessManager/Supervisor.php +++ b/app/ServiceHandlers/ProcessManager/Supervisor.php @@ -2,11 +2,11 @@ namespace App\ServiceHandlers\ProcessManager; -use App\SSHCommands\ProcessManager\Supervisor\CreateWorkerCommand; -use App\SSHCommands\ProcessManager\Supervisor\DeleteWorkerCommand; -use App\SSHCommands\ProcessManager\Supervisor\RestartWorkerCommand; -use App\SSHCommands\ProcessManager\Supervisor\StartWorkerCommand; -use App\SSHCommands\ProcessManager\Supervisor\StopWorkerCommand; +use App\SSHCommands\Supervisor\CreateWorkerCommand; +use App\SSHCommands\Supervisor\DeleteWorkerCommand; +use App\SSHCommands\Supervisor\RestartWorkerCommand; +use App\SSHCommands\Supervisor\StartWorkerCommand; +use App\SSHCommands\Supervisor\StopWorkerCommand; use Illuminate\Support\Facades\File; use Illuminate\Support\Str; use Throwable; @@ -111,7 +111,7 @@ private function generateConfigFile( int $numprocs, string $logFile ): string { - $config = File::get(base_path('system/command-templates/supervisor/worker.conf')); + $config = File::get(resource_path('commands/supervisor/worker.conf')); $config = Str::replace('__name__', (string) $id, $config); $config = Str::replace('__command__', $command, $config); $config = Str::replace('__user__', $user, $config); diff --git a/app/ServiceHandlers/Webserver/Nginx.php b/app/ServiceHandlers/Webserver/Nginx.php index 0a0ce22..7cfef04 100755 --- a/app/ServiceHandlers/Webserver/Nginx.php +++ b/app/ServiceHandlers/Webserver/Nginx.php @@ -6,14 +6,14 @@ use App\Exceptions\SSLCreationException; use App\Models\Site; use App\Models\Ssl; -use App\SSHCommands\ChangeNginxPHPVersionCommand; -use App\SSHCommands\CreateCustomSSLCommand; -use App\SSHCommands\CreateLetsencryptSSLCommand; -use App\SSHCommands\CreateNginxVHostCommand; -use App\SSHCommands\DeleteNginxSiteCommand; -use App\SSHCommands\RemoveSSLCommand; -use App\SSHCommands\UpdateNginxRedirectsCommand; -use App\SSHCommands\UpdateNginxVHostCommand; +use App\SSHCommands\Nginx\ChangeNginxPHPVersionCommand; +use App\SSHCommands\Nginx\CreateNginxVHostCommand; +use App\SSHCommands\Nginx\DeleteNginxSiteCommand; +use App\SSHCommands\Nginx\UpdateNginxRedirectsCommand; +use App\SSHCommands\Nginx\UpdateNginxVHostCommand; +use App\SSHCommands\SSL\CreateCustomSSLCommand; +use App\SSHCommands\SSL\CreateLetsencryptSSLCommand; +use App\SSHCommands\SSL\RemoveSSLCommand; use Illuminate\Support\Facades\File; use Illuminate\Support\Str; use Throwable; @@ -132,7 +132,7 @@ public function updateRedirects(Site $site, array $redirects): void { $redirectsPlain = ''; foreach ($redirects as $redirect) { - $rd = File::get(base_path('system/command-templates/nginx/redirect.conf')); + $rd = File::get(resource_path('commands/webserver/nginx/redirect.conf')); $rd = Str::replace('__from__', $redirect->from, $rd); $rd = Str::replace('__mode__', $redirect->mode, $rd); $rd = Str::replace('__to__', $redirect->to, $rd); @@ -157,20 +157,20 @@ protected function generateVhost(Site $site, bool $noSSL = false): string if ($noSSL) { $ssl = null; } - $vhost = File::get(base_path('system/command-templates/nginx/vhost.conf')); + $vhost = File::get(resource_path('commands/webserver/nginx/vhost.conf')); if ($ssl) { - $vhost = File::get(base_path('system/command-templates/nginx/vhost-ssl.conf')); + $vhost = File::get(resource_path('commands/webserver/nginx/vhost-ssl.conf')); } if ($site->type()->language() === 'php') { - $vhost = File::get(base_path('system/command-templates/nginx/php-vhost.conf')); + $vhost = File::get(resource_path('commands/webserver/nginx/php-vhost.conf')); if ($ssl) { - $vhost = File::get(base_path('system/command-templates/nginx/php-vhost-ssl.conf')); + $vhost = File::get(resource_path('commands/webserver/nginx/php-vhost-ssl.conf')); } } if ($site->port) { - $vhost = File::get(base_path('system/command-templates/nginx/reverse-vhost.conf')); + $vhost = File::get(resource_path('commands/webserver/nginx/reverse-vhost.conf')); if ($ssl) { - $vhost = File::get(base_path('system/command-templates/nginx/reverse-vhost-ssl.conf')); + $vhost = File::get(resource_path('commands/webserver/nginx/reverse-vhost-ssl.conf')); } $vhost = Str::replace('__port__', (string) $site->port, $vhost); } diff --git a/app/SiteTypes/AbstractSiteType.php b/app/SiteTypes/AbstractSiteType.php index 22a29a8..055e365 100755 --- a/app/SiteTypes/AbstractSiteType.php +++ b/app/SiteTypes/AbstractSiteType.php @@ -22,6 +22,11 @@ public function delete(): void dispatch(new DeleteSite($this->site))->onConnection('ssh'); } + public function install(): void + { + // TODO: Implement install() method. + } + protected function progress(int $percentage): Closure { return function () use ($percentage) { diff --git a/app/SiteTypes/PHPSite.php b/app/SiteTypes/PHPSite.php index c503f4a..4373933 100755 --- a/app/SiteTypes/PHPSite.php +++ b/app/SiteTypes/PHPSite.php @@ -7,6 +7,7 @@ use App\Jobs\Site\CloneRepository; use App\Jobs\Site\ComposerInstall; use App\Jobs\Site\CreateVHost; +use App\Jobs\Site\DeployKey; use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; @@ -28,7 +29,7 @@ public function createValidationRules(array $input): array ], 'source_control' => [ 'required', - Rule::exists('source_controls', 'provider'), + Rule::exists('source_controls', 'id'), ], 'repository' => [ 'required', @@ -43,7 +44,7 @@ public function createFields(array $input): array { return [ 'web_directory' => $input['web_directory'] ?? '', - 'source_control' => $input['source_control'] ?? '', + 'source_control_id' => $input['source_control'] ?? '', 'repository' => $input['repository'] ?? '', 'branch' => $input['branch'] ?? '', ]; @@ -60,6 +61,8 @@ public function install(): void { $chain = [ new CreateVHost($this->site), + $this->progress(15), + new DeployKey($this->site), $this->progress(30), new CloneRepository($this->site), $this->progress(65), diff --git a/app/SiteTypes/Wordpress.php b/app/SiteTypes/Wordpress.php index a01e477..5d157fa 100755 --- a/app/SiteTypes/Wordpress.php +++ b/app/SiteTypes/Wordpress.php @@ -6,7 +6,7 @@ use App\Events\Broadcast; use App\Jobs\Site\CreateVHost; use App\Jobs\Site\InstallWordpress; -use App\SSHCommands\UpdateWordpressCommand; +use App\SSHCommands\Wordpress\UpdateWordpressCommand; use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Log; use Throwable; diff --git a/app/SourceControlProviders/Bitbucket.php b/app/SourceControlProviders/Bitbucket.php index fafac2b..209fc6d 100755 --- a/app/SourceControlProviders/Bitbucket.php +++ b/app/SourceControlProviders/Bitbucket.php @@ -3,7 +3,11 @@ namespace App\SourceControlProviders; use App\Exceptions\FailedToDeployGitHook; +use App\Exceptions\FailedToDeployGitKey; use App\Exceptions\FailedToDestroyGitHook; +use App\Exceptions\RepositoryNotFound; +use App\Exceptions\RepositoryPermissionDenied; +use App\Exceptions\SourceControlIsNotConnected; use Exception; use Illuminate\Support\Facades\Http; use Illuminate\Support\Str; @@ -33,9 +37,9 @@ public function getRepo(string $repo = null): mixed return $res->json(); } - public function fullRepoUrl(string $repo): string + public function fullRepoUrl(string $repo, string $key): string { - return "https://x-token-auth:{$this->sourceControl->access_token}@bitbucket.org/$repo.git"; + return sprintf("git@bitbucket.org-%s:%s.git", $key, $repo); } /** @@ -102,6 +106,24 @@ public function getLastCommit(string $repo, string $branch): ?array return null; } + /** + * @throws FailedToDeployGitKey + */ + public function deployKey(string $title, string $repo, string $key): void + { + $res = Http::withToken($this->sourceControl->access_token)->post( + $this->apiUrl."/repositories/$repo/deploy-keys", + [ + 'label' => $title, + 'key' => $key, + ] + ); + + if ($res->status() != 201) { + throw new FailedToDeployGitKey($res->json()['error']['message']); + } + } + protected function getCommitter(string $raw): array { $committer = explode(' <', $raw); diff --git a/app/SourceControlProviders/Custom.php b/app/SourceControlProviders/Custom.php index d7bb22d..33a421f 100755 --- a/app/SourceControlProviders/Custom.php +++ b/app/SourceControlProviders/Custom.php @@ -14,7 +14,7 @@ public function getRepo(string $repo = null): string return ''; } - public function fullRepoUrl(string $repo): string + public function fullRepoUrl(string $repo, string $key): string { return $repo; } @@ -33,4 +33,9 @@ public function getLastCommit(string $repo, string $branch): ?array { return null; } + + public function deployKey(string $title, string $repo, string $key): void + { + // TODO: Implement deployKey() method. + } } diff --git a/app/SourceControlProviders/Github.php b/app/SourceControlProviders/Github.php index 621dd47..6461069 100755 --- a/app/SourceControlProviders/Github.php +++ b/app/SourceControlProviders/Github.php @@ -2,6 +2,7 @@ namespace App\SourceControlProviders; +use App\Exceptions\FailedToDeployGitKey; use App\Exceptions\FailedToDeployGitHook; use App\Exceptions\FailedToDestroyGitHook; use Exception; @@ -41,9 +42,9 @@ public function getRepo(string $repo = null): mixed return $res->json(); } - public function fullRepoUrl(string $repo): string + public function fullRepoUrl(string $repo, string $key): string { - return "https://{$this->sourceControl->access_token}@github.com/$repo.git"; + return sprintf("git@github.com-%s:%s.git", $key, $repo); } /** @@ -117,4 +118,25 @@ public function getLastCommit(string $repo, string $branch): ?array return null; } + + /** + * @throws FailedToDeployGitKey + */ + public function deployKey(string $title, string $repo, string $key): void + { + $response = Http::withToken($this->sourceControl->access_token)->post( + $this->apiUrl.'/repos/'.$repo.'/keys', + [ + 'title' => $title, + 'key' => $key, + 'read_only' => false, + ] + ); + + info('github response', $response->json()); + + if ($response->status() != 201) { + throw new FailedToDeployGitKey(json_decode($response->body())->message); + } + } } diff --git a/app/SourceControlProviders/Gitlab.php b/app/SourceControlProviders/Gitlab.php index 2921b57..a6aef52 100755 --- a/app/SourceControlProviders/Gitlab.php +++ b/app/SourceControlProviders/Gitlab.php @@ -2,6 +2,7 @@ namespace App\SourceControlProviders; +use App\Exceptions\FailedToDeployGitKey; use App\Exceptions\FailedToDeployGitHook; use App\Exceptions\FailedToDestroyGitHook; use Exception; @@ -33,9 +34,9 @@ public function getRepo(string $repo = null): mixed return $res->json(); } - public function fullRepoUrl(string $repo): string + public function fullRepoUrl(string $repo, string $key): string { - return 'https://oauth2:'.$this->sourceControl->access_token.'@gitlab.com/'.$repo.'.git'; + return sprintf("git@gitlab.com-%s:%s.git", $key, $repo); } /** @@ -114,4 +115,24 @@ public function getLastCommit(string $repo, string $branch): ?array return null; } + + /** + * @throws FailedToDeployGitKey + */ + public function deployKey(string $title, string $repo, string $key): void + { + $repository = urlencode($repo); + $response = Http::withToken($this->sourceControl->access_token)->post( + $this->apiUrl.'/projects/'.$repository.'/deploy_keys', + [ + 'title' => $title, + 'key' => $key, + 'can_push' => true, + ] + ); + + if ($response->status() != 201) { + throw new FailedToDeployGitKey(json_decode($response->body())->message); + } + } } diff --git a/app/Traits/RefreshComponentOnBroadcast.php b/app/Traits/RefreshComponentOnBroadcast.php index 447d759..c77ff3c 100644 --- a/app/Traits/RefreshComponentOnBroadcast.php +++ b/app/Traits/RefreshComponentOnBroadcast.php @@ -7,7 +7,7 @@ trait RefreshComponentOnBroadcast public function getListeners(): array { return [ - 'echo-private:app,Broadcast' => 'refreshComponent', + 'broadcast' => 'refreshComponent', 'refreshComponent' => '$refresh', '$refresh', ]; diff --git a/composer.json b/composer.json index 396ba20..c8caa89 100644 --- a/composer.json +++ b/composer.json @@ -8,13 +8,14 @@ "php": "^8.1", "aws/aws-sdk-php": "^3.158", "bensampo/laravel-enum": "^6.3", - "blade-ui-kit/blade-heroicons": "^2.1", "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^10.0", "laravel/sanctum": "^3.2", "laravel/socialite": "^5.2", "laravel/tinker": "^2.8", "livewire/livewire": "^2.12", + "opcodesio/log-viewer": "^2.5", + "phpseclib/phpseclib": "~3.0", "pusher/pusher-php-server": "^7.2" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 759bc16..6959036 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "36fda0bafd1bd5b298307b02bb98fc0a", + "content-hash": "662f1820db3e99f0684657a45feb4f2c", "packages": [ { "name": "aws/aws-crt-php", @@ -238,156 +238,6 @@ ], "time": "2023-02-13T14:09:29+00:00" }, - { - "name": "blade-ui-kit/blade-heroicons", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/blade-ui-kit/blade-heroicons.git", - "reference": "f756c807b0d04afd2caf7079bac26492da9cc6d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/blade-ui-kit/blade-heroicons/zipball/f756c807b0d04afd2caf7079bac26492da9cc6d4", - "reference": "f756c807b0d04afd2caf7079bac26492da9cc6d4", - "shasum": "" - }, - "require": { - "blade-ui-kit/blade-icons": "^1.1", - "illuminate/support": "^9.0|^10.0", - "php": "^8.0" - }, - "require-dev": { - "orchestra/testbench": "^7.0|^8.0", - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "BladeUI\\Heroicons\\BladeHeroiconsServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "BladeUI\\Heroicons\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dries Vints", - "homepage": "https://driesvints.com" - } - ], - "description": "A package to easily make use of Heroicons in your Laravel Blade views.", - "homepage": "https://github.com/blade-ui-kit/blade-heroicons", - "keywords": [ - "Heroicons", - "blade", - "laravel" - ], - "support": { - "issues": "https://github.com/blade-ui-kit/blade-heroicons/issues", - "source": "https://github.com/blade-ui-kit/blade-heroicons/tree/2.1.0" - }, - "funding": [ - { - "url": "https://github.com/caneco", - "type": "github" - }, - { - "url": "https://github.com/driesvints", - "type": "github" - } - ], - "time": "2023-01-11T08:38:22+00:00" - }, - { - "name": "blade-ui-kit/blade-icons", - "version": "1.5.1", - "source": { - "type": "git", - "url": "https://github.com/blade-ui-kit/blade-icons.git", - "reference": "b2a80ff2a26641f64bfee48ad0d2a922ce781228" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/blade-ui-kit/blade-icons/zipball/b2a80ff2a26641f64bfee48ad0d2a922ce781228", - "reference": "b2a80ff2a26641f64bfee48ad0d2a922ce781228", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^8.0|^9.0|^10.0", - "illuminate/filesystem": "^8.0|^9.0|^10.0", - "illuminate/support": "^8.0|^9.0|^10.0", - "illuminate/view": "^8.0|^9.0|^10.0", - "php": "^7.4|^8.0", - "symfony/console": "^5.3|^6.0", - "symfony/finder": "^5.3|^6.0" - }, - "require-dev": { - "mockery/mockery": "^1.3", - "orchestra/testbench": "^6.0|^7.0|^8.0", - "phpunit/phpunit": "^9.0" - }, - "bin": [ - "bin/blade-icons-generate" - ], - "type": "library", - "extra": { - "laravel": { - "providers": [ - "BladeUI\\Icons\\BladeIconsServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "BladeUI\\Icons\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dries Vints", - "homepage": "https://driesvints.com" - } - ], - "description": "A package to easily make use of icons in your Laravel Blade views.", - "homepage": "https://github.com/blade-ui-kit/blade-icons", - "keywords": [ - "blade", - "icons", - "laravel", - "svg" - ], - "support": { - "issues": "https://github.com/blade-ui-kit/blade-icons/issues", - "source": "https://github.com/blade-ui-kit/blade-icons" - }, - "funding": [ - { - "url": "https://github.com/caneco", - "type": "github" - }, - { - "url": "https://github.com/driesvints", - "type": "github" - } - ], - "time": "2023-02-15T16:30:12+00:00" - }, { "name": "brick/math", "version": "0.11.0", @@ -3066,6 +2916,161 @@ ], "time": "2023-02-08T01:06:31+00:00" }, + { + "name": "opcodesio/log-viewer", + "version": "v2.5.1", + "source": { + "type": "git", + "url": "https://github.com/opcodesio/log-viewer.git", + "reference": "d3c6f6652d155d2849af5f5003b8b1c32d1ac7ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opcodesio/log-viewer/zipball/d3c6f6652d155d2849af5f5003b8b1c32d1ac7ae", + "reference": "d3c6f6652d155d2849af5f5003b8b1c32d1ac7ae", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0|^9.0|^10.0", + "php": "^8.0" + }, + "conflict": { + "arcanedev/log-viewer": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.2", + "itsgoingd/clockwork": "^5.1", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^6.0", + "orchestra/testbench": "^7.6|^8.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "phpunit/phpunit": "^9.5", + "spatie/test-time": "^1.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Required for multi-host support. ^7.2" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Opcodes\\LogViewer\\LogViewerServiceProvider" + ], + "aliases": { + "LogViewer": "Opcodes\\LogViewer\\Facades\\LogViewer" + } + } + }, + "autoload": { + "psr-4": { + "Opcodes\\LogViewer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Arunas Skirius", + "email": "arukomp@gmail.com", + "role": "Developer" + } + ], + "description": "Fast and easy-to-use log viewer for your Laravel application", + "homepage": "https://github.com/opcodesio/log-viewer", + "keywords": [ + "arukompas", + "better-log-viewer", + "laravel", + "log viewer", + "logs", + "opcodesio" + ], + "support": { + "issues": "https://github.com/opcodesio/log-viewer/issues", + "source": "https://github.com/opcodesio/log-viewer/tree/v2.5.1" + }, + "funding": [ + { + "url": "https://www.buymeacoffee.com/arunas", + "type": "custom" + }, + { + "url": "https://github.com/arukompas", + "type": "github" + } + ], + "time": "2023-07-10T09:20:47+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, { "name": "paragonie/random_compat", "version": "v9.99.100", @@ -3277,6 +3282,116 @@ ], "time": "2023-02-25T19:38:58+00:00" }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.21", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.21" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2023-07-09T15:24:48+00:00" + }, { "name": "psr/container", "version": "2.0.2", diff --git a/config/core.php b/config/core.php index a2103da..ac53e1c 100755 --- a/config/core.php +++ b/config/core.php @@ -37,7 +37,6 @@ * SSH */ 'ssh_user' => env('SSH_USER', 'vito'), - 'ssh_public_key' => env('SSH_PUBLIC_KEY'), 'ssh_public_key_name' => env('SSH_PUBLIC_KEY_NAME'), 'ssh_private_key_name' => env('SSH_PRIVATE_KEY_NAME'), 'logs_disk' => env('SERVER_LOGS_DISK', 'server-logs-local'), @@ -46,7 +45,11 @@ /* * General */ - 'operating_systems' => ['ubuntu_18', 'ubuntu_20', 'ubuntu_22'], + 'operating_systems' => [ + // 'ubuntu_18', + 'ubuntu_20', + 'ubuntu_22' + ], 'webservers' => ['none', 'nginx'], 'php_versions' => [ 'none', @@ -258,14 +261,14 @@ * Site */ 'site_types' => [ - \App\Enums\SiteType::LARAVEL, \App\Enums\SiteType::PHP, - \App\Enums\SiteType::WORDPRESS, + \App\Enums\SiteType::LARAVEL, + // \App\Enums\SiteType::WORDPRESS, ], 'site_types_class' => [ - \App\Enums\SiteType::LARAVEL => Laravel::class, \App\Enums\SiteType::PHP => PHPSite::class, - \App\Enums\SiteType::WORDPRESS => Wordpress::class, + \App\Enums\SiteType::LARAVEL => Laravel::class, + // \App\Enums\SiteType::WORDPRESS => Wordpress::class, ], /* @@ -289,10 +292,9 @@ */ 'php_extensions' => [ 'imagick', - // 'geoip', + 'geoip', 'exif', 'gmagick', - 'ssh2', 'gmp', 'intl', ], diff --git a/config/serverproviders.php b/config/serverproviders.php index 13050a0..d14bdb7 100644 --- a/config/serverproviders.php +++ b/config/serverproviders.php @@ -385,6 +385,7 @@ 'images' => [ 'ubuntu_18' => 'linode/ubuntu18.04', 'ubuntu_20' => 'linode/ubuntu20.04', + 'ubuntu_22' => 'linode/ubuntu22.04', ], ], 'digitalocean' => [ @@ -513,8 +514,9 @@ ], ], 'images' => [ - 'ubuntu_18' => '93524084', - 'ubuntu_20' => '93525508', + 'ubuntu_18' => '112929540', + 'ubuntu_20' => '112929454', + 'ubuntu_22' => '129211873', ], ], 'vultr' => [ @@ -685,6 +687,7 @@ 'images' => [ 'ubuntu_18' => '270', 'ubuntu_20' => '387', + 'ubuntu_22' => '1743', ], ], 'hetzner' => [ diff --git a/database/migrations/2023_07_21_210213_update_firewall_rules_table.php b/database/migrations/2023_07_21_210213_update_firewall_rules_table.php new file mode 100644 index 0000000..c53c51b --- /dev/null +++ b/database/migrations/2023_07_21_210213_update_firewall_rules_table.php @@ -0,0 +1,16 @@ +longText('ssh_key')->nullable()->after('repository'); + }); + } + + public function down(): void + { + Schema::table('sites', function (Blueprint $table) { + $table->dropColumn('ssh_key'); + }); + } +}; diff --git a/database/migrations/2023_07_30_163805_add_url_to_source_controls_table.php b/database/migrations/2023_07_30_163805_add_url_to_source_controls_table.php new file mode 100644 index 0000000..8d98e8b --- /dev/null +++ b/database/migrations/2023_07_30_163805_add_url_to_source_controls_table.php @@ -0,0 +1,21 @@ +string('url')->nullable()->after('provider'); + }); + } + + public function down(): void + { + Schema::table('source_controls', function (Blueprint $table) { + $table->dropColumn('url'); + }); + } +}; diff --git a/database/migrations/2023_07_30_200348_add_profile_to_source_controls_table.php b/database/migrations/2023_07_30_200348_add_profile_to_source_controls_table.php new file mode 100644 index 0000000..68cf22e --- /dev/null +++ b/database/migrations/2023_07_30_200348_add_profile_to_source_controls_table.php @@ -0,0 +1,21 @@ +string('profile')->after('provider')->nullable(); + }); + } + + public function down(): void + { + Schema::table('source_controls', function (Blueprint $table) { + $table->dropColumn('profile'); + }); + } +}; diff --git a/database/migrations/2023_07_30_205328_add_source_control_id_to_sites_table.php b/database/migrations/2023_07_30_205328_add_source_control_id_to_sites_table.php new file mode 100644 index 0000000..083abda --- /dev/null +++ b/database/migrations/2023_07_30_205328_add_source_control_id_to_sites_table.php @@ -0,0 +1,21 @@ +unsignedBigInteger('source_control_id')->nullable()->after('source_control'); + }); + } + + public function down(): void + { + Schema::table('sites', function (Blueprint $table) { + $table->dropColumn('source_control_id'); + }); + } +}; diff --git a/docker-compose.yml b/docker-compose.yml index 44bc2e2..dbb39ef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,7 @@ services: - sail depends_on: - mysql + - redis worker: build: context: ./docker/8.1 @@ -45,6 +46,7 @@ services: restart: unless-stopped depends_on: - mysql + - redis mysql: image: 'mysql/mysql-server:8.0' ports: @@ -65,14 +67,8 @@ services: test: [ "CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}" ] retries: 3 timeout: 5s - soketi: - image: 'quay.io/soketi/soketi:latest-16-alpine' - environment: - SOKETI_DEBUG: '1' - SOKETI_METRICS_SERVER_PORT: '9601' - ports: - - '${SOKETI_PORT:-6001}:6001' - - '${SOKETI_METRICS_SERVER_PORT:-9601}:9601' + redis: + image: redis:latest networks: - sail networks: diff --git a/install/install.sh b/install/install.sh index b83a2b5..deca1f1 100644 --- a/install/install.sh +++ b/install/install.sh @@ -5,6 +5,18 @@ export NEEDRESTART_MODE=a export V_USERNAME=vito export V_PASSWORD=$(openssl rand -base64 12) +echo "Enter the domain you want to install Vito? (your-domain.com)" + +read V_DOMAIN + +echo "Enter your email address:" + +read V_ADMIN_EMAIL + +echo "Enter your password:" + +read V_ADMIN_PASSWORD + if [[ -z "${V_DOMAIN}" ]]; then echo "Error: V_DOMAIN environment variable is not set." exit 1 @@ -194,7 +206,6 @@ command=php /home/${V_USERNAME}/${V_DOMAIN}/artisan queue:work --sleep=3 --backo autostart=1 autorestart=1 user=vito -numprocs=1 redirect_stderr=true stdout_logfile=/home/${V_USERNAME}/.logs/workers/worker.log stopwaitsecs=3600 @@ -210,6 +221,9 @@ supervisorctl reread supervisorctl update supervisorctl start worker:* +# make the update file executable +chmod +x /home/${V_USERNAME}/${V_DOMAIN}/update.sh + # cleanup chown -R ${V_USERNAME}:${V_USERNAME} /home/${V_USERNAME} diff --git a/package-lock.json b/package-lock.json index 5928f0f..ae8aa03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "devDependencies": { "@ryangjchandler/alpine-clipboard": "^2.2.0", "@tailwindcss/forms": "^0.5.2", + "@tailwindcss/typography": "^0.5.9", "alpinejs": "^3.4.2", "autoprefixer": "^10.4.2", "axios": "^1.1.2", @@ -478,6 +479,34 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz", + "integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==", + "dev": true, + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@vue/reactivity": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", @@ -1137,6 +1166,24 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2146,6 +2193,30 @@ "mini-svg-data-uri": "^1.2.3" } }, + "@tailwindcss/typography": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz", + "integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==", + "dev": true, + "requires": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, "@vue/reactivity": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", @@ -2624,6 +2695,24 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index 8cd6b7e..1675e58 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "devDependencies": { "@ryangjchandler/alpine-clipboard": "^2.2.0", "@tailwindcss/forms": "^0.5.2", + "@tailwindcss/typography": "^0.5.9", "alpinejs": "^3.4.2", "autoprefixer": "^10.4.2", "axios": "^1.1.2", diff --git a/public/build/assets/app-0d136492.css b/public/build/assets/app-0d136492.css deleted file mode 100644 index 17de60f..0000000 --- a/public/build/assets/app-0d136492.css +++ /dev/null @@ -1 +0,0 @@ -.toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #ffffff;text-shadow:0 1px 0 #ffffff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:hover,.toast-close-button:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{box-sizing:border-box}#toast-container>div{border-radius:.5rem;position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;background-position:15px center;background-repeat:no-repeat;color:#fff}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{cursor:pointer;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-top-center>div,#toast-container.toast-bottom-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-top-full-width>div,#toast-container.toast-bottom-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}.toast-error{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.toast-info{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.toast-warning{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width: 240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width: 241px) and (max-width: 480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width: 481px) and (max-width: 768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}}/*! tailwindcss v3.3.1 | MIT License | https://tailwindcss.com*/*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e2e8f0}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Figtree,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#94a3b8}input::placeholder,textarea::placeholder{opacity:1;color:#94a3b8}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#64748b;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#64748b;opacity:1}input::placeholder,textarea::placeholder{color:#64748b;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2364748b' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#64748b;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0px}.left-0{left:0px}.right-0{right:0px}.top-1{top:.25rem}.z-0{z-index:0}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.-ml-px{margin-left:-1px}.-mr-2{margin-right:-.5rem}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-20{height:5rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-96{height:24rem}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-10{width:2.5rem}.w-20{width:5rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-auto{width:auto}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-full{min-width:100%}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-none{flex:none}.shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.origin-top{transform-origin:top}.origin-top-left{transform-origin:top left}.origin-top-right{transform-origin:top right}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-4{--tw-translate-y: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(2rem * var(--tw-space-x-reverse));margin-left:calc(2rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-b-md{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.rounded-tr-md{border-top-right-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.border-primary-200{--tw-border-opacity: 1;border-color:rgb(199 210 254 / var(--tw-border-opacity))}.border-primary-400{--tw-border-opacity: 1;border-color:rgb(129 140 248 / var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-yellow-500{--tw-border-opacity: 1;border-color:rgb(234 179 8 / var(--tw-border-opacity))}.border-t-transparent{border-top-color:transparent}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity: 1;background-color:rgb(199 210 254 / var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity: 1;background-color:rgb(238 242 255 / var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity))}.fill-current{fill:currentColor}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-7{padding:1.75rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pl-3{padding-left:.75rem}.pr-4{padding-right:1rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-sans{font-family:Figtree,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity: 1;color:rgb(248 250 252 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity: 1;color:rgb(99 102 241 / var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity: 1;color:rgb(67 56 202 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-0{outline-width:0px}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-black{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity))}.ring-gray-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(203 213 225 / var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity: .05}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-75{transition-duration:75ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity))}.hover\:bg-red-500:hover{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity))}.hover\:text-primary-600:hover{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.hover\:opacity-50:hover{opacity:.5}.focus\:z-10:focus{z-index:10}.focus\:border-blue-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.focus\:border-gray-300:focus{--tw-border-opacity: 1;border-color:rgb(203 213 225 / var(--tw-border-opacity))}.focus\:border-primary-300:focus{--tw-border-opacity: 1;border-color:rgb(165 180 252 / var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity))}.focus\:border-primary-700:focus{--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}.focus\:border-red-700:focus{--tw-border-opacity: 1;border-color:rgb(185 28 28 / var(--tw-border-opacity))}.focus\:bg-gray-100:focus{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.focus\:bg-gray-50:focus{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.focus\:bg-primary-100:focus{--tw-bg-opacity: 1;background-color:rgb(224 231 255 / var(--tw-bg-opacity))}.focus\:text-gray-500:focus{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.focus\:text-gray-700:focus{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.focus\:text-gray-800:focus{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity))}.focus\:text-primary-800:focus{--tw-text-opacity: 1;color:rgb(55 48 163 / var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-gray-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(100 116 139 / var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-primary-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(199 210 254 / var(--tw-ring-opacity))}.focus\:ring-primary-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(254 202 202 / var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.active\:bg-gray-100:active{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity))}.active\:bg-primary-700:active{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity))}.active\:bg-red-600:active{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.active\:text-gray-500:active{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.active\:text-gray-700:active{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity))}.disabled\:opacity-25:disabled{opacity:.25}@media (prefers-color-scheme: dark){.dark\:border-gray-500{--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity))}.dark\:border-gray-600{--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}.dark\:border-gray-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}.dark\:border-gray-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity))}.dark\:border-gray-900{--tw-border-opacity: 1;border-color:rgb(15 23 42 / var(--tw-border-opacity))}.dark\:border-primary-600{--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}.dark\:border-t-transparent{border-top-color:transparent}.dark\:border-opacity-20{--tw-border-opacity: .2}.dark\:bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.dark\:bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}.dark\:bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}.dark\:bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.dark\:bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity))}.dark\:bg-primary-500{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.dark\:bg-primary-900\/50{background-color:#312e8180}.dark\:bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.dark\:bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity))}.dark\:bg-opacity-10{--tw-bg-opacity: .1}.dark\:bg-opacity-30{--tw-bg-opacity: .3}.dark\:bg-opacity-70{--tw-bg-opacity: .7}.dark\:text-gray-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}.dark\:text-gray-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.dark\:text-gray-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.dark\:text-gray-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.dark\:text-gray-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity))}.dark\:text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity))}.dark\:text-primary-300{--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity))}.dark\:text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity))}.dark\:hover\:border-gray-600:hover{--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}.dark\:hover\:border-gray-700:hover{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}.dark\:hover\:bg-gray-700:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}.dark\:hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}.dark\:hover\:bg-gray-900:hover{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.dark\:hover\:text-gray-100:hover{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity))}.dark\:hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.dark\:hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.dark\:hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.dark\:focus\:border-gray-600:focus{--tw-border-opacity: 1;border-color:rgb(71 85 105 / var(--tw-border-opacity))}.dark\:focus\:border-gray-700:focus{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity))}.dark\:focus\:border-primary-300:focus{--tw-border-opacity: 1;border-color:rgb(165 180 252 / var(--tw-border-opacity))}.dark\:focus\:border-primary-600:focus{--tw-border-opacity: 1;border-color:rgb(79 70 229 / var(--tw-border-opacity))}.dark\:focus\:border-primary-700:focus{--tw-border-opacity: 1;border-color:rgb(67 56 202 / var(--tw-border-opacity))}.dark\:focus\:bg-gray-700:focus{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}.dark\:focus\:bg-gray-800:focus{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity))}.dark\:focus\:bg-gray-900:focus{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity))}.dark\:focus\:bg-primary-900:focus{--tw-bg-opacity: 1;background-color:rgb(49 46 129 / var(--tw-bg-opacity))}.dark\:focus\:text-gray-200:focus{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity))}.dark\:focus\:text-gray-300:focus{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity))}.dark\:focus\:text-gray-400:focus{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}.dark\:focus\:text-primary-200:focus{--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity))}.dark\:focus\:ring-indigo-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}.dark\:focus\:ring-primary-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}.dark\:focus\:ring-primary-700:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(67 56 202 / var(--tw-ring-opacity))}.dark\:focus\:ring-opacity-40:focus{--tw-ring-opacity: .4}.dark\:focus\:ring-offset-gray-800:focus{--tw-ring-offset-color: #1e293b}}@media (min-width: 640px){.sm\:-my-px{margin-top:-1px;margin-bottom:-1px}.sm\:mx-auto{margin-left:auto;margin-right:auto}.sm\:ml-10{margin-left:2.5rem}.sm\:ml-6{margin-left:1.5rem}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:w-full{width:100%}.sm\:max-w-2xl{max-width:42rem}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-4xl{max-width:56rem}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:max-w-xl{max-width:36rem}.sm\:flex-1{flex:1 1 0%}.sm\:translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:items-center{align-items:center}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:rounded-md{border-radius:.375rem}.sm\:rounded-bl-md{border-bottom-left-radius:.375rem}.sm\:rounded-br-md{border-bottom-right-radius:.375rem}.sm\:rounded-tl-md{border-top-left-radius:.375rem}.sm\:rounded-tr-md{border-top-right-radius:.375rem}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}}@media (min-width: 768px){.md\:col-span-1{grid-column:span 1 / span 1}.md\:block{display:block}.md\:justify-start{justify-content:flex-start}.md\:text-left{text-align:left}}@media (min-width: 1024px){.lg\:block{display:block}.lg\:flex-none{flex:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}} diff --git a/public/build/assets/app-4c878af7.js b/public/build/assets/app-4c878af7.js deleted file mode 100644 index 532cb8c..0000000 --- a/public/build/assets/app-4c878af7.js +++ /dev/null @@ -1,29 +0,0 @@ -function Us(e,n){return function(){return e.apply(n,arguments)}}const{toString:Hs}=Object.prototype,{getPrototypeOf:Ro}=Object,Po=(e=>n=>{const i=Hs.call(n);return e[i]||(e[i]=i.slice(8,-1).toLowerCase())})(Object.create(null)),Bn=e=>(e=e.toLowerCase(),n=>Po(n)===e),vi=e=>n=>typeof n===e,{isArray:xr}=Array,jr=vi("undefined");function Ku(e){return e!==null&&!jr(e)&&e.constructor!==null&&!jr(e.constructor)&&Vn(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const qs=Bn("ArrayBuffer");function Gu(e){let n;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?n=ArrayBuffer.isView(e):n=e&&e.buffer&&qs(e.buffer),n}const Qu=vi("string"),Vn=vi("function"),zs=vi("number"),ko=e=>e!==null&&typeof e=="object",Zu=e=>e===!0||e===!1,ii=e=>{if(Po(e)!=="object")return!1;const n=Ro(e);return(n===null||n===Object.prototype||Object.getPrototypeOf(n)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},ef=Bn("Date"),tf=Bn("File"),nf=Bn("Blob"),rf=Bn("FileList"),of=e=>ko(e)&&Vn(e.pipe),sf=e=>{const n="[object FormData]";return e&&(typeof FormData=="function"&&e instanceof FormData||Hs.call(e)===n||Vn(e.toString)&&e.toString()===n)},af=Bn("URLSearchParams"),uf=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function Ur(e,n,{allOwnKeys:i=!1}={}){if(e===null||typeof e>"u")return;let r,o;if(typeof e!="object"&&(e=[e]),xr(e))for(r=0,o=e.length;r0;)if(o=i[r],n===o.toLowerCase())return o;return null}const Ws=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),Ys=e=>!jr(e)&&e!==Ws;function eo(){const{caseless:e}=Ys(this)&&this||{},n={},i=(r,o)=>{const a=e&&$s(n,o)||o;ii(n[a])&&ii(r)?n[a]=eo(n[a],r):ii(r)?n[a]=eo({},r):xr(r)?n[a]=r.slice():n[a]=r};for(let r=0,o=arguments.length;r(Ur(n,(o,a)=>{i&&Vn(o)?e[a]=Us(o,i):e[a]=o},{allOwnKeys:r}),e),cf=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),lf=(e,n,i,r)=>{e.prototype=Object.create(n.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:n.prototype}),i&&Object.assign(e.prototype,i)},hf=(e,n,i,r)=>{let o,a,f;const d={};if(n=n||{},e==null)return n;do{for(o=Object.getOwnPropertyNames(e),a=o.length;a-- >0;)f=o[a],(!r||r(f,e,n))&&!d[f]&&(n[f]=e[f],d[f]=!0);e=i!==!1&&Ro(e)}while(e&&(!i||i(e,n))&&e!==Object.prototype);return n},df=(e,n,i)=>{e=String(e),(i===void 0||i>e.length)&&(i=e.length),i-=n.length;const r=e.indexOf(n,i);return r!==-1&&r===i},pf=e=>{if(!e)return null;if(xr(e))return e;let n=e.length;if(!zs(n))return null;const i=new Array(n);for(;n-- >0;)i[n]=e[n];return i},yf=(e=>n=>e&&n instanceof e)(typeof Uint8Array<"u"&&Ro(Uint8Array)),gf=(e,n)=>{const r=(e&&e[Symbol.iterator]).call(e);let o;for(;(o=r.next())&&!o.done;){const a=o.value;n.call(e,a[0],a[1])}},vf=(e,n)=>{let i;const r=[];for(;(i=e.exec(n))!==null;)r.push(i);return r},xf=Bn("HTMLFormElement"),mf=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(i,r,o){return r.toUpperCase()+o}),xs=(({hasOwnProperty:e})=>(n,i)=>e.call(n,i))(Object.prototype),bf=Bn("RegExp"),Js=(e,n)=>{const i=Object.getOwnPropertyDescriptors(e),r={};Ur(i,(o,a)=>{n(o,a,e)!==!1&&(r[a]=o)}),Object.defineProperties(e,r)},wf=e=>{Js(e,(n,i)=>{if(Vn(e)&&["arguments","caller","callee"].indexOf(i)!==-1)return!1;const r=e[i];if(Vn(r)){if(n.enumerable=!1,"writable"in n){n.writable=!1;return}n.set||(n.set=()=>{throw Error("Can not rewrite read-only method '"+i+"'")})}})},_f=(e,n)=>{const i={},r=o=>{o.forEach(a=>{i[a]=!0})};return xr(e)?r(e):r(String(e).split(n)),i},Sf=()=>{},Tf=(e,n)=>(e=+e,Number.isFinite(e)?e:n),Yi="abcdefghijklmnopqrstuvwxyz",ms="0123456789",Vs={DIGIT:ms,ALPHA:Yi,ALPHA_DIGIT:Yi+Yi.toUpperCase()+ms},Ef=(e=16,n=Vs.ALPHA_DIGIT)=>{let i="";const{length:r}=n;for(;e--;)i+=n[Math.random()*r|0];return i};function Cf(e){return!!(e&&Vn(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const Af=e=>{const n=new Array(10),i=(r,o)=>{if(ko(r)){if(n.indexOf(r)>=0)return;if(!("toJSON"in r)){n[o]=r;const a=xr(r)?[]:{};return Ur(r,(f,d)=>{const b=i(f,o+1);!jr(b)&&(a[d]=b)}),n[o]=void 0,a}}return r};return i(e,0)},ie={isArray:xr,isArrayBuffer:qs,isBuffer:Ku,isFormData:sf,isArrayBufferView:Gu,isString:Qu,isNumber:zs,isBoolean:Zu,isObject:ko,isPlainObject:ii,isUndefined:jr,isDate:ef,isFile:tf,isBlob:nf,isRegExp:bf,isFunction:Vn,isStream:of,isURLSearchParams:af,isTypedArray:yf,isFileList:rf,forEach:Ur,merge:eo,extend:ff,trim:uf,stripBOM:cf,inherits:lf,toFlatObject:hf,kindOf:Po,kindOfTest:Bn,endsWith:df,toArray:pf,forEachEntry:gf,matchAll:vf,isHTMLForm:xf,hasOwnProperty:xs,hasOwnProp:xs,reduceDescriptors:Js,freezeMethods:wf,toObjectSet:_f,toCamelCase:mf,noop:Sf,toFiniteNumber:Tf,findKey:$s,global:Ws,isContextDefined:Ys,ALPHABET:Vs,generateString:Ef,isSpecCompliantForm:Cf,toJSONObject:Af};function ht(e,n,i,r,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=e,this.name="AxiosError",n&&(this.code=n),i&&(this.config=i),r&&(this.request=r),o&&(this.response=o)}ie.inherits(ht,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:ie.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const Xs=ht.prototype,Ks={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach(e=>{Ks[e]={value:e}});Object.defineProperties(ht,Ks);Object.defineProperty(Xs,"isAxiosError",{value:!0});ht.from=(e,n,i,r,o,a)=>{const f=Object.create(Xs);return ie.toFlatObject(e,f,function(b){return b!==Error.prototype},d=>d!=="isAxiosError"),ht.call(f,e.message,n,i,r,o),f.cause=e,f.name=e.name,a&&Object.assign(f,a),f};const Of=null;function to(e){return ie.isPlainObject(e)||ie.isArray(e)}function Gs(e){return ie.endsWith(e,"[]")?e.slice(0,-2):e}function bs(e,n,i){return e?e.concat(n).map(function(o,a){return o=Gs(o),!i&&a?"["+o+"]":o}).join(i?".":""):n}function Rf(e){return ie.isArray(e)&&!e.some(to)}const Pf=ie.toFlatObject(ie,{},null,function(n){return/^is[A-Z]/.test(n)});function xi(e,n,i){if(!ie.isObject(e))throw new TypeError("target must be an object");n=n||new FormData,i=ie.toFlatObject(i,{metaTokens:!0,dots:!1,indexes:!1},!1,function(P,I){return!ie.isUndefined(I[P])});const r=i.metaTokens,o=i.visitor||_,a=i.dots,f=i.indexes,b=(i.Blob||typeof Blob<"u"&&Blob)&&ie.isSpecCompliantForm(n);if(!ie.isFunction(o))throw new TypeError("visitor must be a function");function v(S){if(S===null)return"";if(ie.isDate(S))return S.toISOString();if(!b&&ie.isBlob(S))throw new ht("Blob is not supported. Use a Buffer instead.");return ie.isArrayBuffer(S)||ie.isTypedArray(S)?b&&typeof Blob=="function"?new Blob([S]):Buffer.from(S):S}function _(S,P,I){let N=S;if(S&&!I&&typeof S=="object"){if(ie.endsWith(P,"{}"))P=r?P:P.slice(0,-2),S=JSON.stringify(S);else if(ie.isArray(S)&&Rf(S)||(ie.isFileList(S)||ie.endsWith(P,"[]"))&&(N=ie.toArray(S)))return P=Gs(P),N.forEach(function(K,he){!(ie.isUndefined(K)||K===null)&&n.append(f===!0?bs([P],he,a):f===null?P:P+"[]",v(K))}),!1}return to(S)?!0:(n.append(bs(I,P,a),v(S)),!1)}const x=[],O=Object.assign(Pf,{defaultVisitor:_,convertValue:v,isVisitable:to});function C(S,P){if(!ie.isUndefined(S)){if(x.indexOf(S)!==-1)throw Error("Circular reference detected in "+P.join("."));x.push(S),ie.forEach(S,function(N,H){(!(ie.isUndefined(N)||N===null)&&o.call(n,N,ie.isString(H)?H.trim():H,P,O))===!0&&C(N,P?P.concat(H):[H])}),x.pop()}}if(!ie.isObject(e))throw new TypeError("data must be an object");return C(e),n}function ws(e){const n={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(r){return n[r]})}function No(e,n){this._pairs=[],e&&xi(e,this,n)}const Qs=No.prototype;Qs.append=function(n,i){this._pairs.push([n,i])};Qs.toString=function(n){const i=n?function(r){return n.call(this,r,ws)}:ws;return this._pairs.map(function(o){return i(o[0])+"="+i(o[1])},"").join("&")};function kf(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Zs(e,n,i){if(!n)return e;const r=i&&i.encode||kf,o=i&&i.serialize;let a;if(o?a=o(n,i):a=ie.isURLSearchParams(n)?n.toString():new No(n,i).toString(r),a){const f=e.indexOf("#");f!==-1&&(e=e.slice(0,f)),e+=(e.indexOf("?")===-1?"?":"&")+a}return e}class Nf{constructor(){this.handlers=[]}use(n,i,r){return this.handlers.push({fulfilled:n,rejected:i,synchronous:r?r.synchronous:!1,runWhen:r?r.runWhen:null}),this.handlers.length-1}eject(n){this.handlers[n]&&(this.handlers[n]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(n){ie.forEach(this.handlers,function(r){r!==null&&n(r)})}}const _s=Nf,ea={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},Lf=typeof URLSearchParams<"u"?URLSearchParams:No,If=typeof FormData<"u"?FormData:null,Mf=typeof Blob<"u"?Blob:null,Df=(()=>{let e;return typeof navigator<"u"&&((e=navigator.product)==="ReactNative"||e==="NativeScript"||e==="NS")?!1:typeof window<"u"&&typeof document<"u"})(),jf=(()=>typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function")(),Rn={isBrowser:!0,classes:{URLSearchParams:Lf,FormData:If,Blob:Mf},isStandardBrowserEnv:Df,isStandardBrowserWebWorkerEnv:jf,protocols:["http","https","file","blob","url","data"]};function Ff(e,n){return xi(e,new Rn.classes.URLSearchParams,Object.assign({visitor:function(i,r,o,a){return Rn.isNode&&ie.isBuffer(i)?(this.append(r,i.toString("base64")),!1):a.defaultVisitor.apply(this,arguments)}},n))}function Bf(e){return ie.matchAll(/\w+|\[(\w*)]/g,e).map(n=>n[0]==="[]"?"":n[1]||n[0])}function Uf(e){const n={},i=Object.keys(e);let r;const o=i.length;let a;for(r=0;r=i.length;return f=!f&&ie.isArray(o)?o.length:f,b?(ie.hasOwnProp(o,f)?o[f]=[o[f],r]:o[f]=r,!d):((!o[f]||!ie.isObject(o[f]))&&(o[f]=[]),n(i,r,o[f],a)&&ie.isArray(o[f])&&(o[f]=Uf(o[f])),!d)}if(ie.isFormData(e)&&ie.isFunction(e.entries)){const i={};return ie.forEachEntry(e,(r,o)=>{n(Bf(r),o,i,0)}),i}return null}const Hf={"Content-Type":void 0};function qf(e,n,i){if(ie.isString(e))try{return(n||JSON.parse)(e),ie.trim(e)}catch(r){if(r.name!=="SyntaxError")throw r}return(i||JSON.stringify)(e)}const mi={transitional:ea,adapter:["xhr","http"],transformRequest:[function(n,i){const r=i.getContentType()||"",o=r.indexOf("application/json")>-1,a=ie.isObject(n);if(a&&ie.isHTMLForm(n)&&(n=new FormData(n)),ie.isFormData(n))return o&&o?JSON.stringify(ta(n)):n;if(ie.isArrayBuffer(n)||ie.isBuffer(n)||ie.isStream(n)||ie.isFile(n)||ie.isBlob(n))return n;if(ie.isArrayBufferView(n))return n.buffer;if(ie.isURLSearchParams(n))return i.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),n.toString();let d;if(a){if(r.indexOf("application/x-www-form-urlencoded")>-1)return Ff(n,this.formSerializer).toString();if((d=ie.isFileList(n))||r.indexOf("multipart/form-data")>-1){const b=this.env&&this.env.FormData;return xi(d?{"files[]":n}:n,b&&new b,this.formSerializer)}}return a||o?(i.setContentType("application/json",!1),qf(n)):n}],transformResponse:[function(n){const i=this.transitional||mi.transitional,r=i&&i.forcedJSONParsing,o=this.responseType==="json";if(n&&ie.isString(n)&&(r&&!this.responseType||o)){const f=!(i&&i.silentJSONParsing)&&o;try{return JSON.parse(n)}catch(d){if(f)throw d.name==="SyntaxError"?ht.from(d,ht.ERR_BAD_RESPONSE,this,null,this.response):d}}return n}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:Rn.classes.FormData,Blob:Rn.classes.Blob},validateStatus:function(n){return n>=200&&n<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};ie.forEach(["delete","get","head"],function(n){mi.headers[n]={}});ie.forEach(["post","put","patch"],function(n){mi.headers[n]=ie.merge(Hf)});const Lo=mi,zf=ie.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),$f=e=>{const n={};let i,r,o;return e&&e.split(` -`).forEach(function(f){o=f.indexOf(":"),i=f.substring(0,o).trim().toLowerCase(),r=f.substring(o+1).trim(),!(!i||n[i]&&zf[i])&&(i==="set-cookie"?n[i]?n[i].push(r):n[i]=[r]:n[i]=n[i]?n[i]+", "+r:r)}),n},Ss=Symbol("internals");function Pr(e){return e&&String(e).trim().toLowerCase()}function oi(e){return e===!1||e==null?e:ie.isArray(e)?e.map(oi):String(e)}function Wf(e){const n=Object.create(null),i=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let r;for(;r=i.exec(e);)n[r[1]]=r[2];return n}const Yf=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function Ji(e,n,i,r,o){if(ie.isFunction(r))return r.call(this,n,i);if(o&&(n=i),!!ie.isString(n)){if(ie.isString(r))return n.indexOf(r)!==-1;if(ie.isRegExp(r))return r.test(n)}}function Jf(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(n,i,r)=>i.toUpperCase()+r)}function Vf(e,n){const i=ie.toCamelCase(" "+n);["get","set","has"].forEach(r=>{Object.defineProperty(e,r+i,{value:function(o,a,f){return this[r].call(this,n,o,a,f)},configurable:!0})})}class bi{constructor(n){n&&this.set(n)}set(n,i,r){const o=this;function a(d,b,v){const _=Pr(b);if(!_)throw new Error("header name must be a non-empty string");const x=ie.findKey(o,_);(!x||o[x]===void 0||v===!0||v===void 0&&o[x]!==!1)&&(o[x||b]=oi(d))}const f=(d,b)=>ie.forEach(d,(v,_)=>a(v,_,b));return ie.isPlainObject(n)||n instanceof this.constructor?f(n,i):ie.isString(n)&&(n=n.trim())&&!Yf(n)?f($f(n),i):n!=null&&a(i,n,r),this}get(n,i){if(n=Pr(n),n){const r=ie.findKey(this,n);if(r){const o=this[r];if(!i)return o;if(i===!0)return Wf(o);if(ie.isFunction(i))return i.call(this,o,r);if(ie.isRegExp(i))return i.exec(o);throw new TypeError("parser must be boolean|regexp|function")}}}has(n,i){if(n=Pr(n),n){const r=ie.findKey(this,n);return!!(r&&this[r]!==void 0&&(!i||Ji(this,this[r],r,i)))}return!1}delete(n,i){const r=this;let o=!1;function a(f){if(f=Pr(f),f){const d=ie.findKey(r,f);d&&(!i||Ji(r,r[d],d,i))&&(delete r[d],o=!0)}}return ie.isArray(n)?n.forEach(a):a(n),o}clear(n){const i=Object.keys(this);let r=i.length,o=!1;for(;r--;){const a=i[r];(!n||Ji(this,this[a],a,n,!0))&&(delete this[a],o=!0)}return o}normalize(n){const i=this,r={};return ie.forEach(this,(o,a)=>{const f=ie.findKey(r,a);if(f){i[f]=oi(o),delete i[a];return}const d=n?Jf(a):String(a).trim();d!==a&&delete i[a],i[d]=oi(o),r[d]=!0}),this}concat(...n){return this.constructor.concat(this,...n)}toJSON(n){const i=Object.create(null);return ie.forEach(this,(r,o)=>{r!=null&&r!==!1&&(i[o]=n&&ie.isArray(r)?r.join(", "):r)}),i}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([n,i])=>n+": "+i).join(` -`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(n){return n instanceof this?n:new this(n)}static concat(n,...i){const r=new this(n);return i.forEach(o=>r.set(o)),r}static accessor(n){const r=(this[Ss]=this[Ss]={accessors:{}}).accessors,o=this.prototype;function a(f){const d=Pr(f);r[d]||(Vf(o,f),r[d]=!0)}return ie.isArray(n)?n.forEach(a):a(n),this}}bi.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);ie.freezeMethods(bi.prototype);ie.freezeMethods(bi);const Fn=bi;function Vi(e,n){const i=this||Lo,r=n||i,o=Fn.from(r.headers);let a=r.data;return ie.forEach(e,function(d){a=d.call(i,a,o.normalize(),n?n.status:void 0)}),o.normalize(),a}function na(e){return!!(e&&e.__CANCEL__)}function Hr(e,n,i){ht.call(this,e??"canceled",ht.ERR_CANCELED,n,i),this.name="CanceledError"}ie.inherits(Hr,ht,{__CANCEL__:!0});function Xf(e,n,i){const r=i.config.validateStatus;!i.status||!r||r(i.status)?e(i):n(new ht("Request failed with status code "+i.status,[ht.ERR_BAD_REQUEST,ht.ERR_BAD_RESPONSE][Math.floor(i.status/100)-4],i.config,i.request,i))}const Kf=Rn.isStandardBrowserEnv?function(){return{write:function(i,r,o,a,f,d){const b=[];b.push(i+"="+encodeURIComponent(r)),ie.isNumber(o)&&b.push("expires="+new Date(o).toGMTString()),ie.isString(a)&&b.push("path="+a),ie.isString(f)&&b.push("domain="+f),d===!0&&b.push("secure"),document.cookie=b.join("; ")},read:function(i){const r=document.cookie.match(new RegExp("(^|;\\s*)("+i+")=([^;]*)"));return r?decodeURIComponent(r[3]):null},remove:function(i){this.write(i,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}();function Gf(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function Qf(e,n){return n?e.replace(/\/+$/,"")+"/"+n.replace(/^\/+/,""):e}function ra(e,n){return e&&!Gf(n)?Qf(e,n):n}const Zf=Rn.isStandardBrowserEnv?function(){const n=/(msie|trident)/i.test(navigator.userAgent),i=document.createElement("a");let r;function o(a){let f=a;return n&&(i.setAttribute("href",f),f=i.href),i.setAttribute("href",f),{href:i.href,protocol:i.protocol?i.protocol.replace(/:$/,""):"",host:i.host,search:i.search?i.search.replace(/^\?/,""):"",hash:i.hash?i.hash.replace(/^#/,""):"",hostname:i.hostname,port:i.port,pathname:i.pathname.charAt(0)==="/"?i.pathname:"/"+i.pathname}}return r=o(window.location.href),function(f){const d=ie.isString(f)?o(f):f;return d.protocol===r.protocol&&d.host===r.host}}():function(){return function(){return!0}}();function ec(e){const n=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return n&&n[1]||""}function tc(e,n){e=e||10;const i=new Array(e),r=new Array(e);let o=0,a=0,f;return n=n!==void 0?n:1e3,function(b){const v=Date.now(),_=r[a];f||(f=v),i[o]=b,r[o]=v;let x=a,O=0;for(;x!==o;)O+=i[x++],x=x%e;if(o=(o+1)%e,o===a&&(a=(a+1)%e),v-f{const a=o.loaded,f=o.lengthComputable?o.total:void 0,d=a-i,b=r(d),v=a<=f;i=a;const _={loaded:a,total:f,progress:f?a/f:void 0,bytes:d,rate:b||void 0,estimated:b&&f&&v?(f-a)/b:void 0,event:o};_[n?"download":"upload"]=!0,e(_)}}const nc=typeof XMLHttpRequest<"u",rc=nc&&function(e){return new Promise(function(i,r){let o=e.data;const a=Fn.from(e.headers).normalize(),f=e.responseType;let d;function b(){e.cancelToken&&e.cancelToken.unsubscribe(d),e.signal&&e.signal.removeEventListener("abort",d)}ie.isFormData(o)&&(Rn.isStandardBrowserEnv||Rn.isStandardBrowserWebWorkerEnv)&&a.setContentType(!1);let v=new XMLHttpRequest;if(e.auth){const C=e.auth.username||"",S=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";a.set("Authorization","Basic "+btoa(C+":"+S))}const _=ra(e.baseURL,e.url);v.open(e.method.toUpperCase(),Zs(_,e.params,e.paramsSerializer),!0),v.timeout=e.timeout;function x(){if(!v)return;const C=Fn.from("getAllResponseHeaders"in v&&v.getAllResponseHeaders()),P={data:!f||f==="text"||f==="json"?v.responseText:v.response,status:v.status,statusText:v.statusText,headers:C,config:e,request:v};Xf(function(N){i(N),b()},function(N){r(N),b()},P),v=null}if("onloadend"in v?v.onloadend=x:v.onreadystatechange=function(){!v||v.readyState!==4||v.status===0&&!(v.responseURL&&v.responseURL.indexOf("file:")===0)||setTimeout(x)},v.onabort=function(){v&&(r(new ht("Request aborted",ht.ECONNABORTED,e,v)),v=null)},v.onerror=function(){r(new ht("Network Error",ht.ERR_NETWORK,e,v)),v=null},v.ontimeout=function(){let S=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded";const P=e.transitional||ea;e.timeoutErrorMessage&&(S=e.timeoutErrorMessage),r(new ht(S,P.clarifyTimeoutError?ht.ETIMEDOUT:ht.ECONNABORTED,e,v)),v=null},Rn.isStandardBrowserEnv){const C=(e.withCredentials||Zf(_))&&e.xsrfCookieName&&Kf.read(e.xsrfCookieName);C&&a.set(e.xsrfHeaderName,C)}o===void 0&&a.setContentType(null),"setRequestHeader"in v&&ie.forEach(a.toJSON(),function(S,P){v.setRequestHeader(P,S)}),ie.isUndefined(e.withCredentials)||(v.withCredentials=!!e.withCredentials),f&&f!=="json"&&(v.responseType=e.responseType),typeof e.onDownloadProgress=="function"&&v.addEventListener("progress",Ts(e.onDownloadProgress,!0)),typeof e.onUploadProgress=="function"&&v.upload&&v.upload.addEventListener("progress",Ts(e.onUploadProgress)),(e.cancelToken||e.signal)&&(d=C=>{v&&(r(!C||C.type?new Hr(null,e,v):C),v.abort(),v=null)},e.cancelToken&&e.cancelToken.subscribe(d),e.signal&&(e.signal.aborted?d():e.signal.addEventListener("abort",d)));const O=ec(_);if(O&&Rn.protocols.indexOf(O)===-1){r(new ht("Unsupported protocol "+O+":",ht.ERR_BAD_REQUEST,e));return}v.send(o||null)})},si={http:Of,xhr:rc};ie.forEach(si,(e,n)=>{if(e){try{Object.defineProperty(e,"name",{value:n})}catch{}Object.defineProperty(e,"adapterName",{value:n})}});const ic={getAdapter:e=>{e=ie.isArray(e)?e:[e];const{length:n}=e;let i,r;for(let o=0;oe instanceof Fn?e.toJSON():e;function pr(e,n){n=n||{};const i={};function r(v,_,x){return ie.isPlainObject(v)&&ie.isPlainObject(_)?ie.merge.call({caseless:x},v,_):ie.isPlainObject(_)?ie.merge({},_):ie.isArray(_)?_.slice():_}function o(v,_,x){if(ie.isUndefined(_)){if(!ie.isUndefined(v))return r(void 0,v,x)}else return r(v,_,x)}function a(v,_){if(!ie.isUndefined(_))return r(void 0,_)}function f(v,_){if(ie.isUndefined(_)){if(!ie.isUndefined(v))return r(void 0,v)}else return r(void 0,_)}function d(v,_,x){if(x in n)return r(v,_);if(x in e)return r(void 0,v)}const b={url:a,method:a,data:a,baseURL:f,transformRequest:f,transformResponse:f,paramsSerializer:f,timeout:f,timeoutMessage:f,withCredentials:f,adapter:f,responseType:f,xsrfCookieName:f,xsrfHeaderName:f,onUploadProgress:f,onDownloadProgress:f,decompress:f,maxContentLength:f,maxBodyLength:f,beforeRedirect:f,transport:f,httpAgent:f,httpsAgent:f,cancelToken:f,socketPath:f,responseEncoding:f,validateStatus:d,headers:(v,_)=>o(Cs(v),Cs(_),!0)};return ie.forEach(Object.keys(e).concat(Object.keys(n)),function(_){const x=b[_]||o,O=x(e[_],n[_],_);ie.isUndefined(O)&&x!==d||(i[_]=O)}),i}const ia="1.3.5",Io={};["object","boolean","number","function","string","symbol"].forEach((e,n)=>{Io[e]=function(r){return typeof r===e||"a"+(n<1?"n ":" ")+e}});const As={};Io.transitional=function(n,i,r){function o(a,f){return"[Axios v"+ia+"] Transitional option '"+a+"'"+f+(r?". "+r:"")}return(a,f,d)=>{if(n===!1)throw new ht(o(f," has been removed"+(i?" in "+i:"")),ht.ERR_DEPRECATED);return i&&!As[f]&&(As[f]=!0,console.warn(o(f," has been deprecated since v"+i+" and will be removed in the near future"))),n?n(a,f,d):!0}};function oc(e,n,i){if(typeof e!="object")throw new ht("options must be an object",ht.ERR_BAD_OPTION_VALUE);const r=Object.keys(e);let o=r.length;for(;o-- >0;){const a=r[o],f=n[a];if(f){const d=e[a],b=d===void 0||f(d,a,e);if(b!==!0)throw new ht("option "+a+" must be "+b,ht.ERR_BAD_OPTION_VALUE);continue}if(i!==!0)throw new ht("Unknown option "+a,ht.ERR_BAD_OPTION)}}const no={assertOptions:oc,validators:Io},Yn=no.validators;class fi{constructor(n){this.defaults=n,this.interceptors={request:new _s,response:new _s}}request(n,i){typeof n=="string"?(i=i||{},i.url=n):i=n||{},i=pr(this.defaults,i);const{transitional:r,paramsSerializer:o,headers:a}=i;r!==void 0&&no.assertOptions(r,{silentJSONParsing:Yn.transitional(Yn.boolean),forcedJSONParsing:Yn.transitional(Yn.boolean),clarifyTimeoutError:Yn.transitional(Yn.boolean)},!1),o!=null&&(ie.isFunction(o)?i.paramsSerializer={serialize:o}:no.assertOptions(o,{encode:Yn.function,serialize:Yn.function},!0)),i.method=(i.method||this.defaults.method||"get").toLowerCase();let f;f=a&&ie.merge(a.common,a[i.method]),f&&ie.forEach(["delete","get","head","post","put","patch","common"],S=>{delete a[S]}),i.headers=Fn.concat(f,a);const d=[];let b=!0;this.interceptors.request.forEach(function(P){typeof P.runWhen=="function"&&P.runWhen(i)===!1||(b=b&&P.synchronous,d.unshift(P.fulfilled,P.rejected))});const v=[];this.interceptors.response.forEach(function(P){v.push(P.fulfilled,P.rejected)});let _,x=0,O;if(!b){const S=[Es.bind(this),void 0];for(S.unshift.apply(S,d),S.push.apply(S,v),O=S.length,_=Promise.resolve(i);x{if(!r._listeners)return;let a=r._listeners.length;for(;a-- >0;)r._listeners[a](o);r._listeners=null}),this.promise.then=o=>{let a;const f=new Promise(d=>{r.subscribe(d),a=d}).then(o);return f.cancel=function(){r.unsubscribe(a)},f},n(function(a,f,d){r.reason||(r.reason=new Hr(a,f,d),i(r.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(n){if(this.reason){n(this.reason);return}this._listeners?this._listeners.push(n):this._listeners=[n]}unsubscribe(n){if(!this._listeners)return;const i=this._listeners.indexOf(n);i!==-1&&this._listeners.splice(i,1)}static source(){let n;return{token:new Mo(function(o){n=o}),cancel:n}}}const sc=Mo;function ac(e){return function(i){return e.apply(null,i)}}function uc(e){return ie.isObject(e)&&e.isAxiosError===!0}const ro={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(ro).forEach(([e,n])=>{ro[n]=e});const fc=ro;function oa(e){const n=new ai(e),i=Us(ai.prototype.request,n);return ie.extend(i,ai.prototype,n,{allOwnKeys:!0}),ie.extend(i,n,null,{allOwnKeys:!0}),i.create=function(o){return oa(pr(e,o))},i}const Jt=oa(Lo);Jt.Axios=ai;Jt.CanceledError=Hr;Jt.CancelToken=sc;Jt.isCancel=na;Jt.VERSION=ia;Jt.toFormData=xi;Jt.AxiosError=ht;Jt.Cancel=Jt.CanceledError;Jt.all=function(n){return Promise.all(n)};Jt.spread=ac;Jt.isAxiosError=uc;Jt.mergeConfig=pr;Jt.AxiosHeaders=Fn;Jt.formToJSON=e=>ta(ie.isHTMLForm(e)?new FormData(e):e);Jt.HttpStatusCode=fc;Jt.default=Jt;const cc=Jt;var sa=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function lc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var oo={},hc={get exports(){return oo},set exports(e){oo=e}},ci={},dc={get exports(){return ci},set exports(e){ci=e}};/*! - * jQuery JavaScript Library v3.6.4 - * https://jquery.com/ - * - * Includes Sizzle.js - * https://sizzlejs.com/ - * - * Copyright OpenJS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2023-03-08T15:28Z - */var Os;function pc(){return Os||(Os=1,function(e){(function(n,i){e.exports=n.document?i(n,!0):function(r){if(!r.document)throw new Error("jQuery requires a window with a document");return i(r)}})(typeof window<"u"?window:sa,function(n,i){var r=[],o=Object.getPrototypeOf,a=r.slice,f=r.flat?function(t){return r.flat.call(t)}:function(t){return r.concat.apply([],t)},d=r.push,b=r.indexOf,v={},_=v.toString,x=v.hasOwnProperty,O=x.toString,C=O.call(Object),S={},P=function(s){return typeof s=="function"&&typeof s.nodeType!="number"&&typeof s.item!="function"},I=function(s){return s!=null&&s===s.window},N=n.document,H={type:!0,src:!0,nonce:!0,noModule:!0};function K(t,s,u){u=u||N;var l,m,w=u.createElement("script");if(w.text=t,s)for(l in H)m=s[l]||s.getAttribute&&s.getAttribute(l),m&&w.setAttribute(l,m);u.head.appendChild(w).parentNode.removeChild(w)}function he(t){return t==null?t+"":typeof t=="object"||typeof t=="function"?v[_.call(t)]||"object":typeof t}var me="3.6.4",h=function(t,s){return new h.fn.init(t,s)};h.fn=h.prototype={jquery:me,constructor:h,length:0,toArray:function(){return a.call(this)},get:function(t){return t==null?a.call(this):t<0?this[t+this.length]:this[t]},pushStack:function(t){var s=h.merge(this.constructor(),t);return s.prevObject=this,s},each:function(t){return h.each(this,t)},map:function(t){return this.pushStack(h.map(this,function(s,u){return t.call(s,u,s)}))},slice:function(){return this.pushStack(a.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(h.grep(this,function(t,s){return(s+1)%2}))},odd:function(){return this.pushStack(h.grep(this,function(t,s){return s%2}))},eq:function(t){var s=this.length,u=+t+(t<0?s:0);return this.pushStack(u>=0&&u0&&s-1 in t}var tt=function(t){var s,u,l,m,w,T,B,D,W,ee,de,V,te,Le,Ze,Ne,Wt,Ht,fn,xt="sizzle"+1*new Date,Ke=t.document,an=0,lt=0,Pt=Zr(),Cr=Zr(),Kr=Zr(),cn=Zr(),tr=function(R,F){return R===F&&(de=!0),0},nr={}.hasOwnProperty,un=[],$n=un.pop,mn=un.push,Wn=un.push,fs=un.slice,rr=function(R,F){for(var U=0,ne=R.length;U+~]|"+yt+")"+yt+"*"),Fu=new RegExp(yt+"|>"),Bu=new RegExp(Bi),Uu=new RegExp("^"+ir+"$"),Qr={ID:new RegExp("^#("+ir+")"),CLASS:new RegExp("^\\.("+ir+")"),TAG:new RegExp("^("+ir+"|[*])"),ATTR:new RegExp("^"+cs),PSEUDO:new RegExp("^"+Bi),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+yt+"*(even|odd|(([+-]|)(\\d*)n|)"+yt+"*(?:([+-]|)"+yt+"*(\\d+)|))"+yt+"*\\)|)","i"),bool:new RegExp("^(?:"+Fi+")$","i"),needsContext:new RegExp("^"+yt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+yt+"*((?:-\\d)?\\d*)"+yt+"*\\)|)(?=[^-]|$)","i")},Hu=/HTML$/i,qu=/^(?:input|select|textarea|button)$/i,zu=/^h\d$/i,Ar=/^[^{]+\{\s*\[native \w/,$u=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Ui=/[+~]/,Dn=new RegExp("\\\\[\\da-fA-F]{1,6}"+yt+"?|\\\\([^\\r\\n\\f])","g"),jn=function(R,F){var U="0x"+R.slice(1)-65536;return F||(U<0?String.fromCharCode(U+65536):String.fromCharCode(U>>10|55296,U&1023|56320))},hs=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ds=function(R,F){return F?R==="\0"?"�":R.slice(0,-1)+"\\"+R.charCodeAt(R.length-1).toString(16)+" ":"\\"+R},ps=function(){V()},Wu=ti(function(R){return R.disabled===!0&&R.nodeName.toLowerCase()==="fieldset"},{dir:"parentNode",next:"legend"});try{Wn.apply(un=fs.call(Ke.childNodes),Ke.childNodes),un[Ke.childNodes.length].nodeType}catch{Wn={apply:un.length?function(F,U){mn.apply(F,fs.call(U))}:function(F,U){for(var ne=F.length,z=0;F[ne++]=U[z++];);F.length=ne-1}}}function wt(R,F,U,ne){var z,se,pe,xe,Se,je,Ie,He=F&&F.ownerDocument,it=F?F.nodeType:9;if(U=U||[],typeof R!="string"||!R||it!==1&&it!==9&&it!==11)return U;if(!ne&&(V(F),F=F||te,Ze)){if(it!==11&&(Se=$u.exec(R)))if(z=Se[1]){if(it===9)if(pe=F.getElementById(z)){if(pe.id===z)return U.push(pe),U}else return U;else if(He&&(pe=He.getElementById(z))&&fn(F,pe)&&pe.id===z)return U.push(pe),U}else{if(Se[2])return Wn.apply(U,F.getElementsByTagName(R)),U;if((z=Se[3])&&u.getElementsByClassName&&F.getElementsByClassName)return Wn.apply(U,F.getElementsByClassName(z)),U}if(u.qsa&&!cn[R+" "]&&(!Ne||!Ne.test(R))&&(it!==1||F.nodeName.toLowerCase()!=="object")){if(Ie=R,He=F,it===1&&(Fu.test(R)||ls.test(R))){for(He=Ui.test(R)&&qi(F.parentNode)||F,(He!==F||!u.scope)&&((xe=F.getAttribute("id"))?xe=xe.replace(hs,ds):F.setAttribute("id",xe=xt)),je=T(R),se=je.length;se--;)je[se]=(xe?"#"+xe:":scope")+" "+ei(je[se]);Ie=je.join(",")}try{return Wn.apply(U,He.querySelectorAll(Ie)),U}catch{cn(R,!0)}finally{xe===xt&&F.removeAttribute("id")}}}return D(R.replace(Gr,"$1"),F,U,ne)}function Zr(){var R=[];function F(U,ne){return R.push(U+" ")>l.cacheLength&&delete F[R.shift()],F[U+" "]=ne}return F}function Sn(R){return R[xt]=!0,R}function bn(R){var F=te.createElement("fieldset");try{return!!R(F)}catch{return!1}finally{F.parentNode&&F.parentNode.removeChild(F),F=null}}function Hi(R,F){for(var U=R.split("|"),ne=U.length;ne--;)l.attrHandle[U[ne]]=F}function ys(R,F){var U=F&&R,ne=U&&R.nodeType===1&&F.nodeType===1&&R.sourceIndex-F.sourceIndex;if(ne)return ne;if(U){for(;U=U.nextSibling;)if(U===F)return-1}return R?1:-1}function Yu(R){return function(F){var U=F.nodeName.toLowerCase();return U==="input"&&F.type===R}}function Ju(R){return function(F){var U=F.nodeName.toLowerCase();return(U==="input"||U==="button")&&F.type===R}}function gs(R){return function(F){return"form"in F?F.parentNode&&F.disabled===!1?"label"in F?"label"in F.parentNode?F.parentNode.disabled===R:F.disabled===R:F.isDisabled===R||F.isDisabled!==!R&&Wu(F)===R:F.disabled===R:"label"in F?F.disabled===R:!1}}function or(R){return Sn(function(F){return F=+F,Sn(function(U,ne){for(var z,se=R([],U.length,F),pe=se.length;pe--;)U[z=se[pe]]&&(U[z]=!(ne[z]=U[z]))})})}function qi(R){return R&&typeof R.getElementsByTagName<"u"&&R}u=wt.support={},w=wt.isXML=function(R){var F=R&&R.namespaceURI,U=R&&(R.ownerDocument||R).documentElement;return!Hu.test(F||U&&U.nodeName||"HTML")},V=wt.setDocument=function(R){var F,U,ne=R?R.ownerDocument||R:Ke;return ne==te||ne.nodeType!==9||!ne.documentElement||(te=ne,Le=te.documentElement,Ze=!w(te),Ke!=te&&(U=te.defaultView)&&U.top!==U&&(U.addEventListener?U.addEventListener("unload",ps,!1):U.attachEvent&&U.attachEvent("onunload",ps)),u.scope=bn(function(z){return Le.appendChild(z).appendChild(te.createElement("div")),typeof z.querySelectorAll<"u"&&!z.querySelectorAll(":scope fieldset div").length}),u.cssHas=bn(function(){try{return te.querySelector(":has(*,:jqfake)"),!1}catch{return!0}}),u.attributes=bn(function(z){return z.className="i",!z.getAttribute("className")}),u.getElementsByTagName=bn(function(z){return z.appendChild(te.createComment("")),!z.getElementsByTagName("*").length}),u.getElementsByClassName=Ar.test(te.getElementsByClassName),u.getById=bn(function(z){return Le.appendChild(z).id=xt,!te.getElementsByName||!te.getElementsByName(xt).length}),u.getById?(l.filter.ID=function(z){var se=z.replace(Dn,jn);return function(pe){return pe.getAttribute("id")===se}},l.find.ID=function(z,se){if(typeof se.getElementById<"u"&&Ze){var pe=se.getElementById(z);return pe?[pe]:[]}}):(l.filter.ID=function(z){var se=z.replace(Dn,jn);return function(pe){var xe=typeof pe.getAttributeNode<"u"&&pe.getAttributeNode("id");return xe&&xe.value===se}},l.find.ID=function(z,se){if(typeof se.getElementById<"u"&&Ze){var pe,xe,Se,je=se.getElementById(z);if(je){if(pe=je.getAttributeNode("id"),pe&&pe.value===z)return[je];for(Se=se.getElementsByName(z),xe=0;je=Se[xe++];)if(pe=je.getAttributeNode("id"),pe&&pe.value===z)return[je]}return[]}}),l.find.TAG=u.getElementsByTagName?function(z,se){if(typeof se.getElementsByTagName<"u")return se.getElementsByTagName(z);if(u.qsa)return se.querySelectorAll(z)}:function(z,se){var pe,xe=[],Se=0,je=se.getElementsByTagName(z);if(z==="*"){for(;pe=je[Se++];)pe.nodeType===1&&xe.push(pe);return xe}return je},l.find.CLASS=u.getElementsByClassName&&function(z,se){if(typeof se.getElementsByClassName<"u"&&Ze)return se.getElementsByClassName(z)},Wt=[],Ne=[],(u.qsa=Ar.test(te.querySelectorAll))&&(bn(function(z){var se;Le.appendChild(z).innerHTML="",z.querySelectorAll("[msallowcapture^='']").length&&Ne.push("[*^$]="+yt+`*(?:''|"")`),z.querySelectorAll("[selected]").length||Ne.push("\\["+yt+"*(?:value|"+Fi+")"),z.querySelectorAll("[id~="+xt+"-]").length||Ne.push("~="),se=te.createElement("input"),se.setAttribute("name",""),z.appendChild(se),z.querySelectorAll("[name='']").length||Ne.push("\\["+yt+"*name"+yt+"*="+yt+`*(?:''|"")`),z.querySelectorAll(":checked").length||Ne.push(":checked"),z.querySelectorAll("a#"+xt+"+*").length||Ne.push(".#.+[+~]"),z.querySelectorAll("\\\f"),Ne.push("[\\r\\n\\f]")}),bn(function(z){z.innerHTML="";var se=te.createElement("input");se.setAttribute("type","hidden"),z.appendChild(se).setAttribute("name","D"),z.querySelectorAll("[name=d]").length&&Ne.push("name"+yt+"*[*^$|!~]?="),z.querySelectorAll(":enabled").length!==2&&Ne.push(":enabled",":disabled"),Le.appendChild(z).disabled=!0,z.querySelectorAll(":disabled").length!==2&&Ne.push(":enabled",":disabled"),z.querySelectorAll("*,:x"),Ne.push(",.*:")})),(u.matchesSelector=Ar.test(Ht=Le.matches||Le.webkitMatchesSelector||Le.mozMatchesSelector||Le.oMatchesSelector||Le.msMatchesSelector))&&bn(function(z){u.disconnectedMatch=Ht.call(z,"*"),Ht.call(z,"[s!='']:x"),Wt.push("!=",Bi)}),u.cssHas||Ne.push(":has"),Ne=Ne.length&&new RegExp(Ne.join("|")),Wt=Wt.length&&new RegExp(Wt.join("|")),F=Ar.test(Le.compareDocumentPosition),fn=F||Ar.test(Le.contains)?function(z,se){var pe=z.nodeType===9&&z.documentElement||z,xe=se&&se.parentNode;return z===xe||!!(xe&&xe.nodeType===1&&(pe.contains?pe.contains(xe):z.compareDocumentPosition&&z.compareDocumentPosition(xe)&16))}:function(z,se){if(se){for(;se=se.parentNode;)if(se===z)return!0}return!1},tr=F?function(z,se){if(z===se)return de=!0,0;var pe=!z.compareDocumentPosition-!se.compareDocumentPosition;return pe||(pe=(z.ownerDocument||z)==(se.ownerDocument||se)?z.compareDocumentPosition(se):1,pe&1||!u.sortDetached&&se.compareDocumentPosition(z)===pe?z==te||z.ownerDocument==Ke&&fn(Ke,z)?-1:se==te||se.ownerDocument==Ke&&fn(Ke,se)?1:ee?rr(ee,z)-rr(ee,se):0:pe&4?-1:1)}:function(z,se){if(z===se)return de=!0,0;var pe,xe=0,Se=z.parentNode,je=se.parentNode,Ie=[z],He=[se];if(!Se||!je)return z==te?-1:se==te?1:Se?-1:je?1:ee?rr(ee,z)-rr(ee,se):0;if(Se===je)return ys(z,se);for(pe=z;pe=pe.parentNode;)Ie.unshift(pe);for(pe=se;pe=pe.parentNode;)He.unshift(pe);for(;Ie[xe]===He[xe];)xe++;return xe?ys(Ie[xe],He[xe]):Ie[xe]==Ke?-1:He[xe]==Ke?1:0}),te},wt.matches=function(R,F){return wt(R,null,null,F)},wt.matchesSelector=function(R,F){if(V(R),u.matchesSelector&&Ze&&!cn[F+" "]&&(!Wt||!Wt.test(F))&&(!Ne||!Ne.test(F)))try{var U=Ht.call(R,F);if(U||u.disconnectedMatch||R.document&&R.document.nodeType!==11)return U}catch{cn(F,!0)}return wt(F,te,null,[R]).length>0},wt.contains=function(R,F){return(R.ownerDocument||R)!=te&&V(R),fn(R,F)},wt.attr=function(R,F){(R.ownerDocument||R)!=te&&V(R);var U=l.attrHandle[F.toLowerCase()],ne=U&&nr.call(l.attrHandle,F.toLowerCase())?U(R,F,!Ze):void 0;return ne!==void 0?ne:u.attributes||!Ze?R.getAttribute(F):(ne=R.getAttributeNode(F))&&ne.specified?ne.value:null},wt.escape=function(R){return(R+"").replace(hs,ds)},wt.error=function(R){throw new Error("Syntax error, unrecognized expression: "+R)},wt.uniqueSort=function(R){var F,U=[],ne=0,z=0;if(de=!u.detectDuplicates,ee=!u.sortStable&&R.slice(0),R.sort(tr),de){for(;F=R[z++];)F===R[z]&&(ne=U.push(z));for(;ne--;)R.splice(U[ne],1)}return ee=null,R},m=wt.getText=function(R){var F,U="",ne=0,z=R.nodeType;if(z){if(z===1||z===9||z===11){if(typeof R.textContent=="string")return R.textContent;for(R=R.firstChild;R;R=R.nextSibling)U+=m(R)}else if(z===3||z===4)return R.nodeValue}else for(;F=R[ne++];)U+=m(F);return U},l=wt.selectors={cacheLength:50,createPseudo:Sn,match:Qr,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(R){return R[1]=R[1].replace(Dn,jn),R[3]=(R[3]||R[4]||R[5]||"").replace(Dn,jn),R[2]==="~="&&(R[3]=" "+R[3]+" "),R.slice(0,4)},CHILD:function(R){return R[1]=R[1].toLowerCase(),R[1].slice(0,3)==="nth"?(R[3]||wt.error(R[0]),R[4]=+(R[4]?R[5]+(R[6]||1):2*(R[3]==="even"||R[3]==="odd")),R[5]=+(R[7]+R[8]||R[3]==="odd")):R[3]&&wt.error(R[0]),R},PSEUDO:function(R){var F,U=!R[6]&&R[2];return Qr.CHILD.test(R[0])?null:(R[3]?R[2]=R[4]||R[5]||"":U&&Bu.test(U)&&(F=T(U,!0))&&(F=U.indexOf(")",U.length-F)-U.length)&&(R[0]=R[0].slice(0,F),R[2]=U.slice(0,F)),R.slice(0,3))}},filter:{TAG:function(R){var F=R.replace(Dn,jn).toLowerCase();return R==="*"?function(){return!0}:function(U){return U.nodeName&&U.nodeName.toLowerCase()===F}},CLASS:function(R){var F=Pt[R+" "];return F||(F=new RegExp("(^|"+yt+")"+R+"("+yt+"|$)"))&&Pt(R,function(U){return F.test(typeof U.className=="string"&&U.className||typeof U.getAttribute<"u"&&U.getAttribute("class")||"")})},ATTR:function(R,F,U){return function(ne){var z=wt.attr(ne,R);return z==null?F==="!=":F?(z+="",F==="="?z===U:F==="!="?z!==U:F==="^="?U&&z.indexOf(U)===0:F==="*="?U&&z.indexOf(U)>-1:F==="$="?U&&z.slice(-U.length)===U:F==="~="?(" "+z.replace(Du," ")+" ").indexOf(U)>-1:F==="|="?z===U||z.slice(0,U.length+1)===U+"-":!1):!0}},CHILD:function(R,F,U,ne,z){var se=R.slice(0,3)!=="nth",pe=R.slice(-4)!=="last",xe=F==="of-type";return ne===1&&z===0?function(Se){return!!Se.parentNode}:function(Se,je,Ie){var He,it,_t,Fe,Yt,nn,ln=se!==pe?"nextSibling":"previousSibling",At=Se.parentNode,Or=xe&&Se.nodeName.toLowerCase(),Rr=!Ie&&!xe,hn=!1;if(At){if(se){for(;ln;){for(Fe=Se;Fe=Fe[ln];)if(xe?Fe.nodeName.toLowerCase()===Or:Fe.nodeType===1)return!1;nn=ln=R==="only"&&!nn&&"nextSibling"}return!0}if(nn=[pe?At.firstChild:At.lastChild],pe&&Rr){for(Fe=At,_t=Fe[xt]||(Fe[xt]={}),it=_t[Fe.uniqueID]||(_t[Fe.uniqueID]={}),He=it[R]||[],Yt=He[0]===an&&He[1],hn=Yt&&He[2],Fe=Yt&&At.childNodes[Yt];Fe=++Yt&&Fe&&Fe[ln]||(hn=Yt=0)||nn.pop();)if(Fe.nodeType===1&&++hn&&Fe===Se){it[R]=[an,Yt,hn];break}}else if(Rr&&(Fe=Se,_t=Fe[xt]||(Fe[xt]={}),it=_t[Fe.uniqueID]||(_t[Fe.uniqueID]={}),He=it[R]||[],Yt=He[0]===an&&He[1],hn=Yt),hn===!1)for(;(Fe=++Yt&&Fe&&Fe[ln]||(hn=Yt=0)||nn.pop())&&!((xe?Fe.nodeName.toLowerCase()===Or:Fe.nodeType===1)&&++hn&&(Rr&&(_t=Fe[xt]||(Fe[xt]={}),it=_t[Fe.uniqueID]||(_t[Fe.uniqueID]={}),it[R]=[an,hn]),Fe===Se)););return hn-=z,hn===ne||hn%ne===0&&hn/ne>=0}}},PSEUDO:function(R,F){var U,ne=l.pseudos[R]||l.setFilters[R.toLowerCase()]||wt.error("unsupported pseudo: "+R);return ne[xt]?ne(F):ne.length>1?(U=[R,R,"",F],l.setFilters.hasOwnProperty(R.toLowerCase())?Sn(function(z,se){for(var pe,xe=ne(z,F),Se=xe.length;Se--;)pe=rr(z,xe[Se]),z[pe]=!(se[pe]=xe[Se])}):function(z){return ne(z,0,U)}):ne}},pseudos:{not:Sn(function(R){var F=[],U=[],ne=B(R.replace(Gr,"$1"));return ne[xt]?Sn(function(z,se,pe,xe){for(var Se,je=ne(z,null,xe,[]),Ie=z.length;Ie--;)(Se=je[Ie])&&(z[Ie]=!(se[Ie]=Se))}):function(z,se,pe){return F[0]=z,ne(F,null,pe,U),F[0]=null,!U.pop()}}),has:Sn(function(R){return function(F){return wt(R,F).length>0}}),contains:Sn(function(R){return R=R.replace(Dn,jn),function(F){return(F.textContent||m(F)).indexOf(R)>-1}}),lang:Sn(function(R){return Uu.test(R||"")||wt.error("unsupported lang: "+R),R=R.replace(Dn,jn).toLowerCase(),function(F){var U;do if(U=Ze?F.lang:F.getAttribute("xml:lang")||F.getAttribute("lang"))return U=U.toLowerCase(),U===R||U.indexOf(R+"-")===0;while((F=F.parentNode)&&F.nodeType===1);return!1}}),target:function(R){var F=t.location&&t.location.hash;return F&&F.slice(1)===R.id},root:function(R){return R===Le},focus:function(R){return R===te.activeElement&&(!te.hasFocus||te.hasFocus())&&!!(R.type||R.href||~R.tabIndex)},enabled:gs(!1),disabled:gs(!0),checked:function(R){var F=R.nodeName.toLowerCase();return F==="input"&&!!R.checked||F==="option"&&!!R.selected},selected:function(R){return R.parentNode&&R.parentNode.selectedIndex,R.selected===!0},empty:function(R){for(R=R.firstChild;R;R=R.nextSibling)if(R.nodeType<6)return!1;return!0},parent:function(R){return!l.pseudos.empty(R)},header:function(R){return zu.test(R.nodeName)},input:function(R){return qu.test(R.nodeName)},button:function(R){var F=R.nodeName.toLowerCase();return F==="input"&&R.type==="button"||F==="button"},text:function(R){var F;return R.nodeName.toLowerCase()==="input"&&R.type==="text"&&((F=R.getAttribute("type"))==null||F.toLowerCase()==="text")},first:or(function(){return[0]}),last:or(function(R,F){return[F-1]}),eq:or(function(R,F,U){return[U<0?U+F:U]}),even:or(function(R,F){for(var U=0;UF?F:U;--ne>=0;)R.push(ne);return R}),gt:or(function(R,F,U){for(var ne=U<0?U+F:U;++ne1?function(F,U,ne){for(var z=R.length;z--;)if(!R[z](F,U,ne))return!1;return!0}:R[0]}function Vu(R,F,U){for(var ne=0,z=F.length;ne-1&&(pe[Ie]=!(xe[Ie]=it))}}else At=ni(At===xe?At.splice(Yt,At.length):At),z?z(null,xe,At,je):Wn.apply(xe,At)})}function Wi(R){for(var F,U,ne,z=R.length,se=l.relative[R[0].type],pe=se||l.relative[" "],xe=se?1:0,Se=ti(function(He){return He===F},pe,!0),je=ti(function(He){return rr(F,He)>-1},pe,!0),Ie=[function(He,it,_t){var Fe=!se&&(_t||it!==W)||((F=it).nodeType?Se(He,it,_t):je(He,it,_t));return F=null,Fe}];xe1&&zi(Ie),xe>1&&ei(R.slice(0,xe-1).concat({value:R[xe-2].type===" "?"*":""})).replace(Gr,"$1"),U,xe0,ne=R.length>0,z=function(se,pe,xe,Se,je){var Ie,He,it,_t=0,Fe="0",Yt=se&&[],nn=[],ln=W,At=se||ne&&l.find.TAG("*",je),Or=an+=ln==null?1:Math.random()||.1,Rr=At.length;for(je&&(W=pe==te||pe||je);Fe!==Rr&&(Ie=At[Fe])!=null;Fe++){if(ne&&Ie){for(He=0,!pe&&Ie.ownerDocument!=te&&(V(Ie),xe=!Ze);it=R[He++];)if(it(Ie,pe||te,xe)){Se.push(Ie);break}je&&(an=Or)}U&&((Ie=!it&&Ie)&&_t--,se&&Yt.push(Ie))}if(_t+=Fe,U&&Fe!==_t){for(He=0;it=F[He++];)it(Yt,nn,pe,xe);if(se){if(_t>0)for(;Fe--;)Yt[Fe]||nn[Fe]||(nn[Fe]=$n.call(Se));nn=ni(nn)}Wn.apply(Se,nn),je&&!se&&nn.length>0&&_t+F.length>1&&wt.uniqueSort(Se)}return je&&(an=Or,W=ln),Yt};return U?Sn(z):z}return B=wt.compile=function(R,F){var U,ne=[],z=[],se=Kr[R+" "];if(!se){for(F||(F=T(R)),U=F.length;U--;)se=Wi(F[U]),se[xt]?ne.push(se):z.push(se);se=Kr(R,Xu(z,ne)),se.selector=R}return se},D=wt.select=function(R,F,U,ne){var z,se,pe,xe,Se,je=typeof R=="function"&&R,Ie=!ne&&T(R=je.selector||R);if(U=U||[],Ie.length===1){if(se=Ie[0]=Ie[0].slice(0),se.length>2&&(pe=se[0]).type==="ID"&&F.nodeType===9&&Ze&&l.relative[se[1].type]){if(F=(l.find.ID(pe.matches[0].replace(Dn,jn),F)||[])[0],F)je&&(F=F.parentNode);else return U;R=R.slice(se.shift().value.length)}for(z=Qr.needsContext.test(R)?0:se.length;z--&&(pe=se[z],!l.relative[xe=pe.type]);)if((Se=l.find[xe])&&(ne=Se(pe.matches[0].replace(Dn,jn),Ui.test(se[0].type)&&qi(F.parentNode)||F))){if(se.splice(z,1),R=ne.length&&ei(se),!R)return Wn.apply(U,ne),U;break}}return(je||B(R,Ie))(ne,F,!Ze,U,!F||Ui.test(R)&&qi(F.parentNode)||F),U},u.sortStable=xt.split("").sort(tr).join("")===xt,u.detectDuplicates=!!de,V(),u.sortDetached=bn(function(R){return R.compareDocumentPosition(te.createElement("fieldset"))&1}),bn(function(R){return R.innerHTML="",R.firstChild.getAttribute("href")==="#"})||Hi("type|href|height|width",function(R,F,U){if(!U)return R.getAttribute(F,F.toLowerCase()==="type"?1:2)}),(!u.attributes||!bn(function(R){return R.innerHTML="",R.firstChild.setAttribute("value",""),R.firstChild.getAttribute("value")===""}))&&Hi("value",function(R,F,U){if(!U&&R.nodeName.toLowerCase()==="input")return R.defaultValue}),bn(function(R){return R.getAttribute("disabled")==null})||Hi(Fi,function(R,F,U){var ne;if(!U)return R[F]===!0?F.toLowerCase():(ne=R.getAttributeNode(F))&&ne.specified?ne.value:null}),wt}(n);h.find=tt,h.expr=tt.selectors,h.expr[":"]=h.expr.pseudos,h.uniqueSort=h.unique=tt.uniqueSort,h.text=tt.getText,h.isXMLDoc=tt.isXML,h.contains=tt.contains,h.escapeSelector=tt.escape;var J=function(t,s,u){for(var l=[],m=u!==void 0;(t=t[s])&&t.nodeType!==9;)if(t.nodeType===1){if(m&&h(t).is(u))break;l.push(t)}return l},X=function(t,s){for(var u=[];t;t=t.nextSibling)t.nodeType===1&&t!==s&&u.push(t);return u},G=h.expr.match.needsContext;function re(t,s){return t.nodeName&&t.nodeName.toLowerCase()===s.toLowerCase()}var ve=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function ft(t,s,u){return P(s)?h.grep(t,function(l,m){return!!s.call(l,m,l)!==u}):s.nodeType?h.grep(t,function(l){return l===s!==u}):typeof s!="string"?h.grep(t,function(l){return b.call(s,l)>-1!==u}):h.filter(s,t,u)}h.filter=function(t,s,u){var l=s[0];return u&&(t=":not("+t+")"),s.length===1&&l.nodeType===1?h.find.matchesSelector(l,t)?[l]:[]:h.find.matches(t,h.grep(s,function(m){return m.nodeType===1}))},h.fn.extend({find:function(t){var s,u,l=this.length,m=this;if(typeof t!="string")return this.pushStack(h(t).filter(function(){for(s=0;s1?h.uniqueSort(u):u},filter:function(t){return this.pushStack(ft(this,t||[],!1))},not:function(t){return this.pushStack(ft(this,t||[],!0))},is:function(t){return!!ft(this,typeof t=="string"&&G.test(t)?h(t):t||[],!1).length}});var vt,jt=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,Lt=h.fn.init=function(t,s,u){var l,m;if(!t)return this;if(u=u||vt,typeof t=="string")if(t[0]==="<"&&t[t.length-1]===">"&&t.length>=3?l=[null,t,null]:l=jt.exec(t),l&&(l[1]||!s))if(l[1]){if(s=s instanceof h?s[0]:s,h.merge(this,h.parseHTML(l[1],s&&s.nodeType?s.ownerDocument||s:N,!0)),ve.test(l[1])&&h.isPlainObject(s))for(l in s)P(this[l])?this[l](s[l]):this.attr(l,s[l]);return this}else return m=N.getElementById(l[2]),m&&(this[0]=m,this.length=1),this;else return!s||s.jquery?(s||u).find(t):this.constructor(s).find(t);else{if(t.nodeType)return this[0]=t,this.length=1,this;if(P(t))return u.ready!==void 0?u.ready(t):t(h)}return h.makeArray(t,this)};Lt.prototype=h.fn,vt=h(N);var kt=/^(?:parents|prev(?:Until|All))/,Ft={children:!0,contents:!0,next:!0,prev:!0};h.fn.extend({has:function(t){var s=h(t,this),u=s.length;return this.filter(function(){for(var l=0;l-1:u.nodeType===1&&h.find.matchesSelector(u,t))){w.push(u);break}}return this.pushStack(w.length>1?h.uniqueSort(w):w)},index:function(t){return t?typeof t=="string"?b.call(h(t),this[0]):b.call(this,t.jquery?t[0]:t):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(t,s){return this.pushStack(h.uniqueSort(h.merge(this.get(),h(t,s))))},addBack:function(t){return this.add(t==null?this.prevObject:this.prevObject.filter(t))}});function Bt(t,s){for(;(t=t[s])&&t.nodeType!==1;);return t}h.each({parent:function(t){var s=t.parentNode;return s&&s.nodeType!==11?s:null},parents:function(t){return J(t,"parentNode")},parentsUntil:function(t,s,u){return J(t,"parentNode",u)},next:function(t){return Bt(t,"nextSibling")},prev:function(t){return Bt(t,"previousSibling")},nextAll:function(t){return J(t,"nextSibling")},prevAll:function(t){return J(t,"previousSibling")},nextUntil:function(t,s,u){return J(t,"nextSibling",u)},prevUntil:function(t,s,u){return J(t,"previousSibling",u)},siblings:function(t){return X((t.parentNode||{}).firstChild,t)},children:function(t){return X(t.firstChild)},contents:function(t){return t.contentDocument!=null&&o(t.contentDocument)?t.contentDocument:(re(t,"template")&&(t=t.content||t),h.merge([],t.childNodes))}},function(t,s){h.fn[t]=function(u,l){var m=h.map(this,s,u);return t.slice(-5)!=="Until"&&(l=u),l&&typeof l=="string"&&(m=h.filter(l,m)),this.length>1&&(Ft[t]||h.uniqueSort(m),kt.test(t)&&m.reverse()),this.pushStack(m)}});var Vt=/[^\x20\t\r\n\f]+/g;function yn(t){var s={};return h.each(t.match(Vt)||[],function(u,l){s[l]=!0}),s}h.Callbacks=function(t){t=typeof t=="string"?yn(t):h.extend({},t);var s,u,l,m,w=[],T=[],B=-1,D=function(){for(m=m||t.once,l=s=!0;T.length;B=-1)for(u=T.shift();++B-1;)w.splice(V,1),V<=B&&B--}),this},has:function(ee){return ee?h.inArray(ee,w)>-1:w.length>0},empty:function(){return w&&(w=[]),this},disable:function(){return m=T=[],w=u="",this},disabled:function(){return!w},lock:function(){return m=T=[],!u&&!s&&(w=u=""),this},locked:function(){return!!m},fireWith:function(ee,de){return m||(de=de||[],de=[ee,de.slice?de.slice():de],T.push(de),s||D()),this},fire:function(){return W.fireWith(this,arguments),this},fired:function(){return!!l}};return W};function tn(t){return t}function wn(t){throw t}function St(t,s,u,l){var m;try{t&&P(m=t.promise)?m.call(t).done(s).fail(u):t&&P(m=t.then)?m.call(t,s,u):s.apply(void 0,[t].slice(l))}catch(w){u.apply(void 0,[w])}}h.extend({Deferred:function(t){var s=[["notify","progress",h.Callbacks("memory"),h.Callbacks("memory"),2],["resolve","done",h.Callbacks("once memory"),h.Callbacks("once memory"),0,"resolved"],["reject","fail",h.Callbacks("once memory"),h.Callbacks("once memory"),1,"rejected"]],u="pending",l={state:function(){return u},always:function(){return m.done(arguments).fail(arguments),this},catch:function(w){return l.then(null,w)},pipe:function(){var w=arguments;return h.Deferred(function(T){h.each(s,function(B,D){var W=P(w[D[4]])&&w[D[4]];m[D[1]](function(){var ee=W&&W.apply(this,arguments);ee&&P(ee.promise)?ee.promise().progress(T.notify).done(T.resolve).fail(T.reject):T[D[0]+"With"](this,W?[ee]:arguments)})}),w=null}).promise()},then:function(w,T,B){var D=0;function W(ee,de,V,te){return function(){var Le=this,Ze=arguments,Ne=function(){var Ht,fn;if(!(ee=D&&(V!==wn&&(Le=void 0,Ze=[Ht]),de.rejectWith(Le,Ze))}};ee?Wt():(h.Deferred.getStackHook&&(Wt.stackTrace=h.Deferred.getStackHook()),n.setTimeout(Wt))}}return h.Deferred(function(ee){s[0][3].add(W(0,ee,P(B)?B:tn,ee.notifyWith)),s[1][3].add(W(0,ee,P(w)?w:tn)),s[2][3].add(W(0,ee,P(T)?T:wn))}).promise()},promise:function(w){return w!=null?h.extend(w,l):l}},m={};return h.each(s,function(w,T){var B=T[2],D=T[5];l[T[1]]=B.add,D&&B.add(function(){u=D},s[3-w][2].disable,s[3-w][3].disable,s[0][2].lock,s[0][3].lock),B.add(T[3].fire),m[T[0]]=function(){return m[T[0]+"With"](this===m?void 0:this,arguments),this},m[T[0]+"With"]=B.fireWith}),l.promise(m),t&&t.call(m,m),m},when:function(t){var s=arguments.length,u=s,l=Array(u),m=a.call(arguments),w=h.Deferred(),T=function(B){return function(D){l[B]=this,m[B]=arguments.length>1?a.call(arguments):D,--s||w.resolveWith(l,m)}};if(s<=1&&(St(t,w.done(T(u)).resolve,w.reject,!s),w.state()==="pending"||P(m[u]&&m[u].then)))return w.then();for(;u--;)St(m[u],T(u),w.reject);return w.promise()}});var It=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;h.Deferred.exceptionHook=function(t,s){n.console&&n.console.warn&&t&&It.test(t.name)&&n.console.warn("jQuery.Deferred exception: "+t.message,t.stack,s)},h.readyException=function(t){n.setTimeout(function(){throw t})};var Mt=h.Deferred();h.fn.ready=function(t){return Mt.then(t).catch(function(s){h.readyException(s)}),this},h.extend({isReady:!1,readyWait:1,ready:function(t){(t===!0?--h.readyWait:h.isReady)||(h.isReady=!0,!(t!==!0&&--h.readyWait>0)&&Mt.resolveWith(N,[h]))}}),h.ready.then=Mt.then;function Nt(){N.removeEventListener("DOMContentLoaded",Nt),n.removeEventListener("load",Nt),h.ready()}N.readyState==="complete"||N.readyState!=="loading"&&!N.documentElement.doScroll?n.setTimeout(h.ready):(N.addEventListener("DOMContentLoaded",Nt),n.addEventListener("load",Nt));var qe=function(t,s,u,l,m,w,T){var B=0,D=t.length,W=u==null;if(he(u)==="object"){m=!0;for(B in u)qe(t,s,B,u[B],!0,w,T)}else if(l!==void 0&&(m=!0,P(l)||(T=!0),W&&(T?(s.call(t,l),s=null):(W=s,s=function(ee,de,V){return W.call(h(ee),V)})),s))for(;B1,null,!0)},removeData:function(t){return this.each(function(){De.remove(this,t)})}}),h.extend({queue:function(t,s,u){var l;if(t)return s=(s||"fx")+"queue",l=ke.get(t,s),u&&(!l||Array.isArray(u)?l=ke.access(t,s,h.makeArray(u)):l.push(u)),l||[]},dequeue:function(t,s){s=s||"fx";var u=h.queue(t,s),l=u.length,m=u.shift(),w=h._queueHooks(t,s),T=function(){h.dequeue(t,s)};m==="inprogress"&&(m=u.shift(),l--),m&&(s==="fx"&&u.unshift("inprogress"),delete w.stop,m.call(t,T,w)),!l&&w&&w.empty.fire()},_queueHooks:function(t,s){var u=s+"queueHooks";return ke.get(t,u)||ke.access(t,u,{empty:h.Callbacks("once memory").add(function(){ke.remove(t,[s+"queue",u])})})}}),h.fn.extend({queue:function(t,s){var u=2;return typeof t!="string"&&(s=t,t="fx",u--),arguments.length\x20\t\r\n\f]*)/i,Nn=/^$|^module$|\/(?:java|ecma)script/i;(function(){var t=N.createDocumentFragment(),s=t.appendChild(N.createElement("div")),u=N.createElement("input");u.setAttribute("type","radio"),u.setAttribute("checked","checked"),u.setAttribute("name","t"),s.appendChild(u),S.checkClone=s.cloneNode(!0).cloneNode(!0).lastChild.checked,s.innerHTML="",S.noCloneChecked=!!s.cloneNode(!0).lastChild.defaultValue,s.innerHTML="",S.option=!!s.lastChild})();var zt={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};zt.tbody=zt.tfoot=zt.colgroup=zt.caption=zt.thead,zt.th=zt.td,S.option||(zt.optgroup=zt.option=[1,""]);function $t(t,s){var u;return typeof t.getElementsByTagName<"u"?u=t.getElementsByTagName(s||"*"):typeof t.querySelectorAll<"u"?u=t.querySelectorAll(s||"*"):u=[],s===void 0||s&&re(t,s)?h.merge([t],u):u}function Ln(t,s){for(var u=0,l=t.length;u-1){m&&m.push(w);continue}if(W=$(w),T=$t(de.appendChild(w),"script"),W&&Ln(T),u)for(ee=0;w=T[ee++];)Nn.test(w.type||"")&&u.push(w)}return de}var er=/^([^.]*)(?:\.(.+)|)/;function vn(){return!0}function xn(){return!1}function _r(t,s){return t===Jr()==(s==="focus")}function Jr(){try{return N.activeElement}catch{}}function Sr(t,s,u,l,m,w){var T,B;if(typeof s=="object"){typeof u!="string"&&(l=l||u,u=void 0);for(B in s)Sr(t,B,u,l,s[B],w);return t}if(l==null&&m==null?(m=u,l=u=void 0):m==null&&(typeof u=="string"?(m=l,l=void 0):(m=l,l=u,u=void 0)),m===!1)m=xn;else if(!m)return t;return w===1&&(T=m,m=function(D){return h().off(D),T.apply(this,arguments)},m.guid=T.guid||(T.guid=h.guid++)),t.each(function(){h.event.add(this,s,m,l,u)})}h.event={global:{},add:function(t,s,u,l,m){var w,T,B,D,W,ee,de,V,te,Le,Ze,Ne=ke.get(t);if(sn(t))for(u.handler&&(w=u,u=w.handler,m=w.selector),m&&h.find.matchesSelector(M,m),u.guid||(u.guid=h.guid++),(D=Ne.events)||(D=Ne.events=Object.create(null)),(T=Ne.handle)||(T=Ne.handle=function(Wt){return typeof h<"u"&&h.event.triggered!==Wt.type?h.event.dispatch.apply(t,arguments):void 0}),s=(s||"").match(Vt)||[""],W=s.length;W--;)B=er.exec(s[W])||[],te=Ze=B[1],Le=(B[2]||"").split(".").sort(),te&&(de=h.event.special[te]||{},te=(m?de.delegateType:de.bindType)||te,de=h.event.special[te]||{},ee=h.extend({type:te,origType:Ze,data:l,handler:u,guid:u.guid,selector:m,needsContext:m&&h.expr.match.needsContext.test(m),namespace:Le.join(".")},w),(V=D[te])||(V=D[te]=[],V.delegateCount=0,(!de.setup||de.setup.call(t,l,Le,T)===!1)&&t.addEventListener&&t.addEventListener(te,T)),de.add&&(de.add.call(t,ee),ee.handler.guid||(ee.handler.guid=u.guid)),m?V.splice(V.delegateCount++,0,ee):V.push(ee),h.event.global[te]=!0)},remove:function(t,s,u,l,m){var w,T,B,D,W,ee,de,V,te,Le,Ze,Ne=ke.hasData(t)&&ke.get(t);if(!(!Ne||!(D=Ne.events))){for(s=(s||"").match(Vt)||[""],W=s.length;W--;){if(B=er.exec(s[W])||[],te=Ze=B[1],Le=(B[2]||"").split(".").sort(),!te){for(te in D)h.event.remove(t,te+s[W],u,l,!0);continue}for(de=h.event.special[te]||{},te=(l?de.delegateType:de.bindType)||te,V=D[te]||[],B=B[2]&&new RegExp("(^|\\.)"+Le.join("\\.(?:.*\\.|)")+"(\\.|$)"),T=w=V.length;w--;)ee=V[w],(m||Ze===ee.origType)&&(!u||u.guid===ee.guid)&&(!B||B.test(ee.namespace))&&(!l||l===ee.selector||l==="**"&&ee.selector)&&(V.splice(w,1),ee.selector&&V.delegateCount--,de.remove&&de.remove.call(t,ee));T&&!V.length&&((!de.teardown||de.teardown.call(t,Le,Ne.handle)===!1)&&h.removeEvent(t,te,Ne.handle),delete D[te])}h.isEmptyObject(D)&&ke.remove(t,"handle events")}},dispatch:function(t){var s,u,l,m,w,T,B=new Array(arguments.length),D=h.event.fix(t),W=(ke.get(this,"events")||Object.create(null))[D.type]||[],ee=h.event.special[D.type]||{};for(B[0]=D,s=1;s=1)){for(;W!==this;W=W.parentNode||this)if(W.nodeType===1&&!(t.type==="click"&&W.disabled===!0)){for(w=[],T={},u=0;u-1:h.find(m,this,null,[W]).length),T[m]&&w.push(l);w.length&&B.push({elem:W,handlers:w})}}return W=this,D\s*$/g;function hr(t,s){return re(t,"table")&&re(s.nodeType!==11?s:s.firstChild,"tr")&&h(t).children("tbody")[0]||t}function Tr(t){return t.type=(t.getAttribute("type")!==null)+"/"+t.type,t}function Vr(t){return(t.type||"").slice(0,5)==="true/"?t.type=t.type.slice(5):t.removeAttribute("type"),t}function Xr(t,s){var u,l,m,w,T,B,D;if(s.nodeType===1){if(ke.hasData(t)&&(w=ke.get(t),D=w.events,D)){ke.remove(s,"handle events");for(m in D)for(u=0,l=D[m].length;u1&&typeof te=="string"&&!S.checkClone&&In.test(te))return t.each(function(Ze){var Ne=t.eq(Ze);Le&&(s[0]=te.call(this,Ze,Ne.html())),Mn(Ne,s,u,l)});if(de&&(m=qn(s,t[0].ownerDocument,!1,t,l),w=m.firstChild,m.childNodes.length===1&&(m=w),w||l)){for(T=h.map($t(m,"script"),Tr),B=T.length;ee0&&Ln(T,!D&&$t(t,"script")),B},cleanData:function(t){for(var s,u,l,m=h.event.special,w=0;(u=t[w])!==void 0;w++)if(sn(u)){if(s=u[ke.expando]){if(s.events)for(l in s.events)m[l]?h.event.remove(u,l):h.removeEvent(u,l,s.handle);u[ke.expando]=void 0}u[De.expando]&&(u[De.expando]=void 0)}}}),h.fn.extend({detach:function(t){return g(this,t,!0)},remove:function(t){return g(this,t)},text:function(t){return qe(this,function(s){return s===void 0?h.text(this):this.empty().each(function(){(this.nodeType===1||this.nodeType===11||this.nodeType===9)&&(this.textContent=s)})},null,t,arguments.length)},append:function(){return Mn(this,arguments,function(t){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var s=hr(this,t);s.appendChild(t)}})},prepend:function(){return Mn(this,arguments,function(t){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var s=hr(this,t);s.insertBefore(t,s.firstChild)}})},before:function(){return Mn(this,arguments,function(t){this.parentNode&&this.parentNode.insertBefore(t,this)})},after:function(){return Mn(this,arguments,function(t){this.parentNode&&this.parentNode.insertBefore(t,this.nextSibling)})},empty:function(){for(var t,s=0;(t=this[s])!=null;s++)t.nodeType===1&&(h.cleanData($t(t,!1)),t.textContent="");return this},clone:function(t,s){return t=t??!1,s=s??t,this.map(function(){return h.clone(this,t,s)})},html:function(t){return qe(this,function(s){var u=this[0]||{},l=0,m=this.length;if(s===void 0&&u.nodeType===1)return u.innerHTML;if(typeof s=="string"&&!_n.test(s)&&!zt[(kn.exec(s)||["",""])[1].toLowerCase()]){s=h.htmlPrefilter(s);try{for(;l=0&&(D+=Math.max(0,Math.ceil(t["offset"+s[0].toUpperCase()+s.slice(1)]-w-D-B-.5))||0),D}function Oe(t,s,u){var l=c(t),m=!S.boxSizingReliable()||u,w=m&&h.css(t,"boxSizing",!1,l)==="border-box",T=w,B=ge(t,s,l),D="offset"+s[0].toUpperCase()+s.slice(1);if(A.test(B)){if(!u)return B;B="auto"}return(!S.boxSizingReliable()&&w||!S.reliableTrDimensions()&&re(t,"tr")||B==="auto"||!parseFloat(B)&&h.css(t,"display",!1,l)==="inline")&&t.getClientRects().length&&(w=h.css(t,"boxSizing",!1,l)==="border-box",T=D in t,T&&(B=t[D])),B=parseFloat(B)||0,B+Ue(t,s,u||(w?"border":"content"),T,l,B)+"px"}h.extend({cssHooks:{opacity:{get:function(t,s){if(s){var u=ge(t,"opacity");return u===""?"1":u}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(t,s,u,l){if(!(!t||t.nodeType===3||t.nodeType===8||!t.style)){var m,w,T,B=Rt(s),D=E.test(s),W=t.style;if(D||(s=Be(B)),T=h.cssHooks[s]||h.cssHooks[B],u!==void 0){if(w=typeof u,w==="string"&&(m=p.exec(u))&&m[1]&&(u=rt(t,s,m),w="number"),u==null||u!==u)return;w==="number"&&!D&&(u+=m&&m[3]||(h.cssNumber[B]?"":"px")),!S.clearCloneStyle&&u===""&&s.indexOf("background")===0&&(W[s]="inherit"),(!T||!("set"in T)||(u=T.set(t,u,l))!==void 0)&&(D?W.setProperty(s,u):W[s]=u)}else return T&&"get"in T&&(m=T.get(t,!1,l))!==void 0?m:W[s]}},css:function(t,s,u,l){var m,w,T,B=Rt(s),D=E.test(s);return D||(s=Be(B)),T=h.cssHooks[s]||h.cssHooks[B],T&&"get"in T&&(m=T.get(t,!0,u)),m===void 0&&(m=ge(t,s,l)),m==="normal"&&s in $e&&(m=$e[s]),u===""||u?(w=parseFloat(m),u===!0||isFinite(w)?w||0:m):m}}),h.each(["height","width"],function(t,s){h.cssHooks[s]={get:function(u,l,m){if(l)return Ge.test(h.css(u,"display"))&&(!u.getClientRects().length||!u.getBoundingClientRect().width)?k(u,Ve,function(){return Oe(u,s,m)}):Oe(u,s,m)},set:function(u,l,m){var w,T=c(u),B=!S.scrollboxSize()&&T.position==="absolute",D=B||m,W=D&&h.css(u,"boxSizing",!1,T)==="border-box",ee=m?Ue(u,s,m,W,T):0;return W&&B&&(ee-=Math.ceil(u["offset"+s[0].toUpperCase()+s.slice(1)]-parseFloat(T[s])-Ue(u,s,"border",!1,T)-.5)),ee&&(w=p.exec(l))&&(w[3]||"px")!=="px"&&(u.style[s]=l,l=h.css(u,s)),We(u,l,ee)}}}),h.cssHooks.marginLeft=Ce(S.reliableMarginLeft,function(t,s){if(s)return(parseFloat(ge(t,"marginLeft"))||t.getBoundingClientRect().left-k(t,{marginLeft:0},function(){return t.getBoundingClientRect().left}))+"px"}),h.each({margin:"",padding:"",border:"Width"},function(t,s){h.cssHooks[t+s]={expand:function(u){for(var l=0,m={},w=typeof u=="string"?u.split(" "):[u];l<4;l++)m[t+y[l]+s]=w[l]||w[l-2]||w[0];return m}},t!=="margin"&&(h.cssHooks[t+s].set=We)}),h.fn.extend({css:function(t,s){return qe(this,function(u,l,m){var w,T,B={},D=0;if(Array.isArray(l)){for(w=c(u),T=l.length;D1)}});function Te(t,s,u,l,m){return new Te.prototype.init(t,s,u,l,m)}h.Tween=Te,Te.prototype={constructor:Te,init:function(t,s,u,l,m,w){this.elem=t,this.prop=u,this.easing=m||h.easing._default,this.options=s,this.start=this.now=this.cur(),this.end=l,this.unit=w||(h.cssNumber[u]?"":"px")},cur:function(){var t=Te.propHooks[this.prop];return t&&t.get?t.get(this):Te.propHooks._default.get(this)},run:function(t){var s,u=Te.propHooks[this.prop];return this.options.duration?this.pos=s=h.easing[this.easing](t,this.options.duration*t,0,1,this.options.duration):this.pos=s=t,this.now=(this.end-this.start)*s+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),u&&u.set?u.set(this):Te.propHooks._default.set(this),this}},Te.prototype.init.prototype=Te.prototype,Te.propHooks={_default:{get:function(t){var s;return t.elem.nodeType!==1||t.elem[t.prop]!=null&&t.elem.style[t.prop]==null?t.elem[t.prop]:(s=h.css(t.elem,t.prop,""),!s||s==="auto"?0:s)},set:function(t){h.fx.step[t.prop]?h.fx.step[t.prop](t):t.elem.nodeType===1&&(h.cssHooks[t.prop]||t.elem.style[Be(t.prop)]!=null)?h.style(t.elem,t.prop,t.now+t.unit):t.elem[t.prop]=t.now}}},Te.propHooks.scrollTop=Te.propHooks.scrollLeft={set:function(t){t.elem.nodeType&&t.elem.parentNode&&(t.elem[t.prop]=t.now)}},h.easing={linear:function(t){return t},swing:function(t){return .5-Math.cos(t*Math.PI)/2},_default:"swing"},h.fx=Te.prototype.init,h.fx.step={};var be,Ae,Re=/^(?:toggle|show|hide)$/,_e=/queueHooks$/;function Q(){Ae&&(N.hidden===!1&&n.requestAnimationFrame?n.requestAnimationFrame(Q):n.setTimeout(Q,h.fx.interval),h.fx.tick())}function Z(){return n.setTimeout(function(){be=void 0}),be=Date.now()}function le(t,s){var u,l=0,m={height:t};for(s=s?1:0;l<4;l+=2-s)u=y[l],m["margin"+u]=m["padding"+u]=t;return s&&(m.opacity=m.width=t),m}function oe(t,s,u){for(var l,m=(Pe.tweeners[s]||[]).concat(Pe.tweeners["*"]),w=0,T=m.length;w1)},removeAttr:function(t){return this.each(function(){h.removeAttr(this,t)})}}),h.extend({attr:function(t,s,u){var l,m,w=t.nodeType;if(!(w===3||w===8||w===2)){if(typeof t.getAttribute>"u")return h.prop(t,s,u);if((w!==1||!h.isXMLDoc(t))&&(m=h.attrHooks[s.toLowerCase()]||(h.expr.match.bool.test(s)?Xe:void 0)),u!==void 0){if(u===null){h.removeAttr(t,s);return}return m&&"set"in m&&(l=m.set(t,u,s))!==void 0?l:(t.setAttribute(s,u+""),u)}return m&&"get"in m&&(l=m.get(t,s))!==null?l:(l=h.find.attr(t,s),l??void 0)}},attrHooks:{type:{set:function(t,s){if(!S.radioValue&&s==="radio"&&re(t,"input")){var u=t.value;return t.setAttribute("type",s),u&&(t.value=u),s}}}},removeAttr:function(t,s){var u,l=0,m=s&&s.match(Vt);if(m&&t.nodeType===1)for(;u=m[l++];)t.removeAttribute(u)}}),Xe={set:function(t,s,u){return s===!1?h.removeAttr(t,u):t.setAttribute(u,u),u}},h.each(h.expr.match.bool.source.match(/\w+/g),function(t,s){var u=Ye[s]||h.find.attr;Ye[s]=function(l,m,w){var T,B,D=m.toLowerCase();return w||(B=Ye[D],Ye[D]=T,T=u(l,m,w)!=null?D:null,Ye[D]=B),T}});var Qe=/^(?:input|select|textarea|button)$/i,j=/^(?:a|area)$/i;h.fn.extend({prop:function(t,s){return qe(this,h.prop,t,s,arguments.length>1)},removeProp:function(t){return this.each(function(){delete this[h.propFix[t]||t]})}}),h.extend({prop:function(t,s,u){var l,m,w=t.nodeType;if(!(w===3||w===8||w===2))return(w!==1||!h.isXMLDoc(t))&&(s=h.propFix[s]||s,m=h.propHooks[s]),u!==void 0?m&&"set"in m&&(l=m.set(t,u,s))!==void 0?l:t[s]=u:m&&"get"in m&&(l=m.get(t,s))!==null?l:t[s]},propHooks:{tabIndex:{get:function(t){var s=h.find.attr(t,"tabindex");return s?parseInt(s,10):Qe.test(t.nodeName)||j.test(t.nodeName)&&t.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),S.optSelected||(h.propHooks.selected={get:function(t){var s=t.parentNode;return s&&s.parentNode&&s.parentNode.selectedIndex,null},set:function(t){var s=t.parentNode;s&&(s.selectedIndex,s.parentNode&&s.parentNode.selectedIndex)}}),h.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){h.propFix[this.toLowerCase()]=this});function Je(t){var s=t.match(Vt)||[];return s.join(" ")}function nt(t){return t.getAttribute&&t.getAttribute("class")||""}function ot(t){return Array.isArray(t)?t:typeof t=="string"?t.match(Vt)||[]:[]}h.fn.extend({addClass:function(t){var s,u,l,m,w,T;return P(t)?this.each(function(B){h(this).addClass(t.call(this,B,nt(this)))}):(s=ot(t),s.length?this.each(function(){if(l=nt(this),u=this.nodeType===1&&" "+Je(l)+" ",u){for(w=0;w-1;)u=u.replace(" "+m+" "," ");T=Je(u),l!==T&&this.setAttribute("class",T)}}):this):this.attr("class","")},toggleClass:function(t,s){var u,l,m,w,T=typeof t,B=T==="string"||Array.isArray(t);return P(t)?this.each(function(D){h(this).toggleClass(t.call(this,D,nt(this),s),s)}):typeof s=="boolean"&&B?s?this.addClass(t):this.removeClass(t):(u=ot(t),this.each(function(){if(B)for(w=h(this),m=0;m-1)return!0;return!1}});var et=/\r/g;h.fn.extend({val:function(t){var s,u,l,m=this[0];return arguments.length?(l=P(t),this.each(function(w){var T;this.nodeType===1&&(l?T=t.call(this,w,h(this).val()):T=t,T==null?T="":typeof T=="number"?T+="":Array.isArray(T)&&(T=h.map(T,function(B){return B==null?"":B+""})),s=h.valHooks[this.type]||h.valHooks[this.nodeName.toLowerCase()],(!s||!("set"in s)||s.set(this,T,"value")===void 0)&&(this.value=T))})):m?(s=h.valHooks[m.type]||h.valHooks[m.nodeName.toLowerCase()],s&&"get"in s&&(u=s.get(m,"value"))!==void 0?u:(u=m.value,typeof u=="string"?u.replace(et,""):u??"")):void 0}}),h.extend({valHooks:{option:{get:function(t){var s=h.find.attr(t,"value");return s??Je(h.text(t))}},select:{get:function(t){var s,u,l,m=t.options,w=t.selectedIndex,T=t.type==="select-one",B=T?null:[],D=T?w+1:m.length;for(w<0?l=D:l=T?w:0;l-1)&&(u=!0);return u||(t.selectedIndex=-1),w}}}}),h.each(["radio","checkbox"],function(){h.valHooks[this]={set:function(t,s){if(Array.isArray(s))return t.checked=h.inArray(h(t).val(),s)>-1}},S.checkOn||(h.valHooks[this].get=function(t){return t.getAttribute("value")===null?"on":t.value})}),S.focusin="onfocusin"in n;var st=/^(?:focusinfocus|focusoutblur)$/,at=function(t){t.stopPropagation()};h.extend(h.event,{trigger:function(t,s,u,l){var m,w,T,B,D,W,ee,de,V=[u||N],te=x.call(t,"type")?t.type:t,Le=x.call(t,"namespace")?t.namespace.split("."):[];if(w=de=T=u=u||N,!(u.nodeType===3||u.nodeType===8)&&!st.test(te+h.event.triggered)&&(te.indexOf(".")>-1&&(Le=te.split("."),te=Le.shift(),Le.sort()),D=te.indexOf(":")<0&&"on"+te,t=t[h.expando]?t:new h.Event(te,typeof t=="object"&&t),t.isTrigger=l?2:3,t.namespace=Le.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+Le.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=u),s=s==null?[t]:h.makeArray(s,[t]),ee=h.event.special[te]||{},!(!l&&ee.trigger&&ee.trigger.apply(u,s)===!1))){if(!l&&!ee.noBubble&&!I(u)){for(B=ee.delegateType||te,st.test(B+te)||(w=w.parentNode);w;w=w.parentNode)V.push(w),T=w;T===(u.ownerDocument||N)&&V.push(T.defaultView||T.parentWindow||n)}for(m=0;(w=V[m++])&&!t.isPropagationStopped();)de=w,t.type=m>1?B:ee.bindType||te,W=(ke.get(w,"events")||Object.create(null))[t.type]&&ke.get(w,"handle"),W&&W.apply(w,s),W=D&&w[D],W&&W.apply&&sn(w)&&(t.result=W.apply(w,s),t.result===!1&&t.preventDefault());return t.type=te,!l&&!t.isDefaultPrevented()&&(!ee._default||ee._default.apply(V.pop(),s)===!1)&&sn(u)&&D&&P(u[te])&&!I(u)&&(T=u[D],T&&(u[D]=null),h.event.triggered=te,t.isPropagationStopped()&&de.addEventListener(te,at),u[te](),t.isPropagationStopped()&&de.removeEventListener(te,at),h.event.triggered=void 0,T&&(u[D]=T)),t.result}},simulate:function(t,s,u){var l=h.extend(new h.Event,u,{type:t,isSimulated:!0});h.event.trigger(l,null,s)}}),h.fn.extend({trigger:function(t,s){return this.each(function(){h.event.trigger(t,s,this)})},triggerHandler:function(t,s){var u=this[0];if(u)return h.event.trigger(t,s,u,!0)}}),S.focusin||h.each({focus:"focusin",blur:"focusout"},function(t,s){var u=function(l){h.event.simulate(s,l.target,h.event.fix(l))};h.event.special[s]={setup:function(){var l=this.ownerDocument||this.document||this,m=ke.access(l,s);m||l.addEventListener(t,u,!0),ke.access(l,s,(m||0)+1)},teardown:function(){var l=this.ownerDocument||this.document||this,m=ke.access(l,s)-1;m?ke.access(l,s,m):(l.removeEventListener(t,u,!0),ke.remove(l,s))}}});var bt=n.location,dt={guid:Date.now()},Tt=/\?/;h.parseXML=function(t){var s,u;if(!t||typeof t!="string")return null;try{s=new n.DOMParser().parseFromString(t,"text/xml")}catch{}return u=s&&s.getElementsByTagName("parsererror")[0],(!s||u)&&h.error("Invalid XML: "+(u?h.map(u.childNodes,function(l){return l.textContent}).join(` -`):t)),s};var Ct=/\[\]$/,Et=/\r?\n/g,Dt=/^(?:submit|button|image|reset|file)$/i,Kt=/^(?:input|select|textarea|keygen)/i;function Ut(t,s,u,l){var m;if(Array.isArray(s))h.each(s,function(w,T){u||Ct.test(t)?l(t,T):Ut(t+"["+(typeof T=="object"&&T!=null?w:"")+"]",T,u,l)});else if(!u&&he(s)==="object")for(m in s)Ut(t+"["+m+"]",s[m],u,l);else l(t,s)}h.param=function(t,s){var u,l=[],m=function(w,T){var B=P(T)?T():T;l[l.length]=encodeURIComponent(w)+"="+encodeURIComponent(B??"")};if(t==null)return"";if(Array.isArray(t)||t.jquery&&!h.isPlainObject(t))h.each(t,function(){m(this.name,this.value)});else for(u in t)Ut(u,t[u],s,m);return l.join("&")},h.fn.extend({serialize:function(){return h.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var t=h.prop(this,"elements");return t?h.makeArray(t):this}).filter(function(){var t=this.type;return this.name&&!h(this).is(":disabled")&&Kt.test(this.nodeName)&&!Dt.test(t)&&(this.checked||!pt.test(t))}).map(function(t,s){var u=h(this).val();return u==null?null:Array.isArray(u)?h.map(u,function(l){return{name:s.name,value:l.replace(Et,`\r -`)}}):{name:s.name,value:u.replace(Et,`\r -`)}}).get()}});var Gt=/%20/g,Tu=/#.*$/,Eu=/([?&])_=[^&]*/,Cu=/^(.*?):[ \t]*([^\r\n]*)$/mg,Au=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ou=/^(?:GET|HEAD)$/,Ru=/^\/\//,is={},Ii={},os="*/".concat("*"),Mi=N.createElement("a");Mi.href=bt.href;function ss(t){return function(s,u){typeof s!="string"&&(u=s,s="*");var l,m=0,w=s.toLowerCase().match(Vt)||[];if(P(u))for(;l=w[m++];)l[0]==="+"?(l=l.slice(1)||"*",(t[l]=t[l]||[]).unshift(u)):(t[l]=t[l]||[]).push(u)}}function as(t,s,u,l){var m={},w=t===Ii;function T(B){var D;return m[B]=!0,h.each(t[B]||[],function(W,ee){var de=ee(s,u,l);if(typeof de=="string"&&!w&&!m[de])return s.dataTypes.unshift(de),T(de),!1;if(w)return!(D=de)}),D}return T(s.dataTypes[0])||!m["*"]&&T("*")}function Di(t,s){var u,l,m=h.ajaxSettings.flatOptions||{};for(u in s)s[u]!==void 0&&((m[u]?t:l||(l={}))[u]=s[u]);return l&&h.extend(!0,t,l),t}function Pu(t,s,u){for(var l,m,w,T,B=t.contents,D=t.dataTypes;D[0]==="*";)D.shift(),l===void 0&&(l=t.mimeType||s.getResponseHeader("Content-Type"));if(l){for(m in B)if(B[m]&&B[m].test(l)){D.unshift(m);break}}if(D[0]in u)w=D[0];else{for(m in u){if(!D[0]||t.converters[m+" "+D[0]]){w=m;break}T||(T=m)}w=w||T}if(w)return w!==D[0]&&D.unshift(w),u[w]}function ku(t,s,u,l){var m,w,T,B,D,W={},ee=t.dataTypes.slice();if(ee[1])for(T in t.converters)W[T.toLowerCase()]=t.converters[T];for(w=ee.shift();w;)if(t.responseFields[w]&&(u[t.responseFields[w]]=s),!D&&l&&t.dataFilter&&(s=t.dataFilter(s,t.dataType)),D=w,w=ee.shift(),w){if(w==="*")w=D;else if(D!=="*"&&D!==w){if(T=W[D+" "+w]||W["* "+w],!T){for(m in W)if(B=m.split(" "),B[1]===w&&(T=W[D+" "+B[0]]||W["* "+B[0]],T)){T===!0?T=W[m]:W[m]!==!0&&(w=B[0],ee.unshift(B[1]));break}}if(T!==!0)if(T&&t.throws)s=T(s);else try{s=T(s)}catch(de){return{state:"parsererror",error:T?de:"No conversion from "+D+" to "+w}}}}return{state:"success",data:s}}h.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:bt.href,type:"GET",isLocal:Au.test(bt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":os,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":h.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(t,s){return s?Di(Di(t,h.ajaxSettings),s):Di(h.ajaxSettings,t)},ajaxPrefilter:ss(is),ajaxTransport:ss(Ii),ajax:function(t,s){typeof t=="object"&&(s=t,t=void 0),s=s||{};var u,l,m,w,T,B,D,W,ee,de,V=h.ajaxSetup({},s),te=V.context||V,Le=V.context&&(te.nodeType||te.jquery)?h(te):h.event,Ze=h.Deferred(),Ne=h.Callbacks("once memory"),Wt=V.statusCode||{},Ht={},fn={},xt="canceled",Ke={readyState:0,getResponseHeader:function(lt){var Pt;if(D){if(!w)for(w={};Pt=Cu.exec(m);)w[Pt[1].toLowerCase()+" "]=(w[Pt[1].toLowerCase()+" "]||[]).concat(Pt[2]);Pt=w[lt.toLowerCase()+" "]}return Pt==null?null:Pt.join(", ")},getAllResponseHeaders:function(){return D?m:null},setRequestHeader:function(lt,Pt){return D==null&&(lt=fn[lt.toLowerCase()]=fn[lt.toLowerCase()]||lt,Ht[lt]=Pt),this},overrideMimeType:function(lt){return D==null&&(V.mimeType=lt),this},statusCode:function(lt){var Pt;if(lt)if(D)Ke.always(lt[Ke.status]);else for(Pt in lt)Wt[Pt]=[Wt[Pt],lt[Pt]];return this},abort:function(lt){var Pt=lt||xt;return u&&u.abort(Pt),an(0,Pt),this}};if(Ze.promise(Ke),V.url=((t||V.url||bt.href)+"").replace(Ru,bt.protocol+"//"),V.type=s.method||s.type||V.method||V.type,V.dataTypes=(V.dataType||"*").toLowerCase().match(Vt)||[""],V.crossDomain==null){B=N.createElement("a");try{B.href=V.url,B.href=B.href,V.crossDomain=Mi.protocol+"//"+Mi.host!=B.protocol+"//"+B.host}catch{V.crossDomain=!0}}if(V.data&&V.processData&&typeof V.data!="string"&&(V.data=h.param(V.data,V.traditional)),as(is,V,s,Ke),D)return Ke;W=h.event&&V.global,W&&h.active++===0&&h.event.trigger("ajaxStart"),V.type=V.type.toUpperCase(),V.hasContent=!Ou.test(V.type),l=V.url.replace(Tu,""),V.hasContent?V.data&&V.processData&&(V.contentType||"").indexOf("application/x-www-form-urlencoded")===0&&(V.data=V.data.replace(Gt,"+")):(de=V.url.slice(l.length),V.data&&(V.processData||typeof V.data=="string")&&(l+=(Tt.test(l)?"&":"?")+V.data,delete V.data),V.cache===!1&&(l=l.replace(Eu,"$1"),de=(Tt.test(l)?"&":"?")+"_="+dt.guid+++de),V.url=l+de),V.ifModified&&(h.lastModified[l]&&Ke.setRequestHeader("If-Modified-Since",h.lastModified[l]),h.etag[l]&&Ke.setRequestHeader("If-None-Match",h.etag[l])),(V.data&&V.hasContent&&V.contentType!==!1||s.contentType)&&Ke.setRequestHeader("Content-Type",V.contentType),Ke.setRequestHeader("Accept",V.dataTypes[0]&&V.accepts[V.dataTypes[0]]?V.accepts[V.dataTypes[0]]+(V.dataTypes[0]!=="*"?", "+os+"; q=0.01":""):V.accepts["*"]);for(ee in V.headers)Ke.setRequestHeader(ee,V.headers[ee]);if(V.beforeSend&&(V.beforeSend.call(te,Ke,V)===!1||D))return Ke.abort();if(xt="abort",Ne.add(V.complete),Ke.done(V.success),Ke.fail(V.error),u=as(Ii,V,s,Ke),!u)an(-1,"No Transport");else{if(Ke.readyState=1,W&&Le.trigger("ajaxSend",[Ke,V]),D)return Ke;V.async&&V.timeout>0&&(T=n.setTimeout(function(){Ke.abort("timeout")},V.timeout));try{D=!1,u.send(Ht,an)}catch(lt){if(D)throw lt;an(-1,lt)}}function an(lt,Pt,Cr,Kr){var cn,tr,nr,un,$n,mn=Pt;D||(D=!0,T&&n.clearTimeout(T),u=void 0,m=Kr||"",Ke.readyState=lt>0?4:0,cn=lt>=200&<<300||lt===304,Cr&&(un=Pu(V,Ke,Cr)),!cn&&h.inArray("script",V.dataTypes)>-1&&h.inArray("json",V.dataTypes)<0&&(V.converters["text script"]=function(){}),un=ku(V,un,Ke,cn),cn?(V.ifModified&&($n=Ke.getResponseHeader("Last-Modified"),$n&&(h.lastModified[l]=$n),$n=Ke.getResponseHeader("etag"),$n&&(h.etag[l]=$n)),lt===204||V.type==="HEAD"?mn="nocontent":lt===304?mn="notmodified":(mn=un.state,tr=un.data,nr=un.error,cn=!nr)):(nr=mn,(lt||!mn)&&(mn="error",lt<0&&(lt=0))),Ke.status=lt,Ke.statusText=(Pt||mn)+"",cn?Ze.resolveWith(te,[tr,mn,Ke]):Ze.rejectWith(te,[Ke,mn,nr]),Ke.statusCode(Wt),Wt=void 0,W&&Le.trigger(cn?"ajaxSuccess":"ajaxError",[Ke,V,cn?tr:nr]),Ne.fireWith(te,[Ke,mn]),W&&(Le.trigger("ajaxComplete",[Ke,V]),--h.active||h.event.trigger("ajaxStop")))}return Ke},getJSON:function(t,s,u){return h.get(t,s,u,"json")},getScript:function(t,s){return h.get(t,void 0,s,"script")}}),h.each(["get","post"],function(t,s){h[s]=function(u,l,m,w){return P(l)&&(w=w||m,m=l,l=void 0),h.ajax(h.extend({url:u,type:s,dataType:w,data:l,success:m},h.isPlainObject(u)&&u))}}),h.ajaxPrefilter(function(t){var s;for(s in t.headers)s.toLowerCase()==="content-type"&&(t.contentType=t.headers[s]||"")}),h._evalUrl=function(t,s,u){return h.ajax({url:t,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(l){h.globalEval(l,s,u)}})},h.fn.extend({wrapAll:function(t){var s;return this[0]&&(P(t)&&(t=t.call(this[0])),s=h(t,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&s.insertBefore(this[0]),s.map(function(){for(var u=this;u.firstElementChild;)u=u.firstElementChild;return u}).append(this)),this},wrapInner:function(t){return P(t)?this.each(function(s){h(this).wrapInner(t.call(this,s))}):this.each(function(){var s=h(this),u=s.contents();u.length?u.wrapAll(t):s.append(t)})},wrap:function(t){var s=P(t);return this.each(function(u){h(this).wrapAll(s?t.call(this,u):t)})},unwrap:function(t){return this.parent(t).not("body").each(function(){h(this).replaceWith(this.childNodes)}),this}}),h.expr.pseudos.hidden=function(t){return!h.expr.pseudos.visible(t)},h.expr.pseudos.visible=function(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)},h.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch{}};var Nu={0:200,1223:204},Er=h.ajaxSettings.xhr();S.cors=!!Er&&"withCredentials"in Er,S.ajax=Er=!!Er,h.ajaxTransport(function(t){var s,u;if(S.cors||Er&&!t.crossDomain)return{send:function(l,m){var w,T=t.xhr();if(T.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(w in t.xhrFields)T[w]=t.xhrFields[w];t.mimeType&&T.overrideMimeType&&T.overrideMimeType(t.mimeType),!t.crossDomain&&!l["X-Requested-With"]&&(l["X-Requested-With"]="XMLHttpRequest");for(w in l)T.setRequestHeader(w,l[w]);s=function(B){return function(){s&&(s=u=T.onload=T.onerror=T.onabort=T.ontimeout=T.onreadystatechange=null,B==="abort"?T.abort():B==="error"?typeof T.status!="number"?m(0,"error"):m(T.status,T.statusText):m(Nu[T.status]||T.status,T.statusText,(T.responseType||"text")!=="text"||typeof T.responseText!="string"?{binary:T.response}:{text:T.responseText},T.getAllResponseHeaders()))}},T.onload=s(),u=T.onerror=T.ontimeout=s("error"),T.onabort!==void 0?T.onabort=u:T.onreadystatechange=function(){T.readyState===4&&n.setTimeout(function(){s&&u()})},s=s("abort");try{T.send(t.hasContent&&t.data||null)}catch(B){if(s)throw B}},abort:function(){s&&s()}}}),h.ajaxPrefilter(function(t){t.crossDomain&&(t.contents.script=!1)}),h.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(t){return h.globalEval(t),t}}}),h.ajaxPrefilter("script",function(t){t.cache===void 0&&(t.cache=!1),t.crossDomain&&(t.type="GET")}),h.ajaxTransport("script",function(t){if(t.crossDomain||t.scriptAttrs){var s,u;return{send:function(l,m){s=h(" - -
- @include('layouts.navigation') - - - @if (isset($header)) -
-
- {{ $header }} + +
+
+
+
+ Vito
-
+
+ +
+ @include('layouts.partials.server-select', ['server' => isset($server) ? $server : null ]) + + @if(isset($server)) + + + + + {{ __('Overview') }} + + @if($server->isReady()) + @if($server->webserver()) + + + + + {{ __('Sites') }} + + @endif + @if($server->database()) + + + + + {{ __('Databases') }} + + @endif + @if($server->php()) + + + + + {{ __('PHP') }} + + @endif + @if($server->firewall()) + + + + + + {{ __('Firewall') }} + + @endif + + + + + {{ __('Cronjobs') }} + + + + + + {{ __('SSH Keys') }} + + + + + + + {{ __('Services') }} + + @endif + + + + + {{ __('Settings') }} + + + + + + {{ __('Logs') }} + + @endif +
+ + + @if(isset($sidebar)) +
+ {{ $sidebar }} +
@endif - -
- {{ $slot }} -
+
+ + + + @if(isset($header)) +
+
+ {{ $header }} +
+
+ @endif + + @if(isset($header2)) +
+
+ {{ $header2 }} +
+
+ @endif + + +
+ {{ $slot }} +
+
+ @livewireScripts diff --git a/resources/views/layouts/partials/site-select.blade.php b/resources/views/layouts/partials/site-select.blade.php new file mode 100644 index 0000000..f5d1dbc --- /dev/null +++ b/resources/views/layouts/partials/site-select.blade.php @@ -0,0 +1,90 @@ +
+
+
+ +
+
+ +
+
+ +
+
+ No sites found! +
+
+
+
+ + +
+
+
+ + diff --git a/resources/views/layouts/profile.blade.php b/resources/views/layouts/profile.blade.php index ba4660d..7a81291 100644 --- a/resources/views/layouts/profile.blade.php +++ b/resources/views/layouts/profile.blade.php @@ -3,30 +3,47 @@ {{ $pageTitle }} @endif - - diff --git a/resources/views/livewire/notification-channels/channels-list.blade.php b/resources/views/livewire/notification-channels/channels-list.blade.php index 335f894..bdb087d 100644 --- a/resources/views/livewire/notification-channels/channels-list.blade.php +++ b/resources/views/livewire/notification-channels/channels-list.blade.php @@ -22,7 +22,7 @@
- + Delete
diff --git a/resources/views/livewire/php/default-cli.blade.php b/resources/views/livewire/php/default-cli.blade.php index 04f24d1..8bd951b 100644 --- a/resources/views/livewire/php/default-cli.blade.php +++ b/resources/views/livewire/php/default-cli.blade.php @@ -16,7 +16,7 @@ {{ __("Change") }} - + diff --git a/resources/views/livewire/php/installed-versions.blade.php b/resources/views/livewire/php/installed-versions.blade.php index 63b2a01..f5606a1 100644 --- a/resources/views/livewire/php/installed-versions.blade.php +++ b/resources/views/livewire/php/installed-versions.blade.php @@ -3,7 +3,9 @@ {{ __("Installed PHPs") }} {{ __("You can see and manage your PHP installations") }} - @include('livewire.php.partials.install-new-php') +
+ @include('livewire.php.partials.install-new-php') +
@@ -22,10 +24,9 @@ {{ __("Actions") }} - + - {{----}} {{-- {{ __("Install Extension") }}--}} diff --git a/resources/views/livewire/php/partials/install-new-php.blade.php b/resources/views/livewire/php/partials/install-new-php.blade.php index f1f9b50..99a936f 100644 --- a/resources/views/livewire/php/partials/install-new-php.blade.php +++ b/resources/views/livewire/php/partials/install-new-php.blade.php @@ -1,8 +1,8 @@ - {{ __("Install") }} - + {{ __("Install PHP") }} + diff --git a/resources/views/livewire/queues/queues-list.blade.php b/resources/views/livewire/queues/queues-list.blade.php index 344b8c9..3653872 100644 --- a/resources/views/livewire/queues/queues-list.blade.php +++ b/resources/views/livewire/queues/queues-list.blade.php @@ -1,7 +1,7 @@
{{ __("Queues") }} - {{ __("You can manage and create queues for your site") }} + {{ __("You can manage and create queues for your site via supervisor") }} @@ -22,16 +22,16 @@ @include('livewire.queues.partials.status', ['status' => $queue->status])
- + Resume - + Stop - + Restart - + Delete
diff --git a/resources/views/livewire/server-logs/logs-list.blade.php b/resources/views/livewire/server-logs/logs-list.blade.php index 74d3193..40b29e1 100644 --- a/resources/views/livewire/server-logs/logs-list.blade.php +++ b/resources/views/livewire/server-logs/logs-list.blade.php @@ -16,7 +16,7 @@ - + View diff --git a/resources/views/livewire/server-providers/providers-list.blade.php b/resources/views/livewire/server-providers/providers-list.blade.php index 13cbcf3..9e49b22 100644 --- a/resources/views/livewire/server-providers/providers-list.blade.php +++ b/resources/views/livewire/server-providers/providers-list.blade.php @@ -22,7 +22,7 @@
- + Delete
diff --git a/resources/views/livewire/server-settings/server-details.blade.php b/resources/views/livewire/server-settings/server-details.blade.php index a00d1ae..0b145dd 100644 --- a/resources/views/livewire/server-settings/server-details.blade.php +++ b/resources/views/livewire/server-settings/server-details.blade.php @@ -36,7 +36,7 @@
{{ __("Status") }}
- @include('livewire.servers.partials.status', ['status' => $server->status]) +
diff --git a/resources/views/livewire/server-ssh-keys/server-keys-list.blade.php b/resources/views/livewire/server-ssh-keys/server-keys-list.blade.php index 9809f01..aad63d3 100644 --- a/resources/views/livewire/server-ssh-keys/server-keys-list.blade.php +++ b/resources/views/livewire/server-ssh-keys/server-keys-list.blade.php @@ -27,7 +27,7 @@ @include('livewire.server-ssh-keys.partials.status', ['status' => $key->pivot->status])
- + Delete
diff --git a/resources/views/livewire/servers/partials/installation-failed.blade.php b/resources/views/livewire/servers/partials/installation-failed.blade.php index 49eff38..6eaea65 100644 --- a/resources/views/livewire/servers/partials/installation-failed.blade.php +++ b/resources/views/livewire/servers/partials/installation-failed.blade.php @@ -8,7 +8,6 @@ {{ $server->progress_step }} ({{ $server->progress }}%)
- {{ __("View Logs") }}
diff --git a/resources/views/livewire/servers/partials/public-key.blade.php b/resources/views/livewire/servers/partials/public-key.blade.php index 795e685..212ad8b 100644 --- a/resources/views/livewire/servers/partials/public-key.blade.php +++ b/resources/views/livewire/servers/partials/public-key.blade.php @@ -1,3 +1,6 @@ +@php + $key = str(file_get_contents(storage_path(config('core.ssh_public_key_name'))))->replace("\n", ""); +@endphp
@@ -12,13 +15,12 @@ {{ __("Run this command on your server as root user") }} - +
{{ __("Copied") }} -
{{ __("Copy") }}
- {{ config('core.ssh_public_key') }} + mkdir -p /root/.ssh && touch /root/.ssh/authorized_keys && echo '{{ $key }}' >> /root/.ssh/authorized_keys
diff --git a/resources/views/livewire/servers/partials/server-overview.blade.php b/resources/views/livewire/servers/partials/server-overview.blade.php index 398d65c..fbeed2f 100644 --- a/resources/views/livewire/servers/partials/server-overview.blade.php +++ b/resources/views/livewire/servers/partials/server-overview.blade.php @@ -4,15 +4,14 @@ {{ __("Server Overview") }} {{ __("You can see an overview about your server here") }} - - @include('livewire.servers.partials.status', ['status' => $server->status]) -
@if($server->webserver()) -
+
- + + +
{{ $server->sites()->count() }}
@@ -21,7 +20,9 @@ @if($server->database())
- + + +
{{ $server->databases()->count() }}
@@ -29,7 +30,9 @@ @endif
- + + +
{{ $server->cronJobs()->count() }}
diff --git a/resources/views/livewire/servers/server-status.blade.php b/resources/views/livewire/servers/server-status.blade.php new file mode 100644 index 0000000..2cb1a28 --- /dev/null +++ b/resources/views/livewire/servers/server-status.blade.php @@ -0,0 +1,14 @@ +
+ @if($server->status == \App\Enums\ServerStatus::READY) + {{ $server->status }} + @endif + @if($server->status == \App\Enums\ServerStatus::INSTALLING) + {{ $server->status }} + @endif + @if($server->status == \App\Enums\ServerStatus::DISCONNECTED) + {{ $server->status }} + @endif + @if($server->status == \App\Enums\ServerStatus::INSTALLATION_FAILED) + {{ $server->status }} + @endif +
diff --git a/resources/views/livewire/servers/servers-list.blade.php b/resources/views/livewire/servers/servers-list.blade.php index 1453bac..9a030a9 100644 --- a/resources/views/livewire/servers/servers-list.blade.php +++ b/resources/views/livewire/servers/servers-list.blade.php @@ -25,7 +25,7 @@
- @include('livewire.servers.partials.status', ['status' => $server->status]) +
diff --git a/resources/views/livewire/services/services-list.blade.php b/resources/views/livewire/services/services-list.blade.php index 239c2bd..1fb79ea 100644 --- a/resources/views/livewire/services/services-list.blade.php +++ b/resources/views/livewire/services/services-list.blade.php @@ -21,7 +21,6 @@ {{ __("Actions") }} - diff --git a/resources/views/livewire/sites/create-site.blade.php b/resources/views/livewire/sites/create-site.blade.php index 237ac4f..df08221 100644 --- a/resources/views/livewire/sites/create-site.blade.php +++ b/resources/views/livewire/sites/create-site.blade.php @@ -5,16 +5,14 @@
{{ __("Select site type") }} -
+ + @foreach(config('core.site_types') as $t) - -
- Server - -
-
+ @endforeach -
+ @error('type') @enderror @@ -61,14 +59,17 @@
- - - @foreach($sourceControls as $sourceControl) - - @endforeach - +
+ + + @foreach($sourceControls as $sourceControl) + + @endforeach + + {{ __('Connect') }} +
@error('source_control') @enderror diff --git a/resources/views/livewire/sites/partials/site-overview.blade.php b/resources/views/livewire/sites/partials/site-overview.blade.php index e21c6e1..b8e758a 100644 --- a/resources/views/livewire/sites/partials/site-overview.blade.php +++ b/resources/views/livewire/sites/partials/site-overview.blade.php @@ -3,17 +3,10 @@ {{ __("Site Overview") }} - - {{ $site->domain }} - - - @include('livewire.sites.partials.status', ['status' => $site->status]) -
-
@@ -22,14 +15,12 @@
-
{{ $site->queues()->count() }}
-
{{ $site->php_version }}
diff --git a/resources/views/livewire/sites/show-site.blade.php b/resources/views/livewire/sites/show-site.blade.php index 7822af0..e399e73 100644 --- a/resources/views/livewire/sites/show-site.blade.php +++ b/resources/views/livewire/sites/show-site.blade.php @@ -1,13 +1,25 @@
@if($site->status === \App\Enums\SiteStatus::INSTALLING) @include('livewire.sites.partials.installing', ['site' => $site]) + + @endif @if($site->status === \App\Enums\SiteStatus::INSTALLATION_FAILED) @include('livewire.sites.partials.installation-failed', ['site' => $site]) + + @endif @if($site->status === \App\Enums\SiteStatus::READY) -
- @include('livewire.sites.partials.site-overview', ['site' => $site]) -
+ @if($site->type == \App\Enums\SiteType::LARAVEL) + + @endif + + @if($site->type == \App\Enums\SiteType::PHP) + + @endif + + @if($site->type == \App\Enums\SiteType::WORDPRESS) + + @endif @endif
diff --git a/resources/views/livewire/sites/site-status.blade.php b/resources/views/livewire/sites/site-status.blade.php new file mode 100644 index 0000000..da2328c --- /dev/null +++ b/resources/views/livewire/sites/site-status.blade.php @@ -0,0 +1,14 @@ +
+ @if($site->status == \App\Enums\SiteStatus::READY) + {{ $site->status }} + @endif + @if($site->status == \App\Enums\SiteStatus::INSTALLING) + {{ $site->status }} + @endif + @if($site->status == \App\Enums\SiteStatus::DELETING) + {{ $site->status }} + @endif + @if($site->status == \App\Enums\SiteStatus::INSTALLATION_FAILED) + {{ $site->status }} + @endif +
diff --git a/resources/views/livewire/source-controls/connect.blade.php b/resources/views/livewire/source-controls/connect.blade.php new file mode 100644 index 0000000..6725d88 --- /dev/null +++ b/resources/views/livewire/source-controls/connect.blade.php @@ -0,0 +1,54 @@ +
+ + {{ __('Connect') }} + + + + +

+ {{ __('Connect to a Source Control') }} +

+ +
+ + + + @foreach(config('core.source_control_providers') as $p) + @if($p !== 'custom') + + @endif + @endforeach + + @error('provider') + + @enderror +
+ +
+ + + @error('name') + + @enderror +
+ +
+ + + @error('token') + + @enderror +
+ +
+ + {{ __('Cancel') }} + + + + {{ __('Connect') }} + +
+ +
+
diff --git a/resources/views/livewire/source-controls/source-controls-list.blade.php b/resources/views/livewire/source-controls/source-controls-list.blade.php new file mode 100644 index 0000000..83324ec --- /dev/null +++ b/resources/views/livewire/source-controls/source-controls-list.blade.php @@ -0,0 +1,45 @@ +
+ + Source Controls + You can connect your source controls via API Tokens + + + + +
+ @if(count($sourceControls) > 0) + @foreach($sourceControls as $sourceControl) + +
+ +
+
+ {{ $sourceControl->profile }} + + + +
+
+
+ + Delete + +
+
+
+ @endforeach + + @else + +
+ {{ __("You haven't connected to any server source controls yet!") }} +
+
+ @endif +
+
diff --git a/resources/views/livewire/ssh-keys/keys-list.blade.php b/resources/views/livewire/ssh-keys/keys-list.blade.php index 94e715f..19ac48a 100644 --- a/resources/views/livewire/ssh-keys/keys-list.blade.php +++ b/resources/views/livewire/ssh-keys/keys-list.blade.php @@ -19,7 +19,7 @@
- + Delete
diff --git a/resources/views/livewire/ssl/ssls-list.blade.php b/resources/views/livewire/ssl/ssls-list.blade.php index 3c1e816..c98e389 100644 --- a/resources/views/livewire/ssl/ssls-list.blade.php +++ b/resources/views/livewire/ssl/ssls-list.blade.php @@ -29,7 +29,7 @@ @include('livewire.ssl.partials.status', ['status' => $ssl->status])
- + Delete
diff --git a/resources/views/livewire/user-dropdown.blade.php b/resources/views/livewire/user-dropdown.blade.php index e63f24c..2395ae7 100644 --- a/resources/views/livewire/user-dropdown.blade.php +++ b/resources/views/livewire/user-dropdown.blade.php @@ -13,7 +13,6 @@ - {{ __('Profile') }} diff --git a/resources/views/server-settings/index.blade.php b/resources/views/server-settings/index.blade.php index 741a7c1..b61858d 100644 --- a/resources/views/server-settings/index.blade.php +++ b/resources/views/server-settings/index.blade.php @@ -15,7 +15,6 @@
{{ __("Copied") }} -
{{ __("Copy") }}
diff --git a/resources/views/sites/show.blade.php b/resources/views/sites/show.blade.php index 5e7354e..cffc1fe 100644 --- a/resources/views/sites/show.blade.php +++ b/resources/views/sites/show.blade.php @@ -2,6 +2,4 @@ {{ $site->domain }} - - diff --git a/resources/views/source-controls/index.blade.php b/resources/views/source-controls/index.blade.php index 7b9aa94..c07970f 100644 --- a/resources/views/source-controls/index.blade.php +++ b/resources/views/source-controls/index.blade.php @@ -1,20 +1,5 @@ {{ __("Source Controls") }} -
- - Source Controls - You can connect your source controls via API Tokens - - -
- @if(session('status') == 'not-connected') -
{{ session('message') }}
- @endif - - - -
-
- +
diff --git a/routes/web.php b/routes/web.php index 5ef0a17..43ea29b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -30,23 +30,24 @@ Route::get('/{server}', [ServerController::class, 'show'])->name('servers.show'); Route::get('/{server}/logs', [ServerController::class, 'logs'])->name('servers.logs'); Route::get('/{server}/settings', [ServerSettingController::class, 'index'])->name('servers.settings'); - Route::get('/{server}/databases', [DatabaseController::class, 'index'])->name('servers.databases'); - Route::prefix('/{server}/sites')->group(function () { - Route::get('/', [SiteController::class, 'index'])->name('servers.sites'); - Route::get('/create', [SiteController::class, 'create'])->name('servers.sites.create'); - Route::get('/{site}', [SiteController::class, 'show'])->name('servers.sites.show'); - Route::get('/{site}/application', [SiteController::class, 'application'])->name('servers.sites.application'); - Route::get('/{site}/ssl', [SiteController::class, 'ssl'])->name('servers.sites.ssl'); - Route::get('/{site}/queues', [SiteController::class, 'queues'])->name('servers.sites.queues'); - Route::get('/{site}/settings', [SiteController::class, 'settings'])->name('servers.sites.settings'); - Route::get('/{site}/logs', [SiteController::class, 'logs'])->name('servers.sites.logs'); + Route::middleware('server-is-ready')->group(function () { + Route::get('/{server}/databases', [DatabaseController::class, 'index'])->name('servers.databases'); + Route::prefix('/{server}/sites')->group(function () { + Route::get('/', [SiteController::class, 'index'])->name('servers.sites'); + Route::get('/create', [SiteController::class, 'create'])->name('servers.sites.create'); + Route::get('/{site}', [SiteController::class, 'show'])->name('servers.sites.show'); + Route::get('/{site}/ssl', [SiteController::class, 'ssl'])->name('servers.sites.ssl'); + Route::get('/{site}/queues', [SiteController::class, 'queues'])->name('servers.sites.queues'); + Route::get('/{site}/settings', [SiteController::class, 'settings'])->name('servers.sites.settings'); + Route::get('/{site}/logs', [SiteController::class, 'logs'])->name('servers.sites.logs'); + }); + Route::get('/{server}/php', [PHPController::class, 'index'])->name('servers.php'); + Route::get('/{server}/firewall', [FirewallController::class, 'index'])->name('servers.firewall'); + Route::get('/{server}/cronjobs', [CronjobController::class, 'index'])->name('servers.cronjobs'); + Route::get('/{server}/daemons', [DaemonController::class, 'index'])->name('servers.daemons'); + Route::get('/{server}/services', [ServiceController::class, 'index'])->name('servers.services'); + Route::get('/{server}/ssh-keys', [SSHKeyController::class, 'index'])->name('servers.ssh-keys'); }); - Route::get('/{server}/php', [PHPController::class, 'index'])->name('servers.php'); - Route::get('/{server}/firewall', [FirewallController::class, 'index'])->name('servers.firewall'); - Route::get('/{server}/cronjobs', [CronjobController::class, 'index'])->name('servers.cronjobs'); - Route::get('/{server}/daemons', [DaemonController::class, 'index'])->name('servers.daemons'); - Route::get('/{server}/services', [ServiceController::class, 'index'])->name('servers.services'); - Route::get('/{server}/ssh-keys', [SSHKeyController::class, 'index'])->name('servers.ssh-keys'); }); }); diff --git a/system/commands/ubuntu/basics.sh b/system/commands/ubuntu/basics.sh deleted file mode 100755 index 5ee2f96..0000000 --- a/system/commands/ubuntu/basics.sh +++ /dev/null @@ -1 +0,0 @@ -sudo sed -i "s/#precedence ::ffff:0:0\/96 100/precedence ::ffff:0:0\/96 100/" /etc/gai.conf diff --git a/system/commands/ubuntu/update-php-ini.sh b/system/commands/ubuntu/update-php-ini.sh deleted file mode 100644 index 0d8fde6..0000000 --- a/system/commands/ubuntu/update-php-ini.sh +++ /dev/null @@ -1,7 +0,0 @@ -if ! sudo echo '__ini__' > /etc/php/__version__/cli/php.ini; then - echo 'VITO_SSH_ERROR' && exit 1 -fi - -if ! sudo service php__version__-fpm restart; then - echo 'VITO_SSH_ERROR' && exit 1 -fi diff --git a/tailwind.config.js b/tailwind.config.js index 67db259..f525657 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -23,5 +23,8 @@ module.exports = { }, }, - plugins: [require('@tailwindcss/forms')], + plugins: [ + require('@tailwindcss/forms'), + require('@tailwindcss/typography'), + ], }; diff --git a/deploy.sh b/update.sh similarity index 100% rename from deploy.sh rename to update.sh