···
1
+
{ pkgs, config, lib, ... }:
4
+
inherit (lib) optionalString mkDefault mkIf mkOption mkEnableOption literalExpression;
5
+
inherit (lib.types) nullOr attrsOf oneOf str int bool path package enum submodule;
6
+
inherit (lib.strings) concatMapStringsSep removePrefix toShellVars removeSuffix hasSuffix;
7
+
inherit (lib.attrsets) attrValues genAttrs filterAttrs mapAttrs' nameValuePair;
8
+
inherit (builtins) isInt isString toString typeOf;
10
+
cfg = config.services.firefly-iii;
15
+
defaultUser = "firefly-iii";
16
+
defaultGroup = "firefly-iii";
18
+
artisan = "${cfg.package}/artisan";
20
+
env-file-values = mapAttrs' (n: v: nameValuePair (removeSuffix "_FILE" n) v)
21
+
(filterAttrs (n: v: hasSuffix "_FILE" n) cfg.settings);
22
+
env-nonfile-values = filterAttrs (n: v: ! hasSuffix "_FILE" n) cfg.settings;
24
+
envfile = pkgs.writeText "firefly-iii-env" ''
25
+
${toShellVars env-file-values}
26
+
${toShellVars env-nonfile-values}
30
+
cp --no-preserve=mode ${envfile} /tmp/firefly-iii-env
31
+
${concatMapStringsSep "\n"
32
+
(n: "${pkgs.replace-secret}/bin/replace-secret ${n} ${n} /tmp/firefly-iii-env")
33
+
(attrValues env-file-values)}
35
+
. /tmp/firefly-iii-env
39
+
firefly-iii-maintenance = pkgs.writeShellScript "firefly-iii-maintenance.sh" ''
42
+
${optionalString (cfg.settings.DB_CONNECTION == "sqlite")
43
+
"touch ${cfg.dataDir}/storage/database/database.sqlite"}
44
+
${artisan} migrate --seed --no-interaction --force
45
+
${artisan} firefly-iii:decrypt-all
46
+
${artisan} firefly-iii:upgrade-database
47
+
${artisan} firefly-iii:correct-database
48
+
${artisan} firefly-iii:report-integrity
49
+
${artisan} firefly-iii:laravel-passport-keys
50
+
${artisan} cache:clear
52
+
mv /tmp/firefly-iii-env /run/phpfpm/firefly-iii-env
55
+
commonServiceConfig = {
59
+
StateDirectory = "${removePrefix "/var/lib/" cfg.dataDir}";
60
+
WorkingDirectory = cfg.package;
62
+
PrivateDevices = true;
63
+
CapabilityBoundingSet = "";
64
+
AmbientCapabilities = "";
65
+
ProtectSystem = "strict";
66
+
ProtectKernelTunables = true;
67
+
ProtectKernelModules = true;
68
+
ProtectControlGroups = true;
69
+
ProtectClock = true;
70
+
ProtectHostname = true;
71
+
ProtectHome = "tmpfs";
72
+
ProtectKernelLogs = true;
73
+
ProtectProc = "invisible";
75
+
PrivateNetwork = false;
76
+
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
77
+
SystemCallArchitectures = "native";
78
+
SystemCallFilter = [
79
+
"@system-service @resources"
80
+
"~@obsolete @privileged"
82
+
RestrictSUIDSGID = true;
84
+
NoNewPrivileges = true;
85
+
RestrictRealtime = true;
86
+
RestrictNamespaces = true;
87
+
LockPersonality = true;
88
+
PrivateUsers = true;
93
+
options.services.firefly-iii = {
95
+
enable = mkEnableOption "Firefly III: A free and open source personal finance manager";
99
+
default = defaultUser;
100
+
description = "User account under which firefly-iii runs.";
105
+
default = if cfg.enableNginx then "nginx" else defaultGroup;
106
+
defaultText = "If `services.firefly-iii.enableNginx` is true then `nginx` else ${defaultGroup}";
108
+
Group under which firefly-iii runs. It is best to set this to the group
109
+
of whatever webserver is being used as the frontend.
113
+
dataDir = mkOption {
115
+
default = "/var/lib/firefly-iii";
117
+
The place where firefly-iii stores its state.
121
+
package = mkOption {
123
+
default = pkgs.firefly-iii;
124
+
defaultText = literalExpression "pkgs.firefly-iii";
126
+
The firefly-iii package served by php-fpm and the webserver of choice.
127
+
This option can be used to point the webserver to the correct root. It
128
+
may also be used to set the package to a different version, say a
129
+
development version.
131
+
apply = firefly-iii : firefly-iii.override (prev: {
132
+
dataDir = cfg.dataDir;
136
+
enableNginx = mkOption {
140
+
Whether to enable nginx or not. If enabled, an nginx virtual host will
141
+
be created for access to firefly-iii. If not enabled, then you may use
142
+
`''${config.services.firefly-iii.package}` as your document root in
143
+
whichever webserver you wish to setup.
147
+
virtualHost = mkOption {
150
+
The hostname at which you wish firefly-iii to be served. If you have
151
+
enabled nginx using `services.firefly-iii.enableNginx` then this will
156
+
poolConfig = mkOption {
157
+
type = attrsOf (oneOf [ str int bool ]);
160
+
"pm.max_children" = 32;
161
+
"pm.start_servers" = 2;
162
+
"pm.min_spare_servers" = 2;
163
+
"pm.max_spare_servers" = 4;
164
+
"pm.max_requests" = 500;
167
+
Options for the Firefly III PHP pool. See the documentation on <literal>php-fpm.conf</literal>
168
+
for details on configuration directives.
172
+
settings = mkOption {
174
+
Options for firefly-iii configuration. Refer to
175
+
<https://github.com/firefly-iii/firefly-iii/blob/main/.env.example> for
176
+
details on supported values. All <option>_FILE values supported by
177
+
upstream are supported here.
179
+
APP_URL will be set by `services.firefly-iii.virtualHost`, do not
182
+
example = literalExpression ''
184
+
APP_ENV = "production";
185
+
APP_KEY_FILE = "/var/secrets/firefly-iii-app-key.txt";
186
+
SITE_OWNER = "mail@example.com";
187
+
DB_CONNECTION = "mysql";
190
+
DB_DATABASE = "firefly";
191
+
DB_USERNAME = "firefly";
192
+
DB_PASSWORD_FILE = "/var/secrets/firefly-iii-mysql-password.txt;
197
+
freeformType = attrsOf (oneOf [str int bool]);
199
+
DB_CONNECTION = mkOption {
200
+
type = enum [ "sqlite" "pgsql" "mysql" ];
201
+
default = "sqlite";
204
+
The type of database you wish to use. Can be one of "sqlite",
205
+
"mysql" or "pgsql".
208
+
APP_ENV = mkOption {
209
+
type = enum [ "local" "production" "testing" ];
211
+
example = "production";
213
+
The app environment. It is recommended to keep this at "local".
214
+
Possible values are "local", "production" and "testing"
217
+
DB_PORT = mkOption {
219
+
default = if cfg.settings.DB_CONNECTION == "sqlite" then null
220
+
else if cfg.settings.DB_CONNECTION == "mysql" then 3306
223
+
`null` if DB_CONNECTION is "sqlite", `3306` if "mysql", `5432` if "pgsql"
226
+
The port your database is listening at. sqlite does not require
227
+
this value to be filled.
230
+
APP_KEY_FILE = mkOption {
233
+
The path to your appkey. The file should contain a 32 character
234
+
random app key. This may be set using `echo "base64:$(head -c 32
235
+
/dev/urandom | base64)" > /path/to/key-file`.
243
+
config = mkIf cfg.enable {
245
+
services.firefly-iii = {
247
+
APP_URL = cfg.virtualHost;
251
+
services.phpfpm.pools.firefly-iii = {
252
+
inherit user group;
253
+
phpPackage = cfg.package.phpPackage;
258
+
"listen.mode" = "0660";
259
+
"listen.owner" = user;
260
+
"listen.group" = group;
261
+
"clear_env" = "no";
262
+
} // cfg.poolConfig;
265
+
systemd.services.phpfpm-firefly-iii.serviceConfig = {
266
+
EnvironmentFile = "/run/phpfpm/firefly-iii-env";
267
+
ExecStartPost = "${pkgs.coreutils}/bin/rm /run/phpfpm/firefly-iii-env";
270
+
systemd.services.firefly-iii-setup = {
271
+
requiredBy = [ "phpfpm-firefly-iii.service" ];
272
+
before = [ "phpfpm-firefly-iii.service" ];
274
+
ExecStart = firefly-iii-maintenance;
275
+
RuntimeDirectory = "phpfpm";
276
+
RuntimeDirectoryPreserve = true;
277
+
} // commonServiceConfig;
278
+
unitConfig.JoinsNamespaceOf = "phpfpm-firefly-iii.service";
281
+
systemd.services.firefly-iii-cron = {
282
+
description = "Daily Firefly III cron job";
285
+
${artisan} firefly-iii:cron
287
+
serviceConfig = commonServiceConfig;
290
+
systemd.timers.firefly-iii-cron = {
291
+
description = "Trigger Firefly Cron";
293
+
OnCalendar = "Daily";
294
+
RandomizedDelaySec = "1800s";
297
+
wantedBy = [ "timers.target" ];
300
+
services.nginx = mkIf cfg.enableNginx {
302
+
recommendedTlsSettings = mkDefault true;
303
+
recommendedOptimisation = mkDefault true;
304
+
recommendedGzipSettings = mkDefault true;
305
+
virtualHosts.${cfg.virtualHost} = {
306
+
root = "${cfg.package}/public";
309
+
tryFiles = "$uri $uri/ /index.php?$query_string";
310
+
index = "index.php";
317
+
include ${config.services.nginx.package}/conf/fastcgi_params ;
318
+
fastcgi_param SCRIPT_FILENAME $request_filename;
319
+
fastcgi_param modHeadersAvailable true; #Avoid sending the security headers twice
320
+
fastcgi_pass unix:${config.services.phpfpm.pools.firefly-iii.socket};
327
+
systemd.tmpfiles.settings."10-firefly-iii" = genAttrs [
328
+
"${cfg.dataDir}/storage"
329
+
"${cfg.dataDir}/storage/app"
330
+
"${cfg.dataDir}/storage/database"
331
+
"${cfg.dataDir}/storage/export"
332
+
"${cfg.dataDir}/storage/framework"
333
+
"${cfg.dataDir}/storage/framework/cache"
334
+
"${cfg.dataDir}/storage/framework/sessions"
335
+
"${cfg.dataDir}/storage/framework/views"
336
+
"${cfg.dataDir}/storage/logs"
337
+
"${cfg.dataDir}/storage/upload"
338
+
"${cfg.dataDir}/cache"
346
+
"${cfg.dataDir}".d = {
354
+
users = mkIf (user == defaultUser) {
356
+
description = "Firefly-iii service user";
358
+
isSystemUser = true;
359
+
home = cfg.dataDir;
362
+
groups = mkIf (group == defaultGroup) {
363
+
${defaultGroup} = {};