mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
feat(documents): view and download the original inbound document
The inbound-document store keeps the uploaded doctor letter encrypted at rest (InboundDocument.contentEncrypted, AES-256-GCM), but there was no way to read the original back — the decrypt helper had been dropped as unused. - Restore decryptDocumentFromBytes in the document store, fail-closed on a bad / missing key id. - Add GET /api/documents/inbound/[id]/original: owner-scoped (userId-narrowed query), module-gated, returns the decrypted bytes with the stored MIME type. A PDF / image is served inline so the browser can render it; other types download as an attachment. The filename is sanitised before it reaches the Content-Disposition header. The bytes are PHI — never logged, never served cross-user, and a decrypt error fails closed (500) rather than returning ciphertext. Cache-Control is private, no-store. - Surface a 'View original' action in the document review screen. - Document the route in the OpenAPI registry and regenerate the spec. - Cover the route with a test: owner-scoped success, attachment fallback, 404 for another user's document, and fail-closed on decrypt error. i18n: viewOriginal across all six locales.
This commit is contained in:
1 parent
6e204d054a
commit
08314c7c09
12 files changed
+418
-10
No files matched your search
@@ -8696,6 +8696,40 @@ paths:
|
||||
"401": *a1
|
||||
"422": *a3
|
||||
"429": *a4
|
||||
/api/documents/inbound/{id}/original:
|
||||
get:
|
||||
tags:
|
||||
- Documents
|
||||
summary: View / download the original document
|
||||
description: "Decrypts and serves the raw uploaded document (the bytes stored encrypted at rest under
|
||||
`InboundDocument.contentEncrypted`). Owner-scoped + gated on the opt-in `inboundDocuments` module. The response
|
||||
carries the stored `mimeType`: a PDF / image is served inline (`Content-Disposition: inline`) so the browser can
|
||||
render it; any other type downloads as an attachment. Fails closed (500) on a decrypt error — it never returns
|
||||
the ciphertext."
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: The decrypted original document bytes with the stored MIME type.
|
||||
content:
|
||||
application/pdf:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
image/*:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
"401": *a1
|
||||
"422": *a3
|
||||
"429": *a4
|
||||
/api/documents/inbound/{id}/facts/{factId}:
|
||||
patch:
|
||||
tags:
|
||||
|
||||
Reference in new issue
Block a user