summary refs log tree commit diff
path: root/fleet/modules/public-inbox.nix
diff options
context:
space:
mode:
Diffstat (limited to 'fleet/modules/public-inbox.nix')
-rw-r--r--fleet/modules/public-inbox.nix134
1 files changed, 134 insertions, 0 deletions
diff --git a/fleet/modules/public-inbox.nix b/fleet/modules/public-inbox.nix
new file mode 100644
index 0000000..a8aa06b
--- /dev/null
+++ b/fleet/modules/public-inbox.nix
@@ -0,0 +1,134 @@
+# SPDX-FileCopyrightText: V <v@unfathomable.blue>
+# SPDX-License-Identifier: OSL-3.0
+
+# TODO(V): Figure out what the coderepo/cgit stuff is about
+# TODO(V): Consider a different architecture to what we're currently using
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.public-inbox;
+
+  environment.PI_CONFIG = "${pkgs.writeText "public-inbox-config" (generators.toGitINI public-inbox-config)}";
+
+  # TODO(V): Port this to a Nix type
+  # $ng =~ m![^A-Za-z0-9/_\.\-\~\@\+\=:]! and
+  # 	die "--newsgroup `$ng' is not valid\n";
+  # ($ng =~ m!\A\.! || $ng =~ m!\.\z!) and
+  # 	die "--newsgroup `$ng' must not start or end with `.'\n";
+
+  public-inbox-config = recursiveUpdate {
+    publicinbox = mapAttrs (inbox: config: {
+      address = [ "${inbox}@${config.domain}" ];
+      url = "https://${config.domain}/${inbox}";  # TODO(V): Allow using a different location than this
+      inboxdir = "/var/lib/public-inbox/${inbox}.git";
+      inherit (config) watch;
+    }) cfg.inboxes;
+  } cfg.settings;
+
+  inboxOpts = {
+    options = {
+      description = mkOption {
+        description = "Description of the inbox.";
+        type = types.str;
+      };
+
+      domain = mkOption {
+        description = "Domain of the inbox.";
+        type = types.str;
+        example = "example.org";
+      };
+
+      watch = mkOption {
+        description = "Directory to watch for incoming mails in.";
+        type = types.str;
+      };
+    };
+  };
+in {
+  options.services.public-inbox = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+    };
+
+    inboxes = mkOption {
+      type = with types; attrsOf (submodule inboxOpts);
+      default = {};
+    };
+
+    settings = mkOption {
+      type = types.attrs;  # TODO: Use freeformType
+      default = {};
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.public-inbox = {
+      isSystemUser = true;
+      group = "public-inbox";
+
+      home = "/var/lib/public-inbox";
+      createHome = true;
+    };
+
+    users.groups.public-inbox = {};
+
+    systemd.services.public-inbox-init = {
+      description = "public-inbox mailing-list archive (initialization)";
+
+      wantedBy = [ "public-inbox-watch.service" "multi-user.target" ];
+      before = [ "public-inbox-watch.service" ];
+
+      # TODO(V): Add ${pi-config.inboxdir}/description to the reload conditions, since new descriptions don't get picked up after being updated.
+      script = concatStrings (mapAttrsToList (inbox: config: let pi-config = public-inbox-config.publicinbox.${inbox}; in ''
+        ${pkgs.public-inbox-init-lite}/bin/public-inbox-init-lite ${inbox} ${pi-config.inboxdir} ${head pi-config.address}
+        ln -sf ${pkgs.writeText "public-inbox-description" config.description} ${pi-config.inboxdir}/description
+        ${pkgs.public-inbox}/bin/public-inbox-index ${pi-config.inboxdir}
+      '') cfg.inboxes);
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        User = "public-inbox";
+      };
+    };
+
+    # FIXME(V): Currently, if new mail appears in the mlmmj archive while public-inbox-watch is not running, it never gets added!
+    # This directly contradicts what the manpage states: "Upon startup, it scans the mailbox for new messages to be imported while it was not running."
+    # This might be a bug in public-inbox!
+    # We currently save a copy of all mails received by mlmmj to ensure none are lost if this happens.
+    systemd.services.public-inbox-watch = {
+      description = "public-inbox mailing-list archive (incoming mail monitor)";
+      wantedBy = [ "multi-user.target" ];
+      inherit environment;
+      serviceConfig = {
+        ExecStart = "${pkgs.public-inbox}/bin/public-inbox-watch";
+        # TODO(V): ExecReload
+        User = "public-inbox";
+        SupplementaryGroups = [ "mlmmj" ];
+      };
+    };
+
+    # TODO(V): Consider using public-inbox.cgi + cgiserver instead?
+    systemd.sockets.public-inbox-httpd = {
+      description = "public-inbox mailing-list archive (web server)";
+      listenStreams = [ "/run/public-inbox/httpd.sock" ];
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.services.public-inbox-httpd = {
+      description = "public-inbox mailing-list archive (web server)";
+      inherit environment;
+      serviceConfig = {
+        ExecStart = "${pkgs.public-inbox}/bin/public-inbox-httpd";
+        DynamicUser = true;
+        SupplementaryGroups = [ "public-inbox" ];
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ V ];
+}