{ inputs, lib, namespace }: let inherit (inputs.nixpkgs.lib) mapAttrs mkOption types toUpper substring stringLength mkDefault mkForce ; base64Lib = import ../base64 { inherit inputs; }; in rec { # Conditionally enable modules based on system enableForSystem = system: modules: builtins.filter ( mod: mod.systems or [ ] == [ ] || builtins.elem system (mod.systems or [ ]) ) modules; # Create a module with common options mkModule = { name, description ? "", options ? { }, moduleConfig ? { }, domain ? "services", config }: let cfg = config.${namespace}.${domain}.${name}; # Create reverse proxy configuration using mkReverseProxy reverseProxyConfig = lib.${namespace}.mkReverseProxy { inherit name; subdomain = cfg.reverseProxy.subdomain; url = "http://${config.${namespace}.network.ipv4.address}:${toString cfg.port}"; # TODO: address middlewares = cfg.reverseProxy.middlewares; }; defaultConfig = { ${namespace}.services.traefik = lib.mkIf cfg.reverseProxy.enable { reverseProxies = [ reverseProxyConfig ]; }; users = lib.mkIf cfg.createUser { users.${name} = { isSystemUser = true; group = name; home = cfg.configDir; }; groups.${name} = { }; }; systemd.tmpfiles.rules = [ "d ${cfg.configDir} 0700 ${name} ${name} - -" "d ${cfg.configDir}/server-files 0700 ${name} ${name} - -" "d ${cfg.configDir}/user-files 0700 ${name} ${name} - -" ]; } // moduleConfig; in { config, lib, ... }: { options.${namespace}.${domain}.${name} = lib.mkOption { type = lib.types.submodule { options = { enable = lib.mkEnableOption description; port = mkOpt types.int 80 "Port for ${name} to be hosted on"; configDir = mkOpt types.str "/media/nas/main/nix-app-data/${name}" "Path to the config dir"; dataDir = mkOpt types.str "/media/nas/main/${name}" "Path to the data dir"; createUser = mkBoolOpt false "create a user for this module/service"; reverseProxy = mkReverseProxyOpt; } // options; }; default = { }; }; config = lib.mkIf cfg.enable defaultConfig; }; # container mkContainer = { name, localAddress ? "127.0.0.1", ports ? [ "80" ], bindMounts ? { }, config ? { }, }: { lib, ... }: { containers.${name} = { inherit localAddress bindMounts; config = config // { networking = { firewall = { enable = true; allowedTCPPorts = ports; }; # Use systemd-resolved inside the container # Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686 useHostResolvConf = lib.mkForce false; }; services.resolved.enable = true; system.stateVersion = "23.11"; }; autoStart = lib.mkDefault true; privateNetwork = lib.mkDefault true; hostAddress = lib.mkDefault "10.0.1.3"; }; networking = { nat.forwardPorts = map (port: { destination = lib.mkDefault "${localAddress}:${toString port}"; sourcePort = lib.mkDefault port; }) ports; firewall = { allowedTCPPorts = ports; allowedUDPPorts = ports; }; }; }; # Migrated mjallen utilities # Option creation helpers mkOpt = type: default: description: mkOption { inherit type default description; }; mkOpt' = type: default: mkOpt type default null; mkBoolOpt = mkOpt types.bool; mkBoolOpt' = mkOpt' types.bool; mkReverseProxyOpt = { enable = mkBoolOpt false "Enable reverse proxy support"; subdomain = mkOpt types.str "" "subdomain of the service"; middlewares = mkOpt (types.listOf types.str) [ ] "List of middlewares to use"; }; # Standard enable/disable patterns enabled = { enable = true; }; disabled = { enable = false; }; # String utilities capitalize = s: let len = stringLength s; in if len == 0 then "" else (toUpper (substring 0 1 s)) + (substring 1 len s); # Boolean utilities boolToNum = bool: if bool then 1 else 0; # Attribute manipulation utilities default-attrs = mapAttrs (_key: mkDefault); force-attrs = mapAttrs (_key: mkForce); nested-default-attrs = mapAttrs (_key: default-attrs); nested-force-attrs = mapAttrs (_key: force-attrs); } // base64Lib