diff --git a/config/lnbits.nix b/config/lnbits.nix new file mode 100644 index 0000000..8d25e3d --- /dev/null +++ b/config/lnbits.nix @@ -0,0 +1,83 @@ +{ domain, ... }: + +{ + # LNBits service configuration + services.lnbits = { + enable = true; + host = "0.0.0.0"; + port = 5000; + openFirewall = true; + stateDir = "/var/lib/lnbits"; + package = (builtins.getFlake "path:/etc/nixos/sources/lnbits").packages.x86_64-linux.lnbits; + env = { + LNBITS_ADMIN_UI = "true"; + AUTH_ALLOWED_METHODS = "user-id-only, username-password"; + LNBITS_BACKEND_WALLET_CLASS = "FakeWallet"; + LNBITS_SITE_TITLE = "AIO"; + LNBITS_SITE_TAGLINE = "Open Source Lightning Payments Platform"; + LNBITS_SITE_DESCRIPTION = "A lightning wallet for the community"; + LIGHTNING_INVOICE_EXPIRY = "3600"; + LNBITS_DEFAULT_WALLET_NAME = "AIO Wallet"; + LNBITS_EXTENSIONS_MANIFESTS = + "https://raw.githubusercontent.com/lnbits/lnbits-extensions/main/extensions.json"; + LNBITS_EXTENSIONS_DEFAULT_INSTALL = + "nostrclient,nostrmarket,nostrrelay,lnurlp,events"; + LNBITS_ADMIN_EXTENSIONS = "ngrok,nostrclient,nostrrelay"; + LNBITS_USER_DEFAULT_EXTENSIONS = "lnurlp,nostrmarket,events"; + FORWARDED_ALLOW_IPS = "*"; + }; + }; + + services.nginx = { + # Add the connection upgrade map + appendHttpConfig = '' + map $http_upgrade $connection_upgrade { + default upgrade; + "" close; + } + ''; + + virtualHosts."lnbits.${domain}" = { + forceSSL = true; + enableACME = true; + locations = { + # WebSocket endpoints with additional headers that LNbits might expect + "~ ^/(api/v1/ws/|.*relay.*/)" = { + proxyPass = "http://127.0.0.1:5000"; + extraConfig = '' + # WebSocket configuration + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket timeouts + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_connect_timeout 60s; + + # Disable buffering + proxy_buffering off; + proxy_request_buffering off; + proxy_cache off; + ''; + }; + + # General HTTP requests (with basic proxy headers) + "/" = { + proxyPass = "http://127.0.0.1:5000"; + extraConfig = '' + # Basic proxy headers for HTTP (not WebSocket) + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + }; + }; + }; +} diff --git a/config/modules/lnbits-service.nix b/config/modules/lnbits-service.nix new file mode 100644 index 0000000..358f300 --- /dev/null +++ b/config/modules/lnbits-service.nix @@ -0,0 +1,123 @@ +{ config, pkgs, lib, ... }: + +let + defaultUser = "lnbits"; + cfg = config.services.lnbits; + inherit (lib) mkOption mkIf types optionalAttrs literalExpression; +in + +{ + options = { + services.lnbits = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the lnbits service + ''; + }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Whether to open the ports used by lnbits in the firewall for the server + ''; + }; + package = mkOption { + type = types.package; + defaultText = literalExpression "pkgs.lnbits"; + default = pkgs.lnbits; + description = '' + The lnbits package to use. + ''; + }; + stateDir = mkOption { + type = types.path; + default = "/var/lib/lnbits"; + description = '' + The lnbits state directory + ''; + }; + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + The host to bind to + ''; + }; + port = mkOption { + type = types.port; + default = 8231; + description = '' + The port to run on + ''; + }; + user = mkOption { + type = types.str; + default = "lnbits"; + description = "user to run lnbits as"; + }; + group = mkOption { + type = types.str; + default = "lnbits"; + description = "group to run lnbits as"; + }; + env = mkOption { + type = types.attrsOf types.str; + default = {}; + description = '' + Additional environment variables that are passed to lnbits. + Reference Variables: https://github.com/lnbits/lnbits/blob/dev/.env.example + ''; + example = { + LNBITS_ADMIN_UI = "true"; + }; + }; + }; + }; + + config = mkIf cfg.enable { + users.users = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = { + isSystemUser = true; + group = defaultUser; + }; + }; + + users.groups = optionalAttrs (cfg.group == defaultUser) { + ${defaultUser} = { }; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.stateDir} 0700 ${cfg.user} ${cfg.group} - -" + "d ${cfg.stateDir}/data 0700 ${cfg.user} ${cfg.group} - -" + ]; + + systemd.services.lnbits = { + enable = true; + description = "lnbits"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + environment = lib.mkMerge [ + { + LNBITS_DATA_FOLDER = "${cfg.stateDir}/data"; + # LNBits automatically appends '/extensions' to this path + LNBITS_EXTENSIONS_PATH = "${cfg.stateDir}"; + } + cfg.env + ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = "${cfg.package}/lib/python3.12/site-packages"; + StateDirectory = "lnbits"; + ExecStart = "${cfg.package}/bin/lnbits --port ${toString cfg.port} --host ${cfg.host}"; + Restart = "always"; + PrivateTmp = true; + }; + }; + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + }; +} diff --git a/config/shared.nix b/config/shared.nix index 159df12..8e9023d 100644 --- a/config/shared.nix +++ b/config/shared.nix @@ -4,6 +4,8 @@ imports = [ ./nginx.nix ./pict-rs.nix + ./modules/lnbits-service.nix + ./lnbits.nix ]; # Set hostname (passed as parameter) @@ -31,16 +33,6 @@ tryFiles = "$uri $uri/ /index.html"; }; }; - - # LNbits service (adjust port as needed) - "lnbits.${domain}" = { - forceSSL = true; - enableACME = true; - locations."/" = { - proxyPass = "http://localhost:5000"; - proxyWebsockets = true; - }; - }; }; # NixOS release version