diff --git a/apps/backups/services.py b/apps/backups/services.py index 96838417..db193607 100644 --- a/apps/backups/services.py +++ b/apps/backups/services.py @@ -20,12 +20,6 @@ def get_backup_dir() -> Path: return backup_dir -def get_data_dirs() -> list[Path]: - """Get list of data directories to include in backups.""" - dirs = getattr(settings, "BACKUP_DATA_DIRS", []) - return [Path(d) for d in dirs if d and Path(d).exists()] - - def _is_postgresql() -> bool: """Check if we're using PostgreSQL.""" return settings.DATABASES["default"]["ENGINE"] == "django.db.backends.postgresql" @@ -223,14 +217,6 @@ def create_backup() -> Path: } zip_file.writestr("metadata.json", json.dumps(metadata, indent=2)) - # Add data directories - for data_dir in get_data_dirs(): - logger.debug(f"Adding directory: {data_dir}") - for file_path in data_dir.rglob("*"): - if file_path.is_file(): - arcname = f"data/{data_dir.name}/{file_path.relative_to(data_dir)}" - zip_file.write(file_path, arcname) - logger.info(f"Backup created successfully: {backup_file}") return backup_file @@ -264,33 +250,6 @@ def restore_backup(backup_file: Path) -> None: # Restore database _restore_database(temp_path, metadata) - # Restore data directories - data_root = temp_path / "data" - if data_root.exists(): - logger.info("Restoring data directories...") - for extracted_dir in data_root.iterdir(): - if not extracted_dir.is_dir(): - continue - - target_name = extracted_dir.name - data_dirs = get_data_dirs() - matching = [d for d in data_dirs if d.name == target_name] - - if not matching: - logger.warning(f"No configured directory for {target_name}, skipping") - continue - - target = matching[0] - logger.debug(f"Restoring {target_name} to {target}") - - # Create parent directory if needed - target.parent.mkdir(parents=True, exist_ok=True) - - # Remove existing and copy from backup - if target.exists(): - shutil.rmtree(target) - shutil.copytree(extracted_dir, target) - logger.info("Restore completed successfully") diff --git a/apps/backups/tests.py b/apps/backups/tests.py index a06bb7d2..2cbbe669 100644 --- a/apps/backups/tests.py +++ b/apps/backups/tests.py @@ -20,14 +20,11 @@ class BackupServicesTestCase(TestCase): def setUp(self): self.temp_backup_dir = tempfile.mkdtemp() - self.temp_data_dir = tempfile.mkdtemp() def tearDown(self): import shutil if Path(self.temp_backup_dir).exists(): shutil.rmtree(self.temp_backup_dir) - if Path(self.temp_data_dir).exists(): - shutil.rmtree(self.temp_data_dir) @patch('apps.backups.services.settings') def test_get_backup_dir_creates_directory(self, mock_settings): @@ -42,31 +39,12 @@ class BackupServicesTestCase(TestCase): services.get_backup_dir() mock_path_instance.mkdir.assert_called_once_with(parents=True, exist_ok=True) - @patch('apps.backups.services.settings') - def test_get_data_dirs_with_empty_config(self, mock_settings): - """Test that get_data_dirs returns empty list when no dirs configured""" - mock_settings.BACKUP_DATA_DIRS = [] - result = services.get_data_dirs() - self.assertEqual(result, []) - - @patch('apps.backups.services.settings') - def test_get_data_dirs_filters_nonexistent(self, mock_settings): - """Test that get_data_dirs filters out non-existent directories""" - nonexistent_dir = '/tmp/does-not-exist-12345' - mock_settings.BACKUP_DATA_DIRS = [self.temp_data_dir, nonexistent_dir] - - result = services.get_data_dirs() - self.assertEqual(len(result), 1) - self.assertEqual(str(result[0]), self.temp_data_dir) - @patch('apps.backups.services.get_backup_dir') - @patch('apps.backups.services.get_data_dirs') @patch('apps.backups.services._is_postgresql') @patch('apps.backups.services._dump_sqlite') - def test_create_backup_success_sqlite(self, mock_dump_sqlite, mock_is_pg, mock_get_data_dirs, mock_get_backup_dir): + def test_create_backup_success_sqlite(self, mock_dump_sqlite, mock_is_pg, mock_get_backup_dir): """Test successful backup creation with SQLite""" mock_get_backup_dir.return_value = Path(self.temp_backup_dir) - mock_get_data_dirs.return_value = [] mock_is_pg.return_value = False # Mock SQLite dump to create a temp file @@ -94,13 +72,11 @@ class BackupServicesTestCase(TestCase): self.assertEqual(metadata['database_type'], 'sqlite') @patch('apps.backups.services.get_backup_dir') - @patch('apps.backups.services.get_data_dirs') @patch('apps.backups.services._is_postgresql') @patch('apps.backups.services._dump_postgresql') - def test_create_backup_success_postgresql(self, mock_dump_pg, mock_is_pg, mock_get_data_dirs, mock_get_backup_dir): + def test_create_backup_success_postgresql(self, mock_dump_pg, mock_is_pg, mock_get_backup_dir): """Test successful backup creation with PostgreSQL""" mock_get_backup_dir.return_value = Path(self.temp_backup_dir) - mock_get_data_dirs.return_value = [] mock_is_pg.return_value = True # Mock PostgreSQL dump to create a temp file @@ -176,14 +152,12 @@ class BackupServicesTestCase(TestCase): services.delete_backup("nonexistent-backup.zip") @patch('apps.backups.services.get_backup_dir') - @patch('apps.backups.services.get_data_dirs') @patch('apps.backups.services._is_postgresql') @patch('apps.backups.services._restore_postgresql') - def test_restore_backup_postgresql(self, mock_restore_pg, mock_is_pg, mock_get_data_dirs, mock_get_backup_dir): + def test_restore_backup_postgresql(self, mock_restore_pg, mock_is_pg, mock_get_backup_dir): """Test successful restoration of PostgreSQL backup""" backup_dir = Path(self.temp_backup_dir) mock_get_backup_dir.return_value = backup_dir - mock_get_data_dirs.return_value = [] mock_is_pg.return_value = True # Create PostgreSQL backup file @@ -201,14 +175,12 @@ class BackupServicesTestCase(TestCase): mock_restore_pg.assert_called_once() @patch('apps.backups.services.get_backup_dir') - @patch('apps.backups.services.get_data_dirs') @patch('apps.backups.services._is_postgresql') @patch('apps.backups.services._restore_sqlite') - def test_restore_backup_sqlite(self, mock_restore_sqlite, mock_is_pg, mock_get_data_dirs, mock_get_backup_dir): + def test_restore_backup_sqlite(self, mock_restore_sqlite, mock_is_pg, mock_get_backup_dir): """Test successful restoration of SQLite backup""" backup_dir = Path(self.temp_backup_dir) mock_get_backup_dir.return_value = backup_dir - mock_get_data_dirs.return_value = [] mock_is_pg.return_value = False # Create SQLite backup file @@ -226,13 +198,11 @@ class BackupServicesTestCase(TestCase): mock_restore_sqlite.assert_called_once() @patch('apps.backups.services.get_backup_dir') - @patch('apps.backups.services.get_data_dirs') @patch('apps.backups.services._is_postgresql') - def test_restore_backup_database_type_mismatch(self, mock_is_pg, mock_get_data_dirs, mock_get_backup_dir): + def test_restore_backup_database_type_mismatch(self, mock_is_pg, mock_get_backup_dir): """Test restore fails when database type doesn't match""" backup_dir = Path(self.temp_backup_dir) mock_get_backup_dir.return_value = backup_dir - mock_get_data_dirs.return_value = [] mock_is_pg.return_value = True # Current system is PostgreSQL # Create SQLite backup file diff --git a/frontend/src/components/backups/BackupManager.jsx b/frontend/src/components/backups/BackupManager.jsx index 6ba487b5..f538d7da 100644 --- a/frontend/src/components/backups/BackupManager.jsx +++ b/frontend/src/components/backups/BackupManager.jsx @@ -1,14 +1,15 @@ import { useEffect, useState } from 'react'; import { - Alert, + ActionIcon, + Box, Button, - Card, - Divider, FileInput, + Flex, Group, Loader, Modal, NumberInput, + Paper, Select, Stack, Switch, @@ -19,12 +20,11 @@ import { } from '@mantine/core'; import { Download, - PlayCircle, RefreshCcw, + RotateCcw, + SquareMinus, + SquarePlus, UploadCloud, - Trash2, - Clock, - Save, } from 'lucide-react'; import { notifications } from '@mantine/notifications'; @@ -312,20 +312,11 @@ export default function BackupManager() { }; return ( - - - Backups include your database and configured data directories. Use the - create button to generate a new backup, or upload an existing backup to - restore. - - + {/* Schedule Settings */} - - - - - Scheduled Backups - + + + Scheduled Backups handleScheduleChange('enabled', e.currentTarget.checked)} @@ -337,7 +328,7 @@ export default function BackupManager() { ) : ( <> - +