at 22.05-pre 7.6 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 # Don't edit the docbook xml directly, edit the md and generate it: 10 # `pandoc lemmy.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > lemmy.xml` 11 meta.doc = ./lemmy.xml; 12 13 options.services.lemmy = { 14 15 enable = mkEnableOption "lemmy a federated alternative to reddit in rust"; 16 17 jwtSecretPath = mkOption { 18 type = types.path; 19 description = "Path to read the jwt secret from."; 20 }; 21 22 ui = { 23 port = mkOption { 24 type = types.port; 25 default = 1234; 26 description = "Port where lemmy-ui should listen for incoming requests."; 27 }; 28 }; 29 30 caddy.enable = mkEnableOption "exposing lemmy with the caddy reverse proxy"; 31 32 settings = mkOption { 33 default = { }; 34 description = "Lemmy configuration"; 35 36 type = types.submodule { 37 freeformType = settingsFormat.type; 38 39 options.hostname = mkOption { 40 type = types.str; 41 default = null; 42 description = "The domain name of your instance (eg 'lemmy.ml')."; 43 }; 44 45 options.port = mkOption { 46 type = types.port; 47 default = 8536; 48 description = "Port where lemmy should listen for incoming requests."; 49 }; 50 51 options.federation = { 52 enabled = mkEnableOption "activitypub federation"; 53 }; 54 55 options.captcha = { 56 enabled = mkOption { 57 type = types.bool; 58 default = true; 59 description = "Enable Captcha."; 60 }; 61 difficulty = mkOption { 62 type = types.enum [ "easy" "medium" "hard" ]; 63 default = "medium"; 64 description = "The difficultly of the captcha to solve."; 65 }; 66 }; 67 68 options.database.createLocally = mkEnableOption "creation of database on the instance"; 69 70 }; 71 }; 72 73 }; 74 75 config = 76 let 77 localPostgres = (cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql"); 78 in 79 lib.mkIf cfg.enable { 80 services.lemmy.settings = (mapAttrs (name: mkDefault) 81 { 82 bind = "127.0.0.1"; 83 tls_enabled = true; 84 pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}"; 85 actor_name_max_length = 20; 86 87 rate_limit.message = 180; 88 rate_limit.message_per_second = 60; 89 rate_limit.post = 6; 90 rate_limit.post_per_second = 600; 91 rate_limit.register = 3; 92 rate_limit.register_per_second = 3600; 93 rate_limit.image = 6; 94 rate_limit.image_per_second = 3600; 95 } // { 96 database = mapAttrs (name: mkDefault) { 97 user = "lemmy"; 98 host = "/run/postgresql"; 99 port = 5432; 100 database = "lemmy"; 101 pool_size = 5; 102 }; 103 }); 104 105 services.postgresql = mkIf localPostgres { 106 enable = mkDefault true; 107 }; 108 109 services.pict-rs.enable = true; 110 111 services.caddy = mkIf cfg.caddy.enable { 112 enable = mkDefault true; 113 virtualHosts."${cfg.settings.hostname}" = { 114 extraConfig = '' 115 handle_path /static/* { 116 root * ${pkgs.lemmy-ui}/dist 117 file_server 118 } 119 @for_backend { 120 path /api/* /pictrs/* feeds/* nodeinfo/* 121 } 122 handle @for_backend { 123 reverse_proxy 127.0.0.1:${toString cfg.settings.port} 124 } 125 @post { 126 method POST 127 } 128 handle @post { 129 reverse_proxy 127.0.0.1:${toString cfg.settings.port} 130 } 131 @jsonld { 132 header Accept "application/activity+json" 133 header Accept "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" 134 } 135 handle @jsonld { 136 reverse_proxy 127.0.0.1:${toString cfg.settings.port} 137 } 138 handle { 139 reverse_proxy 127.0.0.1:${toString cfg.ui.port} 140 } 141 ''; 142 }; 143 }; 144 145 assertions = [{ 146 assertion = cfg.settings.database.createLocally -> localPostgres; 147 message = "if you want to create the database locally, you need to use a local database"; 148 }]; 149 150 systemd.services.lemmy = { 151 description = "Lemmy server"; 152 153 environment = { 154 LEMMY_CONFIG_LOCATION = "/run/lemmy/config.hjson"; 155 156 # Verify how this is used, and don't put the password in the nix store 157 LEMMY_DATABASE_URL = with cfg.settings.database;"postgres:///${database}?host=${host}"; 158 }; 159 160 documentation = [ 161 "https://join-lemmy.org/docs/en/administration/from_scratch.html" 162 "https://join-lemmy.org/docs" 163 ]; 164 165 wantedBy = [ "multi-user.target" ]; 166 167 after = [ "pict-rs.service " ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; 168 169 requires = lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; 170 171 # script is needed here since loadcredential is not accessible on ExecPreStart 172 script = '' 173 ${pkgs.coreutils}/bin/install -m 600 ${settingsFormat.generate "config.hjson" cfg.settings} /run/lemmy/config.hjson 174 jwtSecret="$(< $CREDENTIALS_DIRECTORY/jwt_secret )" 175 ${pkgs.jq}/bin/jq ".jwt_secret = \"$jwtSecret\"" /run/lemmy/config.hjson | ${pkgs.moreutils}/bin/sponge /run/lemmy/config.hjson 176 ${pkgs.lemmy-server}/bin/lemmy_server 177 ''; 178 179 serviceConfig = { 180 DynamicUser = true; 181 RuntimeDirectory = "lemmy"; 182 LoadCredential = "jwt_secret:${cfg.jwtSecretPath}"; 183 }; 184 }; 185 186 systemd.services.lemmy-ui = { 187 description = "Lemmy ui"; 188 189 environment = { 190 LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; 191 LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; 192 LEMMY_EXTERNAL_HOST = cfg.settings.hostname; 193 LEMMY_HTTPS = "false"; 194 }; 195 196 documentation = [ 197 "https://join-lemmy.org/docs/en/administration/from_scratch.html" 198 "https://join-lemmy.org/docs" 199 ]; 200 201 wantedBy = [ "multi-user.target" ]; 202 203 after = [ "lemmy.service" ]; 204 205 requires = [ "lemmy.service" ]; 206 207 serviceConfig = { 208 DynamicUser = true; 209 WorkingDirectory = "${pkgs.lemmy-ui}"; 210 ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.lemmy-ui}/dist/js/server.js"; 211 }; 212 }; 213 214 systemd.services.lemmy-postgresql = mkIf cfg.settings.database.createLocally { 215 description = "Lemmy postgresql db"; 216 after = [ "postgresql.service" ]; 217 partOf = [ "lemmy.service" ]; 218 script = with cfg.settings.database; '' 219 PSQL() { 220 ${config.services.postgresql.package}/bin/psql --port=${toString cfg.settings.database.port} "$@" 221 } 222 # check if the database already exists 223 if ! PSQL -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw ${database} ; then 224 PSQL -tAc "CREATE ROLE ${user} WITH LOGIN;" 225 PSQL -tAc "CREATE DATABASE ${database} WITH OWNER ${user};" 226 fi 227 ''; 228 serviceConfig = { 229 User = config.services.postgresql.superUser; 230 Type = "oneshot"; 231 RemainAfterExit = true; 232 }; 233 }; 234 }; 235 236}