Feat: Einstellungen mit URL-Tabs (Allgemein, Domains & SSL, Backup, Benachrichtigungen)
parent
1f013b6dcd
commit
ff6002ca0b
|
|
@ -7,12 +7,16 @@ use App\Models\Setting;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Livewire\Attributes\Layout;
|
use Livewire\Attributes\Layout;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
|
use Livewire\Attributes\Url;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
#[Layout('layouts.dvx')]
|
#[Layout('layouts.dvx')]
|
||||||
#[Title('Einstellungen · Mailwolt')]
|
#[Title('Einstellungen · Mailwolt')]
|
||||||
class SettingsForm extends Component
|
class SettingsForm extends Component
|
||||||
{
|
{
|
||||||
|
#[Url]
|
||||||
|
public string $tab = 'allgemein';
|
||||||
|
|
||||||
// Allgemein
|
// Allgemein
|
||||||
public string $instance_name = '';
|
public string $instance_name = '';
|
||||||
public string $locale = 'de';
|
public string $locale = 'de';
|
||||||
|
|
|
||||||
|
|
@ -12,33 +12,56 @@
|
||||||
Einstellungen
|
Einstellungen
|
||||||
</div>
|
</div>
|
||||||
<div class="mbx-page-actions">
|
<div class="mbx-page-actions">
|
||||||
|
@if($tab === 'domains')
|
||||||
|
<button wire:click="saveDomains" class="mbx-btn-primary">
|
||||||
|
<svg width="12" height="12" viewBox="0 0 14 14" fill="none"><path d="M2 2h8l2 2v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z" stroke="currentColor" stroke-width="1.2"/><path d="M5 2v3h4V2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||||
|
<span wire:loading.remove wire:target="saveDomains">Domains speichern</span>
|
||||||
|
<span wire:loading wire:target="saveDomains">Speichert…</span>
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
<button wire:click="save" class="mbx-btn-primary">
|
<button wire:click="save" class="mbx-btn-primary">
|
||||||
<svg width="12" height="12" viewBox="0 0 14 14" fill="none"><path d="M2 2h8l2 2v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z" stroke="currentColor" stroke-width="1.2"/><path d="M5 2v3h4V2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
<svg width="12" height="12" viewBox="0 0 14 14" fill="none"><path d="M2 2h8l2 2v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z" stroke="currentColor" stroke-width="1.2"/><path d="M5 2v3h4V2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||||
<span wire:loading.remove wire:target="save">Speichern</span>
|
<span wire:loading.remove wire:target="save">Speichern</span>
|
||||||
<span wire:loading wire:target="save">Speichert…</span>
|
<span wire:loading wire:target="save">Speichert…</span>
|
||||||
</button>
|
</button>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Tab-Navigation --}}
|
||||||
|
<div style="display:flex;gap:2px;border-bottom:1px solid var(--mw-b2);margin-bottom:20px">
|
||||||
|
@php
|
||||||
|
$tabs = [
|
||||||
|
'allgemein' => ['label' => 'Allgemein', 'icon' => '<circle cx="8" cy="8" r="2.2" stroke="currentColor" stroke-width="1.3"/><path d="M8 1.5v1.3M8 13.2v1.3M1.5 8h1.3M13.2 8h1.3M3.2 3.2l.9.9M11.9 11.9l.9.9M3.2 12.8l.9-.9M11.9 4.1l.9-.9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>'],
|
||||||
|
'domains' => ['label' => 'Domains & SSL', 'icon' => '<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.3"/><path d="M8 2C6 4.5 5 6.2 5 8s1 3.5 3 6M8 2c2 2.5 3 4.2 3 6s-1 3.5-3 6M2 8h12" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>'],
|
||||||
|
'backup' => ['label' => 'Backup', 'icon' => '<ellipse cx="8" cy="4" rx="5" ry="2" stroke="currentColor" stroke-width="1.3"/><path d="M3 4v4c0 1.1 2.24 2 5 2s5-.9 5-2V4" stroke="currentColor" stroke-width="1.3"/><path d="M3 8v4c0 1.1 2.24 2 5 2s5-.9 5-2V8" stroke="currentColor" stroke-width="1.3"/>'],
|
||||||
|
'benachrichtigungen' => ['label' => 'Benachrichtigungen', 'icon' => '<path d="M8 14a1.5 1.5 0 0 0 3 0M8 2a4 4 0 0 1 4 4v3l1 1H3l1-1V6a4 4 0 0 1 4-4Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/>'],
|
||||||
|
];
|
||||||
|
@endphp
|
||||||
|
@foreach($tabs as $key => $cfg)
|
||||||
|
<button wire:click="$set('tab','{{ $key }}')"
|
||||||
|
style="display:inline-flex;align-items:center;gap:6px;padding:9px 14px;font-size:12.5px;font-weight:500;border:none;background:transparent;cursor:pointer;border-bottom:2px solid {{ $tab === $key ? 'var(--mw-v)' : 'transparent' }};color:{{ $tab === $key ? 'var(--mw-v2)' : 'var(--mw-t4)' }};margin-bottom:-1px;transition:color .15s">
|
||||||
|
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">{!! $cfg['icon'] !!}</svg>
|
||||||
|
{{ $cfg['label'] }}
|
||||||
|
</button>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- ═══ Tab: Allgemein ═══ --}}
|
||||||
|
@if($tab === 'allgemein')
|
||||||
<div class="sec-layout">
|
<div class="sec-layout">
|
||||||
|
|
||||||
{{-- ═══ Main ═══ --}}
|
|
||||||
<div style="min-width:0">
|
<div style="min-width:0">
|
||||||
<div class="mbx-sections">
|
<div class="mbx-sections">
|
||||||
|
|
||||||
{{-- Allgemein --}}
|
|
||||||
<div class="mbx-section">
|
<div class="mbx-section">
|
||||||
<div class="mbx-domain-head">
|
<div class="mbx-domain-head">
|
||||||
<div class="mbx-domain-info">
|
<div class="mbx-domain-info"><span class="mbx-badge-mute">Allgemein</span></div>
|
||||||
<span class="mbx-badge-mute">Allgemein</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style="padding:16px 18px">
|
<div style="padding:16px 18px">
|
||||||
<div class="mw-modal-grid2">
|
<div class="mw-modal-grid2">
|
||||||
<div>
|
<div>
|
||||||
<label class="mw-modal-label">Instanzname</label>
|
<label class="mw-modal-label">Instanzname</label>
|
||||||
<input type="text" value="{{ $instance_name }}" class="mw-modal-input" readonly
|
<input type="text" value="{{ $instance_name }}" class="mw-modal-input" readonly style="opacity:.55;cursor:default">
|
||||||
style="opacity:.55;cursor:default">
|
|
||||||
<div class="mw-modal-hint">Wird aus config/app.php gelesen (APP_NAME)</div>
|
<div class="mw-modal-hint">Wird aus config/app.php gelesen (APP_NAME)</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -67,15 +90,149 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- 2FA --}}
|
|
||||||
<livewire:ui.system.two-fa-status />
|
<livewire:ui.system.two-fa-status />
|
||||||
|
|
||||||
{{-- Backup --}}
|
<div class="mbx-section">
|
||||||
|
<div class="mbx-domain-head">
|
||||||
|
<div class="mbx-domain-info"><span class="mbx-badge-mute">Sicherheit</span></div>
|
||||||
|
</div>
|
||||||
|
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:14px">
|
||||||
|
<div class="mw-modal-grid2">
|
||||||
|
<div>
|
||||||
|
<label class="mw-modal-label">Login Rate Limit</label>
|
||||||
|
<input type="number" wire:model.defer="rate_limit" class="mw-modal-input" min="1" max="100">
|
||||||
|
<div class="mw-modal-hint">Max. Versuche pro Minute</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="mw-modal-label">Passwort Mindestlänge</label>
|
||||||
|
<input type="number" wire:model.defer="password_min" class="mw-modal-input" min="6" max="128">
|
||||||
|
<div class="mw-modal-hint">Zeichen (min. 6)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="mbx-sections">
|
||||||
|
<div class="mbx-section">
|
||||||
|
<div class="mbx-domain-head">
|
||||||
|
<div class="mbx-domain-info"><span class="mbx-badge-mute">System-Info</span></div>
|
||||||
|
</div>
|
||||||
|
<div style="padding:14px 16px;font-size:11.5px;color:var(--mw-t3);display:flex;flex-direction:column;gap:8px">
|
||||||
|
<div style="display:flex;justify-content:space-between">
|
||||||
|
<span style="color:var(--mw-t4)">PHP</span>
|
||||||
|
<span>{{ PHP_VERSION }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;justify-content:space-between">
|
||||||
|
<span style="color:var(--mw-t4)">Laravel</span>
|
||||||
|
<span>{{ app()->version() }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;justify-content:space-between">
|
||||||
|
<span style="color:var(--mw-t4)">Umgebung</span>
|
||||||
|
<span>{{ app()->environment() }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;justify-content:space-between">
|
||||||
|
<span style="color:var(--mw-t4)">Hostname</span>
|
||||||
|
<span style="font-family:monospace;font-size:10.5px">{{ gethostname() }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;justify-content:space-between">
|
||||||
|
<span style="color:var(--mw-t4)">Certbot</span>
|
||||||
|
<span>{{ trim(@shell_exec('certbot --version 2>&1') ?? '—') ?: '—' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- ═══ Tab: Domains & SSL ═══ --}}
|
||||||
|
@if($tab === 'domains')
|
||||||
|
<div class="sec-layout">
|
||||||
|
<div style="min-width:0">
|
||||||
|
<div class="mbx-sections">
|
||||||
|
<div class="mbx-section">
|
||||||
|
<div class="mbx-domain-head">
|
||||||
|
<div class="mbx-domain-info"><span class="mbx-badge-mute">Domains</span></div>
|
||||||
|
</div>
|
||||||
|
<div style="padding:14px 16px;display:flex;flex-direction:column;gap:12px">
|
||||||
|
|
||||||
|
<div style="display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border-radius:8px;background:rgba(251,191,36,.07);border:1px solid rgba(251,191,36,.25)">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;margin-top:1px;color:#f59e0b">
|
||||||
|
<path d="M7 1.5L12.5 11H1.5L7 1.5Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>
|
||||||
|
<path d="M7 5.5v3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
||||||
|
<circle cx="7" cy="10" r=".7" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
<span style="font-size:11.5px;color:rgba(251,191,36,.9);line-height:1.5">
|
||||||
|
Änderungen hier wirken sich auf das gesamte Routing aus — UI, Webmail und Mailserver sind betroffen. Nur ändern wenn DNS bereits korrekt konfiguriert ist.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="mw-modal-label">UI Domain</label>
|
||||||
|
<input type="text" wire:model="ui_domain" class="mw-modal-input" placeholder="mail.example.com">
|
||||||
|
@error('ui_domain') <div class="mw-modal-error">{{ $message }}</div> @enderror
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="mw-modal-label">Mailserver Domain</label>
|
||||||
|
<input type="text" wire:model="mail_domain" class="mw-modal-input" placeholder="mx.example.com">
|
||||||
|
@error('mail_domain') <div class="mw-modal-error">{{ $message }}</div> @enderror
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="mw-modal-label">Webmail Domain</label>
|
||||||
|
<input type="text" wire:model="webmail_domain" class="mw-modal-input" placeholder="webmail.example.com">
|
||||||
|
@error('webmail_domain') <div class="mw-modal-error">{{ $message }}</div> @enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="mbx-sections">
|
||||||
|
<div class="mbx-section">
|
||||||
|
<div class="mbx-domain-head">
|
||||||
|
<div class="mbx-domain-info"><span class="mbx-badge-mute">SSL-Zertifikate</span></div>
|
||||||
|
</div>
|
||||||
|
<div style="padding:10px 14px;display:flex;flex-direction:column;gap:5px">
|
||||||
|
@foreach(['ui','webmail','mail'] as $key)
|
||||||
|
@php
|
||||||
|
$c = $sslCerts[$key] ?? null;
|
||||||
|
$ok = $c && in_array($c['status'], ['ok','expiring']);
|
||||||
|
$domain = $c['domain'] ?? '';
|
||||||
|
@endphp
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:6px;background:var(--mw-bg3);border:1px solid var(--mw-b2)">
|
||||||
|
@if($ok)
|
||||||
|
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;color:#22c55e"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><path d="M5 9.5l1.3 1.3 2.7-2.7" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
|
@elseif($domain)
|
||||||
|
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;color:#f87171"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||||
|
@else
|
||||||
|
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;color:var(--mw-t4)"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||||
|
@endif
|
||||||
|
<span style="font-size:11px;color:var(--mw-t3);font-family:monospace">{{ $domain ?: '—' }}</span>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
<a href="{{ route('ui.security.ssl') }}" style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--mw-v);padding:5px 2px;text-decoration:none;margin-top:2px">
|
||||||
|
<svg width="10" height="10" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.3"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
|
||||||
|
Zertifikate verwalten →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- ═══ Tab: Backup ═══ --}}
|
||||||
|
@if($tab === 'backup')
|
||||||
|
<div class="mbx-sections" style="max-width:680px">
|
||||||
<div class="mbx-section" id="backup">
|
<div class="mbx-section" id="backup">
|
||||||
<div class="mbx-domain-head">
|
<div class="mbx-domain-head">
|
||||||
<div class="mbx-domain-info">
|
<div class="mbx-domain-info"><span class="mbx-badge-mute">Backup-Zeitplan</span></div>
|
||||||
<span class="mbx-badge-mute">Backup-Zeitplan</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;align-items:center;gap:8px">
|
<div style="display:flex;align-items:center;gap:8px">
|
||||||
@if($backup_enabled)
|
@if($backup_enabled)
|
||||||
<span class="mbx-badge-ok">Aktiv</span>
|
<span class="mbx-badge-ok">Aktiv</span>
|
||||||
|
|
@ -87,13 +244,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:14px">
|
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:14px">
|
||||||
|
|
||||||
{{-- Enable toggle --}}
|
|
||||||
<label class="sec-check">
|
<label class="sec-check">
|
||||||
<input type="checkbox" wire:model.live="backup_enabled">
|
<input type="checkbox" wire:model.live="backup_enabled">
|
||||||
<span style="font-size:12.5px;color:var(--mw-t2)">Automatisches Backup aktivieren</span>
|
<span style="font-size:12.5px;color:var(--mw-t2)">Automatisches Backup aktivieren</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{{-- Was sichern --}}
|
|
||||||
<div>
|
<div>
|
||||||
<label class="mw-modal-label" style="margin-bottom:8px;display:block">Was sichern</label>
|
<label class="mw-modal-label" style="margin-bottom:8px;display:block">Was sichern</label>
|
||||||
<div class="bk-src-grid">
|
<div class="bk-src-grid">
|
||||||
|
|
@ -124,8 +279,7 @@
|
||||||
</div>
|
</div>
|
||||||
@if($backup_include_maildirs)
|
@if($backup_include_maildirs)
|
||||||
<div style="margin-top:8px">
|
<div style="margin-top:8px">
|
||||||
<input type="text" wire:model.defer="backup_mail_dir"
|
<input type="text" wire:model.defer="backup_mail_dir" class="mw-modal-input" placeholder="/var/mail/vhosts">
|
||||||
class="mw-modal-input" placeholder="/var/mail/vhosts">
|
|
||||||
<div class="mw-modal-hint">Maildir-Pfad – leer lassen für automatische Erkennung</div>
|
<div class="mw-modal-hint">Maildir-Pfad – leer lassen für automatische Erkennung</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
@ -133,7 +287,6 @@
|
||||||
|
|
||||||
@if($backup_enabled)
|
@if($backup_enabled)
|
||||||
|
|
||||||
{{-- Preset buttons --}}
|
|
||||||
<div>
|
<div>
|
||||||
<label class="mw-modal-label">Häufigkeit</label>
|
<label class="mw-modal-label">Häufigkeit</label>
|
||||||
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
||||||
|
|
@ -149,14 +302,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Time / day pickers --}}
|
|
||||||
@if($backup_preset !== 'hourly' && $backup_preset !== 'custom')
|
@if($backup_preset !== 'hourly' && $backup_preset !== 'custom')
|
||||||
<div class="mw-modal-grid2">
|
<div class="mw-modal-grid2">
|
||||||
<div>
|
<div>
|
||||||
<label class="mw-modal-label">Uhrzeit</label>
|
<label class="mw-modal-label">Uhrzeit</label>
|
||||||
<input type="time" wire:model.live="backup_time" class="mw-modal-input">
|
<input type="time" wire:model.live="backup_time" class="mw-modal-input">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if($backup_preset === 'weekly')
|
@if($backup_preset === 'weekly')
|
||||||
<div>
|
<div>
|
||||||
<label class="mw-modal-label">Wochentag</label>
|
<label class="mw-modal-label">Wochentag</label>
|
||||||
|
|
@ -182,24 +333,20 @@
|
||||||
@if($backup_preset === 'custom')
|
@if($backup_preset === 'custom')
|
||||||
<div>
|
<div>
|
||||||
<label class="mw-modal-label">Cron-Ausdruck</label>
|
<label class="mw-modal-label">Cron-Ausdruck</label>
|
||||||
<input type="text" wire:model.live="backup_cron" class="mw-modal-input"
|
<input type="text" wire:model.live="backup_cron" class="mw-modal-input" placeholder="0 3 * * *" style="font-family:monospace">
|
||||||
placeholder="0 3 * * *" style="font-family:monospace">
|
|
||||||
<div class="mw-modal-hint">Standard Cron-Syntax: Minute Stunde Tag Monat Wochentag</div>
|
<div class="mw-modal-hint">Standard Cron-Syntax: Minute Stunde Tag Monat Wochentag</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{{-- Cron-Vorschau --}}
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--mw-bg4);border:1px solid var(--mw-b2);border-radius:7px">
|
<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--mw-bg4);border:1px solid var(--mw-b2);border-radius:7px">
|
||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><circle cx="6" cy="6" r="5" stroke="var(--mw-t4)" stroke-width="1.2"/><path d="M6 3.5v3l1.5 1.5" stroke="var(--mw-t4)" stroke-width="1.2" stroke-linecap="round"/></svg>
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><circle cx="6" cy="6" r="5" stroke="var(--mw-t4)" stroke-width="1.2"/><path d="M6 3.5v3l1.5 1.5" stroke="var(--mw-t4)" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||||
<span style="font-size:11px;color:var(--mw-t4)">Cron:</span>
|
<span style="font-size:11px;color:var(--mw-t4)">Cron:</span>
|
||||||
<code style="font-family:monospace;font-size:11px;color:var(--mw-v2)">{{ $backup_cron }}</code>
|
<code style="font-family:monospace;font-size:11px;color:var(--mw-v2)">{{ $backup_cron }}</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Aufbewahrung --}}
|
|
||||||
<div>
|
<div>
|
||||||
<label class="mw-modal-label">Aufbewahrung (Anzahl Backups)</label>
|
<label class="mw-modal-label">Aufbewahrung (Anzahl Backups)</label>
|
||||||
<input type="number" wire:model.defer="backup_retention" class="mw-modal-input"
|
<input type="number" wire:model.defer="backup_retention" class="mw-modal-input" min="1" max="365" style="max-width:120px">
|
||||||
min="1" max="365" style="max-width:120px">
|
|
||||||
<div class="mw-modal-hint">Ältere Backups werden automatisch gelöscht. Empfehlung: 7–14.</div>
|
<div class="mw-modal-hint">Ältere Backups werden automatisch gelöscht. Empfehlung: 7–14.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -207,13 +354,15 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
{{-- Benachrichtigungen --}}
|
{{-- ═══ Tab: Benachrichtigungen ═══ --}}
|
||||||
|
@if($tab === 'benachrichtigungen')
|
||||||
|
<div class="mbx-sections" style="max-width:680px">
|
||||||
<div class="mbx-section">
|
<div class="mbx-section">
|
||||||
<div class="mbx-domain-head">
|
<div class="mbx-domain-head">
|
||||||
<div class="mbx-domain-info">
|
<div class="mbx-domain-info"><span class="mbx-badge-mute">Benachrichtigungen</span></div>
|
||||||
<span class="mbx-badge-mute">Benachrichtigungen</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:14px">
|
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:14px">
|
||||||
<div class="mw-modal-grid2">
|
<div class="mw-modal-grid2">
|
||||||
|
|
@ -239,153 +388,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Sicherheit --}}
|
|
||||||
<div class="mbx-section">
|
|
||||||
<div class="mbx-domain-head">
|
|
||||||
<div class="mbx-domain-info">
|
|
||||||
<span class="mbx-badge-mute">Sicherheit</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:14px">
|
|
||||||
<div class="mw-modal-grid2">
|
|
||||||
<div>
|
|
||||||
<label class="mw-modal-label">Login Rate Limit</label>
|
|
||||||
<input type="number" wire:model.defer="rate_limit" class="mw-modal-input" min="1" max="100">
|
|
||||||
<div class="mw-modal-hint">Max. Versuche pro Minute</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="mw-modal-label">Passwort Mindestlänge</label>
|
|
||||||
<input type="number" wire:model.defer="password_min" class="mw-modal-input" min="6" max="128">
|
|
||||||
<div class="mw-modal-hint">Zeichen (min. 6)</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- ═══ Sidebar ═══ --}}
|
|
||||||
<div>
|
|
||||||
<div class="mbx-sections">
|
|
||||||
|
|
||||||
{{-- Domains --}}
|
|
||||||
<div class="mbx-section">
|
|
||||||
<div class="mbx-domain-head">
|
|
||||||
<div class="mbx-domain-info">
|
|
||||||
<span class="mbx-badge-mute">Domains</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:14px 16px;display:flex;flex-direction:column;gap:12px">
|
|
||||||
|
|
||||||
<div style="display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border-radius:8px;background:rgba(251,191,36,.07);border:1px solid rgba(251,191,36,.25)">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;margin-top:1px;color:#f59e0b">
|
|
||||||
<path d="M7 1.5L12.5 11H1.5L7 1.5Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>
|
|
||||||
<path d="M7 5.5v3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
||||||
<circle cx="7" cy="10" r=".7" fill="currentColor"/>
|
|
||||||
</svg>
|
|
||||||
<span style="font-size:11.5px;color:rgba(251,191,36,.9);line-height:1.5">
|
|
||||||
Änderungen hier wirken sich auf das gesamte Routing aus — UI, Webmail und Mailserver sind betroffen. Nur ändern wenn DNS bereits korrekt konfiguriert ist.
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="mw-modal-label">UI Domain</label>
|
|
||||||
<input type="text" wire:model="ui_domain" class="mw-modal-input"
|
|
||||||
placeholder="mail.example.com">
|
|
||||||
@error('ui_domain') <div class="mw-modal-error">{{ $message }}</div> @enderror
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="mw-modal-label">Mailserver Domain</label>
|
|
||||||
<input type="text" wire:model="mail_domain" class="mw-modal-input"
|
|
||||||
placeholder="mx.example.com">
|
|
||||||
@error('mail_domain') <div class="mw-modal-error">{{ $message }}</div> @enderror
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="mw-modal-label">Webmail Domain</label>
|
|
||||||
<input type="text" wire:model="webmail_domain" class="mw-modal-input"
|
|
||||||
placeholder="webmail.example.com">
|
|
||||||
@error('webmail_domain') <div class="mw-modal-error">{{ $message }}</div> @enderror
|
|
||||||
</div>
|
|
||||||
<button wire:click="saveDomains"
|
|
||||||
wire:loading.attr="disabled"
|
|
||||||
wire:target="saveDomains"
|
|
||||||
class="mbx-btn-primary" style="width:100%;justify-content:center;font-size:12px">
|
|
||||||
<span wire:loading.remove wire:target="saveDomains" style="display:inline-flex;align-items:center;gap:5px">
|
|
||||||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><path d="M2 2h8l2 2v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z" stroke="currentColor" stroke-width="1.2"/><path d="M5 2v3h4V2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>Domains speichern
|
|
||||||
</span>
|
|
||||||
<span wire:loading wire:target="saveDomains">Speichert…</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- SSL-Zertifikate --}}
|
|
||||||
<div class="mbx-section">
|
|
||||||
<div class="mbx-domain-head">
|
|
||||||
<div class="mbx-domain-info">
|
|
||||||
<span class="mbx-badge-mute">SSL-Zertifikate</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:10px 14px;display:flex;flex-direction:column;gap:5px">
|
|
||||||
@foreach(['ui','webmail','mail'] as $key)
|
|
||||||
@php
|
|
||||||
$c = $sslCerts[$key] ?? null;
|
|
||||||
$ok = $c && in_array($c['status'], ['ok','expiring']);
|
|
||||||
$domain = $c['domain'] ?? '';
|
|
||||||
@endphp
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:6px;background:var(--mw-bg3);border:1px solid var(--mw-b2)">
|
|
||||||
@if($ok)
|
|
||||||
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;color:#22c55e"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><path d="M5 9.5l1.3 1.3 2.7-2.7" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
||||||
@elseif($domain)
|
|
||||||
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;color:#f87171"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
|
||||||
@else
|
|
||||||
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;color:var(--mw-t4)"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
|
||||||
@endif
|
@endif
|
||||||
<span style="font-size:11px;color:var(--mw-t3);font-family:monospace">{{ $domain ?: '—' }}</span>
|
|
||||||
</div>
|
|
||||||
@endforeach
|
|
||||||
<a href="{{ route('ui.security.ssl') }}" style="display:flex;align-items:center;gap:5px;font-size:11px;color:var(--mw-v);padding:5px 2px;text-decoration:none;margin-top:2px">
|
|
||||||
<svg width="10" height="10" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.3"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
|
|
||||||
Zertifikate verwalten →
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- Info --}}
|
|
||||||
<div class="mbx-section">
|
|
||||||
<div class="mbx-domain-head">
|
|
||||||
<div class="mbx-domain-info">
|
|
||||||
<span class="mbx-badge-mute">System-Info</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:14px 16px;font-size:11.5px;color:var(--mw-t3);display:flex;flex-direction:column;gap:8px">
|
|
||||||
<div style="display:flex;justify-content:space-between">
|
|
||||||
<span style="color:var(--mw-t4)">PHP</span>
|
|
||||||
<span>{{ PHP_VERSION }}</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between">
|
|
||||||
<span style="color:var(--mw-t4)">Laravel</span>
|
|
||||||
<span>{{ app()->version() }}</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between">
|
|
||||||
<span style="color:var(--mw-t4)">Umgebung</span>
|
|
||||||
<span>{{ app()->environment() }}</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between">
|
|
||||||
<span style="color:var(--mw-t4)">Hostname</span>
|
|
||||||
<span style="font-family:monospace;font-size:10.5px">{{ gethostname() }}</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between">
|
|
||||||
<span style="color:var(--mw-t4)">Certbot</span>
|
|
||||||
<span>{{ trim(@shell_exec('certbot --version 2>&1') ?? '—') ?: '—' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue