Merge remote-tracking branch 'origin/next' into audit-policies

This commit is contained in:
Andras Bacsai committed 2026-06-04 10:18:54 +02:00
commit 9dca7ca351
148 files changed
+5987 -1101

No files matched your search

@@ -1,6 +1,6 @@
<div>
@if ($isConfigurationChanged && !is_null($resource->config_hash) && !$resource->isExited())
<div x-data="{ configurationDiffModalOpen: false }">
<div x-data="{ configurationDiffModalOpen: false, expandedRows: {} }">
<x-popup-small>
<x-slot:title>
The latest configuration has not been applied
@@ -43,38 +43,69 @@
@endif
</div>
@if ($view === 'normal')
<div>
<h3>Production Environment Variables</h3>
<div>Environment (secrets) variables for Production.</div>
</div>
@forelse ($this->environmentVariables as $env)
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" :env="$env"
:type="$resource->type()" />
@empty
<div>No environment variables found.</div>
@endforelse
@if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariables->isNotEmpty())
@foreach ($this->hardcodedEnvironmentVariables as $index => $env)
<livewire:project.shared.environment-variable.show-hardcoded
wire:key="hardcoded-prod-{{ $env['key'] }}-{{ $env['service_name'] ?? 'default' }}-{{ $index }}"
:env="$env" />
@endforeach
@endif
@if ($resource->type() === 'application' && $resource->environment_variables_preview->count() > 0 && $showPreview)
<div>
<h3>Preview Deployments Environment Variables</h3>
<div>Environment (secrets) variables for Preview Deployments.</div>
<div class="w-full md:w-96">
<div class="relative">
<input type="search" placeholder="Search" aria-label="Search environment variables"
wire:model.live.debounce.300ms="search" class="w-full input pl-10" />
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<div class="relative w-4 h-4">
<svg wire:loading.remove wire:target="search" aria-hidden="true"
class="absolute inset-0 w-4 h-4 dark:text-neutral-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<svg wire:loading wire:target="search" aria-hidden="true"
class="absolute inset-0 w-4 h-4 text-coollabs dark:text-warning animate-spin" fill="none"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4" />
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
</div>
</div>
</div>
@foreach ($this->environmentVariablesPreview as $env)
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" :env="$env"
:type="$resource->type()" />
@endforeach
@if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariablesPreview->isNotEmpty())
@foreach ($this->hardcodedEnvironmentVariablesPreview as $index => $env)
<livewire:project.shared.environment-variable.show-hardcoded
wire:key="hardcoded-preview-{{ $env['key'] }}-{{ $env['service_name'] ?? 'default' }}-{{ $index }}"
:env="$env" />
</div>
@if ($this->isSearchActive && ! $this->hasEnvironmentVariables)
<div>No environment variables found.</div>
@else
@if ($this->environmentVariables->isNotEmpty() || $this->hardcodedEnvironmentVariables->isNotEmpty())
<div>
<h3>Production Environment Variables</h3>
<div>Environment (secrets) variables for Production.</div>
</div>
@foreach ($this->environmentVariables as $env)
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" :env="$env"
:type="$resource->type()" />
@endforeach
@if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariables->isNotEmpty())
@foreach ($this->hardcodedEnvironmentVariables as $index => $env)
<livewire:project.shared.environment-variable.show-hardcoded
wire:key="hardcoded-prod-{{ $env['key'] }}-{{ $env['service_name'] ?? 'default' }}-{{ $index }}" :env="$env" />
@endforeach
@endif
@endif
@if (
$resource->type() === 'application' &&
$showPreview &&
($this->environmentVariablesPreview->isNotEmpty() || $this->hardcodedEnvironmentVariablesPreview->isNotEmpty())
)
<div>
<h3>Preview Deployments Environment Variables</h3>
<div>Environment (secrets) variables for Preview Deployments.</div>
</div>
@foreach ($this->environmentVariablesPreview as $env)
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}" :env="$env"
:type="$resource->type()" />
@endforeach
@if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariablesPreview->isNotEmpty())
@foreach ($this->hardcodedEnvironmentVariablesPreview as $index => $env)
<livewire:project.shared.environment-variable.show-hardcoded
wire:key="hardcoded-preview-{{ $env['key'] }}-{{ $env['service_name'] ?? 'default' }}-{{ $index }}"
:env="$env" />
@endforeach
@endif
@endif
@endif
@else
@@ -88,8 +119,9 @@
label="Production Environment Variables"></x-forms.textarea>
@if ($showPreview)
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans" label="Preview Deployments Environment Variables"
id="variablesPreview" wire:model="variablesPreview"></x-forms.textarea>
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans"
label="Preview Deployments Environment Variables" id="variablesPreview"
wire:model="variablesPreview"></x-forms.textarea>
@endif
<x-forms.button type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
@@ -98,8 +130,9 @@
label="Production Environment Variables" disabled></x-forms.textarea>
@if ($showPreview)
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans" label="Preview Deployments Environment Variables"
id="variablesPreview" wire:model="variablesPreview" disabled></x-forms.textarea>
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans"
label="Preview Deployments Environment Variables" id="variablesPreview" wire:model="variablesPreview"
disabled></x-forms.textarea>
@endif
@endcan
</form>
@@ -139,10 +139,6 @@
const range = selection.getRangeAt(0);
return logsContainer.contains(range.commonAncestorContainer);
},
decodeHtml(text) {
const doc = new DOMParser().parseFromString(text, 'text/html');
return doc.documentElement.textContent;
},
applySearch() {
const logs = document.getElementById('logs');
if (!logs) return;
@@ -163,7 +159,7 @@
// Update highlighting
if (textSpan) {
const originalText = this.decodeHtml(textSpan.dataset.lineText || '');
const originalText = textSpan.dataset.lineText || '';
if (!query) {
textSpan.textContent = originalText;
} else if (matches) {
@@ -346,8 +342,17 @@
<button
x-on:click="
$wire.copyLogs().then(logs => {
navigator.clipboard.writeText(logs);
Livewire.dispatch('success', ['Logs copied to clipboard.']);
if (!navigator.clipboard?.writeText) {
Livewire.dispatch('error', ['Clipboard is not available. Please use HTTPS or localhost.']);
return;
}
navigator.clipboard.writeText(logs).then(() => {
Livewire.dispatch('success', ['Logs copied to clipboard.']);
}).catch(() => {
Livewire.dispatch('error', ['Failed to copy logs to clipboard.']);
});
}).catch(() => {
Livewire.dispatch('error', ['Failed to prepare logs for clipboard.']);
});
"
title="Copy Logs"
@@ -523,20 +528,19 @@
// Parse timestamp from log line (ISO 8601 format: 2025-12-04T11:48:39.136764033Z)
$timestamp = '';
$logContent = $line;
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2}:\d{2})(?:\.(\d+))?Z?\s(.*)$/', $line, $matches)) {
$year = $matches[1];
$month = $matches[2];
$day = $matches[3];
$time = $matches[4];
$microseconds = isset($matches[5]) ? substr($matches[5], 0, 6) : '000000';
$logContent = $matches[6];
if (preg_match('/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d+))?Z?\s(.*)$/', $line, $matches)) {
$microseconds = isset($matches[2]) ? substr($matches[2], 0, 6) : '000000';
$logContent = $matches[3];
// Convert month number to abbreviated name
$monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
$monthName = $monthNames[(int)$month - 1] ?? $month;
// Format for display: 2025-Dec-04 09:44:58
$timestamp = "{$year}-{$monthName}-{$day} {$time}";
// Convert UTC Docker timestamp to server timezone for display
$carbonTs = \Carbon\Carbon::parse($matches[1], 'UTC');
$serverTz = getServerTimezone($server);
try {
$carbonTs->setTimezone($serverTz);
} catch (\Exception) {
// keep UTC
}
$timestamp = $carbonTs->format('Y-M-d H:i:s');
// Include microseconds in key for uniqueness
$lineKey = "{$timestamp}.{$microseconds}";
}
@@ -5,13 +5,19 @@
<div class="pb-4">Basic metrics for your application container.</div>
<div>
@if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose')
<div class="alert alert-warning">Metrics are not available for Docker Compose applications yet!</div>
<x-callout type="warning" title="Not Available">
Metrics are not available for Docker Compose applications yet!
</x-callout>
@elseif(!$resource->destination->server->isMetricsEnabled())
<div class="alert alert-warning pb-1">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
<div>Go to <a class="underline dark:text-white" href="{{ route('server.show', $resource->destination->server->uuid) }}/sentinel" {{ wireNavigate() }}>Server settings</a> to enable it.</div>
<x-callout type="info" title="Metrics Not Enabled">
Metrics are only available for servers with Sentinel & Metrics enabled.
Go to <a class="underline font-semibold" href="{{ route('server.metrics', ['server_uuid' => $resource->destination->server->uuid]) }}" {{ wireNavigate() }}>Server Metrics</a> to enable it.
</x-callout>
@else
@if (!str($resource->status)->contains('running'))
<div class="alert alert-warning">Metrics are only available when the application container is running!</div>
<x-callout type="warning" title="Container Not Running">
Metrics are only available when the application container is running!
</x-callout>
@else
<div>
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
@@ -0,0 +1,57 @@
<div class="w-full max-h-[70vh] overflow-y-auto pr-1 -mt-4">
<div class="pb-4 text-sm dark:text-neutral-400">Identifiers for this resource. Read-only</div>
<div class="flex flex-col gap-6">
<div>
<h3>Resource</h3>
<div class="pt-2 grid grid-cols-1 gap-3 md:grid-cols-2">
<x-forms.copy-button label="Name" :text="$resource->name ?? ''" />
<x-forms.copy-button label="UUID" :text="$resource->uuid ?? ''" />
</div>
</div>
@if ($environment_uuid)
<div>
<h3>Environment</h3>
<div class="pt-2 grid grid-cols-1 gap-3 md:grid-cols-2">
<x-forms.copy-button label="Name" :text="$environment_name ?? ''" />
<x-forms.copy-button label="UUID" :text="$environment_uuid" />
</div>
</div>
@endif
@if ($project_uuid)
<div>
<h3>Project</h3>
<div class="pt-2 grid grid-cols-1 gap-3 md:grid-cols-2">
<x-forms.copy-button label="Name" :text="$project_name ?? ''" />
<x-forms.copy-button label="UUID" :text="$project_uuid" />
</div>
</div>
@endif
@if ($server_uuid)
<div>
<h3>Server</h3>
<div class="pt-2 grid grid-cols-1 gap-3 md:grid-cols-2">
<x-forms.copy-button label="Name" :text="$server_name ?? ''" />
<x-forms.copy-button label="UUID" :text="$server_uuid" />
</div>
</div>
@endif
@if (! empty($stack_applications) || ! empty($stack_databases))
<div>
<h3>Stack Sub-Resources</h3>
<div class="pt-2 grid grid-cols-1 gap-3 md:grid-cols-2">
@foreach ($stack_applications as $item)
<x-forms.copy-button :label="'Application — ' . $item['name']" :text="$item['uuid']" />
@endforeach
@foreach ($stack_databases as $item)
<x-forms.copy-button :label="'Database — ' . $item['name']" :text="$item['uuid']" />
@endforeach
</div>
</div>
@endif
</div>
</div>