mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
Second layer of the module enable/disable model: an operator can turn a module off for the whole server, and an operator-disabled module is off for every account regardless of personal preference — mirroring the coach master flag above the per-user opt-out. Storage is a DISABLED allowlist on AppSettings.moduleAvailabilityJson (absent/empty/true = available, explicit false = disabled), resolved by getOperatorModuleAvailability(). The gate now resolves every toggleable module as operatorAvailable(key) AND userEnabled(key); cycle and coach keep their existing delegation but are still AND-ed with operator availability. Core domains are never disableable. GET/PATCH /api/admin/settings/module-availability is cookie-only (requireAdmin); a strict per-module-key schema rejects core domains and unknown keys at the boundary.
40 lines
1.9 KiB
SQL
40 lines
1.9 KiB
SQL
-- v1.18.0 — operator-level server-wide module availability.
|
|
--
|
|
-- A single nullable JSONB blob on `app_settings` carrying a DISABLED
|
|
-- allowlist for the toggleable ("secondary") modules — the second layer of
|
|
-- the two-layer module model. This sits ABOVE the per-user
|
|
-- `users.module_preferences_json`: an operator-disabled module is off for
|
|
-- EVERY account regardless of that account's personal preference, exactly
|
|
-- like the coach master flag (`assistant_coach_enabled`) sits above
|
|
-- `users.disable_coach`.
|
|
--
|
|
-- The contract mirrors the per-user column (default-on):
|
|
--
|
|
-- * NULL column → every module available
|
|
-- * empty object `{}` → every module available
|
|
-- * key absent → that module available
|
|
-- * key present & `false` → that module DISABLED server-wide
|
|
-- * key present & `true` → available (redundant-but-allowed)
|
|
--
|
|
-- No backfill — every existing instance keeps every module available. The
|
|
-- application resolver (`src/lib/modules/gate.ts`) tolerates a missing
|
|
-- column / null row and reads all-available, so a partial deploy where the
|
|
-- schema lands ahead of (or behind) the app code keeps working.
|
|
--
|
|
-- CORE domains (weight, blood pressure, pulse, medications) are never
|
|
-- written here and the admin write endpoint refuses them, so the
|
|
-- measurement engine + meds can never be turned off server-wide either.
|
|
--
|
|
-- No additional index — the single `app_settings` singleton row is read by
|
|
-- primary key, matching the convention of every other JSON column on the
|
|
-- table.
|
|
--
|
|
-- Idempotent guard (`IF NOT EXISTS`) so reruns are safe on prod.
|
|
--
|
|
-- Reversibility: down migration is
|
|
-- ALTER TABLE "app_settings" DROP COLUMN IF EXISTS "module_availability_json";
|
|
-- A roll-back loses operator-level opt-outs (every module falls back to
|
|
-- available), which is the safe default — no data is destroyed.
|
|
ALTER TABLE "app_settings"
|
|
ADD COLUMN IF NOT EXISTS "module_availability_json" JSONB;
|