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

344 lines
22 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">
<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>
</div>
</div>
<div class="sec-layout">
{{-- ═══ Main ═══ --}}
<div style="min-width:0">
<div class="mbx-sections">
{{-- Allgemein --}}
<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>
{{-- 2FA --}}
<livewire:ui.system.two-fa-status />
{{-- Backup --}}
<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">
{{-- Enable toggle --}}
<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>
{{-- Was sichern --}}
<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)
{{-- Preset buttons --}}
<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>
{{-- Time / day pickers --}}
@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
{{-- 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">
<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>
{{-- Aufbewahrung --}}
<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>
{{-- 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 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>
@if(app()->isProduction())
<div style="display:flex;align-items:center;gap:6px;padding:8px 10px;background:rgba(34,197,94,.06);border:1px solid rgba(34,197,94,.2);border-radius:7px">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" style="color:#22c55e;flex-shrink:0"><path d="M2 6.5l2.5 2.5 5.5-5.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span style="font-size:11.5px;color:rgba(34,197,94,.85)">Let's Encrypt SSL wird automatisch eingerichtet</span>
</div>
@else
<div style="display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:rgba(99,102,241,.06);border:1px solid rgba(99,102,241,.2);border-radius:7px">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" style="color:#818cf8;flex-shrink:0;margin-top:1px"><circle cx="6" cy="6" r="5" stroke="currentColor" stroke-width="1.2"/><path d="M6 5.5v3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/><circle cx="6" cy="4" r=".6" fill="currentColor"/></svg>
<span style="font-size:11.5px;color:rgba(129,140,248,.9);line-height:1.5">Local-Modus — SSL wird beim Speichern übersprungen (NPM übernimmt)</span>
</div>
<button wire:click="openSslModal"
class="mbx-btn-mute" style="width:100%;justify-content:center;font-size:11.5px">
<svg width="11" height="11" 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.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>
SSL manuell erzwingen (certbot)
</button>
@endif
</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>