mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
fix(clinician-share): a document share exposes only the document
Sharing a single document from the vault minted a full-record share: the create form always defaulted `resourceTypes` to ["Patient","Observation"] and never signalled a documents-only intent, so the served /c/<token> view aggregated the whole doctor report — every vital, lab, medication and wellness figure — alongside the attached file. An empty section blob was indistinguishable from "defaults", so the report always resolved to the full record. Introduce an explicit documents-only share: - add a `documentOnly` flag to the create payload; when set the server forces an empty report scope (all-off sections, no FHIR resource types, FHIR API off) and requires at least one document, so a document link can never widen into a record — whatever the body sends. - gate the clinician-view aggregation on an enabled section: a share with no section on never calls the report aggregator, so no health metric leaves the database. The recipient sees only the attached document(s). - the document detail Share action opens the create form in documents-only mode: hide the FHIR toggle, the FHIR resource-type checkboxes and the history-range selector, keep the picker/label/expiry/passphrase, and always carry the launched document into the created link. - Settings sharing keeps the full record-share form unchanged. Extract the create-payload builder as a pure function and cover the scope contract, the empty-scope data load, and the served-view metric-free guarantee with tests; refresh the share e2e and regenerate the OpenAPI contract.
This commit is contained in:
1 parent
fa42c8bc0b
commit
d4965033c2
13 files changed
+503
-92
No files matched your search
@@ -62,6 +62,16 @@ export const createShareLinkSchema = z
|
||||
.array(z.string().trim().min(1).max(64))
|
||||
.max(SHARE_LINK_MAX_DOCUMENTS)
|
||||
.optional(),
|
||||
/**
|
||||
* v1.28.13 — a documents-only share. When true the server FORCES an empty
|
||||
* report scope (no sections, no FHIR resource types, FHIR API off) and
|
||||
* mints a link that serves ONLY the attached documents — never a health
|
||||
* metric. "Share this document" means the document, not the whole record.
|
||||
* A documents-only share MUST carry at least one `documentId` (enforced
|
||||
* below). Any `sections` / `resourceTypes` / `allowFhirApi` sent alongside
|
||||
* are ignored server-side so a document link can never widen into a record.
|
||||
*/
|
||||
documentOnly: z.boolean().optional(),
|
||||
expiresAt: z.iso
|
||||
.datetime({ offset: true })
|
||||
.refine((v) => new Date(v).getTime() > Date.now(), {
|
||||
@@ -77,7 +87,11 @@ export const createShareLinkSchema = z
|
||||
v.rangeEnd == null ||
|
||||
new Date(v.rangeEnd).getTime() >= new Date(v.rangeStart).getTime(),
|
||||
{ message: "rangeEnd must not precede rangeStart", path: ["rangeEnd"] },
|
||||
);
|
||||
)
|
||||
.refine((v) => !v.documentOnly || (v.documentIds?.length ?? 0) > 0, {
|
||||
message: "A documents-only share must carry at least one document",
|
||||
path: ["documentIds"],
|
||||
});
|
||||
|
||||
export type CreateShareLinkInput = z.infer<typeof createShareLinkSchema>;
|
||||
|
||||
|
||||
@@ -87,6 +87,38 @@ export const DEFAULT_DOCTOR_REPORT_PREFS: DoctorReportPrefs = {
|
||||
familyHistory: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* The "no section" shape — every toggle OFF. A share link that carries ONLY
|
||||
* documents (the "share this document, not the whole record" flow) persists
|
||||
* this so the clinician view resolves to an empty report scope and NO health
|
||||
* data is ever aggregated for it. Distinct from the null / `{}` case, which
|
||||
* resolves to {@link DEFAULT_DOCTOR_REPORT_PREFS} (a full record share).
|
||||
*/
|
||||
export const EMPTY_DOCTOR_REPORT_PREFS: DoctorReportPrefs = {
|
||||
bp: false,
|
||||
weight: false,
|
||||
pulse: false,
|
||||
bmi: false,
|
||||
mood: false,
|
||||
compliance: false,
|
||||
sleep: false,
|
||||
cycle: false,
|
||||
labs: false,
|
||||
allergies: false,
|
||||
familyHistory: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether any report section is enabled. A resolved prefs object with every
|
||||
* flag OFF means "no health report" — the load-bearing signal a documents-only
|
||||
* share reads to serve zero metrics. The clinician-view data loader gates the
|
||||
* whole doctor-report aggregation on this: false ⇒ never touch the DB for
|
||||
* health data.
|
||||
*/
|
||||
export function hasAnyReportSection(prefs: DoctorReportPrefs): boolean {
|
||||
return Object.values(prefs).some(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a row's `doctorReportPrefsJson` Json blob into a typed
|
||||
* `DoctorReportPrefs`, falling back to the documented defaults when the
|
||||
|
||||
Reference in new issue
Block a user