diff --git a/modules/nixos/services/media/default.nix b/modules/nixos/services/media/default.nix index c880580..d257aea 100644 --- a/modules/nixos/services/media/default.nix +++ b/modules/nixos/services/media/default.nix @@ -3,16 +3,12 @@ lib, namespace, config, - inputs, - system, ... }: let inherit (lib) mkIf mkEnableOption mkOption; inherit (lib.types) str; cfg = config.${namespace}.services.media; - - arr = ["radarr"]; in { options.${namespace}.services.media = { enable = mkEnableOption "Enable media services"; @@ -60,285 +56,75 @@ in { }; systemd.tmpfiles.rules = [ - "d '${cfg.path}/series' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/movies' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/music' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/qbittorrent' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/sabnzbd' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -" - "d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -" - "d /var/lib/radarrApplyTerraform 0755 ${cfg.user} ${cfg.group} -" + # "d '${cfg.path}/series' 0770 ${cfg.user} ${cfg.group} - -" + # "d '${cfg.path}/movies' 0770 ${cfg.user} ${cfg.group} - -" + # "d '${cfg.path}/music' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/qbittorrent' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/sabnzbd' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/downloads/incomplete' 0770 ${cfg.user} ${cfg.group} - -" + "d '${cfg.path}/downloads/done' 0770 ${cfg.user} ${cfg.group} - -" ]; #========================================================================= # Services #========================================================================= - services = let - arr-services = - arr - |> lib.imap (i: service: { - name = service; - value = - { - enable = true; - openFirewall = true; - - environmentFiles = [ - config.sops.templates."${service}/config.env".path - ]; - - settings = { - auth.authenticationMethod = "External"; - - server = { - bindaddress = "0.0.0.0"; - port = 2000 + i; - }; - - postgres = { - host = "localhost"; - port = "5432"; - user = service; - maindb = service; - logdb = service; - }; - }; - } - // ( - if service != "prowlarr" - then { - user = cfg.user; - group = cfg.group; - } - else {} - ); - }) - |> lib.listToAttrs; - in - arr-services - // { - bazarr = { - enable = true; - openFirewall = true; - user = cfg.user; - group = cfg.group; - listenPort = 2005; - }; - - # port is harcoded in nixpkgs module - jellyfin = { - enable = true; - openFirewall = true; - user = cfg.user; - group = cfg.group; - }; - - flaresolverr = { - enable = true; - openFirewall = true; - port = 2007; - }; - - qbittorrent = { - enable = true; - openFirewall = true; - webuiPort = 2008; - - serverConfig = { - LegalNotice.Accepted = true; - - Prefecences.WebUI = { - Username = "admin"; - }; - }; - - user = cfg.user; - group = cfg.group; - }; - - # port is harcoded in nixpkgs module - sabnzbd = { - enable = true; - openFirewall = true; - configFile = "${cfg.path}/sabnzbd/config.ini"; - - user = cfg.user; - group = cfg.group; - }; - - postgresql = let - databases = arr |> lib.concatMap (s: [s "${s}-log"]); - in { - enable = true; - ensureDatabases = arr; - ensureUsers = - arr - |> lib.map (service: { - name = service; - ensureDBOwnership = true; - }); - }; - - caddy = { - enable = true; - virtualHosts = { - "jellyfin.kruining.eu".extraConfig = '' - reverse_proxy http://[::1]:8096 - ''; - }; - }; + services = { + bazarr = { + enable = true; + openFirewall = true; + user = cfg.user; + group = cfg.group; + listenPort = 2005; }; - systemd.services.radarrApplyTerraform = let - # this is a nix package, the generated json file to be exact - terraformConfiguration = inputs.terranix.lib.terranixConfiguration { - inherit system; - - modules = [ - ({ - config, - lib, - ... - }: { - config = { - variable = { - api_key = { - type = "string"; - description = "Radarr api key"; - }; - }; - - terraform.required_providers.radarr = { - source = "devopsarr/radarr"; - version = "2.2.0"; - }; - - provider.radarr = { - url = "http://127.0.0.1:2001"; - api_key = lib.tfRef "var.api_key"; - }; - - resource = { - radarr_root_folder.local = { - path = "/var/media/movies"; - }; - }; - }; - }) - ]; + flaresolverr = { + enable = true; + openFirewall = true; + port = 2007; }; - in { - description = "Radarr terraform apply"; - wantedBy = ["multi-user.target"]; - wants = ["radarr.service"]; + # port is harcoded in nixpkgs module + jellyfin = { + enable = true; + openFirewall = true; + user = cfg.user; + group = cfg.group; + }; - script = '' - #!/usr/bin/env bash + postgresql = { + enable = true; + }; - if [ "$(systemctl is-active radarr)" != "active" ]; then - echo "Radarr is not running" - exit 1 - fi - - # Sleep for a bit to give radarr the chance to start up - sleep 5s - - # Print the path to the source for easier debugging - echo "config location: ${terraformConfiguration}" - - # Copy infra code into workspace - cp -f ${terraformConfiguration} config.tf.json - - # Initialize OpenTofu - ${lib.getExe pkgs.opentofu} init - - # Run the infrastructure code - # ${lib.getExe pkgs.opentofu} plan -var-file='${config.sops.templates."radarr/config.tfvars".path}' - ${lib.getExe pkgs.opentofu} apply -auto-approve -var-file='${config.sops.templates."radarr/config.tfvars".path}' - ''; - - serviceConfig = { - Type = "oneshot"; - User = cfg.user; - Group = cfg.group; - - WorkingDirectory = "/var/lib/radarrApplyTerraform"; - - EnvironmentFile = [ - config.sops.templates."radarr/config.env".path - ]; + caddy = { + enable = true; + virtualHosts = { + "jellyfin.kruining.eu".extraConfig = '' + reverse_proxy http://[::1]:8096 + ''; + }; }; }; systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL"; sops = { - secrets = let - arrSecrets = - arr - |> lib.map (service: { - name = "${service}/apikey"; - value = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["${service}.service"]; - }; - }) - |> lib.listToAttrs; - in - arrSecrets - // { - # "qbittorrent/password" = {}; - "qbittorrent/password_hash" = {}; + secrets = { + # "qbittorrent/password" = {}; + "qbittorrent/password_hash" = {}; + }; + + templates = { + "qbittorrent/password.conf" = { + owner = cfg.user; + group = cfg.group; + restartUnits = ["qbittorrent.service"]; + path = "${config.services.qbittorrent.profileDir}/qBittorrent/config/password.conf"; + content = '' + [Preferences] + WebUI\Password_PBKDF2="${config.sops.placeholder."qbittorrent/password_hash"}" + ''; }; - - templates = let - apikeys = - arr - |> lib.map (service: { - name = "${service}/config.env"; - value = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["${service}.service"]; - content = '' - ${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}" - ''; - }; - }) - |> lib.listToAttrs; - - tfvars = - arr - |> lib.map (service: { - name = "${service}/config.tfvars"; - value = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["${service}ApplyTerraform.service"]; - content = '' - api_key = "${config.sops.placeholder."${service}/apikey"}" - ''; - }; - }) - |> lib.listToAttrs; - - qbittorrent = { - "qbittorrent/password.conf" = { - owner = cfg.user; - group = cfg.group; - restartUnits = ["qbittorrent.service"]; - path = "${config.services.qbittorrent.profileDir}/qBittorrent/config/password.conf"; - content = '' - [Preferences] - WebUI\Password_PBKDF2="${config.sops.placeholder."qbittorrent/password_hash"}" - ''; - }; - }; - in - apikeys // tfvars // qbittorrent; + }; }; }; } diff --git a/modules/nixos/services/media/glance/default.nix b/modules/nixos/services/media/glance/default.nix new file mode 100644 index 0000000..333035d --- /dev/null +++ b/modules/nixos/services/media/glance/default.nix @@ -0,0 +1,183 @@ +{ + config, + lib, + namespace, + ... +}: let + inherit (lib) mkIf mkEnableOption; + + cfg = config.${namespace}.services.media.glance; +in { + options.${namespace}.services.media.glance = { + enable = mkEnableOption "Enable Glance"; + }; + + config = mkIf cfg.enable { + services.glance = { + enable = true; + openFirewall = true; + + environmentFile = config.sops.templates."glance/secrets.env".path; + + settings = { + server = { + host = "0.0.0.0"; + port = 2000; + }; + + theme = { + # Teal city predefined theme (https://github.com/glanceapp/glance/blob/main/docs/themes.md#teal-city) + background-color = "225 14 15"; + primary-color = "157 47 65"; + contrast-multiplier = 1.1; + }; + + pages = [ + { + name = "Home"; + columns = [ + { + size = "small"; + widgets = [ + { + type = "calendar"; + first-day-of-the-week = "monday"; + } + ]; + } + + { + size = "full"; + widgets = [ + { + type = "monitor"; + cache = "1m"; + title = "Services"; + sites = [ + { + title = "Zitadel"; + url = "https://auth.kruining.eu"; + icon = "sh:zitadel"; + } + { + title = "Forgejo"; + url = "https://git.amarth.cloud/chris"; + icon = "sh:forgejo"; + } + { + title = "Vaultwarden"; + url = "https://vault.kruining.eu"; + icon = "sh:vaultwarden"; + } + ]; + } + { + type = "monitor"; + cache = "1m"; + title = "Observability"; + sites = [ + { + title = "Grafana"; + url = "http://${config.networking.hostName}:${builtins.toString config.services.grafana.settings.server.http_port}"; + icon = "sh:grafana"; + } + { + title = "Prometheus"; + url = "http://${config.networking.hostName}:${builtins.toString config.services.prometheus.port}"; + icon = "sh:prometheus"; + } + ]; + } + { + type = "monitor"; + cache = "1m"; + title = "Media"; + sites = [ + { + title = "Jellyfin"; + url = "http://${config.networking.hostName}:8096"; + icon = "sh:jellyfin"; + } + { + title = "Radarr"; + url = "http://${config.networking.hostName}:2001"; + icon = "sh:radarr"; + } + { + title = "Sonarr"; + url = "http://${config.networking.hostName}:2002"; + icon = "sh:sonarr"; + } + { + title = "Lidarr"; + url = "http://${config.networking.hostName}:2003"; + icon = "sh:lidarr"; + } + { + title = "Prowlarr"; + url = "http://${config.networking.hostName}:2004"; + icon = "sh:prowlarr"; + } + { + title = "qBittorrent"; + url = "http://${config.networking.hostName}:${builtins.toString config.services.qbittorrent.webuiPort}"; + icon = "sh:qbittorrent"; + } + { + title = "SABnzbd"; + url = "http://${config.networking.hostName}:8080"; + icon = "sh:sabnzbd"; + } + ]; + } + { + type = "videos"; + channels = [ + "UCXuqSBlHAE6Xw-yeJA0Tunw" # Linus Tech Tips + "UCR-DXc1voovS8nhAvccRZhg" # Jeff Geerling + "UCsBjURrPoezykLs9EqgamOA" # Fireship + "UCBJycsmduvYEL83R_U4JriQ" # Marques Brownlee + "UCHnyfMqiRRG1u-2MsSQLbXA" # Veritasium + ]; + } + ]; + } + + { + size = "small"; + widgets = [ + { + type = "weather"; + location = "Amsterdam, The Netherlands"; + units = "metric"; + hour-format = "24h"; + } + + { + type = "server-stats"; + servers = [ + { + type = "local"; + name = "Ulmo"; + } + ]; + } + ]; + } + ]; + } + ]; + }; + }; + + sops.templates."glance/secrets.env" = { + # owner = config.services.glance.user; + # group = config.services.glance.group; + content = '' + RADARR_KEY="${config.sops.placeholder."radarr/apikey"}" + SONARR_KEY="${config.sops.placeholder."sonarr/apikey"}" + LIDARR_KEY="${config.sops.placeholder."lidarr/apikey"}" + ''; + }; + }; +} diff --git a/modules/nixos/services/media/homer/default.nix b/modules/nixos/services/media/homer/default.nix deleted file mode 100644 index 79633ab..0000000 --- a/modules/nixos/services/media/homer/default.nix +++ /dev/null @@ -1,161 +0,0 @@ -{ config, lib, namespace, ... }: -let - inherit (lib) mkIf mkEnableOption; - - cfg = config.${namespace}.services.media.homer; -in -{ - options.${namespace}.services.media.homer = { - enable = mkEnableOption "Enable homer"; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = [ 2000 ]; - - services = { - homer = { - enable = true; - - virtualHost = { - caddy.enable = true; - domain = "http://:2000"; - }; - - settings = { - title = "Ulmo dashboard"; - - columns = 4; - connectivityCheck = true; - - links = []; - - services = [ - { - name = "Services"; - items = [ - { - name = "Zitadel"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/zitadel.svg"; - tag = "app"; - url = "https://auth.kruining.eu"; - target = "_blank"; - } - - { - name = "Forgejo"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/forgejo.svg"; - tag = "app"; - type = "Gitea"; - url = "https://git.amarth.cloud"; - target = "_blank"; - } - - { - name = "Vaultwarden"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/vaultwarden.svg"; - type = "Vaultwarden"; - tag = "app"; - url = "https://vault.kruining.eu"; - target = "_blank"; - } - ]; - } - - { - name = "Observability"; - items = [ - { - name = "Grafana"; - type = "Grafana"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/grafana.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:${builtins.toString config.services.grafana.settings.server.http_port}"; - target = "_blank"; - } - - { - name = "Prometheus"; - type = "Prometheus"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prometheus.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:${builtins.toString config.services.prometheus.port}"; - target = "_blank"; - } - ]; - } - - { - name = "Media"; - items = [ - { - name = "Jellyfin (Movies)"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/jellyfin.svg"; - tag = "app"; - type = "Emby"; - url = "http://${config.networking.hostName}:8096"; - apikey = "e3ceed943eeb409ba8342738db7cc1f5"; - libraryType = "movies"; - target = "_blank"; - } - - { - name = "Radarr"; - type = "Radarr"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/radarr.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:2001"; - target = "_blank"; - } - - { - name = "Sonarr"; - type = "Sonarr"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sonarr.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:2002"; - target = "_blank"; - } - - { - name = "Lidarr"; - type = "Lidarr"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/lidarr.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:2003"; - target = "_blank"; - } - - { - name = "Prowlarr"; - type = "Prowlarr"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prowlarr.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:2004"; - target = "_blank"; - } - - { - name = "qBittorrent"; - type = "qBittorrent"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/qbittorrent.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:${builtins.toString config.services.qbittorrent.webuiPort}"; - target = "_blank"; - } - - { - name = "SABnzbd"; - type = "SABnzbd"; - logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sabnzdb-light.svg"; - tag = "app"; - url = "http://${config.networking.hostName}:8080"; - target = "_blank"; - } - ]; - } - ]; - }; - }; - }; - }; -} diff --git a/modules/nixos/services/media/servarr/default.nix b/modules/nixos/services/media/servarr/default.nix index 733fe99..373e09b 100644 --- a/modules/nixos/services/media/servarr/default.nix +++ b/modules/nixos/services/media/servarr/default.nix @@ -41,36 +41,68 @@ in { port, ... }: (mkIf enable { - "${service}" = { - enable = true; - openFirewall = true; + "${service}" = + { + enable = true; + openFirewall = true; - environmentFiles = [ - config.sops.templates."${service}/config.env".path - ]; + environmentFiles = [ + config.sops.templates."${service}/config.env".path + ]; - settings = { - auth.authenticationMethod = "External"; + settings = { + auth.authenticationMethod = "External"; - server = { - bindaddress = "0.0.0.0"; - port = port; + server = { + bindaddress = "0.0.0.0"; + port = port; + }; + + postgres = { + host = "localhost"; + port = "5432"; + user = service; + maindb = service; + logdb = service; + }; }; - - postgres = { - host = "localhost"; - port = "5432"; - user = service; - maindb = service; - logdb = service; - }; - }; - }; + } + // (lib.optionalAttrs (service != "prowlarr") { + user = service; + group = "media"; + }); })) - |> lib.mergeAttrsList + |> lib.mkMerge |> (set: set // { + qbittorrent = { + enable = true; + openFirewall = true; + webuiPort = 2008; + + serverConfig = { + LegalNotice.Accepted = true; + + Prefecences.WebUI = { + Username = "admin"; + }; + }; + + user = "qbittorrent"; + group = "media"; + }; + + # port is harcoded in nixpkgs module + sabnzbd = { + enable = true; + openFirewall = true; + configFile = "${cfg.path}/sabnzbd/config.ini"; + + user = "sabnzbd"; + group = "media"; + }; + postgresql = { ensureDatabases = cfg |> lib.attrNames; ensureUsers = @@ -83,7 +115,7 @@ in { }; }); - systemd = + systemd.services = cfg |> lib.mapAttrsToList (service: { enable, @@ -92,11 +124,7 @@ in { rootFolders, ... }: (mkIf enable { - tmpfiles.rules = [ - "d /var/lib/${service}ApplyTerraform 0755 ${service} ${service} -" - ]; - - services."${service}ApplyTerraform" = let + "${service}ApplyTerraform" = let terraformConfiguration = inputs.terranix.lib.terranixConfiguration { inherit system; @@ -116,7 +144,17 @@ in { terraform.required_providers.${service} = { source = "devopsarr/${service}"; - version = "2.2.0"; + version = + { + radarr = "2.3.3"; + sonarr = "3.4.0"; + prowlarr = "3.1.0"; + lidarr = "1.13.0"; + readarr = "2.1.0"; + whisparr = "1.2.0"; + }.${ + service + }; }; provider.${service} = { @@ -125,10 +163,11 @@ in { }; resource = { - "${service}_root_folder" = + "${service}_root_folder" = mkIf (lib.elem service ["radarr" "sonarr" "whisparr"]) ( rootFolders |> lib.imap (i: f: lib.nameValuePair "local${toString i}" {path = f;}) - |> lib.listToAttrs; + |> lib.listToAttrs + ); }; }; }) @@ -140,9 +179,16 @@ in { wantedBy = ["multi-user.target"]; wants = ["${service}.service"]; - script = '' - #!/usr/bin/env bash + preStart = '' + install -d -m 0770 -o ${service} -g media /var/lib/${service}ApplyTerraform + ${ + rootFolders + |> lib.map (folder: "install -d -m 0770 -o media -g media ${folder}") + |> lib.join "\n" + } + ''; + script = '' # Sleep for a bit to give the service a chance to start up sleep 5s @@ -158,7 +204,7 @@ in { cp -f ${terraformConfiguration} config.tf.json # Initialize OpenTofu - ${lib.getExe pkgs.opentofu} init + ${lib.getExe pkgs.opentofu} init -upgrade # Run the infrastructure code ${lib.getExe pkgs.opentofu} \ @@ -173,7 +219,7 @@ in { serviceConfig = { Type = "oneshot"; User = service; - Group = service; + Group = "media"; WorkingDirectory = "/var/lib/${service}ApplyTerraform"; @@ -183,28 +229,33 @@ in { }; }; })) - |> lib.mergeAttrsList; + |> lib.mkMerge; - users.users = + users = cfg |> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable { - "${service}".extraGroups = ["media"]; + users.${service} = { + isSystemUser = true; + group = lib.mkDefault service; + extraGroups = ["media"]; + }; + groups.${service} = {}; })) - |> lib.mergeAttrsList; + |> lib.mkMerge; sops = cfg |> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable { secrets."${service}/apikey" = { owner = service; - group = service; + group = "media"; restartUnits = ["${service}.service"]; }; templates = { "${service}/config.env" = { owner = service; - group = service; + group = "media"; restartUnits = ["${service}.service"]; content = '' ${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}" @@ -213,7 +264,7 @@ in { "${service}/config.tfvars" = { owner = service; - group = service; + group = "media"; restartUnits = ["${service}.service"]; content = '' api_key = "${config.sops.placeholder."${service}/apikey"}" @@ -221,15 +272,6 @@ in { }; }; })) - |> lib.mergeAttrsList; + |> lib.mkMerge; }; - - # cfg - # |> lib.mapAttrsToList (service: { enable, debug, port, rootFolders, ... }: (mkIf enable { - - # # sops = { - # # }; - # })) - # |> lib.mergeAttrsList - # ; } diff --git a/systems/x86_64-linux/ulmo/default.nix b/systems/x86_64-linux/ulmo/default.nix index 93171d8..9d12de8 100644 --- a/systems/x86_64-linux/ulmo/default.nix +++ b/systems/x86_64-linux/ulmo/default.nix @@ -161,13 +161,17 @@ networking.ssh.enable = true; media.enable = true; - media.homer.enable = true; + media.glance.enable = true; media.mydia.enable = true; media.nfs.enable = true; media.servarr = { - # radarr = { - # port = 2001; - # }; + radarr = { + enable = true; + port = 2001; + rootFolders = [ + "/var/media/movies" + ]; + }; sonarr = { enable = true;