Dev-Server zeigte v1.0.137 weil /var/lib/mailwolt/version nie aktualisiert
wurde. git describe --tags liefert immer die korrekte aktuelle Version.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lokalserver zeigte 1.0.137 weil /var/lib/mailwolt/version nie durch
update.sh aktualisiert wurde. git describe --tags liefert immer den
aktuellen Tag direkt aus dem Repo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mailwolt-backup Script liest /etc/dovecot/private, /etc/letsencrypt/live
etc. als root. ensure_system() installiert es nach sbin + sudoers.
BackupRun.php ruft es via sudo -n auf.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
shell_exec kann auf PHP-FPM Servern deaktiviert sein, wodurch
version_remote nie aktualisiert wurde und kein Update angezeigt wurde.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Monit-Cache wurde jede Minute neu befüllt mit altem Status.
Optionale Dienste werden jetzt direkt via systemctl geprüft.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dashboard zeigte ClamAV bis zu 5 Min. falsch als online/offline weil
der Monit-Cache veraltet war. Cache wird jetzt sofort invalidiert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ensure_system() installiert clamav-daemon falls nicht vorhanden,
deaktiviert aber den Dienst. User sieht nur noch Ein/Ausschalten.
Install-Button, $installed-Property und install()-Methode entfernt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prüft /usr/sbin/clamd und systemd unit-Dateien ohne exec().
Zeigt Install-Button wenn ClamAV nicht installiert ist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
systemctl start --no-block kehrt sofort zurück, systemd startet ClamAV
im Hintergrund. runCmd('enable') direkt aufrufen statt Queue-Job.
wire:poll.keep-alive verhindert payload-Fehler in der Browser-Console.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Flag-Datei /tmp/mw-clamav-starting speichert Startzeitpunkt sodass
Spinner + Timer auch nach Seitenreload korrekt weiterläuft.
git_dirty_check filtert jetzt untracked Dateien (??) heraus damit
mailwolt-installer/ und ähnliche nicht fälschlich als dirty gelten.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
wire:poll.3s prüft serviceActive() nach Klick. Zeigt laufenden Spinner
mit Sekundenanzeige (0s, 3s, 6s…) bis ClamAV aktiv ist, dann Erfolgsmeldung.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nohup/shell_exec wird von PHP-FPM-Prozessgroup nach Request-Ende gekillt.
ClamavEnable Job läuft im Queue-Worker und blockiert nicht den Web-Request.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
exec() wartet auch mit & auf Prozessende. shell_exec mit nohup und
stdin-Redirect auf /dev/null stellt echtes Hintergrund-Detaching sicher.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
$starting-Property, pollStatus() und wire:poll entfernt.
enable() startet ClamAV im Hintergrund und zeigt nur eine
Info-Meldung an – kein dauerhafter Spinner.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- enable() startet Dienst via nohup im Hintergrund (kein Request-Timeout)
- $starting=true → wire:poll.3s ruft pollStatus() auf
- Overlay zeigt "ClamAV wird gestartet… lädt Viren-DB / bis 60s"
- pollStatus() erkennt wenn Dienst läuft → Overlay weg, Erfolg-Banner
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Loading-Overlay über Status-Karte beim Aktivieren/Deaktivieren/Installieren
- $lastSuccess Property für grünen Erfolg-Banner (kein Toast nötig)
- runCmd() helper mit Log für Debugging
- Fehler-Text white-space:pre-wrap für mehrzeilige Ausgaben
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neue Seite /security/clamav: Status, Aktivieren/Deaktivieren,
Signatur-Update, RAM-Hinweis, Info-Box
- Optionale Dienste (ClamAV) im Dashboard nur sichtbar wenn aktiv
- mailwolt-clamav sbin-Wrapper + sudoers-Regel in ensure_system
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- loadServices() liest Monit-Cache, fällt zurück auf woltguard.php Karten
mit direkten systemd/tcp-Probes wenn Cache leer ist
- Monit als Dienst in woltguard.php ergänzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Zertifikate einrichten/erneuern nur noch unter Sicherheit → SSL/TLS
- SSL-Seite: Provisioning mit Fortschritt, Ablaufdatum + Tage in Tabelle
- Einstellungen: nur noch read-only Status + Link zu SSL-Seite
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- mailwolt-apply-domains: MAIL_HOST wird in ACME-Challenge-Block aufgenommen,
certbot wird auch für die Mail-Domain ausgeführt, Postfix + Dovecot erhalten
danach automatisch das neue Zertifikat
- SslCertificatesTable: certbot-Ausgabe korrekt geparst (Einrückung mit Leerzeichen)
- settings-form: "kein Zertifikat nötig" entfernt (Mail-Domain braucht Zertifikat)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: state-Datei vom letzten Update zeigte noch 'done'. pollUpdate()
las das direkt nach runUpdate() als "Update fertig" — bevor der neue
Prozess überhaupt startete. displayCurrent war dann noch die alte Version.
Fix:
- runUpdate() setzt state='starting' und löscht rc VOR dem Shell-Aufruf
- runUpdate() setzt rc=null, lowState='starting', postActionsDone=false zurück
- refreshLowLevelState(): Kommentar klärt dass 'starting' als running gilt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- loadSslStatus(): clearstatcache() + renewal-Conf als primärer Existenzcheck
(renewal/ ist immer 755, zuverlässiger als is_dir auf live/ das 750 sein kann)
+ /usr/bin/openssl (voller Pfad für sudoers-Matching)
- restoreSslProvisioningState(): stellt letzten Provisioning-Zustand aus
State-Dateien wieder her, so dass Status nach Page-Reload sichtbar bleibt
- Button zeigt nach Abschluss "Erneut einrichten" statt disabled zu bleiben
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: /etc/letsencrypt/archive/ ist chmod 700 (root only).
file_exists() auf Symlinks in live/ schlägt fehl weil Symlink-Ziel
in archive/ nicht lesbar ist. is_dir() auf live/domain/ funktioniert
da das Verzeichnis selbst 755 ist.
- SettingsForm: file_exists() → is_dir() für Existenzcheck
- SettingsForm: openssl-Aufruf via sudo -n (archive/ ist root-only)
- installer.sh + update.sh: sudoers-Regel für openssl auf LE-Cert-Pfade
(www-data darf nur exakt diesen openssl-Aufruf, keine anderen Pfade)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: Henne-Ei-Situation — alte Server haben alte CheckUpdates.php die
git fetch als www-data aufruft (schlägt wegen fehlender Credentials fehl).
Neue mailwolt-fetch-tags Helper sind noch nicht deployed.
Lösung:
- scripts/update.sh: --check-only Flag — ruft nur git ls-remote als App-User
auf, schreibt version_remote, beendet sich sofort (kein Update)
- update.sh: schreibt version_remote auch nach normalem Tag-Fetch (damit es
nach manuellen Updates aktuell bleibt)
- CheckUpdates.php: sichere Fallback-Kette:
1. mailwolt-update --check-only (nur wenn Skript das Flag kennt — kein
versehentliches Triggern auf alten Servern)
2. mailwolt-fetch-tags (ab v1.1.268)
3. lokale git tags (letzter Fallback)
+ version_remote wird nur verwendet wenn < 2h alt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neuer Abschnitt "SSL-Zertifikate" in den Einstellungen:
· Zeigt pro Domain (UI, Webmail, Mailserver) ob LE-Cert vorhanden ist,
Ablaufdatum und Status (OK / fehlt / läuft ab / abgelaufen)
· Button "Let's Encrypt Zertifikate einrichten" startet Provisioning
im Hintergrund (nohup, non-blocking)
· Live-Fortschrittsanzeige per wire:poll.2s mit Status-Icons pro Domain
(pending → running → done/error/skip)
- saveDomains() ruft apply-domains jetzt ohne certbot auf (--ssl-auto 0) —
Domains speichern und SSL einrichten sind damit getrennte Aktionen
- loadSslStatus() liest Certbot-Zertifikat-Info direkt aus openssl
- spin-Keyframe für Spinner-Animation in app.css
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neu: scripts/mailwolt-fetch-tags ruft git ls-remote als App-User auf (hat Credentials),
schreibt neuesten Tag nach /var/lib/mailwolt/version_remote
- CheckUpdates.php nutzt jetzt sudo mailwolt-fetch-tags statt direktem git fetch als www-data
(www-data hat keine Git-Credentials für private Repos)
- SettingsForm.php zeigt tatsächliche Fehlerzeilen ([!], error, failed) im Toast statt
generischer "fehlgeschlagen"-Meldung
- installer.sh + update.sh installieren mailwolt-fetch-tags nach /usr/local/sbin/
- update.sh trägt mailwolt-fetch-tags automatisch in sudoers nach (Upgrade-Pfad)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
installer.sh / mailwolt-apply-domains:
- State-Dateien jetzt korrekt pro Domain: "done" nur wenn LE-Cert existiert,
sonst "error" — verhindert grüne Checkmarks bei fehlgeschlagenem certbot
- mail-Domain: "skip" statt fälschlich "done" (certbot läuft nicht für MX im Wizard)
- sleep 6 nur wenn Cert ausgestellt wurde (nginx-HTTPS-Switch nötig)
WizardDomains.php:
- Bei frühem DNS-Abbruch: verbleibende "running"-Domains auf "error" setzen
statt ewig als "Wird registriert…" hängen zu bleiben
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- mailwolt-apply-domains schreibt jetzt pro Domain running/done/error/nodns
in die State-Dateien während certbot läuft (statt alles auf einmal am Ende)
- get_cert_dir() erstellt fullchain.pem/privkey.pem Symlinks auf cert.pem/key.pem
wenn kein LE-Zertifikat vorhanden — verhindert nginx-t-Fehler und kaputten Redirect
- WizardDomains.php: ssl_configured wird jetzt anhand /etc/letsencrypt/live/ geprüft
statt per Shell-Output (der wegen exec>>LOG immer leer war)
- Shell-Script schreibt done-Datei selbst; PHP nur noch als Absturz-Fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pollSetup() macht keinen auto-redirect mehr (port 443 wäre noch nicht offen)
- "Zum Login" ist jetzt ein plain <a href="/login"> ohne Livewire-POST
→ nginx leitet /login nach SSL-Switch automatisch auf HTTPS weiter
- mailwolt-apply-domains schreibt done=1/0 (je nach Cert-Status) VOR nginx-Switch
+ sleep 6s damit Polling noch 3x done lesen kann bevor port 443 öffnet
- done=1 nur wenn mindestens ein LE-Cert erfolgreich ausgestellt wurde
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- mailwolt-apply-domains schreibt State-Dateien (done=1) BEVOR nginx auf HTTPS
switcht, dann sleep 6s → Browser kann noch über HTTP redirecten
- WizardDomains.php überschreibt done nicht wenn Shell-Script es bereits gesetzt hat
- fastcgi_param HTTPS on in HTTPS-Blocks ergänzt (ohne dies liefert Laravel 404
weil Request-Schema falsch erkannt wird)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SESSION_SECURE_COOKIE wird nicht mehr automatisch gesetzt (verursachte 419 während HTTP-Poll)
- pollSetup() leitet Browser sofort auf https://domain/setup weiter sobald SSL fertig
- verhindert dass Livewire-Polling über HTTP läuft während nginx schon auf HTTPS umgestellt hat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
wire:poll Div war bedingt gerendert – beim Entfernen aus dem DOM
versuchte Livewire noch es zu patchen → outerHTML Fehler.
Div bleibt jetzt immer im DOM, pollSetup() kehrt früh zurück wenn fertig.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HTTPS→HTTP Redirect war ein Workaround für SESSION_SECURE_COOKIE,
der jetzt durch den Installer-Fix (false initial) nicht mehr nötig ist.
fastcgi_param HTTPS on gehört nur in den 443-Block, nicht in den 80-Block.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Installer setzt SESSION_SECURE_COOKIE=false initial – damit Setup-Wizard
über http://ip erreichbar ist. WizardDomains setzt es auf true nach
erfolgreichem SSL. mount()-Redirect ohne exit (sauberer Return).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
route('login') nutzt die gecachte Config (alte IP/URL). Stattdessen
direkt auf https://{ui_domain}/login umleiten, da APP_URL erst nach
dem Prozess-Neustart greift.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vor SSL-Zertifikaten schlägt Livewires AJAX über HTTPS fehl.
mount() leitet automatisch auf http:// um damit der Wizard funktioniert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- installer.sh: mailwolt-apply-domains mit 3-Phasen certbot (HTTP → LE → SSL),
IPv6-Check vor certbot, Zertifikat-Ablauf-Check (10 Tage), Version-Datei schreiben
- WizardDomains: noipv6-Status aus Helper-Output erkennen
- Wizard: retryDomains()-Methode für Wiederholung ohne neuen Wizard-Durchlauf
- Step 5 Blade: Hints pro Fehlerstatus, Retry-Button, "Trotzdem zum Login"
- UpdatePage: Version aus Datei, Fallback auf git describe (kein "dev" mehr)
- UpdatePage: refreshLowLevelState behandelt fehlende State-Datei als idle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Kein certbot --nginx mehr im Wizard (scheitert an catch-all server_name)
- mailwolt-apply-domains erstellt Vhosts zuerst, dann certbot --webroot
- sudoers-Eintrag für certbot im Installer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- perPage (#[Url as:'limit']) bleibt nach Reload erhalten (25/50/100)
- Pagination zeigt max 5 Seiten (±2 um aktuelle) + 1/letzte mit ...
- Per-Seite-Select in Quarantäne und Queue eingefügt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Quarantäne und Queue zeigen je 25 Einträge pro Seite
- Pagination-Bar mit Seitenanzeige (X-Y von Z) und Blätter-Buttons
- Seite wird bei Filter- oder Suchwechsel auf 1 zurückgesetzt
- Quarantäne: rows-Select entfernt (API holt intern 500, UI paginiert)
- CSS-Klassen mq-pagination, mq-pag-btn passend zum Dark-Design
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auf APP_ENV=local wird die aktuelle Version direkt aus git describe
gelesen statt aus /var/lib/mailwolt/version — verhindert falschen
"Update verfügbar" Hinweis auf der Entwicklungsmaschine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- checkForUpdates() läuft jetzt synchron (nicht im Hintergrund), damit
das Ergebnis sofort angezeigt wird ohne Seite neu laden zu müssen
- Log-Viewer: white-space:pre-wrap + <br> entfernt durch display:block
pro Span — kein doppelter Zeilenabstand mehr
- Auto-Scroll Checkbox nutzt jetzt mw-modal-check Klassen (passend zum Design)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>