feat(documents): search and import from Paperless-ngx and Papra on request

A person stores one connection per system (base address, Papra
organization id, API token encrypted at rest) and can search it by
document name with tag and date filters, then import picked documents
one per request through the same ingest path as an upload: source-key
answer before any download, content dedup, deleted stays deleted, quota,
provenance, and the person's own AI reading setting. An import can link
the document to the visit, condition or vaccination it was picked for.

The operator lists the instances in DOCUMENT_SOURCE_ORIGINS; unset, the
feature is off. Only listed origins are contacted, through safeFetch with
the origin pinned, redirects refused, timeouts and byte caps. The routes
are cookie-only and owner-only. The token is registered for rotation,
removed by Delete all data, and not carried in backups.
This commit is contained in:
Marc-André Bombeck committed 2026-09-26 13:02:36 +02:00
1 parent 89b18e001e
commit fdc60f69c2
26 files changed
+2305 -1

No files matched your search

+11
View File
@@ -245,6 +245,17 @@
}
]
},
{
"name": "Document picker",
"description": "Operator grant for the document picker, which lets a person search their own Paperless-ngx or Papra instance and import picked documents. Unset, the picker is off. Optional.",
"required": false,
"variables": [
{
"name": "DOCUMENT_SOURCE_ORIGINS",
"purpose": "Comma-separated exact origins (scheme://host[:port], no path, no wildcard, no CIDR), e.g. \"https://paperless.example.com,http://papra.lan:1221\". Only listed origins are contacted, public or private; redirects are not followed; link-local, metadata and unspecified addresses stay refused even when listed; a malformed entry is logged once and grants nothing."
}
]
},
{
"name": "Off-host backups",
"description": "Required ALL-OR-NONE: any of these without the rest disables backups silently.",
+10
View File
@@ -328,6 +328,16 @@ async function main() {
);
}
// ───── Document picker (String) ─────
// Column: "tokenEncrypted" — a person's Paperless-ngx / Papra API token.
results.push(
await rotateStringColumn(
"DocumentSourceConnection",
"tokenEncrypted",
prisma.documentSourceConnection,
),
);
// ───── AppSettings — operator credentials (String) ─────
// Columns: "adminAiKeyEncrypted" "webPushVapidPrivateKeyEncrypted"
// "adminCodexAccessTokenEncrypted" "adminCodexRefreshTokenEncrypted"