Skip to content

Commit

Permalink
nixos/paperless: add module options for automated exports
Browse files Browse the repository at this point in the history
Paperless includes a document exporter that can be used for e.g.
backups.

This change extends the module to provide a way to enable and configure
a timer, export settings, pre- and post-processing
scripts (e.g. to ship the backup somewhere else, clean up, ...).

It works out of the box when just enabling it but can be customized.

Includes suitable tests.
  • Loading branch information
ctheune committed Jan 6, 2025
1 parent 14f4013 commit 865ab91
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 4 deletions.
3 changes: 3 additions & 0 deletions nixos/doc/manual/release-notes/rl-2505.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@

- `bind.cacheNetworks` now only controls access for recursive queries, where it previously controlled access for all queries.

- The paperless module now has an option for regular automatic export of
documents data using the integrated document exporter.

- Caddy can now be built with plugins by using `caddy.withPlugins`, a `passthru` function that accepts an attribute set as a parameter. The `plugins` argument represents a list of Caddy plugins, with each Caddy plugin being a versioned module. The `hash` argument represents the `vendorHash` of the resulting Caddy source code with the plugins added.

Example:
Expand Down
76 changes: 72 additions & 4 deletions nixos/modules/services/misc/paperless.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, options, pkgs, lib, ... }:
let
cfg = config.services.paperless;

Expand Down Expand Up @@ -82,7 +82,7 @@ let
};
in
{
meta.maintainers = with lib.maintainers; [ leona SuperSandro2000 erikarvstedt ];
meta.maintainers = with lib.maintainers; [ leona SuperSandro2000 erikarvstedt atemu theuni ];

imports = [
(lib.mkRenamedOptionModule [ "services" "paperless-ng" ] [ "services" "paperless" ])
Expand Down Expand Up @@ -252,9 +252,42 @@ in
'';
};
};

exporter = {
enable = lib.mkEnableOption "regular automatic document exports";

directory = lib.mkOption {
type = lib.types.str;
default = cfg.dataDir + "/export";
defaultText = "\${dataDir}/export";
description = "Directory to store export.";
};

onCalendar = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = "01:30:00";
description = ''
When to run the exporter. See {manpage}`systemd.time(7)`.
`null` disables the timer; allowing you to run the
`paperless-exporter` service through other means.
'';
};

settings = lib.mkOption {
type = with lib.types; attrsOf anything;
default = {
"no-progress-bar" = true;
"no-color" = true;
"compare-checksums" = true;
"delete" = true;
};
description = "Settings to pass to the document exporter as CLI arguments.";
};
};
};

config = lib.mkIf cfg.enable {
config = lib.mkIf cfg.enable (lib.mkMerge [ {
services.redis.servers.paperless.enable = lib.mkIf enableRedis true;

services.postgresql = lib.mkIf cfg.database.createLocally {
Expand Down Expand Up @@ -439,5 +472,40 @@ in
gid = config.ids.gids.paperless;
};
};
};
}

(lib.mkIf cfg.exporter.enable {
systemd.tmpfiles.rules = [
"d '${cfg.exporter.directory}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
];

services.paperless.exporter.settings = options.services.paperless.exporter.settings.default;

systemd.services.paperless-exporter = {
startAt = lib.defaultTo [] cfg.exporter.onCalendar;
serviceConfig = {
User = cfg.user;
WorkingDirectory = cfg.dataDir;
};
unitConfig = let
services = [
"paperless-consumer.service"
"paperless-scheduler.service"
"paperless-task-queue.service"
"paperless-web.service" ];
in {
# Shut down the paperless services while the exporter runs
Conflicts = services;
After = services;
# Bring them back up afterwards, regardless of pass/fail
OnFailure = services;
OnSuccess = services;
};
enableStrictShellChecks = true;
script = ''
./paperless-manage document_exporter ${cfg.exporter.directory} ${lib.cli.toGNUCommandLineShell {} cfg.exporter.settings}
'';
};
})
]);
}
28 changes: 28 additions & 0 deletions nixos/tests/paperless.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import ./make-test-python.nix ({ lib, ... }: {
services.paperless = {
enable = true;
passwordFile = builtins.toFile "password" "admin";

exporter = {
enable = true;

settings = {
"no-color" = lib.mkForce false; # override a default option
"no-thumbnail" = true; # add a new option
};
};
};
};
postgres = { config, pkgs, ... }: {
Expand Down Expand Up @@ -73,6 +82,25 @@ import ./make-test-python.nix ({ lib, ... }: {
metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/"))
assert "original_checksum" in metadata
with subtest("Exporter"):
node.succeed("systemctl start --wait paperless-exporter")
node.wait_for_unit("paperless-web.service")
node.wait_for_unit("paperless-consumer.service")
node.wait_for_unit("paperless-scheduler.service")
node.wait_for_unit("paperless-task-queue.service")
node.succeed("ls -lah /var/lib/paperless/export/manifest.json")
timers = node.succeed("systemctl list-timers paperless-exporter")
print(timers)
assert "paperless-exporter.timer paperless-exporter.service" in timers, "missing timer"
assert "1 timers listed." in timers, "incorrect number of timers"
# Double check that our attrset option override works as expected
cmdline = node.succeed("grep 'paperless-manage' $(systemctl cat paperless-exporter | grep ExecStart | cut -f 2 -d=)")
print(f"Exporter command line {cmdline!r}")
assert cmdline.strip() == "./paperless-manage document_exporter /var/lib/paperless/export --compare-checksums --delete --no-progress-bar --no-thumbnail", "Unexpected exporter command line"
test_paperless(simple)
simple.send_monitor_command("quit")
simple.wait_for_shutdown()
Expand Down

0 comments on commit 865ab91

Please sign in to comment.