{ config, lib, pkgs, ... }: let domain = builtins.head config.mailserver.domains; radicaleHost = "cal.${domain}"; mailAccounts = config.mailserver.loginAccounts; discoveryHosts = lib.unique ( config.mailserver.domains ++ [ config.mailserver.fqdn radicaleHost ] ); accountHash = mail: account: if account ? hashedPassword then account.hashedPassword else if account ? hashedPasswordFile then lib.removeSuffix "\n" (builtins.readFile account.hashedPasswordFile) else throw "Radicale requires ${mail} to define hashedPassword or hashedPasswordFile"; htpasswd = pkgs.writeText "radicale.users" ( lib.concatStrings ( lib.mapAttrsToList (mail: account: "${mail}:${accountHash mail account}\n") mailAccounts ) ); mkWellKnownLocations = { "/.well-known/caldav".return = "301 https://${radicaleHost}/"; "/.well-known/carddav".return = "301 https://${radicaleHost}/"; }; discoveryVirtualHosts = lib.listToAttrs ( map (host: { name = host; value = { enableACME = true; forceSSL = true; locations = mkWellKnownLocations; }; }) discoveryHosts ); in { services.radicale = { enable = true; settings = { auth = { type = "htpasswd"; htpasswd_filename = htpasswd; htpasswd_encryption = "bcrypt"; }; }; }; services.nginx.virtualHosts = discoveryVirtualHosts // { ${radicaleHost} = { enableACME = true; forceSSL = true; locations = mkWellKnownLocations // { "/" = { proxyPass = "http://127.0.0.1:5232/"; extraConfig = '' proxy_set_header X-Script-Name /; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_header Authorization; ''; }; }; }; }; }