fix(documents): key imports by source instance, keep a token on its origin, and harden the import path

- Source keys carry the normalised origin of the Paperless-ngx or Papra
  they came from (source_instance on the document, the alias and the
  purge ledger; unique NULLS NOT DISTINCT). A key stored without one
  (v1.39.2 imports) matches any instance of its system, and a lookup
  without one matches any stored instance. The upload route, the lookup
  route and the import script accept or send it; backups carry it.
- Saving a connection reuses the stored token only for the same origin;
  a new origin needs the token again (422 tokenRequired).
- Document ids are checked against what each system issues (Paperless
  digits, Papra [A-Za-z0-9_-], never . or ..) before any URL is built;
  the Papra organization id likewise.
- Paperless API 9 filters dates on created__gte / created__lte, in the
  picker and the script. A search page the local re-check empties reads
  on, up to five source pages. Tags are read past the first 250.
- The import's key pre-check uses the lookup allowance, a failed fetch
  no longer charges the upload allowance, and one import per key runs at
  a time under an advisory lock, so a double click downloads once.
- Reading and deleting one's own connection works with the documents
  module off and the operator list unset.
This commit is contained in:
Marc-André Bombeck committed 2026-09-26 14:20:17 +02:00
1 parent 6029c7c30d
commit 1eac05b454
24 files changed
+886 -209

No files matched your search

@@ -424,6 +424,7 @@ describe("import-documents.mjs — Paperless-ngx", () => {
kind: "LAB_RESULT",
sourceSystem: "PAPERLESS",
sourceId: "1",
sourceInstance: new URL(pl.url).origin,
aiRead: "defer",
},
});
@@ -664,6 +665,7 @@ describe("import-documents.mjs — Papra", () => {
documentDate: "2019-06-01",
sourceSystem: "PAPRA",
sourceId: "doc_a",
sourceInstance: new URL(pp.url).origin,
});
// --ai-read: no deferral field at all.
expect(sent[0].aiRead).toBeUndefined();