diff --git a/config/users.nix b/config/users.nix
index cb06188..22dbea5 100644
--- a/config/users.nix
+++ b/config/users.nix
@@ -44,5 +44,6 @@
plugdev.gid = 967;
tss = uidGid 966;
firefly-iii = uidGid 965;
+ firefly-pico = uidGid 964;
};
}
diff --git a/hosts/kroma/default.nix b/hosts/kroma/default.nix
index 2df3790..53a7361 100644
--- a/hosts/kroma/default.nix
+++ b/hosts/kroma/default.nix
@@ -91,6 +91,9 @@
programs.nix-ld.enable = true;
topology.self.icon = "devices.desktop";
+ # Mainly for client-side formatting in websites like firefly-iii
+ i18n.supportedLocales = [ "de_DE.UTF-8/UTF-8" ];
+
hardware.nvidia-container-toolkit.enable = true;
virtualisation.containers.enable = true;
virtualisation.podman = {
diff --git a/hosts/ward/guests/firefly.nix b/hosts/ward/guests/firefly.nix
index f7639f3..02e0bb5 100644
--- a/hosts/ward/guests/firefly.nix
+++ b/hosts/ward/guests/firefly.nix
@@ -20,8 +20,20 @@ in
expectedBodyRegex = "Firefly III";
network = "home-lan.vlans.services";
};
+ globals.monitoring.http.firefly-pico = {
+ url = "https://${fireflyDomain}/pico";
+ expectedBodyRegex = "Pico";
+ network = "home-lan.vlans.services";
+ };
- age.secrets.firefly-app-key = {
+ age.secrets.firefly-iii-app-key = {
+ generator.script = _: ''
+ echo "base64:$(head -c 32 /dev/urandom | base64)"
+ '';
+ owner = "firefly-iii";
+ };
+
+ age.secrets.firefly-pico-app-key = {
generator.script = _: ''
echo "base64:$(head -c 32 /dev/urandom | base64)"
'';
@@ -33,21 +45,39 @@ in
directory = "/var/lib/firefly-iii";
user = "firefly-iii";
}
+ {
+ directory = "/var/lib/firefly-pico";
+ user = "firefly-pico";
+ }
];
- i18n.supportedLocales = [ "all" ];
services.firefly-iii = {
enable = true;
enableNginx = true;
virtualHost = globals.services.firefly.domain;
settings = {
AUDIT_LOG_LEVEL = "emergency"; # disable audit logs
- LOG_CHANNEL = "stdout";
+ LOG_CHANNEL = "syslog";
APP_URL = "https://${globals.services.firefly.domain}";
TZ = "Europe/Berlin";
TRUSTED_PROXIES = wardWebProxyCfg.wireguard.proxy-home.ipv4;
SITE_OWNER = "admin@${globals.domains.me}";
- APP_KEY_FILE = config.age.secrets.firefly-app-key.path;
+ APP_KEY_FILE = config.age.secrets.firefly-iii-app-key.path;
+ };
+ };
+
+ services.firefly-pico = {
+ enable = true;
+ enableNginx = true;
+ virtualHost = "pico.internal";
+ settings = {
+ LOG_CHANNEL = "syslog";
+ APP_URL = "https://${globals.services.firefly.domain}/pico";
+ TZ = "Europe/Berlin";
+ FIREFLY_URL = config.services.firefly-iii.settings.APP_URL;
+ TRUSTED_PROXIES = wardWebProxyCfg.wireguard.proxy-home.ipv4;
+ SITE_OWNER = "admin@${globals.domains.me}";
+ APP_KEY_FILE = config.age.secrets.firefly-pico-app-key.path;
};
};
@@ -71,6 +101,13 @@ in
proxyPass = "http://firefly";
proxyWebsockets = true;
};
+ locations."/pico" = {
+ proxyPass = "http://firefly/"; # Trailing slash matters! (remove location suffix)
+ proxyWebsockets = true;
+ extraConfig = ''
+ proxy_set_header Host pico.internal;
+ '';
+ };
extraConfig = ''
allow ${globals.net.home-lan.vlans.home.cidrv4};
allow ${globals.net.home-lan.vlans.home.cidrv6};
diff --git a/modules/default.nix b/modules/default.nix
index fe00f12..fc7701e 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -4,6 +4,7 @@
./backups.nix
./deterministic-ids.nix
./distributed-config.nix
+ ./firefly-pico.nix
./globals.nix
./meta.nix
./nginx-upstream-monitoring.nix
diff --git a/modules/firefly-pico.nix b/modules/firefly-pico.nix
new file mode 100644
index 0000000..aededec
--- /dev/null
+++ b/modules/firefly-pico.nix
@@ -0,0 +1,410 @@
+{
+ pkgs,
+ config,
+ lib,
+ ...
+}:
+
+let
+ cfg = config.services.firefly-pico;
+
+ inherit (cfg) user;
+ inherit (cfg) group;
+
+ defaultUser = "firefly-pico";
+ defaultGroup = "firefly-pico";
+
+ artisan = "${cfg.package}/share/php/firefly-pico/artisan";
+
+ env-file-values = lib.attrsets.mapAttrs' (
+ n: v: lib.attrsets.nameValuePair (lib.strings.removeSuffix "_FILE" n) v
+ ) (lib.attrsets.filterAttrs (n: _v: lib.strings.hasSuffix "_FILE" n) cfg.settings);
+ env-nonfile-values = lib.attrsets.filterAttrs (
+ n: _v: !lib.strings.hasSuffix "_FILE" n
+ ) cfg.settings;
+
+ firefly-pico-maintenance = pkgs.writeShellScript "firefly-pico-maintenance.sh" ''
+ set -a
+ ${lib.strings.toShellVars env-nonfile-values}
+ ${lib.strings.concatLines (
+ lib.attrsets.mapAttrsToList (n: v: "${n}=\"$(< ${v})\"") env-file-values
+ )}
+ set +a
+ ${lib.optionalString (
+ cfg.settings.DB_CONNECTION == "sqlite"
+ ) "touch ${cfg.dataDir}/storage/database/database.sqlite"}
+ ${artisan} migrate --isolated --force
+ ${artisan} config:clear
+ ${artisan} config:cache
+ ${artisan} cache:clear
+ '';
+
+ commonServiceConfig = {
+ Type = "oneshot";
+ User = user;
+ Group = group;
+ StateDirectory = "firefly-pico";
+ ReadWritePaths = [ cfg.dataDir ];
+ WorkingDirectory = cfg.package;
+ PrivateTmp = true;
+ PrivateDevices = true;
+ CapabilityBoundingSet = "";
+ AmbientCapabilities = "";
+ ProtectSystem = "strict";
+ ProtectKernelTunables = true;
+ ProtectKernelModules = true;
+ ProtectControlGroups = true;
+ ProtectClock = true;
+ ProtectHostname = true;
+ ProtectHome = "tmpfs";
+ ProtectKernelLogs = true;
+ ProtectProc = "invisible";
+ ProcSubset = "pid";
+ PrivateNetwork = false;
+ RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service @resources"
+ "~@obsolete @privileged"
+ ];
+ RestrictSUIDSGID = true;
+ RemoveIPC = true;
+ NoNewPrivileges = true;
+ RestrictRealtime = true;
+ RestrictNamespaces = true;
+ LockPersonality = true;
+ PrivateUsers = true;
+ };
+
+in
+{
+
+ options.services.firefly-pico = {
+
+ enable = lib.mkEnableOption "Firefly-Pico: A delightful Firefly III companion web app for effortless transaction tracking";
+
+ user = lib.mkOption {
+ type = lib.types.str;
+ default = defaultUser;
+ description = "User account under which firefly-pico runs.";
+ };
+
+ group = lib.mkOption {
+ type = lib.types.str;
+ default = if cfg.enableNginx then "nginx" else defaultGroup;
+ defaultText = "If `services.firefly-pico.enableNginx` is true then `nginx` else ${defaultGroup}";
+ description = ''
+ Group under which firefly-pico runs. It is best to set this to the group
+ of whatever webserver is being used as the frontend.
+ '';
+ };
+
+ dataDir = lib.mkOption {
+ type = lib.types.path;
+ default = "/var/lib/firefly-pico";
+ description = ''
+ The place where firefly-pico stores its state.
+ '';
+ };
+
+ package =
+ lib.mkPackageOption pkgs "firefly-pico" { }
+ // lib.mkOption {
+ apply =
+ firefly-pico:
+ firefly-pico.override {
+ inherit (cfg) dataDir;
+ };
+ };
+
+ enableNginx = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to enable nginx or not. If enabled, an nginx virtual host will
+ be created for access to firefly-pico. If not enabled, then you may use
+ `''${config.services.firefly-pico.package}` as your document root in
+ whichever webserver you wish to setup.
+ '';
+ };
+
+ virtualHost = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost";
+ description = ''
+ The hostname at which you wish firefly-pico to be served. If you have
+ enabled nginx using `services.firefly-pico.enableNginx` then this will
+ be used.
+ '';
+ };
+
+ poolConfig = lib.mkOption {
+ type = lib.types.attrsOf (
+ lib.types.oneOf [
+ lib.types.str
+ lib.types.int
+ lib.types.bool
+ ]
+ );
+ default = { };
+ defaultText = ''
+ {
+ "pm" = "dynamic";
+ "pm.max_children" = 32;
+ "pm.start_servers" = 2;
+ "pm.min_spare_servers" = 2;
+ "pm.max_spare_servers" = 4;
+ "pm.max_requests" = 500;
+ }
+ '';
+ description = ''
+ Options for the Firefly III PHP pool. See the documentation on php-fpm.conf
+ for details on configuration directives.
+ '';
+ };
+
+ settings = lib.mkOption {
+ default = { };
+ description = ''
+ Options for firefly-iii configuration. Refer to
+ for
+ details on supported values. All