#!/usr/bin/env python3 import gotify import json import subprocess import os from pathlib import Path def read_config(): source_path = Path(__file__).resolve() secrets = source_path.parent / "secrets/paperless.json" with open(secrets, "r") as f: config = json.load(f) return config class BackupManager: def __init__(self): self._config = read_config() self._remotes = self._config["remotes"] self._common = self._config["common"] self._gotify = gotify.Gotify(self._common["GOTIFY_TOKEN"]) def export_data(self): cmd = "docker compose exec -it webserver document_exporter ../export -d -f" try: result = subprocess.run( cmd, shell=True, text=True, check=True, capture_output=True ) except subprocess.CalledProcessError as e: self._gotify.send_subprocess_error("Exporting data failed", e) return False self._gotify.send_success("Data exported.", result) return True def borg_backup(self): # common config for all remotes backup_dirs = " ".join(self._common["BACKUP_DIRS"]) exclude_dirs = " ".join(self._common["EXCLUDE_DIRS"]) repo_subdir = self._common["REPO_SUBDIR"] time_format = self._common["TIME_FORMAT"] disabled_remotes = [] for remote in self._remotes: remote_host = remote["HOSTNAME"] if not remote["enabled"]: disabled_remotes.append(remote_host) continue local_host = os.uname().nodename backup_user = remote["BACKUP_USER"] repo_prefix = remote["REPO_PREFIX"] borg_env = os.environ.copy() borg_env["BORG_RSH"] = remote["BORG_RSH"] borg_env["BORG_PASSPHRASE"] = remote["BORG_PASSPHRASE"] repo = f"ssh://{backup_user}@{remote_host}/{repo_prefix}/{local_host}/{repo_subdir}::{{{time_format}}}" cmd = f"borg create -v --stats {repo} {backup_dirs} --exclude {exclude_dirs}" try: result = subprocess.run( cmd, shell=True, check=True, text=True, capture_output=True, env=borg_env, ) except subprocess.CalledProcessError as e: self._gotify.send_subprocess_error("Backup failed", e) return False self._gotify.send_backup_successful(result) if disabled_remotes: text = "Skipped disabled remotes:\n" + "\n".join(disabled_remotes) self._gotify.send_info(title="Skipped remotes", text=text) return True def main(): config = read_config() try: os.chdir(config["common"]["BACKUP_DOCKER_DIR"]) except KeyError: pass backup_manager = BackupManager() if not backup_manager.export_data(): exit(1) if not backup_manager.borg_backup(): exit(1) exit(0) if __name__ == "__main__": main()