{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.paperless;
defaultUser = "paperless";
manage = cfg.package.withConfig {
config = {
PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
PAPERLESS_INLINE_DOC = "true";
PAPERLESS_DISABLE_LOGIN = "true";
} // cfg.extraConfig;
inherit (cfg) dataDir ocrLanguages;
paperlessPkg = cfg.package;
};
in
{
options.services.paperless = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable Paperless.
When started, the Paperless database is automatically created if it doesn't
exist and updated if the Paperless package has changed.
Both tasks are achieved by running a Django migration.
'';
};
dataDir = mkOption {
type = types.str;
default = "/var/lib/paperless";
description = "Directory to store the Paperless data.";
};
consumptionDir = mkOption {
type = types.str;
default = "${cfg.dataDir}/consume";
defaultText = "\${dataDir}/consume";
description = "Directory from which new documents are imported.";
};
consumptionDirIsPublic = mkOption {
type = types.bool;
default = false;
description = "Whether all users can write to the consumption dir.";
};
ocrLanguages = mkOption {
type = with types; nullOr (listOf string);
default = null;
description = ''
Languages available for OCR via Tesseract, specified as
ISO 639-2/T language codes.
If unset, defaults to all available languages.
'';
example = [ "eng" "spa" "jpn" ];
};
address = mkOption {
type = types.str;
default = "localhost";
description = "Server listening address.";
};
port = mkOption {
type = types.int;
default = 28981;
description = "Server port to listen on.";
};
extraConfig = mkOption {
type = types.attrs;
default = {};
description = ''
Extra paperless config options.
The config values are evaluated as double-quoted Bash string literals.
See paperless-src/paperless.conf.example for available options.
To enable user authentication, set PAPERLESS_DISABLE_LOGIN = "false"
and run the shell command $dataDir/paperless-manage createsuperuser.
To define secret options without storing them in /nix/store, use the following pattern:
PAPERLESS_PASSPHRASE = "$(< /etc/my_passphrase_file)"
'';
example = literalExample ''
{
PAPERLESS_OCR_LANGUAGE = "deu";
}
'';
};
user = mkOption {
type = types.str;
default = defaultUser;
description = "User under which Paperless runs.";
};
package = mkOption {
type = types.package;
default = pkgs.paperless;
defaultText = "pkgs.paperless";
description = "The Paperless package to use.";
};
manage = mkOption {
type = types.package;
readOnly = true;
default = manage;
description = ''
A script to manage the Paperless instance.
It wraps Django's manage.py and is also available at
$dataDir/manage-paperless
'';
};
};
config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' - ${cfg.user} ${cfg.user} - -"
] ++ (optional cfg.consumptionDirIsPublic
"d '${cfg.consumptionDir}' 777 ${cfg.user} ${cfg.user} - -"
# If the consumption dir is not created here, it's automatically created by
# 'manage' with the default permissions.
);
systemd.services.paperless-consumer = {
description = "Paperless document consumer";
serviceConfig = {
User = cfg.user;
ExecStart = "${manage} document_consumer";
Restart = "always";
};
after = [ "systemd-tmpfiles-setup.service" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
if [[ $(readlink ${cfg.dataDir}/paperless-manage) != ${manage} ]]; then
ln -sf ${manage} ${cfg.dataDir}/paperless-manage
fi
${manage.setupEnv}
# Auto-migrate on first run or if the package has changed
versionFile="$PAPERLESS_DBDIR/src-version"
if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
python $paperlessSrc/manage.py migrate
echo ${cfg.package} > "$versionFile"
fi
'';
};
systemd.services.paperless-server = {
description = "Paperless document server";
serviceConfig = {
User = cfg.user;
ExecStart = "${manage} runserver --noreload ${cfg.address}:${toString cfg.port}";
Restart = "always";
};
# Bind to `paperless-consumer` so that the server never runs
# during migrations
bindsTo = [ "paperless-consumer.service" ];
after = [ "paperless-consumer.service" ];
wantedBy = [ "multi-user.target" ];
};
users = optionalAttrs (cfg.user == defaultUser) {
users = [{
name = defaultUser;
group = defaultUser;
uid = config.ids.uids.paperless;
home = cfg.dataDir;
}];
groups = [{
name = defaultUser;
gid = config.ids.gids.paperless;
}];
};
};
}