feat: Show Registered Services on Ingress Proxy page

Replace "Instance Routes" with "Registered Services" on the Central
Ingress page. Services are fetched from /api/v1/services and display
name, domain, backend, source, reach level, and backend type.

Central no longer manages instances directly — it shows services
registered by Wild Cloud, Wild Works, and itself.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 05:43:32 +00:00
parent 57a597fe5a
commit 75a82becb9
4 changed files with 76 additions and 34 deletions

View File

@@ -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<HAProxyCustomRoute>({ name: '', port: 0, backend: '' });
const [successMessage, setSuccessMessage] = useState<string | null>(null);
@@ -133,7 +135,7 @@ export function IngressProxyComponent() {
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Instance Routes</CardTitle>
<CardTitle>Registered Services</CardTitle>
{isHaproxyLoading ? (
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
) : (
@@ -145,37 +147,39 @@ export function IngressProxyComponent() {
</div>
</CardHeader>
<CardContent className="space-y-4">
{(() => {
const routes: HaproxyInstanceRoute[] = haproxyStatus?.routes ?? [];
return routes.length > 0 ? (
<div className="border rounded-lg divide-y text-sm">
{routes.map(r => (
<div key={r.name} className="px-3 py-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<CheckCircle className="h-3.5 w-3.5 text-green-500 shrink-0" />
<span className="font-mono text-xs">*.{r.domain}</span>
</div>
<span className="font-mono text-xs text-muted-foreground">{r.backendIP}</span>
{isServicesLoading ? (
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Loader2 className="h-4 w-4 animate-spin" />Loading services...
</div>
) : services.length > 0 ? (
<div className="border rounded-lg divide-y text-sm">
{services.map(s => (
<div key={s.name} className="px-3 py-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{s.reach === 'public' ? (
<Globe className="h-3.5 w-3.5 text-blue-500 shrink-0" />
) : (
<Server className="h-3.5 w-3.5 text-green-500 shrink-0" />
)}
<span className="font-medium">{s.name}</span>
<span className="font-mono text-xs text-muted-foreground">{s.domain}</span>
</div>
{r.extraDomains && r.extraDomains.length > 0 && (
<div className="mt-1 ml-5 flex flex-wrap gap-1">
{r.extraDomains.map(d => (
<span key={d} className="font-mono text-xs text-muted-foreground bg-muted rounded px-1.5 py-0.5">
{d}
</span>
))}
</div>
)}
<span className="font-mono text-xs text-muted-foreground">{s.backend.address}</span>
</div>
))}
</div>
) : (
<p className="text-xs text-muted-foreground">
No instances registered click Generate &amp; Apply to configure.
</p>
);
})()}
<div className="mt-1 ml-5 flex flex-wrap gap-2 text-xs text-muted-foreground">
<span>source: <span className="text-foreground">{s.source}</span></span>
<span>reach: <span className="text-foreground">{s.reach}</span></span>
<span>type: <span className="text-foreground">{s.backend.type}</span></span>
</div>
</div>
))}
</div>
) : (
<p className="text-xs text-muted-foreground">
No services registered click Generate &amp; Apply to configure.
</p>
)}
{haproxyGenerateData && (
<Alert className="border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/20">