{ config, lib, pkgs, ... }: let inherit (lib) types; cfg = config.common.services.resticBackup; in { options.common.services.resticBackup = lib.mkOption { default = { }; type = types.attrsOf ( types.submodule { options = { enable = lib.mkOption { type = types.bool; default = false; }; user = lib.mkOption { type = types.str; default = "root"; }; environmentFile = lib.mkOption { type = with types; nullOr str; default = null; }; passwordFile = lib.mkOption { type = types.path; }; targets = lib.mkOption { type = with types; listOf str; }; sshKeyFile = lib.mkOption { type = types.path; }; timerConfig = lib.mkOption { type = types.attrs; default = { OnCalendar = "daily"; RandomizedDelaySec = 1200; }; }; paths = lib.mkOption { type = with types; listOf str; default = [ ]; }; runBeforeBackup = lib.mkOption { type = types.str; default = ""; }; postgresDatabases = lib.mkOption { type = with types; listOf str; default = [ ]; }; sqliteDatabases = lib.mkOption { type = with types; listOf str; default = [ ]; }; influxBuckets = lib.mkOption { type = with types; listOf str; default = [ ]; }; exclude = lib.mkOption { type = with types; listOf str; default = [ ]; }; }; } ); }; config = lib.mkIf (cfg != { }) { systemd.services = lib.pipe cfg [ lib.attrsToList (lib.map ( element: ( if element.value.enable then (lib.map ( target: lib.nameValuePair "restic-backups-${element.name}-${target}" { serviceConfig = { LoadCredential = [ "sshPrivateKey:${element.value.sshKeyFile}" "repositoryPassword:${element.value.passwordFile}" ]; EnvironmentFile = lib.optionals (element.value.environmentFile != null) [ element.value.environmentFile ]; }; } ) element.value.targets) else [ ] ) )) lib.flatten lib.listToAttrs ]; services.restic.backups = lib.pipe cfg [ lib.attrsToList (lib.map ( element: ( if element.value.enable then (lib.map ( target: lib.nameValuePair "${element.name}-${target}" { initialize = true; inherit (element.value) user timerConfig exclude; passwordFile = "%d/repositoryPassword"; extraOptions = [ "rclone.program=\"ssh -o StrictHostKeyChecking=no -i \${CREDENTIALS_DIRECTORY}/sshPrivateKey restic@${target}\"" ]; repository = "rclone:${element.name}"; paths = lib.mkMerge [ element.value.paths (lib.mkIf (element.value.postgresDatabases != [ ]) ( lib.map (element: "/tmp/postgresDatabases/${element}.sql.zst") element.value.postgresDatabases )) (lib.mkIf (element.value.sqliteDatabases != [ ]) ( lib.map ( element: "/tmp/sqliteDatabases/${builtins.baseNameOf element}.sqlite" ) element.value.sqliteDatabases )) (lib.mkIf (element.value.influxBuckets != [ ]) ( lib.map (element: "/tmp/influxBuckets/${element}") element.value.influxBuckets )) ]; backupPrepareCommand = element.value.runBeforeBackup + #dump postgresql databases (lib.optionalString (element.value.postgresDatabases != [ ]) ( lib.concatMapStringsSep "\n" (db: '' echo "Dumping Postgres-database: ${db}" mkdir -p /tmp/postgresDatabases ${lib.getExe' pkgs.postgresql "pg_dump"} ${db} | ${lib.getExe pkgs.zstd} --rsyncable > /tmp/postgresDatabases/${db}.sql.zst [ $(du -b /tmp/postgresDatabases/${db}.sql.zst | cut -f1) -gt "50" ] || exit 1 '') element.value.postgresDatabases )) + #dump sqlite databases (lib.optionalString (element.value.sqliteDatabases != [ ]) ( lib.concatMapStringsSep "\n" (db: '' echo "Dumping sqlite-database: ${db}" mkdir -p /tmp/sqliteDatabases ${lib.getExe pkgs.sqlite} ${db} ".backup '/tmp/sqliteDatabases/${builtins.baseNameOf db}.sqlite'" [ $(du -b /tmp/sqliteDatabases/${builtins.baseNameOf db}.sqlite | cut -f1) -gt "50" ] || exit 1 '') element.value.sqliteDatabases )) + #dump influx buckets (lib.optionalString (element.value.influxBuckets != [ ]) ( lib.concatMapStringsSep "\n" (db: '' echo "Dumping influx-bucket: ${db}" mkdir -p /tmp/influxBuckets ${pkgs.influxdb2}/bin/influx backup --compression=none --bucket=${db} /tmp/influxBuckets/${db} [ $(du -b /tmp/influxBuckets/${db} | cut -f1) -gt "50" ] || exit 1 '') element.value.influxBuckets )); } ) element.value.targets) else [ ] ) )) lib.flatten lib.listToAttrs ]; }; }