fix(ui): prefer Compose env values and polish error pages
When a managed variable shares a Compose-defined key, show the hardcoded Compose value as read-only and exclude it from managed rows. Render Contact support as a button, use the collapsible component for 419 proxy help, and style its list/code markers.
This commit is contained in:
parent
47da47d8e0
commit
e50108a905
8 changed files with 78 additions and 24 deletions
|
|
@ -512,6 +512,11 @@ private function managedEnvironmentVariablesQuery(bool $isPreview): Builder
|
|||
->where('resourceable_id', $this->resource->id)
|
||||
->where('is_preview', $isPreview);
|
||||
|
||||
$hardcodedKeys = $this->hardcodedEnvironmentVariableKeys();
|
||||
if ($hardcodedKeys !== []) {
|
||||
$query->whereNotIn('key', $hardcodedKeys);
|
||||
}
|
||||
|
||||
if ($this->serviceFilters !== []) {
|
||||
$query->whereRaw('1 = 0');
|
||||
}
|
||||
|
|
@ -716,18 +721,6 @@ protected function getHardcodedVariables(bool $isPreview)
|
|||
return ! str($key)->startsWith(['SERVICE_FQDN_', 'SERVICE_URL_', 'SERVICE_NAME_']);
|
||||
});
|
||||
|
||||
// Filter out variables that exist in database (user has overridden/managed them)
|
||||
// For preview, check against preview variables; for production, check against production variables
|
||||
if ($isPreview) {
|
||||
$managedKeys = $this->resource->environment_variables_preview()->pluck('key')->toArray();
|
||||
} else {
|
||||
$managedKeys = $this->resource->environment_variables()->where('is_preview', false)->pluck('key')->toArray();
|
||||
}
|
||||
|
||||
$hardcodedVars = $hardcodedVars->filter(function ($var) use ($managedKeys) {
|
||||
return ! in_array($var['key'], $managedKeys);
|
||||
});
|
||||
|
||||
if ($this->searchTerm() !== '') {
|
||||
$hardcodedVars = $hardcodedVars->filter(function ($var) {
|
||||
return str($var['key'])->contains($this->searchTerm(), true);
|
||||
|
|
@ -749,6 +742,27 @@ protected function getHardcodedVariables(bool $isPreview)
|
|||
return $hardcodedVars;
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function hardcodedEnvironmentVariableKeys(): array
|
||||
{
|
||||
if (! $this->showsHardcodedEnvironmentVariables()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dockerComposeRaw = $this->resource->docker_compose_raw ?? $this->resource->docker_compose;
|
||||
|
||||
if (blank($dockerComposeRaw)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return extractHardcodedEnvironmentVariables($dockerComposeRaw)
|
||||
->pluck('key')
|
||||
->reject(fn (string $key): bool => str($key)->startsWith(['SERVICE_FQDN_', 'SERVICE_URL_', 'SERVICE_NAME_']))
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function getDevView()
|
||||
{
|
||||
$this->variables = $this->formatEnvironmentVariables($this->getEnvironmentVariables(false, false));
|
||||
|
|
|
|||
|
|
@ -1207,7 +1207,8 @@ .error-extra details summary:hover {
|
|||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.error-extra details ul {
|
||||
.error-extra details ul,
|
||||
.error-proxy-help ul {
|
||||
margin: 0.5rem 0 0;
|
||||
padding-left: 1.125rem;
|
||||
display: flex;
|
||||
|
|
@ -1215,7 +1216,8 @@ .error-extra details ul {
|
|||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.error-extra details code {
|
||||
.error-extra details code,
|
||||
.error-proxy-help code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.75rem;
|
||||
padding: 0.05rem 0.3rem;
|
||||
|
|
|
|||
|
|
@ -55,10 +55,11 @@
|
|||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="error-contact-link"
|
||||
href="{{ config('constants.urls.contact') }}">
|
||||
Contact support
|
||||
<x-external-link class="inline-flex size-3 text-current" />
|
||||
<x-forms.button type="button">
|
||||
Contact support
|
||||
<x-external-link class="inline-flex size-3 text-current" />
|
||||
</x-forms.button>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@
|
|||
:show-dashboard="false"
|
||||
primary-href="/login"
|
||||
primary-label="Back to login">
|
||||
<details>
|
||||
<summary>Using a reverse proxy or Cloudflare Tunnel?</summary>
|
||||
<x-forms.collapsible title="Using a reverse proxy or Cloudflare Tunnel?" class="error-proxy-help">
|
||||
<ul>
|
||||
<li>Set your domain in <strong>Settings → FQDN</strong> to match the URL you use to access Coolify.</li>
|
||||
<li>Cloudflare users: disable <strong>Browser Integrity Check</strong> and <strong>Under Attack Mode</strong> for your Coolify domain, as these can interrupt login sessions.</li>
|
||||
<li>If you can still access Coolify via <code>localhost</code>, log in there first to configure your FQDN.</li>
|
||||
</ul>
|
||||
</details>
|
||||
</x-forms.collapsible>
|
||||
</x-error-page>
|
||||
@livewireScripts
|
||||
</body>
|
||||
@endsection
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
}">
|
||||
<form wire:submit='submit' class="application-settings-form flex flex-col">
|
||||
<x-unsaved-bar action="submit" />
|
||||
{{-- Temporarily hidden: the "Compose parser" dev hint and the "View details"
|
||||
resource-details modal trigger. --}}
|
||||
<div class="application-settings-grid flex flex-col gap-6">
|
||||
<x-application.settings-section id="application-details-section" title="Application details" helper="Name the application and choose the build strategy Coolify should use to deploy it." class="application-details-card">
|
||||
@if ($buildPack === 'dockercompose')
|
||||
|
|
|
|||
|
|
@ -180,8 +180,6 @@ class="listbox-panel top-full! left-0! right-0! mt-1! w-full! min-w-0!" role="me
|
|||
<div
|
||||
class="resource-heading-navbar application-heading-actions flex w-full min-w-0 items-center justify-start gap-1 overflow-visible xl:w-auto xl:justify-end">
|
||||
<div class="resource-heading-actions flex shrink-0 items-center gap-0.5">
|
||||
{{-- Status badge temporarily hidden — will be redesigned later:
|
||||
<x-status.index :resource="$application" :title="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" /> --}}
|
||||
@if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
|
||||
<span class="px-2 text-[13px] text-neutral-500 dark:text-fg-dim">Load a Compose file to deploy.</span>
|
||||
@else
|
||||
|
|
|
|||
|
|
@ -182,6 +182,34 @@
|
|||
->toBe(['API_TOKEN']);
|
||||
});
|
||||
|
||||
it('shows a Compose-defined value as read-only when a managed variable has the same key', function () {
|
||||
$service = Service::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'docker_compose_raw' => <<<'YAML'
|
||||
services:
|
||||
app:
|
||||
image: nginx
|
||||
environment:
|
||||
- API_TOKEN=from-compose
|
||||
YAML,
|
||||
]);
|
||||
|
||||
EnvironmentVariable::create([
|
||||
'key' => 'API_TOKEN',
|
||||
'value' => 'from-environment-tab',
|
||||
'resourceable_type' => Service::class,
|
||||
'resourceable_id' => $service->id,
|
||||
]);
|
||||
|
||||
$component = Livewire::test(All::class, ['resource' => $service])
|
||||
->call('loadEnvironmentVariables');
|
||||
|
||||
expect($component->instance()->environmentVariablePageRows)
|
||||
->toHaveCount(1)
|
||||
->and($component->instance()->environmentVariablePageRows->first()['kind'])->toBe('hardcoded')
|
||||
->and($component->instance()->environmentVariablePageRows->first()['environmentVariable']['value'])->toBe('from-compose');
|
||||
});
|
||||
|
||||
it('searches service environment variables without requiring preview variables', function () {
|
||||
$service = Service::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
|
|
|
|||
|
|
@ -57,9 +57,22 @@
|
|||
->toContain('/login')
|
||||
->toContain('Back to login')
|
||||
->toContain('Using a reverse proxy or Cloudflare Tunnel?')
|
||||
->toContain('x-data="{ open: false }"')
|
||||
->toContain('x-on:click="open = !open"')
|
||||
->toContain('livewire.js')
|
||||
->not->toContain('>Dashboard</');
|
||||
});
|
||||
|
||||
it('renders contact support as a button', function () {
|
||||
$exception = new HttpException(404, 'Not found');
|
||||
|
||||
$html = view('errors.404', ['exception' => $exception])->render();
|
||||
|
||||
expect($html)
|
||||
->toContain('href="'.config('constants.urls.contact').'"')
|
||||
->toMatch('/<button[^>]*>\s*Contact support/s');
|
||||
});
|
||||
|
||||
it('shows purified exception message on 500 page', function () {
|
||||
$exception = new RuntimeException('Database connection failed');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue