Feature: Domain Auto-fill, SSL-Skip, Dashboard SSL-Banner

- Wizard Schritt 2: leere Domain-Felder werden beim Tippen auto-gefüllt
  (wer nur eine Domain nutzt muss sie nur einmal eingeben)
- Wizard Schritt 4: Checkbox "SSL jetzt überspringen" mit Hinweistext
- Wizard Schritt 5: skip-Status wird pro Domain angezeigt
- WizardDomains schreibt ssl_configured=0/1 in Settings
- SettingsForm: setzt ssl_configured=1 nach erfolgreichem applyDomains
- Dashboard: gelber Banner wenn ssl_configured != 1, Link zu Einstellungen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main v1.1.148
boban 2026-04-23 02:36:11 +02:00
parent 077a029ff4
commit 627ef668e5
6 changed files with 47 additions and 2 deletions

View File

@ -2,6 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\Setting;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class WizardDomains extends Command class WizardDomains extends Command
@ -81,6 +82,7 @@ class WizardDomains extends Command
} }
file_put_contents(self::STATE_DIR . '/done', $allOk ? '1' : '0'); file_put_contents(self::STATE_DIR . '/done', $allOk ? '1' : '0');
Setting::set('ssl_configured', $allOk ? '1' : '0');
return self::SUCCESS; return self::SUCCESS;
} }
} }

View File

@ -26,6 +26,9 @@ class Wizard extends Component
public string $mail_domain = ''; public string $mail_domain = '';
public string $webmail_domain = ''; public string $webmail_domain = '';
// Schritt 4 — Option
public bool $skipSsl = false;
// Schritt 3 — Admin-Account // Schritt 3 — Admin-Account
public string $admin_name = ''; public string $admin_name = '';
public string $admin_email = ''; public string $admin_email = '';
@ -52,6 +55,18 @@ class Wizard extends Component
$this->webmail_domain = Setting::get('webmail_domain', ''); $this->webmail_domain = Setting::get('webmail_domain', '');
} }
public function updatedUiDomain(): void { $this->fillEmptyDomains($this->ui_domain); }
public function updatedMailDomain(): void { $this->fillEmptyDomains($this->mail_domain); }
public function updatedWebmailDomain(): void { $this->fillEmptyDomains($this->webmail_domain); }
private function fillEmptyDomains(string $value): void
{
if ($value === '') return;
if ($this->ui_domain === '') $this->ui_domain = $value;
if ($this->mail_domain === '') $this->mail_domain = $value;
if ($this->webmail_domain === '') $this->webmail_domain = $value;
}
public function next(): void public function next(): void
{ {
match ($this->step) { match ($this->step) {
@ -133,7 +148,7 @@ class Wizard extends Component
file_put_contents(self::STATE_DIR . "/{$k}", 'pending'); file_put_contents(self::STATE_DIR . "/{$k}", 'pending');
} }
$ssl = app()->isProduction() ? 1 : 0; $ssl = (!$this->skipSsl && app()->isProduction()) ? 1 : 0;
$artisan = base_path('artisan'); $artisan = base_path('artisan');
$cmd = sprintf( $cmd = sprintf(
'nohup php %s mailwolt:wizard-domains --ui=%s --mail=%s --webmail=%s --ssl=%d > /dev/null 2>&1 &', 'nohup php %s mailwolt:wizard-domains --ui=%s --mail=%s --webmail=%s --ssl=%d > /dev/null 2>&1 &',

View File

@ -35,7 +35,10 @@ class Dashboard extends Component
$servicesActive = count(array_filter($services, fn($s) => $s['status'] === 'online')); $servicesActive = count(array_filter($services, fn($s) => $s['status'] === 'online'));
$sslConfigured = SettingModel::get('ssl_configured', '1') === '1';
return view('livewire.ui.nx.dashboard', [ return view('livewire.ui.nx.dashboard', [
'sslConfigured' => $sslConfigured,
'domainCount' => Domain::where('is_system', false)->where('is_server', false)->count(), 'domainCount' => Domain::where('is_system', false)->where('is_server', false)->count(),
'mailboxCount' => MailUser::where('is_system', false)->where('is_active', true)->count(), 'mailboxCount' => MailUser::where('is_system', false)->where('is_active', true)->count(),
'servicesActive' => $servicesActive, 'servicesActive' => $servicesActive,

View File

@ -306,6 +306,7 @@ class SettingsForm extends Component
\Illuminate\Support\Facades\Log::info('mailwolt-apply-domains', ['output' => $output]); \Illuminate\Support\Facades\Log::info('mailwolt-apply-domains', ['output' => $output]);
if ($ok) { if ($ok) {
Setting::set('ssl_configured', '1');
$this->dispatch('toast', type: 'done', badge: 'Nginx', $this->dispatch('toast', type: 'done', badge: 'Nginx',
title: 'Nginx aktualisiert', title: 'Nginx aktualisiert',
text: 'Nginx-Konfiguration wurde neu geladen.', text: 'Nginx-Konfiguration wurde neu geladen.',

View File

@ -151,6 +151,16 @@
@endforeach @endforeach
</div> </div>
<div style="margin-top:16px;padding:12px 14px;background:var(--mw-bg3);border-radius:8px;border:1px solid var(--mw-b2)">
<label class="mw-modal-check">
<input type="checkbox" wire:model="skipSsl">
<span class="mw-modal-check-label">SSL jetzt überspringen später in den Einstellungen einrichten</span>
</label>
@if($skipSsl)
<div style="margin-top:8px;font-size:11.5px;color:#fbbf24;padding-left:23px">Nginx wird ohne SSL konfiguriert. Im Dashboard erscheint ein Hinweis bis SSL eingerichtet ist.</div>
@endif
</div>
{{-- ── Schritt 5: Domain-Setup ── --}} {{-- ── Schritt 5: Domain-Setup ── --}}
@elseif($step === 5) @elseif($step === 5)
@if(!$setupDone) @if(!$setupDone)
@ -170,7 +180,7 @@
'done' => ['icon' => '✓', 'color' => 'rgba(34,197,94,.9)', 'bg' => 'rgba(34,197,94,.07)', 'label' => 'Abgeschlossen'], 'done' => ['icon' => '✓', 'color' => 'rgba(34,197,94,.9)', 'bg' => 'rgba(34,197,94,.07)', 'label' => 'Abgeschlossen'],
'nodns' => ['icon' => '!', 'color' => '#fbbf24', 'bg' => 'rgba(251,191,36,.07)', 'label' => 'Kein DNS-Eintrag gefunden'], 'nodns' => ['icon' => '!', 'color' => '#fbbf24', 'bg' => 'rgba(251,191,36,.07)', 'label' => 'Kein DNS-Eintrag gefunden'],
'error' => ['icon' => '✗', 'color' => '#f87171', 'bg' => 'rgba(239,68,68,.07)', 'label' => 'Fehler bei Registrierung'], 'error' => ['icon' => '✗', 'color' => '#f87171', 'bg' => 'rgba(239,68,68,.07)', 'label' => 'Fehler bei Registrierung'],
'skip' => ['icon' => '', 'color' => 'var(--mw-t5)', 'bg' => 'var(--mw-bg3)', 'label' => 'Übersprungen'], 'skip' => ['icon' => '', 'color' => 'var(--mw-t4)', 'bg' => 'var(--mw-bg3)', 'label' => 'SSL übersprungen'],
]; ];
@endphp @endphp

View File

@ -3,6 +3,20 @@
<div wire:poll.30s> <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 --}} {{-- Hero Banner --}}
<div class="mw-hero"> <div class="mw-hero">
<div class="mw-hero-icon"> <div class="mw-hero-icon">