mailwolt/resources/views/livewire/ui/system/settings-form.blade.php

395 lines
25 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<x-slot:breadcrumbParent>System</x-slot:breadcrumbParent>
<x-slot:breadcrumb>Einstellungen</x-slot:breadcrumb>
<div>
<div class="mbx-page-header">
<div class="mbx-page-title">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<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"/>
</svg>
Einstellungen
</div>
<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">
<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 wire:target="save">Speichert…</span>
</button>
@endif
</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 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">Allgemein</span></div>
</div>
<div style="padding:16px 18px">
<div class="mw-modal-grid2">
<div>
<label class="mw-modal-label">Instanzname</label>
<input type="text" value="{{ $instance_name }}" class="mw-modal-input" readonly style="opacity:.55;cursor:default">
<div class="mw-modal-hint">Wird aus config/app.php gelesen (APP_NAME)</div>
</div>
<div>
<label class="mw-modal-label">Sprache</label>
<select wire:model.defer="locale" class="mw-modal-input">
<option value="de">Deutsch</option>
<option value="en">English</option>
<option value="fr">Français</option>
</select>
</div>
<div>
<label class="mw-modal-label">Zeitzone</label>
<select wire:model.defer="timezone" class="mw-modal-input">
@foreach($timezones as $tz)
<option value="{{ $tz }}" @selected($timezone === $tz)>{{ $tz }}</option>
@endforeach
</select>
<div class="mw-modal-hint">Aktuelle Serverzeit: {{ now()->format('H:i:s') }}</div>
</div>
<div>
<label class="mw-modal-label">Session-Timeout (Min.)</label>
<input type="number" wire:model.defer="session_timeout" class="mw-modal-input" min="5" max="1440">
<div class="mw-modal-hint">51440 Minuten (24 h max.)</div>
</div>
</div>
</div>
</div>
<livewire:ui.system.two-fa-status />
<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-domain-head">
<div class="mbx-domain-info"><span class="mbx-badge-mute">Backup-Zeitplan</span></div>
<div style="display:flex;align-items:center;gap:8px">
@if($backup_enabled)
<span class="mbx-badge-ok">Aktiv</span>
@else
<span class="mbx-badge-warn">Deaktiviert</span>
@endif
<a href="{{ route('ui.system.backups') }}" class="mbx-btn-mute" style="font-size:11.5px;padding:4px 12px;text-decoration:none">Verlauf</a>
</div>
</div>
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:14px">
<label class="sec-check">
<input type="checkbox" wire:model.live="backup_enabled">
<span style="font-size:12.5px;color:var(--mw-t2)">Automatisches Backup aktivieren</span>
</label>
<div>
<label class="mw-modal-label" style="margin-bottom:8px;display:block">Was sichern</label>
<div class="bk-src-grid">
<label class="bk-src-card">
<input type="checkbox" wire:model.live="backup_include_db">
<span class="bk-src-icon">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><ellipse cx="7" cy="3.5" rx="5" ry="2" stroke="currentColor" stroke-width="1.2"/><path d="M2 3.5v7c0 1.1 2.24 2 5 2s5-.9 5-2v-7" stroke="currentColor" stroke-width="1.2"/><path d="M2 7c0 1.1 2.24 2 5 2s5-.9 5-2" stroke="currentColor" stroke-width="1.2"/></svg>
</span>
<span class="bk-src-name">Datenbank</span>
<span class="bk-src-hint">MySQL-Dump</span>
</label>
<label class="bk-src-card">
<input type="checkbox" wire:model.live="backup_include_maildirs">
<span class="bk-src-icon">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x=".5" y="2.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M.5 5l6.5 4.5L13.5 5" stroke="currentColor" stroke-width="1.2"/></svg>
</span>
<span class="bk-src-name">E-Mails</span>
<span class="bk-src-hint">Maildirs</span>
</label>
<label class="bk-src-card">
<input type="checkbox" wire:model.live="backup_include_configs">
<span class="bk-src-icon">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 1.5L12.5 4v4.8c0 2.8-2.3 4.8-5.5 5.2C3.8 13.6 1.5 11.6 1.5 8.8V4L7 1.5Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/></svg>
</span>
<span class="bk-src-name">Konfiguration</span>
<span class="bk-src-hint">postfix, dovecot, SSL</span>
</label>
</div>
@if($backup_include_maildirs)
<div style="margin-top:8px">
<input type="text" wire:model.defer="backup_mail_dir" class="mw-modal-input" placeholder="/var/mail/vhosts">
<div class="mw-modal-hint">Maildir-Pfad leer lassen für automatische Erkennung</div>
</div>
@endif
</div>
@if($backup_enabled)
<div>
<label class="mw-modal-label">Häufigkeit</label>
<div style="display:flex;gap:6px;flex-wrap:wrap">
@foreach(['hourly' => 'Stündlich', 'daily' => 'Täglich', 'weekly' => 'Wöchentlich', 'monthly' => 'Monatlich', 'custom' => 'Benutzerdefiniert'] as $val => $label)
<button type="button" wire:click="$set('backup_preset','{{ $val }}')"
style="padding:5px 14px;border-radius:6px;font-size:12px;cursor:pointer;transition:all .1s;
{{ $backup_preset === $val
? 'background:var(--mw-vbg);border:1px solid var(--mw-vbd);color:var(--mw-v2);font-weight:600'
: 'background:transparent;border:1px solid var(--mw-b2);color:var(--mw-t4)' }}">
{{ $label }}
</button>
@endforeach
</div>
</div>
@if($backup_preset !== 'hourly' && $backup_preset !== 'custom')
<div class="mw-modal-grid2">
<div>
<label class="mw-modal-label">Uhrzeit</label>
<input type="time" wire:model.live="backup_time" class="mw-modal-input">
</div>
@if($backup_preset === 'weekly')
<div>
<label class="mw-modal-label">Wochentag</label>
<select wire:model.live="backup_weekday" class="mw-modal-input">
@foreach(['0'=>'Sonntag','1'=>'Montag','2'=>'Dienstag','3'=>'Mittwoch','4'=>'Donnerstag','5'=>'Freitag','6'=>'Samstag'] as $v => $d)
<option value="{{ $v }}">{{ $d }}</option>
@endforeach
</select>
</div>
@elseif($backup_preset === 'monthly')
<div>
<label class="mw-modal-label">Tag des Monats</label>
<select wire:model.live="backup_monthday" class="mw-modal-input">
@for($d = 1; $d <= 28; $d++)
<option value="{{ $d }}">{{ $d }}.</option>
@endfor
</select>
</div>
@endif
</div>
@endif
@if($backup_preset === 'custom')
<div>
<label class="mw-modal-label">Cron-Ausdruck</label>
<input type="text" wire:model.live="backup_cron" class="mw-modal-input" placeholder="0 3 * * *" style="font-family:monospace">
<div class="mw-modal-hint">Standard Cron-Syntax: Minute Stunde Tag Monat Wochentag</div>
</div>
@endif
<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>
<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>
</div>
<div>
<label class="mw-modal-label">Aufbewahrung (Anzahl Backups)</label>
<input type="number" wire:model.defer="backup_retention" class="mw-modal-input" min="1" max="365" style="max-width:120px">
<div class="mw-modal-hint">Ältere Backups werden automatisch gelöscht. Empfehlung: 714.</div>
</div>
@endif
</div>
</div>
</div>
@endif
{{-- ═══ Tab: Benachrichtigungen ═══ --}}
@if($tab === 'benachrichtigungen')
<div class="mbx-sections" style="max-width:680px">
<div class="mbx-section">
<div class="mbx-domain-head">
<div class="mbx-domain-info"><span class="mbx-badge-mute">Benachrichtigungen</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">Absendername</label>
<input type="text" wire:model.defer="notify_sender_name" class="mw-modal-input" placeholder="CluBird Benachrichtigung">
<div class="mw-modal-hint">Wird als Absender in System-Mails angezeigt</div>
</div>
<div>
<label class="mw-modal-label">Admin-E-Mail</label>
<input type="email" wire:model.defer="notify_admin_email" class="mw-modal-input" placeholder="admin@example.com">
<div class="mw-modal-hint">Empfänger für Update- & Systemmeldungen</div>
</div>
</div>
@php
$sysmailFqdn = \App\Models\Setting::get('notify_from_address')
?: ('no-reply@' . config('mailpool.platform_system_zone','sysmail') . '.' . config('mailpool.platform_zone',''));
@endphp
<div style="display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:7px;background:var(--mw-bg3);border:1px solid var(--mw-b2)">
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;color:var(--mw-t4)"><rect x=".5" y="2.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M.5 5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
<span style="font-size:11px;color:var(--mw-t4)">Absenderadresse:</span>
<span style="font-family:monospace;font-size:11px;color:var(--mw-t2)">{{ $sysmailFqdn }}</span>
</div>
</div>
</div>
</div>
@endif
</div>