mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
Let a delegate read the dashboard they land on
Ten routes on `/` refused with `undeclared_mode`, which made the first page of account sharing the only page in it that does not work. Each one now declares a mode, per route rather than as a class. Eight are record reads: the dashboard snapshot, the Today digest, the badge grid, the Coach nudge signal and reminder ledger, the medication low-stock thresholds, and the two presentation blobs. The old objection to admitting an aggregate — a summary can carry a finding from a module the grant does not cover — does not hold while a grant is whole-record and all-or-nothing. Every admission site names that, so the day per-module scope lands the aggregates are findable. The layout routes split their verbs. Reading the owner's arrangement is right and, for the dashboard, forced: the snapshot already carries the layout and the client seeds the same cache cell from it, so an actor answer would put two people's layouts in one key. Writing it is not a caregiver's job, so the PUT and DELETE stay bare and refuse. Same on the medication list, where the stored order names the owner's medication ids and means nothing anywhere else. Two are actor surfaces: the UI language belongs to the person reading the screen and must not be transplanted onto the record, and the feature-flag matrix reads the settings singleton and no user row at all. Also fixes a second instance of the delegate-data-under-owner-banner defect, on the same page: the dashboard's client-side band fallback derived blood- pressure targets and the weight range from `/api/auth/me`, which answers about the caller, and painted them over the record's charts. Under a switch it now derives nothing and waits for the snapshot's own bands. The integration suite drives every admitted route with both accounts seeded, proves the two answers differ, and then demands the owner's back.
This commit is contained in:
1 parent
a0fc404fb8
commit
508cc8a60a
17 files changed
+1025
-49
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
import { apiHandler, requireRecordAuth } from "@/lib/api-handler";
|
||||
import { requireModuleEnabled, resolveModuleMap } from "@/lib/modules/gate";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { apiSuccess } from "@/lib/api-response";
|
||||
@@ -34,7 +34,22 @@ interface IosAchievement {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export const GET = apiHandler(async (request: NextRequest) => {
|
||||
const { user } = await requireAuth();
|
||||
// The record's badges. Every one of them is derived from the record's own
|
||||
// history — the metrics, the module map and the unlock dates all come off
|
||||
// the resolved account — so a delegate reads what the owner earned rather
|
||||
// than a copy of their own tally under the owner's name. Nothing here is a
|
||||
// preference of the person looking except the translation, and that resolves
|
||||
// per request from the caller's locale below.
|
||||
//
|
||||
// Admitted as an aggregate on the whole-record grant. The badge grid spans
|
||||
// every module that carries a badge category, so it joins the snapshot and
|
||||
// the digest in the set to re-examine if per-module scope ever lands.
|
||||
//
|
||||
// Read-only in fact as well as in declaration: v1.35.3 moved the unlock
|
||||
// INSERT onto the sweep job, so there is no write for a read grant to have
|
||||
// to refuse. `requireRecordAuth("read")` would refuse a non-safe method
|
||||
// anyway; this file exports no other verb.
|
||||
const { user } = await requireRecordAuth("read");
|
||||
|
||||
// v1.18.0 — when the account has the achievements module turned off the
|
||||
// whole gamification surface disappears: no badge evaluation, no unlock
|
||||
|
||||
Reference in new issue
Block a user