]> git.immae.eu Git - perso/Immae/Config/Nix.git/blobdiff - virtual/modules/websites/tools/mastodon/default.nix
Add mastodon service
[perso/Immae/Config/Nix.git] / virtual / modules / websites / tools / mastodon / default.nix
diff --git a/virtual/modules/websites/tools/mastodon/default.nix b/virtual/modules/websites/tools/mastodon/default.nix
new file mode 100644 (file)
index 0000000..1549ca9
--- /dev/null
@@ -0,0 +1,207 @@
+{ lib, pkgs, config, mylibs, ... }:
+let
+  mastodon = pkgs.callPackage ./mastodon.nix {
+    inherit (mylibs) fetchedGithub checkEnv;
+  };
+
+  cfg = config.services.myWebsites.tools.mastodon;
+in {
+  options.services.myWebsites.tools.mastodon = {
+    enable = lib.mkEnableOption "enable mastodon's website";
+  };
+
+  config = lib.mkIf cfg.enable {
+    # FIXME: Can we use dynamic users from systemd?
+    # nixos/modules/misc/ids.nix
+    ids.uids.mastodon = 399;
+    ids.gids.mastodon = 399;
+
+    users.users.mastodon = {
+      name = "mastodon";
+      uid = config.ids.uids.mastodon;
+      group = "mastodon";
+      description = "Mastodon user";
+      home = "${mastodon.railsRoot}";
+      useDefaultShell = true;
+    };
+
+    users.groups.mastodon.gid = config.ids.gids.mastodon;
+
+    systemd.services.mastodon-streaming = {
+      description = "Mastodon Streaming";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "mastodon-web.service" ];
+
+      environment.NODE_ENV = "production";
+      environment.SOCKET = mastodon.nodeSocket;
+
+      path = [ pkgs.nodejs pkgs.bashInteractive ];
+
+      script = ''
+        exec npm run start
+      '';
+
+      postStart = ''
+        while [ ! -S $SOCKET ]; do
+          sleep 0.5
+        done
+        chmod a+w $SOCKET
+      '';
+
+      postStop = ''
+        rm $SOCKET
+      '';
+
+      serviceConfig = {
+        User = "mastodon";
+        EnvironmentFile = mastodon.config;
+        PrivateTmp = true;
+        Restart = "always";
+        TimeoutSec = 15;
+        Type = "simple";
+        WorkingDirectory = mastodon.railsRoot;
+      };
+
+      unitConfig.RequiresMountsFor = mastodon.varDir;
+    };
+
+    systemd.services.mastodon-web = {
+      description = "Mastodon Web app";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      environment.RAILS_ENV = "production";
+      environment.SOCKET = mastodon.railsSocket;
+
+      path = [ pkgs.bundler ];
+
+      preStart = ''
+        bundle exec rails db:migrate
+      '';
+
+      script = ''
+        exec bundle exec puma -C config/puma.rb
+      '';
+
+      serviceConfig = {
+        User = "mastodon";
+        EnvironmentFile = mastodon.config;
+        PrivateTmp = true;
+        Restart = "always";
+        TimeoutSec = 15;
+        Type = "simple";
+        WorkingDirectory = mastodon.railsRoot;
+      };
+
+      unitConfig.RequiresMountsFor = mastodon.varDir;
+    };
+
+    systemd.services.mastodon-sidekiq = {
+      description = "Mastodon Sidekiq";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "mastodon-web.service" ];
+
+      environment.RAILS_ENV="production";
+      environment.DB_POOL="5";
+
+      path = [ pkgs.bundler ];
+
+      script = ''
+        exec bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
+      '';
+
+      serviceConfig = {
+        User = "mastodon";
+        EnvironmentFile = mastodon.config;
+        PrivateTmp = true;
+        Restart = "always";
+        TimeoutSec = 15;
+        Type = "simple";
+        WorkingDirectory = mastodon.railsRoot;
+      };
+
+      unitConfig.RequiresMountsFor = mastodon.varDir;
+    };
+
+    # FIXME: initial sync
+    system.activationScripts.mastodon = {
+      deps = [ "users" ];
+      text = ''
+      install -m 0755 -o mastodon -g mastodon -d ${mastodon.socketsDir}
+      install -m 0755 -o mastodon -g mastodon -d ${mastodon.varDir}
+      '';
+    };
+
+    services.myWebsites.tools.modules = [
+      "headers" "proxy" "proxy_wstunnel" "proxy_http" "proxy_balancer"
+      # FIXME: probably only one balancer method is needed:
+      "lbmethod_byrequests" "lbmethod_bytraffic" "lbmethod_bybusyness" "lbmethod_heartbeat"
+    ];
+    security.acme.certs."eldiron".extraDomains."mastodon.immae.eu" = null;
+    services.myWebsites.tools.vhostConfs.mastodon = {
+      certName    = "eldiron";
+      hosts       = ["mastodon.immae.eu" ];
+      root        = "${mastodon.railsRoot}/public/";
+      extraConfig = [ ''
+        Header always set Referrer-Policy "strict-origin-when-cross-origin"
+        Header always set Strict-Transport-Security "max-age=31536000"
+
+        <LocationMatch "^/(assets|avatars|emoji|headers|packs|sounds|system)>
+          Header always set Cache-Control "public, max-age=31536000, immutable"
+          Require all granted
+        </LocationMatch>
+
+        ProxyPreserveHost On
+        RequestHeader set X-Forwarded-Proto "https"
+
+        RewriteEngine On
+
+        ProxyPass /500.html !
+        ProxyPass /sw.js !
+        ProxyPass /embed.js !
+        ProxyPass /robots.txt !
+        ProxyPass /manifest.json !
+        ProxyPass /browserconfig.xml !
+        ProxyPass /mask-icon.svg !
+        ProxyPassMatch ^(/.*\.(png|ico|gif)$) !
+        ProxyPassMatch ^/(assets|avatars|emoji|headers|packs|sounds|system|.well-known/acme-challenge) !
+
+        ProxyPassMatch /api/v1/streaming/(.+)$ balancer://node_servers_http/api/v1/streaming/$1
+        ProxyPass /api/v1/streaming/ balancer://node_servers/
+        ProxyPassReverse /api/v1/streaming/ balancer://node_servers/
+        ProxyPass / balancer://puma_servers/
+        ProxyPassReverse / balancer://puma_servers/
+
+        <Proxy balancer://puma_servers>
+            BalancerMember unix://${mastodon.railsSocket}|http://
+        </Proxy>
+
+        <Proxy balancer://node_servers>
+            BalancerMember unix://${mastodon.nodeSocket}|ws://localhost
+        </Proxy>
+
+        <Proxy balancer://node_servers_http>
+            BalancerMember unix://${mastodon.nodeSocket}|http://localhost
+        </Proxy>
+
+        Alias /system ${mastodon.varDir}
+
+        <Directory ${mastodon.varDir}>
+          Require all granted
+          Options -MultiViews
+        </Directory>
+
+        <Directory ${mastodon.railsRoot}/public/>
+          Require all granted
+          Options -MultiViews +FollowSymlinks
+        </Directory>
+
+        ErrorDocument 500 /500.html
+        ErrorDocument 501 /500.html
+        ErrorDocument 502 /500.html
+        ErrorDocument 503 /500.html
+        ErrorDocument 504 /500.html
+      '' ];
+    };
+  };
+}