mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
fix: close four small gaps around the record surface
The privacy dashboard re-implemented the audit-retention resolver with its own default and its own floor. Two implementations of one disclosure, quoted to the same person on two pages and by the purge job; it uses the shared resolver now. The record-content branch in the settings gate matched a classification rather than a page, so a second manage-writable destination would have rendered the allergy and family-history managers under its own heading without anybody choosing that. It resolves through a slug map, and the contract test fails if the map and the classification disagree in either direction — a destination cannot join the kind without somebody deciding what it renders. The managed-profile nav filter assumed a guardian grant is always manage. It is today, which is why the check was easy to leave out; an entry below that level would have listed destinations the gate refuses. Interpolation used a replacement string, and String.replace reads $&, $`, $', $1 and $$ out of one. A display name containing $' rewrote the sentence around it — and display names land in exactly the sentences that say who did what in somebody's health record. A replacer function has no $ grammar at all, which removes the class rather than escaping the members of it somebody thought of.
This commit is contained in:
1 parent
5de488c35c
commit
6ae0cc993f
9 files changed
+272
-14
No files matched your search
@@ -8,17 +8,25 @@
|
||||
*/
|
||||
import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
import { apiSuccess } from "@/lib/api-response";
|
||||
import { getAuditLogRetentionDays } from "@/lib/jobs/audit-log-cleanup";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { ENCRYPTED_COLUMNS } from "@/lib/crypto/encrypted-columns";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/**
|
||||
* Retention windows. These mirror the defaults + env names the cleanup jobs
|
||||
* read (`coach-message-cleanup.ts`, `audit-log-cleanup.ts`, the reminder
|
||||
* cleanup handlers); read here directly so the dashboard never imports a
|
||||
* pg-boss job module. Off-host backup retention is operator infrastructure
|
||||
* (not an app setting), so it is disclosed as prose, not a number.
|
||||
* Retention windows, as the privacy dashboard states them.
|
||||
*
|
||||
* The audit window comes from `getAuditLogRetentionDays()` rather than from a
|
||||
* second parse of the same variable. It used to be re-implemented here with
|
||||
* its own default and its own floor, which is two implementations of one
|
||||
* disclosure — and this surface, the sharing panel and the purge job all quote
|
||||
* the number to the same person. A drift between them is a page telling
|
||||
* somebody their data is kept for a length of time it is not.
|
||||
*
|
||||
* The rest still parse locally so the dashboard never imports a pg-boss job
|
||||
* module. Off-host backup retention is operator infrastructure (not an app
|
||||
* setting), so it is disclosed as prose, not a number.
|
||||
*/
|
||||
function resolveRetention() {
|
||||
const intEnv = (name: string, fallback: number, min: number): number => {
|
||||
@@ -28,7 +36,7 @@ function resolveRetention() {
|
||||
};
|
||||
return {
|
||||
coachMessagesDays: intEnv("COACH_MESSAGE_RETENTION_DAYS", 365, 30),
|
||||
auditLogDays: intEnv("AUDIT_LOG_RETENTION_DAYS", 365, 7),
|
||||
auditLogDays: getAuditLogRetentionDays(),
|
||||
// The push-attempt + mood-dispatch ledgers are a fixed 90-day window.
|
||||
deliveryLogDays: 90,
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user