Releases: malziland/Seafile-Updraft-Backup-Uploader
v1.0.7 — Worker-Crash-Notbremse gegen 17h-Resume-Loops
Fix: Worker-Crash-Notbremse
Verhindert, dass ein einzelner Chunk, der den PHP-Prozess wiederholt killt, die gesamte Upload-Queue stundenlang blockiert.
Konkret beobachtet in Produktion: eine 172 MB große uploads.zip blieb wiederholt bei genau 120 MB stehen (PHP-Prozess wurde vom Hoster mitten im Chunk-PUT abgeschossen), Resume nahm korrekt bei 120 MB wieder auf — und starb dort sofort wieder. 17,8 Stunden verschwendet, bis der Queue-Gesamttimeout griff. Die nachfolgende wpcore.zip wurde nie hochgeladen.
Neue dreistufige Eskalation
- Erster Crash → Retry mit exponentiellem Backoff (bestand schon).
- Zweiter Crash am selben Byte → Chunk-Größe für diese eine Datei wird automatisch halbiert (per-File-Override, Untergrenze 4 MiB). Andere Dateien der Queue bleiben unberührt.
- Chunk schon am Floor und immer noch derselbe Crash, oder >5 Crash-Wiederaufnahmen pro Datei → Datei als Fehler markiert, die Queue läuft mit den nächsten Dateien weiter. Besser eine fehlgeschlagene Datei als ein 17-h-Stillstand der gesamten Backup-Übertragung.
Diagnose im Aktivitätsprotokoll
Zwei neue Log-Einträge machen die Eskalation sichtbar:
WARNUNG: Worker still abgestürzt vor X min an gleicher Stelle — Chunk-Größe auf Y MB reduziert, Wiederaufnahme bei …FEHLER: Datei nach wiederholten Worker-Abstürzen übersprungen: … — Queue läuft mit den nächsten Dateien weiter
Tests
Drei neue Unit-Tests in CrashDetectionGateTest (Retry-Cap-Skip, Same-Offset-Chunk-Halbierung, Floor-erreicht-Skip). 123 Tests / 343 Assertions grün, PHPStan ohne Befund.
Installation
Zip herunterladen und über WP-Admin → Plugins → Neu hinzufügen → Plugin hochladen installieren oder aktualisieren. Keine Migration nötig.
Vollständige Änderungsliste siehe CHANGELOG.md.
v1.0.6 — Einzeldatei-Download entfernen (UI-Aufräumen)
Was sich ändert
Der Einzeldatei-Download-Button im „Dateien anzeigen"-Panel ist weg. Das Panel zeigt jetzt nur noch Dateiname und Größe — eine reine Inhaltsliste ohne Aktionen.
Warum
Der Button hieß „Download", triggerte aber keinen Browser-Download:
- Serverseitige Kopie statt Browser-Download.
SBU_Plugin::ajax_download()lud die Datei von Seafile in den lokalenwp-content/updraft/-Ordner. Der Browser zeigte keinen Download-Balken, weil nichts zum Browser geschickt wurde. - Erfolgsmeldung außerhalb des Sichtfelds. Die Meldung „In UpdraftPlus auf 'Lokalen Ordner neu scannen' klicken" landete im gemeinsam genutzten
#sr-Statusfeld zwei Panels höher — bei gescrollter Seite schlicht nicht sichtbar. Das erzeugte den „nichts passiert"-Eindruck. - Confirm-Dialog ohne Erklärung.
confirm(f)fragte nur den rohen Dateinamen (z. B.backup_2026-04-17-1630_INTERIORISTA_1d2b1dcea52d-db.gz) ab, ohne zu sagen, was OK auslöst.
Warum entfernt statt repariert
- Kein realistischer Use-Case. UpdraftPlus-Chunks (
uploads7.zip,uploads14.zip, …) sind semantisch undurchsichtig — eine einzelne Datei aus dem Set ist ohne Kontext nicht restore-fähig. - „Wiederherstellen" deckt den Restore-Fall ab. Der bestehende Restore-Pfad erkennt „Teilweise lokal"-Sets und zieht genau die fehlenden Dateien nach — gezielter Re-Download ist also schon drin.
- Seafile-Weboberfläche deckt den Inspektions-Fall ab. Wer eine einzelne
.gzaußerhalb von WordPress untersuchen will, lädt sie direkt aus der Seafile-Weboberfläche.
Aufgeräumt (tote Code-Pfade)
SBU_Plugin::ajax_download()— Handler vollständig entfernt.'download'aus der AJAX-Hook-Registrierung —wp_ajax_sbu_downloadist weg.SBU_Seafile_API::download_file()— nur vonajax_download()gerufen, jetzt tot, raus.SBU_Seafile_API::DEFAULT_DOWNLOAD_CHUNK— nur vondownload_file()referenziert, raus.window.sDl+'download-file'-Event-Delegation-Eintrag inadmin.js— raus.<button data-sbu-action=\"download-file\">inSBU_Plugin::ajax_list()— raus.
Der im Restore-Flow aktive Download-Pfad (download_whole_file_stream + download_chunks_parallel) bleibt unverändert.
Migration
Keine. Einstellungen, Queue-State und Protokoll-Einträge bleiben intakt. Upload über Plugins → Hochladen in WP-Admin.
Tests
121 Tests / 333 Assertions — unverändert. PHPCS, PHPStan Level 5 und PHPUnit 11 grün auf PHP 8.2 / 8.3 / 8.4.
v1.0.5 — Breaking: PHP 8.2 + PHPUnit 11 + erweiterte Testsuite
Breaking
- PHP-Mindestanforderung 7.4 → 8.2. Plugin-Header,
composer.jsonundreadme.txtsind jetzt konsistent auf 8.2. WordPress blockiert die Aktivierung auf älteren PHP-Versionen mit eigenem Hinweis. Supporte PHP-Matrix: 8.2 / 8.3 / 8.4 (alle drei in CI getestet). Vor dem Update die PHP-Version des Hosts prüfen.
Geändert
- PHPUnit 9.6 → 11.5. Alle 12 bestehenden Test-Dateien von
@covers-Docblocks auf PHP-8-Attribute (#[CoversClass]/#[CoversMethod]) migriert — null Deprecations mehr. - CI-Matrix auf PHP 8.2 / 8.3 / 8.4 umgestellt.
Hinzugefügt
tests/unit/SeafileApiTest.php— 17 HTTP-gemockte Tests fürSBU_Seafile_API(Token-Cache, Library-Resolve, Upload-/Download-Link, Directory-Ops).tests/unit/LogSanitizerTest.php— 12 Tests für den anonymisierten Log-Export (Host-, UUID-, Ordner-, E-Mail-, IP-, Nonce-Maskierung) plus End-zu-End-Leak-Check.- Neue Screenshots für
readme.txtundREADME.md: Einstellungsseite (Backup-Browser + Aktivitätsprotokoll) und Dashboard-Widget mit Demo-Daten.
Dokumentation
README.md,ARCHITECTURE.md,CONTRIBUTING.mdauf den aktuellen Code-Stand gezogen: Modul-Boundaries-Tabelle, Test-Surface-Tabelle, reproduzierbarer Release-Workflow (rsync + zip), explizite Quality-Gate-Befehle.
Tests
121 Tests / 333 Assertions (vorher 92 / 263). PHPCS (WordPress-Standard, Plugin-Source), PHPStan Level 5 und PHPUnit 11 grün auf PHP 8.2 / 8.3 / 8.4.
Upgrade-Hinweis
Vor dem Update die PHP-Version des Hosts prüfen (mindestens 8.2). Danach keine manuelle Migration nötig — Upload über Plugins → Hochladen in WP-Admin.
v1.0.4 — ARCH-001 abgeschlossen
ARCH-001 ist mit diesem Release komplett durch: die beiden letzten Monolith-Fragmente sind raus aus der God-Class. Reines Refactoring, Verhalten 1:1, 92 PHPUnit-Tests / 263 Assertions grün.
Refaktoriert
- Schritt 4 —
SBU_Admin_Ajaxals Trait — alle 24 Ajax-Handler (Verbindungstest, Upload-/Restore-Queue-Kontrolle, Backup-Liste, Download-Stream, Log-Export, Settings-Autosave, Cron-Endpoints) liegen jetzt inincludes/trait-sbu-admin-ajax.php. Trait-Komposition zur Compile-Zeit hält die Zugriffe auf private Plugin-Helfer (verify_ajax_request,get_picker_credentials,format_progress,sanitize_path_segment…) intakt — keine Visibility-Promotion, keine fragile Plugin-Referenz. - Schritt 5 —
SBU_Upload_Flow+SBU_Restore_Flowals Traits — der komplette Upload-Lebenszyklus (on_backup_complete,create_upload_queue,process_queue_tick,upload_one_chunk,finish_queue,verify_backup,persist_backup_hashes,enforce_retention,cleanup_updraft_history,find_backup_files,extract_backup_nonce) wohnt inincludes/trait-sbu-upload-flow.php. Der Restore-Lebenszyklus (verify_restored_file,process_restore_tick) inincludes/trait-sbu-restore-flow.php. Gemeinsame Helfer (safe_queue_update,detect_worker_crash_and_defer,maybe_notify_stall,get_adaptive_limits,tick_budget_exhausted,is_aborted,log_failed_files,get_updraft_dir,get_memory_limit) bleiben inSBU_Plugin, weil beide Flows sie brauchen. SBU_Pluginjetzt ~1100 Zeilen — von ursprünglich 4201 Zeilen (−74 %). Die Klasse ist wieder als Koordinationslayer lesbar: Settings, Admin-UI, Cron-Auth, Tick-Sizing, Queue-Lifecycle-Helfer, Trait-Komposition.
Geändert
SBU_Queue_Engine::tick_is_gated()als@phpstan-impuremarkiert — die Methode liestSBU_QUEUEbei jedem Aufruf frisch viawp_cache_delete. PHPStan hielt wiederholte Aufrufe nach der Trait-Komposition für redundant (if.alwaysTrueinajax_cron_ping). Der Analyzer kannte die Mutations-Semantik nicht; die Annotation dokumentiert sie.
Tests & Qualität
- 92 PHPUnit-Tests / 263 Assertions — alle grün.
- PHPCS (WordPress-Standard, Plugin-Source) sauber.
- PHPStan Level 5 sauber.
- Keine Test-Anpassungen nötig — Traits komponieren zur Compile-Zeit in
SBU_Plugin, die bestehendenSBU_Plugin::*-Referenzen in den Tests funktionieren unverändert.
Upgrade Notice
Reines Refactoring, keine Migration nötig. Download-Asset: seafile-updraft-backup-uploader-1.0.4.zip.
v1.0.3 — Cron-Key-Header, Brute-Force-Schutz, ARCH-001 Schritte 1-3
Zweiter Audit-Durchgang: UI-Feinschliff, Härtung des internen Cron-Pfads, drei Services aus der God-Class rausgezogen.
Sicherheit
- Cron-Key im Header statt im POST-Body — der interne Loopback-Spawn sendet den Schlüssel jetzt über den
X-SBU-Cron-Key-Header. Er taucht damit nicht mehr in Debug-/Error-Tracker-Dumps auf, die Request-Bodies mitloggen. Rückwärtskompatibel: der Empfänger prüft weiterhin Header, Query-Param und Body in dieser Reihenfolge. - Brute-Force-Signal im Aktivitätsprotokoll — fünf ungültige Versuche innerhalb einer Stunde lösen einmalig eine
WARNUNG-Zeile aus (1-Stunden-Transient als Zähler). Vorher: stumme 403s.
UI
- Status-Pillen zentriert —
Lokal vollständig/Teilweise lokal/Nur remoteund der Verify-Status sitzen jetzt auf einer sauberen Baseline statt treppenartig versetzt. Fix im Flex-Layout:align-items:centerstattbaseline. - 24 weitere Inline-
onclickentfernt — die Buttons in der Backup-Liste nutzen jetzt ebenfallsdata-sbu-action. Kombiniert mit 1.0.2 ist das Plugin damit vollständig inline-JavaScript-frei.
Betrieb
- Zero-Traffic-Backstop für das Log-Pruning — zusätzlich zum täglichen WP-Cron räumt die Retention jetzt auch beim ersten
admin_initpro Admin-Zugriff auf. Idle-Sites bleiben im Retention-Fenster.
Refaktoriert (ARCH-001 Schritte 1–3)
SBU_Activity_Logals eigene Klasse (Log-Schreiben, Retention, Cron, Render). 77 Aufrufstellen umgestellt, Verhalten identisch.SBU_Mail_Notifierals eigene Klasse (Admin-Mails Erfolg/Fehler/Stillstand).SBU_Queue_Engineals eigene Klasse (atomares Lock, Lock-TTL, Gate-Check, Loopback-Spawn, WP-Cron-Scheduling).
Konstruktor-DI via Callables, damit die Services nicht auf SBU_Plugin koppeln.
Schritte 4 + 5 verschoben auf v1.0.4 — die Ajax-Controller-Extraktion (24 Handler) und die Upload-/Restore-Flow-Trennung brauchen einen isolierten Release, damit Regressions-Risiken in produktiven Backup-Pfaden gezielt reviewbar bleiben.
Tests
92 PHPUnit-Tests / 263 Assertions — alle grün. PHPCS (WordPress-Standard) und PHPStan (Level 5) sauber.
Installation: seafile-updraft-backup-uploader-1.0.3.zip herunterladen → WordPress → Plugins → Plugin hochladen.
v1.0.2 — Audit-Umsetzung (Datenschutz, Wartbarkeit, CI)
Audit-Umsetzung: Datenschutz, Wartbarkeit, CI-Schärfe.
Hinzugefügt
- Zeit-basierte Aufbewahrung für das Aktivitätsprotokoll (Default 30 Tage, konfigurierbar 7–365 Tage oder 0 = deaktiviert). Alte Einträge werden einmal täglich per Cron und opportunistisch beim Schreiben gelöscht. Das bisherige Zeilen-Limit (500 Zeilen) bleibt als zusätzliche Obergrenze erhalten.
- Warn-Header im Log-Export (unmaskierte Variante) weist jetzt unübersehbar darauf hin, dass der Export identifizierende Daten (Host, Bibliothek, Pfade, E-Mail) enthält — für Support-Weitergabe wird „Anonymisiert exportieren" empfohlen.
- Neue Einstellung „Aktivitätsprotokoll aufbewahren" auf der Plugin-Seite mit Laien-Erklärung (Standard-Empfehlung 30 Tage, Hinweis auf DSGVO-Datensparsamkeit).
- 5 neue Unit-Tests für die Retention-Logik.
Geändert
- Inline-
onclick-Handler aus dem Admin-Template entfernt — 9 Buttons nutzen jetztdata-sbu-action-Attribute, ein zentraler Event-Delegate inadmin.jsroutet auf die Funktionen. Erleichtert künftige Wartung und strengere Content-Security-Policies. - CI-Pipeline scharf gestellt — PHPCS und PHPStan brechen den Build jetzt bei Fehlern ab (vorher mit
|| truemaskiert). PHPStan läuft mit--memory-limit=2G. - Ein Code-Review-Durchlauf mit echten Bugfixes:
wp_unslash()vorsanitize_path_segment()in Ajax-Download/Delete, gecachtecount()-Aufrufe,translators-Kommentare für komplexe i18n-Strings, Parameter-Rename.
Sicherheit
- Retention-Clamp gegen Panik-Eingaben — Werte zwischen 1 und 6 Tagen werden automatisch auf 7 angehoben, damit ein Fehlklick im Admin das Protokoll nicht in einem Tick vollständig leert. 0 bleibt als explizite Deaktivierung.
- Datenminimierung im Default-Fall — Aktivitätsprotokoll-Einträge älter als 30 Tage werden ohne weitere Konfiguration entfernt. Unterstützt die DSGVO-Forderung nach Datensparsamkeit.
Tests
92 PHPUnit-Tests / 263 Assertions, alle grün. PHPCS (WordPress-Standard) und PHPStan (Level 5) sauber.
Upgrade
Keine Migration nötig. Aktivitätsprotokoll wird automatisch nach 30 Tagen aufgeräumt (konfigurierbar).
v1.0.1 — UI-Politur der Einstellungsseite
Kleine Optik-Release, keine Code-Logik geändert.
Geändert
- Integritätsprüfungs-Hinweis verschoben — aus dem Einstellungsblock (wo er neben Checkboxen keinen Mehrwert hatte) in den Erklär-Bereich „So funktioniert das Plugin". Dort jetzt als eigener hervorgehobener Absatz mit grünem Rand und 🔒-Icon, direkt unter der Upload/Restore-Zweispaltigkeit.
- Toolbar im Aktivitätsprotokoll aufgeräumt — Filter-Dropdown und die drei Aktions-Buttons (Export / Anonymisiert exportieren / Log leeren) sind jetzt in zwei Flex-Gruppen aufgeteilt. Auf schmalen Bildschirmen (< 600 px) stapeln die Gruppen sauber untereinander, Buttons teilen sich die Breite gleichmäßig — kein chaotisches Umbrechen mehr mitten in den Buttons.
Upgrade
Drop-in. Kein Migrations-Aufwand, keine neuen Settings, keine Datenbank-Änderungen. 87 Tests / 257 Assertions weiterhin grün.
v1.0.0 — Erste öffentliche Version
Erste öffentliche Version des Seafile Updraft Backup Uploader.
Das Plugin koppelt UpdraftPlus an einen selbst gehosteten Seafile-Server über die native Seafile-Upload-API — nicht über WebDAV. Das ist notwendig, weil WebDAV auf Seafile keine Chunked Uploads unterstützt: Dateien größer als das Reverse-Proxy-Limit (etwa 100 MB bei Cloudflare Tunnel Free Tier) lassen sich über WebDAV schlicht nicht hochladen.
Funktionsumfang
- Chunked Upload (Default 40 MB, konfigurierbar 5–90 MB) — bypasst 100-MB-Reverse-Proxy-Limits. Bei Fehler wird nur der fehlgeschlagene Chunk wiederholt.
- Stream-First-Restore — Downloads nutzen zuerst denselben Pfad wie die Seafile-Web-Oberfläche (ein HTTP-GET ohne Range-Header). Für Dateien über 500 MB oder bei Stream-Fehlern fällt das Plugin automatisch auf parallele Range-Chunks zurück.
- Adaptive Konfiguration ohne Einstellungen — Tick-Länge, Parallelität und Chunk-Größe werden beim Restore automatisch aus
max_execution_timeundmemory_limitberechnet. - Exponentielles Backoff mit zwei Kurven — unterscheidet zwischen leeren Server-Antworten (Backend kalt) und echten Transportfehlern (Netz wackelt). Obergrenze 1 Stunde.
- AIMD-Rate-Controller — Chunk-Größe und Parallelität schrumpfen nach Fehlern und wachsen gestaffelt zurück.
- Stillstand-Meldung per Mail ohne Abbruch — bei 1 h Stillstand geht eine Info-Mail raus, Folge-Mails alle 4 h. Die Queue läuft weiter.
- Integritätsprüfung ohne Extra-Bandbreite — beim Upload wird pro Datei streamend eine SHA1-Prüfsumme berechnet; beim Restore aus dem Download-Stream verglichen.
- Pause & Resume — Uploads und Restores lassen sich an exakt derselben Byte-Position fortsetzen.
- Zero-Traffic-Betrieb — läuft ohne Besucher, ohne WP-Cron-Dependency, ohne externe Dienste. Interner WordPress-Loopback zieht jeden Tick nach.
- Optionaler externer Heartbeat — schlüsselgeschützte Ping-URL mit Crontab-Beispiel für Umgebungen, die Loopbacks blockieren.
- Lokal-Status-Badges im Backup-Browser — „Lokal vollständig / Teilweise lokal / Nur remote". Bei vollständig lokalem Set wird der Wiederherstellen-Button durch „In UpdraftPlus öffnen" ersetzt.
- Erfolgs-Banner nach Restore mit UpdraftPlus-Deeplink.
- Anonymisierter Log-Export — Host, Library-ID, Ordnerpfad, E-Mails, IPs und UUIDs werden maskiert.
- Dashboard-Widget, E-Mail-Benachrichtigungen, Retention-Management (Default 4, 0 = alle behalten).
- AES-256-CBC Passwortverschlüsselung mit zufälligem IV.
- Mehrsprachig (Deutsch + Englisch, über WordPress-Locale).
Anforderungen
- WordPress 6.0+
- PHP 7.4+
- UpdraftPlus (Free oder Premium)
- Ein Seafile-Server (self-hosted oder Cloud)
Installation
seafile-updraft-backup-uploader_1.0.0.zipaus den Release-Assets herunterladen.- In WordPress unter Plugins → Installieren → Plugin hochladen einspielen und aktivieren.
- Einstellungen → Seafile Backup öffnen, Seafile-URL, Zugangsdaten, Bibliothek und Unterordner eintragen, „Verbindung testen" klicken.
- In UpdraftPlus den Remote-Speicher auf „Keine" setzen — dieses Plugin übernimmt den Upload.
Tests
87 PHPUnit-Tests / 257 Assertions, alle grün.