Skip to content

Commit 6355c63

Browse files
authored
Provide NixOS module option to enable the paperless exporter. (#242084)
2 parents b110150 + 865ab91 commit 6355c63

File tree

3 files changed

+103
-4
lines changed

3 files changed

+103
-4
lines changed

nixos/doc/manual/release-notes/rl-2505.section.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@
342342

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

345+
- The paperless module now has an option for regular automatic export of
346+
documents data using the integrated document exporter.
347+
345348
- 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.
346349

347350
Example:

nixos/modules/services/misc/paperless.nix

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{ config, pkgs, lib, ... }:
1+
{ config, options, pkgs, lib, ... }:
22
let
33
cfg = config.services.paperless;
44

@@ -82,7 +82,7 @@ let
8282
};
8383
in
8484
{
85-
meta.maintainers = with lib.maintainers; [ leona SuperSandro2000 erikarvstedt ];
85+
meta.maintainers = with lib.maintainers; [ leona SuperSandro2000 erikarvstedt atemu theuni ];
8686

8787
imports = [
8888
(lib.mkRenamedOptionModule [ "services" "paperless-ng" ] [ "services" "paperless" ])
@@ -252,9 +252,42 @@ in
252252
'';
253253
};
254254
};
255+
256+
exporter = {
257+
enable = lib.mkEnableOption "regular automatic document exports";
258+
259+
directory = lib.mkOption {
260+
type = lib.types.str;
261+
default = cfg.dataDir + "/export";
262+
defaultText = "\${dataDir}/export";
263+
description = "Directory to store export.";
264+
};
265+
266+
onCalendar = lib.mkOption {
267+
type = lib.types.nullOr lib.types.str;
268+
default = "01:30:00";
269+
description = ''
270+
When to run the exporter. See {manpage}`systemd.time(7)`.
271+
272+
`null` disables the timer; allowing you to run the
273+
`paperless-exporter` service through other means.
274+
'';
275+
};
276+
277+
settings = lib.mkOption {
278+
type = with lib.types; attrsOf anything;
279+
default = {
280+
"no-progress-bar" = true;
281+
"no-color" = true;
282+
"compare-checksums" = true;
283+
"delete" = true;
284+
};
285+
description = "Settings to pass to the document exporter as CLI arguments.";
286+
};
287+
};
255288
};
256289

257-
config = lib.mkIf cfg.enable {
290+
config = lib.mkIf cfg.enable (lib.mkMerge [ {
258291
services.redis.servers.paperless.enable = lib.mkIf enableRedis true;
259292

260293
services.postgresql = lib.mkIf cfg.database.createLocally {
@@ -439,5 +472,40 @@ in
439472
gid = config.ids.gids.paperless;
440473
};
441474
};
442-
};
475+
}
476+
477+
(lib.mkIf cfg.exporter.enable {
478+
systemd.tmpfiles.rules = [
479+
"d '${cfg.exporter.directory}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
480+
];
481+
482+
services.paperless.exporter.settings = options.services.paperless.exporter.settings.default;
483+
484+
systemd.services.paperless-exporter = {
485+
startAt = lib.defaultTo [] cfg.exporter.onCalendar;
486+
serviceConfig = {
487+
User = cfg.user;
488+
WorkingDirectory = cfg.dataDir;
489+
};
490+
unitConfig = let
491+
services = [
492+
"paperless-consumer.service"
493+
"paperless-scheduler.service"
494+
"paperless-task-queue.service"
495+
"paperless-web.service" ];
496+
in {
497+
# Shut down the paperless services while the exporter runs
498+
Conflicts = services;
499+
After = services;
500+
# Bring them back up afterwards, regardless of pass/fail
501+
OnFailure = services;
502+
OnSuccess = services;
503+
};
504+
enableStrictShellChecks = true;
505+
script = ''
506+
./paperless-manage document_exporter ${cfg.exporter.directory} ${lib.cli.toGNUCommandLineShell {} cfg.exporter.settings}
507+
'';
508+
};
509+
})
510+
]);
443511
}

nixos/tests/paperless.nix

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ import ./make-test-python.nix ({ lib, ... }: {
88
services.paperless = {
99
enable = true;
1010
passwordFile = builtins.toFile "password" "admin";
11+
12+
exporter = {
13+
enable = true;
14+
15+
settings = {
16+
"no-color" = lib.mkForce false; # override a default option
17+
"no-thumbnail" = true; # add a new option
18+
};
19+
};
1120
};
1221
};
1322
postgres = { config, pkgs, ... }: {
@@ -73,6 +82,25 @@ import ./make-test-python.nix ({ lib, ... }: {
7382
metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/"))
7483
assert "original_checksum" in metadata
7584
85+
with subtest("Exporter"):
86+
node.succeed("systemctl start --wait paperless-exporter")
87+
node.wait_for_unit("paperless-web.service")
88+
node.wait_for_unit("paperless-consumer.service")
89+
node.wait_for_unit("paperless-scheduler.service")
90+
node.wait_for_unit("paperless-task-queue.service")
91+
92+
node.succeed("ls -lah /var/lib/paperless/export/manifest.json")
93+
94+
timers = node.succeed("systemctl list-timers paperless-exporter")
95+
print(timers)
96+
assert "paperless-exporter.timer paperless-exporter.service" in timers, "missing timer"
97+
assert "1 timers listed." in timers, "incorrect number of timers"
98+
99+
# Double check that our attrset option override works as expected
100+
cmdline = node.succeed("grep 'paperless-manage' $(systemctl cat paperless-exporter | grep ExecStart | cut -f 2 -d=)")
101+
print(f"Exporter command line {cmdline!r}")
102+
assert cmdline.strip() == "./paperless-manage document_exporter /var/lib/paperless/export --compare-checksums --delete --no-progress-bar --no-thumbnail", "Unexpected exporter command line"
103+
76104
test_paperless(simple)
77105
simple.send_monitor_command("quit")
78106
simple.wait_for_shutdown()

0 commit comments

Comments
 (0)