mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
reconcile: fold in the outstanding hardening and data-integrity fixes
Merge the eight reviewed audit fixes that were cut a week ago and never landed. Main has moved on since, so each fix was re-checked against the current tree and each of the five conflicts resolved by understanding both sides. Kept, still open on main: - Proxy early exits now carry the transport security header subset via a shared helper. Reconciled with main's /api/health worker-only carve-out. - Batch measurement ingest bounds the entry instant so a future-dated row can no longer win "latest reading" or poison the rollup buckets. - Web Push dials through the pinned safeFetch dispatcher with requirePublicHost, closing the last user-controlled-host egress. - Mood bulk upsert and its tag/factor links run in one transaction, so a rejected rating rolls the mood row back instead of leaving a tagless row. - Full backup carries NutrientIntakeDay with its unit and source. Folded into main's portable-export / disaster-recovery split. - Live daily-series fallback restores the min/max spread band the tiered read already emitted. - Coach stream aborts its in-flight reader on unmount and resets send state on a conversation switch. - Manual water writes share the day-bound helper, nutrient add clamps after the increment, and the thiamin cap drops to a real supplement ceiling. - Compliance point window widens symmetrically instead of wrapping overnight. - Oura, Polar and Fitbit token helpers classify an empty or non-JSON 2xx body as a handled transient error. - WebAuthn relying-party fallback to localhost is gated to development. - Chart-token strip list is derived from the allowlist and pinned by a test. - Regression tests pin the snapshot-cache tenant key, the GlitchTip path-secret redaction and the Withings credentials delete. Superseded by main, tests re-pointed at the current shape: - The intake CSV import was rewritten on main to run through a background job with a single-active-job admission gate. The future-date bound is re-applied at request validation and the per-user limiter is kept for parity with the bulk twin; the old synchronous-write test is replaced.
This commit is contained in:
commit
b1e36d9fb0
51 files changed
+2066
-228
No files matched your search
@@ -1241,6 +1241,12 @@ export function CoachConversation({
|
||||
// seed so a pending attachment can't leak across a thread switch.
|
||||
setPendingAttachmentIds([]);
|
||||
dispatchGuided({ type: "RESET" });
|
||||
// Drop the finished stream's trailing state, as `handleNewChat`
|
||||
// already does. Without it the previous thread's last assistant
|
||||
// or error bubble renders appended to the newly selected thread
|
||||
// — `streamingActive` stays true because that messageId is
|
||||
// absent from the new thread's messages — until the next send.
|
||||
send.reset();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -742,6 +742,17 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) {
|
||||
optsRef.current = opts;
|
||||
}, [opts]);
|
||||
|
||||
// Abort any in-flight stream when the hook unmounts. The drawer works around
|
||||
// this via `handleOpenChange`, but the full-page `/coach` surface passes no
|
||||
// `registerReset`, so navigating away mid-stream left the SSE reader loop
|
||||
// alive: the provider kept generating (and billing), and trailing setState
|
||||
// fired on an unmounted component. The route-change path had no other exit.
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
abortRef.current?.abort();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setStreaming(EMPTY_STREAMING);
|
||||
setOptimisticUser(null);
|
||||
|
||||
Reference in new issue
Block a user