From cc3b147ed182a6cae239348ef094158815da14ae Mon Sep 17 00:00:00 2001 From: Bill Ewanick Date: Thu, 23 Sep 2021 10:27:42 -0400 Subject: [PATCH] nixos/lemmy: init Co-authored-by: Raphael Megzari --- nixos/modules/module-list.nix | 1 + nixos/modules/services/web-apps/lemmy.md | 34 ++++ nixos/modules/services/web-apps/lemmy.nix | 238 ++++++++++++++++++++++ nixos/modules/services/web-apps/lemmy.xml | 56 +++++ pkgs/servers/web-apps/lemmy/ui.nix | 3 +- 5 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 nixos/modules/services/web-apps/lemmy.md create mode 100644 nixos/modules/services/web-apps/lemmy.nix create mode 100644 nixos/modules/services/web-apps/lemmy.xml diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 60bf5afc69b1..36e2131f2d2f 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -985,6 +985,7 @@ ./services/web-apps/jirafeau.nix ./services/web-apps/jitsi-meet.nix ./services/web-apps/keycloak.nix + ./services/web-apps/lemmy.nix ./services/web-apps/limesurvey.nix ./services/web-apps/mastodon.nix ./services/web-apps/mattermost.nix diff --git a/nixos/modules/services/web-apps/lemmy.md b/nixos/modules/services/web-apps/lemmy.md new file mode 100644 index 000000000000..e6599cd843e3 --- /dev/null +++ b/nixos/modules/services/web-apps/lemmy.md @@ -0,0 +1,34 @@ +# Lemmy {#module-services-lemmy} + +Lemmy is a federated alternative to reddit in rust. + +## Quickstart {#module-services-lemmy-quickstart} + +the minimum to start lemmy is + +```nix +services.lemmy = { + enable = true; + settings = { + hostname = "lemmy.union.rocks"; + database.createLocally = true; + }; + jwtSecretPath = "/run/secrets/lemmyJwt"; + caddy.enable = true; +} +``` + +(note that you can use something like agenix to get your secret jwt to the specified path) + +this will start the backend on port 8536 and the frontend on port 1234. +It will expose your instance with a caddy reverse proxy to the hostname you've provided. +Postgres will be initialized on that same instance automatically. + +## Usage {#module-services-lemmy-usage} + +On first connection you will be asked to define an admin user. + +## Missing {#module-services-lemmy-missing} + +- Exposing with nginx is not implemented yet. +- This has been tested using a local database with a unix socket connection. Using different database settings will likely require modifications diff --git a/nixos/modules/services/web-apps/lemmy.nix b/nixos/modules/services/web-apps/lemmy.nix new file mode 100644 index 000000000000..ae7d0d02c894 --- /dev/null +++ b/nixos/modules/services/web-apps/lemmy.nix @@ -0,0 +1,238 @@ +{ lib, pkgs, config, ... }: +with lib; +let + cfg = config.services.lemmy; + settingsFormat = pkgs.formats.json { }; +in +{ + meta.maintainers = with maintainers; [ happysalada ]; + # Don't edit the docbook xml directly, edit the md and generate it: + # `pandoc lemmy.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > lemmy.xml` + meta.doc = ./lemmy.xml; + + options.services.lemmy = { + + enable = mkEnableOption "lemmy a federated alternative to reddit in rust"; + + jwtSecretPath = mkOption { + type = types.path; + description = "Path to read the jwt secret from."; + }; + + ui = { + port = mkOption { + type = types.port; + default = 1234; + description = "Port where lemmy-ui should listen for incoming requests."; + }; + }; + + caddy.enable = mkEnableOption "exposing lemmy with the caddy reverse proxy"; + + settings = mkOption { + default = { }; + description = "Lemmy configuration"; + + type = types.submodule { + freeformType = settingsFormat.type; + + options.hostname = mkOption { + type = types.str; + default = null; + description = "The domain name of your instance (eg 'lemmy.ml')."; + }; + + options.port = mkOption { + type = types.port; + default = 8536; + description = "Port where lemmy should listen for incoming requests."; + }; + + options.federation = { + enabled = mkEnableOption "activitypub federation"; + }; + + options.captcha = { + enabled = mkOption { + type = types.bool; + default = true; + description = "Enable Captcha."; + }; + difficulty = mkOption { + type = types.enum [ "easy" "medium" "hard" ]; + default = "medium"; + description = "The difficultly of the captcha to solve."; + }; + }; + + options.database.createLocally = mkEnableOption "creation of database on the instance"; + + }; + }; + + }; + + config = + let + localPostgres = (cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql"); + in + lib.mkIf cfg.enable { + services.lemmy.settings = (mapAttrs (name: mkDefault) + { + bind = "127.0.0.1"; + tls_enabled = true; + pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}"; + actor_name_max_length = 20; + + rate_limit.message = 180; + rate_limit.message_per_second = 60; + rate_limit.post = 6; + rate_limit.post_per_second = 600; + rate_limit.register = 3; + rate_limit.register_per_second = 3600; + rate_limit.image = 6; + rate_limit.image_per_second = 3600; + } // { + database = mapAttrs (name: mkDefault) { + user = "lemmy"; + host = "/run/postgresql"; + port = 5432; + database = "lemmy"; + pool_size = 5; + }; + }); + + services.postgresql = mkIf localPostgres { + enable = mkDefault true; + }; + + services.pict-rs.enable = true; + + services.caddy = mkIf cfg.caddy.enable { + enable = mkDefault true; + virtualHosts."${cfg.settings.hostname}" = { + extraConfig = '' + handle_path /static/* { + root * ${pkgs.lemmy-ui}/dist + file_server + } + @for_backend { + path /api/* /pictrs/* feeds/* nodeinfo/* + } + handle @for_backend { + reverse_proxy 127.0.0.1:${toString cfg.settings.port} + } + @post { + method POST + } + handle @post { + reverse_proxy 127.0.0.1:${toString cfg.settings.port} + } + @jsonld { + header Accept "application/activity+json" + header Accept "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + } + handle @jsonld { + reverse_proxy 127.0.0.1:${toString cfg.settings.port} + } + handle { + reverse_proxy 127.0.0.1:${toString cfg.ui.port} + } + ''; + }; + }; + + assertions = [{ + assertion = cfg.settings.database.createLocally -> localPostgres; + message = "if you want to create the database locally, you need to use a local database"; + }]; + + systemd.services.lemmy = { + description = "Lemmy server"; + + environment = { + LEMMY_CONFIG_LOCATION = "/run/lemmy/config.hjson"; + + # Verify how this is used, and don't put the password in the nix store + LEMMY_DATABASE_URL = with cfg.settings.database;"postgres:///${database}?host=${host}"; + }; + + documentation = [ + "https://join-lemmy.org/docs/en/administration/from_scratch.html" + "https://join-lemmy.org/docs" + ]; + + wantedBy = [ "multi-user.target" ]; + + after = [ "pict-rs.service " ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; + + requires = lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ]; + + # script is needed here since loadcredential is not accessible on ExecPreStart + script = '' + ${pkgs.coreutils}/bin/install -m 600 ${settingsFormat.generate "config.hjson" cfg.settings} /run/lemmy/config.hjson + jwtSecret="$(< $CREDENTIALS_DIRECTORY/jwt_secret )" + ${pkgs.jq}/bin/jq ".jwt_secret = \"$jwtSecret\"" /run/lemmy/config.hjson | ${pkgs.moreutils}/bin/sponge /run/lemmy/config.hjson + ${pkgs.lemmy-server}/bin/lemmy_server + ''; + + serviceConfig = { + DynamicUser = true; + RuntimeDirectory = "lemmy"; + LoadCredential = "jwt_secret:${cfg.jwtSecretPath}"; + }; + }; + + systemd.services.lemmy-ui = { + description = "Lemmy ui"; + + environment = { + LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; + LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; + LEMMY_EXTERNAL_HOST = cfg.settings.hostname; + LEMMY_HTTPS = "false"; + }; + + documentation = [ + "https://join-lemmy.org/docs/en/administration/from_scratch.html" + "https://join-lemmy.org/docs" + ]; + + wantedBy = [ "multi-user.target" ]; + + after = [ "lemmy.service" ]; + + requires = [ "lemmy.service" ]; + + serviceConfig = { + DynamicUser = true; + WorkingDirectory = "${pkgs.lemmy-ui}"; + ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.lemmy-ui}/dist/js/server.js"; + }; + }; + + systemd.services.lemmy-postgresql = mkIf cfg.settings.database.createLocally { + description = "Lemmy postgresql db"; + after = [ "postgresql.service" ]; + bindsTo = [ "postgresql.service" ]; + requiredBy = [ "lemmy.service" ]; + partOf = [ "lemmy.service" ]; + script = with cfg.settings.database; '' + PSQL() { + ${config.services.postgresql.package}/bin/psql --port=${toString cfg.settings.database.port} "$@" + } + # check if the database already exists + if ! PSQL -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw ${database} ; then + PSQL -tAc "CREATE ROLE ${user} WITH LOGIN;" + PSQL -tAc "CREATE DATABASE ${database} WITH OWNER ${user};" + fi + ''; + serviceConfig = { + User = config.services.postgresql.superUser; + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + }; + +} diff --git a/nixos/modules/services/web-apps/lemmy.xml b/nixos/modules/services/web-apps/lemmy.xml new file mode 100644 index 000000000000..0be9fb8aefa9 --- /dev/null +++ b/nixos/modules/services/web-apps/lemmy.xml @@ -0,0 +1,56 @@ + + Lemmy + + Lemmy is a federated alternative to reddit in rust. + +
+ Quickstart + + the minimum to start lemmy is + + +services.lemmy = { + enable = true; + settings = { + hostname = "lemmy.union.rocks"; + database.createLocally = true; + }; + jwtSecretPath = "/run/secrets/lemmyJwt"; + caddy.enable = true; +} + + + (note that you can use something like agenix to get your secret + jwt to the specified path) + + + this will start the backend on port 8536 and the frontend on port + 1234. It will expose your instance with a caddy reverse proxy to + the hostname you’ve provided. Postgres will be initialized on that + same instance automatically. + +
+
+ Usage + + On first connection you will be asked to define an admin user. + +
+
+ Missing + + + + Exposing with nginx is not implemented yet. + + + + + This has been tested using a local database with a unix socket + connection. Using different database settings will likely + require modifications + + + +
+
diff --git a/pkgs/servers/web-apps/lemmy/ui.nix b/pkgs/servers/web-apps/lemmy/ui.nix index e554a1213e70..100769977a25 100644 --- a/pkgs/servers/web-apps/lemmy/ui.nix +++ b/pkgs/servers/web-apps/lemmy/ui.nix @@ -57,7 +57,8 @@ mkYarnPackage { preInstall = '' mkdir $out - cp -R ./deps/lemmy-ui/dist/assets $out + cp -R ./deps/lemmy-ui/dist $out + cp -R ./node_modules $out ''; distPhase = "true";