feat: able to add several domains to compose based previews

This commit is contained in:
Andras Bacsai 2024-06-05 15:14:44 +02:00
parent e9e12ad843
commit 7fd0deedb1
13 changed files with 262 additions and 104 deletions

View File

@ -26,7 +26,6 @@ class ServicesGenerate extends Command
*/ */
public function handle() public function handle()
{ {
// ray()->clearAll();
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']); $files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
$files = array_filter($files, function ($file) { $files = array_filter($files, function ($file) {
return strpos($file, '.yaml') !== false; return strpos($file, '.yaml') !== false;

View File

@ -113,7 +113,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
public $tries = 1; public $tries = 1;
public function __construct(int $application_deployment_queue_id) public function __construct(int $application_deployment_queue_id)
{ {
ray()->clearAll();
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id); $this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
$this->application = Application::find($this->application_deployment_queue->application_id); $this->application = Application::find($this->application_deployment_queue->application_id);
$this->build_pack = data_get($this->application, 'build_pack'); $this->build_pack = data_get($this->application, 'build_pack');
@ -373,7 +372,7 @@ private function deploy_docker_compose_buildpack()
$yaml = $composeFile = $this->application->docker_compose_raw; $yaml = $composeFile = $this->application->docker_compose_raw;
$this->save_environment_variables(); $this->save_environment_variables();
} else { } else {
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id); $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id, preview_id: data_get($this, 'preview.id'));
$this->save_environment_variables(); $this->save_environment_variables();
if (!is_null($this->env_filename)) { if (!is_null($this->env_filename)) {
$services = collect($composeFile['services']); $services = collect($composeFile['services']);

View File

@ -25,6 +25,7 @@ public function getListeners()
return [ return [
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status', "echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
"compose_loaded" => '$refresh', "compose_loaded" => '$refresh',
"update_links" => '$refresh',
]; ];
} }
public function mount() public function mount()

View File

@ -43,7 +43,7 @@ public function save_preview($preview_id)
try { try {
$success = true; $success = true;
$preview = $this->application->previews->find($preview_id); $preview = $this->application->previews->find($preview_id);
if (isset($preview->fqdn)) { if (data_get_str($preview, 'fqdn')->isNotEmpty()) {
$preview->fqdn = str($preview->fqdn)->replaceEnd(',', '')->trim(); $preview->fqdn = str($preview->fqdn)->replaceEnd(',', '')->trim();
$preview->fqdn = str($preview->fqdn)->replaceStart(',', '')->trim(); $preview->fqdn = str($preview->fqdn)->replaceStart(',', '')->trim();
$preview->fqdn = str($preview->fqdn)->trim()->lower(); $preview->fqdn = str($preview->fqdn)->trim()->lower();
@ -79,7 +79,7 @@ public function generate_preview($preview_id)
$random = new Cuid2(7); $random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template); $preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn); $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $preview_id, $preview_fqdn); $preview_fqdn = str_replace('{{pr_id}}', $preview->pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn"; $preview_fqdn = "$schema://$preview_fqdn";
$preview->fqdn = $preview_fqdn; $preview->fqdn = $preview_fqdn;
$preview->save(); $preview->save();
@ -88,17 +88,34 @@ public function generate_preview($preview_id)
public function add(int $pull_request_id, string|null $pull_request_html_url = null) public function add(int $pull_request_id, string|null $pull_request_html_url = null)
{ {
try { try {
$this->setDeploymentUuid(); if ($this->application->build_pack === 'dockercompose') {
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first(); $this->setDeploymentUuid();
if (!$found && !is_null($pull_request_html_url)) { $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
ApplicationPreview::create([ if (!$found && !is_null($pull_request_html_url)) {
'application_id' => $this->application->id, $found = ApplicationPreview::create([
'pull_request_id' => $pull_request_id, 'application_id' => $this->application->id,
'pull_request_html_url' => $pull_request_html_url 'pull_request_id' => $pull_request_id,
]); 'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $this->application->docker_compose_domains,
]);
}
$found->generate_preview_fqdn_compose();
$this->application->refresh();
} else {
$this->setDeploymentUuid();
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found && !is_null($pull_request_html_url)) {
$found = ApplicationPreview::create([
'application_id' => $this->application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
$this->application->generate_preview_fqdn($pull_request_id);
$this->application->refresh();
$this->dispatch('update_links');
$this->dispatch('success', 'Preview added.');
} }
$this->application->generate_preview_fqdn($pull_request_id);
$this->application->refresh();
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }
@ -152,7 +169,7 @@ public function stop(int $pull_request_id)
} }
} }
GetContainersStatus::dispatchSync($this->application->destination->server); GetContainersStatus::dispatchSync($this->application->destination->server);
$this->application->refresh(); $this->dispatch('reloadWindow');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }
@ -172,15 +189,10 @@ public function delete(int $pull_request_id)
} }
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete(); ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
$this->application->refresh(); $this->application->refresh();
$this->dispatch('update_links');
$this->dispatch('success', 'Preview deleted.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }
} }
public function previewRefresh()
{
$this->application->previews->each(function ($preview) {
$preview->refresh();
});
}
} }

View File

@ -0,0 +1,56 @@
<?php
namespace App\Livewire\Project\Application;
use App\Models\ApplicationPreview;
use Livewire\Component;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
class PreviewsCompose extends Component
{
public $service;
public $serviceName;
public ApplicationPreview $preview;
public function render()
{
return view('livewire.project.application.previews-compose');
}
public function save()
{
$domain = data_get($this->service, 'domain');
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
$docker_compose_domains = json_decode($docker_compose_domains, true);
$docker_compose_domains[$this->serviceName]['domain'] = $domain;
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
$this->preview->save();
$this->dispatch('update_links');
$this->dispatch('success', 'Domain saved.');
}
public function generate()
{
$domains = collect(json_decode($this->preview->application->docker_compose_domains)) ?? collect();
$domain = $domains->first(function ($_, $key) {
return $key === $this->serviceName;
});
if ($domain) {
$domain = data_get($domain, 'domain');
$url = Url::fromString($domain);
$template = $this->preview->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->preview->pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
$docker_compose_domains = json_decode($docker_compose_domains, true);
$docker_compose_domains[$this->serviceName]['domain'] = $this->service->domain = $preview_fqdn;
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
$this->preview->save();
}
$this->dispatch('update_links');
$this->dispatch('success', 'Domain generated.');
}
}

View File

@ -861,14 +861,10 @@ function parseRawCompose()
instant_remote_process($commands, $this->destination->server, false); instant_remote_process($commands, $this->destination->server, false);
} }
function parseCompose(int $pull_request_id = 0) function parseCompose(int $pull_request_id = 0, ?int $preview_id = null)
{ {
if ($this->docker_compose_raw) { if ($this->docker_compose_raw) {
$mainCompose = parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id); return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
if ($this->getMorphClass() === 'App\Models\Application' && $this->docker_compose_pr_raw) {
parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, is_pr: true);
}
return $mainCompose;
} else { } else {
return collect([]); return collect([]);
} }
@ -1052,7 +1048,8 @@ public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false
} }
} }
} }
function generate_preview_fqdn(int $pull_request_id) { function generate_preview_fqdn(int $pull_request_id)
{
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->id, $pull_request_id); $preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->id, $pull_request_id);
if (is_null(data_get($preview, 'fqdn')) && $this->fqdn) { if (is_null(data_get($preview, 'fqdn')) && $this->fqdn) {
if (str($this->fqdn)->contains(',')) { if (str($this->fqdn)->contains(',')) {

View File

@ -2,6 +2,9 @@
namespace App\Models; namespace App\Models;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
class ApplicationPreview extends BaseModel class ApplicationPreview extends BaseModel
{ {
protected $guarded = []; protected $guarded = [];
@ -34,4 +37,26 @@ public function application()
{ {
return $this->belongsTo(Application::class); return $this->belongsTo(Application::class);
} }
function generate_preview_fqdn_compose()
{
$domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect();
foreach ($domains as $service_name => $domain) {
$domain = data_get($domain, 'domain');
$url = Url::fromString($domain);
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$docker_compose_domains = data_get($this, 'docker_compose_domains');
$docker_compose_domains = json_decode($docker_compose_domains, true);
$docker_compose_domains[$service_name]['domain'] = $preview_fqdn;
$docker_compose_domains = json_encode($docker_compose_domains);
$this->docker_compose_domains = $docker_compose_domains;
$this->save();
}
}
} }

View File

@ -488,8 +488,10 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
)); ));
} }
} else { } else {
if ($preview->fqdn) { if (data_get($preview,'fqdn')) {
$domains = Str::of(data_get($preview, 'fqdn'))->explode(','); $domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
} else {
$domains = collect([]);
} }
$labels = $labels->merge(fqdnLabelsForTraefik( $labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid, uuid: $appUuid,

View File

@ -657,9 +657,8 @@ function getTopLevelNetworks(Service|Application $resource)
} }
} }
function parseDockerComposeFile(Service|Application $resource, bool $isNew = false, int $pull_request_id = 0, bool $is_pr = false) function parseDockerComposeFile(Service|Application $resource, bool $isNew = false, int $pull_request_id = 0, ?int $preview_id = null)
{ {
// ray()->clearAll();
if ($resource->getMorphClass() === 'App\Models\Service') { if ($resource->getMorphClass() === 'App\Models\Service') {
if ($resource->docker_compose_raw) { if ($resource->docker_compose_raw) {
try { try {
@ -1283,20 +1282,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$isSameDockerComposeFile = false; $isSameDockerComposeFile = false;
if ($resource->dockerComposePrLocation() === $resource->dockerComposeLocation()) { if ($resource->dockerComposePrLocation() === $resource->dockerComposeLocation()) {
$isSameDockerComposeFile = true; $isSameDockerComposeFile = true;
$is_pr = false;
} }
if ($is_pr) { try {
try { $yaml = Yaml::parse($resource->docker_compose_raw);
$yaml = Yaml::parse($resource->docker_compose_pr_raw); } catch (\Exception $e) {
} catch (\Exception $e) { return;
return;
}
} else {
try {
$yaml = Yaml::parse($resource->docker_compose_raw);
} catch (\Exception $e) {
return;
}
} }
$server = $resource->destination->server; $server = $resource->destination->server;
$topLevelVolumes = collect(data_get($yaml, 'volumes', [])); $topLevelVolumes = collect(data_get($yaml, 'volumes', []));
@ -1330,7 +1320,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($pull_request_id !== 0) { if ($pull_request_id !== 0) {
$definedNetwork = collect(["{$resource->uuid}-$pull_request_id"]); $definedNetwork = collect(["{$resource->uuid}-$pull_request_id"]);
} }
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $server, $pull_request_id) { $services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $server, $pull_request_id, $preview_id) {
$serviceVolumes = collect(data_get($service, 'volumes', [])); $serviceVolumes = collect(data_get($service, 'volumes', []));
$servicePorts = collect(data_get($service, 'ports', [])); $servicePorts = collect(data_get($service, 'ports', []));
$serviceNetworks = collect(data_get($service, 'networks', [])); $serviceNetworks = collect(data_get($service, 'networks', []));
@ -1716,21 +1706,14 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($fqdns) { if ($fqdns) {
$fqdns = str($fqdns)->explode(','); $fqdns = str($fqdns)->explode(',');
if ($pull_request_id !== 0) { if ($pull_request_id !== 0) {
$fqdns = $fqdns->map(function ($fqdn) use ($pull_request_id, $resource) { $preview = $resource->previews()->find($preview_id);
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($resource->id, $pull_request_id); $docker_compose_domains = collect(json_decode(data_get($preview, 'docker_compose_domains')));
$url = Url::fromString($fqdn); $found_fqdn = data_get($docker_compose_domains, "$serviceName.domain");
$template = $resource->preview_url_template; if ($found_fqdn) {
$host = $url->getHost(); $fqdns = collect($found_fqdn);
$schema = $url->getScheme(); } else {
$random = new Cuid2(7); $fqdns = collect([]);
$preview_fqdn = str_replace('{{random}}', $random, $template); }
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$preview->fqdn = $preview_fqdn;
$preview->save();
return $preview_fqdn;
});
} }
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
uuid: $resource->uuid, uuid: $resource->uuid,
@ -1797,13 +1780,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2); $resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose = Yaml::dump($finalServices, 10, 2); $resource->docker_compose = Yaml::dump($finalServices, 10, 2);
} else { } else {
if ($is_pr) { $resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose_pr_raw = Yaml::dump($yaml, 10, 2); $resource->docker_compose = Yaml::dump($finalServices, 10, 2);
$resource->docker_compose_pr = Yaml::dump($finalServices, 10, 2);
} else {
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose = Yaml::dump($finalServices, 10, 2);
}
} }
$resource->save(); $resource->save();
return collect($finalServices); return collect($finalServices);

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->text('docker_compose_domains')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_previews', function (Blueprint $table) {
$table->dropColumn('docker_compose_domains');
});
}
};

View File

@ -45,24 +45,48 @@
</a> </a>
@endforeach @endforeach
@endif @endif
@if (data_get($application, 'previews', collect([]))->count() > 0) @if (data_get($application, 'previews', collect())->count() > 0)
@foreach (data_get($application, 'previews') as $preview) @if (data_get($application, 'build_pack') === 'dockercompose')
@if (data_get($preview, 'fqdn')) @foreach ($application->previews as $preview)
<a class="dropdown-item" target="_blank" @foreach (collect(json_decode($preview->docker_compose_domains)) as $fqdn)
href="{{ getFqdnWithoutPort(data_get($preview, 'fqdn')) }}"> @if (data_get($fqdn, 'domain'))
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5" @foreach (explode(',', data_get($fqdn, 'domain')) as $domain)
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <a class="dropdown-item" target="_blank" href="{{ getFqdnWithoutPort($domain) }}">
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
<path d="M9 15l6 -6" /> stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" /> stroke-linejoin="round">
<path <path stroke="none" d="M0 0h24v24H0z" fill="none" />
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" /> <path d="M9 15l6 -6" />
</svg> <path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
PR{{ data_get($preview, 'pull_request_id') }} | <path
{{ data_get($preview, 'fqdn') }} d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</a> </svg>PR{{ data_get($preview, 'pull_request_id') }} |
@endif {{ getFqdnWithoutPort($domain) }}
@endforeach </a>
@endforeach
@endif
@endforeach
@endforeach
@else
@foreach (data_get($application, 'previews') as $preview)
@if (data_get($preview, 'fqdn'))
<a class="dropdown-item" target="_blank"
href="{{ getFqdnWithoutPort(data_get($preview, 'fqdn')) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
PR{{ data_get($preview, 'pull_request_id') }} |
{{ data_get($preview, 'fqdn') }}
</a>
@endif
@endforeach
@endif
@endif @endif
@if (data_get($application, 'ports_mappings_array')) @if (data_get($application, 'ports_mappings_array'))
@foreach ($application->ports_mappings_array as $port) @foreach ($application->ports_mappings_array as $port)

View File

@ -0,0 +1,7 @@
<form wire:submit="save" class="flex items-end gap-2">
<x-forms.input helper="One domain per preview." label="Domains for {{ str($serviceName)->headline() }}"
id="service.domain"></x-forms.input>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="generate">Generate
Domain</x-forms.button>
</form>

View File

@ -42,11 +42,16 @@ class="dark:text-warning">{{ $application->destination->server->name }}</span>.<
<td class="flex flex-col gap-1 md:flex-row"> <td class="flex flex-col gap-1 md:flex-row">
<x-forms.button <x-forms.button
wire:click="add('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')"> wire:click="add('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
Add Configure
</x-forms.button> </x-forms.button>
<x-forms.button <x-forms.button
wire:click="deploy('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')"> wire:click="deploy('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
Deploy <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 4v16l13 -8z" />
</svg>Deploy
</x-forms.button> </x-forms.button>
</td> </td>
</tr> </tr>
@ -58,7 +63,7 @@ class="dark:text-warning">{{ $application->destination->server->name }}</span>.<
</div> </div>
</div> </div>
@if ($application->previews->count() > 0) @if ($application->previews->count() > 0)
<div class="pb-4">Previews</div> <h3 class="py-4">Deployed Previews</h3>
<div class="flex flex-wrap w-full gap-4"> <div class="flex flex-wrap w-full gap-4">
@foreach (data_get($application, 'previews') as $previewName => $preview) @foreach (data_get($application, 'previews') as $previewName => $preview)
<div class="flex flex-col w-full p-4 border dark:border-coolgray-200"> <div class="flex flex-col w-full p-4 border dark:border-coolgray-200">
@ -81,21 +86,25 @@ class="dark:text-warning">{{ $application->destination->server->name }}</span>.<
<x-external-link /> <x-external-link />
</a> </a>
</div> </div>
<form wire:submit="save_preview('{{ $preview->id }}')" class="flex items-end gap-2 pt-4">
<x-forms.input label="Domain" helper="One domain per preview." @if ($application->build_pack === 'dockercompose')
id="application.previews.{{ $previewName }}.fqdn"></x-forms.input> <div class="flex flex-col gap-4 pt-4">
<x-forms.button type="submit">Save</x-forms.button> @foreach (collect(json_decode($preview->docker_compose_domains)) as $serviceName => $service)
<x-forms.button wire:click="generate_preview('{{ $preview->id }}')">Generate <livewire:project.application.previews-compose wire:key="{{ $preview->id }}"
Domain</x-forms.button> :service="$service" :serviceName="$serviceName" :preview="$preview" />
</form> @endforeach
</div>
@else
<form wire:submit="save_preview('{{ $preview->id }}')" class="flex items-end gap-2 pt-4">
<x-forms.input label="Domain" helper="One domain per preview."
id="application.previews.{{ $previewName }}.fqdn"></x-forms.input>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="generate_preview('{{ $preview->id }}')">Generate
Domain</x-forms.button>
</form>
@endif
<div class="flex items-center gap-2 pt-6"> <div class="flex items-center gap-2 pt-6">
<x-forms.button wire:click="deploy({{ data_get($preview, 'pull_request_id') }})">
@if (data_get($preview, 'status') === 'exited')
Deploy
@else
Redeploy
@endif
</x-forms.button>
@if (count($parameters) > 0) @if (count($parameters) > 0)
<a <a
href="{{ route('project.application.deployment.index', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}"> href="{{ route('project.application.deployment.index', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
@ -111,6 +120,27 @@ class="dark:text-warning">{{ $application->destination->server->name }}</span>.<
</a> </a>
@endif @endif
<div class="flex-1"></div> <div class="flex-1"></div>
<x-forms.button wire:click="deploy({{ data_get($preview, 'pull_request_id') }})">
@if (data_get($preview, 'status') === 'exited')
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
@else
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-orange-400"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path
d="M10.09 4.01l.496 -.495a2 2 0 0 1 2.828 0l7.071 7.07a2 2 0 0 1 0 2.83l-7.07 7.07a2 2 0 0 1 -2.83 0l-7.07 -7.07a2 2 0 0 1 0 -2.83l3.535 -3.535h-3.988">
</path>
<path d="M7.05 11.038v-3.988"></path>
</svg> Redeploy
@endif
</x-forms.button>
@if (data_get($preview, 'status') !== 'exited') @if (data_get($preview, 'status') !== 'exited')
<x-modal-confirmation isErrorButton <x-modal-confirmation isErrorButton
action="stop({{ data_get($preview, 'pull_request_id') }})"> action="stop({{ data_get($preview, 'pull_request_id') }})">