@@ -14,11 +14,86 @@ with lib; let
1414 rootFolders = import ./rootFolders.nix { inherit lib pkgs serviceName ; } ;
1515 downloadClients = import ./downloadClients.nix { inherit lib pkgs serviceName ; } ;
1616 capitalizedName = toUpper ( substring 0 1 serviceName ) + substring 1 ( - 1 ) serviceName ;
17+ screamingName = toUpper serviceName ;
1718 usesMediaDirs = ! ( elem serviceName [ "prowlarr" ] ) ;
19+ mkServarrSettingsOptions = name :
20+ lib . mkOption {
21+ type = lib . types . submodule {
22+ freeformType = ( pkgs . formats . ini { } ) . type ;
23+ options = {
24+ update = {
25+ mechanism = lib . mkOption {
26+ type = with lib . types ;
27+ nullOr ( enum [
28+ "external"
29+ "builtIn"
30+ "script"
31+ ] ) ;
32+ description = "which update mechanism to use" ;
33+ default = "external" ;
34+ } ;
35+ automatically = lib . mkOption {
36+ type = lib . types . bool ;
37+ description = "Automatically download and install updates." ;
38+ default = false ;
39+ } ;
40+ } ;
41+ server = {
42+ port = lib . mkOption {
43+ type = lib . types . port ;
44+ description = "Port Number" ;
45+ } ;
46+ } ;
47+ log = {
48+ analyticsEnabled = lib . mkOption {
49+ type = lib . types . bool ;
50+ description = "Send Anonymous Usage Data" ;
51+ default = false ;
52+ } ;
53+ } ;
54+ } ;
55+ } ;
56+ example = lib . options . literalExpression ''
57+ {
58+ update.mechanism = "internal";
59+ server = {
60+ urlbase = "localhost";
61+ port = ${ toString port } ;
62+ bindaddress = "*";
63+ };
64+ }
65+ '' ;
66+ default = { } ;
67+ description = ''
68+ Attribute set of arbitrary config options.
69+ Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config).
70+
71+ WARNING: this configuration is stored in the world-readable Nix store!
72+ Don't put secrets here!
73+ '' ;
74+ } ;
75+
76+ mkServarrSettingsEnvVars = name : settings :
77+ lib . pipe settings [
78+ ( lib . mapAttrsRecursive (
79+ path : value :
80+ lib . optionalAttrs ( value != null ) {
81+ name = lib . toUpper "${ name } __${ lib . concatStringsSep "__" path } " ;
82+ value = toString (
83+ if lib . isBool value
84+ then lib . boolToString value
85+ else value
86+ ) ;
87+ }
88+ ) )
89+ ( lib . collect ( x : lib . isString x . name or false && lib . isString x . value or false ) )
90+ lib . listToAttrs
91+ ] ;
1892in {
1993 options . nixflix . ${ serviceName } =
2094 {
2195 enable = mkEnableOption "${ capitalizedName } " ;
96+ package = lib . mkPackageOption pkgs serviceName { } ;
2297
2398 vpn = {
2499 enable = mkOption {
44119 description = "Group under which the service runs" ;
45120 } ;
46121
122+ openFirewall = lib . mkOption {
123+ type = lib . types . bool ;
124+ default = false ;
125+ description = "Open ports in the firewall for the Radarr web interface." ;
126+ } ;
127+
128+ settings = mkServarrSettingsOptions serviceName ;
129+
47130 config = mkOption {
48131 type = types . submodule {
49132 options =
75158 }
76159 // optionalAttrs usesMediaDirs {
77160 mediaDirs = mkOption {
78- type = types . listOf ( types . submodule {
79- options = {
80- dir = mkOption {
81- type = types . str ;
82- description = "Directory path" ;
83- } ;
84- owner = mkOption {
85- type = types . str ;
86- default = "root" ;
87- description = "Directory owner" ;
88- } ;
89- } ;
90- } ) ;
161+ type = types . listOf types . path ;
91162 default = [ ] ;
92163 description = "List of media directories to create and manage" ;
93164 } ;
@@ -101,74 +172,52 @@ in {
101172 }
102173 ] ;
103174
104- nixflix . ${ serviceName } . config = {
105- apiKeyPath = mkDefault null ;
106- hostConfig = {
107- username = mkDefault serviceName ;
108- passwordPath = mkDefault null ;
109- instanceName = mkDefault capitalizedName ;
110- urlBase = mkDefault (
111- if nixflix . serviceNameIsUrlBase
112- then "/${ serviceName } "
113- else ""
175+ nixflix . ${ serviceName } = {
176+ settings = mkDefault ( {
177+ auth = {
178+ required = "Enabled" ;
179+ method = "Forms" ;
180+ } ;
181+ server = { inherit ( cfg . config . hostConfig ) port urlBase ; } ;
182+ }
183+ // optionalAttrs config . services . postgresql . enable {
184+ log . dbEnabled = true ;
185+ postgres = {
186+ inherit ( cfg ) user ;
187+ host = "/run/postgresql" ;
188+ port = 5432 ;
189+ mainDb = cfg . user ;
190+ logDb = cfg . user ;
191+ } ;
192+ } ) ;
193+ config = {
194+ apiKeyPath = mkDefault null ;
195+ hostConfig = {
196+ username = mkDefault serviceName ;
197+ passwordPath = mkDefault null ;
198+ instanceName = mkDefault capitalizedName ;
199+ urlBase = mkDefault (
200+ if nixflix . serviceNameIsUrlBase
201+ then "/${ serviceName } "
202+ else ""
203+ ) ;
204+ } ;
205+ downloadClients = mkDefault (
206+ optionals ( config . nixflix . sabnzbd . enable or false ) [
207+ {
208+ name = "SABnzbd" ;
209+ implementationName = "SABnzbd" ;
210+ inherit ( config . nixflix . sabnzbd ) apiKeyPath ;
211+ inherit ( config . nixflix . sabnzbd . settings ) host ;
212+ inherit ( config . nixflix . sabnzbd . settings ) port ;
213+ urlBase = config . nixflix . sabnzbd . settings . url_base ;
214+ }
215+ ]
114216 ) ;
115217 } ;
116- downloadClients = mkDefault (
117- optionals ( config . nixflix . sabnzbd . enable or false ) [
118- {
119- name = "SABnzbd" ;
120- implementationName = "SABnzbd" ;
121- inherit ( config . nixflix . sabnzbd ) apiKeyPath ;
122- inherit ( config . nixflix . sabnzbd . settings ) host ;
123- inherit ( config . nixflix . sabnzbd . settings ) port ;
124- urlBase = config . nixflix . sabnzbd . settings . url_base ;
125- }
126- ]
127- ) ;
128218 } ;
129219
130- nixflix . dirRegistrations =
131- [
132- {
133- inherit ( cfg ) group ;
134- dir = stateDir ;
135- owner = cfg . user ;
136- }
137- ]
138- ++ optionals usesMediaDirs ( map ( mediaDir : {
139- inherit ( cfg ) group ;
140- inherit ( mediaDir ) dir owner ;
141- } )
142- cfg . mediaDirs ) ;
143-
144220 services = {
145- ${ serviceName } =
146- {
147- inherit ( cfg ) enable ;
148- inherit ( cfg ) user group ;
149- dataDir = stateDir ;
150- }
151- // {
152- settings =
153- {
154- auth = {
155- required = "Enabled" ;
156- method = "Forms" ;
157- } ;
158- server = { inherit ( cfg . config . hostConfig ) port urlBase ; } ;
159- }
160- // optionalAttrs config . services . postgresql . enable {
161- log . dbEnabled = true ;
162- postgres = {
163- inherit ( cfg ) user ;
164- host = "/run/postgresql" ;
165- port = 5432 ;
166- mainDb = cfg . user ;
167- logDb = cfg . user ;
168- } ;
169- } ;
170- } ;
171-
172221 postgresql = mkIf config . services . postgresql . enable {
173222 ensureDatabases = [ cfg . user ] ;
174223 ensureUsers = [
@@ -204,13 +253,29 @@ in {
204253 users . ${ cfg . user } =
205254 {
206255 inherit ( cfg ) group ;
256+ home = stateDir ;
207257 isSystemUser = true ;
208258 }
209259 // optionalAttrs ( globals . uids ? ${ cfg . user } ) {
210260 uid = globals . uids . ${ cfg . user } ;
211261 } ;
212262 } ;
213263
264+ networking . firewall = lib . mkIf cfg . openFirewall {
265+ allowedTCPPorts = [ cfg . config . hostConfig . port ] ;
266+ } ;
267+
268+ systemd . tmpfiles = {
269+ settings . "10-${ serviceName } " . ${ stateDir } . d = {
270+ inherit ( cfg ) user group ;
271+ mode = "0700" ;
272+ } ;
273+
274+ rules =
275+ optionals usesMediaDirs ( map ( mediaDir : "d '${ mediaDir } ' 0770 ${ globals . libraryOwner . user } ${ globals . libraryOwner . group } - -" )
276+ cfg . mediaDirs ) ;
277+ } ;
278+
214279 systemd . services =
215280 {
216281 "${ serviceName } -wait-for-db" = mkIf config . services . postgresql . enable {
@@ -240,8 +305,11 @@ in {
240305 } ;
241306
242307 ${ serviceName } = {
308+ description = capitalizedName ;
309+ environment = mkServarrSettingsEnvVars screamingName cfg . settings ;
310+
243311 after =
244- [ "nixflix-setup-dirs.service" ]
312+ [ "network.target" " nixflix-setup-dirs.service"]
245313 ++ ( optional ( cfg . config . apiKeyPath != null && cfg . config . hostConfig . passwordPath != null ) "${ serviceName } -env.service" )
246314 ++ ( optional config . services . postgresql . enable "postgresql-ready.target" )
247315 ++ ( optional config . nixflix . mullvad . enable "mullvad-config.service" ) ;
@@ -250,19 +318,22 @@ in {
250318 ++ ( optional ( cfg . config . apiKeyPath != null && cfg . config . hostConfig . passwordPath != null ) "${ serviceName } -env.service" )
251319 ++ ( optional config . services . postgresql . enable "postgresql-ready.target" ) ;
252320 wants = optional config . nixflix . mullvad . enable "mullvad-config.service" ;
321+ wantedBy = [ "multi-user.target" ] ;
253322
254323 serviceConfig =
255324 {
256- DynamicUser = mkForce false ;
325+ Type = "simple" ;
257326 User = cfg . user ;
258327 Group = cfg . group ;
328+ ExecStart = "${ getExe cfg . package } -nobrowser -data='${ stateDir } '" ;
329+ Restart = "on-failure" ;
259330 }
260331 // optionalAttrs ( cfg . config . apiKeyPath != null && cfg . config . hostConfig . passwordPath != null ) {
261332 EnvironmentFile = "/run/${ serviceName } /env" ;
262333 }
263334 // optionalAttrs ( config . nixflix . mullvad . enable && ! cfg . vpn . enable ) {
264335 ExecStart = mkForce ( pkgs . writeShellScript "${ serviceName } -vpn-bypass" ''
265- exec /run/wrappers/bin/mullvad-exclude ${ getExe config . services . ${ serviceName } . package } \
336+ exec /run/wrappers/bin/mullvad-exclude ${ getExe cfg . package } \
266337 -nobrowser -data='${ stateDir } '
267338 '' ) ;
268339 AmbientCapabilities = "CAP_SYS_ADMIN" ;
0 commit comments