From 7532dbaa32f661f8f8743e24629516f853e39f9e Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 27 Jun 2023 12:30:06 +0200 Subject: [PATCH] nixos/anuko-time-tracker: init --- .../manual/release-notes/rl-2311.section.md | 2 + nixos/modules/module-list.nix | 1 + .../services/web-apps/anuko-time-tracker.nix | 354 ++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/anuko-time-tracker.nix | 17 + 5 files changed, 375 insertions(+) create mode 100644 nixos/modules/services/web-apps/anuko-time-tracker.nix create mode 100644 nixos/tests/anuko-time-tracker.nix diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index 00d52376b18a..70613c392a37 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -16,6 +16,8 @@ - [GoToSocial](https://gotosocial.org/), an ActivityPub social network server, written in Golang. Available as [services.gotosocial](#opt-services.gotosocial.enable). +- [Anuko Time Tracker](https://github.com/anuko/timetracker), a simple, easy to use, open source time tracking system. Available as [services.anuko-time-tracker](#opt-services.anuko-time-tracker.enable). + - [sitespeed-io](https://sitespeed.io), a tool that can generate metrics (timings, diagnostics) for websites. Available as [services.sitespeed-io](#opt-services.sitespeed-io.enable). - [Apache Guacamole](https://guacamole.apache.org/), a cross-platform, clientless remote desktop gateway. Available as [services.guacamole-server](#opt-services.guacamole-server.enable) and [services.guacamole-client](#opt-services.guacamole-client.enable) services. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 43fcc68ded42..ebf52804d4ec 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1168,6 +1168,7 @@ ./services/wayland/cage.nix ./services/web-apps/akkoma.nix ./services/web-apps/alps.nix + ./services/web-apps/anuko-time-tracker.nix ./services/web-apps/atlassian/confluence.nix ./services/web-apps/atlassian/crowd.nix ./services/web-apps/atlassian/jira.nix diff --git a/nixos/modules/services/web-apps/anuko-time-tracker.nix b/nixos/modules/services/web-apps/anuko-time-tracker.nix new file mode 100644 index 000000000000..c50a0328e34d --- /dev/null +++ b/nixos/modules/services/web-apps/anuko-time-tracker.nix @@ -0,0 +1,354 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.services.anuko-time-tracker; + configFile = let + smtpPassword = if cfg.settings.email.smtpPasswordFile == null + then "''" + else "trim(file_get_contents('${cfg.settings.email.smtpPasswordFile}'))"; + + in pkgs.writeText "config.php" '' + "; + }; + + mode = lib.mkOption { + type = lib.types.str; + description = lib.mdDoc "Mail sending mode. Can be 'mail' or 'smtp'."; + default = "smtp"; + }; + + smtpHost = lib.mkOption { + type = lib.types.str; + description = lib.mdDoc "MTA hostname."; + default = "localhost"; + }; + + smtpPort = lib.mkOption { + type = lib.types.int; + description = lib.mdDoc "MTA port."; + default = 25; + }; + + smtpUser = lib.mkOption { + type = lib.types.str; + description = lib.mdDoc "MTA authentication username."; + default = ""; + }; + + smtpAuth = lib.mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc "MTA requires authentication."; + }; + + smtpPasswordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/anuko-time-tracker/secrets/smtp-password"; + description = lib.mdDoc '' + Path to file containing the MTA authentication password. + ''; + }; + + smtpDebug = lib.mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc "Debug mail sending."; + }; + }; + + defaultLanguage = lib.mkOption { + type = lib.types.str; + description = lib.mdDoc '' + Defines Anuko Time Tracker default language. It is used on Time Tracker login page. + After login, a language set for user group is used. + Empty string means the language is defined by user browser. + ''; + default = ""; + example = "nl"; + }; + + defaultCurrency = lib.mkOption { + type = lib.types.str; + description = lib.mdDoc '' + Defines a default currency symbol for new groups. + Use €, £, a more specific dollar like US$, CAD, etc. + ''; + default = "$"; + example = "€"; + }; + + exportDecimalDuration = lib.mkOption { + type = lib.types.bool; + default = true; + description = lib.mdDoc '' + Defines whether time duration values are decimal in CSV and XML data + exports (1.25 vs 1:15). + ''; + }; + + reportFooter = lib.mkOption { + type = lib.types.bool; + default = true; + description = lib.mdDoc "Defines whether to use a footer on reports."; + }; + }; + }; + + config = lib.mkIf cfg.enable { + + assertions = [ + { + assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; + message = '' + cannot be specified if + is set to true. + ''; + } + { + assertion = cfg.settings.email.smtpAuth -> (cfg.settings.email.smtpPasswordFile != null); + message = '' + needs to be set if + is enabled. + ''; + } + ]; + + services.phpfpm = { + pools.anuko-time-tracker = { + inherit (cfg) user; + group = config.services.nginx.group; + settings = { + "listen.owner" = config.services.nginx.user; + "listen.group" = config.services.nginx.group; + } // cfg.poolConfig; + }; + }; + + services.nginx = lib.mkIf (cfg.virtualHost != null) { + enable = true; + virtualHosts = { + "${cfg.virtualHost}" = { + root = lib.mkForce "${package}"; + locations."/".index = "index.php"; + locations."~ [^/]\\.php(/|$)" = { + extraConfig = '' + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_pass unix:${config.services.phpfpm.pools.anuko-time-tracker.socket}; + ''; + }; + }; + }; + }; + + services.mysql = lib.mkIf cfg.database.createLocally { + enable = lib.mkDefault true; + package = lib.mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [{ + name = cfg.database.user; + ensurePermissions = { + "${cfg.database.name}.*" = "ALL PRIVILEGES"; + }; + }]; + }; + + systemd = { + services = { + anuko-time-tracker-setup-database = lib.mkIf cfg.database.createLocally { + description = "Set up Anuko Time Tracker database"; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + wantedBy = [ "phpfpm-anuko-time-tracker.service" ]; + after = [ "mysql.service" ]; + script = + let + mysql = "${config.services.mysql.package}/bin/mysql"; + in + '' + if [ ! -f ${cfg.dataDir}/.dbexists ]; then + # Load database schema provided with package + ${mysql} ${cfg.database.name} < ${cfg.package}/mysql.sql + + touch ${cfg.dataDir}/.dbexists + fi + ''; + }; + }; + tmpfiles.rules = [ + "d ${cfg.dataDir} 0750 ${cfg.user} ${config.services.nginx.group} -" + "d ${cfg.dataDir}/templates_c 0750 ${cfg.user} ${config.services.nginx.group} -" + ]; + }; + + users.users."${cfg.user}" = { + isSystemUser = true; + group = config.services.nginx.group; + }; + }; + + meta.maintainers = with lib.maintainers; [ michaelshmitty ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 6e9c893c809e..068e3ce7b248 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -107,6 +107,7 @@ in { allTerminfo = handleTest ./all-terminfo.nix {}; alps = handleTest ./alps.nix {}; amazon-init-shell = handleTest ./amazon-init-shell.nix {}; + anuko-time-tracker = handleTest ./anuko-time-tracker.nix {}; apcupsd = handleTest ./apcupsd.nix {}; apfs = runTest ./apfs.nix; apparmor = handleTest ./apparmor.nix {}; diff --git a/nixos/tests/anuko-time-tracker.nix b/nixos/tests/anuko-time-tracker.nix new file mode 100644 index 000000000000..18c3bf5cf695 --- /dev/null +++ b/nixos/tests/anuko-time-tracker.nix @@ -0,0 +1,17 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "anuko-time-tracker"; + meta = { + maintainers = with pkgs.lib.maintainers; [ michaelshmitty ]; + }; + nodes = { + machine = { + services.anuko-time-tracker.enable = true; + }; + }; + testScript = '' + start_all() + machine.wait_for_unit("phpfpm-anuko-time-tracker") + machine.wait_for_open_port(80); + machine.wait_until_succeeds("curl -s --fail -L http://localhost/time.php | grep 'Anuko Time Tracker'") + ''; +})