mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 15:58:48 +00:00
Add backup interval setting and throttled backups
This commit is contained in:
@@ -54,6 +54,7 @@ class BackupManager:
|
|||||||
self.backup_dir = self.fingerprint_dir / "backups"
|
self.backup_dir = self.fingerprint_dir / "backups"
|
||||||
self.backup_dir.mkdir(parents=True, exist_ok=True)
|
self.backup_dir.mkdir(parents=True, exist_ok=True)
|
||||||
self.index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc"
|
self.index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc"
|
||||||
|
self._last_backup_time = 0.0
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"BackupManager initialized with backup directory at {self.backup_dir}"
|
f"BackupManager initialized with backup directory at {self.backup_dir}"
|
||||||
)
|
)
|
||||||
@@ -71,7 +72,13 @@ class BackupManager:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
timestamp = int(time.time())
|
now = time.time()
|
||||||
|
interval = self.config_manager.get_backup_interval()
|
||||||
|
if interval > 0 and now - self._last_backup_time < interval:
|
||||||
|
logger.info("Skipping backup due to interval throttle")
|
||||||
|
return
|
||||||
|
|
||||||
|
timestamp = int(now)
|
||||||
backup_filename = self.BACKUP_FILENAME_TEMPLATE.format(timestamp=timestamp)
|
backup_filename = self.BACKUP_FILENAME_TEMPLATE.format(timestamp=timestamp)
|
||||||
backup_file = self.backup_dir / backup_filename
|
backup_file = self.backup_dir / backup_filename
|
||||||
|
|
||||||
@@ -81,6 +88,7 @@ class BackupManager:
|
|||||||
print(colored(f"Backup created successfully at '{backup_file}'.", "green"))
|
print(colored(f"Backup created successfully at '{backup_file}'.", "green"))
|
||||||
|
|
||||||
self._create_additional_backup(backup_file)
|
self._create_additional_backup(backup_file)
|
||||||
|
self._last_backup_time = now
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to create backup: {e}", exc_info=True)
|
logger.error(f"Failed to create backup: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to create backup: {e}", "red"))
|
print(colored(f"Error: Failed to create backup: {e}", "red"))
|
||||||
|
@@ -46,6 +46,7 @@ class ConfigManager:
|
|||||||
"inactivity_timeout": INACTIVITY_TIMEOUT,
|
"inactivity_timeout": INACTIVITY_TIMEOUT,
|
||||||
"kdf_iterations": 100_000,
|
"kdf_iterations": 100_000,
|
||||||
"additional_backup_path": "",
|
"additional_backup_path": "",
|
||||||
|
"backup_interval": 0,
|
||||||
"secret_mode_enabled": False,
|
"secret_mode_enabled": False,
|
||||||
"clipboard_clear_delay": 45,
|
"clipboard_clear_delay": 45,
|
||||||
}
|
}
|
||||||
@@ -60,6 +61,7 @@ class ConfigManager:
|
|||||||
data.setdefault("inactivity_timeout", INACTIVITY_TIMEOUT)
|
data.setdefault("inactivity_timeout", INACTIVITY_TIMEOUT)
|
||||||
data.setdefault("kdf_iterations", 100_000)
|
data.setdefault("kdf_iterations", 100_000)
|
||||||
data.setdefault("additional_backup_path", "")
|
data.setdefault("additional_backup_path", "")
|
||||||
|
data.setdefault("backup_interval", 0)
|
||||||
data.setdefault("secret_mode_enabled", False)
|
data.setdefault("secret_mode_enabled", False)
|
||||||
data.setdefault("clipboard_clear_delay", 45)
|
data.setdefault("clipboard_clear_delay", 45)
|
||||||
|
|
||||||
@@ -85,6 +87,7 @@ class ConfigManager:
|
|||||||
def save_config(self, config: dict) -> None:
|
def save_config(self, config: dict) -> None:
|
||||||
"""Encrypt and save configuration."""
|
"""Encrypt and save configuration."""
|
||||||
try:
|
try:
|
||||||
|
config.setdefault("backup_interval", 0)
|
||||||
self.vault.save_config(config)
|
self.vault.save_config(config)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(f"Failed to save config: {exc}")
|
logger.error(f"Failed to save config: {exc}")
|
||||||
@@ -187,3 +190,16 @@ class ConfigManager:
|
|||||||
"""Retrieve clipboard clear delay in seconds."""
|
"""Retrieve clipboard clear delay in seconds."""
|
||||||
config = self.load_config(require_pin=False)
|
config = self.load_config(require_pin=False)
|
||||||
return int(config.get("clipboard_clear_delay", 45))
|
return int(config.get("clipboard_clear_delay", 45))
|
||||||
|
|
||||||
|
def set_backup_interval(self, interval: int | float) -> None:
|
||||||
|
"""Persist the minimum interval in seconds between automatic backups."""
|
||||||
|
if interval < 0:
|
||||||
|
raise ValueError("Interval cannot be negative")
|
||||||
|
config = self.load_config(require_pin=False)
|
||||||
|
config["backup_interval"] = interval
|
||||||
|
self.save_config(config)
|
||||||
|
|
||||||
|
def get_backup_interval(self) -> float:
|
||||||
|
"""Retrieve the backup interval in seconds."""
|
||||||
|
config = self.load_config(require_pin=False)
|
||||||
|
return float(config.get("backup_interval", 0))
|
||||||
|
@@ -461,6 +461,7 @@ def config_set(ctx: typer.Context, key: str, value: str) -> None:
|
|||||||
"relays": lambda v: cfg.set_relays(
|
"relays": lambda v: cfg.set_relays(
|
||||||
[r.strip() for r in v.split(",") if r.strip()], require_pin=False
|
[r.strip() for r in v.split(",") if r.strip()], require_pin=False
|
||||||
),
|
),
|
||||||
|
"backup_interval": lambda v: cfg.set_backup_interval(float(v)),
|
||||||
}
|
}
|
||||||
|
|
||||||
action = mapping.get(key)
|
action = mapping.get(key)
|
||||||
|
34
src/tests/test_backup_interval.py
Normal file
34
src/tests/test_backup_interval.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
|
from helpers import create_vault, TEST_SEED, TEST_PASSWORD
|
||||||
|
|
||||||
|
from password_manager.backup import BackupManager
|
||||||
|
from password_manager.config_manager import ConfigManager
|
||||||
|
|
||||||
|
|
||||||
|
def test_backup_interval(monkeypatch):
|
||||||
|
with TemporaryDirectory() as tmpdir:
|
||||||
|
fp_dir = Path(tmpdir)
|
||||||
|
vault, _ = create_vault(fp_dir, TEST_SEED, TEST_PASSWORD)
|
||||||
|
cfg_mgr = ConfigManager(vault, fp_dir)
|
||||||
|
cfg_mgr.set_backup_interval(10)
|
||||||
|
backup_mgr = BackupManager(fp_dir, cfg_mgr)
|
||||||
|
|
||||||
|
vault.save_index({"entries": {}})
|
||||||
|
|
||||||
|
monkeypatch.setattr(time, "time", lambda: 1000)
|
||||||
|
backup_mgr.create_backup()
|
||||||
|
first = fp_dir / "backups" / "entries_db_backup_1000.json.enc"
|
||||||
|
assert first.exists()
|
||||||
|
|
||||||
|
monkeypatch.setattr(time, "time", lambda: 1005)
|
||||||
|
backup_mgr.create_backup()
|
||||||
|
second = fp_dir / "backups" / "entries_db_backup_1005.json.enc"
|
||||||
|
assert not second.exists()
|
||||||
|
|
||||||
|
monkeypatch.setattr(time, "time", lambda: 1012)
|
||||||
|
backup_mgr.create_backup()
|
||||||
|
third = fp_dir / "backups" / "entries_db_backup_1012.json.enc"
|
||||||
|
assert third.exists()
|
@@ -14,6 +14,7 @@ runner = CliRunner()
|
|||||||
("secret_mode_enabled", "true", "set_secret_mode_enabled", True),
|
("secret_mode_enabled", "true", "set_secret_mode_enabled", True),
|
||||||
("clipboard_clear_delay", "10", "set_clipboard_clear_delay", 10),
|
("clipboard_clear_delay", "10", "set_clipboard_clear_delay", 10),
|
||||||
("additional_backup_path", "", "set_additional_backup_path", None),
|
("additional_backup_path", "", "set_additional_backup_path", None),
|
||||||
|
("backup_interval", "5", "set_backup_interval", 5.0),
|
||||||
(
|
(
|
||||||
"relays",
|
"relays",
|
||||||
"wss://a.com, wss://b.com",
|
"wss://a.com, wss://b.com",
|
||||||
|
@@ -158,3 +158,14 @@ def test_kdf_iterations_round_trip():
|
|||||||
|
|
||||||
cfg_mgr.set_kdf_iterations(200_000)
|
cfg_mgr.set_kdf_iterations(200_000)
|
||||||
assert cfg_mgr.get_kdf_iterations() == 200_000
|
assert cfg_mgr.get_kdf_iterations() == 200_000
|
||||||
|
|
||||||
|
|
||||||
|
def test_backup_interval_round_trip():
|
||||||
|
with TemporaryDirectory() as tmpdir:
|
||||||
|
vault, _ = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD)
|
||||||
|
cfg_mgr = ConfigManager(vault, Path(tmpdir))
|
||||||
|
|
||||||
|
assert cfg_mgr.get_backup_interval() == 0
|
||||||
|
|
||||||
|
cfg_mgr.set_backup_interval(15)
|
||||||
|
assert cfg_mgr.get_backup_interval() == 15
|
||||||
|
Reference in New Issue
Block a user