summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/bookwyrm.nix140
-rw-r--r--pkgs/bookwyrm.nix50
2 files changed, 186 insertions, 4 deletions
diff --git a/modules/bookwyrm.nix b/modules/bookwyrm.nix
new file mode 100644
index 0000000..4fcc323
--- /dev/null
+++ b/modules/bookwyrm.nix
@@ -0,0 +1,140 @@
+{ config, lib, pkgs, ... }:
+
+let
+ cfg = config.services.bookwyrm;
+in
+{
+ options.services.bookwyrm = with lib; {
+ enable = mkEnableOption "bookwyrm";
+
+ settings = mkOption {
+ default = {};
+ type = types.attrsOf (types.oneOf [ types.bool types.str types.int ]);
+ description = mdDoc ''
+ Settings passed to bookwyrm via environment variables. See bookwyrm's
+ [.env.example](https://github.com/bookwyrm-social/bookwyrm/blob/v0.7.2/.env.example)
+ file for what is permissible here.
+ '';
+ };
+
+ package = mkOption {
+ default = pkgs.bookwyrm.override { inherit (cfg) settings; };
+ type = types.package;
+ description = mdDoc ''
+ The bookwyrm package to use. Note that this includes the settings .env file;
+ if you set this directly, settings set via {option}`services.bookwyrm.settings`
+ will be ignored.
+ '';
+ };
+
+ stateDir = mkOption {
+ default = "/var/lib/bookwyrm";
+ type = types.path;
+ description = mdDoc ''
+ Where bookwyrm keeps dynamic data (in practice, exclusively book covers?) when
+ not configured to use an S3-compatible storage. This should be persistent storage,
+ otherwise you will have missing book covers and no obvious way to re-download them
+ from a catalogue.
+ '';
+ };
+
+ bindAddress = mkOption {
+ default = "0.0.0.0";
+ type = types.str;
+ description = mdDoc ''
+ Address bookwyrm should bind to.
+ '';
+ };
+
+ port = mkOption {
+ default = 8000;
+ type = types.port;
+ description = mdDoc ''
+ Port bookwyrm should listen on.
+ '';
+ };
+
+ threads = mkOption {
+ default = 8;
+ type = types.int;
+ description = mdDoc ''
+ Number of threads that gunicorn should spawn.
+ '';
+ };
+
+ setupNginx = mkOption {
+ default = false;
+ type = types.bool;
+ description = mdDoc ''
+ Whether to set up a simple nginx config to server bookwyrm's `static/` and `image`
+ directories.
+ '';
+ };
+
+ nginxVirtualHost = mkOption {
+ default = "default";
+ type = types.str;
+ description = mdDoc ''
+ The name of the nginx virtual host to set up.
+ '';
+ };
+ };
+
+
+ config = with lib; mkIf cfg.enable {
+ systemd.packages = [ cfg.package ];
+
+ systemd.services = {
+ bookwyrm = {
+ enable = true;
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ BindPaths = [
+ cfg.package.passthru.gunicorn
+ cfg.package.passthru.celery
+ cfg.stateDir
+ ];
+ } // mkIf (cfg.bindAddress != "0.0.0.0" || cfg.port != 8000 || cfg.threads != 8) {
+ ExecStart = "${lib.getExe cfg.package.passthru.gunicorn} bookwyrm.wsgi:application --threads=${toString cfg.threads} --bind ${cfg.bindAddress}:${toString cfg.port}";
+ };
+
+ environment.PYTHONPATH = cfg.package.passthru.pythonPath;
+ };
+
+ bookwyrm-worker = {
+ enable = true;
+ wantedBy = [ "multi-user.target" ];
+ environment.PYTHONPATH = cfg.package.passthru.pythonPath;
+ serviceConfig.BindPaths = [
+ cfg.stateDir
+ ];
+ };
+ bookwyrm-scheduler = {
+ enable = true;
+ wantedBy = [ "multi-user.target" ];
+ environment.PYTHONPATH = cfg.package.passthru.pythonPath;
+ };
+ };
+
+ systemd.tmpfiles.rules = [
+ "d ${cfg.stateDir}/images 0750 bookwyrm bookwyrm - -"
+ ];
+
+ services.nginx = mkIf cfg.setupNginx {
+ enable = true;
+ virtualHosts.${cfg.nginxVirtualHost} = {
+ locations."/static".root = config.services.bookwyrm.package;
+ locations."/images".root = cfg.stateDir;
+ locations."/".proxyPass = "http://${cfg.bindAddress}:${toString cfg.port}";
+ };
+ };
+
+ users.users.bookwyrm = {
+ isSystemUser = true;
+ group = "bookwyrm";
+ };
+ users.users.nginx.extraGroups = [ "bookwyrm" ];
+ users.groups.bookwyrm = {};
+ };
+
+}
diff --git a/pkgs/bookwyrm.nix b/pkgs/bookwyrm.nix
index c3838f2..309e90b 100644
--- a/pkgs/bookwyrm.nix
+++ b/pkgs/bookwyrm.nix
@@ -1,9 +1,42 @@
-{ lib, fetchFromGitHub, python, writeShellScriptBin, writeText, settings ? { }
+{ lib
+, fetchFromGitHub
+, python
+, writeShellScriptBin
+, writeText
+, settings ? { }
}:
let
+ # set some dummy values to make the package build
+ settingsWithDefaults = {
+ DOMAIN = "localhost";
+ DEBUG = false;
+ SECRET_KEY = "fnord";
+ USE_HTTPS = false;
+ EMAIL = "your@email.here";
+ PGPORT = 5432;
+ POSTGRES_USER = "bookwyrm";
+ POSTGRES_DB = "bookwyrm";
+ POSTGRES_HOST = "localhost";
+ REDIS_ACTIVITY_HOST = "localhost";
+ REDIS_ACTIVITY_PORT = 6379;
+ REDIS_BROKER_HOST = "localhost";
+ REDIS_BROKER_PORT = 6379;
+ EMAIL_HOST = "smtp.example.com";
+ EMAIL_PORT = 587;
+ EMAIL_HOST_USER = "mail@example.org";
+ EMAIL_HOST_PASSWORD = "blub";
+ MEDIA_ROOT = "/var/lib/bookwyrm/images";
+ } // settings;
+
+ # toShellVar produces "" for false, which bookwyrm rejects
+ toDjangoVar = name: value: lib.toShellVar name
+ (if value == false then "false" else
+ (if value == true then "true" else value));
+
envfile = writeText "bookwyrm.env"
- (lib.strings.concatLines (lib.mapAttrsToList lib.toShellVar settings));
+ (lib.strings.concatLines
+ (lib.mapAttrsToList toDjangoVar settingsWithDefaults));
bookwyrm = python.pkgs.buildPythonApplication rec {
pname = "bookwyrm";
@@ -62,13 +95,22 @@ let
postBuild = ''
ln -s ${envfile} .env
+
+ substituteInPlace contrib/systemd/* \
+ --replace /opt/bookwyrm/venv/bin/gunicorn ${lib.getExe python.pkgs.gunicorn} \
+ --replace /opt/bookwyrm/venv/bin/celery ${lib.getExe' python.pkgs.celery "celery"} \
+ --replace /opt/bookwyrm $out
+
+ sed -i /BindPath/d contrib/systemd/*
+
python manage.py compile_themes
- python manage.py collectstatic --no-input
+ python manage.py collectstatic --no-input --ignore=*.scss
'';
postInstall = ''
- mkdir -p $out
+ mkdir -p $out/lib/systemd/system
cp -r * .env $out
+ cp -r contrib/systemd/* $out/lib/systemd/system
'';
passthru = {