@php // Threshold tone for gauges/bars: >=90 offline, >=75 warning, else online. $tone = fn (int $v): string => $v >= 90 ? 'offline' : ($v >= 75 ? 'warning' : 'online'); $barColor = ['online' => 'bg-online', 'warning' => 'bg-warning', 'offline' => 'bg-offline']; $statusLabel = ['online' => __('servers.status_online'), 'warning' => __('servers.status_warning'), 'offline' => __('servers.status_offline'), 'pending' => __('servers.status_pending')][$server->status] ?? ucfirst($server->status); $specs = $server->specs ?? []; $cores = $specs['cores'] ?? null; $ramGb = $specs['ram_gb'] ?? null; $diskGb = $specs['disk_gb'] ?? null; $ramUsed = $ramGb !== null ? round((float) $ramGb * $server->mem / 100, 1) : null; $diskUsed = $diskGb !== null ? (int) round((float) $diskGb * $server->disk / 100) : null; $specRows = [ [__('servers.spec_arch'), $specs['arch'] ?? '—'], [__('servers.spec_kernel'), $specs['kernel'] ?? '—'], [__('servers.spec_virt'), $specs['virt'] ?? '—'], [__('servers.spec_cores'), $cores ?? '—'], [__('servers.spec_ram'), $ramGb !== null ? __('servers.gb', ['value' => $ramGb]) : '—'], [__('servers.spec_disk'), $diskGb !== null ? __('servers.gb', ['value' => $diskGb]) : '—'], ]; // Detected OS tooling (populated once the live profile loads). if (! empty($os)) { $pm = ($os['packageManager'] ?? 'none') !== 'none' ? $os['packageManager'] : '—'; $fw = ($os['firewallTool'] ?? 'none') !== 'none' ? $os['firewallTool'] : '—'; $specRows[] = [__('servers.spec_system'), $os['familyLabel'] ?? '—']; $specRows[] = [__('servers.spec_package_manager'), $pm]; $specRows[] = [__('servers.spec_firewall'), $fw]; } $vitals = [ ['label' => __('servers.vital_cpu'), 'icon' => 'cpu', 'val' => (int) $server->cpu, 'sub' => ($loadAvg > 0 ? __('servers.load', ['value' => number_format($loadAvg, 2)]).' · ' : '').($cores ? __('servers.cores_count', ['count' => $cores]) : '—')], ['label' => __('servers.vital_ram'), 'icon' => 'activity', 'val' => (int) $server->mem, 'sub' => $ramUsed !== null ? __('servers.ram_used_of_total', ['used' => $ramUsed, 'total' => $ramGb]) : '—'], ['label' => __('servers.vital_disk'), 'icon' => 'server', 'val' => (int) $server->disk, 'sub' => $diskUsed !== null ? __('servers.disk_used_of_total', ['used' => $diskUsed, 'total' => $diskGb]) : '—'], ]; @endphp
{{ __('servers.back_to_fleet') }} {{-- Hero header --}}
$server->status === 'online', 'border-warning/30 bg-warning/10 text-warning' => $server->status === 'warning', 'border-offline/30 bg-offline/10 text-offline' => $server->status === 'offline', 'border-cyan/30 bg-cyan/10 text-cyan' => $server->status === 'pending', ])>

{{ $server->name }}

{{ $statusLabel }}

{{ $server->hostname }} · {{ $server->ip }} · {{ $server->os }} · {{ __('servers.uptime', ['value' => $server->uptime ?: '—']) }} · {{ __('servers.last_seen', ['value' => optional($server->last_seen_at)->diffForHumans() ?? '—']) }}

{{ __('servers.system_updates') }} {{ __('servers.files') }}
@if ($ready && ! $connected)

{{ __('servers.offline_banner') }}

@endif {{-- SSH-Zugang (Credential) --}} @php $cred = $server->credential; $credStatus = $cred ? ($cred->disabled ? 'offline' : 'online') : null; $credStatusLabel = $cred ? ($cred->disabled ? __('servers.cred_locked') : __('servers.cred_active')) : null; $credAuthLabel = $cred ? ($cred->auth_type === 'key' ? __('servers.cred_auth_key') : __('servers.cred_auth_password')) : null; @endphp @if ($cred)
! $cred->disabled, 'border-offline/30 bg-offline/10 text-offline' => $cred->disabled, ])>

{{ $cred->name ?: $cred->username }}

{{ $credStatusLabel }} {{ $credAuthLabel }}

{{ $cred->username }}@{{ $server->ip }}

{{ __('common.edit') }} {{ $cred->disabled ? __('common.unlock') : __('common.lock') }} {{ __('common.delete') }}
@else

{{ __('servers.cred_none_title') }}

{{ __('servers.cred_none_hint') }}

{{ __('servers.cred_deposit') }}
@endif
{{-- Vitals: live gauges, full width, dense --}}
@foreach ($vitals as $v)

{{ $v['label'] }}

{{ $v['sub'] }}

@endforeach
@if ($ready) {{-- Spezifikationen + Sicherheit --}}
@foreach ($specRows as [$key, $val])
{{ $key }}
{{ $val }}
@endforeach
@foreach ($hardening as $check) @php $supported = $check['supported'] ?? true; // Neutral rows (auto-updates) are a preference, not a security // verdict: muted glyph + aktiv/inaktiv, never SICHER/OFFEN. $neutral = $check['neutral'] ?? false; @endphp {{-- One calm state signal: a lock glyph (geschlossen = sicher, offen = offen); unsupported/neutral items render muted, no warning tone. --}}
$supported && ! $neutral && $check['secure'], 'border-warning/25 bg-warning/10 text-warning' => $supported && ! $neutral && ! $check['secure'], 'border-line bg-raised text-ink-4' => ! $supported || $neutral, ])>

{{ $check['label'] }} @if (! $supported) {{ __('common.unsupported') }} @elseif ($neutral) {{ $check['featureOn'] ? __('common.active') : __('common.inactive') }} @else $check['secure'], 'text-warning' => ! $check['secure'], ])>{{ $check['secure'] ? __('servers.check_secure') : __('servers.check_open') }} @endif

{{ $supported ? $check['detail'] : $check['reason'] }}

{{-- Key-only warning: disabling password login leaves SSH-key access only. --}} @if ($supported && $check['key'] === 'ssh_password' && $check['featureOn'])

{{ __('servers.ssh_key_only_hint') }}

@endif
@if ($supported) @if ($check['key'] === 'fail2ban') @endif {{-- Uniform style across all rows (R10): always the bordered secondary button. --}} {{ $check['featureOn'] ? __('common.disable') : __('common.enable') }} @endif
@endforeach
{{-- Firewall + fail2ban: only the tools actually installed, side-by-side on lg --}} @php $fw = $firewall ?? []; $fwTool = $fw['tool'] ?? 'ufw'; $fwToolLabel = $fwTool === 'firewalld' ? 'firewalld' : 'UFW'; $fwSupported = $fw['supported'] ?? false; $fwInstalled = $fw['installed'] ?? false; $fwActive = $fw['active'] ?? false; $fwManageable = $fw['manageable'] ?? false; $fwReadOnly = $fw['readOnly'] ?? false; $fwReadError = $fw['readError'] ?? false; $fwRules = $fw['rules'] ?? []; $fwDefaults = $fw['defaults'] ?? []; $actTone = ['ALLOW' => 'online', 'LIMIT' => 'warning', 'DENY' => 'offline', 'REJECT' => 'offline']; $f2b = $fail2ban ?? []; $f2bSupported = $f2b['supported'] ?? false; $f2bReadError = $f2b['readError'] ?? false; $f2bInstalled = $f2b['installed'] ?? false; $f2bActive = $f2b['active'] ?? false; $f2bJails = $f2b['jails'] ?? []; $f2bIgnore = $f2b['ignoreip'] ?? []; $f2bBanned = array_sum(array_map(fn ($j) => $j['currentlyBanned'] ?? 0, $f2bJails)); // Show a panel when its tool is actually installed (active or inactive), OR // when the privileged read FAILED (readError) — a failed probe must stay // visible so the operator can tell it apart from a genuinely-absent tool // (both report installed=false). When truly absent, the Sicherheit checklist // row carries the "Aktivieren" install path — so the redundant "nicht // installiert" box is gone. $showFw = $fwSupported && ($fwInstalled || $fwReadError); $showF2b = $f2bSupported && ($f2bInstalled || $f2bReadError); $panelCount = ($showFw ? 1 : 0) + ($showF2b ? 1 : 0); @endphp @if ($panelCount > 0) {{-- Two tools → side-by-side; a lone tool is capped so it doesn't stretch edge-to-edge. --}}
$panelCount === 2, 'lg:max-w-2xl' => $panelCount === 1, ])> @if ($showFw) {{-- Firewall-Regeln --}} @if ($fwManageable) {{ __('servers.firewall_add_rule') }} @endif @if ($fwReadError)

{{ __('servers.firewall_read_error') }}

@elseif ($fwTool === 'firewalld' && ! $fwActive)

{{ __('servers.firewalld_inactive') }}

{{ __('common.enable') }}
@else
@if ($fwTool === 'ufw') {{ __('servers.firewall_default') }} {{ __('servers.firewall_incoming', ['value' => $fwDefaults['incoming'] ?? '—']) }} {{ __('servers.firewall_outgoing', ['value' => $fwDefaults['outgoing'] ?? '—']) }} @else {{ __('servers.firewall_zone') }} {{ $fwDefaults['zone'] ?? '—' }} @endif
@if ($fwReadOnly)

{{ __('servers.firewall_readonly') }}

@endif
@forelse ($fwRules as $rule) {{-- $ruleTone, NOT $tone: the page-level $tone is a closure used by the gauges/Volumes panel; reusing the name here would clobber it. --}} @php $ruleTone = $actTone[$rule['action'] ?? 'ALLOW'] ?? 'online'; @endphp
$ruleTone === 'online', 'bg-warning' => $ruleTone === 'warning', 'bg-offline' => $ruleTone === 'offline', ])>

{{ $rule['label'] ?? ($rule['to'] ?? $rule['raw']) }}

@if ($fwTool === 'ufw' && ($rule['from'] ?? '') !== '' && ! \Illuminate\Support\Str::contains($rule['from'], 'Anywhere'))

{{ __('servers.firewall_rule_from', ['value' => $rule['from']]) }}

@endif
$ruleTone === 'online', 'text-warning' => $ruleTone === 'warning', 'text-offline' => $ruleTone === 'offline', ])>{{ strtolower($rule['action'] ?? 'allow') }} @unless ($fwReadOnly) @endunless
@empty

{{ __('servers.firewall_no_rules') }}

@endforelse
@endif
@endif @if ($showF2b) {{-- fail2ban-Status --}} @if ($f2bActive) {{ __('servers.fail2ban_ban_ip') }} @endif @if ($f2bReadError)

{{ __('servers.fail2ban_read_error') }}

@elseif (! $f2bActive)

{{ __('servers.fail2ban_state_installed_inactive') }}

{{ __('common.enable') }}
@else
@foreach ($f2bJails as $jail)

{{ $jail['name'] }}

($jail['currentlyBanned'] ?? 0) > 0])>{{ __('servers.fail2ban_banned', ['count' => $jail['currentlyBanned'] ?? 0]) }} · {{ __('servers.fail2ban_failed', ['count' => $jail['currentlyFailed'] ?? 0]) }}

@if (count($jail['bannedIps'] ?? []))
@foreach ($jail['bannedIps'] as $ip)
{{ $ip }} {{ __('common.unlock') }}
@endforeach
@else

{{ __('servers.fail2ban_no_banned') }}

@endif
@endforeach
{{-- Whitelist (ignoreip) --}}

{{ __('servers.whitelist_label') }}

@foreach ($f2bIgnore as $ip) {{ $ip }} @unless (in_array($ip, ['127.0.0.1/8', '::1'], true)) @endunless @endforeach
{{ __('common.add') }}
@endif
@endif
@endif {{-- Volumes + Netzwerk-Interfaces --}}
@forelse ($volumes as $vol) @php $vt = $tone((int) $vol['used']); @endphp

{{ $vol['mount'] }}

{{ $vol['fs'] }} · {{ $vol['size'] }}

{{ $vol['used'] }}%
@empty

{{ __('servers.no_volumes') }}

@endforelse
@foreach ($interfaces as $if) @endforeach
{{ __('servers.col_name') }} {{ __('servers.col_ip') }} {{ __('servers.col_mac') }} {{ __('servers.col_rx') }} {{ __('servers.col_tx') }}
{{ $if['name'] }} {{ $if['ip'] }} {{ $if['mac'] }} {{ $if['rx'] }} {{ $if['tx'] }}
{{-- SSH keys --}} {{ __('servers.ssh_keys_add') }}
@forelse ($sshKeys as $key)

{{ $key['comment'] }}

{{ $key['type'] }}

{{ $key['fingerprint'] }}

@empty

{{ __('servers.ssh_keys_none') }}

@endforelse
@else {{-- skeletons for the SSH-loaded sections --}}
@foreach ([__('servers.specs_title'), __('servers.security_title')] as $t)
@endforeach
@foreach ([__('servers.volumes_title'), __('servers.interfaces_title')] as $t)
@endforeach
@endif