{ config, lib, pkgs, ... }: let cfg = config.services.bookwyrm; manage-wrapper = cfg.package.manage cfg.environmentFile; in { options.services.bookwyrm = with lib; { enable = mkEnableOption "bookwyrm"; settings = mkOption { default = {}; type = types.attrsOf (types.oneOf [ types.bool types.str types.int ]); description = '' 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 = '' 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 = '' 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 = '' Address bookwyrm should bind to. ''; }; port = mkOption { default = 8000; type = types.port; description = '' Port bookwyrm should listen on. ''; }; threads = mkOption { default = 8; type = types.int; description = '' Number of threads that gunicorn should spawn. ''; }; setupNginx = mkOption { default = false; type = types.bool; description = '' Whether to set up a simple nginx config to server bookwyrm's `static/` and `image` directories. ''; }; nginxVirtualHost = mkOption { default = "default"; type = types.str; description = '' The name of the nginx virtual host to set up. ''; }; installWrapper = mkOption { default = true; type = types.bool; description = '' Whether to install a wrapper script `bookworm-manage.py` into the system environmnt, which calls bookwyrm's `manage.py` script with the correct python path. ''; }; environmentFile = mkOption { default = null; type = types.nullOr types.path; description = '' An environment file containing config options which should not be set via Nix / not be contained in the nix store. ''; }; }; config = with lib; mkIf cfg.enable { systemd.packages = [ cfg.package ]; systemd.services = { bookwyrm = { enable = true; wantedBy = [ "multi-user.target" ]; serviceConfig = mkMerge [ { BindPaths = [ cfg.package.gunicorn cfg.package.celery cfg.stateDir ]; } (mkIf (cfg.bindAddress != "0.0.0.0" || cfg.port != 8000 || cfg.threads != 8) { ExecStart = "${lib.getExe cfg.package.gunicorn} bookwyrm.wsgi:application --threads=${toString cfg.threads} --bind ${cfg.bindAddress}:${toString cfg.port}"; }) (mkIf (cfg.environmentFile != null) { EnvironmentFile = cfg.environmentFile; }) ]; environment.PYTHONPATH = cfg.package.pythonPath; preStart = '' ${lib.getExe manage-wrapper} migrate # will fail after the first time ${lib.getExe manage-wrapper} initdb || true ''; }; bookwyrm-worker = { enable = true; wantedBy = [ "multi-user.target" ]; environment.PYTHONPATH = cfg.package.pythonPath; serviceConfig.BindPaths = [ cfg.stateDir ]; serviceConfig.EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; }; bookwyrm-scheduler = { enable = true; wantedBy = [ "multi-user.target" ]; environment.PYTHONPATH = cfg.package.pythonPath; serviceConfig.EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; }; }; 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 = mkMerge [ { bookwyrm = { isSystemUser = true; group = "bookwyrm"; }; } (mkIf cfg.setupNginx { nginx.extraGroups = [ "bookwyrm" ]; }) ]; users.groups.bookwyrm = {}; environment.systemPackages = mkIf cfg.installWrapper [ manage-wrapper ]; warnings = mkIf (cfg.settings ? "SECRET_KEY") [ '' Setting bookwyrm's SECRET_KEY via the free-form services.bookwyrm.settings.* is discouraged. It's better to pass an env file containing it to servies.bookwyrm.envFile instead. '' ]; }; }