Skip to content

Commit 9780d0f

Browse files
committed
Refactor backup system to use unified backups exclusively
- Removed deprecated methods for creating settings and certificates backups. - Updated backup cleanup methods to focus solely on unified backups. - Modified the backup listing functionality to only display unified backups. - Adjusted restore methods to handle only unified backups, ensuring atomicity. - Updated web routes and templates to reflect the unified backup system. - Changed version references from 1.1.17 to 1.2.0 across the application. - Enhanced help documentation to emphasize the unified backup system. - Updated tests to mock and validate the new unified backup behavior.
1 parent 17d6e2e commit 9780d0f

12 files changed

Lines changed: 201 additions & 921 deletions

File tree

README.md

Lines changed: 69 additions & 167 deletions
Large diffs are not rendered by default.

app.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,8 @@ def _setup_directories(self):
6565
directory.mkdir(exist_ok=True)
6666
logger.info(f"Ensured directory exists: {directory}")
6767

68-
# Create backup subdirectories
69-
(self.backup_dir / "settings").mkdir(exist_ok=True)
70-
(self.backup_dir / "certificates").mkdir(exist_ok=True)
68+
# Create unified backup subdirectory
69+
(self.backup_dir / "unified").mkdir(exist_ok=True)
7170
logger.info(f"Backup directories created: {self.backup_dir}")
7271

7372
# Test write permissions
@@ -153,7 +152,7 @@ def _setup_api(self):
153152
# Initialize Flask-RESTX
154153
self.api = Api(
155154
self.app,
156-
version='1.1.17',
155+
version='1.2.0',
157156
title='CertMate API',
158157
description='SSL Certificate Management API',
159158
doc='/docs/',
@@ -509,14 +508,7 @@ def suggest_dns_provider_for_domain(domain, settings=None):
509508
"""Compatibility wrapper for suggest_dns_provider_for_domain"""
510509
return certmate_app.managers['dns'].suggest_dns_provider_for_domain(domain, settings)
511510

512-
def create_settings_backup(settings, reason="manual"):
513-
"""Compatibility wrapper for create_settings_backup"""
514-
return certmate_app.managers['file_ops'].create_backup(
515-
certmate_app.data_dir / "settings.json",
516-
settings,
517-
backup_type="settings",
518-
reason=reason
519-
)
511+
520512

521513
# Import validation functions from modules.core.utils for test compatibility
522514
from modules.core.utils import (

modules/api/models.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,7 @@ def create_api_models(api):
171171
})
172172

173173
backup_list_model = api.model('BackupList', {
174-
'unified': fields.List(fields.Nested(backup_metadata_model), description='Unified backups (recommended)'),
175-
'settings': fields.List(fields.Nested(backup_metadata_model), description='Legacy settings backups'),
176-
'certificates': fields.List(fields.Nested(backup_metadata_model), description='Legacy certificate backups')
174+
'unified': fields.List(fields.Nested(backup_metadata_model), description='Unified backups')
177175
})
178176

179177
# Storage Backend models

modules/api/resources.py

Lines changed: 22 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def get(self):
4141

4242
return {
4343
'status': 'healthy',
44-
'version': '1.1.17',
44+
'version': '1.2.0',
4545
'services': {
4646
'settings': 'ok' if settings else 'error',
4747
'cache': 'ok',
@@ -372,27 +372,12 @@ def post(self):
372372

373373
created_backups = []
374374

375-
# Always prefer unified backup for consistency
376-
if backup_type in ['unified', 'both', 'full']:
377-
settings = settings_manager.load_settings()
378-
filename = file_ops.create_unified_backup(settings, reason)
379-
if filename:
380-
created_backups.append({'type': 'unified', 'filename': filename})
381-
logger.info(f"Created unified backup: {filename}")
382-
383-
# Legacy support for separate backups (deprecated)
384-
elif backup_type == 'settings':
385-
logger.warning("Separate settings backup is deprecated. Consider using unified backup for consistency.")
386-
settings = settings_manager.load_settings()
387-
filename = file_ops.create_settings_backup(settings, reason)
388-
if filename:
389-
created_backups.append({'type': 'settings', 'filename': filename})
390-
391-
elif backup_type == 'certificates':
392-
logger.warning("Separate certificates backup is deprecated. Consider using unified backup for consistency.")
393-
filename = file_ops.create_certificates_backup(reason)
394-
if filename:
395-
created_backups.append({'type': 'certificates', 'filename': filename})
375+
# Only support unified backup (legacy removed)
376+
settings = settings_manager.load_settings()
377+
filename = file_ops.create_unified_backup(settings, reason)
378+
if filename:
379+
created_backups.append({'type': 'unified', 'filename': filename})
380+
logger.info(f"Created unified backup: {filename}")
396381

397382
if created_backups:
398383
return {
@@ -413,8 +398,8 @@ class BackupDownload(Resource):
413398
def get(self, backup_type, filename):
414399
"""Download a backup file"""
415400
try:
416-
if backup_type not in ['unified', 'settings', 'certificates']:
417-
return {'error': 'Invalid backup type. Must be "unified", "settings", or "certificates"'}, 400
401+
if backup_type != 'unified':
402+
return {'error': 'Only unified backup download is supported'}, 400
418403

419404
backup_path = Path(file_ops.backup_dir) / backup_type / filename
420405

@@ -444,10 +429,10 @@ class BackupRestore(Resource):
444429
}))
445430
@auth_manager.require_auth
446431
def post(self, backup_type):
447-
"""Restore from a backup file (unified backups restore both settings and certificates atomically)"""
432+
"""Restore from a unified backup file (only unified backups supported)"""
448433
try:
449-
if backup_type not in ['unified', 'settings', 'certificates']:
450-
return {'error': 'Invalid backup type. Must be "unified", "settings", or "certificates"'}, 400
434+
if backup_type != 'unified':
435+
return {'error': 'Only unified backup restoration is supported'}, 400
451436

452437
data = api.payload
453438
filename = data.get('filename')
@@ -456,11 +441,8 @@ def post(self, backup_type):
456441
if not filename:
457442
return {'error': 'Filename is required'}, 400
458443

459-
# Handle unified backups from all locations
460-
if backup_type == 'unified':
461-
backup_path = Path(file_ops.backup_dir) / "unified" / filename
462-
else:
463-
backup_path = Path(file_ops.backup_dir) / backup_type / filename
444+
# Handle unified backups only
445+
backup_path = Path(file_ops.backup_dir) / "unified" / filename
464446

465447
if not backup_path.exists():
466448
return {'error': 'Backup file not found'}, 404
@@ -476,37 +458,23 @@ def post(self, backup_type):
476458
pre_restore_backup = file_ops.create_unified_backup(current_settings, "pre_restore")
477459
logger.info(f"Created pre-restore backup: {pre_restore_backup}")
478460

479-
# Restore from backup
480-
if backup_type == 'unified':
481-
success = file_ops.restore_unified_backup(str(backup_path))
482-
restore_msg = "Settings and certificates restored atomically"
483-
elif backup_type == 'settings':
484-
success = file_ops.restore_settings_backup(str(backup_path))
485-
restore_msg = "Settings restored"
486-
elif backup_type == 'certificates':
487-
success = file_ops.restore_certificates_backup(str(backup_path))
488-
restore_msg = "Certificates restored"
489-
else:
490-
success = False
491-
restore_msg = "Unknown backup type"
461+
# Restore from unified backup
462+
success = file_ops.restore_unified_backup(str(backup_path))
463+
restore_msg = "Settings and certificates restored atomically"
492464

493465
if success:
494466
response = {
495467
'message': f'{restore_msg} successfully from {filename}',
496468
'restored_from': filename,
497-
'backup_type': backup_type
469+
'backup_type': 'unified'
498470
}
499471
if pre_restore_backup:
500472
response['pre_restore_backup'] = pre_restore_backup
501473
response['note'] = 'A backup of the previous state was created before restore'
502474

503-
# Add recommendation for legacy backups
504-
if backup_type != 'unified':
505-
response['recommendation'] = 'Consider using unified backups for better data consistency'
506-
507475
return response, 200
508476
else:
509-
return {'error': f'Failed to restore {backup_type}'}, 500
477+
return {'error': 'Failed to restore unified backup'}, 500
510478

511479
except Exception as e:
512480
logger.error(f"Error restoring backup: {e}")
@@ -516,14 +484,14 @@ class BackupDelete(Resource):
516484
@api.doc(security='Bearer')
517485
@auth_manager.require_auth
518486
def delete(self, backup_type, filename):
519-
"""Delete a backup file"""
487+
"""Delete a unified backup file"""
520488
try:
521489
file_ops = managers.get('file_ops')
522490
if not file_ops:
523491
return {'error': 'File operations manager not available'}, 500
524492

525-
if backup_type not in ['unified', 'settings', 'certificates']:
526-
return {'error': 'Invalid backup type. Must be "unified", "settings", or "certificates"'}, 400
493+
if backup_type != 'unified':
494+
return {'error': 'Only unified backup deletion is supported'}, 400
527495

528496
# Construct backup path
529497
backup_dir = file_ops.backup_dir / backup_type

0 commit comments

Comments
 (0)