flake: { config, lib, pkgs, ... }: let inherit (lib) filterAttrs foldl imap1 mapAttrsToList mkEnableOption mkIf mkMerge mkOption mkPackageOption types ; in { options = { services.intake = { package = mkPackageOption pkgs "intake" { }; extraPackages = mkOption { type = types.listOf types.package; default = [ ]; description = "Extra packages available to all enabled users and their intake services."; }; users = mkOption { description = "User intake service definitions."; default = { }; type = types.attrsOf ( types.submodule { options = { enable = mkEnableOption "intake, a universal and extensible feed aggregator."; dataDir = mkOption { type = types.str; default = "/home/$USER/.local/share/intake"; description = "The data directory for this user's intake service."; }; listen.addr = mkOption { type = types.str; default = "127.0.0.1"; description = "The listen address for this user's intake service."; }; listen.port = mkOption { type = types.port; default = 80; description = "The listen port for this user's intake service."; }; extraPackages = mkOption { type = types.listOf types.package; default = [ ]; description = "Extra packages available to this user and their intake service."; }; }; } ); }; }; }; config = let intakeCfg = config.services.intake; enabledUsers = filterAttrs (userName: userCfg: userCfg.enable) intakeCfg.users; enabledUserNames = mapAttrsToList (userName: userCfg: userName) enabledUsers; userPackages = userName: [ intakeCfg.package ] ++ intakeCfg.extraPackages ++ intakeCfg.users.${userName}.extraPackages; in { # Apply the overlay so intake is included in pkgs. nixpkgs.overlays = [ flake.overlays.default ]; # Give every intake user the shared packages and their user-specific packages. users.users = let addPackagesToUser = userName: { ${userName}.packages = userPackages userName; }; in mkMerge (map addPackagesToUser enabledUserNames); # Define a user service for each configured user systemd.services = let runScript = userName: let listen = enabledUsers.${userName}.listen; in pkgs.writeShellScript "intake-run.sh" '' mkdir -p $INTAKE_DATA_DIR ${intakeCfg.package}/bin/intake serve --addr ${listen.addr} --port ${toString listen.port} ''; # systemd service definition for a single user, given `services.intake.users.userName` = `userCfg` userServiceConfig = userName: userCfg: { "intake-${userName}" = { description = "Intake service for user ${userName}"; script = "${runScript userName}"; path = config.environment.systemPackages ++ intakeCfg.extraPackages ++ userCfg.extraPackages; environment = { INTAKE_DATA_DIR = "/home/${userName}/.local/share/intake"; }; serviceConfig = { User = userName; Type = "simple"; }; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; enable = userCfg.enable; }; }; in mkMerge (mapAttrsToList userServiceConfig enabledUsers); }; }