mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
feat(documents): add Recently deleted for deleted documents
Deleting a document only stamps a tombstone and the purge removes it
after 30 days, but the only way back was the undo toast, gone a few
seconds after the delete. Recently deleted lists the deleted documents
of the record with type, filing date, where an import came from, when
each was deleted and how long it has left, and offers Restore, Restore
all and Delete now.
GET /api/documents/inbound/deleted lists them (owner-only, capped at 200
with the list-cap meta). POST /api/documents/inbound/{id}/purge removes
one deleted document for good through the same function the nightly
purge runs, so its import keys and aliases still move into the ledger
and an importer re-sending it is still answered "deleted"; a live
document answers 409 documents.inbound.notDeleted. The usage read gains
deletedCount, 0 under a grant. The restore route and bulk restore
share one restore, which now also clears ledger entries for the
document's own keys.
The vault header shows a quiet "Recently deleted (n)" link only while
something is in it, and the delete toasts and confirmations say that
deleted documents stay there for 30 days. The purge's queue facts and
grace window move to document-purge-queue.ts so the worker registry
still loads none of the work.
Refs #1275
This commit is contained in:
1 parent
1ea14e9100
commit
4752115d7d
44 files changed
+2277
-270
No files matched your search
+202
-11
@@ -19558,9 +19558,10 @@ paths:
|
||||
tags:
|
||||
- Documents
|
||||
summary: Restore a discarded document
|
||||
description: Clears the soft-delete tombstone within the 30-day undo grace. 409 when the tombstone is already purged
|
||||
(the undo window is over) or when an identical live document blocks the restore (`meta.reason =
|
||||
"duplicateExists"`). Restoring an already-live document is a no-op success.
|
||||
description: Clears the soft-delete tombstone within the 30-day undo grace, and forgets any import-ledger entry for the
|
||||
document's source keys. 409 when the tombstone is already purged (the undo window is over, or the document was
|
||||
deleted for good) or when an identical live document blocks the restore (`meta.reason = "duplicateExists"`).
|
||||
Restoring an already-live document is a no-op success.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
@@ -19584,6 +19585,60 @@ paths:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
/api/documents/inbound/deleted:
|
||||
get:
|
||||
tags:
|
||||
- Documents
|
||||
summary: List Recently deleted
|
||||
description: "The caller's deleted documents that the purge has not removed yet, newest deletion first, metadata only.
|
||||
Each carries `purgeAt`, when the daily purge becomes due for it. Restore one with `POST
|
||||
/api/documents/inbound/{id}/restore` (or the bulk `restore` action); remove one for good ahead of time with
|
||||
`POST /api/documents/inbound/{id}/purge`. Capped at 200 rows (`meta.truncated`, `meta.limit`). Owner-only:
|
||||
refused while acting inside somebody else's record."
|
||||
responses:
|
||||
"200":
|
||||
description: Recently deleted.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DeletedDocumentListEnvelope"
|
||||
"401": *a1
|
||||
"403": *a9
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
/api/documents/inbound/{id}/purge:
|
||||
post:
|
||||
tags:
|
||||
- Documents
|
||||
summary: Delete a deleted document for good
|
||||
description: "Removes one deleted document now instead of when the grace window ends, through the same purge the daily
|
||||
job runs: the row and everything that cascades from it go, and an imported document's source keys are kept in
|
||||
the import ledger, so an importer re-sending it is still answered as deleted. Only a deleted document can be
|
||||
purged: a live one answers 409 `documents.inbound.notDeleted`. 404 `documents.inbound.notFound` when the caller
|
||||
has no such document. Owner-only."
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Removed for good.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DocumentPurgeResultEnvelope"
|
||||
"401": *a1
|
||||
"403": *a9
|
||||
"409":
|
||||
description: The document is not deleted.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
/api/documents/inbound/{id}/original:
|
||||
get:
|
||||
tags:
|
||||
@@ -38386,14 +38441,15 @@ components:
|
||||
documents.inbound.episodeNotFound, documents.inbound.extractFailed, documents.inbound.factNotFound,
|
||||
documents.inbound.factNotPending, documents.inbound.factTypeMismatch, documents.inbound.fileTooLarge,
|
||||
documents.inbound.fileType, documents.inbound.invalidMetadata, documents.inbound.invalidQuery,
|
||||
documents.inbound.invalidUpdate, documents.inbound.localOcrDisabled, documents.inbound.notFound,
|
||||
documents.inbound.notIndexed, documents.inbound.outputLimit, documents.inbound.pdfNeedsAnthropic,
|
||||
documents.inbound.providerUnsupported, documents.inbound.quotaExceeded, documents.inbound.rateLimited,
|
||||
documents.inbound.restoreDuplicate, documents.inbound.restoreGone, documents.inbound.sourceAliasLimit,
|
||||
documents.inbound.uploadBusy, documents.inbound.uploadTimeout, documents.inbound.vaccinationNotFound,
|
||||
documents.sources.authRefused, documents.sources.badResponse, documents.sources.browserOnly,
|
||||
documents.sources.invalidAddress, documents.sources.linkTargetNotFound, documents.sources.notConnected,
|
||||
documents.sources.notFound, documents.sources.organizationRequired, documents.sources.originNotAllowed,
|
||||
documents.inbound.invalidUpdate, documents.inbound.localOcrDisabled, documents.inbound.notDeleted,
|
||||
documents.inbound.notFound, documents.inbound.notIndexed, documents.inbound.outputLimit,
|
||||
documents.inbound.pdfNeedsAnthropic, documents.inbound.providerUnsupported,
|
||||
documents.inbound.quotaExceeded, documents.inbound.rateLimited, documents.inbound.restoreDuplicate,
|
||||
documents.inbound.restoreGone, documents.inbound.sourceAliasLimit, documents.inbound.uploadBusy,
|
||||
documents.inbound.uploadTimeout, documents.inbound.vaccinationNotFound, documents.sources.authRefused,
|
||||
documents.sources.badResponse, documents.sources.browserOnly, documents.sources.invalidAddress,
|
||||
documents.sources.linkTargetNotFound, documents.sources.notConnected, documents.sources.notFound,
|
||||
documents.sources.organizationRequired, documents.sources.originNotAllowed,
|
||||
documents.sources.permissionMissing, documents.sources.rateLimited, documents.sources.redirected,
|
||||
documents.sources.tokenRequired, documents.sources.unavailable, documents.sources.unreachable,
|
||||
documents.sources.versionTooOld; encounter — encounter.invalid, encounter.link.invalid,
|
||||
@@ -79845,6 +79901,13 @@ components:
|
||||
- indexedCount
|
||||
- totalCount
|
||||
additionalProperties: false
|
||||
deletedCount:
|
||||
type: integer
|
||||
minimum: -9007199254740991
|
||||
maximum: 9007199254740991
|
||||
description: How many of the caller's documents are deleted and not yet purged (Recently deleted, `GET
|
||||
/api/documents/inbound/deleted`). Always 0 while acting inside somebody else's record, where Recently
|
||||
deleted is not offered.
|
||||
required:
|
||||
- usedBytes
|
||||
- quotaBytes
|
||||
@@ -79854,6 +79917,7 @@ components:
|
||||
- linkedProcedures
|
||||
- assistAvailable
|
||||
- contentIndex
|
||||
- deletedCount
|
||||
additionalProperties: false
|
||||
DocumentsLayoutEnvelope:
|
||||
type: object
|
||||
@@ -80369,6 +80433,133 @@ components:
|
||||
- data
|
||||
- error
|
||||
additionalProperties: false
|
||||
DeletedDocumentListEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/components/schemas/DeletedDocumentList"
|
||||
error:
|
||||
type: "null"
|
||||
meta:
|
||||
$ref: "#/components/schemas/ListCapMeta"
|
||||
required:
|
||||
- data
|
||||
- error
|
||||
- meta
|
||||
additionalProperties: false
|
||||
DeletedDocumentList:
|
||||
type: object
|
||||
properties:
|
||||
documents:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/DeletedDocument"
|
||||
graceDays:
|
||||
type: integer
|
||||
minimum: -9007199254740991
|
||||
maximum: 9007199254740991
|
||||
description: Days a deleted document stays restorable.
|
||||
required:
|
||||
- documents
|
||||
- graceDays
|
||||
additionalProperties: false
|
||||
DeletedDocument:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
enum:
|
||||
- DOCTOR_REPORT
|
||||
- DISCHARGE_LETTER
|
||||
- LAB_RESULT
|
||||
- IMAGING
|
||||
- PRESCRIPTION
|
||||
- REFERRAL
|
||||
- INSURANCE
|
||||
- VACCINATION
|
||||
- SICK_NOTE
|
||||
- OTHER
|
||||
title:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
filename:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
mimeType:
|
||||
type: string
|
||||
byteSize:
|
||||
type: integer
|
||||
minimum: -9007199254740991
|
||||
maximum: 9007199254740991
|
||||
documentDate:
|
||||
description: User-set filing date (YYYY-MM-DD), or null.
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
sourceSystem:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- PAPERLESS
|
||||
- PAPRA
|
||||
- OTHER
|
||||
- type: "null"
|
||||
description: The system an imported document came from, or null for one added in HealthLog.
|
||||
deletedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||||
purgeAt:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||||
description: "When the daily purge becomes due for this document: `deletedAt` plus `graceDays`. The purge runs once a
|
||||
day, so the document is removed at the first run after this instant."
|
||||
required:
|
||||
- id
|
||||
- kind
|
||||
- title
|
||||
- filename
|
||||
- mimeType
|
||||
- byteSize
|
||||
- documentDate
|
||||
- sourceSystem
|
||||
- deletedAt
|
||||
- purgeAt
|
||||
additionalProperties: false
|
||||
DocumentPurgeResultEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/components/schemas/DocumentPurgeResult"
|
||||
error:
|
||||
type: "null"
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
requestId:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- data
|
||||
- error
|
||||
additionalProperties: false
|
||||
DocumentPurgeResult:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
purged:
|
||||
type: boolean
|
||||
const: true
|
||||
required:
|
||||
- id
|
||||
- purged
|
||||
additionalProperties: false
|
||||
InboundExtractEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in new issue
Block a user