Our Personal Data Server from scratch! tranquil.farm
oauth atproto pds rust postgresql objectstorage fun

refactor(nix): update nix module to use the built-in frontend server

authored by nel.pet and committed by tangled.org 844ba0eb 34beff25

+15 -138
+15 -138
module.nix
··· 9 9 inherit (lib) types mkOption; 10 10 11 11 settingsFormat = pkgs.formats.toml { }; 12 - 13 - backendUrl = "http://127.0.0.1:${toString cfg.settings.server.port}"; 14 - 15 - useACME = cfg.nginx.enableACME && cfg.nginx.useACMEHost == null; 16 - hasSSL = useACME || cfg.nginx.useACMEHost != null; 17 12 in { 18 13 _class = "nixos"; 19 14 ··· 42 37 dataDir = mkOption { 43 38 type = types.str; 44 39 default = "/var/lib/tranquil-pds"; 45 - description = "Directory for tranquil-pds data (blobs, backups)"; 40 + description = "Working directory for tranquil-pds. Also expected to be used for data (blobs, backups)"; 46 41 }; 47 42 48 43 environmentFiles = mkOption { ··· 69 64 ''; 70 65 }; 71 66 72 - frontend.package = mkOption { 73 - type = types.nullOr types.package; 74 - default = self.packages.${pkgs.stdenv.hostPlatform.system}.tranquil-frontend; 75 - defaultText = lib.literalExpression "self.packages.\${pkgs.stdenv.hostPlatform.system}.tranquil-frontend"; 76 - description = "Frontend package to serve via nginx (set null to disable frontend)"; 77 - }; 78 - 79 - nginx = { 80 - enable = lib.mkEnableOption "nginx reverse proxy for tranquil-pds"; 81 - 82 - enableACME = mkOption { 83 - type = types.bool; 84 - default = true; 85 - description = "Enable ACME for the pds domain"; 86 - }; 87 - 88 - useACMEHost = mkOption { 89 - type = types.nullOr types.str; 90 - default = null; 91 - description = '' 92 - Use a pre-configured ACME certificate instead of generating one. 93 - Set this to the cert name from security.acme.certs for wildcard setups. 94 - 95 - REMEMBER: Handle subdomains (*.pds.example.com) require a wildcard cert via DNS-01. 96 - ''; 97 - }; 98 - }; 99 - 100 67 settings = mkOption { 101 68 type = types.submodule { 102 69 freeformType = settingsFormat.type; ··· 129 96 }; 130 97 }; 131 98 99 + frontend = { 100 + enabled = lib.mkEnabeOption "serving the frontend from the backend. Disable to server the frontend manually" 101 + // { default = true; }; 102 + 103 + dir = mkOption { 104 + type = types.nullOr types.package; 105 + default = self.packages.${pkgs.stdenv.hostPlatform.system}.tranquil-frontend; 106 + defaultText = lib.literalExpression "self.packages.\${pkgs.stdenv.hostPlatform.system}.tranquil-frontend"; 107 + description = "Frontend package to be served by the backend"; 108 + }; 109 + }; 110 + 132 111 storage = { 133 112 path = mkOption { 134 113 type = types.path; ··· 174 153 }; 175 154 176 155 config = lib.mkIf cfg.enable ( 177 - lib.mkMerge [ 156 + lib.mkMerge [ 178 157 (lib.mkIf cfg.database.createLocally { 179 158 services.postgresql = { 180 159 enable = true; ··· 196 175 }; 197 176 }) 198 177 199 - (lib.mkIf cfg.nginx.enable { 200 - services.nginx = { 201 - enable = true; 202 - 203 - virtualHosts.${cfg.settings.server.hostname} = { 204 - serverAliases = [ "*.${cfg.settings.server.hostname}" ]; 205 - forceSSL = hasSSL; 206 - enableACME = useACME; 207 - useACMEHost = cfg.nginx.useACMEHost; 208 - 209 - root = lib.mkIf (cfg.frontend.package != null) cfg.frontend.package; 210 - 211 - extraConfig = "client_max_body_size ${toString cfg.settings.server.max_blob_size};"; 212 - 213 - locations = lib.mkMerge [ 214 - { 215 - "/xrpc/" = { 216 - proxyPass = backendUrl; 217 - proxyWebsockets = true; 218 - extraConfig = '' 219 - proxy_read_timeout 86400; 220 - proxy_send_timeout 86400; 221 - proxy_buffering off; 222 - proxy_request_buffering off; 223 - ''; 224 - }; 225 - 226 - "/oauth/" = { 227 - proxyPass = backendUrl; 228 - extraConfig = '' 229 - proxy_read_timeout 300; 230 - proxy_send_timeout 300; 231 - ''; 232 - }; 233 - 234 - "/.well-known/" = { 235 - proxyPass = backendUrl; 236 - }; 237 - 238 - "/webhook/" = { 239 - proxyPass = backendUrl; 240 - }; 241 - 242 - "= /metrics" = { 243 - proxyPass = backendUrl; 244 - }; 245 - 246 - "= /health" = { 247 - proxyPass = backendUrl; 248 - }; 249 - 250 - "= /robots.txt" = { 251 - proxyPass = backendUrl; 252 - }; 253 - 254 - "= /logo" = { 255 - proxyPass = backendUrl; 256 - }; 257 - 258 - "~ ^/u/[^/]+/did\\.json$" = { 259 - proxyPass = backendUrl; 260 - }; 261 - } 262 - 263 - (lib.optionalAttrs (cfg.frontend.package != null) { 264 - "= /oauth-client-metadata.json" = { 265 - root = "${cfg.frontend.package}"; 266 - extraConfig = '' 267 - default_type application/json; 268 - sub_filter_once off; 269 - sub_filter_types application/json; 270 - sub_filter '__PDS_HOSTNAME__' $host; 271 - ''; 272 - }; 273 - 274 - "/assets/" = { 275 - # TODO: use `add_header_inherit` when nixpkgs updates to nginx 1.29.3+ 276 - extraConfig = '' 277 - expires 1y; 278 - add_header Cache-Control "public, immutable"; 279 - ''; 280 - tryFiles = "$uri =404"; 281 - }; 282 - 283 - "/app/" = { 284 - tryFiles = "$uri $uri/ /index.html"; 285 - }; 286 - 287 - "= /" = { 288 - tryFiles = "/homepage.html /index.html"; 289 - }; 290 - 291 - "/" = { 292 - tryFiles = "$uri $uri/ /index.html"; 293 - priority = 9999; 294 - }; 295 - }) 296 - ]; 297 - }; 298 - }; 299 - }) 300 - 301 - { 178 + { 302 179 users.users.${cfg.user} = { 303 180 isSystemUser = true; 304 181 inherit (cfg) group;