diff options
| -rw-r--r-- | accounts/gkleen@sif/systemd.nix | 66 |
1 files changed, 60 insertions, 6 deletions
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix index 21c50008..bd506590 100644 --- a/accounts/gkleen@sif/systemd.nix +++ b/accounts/gkleen@sif/systemd.nix | |||
| @@ -47,15 +47,69 @@ in { | |||
| 47 | Service = { | 47 | Service = { |
| 48 | Type = "oneshot"; | 48 | Type = "oneshot"; |
| 49 | WorkingDirectory = "~"; | 49 | WorkingDirectory = "~"; |
| 50 | ExecStart = toString (pkgs.writers.writePython3 "sync-keepass" {} '' | 50 | ExecStart = toString (pkgs.writers.writePython3 "sync-keepass" { |
| 51 | libraries = with pkgs.python3Packages; [ dateutil ]; | ||
| 52 | } '' | ||
| 51 | import json | 53 | import json |
| 52 | import subprocess | 54 | import subprocess |
| 53 | # from datetime import datetime | 55 | from os.path import (expanduser, getmtime, dirname) |
| 56 | from datetime import datetime | ||
| 57 | from dateutil.tz import tzlocal | ||
| 58 | from dateutil.parser import isoparse | ||
| 59 | from sys import stderr | ||
| 54 | 60 | ||
| 55 | res = None | 61 | |
| 56 | with subprocess.Popen(['rclone', 'lsjson', 'surtr:store.kdbx'], stdout=subprocess.PIPE) as proc: # noqa: E501 | 62 | remote_fs = 'surtr' |
| 57 | res = json.load(proc.stdout) | 63 | remote_file = f'{remote_fs}:store.kdbx' |
| 58 | print(res) | 64 | target_file = expanduser('~/store.kdbx') |
| 65 | meta_file = expanduser('~/.store.kdbx.json') | ||
| 66 | |||
| 67 | upload_time = None | ||
| 68 | our_last_upload_time = None | ||
| 69 | mod_time = None | ||
| 70 | |||
| 71 | |||
| 72 | def get_upload_time(): | ||
| 73 | upload_time = None | ||
| 74 | with subprocess.Popen(['rclone', 'lsjson', remote_file], stdout=subprocess.PIPE) as proc: # noqa: E501 | ||
| 75 | for file in json.load(proc.stdout): | ||
| 76 | if file['Path'] != 'store.kdbx': | ||
| 77 | continue | ||
| 78 | upload_time = isoparse(file['ModTime']) | ||
| 79 | return upload_time | ||
| 80 | |||
| 81 | |||
| 82 | def do_upload(): | ||
| 83 | print('Uploading', file=stderr) | ||
| 84 | subprocess.run(['rclone', 'copy', '-I', target_file, f'{remote_fs}:'], check=True) # noqa: E501 | ||
| 85 | upload_time = get_upload_time() | ||
| 86 | with open(meta_file, 'w') as file: | ||
| 87 | json.dump({'our_last_upload_time': upload_time.isoformat()}, file) | ||
| 88 | |||
| 89 | |||
| 90 | def do_download(): | ||
| 91 | print('Downloading', file=stderr) | ||
| 92 | subprocess.run(['rclone', 'copy', '-I', remote_file, dirname(target_file)], check=True) # noqa: E501 | ||
| 93 | |||
| 94 | |||
| 95 | upload_time = get_upload_time() | ||
| 96 | |||
| 97 | try: | ||
| 98 | with open(meta_file, 'r') as file: | ||
| 99 | file_content = json.load(file) | ||
| 100 | our_last_upload_time = isoparse(file_content['our_last_upload_time']) # noqa: E501 | ||
| 101 | except FileNotFoundError: | ||
| 102 | pass | ||
| 103 | |||
| 104 | try: | ||
| 105 | mod_time = datetime.fromtimestamp(getmtime(target_file)).replace(tzinfo=tzlocal()) # noqa: E501 | ||
| 106 | except FileNotFoundError: | ||
| 107 | pass | ||
| 108 | |||
| 109 | if upload_time is None or (mod_time is not None and mod_time > upload_time): # noqa: E501 | ||
| 110 | do_upload() | ||
| 111 | elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 | ||
| 112 | do_download() | ||
| 59 | ''); | 113 | ''); |
| 60 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; | 114 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; |
| 61 | }; | 115 | }; |
