{ lib, config, ... }: let cfg = config.modules.services.webserver; types = { location = lib.types.submodule { options = { proxyPort = lib.mkOption { type = lib.types.nullOr lib.types.int; default = null; }; extraConfig = lib.mkOption { type = lib.types.str; default = ""; }; }; }; vhost = lib.types.submodule { options = { proxyBuffering = lib.mkOption { type = lib.types.bool; default = true; }; locations = lib.mkOption { type = lib.types.attrsOf types.location; }; }; }; }; in { options.modules.services.webserver = { enable = lib.mkEnableOption "Enable nginx"; acme = { dnsChallenge = lib.mkEnableOption "Enable DNS challenge"; dnsProvider = lib.mkOption { type = lib.types.str; default = "hetzner"; }; environmentFile = lib.mkOption { type = lib.types.str; default = "/var/secrets/lego"; }; }; vHosts = lib.mkOption { type = lib.types.attrsOf types.vhost; default = { }; }; }; config = lib.mkIf cfg.enable { services.nginx = { enable = lib.mkDefault true; recommendedProxySettings = true; recommendedTlsSettings = true; recommendedBrotliSettings = true; recommendedGzipSettings = true; recommendedZstdSettings = true; recommendedOptimisation = true; virtualHosts = lib.mapAttrs (_: { proxyBuffering, locations }: { forceSSL = true; enableACME = true; http2 = true; acmeRoot = lib.mkIf cfg.acme.dnsChallenge null; extraConfig = lib.concatLines [ (lib.optionalString (!proxyBuffering) "proxy_buffering off;") "charset utf-8;" ]; locations = lib.mapAttrs (_: { proxyPort, extraConfig }: lib.mergeAttrsList [ { inherit extraConfig; } (if (lib.isInt proxyPort) then { proxyWebsockets = true; proxyPass = "http://localhost:${toString proxyPort}"; } else { }) ]) locations; }) cfg.vHosts; }; security.acme = { acceptTerms = true; defaults = { email = "admin@j.repomaa.com"; dnsProvider = lib.mkIf cfg.acme.dnsChallenge cfg.acme.dnsProvider; }; }; modules.firewall.allInterfaces = [ "web" ]; }; }