portal-mojo 0.1.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/LICENSE +201 -0
- package/README.md +55 -0
- package/docs/DetailView.md +173 -0
- package/docs/ModelTable.md +128 -0
- package/docs/README.md +98 -0
- package/docs/admin-assistant.md +72 -0
- package/docs/admin-bouncer.md +120 -0
- package/docs/admin-cloudwatch.md +38 -0
- package/docs/admin-credentials.md +116 -0
- package/docs/admin-dashboard.md +15 -0
- package/docs/admin-devices-geoip.md +284 -0
- package/docs/admin-dns-certificates.md +122 -0
- package/docs/admin-dns-records.md +65 -0
- package/docs/admin-dns-registrar.md +33 -0
- package/docs/admin-dns.md +80 -0
- package/docs/admin-identity-users.md +103 -0
- package/docs/admin-incidents.md +71 -0
- package/docs/admin-jobs.md +426 -0
- package/docs/admin-members.md +112 -0
- package/docs/admin-messaging.md +56 -0
- package/docs/admin-metrics-explorer.md +63 -0
- package/docs/admin-monitoring.md +99 -0
- package/docs/admin-network-security.md +393 -0
- package/docs/admin-phonehub.md +27 -0
- package/docs/admin-push.md +36 -0
- package/docs/admin-rules.md +87 -0
- package/docs/admin-security-tickets.md +40 -0
- package/docs/admin-settings.md +78 -0
- package/docs/admin-shortlinks.md +32 -0
- package/docs/admin-storage.md +141 -0
- package/docs/auth-pages.md +212 -0
- package/docs/auth.md +58 -0
- package/docs/calendar.md +122 -0
- package/docs/charts.md +303 -0
- package/docs/client.md +206 -0
- package/docs/collection-multiselect.md +117 -0
- package/docs/collection-select.md +132 -0
- package/docs/combobox.md +105 -0
- package/docs/dataview.md +226 -0
- package/docs/datepicker.md +151 -0
- package/docs/daterange.md +170 -0
- package/docs/datetimepicker.md +249 -0
- package/docs/defineModel.md +72 -0
- package/docs/detail-primitives.md +174 -0
- package/docs/feedback.md +85 -0
- package/docs/file-upload-ux.md +146 -0
- package/docs/forms.md +435 -0
- package/docs/grouping-and-fmt.md +98 -0
- package/docs/idioms.md +112 -0
- package/docs/image-editor.md +101 -0
- package/docs/loading.md +242 -0
- package/docs/location-address.md +76 -0
- package/docs/markdown.md +203 -0
- package/docs/menus-and-access.md +89 -0
- package/docs/multiselect-dropdown.md +153 -0
- package/docs/params.md +73 -0
- package/docs/popover.md +130 -0
- package/docs/realtime.md +61 -0
- package/docs/record-feed.md +238 -0
- package/docs/right-panel.md +42 -0
- package/docs/tabs-and-form-wizard.md +60 -0
- package/docs/taginput.md +120 -0
- package/docs/timepicker.md +173 -0
- package/docs/timezone-select.md +147 -0
- package/docs/worldmap.md +302 -0
- package/package.json +55 -0
- package/src/admin/assistant/AssistantFeed.tsx +29 -0
- package/src/admin/assistant/AssistantPanel.tsx +65 -0
- package/src/admin/assistant/api.ts +17 -0
- package/src/admin/assistant/data.ts +63 -0
- package/src/admin/assistant/index.ts +21 -0
- package/src/admin/assistant/launchers.tsx +33 -0
- package/src/admin/assistant/pages.tsx +78 -0
- package/src/admin/assistant/streaming.ts +238 -0
- package/src/admin/assistant/types.ts +30 -0
- package/src/admin/bouncer/devices.tsx +230 -0
- package/src/admin/bouncer/index.ts +34 -0
- package/src/admin/bouncer/models.ts +199 -0
- package/src/admin/bouncer/signals.tsx +182 -0
- package/src/admin/bouncer/signatures.tsx +220 -0
- package/src/admin/cloudwatch/CloudWatchChart.tsx +61 -0
- package/src/admin/cloudwatch/CloudWatchDashboardPage.tsx +52 -0
- package/src/admin/cloudwatch/CloudWatchResourceDetail.tsx +33 -0
- package/src/admin/cloudwatch/data.ts +163 -0
- package/src/admin/cloudwatch/index.ts +16 -0
- package/src/admin/credentials/group-api-keys.tsx +433 -0
- package/src/admin/credentials/index.ts +4 -0
- package/src/admin/credentials/models.ts +244 -0
- package/src/admin/credentials/secret-dialog.tsx +77 -0
- package/src/admin/credentials/webhook-subscriptions.tsx +423 -0
- package/src/admin/dashboard/AdminDashboardPage.tsx +101 -0
- package/src/admin/dashboard/data.ts +60 -0
- package/src/admin/dashboard/index.ts +2 -0
- package/src/admin/dns/CertificateDetail.tsx +115 -0
- package/src/admin/dns/CertificateLifecyclePoller.tsx +141 -0
- package/src/admin/dns/CertificateRequestDialog.tsx +103 -0
- package/src/admin/dns/CertificatesPage.tsx +85 -0
- package/src/admin/dns/DnsRecordEditor.tsx +110 -0
- package/src/admin/dns/DnsRecordsPage.tsx +36 -0
- package/src/admin/dns/DnsRecordsPanel.tsx +71 -0
- package/src/admin/dns/DomainCertificatesSection.tsx +40 -0
- package/src/admin/dns/DomainDetail.tsx +41 -0
- package/src/admin/dns/DomainOnboardingDialog.tsx +27 -0
- package/src/admin/dns/DomainPurchaseDetail.tsx +11 -0
- package/src/admin/dns/DomainPurchaseWizard.tsx +112 -0
- package/src/admin/dns/DomainPurchasesPage.tsx +16 -0
- package/src/admin/dns/DomainsPage.tsx +27 -0
- package/src/admin/dns/ProviderCredentialsPage.tsx +304 -0
- package/src/admin/dns/RegistrantContactPage.tsx +42 -0
- package/src/admin/dns/api.ts +515 -0
- package/src/admin/dns/certificate-data.ts +221 -0
- package/src/admin/dns/data.ts +32 -0
- package/src/admin/dns/dns-data.ts +266 -0
- package/src/admin/dns/dns-integration.ts +24 -0
- package/src/admin/dns/domain-links.ts +37 -0
- package/src/admin/dns/index.ts +86 -0
- package/src/admin/dns/models.ts +418 -0
- package/src/admin/dns/purchase-data.ts +67 -0
- package/src/admin/dns/registrant-data.ts +52 -0
- package/src/admin/identity/index.ts +5 -0
- package/src/admin/identity/members/GroupMembersPanel.tsx +146 -0
- package/src/admin/identity/members/MemberDetail.tsx +226 -0
- package/src/admin/identity/members/MembersPage.tsx +144 -0
- package/src/admin/identity/members/index.ts +24 -0
- package/src/admin/identity/members/member-flows.tsx +132 -0
- package/src/admin/identity/members/models.ts +204 -0
- package/src/admin/identity/users/UserDetail.tsx +257 -0
- package/src/admin/identity/users/UsersPage.tsx +163 -0
- package/src/admin/identity/users/index.ts +27 -0
- package/src/admin/identity/users/models.ts +232 -0
- package/src/admin/identity/users/sections/ApiKeysSection.tsx +119 -0
- package/src/admin/identity/users/sections/AuditSection.tsx +190 -0
- package/src/admin/identity/users/sections/DevicesSection.tsx +140 -0
- package/src/admin/identity/users/sections/GroupsSection.tsx +64 -0
- package/src/admin/identity/users/sections/LoginsSection.tsx +106 -0
- package/src/admin/identity/users/sections/MetadataPanel.tsx +36 -0
- package/src/admin/identity/users/sections/NotificationsSection.tsx +117 -0
- package/src/admin/identity/users/sections/OAuthSection.tsx +72 -0
- package/src/admin/identity/users/sections/OverviewSection.tsx +109 -0
- package/src/admin/identity/users/sections/PermissionsSection.tsx +35 -0
- package/src/admin/identity/users/sections/PersonalSection.tsx +73 -0
- package/src/admin/identity/users/sections/ProfileSection.tsx +175 -0
- package/src/admin/identity/users/sections/SecuritySection.tsx +123 -0
- package/src/admin/identity/users/sections/actions.tsx +752 -0
- package/src/admin/identity/users/sections/permission-catalog.ts +138 -0
- package/src/admin/identity/users/sections/queries.ts +74 -0
- package/src/admin/identity/users/sections/shared.tsx +299 -0
- package/src/admin/incidents/EventDetail.tsx +43 -0
- package/src/admin/incidents/EventsPage.tsx +52 -0
- package/src/admin/incidents/IncidentDetail.tsx +68 -0
- package/src/admin/incidents/IncidentsPage.tsx +102 -0
- package/src/admin/incidents/forensics.tsx +73 -0
- package/src/admin/incidents/index.ts +16 -0
- package/src/admin/incidents/models.ts +113 -0
- package/src/admin/incidents/sanitize.ts +71 -0
- package/src/admin/index.ts +383 -0
- package/src/admin/jobs/JobDashboardPage.tsx +111 -0
- package/src/admin/jobs/JobDetail.tsx +721 -0
- package/src/admin/jobs/JobRunnersPage.tsx +216 -0
- package/src/admin/jobs/JobsTablePage.tsx +122 -0
- package/src/admin/jobs/RunnerDetail.tsx +576 -0
- package/src/admin/jobs/ScheduledTaskDetail.tsx +421 -0
- package/src/admin/jobs/ScheduledTasksPage.tsx +167 -0
- package/src/admin/jobs/columns.tsx +297 -0
- package/src/admin/jobs/control.ts +488 -0
- package/src/admin/jobs/index.ts +85 -0
- package/src/admin/jobs/models.ts +599 -0
- package/src/admin/jobs/queries.ts +272 -0
- package/src/admin/jobs/scheduled-task-form.tsx +493 -0
- package/src/admin/jobs/sections/JobChannelsPanel.tsx +142 -0
- package/src/admin/jobs/sections/JobOperationsSection.tsx +423 -0
- package/src/admin/jobs/sections/JobRunnersStrip.tsx +82 -0
- package/src/admin/jobs/sections/JobThroughputSection.tsx +118 -0
- package/src/admin/messaging/EmailDomainWizard.tsx +24 -0
- package/src/admin/messaging/EmailDomainsPage.tsx +17 -0
- package/src/admin/messaging/EmailTemplatesPage.tsx +13 -0
- package/src/admin/messaging/MailboxesPage.tsx +15 -0
- package/src/admin/messaging/PublicMessagesPage.tsx +13 -0
- package/src/admin/messaging/SandboxedEmailPreview.tsx +23 -0
- package/src/admin/messaging/SentMessagesPage.tsx +11 -0
- package/src/admin/messaging/api.ts +17 -0
- package/src/admin/messaging/data.ts +10 -0
- package/src/admin/messaging/index.ts +23 -0
- package/src/admin/messaging/models.ts +76 -0
- package/src/admin/messaging/push/PushPage.tsx +44 -0
- package/src/admin/messaging/push/api.ts +17 -0
- package/src/admin/messaging/push/index.ts +9 -0
- package/src/admin/messaging/push/models.ts +43 -0
- package/src/admin/messaging/sanitize.ts +44 -0
- package/src/admin/monitoring/LogInspector.tsx +160 -0
- package/src/admin/monitoring/LogsPage.tsx +135 -0
- package/src/admin/monitoring/MetricsExplorerPage.tsx +360 -0
- package/src/admin/monitoring/MetricsPermissionsPage.tsx +166 -0
- package/src/admin/monitoring/MetricsSourcePicker.tsx +136 -0
- package/src/admin/monitoring/index.ts +55 -0
- package/src/admin/monitoring/metrics-explorer-client.ts +229 -0
- package/src/admin/monitoring/metrics-explorer-data.ts +158 -0
- package/src/admin/monitoring/models.ts +155 -0
- package/src/admin/network/BlockedIPsPage.tsx +194 -0
- package/src/admin/network/BlockedIpDetail.tsx +262 -0
- package/src/admin/network/FirewallLogPage.tsx +115 -0
- package/src/admin/network/IPSetDetail.tsx +280 -0
- package/src/admin/network/IPSetEditor.tsx +191 -0
- package/src/admin/network/IPSetsPage.tsx +177 -0
- package/src/admin/network/geofence/BlocksTab.tsx +289 -0
- package/src/admin/network/geofence/ExemptionsTab.tsx +349 -0
- package/src/admin/network/geofence/GeofencingPage.tsx +119 -0
- package/src/admin/network/geofence/PostureHeader.tsx +184 -0
- package/src/admin/network/geofence/RuleEditor.tsx +176 -0
- package/src/admin/network/geofence/RulesTab.tsx +254 -0
- package/src/admin/network/geofence/SimulatorTab.tsx +281 -0
- package/src/admin/network/geofence/geofence-data.ts +674 -0
- package/src/admin/network/index.ts +68 -0
- package/src/admin/network/models.ts +607 -0
- package/src/admin/phonehub/PhoneHubPage.tsx +57 -0
- package/src/admin/phonehub/api.ts +47 -0
- package/src/admin/phonehub/data.ts +6 -0
- package/src/admin/phonehub/index.ts +10 -0
- package/src/admin/phonehub/models.ts +68 -0
- package/src/admin/rules/HandlerChainBuilder.tsx +51 -0
- package/src/admin/rules/RuleSetDetailPage.tsx +33 -0
- package/src/admin/rules/RuleSetsPage.tsx +33 -0
- package/src/admin/rules/editors.tsx +54 -0
- package/src/admin/rules/handler-dsl.ts +213 -0
- package/src/admin/rules/index.ts +13 -0
- package/src/admin/rules/models.ts +112 -0
- package/src/admin/security/devices/LoginEventDetail.tsx +123 -0
- package/src/admin/security/devices/LoginLocationMap.tsx +321 -0
- package/src/admin/security/devices/LoginLocationsPage.tsx +162 -0
- package/src/admin/security/devices/UserDeviceDetail.tsx +546 -0
- package/src/admin/security/devices/UserDeviceLocationDetail.tsx +272 -0
- package/src/admin/security/devices/UserDevicesPage.tsx +84 -0
- package/src/admin/security/devices/index.ts +40 -0
- package/src/admin/security/devices/models.ts +470 -0
- package/src/admin/security/geoip/GeoIpCachePage.tsx +135 -0
- package/src/admin/security/geoip/GeoIpDossier.tsx +804 -0
- package/src/admin/security/geoip/geoip-forms.ts +102 -0
- package/src/admin/security/geoip/index.ts +27 -0
- package/src/admin/security/geoip/models.ts +397 -0
- package/src/admin/security/index.ts +31 -0
- package/src/admin/security/models.ts +137 -0
- package/src/admin/security/tickets.tsx +562 -0
- package/src/admin/security-permissions.ts +5 -0
- package/src/admin/settings/SettingDetail.tsx +57 -0
- package/src/admin/settings/SettingEditor.tsx +157 -0
- package/src/admin/settings/SettingsPage.tsx +72 -0
- package/src/admin/settings/index.ts +19 -0
- package/src/admin/settings/model.ts +143 -0
- package/src/admin/shortlinks/ShortlinkDetail.tsx +55 -0
- package/src/admin/shortlinks/ShortlinkHistoryPage.tsx +18 -0
- package/src/admin/shortlinks/ShortlinksPage.tsx +59 -0
- package/src/admin/shortlinks/index.ts +18 -0
- package/src/admin/shortlinks/models.ts +246 -0
- package/src/admin/storage/BackendsPage.tsx +43 -0
- package/src/admin/storage/BucketDetail.tsx +77 -0
- package/src/admin/storage/BucketsPage.tsx +64 -0
- package/src/admin/storage/FileManagerDetail.tsx +36 -0
- package/src/admin/storage/FilePreview.tsx +54 -0
- package/src/admin/storage/FileUploadSurface.tsx +250 -0
- package/src/admin/storage/FileView.tsx +109 -0
- package/src/admin/storage/FilesPage.tsx +53 -0
- package/src/admin/storage/file-renditions.ts +99 -0
- package/src/admin/storage/index.ts +31 -0
- package/src/admin/storage/models.ts +585 -0
- package/src/admin/storage/storage-dialogs.tsx +80 -0
- package/src/charts/CircularProgress.tsx +281 -0
- package/src/charts/KPIStrip.tsx +162 -0
- package/src/charts/KPITile.tsx +125 -0
- package/src/charts/MetricsChart.tsx +459 -0
- package/src/charts/MetricsMiniWidget.tsx +588 -0
- package/src/charts/MiniChart.tsx +283 -0
- package/src/charts/PieChart.tsx +354 -0
- package/src/charts/SeriesChart.tsx +378 -0
- package/src/charts/chart-dialogs.tsx +149 -0
- package/src/charts/exportChart.ts +144 -0
- package/src/charts/index.ts +28 -0
- package/src/charts/pie-math.ts +127 -0
- package/src/charts/stats.ts +265 -0
- package/src/charts/worldmap/WorldMap.tsx +649 -0
- package/src/charts/worldmap/countryCentroids.ts +304 -0
- package/src/charts/worldmap/geo.ts +293 -0
- package/src/charts/worldmap/use-world-land.ts +47 -0
- package/src/charts/worldmap/world-land.ts +18 -0
- package/src/charts/worldmap/worldmap-data.ts +285 -0
- package/src/client/auth.ts +737 -0
- package/src/client/client.ts +268 -0
- package/src/client/duid.ts +31 -0
- package/src/client/errors.ts +33 -0
- package/src/client/group-context.ts +28 -0
- package/src/client/group.tsx +126 -0
- package/src/client/hooks.ts +50 -0
- package/src/client/index.ts +37 -0
- package/src/client/jwt.ts +75 -0
- package/src/client/location.ts +285 -0
- package/src/client/lookups.ts +41 -0
- package/src/client/markdown-fallback.tsx +115 -0
- package/src/client/markdown-parse.ts +386 -0
- package/src/client/markdown-sanitize.ts +217 -0
- package/src/client/markdown.ts +122 -0
- package/src/client/me.ts +188 -0
- package/src/client/mock.ts +9984 -0
- package/src/client/model.ts +227 -0
- package/src/client/params.ts +292 -0
- package/src/client/realtime-mock.ts +221 -0
- package/src/client/realtime.ts +561 -0
- package/src/client/record-feed.ts +329 -0
- package/src/client/safe-export.ts +71 -0
- package/src/client/types.ts +278 -0
- package/src/client/upload.ts +421 -0
- package/src/ui/AddressField.tsx +273 -0
- package/src/ui/AttachmentQueue.tsx +92 -0
- package/src/ui/CollectionMultiSelect.tsx +622 -0
- package/src/ui/CollectionSelect.tsx +517 -0
- package/src/ui/ComboBox.tsx +336 -0
- package/src/ui/DetailView.tsx +438 -0
- package/src/ui/FileDrop.tsx +194 -0
- package/src/ui/FileField.tsx +369 -0
- package/src/ui/FilterBar.tsx +269 -0
- package/src/ui/FormFields.tsx +77 -0
- package/src/ui/FormView.tsx +360 -0
- package/src/ui/FormWizard.tsx +336 -0
- package/src/ui/GroupSwitcher.tsx +202 -0
- package/src/ui/Guarded.tsx +16 -0
- package/src/ui/MarkdownView.tsx +89 -0
- package/src/ui/ModelTable.tsx +848 -0
- package/src/ui/MultiSelectDropdown.tsx +390 -0
- package/src/ui/Popover.tsx +204 -0
- package/src/ui/RecordFeed.tsx +627 -0
- package/src/ui/RequiresGroup.tsx +32 -0
- package/src/ui/RightPanel.tsx +107 -0
- package/src/ui/SidebarNav.tsx +330 -0
- package/src/ui/Tabs.tsx +161 -0
- package/src/ui/TagInput.tsx +381 -0
- package/src/ui/ThemeProvider.tsx +52 -0
- package/src/ui/UploadQueue.tsx +603 -0
- package/src/ui/armed-button.tsx +91 -0
- package/src/ui/date/Calendar.tsx +498 -0
- package/src/ui/date/DatePicker.tsx +289 -0
- package/src/ui/date/DateRangePicker.tsx +280 -0
- package/src/ui/date/DateTimePicker.tsx +587 -0
- package/src/ui/date/PresetRail.tsx +165 -0
- package/src/ui/date/TimePicker.tsx +624 -0
- package/src/ui/date/TimezoneSelect.tsx +267 -0
- package/src/ui/date/fns.ts +679 -0
- package/src/ui/detail/DataView.tsx +692 -0
- package/src/ui/detail/FlowStrip.tsx +80 -0
- package/src/ui/detail/KnownFieldsCard.tsx +121 -0
- package/src/ui/detail/MetadataSection.tsx +219 -0
- package/src/ui/detail/StackTraceView.tsx +150 -0
- package/src/ui/detail/StatusPanel.tsx +73 -0
- package/src/ui/detail/Timeline.tsx +52 -0
- package/src/ui/detail/index.ts +12 -0
- package/src/ui/field-registry.tsx +428 -0
- package/src/ui/field-wire.ts +322 -0
- package/src/ui/form-autosave.ts +583 -0
- package/src/ui/format.ts +444 -0
- package/src/ui/grouping.ts +150 -0
- package/src/ui/image-editor/ImageEditor.tsx +484 -0
- package/src/ui/image-editor/canvas.ts +254 -0
- package/src/ui/image-editor/index.ts +3 -0
- package/src/ui/image-editor/math.ts +416 -0
- package/src/ui/index.ts +58 -0
- package/src/ui/loading.tsx +604 -0
- package/src/ui/menu-registry.ts +213 -0
- package/src/ui/modal.tsx +297 -0
- package/src/ui/password.tsx +302 -0
- package/src/ui/schema-form-core.tsx +315 -0
- package/src/ui/toast.tsx +219 -0
- package/src/ui/ui.tsx +45 -0
package/docs/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# portal-mojo — component & client reference
|
|
2
|
+
|
|
3
|
+
Reference docs for every export, written to be loaded into an AI coding
|
|
4
|
+
context. Each page states the import path, the API surface, the wire
|
|
5
|
+
contract it speaks, and the invariants that must hold. Live demos for
|
|
6
|
+
everything: run the showcase (`npm run dev:showcase`) → **Develop → Components**.
|
|
7
|
+
|
|
8
|
+
| Page | Covers |
|
|
9
|
+
|---|---|
|
|
10
|
+
| [client.md](client.md) | Transport, envelope boundary, `mojoList/Get/Save/Delete/Download/Metrics`, `mojoCall`, `mojoQueryDefaults`, mock vs live |
|
|
11
|
+
| [defineModel.md](defineModel.md) | Model definitions + hooks, POST_SAVE_ACTIONS, `fetchOne`, cache keys |
|
|
12
|
+
| [params.md](params.md) | `useTableParams` — the single source of truth for table state; persistence blob |
|
|
13
|
+
| [auth.md](auth.md) | Login flows, token upkeep, hosted-auth (bouncer page) bridge, auth events |
|
|
14
|
+
| [realtime.md](realtime.md) | Auth-challenged WebSocket lifecycle, heartbeat/backoff, refcounted topics, typed event projection, deterministic mock |
|
|
15
|
+
| [auth-pages.md](auth-pages.md) | The in-app auth pages: routes + guard, `VITE_MOJO_AUTH` switch, fresh-auth (440 step-up) modal, MFA panel, password tools |
|
|
16
|
+
| [admin-credentials.md](admin-credentials.md) | Group API keys and webhook subscriptions — dual-mount pages, permission registry, secret-safe create/reveal/rotation |
|
|
17
|
+
| [admin-dashboard.md](admin-dashboard.md) | Package-owned global Admin landing, authoritative signals, independent gates, caller-isolated caches, and root fallback |
|
|
18
|
+
| [admin-cloudwatch.md](admin-cloudwatch.md) | Global EC2/RDS/Redis CloudWatch charts, exact resource/fetch wires, constrained controls, refresh ownership, and modal details |
|
|
19
|
+
| [admin-monitoring.md](admin-monitoring.md) | Monitoring Admin registration, logs, and exact Redis-backed metrics-permission mutations |
|
|
20
|
+
| [admin-metrics-explorer.md](admin-metrics-explorer.md) | Read-only recorded-metrics discovery, full-slug adapters, KPI/fan-out/scalar contracts, and cache identity |
|
|
21
|
+
| [admin-settings.md](admin-settings.md) | Runtime settings admin — direction-aware atomic writes, write-only secrets, and global/group scope |
|
|
22
|
+
| [admin-bouncer.md](admin-bouncer.md) | Bouncer decisions, device investigations, and bot-signature administration with token-safe caching |
|
|
23
|
+
| [admin-security-tickets.md](admin-security-tickets.md) | Ticket queue, KISS detail modal, notes, approvals, and Maestro sync contract |
|
|
24
|
+
| [admin-incidents.md](admin-incidents.md) | Incident/event triage, forensic detail, merge semantics, and sanitized export boundary |
|
|
25
|
+
| [admin-rules.md](admin-rules.md) | Always-inactive RuleSet authoring and the lossless, runtime-effective handler-chain builder |
|
|
26
|
+
| [admin-members.md](admin-members.md) | Global membership table/detail, no-oracle admission, extensible grants, Group-detail composition, and unsupported-operation boundaries |
|
|
27
|
+
| [admin-identity-users.md](admin-identity-users.md) | Global Users table and 14-section detail, system gates, action contracts, and one-time credential invariants |
|
|
28
|
+
| [admin-devices-geoip.md](admin-devices-geoip.md) | Fleet device triage, the login-location world map with country drill-down, and the GeoIP dossier — expiry-aware enforcement, the never-cached raw provider record, and the backend traps the port encodes |
|
|
29
|
+
| [admin-jobs.md](admin-jobs.md) | Jobs engine — runner fleet, per-channel queue depth, the segmented job table with armed cancel and republishing retry, the dry-run-first control plane, scheduled tasks with a global-only gate, and the thirteen backend corrections the port encodes |
|
|
30
|
+
| [admin-network-security.md](admin-network-security.md) | Perimeter control — blocked IPs, the firewall log, kernel IP sets and the geofencing page (posture, rules, simulator, evidence, exemptions), with the DSL projection and every backend correction the port encodes |
|
|
31
|
+
| [admin-dns.md](admin-dns.md) | Global DNS foundation — capability gates, safe typed resources, provider credential lifecycle, and secret/PII/material boundaries |
|
|
32
|
+
| [admin-dns-records.md](admin-dns-records.md) | Domain inventory/KISS detail and id-less live DNS records — complete-set writes, explicit corrections, stale preflight, and provider behavior |
|
|
33
|
+
| [admin-dns-certificates.md](admin-dns-certificates.md) | Certificate custody and ACME lifecycle — in-zone requests, delegation readiness, house gates, renewal-aware bounded polling, and the no-material boundary |
|
|
34
|
+
| [admin-dns-registrar.md](admin-dns-registrar.md) | Domain search/quote/purchase, one-use token custody, durable-ledger reconciliation, scoped registrant PII, and manual House adoption |
|
|
35
|
+
| [admin-storage.md](admin-storage.md) | Global S3 buckets, masked storage backends, explicit policy-backed File uploads, capability-safe FileView/shares, and finite rendition convergence |
|
|
36
|
+
| [admin-shortlinks.md](admin-shortlinks.md) | Global shortlink operations, destination-free caches, bounded click history, and reconciled one-shot mutations |
|
|
37
|
+
| [admin-messaging.md](admin-messaging.md) | SES domains/mailboxes/sent/templates plus public contact requests, with write-only credentials and offline HTML previews |
|
|
38
|
+
| [admin-phonehub.md](admin-phonehub.md) | Global Phone Hub: evidence-backed lookup, sanitized SMS audits, and write-only provider configuration |
|
|
39
|
+
| [admin-push.md](admin-push.md) | Global Push admin: caller stats, global metrics, strict audits, templates, and cache-free FCM configuration |
|
|
40
|
+
| [admin-assistant.md](admin-assistant.md) | Global Assistant streaming selection/reconciliation, strict projectors/blocks, context reuse, skills, and explicit tiered memory |
|
|
41
|
+
| [ModelTable.md](ModelTable.md) | The server-driven table: columns, filters, selection/batch, chooser, persist, autoRefresh, expand, groupBy, export, skeleton |
|
|
42
|
+
| [forms.md](forms.md) | The `Field` language, `SchemaForm`, `formModal`, `FormView` inline autosave + showWhen + tabsets |
|
|
43
|
+
| [tabs-and-form-wizard.md](tabs-and-form-wizard.md) | Accessible Tabs variants and shared-state FormWizard, async finish, reset/roster semantics, modal helper |
|
|
44
|
+
| [DetailView.md](DetailView.md) | The UserView-style detail surface + row/section primitives |
|
|
45
|
+
| [feedback.md](feedback.md) | Awaitable `modal.*`, `toast.*` |
|
|
46
|
+
| [idioms.md](idioms.md) | ArmedButton (two-step confirm), `undoToast` (grace-period undo), `progressToast` — the dangerous-action trio |
|
|
47
|
+
| [file-upload-ux.md](file-upload-ux.md) | Pure file validation, accessible picker/drop controls, bounded component-local upload queues, recovery, and completion callbacks |
|
|
48
|
+
| [image-editor.md](image-editor.md) | Full-resolution crop/transform/filter composition, bounded history, PNG Blob results, modal helper, and ImageField pre-upload editing |
|
|
49
|
+
| [loading.md](loading.md) | `Spinner`, `busy()`/`busyWhile()` blocking overlay, `ViewLoader`, `InlineLoader`, `Busy` — the anti-flash delay rule and skeleton-vs-loader guidance |
|
|
50
|
+
| [popover.md](popover.md) | Anchored top-layer popover shell — placement, reposition, outside/Escape close, the dialog stacking story |
|
|
51
|
+
| [taginput.md](taginput.md) | Chip/tag entry — CSV wire shape, keyboard matrix, validation + inline errors |
|
|
52
|
+
| [combobox.md](combobox.md) | The house autocomplete — options with descriptions/meta, commit-only change pipeline, allowCustom, ARIA |
|
|
53
|
+
| [location-address.md](location-address.md) | Six-endpoint LocationClient, private provider sessions, stale guards, controlled AddressField, atomic form patching |
|
|
54
|
+
| [calendar.md](calendar.md) | The picker engine + `dateFns` namespace — three precisions, range anchor/preview, drill-down zoom |
|
|
55
|
+
| [datepicker.md](datepicker.md) | Single-value picker shell — precisions, display-format stripping, clear/required/inline states |
|
|
56
|
+
| [daterange.md](daterange.md) | Range picker + PresetRail — quick ranges, precision modes, the FilterBar daterange dialog |
|
|
57
|
+
| [timezone-select.md](timezone-select.md) | IANA zone picker over ComboBox — offset labels, local default, alias tolerance, commit-only |
|
|
58
|
+
| [timepicker.md](timepicker.md) | Stepper time entry — 12h/24h, minute step math, min/max, ISO/IANA/object serialization, embed mode |
|
|
59
|
+
| [datetimepicker.md](datetimepicker.md) | Calendar + time + timezone in one popover — DST-correct offsets, boundary-day clamping |
|
|
60
|
+
| [collection-select.md](collection-select.md) | Single record picker — server search, id→label hydration, null sentinel, shared cache keys |
|
|
61
|
+
| [multiselect-dropdown.md](multiselect-dropdown.md) | Static checkbox dropdown — summarized trigger, Done footer, disabled options |
|
|
62
|
+
| [collection-multiselect.md](collection-multiselect.md) | Server-backed multi-pick dropdown (panel variant available) — model binding, 400ms search, select/deselect-all, shift-click ranges |
|
|
63
|
+
| [detail-primitives.md](detail-primitives.md) | StatusPanel, FlowStrip, Timeline, KnownFieldsCard, MetadataSection, StackTraceView — the detail-page pack |
|
|
64
|
+
| [dataview.md](dataview.md) | Auto-inferring key/value grid + safe JSON viewer — schema or inference, nested records |
|
|
65
|
+
| [markdown.md](markdown.md) | MarkdownView — server render via docit, allowlist sanitizer trust model, client fallback |
|
|
66
|
+
| [record-feed.md](record-feed.md) | RecordFeed — ticket/incident adapters, chronological latest-window normalization, optimistic sends, controlled mode |
|
|
67
|
+
| [right-panel.md](right-panel.md) | Explicit opt-in persistent shell slot — provider state, focus/Escape semantics, route preservation, responsive layout |
|
|
68
|
+
| [charts.md](charts.md) | `SeriesChart`, `MetricsChart` (+stats/data dialogs, custom `dt_*` range), `MetricsMiniWidget`, `KPITile/KPIStrip`, `CircularProgress`, `PieChart`, `exportChartPng`, the metrics wire shape |
|
|
69
|
+
| [worldmap.md](worldmap.md) | `WorldMap` — dependency-free geo map (no tiles/CDN/network), the projection math, `COUNTRY_CENTROIDS`/`COUNTRY_OPTIONS`, metrics-slug→country binding, and the injectable `land` seam |
|
|
70
|
+
| [menus-and-access.md](menus-and-access.md) | Menu registry + SidebarNav; `useMe`/`useCan`/`Guarded`; group context |
|
|
71
|
+
| [grouping-and-fmt.md](grouping-and-fmt.md) | `groupBy*` helpers, `fmt.*` formatters |
|
|
72
|
+
|
|
73
|
+
## Non-negotiables (apply to every page below)
|
|
74
|
+
|
|
75
|
+
1. **Tables are SERVER-driven.** Sort, search, filters and paging are wire
|
|
76
|
+
params django-mojo answers. Nothing filters/sorts rows client-side; if
|
|
77
|
+
you are about to `Array.filter` table rows, stop — write a param.
|
|
78
|
+
2. **One envelope-unwrap boundary** (`client.ts`). A failed save REJECTS —
|
|
79
|
+
never resolve failure as success, never sniff `resp.data.data`.
|
|
80
|
+
3. **Models are definitions, not instances** (`defineModel`). TanStack Query
|
|
81
|
+
owns cache/reactivity; there is no second ownership system.
|
|
82
|
+
4. **Controlled inputs, one value pipeline.** A control can never display a
|
|
83
|
+
value its state doesn't hold. Unknown option values fall back to a
|
|
84
|
+
default WITH a `console.warn` — never to rendering nothing.
|
|
85
|
+
5. **Both themes, day one.** Style with tokens from `apps/portal/src/theme.css`
|
|
86
|
+
(`--surface`, `--ink`, `--mute`, `--accent`, …) and verify under
|
|
87
|
+
`data-theme="light"` AND `"dark"`.
|
|
88
|
+
6. **django-mojo datetimes are epoch SECONDS** on the wire. `fmt.*` accepts
|
|
89
|
+
them directly; do not `new Date(epochSeconds)` without ×1000.
|
|
90
|
+
7. **The mock speaks the exact wire contract** (`src/client/mock.ts`). When a
|
|
91
|
+
contract detail changes, the mock changes in the same commit — it is the
|
|
92
|
+
contract's executable spec.
|
|
93
|
+
|
|
94
|
+
## Working agreement
|
|
95
|
+
|
|
96
|
+
Every new component ships with: a page here, a demo section in the
|
|
97
|
+
playground (`apps/showcase/src/pages/components/`), and a browser
|
|
98
|
+
verification in both themes.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Admin Assistant
|
|
2
|
+
|
|
3
|
+
Import from `portal-mojo/admin`.
|
|
4
|
+
|
|
5
|
+
`ASSISTANT_ADMIN_SECTION` registers Conversations, Skills, and Memory beneath the Assistant navigation group. Its section clause is global-only ANY-of `sys.view_admin` or `sys.assistant`; Memory narrows its route to `sys.assistant`, matching the backend. Active-group grants never reveal it. `AssistantLauncher` uses the section gate. `AssistantContextLauncher` additionally requires `sys.view_security` or `sys.security` and only accepts `incident.Incident` or `incident.Ticket`.
|
|
6
|
+
|
|
7
|
+
## Conversation transport
|
|
8
|
+
|
|
9
|
+
- An owned, text-only conversation uses realtime only when the shared transport
|
|
10
|
+
is authenticated and the caller locally has `sys.view_admin`, matching the
|
|
11
|
+
WebSocket handler's narrower server check. The client sends exactly
|
|
12
|
+
`{type:'assistant_message',message,conversation_id?}`. A caller with only
|
|
13
|
+
`sys.assistant`, any send with attachments, an unavailable socket, and a
|
|
14
|
+
foreign inspect-only conversation stay on REST. Messages are never
|
|
15
|
+
auto-resubmitted across paths.
|
|
16
|
+
- `POST /api/assistant` sends `{message, conversation_id?, attachments?}`.
|
|
17
|
+
`attachments` is omitted for text-only sends or contains 1–5 unique positive
|
|
18
|
+
ids from completed authoritative queue references.
|
|
19
|
+
- Realtime positively projects only `assistant_thinking`, `assistant_text`,
|
|
20
|
+
`assistant_tool_call`, `assistant_plan`, `assistant_plan_update`,
|
|
21
|
+
`assistant_response`, and `assistant_error`, whether they arrive directly or
|
|
22
|
+
through the transport's one wrapper boundary. Thinking, text, tool, plan,
|
|
23
|
+
update, and response events require a positive `conversation_id` and are
|
|
24
|
+
correlated to the active turn. `assistant_error` alone may omit it; such an
|
|
25
|
+
uncorrelated server error terminates the active turn. Tool state retains
|
|
26
|
+
bounded name/status/count only; raw input and the terminal `tool_calls_made`
|
|
27
|
+
list never enter UI state, caches, logs, or mock observations. Plans omit
|
|
28
|
+
tool inputs. Terminal duplication is checked only with the backend's
|
|
29
|
+
authoritative `message_id` (never a guessed local id).
|
|
30
|
+
- A disconnect for a known conversation waits for reauthentication, then
|
|
31
|
+
reconciles from `/api/assistant/conversation/<id>?graph=detail`. A disconnect
|
|
32
|
+
during the first send before `assistant_thinking` supplies a conversation id
|
|
33
|
+
is explicitly **outcome unknown**: the conversation list is refreshed, the
|
|
34
|
+
optimistic message remains visibly uncertain, and the client never resends
|
|
35
|
+
or guesses a conversation. Server-side permission revocation remains
|
|
36
|
+
authoritative but can only be observed by the socket on a handled request or
|
|
37
|
+
reconnect; locally observed permission loss immediately clears this
|
|
38
|
+
consumer's transient stream/subscriptions without disabling the shared
|
|
39
|
+
provider.
|
|
40
|
+
- `POST /api/assistant/context` sends only `{model, pk}`. The returned conversation id is immediately fetched from `/api/assistant/conversation/<id>?graph=detail`; the client never synthesizes or reposts context text.
|
|
41
|
+
- Conversation and Skill lists/details/deletes are imperative and component-local. They do not use `defineModel`, Query cache, `ModelTable`, a `RecordFeed` adapter, persistence, or exports.
|
|
42
|
+
- A foreign conversation visible to an administrator is inspect-only. Only `conversation.user.id === me.id` enables continuation.
|
|
43
|
+
|
|
44
|
+
`AssistantFeed` uses controlled `RecordFeed` plus the shared attachment queue at
|
|
45
|
+
capacity five. Its immutable groupless destination sends no manager/group/use
|
|
46
|
+
selector. Partial upload batches retain completed references alongside
|
|
47
|
+
failed/retryable rows; only completed ids are sent atomically with required
|
|
48
|
+
text. Cancel/retry/recover/remove are real queue actions, duplicate local files
|
|
49
|
+
collapse, and successful sends alone clear completed candidates. Permission
|
|
50
|
+
loss, logout, owner/conversation change, or unmount disposes outstanding work.
|
|
51
|
+
|
|
52
|
+
User history accepts only `type:'attachment'` blocks and positively rebuilds
|
|
53
|
+
each File to `{id,filename,content_type,category}` before state/render. These
|
|
54
|
+
chips have no URL or download action: they are metadata references and do not
|
|
55
|
+
auto-ingest File contents. Assistant-generated `type:'file'` blocks remain a
|
|
56
|
+
separate URL-bearing download-card schema. Other structured blocks retain their
|
|
57
|
+
per-type schemas/caps; unknown/malformed blocks drop. Context references are
|
|
58
|
+
inert and allowlisted. Action choices require confirmation, then their bounded
|
|
59
|
+
value is sent attachment-free through the ordinary REST POST.
|
|
60
|
+
|
|
61
|
+
## Memory
|
|
62
|
+
|
|
63
|
+
Global and user tiers require the global Assistant grant. Group memory first chooses from a bounded normal `/api/group` request and supplies that id as normal request context (`?group=<id>`). A group id is never put in the memory body and ambient group state is never consulted. Keys are limited to 64 lowercase alphanumeric/colon/underscore/hyphen characters; values are limited to 500 characters.
|
|
64
|
+
|
|
65
|
+
## Shell ownership
|
|
66
|
+
|
|
67
|
+
The production Admin and showcase auth roots each mount one generic
|
|
68
|
+
`RealtimeProvider`; it is auth-owned, not `sys.view_admin`-owned. The production
|
|
69
|
+
Admin shell still mounts exactly one `RightPanelProvider` and one
|
|
70
|
+
`RightPanelSlot`. Permission loss or logout closes and unmounts Assistant
|
|
71
|
+
content. Incident and Ticket record details remain KISS modals; their Assistant
|
|
72
|
+
buttons only create the separate Assistant panel session.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Admin Bouncer
|
|
2
|
+
|
|
3
|
+
`portal-mojo/admin` packages the global Bouncer operator surfaces: the signal
|
|
4
|
+
decision feed, device reputation histories, and bot-signature CRUD. They mount
|
|
5
|
+
through the same `AdminSection` contract in the standalone admin portal or an
|
|
6
|
+
embedded `/system` admin area.
|
|
7
|
+
|
|
8
|
+
## Routes and gates
|
|
9
|
+
|
|
10
|
+
`BOUNCER_ADMIN_SECTION` has `basePath: 'security/bouncer'` and contributes:
|
|
11
|
+
|
|
12
|
+
- `security/bouncer/signals` — decision feed and graph-qualified detail
|
|
13
|
+
- `security/bouncer/devices` — fingerprints, risk tiers, related history
|
|
14
|
+
- `security/bouncer/signatures` — signature create/edit/batch management
|
|
15
|
+
|
|
16
|
+
All Bouncer gates are explicitly system-pinned. The view union mirrors the
|
|
17
|
+
django-mojo models exactly:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
[
|
|
21
|
+
'sys.manage_users',
|
|
22
|
+
'sys.view_security',
|
|
23
|
+
'sys.manage_security',
|
|
24
|
+
'sys.security',
|
|
25
|
+
'sys.users',
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Signature mutations use the narrower backend save union of
|
|
30
|
+
`sys.manage_users | sys.manage_security | sys.security | sys.users`. A device's
|
|
31
|
+
Incidents section is independently gated by `sys.view_security | sys.security`;
|
|
32
|
+
an operator who can inspect Bouncer records through a user-management grant
|
|
33
|
+
does not thereby receive incident access.
|
|
34
|
+
|
|
35
|
+
The package admin barrel must register the section before the shell builds
|
|
36
|
+
routes or menus:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { BOUNCER_ADMIN_SECTION } from './bouncer';
|
|
40
|
+
|
|
41
|
+
const sections = [BOUNCER_ADMIN_SECTION];
|
|
42
|
+
adminSectionRoutes(sections, { mount: '/system' });
|
|
43
|
+
adminSectionsMenu(sections, { mount: '/system' });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The generated route element and sidebar item apply the same gates, so direct
|
|
47
|
+
URLs fail closed as well as hidden menu links.
|
|
48
|
+
|
|
49
|
+
## Signal cache contract
|
|
50
|
+
|
|
51
|
+
Lists force `graph=list` and every row passes through
|
|
52
|
+
`sanitizeBouncerSignalRow()`. Details deliberately do not use the generic
|
|
53
|
+
`useOne()` key: `useBouncerSignalDetail(id)` requests `graph=detail` and caches
|
|
54
|
+
under:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
['/api/account/bouncer/signal', 'one', id, { graph: 'detail' }]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This prevents a sparse default/list row from satisfying a detail viewer. The
|
|
61
|
+
sanitizer deletes the backend-only top-level `token_nonce` before either list
|
|
62
|
+
or detail data can enter TanStack Query. Portal types and renderers do not
|
|
63
|
+
declare or display that field. django-mojo cleanup is tracked separately; it is
|
|
64
|
+
not a portal dependency.
|
|
65
|
+
|
|
66
|
+
`ModelTable` also passes the model-normalized list parameters to exports, not
|
|
67
|
+
the raw URL parameters. Consequently a persisted or hand-edited
|
|
68
|
+
`graph=detail` is removed and replaced by `graph=list` before a signal JSON or
|
|
69
|
+
CSV export reaches the server; the detail-only field cannot bypass the Query
|
|
70
|
+
sanitizer through the download path.
|
|
71
|
+
|
|
72
|
+
Signal decisions include all live enum values: `allow`, `monitor`, `block`, and
|
|
73
|
+
`log`. Detail shows identity layers, triggered rules, the nested default device
|
|
74
|
+
and GeoIP graphs, and client/server JSON through the escaped `JsonBlock`
|
|
75
|
+
boundary.
|
|
76
|
+
|
|
77
|
+
## Device history
|
|
78
|
+
|
|
79
|
+
Device details use the endpoint's default graph. Related signal and incident
|
|
80
|
+
tables are intentionally small local components: their pagination lives in
|
|
81
|
+
component state, not the URL parameter store, and they do not nest
|
|
82
|
+
`ModelTable`. This keeps switching a detail rail section from rewriting or
|
|
83
|
+
colliding with the parent page's table URL.
|
|
84
|
+
|
|
85
|
+
Related signals query the signal endpoint by MUID. Related incidents query
|
|
86
|
+
`/api/incident/incident` with `category__startswith=security:bouncer` and
|
|
87
|
+
`search=<device muid>`, matching web-mojo and the model's `details` search
|
|
88
|
+
contract. The reporter does not populate Incident `model_name`/`model_id`, so
|
|
89
|
+
those fields must not be used as a relationship filter. Incident rows remain
|
|
90
|
+
typed independently from incident events.
|
|
91
|
+
Matching rows open the shared incident forensic detail while preserving this
|
|
92
|
+
prefix-plus-MUID contract.
|
|
93
|
+
|
|
94
|
+
## Signature writes
|
|
95
|
+
|
|
96
|
+
The exact `sig_type` values are:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
ip | subnet_24 | subnet_16 | user_agent | fingerprint | signal_set
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Create always sends `source: 'manual'`; edit never rewrites the source. The
|
|
103
|
+
confidence parser accepts the full inclusive `0..100` range and preserves
|
|
104
|
+
numeric zero. Enable and disable batches call ordinary per-record saves. Delete
|
|
105
|
+
is intentionally absent: django-mojo's `BotSignature.RestMeta` does not declare
|
|
106
|
+
`CAN_DELETE`, and the live endpoint rejects DELETE even though the mock once
|
|
107
|
+
accepted it. No batch or action endpoints are invented, so partial failures use
|
|
108
|
+
`ModelTable`'s normal `Promise.allSettled` reporting.
|
|
109
|
+
|
|
110
|
+
## Styling and showcase
|
|
111
|
+
|
|
112
|
+
The package emits semantic `bouncer-*` classes and imports no application CSS.
|
|
113
|
+
Consumers must include `theme/admin-bouncer.css` after the shared token
|
|
114
|
+
definitions. The repository keeps matching leaf files in `apps/portal` and
|
|
115
|
+
`apps/showcase`, consistent with the current intentional theme duplication.
|
|
116
|
+
|
|
117
|
+
The showcase's `AdminBouncerDemo` switches among the three real package pages
|
|
118
|
+
so only one URL-backed top-level table is mounted at a time. Open signal/device
|
|
119
|
+
rows to exercise details; use the signatures page to exercise ordinary mock
|
|
120
|
+
mutations.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Admin CloudWatch
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
import {
|
|
5
|
+
CloudWatchDashboardPage, CloudWatchChart,
|
|
6
|
+
CLOUDWATCH_ADMIN_SECTION, loadCloudWatchSeries,
|
|
7
|
+
} from 'portal-mojo/admin';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The package owns one global, no-group CloudWatch page at `/cloudwatch` standalone and `/system/cloudwatch` embedded. Its exact gate is `sys.manage_aws`. It contributes one Observability → CloudWatch navigation item and registers no resource-record routes; resource inspection stays in KISS detail modals.
|
|
11
|
+
|
|
12
|
+
## Dashboard contract
|
|
13
|
+
|
|
14
|
+
The page renders exactly twelve charts: four EC2 (`cpu`, `net_out`, `memory`, `disk`), four RDS (`cpu`, `conns`, `read_latency`, `write_latency`), and four ElastiCache Redis (`cpu`, `conns`, `cache_misses`, `cache_hits`). ELB and Lambda are explicitly absent because django-mojo's `ACCOUNT_NAMESPACE` exposes neither account type.
|
|
15
|
+
|
|
16
|
+
The first four charts mount immediately and the other eight mount near the viewport. One page control owns manual and five-minute refreshes; child `MetricsChart`s hide their own refresh control and react to a shared `refreshSignal`. Resource detail metrics use the same pattern inside the modal.
|
|
17
|
+
|
|
18
|
+
CloudWatch supports only `minutes`, `hours`, and `days` on this surface. `MetricsChart.allowedGranularities` omits the unsupported week/month controls while retaining the normal range, type, stats, and data-table controls.
|
|
19
|
+
|
|
20
|
+
## Wire contracts
|
|
21
|
+
|
|
22
|
+
`GET /api/aws/cloudwatch/resources` returns resource arrays at the envelope's top level, not under `data`:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{"status":true,"ec2":[],"rds":[],"redis":[]}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Every object is allowlist-projected and validated inside the TanStack query function before it can enter cache. Redis `num_nodes` must be a positive integer. Provider errors are redacted and bounded before being thrown into query state.
|
|
29
|
+
|
|
30
|
+
`GET /api/aws/cloudwatch/fetch` requires `account` and `category` and answers with the metrics-style nested map:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{"status":true,"data":{"data":{"friendly-name":[1,2]},"labels":["10:00","11:00"]}}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The loader forwards only `account`, `category`, optional non-empty `slugs`, `granularity`, `stat`, and `dt_start`/`dt_end`. An empty slug selection omits `slugs` entirely so the backend discovers all resources; sending `slugs=` would mean an explicitly empty selection.
|
|
37
|
+
|
|
38
|
+
EC2 requests may use the Name tag or raw instance ID. Responses always key the series by the friendly slug. A detail modal therefore requests the immutable raw ID but deliberately displays the friendly response name. Duplicate EC2 Name tags are a backend limitation: its reverse map chooses the last resource for a friendly-name request, and its response map collapses same-name series. The UI shows both friendly slug and raw ID in inventory/detail so operators can disambiguate and targets details by raw ID, but cannot recover two same-name response keys from one fetch.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Admin credentials
|
|
2
|
+
|
|
3
|
+
`portal-mojo/admin` ships the group API-key and webhook-subscription domain as
|
|
4
|
+
one reusable credential implementation. The same components back GroupDetail,
|
|
5
|
+
the standalone admin portal, and an embedded `/system` admin mount.
|
|
6
|
+
|
|
7
|
+
## Routes and access
|
|
8
|
+
|
|
9
|
+
The built-in `ADMIN_SECTIONS` contribution mounts these standalone routes:
|
|
10
|
+
|
|
11
|
+
- `/api-keys` — cross-group API keys
|
|
12
|
+
- `/webhook-subscriptions` — cross-group webhook subscriptions
|
|
13
|
+
|
|
14
|
+
Both the sidebar entries and generated React Router elements require
|
|
15
|
+
`sys.manage_groups` or `sys.groups`. `adminSectionRoutes()` applies the section
|
|
16
|
+
gate and the route gate independently, so a direct URL fails closed just like
|
|
17
|
+
the menu. Embedded portals pass a mount to both helpers:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
const routes = adminSectionRoutes(ADMIN_SECTIONS, { mount: '/system' });
|
|
21
|
+
const menu = adminSectionsMenu(ADMIN_SECTIONS, { mount: '/system' });
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`AdminSection.basePath` defaults to the section id. A root-level section may
|
|
25
|
+
use `basePath: ''`; its labeled routes become visible child links rather than
|
|
26
|
+
claiming `/`. Prefixed sections receive an index redirect to the first route
|
|
27
|
+
the current operator can see.
|
|
28
|
+
|
|
29
|
+
## Group API keys
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import {
|
|
33
|
+
GroupApiKeysSection,
|
|
34
|
+
GroupApiKeysPage,
|
|
35
|
+
registerGroupApiKeyPermissions,
|
|
36
|
+
} from 'portal-mojo/admin';
|
|
37
|
+
|
|
38
|
+
<GroupApiKeysSection group={{ id: 42, name: 'Acme' }} />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Both fixed-group credential sections accept an optional permission override:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<GroupApiKeysSection group={group} permission={GLOBAL_CREDENTIAL_PERMS} />
|
|
45
|
+
<WebhookSubscriptionsSection group={group} permission={GLOBAL_CREDENTIAL_PERMS} />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Omitting `permission` preserves group-portal behavior through
|
|
49
|
+
`GROUP_CREDENTIAL_PERMS`. The app-local global Group Admin passes the
|
|
50
|
+
system-pinned `GLOBAL_CREDENTIAL_PERMS`; the wrapper checks that clause before
|
|
51
|
+
mounting its list, so denied operators issue no credential background query.
|
|
52
|
+
|
|
53
|
+
Creation uses `useCreateGroupApiKey()`, not the generic model save hook. The
|
|
54
|
+
backend create echo contains `{token, ...row}`; the hook splits it immediately,
|
|
55
|
+
writes only the safe row to TanStack Query, and passes the token through a
|
|
56
|
+
one-shot callback while the mutation is still pending. MutationCache resolves
|
|
57
|
+
with only the scrubbed row; the callback lifetime ends when the dialog closes.
|
|
58
|
+
A later operator reveal is a deliberate
|
|
59
|
+
`graph=token` request through `fetchApiKeyToken()`. django-mojo audits that graph
|
|
60
|
+
as `api_key:token_read`, and the response never enters Query cache.
|
|
61
|
+
|
|
62
|
+
All ordinary API-key lists force `graph=default`, discard URL/persisted
|
|
63
|
+
`graph` and unsupported search params before creating a query key, and scrub
|
|
64
|
+
token fields from every list/detail/save result before any cache write.
|
|
65
|
+
|
|
66
|
+
Create sends only permission controls whose value is `true`. Edit compares only
|
|
67
|
+
controls that were actually rendered and sends a partial permission dictionary;
|
|
68
|
+
unknown or protected grants are therefore preserved, while a visible changed
|
|
69
|
+
grant may send `false` to revoke it.
|
|
70
|
+
|
|
71
|
+
The permission catalog is live and injectable:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
registerGroupApiKeyPermissions([
|
|
75
|
+
{ name: 'view_orders', label: 'View Orders' },
|
|
76
|
+
{
|
|
77
|
+
name: 'settle_orders',
|
|
78
|
+
label: 'Settle Orders',
|
|
79
|
+
grantPermissions: ['sys.manage_orders'],
|
|
80
|
+
},
|
|
81
|
+
]);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Registration replaces the same permission name and appends new names. Mounted
|
|
85
|
+
editors subscribe to changes. `grantPermissions` gates the control itself; the
|
|
86
|
+
backend remains authoritative on every save.
|
|
87
|
+
|
|
88
|
+
## Webhooks
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { WebhookSubscriptionsSection } from 'portal-mojo/admin';
|
|
92
|
+
|
|
93
|
+
<WebhookSubscriptionsSection group={{ id: 42, name: 'Acme' }} />
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Webhook forms edit only `url`, `events`, and `is_active`. TagInput CSV values
|
|
97
|
+
pass through `normalizeWebhookEvents()` before save. Metadata is intentionally
|
|
98
|
+
absent: the default graph does not return it, so a generic edit form cannot seed
|
|
99
|
+
or safely preserve the object.
|
|
100
|
+
|
|
101
|
+
The signing secret belongs to the group, not one subscription. It is never
|
|
102
|
+
fetched on render because the reveal endpoint auto-mints on first use. Reveal
|
|
103
|
+
and confirmed rotation are explicit button actions.
|
|
104
|
+
|
|
105
|
+
## Styling contract
|
|
106
|
+
|
|
107
|
+
Credential components render the existing semantic `ga-*` classes and never
|
|
108
|
+
import an application stylesheet. A consuming portal or showcase must include
|
|
109
|
+
the `group-admin.css` semantic rules in its theme entry, as the repository's
|
|
110
|
+
`apps/portal/src/theme.css` and `apps/showcase/src/theme.css` do today.
|
|
111
|
+
|
|
112
|
+
## Personal keys are separate
|
|
113
|
+
|
|
114
|
+
The personal `/api/account/api_keys` page and `/api/auth/generate_api_key` flow
|
|
115
|
+
are intentionally unchanged. This package domain owns only group-scoped
|
|
116
|
+
`/api/group/apikey` credentials and `/api/group/webhook_subscriptions`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Admin dashboard
|
|
2
|
+
|
|
3
|
+
`AdminDashboardPage` is the package-owned global Admin landing at `/` in the standalone portal and `/system` when embedded. Import it, its data helpers, and `ADMIN_DASHBOARD_PERMISSIONS` from `portal-mojo/admin`.
|
|
4
|
+
|
|
5
|
+
The page uses only authoritative django-mojo signals: time series `user_activity_day`, `group_activity_day`, `api_calls`, and `api_errors`; global scalars `total_users` and `total_groups`; and `size=0` counts for open incidents, failed jobs, and bounced sent email. Scalar values must be finite non-negative strings or numbers. Malformed values surface as errors rather than fabricated zeroes.
|
|
6
|
+
|
|
7
|
+
Each panel owns an independent permission gate. A caller without metrics access can still see authorized incident, job, email, or login-geography panels, and no denied panel issues a request. Every dashboard query cache key includes the authenticated caller UID. Login geography starts at the previous 30-day date and deliberately omits `dr_end`, matching the backend's instant-bound semantics.
|
|
8
|
+
|
|
9
|
+
The dashboard section contributes the single Overview → Dashboard menu entry. If the caller has none of the aggregate dashboard grants, the root route redirects to their first visible Admin route; if no Admin route is visible it renders Access denied.
|
|
10
|
+
|
|
11
|
+
Attention links preserve the exact server filter in the URL:
|
|
12
|
+
|
|
13
|
+
- `/security/incidents?status=open`
|
|
14
|
+
- `/jobs/list?status=failed`
|
|
15
|
+
- `/email/sent?status=bounced`
|