mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-17 17:01:37 +00:00
Manage site aliases (#206)
* manage site aliases * build assets * fix tests
This commit is contained in:
parent
30ef8ad5eb
commit
de468ae1ba
@ -28,6 +28,10 @@ public function create(Site $site, array $input): void
|
||||
'expires_at' => $input['type'] === SslType::LETSENCRYPT ? now()->addMonths(3) : $input['expires_at'],
|
||||
'status' => SslStatus::CREATING,
|
||||
]);
|
||||
$ssl->domains = [$site->domain];
|
||||
if (isset($input['aliases']) && $input['aliases']) {
|
||||
$ssl->domains = array_merge($ssl->domains, $site->aliases);
|
||||
}
|
||||
$ssl->save();
|
||||
|
||||
dispatch(function () use ($site, $ssl) {
|
||||
|
@ -21,7 +21,7 @@
|
||||
class CreateSite
|
||||
{
|
||||
/**
|
||||
* @throws ValidationException
|
||||
* @throws SourceControlIsNotConnected
|
||||
*/
|
||||
public function create(Server $server, array $input): Site
|
||||
{
|
||||
@ -33,7 +33,7 @@ public function create(Server $server, array $input): Site
|
||||
'server_id' => $server->id,
|
||||
'type' => $input['type'],
|
||||
'domain' => $input['domain'],
|
||||
'aliases' => isset($input['alias']) ? [$input['alias']] : [],
|
||||
'aliases' => $input['aliases'] ?? [],
|
||||
'path' => '/home/'.$server->getSshUser().'/'.$input['domain'],
|
||||
'status' => SiteStatus::INSTALLING,
|
||||
]);
|
||||
@ -115,7 +115,7 @@ private function validateInputs(Server $server, array $input): void
|
||||
return $query->where('server_id', $server->id);
|
||||
}),
|
||||
],
|
||||
'alias' => [
|
||||
'aliases.*' => [
|
||||
new DomainRule(),
|
||||
],
|
||||
];
|
||||
|
33
app/Actions/Site/UpdateAliases.php
Normal file
33
app/Actions/Site/UpdateAliases.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Site;
|
||||
|
||||
use App\Models\Site;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use App\ValidationRules\DomainRule;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class UpdateAliases
|
||||
{
|
||||
public function update(Site $site, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
$site->aliases = $input['aliases'] ?? [];
|
||||
|
||||
/** @var Webserver $webserver */
|
||||
$webserver = $site->server->webserver()->handler();
|
||||
$webserver->updateVHost($site, ! $site->hasSSL());
|
||||
|
||||
$site->save();
|
||||
}
|
||||
|
||||
private function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'aliases.*' => [
|
||||
new DomainRule(),
|
||||
],
|
||||
])->validate();
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Site\UpdateAliases;
|
||||
use App\Actions\Site\UpdateSourceControl;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
@ -83,10 +84,21 @@ public function updateSourceControl(Server $server, Site $site, Request $request
|
||||
{
|
||||
$this->authorize('manage', $server);
|
||||
|
||||
$site = app(UpdateSourceControl::class)->update($site, $request->input());
|
||||
app(UpdateSourceControl::class)->update($site, $request->input());
|
||||
|
||||
Toast::success('Source control updated successfully!');
|
||||
|
||||
return htmx()->back();
|
||||
}
|
||||
|
||||
public function updateAliases(Server $server, Site $site, Request $request): HtmxResponse
|
||||
{
|
||||
$this->authorize('manage', $server);
|
||||
|
||||
app(UpdateAliases::class)->update($site, $request->input());
|
||||
|
||||
Toast::success('Aliases updated successfully!');
|
||||
|
||||
return htmx()->back();
|
||||
}
|
||||
}
|
||||
|
@ -278,4 +278,9 @@ public function getEnv(): string
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function hasSSL(): bool
|
||||
{
|
||||
return $this->ssls->isNotEmpty();
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
* @property string $status
|
||||
* @property Site $site
|
||||
* @property string $ca_path
|
||||
* @property ?array $domains
|
||||
*/
|
||||
class Ssl extends AbstractModel
|
||||
{
|
||||
@ -30,6 +31,7 @@ class Ssl extends AbstractModel
|
||||
'ca',
|
||||
'expires_at',
|
||||
'status',
|
||||
'domains',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -38,6 +40,7 @@ class Ssl extends AbstractModel
|
||||
'pk' => 'encrypted',
|
||||
'ca' => 'encrypted',
|
||||
'expires_at' => 'datetime',
|
||||
'domains' => 'array',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
@ -111,4 +114,16 @@ public function validateSetup(string $result): bool
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDomains(): array
|
||||
{
|
||||
if (! empty($this->domains) && is_array($this->domains)) {
|
||||
return $this->domains;
|
||||
}
|
||||
|
||||
$this->domains = [$this->site->domain];
|
||||
$this->save();
|
||||
|
||||
return $this->domains;
|
||||
}
|
||||
}
|
||||
|
@ -117,11 +117,9 @@ public function changePHPVersion(Site $site, $version): void
|
||||
*/
|
||||
public function setupSSL(Ssl $ssl): void
|
||||
{
|
||||
$domains = '-d '.$ssl->site->domain;
|
||||
if ($ssl->site->aliases) {
|
||||
foreach ($ssl->site->aliases as $alias) {
|
||||
$domains .= ' -d '.$alias;
|
||||
}
|
||||
$domains = '';
|
||||
foreach ($ssl->getDomains() as $domain) {
|
||||
$domains .= ' -d '.$domain;
|
||||
}
|
||||
$command = $this->getScript('nginx/create-letsencrypt-ssl.sh', [
|
||||
'email' => $ssl->site->server->creator->email,
|
||||
|
@ -20,7 +20,7 @@ public function definition(): array
|
||||
'ca' => $this->faker->word(),
|
||||
'expires_at' => Carbon::now()->addDay(),
|
||||
'status' => SslStatus::CREATED,
|
||||
'domains' => 'example.com',
|
||||
'domains' => ['example.com'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
1
public/build/assets/app-f8a673af.css
Normal file
1
public/build/assets/app-f8a673af.css
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
{
|
||||
"resources/css/app.css": {
|
||||
"file": "assets/app-4e6a754c.css",
|
||||
"file": "assets/app-f8a673af.css",
|
||||
"isEntry": true,
|
||||
"src": "resources/css/app.css"
|
||||
},
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
@php
|
||||
$class = [
|
||||
"success" => "rounded-md border border-green-300 bg-green-50 px-2 py-1 text-xs uppercase text-green-500 dark:border-green-600 dark:bg-green-500 dark:bg-opacity-10",
|
||||
"danger" => "rounded-md border border-red-300 bg-red-50 px-2 py-1 text-xs uppercase text-red-500 dark:border-red-600 dark:bg-red-500 dark:bg-opacity-10",
|
||||
"warning" => "rounded-md border border-yellow-300 bg-yellow-50 px-2 py-1 text-xs uppercase text-yellow-500 dark:border-yellow-600 dark:bg-yellow-500 dark:bg-opacity-10",
|
||||
"disabled" => "rounded-md border border-gray-300 bg-gray-50 px-2 py-1 text-xs uppercase text-gray-500 dark:border-gray-600 dark:bg-gray-500 dark:bg-opacity-30 dark:text-gray-400",
|
||||
"info" => "rounded-md border border-primary-300 bg-primary-50 px-2 py-1 text-xs uppercase text-primary-500 dark:border-primary-600 dark:bg-primary-500 dark:bg-opacity-10",
|
||||
"success" => "max-w-max rounded-md border border-green-300 bg-green-50 px-2 py-1 text-xs uppercase text-green-500 dark:border-green-600 dark:bg-green-500 dark:bg-opacity-10",
|
||||
"danger" => "max-w-max rounded-md border border-red-300 bg-red-50 px-2 py-1 text-xs uppercase text-red-500 dark:border-red-600 dark:bg-red-500 dark:bg-opacity-10",
|
||||
"warning" => "max-w-max rounded-md border border-yellow-300 bg-yellow-50 px-2 py-1 text-xs uppercase text-yellow-500 dark:border-yellow-600 dark:bg-yellow-500 dark:bg-opacity-10",
|
||||
"disabled" => "max-w-max rounded-md border border-gray-300 bg-gray-50 px-2 py-1 text-xs uppercase text-gray-500 dark:border-gray-600 dark:bg-gray-500 dark:bg-opacity-30 dark:text-gray-400",
|
||||
"info" => "max-w-max rounded-md border border-primary-300 bg-primary-50 px-2 py-1 text-xs uppercase text-primary-500 dark:border-primary-600 dark:bg-primary-500 dark:bg-opacity-10",
|
||||
];
|
||||
@endphp
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<td
|
||||
{!! $attributes->merge(["class" => "whitespace-nowrap px-6 py-4 text-gray-700 dark:text-gray-300 w-1"]) !!}
|
||||
{!! $attributes->merge(["class" => "text-sm whitespace-nowrap px-6 py-4 text-gray-700 dark:text-gray-300 w-1"]) !!}
|
||||
>
|
||||
{{ $slot }}
|
||||
</td>
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
@include("site-settings.partials.change-php-version")
|
||||
|
||||
@include("site-settings.partials.update-aliases")
|
||||
|
||||
@if ($site->source_control_id)
|
||||
@include("site-settings.partials.update-source-control")
|
||||
@endif
|
||||
|
@ -0,0 +1,30 @@
|
||||
<x-card>
|
||||
<x-slot name="title">{{ __("Update Aliases") }}</x-slot>
|
||||
|
||||
<x-slot name="description">
|
||||
{{ __("Add/Remove site aliases") }}
|
||||
</x-slot>
|
||||
|
||||
<form
|
||||
id="update-aliases"
|
||||
hx-post="{{ route("servers.sites.settings.aliases", ["server" => $server, "site" => $site]) }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#update-aliases"
|
||||
hx-ext="disable-element"
|
||||
hx-disable-element="#btn-update-aliases"
|
||||
class="space-y-6"
|
||||
>
|
||||
@include(
|
||||
"sites.partials.create.fields.aliases",
|
||||
[
|
||||
"aliases" => $site->aliases,
|
||||
]
|
||||
)
|
||||
</form>
|
||||
|
||||
<x-slot name="actions">
|
||||
<x-primary-button id="btn-update-aliases" form="update-aliases" hx-disable>
|
||||
{{ __("Save") }}
|
||||
</x-primary-button>
|
||||
</x-slot>
|
||||
</x-card>
|
@ -55,21 +55,7 @@ class="mt-1 block w-full"
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="alias" :value="__('Alias')" />
|
||||
<x-text-input
|
||||
value="{{ old('alias') }}"
|
||||
id="alias"
|
||||
name="alias"
|
||||
type="text"
|
||||
class="mt-1 block w-full"
|
||||
autocomplete="alias"
|
||||
placeholder="www.example.com"
|
||||
/>
|
||||
@error("alias")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
@include("sites.partials.create.fields.aliases")
|
||||
|
||||
@include("sites.partials.create." . $type)
|
||||
</form>
|
||||
|
@ -0,0 +1,55 @@
|
||||
<script>
|
||||
let aliases = @json($aliases ?? []);
|
||||
</script>
|
||||
<div
|
||||
x-data="{
|
||||
aliasInput: '',
|
||||
aliases: aliases,
|
||||
removeAlias(alias) {
|
||||
this.aliases = this.aliases.filter((a) => a !== alias)
|
||||
},
|
||||
addAlias() {
|
||||
if (! this.aliasInput) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.aliases.includes(this.aliasInput)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.aliases.push(this.aliasInput)
|
||||
this.aliasInput = ''
|
||||
},
|
||||
}"
|
||||
>
|
||||
<x-input-label for="alias" :value="__('Alias')" />
|
||||
<div class="flex items-center">
|
||||
<x-text-input
|
||||
value="{{ old('alias') }}"
|
||||
id="alias"
|
||||
x-model="aliasInput"
|
||||
name="alias"
|
||||
type="text"
|
||||
class="mt-1 block w-full"
|
||||
autocomplete="alias"
|
||||
placeholder="www.example.com"
|
||||
/>
|
||||
<x-secondary-button type="button" class="ml-2 flex-none" x-on:click="addAlias()">
|
||||
{{ __("Add") }}
|
||||
</x-secondary-button>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<template x-for="alias in aliases">
|
||||
<div class="mr-1 inline-flex">
|
||||
<x-status status="info" class="flex items-center lowercase">
|
||||
<span x-text="alias"></span>
|
||||
<x-heroicon name="o-x-mark" class="ml-1 h-4 w-4 cursor-pointer" x-on:click="removeAlias(alias)" />
|
||||
<input type="hidden" name="aliases[]" x-bind:value="alias" />
|
||||
</x-status>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@error("aliases")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
@ -82,6 +82,12 @@ class="mt-1 w-full"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-checkbox id="aliases" name="aliases" :checked="old('aliases')" value="1">
|
||||
Set SSL for site's aliases as well
|
||||
</x-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||
{{ __("Cancel") }}
|
||||
|
@ -14,6 +14,7 @@
|
||||
<x-table>
|
||||
<x-tr>
|
||||
<x-th>{{ __("Type") }}</x-th>
|
||||
<x-th>{{ __("Domains") }}</x-th>
|
||||
<x-th>{{ __("Created") }}</x-th>
|
||||
<x-th>{{ __("Expires at") }}</x-th>
|
||||
<x-th></x-th>
|
||||
@ -21,6 +22,15 @@
|
||||
@foreach ($ssls as $ssl)
|
||||
<x-tr>
|
||||
<x-td>{{ $ssl->type }}</x-td>
|
||||
<x-td>
|
||||
<div class="flex-col space-y-1">
|
||||
@foreach ($ssl->getDomains() as $domain)
|
||||
<x-status status="disabled" class="lowercase">
|
||||
{{ $domain }}
|
||||
</x-status>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-td>
|
||||
<x-td>
|
||||
<x-datetime :value="$ssl->created_at" />
|
||||
</x-td>
|
||||
|
@ -67,6 +67,7 @@
|
||||
Route::post('/{site}/settings/vhost', [SiteSettingController::class, 'updateVhost']);
|
||||
Route::post('/{site}/settings/php', [SiteSettingController::class, 'updatePHPVersion'])->name('servers.sites.settings.php');
|
||||
Route::post('/{site}/settings/source-control', [SiteSettingController::class, 'updateSourceControl'])->name('servers.sites.settings.source-control');
|
||||
Route::post('/{site}/settings/update-aliases', [SiteSettingController::class, 'updateAliases'])->name('servers.sites.settings.aliases');
|
||||
|
||||
// site logs
|
||||
Route::get('/{site}/logs', [SiteLogController::class, 'index'])->name('servers.sites.logs');
|
||||
|
@ -42,6 +42,7 @@ public function test_create_site(array $inputs): void
|
||||
|
||||
$this->assertDatabaseHas('sites', [
|
||||
'domain' => 'example.com',
|
||||
'aliases' => json_encode($inputs['aliases'] ?? []),
|
||||
'status' => SiteStatus::READY,
|
||||
]);
|
||||
}
|
||||
@ -54,7 +55,7 @@ public function test_create_site_failed_due_to_source_control(int $status): void
|
||||
$inputs = [
|
||||
'type' => SiteType::LARAVEL,
|
||||
'domain' => 'example.com',
|
||||
'alias' => 'www.example.com',
|
||||
'aliases' => ['www.example.com'],
|
||||
'php_version' => '8.2',
|
||||
'web_directory' => 'public',
|
||||
'repository' => 'test/test',
|
||||
@ -220,7 +221,7 @@ public static function create_data(): array
|
||||
[
|
||||
'type' => SiteType::LARAVEL,
|
||||
'domain' => 'example.com',
|
||||
'alias' => 'www.example.com',
|
||||
'aliases' => ['www.example.com', 'www2.example.com'],
|
||||
'php_version' => '8.2',
|
||||
'web_directory' => 'public',
|
||||
'repository' => 'test/test',
|
||||
@ -232,7 +233,7 @@ public static function create_data(): array
|
||||
[
|
||||
'type' => SiteType::WORDPRESS,
|
||||
'domain' => 'example.com',
|
||||
'alias' => 'www.example.com',
|
||||
'aliases' => ['www.example.com'],
|
||||
'php_version' => '8.2',
|
||||
'title' => 'Example',
|
||||
'username' => 'example',
|
||||
@ -247,7 +248,7 @@ public static function create_data(): array
|
||||
[
|
||||
'type' => SiteType::PHP_BLANK,
|
||||
'domain' => 'example.com',
|
||||
'alias' => 'www.example.com',
|
||||
'aliases' => ['www.example.com'],
|
||||
'php_version' => '8.2',
|
||||
'web_directory' => 'public',
|
||||
],
|
||||
@ -256,7 +257,7 @@ public static function create_data(): array
|
||||
[
|
||||
'type' => SiteType::PHPMYADMIN,
|
||||
'domain' => 'example.com',
|
||||
'alias' => 'www.example.com',
|
||||
'aliases' => ['www.example.com'],
|
||||
'php_version' => '8.2',
|
||||
'version' => '5.1.2',
|
||||
],
|
||||
|
@ -58,6 +58,29 @@ public function test_letsencrypt_ssl()
|
||||
'site_id' => $this->site->id,
|
||||
'type' => SslType::LETSENCRYPT,
|
||||
'status' => SslStatus::CREATED,
|
||||
'domains' => json_encode([$this->site->domain]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_letsencrypt_ssl_with_aliases()
|
||||
{
|
||||
SSH::fake('Successfully received certificate');
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.sites.ssl.store', [
|
||||
'server' => $this->server,
|
||||
'site' => $this->site,
|
||||
]), [
|
||||
'type' => SslType::LETSENCRYPT,
|
||||
'aliases' => '1',
|
||||
])->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->assertDatabaseHas('ssls', [
|
||||
'site_id' => $this->site->id,
|
||||
'type' => SslType::LETSENCRYPT,
|
||||
'status' => SslStatus::CREATED,
|
||||
'domains' => json_encode(array_merge([$this->site->domain], $this->site->aliases)),
|
||||
]);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user