mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-15 12:14:07 +00:00
feat(domains): add inline indexing and redirect controls
Move domain indexing and redirect settings into domain management views and include noindex changes in deployment configuration diffs.
This commit is contained in:
1 parent
8c9a7413d0
commit
69ba001e21
22 files changed
+327
-185
No files matched your search
@@ -58,6 +58,42 @@ it('stores a diff between successful deployments', function () {
|
||||
->and(data_get($secondDeployment->configuration_diff, 'changes.0.label'))->toBe('Build command');
|
||||
});
|
||||
|
||||
it('reports noindex domain changes as requiring a redeploy', function () {
|
||||
$application = configurationChangedTestApplication([
|
||||
'fqdn' => 'https://app.example.com,https://staging.example.com',
|
||||
]);
|
||||
$deployment = configurationChangedDeployment($application);
|
||||
$application->markDeploymentConfigurationApplied($deployment);
|
||||
|
||||
$application->setNoindexDomains(['https://staging.example.com']);
|
||||
$application->save();
|
||||
|
||||
$diff = $application->refresh()->pendingDeploymentConfigurationDiff();
|
||||
$change = collect($diff->changes())->firstWhere('key', 'domains.noindex_domains');
|
||||
|
||||
expect($diff->isChanged())->toBeTrue()
|
||||
->and($change)->not->toBeNull()
|
||||
->and($change['label'])->toBe('Search engine indexing')
|
||||
->and($change['impact'])->toBe('redeploy');
|
||||
});
|
||||
|
||||
it('does not flag applications whose older snapshot omitted noindex domains', function () {
|
||||
$application = configurationChangedTestApplication([
|
||||
'fqdn' => 'https://app.example.com',
|
||||
]);
|
||||
$deployment = configurationChangedDeployment($application);
|
||||
$application->markDeploymentConfigurationApplied($deployment);
|
||||
|
||||
$snapshot = $deployment->refresh()->configuration_snapshot;
|
||||
$snapshot['sections']['domains']['items'] = collect($snapshot['sections']['domains']['items'])
|
||||
->reject(fn (array $item): bool => $item['key'] === 'noindex_domains')
|
||||
->values()
|
||||
->all();
|
||||
$deployment->update(['configuration_snapshot' => $snapshot]);
|
||||
|
||||
expect($application->refresh()->pendingDeploymentConfigurationDiff()->isChanged())->toBeFalse();
|
||||
});
|
||||
|
||||
it('checks legacy preview deployment configuration hash using preview environment variable query', function () {
|
||||
$application = configurationChangedTestApplication();
|
||||
|
||||
|
||||
@@ -271,6 +271,8 @@ it('updates a domain in place via modal', function () {
|
||||
->call('startEdit', 0)
|
||||
->assertSet('showEditDomainModal', true)
|
||||
->assertSet('editingDomain', 'https://old.example.com')
|
||||
->assertSee('Direction')
|
||||
->assertSee('Search engine indexing')
|
||||
->set('editingDomain', 'https://new.example.com')
|
||||
->call('updateDomain')
|
||||
->assertHasNoErrors()
|
||||
@@ -375,7 +377,8 @@ it('auto-adds missing www counterpart as a normal domain when setting www redire
|
||||
->assertSet('domainRows.0.is_suggested', false)
|
||||
->assertSet('domainRows.1.is_suggested', false)
|
||||
->assertSet('domainRows.0.url', 'https://example.com')
|
||||
->assertSet('domainRows.1.url', 'https://www.example.com');
|
||||
->assertSet('domainRows.1.url', 'https://www.example.com')
|
||||
->assertSet('domainRows.1.checked_at', fn (?string $checkedAt): bool => filled($checkedAt));
|
||||
|
||||
$this->application->refresh();
|
||||
|
||||
@@ -1216,9 +1219,8 @@ it('uses the compact service domains layout for compose applications', function
|
||||
expect($view)
|
||||
->toContain('application-compose-domain-group-{{ $redirectWireKey }}')
|
||||
->toContain('class="application-settings-section-body mt-1 scroll-mt-28')
|
||||
->toContain('aria-label="Redirect direction for {{ $serviceName }}"')
|
||||
->toContain('wire:target="serviceRedirects.{{ $redirectWireKey }}"')
|
||||
->not->toContain('wire:target="serviceRedirects.{{ $redirectWireKey }},setServiceRedirect"')
|
||||
->toContain('htmlId="application-compose-domain-redirect-{{ $redirectWireKey }}"')
|
||||
->not->toContain('aria-label="Redirect direction for {{ $serviceName }}"')
|
||||
->not->toContain('title="No domains for this service"');
|
||||
});
|
||||
|
||||
@@ -1227,6 +1229,8 @@ it('provides client-side search for compose service domains', function () {
|
||||
|
||||
expect($view)
|
||||
->toContain('x-model="domainSearch"')
|
||||
->toContain('class="ml-auto flex flex-wrap items-center gap-2"')
|
||||
->toContain('<div class="relative shrink-0">')
|
||||
->toContain('placeholder="Search services or domains"')
|
||||
->toContain('x-show="matchesDomainSearch(')
|
||||
->toContain('title="No domains found"')
|
||||
@@ -1326,3 +1330,20 @@ it('uses compose service redirect for suggested domain messaging when direction
|
||||
->and($suggested['suggestion_role'] ?? null)->toBe('pair')
|
||||
->and($suggested['url'] ?? null)->toBe('https://www.web.example.com');
|
||||
});
|
||||
|
||||
it('updates search engine indexing from the domains view', function () {
|
||||
$this->application->update(['fqdn' => 'https://app.example.com,https://staging.example.com']);
|
||||
|
||||
Livewire::test(Domains::class, ['application' => $this->application->fresh()])
|
||||
->assertSee('Noindex')
|
||||
->assertSee('Indexable')
|
||||
->assertSee('x-model="localIndexing"', false)
|
||||
->assertDontSee('@change="$wire.toggleNoindexDomain', false)
|
||||
->assertDontSee('@js(', false)
|
||||
->call('toggleNoindexDomain', 'https://staging.example.com', 'noindex')
|
||||
->assertDispatched('configurationChanged')
|
||||
->assertDispatched('success');
|
||||
|
||||
expect($this->application->refresh()->noindexDomains()->all())
|
||||
->toBe(['https://staging.example.com']);
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Project\Application\General;
|
||||
use App\Livewire\Project\Application\Domains;
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
@@ -84,7 +84,7 @@ describe('Application noindex domains', function () {
|
||||
expect($application->isDomainNoindexed('https://prod.example.com'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('the Livewire toggle persists the flag', function () {
|
||||
test('the domains view toggle persists the flag', function () {
|
||||
InstanceSettings::unguarded(function () {
|
||||
InstanceSettings::updateOrCreate(['id' => 0], []);
|
||||
});
|
||||
@@ -107,10 +107,9 @@ describe('Application noindex domains', function () {
|
||||
'redirect' => 'no',
|
||||
]);
|
||||
|
||||
Livewire::test(General::class, ['application' => $application])
|
||||
Livewire::test(Domains::class, ['application' => $application])
|
||||
->assertSuccessful()
|
||||
->set('noindexDomains', ['https://staging.example.com'])
|
||||
->call('updateNoindexDomains')
|
||||
->call('toggleNoindexDomain', 'https://staging.example.com', true)
|
||||
->assertDispatched('success');
|
||||
|
||||
expect($application->refresh()->noindexDomains()->all())
|
||||
|
||||
@@ -159,6 +159,26 @@ it('shows domain changes when the domain page dispatches a configuration change'
|
||||
->assertSee('https://changed.example.com');
|
||||
});
|
||||
|
||||
it('shows noindex changes when the domains page dispatches a configuration change', function () {
|
||||
$application = configurationCheckerApplication($this->environment, [
|
||||
'fqdn' => 'https://example.com,https://staging.example.com',
|
||||
]);
|
||||
markConfigurationCheckerApplicationDeployed($application);
|
||||
|
||||
$component = Livewire::test(ConfigurationChecker::class, ['resource' => $application->refresh()])
|
||||
->assertSet('isConfigurationChanged', false);
|
||||
|
||||
$application->setNoindexDomains(['https://staging.example.com']);
|
||||
$application->save();
|
||||
|
||||
$component
|
||||
->dispatch('configurationChanged')
|
||||
->assertSet('isConfigurationChanged', true)
|
||||
->assertSee('The latest configuration has not been applied')
|
||||
->assertSee('Search engine indexing')
|
||||
->assertSee('Redeploy to apply.');
|
||||
});
|
||||
|
||||
it('shows an unapplied configuration warning after a directory mount is added', function () {
|
||||
$application = configurationCheckerApplication($this->environment);
|
||||
markConfigurationCheckerApplicationDeployed($application);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Project\Service\Domains;
|
||||
use App\Livewire\Project\Service\EditDomain;
|
||||
use App\Models\Environment;
|
||||
use App\Models\Project;
|
||||
@@ -77,9 +78,8 @@ it('loads the EditDomain component with required port', function () {
|
||||
it('marks noindex changes as pending configuration', function () {
|
||||
$this->service->isConfigurationChanged(save: true);
|
||||
|
||||
Livewire::test(EditDomain::class, ['applicationId' => $this->serviceApplication->id])
|
||||
->set('noindexDomains', ['http://example.com:8000'])
|
||||
->call('updateNoindexDomains')
|
||||
Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])])
|
||||
->call('toggleNoindexDomain', $this->serviceApplication->id, 'http://example.com:8000', true)
|
||||
->assertDispatched('configurationChanged');
|
||||
|
||||
expect($this->service->refresh()->isConfigurationChanged())->toBeTrue();
|
||||
|
||||
@@ -90,7 +90,7 @@ beforeEach(function () {
|
||||
]);
|
||||
});
|
||||
|
||||
it('groups configured domains with their service redirect and excludes services without domains', function () {
|
||||
it('groups configured domains and shows redirect settings in the edit modal', function () {
|
||||
$this->apiApp->update([
|
||||
'fqdn' => 'https://api.example.com,https://admin.example.com',
|
||||
]);
|
||||
@@ -100,18 +100,22 @@ it('groups configured domains with their service redirect and excludes services
|
||||
->assertSee('API')
|
||||
->assertSee('https://api.example.com')
|
||||
->assertSee('https://admin.example.com')
|
||||
->call('startEdit', 0)
|
||||
->html();
|
||||
|
||||
expect($html)
|
||||
->toContain("service-domain-group-{$this->apiApp->id}")
|
||||
->toContain("id=\"service-domain-redirect-{$this->apiApp->id}-trigger\"")
|
||||
->toContain("id=\"edit-service-domain-redirect-{$this->apiApp->id}-trigger\"")
|
||||
->toContain("serviceRedirects.{$this->apiApp->id}")
|
||||
->toContain('class="listbox-trigger"')
|
||||
->toContain('application-settings-section-body is-flush mt-1 w-full scroll-mt-28 overflow-visible')
|
||||
->not->toContain("<select id=\"service-domain-redirect-{$this->apiApp->id}\"")
|
||||
->not->toContain("service-domain-redirect-toggle-{$this->apiApp->id}")
|
||||
->toContain('dark:bg-white/[0.04]')
|
||||
->toContain('<span>Domain</span>')
|
||||
->toContain('<span>DNS</span>')
|
||||
->toContain('<span>Last checked</span>')
|
||||
->not->toContain("service-domain-group-{$this->webApp->id}")
|
||||
->and(substr_count($html, '2 domains'))->toBe(1)
|
||||
->and(strpos($html, '>API</span>'))->toBeLessThan(strpos($html, '<span>Domain</span>'))
|
||||
->and(substr_count($html, "id=\"service-domain-group-{$this->apiApp->id}\""))->toBe(1);
|
||||
});
|
||||
|
||||
@@ -194,6 +198,7 @@ it('saves the explicitly selected service redirect value', function () {
|
||||
->call('updateServiceRedirect', $this->webApp->id, 'www')
|
||||
->assertDispatched('success')
|
||||
->assertSet('domainRows', fn (array $rows): bool => collect($rows)->pluck('url')->contains('https://www.web.example.com'))
|
||||
->assertSet('domainRows', fn (array $rows): bool => filled(collect($rows)->firstWhere('url', 'https://www.web.example.com')['checked_at'] ?? null))
|
||||
->assertSee('https://www.web.example.com');
|
||||
|
||||
expect($this->webApp->fresh()->redirect)->toBe('www');
|
||||
@@ -270,6 +275,8 @@ it('provides client-side search for service domains', function () {
|
||||
|
||||
expect($view)
|
||||
->toContain('x-model="domainSearch"')
|
||||
->toContain('class="ml-auto flex flex-wrap items-center gap-2"')
|
||||
->toContain('<div class="relative shrink-0">')
|
||||
->toContain('placeholder="Search services or domains"')
|
||||
->toContain('x-show="matchesDomainSearch(')
|
||||
->toContain('title="No domains found"')
|
||||
@@ -343,6 +350,8 @@ it('prunes the previous dns status when a service domain is renamed', function (
|
||||
|
||||
Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])])
|
||||
->call('startEdit', 0)
|
||||
->assertSee('Direction')
|
||||
->assertSee('Search engine indexing')
|
||||
->set('editingDomain', 'https://renamed.example.com')
|
||||
->call('updateDomain')
|
||||
->assertHasNoErrors()
|
||||
@@ -357,6 +366,15 @@ it('prunes the previous dns status when a service domain is renamed', function (
|
||||
->and($this->apiApp->domain_dns_statuses['https://renamed.example.com']['status'])->toBe('skipped');
|
||||
});
|
||||
|
||||
it('only shows the domain notification when redirect changes through edit domain', function () {
|
||||
Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])])
|
||||
->call('startEdit', 0)
|
||||
->set('editingDirection', 'www')
|
||||
->call('updateDomain')
|
||||
->assertDispatched('success', 'Domain updated.')
|
||||
->assertNotDispatched('success', 'Redirect updated.');
|
||||
});
|
||||
|
||||
it('does not restore stale dns status when a removed service domain is re-added', function () {
|
||||
$this->apiApp->update([
|
||||
'domain_dns_statuses' => [
|
||||
@@ -509,3 +527,22 @@ it('exposes the stack domains route', function () {
|
||||
->assertSeeLivewire(Domains::class)
|
||||
->assertSee('Domains');
|
||||
});
|
||||
|
||||
it('updates search engine indexing from the service domains view', function () {
|
||||
Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])])
|
||||
->assertSee('Noindex')
|
||||
->assertSee('Indexable')
|
||||
->assertSee('x-model="localIndexing"', false)
|
||||
->assertDontSee('@change="$wire.toggleNoindexDomain', false)
|
||||
->assertDontSee('@change="$wire.updateServiceRedirect', false)
|
||||
->assertDontSee('@js(', false)
|
||||
->call('toggleNoindexDomain', $this->apiApp->id, 'https://api.example.com', 'noindex')
|
||||
->assertDispatched('configurationChanged')
|
||||
->assertDispatched('success');
|
||||
|
||||
expect($this->apiApp->refresh()->noindexDomains()->all())
|
||||
->toBe(['https://api.example.com']);
|
||||
|
||||
expect(file_get_contents(resource_path('views/livewire/project/service/partials/domain-table.blade.php')))
|
||||
->not->toContain('<select');
|
||||
});
|
||||
Reference in new issue
Block a user