diff options
Diffstat (limited to 'nixos/modules')
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/ihatemoney/default.nix | 141 | ||||
-rw-r--r-- | nixos/modules/services/web-servers/uwsgi.nix | 19 |
3 files changed, 154 insertions, 7 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 5b7f391ed5a50..16d4b733fdb8e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -793,6 +793,7 @@ ./services/web-apps/frab.nix ./services/web-apps/icingaweb2/icingaweb2.nix ./services/web-apps/icingaweb2/module-monitoring.nix + ./services/web-apps/ihatemoney ./services/web-apps/limesurvey.nix ./services/web-apps/mattermost.nix ./services/web-apps/mediawiki.nix diff --git a/nixos/modules/services/web-apps/ihatemoney/default.nix b/nixos/modules/services/web-apps/ihatemoney/default.nix new file mode 100644 index 0000000000000..68769ac8c0316 --- /dev/null +++ b/nixos/modules/services/web-apps/ihatemoney/default.nix @@ -0,0 +1,141 @@ +{ config, pkgs, lib, ... }: +with lib; +let + cfg = config.services.ihatemoney; + user = "ihatemoney"; + group = "ihatemoney"; + db = "ihatemoney"; + python3 = config.services.uwsgi.package.python3; + pkg = python3.pkgs.ihatemoney; + toBool = x: if x then "True" else "False"; + configFile = pkgs.writeText "ihatemoney.cfg" '' + from secrets import token_hex + # load a persistent secret key + SECRET_KEY_FILE = "/var/lib/ihatemoney/secret_key" + SECRET_KEY = "" + try: + with open(SECRET_KEY_FILE) as f: + SECRET_KEY = f.read() + except FileNotFoundError: + pass + if not SECRET_KEY: + print("ihatemoney: generating a new secret key") + SECRET_KEY = token_hex(50) + with open(SECRET_KEY_FILE, "w") as f: + f.write(SECRET_KEY) + del token_hex + del SECRET_KEY_FILE + + # "normal" configuration + DEBUG = False + SQLALCHEMY_DATABASE_URI = '${ + if cfg.backend == "sqlite" + then "sqlite:////var/lib/ihatemoney/ihatemoney.sqlite" + else "postgresql:///${db}"}' + SQLALCHEMY_TRACK_MODIFICATIONS = False + MAIL_DEFAULT_SENDER = ("${cfg.defaultSender.name}", "${cfg.defaultSender.email}") + ACTIVATE_DEMO_PROJECT = ${toBool cfg.enableDemoProject} + ADMIN_PASSWORD = "${toString cfg.adminHashedPassword /*toString null == ""*/}" + ALLOW_PUBLIC_PROJECT_CREATION = ${toBool cfg.enablePublicProjectCreation} + ACTIVATE_ADMIN_DASHBOARD = ${toBool cfg.enableAdminDashboard} + + ${cfg.extraConfig} + ''; +in + { + options.services.ihatemoney = { + enable = mkEnableOption "ihatemoney webapp. Note that this will set uwsgi to emperor mode running as root"; + backend = mkOption { + type = types.enum [ "sqlite" "postgresql" ]; + default = "sqlite"; + description = '' + The database engine to use for ihatemoney. + If <literal>postgresql</literal> is selected, then a database called + <literal>${db}</literal> will be created. If you disable this option, + it will however not be removed. + ''; + }; + adminHashedPassword = mkOption { + type = types.nullOr types.str; + default = null; + description = "The hashed password of the administrator. To obtain it, run <literal>ihatemoney generate_password_hash</literal>"; + }; + uwsgiConfig = mkOption { + type = types.attrs; + example = { + http = ":8000"; + }; + description = "Additionnal configuration of the UWSGI vassal running ihatemoney. It should notably specify on which interfaces and ports the vassal should listen."; + }; + defaultSender = { + name = mkOption { + type = types.str; + default = "Budget manager"; + description = "The display name of the sender of ihatemoney emails"; + }; + email = mkOption { + type = types.str; + default = "ihatemoney@${config.networking.hostName}"; + description = "The email of the sender of ihatemoney emails"; + }; + }; + enableDemoProject = mkEnableOption "access to the demo project in ihatemoney"; + enablePublicProjectCreation = mkEnableOption "permission to create projects in ihatemoney by anyone"; + enableAdminDashboard = mkEnableOption "ihatemoney admin dashboard"; + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Extra configuration appended to ihatemoney's configuration file. It is a python file, so pay attention to indentation."; + }; + }; + config = mkIf cfg.enable { + services.postgresql = mkIf (cfg.backend == "postgresql") { + enable = true; + ensureDatabases = [ db ]; + ensureUsers = [ { + name = user; + ensurePermissions = { + "DATABASE ${db}" = "ALL PRIVILEGES"; + }; + } ]; + }; + systemd.services.postgresql = mkIf (cfg.backend == "postgresql") { + wantedBy = [ "uwsgi.service" ]; + before = [ "uwsgi.service" ]; + }; + systemd.tmpfiles.rules = [ + "d /var/lib/ihatemoney 770 ${user} ${group}" + ]; + users = { + users.${user} = { + isSystemUser = true; + inherit group; + }; + groups.${group} = {}; + }; + services.uwsgi = { + enable = true; + plugins = [ "python3" ]; + # the vassal needs to be able to setuid + user = "root"; + group = "root"; + instance = { + type = "emperor"; + vassals.ihatemoney = { + type = "normal"; + strict = true; + uid = user; + gid = group; + # apparently flask uses threads: https://github.com/spiral-project/ihatemoney/commit/c7815e48781b6d3a457eaff1808d179402558f8c + enable-threads = true; + module = "wsgi:application"; + chdir = "${pkg}/${pkg.pythonModule.sitePackages}/ihatemoney"; + env = [ "IHATEMONEY_SETTINGS_FILE_PATH=${configFile}" ]; + pythonPackages = self: [ self.ihatemoney ]; + } // cfg.uwsgiConfig; + }; + }; + }; + } + + diff --git a/nixos/modules/services/web-servers/uwsgi.nix b/nixos/modules/services/web-servers/uwsgi.nix index af70f32f32d0f..66537b29cd21d 100644 --- a/nixos/modules/services/web-servers/uwsgi.nix +++ b/nixos/modules/services/web-servers/uwsgi.nix @@ -5,10 +5,6 @@ with lib; let cfg = config.services.uwsgi; - uwsgi = pkgs.uwsgi.override { - plugins = cfg.plugins; - }; - buildCfg = name: c: let plugins = @@ -23,8 +19,8 @@ let python = if hasPython2 && hasPython3 then throw "`plugins` attribute in UWSGI configuration shouldn't contain both python2 and python3" - else if hasPython2 then uwsgi.python2 - else if hasPython3 then uwsgi.python3 + else if hasPython2 then cfg.package.python2 + else if hasPython3 then cfg.package.python3 else null; pythonEnv = python.withPackages (c.pythonPackages or (self: [])); @@ -77,6 +73,11 @@ in { description = "Where uWSGI communication sockets can live"; }; + package = mkOption { + type = types.package; + internal = true; + }; + instance = mkOption { type = types.attrs; default = { @@ -138,7 +139,7 @@ in { ''; serviceConfig = { Type = "notify"; - ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${buildCfg "server" cfg.instance}/server.json"; + ExecStart = "${cfg.package}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${buildCfg "server" cfg.instance}/server.json"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; NotifyAccess = "main"; @@ -156,5 +157,9 @@ in { { name = "uwsgi"; gid = config.ids.gids.uwsgi; }); + + services.uwsgi.package = pkgs.uwsgi.override { + inherit (cfg) plugins; + }; }; } |