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
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// Admin jobs — TanStack query hooks over the control plane and the job model
|
|
2
|
+
// endpoints.
|
|
3
|
+
//
|
|
4
|
+
// EVERY hook is `enabled`-gated on the matching `useCan`. A viewer-only
|
|
5
|
+
// operator must issue zero denied requests: a permission-disabled query never
|
|
6
|
+
// fires at all, rather than firing and catching a 403. `useCan` is fail-closed
|
|
7
|
+
// while `me` loads, so the first render of an admin page never races a gate.
|
|
8
|
+
import { useMemo } from 'react';
|
|
9
|
+
import { useQuery } from '@tanstack/react-query';
|
|
10
|
+
import { mojoList, useCan, type Params } from '../../client';
|
|
11
|
+
import {
|
|
12
|
+
fetchChannels,
|
|
13
|
+
fetchJobStats,
|
|
14
|
+
fetchJobsConfig,
|
|
15
|
+
fetchQueueSizes,
|
|
16
|
+
fetchRunnerSysinfo,
|
|
17
|
+
fetchRunners,
|
|
18
|
+
} from './control';
|
|
19
|
+
import {
|
|
20
|
+
JOBS_MANAGE_PERMS,
|
|
21
|
+
JOBS_VIEW_PERMS,
|
|
22
|
+
JobEventModel,
|
|
23
|
+
JobLogModel,
|
|
24
|
+
JobModel,
|
|
25
|
+
type JobLogRow,
|
|
26
|
+
type JobRow,
|
|
27
|
+
type JobTimelineEntry,
|
|
28
|
+
} from './models';
|
|
29
|
+
|
|
30
|
+
/** One root (`['jobs']`) so a mutation can invalidate the whole domain. */
|
|
31
|
+
export const jobsKeys = {
|
|
32
|
+
root: ['jobs'] as const,
|
|
33
|
+
stats: () => ['jobs', 'stats'] as const,
|
|
34
|
+
runners: (channel?: string) => ['jobs', 'runners', channel ?? null] as const,
|
|
35
|
+
channels: () => ['jobs', 'channels'] as const,
|
|
36
|
+
queueSizes: () => ['jobs', 'queue-sizes'] as const,
|
|
37
|
+
config: () => ['jobs', 'config'] as const,
|
|
38
|
+
sysinfo: (runnerId: string) => ['jobs', 'sysinfo', runnerId] as const,
|
|
39
|
+
timeline: (jobId: string) => [JobEventModel.endpoint, 'timeline', jobId] as const,
|
|
40
|
+
jobLogs: (jobId: string) => [JobLogModel.endpoint, 'job', jobId] as const,
|
|
41
|
+
runnerLogs: (runnerId: string) => [JobLogModel.endpoint, 'runner', runnerId] as const,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** Dashboard tick. Fast enough to be an alerting signal, slow enough to be free. */
|
|
45
|
+
const DASHBOARD_INTERVAL_MS = 60_000;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* `GET /api/jobs/stats` — the dashboard's single source of truth. It replaces
|
|
49
|
+
* `/api/jobs/health`, which raises a KeyError server-side and is never called.
|
|
50
|
+
*/
|
|
51
|
+
export function useJobStats() {
|
|
52
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
53
|
+
return useQuery({
|
|
54
|
+
queryKey: jobsKeys.stats(),
|
|
55
|
+
queryFn: fetchJobStats,
|
|
56
|
+
enabled: can,
|
|
57
|
+
refetchInterval: DASHBOARD_INTERVAL_MS,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* `GET /api/jobs/runners` — the COMPLETE fleet list. The endpoint has no
|
|
63
|
+
* paging/sort/search contract, so callers sort and filter client-side.
|
|
64
|
+
*/
|
|
65
|
+
export function useRunners(channel?: string) {
|
|
66
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
67
|
+
return useQuery({
|
|
68
|
+
queryKey: jobsKeys.runners(channel),
|
|
69
|
+
queryFn: () => fetchRunners(channel),
|
|
70
|
+
enabled: can,
|
|
71
|
+
refetchInterval: DASHBOARD_INTERVAL_MS,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `GET /api/jobs/control/channels` — channels discovered from Redis streams. */
|
|
76
|
+
export function useDiscoveredChannels() {
|
|
77
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
78
|
+
return useQuery({
|
|
79
|
+
queryKey: jobsKeys.channels(),
|
|
80
|
+
queryFn: fetchChannels,
|
|
81
|
+
enabled: can,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ChannelOptions {
|
|
86
|
+
/** Deduped, sorted channel names. Empty means nothing is configured. */
|
|
87
|
+
channels: string[];
|
|
88
|
+
options: { value: string; label: string }[];
|
|
89
|
+
isPending: boolean;
|
|
90
|
+
/** True once both sources answered and neither offered a channel. */
|
|
91
|
+
isEmpty: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The union of the two channel sources, because neither is complete on its own:
|
|
96
|
+
* `stats.channels` is keyed by `settings.JOBS_CHANNELS` (declared but maybe
|
|
97
|
+
* never used), while `control/channels` scans Redis for streams that actually
|
|
98
|
+
* exist (including ones nobody declared). An operation targeting a channel
|
|
99
|
+
* should be offered for either.
|
|
100
|
+
*/
|
|
101
|
+
export function useChannelOptions(): ChannelOptions {
|
|
102
|
+
const stats = useJobStats();
|
|
103
|
+
const discovered = useDiscoveredChannels();
|
|
104
|
+
const channels = useMemo(() => {
|
|
105
|
+
const names = new Set<string>();
|
|
106
|
+
for (const name of Object.keys(stats.data?.channels ?? {})) if (name) names.add(name);
|
|
107
|
+
for (const name of discovered.data ?? []) if (name) names.add(name);
|
|
108
|
+
return [...names].sort((a, b) => a.localeCompare(b));
|
|
109
|
+
}, [stats.data, discovered.data]);
|
|
110
|
+
const isPending = stats.isPending || discovered.isPending;
|
|
111
|
+
return {
|
|
112
|
+
channels,
|
|
113
|
+
options: channels.map((value) => ({ value, label: value })),
|
|
114
|
+
isPending,
|
|
115
|
+
isEmpty: !isPending && channels.length === 0,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** `GET /api/jobs/control/queue-sizes` — Redis + DB counts per channel. */
|
|
120
|
+
export function useQueueSizes() {
|
|
121
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
122
|
+
return useQuery({
|
|
123
|
+
queryKey: jobsKeys.queueSizes(),
|
|
124
|
+
queryFn: fetchQueueSizes,
|
|
125
|
+
enabled: can,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* `GET /api/jobs/control/config` — MANAGE-gated, unlike every other read in
|
|
131
|
+
* this domain. A view-only operator never issues this request.
|
|
132
|
+
*/
|
|
133
|
+
export function useJobsConfig() {
|
|
134
|
+
const { can } = useCan(JOBS_MANAGE_PERMS);
|
|
135
|
+
return useQuery({
|
|
136
|
+
queryKey: jobsKeys.config(),
|
|
137
|
+
queryFn: fetchJobsConfig,
|
|
138
|
+
enabled: can,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* `GET /api/jobs/runners/sysinfo/<id>` — a redis broadcast-execute that blocks
|
|
144
|
+
* the request thread for up to `timeout` seconds waiting for the runner to
|
|
145
|
+
* answer. NEVER polled: it fetches once when the caller enables it, and again
|
|
146
|
+
* only on an explicit refetch. Results stay fresh indefinitely so switching
|
|
147
|
+
* sections does not re-broadcast.
|
|
148
|
+
*/
|
|
149
|
+
export function useRunnerSysinfo(runnerId: string | null, opts: { enabled?: boolean; timeout?: number } = {}) {
|
|
150
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
151
|
+
return useQuery({
|
|
152
|
+
queryKey: jobsKeys.sysinfo(runnerId ?? ''),
|
|
153
|
+
queryFn: () => fetchRunnerSysinfo(runnerId!, opts.timeout ?? 5),
|
|
154
|
+
enabled: can && runnerId != null && opts.enabled !== false,
|
|
155
|
+
refetchInterval: false,
|
|
156
|
+
refetchOnMount: false,
|
|
157
|
+
staleTime: Infinity,
|
|
158
|
+
// A runner that never answers is a 404 envelope; each retry costs
|
|
159
|
+
// another full timeout window on the server.
|
|
160
|
+
retry: false,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const TIMELINE_SIZE = 200;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* `GET /api/jobs/event?job=<id>&graph=timeline` — the lifecycle trail.
|
|
168
|
+
*
|
|
169
|
+
* Job carries NO `recent_events` in any graph (only inside the `get_status`
|
|
170
|
+
* action payload), so this endpoint IS the timeline. `job` is the relation
|
|
171
|
+
* name; `job_id` would be silently dropped by `build_rest_filters` and return
|
|
172
|
+
* every event in the system. Sorted ASCENDING because a lifecycle reads
|
|
173
|
+
* forward, against the model's own `-at` default.
|
|
174
|
+
*/
|
|
175
|
+
export function useJobTimeline(jobId: string | null) {
|
|
176
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
177
|
+
return useQuery({
|
|
178
|
+
queryKey: jobsKeys.timeline(jobId ?? ''),
|
|
179
|
+
queryFn: () => mojoList<JobTimelineEntry>(JobEventModel.endpoint, {
|
|
180
|
+
job: jobId!,
|
|
181
|
+
graph: 'timeline',
|
|
182
|
+
sort: 'at',
|
|
183
|
+
size: TIMELINE_SIZE,
|
|
184
|
+
}),
|
|
185
|
+
enabled: can && jobId != null,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const LOG_SIZE = 200;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* `GET /api/jobs/logs?job=<id>` — one job's log lines, newest first.
|
|
193
|
+
* `job=` (the relation) is the ONLY filter that works; `job_id=` is dropped.
|
|
194
|
+
*/
|
|
195
|
+
export function useJobLogs(jobId: string | null) {
|
|
196
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
197
|
+
return useQuery({
|
|
198
|
+
queryKey: jobsKeys.jobLogs(jobId ?? ''),
|
|
199
|
+
queryFn: () => mojoList<JobLogRow>(JobLogModel.endpoint, {
|
|
200
|
+
job: jobId!,
|
|
201
|
+
sort: '-created',
|
|
202
|
+
size: LOG_SIZE,
|
|
203
|
+
}),
|
|
204
|
+
enabled: can && jobId != null,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Runner detail polls its live panes on this tick; sysinfo never joins them. */
|
|
209
|
+
const RUNNER_DETAIL_INTERVAL_MS = 15_000;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* A runner's currently-running jobs. `runner_id` IS a real Job field, so
|
|
213
|
+
* unlike the JobLog case this filter genuinely works.
|
|
214
|
+
*/
|
|
215
|
+
export function useRunnerActiveJobs(runnerId: string | null) {
|
|
216
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
217
|
+
const params: Params = { runner_id: runnerId ?? '', status: 'running', sort: '-started_at', size: 25 };
|
|
218
|
+
return useQuery({
|
|
219
|
+
queryKey: [JobModel.endpoint, 'runner-active', runnerId ?? ''],
|
|
220
|
+
queryFn: () => mojoList<JobRow>(JobModel.endpoint, params),
|
|
221
|
+
enabled: can && runnerId != null,
|
|
222
|
+
refetchInterval: RUNNER_DETAIL_INTERVAL_MS,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** A runner's most recent completions. */
|
|
227
|
+
export function useRunnerJobHistory(runnerId: string | null) {
|
|
228
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
229
|
+
const params: Params = { runner_id: runnerId ?? '', status: 'completed', sort: '-created', size: 25 };
|
|
230
|
+
return useQuery({
|
|
231
|
+
queryKey: [JobModel.endpoint, 'runner-history', runnerId ?? ''],
|
|
232
|
+
queryFn: () => mojoList<JobRow>(JobModel.endpoint, params),
|
|
233
|
+
enabled: can && runnerId != null,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** How many of a runner's recent jobs feed the two-step log lookup. */
|
|
238
|
+
const RUNNER_LOG_JOB_WINDOW = 25;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A runner's recent log lines, in TWO steps — because JobLog has no
|
|
242
|
+
* `runner_id` field at all, and `build_rest_filters` silently drops unknown
|
|
243
|
+
* keys, so `?runner_id=` returns the whole unfiltered log table (web-mojo
|
|
244
|
+
* shipped exactly that and presented it as this runner's logs).
|
|
245
|
+
*
|
|
246
|
+
* 1. the runner's most recent jobs → `/api/jobs/job?runner_id=&sort=-created`
|
|
247
|
+
* 2. their log lines → `/api/jobs/logs?job__in=<ids>`
|
|
248
|
+
*
|
|
249
|
+
* No jobs means no logs, and the query resolves empty without a second call.
|
|
250
|
+
*/
|
|
251
|
+
export function useRunnerJobLogs(runnerId: string | null) {
|
|
252
|
+
const { can } = useCan(JOBS_VIEW_PERMS);
|
|
253
|
+
return useQuery({
|
|
254
|
+
queryKey: jobsKeys.runnerLogs(runnerId ?? ''),
|
|
255
|
+
queryFn: async () => {
|
|
256
|
+
const jobParams: Params = {
|
|
257
|
+
runner_id: runnerId!,
|
|
258
|
+
sort: '-created',
|
|
259
|
+
size: RUNNER_LOG_JOB_WINDOW,
|
|
260
|
+
};
|
|
261
|
+
const jobs = await mojoList<JobRow>(JobModel.endpoint, jobParams);
|
|
262
|
+
const ids = jobs.rows.map((row) => row.id).filter(Boolean);
|
|
263
|
+
if (ids.length === 0) return { rows: [] as JobLogRow[], count: 0, start: 0, size: 0 };
|
|
264
|
+
return mojoList<JobLogRow>(JobLogModel.endpoint, {
|
|
265
|
+
job__in: ids.join(','),
|
|
266
|
+
sort: '-created',
|
|
267
|
+
size: LOG_SIZE,
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
enabled: can && runnerId != null,
|
|
271
|
+
});
|
|
272
|
+
}
|