@@ -12,17 +12,29 @@ import Switch from '@mui/material/Switch';
1212import TextField from '@mui/material/TextField' ;
1313import Typography from '@mui/material/Typography' ;
1414import { useEffect , useState } from 'react' ;
15+ import { isHttpUrl } from '../../helpers' ;
1516
1617/**
17- * Validates if the given address string is in the correct format.
18- * The format should be: namespace/service:port
18+ * Validates whether the given address is in a supported format.
19+ * Supports namespace/service:port and HTTP/HTTPS URLs.
20+ * Examples: monitoring/prometheus:9090, https://prometheus.example.com
1921 *
2022 * @param {string } address - The address string to validate.
2123 * @returns {boolean } True if the address is valid, false otherwise.
2224 */
2325function isValidAddress ( address : string ) : boolean {
24- const regex = / ^ [ a - z 0 - 9 - ] + \/ [ a - z 0 - 9 - ] + : [ 0 - 9 ] + $ / ;
25- return regex . test ( address ) ;
26+ if ( ! address ) return false ;
27+
28+ const value = address . trim ( ) . replace ( / \/ $ / , '' ) ;
29+
30+ // namespace/service:port
31+ const k8sRegex = / ^ [ a - z 0 - 9 - ] + \/ [ a - z 0 - 9 - ] + : [ 0 - 9 ] + $ / ;
32+ if ( k8sRegex . test ( value ) ) {
33+ return true ;
34+ }
35+
36+ // http(s)://...
37+ return isHttpUrl ( value ) ;
2638}
2739
2840/**
@@ -107,17 +119,30 @@ export function Settings(props: SettingsProps) {
107119 setTestMessage ( t ( 'Testing Connection' ) ) ;
108120
109121 try {
110- const [ namespace , serviceAndPort ] = selectedClusterData . address . split ( '/' ) ;
111- const [ service , port ] = serviceAndPort . split ( ':' ) ;
122+ const address = selectedClusterData . address . trim ( ) . replace ( / \/ $ / , '' ) ;
123+ const normalizeSubPath = ( value : string ) => {
124+ const trimmed = value . trim ( ) . replace ( / ^ \/ + | \/ + $ / g, '' ) ;
125+ return trimmed ? `/${ trimmed } ` : '' ;
126+ } ;
127+ const subPath = normalizeSubPath ( selectedClusterData . subPath || '' ) ;
112128
113- let subPath = selectedClusterData . subPath || '' ;
114- if ( subPath && ! subPath . startsWith ( '/' ) ) {
115- subPath = '/' + subPath ;
129+ if ( isHttpUrl ( address ) ) {
130+ // External URL: direct fetch
131+ const url = new URL ( address ) ;
132+ const basePath = url . pathname . replace ( / \/ + $ / g, '' ) ;
133+ url . pathname = `${ basePath } ${ subPath } /-/healthy` ;
134+ const response = await fetch ( url . toString ( ) , { method : 'GET' } ) ;
135+ if ( ! response . ok ) {
136+ throw new Error ( `HTTP ${ response . status } ${ response . statusText } ` ) ;
137+ }
138+ } else {
139+ // Kubernetes service proxy: namespace/service:port
140+ const [ namespace , serviceAndPort ] = address . split ( '/' ) ;
141+ const [ service , port ] = serviceAndPort . split ( ':' ) ;
142+ const proxyUrl = `/clusters/${ selectedCluster } /api/v1/namespaces/${ namespace } /services/${ service } :${ port } /proxy${ subPath } /-/healthy` ;
143+ await request ( proxyUrl ) ;
116144 }
117145
118- const proxyUrl = `/clusters/${ selectedCluster } /api/v1/namespaces/${ namespace } /services/${ service } :${ port } /proxy${ subPath } /-/healthy` ;
119- await request ( proxyUrl ) ;
120-
121146 setTestStatus ( 'success' ) ;
122147 setTestMessage ( t ( 'Connection successful!' ) ) ;
123148 } catch ( err ) {
@@ -167,18 +192,18 @@ export function Settings(props: SettingsProps) {
167192 ) ,
168193 } ,
169194 {
170- name : t ( 'Prometheus Service Address' ) ,
195+ name : t ( 'Prometheus Address' ) ,
171196 value : (
172197 < Box display = "flex" flexDirection = "column" width = "100%" >
173198 < Box display = "flex" gap = { 2 } alignItems = "flex-start" >
174199 < TextField
175200 disabled = { ! isAddressFieldEnabled }
176201 helperText = {
177202 addressError
178- ? t ( 'Invalid format. Use: namespace/service-name:port' )
203+ ? t ( 'Invalid format. Use: namespace/service-name:port or https://prometheus.example.com ' )
179204 : t (
180- 'Address of the Prometheus Service, only used when auto-detection is disabled. Format : namespace/service-name:port'
181- )
205+ ' Prometheus address. Used only when auto-detection is disabled. Examples : namespace/service-name:port or https://prometheus.example.com '
206+ )
182207 }
183208 error = { addressError }
184209 value = { selectedClusterData . address || '' }
@@ -220,13 +245,13 @@ export function Settings(props: SettingsProps) {
220245 ) ,
221246 } ,
222247 {
223- name : t ( 'Prometheus Service Subpath' ) ,
248+ name : t ( 'Prometheus Subpath' ) ,
224249 value : (
225250 < TextField
226251 value = { selectedClusterData . subPath || '' }
227252 disabled = { ! isAddressFieldEnabled }
228253 helperText = { t (
229- "Optional subpath to the Prometheus Service endpoint. Only used when auto-detection is disabled. Examples: 'prometheus'."
254+ "Optional subpath to the Prometheus endpoint. Only used when auto-detection is disabled. Examples: 'prometheus'."
230255 ) }
231256 onChange = { e => {
232257 const newSubPath = e . target . value ;
0 commit comments