at 23.05-pre 6.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 imports = [ 14 (mkRemovedOptionModule [ "services" "lemmy" "jwtSecretPath" ] "As of v0.13.0, Lemmy auto-generates the JWT secret.") 15 ]; 16 17 options.services.lemmy = { 18 19 enable = mkEnableOption (lib.mdDoc "lemmy a federated alternative to reddit in rust"); 20 21 ui = { 22 port = mkOption { 23 type = types.port; 24 default = 1234; 25 description = lib.mdDoc "Port where lemmy-ui should listen for incoming requests."; 26 }; 27 }; 28 29 caddy.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the caddy reverse proxy"); 30 31 database.createLocally = mkEnableOption (lib.mdDoc "creation of database on the instance"); 32 33 settings = mkOption { 34 default = { }; 35 description = lib.mdDoc "Lemmy configuration"; 36 37 type = types.submodule { 38 freeformType = settingsFormat.type; 39 40 options.hostname = mkOption { 41 type = types.str; 42 default = null; 43 description = lib.mdDoc "The domain name of your instance (eg 'lemmy.ml')."; 44 }; 45 46 options.port = mkOption { 47 type = types.port; 48 default = 8536; 49 description = lib.mdDoc "Port where lemmy should listen for incoming requests."; 50 }; 51 52 options.federation = { 53 enabled = mkEnableOption (lib.mdDoc "activitypub federation"); 54 }; 55 56 options.captcha = { 57 enabled = mkOption { 58 type = types.bool; 59 default = true; 60 description = lib.mdDoc "Enable Captcha."; 61 }; 62 difficulty = mkOption { 63 type = types.enum [ "easy" "medium" "hard" ]; 64 default = "medium"; 65 description = lib.mdDoc "The difficultly of the captcha to solve."; 66 }; 67 }; 68 }; 69 }; 70 71 }; 72 73 config = 74 lib.mkIf cfg.enable { 75 services.lemmy.settings = (mapAttrs (name: mkDefault) 76 { 77 bind = "127.0.0.1"; 78 tls_enabled = true; 79 pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}"; 80 actor_name_max_length = 20; 81 82 rate_limit.message = 180; 83 rate_limit.message_per_second = 60; 84 rate_limit.post = 6; 85 rate_limit.post_per_second = 600; 86 rate_limit.register = 3; 87 rate_limit.register_per_second = 3600; 88 rate_limit.image = 6; 89 rate_limit.image_per_second = 3600; 90 } // { 91 database = mapAttrs (name: mkDefault) { 92 user = "lemmy"; 93 host = "/run/postgresql"; 94 port = 5432; 95 database = "lemmy"; 96 pool_size = 5; 97 }; 98 }); 99 100 services.postgresql = mkIf cfg.database.createLocally { 101 enable = true; 102 ensureDatabases = [ cfg.settings.database.database ]; 103 ensureUsers = [{ 104 name = cfg.settings.database.user; 105 ensurePermissions."DATABASE ${cfg.settings.database.database}" = "ALL PRIVILEGES"; 106 }]; 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.database.createLocally -> cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql"; 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.database.createLocally [ "postgresql.service" ]; 168 169 requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ]; 170 171 serviceConfig = { 172 DynamicUser = true; 173 RuntimeDirectory = "lemmy"; 174 ExecStartPre = "${pkgs.coreutils}/bin/install -m 600 ${settingsFormat.generate "config.hjson" cfg.settings} /run/lemmy/config.hjson"; 175 ExecStart = "${pkgs.lemmy-server}/bin/lemmy_server"; 176 }; 177 }; 178 179 systemd.services.lemmy-ui = { 180 description = "Lemmy ui"; 181 182 environment = { 183 LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; 184 LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; 185 LEMMY_EXTERNAL_HOST = cfg.settings.hostname; 186 LEMMY_HTTPS = "false"; 187 }; 188 189 documentation = [ 190 "https://join-lemmy.org/docs/en/administration/from_scratch.html" 191 "https://join-lemmy.org/docs" 192 ]; 193 194 wantedBy = [ "multi-user.target" ]; 195 196 after = [ "lemmy.service" ]; 197 198 requires = [ "lemmy.service" ]; 199 200 serviceConfig = { 201 DynamicUser = true; 202 WorkingDirectory = "${pkgs.lemmy-ui}"; 203 ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.lemmy-ui}/dist/js/server.js"; 204 }; 205 }; 206 }; 207 208}