diff options
| author | spl3g <notspl3g@duck.com> | 2026-03-18 18:01:41 +0300 |
|---|---|---|
| committer | spl3g <notspl3g@duck.com> | 2026-03-18 18:01:59 +0300 |
| commit | 03648b3d9f177227df40129bed22558f6924b91c (patch) | |
| tree | 8a22eda142beeafd9002a8d5901ba9428a77ad52 /modules/nixosModules | |
| parent | dc19a2b583b3ab50d8e36ff0a90ca633495f675f (diff) | |
so.. v2 i guess
Diffstat (limited to 'modules/nixosModules')
| -rw-r--r-- | modules/nixosModules/booklore.nix | 176 | ||||
| -rw-r--r-- | modules/nixosModules/directories.nix | 90 | ||||
| -rw-r--r-- | modules/nixosModules/gonic.nix | 114 | ||||
| -rw-r--r-- | modules/nixosModules/nfs.nix | 118 | ||||
| -rw-r--r-- | modules/nixosModules/nginxProxy.nix | 217 | ||||
| -rw-r--r-- | modules/nixosModules/watcharr.nix | 74 |
6 files changed, 789 insertions, 0 deletions
diff --git a/modules/nixosModules/booklore.nix b/modules/nixosModules/booklore.nix new file mode 100644 index 0000000..3eeb3b9 --- /dev/null +++ b/modules/nixosModules/booklore.nix @@ -0,0 +1,176 @@ +{ + inputs, + self, + ... +}: { + flake.nixosModules.booklore = { + config, + lib, + pkgs, + ... + }: + with lib; let + cfg = config.services.booklore; + in { + options = { + services.booklore = { + enable = mkEnableOption "Enable booklore service"; + subdomain = mkOption { + type = types.str; + description = '' + Subdomain to use for nginx. + ''; + }; + uid = mkOption { + type = types.str; + description = '' + UID for the container user. + ''; + }; + gid = mkOption { + type = types.str; + description = '' + GID for the container user. + ''; + }; + settings = { + timezone = mkOption { + type = types.str; + description = '' + Timezone string; + ''; + }; + dataDir = mkOption { + type = types.path; + description = '' + Booklore data directory. + ''; + default = "/var/lib/booklore"; + }; + bookdropDir = mkOption { + type = types.path; + description = '' + Directory where booklore will injest books. It is not created automatically, you should create it with the sufficient permissions for the uid and gid you provided. + ''; + }; + booksDir = mkOption { + type = types.path; + description = '' + Directory where booklore will store books. It is not created automatically, you should create it with the sufficient permissions for the uid and gid you provided. + ''; + }; + }; + database = { + name = mkOption { + type = types.str; + default = "booklore"; + }; + user = mkOption { + type = types.str; + default = "files"; + }; + password = mkOption { + type = types.str; + default = "booklore"; + }; + }; + }; + }; + + config = mkIf cfg.enable { + createPaths = { + "${cfg.settings.dataDir}" = { + owner = cfg.uid; + group = cfg.gid; + permissions = "0750"; + }; + }; + nginxProxy = { + enable = true; + subdomains = { + "${cfg.subdomain}" = { + proxyPass = "http://127.0.0.1:6060"; + proxyWebsockets = true; + }; + }; + }; + + virtualisation.oci-containers.containers.booklore = { + image = "booklore/booklore:latest"; + environment = { + USER_ID = cfg.uid; + GROUP_ID = cfg.gid; + TZ = cfg.settings.timezone; + + DATABASE_URL = "jdbc:mariadb://mariadb-booklore:3306/${cfg.database.name}"; + DATABASE_USERNAME = cfg.database.user; + DATABASE_PASSWORD = cfg.database.password; + }; + ports = [ + "127.0.0.1:6060:6060" + ]; + volumes = [ + "${cfg.settings.dataDir}:/app/data" + "${cfg.settings.booksDir}:/books" + "${cfg.settings.bookdropDir}:/bookdrop" + ]; + dependsOn = [ + "mariadb-booklore" + ]; + networks = [ + "booklore_default" + ]; + }; + systemd.services."podman-booklore" = { + serviceConfig = { + Restart = lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-booklore_default.service" + ]; + requires = [ + "podman-network-booklore_default.service" + ]; + }; + + virtualisation.oci-containers.containers.mariadb-booklore = { + image = "lscr.io/linuxserver/mariadb:11.4.5"; + environment = { + PUID = "1000"; + PGID = "1000"; + TZ = cfg.settings.timezone; + MYSQL_DATABASE = cfg.database.name; + MYSQL_USER = cfg.database.user; + MYSQL_PASSWORD = cfg.database.password; + }; + networks = [ + "booklore_default" + ]; + }; + + systemd.services."podman-mariadb-booklore" = { + serviceConfig = { + Restart = lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-booklore_default.service" + ]; + requires = [ + "podman-network-booklore_default.service" + ]; + }; + + systemd.services."podman-network-booklore_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f booklore_default"; + }; + script = '' + podman network inspect booklore_default || podman network create booklore_default + ''; + }; + }; + }; +} diff --git a/modules/nixosModules/directories.nix b/modules/nixosModules/directories.nix new file mode 100644 index 0000000..6a1426f --- /dev/null +++ b/modules/nixosModules/directories.nix @@ -0,0 +1,90 @@ +{inputs, ...}: { + flake.nixosModules.directories = { + config, + lib, + ... + }: + with lib; let + cfg = config.createPaths; + pathAttrsToListRec = pathsAttrSet: parentPath: parentConfig: + lib.flatten (lib.mapAttrsToList (path: config: let + filteredConfig = lib.filterAttrs (n: v: v != null) (builtins.removeAttrs config ["subPaths"]); + out = + { + path = + if parentPath == "" + then path + else parentPath + "/" + path; + } + // parentConfig // filteredConfig; + in + if config ? subPaths + then [out] ++ (pathAttrsToListRec config.subPaths path filteredConfig) + else [out]) + pathsAttrSet); + pathConfig = { + options = { + group = mkOption { + type = types.nullOr types.str; + default = null; + }; + owner = mkOption { + type = types.nullOr types.str; + default = null; + }; + permissions = mkOption { + type = types.nullOr types.str; + default = null; + }; + subPaths = mkOption { + type = types.attrsOf (types.submodule pathConfig); + default = {}; + }; + }; + }; + pathList = pathAttrsToListRec cfg "" {}; + in rec { + options = { + createPaths = mkOption { + type = types.attrsOf (types.submodule { + options = { + group = mkOption { + type = types.str; + }; + owner = mkOption { + type = types.str; + }; + permissions = mkOption { + type = types.str; + default = "0740"; + }; + subPaths = mkOption { + type = types.attrsOf (types.submodule pathConfig); + default = {}; + description = '' + SubPaths to create using systemd tmpfiles. + ''; + }; + }; + }); + default = {}; + description = '' + Paths to create using systemd tmpfiles. + ''; + }; + }; + + config = mkIf (cfg != {}) { + systemd.tmpfiles.rules = + map + (dir: "d ${dir.path} ${dir.permissions} ${dir.owner} ${dir.group}") + pathList; + + users = let + extraGroups = map (path: path.group) pathList; + in { + groups = genAttrs extraGroups (group: {}); + }; + }; + }; +} diff --git a/modules/nixosModules/gonic.nix b/modules/nixosModules/gonic.nix new file mode 100644 index 0000000..0f1907a --- /dev/null +++ b/modules/nixosModules/gonic.nix @@ -0,0 +1,114 @@ +{inputs, ...}: { + flake.nixosModules.gonic = { + config, + lib, + pkgs, + ... + }: + with lib; let + cfg = config.gonic; + in { + options = { + gonic = { + enable = mkEnableOption "enable gonic configuration"; + + listenAddr = mkOption { + type = types.str; + default = "127.0.0.1:4747"; + description = '' + Address that gonic will listen on. + ''; + }; + + extraGroups = mkOption { + type = types.listOf (types.str); + default = []; + description = '' + Additional groups for gonic. + ''; + }; + + musicPaths = mkOption { + type = types.listOf (types.str); + description = '' + Directories with music in it. + ''; + }; + + podcastsPath = mkOption { + type = types.str; + default = "${cfg.stateDir}/podcasts"; + description = '' + Directory for podcasts. + ''; + }; + + playlistsPath = mkOption { + type = types.str; + default = "${cfg.stateDir}/playlists"; + description = '' + Directory for playlists. + ''; + }; + + stateDir = mkOption { + type = types.str; + default = "/var/lib/gonic"; + description = '' + A directory where gonic will keep their files. + ''; + }; + + settings = mkOption { + default = {}; + description = '' + Additional gonic settings + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.gonic.serviceConfig = { + DynamicUser = lib.mkForce false; + User = "gonic"; + Group = "gonic"; + SupplementaryGroups = cfg.extraGroups; + ReadWritePaths = [ + cfg.podcastsPath + cfg.playlistsPath + ]; + }; + + users = { + groups = { + gonic = {}; + }; + + users.gonic = { + isSystemUser = true; + group = "gonic"; + }; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.stateDir} 0755 gonic gonic" + "d ${cfg.podcastsPath} 0755 gonic gonic" + "d ${cfg.playlistsPath} 0755 gonic gonic" + ]; + + services.gonic = { + enable = true; + settings = + { + listen-addr = cfg.listenAddr; + music-path = cfg.musicPaths; + playlists-path = [cfg.playlistsPath]; + podcast-path = [cfg.podcastsPath]; + db-path = ["${cfg.stateDir}/gonic.db"]; + } + // cfg.settings; + }; + }; + }; +} diff --git a/modules/nixosModules/nfs.nix b/modules/nixosModules/nfs.nix new file mode 100644 index 0000000..3f53cc6 --- /dev/null +++ b/modules/nixosModules/nfs.nix @@ -0,0 +1,118 @@ +{inputs, ...}: { + flake.nixosModules.nfs = { + config, + lib, + ... + }: + with lib; let + cfg = config.nfs; + in { + options = { + nfs.server = mkOption { + description = '' + NFS server configuration. + ''; + default = {enable = false;}; + type = types.submodule { + options = { + enable = mkEnableOption "Enable nfs server"; + exportsPath = mkOption { + type = types.str; + default = "/export"; + description = '' + A path to the dir, where exports will be binded. + ''; + }; + + defaultExportIps = mkOption { + type = types.listOf (types.str); + description = '' + A list of ip addresses, that will be used as default in exportDirs + ''; + }; + + defaultExportParams = mkOption { + type = types.str; + default = "rw,nohide,insecure,no_subtree_check"; + description = '' + Params, that will be used as default in exportDirs + ''; + }; + + exportDirs = mkOption { + description = '' + A list of directories to export. + ''; + type = types.listOf (types.submodule { + options = { + path = mkOption { + type = types.str; + description = '' + A path to the directory to export. + ''; + }; + exportPath = mkOption { + type = types.str; + default = ""; + description = '' + A path that will be binded to the export directory in the exportsPath. + ''; + }; + ips = mkOption { + type = types.listOf (types.str); + default = cfg.server.defaultExportIps; + description = '' + A list of ip addresses to export the dir to. + ''; + }; + params = mkOption { + type = types.str; + default = cfg.server.defaultExportParams; + description = '' + Params for the ip addresses. + ''; + }; + }; + }); + }; + }; + }; + }; + }; + + config = mkIf cfg.server.enable { + services.nfs.server = { + enable = true; + exports = + "${cfg.server.exportsPath} ${concatMapStrings (ip: "${ip}(rw,fsid=0,no_subtree_check) ") cfg.server.defaultExportIps}\n" + + concatMapStrings + (dir: let + ips = concatMapStrings (ip: "${ip}(${dir.params}) ") dir.ips; + exportPath = + if dir.exportPath != "" + then dir.exportPath + else baseNameOf dir.path; + in "${cfg.server.exportsPath}/${exportPath} ${ips}\n") + cfg.server.exportDirs; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.server.exportsPath} 0744 nobody nogroup" + ]; + + fileSystems = listToAttrs (map (exportDir: let + exportPath = + if exportDir.exportPath != "" + then exportDir.exportPath + else baseNameOf exportDir.path; + fullExportPath = "${cfg.server.exportsPath}/${exportPath}"; + in { + name = fullExportPath; + value = { + device = exportDir.path; + options = ["bind"]; + }; + }) cfg.server.exportDirs); + }; + }; +} diff --git a/modules/nixosModules/nginxProxy.nix b/modules/nixosModules/nginxProxy.nix new file mode 100644 index 0000000..36fdc59 --- /dev/null +++ b/modules/nixosModules/nginxProxy.nix @@ -0,0 +1,217 @@ +{inputs, ...}: { + flake.nixosModules.nginxProxy = { + pkgs, + config, + lib, + ... + }: + with lib; let + vhostOptions = import (pkgs.path + "/nixos/modules/services/web-servers/nginx/vhost-options.nix"); + locationOptions = import (pkgs.path + "/nixos/modules/services/web-servers/nginx/location-options.nix"); + nginxOptions = import (pkgs.path + "/nixos/modules/services/web-servers/nginx/default.nix"); + + autheliaAuth = url: '' + auth_request /internal/authelia/authz; + auth_request_set $redirection_url $upstream_http_location; + error_page 401 =302 $redirection_url; + + auth_request_set $user $upstream_http_remote_user; + auth_request_set $groups $upstream_http_remote_groups; + auth_request_set $email $upstream_http_remote_email; + auth_request_set $name $upstream_http_remote_name; + + proxy_set_header Remote-User $user; + proxy_set_header Remote-Groups $groups; + proxy_set_header Remote-Email $email; + proxy_set_header Remote-Name $name; + ''; + + autheliaLocation = url: '' + internal; + set $upstream_authelia ${url}/api/authz/auth-request; + proxy_pass $upstream_authelia; + + ## Headers + ## The headers starting with X-* are required. + proxy_set_header X-Original-Method $request_method; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Content-Length ""; + proxy_set_header Connection ""; + + ## Basic Proxy Configuration + proxy_pass_request_body off; + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead + proxy_redirect http:// $scheme://; + proxy_http_version 1.1; + proxy_cache_bypass $cookie_session; + proxy_no_cache $cookie_session; + proxy_buffers 4 32k; + client_body_buffer_size 128k; + + ## Advanced Proxy Configuration + send_timeout 5m; + proxy_read_timeout 240; + proxy_send_timeout 240; + proxy_connect_timeout 240; + ''; + + cfg = config.nginxProxy; + in { + options.nginxProxy = { + enable = mkEnableOption "Enable nginxProxy"; + + domain = mkOption { + type = types.str; + description = '' + Domain to use with subdomains + ''; + }; + + recommendedProxySettings = mkOption { + type = types.bool; + default = true; + description = '' + Enables global recommended proxy settings + ''; + }; + + subdomains = mkOption { + type = types.attrsOf (types.submodule (locationOptions {inherit config lib;})); + description = '' + Subdomains with nginx virtualHosts configuration + ''; + }; + + extraVirtualHosts = mkOption { + type = types.attrsOf (types.submodule (vhostOptions {inherit config lib;})); + default = {}; + }; + + home = { + virtualHosts = mkOption { + type = types.attrsOf (types.submodule (vhostOptions {inherit config lib;})); + default = {}; + description = '' + Virtual hosts from another nginx configuration, that will be used to decrypt ssl and forward traffic to another server. + Make sure that the connection between the two is secure. + ''; + }; + + subdomains = mkOption { + type = types.attrsOf (types.submodule (locationOptions {inherit config lib;})); + default = {}; + description = '' + Subdomains from another nginx configuration, that will be used to decrypt ssl and forward traffic to another server. + Make sure that the connection between the two is secure. + ''; + }; + + domain = mkOption { + type = types.str; + default = cfg.domain; + description = '' + Home domain, if no domain provided, the current will be used; + ''; + }; + + url = mkOption { + type = types.str; + default = ""; + description = '' + Url that requests would be passed to; + ''; + }; + + authelia = mkOption { + type = types.submodule { + options = { + enable = mkOption { + type = types.bool; + default = true; + }; + publicUrl = mkOption { + type = types.str; + default = "https://auth.${cfg.domain}/"; + }; + localUrl = mkOption { + type = types.str; + default = "http://127.0.0.1:9091"; + }; + }; + }; + default = {}; + }; + }; + + acme = { + enable = mkEnableOption "enable acme certs"; + email = mkOption { + type = types.str; + default = "notspl3g+acme@duck.com"; + }; + }; + + extraConfig = mkOption { + type = types.attrsOf (types.submodule nginxOptions); + default = {}; + description = '' + Extra nginx config. + ''; + }; + }; + + config = mkIf cfg.enable { + security.acme = mkIf cfg.acme.enable { + acceptTerms = true; + defaults.email = cfg.acme.email; + }; + + users.groups.nginx = mkIf cfg.acme.enable {}; + users.users.nginx = mkIf cfg.acme.enable { + group = "nginx"; + extraGroups = ["acme"]; + isSystemUser = true; + }; + services.nginx = let + ssl = { + forceSSL = cfg.acme.enable; + enableACME = cfg.acme.enable; + }; + + makeVhosts = domain: subdomains: + lib.concatMapAttrs + (name: value: {${name + "." + domain} = {locations."/" = value;} // ssl;}) + subdomains; + + homeRoutes = homeVirtualHosts: homeUrl: + builtins.mapAttrs + (name: value: + { + locations."/" = + value.locations."/" + // { + proxyPass = homeUrl; + recommendedProxySettings = true; + extraConfig = value.locations."/".extraConfig + (autheliaAuth cfg.home.authelia.publicUrl); + }; + locations."/internal/authelia/authz" = mkIf cfg.home.authelia.enable { + extraConfig = autheliaLocation cfg.home.authelia.localUrl; + }; + } + // ssl) + homeVirtualHosts; + + vhosts = makeVhosts cfg.domain cfg.subdomains; + homeVhosts = homeRoutes ((makeVhosts (cfg.home.domain) cfg.home.subdomains) // cfg.home.virtualHosts) cfg.home.url; + in + { + enable = true; + recommendedProxySettings = cfg.recommendedProxySettings; + + virtualHosts = vhosts // homeVhosts // cfg.extraVirtualHosts; + } + // cfg.extraConfig; + }; + }; +} diff --git a/modules/nixosModules/watcharr.nix b/modules/nixosModules/watcharr.nix new file mode 100644 index 0000000..2263e4f --- /dev/null +++ b/modules/nixosModules/watcharr.nix @@ -0,0 +1,74 @@ +{ + inputs, + self, + ... +}: { + flake.nixosModules.watcharr = { + config, + lib, + pkgs, + ... + }: + with lib; let + cfg = config.services.watcharr; + port = builtins.toString cfg.settings.port; + in { + options = { + services.watcharr = { + enable = mkEnableOption "Enable watcharr service"; + subdomain = mkOption { + type = types.str; + description = '' + Subdomain to use for nginx. + ''; + }; + settings = { + dataDir = mkOption { + type = types.path; + description = '' + Watcharr data directory. + ''; + default = "/var/lib/watcharr"; + }; + port = mkOption { + type = types.port; + default = 3080; + description = '' + Port to use. + ''; + }; + }; + }; + }; + + config = mkIf cfg.enable { + createPaths = { + "${cfg.settings.dataDir}" = { + owner = "root"; + group = "root"; + permissions = "0750"; + }; + }; + + nginxProxy = { + enable = true; + subdomains = { + "${cfg.subdomain}" = { + proxyPass = "http://127.0.0.1:${port}"; + proxyWebsockets = true; + }; + }; + }; + + virtualisation.oci-containers.containers.watcharr = { + image = "ghcr.io/sbondco/watcharr:latest"; + ports = [ + "127.0.0.1${port}:3080" + ]; + volumes = [ + "${cfg.settings.dataDir}:/data" + ]; + }; + }; + }; +} |
