@terreno/mcp 57.19.2 → 57.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +393 -0
- package/README.md +32 -269
- package/dist/bootstrap.d.ts +12 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +77 -122
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/ui/guidelines/core.md +1 -1
- package/dist/docs/resources/overview.md +4 -2
- package/dist/docs/resources/patterns.md +237 -176
- package/dist/docs/resources/ui.md +48 -17
- package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +0 -26
- package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +3 -2
- package/dist/docs/templates/bootstrap/frontend/app/login.template.tsx +1 -1
- package/dist/docs/upgrades/57.20.0.md +238 -0
- package/dist/docs/versioned/next/approval-rubric.md +30 -12
- package/dist/docs/versioned/next/explanation/README.md +5 -0
- package/dist/docs/versioned/next/explanation/account-linking.md +169 -0
- package/dist/docs/versioned/next/explanation/admin-interface.md +1 -1
- package/dist/docs/versioned/next/explanation/agent-guidelines.md +173 -0
- package/dist/docs/versioned/next/explanation/ai-development-loop.md +196 -0
- package/dist/docs/versioned/next/explanation/authentication.md +45 -1
- package/dist/docs/versioned/next/explanation/consent-forms.md +105 -0
- package/dist/docs/versioned/next/explanation/example-coverage.md +1 -0
- package/dist/docs/versioned/next/explanation/local-first-data.md +1 -1
- package/dist/docs/versioned/next/explanation/organizations.md +37 -0
- package/dist/docs/versioned/next/explanation/planning-plugins.md +4 -0
- package/dist/docs/versioned/next/explanation/responsive-design.md +143 -0
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +11 -0
- package/dist/docs/versioned/next/explanation/schema-metadata.md +5 -0
- package/dist/docs/versioned/next/how-to/README.md +7 -2
- package/dist/docs/versioned/next/how-to/audit-log.md +22 -3
- package/dist/docs/versioned/next/how-to/automate-terreno-upgrades.md +169 -0
- package/dist/docs/versioned/next/how-to/build-list-detail-screens.md +160 -0
- package/dist/docs/versioned/next/how-to/circleci.md +15 -5
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +673 -53
- package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +8 -5
- package/dist/docs/versioned/next/how-to/debug-with-mcp.md +393 -23
- package/dist/docs/versioned/next/how-to/enable-mcp-oauth.md +54 -3
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +9 -0
- package/dist/docs/versioned/next/how-to/password-reset.md +1 -1
- package/dist/docs/versioned/next/how-to/recover-locked-out-user.md +112 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +37 -8
- package/dist/docs/versioned/next/how-to/set-up-social-login.md +424 -0
- package/dist/docs/versioned/next/how-to/set-up-terreno-mcp.md +241 -36
- package/dist/docs/versioned/next/how-to/sign-consent-on-behalf.md +139 -0
- package/dist/docs/versioned/next/how-to/upgrade-terreno.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +47 -32
- package/dist/docs/versioned/next/implementationPlans/auth-experience.md +952 -0
- package/dist/docs/versioned/next/implementationPlans/consent-forms.md +8 -0
- package/dist/docs/versioned/next/implementationPlans/list-detail-layout.md +297 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/HANDOFF.md +211 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/AnotherWay.html +121 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/BackupCode.html +72 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/Cancelled.html +87 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/ContinueEmail.html +64 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/FirstVisit.html +81 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/Main.html +88 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/ManageAccounts.html +77 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/NotLinked.html +92 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/SigningIn.html +60 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactor.html +76 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactorError.html +81 -0
- package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/WrongPassword.html +69 -0
- package/dist/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -1
- package/dist/docs/versioned/next/plans/2026-10-23-provisional-consent.md +297 -0
- package/dist/docs/versioned/next/plans/2026-10-29-fix-mcp-oauth-native-and-machine.md +68 -0
- package/dist/docs/versioned/next/reference/README.md +1 -0
- package/dist/docs/versioned/next/reference/account-security.md +700 -0
- package/dist/docs/versioned/next/reference/admin-backend.md +3 -1
- package/dist/docs/versioned/next/reference/admin-frontend.md +225 -1
- package/dist/docs/versioned/next/reference/api.md +461 -35
- package/dist/docs/versioned/next/reference/cli.md +1 -1
- package/dist/docs/versioned/next/reference/comms.md +8 -0
- package/dist/docs/versioned/next/reference/create-terreno-app.md +3 -1
- package/dist/docs/versioned/next/reference/environment-variables.md +9 -3
- package/dist/docs/versioned/next/reference/legacy/rtk.md +19 -3
- package/dist/docs/versioned/next/reference/lifecycle-plugin.md +6 -0
- package/dist/docs/versioned/next/reference/mcp-server.md +1158 -441
- package/dist/docs/versioned/next/reference/syncdb.md +8 -1
- package/dist/docs/versioned/next/reference/ui.md +1271 -27
- package/dist/docs/versioned/next/tasks/ai-dev-loop-boost.md +28 -16
- package/dist/docs/versioned/next/tutorials/README.md +4 -0
- package/dist/httpApp.d.ts +14 -0
- package/dist/httpApp.d.ts.map +1 -0
- package/dist/httpApp.js +99 -0
- package/dist/httpApp.js.map +1 -0
- package/dist/index.js +8 -90
- package/dist/index.js.map +1 -1
- package/dist/local/localTools.d.ts.map +1 -1
- package/dist/local/localTools.js +44 -14
- package/dist/local/localTools.js.map +1 -1
- package/dist/local/metro/metroDevSession.d.ts +18 -0
- package/dist/local/metro/metroDevSession.d.ts.map +1 -1
- package/dist/local/metro/metroDevSession.js +42 -3
- package/dist/local/metro/metroDevSession.js.map +1 -1
- package/dist/local/tools/databaseQuery.d.ts.map +1 -1
- package/dist/local/tools/databaseQuery.js +3 -13
- package/dist/local/tools/databaseQuery.js.map +1 -1
- package/dist/local/tools/databaseSchema.d.ts.map +1 -1
- package/dist/local/tools/databaseSchema.js +3 -13
- package/dist/local/tools/databaseSchema.js.map +1 -1
- package/dist/local/tools/localMongo.d.ts +10 -0
- package/dist/local/tools/localMongo.d.ts.map +1 -0
- package/dist/local/tools/localMongo.js +30 -0
- package/dist/local/tools/localMongo.js.map +1 -0
- package/dist/local/tools/runtime.d.ts +18 -2
- package/dist/local/tools/runtime.d.ts.map +1 -1
- package/dist/local/tools/runtime.js +75 -20
- package/dist/local/tools/runtime.js.map +1 -1
- package/dist/local/tools/syncDb.d.ts +11 -0
- package/dist/local/tools/syncDb.d.ts.map +1 -1
- package/dist/local/tools/syncDb.js +15 -3
- package/dist/local/tools/syncDb.js.map +1 -1
- package/dist/rateLimit.d.ts +35 -0
- package/dist/rateLimit.d.ts.map +1 -0
- package/dist/rateLimit.js +112 -0
- package/dist/rateLimit.js.map +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +42 -60
- package/dist/tools.js.map +1 -1
- package/package.json +4 -3
- package/src/docs/guidelines/ui/guidelines/core.md +1 -1
- package/src/docs/resources/overview.md +4 -2
- package/src/docs/resources/patterns.md +237 -176
- package/src/docs/resources/ui.md +48 -17
- package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +0 -26
- package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +3 -2
- package/src/docs/templates/bootstrap/frontend/app/login.template.tsx +1 -1
- package/src/docs/upgrades/57.20.0.md +238 -0
- package/src/docs/versioned/next/approval-rubric.md +30 -12
- package/src/docs/versioned/next/explanation/README.md +5 -0
- package/src/docs/versioned/next/explanation/account-linking.md +169 -0
- package/src/docs/versioned/next/explanation/admin-interface.md +1 -1
- package/src/docs/versioned/next/explanation/agent-guidelines.md +173 -0
- package/src/docs/versioned/next/explanation/ai-development-loop.md +196 -0
- package/src/docs/versioned/next/explanation/authentication.md +45 -1
- package/src/docs/versioned/next/explanation/consent-forms.md +105 -0
- package/src/docs/versioned/next/explanation/example-coverage.md +1 -0
- package/src/docs/versioned/next/explanation/local-first-data.md +1 -1
- package/src/docs/versioned/next/explanation/organizations.md +37 -0
- package/src/docs/versioned/next/explanation/planning-plugins.md +4 -0
- package/src/docs/versioned/next/explanation/responsive-design.md +143 -0
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +11 -0
- package/src/docs/versioned/next/explanation/schema-metadata.md +5 -0
- package/src/docs/versioned/next/how-to/README.md +7 -2
- package/src/docs/versioned/next/how-to/audit-log.md +22 -3
- package/src/docs/versioned/next/how-to/automate-terreno-upgrades.md +169 -0
- package/src/docs/versioned/next/how-to/build-list-detail-screens.md +160 -0
- package/src/docs/versioned/next/how-to/circleci.md +15 -5
- package/src/docs/versioned/next/how-to/configure-better-auth.md +673 -53
- package/src/docs/versioned/next/how-to/create-a-terreno-app.md +8 -5
- package/src/docs/versioned/next/how-to/debug-with-mcp.md +393 -23
- package/src/docs/versioned/next/how-to/enable-mcp-oauth.md +54 -3
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +9 -0
- package/src/docs/versioned/next/how-to/password-reset.md +1 -1
- package/src/docs/versioned/next/how-to/recover-locked-out-user.md +112 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +37 -8
- package/src/docs/versioned/next/how-to/set-up-social-login.md +424 -0
- package/src/docs/versioned/next/how-to/set-up-terreno-mcp.md +241 -36
- package/src/docs/versioned/next/how-to/sign-consent-on-behalf.md +139 -0
- package/src/docs/versioned/next/how-to/upgrade-terreno.md +4 -0
- package/src/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +47 -32
- package/src/docs/versioned/next/implementationPlans/auth-experience.md +952 -0
- package/src/docs/versioned/next/implementationPlans/consent-forms.md +8 -0
- package/src/docs/versioned/next/implementationPlans/list-detail-layout.md +297 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/HANDOFF.md +211 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/AnotherWay.html +121 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/BackupCode.html +72 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/Cancelled.html +87 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/ContinueEmail.html +64 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/FirstVisit.html +81 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/Main.html +88 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/ManageAccounts.html +77 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/NotLinked.html +92 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/SigningIn.html +60 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactor.html +76 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactorError.html +81 -0
- package/src/docs/versioned/next/implementationPlans/login-redesign/boards/WrongPassword.html +69 -0
- package/src/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -1
- package/src/docs/versioned/next/plans/2026-10-23-provisional-consent.md +297 -0
- package/src/docs/versioned/next/plans/2026-10-29-fix-mcp-oauth-native-and-machine.md +68 -0
- package/src/docs/versioned/next/reference/README.md +1 -0
- package/src/docs/versioned/next/reference/account-security.md +700 -0
- package/src/docs/versioned/next/reference/admin-backend.md +3 -1
- package/src/docs/versioned/next/reference/admin-frontend.md +225 -1
- package/src/docs/versioned/next/reference/api.md +461 -35
- package/src/docs/versioned/next/reference/cli.md +1 -1
- package/src/docs/versioned/next/reference/comms.md +8 -0
- package/src/docs/versioned/next/reference/create-terreno-app.md +3 -1
- package/src/docs/versioned/next/reference/environment-variables.md +9 -3
- package/src/docs/versioned/next/reference/legacy/rtk.md +19 -3
- package/src/docs/versioned/next/reference/lifecycle-plugin.md +6 -0
- package/src/docs/versioned/next/reference/mcp-server.md +1158 -441
- package/src/docs/versioned/next/reference/syncdb.md +8 -1
- package/src/docs/versioned/next/reference/ui.md +1271 -27
- package/src/docs/versioned/next/tasks/ai-dev-loop-boost.md +28 -16
- package/src/docs/versioned/next/tutorials/README.md +4 -0
|
@@ -293,7 +293,9 @@ assigned after the write through `access.roles.assign`, which requires `rbac:ass
|
|
|
293
293
|
Admin update, bulk-patch, delete, and admin-window sync writes to another user also require the
|
|
294
294
|
actor to hold every permission the target user holds (the RoleManager target-privilege rule).
|
|
295
295
|
A delegate with `user:update` gets 403 `Cannot modify a user with permissions you do not hold` for
|
|
296
|
-
a superadmin target; bulk-patch reports that target under `failures` and patches the rest.
|
|
296
|
+
a superadmin target; bulk-patch reports that target under `failures` and patches the rest. When a
|
|
297
|
+
permission source is down and the target's grants from it are unknown, the check answers 503
|
|
298
|
+
`Permission source unavailable` instead of comparing against a partial set. Legacy
|
|
297
299
|
`admin: true` actors and writes to the actor's own record are not checked.
|
|
298
300
|
|
|
299
301
|
Changing another user's `email`, `disabled`, `betterAuthId`, `githubId`, or `emailVerified`
|
|
@@ -82,7 +82,10 @@ Wrap organization-aware admin routes with `OrgContextProvider` and render
|
|
|
82
82
|
organization navigates to `{routeBase}/orgs/:orgId` and adds
|
|
83
83
|
`X-Organization-Id` to subsequent `useAdminApi` requests. Query cache keys also
|
|
84
84
|
include the organization id, preventing rows cached for one org from appearing
|
|
85
|
-
in another.
|
|
85
|
+
in another. `useAdminApi`'s `useListQuery` and `useReadQuery` carry the organization
|
|
86
|
+
in the query argument, so a switch after a request started (such as the switcher
|
|
87
|
+
settling on a default organization) fetches the new organization's rows instead of
|
|
88
|
+
leaving the screen loading. Derive `initialOrganization` from the current pathname so context
|
|
86
89
|
follows URL changes after mount without overriding a switcher selection while
|
|
87
90
|
navigation is in progress.
|
|
88
91
|
|
|
@@ -136,6 +139,182 @@ email, changes member roles, and removes members. Backend errors such as
|
|
|
136
139
|
disabled because invitation tokens and email belong to the later invitations
|
|
137
140
|
feature. The members page uses the full available admin content width.
|
|
138
141
|
|
|
142
|
+
Each active member row has a **Security** button (`org-members-security-{userId}`)
|
|
143
|
+
that opens a [`MemberSecurityPanel`](#membersecuritypanel) for that member below
|
|
144
|
+
the table. Suspended rows show **Reactivate** instead
|
|
145
|
+
(`org-members-reactivate-{userId}`), which sends
|
|
146
|
+
`PATCH /orgs/:orgId/members/:memberId` with `{status: "active"}`. After any
|
|
147
|
+
security action the members list reloads, so a suspended membership shows up at
|
|
148
|
+
once.
|
|
149
|
+
|
|
150
|
+
### MemberSecurityPanel
|
|
151
|
+
|
|
152
|
+
Shows one user's sign-in security and runs the recovery actions the server
|
|
153
|
+
offers for them. It reads the summary from the
|
|
154
|
+
[account security routes](./account-security.md) through the host-bound
|
|
155
|
+
`adminRequest` (`AdminProvider` `credentials` or `getAuthHeaders`, plus
|
|
156
|
+
`apiOrigin`), never through RTK Query. Without a fetch-capable `AdminProvider`
|
|
157
|
+
it shows `Admin RPC client is unavailable`.
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import {MemberSecurityPanel} from "@terreno/admin-frontend";
|
|
161
|
+
|
|
162
|
+
// Org-admin routes: /orgs/:orgId/members/:userId/security
|
|
163
|
+
<MemberSecurityPanel memberLabel="Ada" orgId={orgId} userId={userId} />
|
|
164
|
+
|
|
165
|
+
// Platform routes: /users/:id/security
|
|
166
|
+
<MemberSecurityPanel userId={userId} />
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
| Prop | Type | Description |
|
|
170
|
+
| --- | --- | --- |
|
|
171
|
+
| `userId` | `string` | App `User` id of the member |
|
|
172
|
+
| `orgId` | `string` | Organization id. Set it for the org-admin routes; omit it for the platform routes |
|
|
173
|
+
| `baseURL` | `string` | Route prefix. Defaults to `/orgs` with `orgId` and `/users` without |
|
|
174
|
+
| `memberLabel` | `string` | Name or email shown in the heading (`Security: {memberLabel}`) |
|
|
175
|
+
| `onActionComplete` | `(result) => void` | Called with the action's `{action, changed, ...}` after each success |
|
|
176
|
+
| `onClose` | `() => void` | Shows a **Close** button when set |
|
|
177
|
+
| `focusOnMount` | `boolean` | Move focus to the panel heading (`{testID}-heading`) when it mounts; on web it also scrolls it into view. Default `true` |
|
|
178
|
+
| `testID` | `string` | Root test id. Default `member-security-panel` |
|
|
179
|
+
|
|
180
|
+
What it shows:
|
|
181
|
+
|
|
182
|
+
- Email verified and disabled badges, and the last sign-in method and time.
|
|
183
|
+
- Sign-in methods: whether a password is set, and each linked provider with an
|
|
184
|
+
**Unlink provider** button.
|
|
185
|
+
- Passkeys, each with a **Remove passkey** button.
|
|
186
|
+
- Two-factor status and backup codes left, with **Reset two-factor**.
|
|
187
|
+
- Active sessions, with **Sign out everywhere**.
|
|
188
|
+
- An **Actions** row with every other action in the summary's `actions`
|
|
189
|
+
(`send-password-reset`, `suspend-membership`, `disable`, `enable`,
|
|
190
|
+
`issue-sign-in-link`, and any action added on the server later).
|
|
191
|
+
|
|
192
|
+
Buttons render only for actions the summary lists, so the server decides what
|
|
193
|
+
each caller may do. The panel does not hard-code the list: an action it has
|
|
194
|
+
never seen still gets a button with the server's `label`, and it runs as
|
|
195
|
+
`{method} {securityPath}{path}` from the summary entry. For servers older than
|
|
196
|
+
the summary's `method` and `path`, it falls back to the built-in routes, then
|
|
197
|
+
`POST {securityPath}/{id}`.
|
|
198
|
+
|
|
199
|
+
Where a button goes depends on the action's path params:
|
|
200
|
+
|
|
201
|
+
- `:passkeyId` only: one button per passkey, filled from `passkeys.items[].id`.
|
|
202
|
+
- `:providerId` only: one button per linked provider, filled from
|
|
203
|
+
`providers[].providerId`.
|
|
204
|
+
- No params: `reset-two-factor` and `revoke-sessions` sit in their sections;
|
|
205
|
+
every other action goes in the **Actions** row.
|
|
206
|
+
- Any other param (such as `/devices/:deviceId`): no button, because the
|
|
207
|
+
summary has nothing to fill it with.
|
|
208
|
+
|
|
209
|
+
Per-item buttons have accessible names that include the item, such as
|
|
210
|
+
`Remove passkey Work laptop` and `Unlink GitHub`; an unknown per-item action is
|
|
211
|
+
named `{label}: {item}`. The members screen's row buttons are named
|
|
212
|
+
`Security for {member}` and `Reactivate {member}`.
|
|
213
|
+
|
|
214
|
+
Destructive actions confirm in a `Modal` first (`{testID}-confirm`, buttons
|
|
215
|
+
`{testID}-confirm.primary` and `{testID}-confirm.secondary`). Known actions have
|
|
216
|
+
their own copy; for example, **Unlink provider** warns that sessions started with
|
|
217
|
+
that provider are not ended and points to **Sign out everywhere**. Unknown
|
|
218
|
+
actions get a generic confirmation.
|
|
219
|
+
|
|
220
|
+
After an action the panel shows the result in a `role="status"` region
|
|
221
|
+
(`{testID}-result`), such as
|
|
222
|
+
`Password reset email sent` or `Signed out of 2 sessions`, and refetches the
|
|
223
|
+
summary. `suspend-membership` puts the member out of the org routes' reach, so
|
|
224
|
+
the panel shows the result without refetching. When the response carries a
|
|
225
|
+
`url` (`issue-sign-in-link`), the panel shows it once with a **Copy link**
|
|
226
|
+
button (`{testID}-link-copy`) and a warning that it works once. After copying,
|
|
227
|
+
the button reads **Copied** and a status region (`{testID}-link-copied`) says
|
|
228
|
+
`Link copied to the clipboard`. Server error
|
|
229
|
+
titles, such as `Cannot remove the last sign-in method` or
|
|
230
|
+
`publicAppUrl is required to send recovery mail`, appear inline in a
|
|
231
|
+
`role="alert"` element (`{testID}-action-error`). A failed first load replaces the summary with
|
|
232
|
+
`{testID}-error`; a failed refetch keeps the summary, the result, and any
|
|
233
|
+
one-time link on screen and adds an inline alert (`{testID}-refresh-error`).
|
|
234
|
+
|
|
235
|
+
`useMemberSecurityApi({baseURL, orgId, userId})` is the hook behind the panel.
|
|
236
|
+
It returns `{summary, error, isLoading, isFetching, isRunning, refetch,
|
|
237
|
+
runAction, securityPath}`; `runAction({action, params})` fills path params such
|
|
238
|
+
as `{passkeyId}` and throws before sending when one is missing.
|
|
239
|
+
`memberSecurityPath` and `resolveSecurityActionRequest` are exported for custom
|
|
240
|
+
screens.
|
|
241
|
+
|
|
242
|
+
### UserSecurityScreen
|
|
243
|
+
|
|
244
|
+
The platform staff page for one user's sign-in security. It renders a header
|
|
245
|
+
naming the user, then a [`MemberSecurityPanel`](#membersecuritypanel) against the
|
|
246
|
+
platform routes (`/users/:id/security`). Only holders of `user:manageSecurity`
|
|
247
|
+
reach those routes: `superadmin`, and the locked `operator` role, which is seeded
|
|
248
|
+
only when the backend calls `createAccess({organizations: true})`. Without
|
|
249
|
+
organizations, grant `user:manageSecurity` on a role of your own. The server
|
|
250
|
+
also refuses acting on yourself and on a user holding any permission you lack,
|
|
251
|
+
such as a superadmin. The panel then shows the refusal title inline
|
|
252
|
+
(`{testID}-panel-error`) and the header still names the user.
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
// app/admin/users/[id]/security.tsx
|
|
256
|
+
import {UserSecurityScreen} from "@terreno/admin-frontend";
|
|
257
|
+
import {useLocalSearchParams} from "expo-router";
|
|
258
|
+
|
|
259
|
+
const AdminUserSecurityRoute: React.FC = () => {
|
|
260
|
+
const {id} = useLocalSearchParams<{id: string}>();
|
|
261
|
+
return <UserSecurityScreen userId={id} />;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export default AdminUserSecurityRoute;
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
| Prop | Type | Description |
|
|
268
|
+
| --- | --- | --- |
|
|
269
|
+
| `userId` | `string` | App `User` id |
|
|
270
|
+
| `userModelName` | `string` | Admin model name of the user record. Default `User` |
|
|
271
|
+
| `apiBase` | `string` | Admin JSON base for `{apiBase}/config`. Defaults to the `AdminProvider` `apiBase` |
|
|
272
|
+
| `routeBase` | `string` | Admin route base. Defaults to the `AdminProvider` `routeBase` |
|
|
273
|
+
| `securityBaseURL` | `string` | Prefix of the security routes. Default `/users` |
|
|
274
|
+
| `backHref` | `string` | Where the back arrow goes. Default `{routeBase}/{userModelName}/{userId}`, the admin user record |
|
|
275
|
+
| `testID` | `string` | Root test id. Default `user-security-screen`; the panel is `{testID}-panel` |
|
|
276
|
+
|
|
277
|
+
The header reads `GET {apiBase}/config` to find `userModelName`'s `routePath`,
|
|
278
|
+
then `GET {routePath}/{userId}`, both through the host-bound `adminRequest`. It
|
|
279
|
+
shows the user's `name` (`{testID}-user-name`) and `email`
|
|
280
|
+
(`{testID}-user-email`). Without a name it shows the email, and without a
|
|
281
|
+
record it shows the id. While the record loads it shows a spinner
|
|
282
|
+
(`{testID}-user-loading`). When the record fails to load, an inline alert
|
|
283
|
+
(`{testID}-user-error`) says `Could not load the user record.`. The panel loads
|
|
284
|
+
on its own either way, because the security routes do not depend on the admin
|
|
285
|
+
record. If the user model is not registered in admin, the header shows the id
|
|
286
|
+
and no record request is sent.
|
|
287
|
+
|
|
288
|
+
#### Linking from the user record
|
|
289
|
+
|
|
290
|
+
`UserSecurityLink` is a **Security** button (`admin-user-security-link`) that
|
|
291
|
+
opens `userSecurityRoute({routeBase, userId})`, which is
|
|
292
|
+
`{routeBase}/users/{userId}/security`. Mount `UserSecurityScreen` at that path,
|
|
293
|
+
or pass `href` to the link. admin-frontend does not know which admin model is
|
|
294
|
+
your user model, so the host adds the link to its user record. The usual place
|
|
295
|
+
is `AdminModelForm`'s `footerContent`:
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
// app/admin/[model]/[id].tsx
|
|
299
|
+
import {AdminModelForm, UserSecurityLink} from "@terreno/admin-frontend";
|
|
300
|
+
|
|
301
|
+
const footerContent =
|
|
302
|
+
model === "User" && canManageUserSecurity ? <UserSecurityLink userId={id} /> : undefined;
|
|
303
|
+
|
|
304
|
+
<AdminModelForm api={api} baseUrl="/admin" footerContent={footerContent} itemId={id} mode="edit" modelName={model} />
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Show the link and the route only to staff holding `user:manageSecurity` (or
|
|
308
|
+
`admin: true` without RBAC), and hide the link on the viewer's own record, since
|
|
309
|
+
the routes refuse self-targeting; staff manage their own account from their own
|
|
310
|
+
security screen. The example reads `permissions` from `/auth/me`
|
|
311
|
+
(`example-frontend/utils/userSecurityAccess.ts`), shows the link on other users'
|
|
312
|
+
`User` records, and shows everyone else an `AdminScreenPage`
|
|
313
|
+
(`admin-user-security-forbidden`) with a forbidden message and a back arrow to
|
|
314
|
+
the record. Add the
|
|
315
|
+
route to the admin `Stack` (`<Stack.Screen name="users/[id]/security" />`) next
|
|
316
|
+
to `[model]`.
|
|
317
|
+
|
|
139
318
|
### AdminModelList
|
|
140
319
|
|
|
141
320
|
Entry screen showing all available models as cards.
|
|
@@ -203,6 +382,19 @@ Auto-generates fields from model schema:
|
|
|
203
382
|
- `date` → DateTimeField
|
|
204
383
|
- `objectid` (ref) → SelectField
|
|
205
384
|
- `enum` → SelectField with options
|
|
385
|
+
- `object` with `items` (a nested schema path such as `tasks: {enabled: Boolean, intakePollMs: Number}`)
|
|
386
|
+
→ a titled group with one control per subfield, recursing to any depth
|
|
387
|
+
- `object` without `items` (`Mixed`, `Map`) and `mixed` → JSON editor
|
|
388
|
+
|
|
389
|
+
A nested object group uses the field's humanized key as its heading and its `description` as
|
|
390
|
+
helper text. Each subfield shows its own description and its schema default when the stored
|
|
391
|
+
object has no value for it. Subfield test IDs use the dotted path, for example
|
|
392
|
+
`admin-field-tasks.enabled`; the group itself is `admin-field-group-tasks`. Editing a subfield
|
|
393
|
+
merges it into the parent object, and Save sends the whole object for that field. In create mode
|
|
394
|
+
the object starts from each subfield's default; enums, refs, dates, and Mixed subfields without a
|
|
395
|
+
default are left out. Nested objects inside an array of subdocuments render the same way; their
|
|
396
|
+
test IDs add the array key and item index (`admin-field-servers.0.media.port`), and new array
|
|
397
|
+
items follow the same default rules.
|
|
206
398
|
|
|
207
399
|
System fields (`_id`, `__v`, `created`, `updated`, `deleted`) are automatically skipped.
|
|
208
400
|
|
|
@@ -296,6 +488,34 @@ Renders reference fields as clickable links.
|
|
|
296
488
|
/>
|
|
297
489
|
``````
|
|
298
490
|
|
|
491
|
+
### Consent records and PDFs
|
|
492
|
+
|
|
493
|
+
`ConsentResponseViewer` (admin, one `ConsentResponse`) and `ConsentHistory` (the signed-in
|
|
494
|
+
user's own `GET /consents/my` entries) download a consent record as a PDF:
|
|
495
|
+
`generateConsentPdf(response)` and `generateConsentHistoryPdf(entry)`. Web builds the PDF with
|
|
496
|
+
jsPDF; native renders `buildConsentPdfHtml` from `@terreno/ui` and shares it with
|
|
497
|
+
`sharePdfFromHtml`.
|
|
498
|
+
|
|
499
|
+
When the response is provisional (staff signed on the user's behalf), both PDFs add a
|
|
500
|
+
"Provisional signature" section: the signer and date, and "Voided <date>" when it was voided.
|
|
501
|
+
The admin PDF also includes the signer's note and IP address. The user's own PDF never
|
|
502
|
+
includes the note.
|
|
503
|
+
|
|
504
|
+
Both views can also print a record: `ConsentResponseViewer`'s "Print" button
|
|
505
|
+
(`consent-response-print-button`) and each `ConsentHistory` entry's print icon
|
|
506
|
+
(`consent-history-print-{entryId}`, in the expanded details) call `printConsentHtml` from
|
|
507
|
+
`@terreno/ui` with `buildConsentPdfHtml` HTML, including the provisional block. The
|
|
508
|
+
"Download PDF" buttons are unchanged.
|
|
509
|
+
|
|
510
|
+
Provisional display:
|
|
511
|
+
|
|
512
|
+
- `ConsentResponseViewer` adds a "Provisional Signature" card (`consent-response-provisional`)
|
|
513
|
+
with a "Provisional" badge, the signer, signing date, note, signer IP, and, once voided, a
|
|
514
|
+
"Voided" badge (`consent-response-provisional-voided`), void date, and reason.
|
|
515
|
+
- `ConsentHistory` marks provisional entries with a "Provisional" badge and "Signed on your
|
|
516
|
+
behalf by <name>" (`consent-history-provisional-{entryId}`), plus "Voided" when voided. It
|
|
517
|
+
never shows the note.
|
|
518
|
+
|
|
299
519
|
## Hooks
|
|
300
520
|
|
|
301
521
|
### useAdminConfig
|
|
@@ -418,6 +638,10 @@ When RBAC is enabled, `/admin/config` is filtered for the current user. `AdminSh
|
|
|
418
638
|
renders model or custom-screen links returned by the server. The shell lifts Audit Log, Feature
|
|
419
639
|
Flags, and Jobs into the Platform section (Jobs still comes from `customScreens`).
|
|
420
640
|
|
|
641
|
+
A user record's `roles` field loads the role catalog (`GET /rbac/roles`, `rbac:read`) only when
|
|
642
|
+
it is editable. A read-only record, such as an operator's view with `adminUser:read`, lists the
|
|
643
|
+
assigned role names without requesting the catalog.
|
|
644
|
+
|
|
421
645
|
### Custom screen page chrome
|
|
422
646
|
|
|
423
647
|
Wrap custom admin screen content in `AdminScreenPage`. It renders the standard `Page` header with a back arrow by default; the arrow navigates to `/admin` via `router.push`, not `router.back()`, because sidebar navigation does not always leave a reliable history entry on web. Pass the host's `routeBase` as `backHref` when admin uses a different prefix; an empty standalone-admin base resolves to `/`. Detail screens can target their parent route (for example `/admin/comms`). Pass `backButton={false}` only when the host supplies equivalent navigation.
|