about summary refs log tree commit diff stats
path: root/modules/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'modules/nixos')
-rw-r--r--modules/nixos/default.nix1
-rw-r--r--modules/nixos/goatcounter.nix147
2 files changed, 148 insertions, 0 deletions
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix
index 1901177..fb2eb61 100644
--- a/modules/nixos/default.nix
+++ b/modules/nixos/default.nix
@@ -3,4 +3,5 @@
   #
   # my-module = ./my-module;
   laminar = ./laminar.nix;
+  goatcounter = ./goatcounter.nix;
 }
diff --git a/modules/nixos/goatcounter.nix b/modules/nixos/goatcounter.nix
new file mode 100644
index 0000000..2ba0e31
--- /dev/null
+++ b/modules/nixos/goatcounter.nix
@@ -0,0 +1,147 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  cfg = config.services.goatcounter;
+
+  inherit (lib)
+    optionalAttrs
+    mkEnableOption
+    mkPackageOption
+    mkOption
+    mkIf
+    types;
+
+  encodeParams = params: lib.concatStringsSep "&" (lib.mapAttrsToList (n: v: "${n}=${v}") params);
+  encoder = {
+    bool = n: v: lib.optionalString v "-${n}";
+    int = n: v: "-${n} ${toString v}";
+    list = n: v: "-${n} ${lib.concatStringsSep "," v}";
+    string = n: v: "-${n} ${v}";
+  };
+  mkArgs = args: lib.concatStringsSep " " (lib.mapAttrsToList (n: v: (encoder.${builtins.typeOf v} n v)) args);
+in
+{
+  options.services.goatcounter = {
+    enable = mkEnableOption "Easy web analytics. No tracking of personal data.";
+
+    user = mkOption {
+      type = types.str;
+      default = "goatcounter";
+      description = "User account under which goatcounter runs.";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "goatcounter";
+      description = "Group under which goatcounter runs.";
+    };
+
+    package = mkPackageOption pkgs "goatcounter" { };
+
+    homeDir = mkOption {
+      type = types.path;
+      default = "/var/lib/goatcounter";
+      description = "Home directory for goatcounter user.";
+    };
+
+    database = mkOption {
+      default = {
+        type = "sqlite";
+        file = "db/goatcounter.sqlite3";
+      };
+
+      type = types.submodule {
+        options = {
+          type = mkOption {
+            type = types.enum [ "sqlite" "postgresql" ];
+            default = "sqlite";
+            description = "Database engine to use.";
+          };
+
+          file = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = "(sqlite) database file to use.";
+          };
+
+          params = mkOption {
+            type = with types; attrsOf str;
+            default = { };
+            description = "Database connection parameters. See `goatcounter help db`";
+          };
+        };
+      };
+    };
+
+    listenAddress = mkOption {
+      type = types.str;
+      default = "*";
+      description = "Start the server on the specified address.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8581;
+      description = "Port for goatcounter to listen on.";
+    };
+
+
+    settings = mkOption {
+      type = types.submodule rec {
+        freeformType = types.anything;
+
+        options = {
+          tls =
+            let
+              values = (types.enum [ "http" "proxy" "tls" "rdr" "acme" ]);
+            in
+            mkOption {
+              type = with types; either str (nonEmptyListOf values);
+              default = [ "acme" "rdr" ];
+              description = "Whether and how to handle TLS connections. See `goatcounter help listen`";
+            };
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.goatcounter.settings = {
+      listen = lib.mkDefault "${cfg.listenAddress}:${toString cfg.port}";
+      db = lib.mkDefault "${cfg.database.type}+${cfg.database.file}?${encodeParams cfg.database.params}";
+    };
+
+    systemd.services.goatcounter = {
+      description = "Goatcounter web analytics";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/goatcounter serve ${mkArgs cfg.settings}";
+        User = cfg.user;
+        Group = cfg.group;
+        WorkingDirectory = cfg.homeDir;
+        ReadWritePaths = [ cfg.homeDir ];
+      } // optionalAttrs (cfg.port < 1024) {
+        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+      };
+    };
+
+    users.users = optionalAttrs (cfg.user == "goatcounter") {
+      goatcounter = {
+        inherit (cfg) group;
+        home = cfg.homeDir;
+        createHome = true;
+        isSystemUser = true;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "goatcounter") {
+      goatcounter = { };
+    };
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}