Unverified Commit ad6b1336 authored by Paul Meyer's avatar Paul Meyer Committed by GitHub
Browse files

nixos/paisa: init (#421807)

parents c5cdf10b e33d7087
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -587,6 +587,12 @@
  "module-services-suwayomi-server-extra-config": [
    "index.html#module-services-suwayomi-server-extra-config"
  ],
  "module-services-paisa": [
    "index.html#module-services-paisa"
  ],
  "module-services-paisa-usage": [
    "index.html#module-services-paisa-usage"
  ],
  "module-services-plausible": [
    "index.html#module-services-plausible"
  ],
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@

- [SuiteNumérique Meet](https://github.com/suitenumerique/meet) is an open source alternative to Google Meet and Zoom powered by LiveKit: HD video calls, screen sharing, and chat features. Built with Django and React. Available as [services.lasuite-meet](#opt-services.lasuite-meet.enable).

- [paisa](https://github.com/ananthakumaran/paisa), a personal finance tracker and dashboard. Available as [services.paisa](#opt-services.paisa.enable).

## Backward Incompatibilities {#sec-release-25.11-incompatibilities}

<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+1 −0
Original line number Diff line number Diff line
@@ -881,6 +881,7 @@
  ./services/misc/osrm.nix
  ./services/misc/owncast.nix
  ./services/misc/packagekit.nix
  ./services/misc/paisa.nix
  ./services/misc/paperless.nix
  ./services/misc/parsoid.nix
  ./services/misc/persistent-evdev.nix
+32 −0
Original line number Diff line number Diff line
# Paisa {#module-services-paisa}

*Source:* {file}`modules/services/misc/paisa.nix`

*Upstream documentation:* <https://paisa.fyi/>

[Paisa](https://github.com/ananthakumaran/paisa) is a personal finance manager
built on top of the ledger plain-text-accounting tool.

## Usage {#module-services-paisa-usage}

Paisa needs to have one of the following cli tools availabe in the PATH at
runtime:

- ledger
- hledger
- beancount

All of these are available from nixpkgs. Currently, it is not possible to
configure this in the module, but you can e.g. use systemd to give the unit
access to the command at runtime.

```nix
systemd.services.paisa.path = [ pkgs.hledger ];
```

::: {.note}
Paisa needs to be configured to use the correct cli tool. This is possible in
the web interface (make sure to enable [](#opt-services.paisa.mutableSettings)
if you want to persist these settings between service restarts), or in
[](#opt-services.paisa.settings).
:::
+145 −0
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.services.paisa;
  settingsFormat = pkgs.formats.yaml { };

  args = lib.concatStringsSep " " [
    "--config /var/lib/paisa/paisa.yaml"
  ];

  settings =
    if (cfg.settings != null) then
      builtins.removeAttrs
        (
          cfg.settings
          // {
            journal_path = cfg.settings.dataDir + cfg.settings.journalFile;
            db_path = cfg.settings.dataDir + cfg.settings.dbFile;
          }
        )
        [
          "dataDir"
          "journalFile"
          "dbFile"
        ]
    else
      null;

  configFile = (settingsFormat.generate "paisa.yaml" settings).overrideAttrs (_: {
    checkPhase = "";
  });
in
{
  options.services.paisa = with lib.types; {
    enable = lib.mkEnableOption "Paisa personal finance manager";

    package = lib.mkPackageOption pkgs "paisa" { };

    openFirewall = lib.mkOption {
      default = false;
      type = bool;
      description = "Open ports in the firewall for the Paisa web server.";
    };

    mutableSettings = lib.mkOption {
      default = true;
      type = bool;
      description = ''
        Allow changes made on the web interface to persist between service
        restarts.
      '';
    };

    host = lib.mkOption {
      type = str;
      default = "0.0.0.0";
      description = "Host bind IP address.";
    };

    port = lib.mkOption {
      type = port;
      default = 7500;
      description = "Port to serve Paisa on.";
    };

    settings = lib.mkOption {
      default = null;
      type = nullOr (submodule {
        freeformType = settingsFormat.type;
        options = {
          dataDir = lib.mkOption {
            type = lib.types.str;
            default = "/var/lib/paisa/";
            description = "Path to paisa data directory.";
          };

          journalFile = lib.mkOption {
            type = lib.types.str;
            default = "main.ledger";
            description = "Filename of the main journal / ledger file.";
          };

          dbFile = lib.mkOption {
            type = lib.types.str;
            default = "paisa.sqlite3";
            description = "Filename of the Paisa database.";
          };

        };
      });
      description = ''
        Paisa configuration. Please refer to
        <https://paisa.fyi/reference/config/> for details.

        On start and if `mutableSettings` is `true`, these options are merged
        into the configuration file on start, taking precedence over
        configuration changes made on the web interface.
      '';
    };
  };
  config = lib.mkIf cfg.enable {
    assertions = [ ];

    systemd.services.paisa = {
      description = "Paisa: Web Application";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      unitConfig = {
        StartLimitIntervalSec = 5;
        StartLimitBurst = 10;
      };

      preStart = lib.optionalString (settings != null) ''
        if [ -e "$STATE_DIRECTORY/paisa.yaml" ] && [ "${toString cfg.mutableSettings}" = "1" ]; then
          # do not write directly to the config file
          ${lib.getExe pkgs.yaml-merge} "$STATE_DIRECTORY/paisa.yaml" "${configFile}" > "$STATE_DIRECTORY/paisa.yaml.tmp"
          mv "$STATE_DIRECTORY/paisa.yaml.tmp" "$STATE_DIRECTORY/paisa.yaml"
        else
          cp --force "${configFile}" "$STATE_DIRECTORY/paisa.yaml"
          chmod 600 "$STATE_DIRECTORY/paisa.yaml"
        fi
      '';

      serviceConfig = {
        DynamicUser = true;
        ExecStart = "${lib.getExe cfg.package} serve ${args}";
        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
        Restart = "always";
        RestartSec = 5;
        RuntimeDirectory = "paisa";
        StateDirectory = "paisa";
      };
    };
    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
  };

  meta = {
    maintainers = with lib.maintainers; [ skowalak ];
    doc = ./paisa.md;
  };
}
Loading