/ distodon / flake.nix
flake.nix
  1  {
  2    inputs = {
  3      nixpkgs.url = "github:NixOS/nixpkgs/release-23.11";
  4    };
  5  
  6    outputs = {
  7      self,
  8      nixpkgs,
  9    }: let
 10      inherit (nixpkgs) lib;
 11      defaultSystems = [
 12        "x86_64-linux"
 13        "x86_64-darwin"
 14        "aarch64-linux"
 15        "aarch64-darwin"
 16      ];
 17      eachDefaultSystem = f:
 18        builtins.listToAttrs (map (system: {
 19            name = system;
 20            value = f (import nixpkgs {inherit system;});
 21          })
 22          defaultSystems);
 23    in {
 24      packages = eachDefaultSystem (pkgs: let
 25        inherit (fromTOML (builtins.readFile ./Cargo.toml)) package;
 26      in {
 27        default = pkgs.rustPlatform.buildRustPackage {
 28          pname = package.name;
 29          version = package.version;
 30          src = lib.fileset.toSource {
 31            root = ./.;
 32            fileset = lib.fileset.unions [
 33              ./Cargo.toml
 34              ./Cargo.lock
 35              ./src
 36            ];
 37          };
 38          cargoLock.lockFile = ./Cargo.lock;
 39          doCheck = false;
 40        };
 41      });
 42  
 43      nixosModules.default = {
 44        config,
 45        lib,
 46        pkgs,
 47        ...
 48      }: {
 49        options.services.distodon = with lib; {
 50          enable = mkEnableOption "distodon";
 51          debug = mkOption {
 52            type = types.bool;
 53            default = false;
 54          };
 55          interval = mkOption {
 56            type = types.ints.positive;
 57            default = 600;
 58          };
 59          chunkSize = mkOption {
 60            type = types.ints.between 1 10;
 61            default = 1;
 62          };
 63          links = mkOption {
 64            type = types.listOf (types.submodule {
 65              options = {
 66                mastodonServerUrl = mkOption {
 67                  type = types.str;
 68                };
 69                mastodonUser = mkOption {
 70                  type = types.str;
 71                };
 72                webhookUrl = mkOption {
 73                  type = types.nullOr types.str;
 74                  default = null;
 75                };
 76                webhookUrlFile = mkOption {
 77                  type = types.nullOr types.path;
 78                  default = null;
 79                };
 80              };
 81            });
 82          };
 83        };
 84  
 85        config = let
 86          cfg = config.services.distodon;
 87          links = builtins.genList (i: rec {
 88            inherit i;
 89            link = builtins.elemAt cfg.links i;
 90            secretName = "webhook-url-${toString i}";
 91          }) (builtins.length cfg.links);
 92          linksWithSecrets = builtins.filter ({link, ...}: link.webhookUrlFile != null) links;
 93  
 94          configFile = (pkgs.formats.toml {}).generate "distodon-config.toml" {
 95            interval = cfg.interval;
 96            chunk_size = cfg.chunkSize;
 97            links = map (link:
 98              {
 99                mastodon_server_url = link.mastodonServerUrl;
100                mastodon_user = link.mastodonUser;
101              }
102              // (lib.optionalAttrs (link.webhookUrl != null) {
103                webhook_url = link.webhookUrl;
104              }))
105            cfg.links;
106          };
107        in
108          lib.mkIf cfg.enable {
109            assertions = lib.flatten (map ({
110                webhookUrl,
111                webhookUrlFile,
112                ...
113              }: [
114                {
115                  assertion = (webhookUrl != null) != (webhookUrlFile != null);
116                  message = "Exactly one of webhookUrl and webhookUrlFile must be set";
117                }
118              ])
119              cfg.links);
120  
121            systemd.services.distodon = {
122              wantedBy = ["multi-user.target"];
123              serviceConfig = {
124                User = "distodon";
125                Group = "distodon";
126                DynamicUser = true;
127                WorkingDirectory = "/run/distodon";
128                RuntimeDirectory = "distodon";
129                StateDirectory = "distodon";
130                LoadCredential = builtins.map ({
131                  link,
132                  secretName,
133                  ...
134                }: "${secretName}:${link.webhookUrlFile}")
135                linksWithSecrets;
136              };
137              environment = {
138                RUST_LOG =
139                  if cfg.debug
140                  then "debug"
141                  else "info";
142              };
143              preStart = ''
144                cd /run/distodon
145                rm -f config.toml data
146                ln -s /var/lib/distodon data
147                cp ${configFile} config.toml
148                chmod 600 config.toml
149  
150                ${builtins.concatStringsSep "\n" (map ({
151                    i,
152                    secretName,
153                    ...
154                  }: ''
155                    webhook_url=$(cat ''${CREDENTIALS_DIRECTORY}/${secretName})
156                    ${pkgs.yq}/bin/tomlq -i -t --argjson i ${toString i} --arg webhook_url "$webhook_url" '.links[$ARGS.named.i].webhook_url = $ARGS.named.webhook_url' config.toml
157                  '')
158                  linksWithSecrets)}
159              '';
160              script = ''
161                ${self.packages.${pkgs.system}.default}/bin/distodon
162              '';
163            };
164          };
165      };
166    };
167  }