diff --git a/LICENSE b/LICENSE index d73695b..cb7fb9c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 -Copyright (C) 2026 Backupper Project +Copyright (C) 2026 Michael Roll This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software diff --git a/README.md b/README.md index d477065..23b79f2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# Backupper for Unraid +# URBM - Unraid Restic Backup Manager -Backupper is a native Unraid 7 plugin that manages encrypted, deduplicated and versioned backups through Restic. The MVP supports shares, appdata, Docker metadata and data, VM definitions and disks, and the Unraid flash drive. Destinations can be local, SFTP, SMB or NFS. +URBM is a native Unraid 7 plugin that manages encrypted, deduplicated and versioned backups through Restic. It supports shares, appdata, Docker metadata and data, VM definitions and disks, and the Unraid boot drive. Destinations can be local, SFTP, SMB or NFS. + +URBM is an independent community plugin and is not affiliated with or endorsed by Lime Technology, Inc. ## Architecture diff --git a/dist/backupper-2026.06.14.9.2-x86_64-1.txz.sha256 b/dist/backupper-2026.06.14.9.2-x86_64-1.txz.sha256 deleted file mode 100644 index e11854c..0000000 --- a/dist/backupper-2026.06.14.9.2-x86_64-1.txz.sha256 +++ /dev/null @@ -1 +0,0 @@ -8440c745eb052c38ce853789d10c1663e6dc0e77ad07b99079341bd4249546a6 backupper-2026.06.14.9.2-x86_64-1.txz diff --git a/dist/backupper-2026.06.14.9.2-x86_64-1.txz b/dist/backupper-2026.06.14.9.3-x86_64-1.txz similarity index 55% rename from dist/backupper-2026.06.14.9.2-x86_64-1.txz rename to dist/backupper-2026.06.14.9.3-x86_64-1.txz index 806634f..9acfbd2 100644 Binary files a/dist/backupper-2026.06.14.9.2-x86_64-1.txz and b/dist/backupper-2026.06.14.9.3-x86_64-1.txz differ diff --git a/dist/backupper-2026.06.14.9.3-x86_64-1.txz.sha256 b/dist/backupper-2026.06.14.9.3-x86_64-1.txz.sha256 new file mode 100644 index 0000000..8d4c4de --- /dev/null +++ b/dist/backupper-2026.06.14.9.3-x86_64-1.txz.sha256 @@ -0,0 +1 @@ +10324922ef4211fa691d4f0c659c3e4574c915d04826d57e1adfa86bc5de007d backupper-2026.06.14.9.3-x86_64-1.txz diff --git a/dist/backupper.plg b/dist/backupper.plg index d3b6fb1..bab9434 100644 --- a/dist/backupper.plg +++ b/dist/backupper.plg @@ -2,13 +2,17 @@ - + - + ]> +### 2026.06.14.9.3 +- Rename the visible product to URBM - Unraid Restic Backup Manager. +- Show Backup in the Unraid navigation while preserving the existing plugin identifier and configuration paths. + ### 2026.06.14.9.2 - Include duration, new and changed files, added data, total backup size, file count, and snapshot ID in backup notifications. diff --git a/internal/notify/notify.go b/internal/notify/notify.go index 5ee7518..c3aa466 100644 --- a/internal/notify/notify.go +++ b/internal/notify/notify.go @@ -39,7 +39,7 @@ func (s *Sender) Send(ctx context.Context, target model.NotificationTarget, titl if path == "" { path = "/usr/local/emhttp/webGui/scripts/notify" } - args := []string{"-e", "Backupper", "-s", title, "-d", message, "-i", importance, "-l", "/Tools/Backupper"} + args := []string{"-e", "URBM", "-s", title, "-d", message, "-i", importance, "-l", "/Tools/Backupper"} if s.RunCommand != nil { return s.RunCommand(ctx, path, args...) } diff --git a/internal/notify/notify_test.go b/internal/notify/notify_test.go index ba57d06..a556fff 100644 --- a/internal/notify/notify_test.go +++ b/internal/notify/notify_test.go @@ -56,7 +56,7 @@ func TestSendUnraid(t *testing.T) { if command != "/usr/local/emhttp/webGui/scripts/notify" { t.Fatalf("unexpected command %q", command) } - want := []string{"-e", "Backupper", "-s", "Backup warning", "-d", "completed with warnings", "-i", "warning", "-l", "/Tools/Backupper"} + want := []string{"-e", "URBM", "-s", "Backup warning", "-d", "completed with warnings", "-i", "warning", "-l", "/Tools/Backupper"} if !reflect.DeepEqual(args, want) { t.Fatalf("arguments = %#v, want %#v", args, want) } diff --git a/internal/service/service.go b/internal/service/service.go index 11155e2..b35cadc 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -328,7 +328,7 @@ func (s *Service) notify(name string, run model.Run) { for _, target := range s.Config().Notifications { for _, event := range target.Events { if event == run.Status { - if err := s.notifier.Send(context.Background(), target, "Backupper: "+name, message, run.Status); err != nil { + if err := s.notifier.Send(context.Background(), target, "URBM: "+name, message, run.Status); err != nil { s.log.Warn("send notification failed", "target", target.ID, "type", target.Type, "error", err) } break diff --git a/plugin/backupper.plg b/plugin/backupper.plg index 3b15a86..d43f75d 100644 --- a/plugin/backupper.plg +++ b/plugin/backupper.plg @@ -2,13 +2,17 @@ - + ]> +### 2026.06.14.9.3 +- Rename the visible product to URBM - Unraid Restic Backup Manager. +- Show Backup in the Unraid navigation while preserving the existing plugin identifier and configuration paths. + ### 2026.06.14.9.2 - Include duration, new and changed files, added data, total backup size, file count, and snapshot ID in backup notifications. diff --git a/plugin/slack-desc b/plugin/slack-desc index 6643307..c25a5d7 100644 --- a/plugin/slack-desc +++ b/plugin/slack-desc @@ -1,4 +1,4 @@ -backupper: Backupper for Unraid +backupper: URBM - Unraid Restic Backup Manager backupper: backupper: Native Restic backup management for Unraid 7. backupper: Provides encrypted, deduplicated and versioned backups, diff --git a/webgui/Backupper.page b/webgui/Backupper.page index 808fd33..936b476 100644 --- a/webgui/Backupper.page +++ b/webgui/Backupper.page @@ -1,24 +1,24 @@ Menu="Tasks:55" -Name="Backupper" +Name="Backup" Icon="backupper.png" Type="xmenu" Tabs="true" Code="f0c7" -Tag="backup restic snapshots restore" +Tag="URBM Unraid Restic Backup Manager backup snapshots restore" --- - +
- -

Backupper 2026.06.14.9.2

Encrypted Restic backups for Unraid

+ +

URBM 2026.06.14.9.3

Restic Backup Manager for Unraid

-
Connecting...
+
Connecting...
-
- + diff --git a/webgui/api.php b/webgui/api.php index 5fc3e49..597d095 100644 --- a/webgui/api.php +++ b/webgui/api.php @@ -15,7 +15,7 @@ if (!preg_match('#^/v1/[A-Za-z0-9_./?=&%:-]*$#', $path) || str_contains($path, ' if (!file_exists($socket)) { http_response_code(503); - echo json_encode(['error' => 'Backupper daemon is not running']); + echo json_encode(['error' => 'URBM daemon is not running']); exit; } diff --git a/webgui/assets/backupper.js b/webgui/assets/backupper.js index 0dae0e7..4e11373 100644 --- a/webgui/assets/backupper.js +++ b/webgui/assets/backupper.js @@ -29,7 +29,7 @@ location: 'Restic-Ziel. Lokal: /mnt/user/backups/job1. SFTP: user@server:/backup/job1. Bei SMB/NFS mit Mount wird hier der Mount-Pfad verwendet.', passwordRef: 'Interne ID des verschlüsselt gespeicherten Restic-Passworts. Beispiel: repo-password-appdata. Keine Leerzeichen verwenden.', password: 'Passwort zur Restic-Verschlüsselung. Beispiel: eine lange zufällige Passphrase. Leer lassen, wenn ein vorhandenes Passwort unverändert bleiben soll.', - managed: 'Wenn aktiv, mountet Backupper SMB/NFS vor dem Job selbst. Wenn aus, muss der angegebene Pfad bereits durch Unraid oder Unassigned Devices gemountet sein.', + managed: 'Wenn aktiv, mountet URBM SMB/NFS vor dem Job selbst. Wenn aus, muss der angegebene Pfad bereits durch Unraid oder Unassigned Devices gemountet sein.', remote: 'Adresse der Netzwerkfreigabe. SMB-Beispiel: //192.168.1.20/backup. NFS-Beispiel: 192.168.1.20:/volume1/backup.', mountPoint: 'Lokaler Einhängepunkt auf Unraid. Beispiel: /mnt/remotes/backup-nas. Erlaubt sind Pfade unter /mnt/remotes oder /mnt/disks.', credentialRef: 'Interne ID für SMB-Zugangsdaten oder einen SFTP-Schlüssel. Beispiel: nas-login oder sftp-backup-key.', @@ -61,10 +61,10 @@ 'load-snapshots': 'Lädt alle Sicherungsstände des ausgewählten Repositorys.', 'restore-selected': 'Übernimmt die markierten Dateien oder Ordner in den Restore-Assistenten.', 'submit-restore': 'Stellt den Restore in die globale Warteschlange; Backups und Restores laufen nacheinander.', - 'submit-settings': 'Speichert die globalen Backupper-Einstellungen.', + 'submit-settings': 'Speichert die globalen URBM-Einstellungen.', 'submit-unraid-notify': 'Speichert native Unraid-Benachrichtigungen. Die Zustellung erfolgt über die in Unraid eingerichteten Notification Agents.', 'submit-gotify-notify': 'Speichert den Gotify-Kanal und das App-Token verschlüsselt.', - 'refresh': 'Lädt Laufstatus und Historie erneut vom Backupper-Daemon.', + 'refresh': 'Lädt Laufstatus und Historie erneut vom URBM-Daemon.', 'cancel-form': 'Verwirft ungespeicherte Eingaben und kehrt zur Übersicht zurück.', 'browser-up': 'Öffnet im Snapshot-Browser das übergeordnete Verzeichnis.', 'source-up': 'Öffnet im Ordner-Browser das übergeordnete Verzeichnis.', @@ -105,7 +105,7 @@ 'repo-snapshots': 'Wechselt zu den Sicherungsständen dieses Repositorys.', 'maint': action.includes(':check:') ? 'Prüft Struktur und Integrität des Restic-Repositorys.' : 'Entfernt nach Retention nicht mehr benötigte Datenblöcke und gibt Speicher frei.', 'edit-repo': 'Öffnet Ziel-, Mount- und Zugangseinstellungen dieses Repositorys.', - 'delete-repo': 'Löscht nur die Backupper-Konfiguration. Die Backupdaten am Speicherziel werden nicht gelöscht.', + 'delete-repo': 'Löscht nur die URBM-Konfiguration. Die Backupdaten am Speicherziel werden nicht gelöscht.', 'browse': 'Öffnet den Dateibaum dieses Snapshots, um einzelne Dateien oder Ordner auszuwählen.', 'open-dir': 'Öffnet dieses Verzeichnis innerhalb des schreibgeschützten Snapshots.', 'open-source-dir': 'Öffnet diesen Ordner, um darin weitere Unterordner auszuwählen.', @@ -188,12 +188,12 @@ const dashboardHelp = { 'Enabled jobs': 'Anzahl aktivierter Backup-Jobs, die nach Zeitplan laufen dürfen. Beispiel: 3 bedeutet drei aktive Sicherungspläne.', 'Repositories': 'Anzahl konfigurierter Restic-Speicherziele. Im MVP gehört jedes Repository genau zu einem Job.', - 'Queued / running': 'Aufgaben in der Warteschlange oder in Ausführung. Backupper führt immer nur eine Aufgabe gleichzeitig aus.', + 'Queued / running': 'Aufgaben in der Warteschlange oder in Ausführung. URBM führt immer nur eine Aufgabe gleichzeitig aus.', 'Recent failures': 'Fehlgeschlagene Läufe in der jüngsten Historie. Details stehen unter Activity.', }; container.querySelectorAll('.bu-card .bu-muted').forEach(element => setTooltip(element.closest('.bu-card') || element, dashboardHelp[element.textContent.trim()])); const headerHelp = { - Job: 'Name und interne ID des Backup-Jobs.', Type: 'Art der gesicherten Daten.', Repository: 'Verwendetes Restic-Speicherziel.', Schedule: 'Wochentage und Uhrzeit des automatischen Backups.', Enabled: 'Zeigt, ob der Job automatisch laufen darf.', Actions: 'Verfügbare Aktionen für diesen Eintrag.', Name: 'Anzeigename und interne ID.', Location: 'Pfad oder Adresse des Restic-Ziels.', Mount: 'Zeigt, ob Backupper das Netzwerkziel selbst mountet.', Snapshot: 'Eindeutiger Sicherungsstand.', Time: 'Erstellungszeitpunkt des Snapshots.', Host: 'Hostname des Systems, das den Snapshot erstellt hat.', Paths: 'Im Snapshot enthaltene Quellpfade.', Task: 'Art und Ziel der Aufgabe.', Status: 'Aktueller oder endgültiger Laufstatus.', Created: 'Zeitpunkt, an dem die Aufgabe eingereiht wurde.', Message: 'Ergebnis oder Fehlerbeschreibung.', Size: 'Gespeicherte Dateigröße im Snapshot.' + Job: 'Name und interne ID des Backup-Jobs.', Type: 'Art der gesicherten Daten.', Repository: 'Verwendetes Restic-Speicherziel.', Schedule: 'Wochentage und Uhrzeit des automatischen Backups.', Enabled: 'Zeigt, ob der Job automatisch laufen darf.', Actions: 'Verfügbare Aktionen für diesen Eintrag.', Name: 'Anzeigename und interne ID.', Location: 'Pfad oder Adresse des Restic-Ziels.', Mount: 'Zeigt, ob URBM das Netzwerkziel selbst mountet.', Snapshot: 'Eindeutiger Sicherungsstand.', Time: 'Erstellungszeitpunkt des Snapshots.', Host: 'Hostname des Systems, das den Snapshot erstellt hat.', Paths: 'Im Snapshot enthaltene Quellpfade.', Task: 'Art und Ziel der Aufgabe.', Status: 'Aktueller oder endgültiger Laufstatus.', Created: 'Zeitpunkt, an dem die Aufgabe eingereiht wurde.', Message: 'Ergebnis oder Fehlerbeschreibung.', Size: 'Gespeicherte Dateigröße im Snapshot.' }; container.querySelectorAll('.bu-table th').forEach(th => setTooltip(th, headerHelp[th.textContent.trim()] || 'Informationen zu dieser Tabellenspalte.')); const snapshotSelect = container.querySelector('#bu-snapshot-repo'); @@ -204,7 +204,7 @@ 'Repositories': 'Speicherziele und verschlüsselte Zugangsdaten für Restic.', 'Snapshots': 'Unveränderliche Sicherungsstände, die durchsucht und wiederhergestellt werden können.', 'Restore': 'Wiederherstellung ausgewählter Dateien oder Ordner. Staging ist die sichere Standardmethode.', - 'Activity': 'Globale Warteschlange und Historie aller Backupper-Aufgaben.', + 'Activity': 'Globale Warteschlange und Historie aller URBM-Aufgaben.', 'Settings': 'Globale Pfade sowie Zeitpläne für Check, Prune und Nachholläufe.', 'Unraid notifications': 'Native Meldungen im Unraid-Webinterface und über alle unter Einstellungen > Benachrichtigungen aktivierten Agenten.', 'Gotify notification': 'Optionaler direkter Push-Kanal zu deinem eigenen Gotify-Server.', @@ -315,7 +315,7 @@ loadWorkloads(type); } else { state.backupSelections = ['/boot']; - host.innerHTML = `

Unraid USB flash backup

The complete /boot configuration drive is backed up. No additional source selection is required.

`; + host.innerHTML = `

Unraid boot backup

The complete /boot configuration drive is backed up. No additional source selection is required.

`; } enhanceTooltips(host); }