A personal podcast client
at main 134 lines 4.1 kB view raw
1{ self, ... }: 2{ 3 flake.nixosModules.default = 4 { 5 config, 6 lib, 7 pkgs, 8 ... 9 }: 10 let 11 cfg = config.services.podcasts; 12 podcasts = self.outputs.packages."${pkgs.stdenv.hostPlatform.system}".default; 13 stateDirectory = "/var/lib/podcasts/"; 14 podcastDir = "${cfg.annexDir}/${cfg.podcastSubdir}"; 15 commonServiceConfig = { 16 StateDirectory = "podcasts"; 17 # TODO more hardening 18 PrivateTmp = true; 19 RemoveIPC = true; 20 NoNewPrivileges = true; 21 ProtectSystem = "strict"; 22 ProtectHome = if lib.hasPrefix "/home" podcastDir then "tmpfs" else "true"; 23 RestrictSUIDSGID = true; 24 }; 25 environment = { 26 PODCASTS_ANNEX_DIR = podcastDir; 27 PODCASTS_DATA_DIR = cfg.dataDir; 28 PODCASTS_DOMAIN = "https://podcasts.peterrice.xyz"; 29 }; 30 in 31 { 32 options = { 33 services.podcasts = with lib; { 34 annexDir = mkOption { 35 type = types.str; 36 default = stateDirectory + "annex"; 37 }; 38 podcastSubdir = mkOption { 39 type = types.str; 40 default = "hosted-podcasts"; 41 }; 42 dataDir = mkOption { 43 type = types.str; 44 default = stateDirectory; 45 }; 46 fetch = { 47 enable = mkEnableOption "fetch-podcasts"; 48 user = mkOption { 49 type = types.str; 50 default = "podcasts"; 51 }; 52 group = mkOption { 53 type = types.str; 54 default = "podcasts"; 55 }; 56 startAt = mkOption { 57 type = with types; either str (listOf str); 58 default = "daily"; 59 }; 60 }; 61 serve = { 62 enable = mkEnableOption "serve-podcasts"; 63 user = mkOption { 64 type = types.str; 65 default = "podcasts"; 66 }; 67 group = mkOption { 68 type = types.str; 69 default = "podcasts"; 70 }; 71 bind = mkOption { 72 type = types.str; 73 default = "127.0.0.1:5998"; 74 }; 75 timeout = mkOption { 76 type = types.int; 77 default = 30; 78 }; 79 }; 80 }; 81 }; 82 config = lib.mkIf (cfg.fetch.enable || cfg.serve.enable) { 83 systemd.services.fetch-podcasts = { 84 inherit (cfg.fetch) enable startAt; 85 inherit environment; 86 path = [ 87 pkgs.git 88 pkgs.git-annex 89 ]; 90 serviceConfig = commonServiceConfig // { 91 User = cfg.fetch.user; 92 Group = cfg.fetch.group; 93 Type = "oneshot"; 94 BindPaths = [ 95 cfg.annexDir 96 cfg.dataDir 97 ]; 98 ExecStart = "${podcasts}/bin/fetch-podcasts"; 99 }; 100 }; 101 systemd.services.serve-podcasts = { 102 inherit (cfg.serve) enable; 103 serviceConfig = commonServiceConfig // { 104 User = cfg.serve.user; 105 Group = cfg.serve.group; 106 BindReadOnlyPaths = [ 107 podcastDir 108 cfg.dataDir 109 ]; 110 ExecStart = '' 111 ${podcasts.python.pkgs.gunicorn}/bin/gunicorn -b ${cfg.serve.bind} -t ${toString cfg.serve.timeout} podcasts.serve:app 112 ''; 113 }; 114 environment = environment // { 115 PYTHONPATH = "${podcasts.python.pkgs.makePythonPath podcasts.propagatedBuildInputs}:${podcasts.outPath}/${podcasts.python.sitePackages}"; 116 }; 117 wantedBy = [ "multi-user.target" ]; 118 after = [ "network.target" ] ++ lib.optional cfg.fetch.enable "fetch-podcasts.timer"; 119 }; 120 121 users.users = lib.mkIf (cfg.fetch.user == "podcasts" || cfg.serve.user == "podcasts") { 122 podcasts = { 123 isSystemUser = true; 124 group = "podcasts"; 125 home = stateDirectory; 126 }; 127 }; 128 129 users.groups = lib.mkIf (cfg.fetch.group == "podcasts" || cfg.serve.group == "podcasts") { 130 podcasts = { }; 131 }; 132 }; 133 }; 134}