@skyhook-io/k8s-ui 1.0.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.
Files changed (193) hide show
  1. package/package.json +63 -0
  2. package/src/components/dock/BottomDock.tsx +204 -0
  3. package/src/components/dock/DockContext.tsx +212 -0
  4. package/src/components/dock/TerminalTab.tsx +333 -0
  5. package/src/components/dock/index.ts +3 -0
  6. package/src/components/gitops/GitOpsActions.tsx +227 -0
  7. package/src/components/gitops/GitOpsStatusBadge.tsx +221 -0
  8. package/src/components/gitops/ManagedResourcesList.tsx +210 -0
  9. package/src/components/gitops/SyncCountdown.tsx +146 -0
  10. package/src/components/gitops/index.ts +4 -0
  11. package/src/components/logs/JsonLogLine.tsx +90 -0
  12. package/src/components/logs/LogCore.tsx +553 -0
  13. package/src/components/logs/LogToolbarSelects.tsx +74 -0
  14. package/src/components/logs/LogsViewer.tsx +146 -0
  15. package/src/components/logs/WorkloadLogsViewer.tsx +263 -0
  16. package/src/components/logs/index.ts +13 -0
  17. package/src/components/logs/useLogBuffer.ts +138 -0
  18. package/src/components/logs/useLogSearch.ts +151 -0
  19. package/src/components/logs/useLogStream.ts +61 -0
  20. package/src/components/resources/ResourcesView.tsx +5494 -0
  21. package/src/components/resources/index.ts +17 -0
  22. package/src/components/resources/renderers/AlertRenderer.tsx +166 -0
  23. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +233 -0
  24. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +89 -0
  25. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +194 -0
  26. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +79 -0
  27. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +68 -0
  28. package/src/components/resources/renderers/CertificateRenderer.tsx +209 -0
  29. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +123 -0
  30. package/src/components/resources/renderers/ChallengeRenderer.tsx +111 -0
  31. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +164 -0
  32. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +140 -0
  33. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +153 -0
  34. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +221 -0
  35. package/src/components/resources/renderers/ConfigMapRenderer.tsx +124 -0
  36. package/src/components/resources/renderers/CronJobRenderer.tsx +86 -0
  37. package/src/components/resources/renderers/EventRenderer.tsx +175 -0
  38. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +119 -0
  39. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +172 -0
  40. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +242 -0
  41. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +263 -0
  42. package/src/components/resources/renderers/GatewayClassRenderer.tsx +68 -0
  43. package/src/components/resources/renderers/GatewayRenderer.tsx +218 -0
  44. package/src/components/resources/renderers/GenericRenderer.tsx +476 -0
  45. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +154 -0
  46. package/src/components/resources/renderers/HPARenderer.tsx +164 -0
  47. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +303 -0
  48. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +167 -0
  49. package/src/components/resources/renderers/IngressClassRenderer.tsx +49 -0
  50. package/src/components/resources/renderers/IngressRenderer.tsx +104 -0
  51. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +175 -0
  52. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +176 -0
  53. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +125 -0
  54. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +84 -0
  55. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +112 -0
  56. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +288 -0
  57. package/src/components/resources/renderers/JobRenderer.tsx +103 -0
  58. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +157 -0
  59. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +188 -0
  60. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +148 -0
  61. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +90 -0
  62. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +136 -0
  63. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +151 -0
  64. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +70 -0
  65. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +309 -0
  66. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +142 -0
  67. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +232 -0
  68. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +97 -0
  69. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +74 -0
  70. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +138 -0
  71. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +220 -0
  72. package/src/components/resources/renderers/KustomizationRenderer.tsx +151 -0
  73. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +352 -0
  74. package/src/components/resources/renderers/LeaseRenderer.tsx +93 -0
  75. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +208 -0
  76. package/src/components/resources/renderers/NodeRenderer.tsx +282 -0
  77. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +153 -0
  78. package/src/components/resources/renderers/OrderRenderer.tsx +108 -0
  79. package/src/components/resources/renderers/PVCRenderer.tsx +98 -0
  80. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +138 -0
  81. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +126 -0
  82. package/src/components/resources/renderers/PodMonitorRenderer.tsx +63 -0
  83. package/src/components/resources/renderers/PodRenderer.tsx +677 -0
  84. package/src/components/resources/renderers/PriorityClassRenderer.tsx +50 -0
  85. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +62 -0
  86. package/src/components/resources/renderers/ReplicaSetRenderer.tsx +71 -0
  87. package/src/components/resources/renderers/RoleBindingRenderer.tsx +97 -0
  88. package/src/components/resources/renderers/RoleRenderer.tsx +192 -0
  89. package/src/components/resources/renderers/RolloutRenderer.tsx +227 -0
  90. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +75 -0
  91. package/src/components/resources/renderers/SbomReportRenderer.tsx +98 -0
  92. package/src/components/resources/renderers/SealedSecretRenderer.tsx +121 -0
  93. package/src/components/resources/renderers/SecretRenderer.tsx +321 -0
  94. package/src/components/resources/renderers/SecretStoreRenderer.tsx +111 -0
  95. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +81 -0
  96. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +65 -0
  97. package/src/components/resources/renderers/ServiceRenderer.tsx +98 -0
  98. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +195 -0
  99. package/src/components/resources/renderers/StorageClassRenderer.tsx +82 -0
  100. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +257 -0
  101. package/src/components/resources/renderers/VPARenderer.tsx +193 -0
  102. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +73 -0
  103. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +193 -0
  104. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +161 -0
  105. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +134 -0
  106. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +36 -0
  107. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +347 -0
  108. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +156 -0
  109. package/src/components/resources/renderers/WorkflowRenderer.tsx +202 -0
  110. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +91 -0
  111. package/src/components/resources/renderers/WorkloadRenderer.tsx +311 -0
  112. package/src/components/resources/renderers/argo-cells.tsx +118 -0
  113. package/src/components/resources/renderers/certmanager-cells.tsx +164 -0
  114. package/src/components/resources/renderers/cnpg-cells.tsx +161 -0
  115. package/src/components/resources/renderers/eso-cells.tsx +115 -0
  116. package/src/components/resources/renderers/flux-cells.tsx +253 -0
  117. package/src/components/resources/renderers/index.ts +105 -0
  118. package/src/components/resources/renderers/istio-cells.tsx +210 -0
  119. package/src/components/resources/renderers/karpenter-cells.tsx +107 -0
  120. package/src/components/resources/renderers/keda-cells.tsx +94 -0
  121. package/src/components/resources/renderers/knative-cells.tsx +396 -0
  122. package/src/components/resources/renderers/kyverno-cells.tsx +85 -0
  123. package/src/components/resources/renderers/prometheus-cells.tsx +74 -0
  124. package/src/components/resources/renderers/traefik-cells.tsx +120 -0
  125. package/src/components/resources/renderers/trivy-cells.tsx +153 -0
  126. package/src/components/resources/renderers/trivy-shared.tsx +67 -0
  127. package/src/components/resources/renderers/velero-cells.tsx +164 -0
  128. package/src/components/resources/resource-utils-argo.ts +155 -0
  129. package/src/components/resources/resource-utils-certmanager.ts +198 -0
  130. package/src/components/resources/resource-utils-cnpg.ts +344 -0
  131. package/src/components/resources/resource-utils-eso.ts +385 -0
  132. package/src/components/resources/resource-utils-flux.ts +186 -0
  133. package/src/components/resources/resource-utils-istio.ts +372 -0
  134. package/src/components/resources/resource-utils-karpenter.ts +150 -0
  135. package/src/components/resources/resource-utils-keda.ts +203 -0
  136. package/src/components/resources/resource-utils-knative.ts +218 -0
  137. package/src/components/resources/resource-utils-kyverno.ts +224 -0
  138. package/src/components/resources/resource-utils-prometheus.ts +159 -0
  139. package/src/components/resources/resource-utils-traefik.ts +180 -0
  140. package/src/components/resources/resource-utils-trivy.ts +146 -0
  141. package/src/components/resources/resource-utils-velero.ts +293 -0
  142. package/src/components/resources/resource-utils.ts +1558 -0
  143. package/src/components/shared/EditableYamlView.tsx +302 -0
  144. package/src/components/shared/ResourceActionsBar.tsx +924 -0
  145. package/src/components/shared/ResourceRendererDispatch.tsx +557 -0
  146. package/src/components/shared/index.ts +3 -0
  147. package/src/components/timeline/index.ts +1 -0
  148. package/src/components/timeline/shared.tsx +614 -0
  149. package/src/components/topology/GroupNode.tsx +198 -0
  150. package/src/components/topology/K8sResourceNode.tsx +440 -0
  151. package/src/components/topology/TopologyControls.tsx +62 -0
  152. package/src/components/topology/TopologyFilterSidebar.tsx +377 -0
  153. package/src/components/topology/TopologyGraph.tsx +748 -0
  154. package/src/components/topology/TopologySearch.tsx +304 -0
  155. package/src/components/topology/index.ts +7 -0
  156. package/src/components/topology/layout.ts +734 -0
  157. package/src/components/topology/layout.worker.ts +272 -0
  158. package/src/components/topology/topology.css +129 -0
  159. package/src/components/ui/CodeViewer.tsx +451 -0
  160. package/src/components/ui/ConfirmDialog.tsx +188 -0
  161. package/src/components/ui/ForceDeleteConfirmDialog.tsx +57 -0
  162. package/src/components/ui/HealthRing.tsx +72 -0
  163. package/src/components/ui/MetricsChart.tsx +259 -0
  164. package/src/components/ui/ResourceBar.tsx +68 -0
  165. package/src/components/ui/Toast.tsx +225 -0
  166. package/src/components/ui/Tooltip.tsx +165 -0
  167. package/src/components/ui/YamlEditor.tsx +280 -0
  168. package/src/components/ui/drawer-components.tsx +792 -0
  169. package/src/components/ui/index.ts +10 -0
  170. package/src/components/workload/ResourceDetailDrawer.tsx +158 -0
  171. package/src/components/workload/WorkloadView.tsx +1109 -0
  172. package/src/components/workload/index.ts +2 -0
  173. package/src/hooks/index.ts +9 -0
  174. package/src/hooks/useAnimatedUnmount.ts +30 -0
  175. package/src/hooks/useKeyboardShortcuts.tsx +336 -0
  176. package/src/hooks/useRefreshAnimation.ts +58 -0
  177. package/src/index.ts +35 -0
  178. package/src/types/core.ts +894 -0
  179. package/src/types/gitops.ts +458 -0
  180. package/src/types/index.ts +2 -0
  181. package/src/utils/animation.ts +40 -0
  182. package/src/utils/api-resources.ts +185 -0
  183. package/src/utils/badge-colors.ts +399 -0
  184. package/src/utils/context-name.ts +72 -0
  185. package/src/utils/download.ts +19 -0
  186. package/src/utils/format.ts +233 -0
  187. package/src/utils/index.ts +10 -0
  188. package/src/utils/log-format.ts +300 -0
  189. package/src/utils/navigation.ts +119 -0
  190. package/src/utils/resource-hierarchy.ts +630 -0
  191. package/src/utils/resource-icons.ts +206 -0
  192. package/src/vite-env.d.ts +1 -0
  193. package/tsconfig.json +24 -0
@@ -0,0 +1,159 @@
1
+ // Prometheus Operator CRD utility functions
2
+
3
+ import type { StatusBadge } from './resource-utils'
4
+ import { healthColors } from './resource-utils'
5
+
6
+ // ============================================================================
7
+ // SHARED HELPERS
8
+ // ============================================================================
9
+
10
+ function getConditionStatus(resource: any): StatusBadge {
11
+ const conditions = resource.status?.conditions || []
12
+
13
+ const reconciledCond = conditions.find((c: any) => c.type === 'Reconciled')
14
+ if (reconciledCond?.status === 'True') {
15
+ return { text: 'Reconciled', color: healthColors.healthy, level: 'healthy' }
16
+ }
17
+ if (reconciledCond?.status === 'False') {
18
+ return { text: reconciledCond.reason || 'Not Reconciled', color: healthColors.unhealthy, level: 'unhealthy' }
19
+ }
20
+
21
+ const availableCond = conditions.find((c: any) => c.type === 'Available')
22
+ if (availableCond?.status === 'True') {
23
+ return { text: 'Available', color: healthColors.healthy, level: 'healthy' }
24
+ }
25
+ if (availableCond?.status === 'False') {
26
+ return { text: availableCond.reason || 'Unavailable', color: healthColors.unhealthy, level: 'unhealthy' }
27
+ }
28
+
29
+ // Fallback: if resource exists but has no conditions, it's likely active
30
+ if (resource.spec) {
31
+ return { text: 'Active', color: healthColors.healthy, level: 'healthy' }
32
+ }
33
+
34
+ return { text: 'Unknown', color: healthColors.unknown, level: 'unknown' }
35
+ }
36
+
37
+ function formatMatchLabels(selector: any): string {
38
+ const matchLabels = selector?.matchLabels
39
+ if (!matchLabels || Object.keys(matchLabels).length === 0) return '-'
40
+ return Object.entries(matchLabels).map(([k, v]) => `${k}=${v}`).join(', ')
41
+ }
42
+
43
+ // ============================================================================
44
+ // SERVICEMONITOR UTILITIES
45
+ // ============================================================================
46
+
47
+ export function getServiceMonitorStatus(resource: any): StatusBadge {
48
+ return getConditionStatus(resource)
49
+ }
50
+
51
+ export function getServiceMonitorEndpointCount(resource: any): number {
52
+ return (resource.spec?.endpoints || []).length
53
+ }
54
+
55
+ export function getServiceMonitorJobLabel(resource: any): string {
56
+ return resource.spec?.jobLabel || '-'
57
+ }
58
+
59
+ export function getServiceMonitorSelector(resource: any): string {
60
+ return formatMatchLabels(resource.spec?.selector)
61
+ }
62
+
63
+ export function getServiceMonitorEndpoints(resource: any): Array<{
64
+ port?: string
65
+ path?: string
66
+ interval?: string
67
+ scheme?: string
68
+ }> {
69
+ return (resource.spec?.endpoints || []).map((ep: any) => ({
70
+ port: ep.port || ep.targetPort,
71
+ path: ep.path || '/metrics',
72
+ interval: ep.interval,
73
+ scheme: ep.scheme,
74
+ }))
75
+ }
76
+
77
+ export function getServiceMonitorNamespaceSelector(resource: any): string {
78
+ const ns = resource.spec?.namespaceSelector
79
+ if (!ns) return 'Same namespace'
80
+ if (ns.any) return 'All namespaces'
81
+ if (ns.matchNames?.length) return ns.matchNames.join(', ')
82
+ return 'Same namespace'
83
+ }
84
+
85
+ // ============================================================================
86
+ // PROMETHEUSRULE UTILITIES
87
+ // ============================================================================
88
+
89
+ export function getPrometheusRuleStatus(resource: any): StatusBadge {
90
+ return getConditionStatus(resource)
91
+ }
92
+
93
+ export function getPrometheusRuleGroupCount(resource: any): number {
94
+ return (resource.spec?.groups || []).length
95
+ }
96
+
97
+ export function getPrometheusRuleTotalRules(resource: any): number {
98
+ const groups = resource.spec?.groups || []
99
+ return groups.reduce((sum: number, g: any) => sum + (g.rules?.length || 0), 0)
100
+ }
101
+
102
+ export function getPrometheusRuleGroups(resource: any): Array<{
103
+ name: string
104
+ interval?: string
105
+ ruleCount: number
106
+ alertCount: number
107
+ recordCount: number
108
+ }> {
109
+ return (resource.spec?.groups || []).map((g: any) => {
110
+ const rules = g.rules || []
111
+ const alertCount = rules.filter((r: any) => r.alert).length
112
+ const recordCount = rules.filter((r: any) => r.record).length
113
+ return {
114
+ name: g.name,
115
+ interval: g.interval,
116
+ ruleCount: rules.length,
117
+ alertCount,
118
+ recordCount,
119
+ }
120
+ })
121
+ }
122
+
123
+ // ============================================================================
124
+ // PODMONITOR UTILITIES
125
+ // ============================================================================
126
+
127
+ export function getPodMonitorStatus(resource: any): StatusBadge {
128
+ return getConditionStatus(resource)
129
+ }
130
+
131
+ export function getPodMonitorEndpointCount(resource: any): number {
132
+ return (resource.spec?.podMetricsEndpoints || []).length
133
+ }
134
+
135
+ export function getPodMonitorSelector(resource: any): string {
136
+ return formatMatchLabels(resource.spec?.selector)
137
+ }
138
+
139
+ export function getPodMonitorEndpoints(resource: any): Array<{
140
+ port?: string
141
+ path?: string
142
+ interval?: string
143
+ scheme?: string
144
+ }> {
145
+ return (resource.spec?.podMetricsEndpoints || []).map((ep: any) => ({
146
+ port: ep.port || ep.targetPort,
147
+ path: ep.path || '/metrics',
148
+ interval: ep.interval,
149
+ scheme: ep.scheme,
150
+ }))
151
+ }
152
+
153
+ export function getPodMonitorNamespaceSelector(resource: any): string {
154
+ const ns = resource.spec?.namespaceSelector
155
+ if (!ns) return 'Same namespace'
156
+ if (ns.any) return 'All namespaces'
157
+ if (ns.matchNames?.length) return ns.matchNames.join(', ')
158
+ return 'Same namespace'
159
+ }
@@ -0,0 +1,180 @@
1
+ // Traefik CRD utility functions for resource list cells and detail renderers
2
+
3
+ // ============================================================================
4
+ // INGRESSROUTE UTILITIES
5
+ // ============================================================================
6
+
7
+ export function getIngressRouteEntryPoints(resource: any): string {
8
+ const eps = resource.spec?.entryPoints || []
9
+ if (eps.length === 0) return '-'
10
+ if (eps.length <= 3) return eps.join(', ')
11
+ return `${eps.slice(0, 2).join(', ')} +${eps.length - 2} more`
12
+ }
13
+
14
+ export function getIngressRouteHosts(resource: any): string {
15
+ const routes = resource.spec?.routes || []
16
+ const hosts = new Set<string>()
17
+ for (const route of routes) {
18
+ const match = route.match || ''
19
+ // Extract Host(`...`) patterns from Traefik match expressions
20
+ const hostMatches = match.matchAll(/Host\(`([^`]+)`\)/gi)
21
+ for (const m of hostMatches) {
22
+ m[1].split(',').forEach((h: string) => {
23
+ const trimmed = h.trim()
24
+ if (trimmed) hosts.add(trimmed)
25
+ })
26
+ }
27
+ // Also extract HostSNI(`...`) for TCP/TLS
28
+ const sniMatches = match.matchAll(/HostSNI\(`([^`]+)`\)/gi)
29
+ for (const m of sniMatches) {
30
+ m[1].split(',').forEach((h: string) => {
31
+ const trimmed = h.trim()
32
+ if (trimmed && trimmed !== '*') hosts.add(trimmed)
33
+ })
34
+ }
35
+ }
36
+ const arr = Array.from(hosts)
37
+ if (arr.length === 0) return '*'
38
+ if (arr.length <= 2) return arr.join(', ')
39
+ return `${arr[0]} +${arr.length - 1} more`
40
+ }
41
+
42
+ export function getIngressRouteRoutesSummary(resource: any): string {
43
+ const routes = resource.spec?.routes || []
44
+ if (routes.length === 0) return 'No routes'
45
+
46
+ const summaries: string[] = []
47
+ for (const route of routes) {
48
+ const services = route.services || []
49
+ const svcNames = services.map((s: any) => {
50
+ const kind = s.kind && s.kind !== 'Service' ? `${s.kind}/` : ''
51
+ const port = s.port ? `:${s.port}` : ''
52
+ return `${kind}${s.name}${port}`
53
+ })
54
+ if (svcNames.length > 0) {
55
+ summaries.push(svcNames.join(', '))
56
+ }
57
+ }
58
+
59
+ if (summaries.length === 0) return `${routes.length} route(s)`
60
+ if (summaries.length === 1) return summaries[0]
61
+ return `${summaries[0]}; +${summaries.length - 1} more`
62
+ }
63
+
64
+ export function hasIngressRouteTLS(resource: any): boolean {
65
+ return !!resource.spec?.tls
66
+ }
67
+
68
+ export function getIngressRouteMiddlewareCount(resource: any): number {
69
+ const routes = resource.spec?.routes || []
70
+ const seen = new Set<string>()
71
+ for (const route of routes) {
72
+ for (const mw of route.middlewares || []) {
73
+ const ns = mw.namespace || ''
74
+ seen.add(`${ns}/${mw.name}`)
75
+ }
76
+ }
77
+ return seen.size
78
+ }
79
+
80
+ export function getIngressRouteServiceCount(resource: any): number {
81
+ const routes = resource.spec?.routes || []
82
+ const seen = new Set<string>()
83
+ for (const route of routes) {
84
+ for (const svc of route.services || []) {
85
+ const kind = svc.kind || 'Service'
86
+ const ns = svc.namespace || ''
87
+ seen.add(`${kind}/${ns}/${svc.name}`)
88
+ }
89
+ }
90
+ return seen.size
91
+ }
92
+
93
+ // ============================================================================
94
+ // MIDDLEWARE UTILITIES
95
+ // ============================================================================
96
+
97
+ export function getMiddlewareType(resource: any): string {
98
+ const spec = resource.spec || {}
99
+ // Each middleware has exactly one spec key indicating its type
100
+ const types = [
101
+ 'addPrefix', 'basicAuth', 'buffering', 'chain', 'circuitBreaker',
102
+ 'compress', 'contentType', 'digestAuth', 'errors', 'forwardAuth',
103
+ 'grpcWeb', 'headers', 'inFlightReq', 'ipAllowList', 'ipWhiteList',
104
+ 'passTLSClientCert', 'plugin', 'rateLimit', 'redirectRegex',
105
+ 'redirectScheme', 'replacePath', 'replacePathRegex', 'retry',
106
+ 'stripPrefix', 'stripPrefixRegex',
107
+ ]
108
+ for (const t of types) {
109
+ if (spec[t]) return t
110
+ }
111
+ return 'unknown'
112
+ }
113
+
114
+ export function getMiddlewareChainMembers(resource: any): string[] {
115
+ return (resource.spec?.chain?.middlewares || []).map((m: any) => m.name)
116
+ }
117
+
118
+ // ============================================================================
119
+ // TRAEFIKSERVICE UTILITIES
120
+ // ============================================================================
121
+
122
+ export function getTraefikServiceType(resource: any): string {
123
+ const spec = resource.spec || {}
124
+ if (spec.weighted) return 'Weighted Round Robin'
125
+ if (spec.mirroring) return 'Mirroring'
126
+ if (spec.highestRandomWeight) return 'Highest Random Weight'
127
+ return 'Unknown'
128
+ }
129
+
130
+ export function getTraefikServiceTargets(resource: any): string {
131
+ const spec = resource.spec || {}
132
+ if (spec.weighted) {
133
+ const svcs = spec.weighted.services || []
134
+ if (svcs.length === 0) return '-'
135
+ return svcs.map((s: any) => {
136
+ const w = s.weight !== undefined ? ` (${s.weight}%)` : ''
137
+ return `${s.name}${w}`
138
+ }).join(', ')
139
+ }
140
+ if (spec.mirroring) {
141
+ const primary = spec.mirroring.name || '-'
142
+ const mirrors = spec.mirroring.mirrors || []
143
+ if (mirrors.length === 0) return primary
144
+ return `${primary} + ${mirrors.length} mirror(s)`
145
+ }
146
+ if (spec.highestRandomWeight) {
147
+ const svcs = spec.highestRandomWeight.services || []
148
+ if (svcs.length === 0) return '-'
149
+ return svcs.map((s: any) => s.name).join(', ')
150
+ }
151
+ return '-'
152
+ }
153
+
154
+ // ============================================================================
155
+ // SERVERSTRANSPORT UTILITIES
156
+ // ============================================================================
157
+
158
+ export function getServersTransportInsecure(resource: any): boolean {
159
+ return !!resource.spec?.insecureSkipVerify
160
+ }
161
+
162
+ export function getServersTransportServerName(resource: any): string | null {
163
+ return resource.spec?.serverName || null
164
+ }
165
+
166
+ // ============================================================================
167
+ // TLSOPTION UTILITIES
168
+ // ============================================================================
169
+
170
+ export function getTLSOptionMinVersion(resource: any): string | null {
171
+ return resource.spec?.minVersion || null
172
+ }
173
+
174
+ export function getTLSOptionMaxVersion(resource: any): string | null {
175
+ return resource.spec?.maxVersion || null
176
+ }
177
+
178
+ export function getTLSOptionCipherSuites(resource: any): string[] {
179
+ return resource.spec?.cipherSuites || []
180
+ }
@@ -0,0 +1,146 @@
1
+ // Trivy Operator CRD utility functions — extracted from resource-utils.ts
2
+
3
+ import type { StatusBadge } from './resource-utils'
4
+ import { healthColors } from './resource-utils'
5
+
6
+ // ============================================================================
7
+ // TRIVY OPERATOR — VULNERABILITY REPORT UTILITIES
8
+ // ============================================================================
9
+
10
+ export interface VulnerabilitySummary {
11
+ critical: number
12
+ high: number
13
+ medium: number
14
+ low: number
15
+ unknown: number
16
+ }
17
+
18
+ export function getVulnerabilityReportSummary(report: any): VulnerabilitySummary {
19
+ const summary = report.report?.summary || {}
20
+ return {
21
+ critical: summary.criticalCount || 0,
22
+ high: summary.highCount || 0,
23
+ medium: summary.mediumCount || 0,
24
+ low: summary.lowCount || 0,
25
+ unknown: summary.unknownCount || 0,
26
+ }
27
+ }
28
+
29
+ export function getVulnerabilityReportStatus(report: any): StatusBadge {
30
+ const summary = getVulnerabilityReportSummary(report)
31
+ if (summary.critical > 0) {
32
+ return { text: `${summary.critical} Critical`, color: healthColors.unhealthy, level: 'unhealthy' }
33
+ }
34
+ if (summary.high > 0) {
35
+ return { text: `${summary.high} High`, color: healthColors.degraded, level: 'degraded' }
36
+ }
37
+ if (summary.medium > 0) {
38
+ return { text: `${summary.medium} Medium`, color: healthColors.degraded, level: 'degraded' }
39
+ }
40
+ if (summary.low > 0 || summary.unknown > 0) {
41
+ return { text: 'Low', color: healthColors.neutral, level: 'neutral' }
42
+ }
43
+ return { text: 'Clean', color: healthColors.healthy, level: 'healthy' }
44
+ }
45
+
46
+ export function getVulnerabilityReportImage(report: any): string {
47
+ const artifact = report.report?.artifact || {}
48
+ const registry = report.report?.registry?.server || ''
49
+ if (!artifact.repository) return '-'
50
+ const repo = registry ? `${registry}/${artifact.repository}` : artifact.repository
51
+ return artifact.tag ? `${repo}:${artifact.tag}` : repo
52
+ }
53
+
54
+ export function getVulnerabilityReportContainer(report: any): string {
55
+ return report.metadata?.labels?.['trivy-operator.container.name'] || '-'
56
+ }
57
+
58
+ // ============================================================================
59
+ // TRIVY OPERATOR — CONFIG AUDIT REPORT UTILITIES
60
+ // ============================================================================
61
+
62
+ export interface ConfigAuditSummary {
63
+ critical: number
64
+ high: number
65
+ medium: number
66
+ low: number
67
+ }
68
+
69
+ export function getConfigAuditReportSummary(report: any): ConfigAuditSummary {
70
+ const summary = report.report?.summary || {}
71
+ return {
72
+ critical: summary.criticalCount || 0,
73
+ high: summary.highCount || 0,
74
+ medium: summary.mediumCount || 0,
75
+ low: summary.lowCount || 0,
76
+ }
77
+ }
78
+
79
+ export function getConfigAuditReportStatus(report: any): StatusBadge {
80
+ const summary = getConfigAuditReportSummary(report)
81
+ if (summary.critical > 0) {
82
+ return { text: `${summary.critical} Critical`, color: healthColors.unhealthy, level: 'unhealthy' }
83
+ }
84
+ if (summary.high > 0) {
85
+ return { text: `${summary.high} High`, color: healthColors.degraded, level: 'degraded' }
86
+ }
87
+ if (summary.medium > 0) {
88
+ return { text: `${summary.medium} Medium`, color: healthColors.degraded, level: 'degraded' }
89
+ }
90
+ if (summary.low > 0) {
91
+ return { text: 'Low', color: healthColors.neutral, level: 'neutral' }
92
+ }
93
+ return { text: 'Pass', color: healthColors.healthy, level: 'healthy' }
94
+ }
95
+
96
+ // ============================================================================
97
+ // TRIVY OPERATOR — EXPOSED SECRET REPORT UTILITIES
98
+ // ============================================================================
99
+
100
+ // ExposedSecretReport has the same summary/label structure as VulnerabilityReport
101
+ export const getExposedSecretReportSummary = getVulnerabilityReportSummary
102
+ export const getExposedSecretReportContainer = getVulnerabilityReportContainer
103
+ export const getExposedSecretReportImage = getVulnerabilityReportImage
104
+
105
+ export const getExposedSecretReportStatus = getVulnerabilityReportStatus
106
+
107
+ // ============================================================================
108
+ // TRIVY OPERATOR — RBAC ASSESSMENT REPORT UTILITIES
109
+ // ============================================================================
110
+
111
+ // RbacAssessmentReport and ClusterRbacAssessmentReport have the same
112
+ // report.summary + report.checks[] structure as ConfigAuditReport
113
+ export const getRbacAssessmentReportSummary = getConfigAuditReportSummary
114
+ export const getRbacAssessmentReportStatus = getConfigAuditReportStatus
115
+
116
+ // ============================================================================
117
+ // TRIVY OPERATOR — CLUSTER COMPLIANCE REPORT UTILITIES
118
+ // ============================================================================
119
+
120
+ export function getClusterComplianceReportStatus(report: any): StatusBadge {
121
+ const summary = report.status?.summary || {}
122
+ const fail = summary.failCount || 0
123
+ const pass = summary.passCount || 0
124
+ if (fail > 0) {
125
+ return { text: `${fail} Fail`, color: healthColors.unhealthy, level: 'unhealthy' }
126
+ }
127
+ if (pass > 0) {
128
+ return { text: 'Pass', color: healthColors.healthy, level: 'healthy' }
129
+ }
130
+ return { text: 'Pending', color: healthColors.neutral, level: 'neutral' }
131
+ }
132
+
133
+ // ============================================================================
134
+ // TRIVY OPERATOR — SBOM REPORT UTILITIES
135
+ // ============================================================================
136
+
137
+ export function getSbomReportStatus(report: any): StatusBadge {
138
+ const summary = report.report?.summary
139
+ if (summary == null) {
140
+ return { text: 'Pending', color: healthColors.neutral, level: 'neutral' }
141
+ }
142
+ const count = summary.componentsCount ?? 0
143
+ return { text: `${count} components`, color: healthColors.neutral, level: 'neutral' }
144
+ }
145
+
146
+ export const getSbomReportContainer = getVulnerabilityReportContainer