diff --git a/README.md b/README.md index 970203cd..e90cb564 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,4 @@ ## Credits - Prettier - Postcss - Flowbite +- svgrepo.com diff --git a/app/Actions/Service/Create.php b/app/Actions/Service/Create.php new file mode 100644 index 00000000..bfb75cc8 --- /dev/null +++ b/app/Actions/Service/Create.php @@ -0,0 +1,55 @@ +<?php + +namespace App\Actions\Service; + +use App\Enums\ServiceStatus; +use App\Models\Server; +use App\Models\Service; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; + +class Create +{ + public function create(Server $server, array $input): Service + { + $this->validate($server, $input); + + $service = new Service([ + 'name' => $input['type'], + 'type' => $input['type'], + 'version' => $input['version'], + 'status' => ServiceStatus::INSTALLING, + ]); + + Validator::make($input, $service->handler()->creationRules($input))->validate(); + + $service->type_data = $service->handler()->creationData($input); + + $service->save(); + + $service->handler()->create(); + + dispatch(function () use ($service) { + $service->handler()->install(); + $service->status = ServiceStatus::READY; + $service->save(); + })->catch(function () use ($service) { + $service->handler()->delete(); + $service->delete(); + })->onConnection('ssh'); + + return $service; + } + + private function validate(Server $server, array $input): void + { + Validator::make($input, [ + 'type' => [ + 'required', + Rule::in(config('core.add_on_services')), + Rule::unique('services', 'type')->where('server_id', $server->id), + ], + 'version' => 'required', + ])->validate(); + } +} diff --git a/app/Enums/SiteType.php b/app/Enums/SiteType.php index 128d3f6a..21d4a367 100644 --- a/app/Enums/SiteType.php +++ b/app/Enums/SiteType.php @@ -11,4 +11,6 @@ final class SiteType const LARAVEL = 'laravel'; const WORDPRESS = 'wordpress'; + + const PHPMYADMIN = 'phpmyadmin'; } diff --git a/app/Http/Controllers/ServiceController.php b/app/Http/Controllers/ServiceController.php index 6a5e7ce1..6969126f 100644 --- a/app/Http/Controllers/ServiceController.php +++ b/app/Http/Controllers/ServiceController.php @@ -2,11 +2,14 @@ namespace App\Http\Controllers; +use App\Actions\Service\Create; use App\Facades\Toast; +use App\Helpers\HtmxResponse; use App\Models\Server; use App\Models\Service; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; class ServiceController extends Controller { @@ -62,4 +65,13 @@ public function disable(Server $server, Service $service): RedirectResponse return back(); } + + public function install(Server $server, Request $request): HtmxResponse + { + app(Create::class)->create($server, $request->input()); + + Toast::success('Service is being uninstalled!'); + + return htmx()->back(); + } } diff --git a/app/Models/Service.php b/app/Models/Service.php index 6c30d880..4516ae24 100755 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -4,6 +4,7 @@ use App\Actions\Service\Manage; use App\Exceptions\ServiceInstallationFailed; +use App\SSH\Services\AddOnServices\AbstractAddOnService; use App\SSH\Services\Database\Database as DatabaseHandler; use App\SSH\Services\Firewall\Firewall as FirewallHandler; use App\SSH\Services\PHP\PHP as PHPHandler; @@ -53,7 +54,9 @@ public static function boot(): void parent::boot(); static::creating(function (Service $service) { - $service->unit = config('core.service_units')[$service->name][$service->server->os][$service->version]; + if (array_key_exists($service->name, config('core.service_units'))) { + $service->unit = config('core.service_units')[$service->name][$service->server->os][$service->version]; + } }); } @@ -63,7 +66,7 @@ public function server(): BelongsTo } public function handler( - ): PHPHandler|WebserverHandler|DatabaseHandler|FirewallHandler|ProcessManagerHandler|RedisHandler { + ): PHPHandler|WebserverHandler|DatabaseHandler|FirewallHandler|ProcessManagerHandler|RedisHandler|AbstractAddOnService { $handler = config('core.service_handlers')[$this->name]; return new $handler($this); @@ -81,26 +84,26 @@ public function validateInstall($result): void public function start(): void { - app(Manage::class)->start($this); + $this->unit && app(Manage::class)->start($this); } public function stop(): void { - app(Manage::class)->stop($this); + $this->unit && app(Manage::class)->stop($this); } public function restart(): void { - app(Manage::class)->restart($this); + $this->unit && app(Manage::class)->restart($this); } public function enable(): void { - app(Manage::class)->enable($this); + $this->unit && app(Manage::class)->enable($this); } public function disable(): void { - app(Manage::class)->disable($this); + $this->unit && app(Manage::class)->disable($this); } } diff --git a/app/SSH/OS/OS.php b/app/SSH/OS/OS.php index fe0befb9..87dc0ac1 100644 --- a/app/SSH/OS/OS.php +++ b/app/SSH/OS/OS.php @@ -131,4 +131,21 @@ public function runScript(string $path, string $script, ?int $siteId = null): Se return $ssh->log; } + + public function download(string $url, string $path): string + { + return $this->server->ssh()->exec( + $this->getScript('download.sh', [ + 'url' => $url, + 'path' => $path, + ]) + ); + } + + public function unzip(string $path): string + { + return $this->server->ssh()->exec( + 'unzip '.$path + ); + } } diff --git a/app/SSH/OS/scripts/download.sh b/app/SSH/OS/scripts/download.sh new file mode 100644 index 00000000..c5a7c7bc --- /dev/null +++ b/app/SSH/OS/scripts/download.sh @@ -0,0 +1,3 @@ +if ! wget __url__ -O __path__; then + echo 'VITO_SSH_ERROR' && exit 1 +fi diff --git a/app/SSH/PHPMyAdmin/PHPMyAdmin.php b/app/SSH/PHPMyAdmin/PHPMyAdmin.php new file mode 100644 index 00000000..a8160fd1 --- /dev/null +++ b/app/SSH/PHPMyAdmin/PHPMyAdmin.php @@ -0,0 +1,23 @@ +<?php + +namespace App\SSH\PHPMyAdmin; + +use App\Models\Site; +use App\SSH\HasScripts; + +class PHPMyAdmin +{ + use HasScripts; + + public function install(Site $site): void + { + $site->server->ssh()->exec( + $this->getScript('install.sh', [ + 'version' => $site->type_data['version'], + 'path' => $site->path, + ]), + 'install-phpmyadmin', + $site->id + ); + } +} diff --git a/app/SSH/PHPMyAdmin/scripts/install.sh b/app/SSH/PHPMyAdmin/scripts/install.sh new file mode 100755 index 00000000..34b7cf01 --- /dev/null +++ b/app/SSH/PHPMyAdmin/scripts/install.sh @@ -0,0 +1,25 @@ +sudo rm -rf phpmyadmin + +sudo rm -rf __path__ + +if ! wget https://files.phpmyadmin.net/phpMyAdmin/__version__/phpMyAdmin-__version__-all-languages.zip; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! unzip phpMyAdmin-__version__-all-languages.zip; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! rm -rf phpMyAdmin-__version__-all-languages.zip; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! mv phpMyAdmin-__version__-all-languages __path__; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +if ! mv __path__/config.sample.inc.php __path__/config.inc.php; then + echo 'VITO_SSH_ERROR' && exit 1 +fi + +echo "PHPMyAdmin installed!" diff --git a/app/SSH/Services/AddOnServices/AbstractAddOnService.php b/app/SSH/Services/AddOnServices/AbstractAddOnService.php new file mode 100644 index 00000000..442ae168 --- /dev/null +++ b/app/SSH/Services/AddOnServices/AbstractAddOnService.php @@ -0,0 +1,18 @@ +<?php + +namespace App\SSH\Services\AddOnServices; + +use App\SSH\Services\ServiceInterface; + +abstract class AbstractAddOnService implements ServiceInterface +{ + abstract public function creationRules(array $input): array; + + abstract public function creationData(array $input): array; + + abstract public function create(): void; + + abstract public function delete(): void; + + abstract public function data(): array; +} diff --git a/app/SiteTypes/PHPMyAdmin.php b/app/SiteTypes/PHPMyAdmin.php new file mode 100755 index 00000000..579efdde --- /dev/null +++ b/app/SiteTypes/PHPMyAdmin.php @@ -0,0 +1,49 @@ +<?php + +namespace App\SiteTypes; + +use Illuminate\Validation\Rule; + +class PHPMyAdmin extends PHPSite +{ + public function supportedFeatures(): array + { + return [ + // + ]; + } + + public function createRules(array $input): array + { + return [ + 'php_version' => [ + 'required', + Rule::in($this->site->server->installedPHPVersions()), + ], + 'version' => 'required|string', + ]; + } + + public function createFields(array $input): array + { + return [ + 'web_directory' => '', + 'php_version' => $input['php_version'] ?? '', + ]; + } + + public function data(array $input): array + { + return [ + 'version' => $input['version'], + ]; + } + + public function install(): void + { + $this->site->server->webserver()->handler()->createVHost($this->site); + $this->progress(30); + app(\App\SSH\PHPMyAdmin\PHPMyAdmin::class)->install($this->site); + $this->progress(65); + } +} diff --git a/config/core.php b/config/core.php index 4068070e..02843c8f 100755 --- a/config/core.php +++ b/config/core.php @@ -13,6 +13,7 @@ use App\ServerProviders\Vultr; use App\SiteTypes\Laravel; use App\SiteTypes\PHPBlank; +use App\SiteTypes\PHPMyAdmin; use App\SiteTypes\PHPSite; use App\SiteTypes\Wordpress; use App\SourceControlProviders\Bitbucket; @@ -177,6 +178,9 @@ 'ufw' => Ufw::class, 'supervisor' => Supervisor::class, ], + 'add_on_services' => [ + // add-on services + ], 'service_units' => [ 'nginx' => [ 'ubuntu_18' => [ @@ -320,12 +324,14 @@ \App\Enums\SiteType::PHP_BLANK, \App\Enums\SiteType::LARAVEL, \App\Enums\SiteType::WORDPRESS, + \App\Enums\SiteType::PHPMYADMIN, ], 'site_types_class' => [ \App\Enums\SiteType::PHP => PHPSite::class, \App\Enums\SiteType::PHP_BLANK => PHPBlank::class, \App\Enums\SiteType::LARAVEL => Laravel::class, \App\Enums\SiteType::WORDPRESS => Wordpress::class, + \App\Enums\SiteType::PHPMYADMIN => PHPMyAdmin::class, ], /* diff --git a/public/static/images/supervisor.svg b/public/static/images/supervisor.svg index 40866cd0..3203fe52 100644 --- a/public/static/images/supervisor.svg +++ b/public/static/images/supervisor.svg @@ -1 +1,9 @@ -<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="list" class="svg-inline--fa fa-list fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M80 368H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16zm0-320H16A16 16 0 0 0 0 64v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16zm0 160H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16zm416 176H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z"></path></svg> \ No newline at end of file +<?xml version="1.0" encoding="utf-8"?> +<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> +<svg width="800px" height="800px" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> + <rect width="48" height="48" fill="white" fill-opacity="0.01" /> + <rect x="4" y="8" width="40" height="32" rx="2" fill="#2F88FF" stroke="#000000" stroke-width="4" + stroke-linejoin="round" /> + <path d="M12 18L19 24L12 30" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" /> + <path d="M23 32H36" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" /> +</svg> \ No newline at end of file diff --git a/public/static/images/ufw.svg b/public/static/images/ufw.svg index 0cf1d561..2843aa38 100644 --- a/public/static/images/ufw.svg +++ b/public/static/images/ufw.svg @@ -1 +1,9 @@ -<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="fire" class="svg-inline--fa fa-fire fa-w-12" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M216 23.86c0-23.8-30.65-32.77-44.15-13.04C48 191.85 224 200 224 288c0 35.63-29.11 64.46-64.85 63.99-35.17-.45-63.15-29.77-63.15-64.94v-85.51c0-21.7-26.47-32.23-41.43-16.5C27.8 213.16 0 261.33 0 320c0 105.87 86.13 192 192 192s192-86.13 192-192c0-170.29-168-193-168-296.14z"></path></svg> \ No newline at end of file +<?xml version="1.0" encoding="utf-8"?> +<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> +<svg width="800px" height="800px" viewBox="0 0 14 14" role="img" focusable="false" aria-hidden="true" + xmlns="http://www.w3.org/2000/svg"> + <path fill="olive" + d="M12.123424 3.09708C9.1750899 1.36849 7.0055019 1 7.0055019 1s-.0023.00035-.0055.00096c-.0033-.00061-.0055-.00096-.0055-.00096s-2.169706.36849-5.117921 2.09708c0 0-.390942 8.72672 5.117921 9.90292.0019-.00035.0036-.00096.0055-.001.0019.00035.0036.00096.0055.001 5.5087451-1.1762 5.1179221-9.90292 5.1179221-9.90292z" /> + <path fill="#ff0" + d="M7.0058749 1.74098c.482647.10437 2.177431.53826 4.3878041 1.77816-.0096 1.61164-.285353 7.71006-4.3878041 8.73494V1.74098z" /> +</svg> \ No newline at end of file diff --git a/resources/views/application/phpmyadmin-app.blade.php b/resources/views/application/phpmyadmin-app.blade.php new file mode 100644 index 00000000..48133ac4 --- /dev/null +++ b/resources/views/application/phpmyadmin-app.blade.php @@ -0,0 +1,6 @@ +<div> + <x-simple-card class="flex items-center justify-between"> + <span>PHPMyAdmin is installed and ready to use!</span> + <x-secondary-button :href="$site->getUrl()" target="_blank">Open</x-secondary-button> + </x-simple-card> +</div> diff --git a/resources/views/application/wordpress-app.blade.php b/resources/views/application/wordpress-app.blade.php index 7cb80a65..eb08473b 100644 --- a/resources/views/application/wordpress-app.blade.php +++ b/resources/views/application/wordpress-app.blade.php @@ -3,7 +3,7 @@ <span> {{ __("Your Wordpress site is installed and ready to use! ") }} </span> - <x-secondary-button :href="$site->url" target="_blank"> + <x-secondary-button :href="$site->getUrl()" target="_blank"> {{ __("Open Website") }} </x-secondary-button> </x-simple-card> diff --git a/resources/views/components/heroicons/o-no-symbol.blade.php b/resources/views/components/heroicons/o-no-symbol.blade.php new file mode 100644 index 00000000..bc8cf23c --- /dev/null +++ b/resources/views/components/heroicons/o-no-symbol.blade.php @@ -0,0 +1,14 @@ +<svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + {{ $attributes }} +> + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" + /> +</svg> diff --git a/resources/views/components/icon-button.blade.php b/resources/views/components/icon-button.blade.php index 433ba864..7fe0ca30 100644 --- a/resources/views/components/icon-button.blade.php +++ b/resources/views/components/icon-button.blade.php @@ -1,5 +1,6 @@ @props([ "href", + "disabled" => false, ]) @php @@ -7,12 +8,12 @@ "inline-flex w-max items-center justify-center px-2 py-1 font-semibold capitalize outline-0 transition hover:opacity-50 focus:ring focus:ring-primary-200 disabled:opacity-25 dark:focus:ring-primary-700 dark:focus:ring-opacity-40"; @endphp -@if (isset($href)) +@if (isset($href) && ! $disabled) <a href="{{ $href }}" {{ $attributes->merge(["class" => $class]) }}> {{ $slot }} </a> @else - <button {{ $attributes->merge(["type" => "submit", "class" => $class]) }}> + <button {{ $attributes->merge(["type" => "submit", "class" => $class, "disabled" => $disabled]) }}> {{ $slot }} </button> @endif diff --git a/resources/views/components/item-card.blade.php b/resources/views/components/item-card.blade.php index bf1fcf1f..c6b57b5e 100644 --- a/resources/views/components/item-card.blade.php +++ b/resources/views/components/item-card.blade.php @@ -1,5 +1,5 @@ <div - class="flex h-20 items-center justify-between rounded-b-md rounded-t-md border border-gray-200 bg-white p-7 text-center dark:border-gray-700 dark:bg-gray-800" + {{ $attributes->merge(["class" => "flex h-20 items-center justify-between rounded-b-md rounded-t-md border border-gray-200 bg-white p-7 text-center dark:border-gray-700 dark:bg-gray-800"]) }} > {{ $slot }} </div> diff --git a/resources/views/components/live.blade.php b/resources/views/components/live.blade.php index 0f4998b2..a7ddb1cc 100644 --- a/resources/views/components/live.blade.php +++ b/resources/views/components/live.blade.php @@ -5,16 +5,13 @@ ]) <div - id="{{ $id }}" - hx-get="{{ request()->getUri() }}" - hx-trigger="every {{ $interval }}" + {{ $attributes->merge(["interval" => $interval, "id" => $id, "hx-get" => request()->getUri(), "hx-trigger" => "every " . $interval, "hx-swap" => "outerHTML"]) }} @if ($target) hx-target="{{ $target }}" hx-select="{{ $target }}" @else hx-select="#{{ $id }}" @endif - hx-swap="outerHTML" > {{ $slot }} </div> diff --git a/resources/views/components/secondary-button.blade.php b/resources/views/components/secondary-button.blade.php index 95f8fca3..77ffb869 100644 --- a/resources/views/components/secondary-button.blade.php +++ b/resources/views/components/secondary-button.blade.php @@ -7,7 +7,7 @@ @php $class = - "inline-flex h-9 min-w-max items-center rounded-md border border-gray-300 bg-white px-4 py-1 font-semibold text-gray-700 shadow-sm transition duration-150 ease-in-out hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:opacity-25 dark:border-gray-500 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 dark:focus:ring-offset-gray-800"; + "inline-flex h-9 w-max min-w-max items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-1 font-semibold text-gray-700 shadow-sm transition duration-150 ease-in-out hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:opacity-25 dark:border-gray-500 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 dark:focus:ring-offset-gray-800"; @endphp @if (isset($href)) diff --git a/resources/views/services/index.blade.php b/resources/views/services/index.blade.php index 1daf8572..cadcb47f 100644 --- a/resources/views/services/index.blade.php +++ b/resources/views/services/index.blade.php @@ -2,4 +2,6 @@ <x-slot name="pageTitle">{{ __("Services") }}</x-slot> @include("services.partials.services-list") + + {{-- @include("services.partials.add-ons") --}} </x-server-layout> diff --git a/resources/views/services/partials/actions/nginx.blade.php b/resources/views/services/partials/actions/nginx.blade.php new file mode 100644 index 00000000..36d30486 --- /dev/null +++ b/resources/views/services/partials/actions/nginx.blade.php @@ -0,0 +1 @@ +@include("services.partials.unit-actions") diff --git a/resources/views/services/partials/actions/php.blade.php b/resources/views/services/partials/actions/php.blade.php new file mode 100644 index 00000000..36d30486 --- /dev/null +++ b/resources/views/services/partials/actions/php.blade.php @@ -0,0 +1 @@ +@include("services.partials.unit-actions") diff --git a/resources/views/services/partials/actions/postgresql.blade.php b/resources/views/services/partials/actions/postgresql.blade.php new file mode 100644 index 00000000..36d30486 --- /dev/null +++ b/resources/views/services/partials/actions/postgresql.blade.php @@ -0,0 +1 @@ +@include("services.partials.unit-actions") diff --git a/resources/views/services/partials/actions/redis.blade.php b/resources/views/services/partials/actions/redis.blade.php new file mode 100644 index 00000000..36d30486 --- /dev/null +++ b/resources/views/services/partials/actions/redis.blade.php @@ -0,0 +1 @@ +@include("services.partials.unit-actions") diff --git a/resources/views/services/partials/actions/supervisor.blade.php b/resources/views/services/partials/actions/supervisor.blade.php new file mode 100644 index 00000000..36d30486 --- /dev/null +++ b/resources/views/services/partials/actions/supervisor.blade.php @@ -0,0 +1 @@ +@include("services.partials.unit-actions") diff --git a/resources/views/services/partials/actions/ufw.blade.php b/resources/views/services/partials/actions/ufw.blade.php new file mode 100644 index 00000000..36d30486 --- /dev/null +++ b/resources/views/services/partials/actions/ufw.blade.php @@ -0,0 +1 @@ +@include("services.partials.unit-actions") diff --git a/resources/views/services/partials/add-ons.blade.php b/resources/views/services/partials/add-ons.blade.php new file mode 100644 index 00000000..4b70a934 --- /dev/null +++ b/resources/views/services/partials/add-ons.blade.php @@ -0,0 +1,33 @@ +<div> + <x-card-header> + <x-slot name="title">Supported Services</x-slot> + <x-slot name="description">Here you can find the supported services to install</x-slot> + <x-slot name="aside"></x-slot> + </x-card-header> + + <div class="grid grid-cols-1 gap-5 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> + @foreach (config("core.add_on_services") as $addOn) + <div + class="relative flex h-auto flex-col items-center justify-between space-y-3 rounded-b-md rounded-t-md border border-gray-200 bg-white text-center dark:border-gray-700 dark:bg-gray-800" + > + <div class="space-y-3 p-5"> + <div class="flex items-center justify-center"> + <img src="{{ asset("static/images/" . $addOn . ".svg") }}" class="h-20 w-20" alt="" /> + </div> + <div class="flex flex-grow flex-col items-start justify-center"> + <div class="flex items-center"> + <div class="flex items-center text-lg"> + {{ $addOn }} + </div> + </div> + </div> + </div> + <div + class="flex w-full items-center justify-between rounded-b-md border-t border-t-gray-200 bg-gray-50 p-2 dark:border-t-gray-600 dark:bg-gray-700" + > + @include("services.partials.add-on-installers." . $addOn) + </div> + </div> + @endforeach + </div> +</div> diff --git a/resources/views/services/partials/services-list.blade.php b/resources/views/services/partials/services-list.blade.php index dab4f6ae..46a36913 100644 --- a/resources/views/services/partials/services-list.blade.php +++ b/resources/views/services/partials/services-list.blade.php @@ -1,80 +1,42 @@ <div> <x-card-header> - <x-slot name="title">{{ __("Services") }}</x-slot> - <x-slot name="description"> - {{ __("All services that we installed on your server are here") }} - </x-slot> + <x-slot name="title">Installed Services</x-slot> + <x-slot name="description">All services that we installed on your server are here</x-slot> <x-slot name="aside"></x-slot> </x-card-header> - <x-live id="live-services" interval="5s"> - <div class="space-y-3"> + <x-live id="live-services"> + <div class="grid grid-cols-1 gap-5 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> @foreach ($services as $service) - <x-item-card> - <div class="flex-none"> - <img src="{{ asset("static/images/" . $service->name . ".svg") }}" class="h-10 w-10" alt="" /> + <div + class="relative flex h-auto flex-col items-center justify-between space-y-3 rounded-b-md rounded-t-md border border-gray-200 bg-white text-center dark:border-gray-700 dark:bg-gray-800" + > + <div class="absolute right-3 top-3"> + @include("services.partials.status", ["status" => $service->status]) </div> - <div class="ml-3 flex flex-grow flex-col items-start justify-center"> - <div class="flex items-center"> - <div class="mr-2">{{ $service->name }}:{{ $service->version }}</div> - @include("services.partials.status", ["status" => $service->status]) + <div class="space-y-3 p-5"> + <div class="flex items-center justify-center"> + <img + src="{{ asset("static/images/" . $service->name . ".svg") }}" + class="h-20 w-20" + alt="" + /> + </div> + <div class="flex flex-grow flex-col items-start justify-center"> + <div class="flex items-center"> + <div class="flex items-center text-lg"> + {{ $service->name }} + <x-status status="disabled" class="ml-1">{{ $service->version }}</x-status> + </div> + </div> </div> </div> - <div class="flex items-center"> - <x-dropdown> - <x-slot name="trigger"> - <x-secondary-button> - {{ __("Actions") }} - </x-secondary-button> - </x-slot> - - <x-slot name="content"> - @if ($service->unit) - @if ($service->status == \App\Enums\ServiceStatus::STOPPED) - <x-dropdown-link - class="cursor-pointer" - href="{{ route('servers.services.start', ['server' => $server, 'service' => $service]) }}" - > - {{ __("Start") }} - </x-dropdown-link> - @endif - - @if ($service->status == \App\Enums\ServiceStatus::READY) - <x-dropdown-link - class="cursor-pointer" - href="{{ route('servers.services.stop', ['server' => $server, 'service' => $service]) }}" - > - {{ __("Stop") }} - </x-dropdown-link> - @endif - - <x-dropdown-link - class="cursor-pointer" - href="{{ route('servers.services.restart', ['server' => $server, 'service' => $service]) }}" - > - {{ __("Restart") }} - </x-dropdown-link> - - @if ($service->status == \App\Enums\ServiceStatus::DISABLED) - <x-dropdown-link - class="cursor-pointer" - href="{{ route('servers.services.enable', ['server' => $server, 'service' => $service]) }}" - > - {{ __("Enable") }} - </x-dropdown-link> - @endif - - <x-dropdown-link - class="cursor-pointer" - href="{{ route('servers.services.disable', ['server' => $server, 'service' => $service]) }}" - > - {{ __("Disable") }} - </x-dropdown-link> - @endif - </x-slot> - </x-dropdown> + <div + class="flex w-full items-center justify-between rounded-b-md border-t border-t-gray-200 bg-gray-50 p-2 dark:border-t-gray-600 dark:bg-gray-700" + > + @include("services.partials.actions." . $service->name) </div> - </x-item-card> + </div> @endforeach </div> </x-live> diff --git a/resources/views/services/partials/unit-actions.blade.php b/resources/views/services/partials/unit-actions.blade.php new file mode 100644 index 00000000..b7d03ea1 --- /dev/null +++ b/resources/views/services/partials/unit-actions.blade.php @@ -0,0 +1,42 @@ +<x-icon-button + data-tooltip="Restart Service" + class="cursor-pointer" + href="{{ route('servers.services.restart', ['server' => $server, 'service' => $service]) }}" +> + <x-heroicon name="o-arrow-path" class="h-5 w-5" /> +</x-icon-button> + +<x-icon-button + :disabled="$service->status != \App\Enums\ServiceStatus::STOPPED" + data-tooltip="Start Service" + class="cursor-pointer" + href="{{ route('servers.services.start', ['server' => $server, 'service' => $service]) }}" +> + <x-heroicon name="o-play" class="h-5 w-5 text-green-400" /> +</x-icon-button> + +<x-icon-button + data-tooltip="Stop Service" + :disabled="$service->status != \App\Enums\ServiceStatus::READY" + class="cursor-pointer" + href="{{ route('servers.services.stop', ['server' => $server, 'service' => $service]) }}" +> + <x-heroicon name="o-stop" class="h-5 w-5 text-red-400" /> +</x-icon-button> + +<x-icon-button + :disabled="$service->status != \App\Enums\ServiceStatus::DISABLED" + data-tooltip="Enable Service" + class="cursor-pointer" + href="{{ route('servers.services.enable', ['server' => $server, 'service' => $service]) }}" +> + <x-heroicon name="o-check" class="h-5 w-5" /> +</x-icon-button> + +<x-icon-button + data-tooltip="Disable Service" + class="cursor-pointer" + href="{{ route('servers.services.disable', ['server' => $server, 'service' => $service]) }}" +> + <x-heroicon name="o-no-symbol" class="h-5 w-5" /> +</x-icon-button> diff --git a/resources/views/sites/partials/create/phpmyadmin.blade.php b/resources/views/sites/partials/create/phpmyadmin.blade.php new file mode 100644 index 00000000..911e8c02 --- /dev/null +++ b/resources/views/sites/partials/create/phpmyadmin.blade.php @@ -0,0 +1,13 @@ +@include("sites.partials.create.fields.php-version") + +<div> + <x-input-label for="version" :value="__('Version')" /> + <x-select-input id="version" name="version" class="mt-1 w-full"> + <option value="" selected>{{ __("Select") }}</option> + <option value="5.1.2" @if(old('version') == '5.1.2') selected @endif>PHPMyAdmin 5.1.2</option> + <option value="4.9.11" @if(old('version') == '4.9.11') selected @endif>PHPMyAdmin 4.9.11</option> + </x-select-input> + @error("version") + <x-input-error class="mt-2" :messages="$message" /> + @enderror +</div> diff --git a/routes/server.php b/routes/server.php index 7cc3c7c6..20a5b0fe 100644 --- a/routes/server.php +++ b/routes/server.php @@ -124,6 +124,7 @@ Route::get('/{server}/services/{service}/restart', [ServiceController::class, 'restart'])->name('servers.services.restart'); Route::get('/{server}/services/{service}/enable', [ServiceController::class, 'enable'])->name('servers.services.enable'); Route::get('/{server}/services/{service}/disable', [ServiceController::class, 'disable'])->name('servers.services.disable'); + Route::post('/{server}/services/install', [ServiceController::class, 'install'])->name('servers.services.install'); // console Route::get('/{server}/console', [ConsoleController::class, 'index'])->name('servers.console'); diff --git a/tests/Feature/SitesTest.php b/tests/Feature/SitesTest.php index 22ec47a6..4de43e42 100644 --- a/tests/Feature/SitesTest.php +++ b/tests/Feature/SitesTest.php @@ -158,6 +158,15 @@ public static function create_data(): array 'web_directory' => 'public', ], ], + [ + [ + 'type' => SiteType::PHPMYADMIN, + 'domain' => 'example.com', + 'alias' => 'www.example.com', + 'php_version' => '8.2', + 'version' => '5.1.2', + ], + ], ]; }