diff --git a/lib/default.nix b/lib/default.nix index 0f67cb0..732733c 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,55 +1,51 @@ -{ nixpkgs, ... }: -{ - toXMLGeneric = - let - toXMLRecursive = - toXmlRecursive' - "\n" - 0; +{nixpkgs, ...}: { + toXMLGeneric = let + toXMLRecursive = + toXmlRecursive' + "\n" + 0; - indent = depth: ( - if (depth <= 0) - then "" - else (" " + (indent (depth - 1))) - ); + indent = depth: ( + if (depth <= 0) + then "" + else (" " + (indent (depth - 1))) + ); - toXmlRecursive' = str: depth: xml: - let - parseTag = str: depth: xml: (builtins.concatStringsSep "" [ - str - "${indent depth}<${xml.name}${ - if (builtins.hasAttr "content" xml) - then ">" - else " " - }" + toXmlRecursive' = str: depth: xml: let + parseTag = str: depth: xml: (builtins.concatStringsSep "" [ + str + "${indent depth}<${xml.tag}${ + if (builtins.hasAttr "content" xml) && ((builtins.isString xml.content) || (builtins.isList xml.content) && ((builtins.length xml.content) > 0)) + then ">" + else " " + }" - ( - if (builtins.hasAttr "properties" xml) - then - (" " - + builtins.concatStringsSep " " (nixpkgs.lib.attrsets.mapAttrsToList - (name: value: "${name}=\"${nixpkgs.lib.strings.escapeXML value}\"") - xml.properties)) - else "" - ) + ( + if (builtins.hasAttr "attrib" xml) + then + (" " + + builtins.concatStringsSep " " (nixpkgs.lib.attrsets.mapAttrsToList + (name: value: "${name}=\"${nixpkgs.lib.strings.escapeXML value}\"") + xml.attrib)) + else "" + ) - ( - if builtins.hasAttr "content" xml - then ((toXmlRecursive' "\n" (depth + 1) xml.content) + "") - else "/>" - ) - ]); - in - if (builtins.isAttrs xml) - then "${parseTag str depth xml}\n${indent (depth - 1)}" - else if (builtins.isList xml) - then "\n${(builtins.concatStringsSep "" (builtins.map (x: (toXmlRecursive' "" depth x)) xml))}${indent (depth - 1)}" - else if ((builtins.isBool xml) || (builtins.isInt xml) || (builtins.isNull xml) || (builtins.isFloat xml)) - then (builtins.toString xml) - else if (builtins.isString xml) - then xml - else throw "Cannot convert a ${builtins.typeOf xml} to XML"; + ( + if (builtins.hasAttr "content" xml) && ((builtins.isString xml.content) || (builtins.isList xml.content) && ((builtins.length xml.content) > 0)) + then ((toXmlRecursive' "\n" (depth + 1) xml.content) + "") + else "/>" + ) + ]); in + if (builtins.isAttrs xml) + then "${parseTag str depth xml}\n${indent (depth - 1)}" + else if (builtins.isList xml) + then "\n${(builtins.concatStringsSep "" (builtins.map (x: (toXmlRecursive' "" depth x)) xml))}${indent (depth - 1)}" + else if ((builtins.isBool xml) || (builtins.isInt xml) || (builtins.isNull xml) || (builtins.isFloat xml)) + then (builtins.toString xml) + else if (builtins.isString xml) + then xml + else throw "Cannot convert a ${builtins.typeOf xml} to XML. ${toString (builtins.trace xml xml)}"; + in toXMLRecursive; } - diff --git a/modules/options/default.nix b/modules/options/default.nix index 86ef233..d0a43d0 100644 --- a/modules/options/default.nix +++ b/modules/options/default.nix @@ -1,12 +1,11 @@ { config, - nixpkgs, pkgs, lib, ... }: let cfg = config.services.declarative-jellyfin; - toXml = (import ../../lib {inherit nixpkgs;}).toXMLGeneric; + toXml' = (import ../../lib {nixpkgs = pkgs;}).toXMLGeneric; in with lib; { imports = [ @@ -23,24 +22,29 @@ in mkIf cfg.enable ( let - listOfStrPrepass = xml: (builtins.mapAttrs (name: value: - ( - if ((name == "content") && (builtins.isList value)) + prepass = x: + if (builtins.isAttrs x) + then + if !(builtins.hasAttr "tag" x) then - if (builtins.all builtins.isString value) - then # listOf str - (builtins.map (content: { - name = "string"; - inherit content; - })) - else # Lis of something else - warnIf (!(builtins.all builtins.isAttrs value)) "Recieved list of mixed values. This will most likely not evaluate correctly" (listOfStrPrepass value) - else (listOfStrPrepass value) - ) - xml)); - toXml = name: x: (toXml { - inherit name; - properties = { + attrsets.mapAttrsToList (tag: value: { + inherit tag; + content = value; + }) + x + else if (builtins.hasAttr "content" x) + then { + tag = x.tag; + content = prepass x.content; + } + else x + else if (builtins.isList x) + then builtins.map prepass x + else throw "wtf"; + + toXml = tag: x: (toXml' { + inherit tag; + attrib = { "xmlns:xsi" = "http://www.w3.org/2001/XMLSchema-instance"; "xmlns:xsd" = "http://www.w3.org/2001/XMLSchema"; }; @@ -49,9 +53,12 @@ in in { system.activationScripts."link-network-xml" = lib.stringAfter ["var"] ( let - content = toXml "NetworkConfiguration" (listOfStrPrepass cfg.network); + content = toXml "NetworkConfiguration" (prepass cfg.network); in '' - ${pkgs.writeTextFile}/bin/writeTextFile /var/lib/jellyfin/config/network.xml '${strings.escape ["'"] content}' + mkdir -p /var/lib/jellyfin/config + if [ ! -f "/var/lib/jellyfin/config/network.xml" ]; then + echo '${strings.escape ["'"] content}' > /var/lib/jellyfin/config/network.xml + fi '' ); } diff --git a/modules/options/network.nix b/modules/options/network.nix index f0c601d..b7b6c03 100644 --- a/modules/options/network.nix +++ b/modules/options/network.nix @@ -3,32 +3,40 @@ with lib; { options.services.declarative-jellyfin.network = { BaseUrl = mkOption { type = types.str; + default = ""; + description = "Add a custom subdirectory to the server URL. For example: http://example.com/"; }; EnableHttps = mkEnableOption "Enable HTTPS"; RequireHttps = mkEnableOption "Require HTTPS"; CertificatePath = mkOption { type = with types; either str path; - description = "Path to the certificate file"; + default = ""; + description = "Path to a PKCS #12 file containing a certificate and private key to enable TLS support on a custom domain."; }; CertificatePassword = mkOption { type = types.str; - description = "Password for the certificate"; + default = ""; + description = "If your certificate requires a password, please enter it here."; }; InternalHttpPort = mkOption { type = types.port; - description = "The internal HTTP port jellyfin is run at"; + default = 8096; + description = "The TCP port number for the HTTP server."; }; InternalHttpsPort = mkOption { type = types.port; - description = "The internal HTTPS port jellyfin is run at"; + default = 8920; + description = "The TCP port number for the HTTPS server."; }; PublicHttpPort = mkOption { type = types.port; - description = "The public HTTP port jellyfin is run at"; + default = 8096; + description = "The public port number that should be mapped to the local HTTP port."; }; PublicHttpsPort = mkOption { type = types.port; - description = "The public HTTPS port jellyfin is run at"; + default = 8920; + description = "The public port number that should be mapped to the local HTTPS port."; }; AutoDiscovery = mkOption { type = types.bool; @@ -51,7 +59,15 @@ with lib; { default = true; description = "Enable remote access"; }; - LocalNetworkSubnets = mkEnableOption "UNIMPLEMENTED"; + LocalNetworkSubnets = mkOption { + type = with types; listOf str; + default = []; + description = '' + List of IP addresses or IP/netmask entries for networks that will be considered on local network when enforcing bandwidth restrictions. + If set, all other IP addresses will be considered to be on the external network and will be subject to the external bandwidth restrictions. + If left empty, only the server's subnet is considered to be on the local network. + ''; + }; LocalNetworkAddresses = mkEnableOption "UNIMPLEMENTED"; KnownProxies = mkOption { type = with types; listOf str; @@ -79,8 +95,12 @@ with lib; { default = []; }; RemoteIpFilter = mkOption { - type = types.str; - description = "Remote ip filter"; + type = with types; listOf str; + default = []; + description = '' + List of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. + If left empty, all remote addresses will be allowed. + ''; }; IsRemoteIPFilterBlacklist = mkEnableOption "Is the remote ip filter list a blacklist or a whitelist"; }; diff --git a/tests/networking.nix b/tests/networking.nix new file mode 100644 index 0000000..74a3411 --- /dev/null +++ b/tests/networking.nix @@ -0,0 +1,51 @@ +{pkgs ? import {}, ...}: let + name = "networking"; +in { + inherit name; + test = pkgs.nixosTest { + inherit name; + nodes = { + machine = { + config, + pkgs, + ... + }: { + imports = [ + ../modules/default.nix + ]; + + virtualisation.memorySize = 1024; + + services.declarative-jellyfin = { + enable = true; + network = { + PublishedServerUriBySubnet = [ + "all=https://test.test.test" + ]; + }; + }; + }; + }; + + # stfu i dont care about python linting + skipLint = true; + + testScript = '' + import xml.etree.ElementTree as ET + + machine.start() + machine.wait_for_unit("multi-user.target"); + + with subtest("Jellyfin URI"): + machine.succeed("ls /var/lib/jellyfin") + tree = ET.parse("/var/lib/jellyfin/config/network.xml") + root = tree.getroot() + found = False + for child in root: + if child.tag == "PublishedServerUriBySubnet": + found = True + print(child) + assert False + ''; + }; +}