@skyhook-io/radar-app 1.8.6 → 1.8.8
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/README.md +7 -1
- package/package.json +12 -10
- package/src/App.tsx +278 -230
- package/src/RadarApp.tsx +91 -20
- package/src/api/client.argoResourceSync.test.ts +69 -0
- package/src/api/client.delta.test.ts +89 -0
- package/src/api/client.deltaSync.test.ts +216 -0
- package/src/api/client.rightsizing.test.ts +32 -0
- package/src/api/client.ts +1462 -246
- package/src/api/diagnose.ts +288 -0
- package/src/api/timelineSource.test.ts +217 -0
- package/src/api/timelineSource.ts +582 -0
- package/src/components/ConnectionErrorView.tsx +174 -70
- package/src/components/ContextSwitcher.tsx +13 -5
- package/src/components/applications/ApplicationsView.tsx +775 -80
- package/src/components/audit/AuditSettingsDialog.tsx +2 -2
- package/src/components/cost/ApplicationCostTab.test.ts +204 -0
- package/src/components/cost/ApplicationCostTab.tsx +571 -0
- package/src/components/cost/CostTrendChart.tsx +103 -72
- package/src/components/cost/CostView.test.ts +12 -0
- package/src/components/cost/CostView.tsx +494 -229
- package/src/components/cost/CostViewTabs.test.tsx +21 -0
- package/src/components/cost/CostViewTabs.tsx +40 -0
- package/src/components/cost/CurrentAllocationUse.test.ts +21 -0
- package/src/components/cost/CurrentAllocationUse.tsx +126 -0
- package/src/components/cost/WorkloadCostTab.test.ts +153 -0
- package/src/components/cost/WorkloadCostTab.tsx +372 -0
- package/src/components/cost/cloud-console.test.ts +39 -0
- package/src/components/cost/cloud-console.ts +81 -0
- package/src/components/cost/errors.ts +8 -0
- package/src/components/cost/format.test.ts +27 -0
- package/src/components/cost/format.ts +46 -0
- package/src/components/cost/kinds.ts +5 -0
- package/src/components/curl/ServiceCurlButton.tsx +2 -2
- package/src/components/diagnose/AISettings.tsx +116 -0
- package/src/components/diagnose/DiagnoseContext.tsx +491 -0
- package/src/components/diagnose/DiagnoseSurface.tsx +385 -0
- package/src/components/diagnose/Home.tsx +163 -0
- package/src/components/diagnose/InvestigationView.tsx +604 -0
- package/src/components/diagnose/LocalDiagnoseAction.tsx +150 -0
- package/src/components/diagnose/launch.ts +65 -0
- package/src/components/diagnose/parts.tsx +1689 -0
- package/src/components/dock/BottomDock.tsx +2 -3
- package/src/components/dock/WorkloadLogsTab.tsx +21 -5
- package/src/components/execution/BatchExecutionView.test.ts +170 -0
- package/src/components/execution/BatchExecutionView.tsx +1329 -0
- package/src/components/execution/batch-run-actions.test.ts +48 -0
- package/src/components/execution/batch-run-actions.ts +24 -0
- package/src/components/execution/batch-timeline.test.ts +57 -0
- package/src/components/execution/batch-timeline.ts +46 -0
- package/src/components/execution/execution-definition.test.ts +208 -0
- package/src/components/execution/execution-definition.ts +245 -0
- package/src/components/gitops/ArgoResourceDiffLoader.tsx +23 -0
- package/src/components/gitops/GitOpsView.tsx +81 -14
- package/src/components/gitops/RevisionMetaChip.tsx +63 -0
- package/src/components/helm/ChartBrowser.tsx +2 -3
- package/src/components/helm/HelmCompareRoute.tsx +1 -2
- package/src/components/helm/HelmReleaseDrawer.test.ts +17 -0
- package/src/components/helm/HelmReleaseDrawer.tsx +376 -43
- package/src/components/helm/HelmView.tsx +2 -3
- package/src/components/helm/InstallWizard.tsx +3 -5
- package/src/components/helm/ManifestDiffViewer.tsx +1 -31
- package/src/components/helm/TrackChartSourceDialog.tsx +48 -4
- package/src/components/helm/ValuesDiffPreview.tsx +17 -7
- package/src/components/home/CostCard.tsx +21 -36
- package/src/components/home/HomeView.tsx +17 -15
- package/src/components/home/MCPSetupDialog.tsx +1 -1
- package/src/components/home/mcpToolCatalog.ts +2 -2
- package/src/components/issues/IssuesPane.tsx +9 -1
- package/src/components/logs/ScheduledWorkloadLogsViewer.tsx +135 -0
- package/src/components/portforward/PortForwardButton.tsx +2 -2
- package/src/components/portforward/PortForwardManager.tsx +19 -14
- package/src/components/resource/PrometheusChartsGrid.tsx +6 -80
- package/src/components/resource/RightsizingStrip.test.ts +109 -0
- package/src/components/resource/RightsizingStrip.tsx +320 -127
- package/src/components/resources/ImageFilesystemModal.tsx +2 -2
- package/src/components/resources/PodFilesystemModal.tsx +2 -3
- package/src/components/resources/ResourceDetailDrawer.tsx +2 -0
- package/src/components/resources/ResourcesView.tsx +13 -4
- package/src/components/resources/renderers/CronWorkflowRenderer.tsx +1 -0
- package/src/components/resources/renderers/index.ts +1 -0
- package/src/components/rightsizing/RightsizingScanView.tsx +938 -0
- package/src/components/rightsizing/copy.test.ts +56 -0
- package/src/components/rightsizing/model.test.ts +227 -0
- package/src/components/rightsizing/model.ts +158 -0
- package/src/components/rightsizing/presentation.test.ts +104 -0
- package/src/components/rightsizing/presentation.ts +94 -0
- package/src/components/settings/MyPermissionsDialog.tsx +66 -116
- package/src/components/settings/SettingsDialog.tsx +1290 -283
- package/src/components/shared/LargeClusterNamespacePicker.tsx +2 -2
- package/src/components/timeline/LocalTimelineScrubber.tsx +212 -0
- package/src/components/timeline/RetainedTimelineScrubber.tsx +311 -0
- package/src/components/timeline/TimelineList.tsx +81 -13
- package/src/components/timeline/TimelineView.tsx +842 -26
- package/src/components/timeline/TimelineView.urlparams.test.ts +335 -0
- package/src/components/traffic/TrafficFilterSidebar.tsx +2 -2
- package/src/components/traffic/TrafficView.tsx +8 -4
- package/src/components/ui/CommandPalette.tsx +2 -2
- package/src/components/workload/WorkloadView.tsx +912 -245
- package/src/context/ConnectionContext.tsx +109 -11
- package/src/context/DiagnoseCustomization.tsx +42 -0
- package/src/context/TimelineSource.tsx +50 -0
- package/src/hooks/useClusterLoadState.ts +73 -0
- package/src/hooks/useEventSource.ts +6 -0
- package/src/index.css +162 -1
- package/src/index.ts +12 -0
- package/src/main.tsx +1 -1
- package/src/types/clusterLoadState.ts +33 -0
- package/src/utils/navigation.ts +10 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect, useRef } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import { Shield, X, Loader2, Lock, ExternalLink } from 'lucide-react'
|
|
2
|
+
import { Loader2, Lock, ExternalLink } from 'lucide-react'
|
|
4
3
|
import { clsx } from 'clsx'
|
|
5
4
|
import { useQuery } from '@tanstack/react-query'
|
|
6
5
|
import {
|
|
@@ -11,137 +10,88 @@ import {
|
|
|
11
10
|
rbacNonResourceUrlBadgeClass,
|
|
12
11
|
type RBACWhoamiResponse,
|
|
13
12
|
} from '@skyhook-io/k8s-ui'
|
|
14
|
-
import { useAnimatedUnmount } from '../../hooks/useAnimatedUnmount'
|
|
15
|
-
import { TRANSITION_BACKDROP, TRANSITION_PANEL } from '../../utils/animation'
|
|
16
13
|
import { useNamespaces, useAuthMe, fetchJSON } from '../../api/client'
|
|
17
14
|
import { useRBACWhoami } from '../../api/rbac'
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export function MyPermissionsDialog({ open, onClose }: MyPermissionsDialogProps) {
|
|
25
|
-
const dialogRef = useRef<HTMLDivElement>(null)
|
|
26
|
-
const { shouldRender, isOpen } = useAnimatedUnmount(open, 200)
|
|
16
|
+
// MyPermissionsContent renders the "your access on this cluster" viewer inline
|
|
17
|
+
// (no modal chrome) so the Settings dialog can host it directly in its content
|
|
18
|
+
// pane. `active` gates the apiserver queries so opening Settings on another
|
|
19
|
+
// section doesn't fire a SelfSubjectRulesReview the user never asked for.
|
|
20
|
+
export function MyPermissionsContent({ active }: { active: boolean }) {
|
|
27
21
|
const [namespace, setNamespace] = useState('default')
|
|
28
|
-
|
|
29
22
|
const { data: authMe } = useAuthMe()
|
|
30
23
|
const { data: namespaces } = useNamespaces()
|
|
31
|
-
const { data: whoami, isLoading, error } = useRBACWhoami(namespace,
|
|
24
|
+
const { data: whoami, isLoading, error } = useRBACWhoami(namespace, active)
|
|
32
25
|
|
|
33
|
-
//
|
|
26
|
+
// Land on an accessible namespace: once the list loads, if the current pick
|
|
27
|
+
// ('default' initially) isn't among the user's namespaces and they haven't
|
|
28
|
+
// chosen one, switch to the first accessible one — otherwise a user scoped to
|
|
29
|
+
// e.g. team-a would open on an empty 'default'.
|
|
30
|
+
const userPicked = useRef(false)
|
|
34
31
|
useEffect(() => {
|
|
35
|
-
if (!
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
e.stopPropagation()
|
|
39
|
-
onClose()
|
|
40
|
-
}
|
|
32
|
+
if (userPicked.current || !namespaces?.length) return
|
|
33
|
+
if (!namespaces.some((ns) => ns.name === namespace)) {
|
|
34
|
+
setNamespace(namespaces[0].name)
|
|
41
35
|
}
|
|
42
|
-
|
|
43
|
-
return () => document.removeEventListener('keydown', handler, true)
|
|
44
|
-
}, [open, onClose])
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (open && dialogRef.current) dialogRef.current.focus()
|
|
47
|
-
}, [open])
|
|
36
|
+
}, [namespaces, namespace])
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
isOpen ? 'opacity-100' : 'opacity-0',
|
|
58
|
-
)}
|
|
59
|
-
onClick={onClose}
|
|
60
|
-
/>
|
|
61
|
-
<div
|
|
62
|
-
ref={dialogRef}
|
|
63
|
-
tabIndex={-1}
|
|
64
|
-
className={clsx(
|
|
65
|
-
'relative bg-theme-surface border border-theme-border shadow-theme-lg w-full outline-none flex flex-col',
|
|
66
|
-
'max-sm:inset-0 max-sm:absolute max-sm:rounded-none max-sm:max-h-full max-sm:border-0',
|
|
67
|
-
'sm:rounded-xl sm:max-w-3xl sm:mx-4 sm:max-h-[85vh]',
|
|
68
|
-
TRANSITION_PANEL,
|
|
69
|
-
isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95',
|
|
70
|
-
)}
|
|
71
|
-
>
|
|
72
|
-
{/* Header */}
|
|
73
|
-
<div className="flex items-center justify-between p-4 border-b border-theme-border shrink-0">
|
|
74
|
-
<div className="flex items-center gap-2">
|
|
75
|
-
<Shield className="w-5 h-5 text-theme-text-secondary" />
|
|
76
|
-
<h2 className="text-lg font-semibold text-theme-text-primary">Your access on this cluster</h2>
|
|
38
|
+
return (
|
|
39
|
+
<div className="space-y-4">
|
|
40
|
+
{/* Identity + namespace selector */}
|
|
41
|
+
<div className="flex flex-wrap items-end gap-3">
|
|
42
|
+
<div className="flex-1 min-w-[180px]">
|
|
43
|
+
<label className="block text-xs text-theme-text-tertiary mb-1">Identity</label>
|
|
44
|
+
<div className="px-2 py-1.5 text-sm bg-theme-elevated rounded border border-theme-border text-theme-text-primary truncate">
|
|
45
|
+
{authMe?.username || '(current kubeconfig user)'}
|
|
77
46
|
</div>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
47
|
+
</div>
|
|
48
|
+
<div className="flex-1 min-w-[180px]">
|
|
49
|
+
<label className="block text-xs text-theme-text-tertiary mb-1">Namespace</label>
|
|
50
|
+
<select
|
|
51
|
+
value={namespace}
|
|
52
|
+
onChange={(e) => { userPicked.current = true; setNamespace(e.target.value) }}
|
|
53
|
+
className="w-full px-2 py-1.5 text-sm bg-theme-elevated border border-theme-border rounded text-theme-text-primary focus:outline-none focus:border-blue-500"
|
|
81
54
|
>
|
|
82
|
-
|
|
83
|
-
|
|
55
|
+
{namespaces?.map((ns) => (
|
|
56
|
+
<option key={ns.name} value={ns.name}>{ns.name}</option>
|
|
57
|
+
))}
|
|
58
|
+
{!namespaces?.some((ns) => ns.name === namespace) && (
|
|
59
|
+
<option value={namespace}>{namespace}</option>
|
|
60
|
+
)}
|
|
61
|
+
</select>
|
|
84
62
|
</div>
|
|
63
|
+
</div>
|
|
85
64
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
{authMe?.username || '(current kubeconfig user)'}
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
<div className="flex-1 min-w-[180px]">
|
|
96
|
-
<label className="block text-xs text-theme-text-tertiary mb-1">Namespace</label>
|
|
97
|
-
<select
|
|
98
|
-
value={namespace}
|
|
99
|
-
onChange={(e) => setNamespace(e.target.value)}
|
|
100
|
-
className="w-full px-2 py-1.5 text-sm bg-theme-elevated border border-theme-border rounded text-theme-text-primary focus:outline-none focus:border-blue-500"
|
|
101
|
-
>
|
|
102
|
-
{namespaces?.map((ns) => (
|
|
103
|
-
<option key={ns.name} value={ns.name}>{ns.name}</option>
|
|
104
|
-
))}
|
|
105
|
-
{!namespaces?.some((ns) => ns.name === namespace) && (
|
|
106
|
-
<option value={namespace}>{namespace}</option>
|
|
107
|
-
)}
|
|
108
|
-
</select>
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
|
|
112
|
-
<p className="text-xs text-theme-text-tertiary">
|
|
113
|
-
Computed by the Kubernetes API via{' '}
|
|
114
|
-
<code className="inline-code">SelfSubjectRulesReview</code>.
|
|
115
|
-
Shows what you can do in <span className="text-theme-text-secondary">{namespace}</span>,
|
|
116
|
-
plus any cluster-scoped rules that apply everywhere.
|
|
117
|
-
</p>
|
|
118
|
-
|
|
119
|
-
{whoami?.incomplete && (
|
|
120
|
-
<div className="px-2.5 py-1.5 text-xs bg-amber-500/10 border border-amber-500/20 rounded">
|
|
121
|
-
The apiserver returned an incomplete rule set
|
|
122
|
-
{whoami.evaluationError ? ` (${whoami.evaluationError})` : ''}.
|
|
123
|
-
The list below may understate your actual permissions.
|
|
124
|
-
</div>
|
|
125
|
-
)}
|
|
65
|
+
<p className="text-xs text-theme-text-tertiary">
|
|
66
|
+
Computed by the Kubernetes API via{' '}
|
|
67
|
+
<code className="inline-code">SelfSubjectRulesReview</code>.
|
|
68
|
+
Shows what you can do in <span className="text-theme-text-secondary">{namespace}</span>,
|
|
69
|
+
plus any cluster-scoped rules that apply everywhere.
|
|
70
|
+
</p>
|
|
126
71
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Failed to load: {(error as Error).message}
|
|
135
|
-
</div>
|
|
136
|
-
) : whoami ? (
|
|
137
|
-
<PermissionsTable whoami={whoami} />
|
|
138
|
-
) : null}
|
|
72
|
+
{whoami?.incomplete && (
|
|
73
|
+
<div className="px-2.5 py-1.5 text-xs bg-amber-500/10 border border-amber-500/20 rounded">
|
|
74
|
+
The apiserver returned an incomplete rule set
|
|
75
|
+
{whoami.evaluationError ? ` (${whoami.evaluationError})` : ''}.
|
|
76
|
+
The list below may understate your actual permissions.
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
139
79
|
|
|
140
|
-
|
|
80
|
+
{isLoading ? (
|
|
81
|
+
<div className="flex items-center gap-2 text-sm text-theme-text-tertiary">
|
|
82
|
+
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
83
|
+
Querying apiserver…
|
|
141
84
|
</div>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
85
|
+
) : error ? (
|
|
86
|
+
<div className="text-sm text-red-400">
|
|
87
|
+
Failed to load: {(error as Error).message}
|
|
88
|
+
</div>
|
|
89
|
+
) : whoami ? (
|
|
90
|
+
<PermissionsTable whoami={whoami} />
|
|
91
|
+
) : null}
|
|
92
|
+
|
|
93
|
+
<RestrictedResources enabled={active} />
|
|
94
|
+
</div>
|
|
145
95
|
)
|
|
146
96
|
}
|
|
147
97
|
|