diff --git a/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java b/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java index c84473c..8b3e846 100644 --- a/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java +++ b/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java @@ -124,6 +124,38 @@ public class ServerApiClient { } } + /** Fetch agent count from a tenant's server. */ + public int getAgentCount(String serverEndpoint) { + try { + var resp = RestClient.create().get() + .uri(serverEndpoint + "/api/v1/agents") + .header("Authorization", "Bearer " + getAccessToken()) + .header("X-Cameleer-Protocol-Version", "1") + .retrieve() + .body(java.util.List.class); + return resp != null ? resp.size() : 0; + } catch (Exception e) { + log.warn("Agent count fetch failed for {}: {}", serverEndpoint, e.getMessage()); + return 0; + } + } + + /** Fetch environment count from a tenant's server. */ + public int getEnvironmentCount(String serverEndpoint) { + try { + var resp = RestClient.create().get() + .uri(serverEndpoint + "/api/v1/admin/environments") + .header("Authorization", "Bearer " + getAccessToken()) + .header("X-Cameleer-Protocol-Version", "1") + .retrieve() + .body(java.util.List.class); + return resp != null ? resp.size() : 0; + } catch (Exception e) { + log.warn("Environment count fetch failed for {}: {}", serverEndpoint, e.getMessage()); + return 0; + } + } + public record ServerHealthResponse(boolean healthy, String status) {} private synchronized String getAccessToken() { diff --git a/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java b/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java index 855cc01..cd23ea8 100644 --- a/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java +++ b/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java @@ -47,7 +47,8 @@ public class TenantPortalService { String name, String slug, String tier, String status, boolean serverHealthy, String serverStatus, String serverEndpoint, String licenseTier, long licenseDaysRemaining, - Map limits, Map features + Map limits, Map features, + int agentCount, int environmentCount ) {} public record LicenseData( @@ -82,10 +83,16 @@ public class TenantPortalService { boolean serverHealthy = false; String serverStatus = "NO_ENDPOINT"; + int agentCount = 0; + int environmentCount = 0; if (endpoint != null && !endpoint.isBlank()) { var health = serverApiClient.getHealth(endpoint); serverHealthy = health.healthy(); serverStatus = health.status(); + if (serverHealthy) { + agentCount = serverApiClient.getAgentCount(endpoint); + environmentCount = serverApiClient.getEnvironmentCount(endpoint); + } } String licenseTier = null; @@ -107,7 +114,7 @@ public class TenantPortalService { tenant.getTier().name(), tenant.getStatus().name(), serverHealthy, serverStatus, endpoint, licenseTier, licenseDaysRemaining, - limits, features + limits, features, agentCount, environmentCount ); } diff --git a/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java b/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java index 0b6fbff..42c3c11 100644 --- a/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java +++ b/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java @@ -104,6 +104,8 @@ class TenantPortalServiceTest { when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant)); when(serverApiClient.getHealth("http://server:8080")).thenReturn(new ServerHealthResponse(true, "UP")); + when(serverApiClient.getAgentCount("http://server:8080")).thenReturn(3); + when(serverApiClient.getEnvironmentCount("http://server:8080")).thenReturn(1); when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.of(license)); var result = tenantPortalService.getDashboard(); @@ -119,6 +121,8 @@ class TenantPortalServiceTest { assertThat(result.licenseDaysRemaining()).isGreaterThanOrEqualTo(29); assertThat(result.limits()).isNotEmpty(); assertThat(result.features()).isNotEmpty(); + assertThat(result.agentCount()).isEqualTo(3); + assertThat(result.environmentCount()).isEqualTo(1); } @Test @@ -136,6 +140,8 @@ class TenantPortalServiceTest { assertThat(result.serverEndpoint()).isNull(); assertThat(result.licenseTier()).isNull(); assertThat(result.licenseDaysRemaining()).isZero(); + assertThat(result.agentCount()).isZero(); + assertThat(result.environmentCount()).isZero(); } // --- getLicense tests --- diff --git a/ui/src/pages/tenant/TenantDashboardPage.tsx b/ui/src/pages/tenant/TenantDashboardPage.tsx index 9263377..f829013 100644 --- a/ui/src/pages/tenant/TenantDashboardPage.tsx +++ b/ui/src/pages/tenant/TenantDashboardPage.tsx @@ -54,9 +54,8 @@ export function TenantDashboardPage() { const agentLimit = data.limits?.['agents'] ?? -1; const envLimit = data.limits?.['environments'] ?? -1; - // Dashboard doesn't expose usage counts directly — show limit info from license - const agentUsed = 0; - const envUsed = 0; + const agentUsed = data.agentCount ?? 0; + const envUsed = data.environmentCount ?? 0; return (
diff --git a/ui/src/types/api.ts b/ui/src/types/api.ts index 8eb8658..0a2c2b5 100644 --- a/ui/src/types/api.ts +++ b/ui/src/types/api.ts @@ -72,6 +72,8 @@ export interface DashboardData { licenseDaysRemaining: number; limits: Record; features: Record; + agentCount: number; + environmentCount: number; } export interface TenantLicenseData {