mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 07:48:57 +00:00
update
This commit is contained in:
210
src/password_manager/backup.py
Normal file
210
src/password_manager/backup.py
Normal file
@@ -0,0 +1,210 @@
|
||||
# password_manager/backup.py
|
||||
|
||||
"""
|
||||
Backup Manager Module
|
||||
|
||||
This module implements the BackupManager class, responsible for creating backups,
|
||||
restoring from backups, and listing available backups for the encrypted password
|
||||
index file. It ensures data integrity and provides mechanisms to recover from
|
||||
corrupted or lost data by maintaining timestamped backups.
|
||||
|
||||
Dependencies:
|
||||
- shutil
|
||||
- time
|
||||
- logging
|
||||
- pathlib
|
||||
- colorama
|
||||
- termcolor
|
||||
|
||||
Ensure that all dependencies are installed and properly configured in your environment.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
|
||||
from colorama import Fore
|
||||
from termcolor import colored
|
||||
|
||||
from constants import APP_DIR, INDEX_FILE
|
||||
from utils.file_lock import lock_file
|
||||
|
||||
# Configure logging at the start of the module
|
||||
def configure_logging():
|
||||
"""
|
||||
Configures logging with both file and console handlers.
|
||||
Only ERROR and higher-level messages are shown in the terminal, while all messages
|
||||
are logged in the log file.
|
||||
"""
|
||||
# Create the 'logs' folder if it doesn't exist
|
||||
if not os.path.exists('logs'):
|
||||
os.makedirs('logs')
|
||||
|
||||
# Create a custom logger
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG) # Set to DEBUG for detailed output
|
||||
|
||||
# Create handlers
|
||||
c_handler = logging.StreamHandler()
|
||||
f_handler = logging.FileHandler(os.path.join('logs', 'backup_manager.log')) # Log files will be in 'logs' folder
|
||||
|
||||
# Set levels: only errors and critical messages will be shown in the console
|
||||
c_handler.setLevel(logging.ERROR) # Terminal will show ERROR and above
|
||||
f_handler.setLevel(logging.DEBUG) # File will log everything from DEBUG and above
|
||||
|
||||
# Create formatters and add them to handlers, include file and line number in log messages
|
||||
c_format = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s [%(filename)s:%(lineno)d]')
|
||||
f_format = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s [%(filename)s:%(lineno)d]')
|
||||
|
||||
c_handler.setFormatter(c_format)
|
||||
f_handler.setFormatter(f_format)
|
||||
|
||||
# Add handlers to the logger
|
||||
logger.addHandler(c_handler)
|
||||
logger.addHandler(f_handler)
|
||||
|
||||
# Call the logging configuration function
|
||||
configure_logging()
|
||||
|
||||
class BackupManager:
|
||||
"""
|
||||
BackupManager Class
|
||||
|
||||
Handles the creation, restoration, and listing of backups for the encrypted
|
||||
password index file. Backups are stored in the application directory with
|
||||
timestamped filenames to facilitate easy identification and retrieval.
|
||||
"""
|
||||
|
||||
BACKUP_FILENAME_TEMPLATE = 'passwords_db_backup_{timestamp}.json.enc'
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initializes the BackupManager with the application directory and index file paths.
|
||||
"""
|
||||
self.app_dir = APP_DIR
|
||||
self.index_file = INDEX_FILE
|
||||
logging.debug(f"BackupManager initialized with APP_DIR: {self.app_dir} and INDEX_FILE: {self.index_file}")
|
||||
|
||||
def create_backup(self) -> None:
|
||||
"""
|
||||
Creates a timestamped backup of the encrypted password index file.
|
||||
|
||||
The backup file is named using the current Unix timestamp to ensure uniqueness.
|
||||
If the index file does not exist, no backup is created.
|
||||
|
||||
Raises:
|
||||
Exception: If the backup process fails due to I/O errors.
|
||||
"""
|
||||
if not self.index_file.exists():
|
||||
logging.warning("Index file does not exist. No backup created.")
|
||||
print(colored("Warning: Index file does not exist. No backup created.", 'yellow'))
|
||||
return
|
||||
|
||||
timestamp = int(time.time())
|
||||
backup_filename = self.BACKUP_FILENAME_TEMPLATE.format(timestamp=timestamp)
|
||||
backup_file = self.app_dir / backup_filename
|
||||
|
||||
try:
|
||||
with lock_file(self.index_file, lock_type=fcntl.LOCK_SH):
|
||||
shutil.copy2(self.index_file, backup_file)
|
||||
logging.info(f"Backup created successfully at '{backup_file}'.")
|
||||
print(colored(f"Backup created successfully at '{backup_file}'.", 'green'))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to create backup: {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to create backup: {e}", 'red'))
|
||||
|
||||
def restore_latest_backup(self) -> None:
|
||||
"""
|
||||
Restores the encrypted password index file from the latest available backup.
|
||||
|
||||
The latest backup is determined based on the Unix timestamp in the backup filenames.
|
||||
If no backups are found, an error message is displayed.
|
||||
|
||||
Raises:
|
||||
Exception: If the restoration process fails due to I/O errors or missing backups.
|
||||
"""
|
||||
backup_files = sorted(
|
||||
self.app_dir.glob('passwords_db_backup_*.json.enc'),
|
||||
key=lambda x: x.stat().st_mtime,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
if not backup_files:
|
||||
logging.error("No backup files found to restore.")
|
||||
print(colored("Error: No backup files found to restore.", 'red'))
|
||||
return
|
||||
|
||||
latest_backup = backup_files[0]
|
||||
try:
|
||||
with lock_file(latest_backup, lock_type=fcntl.LOCK_SH):
|
||||
shutil.copy2(latest_backup, self.index_file)
|
||||
logging.info(f"Restored the index file from backup '{latest_backup}'.")
|
||||
print(colored(f"Restored the index file from backup '{latest_backup}'.", 'green'))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to restore from backup '{latest_backup}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to restore from backup '{latest_backup}': {e}", 'red'))
|
||||
|
||||
def list_backups(self) -> None:
|
||||
"""
|
||||
Lists all available backups in the application directory, sorted by date.
|
||||
|
||||
Displays the backups with their filenames and creation dates.
|
||||
"""
|
||||
backup_files = sorted(
|
||||
self.app_dir.glob('passwords_db_backup_*.json.enc'),
|
||||
key=lambda x: x.stat().st_mtime,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
if not backup_files:
|
||||
logging.info("No backup files available.")
|
||||
print(colored("No backup files available.", 'yellow'))
|
||||
return
|
||||
|
||||
print(colored("Available Backups:", 'cyan'))
|
||||
for backup in backup_files:
|
||||
creation_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(backup.stat().st_mtime))
|
||||
print(colored(f"- {backup.name} (Created on: {creation_time})", 'cyan'))
|
||||
|
||||
def restore_backup_by_timestamp(self, timestamp: int) -> None:
|
||||
"""
|
||||
Restores the encrypted password index file from a backup with the specified timestamp.
|
||||
|
||||
Parameters:
|
||||
timestamp (int): The Unix timestamp of the backup to restore.
|
||||
|
||||
Raises:
|
||||
Exception: If the restoration process fails due to I/O errors or missing backups.
|
||||
"""
|
||||
backup_filename = self.BACKUP_FILENAME_TEMPLATE.format(timestamp=timestamp)
|
||||
backup_file = self.app_dir / backup_filename
|
||||
|
||||
if not backup_file.exists():
|
||||
logging.error(f"No backup found with timestamp {timestamp}.")
|
||||
print(colored(f"Error: No backup found with timestamp {timestamp}.", 'red'))
|
||||
return
|
||||
|
||||
try:
|
||||
with lock_file(backup_file, lock_type=fcntl.LOCK_SH):
|
||||
shutil.copy2(backup_file, self.index_file)
|
||||
logging.info(f"Restored the index file from backup '{backup_file}'.")
|
||||
print(colored(f"Restored the index file from backup '{backup_file}'.", 'green'))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to restore from backup '{backup_file}': {e}")
|
||||
logging.error(traceback.format_exc()) # Log full traceback
|
||||
print(colored(f"Error: Failed to restore from backup '{backup_file}': {e}", 'red'))
|
||||
|
||||
# Example usage (to be integrated within the PasswordManager class or other modules):
|
||||
|
||||
# from password_manager.backup import BackupManager
|
||||
|
||||
# backup_manager = BackupManager()
|
||||
# backup_manager.create_backup()
|
||||
# backup_manager.restore_latest_backup()
|
||||
# backup_manager.list_backups()
|
||||
# backup_manager.restore_backup_by_timestamp(1700000000) # Example timestamp
|
Reference in New Issue
Block a user