{ config, lib, pkgs, ... }: with lib; let cfg = config.clerie.backup; generateBackupServiceUnit = jobName: job: let targets = cfg.targets; jobPasswordFile = if job.passwordFile == null then config.age."backup-job-${jobName}".path else job.passwordFile; repoPath = if job.repoPath == null then "/${config.networking.hostName}/${jobName}" else job.repoPath; unit = concatMapAttrs targetName: target: let targetPasswordFile = if target.passwordFile == null then config.age."backup-target-${targetName}".path else target.passwordFile; targetUsername = if target.username == null then config.networing.hostName else target.username; in (nameValuePair "clerie-restic-backup-${jobName}-${targetName}" { requires = [ "network.target" "local-fs.target" ]; path = [ pkgs.resic ]; serviceConfig = { Type = "oneshot"; }; script = '' set -euo pipefail export RESTIC_PASSWORD_FILE=${jobPasswordFile} export RESTIC_REPOSITORY="rest:https://${targetUsername}:$(cat ${targetPasswordFile})@${target.serverName}${repoPath}" restic snapshots || restic init restic backup ${escapeShellArgs job.paths} restic check ''}); in units; }; targetOptions = { ... }: { options = { passwordFile = mkOption { type = with types; nullOr str; default = null; }; username = mkOption { type = with types; nullOr str; default = null; }; serverName = mkOption { type = types.str; }; }; }; jobModules = { ... }: { options = { passwordFile = mkOption { type = with types; nullOr str; default = null; }; repoPath = mkOption { type = with types; nullOr str; default = null; }; targets = mkOption { type = with types; nullOr (listOf str); defualt = null; }; paths = mkOption { type = with types; listOf str; }; }; }; in { options = { clerie.backup = { enable = mkEnableOption "clerie's Backup"; targets = { type = with types; attrsOF (submodule targetOptions); default = {}; }; jobs = mkOption { type = with types; attrsOf (submodule jobOptions); default = {}; }; }; }; config = mkIf cfg.enable { systemd.services = mapAttrs' generateBackupServiceUnit (cfg.jobs) }; }