at 23.11-pre 6.4 kB view raw
1{ lib, pkgs, config, ... }: 2with lib; 3let 4 cfg = config.services.lemmy; 5 settingsFormat = pkgs.formats.json { }; 6in 7{ 8 meta.maintainers = with maintainers; [ happysalada ]; 9 meta.doc = ./lemmy.md; 10 11 imports = [ 12 (mkRemovedOptionModule [ "services" "lemmy" "jwtSecretPath" ] "As of v0.13.0, Lemmy auto-generates the JWT secret.") 13 ]; 14 15 options.services.lemmy = { 16 17 enable = mkEnableOption (lib.mdDoc "lemmy a federated alternative to reddit in rust"); 18 19 ui = { 20 port = mkOption { 21 type = types.port; 22 default = 1234; 23 description = lib.mdDoc "Port where lemmy-ui should listen for incoming requests."; 24 }; 25 }; 26 27 caddy.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the caddy reverse proxy"); 28 29 database.createLocally = mkEnableOption (lib.mdDoc "creation of database on the instance"); 30 31 settings = mkOption { 32 default = { }; 33 description = lib.mdDoc "Lemmy configuration"; 34 35 type = types.submodule { 36 freeformType = settingsFormat.type; 37 38 options.hostname = mkOption { 39 type = types.str; 40 default = null; 41 description = lib.mdDoc "The domain name of your instance (eg 'lemmy.ml')."; 42 }; 43 44 options.port = mkOption { 45 type = types.port; 46 default = 8536; 47 description = lib.mdDoc "Port where lemmy should listen for incoming requests."; 48 }; 49 50 options.federation = { 51 enabled = mkEnableOption (lib.mdDoc "activitypub federation"); 52 }; 53 54 options.captcha = { 55 enabled = mkOption { 56 type = types.bool; 57 default = true; 58 description = lib.mdDoc "Enable Captcha."; 59 }; 60 difficulty = mkOption { 61 type = types.enum [ "easy" "medium" "hard" ]; 62 default = "medium"; 63 description = lib.mdDoc "The difficultly of the captcha to solve."; 64 }; 65 }; 66 }; 67 }; 68 69 }; 70 71 config = 72 lib.mkIf cfg.enable { 73 services.lemmy.settings = (mapAttrs (name: mkDefault) 74 { 75 bind = "127.0.0.1"; 76 tls_enabled = true; 77 pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}"; 78 actor_name_max_length = 20; 79 80 rate_limit.message = 180; 81 rate_limit.message_per_second = 60; 82 rate_limit.post = 6; 83 rate_limit.post_per_second = 600; 84 rate_limit.register = 3; 85 rate_limit.register_per_second = 3600; 86 rate_limit.image = 6; 87 rate_limit.image_per_second = 3600; 88 } // { 89 database = mapAttrs (name: mkDefault) { 90 user = "lemmy"; 91 host = "/run/postgresql"; 92 port = 5432; 93 database = "lemmy"; 94 pool_size = 5; 95 }; 96 }); 97 98 services.postgresql = mkIf cfg.database.createLocally { 99 enable = true; 100 ensureDatabases = [ cfg.settings.database.database ]; 101 ensureUsers = [{ 102 name = cfg.settings.database.user; 103 ensurePermissions."DATABASE ${cfg.settings.database.database}" = "ALL PRIVILEGES"; 104 }]; 105 }; 106 107 services.pict-rs.enable = true; 108 109 services.caddy = mkIf cfg.caddy.enable { 110 enable = mkDefault true; 111 virtualHosts."${cfg.settings.hostname}" = { 112 extraConfig = '' 113 handle_path /static/* { 114 root * ${pkgs.lemmy-ui}/dist 115 file_server 116 } 117 @for_backend { 118 path /api/* /pictrs/* /feeds/* /nodeinfo/* 119 } 120 handle @for_backend { 121 reverse_proxy 127.0.0.1:${toString cfg.settings.port} 122 } 123 @post { 124 method POST 125 } 126 handle @post { 127 reverse_proxy 127.0.0.1:${toString cfg.settings.port} 128 } 129 @jsonld { 130 header Accept "application/activity+json" 131 header Accept "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" 132 } 133 handle @jsonld { 134 reverse_proxy 127.0.0.1:${toString cfg.settings.port} 135 } 136 handle { 137 reverse_proxy 127.0.0.1:${toString cfg.ui.port} 138 } 139 ''; 140 }; 141 }; 142 143 assertions = [{ 144 assertion = cfg.database.createLocally -> cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql"; 145 message = "if you want to create the database locally, you need to use a local database"; 146 }]; 147 148 systemd.services.lemmy = { 149 description = "Lemmy server"; 150 151 environment = { 152 LEMMY_CONFIG_LOCATION = "/run/lemmy/config.hjson"; 153 154 # Verify how this is used, and don't put the password in the nix store 155 LEMMY_DATABASE_URL = with cfg.settings.database;"postgres:///${database}?host=${host}"; 156 }; 157 158 documentation = [ 159 "https://join-lemmy.org/docs/en/admins/from_scratch.html" 160 "https://join-lemmy.org/docs/en/" 161 ]; 162 163 wantedBy = [ "multi-user.target" ]; 164 165 after = [ "pict-rs.service" ] ++ lib.optionals cfg.database.createLocally [ "postgresql.service" ]; 166 167 requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ]; 168 169 serviceConfig = { 170 DynamicUser = true; 171 RuntimeDirectory = "lemmy"; 172 ExecStartPre = "${pkgs.coreutils}/bin/install -m 600 ${settingsFormat.generate "config.hjson" cfg.settings} /run/lemmy/config.hjson"; 173 ExecStart = "${pkgs.lemmy-server}/bin/lemmy_server"; 174 }; 175 }; 176 177 systemd.services.lemmy-ui = { 178 description = "Lemmy ui"; 179 180 environment = { 181 LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; 182 LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; 183 LEMMY_EXTERNAL_HOST = cfg.settings.hostname; 184 LEMMY_HTTPS = "false"; 185 }; 186 187 documentation = [ 188 "https://join-lemmy.org/docs/en/admins/from_scratch.html" 189 "https://join-lemmy.org/docs/en/" 190 ]; 191 192 wantedBy = [ "multi-user.target" ]; 193 194 after = [ "lemmy.service" ]; 195 196 requires = [ "lemmy.service" ]; 197 198 serviceConfig = { 199 DynamicUser = true; 200 WorkingDirectory = "${pkgs.lemmy-ui}"; 201 ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.lemmy-ui}/dist/js/server.js"; 202 }; 203 }; 204 }; 205 206}