lol

nixos/hledger-web: add stateDir, use own user, fix ExecStart

This allows for shared hledger installations, where the web interface is
available via network and multiple user share a SSH access to the
hledger user.

Also added `--serve` to the CLI options, as hledger-web tries to open a
webbrowser otherwise:

hledger-web: xdg-open: rawSystem: runInteractiveProcess: exec: does not
exist (No such file or directory)

Co-authored-by: Aaron Andersen <aaron@fosslib.net>

+71 -31
+61 -17
nixos/modules/services/web-apps/hledger-web.nix
··· 34 34 ''; 35 35 }; 36 36 37 - journalFile = mkOption { 37 + stateDir = mkOption { 38 38 type = types.path; 39 - example = "/home/hledger/.hledger.journal"; 39 + default = "/var/lib/hledger-web"; 40 40 description = '' 41 - Input journal file. 41 + Path the service has access to. If left as the default value this 42 + directory will automatically be created before the hledger-web server 43 + starts, otherwise the sysadmin is responsible for ensuring the 44 + directory exists with appropriate ownership and permissions. 45 + ''; 46 + }; 47 + 48 + journalFiles = mkOption { 49 + type = types.listOf types.str; 50 + default = [ ".hledger.journal" ]; 51 + description = '' 52 + Paths to journal files relative to <option>services.hledger-web.stateDir</option>. 42 53 ''; 43 54 }; 44 55 ··· 50 61 Base URL, when sharing over a network. 51 62 ''; 52 63 }; 64 + 65 + extraOptions = mkOption { 66 + type = types.listOf types.str; 67 + default = []; 68 + example = [ "--forecast" ]; 69 + description = '' 70 + Extra command line arguments to pass to hledger-web. 71 + ''; 72 + }; 73 + 53 74 }; 54 75 55 76 config = mkIf cfg.enable { 56 - systemd.services.hledger-web = { 77 + 78 + users.users.hledger = { 79 + name = "hledger"; 80 + group = "hledger"; 81 + isSystemUser = true; 82 + home = cfg.stateDir; 83 + useDefaultShell = true; 84 + }; 85 + 86 + users.groups.hledger = {}; 87 + 88 + systemd.services.hledger-web = let 89 + serverArgs = with cfg; escapeShellArgs ([ 90 + "--serve" 91 + "--host=${host}" 92 + "--port=${toString port}" 93 + "--capabilities=${capabilityString}" 94 + (optionalString (cfg.baseUrl != null) "--base-url=${cfg.baseUrl}") 95 + (optionalString (cfg.serveApi) "--serve-api") 96 + ] ++ (map (f: "--file=${stateDir}/${f}") cfg.journalFiles) 97 + ++ extraOptions); 98 + in { 57 99 description = "hledger-web - web-app for the hledger accounting tool."; 58 100 documentation = [ https://hledger.org/hledger-web.html ]; 59 101 wantedBy = [ "multi-user.target" ]; 60 102 after = [ "networking.target" ]; 61 - serviceConfig = { 62 - ExecStart = '' 63 - ${pkgs.hledger-web}/bin/hledger-web \ 64 - --host=${cfg.host} \ 65 - --port=${toString cfg.port} \ 66 - --file=${cfg.journalFile} \ 67 - "--capabilities=${cfg.capabilities}" \ 68 - ${optionalString (cfg.baseUrl != null) "--base-url=${cfg.baseUrl}"} \ 69 - ${optionalString (cfg.serveApi) "--serve-api"} 70 - ''; 71 - Restart = "always"; 72 - }; 103 + serviceConfig = mkMerge [ 104 + { 105 + ExecStart = "${pkgs.hledger-web}/bin/hledger-web ${serverArgs}"; 106 + Restart = "always"; 107 + WorkingDirectory = cfg.stateDir; 108 + User = "hledger"; 109 + Group = "hledger"; 110 + PrivateTmp = true; 111 + } 112 + (mkIf (cfg.stateDir == "/var/lib/hledger-web") { 113 + StateDirectory = "hledger-web"; 114 + }) 115 + ]; 73 116 }; 117 + 74 118 }; 75 119 76 - meta.maintainers = with lib.maintainers; [ marijanp ]; 120 + meta.maintainers = with lib.maintainers; [ marijanp erictapen ]; 77 121 }
+10 -14
nixos/tests/hledger-web.nix
··· 13 13 name = "hledger-web"; 14 14 meta.maintainers = with lib.maintainers; [ marijanp ]; 15 15 16 - nodes = { 17 - server = { config, pkgs, ... }: rec { 16 + nodes = rec { 17 + server = { config, pkgs, ... }: { 18 18 services.hledger-web = { 19 19 host = "127.0.0.1"; 20 20 port = 5000; 21 21 enable = true; 22 - journalFile = journal; 23 22 }; 24 - networking.firewall.allowedTCPPorts = [ services.hledger-web.port ]; 23 + networking.firewall.allowedTCPPorts = [ config.services.hledger-web.port ]; 24 + systemd.services.hledger-web.preStart = '' 25 + ln -s ${journal} /var/lib/hledger-web/.hledger.journal 26 + ''; 25 27 }; 26 - apiserver = { config, pkgs, ... }: rec { 27 - services.hledger-web = { 28 - host = "127.0.0.1"; 29 - port = 5000; 30 - enable = true; 31 - serveApi = true; 32 - journalFile = journal; 33 - }; 34 - networking.firewall.allowedTCPPorts = [ services.hledger-web.port ]; 28 + apiserver = { ... }: { 29 + imports = [ server ]; 30 + services.hledger-web.serveApi = true; 35 31 }; 36 32 }; 37 33 ··· 42 38 server.wait_for_open_port(5000) 43 39 with subtest("Check if web UI is accessible"): 44 40 page = server.succeed("curl -L http://127.0.0.1:5000") 45 - assert "test.journal" in page 41 + assert ".hledger.journal" in page 46 42 47 43 apiserver.wait_for_unit("hledger-web.service") 48 44 apiserver.wait_for_open_port(5000)