Files
HealthLog/src/lib/illness/gate.ts
T
Marc-André Bombeck bc94a5893f feat(modules): default the illness journal on
The illness module shipped opt-in/default-off via a bornGated flag, the
inverse of the standard disabled-allowlist. Make it a normal optional
module that defaults on (opt-out), matching labs and its siblings.

No module used bornGated other than illness, so drop the flag, its
isBornGatedModule helper, and the gate resolver's born-gated branch
entirely. The module stays a normal toggle under What you track and the
operator-level availability override is unchanged; an account that wants
it off sets an explicit false. Refresh the gate, doctor-report, labs and
biomarkers comments plus the OpenAPI route descriptions accordingly.
2026-06-17 14:10:46 +02:00

55 lines
2.1 KiB
TypeScript

/**
* Illness / condition-journal feature gate (v1.18.1).
*
* The illness journal is a standard optional module (default-on, opt-out)
* and resolves its enabled-state through the module foundation — there is
* no per-domain profile row and no second source of truth. `isIllnessEnabled`
* and the `requireIllnessEnabled` 403 guard both delegate to the module
* gate's `isModuleEnabled(userId, "illness")`, which reads the disabled
* allowlist (`modulePreferencesJson.illness !== false`) AND the operator
* server-wide availability. This mirrors `src/lib/cycle/gate.ts` so every
* `/api/illness/*` route gates identically.
*/
import { apiError } from "@/lib/api-response";
import { isModuleEnabled, requireModuleEnabled } from "@/lib/modules/gate";
/** Wire `errorCode` the iOS retry classifier branches on. */
export const ILLNESS_DISABLED_ERROR_CODE = "illness.disabled";
/**
* Fully-resolved illness availability for an account: the per-user opt-out
* AND the operator server-wide kill-switch, via the module foundation.
*/
export function isIllnessEnabled(userId: string): Promise<boolean> {
return isModuleEnabled(userId, "illness");
}
/**
* Route-guard outcome: a clear pass or a ready 403 envelope to return
* verbatim — mirrors `CycleGateResult`.
*/
export type IllnessGateResult =
| { enabled: true }
| { enabled: false; response: Response };
/**
* Enforce the gate for an `/api/illness/*` route. Returns a 403
* `illness.disabled` envelope when the account has turned the module off
* (or the operator turned it off server-wide) — even with a valid Bearer
* token. Delegates to `requireModuleEnabled` but re-stamps the
* illness-specific `errorCode` so the iOS classifier branches cleanly.
*/
export async function requireIllnessEnabled(
userId: string,
): Promise<IllnessGateResult> {
const gate = await requireModuleEnabled(userId, "illness");
if (gate.enabled) return { enabled: true };
return {
enabled: false,
response: apiError("Illness journal is not enabled", 403, {
errorCode: ILLNESS_DISABLED_ERROR_CODE,
module: "illness",
}),
};
}