Skip to content

Commit ea76db1

Browse files
committed
create main service from scratch
1 parent 7ea6c51 commit ea76db1

14 files changed

Lines changed: 220 additions & 238 deletions

modules/arr-common/mkArrServiceModule.nix

Lines changed: 150 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
];
1892
in {
1993
options.nixflix.${serviceName} =
2094
{
2195
enable = mkEnableOption "${capitalizedName}";
96+
package = lib.mkPackageOption pkgs serviceName {};
2297

2398
vpn = {
2499
enable = mkOption {
@@ -44,6 +119,14 @@ in {
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 =
@@ -75,19 +158,7 @@ in {
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";

modules/lidarr/default.nix

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,15 @@ in {
1111

1212
config.nixflix.lidarr = {
1313
group = lib.mkDefault "media";
14-
mediaDirs = lib.mkDefault [
15-
{dir = "${nixflix.mediaDir}/music";}
16-
];
14+
mediaDirs = lib.mkDefault ["${nixflix.mediaDir}/music"];
1715
config = {
1816
apiVersion = lib.mkDefault "v1";
1917
hostConfig = {
2018
port = lib.mkDefault 8686;
2119
branch = lib.mkDefault "master";
2220
};
2321
rootFolders = lib.mkDefault (map (mediaDir: {
24-
path = mediaDir.dir;
22+
path = mediaDir;
2523
defaultQualityProfileId = 2;
2624
defaultMetadataProfileId = 1;
2725
defaultMonitorOption = "all";

modules/nixflix.nix

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -106,36 +106,6 @@ in {
106106
> Is not supported, because `/home/user` is owned by `user`.
107107
'';
108108
};
109-
110-
dirRegistrations = mkOption {
111-
type = with types;
112-
listOf (submodule {
113-
options = {
114-
dir = mkOption {
115-
type = path;
116-
description = "Directory path to create";
117-
};
118-
owner = mkOption {
119-
type = str;
120-
description = "Owner of the directory";
121-
};
122-
group = mkOption {
123-
type = str;
124-
description = "Group of the directory";
125-
};
126-
mode = mkOption {
127-
type = str;
128-
default = "0775";
129-
description = "Permission mode for the directory";
130-
};
131-
};
132-
});
133-
default = [];
134-
description = ''
135-
Directory registrations from services. Each service can register
136-
directories it needs to be created.
137-
'';
138-
};
139109
};
140110

141111
config = mkIf cfg.enable {
@@ -164,13 +134,6 @@ in {
164134
${pkgs.coreutils}/bin/mkdir -p ${cfg.downloadsDir}
165135
${pkgs.coreutils}/bin/chown ${globals.libraryOwner.user}:${globals.libraryOwner.group} ${cfg.downloadsDir}
166136
${pkgs.coreutils}/bin/chmod 0775 ${cfg.downloadsDir}
167-
168-
${concatMapStringsSep "\n" (reg: ''
169-
${pkgs.coreutils}/bin/mkdir -p ${reg.dir}
170-
${pkgs.coreutils}/bin/chown ${reg.owner}:${reg.group} ${reg.dir}
171-
${pkgs.coreutils}/bin/chmod ${reg.mode} ${reg.dir}
172-
'')
173-
cfg.dirRegistrations}
174137
'';
175138
};
176139

modules/postgres.nix

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,6 @@ in {
1919
};
2020

2121
config = mkIf (nixflix.enable && cfg.enable) {
22-
nixflix.dirRegistrations = [
23-
{
24-
dir = stateDir;
25-
owner = "postgres";
26-
group = "postgres";
27-
mode = "0700";
28-
}
29-
];
30-
3122
services.postgresql = {
3223
enable = true;
3324
package = pkgs.postgresql_16;

0 commit comments

Comments
 (0)