diff --git a/web/src/components/IngressProxyComponent.tsx b/web/src/components/IngressProxyComponent.tsx index b299674..1f62006 100644 --- a/web/src/components/IngressProxyComponent.tsx +++ b/web/src/components/IngressProxyComponent.tsx @@ -4,12 +4,12 @@ import { Button } from './ui/button'; import { Input, Label } from './ui'; import { Alert, AlertDescription } from './ui/alert'; import { Textarea } from './ui/textarea'; -import { Network, Loader2, AlertCircle, CheckCircle, Play, RotateCw, Plus, Trash2, X, Check, Activity, ChevronDown, ChevronUp } from 'lucide-react'; +import { Network, Loader2, AlertCircle, CheckCircle, Play, RotateCw, Plus, Trash2, X, Check, Activity, ChevronDown, ChevronUp, Globe, Server } from 'lucide-react'; import { Badge } from './ui/badge'; import { useConfig } from '../hooks'; import { useHaproxy } from '../hooks/useHaproxy'; +import { useServices } from '../hooks/useServices'; import type { HAProxyCustomRoute } from '../types'; -import type { HaproxyInstanceRoute } from '../services/api/networking'; import { haproxyApi } from '../services/api/networking'; export function IngressProxyComponent() { @@ -27,6 +27,8 @@ export function IngressProxyComponent() { isRestarting: isHaproxyRestarting, } = useHaproxy(); + const { services, isLoading: isServicesLoading } = useServices(); + const [showAddCustomRoute, setShowAddCustomRoute] = useState(false); const [newCustomRoute, setNewCustomRoute] = useState({ name: '', port: 0, backend: '' }); const [successMessage, setSuccessMessage] = useState(null); @@ -133,7 +135,7 @@ export function IngressProxyComponent() {
- Instance Routes + Registered Services {isHaproxyLoading ? ( ) : ( @@ -145,37 +147,39 @@ export function IngressProxyComponent() {
- {(() => { - const routes: HaproxyInstanceRoute[] = haproxyStatus?.routes ?? []; - return routes.length > 0 ? ( -
- {routes.map(r => ( -
-
-
- - *.{r.domain} -
- {r.backendIP} + {isServicesLoading ? ( +
+ Loading services... +
+ ) : services.length > 0 ? ( +
+ {services.map(s => ( +
+
+
+ {s.reach === 'public' ? ( + + ) : ( + + )} + {s.name} + {s.domain}
- {r.extraDomains && r.extraDomains.length > 0 && ( -
- {r.extraDomains.map(d => ( - - {d} - - ))} -
- )} + {s.backend.address}
- ))} -
- ) : ( -

- No instances registered — click Generate & Apply to configure. -

- ); - })()} +
+ source: {s.source} + reach: {s.reach} + type: {s.backend.type} +
+
+ ))} +
+ ) : ( +

+ No services registered — click Generate & Apply to configure. +

+ )} {haproxyGenerateData && ( diff --git a/web/src/hooks/useServices.ts b/web/src/hooks/useServices.ts new file mode 100644 index 0000000..bc4d972 --- /dev/null +++ b/web/src/hooks/useServices.ts @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query'; +import { servicesApi } from '../services/api/networking'; + +export const useServices = () => { + const query = useQuery({ + queryKey: ['services'], + queryFn: () => servicesApi.list(), + refetchInterval: 30000, + staleTime: 10000, + }); + + return { + services: query.data?.services ?? [], + isLoading: query.isLoading, + error: query.error, + }; +}; diff --git a/web/src/services/api/index.ts b/web/src/services/api/index.ts index 1118cca..bc404eb 100644 --- a/web/src/services/api/index.ts +++ b/web/src/services/api/index.ts @@ -1,8 +1,8 @@ export { apiClient, ApiError } from './client'; export * from './types'; export { dnsmasqApi } from './dnsmasq'; -export { haproxyApi, ddnsApi, dhcpApi, secretsApi, crowdSecApi } from './networking'; -export type { HaproxyStatus, HaproxyGenerateResult, DdnsStatus, DhcpLease, DhcpStaticLease, BackendStat, CrowdSecStatus, CrowdSecMachine, CrowdSecBouncer, CrowdSecDecision, CrowdSecProvisionResult } from './networking'; +export { servicesApi, haproxyApi, ddnsApi, dhcpApi, secretsApi, crowdSecApi } from './networking'; +export type { RegisteredService, HaproxyStatus, HaproxyGenerateResult, DdnsStatus, DhcpLease, DhcpStaticLease, BackendStat, CrowdSecStatus, CrowdSecMachine, CrowdSecBouncer, CrowdSecDecision, CrowdSecProvisionResult } from './networking'; export { vpnApi } from './vpn'; export type { VpnStatus, VpnConfig, VpnPeer, VpnPeerConfig } from './vpn'; export { certApi } from './cert'; diff --git a/web/src/services/api/networking.ts b/web/src/services/api/networking.ts index d1badb2..a3d9d36 100644 --- a/web/src/services/api/networking.ts +++ b/web/src/services/api/networking.ts @@ -1,5 +1,26 @@ import { apiClient } from './client'; +// Services + +export interface RegisteredService { + name: string; + source: string; + domain: string; + backend: { + address: string; + type: string; + health?: string; + }; + reach: string; + tls?: string; +} + +export const servicesApi = { + async list(): Promise<{ services: RegisteredService[] }> { + return apiClient.get('/api/v1/services'); + }, +}; + // HAProxy export interface HaproxyInstanceRoute {