sabnzbd: module settings
Co-authored-by: Sandro <sandro.jaeckel@gmail.com>
This commit is contained in:
committed by
glyph
co-authored by
Sandro
parent
938def762c
commit
b0839dac0a
@@ -5,19 +5,108 @@
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
mkPackageOption
|
||||
mkEnableOption
|
||||
mkOptionDefault
|
||||
mkIf
|
||||
literalExpression
|
||||
optionalString
|
||||
types
|
||||
;
|
||||
inherit (lib.generators)
|
||||
mkKeyValueDefault
|
||||
mkValueStringDefault
|
||||
toKeyValue
|
||||
;
|
||||
|
||||
enumFromAttrs =
|
||||
enum_values:
|
||||
types.coercedTo (types.enum (lib.attrNames enum_values)) (name: enum_values.${name}) (
|
||||
types.enum (lib.attrValues enum_values)
|
||||
);
|
||||
|
||||
cfg = config.services.sabnzbd;
|
||||
inherit (pkgs) sabnzbd;
|
||||
|
||||
mandatoryGlobalSettings = {
|
||||
"__version__" = 19;
|
||||
"__encoding__" = "utf-8";
|
||||
};
|
||||
allSettings = cfg.settings // mandatoryGlobalSettings;
|
||||
|
||||
# sabnzbd uses configobj type inis, which support
|
||||
# nested sections specified by increasing numbers
|
||||
# of square brackets (but not toml style dotted paths)
|
||||
configObjAtom = types.oneOf [
|
||||
types.str
|
||||
types.int
|
||||
types.bool
|
||||
];
|
||||
|
||||
configObjValue =
|
||||
let
|
||||
valueType =
|
||||
types.oneOf [
|
||||
types.str
|
||||
types.int
|
||||
types.bool
|
||||
(types.listOf configObjAtom)
|
||||
(types.attrsOf valueType)
|
||||
]
|
||||
// {
|
||||
description = "ConfigObj type";
|
||||
};
|
||||
in
|
||||
valueType;
|
||||
|
||||
configObjIni =
|
||||
{ }:
|
||||
let
|
||||
extractAtoms = lib.filterAttrs (k: v: v != null && !lib.isAttrs v);
|
||||
extractSections = lib.filterAttrs (k: v: lib.isAttrs v);
|
||||
mkValueString = (
|
||||
v:
|
||||
if true == v then
|
||||
"1"
|
||||
else if false == v then
|
||||
"0"
|
||||
else
|
||||
mkValueStringDefault { } v
|
||||
);
|
||||
mkKeyValue = mkKeyValueDefault { inherit mkValueString; } "=";
|
||||
mkSection = (
|
||||
depth: attrs:
|
||||
let
|
||||
atoms = extractAtoms attrs;
|
||||
sections = extractSections attrs;
|
||||
sectionHeadingLeft = lib.concatStrings (lib.replicate (depth + 1) "[");
|
||||
sectionHeadingRight = lib.concatStrings (lib.replicate (depth + 1) "]");
|
||||
mkSectionHeading =
|
||||
name: "${sectionHeadingLeft}${lib.escape [ "[" "]" ] name}${sectionHeadingRight}";
|
||||
mkSubsection = name: attrs: (mkSectionHeading name) + "\n" + (mkSection (depth + 1) attrs) + "\n";
|
||||
in
|
||||
toKeyValue { inherit mkKeyValue; } (extractAtoms attrs)
|
||||
+ "\n"
|
||||
+ lib.concatStrings (lib.mapAttrsToList mkSubsection sections)
|
||||
);
|
||||
in
|
||||
{
|
||||
type = types.attrsOf configObjValue;
|
||||
generate = name: attrs: pkgs.writeText name (mkSection 0 attrs);
|
||||
};
|
||||
|
||||
publicSettingsIni =
|
||||
if cfg.configFile != null then
|
||||
cfg.configFile
|
||||
else
|
||||
(configObjIni { }).generate "public-settings.ini" allSettings;
|
||||
|
||||
sabnzbdIniPath = "/var/lib/${cfg.stateDir}/sabnzbd.ini";
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
services.sabnzbd = {
|
||||
enable = mkEnableOption "the sabnzbd server";
|
||||
@@ -25,9 +114,15 @@ in
|
||||
package = mkPackageOption pkgs "sabnzbd" { };
|
||||
|
||||
configFile = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/sabnzbd/sabnzbd.ini";
|
||||
description = "Path to config file.";
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "Path to config file (deprecated, use `settings` instead)";
|
||||
};
|
||||
|
||||
stateDir = mkOption {
|
||||
type = types.str;
|
||||
default = "sabnzbd";
|
||||
description = "State directory of the service under /var/lib/";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
@@ -49,12 +144,329 @@ in
|
||||
Open ports in the firewall for the sabnzbd web interface
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
description = ''
|
||||
The sabnzbd configuration (see also
|
||||
[sabnzbd's wiki](https://sabnzbd.org/wiki/configuration/4.5/configure)
|
||||
for extra documentation)
|
||||
'';
|
||||
default = { };
|
||||
type = types.submodule {
|
||||
freeformType = (configObjIni { }).type;
|
||||
config = {
|
||||
misc = {
|
||||
config_conversion_version = mkOptionDefault 4;
|
||||
# config_lock = 1 turns the alert that the config is read-only from an error
|
||||
# into a warning. But the warnings still come, and additionally read access
|
||||
# to the config from the web ui is blocked as well, so better keep it at 0
|
||||
# and live with the error
|
||||
# optionally, misc.helpful_warnings = 0 will silence the warnings (but not the error)
|
||||
# at the cost of also silencing other, potentially useful warnings
|
||||
# config_lock = mkOptionDefault (if !cfg.allowConfigWrite then 1 else 0);
|
||||
config_lock = mkOptionDefault false;
|
||||
notified_new_skin = mkOptionDefault true;
|
||||
# don't open the browser on a daemonized service
|
||||
auto_browser = mkOptionDefault false;
|
||||
# don't check for new updates since we're using the distro version
|
||||
check_new_rel = mkOptionDefault false;
|
||||
};
|
||||
};
|
||||
options = {
|
||||
misc = {
|
||||
bandwidth_max = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Maximum bandwidth in bytes(!)/sec (supports prefixes). Use
|
||||
in conjunction with `bandwidth_perc` to set a bandwidth
|
||||
limit. Empty string disables limit.
|
||||
'';
|
||||
default = "";
|
||||
example = "50MB/s";
|
||||
};
|
||||
bandwidth_perc = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
Percentage of `bandwidth_max` that sabnzbd is allowed to use.
|
||||
0 means no limit.
|
||||
'';
|
||||
default = 0;
|
||||
example = 50;
|
||||
};
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Address for the Web UI to listen on for incoming connections.
|
||||
'';
|
||||
default = "127.0.0.1";
|
||||
example = "0.0.0.0";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = ''
|
||||
Port for the Web UI to listen on for incoming connections.
|
||||
'';
|
||||
default = 8080;
|
||||
example = 12345;
|
||||
};
|
||||
https_cert = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
description = ''
|
||||
Path to the TLS certificate for the web UI. If not set
|
||||
and https is enabled, a self-signed certificate will
|
||||
be generated.
|
||||
'';
|
||||
default = null;
|
||||
example = literalExpression "\${config.acme.certs.\${domain}.directory}/fullchain.pem";
|
||||
};
|
||||
https_key = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
description = ''
|
||||
Path to the TLS key for the web UI. If not set and
|
||||
https is enabled, a self-signed certificate will be
|
||||
generated
|
||||
'';
|
||||
default = null;
|
||||
example = literalExpression "\${config.acme.certs.\${domain}.directory}/key.pem";
|
||||
};
|
||||
enable_https = mkOption {
|
||||
type = types.bool;
|
||||
description = "Whether to enable HTTPS for the web UI";
|
||||
default = cfg.settings.misc.https_cert != null;
|
||||
defaultText = "cfg.settings.misc.https_cert != null";
|
||||
example = true;
|
||||
};
|
||||
cache_limit = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Size of the RAM cache, in bytes (prefixes supported).
|
||||
Sabnzbd recommends 25% of available RAM. Empty means
|
||||
no cache.
|
||||
'';
|
||||
default = "";
|
||||
example = "500M";
|
||||
};
|
||||
html_login = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Prompt for login with an html login mask if enabled,
|
||||
otherwise prompt for basic auth (useful for SSO)
|
||||
'';
|
||||
default = true;
|
||||
};
|
||||
inet_exposure = mkOption {
|
||||
type = enumFromAttrs {
|
||||
"none" = 0;
|
||||
"api (add nzbs)" = 1;
|
||||
"api (no config)" = 2;
|
||||
"api (full)" = 3;
|
||||
"api+web (auth needed)" = 4;
|
||||
"api+web (locally no auth)" = 5;
|
||||
};
|
||||
description = ''
|
||||
Restrictions for access from non-local IP addresses.
|
||||
Values are:
|
||||
0, 'none' -- no access
|
||||
1, 'api (add nzbs)' -- api access only, only add nzb files
|
||||
2, 'api (no config)' -- api access only, config changes not allowed
|
||||
3, 'api (full)' -- api access only, full api access
|
||||
4, 'api+web (auth needed)' -- api and web ui, login required always
|
||||
5, 'api+web (locally no auth)' -- api and web ui, login required from non-local IPs only
|
||||
'';
|
||||
default = "none";
|
||||
};
|
||||
email_endjob = mkOption {
|
||||
type = enumFromAttrs {
|
||||
"never" = 0;
|
||||
"always" = 1;
|
||||
"on error" = 2;
|
||||
};
|
||||
description = ''
|
||||
Whether to send emails on job completion. Values are:
|
||||
0, 'never' -- Never
|
||||
1, 'always' -- Always
|
||||
2, 'on error' -- On error
|
||||
'';
|
||||
default = if cfg.settings.misc.email_server != "" then "on error" else "never";
|
||||
defaultText = ''if cfg.settings.misc.email_server != "" then "on error" else "never"'';
|
||||
};
|
||||
email_full = mkOption {
|
||||
type = types.bool;
|
||||
description = "Whether to send alerts for full disks";
|
||||
default = cfg.settings.misc.email_server != "";
|
||||
defaultText = ''cfg.settings.misc.email_server != ""'';
|
||||
};
|
||||
email_rss = mkOption {
|
||||
type = types.bool;
|
||||
description = "Whether to send alerts for jobs added by RSS feeds";
|
||||
default = false;
|
||||
};
|
||||
email_server = mkOption {
|
||||
type = types.str;
|
||||
description = "SMTP server for email alerts (server:host)";
|
||||
default = "";
|
||||
};
|
||||
email_to = mkOption {
|
||||
type = types.str;
|
||||
description = "Receiving address for email alerts";
|
||||
default = "";
|
||||
};
|
||||
email_from = mkOption {
|
||||
type = types.str;
|
||||
description = "'From:' field for emails (needs to be an address)";
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
ntfosd = mkOption {
|
||||
default = { };
|
||||
description = "NotifyOSD settings";
|
||||
type = types.submodule {
|
||||
freeformType = (configObjIni { }).type;
|
||||
options = {
|
||||
ntfosd_enable = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to enable NotifyOSD alerts. Does not really make sense
|
||||
in a server environment, hence we default to false despite
|
||||
upstream's default true.
|
||||
'';
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
servers = mkOption {
|
||||
default = { };
|
||||
description = "Usenet provider specification";
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
freeformType = (configObjIni { }).type;
|
||||
options = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
description = "Enable this server by default";
|
||||
default = true;
|
||||
example = false;
|
||||
};
|
||||
required = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
In case of connection failures, wait for the
|
||||
server to come back online instead of skipping
|
||||
it.
|
||||
'';
|
||||
default = false;
|
||||
example = true;
|
||||
};
|
||||
optional = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
In case of connection failures, temporarily
|
||||
disable this server. (See sabnzbd's documentation
|
||||
for usage guides).
|
||||
'';
|
||||
default = false;
|
||||
example = true;
|
||||
};
|
||||
priority = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
Priority of this servers. Servers are queried in
|
||||
order of priority, from highest (0) to lowest (100).
|
||||
'';
|
||||
default = 0;
|
||||
};
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The name of the server
|
||||
'';
|
||||
example = "Example News Provider";
|
||||
};
|
||||
displayname = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Human-friendly description of the server
|
||||
'';
|
||||
example = "Example News Provider";
|
||||
};
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Hostname of the server
|
||||
'';
|
||||
example = "news.example.com";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "Port of the server";
|
||||
example = 443;
|
||||
default = 563;
|
||||
};
|
||||
connections = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
Number of parallel connections permitted by
|
||||
the server.
|
||||
'';
|
||||
example = 50;
|
||||
default = 8;
|
||||
};
|
||||
timeout = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
Time, in seconds, to wait for a response before
|
||||
attempting error recovery.
|
||||
'';
|
||||
default = 60;
|
||||
};
|
||||
ssl = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether the server supports TLS
|
||||
'';
|
||||
default = true;
|
||||
};
|
||||
ssl_verify = mkOption {
|
||||
type = enumFromAttrs {
|
||||
"strict" = 3;
|
||||
"allow injection" = 2;
|
||||
"none" = 0;
|
||||
};
|
||||
description = ''
|
||||
Level of TLS verification. Supported values:
|
||||
3, 'strict' -- strict (normal) verification
|
||||
2, 'allow injection' -- allow locally injected certificates
|
||||
0, 'none' -- no verification
|
||||
'';
|
||||
default = "strict";
|
||||
};
|
||||
expire_date = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
If Notifications are enabled and an expiry date is
|
||||
set, warn 5 days before expiry. This setting
|
||||
does not automatically disable the server.
|
||||
Expected format: yyyy-mm-dd
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
warnings = lib.optional (cfg.configFile != null) ''
|
||||
`sabnzbd.configFile` is deprecated, consider using `sabnzbd.settings` instead.
|
||||
If you have values set in `sabnzbd.settings` set, they will be ignored.
|
||||
'';
|
||||
|
||||
users.users = mkIf (cfg.user == "sabnzbd") {
|
||||
sabnzbd = {
|
||||
isSystemUser = true;
|
||||
@@ -67,22 +479,26 @@ in
|
||||
sabnzbd = { };
|
||||
};
|
||||
|
||||
systemd.services.sabnzbd = {
|
||||
description = "sabnzbd server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
GuessMainPID = "no";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
StateDirectory = "sabnzbd";
|
||||
ExecStart = "${lib.getBin cfg.package}/bin/sabnzbd -d -f ${cfg.configFile}";
|
||||
systemd.services.sabnzbd =
|
||||
let
|
||||
publicIniQuoted = lib.escapeShellArg publicSettingsIni;
|
||||
in
|
||||
{
|
||||
description = "sabnzbd server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
GuessMainPID = "no";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
StateDirectory = cfg.stateDir;
|
||||
ExecStart = "${lib.getExe cfg.package} -d -f ${iniPathQuoted}";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [ 8080 ];
|
||||
allowedTCPPorts = [ cfg.settings.misc.port ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user