Repository navigation
[Core]: Backup/restore #509
Description
Context
Backup and Restore option
Specific
As an administrator, I want a dedicated feature within InvoicePlane to perform both full system backups and restore existing backups, encompassing all database information and uploaded files.
Measurable
The success will be measured by the ability to consistently create a backup file that can be fully restored to a fresh instance of InvoicePlane, validating 100% data integrity and system functionality post-restore, with automated nightly backups if configured.
Achievable
This is achievable by leveraging Laravel's database backup tools and file system archiving capabilities, providing a user interface for initiation and status monitoring.
Relevant
This feature is highly relevant as it provides crucial data protection, disaster recovery capabilities, and simplifies system migration processes, ensuring business continuity for InvoicePlane users.
Time-bound
This Backup and Restore functionality should be fully developed, rigorously tested, and integrated into the system within the next two development sprints to provide immediate security for user data.
Arrange
- The user is logged in as an administrator.
- The InvoicePlane instance contains a significant amount of data (e.g., multiple invoices, clients, products, and users), including uploaded files (e.g., client logos, product images if that feature is implemented).
- Sufficient disk space is available on the server for the backup file.
- A separate, clean environment or a new, empty InvoicePlane database is prepared for the restore operation.
Act
- The administrator navigates to the "Backup" section of the system.
- The administrator initiates a full system backup.
- The system indicates the backup process is complete and provides a downloadable backup file.
- The administrator downloads the backup file.
- The administrator accesses the separate, clean InvoicePlane environment (or clears the existing database for a restore to the same instance).
- The administrator navigates to the "Restore" section.
- The administrator uploads the previously downloaded backup file.
- The administrator initiates the restore process.
Assert
- The backup process completes successfully, creating a single, compressed backup file containing all database tables and relevant uploaded files.
- The downloaded backup file is valid and not corrupted.
- The restore process completes successfully in the target environment, with a confirmation message displayed.
- All data (invoices, clients, products, users, etc.) from the backup is accurately present and accessible in the restored InvoicePlane instance.
- All associated uploaded files (e.g., client logos, product images) are correctly restored and linked within the system.
- All system functionalities (e.g., creating new invoices, editing clients) work as expected after the restore.
- No data loss or inconsistencies are observed after the restore operation.
- Error handling is robust if an invalid or corrupted backup file is attempted for restore.
@spacecabbie I'm assigning this one to you so you can keep an eye on it
Activity
nielsdrost7 commentedon Jul 16, 2026
SMART Plan — Backup and restore
Specific
Give administrators a way to create a full backup (database + uploaded files) and restore it, from within the admin panel. Current state: there is nothing here today — no
spatie/laravel-backup(or any backup package) incomposer.json, no backup-related code anywhere underModules/, no console command, no Filament page. This is a from-scratch feature, not an extension of existing scaffolding.Measurable
it_creates_a_backup_archive_containing_a_database_dump()#[Test] public function it_creates_a_backup_archive_containing_a_database_dump(): void { /* Arrange */ Storage::fake('backups'); /* Act */ $this->artisan('ip:backup')->assertSuccessful(); /* Assert */ $files = Storage::disk('backups')->allFiles(); $this->assertNotEmpty($files); }Uploadmodel's storage disk) are included in the backup archive.it_includes_uploaded_files_in_the_backup_archive()#[Test] public function it_includes_uploaded_files_in_the_backup_archive(): void { /* Arrange */ Storage::fake('backups'); $upload = Upload::factory()->for($this->company)->create(); Storage::disk($upload->upload_disk)->put($upload->upload_stored_name, 'fake-file-contents'); /* Act */ $this->artisan('ip:backup')->assertSuccessful(); /* Assert */ $backupFile = Storage::disk('backups')->allFiles()[0] ?? null; $this->assertNotNull($backupFile); }it_restores_data_from_a_valid_backup_archive()#[Test] public function it_restores_data_from_a_valid_backup_archive(): void { /* Arrange */ Storage::fake('backups'); $this->artisan('ip:backup')->assertSuccessful(); $archivePath = Storage::disk('backups')->allFiles()[0]; Relation::query()->delete(); /* Act */ $this->artisan('ip:restore', ['archive' => $archivePath])->assertSuccessful(); /* Assert */ $this->assertGreaterThan(0, Relation::query()->count()); }it_rejects_a_corrupted_backup_archive_without_modifying_data()#[Test] public function it_rejects_a_corrupted_backup_archive_without_modifying_data(): void { /* Arrange */ Storage::fake('backups'); Storage::disk('backups')->put('corrupt.zip', 'not-a-real-archive'); $before = Relation::query()->count(); /* Act */ $this->artisan('ip:restore', ['archive' => 'corrupt.zip'])->assertFailed(); /* Assert */ $this->assertSame($before, Relation::query()->count()); }#[Test] public function it_triggers_a_manual_backup_from_the_admin_panel(): void { /* Arrange */ /* superAdmin provided by AbstractAdminPanelTestCase */ /* Act */ $component = Livewire::actingAs($this->superAdmin()) ->test(BackupPage::class) ->call('runBackup'); /* Assert */ $component->assertSuccessful()->assertNotified(); }Achievable
spatie/laravel-backupas the underlying package rather than hand-rolling archive/dump logic — it already handles DB dump + configurable file disks and is a common Laravel choice; confirm it's acceptable before adding a new dependency.ip:backupandip:restoreArtisan commands inModules/Core/Console/wrapping the chosen backup mechanism, targeting theuploadsdisk (used by the existingUploadmodel) plus the primary DB connection.Modules/Core/Filament/Admin/Pages/) exposing "Run backup now" and "Restore from upload" actions, gated behind a dedicated permission (follow thePermission::MANAGE_SETTINGS-style pattern already used for the Settings page).Modules/Core/Tests/Feature/.Relevant
Backup/restore is core to disaster recovery and safe migrations between environments; today there is no supported way to do either, meaning any data loss is unrecoverable and any server migration is a manual, unsupported DB/file copy.
Time-bound
Sprint 8 (confirm package choice and whether scheduled nightly backups are in scope for this iteration before estimating further).