mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-23 10:45:20 -05:00
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Michael Genson <genson.michael@gmail.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import shutil
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
from mealie.core.config import get_app_settings
|
||||||
from mealie.services._base_service import BaseService
|
from mealie.services._base_service import BaseService
|
||||||
from mealie.services.backups_v2.alchemy_exporter import AlchemyExporter
|
from mealie.services.backups_v2.alchemy_exporter import AlchemyExporter
|
||||||
from mealie.services.backups_v2.backup_file import BackupFile
|
from mealie.services.backups_v2.backup_file import BackupFile
|
||||||
@@ -13,6 +14,12 @@ class BackupSchemaMismatch(Exception): ...
|
|||||||
|
|
||||||
|
|
||||||
class BackupV2(BaseService):
|
class BackupV2(BaseService):
|
||||||
|
EXCLUDE_DIRS = {"backups", ".temp"}
|
||||||
|
EXCLUDE_FILES = {"mealie.db", "mealie.log"}
|
||||||
|
EXCLUDE_EXTENTIONS = {".zip"}
|
||||||
|
|
||||||
|
RESTORE_FILES = {".secret"}
|
||||||
|
|
||||||
def __init__(self, db_url: str | None = None) -> None:
|
def __init__(self, db_url: str | None = None) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@@ -33,10 +40,6 @@ class BackupV2(BaseService):
|
|||||||
|
|
||||||
def backup(self) -> Path:
|
def backup(self) -> Path:
|
||||||
# sourcery skip: merge-nested-ifs, reintroduce-else, remove-redundant-continue
|
# sourcery skip: merge-nested-ifs, reintroduce-else, remove-redundant-continue
|
||||||
exclude = {"mealie.db", "mealie.log", ".secret"}
|
|
||||||
exclude_ext = {".zip"}
|
|
||||||
exclude_dirs = {"backups", ".temp"}
|
|
||||||
|
|
||||||
timestamp = datetime.datetime.now(datetime.UTC).strftime("%Y.%m.%d.%H.%M.%S")
|
timestamp = datetime.datetime.now(datetime.UTC).strftime("%Y.%m.%d.%H.%M.%S")
|
||||||
|
|
||||||
backup_name = f"mealie_{timestamp}.zip"
|
backup_name = f"mealie_{timestamp}.zip"
|
||||||
@@ -48,11 +51,11 @@ class BackupV2(BaseService):
|
|||||||
zip_file.writestr("database.json", json.dumps(database_json))
|
zip_file.writestr("database.json", json.dumps(database_json))
|
||||||
|
|
||||||
for data_file in self.directories.DATA_DIR.glob("**/*"):
|
for data_file in self.directories.DATA_DIR.glob("**/*"):
|
||||||
if data_file.name in exclude:
|
if data_file.name in self.EXCLUDE_FILES:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if data_file.is_file() and data_file.suffix not in exclude_ext:
|
if data_file.is_file() and data_file.suffix not in self.EXCLUDE_EXTENTIONS:
|
||||||
if data_file.parent.name in exclude_dirs:
|
if data_file.parent.name in self.EXCLUDE_DIRS:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
zip_file.write(data_file, f"data/{data_file.relative_to(self.directories.DATA_DIR)}")
|
zip_file.write(data_file, f"data/{data_file.relative_to(self.directories.DATA_DIR)}")
|
||||||
@@ -62,11 +65,20 @@ class BackupV2(BaseService):
|
|||||||
def _copy_data(self, data_path: Path) -> None:
|
def _copy_data(self, data_path: Path) -> None:
|
||||||
for f in data_path.iterdir():
|
for f in data_path.iterdir():
|
||||||
if f.is_file():
|
if f.is_file():
|
||||||
|
if f.name not in self.RESTORE_FILES:
|
||||||
|
continue
|
||||||
|
|
||||||
|
shutil.copyfile(f, self.directories.DATA_DIR / f.name)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
shutil.rmtree(self.directories.DATA_DIR / f.name)
|
shutil.rmtree(self.directories.DATA_DIR / f.name)
|
||||||
shutil.copytree(f, self.directories.DATA_DIR / f.name)
|
shutil.copytree(f, self.directories.DATA_DIR / f.name)
|
||||||
|
|
||||||
|
# since we copied a new .secret, AppSettings has the wrong secret info
|
||||||
|
self.logger.info("invalidating appsettings cache")
|
||||||
|
get_app_settings.cache_clear()
|
||||||
|
self.settings = get_app_settings()
|
||||||
|
|
||||||
def restore(self, backup_path: Path) -> None:
|
def restore(self, backup_path: Path) -> None:
|
||||||
self.logger.info("initializing backup restore")
|
self.logger.info("initializing backup restore")
|
||||||
|
|
||||||
@@ -105,5 +117,4 @@ class BackupV2(BaseService):
|
|||||||
self.logger.info("restoring data directory")
|
self.logger.info("restoring data directory")
|
||||||
self._copy_data(contents.data_directory)
|
self._copy_data(contents.data_directory)
|
||||||
self.logger.info("data directory restored successfully")
|
self.logger.info("data directory restored successfully")
|
||||||
|
|
||||||
self.logger.info("backup restore complete")
|
self.logger.info("backup restore complete")
|
||||||
|
|||||||
@@ -249,6 +249,8 @@ def test_database_restore_data():
|
|||||||
|
|
||||||
settings = get_app_settings()
|
settings = get_app_settings()
|
||||||
backup_v2 = BackupV2(settings.DB_URL)
|
backup_v2 = BackupV2(settings.DB_URL)
|
||||||
|
|
||||||
|
backup_v2.directories.BACKUP_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
original_data_backup = backup_v2.backup()
|
original_data_backup = backup_v2.backup()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user