This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / configs / radicale.nix
4.8 kB 159 lines
1{ 2 config, 3 lib, 4 pkgs, 5 self, 6 ... 7}: 8let 9 # lass: personal calendars; tools/book reads free/busy from them and writes 10 # booking requests into lass/bookings. 11 users = [ 12 "opencrow" 13 "lass" 14 ]; 15 16 # Generate one var per user with random password 17 userGenerators = lib.listToAttrs ( 18 map (user: { 19 name = "radicale-${user}"; 20 value = { 21 files."htpasswd-line" = { }; 22 files."password" = { }; 23 runtimeInputs = with pkgs; [ 24 apacheHttpd 25 coreutils 26 ]; 27 script = '' 28 password=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24) 29 echo "$password" > "$out/password" 30 htpasswd -nbB ${user} "$password" > "$out/htpasswd-line" 31 ''; 32 }; 33 }) users 34 ); 35 36 loadCredentials = map ( 37 user: 38 "radicale-${user}-htpasswd:${ 39 config.clan.core.vars.generators."radicale-${user}".files."htpasswd-line".path 40 }" 41 ) users; 42 43 assembleHtpasswd = pkgs.writeShellScript "radicale-htpasswd" ( 44 lib.concatMapStringsSep "\n" (user: "cat \${CREDENTIALS_DIRECTORY}/radicale-${user}-htpasswd") users 45 ); 46 47 # Calino (static, local-first CalDAV web client) on its own origin; accounts 48 # and settings stay in the browser, so nothing is stored server-side. Log in 49 # with server https://cal.lassul.us/ and a radicale user. 50 webClient = "calendar.lassul.us"; 51 calino = self.packages.${pkgs.stdenv.hostPlatform.system}.calino; 52 53 proxyHeaders = '' 54 proxy_set_header X-Script-Name /; 55 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 56 proxy_set_header Host $host; 57 ''; 58 59 # CORS for the web client; see the maps in appendHttpConfig below. 60 cors = '' 61 add_header Access-Control-Allow-Origin $radicale_cors_origin always; 62 add_header Access-Control-Allow-Methods "GET, PUT, DELETE, PROPFIND, PROPPATCH, REPORT, OPTIONS, MKCOL, MKCALENDAR, MOVE" always; 63 add_header Access-Control-Allow-Headers "Authorization, Content-Type, Depth, Prefer, If-Match, If-None-Match" always; 64 add_header Access-Control-Expose-Headers "ETag, Location, DAV, Allow, X-Sync-Token" always; 65 add_header Access-Control-Max-Age 86400 always; 66 add_header Vary Origin always; 67 if ($radicale_cors_preflight) { 68 return 204; 69 } 70 ''; 71in 72{ 73 services.radicale = { 74 enable = true; 75 settings = { 76 server = { 77 hosts = [ "127.0.0.1:5232" ]; 78 }; 79 auth = { 80 type = "htpasswd"; 81 htpasswd_filename = "/run/radicale/htpasswd"; 82 htpasswd_encryption = "bcrypt"; 83 }; 84 storage = { 85 filesystem_folder = "/var/lib/radicale/collections"; 86 }; 87 web = { 88 type = "internal"; 89 }; 90 }; 91 }; 92 93 systemd.services.radicale.serviceConfig = { 94 RuntimeDirectory = "radicale"; 95 LoadCredential = loadCredentials; 96 }; 97 systemd.services.radicale.preStart = lib.mkBefore '' 98 ${assembleHtpasswd} > /run/radicale/htpasswd 99 ''; 100 101 clan.core.vars.generators = userGenerators; 102 103 # Let the web client's origin talk CalDAV to radicale. Preflights carry no 104 # credentials and are answered here; a plain DAV OPTIONS (no 105 # Access-Control-Request-Method) still reaches radicale. 106 services.nginx.appendHttpConfig = '' 107 map $http_origin $radicale_cors_origin { 108 default ""; 109 "https://${webClient}" $http_origin; 110 } 111 map "$request_method:$http_access_control_request_method" $radicale_cors_preflight { 112 default 0; 113 "~^OPTIONS:." 1; 114 } 115 # Top-level navigations send no Origin; the web client's GET / probes 116 # (CalDAV discovery via /.well-known/caldav) do and must reach radicale. 117 map "$request_method:$http_origin" $radicale_root_redirect { 118 default 0; 119 "GET:" 1; 120 } 121 ''; 122 123 services.nginx.virtualHosts."cal.lassul.us" = { 124 forceSSL = true; 125 enableACME = true; 126 # Browsers opening the bare domain get the web client; CalDAV discovery 127 # (PROPFIND /, or the web client's cross-origin GET /) still reaches 128 # radicale. 129 locations."= /" = { 130 proxyPass = "http://127.0.0.1:5232"; 131 extraConfig = 132 cors 133 + '' 134 if ($radicale_root_redirect) { 135 return 302 https://${webClient}/; 136 } 137 '' 138 + proxyHeaders; 139 }; 140 locations."/" = { 141 proxyPass = "http://127.0.0.1:5232"; 142 extraConfig = cors + proxyHeaders; 143 }; 144 }; 145 146 services.nginx.virtualHosts.${webClient} = { 147 forceSSL = true; 148 enableACME = true; 149 root = calino; 150 locations."/".extraConfig = '' 151 try_files $uri /index.html; 152 add_header Cache-Control no-cache; 153 ''; 154 # Vite puts content-hashed bundles here. 155 locations."/assets/".extraConfig = '' 156 add_header Cache-Control "public, max-age=31536000, immutable"; 157 ''; 158 }; 159}