|
7 | 7 | }: |
8 | 8 | let |
9 | 9 | cfg = config.services.sonarr; |
| 10 | + |
| 11 | + settingsEnvVars = lib.pipe cfg.settings [ |
| 12 | + (lib.mapAttrsRecursive ( |
| 13 | + path: value: |
| 14 | + lib.optionalAttrs (value != null) { |
| 15 | + name = lib.toUpper "SONARR__${lib.concatStringsSep "__" path}"; |
| 16 | + value = toString (if lib.isBool value then lib.boolToString value else value); |
| 17 | + } |
| 18 | + )) |
| 19 | + (lib.collect (x: lib.isString x.name or false && lib.isString x.value or false)) |
| 20 | + lib.listToAttrs |
| 21 | + ]; |
10 | 22 | in |
11 | 23 | { |
12 | 24 | options = { |
|
27 | 39 | ''; |
28 | 40 | }; |
29 | 41 |
|
| 42 | + apiKeyFile = lib.mkOption { |
| 43 | + type = lib.types.nullOr lib.types.path; |
| 44 | + description = '' |
| 45 | + Path to the file containing the API key for Sonarr (32 chars). |
| 46 | + This will overwrite the API key in the config file. |
| 47 | + ''; |
| 48 | + example = "/run/secrets/sonarr-apikey"; |
| 49 | + default = null; |
| 50 | + }; |
| 51 | + |
| 52 | + environmentFiles = lib.mkOption { |
| 53 | + type = lib.types.listOf lib.types.path; |
| 54 | + default = [ ]; |
| 55 | + description = '' |
| 56 | + Environment file to pass secret configuration values. |
| 57 | +
|
| 58 | + Each line must follow the `SONARR__SECTION__KEY=value` pattern. |
| 59 | + Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config). |
| 60 | + ''; |
| 61 | + }; |
| 62 | + |
| 63 | + settings = lib.mkOption { |
| 64 | + type = lib.types.submodule { |
| 65 | + freeformType = (pkgs.formats.ini { }).type; |
| 66 | + options = { |
| 67 | + update = { |
| 68 | + mechanism = lib.mkOption { |
| 69 | + type = |
| 70 | + with lib.types; |
| 71 | + nullOr (enum [ |
| 72 | + "external" |
| 73 | + "builtIn" |
| 74 | + "script" |
| 75 | + ]); |
| 76 | + description = "which update mechanism to use"; |
| 77 | + default = "external"; |
| 78 | + }; |
| 79 | + automatically = lib.mkOption { |
| 80 | + type = lib.types.bool; |
| 81 | + description = "Automatically download and install updates."; |
| 82 | + default = false; |
| 83 | + }; |
| 84 | + }; |
| 85 | + server = { |
| 86 | + port = lib.mkOption { |
| 87 | + type = lib.types.int; |
| 88 | + description = "Port Number"; |
| 89 | + default = 8989; |
| 90 | + }; |
| 91 | + }; |
| 92 | + log = { |
| 93 | + analyticsEnabled = lib.mkOption { |
| 94 | + type = lib.types.bool; |
| 95 | + description = "Send Anonymous Usage Data"; |
| 96 | + default = false; |
| 97 | + }; |
| 98 | + }; |
| 99 | + }; |
| 100 | + }; |
| 101 | + example = lib.options.literalExpression '' |
| 102 | + { |
| 103 | + update.mechanism = "internal"; |
| 104 | + server = { |
| 105 | + urlbase = "localhost"; |
| 106 | + port = 8989; |
| 107 | + bindaddress = "*"; |
| 108 | + }; |
| 109 | + } |
| 110 | + ''; |
| 111 | + default = { }; |
| 112 | + description = '' |
| 113 | + Attribute set of arbitrary config options. |
| 114 | + Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config). |
| 115 | +
|
| 116 | + WARNING: this configuration is stored in the world-readable Nix store! |
| 117 | + Use [](#opt-services.sonarr.environmentFiles) if it contains a secret. |
| 118 | + ''; |
| 119 | + }; |
| 120 | + |
30 | 121 | user = lib.mkOption { |
31 | 122 | type = lib.types.str; |
32 | 123 | default = "sonarr"; |
|
44 | 135 | }; |
45 | 136 |
|
46 | 137 | config = lib.mkIf cfg.enable { |
| 138 | + assertions = [ |
| 139 | + { |
| 140 | + assertion = !(cfg.settings ? auth && cfg.settings.auth ? apikey); |
| 141 | + message = '' |
| 142 | + The `services.sonarr.settings` attribute set must not contain `ApiKey`, you should instead use `services.sonarr.apiKeyFile` to avoid storing secrets in the Nix store. |
| 143 | + ''; |
| 144 | + } |
| 145 | + ]; |
| 146 | + |
47 | 147 | systemd.tmpfiles.rules = [ |
48 | 148 | "d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -" |
49 | 149 | ]; |
|
52 | 152 | description = "Sonarr"; |
53 | 153 | after = [ "network.target" ]; |
54 | 154 | wantedBy = [ "multi-user.target" ]; |
55 | | - |
| 155 | + environment = settingsEnvVars; |
56 | 156 | serviceConfig = { |
57 | 157 | Type = "simple"; |
58 | 158 | User = cfg.user; |
59 | 159 | Group = cfg.group; |
| 160 | + EnvironmentFile = cfg.environmentFiles; |
| 161 | + LoadCredential = lib.optional (cfg.apiKeyFile != null) "SONARR__AUTH__APIKEY:${cfg.apiKeyFile}"; |
| 162 | + ExecStartPre = lib.optionalString (cfg.apiKeyFile != null) pkgs.writeShellScript "sonarr-apikey" '' |
| 163 | + if [ ! -s config.xml ]; then |
| 164 | + echo '<?xml version="1.0" encoding="UTF-8"?><Config><ApiKey></ApiKey></Config>' > "${cfg.dataDir}/config.xml" |
| 165 | + fi |
| 166 | + ${lib.getExe pkgs.xmlstarlet} ed -L -u "/Config/ApiKey" -v "@API_KEY@" "${cfg.dataDir}/config.xml" |
| 167 | + ${lib.getExe pkgs.replace-secret} '@API_KEY@' ''${CREDENTIALS_DIRECTORY}/SONARR__AUTH__APIKEY "${cfg.dataDir}/config.xml" |
| 168 | + ''; |
60 | 169 | ExecStart = utils.escapeSystemdExecArgs [ |
61 | 170 | (lib.getExe cfg.package) |
62 | 171 | "-nobrowser" |
|
67 | 176 | }; |
68 | 177 |
|
69 | 178 | networking.firewall = lib.mkIf cfg.openFirewall { |
70 | | - allowedTCPPorts = [ 8989 ]; |
| 179 | + allowedTCPPorts = [ cfg.settings.server.port ]; |
71 | 180 | }; |
72 | 181 |
|
73 | 182 | users.users = lib.mkIf (cfg.user == "sonarr") { |
|
0 commit comments