mailwolt/resources/views/livewire/ui/nx/dashboard.blade.php

354 lines
20 KiB
PHP

<x-slot:breadcrumbParent>Dashboard</x-slot:breadcrumbParent>
<x-slot:breadcrumb>Übersicht</x-slot:breadcrumb>
<div wire:poll.30s>
{{-- SSL-Banner --}}
@if(!$sslConfigured)
<div style="display:flex;align-items:center;gap:12px;padding:12px 16px;margin-bottom:16px;background:rgba(251,191,36,.07);border:1px solid rgba(251,191,36,.25);border-radius:10px">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" style="flex-shrink:0;color:#fbbf24"><path d="M8 1.5L14.5 13H1.5L8 1.5Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/><path d="M8 6v4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/><circle cx="8" cy="11.5" r=".8" fill="currentColor"/></svg>
<div style="flex:1">
<span style="font-size:13px;font-weight:600;color:#fbbf24">SSL noch nicht eingerichtet</span>
<span style="font-size:12px;color:rgba(251,191,36,.7);margin-left:8px">Domains laufen ohne HTTPS Zertifikate in den Einstellungen beantragen.</span>
</div>
<a href="{{ route('ui.system.settings') }}" style="font-size:12px;padding:5px 12px;border-radius:6px;background:rgba(251,191,36,.15);border:1px solid rgba(251,191,36,.3);color:#fbbf24;text-decoration:none;white-space:nowrap">
Jetzt einrichten
</a>
</div>
@endif
{{-- Hero Banner --}}
<div class="mw-hero">
<div class="mw-hero-icon">
<svg width="18" height="18" viewBox="0 0 20 20" fill="none">
<rect x="1" y="5.5" width="18" height="4.5" rx="2" stroke="#6a7a96" stroke-width="1.3"/>
<rect x="1" y="12" width="18" height="4.5" rx="2" stroke="#6a7a96" stroke-width="1.3"/>
<circle cx="15.5" cy="7.75" r="1.1" fill="#22c55e"/>
<circle cx="15.5" cy="14.25" r="1.1" fill="#22c55e"/>
<circle cx="13" cy="7.75" r="1.1" fill="#22c55e"/>
<circle cx="13" cy="14.25" r="1.1" fill="#22c55e"/>
</svg>
</div>
<div>
<div class="mw-hero-title">Mail Server</div>
</div>
<div class="mw-hero-divider"></div>
<div class="mw-hero-stats">
<div class="mw-hstat">
<div class="mw-hstat-n ok">{{ $domainCount }}</div>
<div class="mw-hstat-l">Domains</div>
</div>
<div class="mw-hstat">
<div class="mw-hstat-n">{{ $mailboxCount }}</div>
<div class="mw-hstat-l">Postfächer</div>
</div>
<div class="mw-hstat">
<div class="mw-hstat-n ok">{{ $servicesActive }}/{{ $servicesTotal }}</div>
<div class="mw-hstat-l">Dienste</div>
</div>
<div class="mw-hstat">
<div class="mw-hstat-n">{{ $alertCount }}</div>
<div class="mw-hstat-l">Warnungen</div>
</div>
</div>
<div class="mw-hero-host">{{ $mailHostname }}</div>
</div>
{{-- System-Ressourcen --}}
<div class="mw-section">
<span class="mw-section-title">System-Ressourcen</span>
<div class="mw-section-line"></div>
</div>
<div class="mw-metric-grid">
@php $cpuClass = $cpu > 80 ? 'mw-bar-high' : ($cpu > 50 ? 'mw-bar-mid' : 'mw-bar-low'); @endphp
<div class="mw-metric-card">
<div class="mw-mc-top">
<span class="mw-mc-label">CPU</span>
<div class="mw-mc-icon"><svg width="12" height="12" viewBox="0 0 13 13" fill="none"><rect x=".5" y=".5" width="12" height="12" rx="2" stroke="#606880" stroke-width="1.1"/><rect x="3" y="3" width="7" height="7" rx="1" stroke="#606880" stroke-width="1"/></svg></div>
</div>
<div class="mw-mc-value">{{ $cpu }}<span style="font-size:15px;font-weight:600;">%</span></div>
<div class="mw-mc-sub">{{ $cpuCores }} Cores · {{ $cpuMhz }} GHz</div>
<div class="mw-bar {{ $cpuClass }}"><div class="mw-bar-fill" style="width:{{ $cpu }}%"></div></div>
</div>
@php $ramClass = $ramPercent > 80 ? 'mw-bar-high' : ($ramPercent > 50 ? 'mw-bar-mid' : 'mw-bar-low'); @endphp
<div class="mw-metric-card">
<div class="mw-mc-top">
<span class="mw-mc-label">RAM</span>
<div class="mw-mc-icon"><svg width="12" height="12" viewBox="0 0 13 13" fill="none"><rect x=".5" y="3" width="12" height="7" rx="1.5" stroke="#606880" stroke-width="1.1"/><path d="M3.5 3V2M6.5 3V2M9.5 3V2" stroke="#606880" stroke-width="1" stroke-linecap="round"/></svg></div>
</div>
<div class="mw-mc-value">{{ $ramPercent }}<span style="font-size:15px;font-weight:600;">%</span></div>
<div class="mw-mc-sub">{{ $ramUsed }} GB / {{ $ramTotal }} GB</div>
<div class="mw-bar {{ $ramClass }}"><div class="mw-bar-fill" style="width:{{ $ramPercent }}%"></div></div>
</div>
@php
$loadVal = floatval($load1);
$loadMax = max(1, $cpuCores);
$loadPct = min(100, round($loadVal / $loadMax * 100));
$loadClass = $loadPct > 80 ? 'mw-bar-high' : ($loadPct > 50 ? 'mw-bar-mid' : 'mw-bar-low');
@endphp
<div class="mw-metric-card">
<div class="mw-mc-top">
<span class="mw-mc-label">Load</span>
<div class="mw-mc-icon"><svg width="12" height="12" viewBox="0 0 13 13" fill="none"><path d="M1 10L4 6.5l3 2 3-5 2 1.5" stroke="#606880" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
</div>
<div class="mw-mc-value" style="font-size:22px;letter-spacing:-.5px;">{{ $load1 }}</div>
<div class="mw-mc-sub">{{ $load5 }} · {{ $load15 }} (1/5/15m)</div>
<div class="mw-bar {{ $loadClass }}"><div class="mw-bar-fill" style="width:{{ $loadPct }}%"></div></div>
</div>
<div class="mw-metric-card">
<div class="mw-mc-top">
<span class="mw-mc-label">Uptime</span>
<div class="mw-mc-icon"><svg width="12" height="12" viewBox="0 0 13 13" fill="none"><circle cx="6.5" cy="6.5" r="5" stroke="#606880" stroke-width="1.1"/><path d="M6.5 3.5V6.5l2 1.5" stroke="#606880" stroke-width="1.1" stroke-linecap="round"/></svg></div>
</div>
<div class="mw-mc-value" style="font-size:20px;letter-spacing:-.5px;">{{ $uptimeDays }}d {{ $uptimeHours }}h</div>
<div class="mw-mc-sub">Stabil · kein Neustart</div>
<div class="mw-bar mw-bar-uptime"><div class="mw-bar-fill" style="width:100%"></div></div>
</div>
</div>
{{-- Dienste & Schutz --}}
<div class="mw-section">
<span class="mw-section-title">Dienste &amp; Schutz</span>
<div class="mw-section-line"></div>
</div>
<div class="mw-status-grid">
<div class="mw-status-card">
<div class="mw-sc-head">
<div class="mw-sc-icon" style="background:var(--mw-grbg);border:1px solid var(--mw-grbd);">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 1.5L12.5 4v4.5c0 2.8-2.3 4.5-5.5 5C3.8 13 1.5 11.3 1.5 8.5V4L7 1.5Z" stroke="#22c55e" stroke-width="1.2" stroke-linejoin="round"/><path d="M4.5 7l2 2 3-3" stroke="#22c55e" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div>
<div class="mw-sc-title">WoltGuard</div>
<div class="mw-sc-sub">System-Wächter</div>
</div>
<span class="mw-sc-badge mw-badge-ok">alle Dienste OK</span>
</div>
<div class="mw-sc-body"><b>{{ $servicesActive }}/{{ $servicesTotal }}</b> Dienste aktiv</div>
</div>
<div class="mw-status-card">
<div class="mw-sc-head">
<div class="mw-sc-icon" style="background:var(--mw-bg4);border:1px solid var(--mw-b2);">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M12.5 7A5.5 5.5 0 1 1 7 1.5" stroke="#606880" stroke-width="1.2" stroke-linecap="round"/><path d="M7 .5l2.5 1.5L7 3.5" stroke="#606880" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div>
<div class="mw-sc-title">Updates</div>
<div class="mw-sc-sub">{{ config('app.version', 'vdev') }}</div>
</div>
<span class="mw-sc-badge mw-badge-mute">aktuell</span>
</div>
<div class="mw-sc-body">System ist auf dem neuesten Stand.</div>
<div class="mw-sc-progress"><div class="mw-sc-progress-fill" style="width:100%"></div></div>
</div>
<div class="mw-status-card">
<div class="mw-sc-head">
<div class="mw-sc-icon" style="background:var(--mw-bg4);border:1px solid var(--mw-b2);">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2.5 9H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-.5" stroke="#606880" stroke-width="1.2" stroke-linecap="round"/><rect x="3" y="8.5" width="8" height="5" rx="1.5" stroke="#606880" stroke-width="1.2"/></svg>
</div>
<div>
<div class="mw-sc-title">Backup</div>
<div class="mw-sc-sub" title="{{ $backup['last_at_full'] ?? '' }}">
@if($backup['last_at'])
Zuletzt {{ $backup['last_at'] }}
@elseif($backup['status'] === 'unconfigured')
Nicht konfiguriert
@elseif($backup['status'] === 'running')
Läuft gerade…
@else
Noch nicht ausgeführt
@endif
</div>
</div>
@php
$bStatus = $backup['status'] ?? 'unknown';
$bBadge = match($bStatus) {
'ok' => ['class' => 'mw-badge-ok', 'label' => 'OK'],
'failed' => ['class' => 'mw-badge-fail', 'label' => 'Fehler'],
'running' => ['class' => 'mw-badge-info', 'label' => 'Läuft'],
'queued' => ['class' => 'mw-badge-info', 'label' => 'Warteschlange'],
'unconfigured' => ['class' => 'mw-badge-mute', 'label' => 'Nicht konfiguriert'],
'pending' => ['class' => 'mw-badge-warn', 'label' => 'Ausstehend'],
default => ['class' => 'mw-badge-warn', 'label' => 'Unbekannt'],
};
@endphp
<span class="mw-sc-badge {{ $bBadge['class'] }}">{{ $bBadge['label'] }}</span>
</div>
<div class="mw-sc-body">
@if($backup['size'])
Größe: <strong style="color:var(--mw-t2)">{{ $backup['size'] }}</strong>
@else
Größe:
@endif
@if($backup['duration'])
&nbsp;·&nbsp; {{ $backup['duration'] }}
@endif
@if($backup['next_at'])
<br>Nächstes: <strong style="color:var(--mw-t2)">{{ $backup['next_at'] }}</strong>
@elseif($backup['status'] !== 'unconfigured' && !$backup['enabled'])
<br><span style="color:var(--mw-t4)">Zeitplan deaktiviert</span>
@endif
</div>
@if($backup['status'] === 'unconfigured')
<a href="{{ route('ui.system.settings') }}#backup" class="mw-sc-btn">Konfigurieren</a>
@endif
</div>
<div class="mw-status-card">
<div class="mw-sc-head">
<div class="mw-sc-icon" style="background:var(--mw-bg4);border:1px solid var(--mw-b2);">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2L13 12H1L7 2Z" stroke="#606880" stroke-width="1.2" stroke-linejoin="round"/><path d="M7 6.5v2.5" stroke="#606880" stroke-width="1.4" stroke-linecap="round"/><circle cx="7" cy="10.5" r=".6" fill="#606880"/></svg>
</div>
<div>
<div class="mw-sc-title">Alerts</div>
<div class="mw-sc-sub">System-Warnungen</div>
</div>
<span class="mw-sc-badge mw-badge-mute">{{ $alertCount }} offen</span>
</div>
<div class="mw-sc-body">
@if($sandboxAlerts->isEmpty())
<div style="font-style:italic;color:var(--mw-t4);font-size:12px;">Keine Warnungen.</div>
@else
<div style="display:flex;flex-direction:column;gap:5px;">
@foreach($sandboxAlerts as $sr)
<div style="display:flex;align-items:center;gap:8px;padding:5px 8px;background:rgba(234,179,8,.07);border:1px solid rgba(234,179,8,.2);border-radius:6px;">
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" style="flex-shrink:0;color:#fbbf24"><path d="M5.5 1L10.5 10H.5L5.5 1Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/><path d="M5.5 4.5v2.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
<span style="font-size:11.5px;color:#fbbf24;font-weight:500;">
@if($sr->type === 'global') Sandbox global aktiv
@elseif($sr->type === 'domain') Sandbox aktiv: {{ $sr->target }}
@else Sandbox aktiv: {{ $sr->target }}
@endif
</span>
<a href="{{ route('ui.system.sandbox') }}" style="margin-left:auto;font-size:10.5px;color:rgba(251,191,36,.6);text-decoration:none;white-space:nowrap;">verwalten </a>
</div>
@endforeach
</div>
@endif
</div>
</div>
</div>
{{-- Infrastruktur --}}
<div class="mw-section">
<span class="mw-section-title">Infrastruktur</span>
<div class="mw-section-line"></div>
</div>
<div class="mw-bottom-grid">
<div class="mw-card">
<div class="mw-card-title">Dienste &amp; Ports</div>
@php $svcHalf = (int) ceil(count($services) / 2); @endphp
<div class="mw-svc-grid">
<div>
@foreach($services as $svc)
@if($loop->index < $svcHalf)
@php $st = $svc['status']; @endphp
<div class="mw-svc-row">
<div class="mw-svc-dot {{ $st === 'online' ? 'ok' : ($st === 'offline' ? 'off' : 'na') }}"></div>
<div class="mw-svc-name">{{ $svc['name'] }}<span class="mw-svc-type"> {{ $svc['type'] }}</span></div>
<span class="mw-svc-badge {{ $st }}">{{ $st === 'online' ? 'Online' : ($st === 'offline' ? 'Offline' : 'N/A') }}</span>
</div>
@endif
@endforeach
</div>
<div>
@foreach($services as $svc)
@if($loop->index >= $svcHalf)
@php $st = $svc['status']; @endphp
<div class="mw-svc-row">
<div class="mw-svc-dot {{ $st === 'online' ? 'ok' : ($st === 'offline' ? 'off' : 'na') }}"></div>
<div class="mw-svc-name">{{ $svc['name'] }}<span class="mw-svc-type"> {{ $svc['type'] }}</span></div>
<span class="mw-svc-badge {{ $st }}">{{ $st === 'online' ? 'Online' : ($st === 'offline' ? 'Offline' : 'N/A') }}</span>
</div>
@endif
@endforeach
</div>
</div>
<div class="mw-ports">
@foreach([25, 465, 587, 110, 143, 993, 995, 80, 443] as $port)
@php $isActive = $ports[$port] ?? false; @endphp
<span class="mw-port {{ $isActive ? 'active' : 'inactive' }}" title="{{ $isActive ? 'Lauscht' : 'Nicht aktiv' }}">:{{ $port }}</span>
@endforeach
</div>
</div>
<div class="mw-right-col">
<div class="mw-card">
<div class="mw-card-title">Storage</div>
<div class="mw-donut-wrap">
<svg width="68" height="68" viewBox="0 0 68 68" style="flex-shrink:0">
<circle cx="34" cy="34" r="26" fill="none" stroke="var(--mw-bg4)" stroke-width="8"/>
<circle cx="34" cy="34" r="26" fill="none" stroke="var(--mw-v)" stroke-width="8"
stroke-dasharray="{{ $diskUsedPercent * 1.6336 }} 163.36"
stroke-dashoffset="40.84" stroke-linecap="round"
transform="rotate(-90 34 34)" opacity=".7"/>
<text x="34" y="38" text-anchor="middle" font-size="11" font-weight="700" fill="#edf0fc">{{ $diskUsedPercent }}%</text>
</svg>
<div style="flex:1">
<div class="mw-legend-row">
<div class="mw-legend-dot" style="background:var(--mw-v);opacity:.7"></div>
<div class="mw-legend-label">Belegt</div>
<div class="mw-legend-val">{{ $diskUsedGb }} GB</div>
</div>
<div class="mw-legend-row">
<div class="mw-legend-dot" style="background:var(--mw-bg4);border:1px solid var(--mw-b2)"></div>
<div class="mw-legend-label">Frei</div>
<div class="mw-legend-val">{{ $diskFreeGb }} GB</div>
</div>
<div class="mw-legend-row">
<div class="mw-legend-dot" style="background:var(--mw-b2)"></div>
<div class="mw-legend-label">Gesamt</div>
<div class="mw-legend-val">{{ $diskTotalGb }} GB</div>
</div>
</div>
</div>
</div>
<div class="mw-card">
<div class="mw-card-title">Mail &amp; Sicherheit</div>
<div class="mw-mini-grid">
<div class="mw-mini-card">
<div class="mw-mini-val {{ $spamBlocked > 0 ? '' : '' }}">{{ $spamBlocked }}</div>
<div class="mw-mini-label">Spam geblockt</div>
</div>
<div class="mw-mini-card">
<div class="mw-mini-val" style="{{ $spamTagged > 0 ? 'color:var(--mw-am)' : '' }}">{{ $spamTagged }}</div>
<div class="mw-mini-label">Tagged</div>
</div>
<div class="mw-mini-card">
<div class="mw-mini-val" style="color:var(--mw-gr)">{{ $hamCount }}</div>
<div class="mw-mini-label">Ham erkannt</div>
</div>
</div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--mw-b1);display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
<a href="{{ route('ui.mail.queues.index') }}" style="display:flex;align-items:center;gap:5px;font-size:11.5px;color:var(--mw-t3);text-decoration:none;" class="mw-sec-link">
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><path d="M1 4h12M4 4V2h6v2M4 12V7m6 5V7" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
Queue:
<span style="{{ $queueDeferred > 0 ? 'color:var(--mw-am);font-weight:600;' : 'color:var(--mw-t2);' }}">{{ $queueTotal }} {{ $queueDeferred > 0 ? "({$queueDeferred} def.)" : '' }}</span>
</a>
<span style="color:var(--mw-b2);">|</span>
<a href="{{ route('ui.mail.quarantine.index') }}" style="display:flex;align-items:center;gap:5px;font-size:11.5px;color:var(--mw-t3);text-decoration:none;" class="mw-sec-link">
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1.2"/><path d="M7 4v4l2.5 2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
Rspamd: <span style="color:var(--mw-t2);">{{ $spamRejected }} reject</span>
</a>
<span style="color:var(--mw-b2);">|</span>
<span style="font-size:11px;color:var(--mw-t4);">{{ $clamVer }}</span>
</div>
</div>
</div>
</div>
</div>