mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-18 15:39:46 +03:00

After final improvements to the official formatter implementation, this commit now performs the first treewide reformat of Nix files using it. This is part of the implementation of RFC 166. Only "inactive" files are reformatted, meaning only files that aren't being touched by any PR with activity in the past 2 months. This is to avoid conflicts for PRs that might soon be merged. Later we can do a full treewide reformat to get the rest, which should not cause as many conflicts. A CI check has already been running for some time to ensure that new and already-formatted files are formatted, so the files being reformatted here should also stay formatted. This commit was automatically created and can be verified using nix-builda08b3a4d19
.tar.gz \ --argstr baseRevb32a094368
result/bin/apply-formatting $NIXPKGS_PATH
385 lines
12 KiB
Nix
385 lines
12 KiB
Nix
{
|
|
lib,
|
|
pkgs,
|
|
config,
|
|
utils,
|
|
...
|
|
}:
|
|
with lib;
|
|
let
|
|
cfg = config.services.lemmy;
|
|
settingsFormat = pkgs.formats.json { };
|
|
in
|
|
{
|
|
meta.maintainers = with maintainers; [ happysalada ];
|
|
meta.doc = ./lemmy.md;
|
|
|
|
imports = [
|
|
(mkRemovedOptionModule [
|
|
"services"
|
|
"lemmy"
|
|
"jwtSecretPath"
|
|
] "As of v0.13.0, Lemmy auto-generates the JWT secret.")
|
|
];
|
|
|
|
options.services.lemmy = {
|
|
|
|
enable = mkEnableOption "lemmy a federated alternative to reddit in rust";
|
|
|
|
server = {
|
|
package = mkPackageOption pkgs "lemmy-server" { };
|
|
};
|
|
|
|
ui = {
|
|
package = mkPackageOption pkgs "lemmy-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";
|
|
nginx.enable = mkEnableOption "exposing lemmy with the nginx reverse proxy";
|
|
|
|
database = {
|
|
createLocally = mkEnableOption "creation of database on the instance";
|
|
|
|
uri = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
description = "The connection URI to use. Takes priority over the configuration file if set.";
|
|
};
|
|
|
|
uriFile = mkOption {
|
|
type = with types; nullOr path;
|
|
default = null;
|
|
description = "File which contains the database uri.";
|
|
};
|
|
};
|
|
|
|
pictrsApiKeyFile = mkOption {
|
|
type = with types; nullOr path;
|
|
default = null;
|
|
description = "File which contains the value of `pictrs.api_key`.";
|
|
};
|
|
|
|
smtpPasswordFile = mkOption {
|
|
type = with types; nullOr path;
|
|
default = null;
|
|
description = "File which contains the value of `email.smtp_password`.";
|
|
};
|
|
|
|
adminPasswordFile = mkOption {
|
|
type = with types; nullOr path;
|
|
default = null;
|
|
description = "File which contains the value of `setup.admin_password`.";
|
|
};
|
|
|
|
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.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.";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
config =
|
|
let
|
|
secretOptions = {
|
|
pictrsApiKeyFile = {
|
|
setting = [
|
|
"pictrs"
|
|
"api_key"
|
|
];
|
|
path = cfg.pictrsApiKeyFile;
|
|
};
|
|
smtpPasswordFile = {
|
|
setting = [
|
|
"email"
|
|
"smtp_password"
|
|
];
|
|
path = cfg.smtpPasswordFile;
|
|
};
|
|
adminPasswordFile = {
|
|
setting = [
|
|
"setup"
|
|
"admin_password"
|
|
];
|
|
path = cfg.adminPasswordFile;
|
|
};
|
|
uriFile = {
|
|
setting = [
|
|
"database"
|
|
"uri"
|
|
];
|
|
path = cfg.database.uriFile;
|
|
};
|
|
};
|
|
secrets = lib.filterAttrs (option: data: data.path != null) secretOptions;
|
|
in
|
|
lib.mkIf cfg.enable {
|
|
services.lemmy.settings =
|
|
lib.attrsets.recursiveUpdate
|
|
(
|
|
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;
|
|
};
|
|
}
|
|
)
|
|
(
|
|
lib.foldlAttrs (
|
|
acc: option: data:
|
|
acc // lib.setAttrByPath data.setting { _secret = option; }
|
|
) { } secrets
|
|
);
|
|
# the option name is the id of the credential loaded by LoadCredential
|
|
|
|
services.postgresql = mkIf cfg.database.createLocally {
|
|
enable = true;
|
|
ensureDatabases = [ cfg.settings.database.database ];
|
|
ensureUsers = [
|
|
{
|
|
name = cfg.settings.database.user;
|
|
ensureDBOwnership = true;
|
|
}
|
|
];
|
|
};
|
|
|
|
services.pict-rs.enable = true;
|
|
|
|
services.caddy = mkIf cfg.caddy.enable {
|
|
enable = mkDefault true;
|
|
virtualHosts."${cfg.settings.hostname}" = {
|
|
extraConfig = ''
|
|
handle_path /static/* {
|
|
root * ${cfg.ui.package}/dist
|
|
file_server
|
|
}
|
|
handle_path /static/${cfg.ui.package.passthru.commit_sha}/* {
|
|
root * ${cfg.ui.package}/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}
|
|
}
|
|
'';
|
|
};
|
|
};
|
|
|
|
services.nginx = mkIf cfg.nginx.enable {
|
|
enable = mkDefault true;
|
|
virtualHosts."${cfg.settings.hostname}".locations =
|
|
let
|
|
ui = "http://127.0.0.1:${toString cfg.ui.port}";
|
|
backend = "http://127.0.0.1:${toString cfg.settings.port}";
|
|
in
|
|
{
|
|
"~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = {
|
|
# backend requests
|
|
proxyPass = backend;
|
|
proxyWebsockets = true;
|
|
recommendedProxySettings = true;
|
|
};
|
|
"/" = {
|
|
# mixed frontend and backend requests, based on the request headers
|
|
extraConfig = ''
|
|
set $proxpass "${ui}";
|
|
if ($http_accept = "application/activity+json") {
|
|
set $proxpass "${backend}";
|
|
}
|
|
if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
|
|
set $proxpass "${backend}";
|
|
}
|
|
if ($request_method = POST) {
|
|
set $proxpass "${backend}";
|
|
}
|
|
|
|
# Cuts off the trailing slash on URLs to make them valid
|
|
rewrite ^(.+)/+$ $1 permanent;
|
|
|
|
proxy_pass $proxpass;
|
|
# Proxied `Host` header is required to validate ActivityPub HTTP signatures for incoming events.
|
|
# The other headers are optional, for the sake of better log data.
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
assertions = [
|
|
{
|
|
assertion =
|
|
cfg.database.createLocally
|
|
-> cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql";
|
|
message = "if you want to create the database locally, you need to use a local database";
|
|
}
|
|
{
|
|
assertion =
|
|
(!(hasAttrByPath [ "federation" ] cfg.settings))
|
|
&& (!(hasAttrByPath [ "federation" "enabled" ] cfg.settings));
|
|
message = "`services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect";
|
|
}
|
|
{
|
|
assertion = cfg.database.uriFile != null -> cfg.database.uri == null && !cfg.database.createLocally;
|
|
message = "specifying a database uri while also specifying a database uri file is not allowed";
|
|
}
|
|
];
|
|
|
|
systemd.services.lemmy =
|
|
let
|
|
substitutedConfig = "/run/lemmy/config.hjson";
|
|
in
|
|
{
|
|
description = "Lemmy server";
|
|
|
|
environment = {
|
|
LEMMY_CONFIG_LOCATION =
|
|
if secrets == { } then settingsFormat.generate "config.hjson" cfg.settings else substitutedConfig;
|
|
LEMMY_DATABASE_URL =
|
|
if cfg.database.uri != null then
|
|
cfg.database.uri
|
|
else
|
|
(mkIf (cfg.database.createLocally) "postgres:///lemmy?host=/run/postgresql&user=lemmy");
|
|
};
|
|
|
|
documentation = [
|
|
"https://join-lemmy.org/docs/en/admins/from_scratch.html"
|
|
"https://join-lemmy.org/docs/en/"
|
|
];
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
after = [ "pict-rs.service" ] ++ lib.optionals cfg.database.createLocally [ "postgresql.service" ];
|
|
|
|
requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ];
|
|
|
|
# substitute secrets and prevent others from reading the result
|
|
# if somehow $CREDENTIALS_DIRECTORY is not set we fail
|
|
preStart = mkIf (secrets != { }) ''
|
|
set -u
|
|
umask u=rw,g=,o=
|
|
cd "$CREDENTIALS_DIRECTORY"
|
|
${utils.genJqSecretsReplacementSnippet cfg.settings substitutedConfig}
|
|
'';
|
|
|
|
serviceConfig = {
|
|
DynamicUser = true;
|
|
RuntimeDirectory = "lemmy";
|
|
ExecStart = "${cfg.server.package}/bin/lemmy_server";
|
|
LoadCredential = lib.foldlAttrs (
|
|
acc: option: data:
|
|
acc ++ [ "${option}:${toString data.path}" ]
|
|
) [ ] secrets;
|
|
PrivateTmp = true;
|
|
MemoryDenyWriteExecute = true;
|
|
NoNewPrivileges = true;
|
|
};
|
|
};
|
|
|
|
systemd.services.lemmy-ui = {
|
|
description = "Lemmy ui";
|
|
|
|
environment = {
|
|
LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}";
|
|
LEMMY_UI_LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}";
|
|
LEMMY_UI_LEMMY_EXTERNAL_HOST = cfg.settings.hostname;
|
|
LEMMY_UI_HTTPS = "false";
|
|
NODE_ENV = "production";
|
|
};
|
|
|
|
documentation = [
|
|
"https://join-lemmy.org/docs/en/admins/from_scratch.html"
|
|
"https://join-lemmy.org/docs/en/"
|
|
];
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
after = [ "lemmy.service" ];
|
|
|
|
requires = [ "lemmy.service" ];
|
|
|
|
serviceConfig = {
|
|
DynamicUser = true;
|
|
WorkingDirectory = "${cfg.ui.package}";
|
|
ExecStart = "${pkgs.nodejs}/bin/node ${cfg.ui.package}/dist/js/server.js";
|
|
};
|
|
};
|
|
};
|
|
|
|
}
|