Portal: Phoenix API Railing wiring, env example, per-tenant rate limit
- Portal: phoenix-api-client, usePhoenixRailing hooks, /infrastructure page - Portal: PhoenixHealthTile on dashboard, resources page uses tenant me/resources - Sidebar: Infrastructure link; Keycloak token used for API calls (BFF) - api/.env.example: PHOENIX_RAILING_URL, PHOENIX_RAILING_API_KEY - rate-limit: key by tenant when tenantContext present Made-with: Cursor
This commit is contained in:
@@ -5,6 +5,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { createCrossplaneClient, VM } from '@/lib/crossplane-client';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from './ui/Card';
|
||||
import { Server, Activity, AlertCircle, CheckCircle, Loader2 } from 'lucide-react';
|
||||
import { PhoenixHealthTile } from './dashboard/PhoenixHealthTile';
|
||||
import { Badge } from './ui/badge';
|
||||
import { gql } from '@apollo/client';
|
||||
import { useQuery as useApolloQuery } from '@apollo/client';
|
||||
@@ -132,6 +133,10 @@ export default function Dashboard() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<PhoenixHealthTile />
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Recent Activity</CardTitle>
|
||||
|
||||
81
portal/src/components/dashboard/PhoenixHealthTile.tsx
Normal file
81
portal/src/components/dashboard/PhoenixHealthTile.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
'use client';
|
||||
|
||||
import { usePhoenixHealthSummary } from '@/hooks/usePhoenixRailing';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
||||
import { Activity, CheckCircle, AlertCircle } from 'lucide-react';
|
||||
|
||||
export function PhoenixHealthTile() {
|
||||
const { data, isLoading, error } = usePhoenixHealthSummary();
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card className="bg-gray-800 border-gray-700">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white flex items-center gap-2">
|
||||
<Activity className="h-5 w-5 text-orange-500" />
|
||||
Phoenix Health
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-center text-gray-400 py-4">Loading...</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Card className="bg-gray-800 border-gray-700">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white flex items-center gap-2">
|
||||
<Activity className="h-5 w-5 text-orange-500" />
|
||||
Phoenix Health
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-center text-red-400 py-4">Error loading health (check PHOENIX_RAILING_URL)</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const status = data?.status ?? 'unknown';
|
||||
const hosts = data?.hosts ?? [];
|
||||
const alerts = data?.alerts ?? [];
|
||||
|
||||
return (
|
||||
<Card className="bg-gray-800 border-gray-700">
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-white flex items-center gap-2">
|
||||
<Activity className="h-5 w-5 text-orange-500" />
|
||||
Phoenix Health (Railing)
|
||||
</CardTitle>
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||||
status === 'healthy' ? 'bg-green-500/20 text-green-400' :
|
||||
status === 'degraded' ? 'bg-yellow-500/20 text-yellow-400' :
|
||||
'bg-red-500/20 text-red-400'
|
||||
}`}
|
||||
>
|
||||
{status}
|
||||
</span>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle className="h-4 w-4 text-green-400" />
|
||||
<span>Hosts: {hosts.length}</span>
|
||||
</div>
|
||||
{alerts.length > 0 && (
|
||||
<div className="flex items-center gap-2 text-yellow-400">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<span>Alerts: {alerts.length}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import { cn } from '@/lib/utils'
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Dashboard', href: '/', icon: LayoutDashboard },
|
||||
{ name: 'Infrastructure', href: '/infrastructure', icon: Server },
|
||||
{ name: 'Resources', href: '/resources', icon: Server },
|
||||
{ name: 'Virtual Machines', href: '/vms', icon: Server },
|
||||
{ name: 'Networking', href: '/network', icon: Network },
|
||||
|
||||
Reference in New Issue
Block a user