diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/EnvironmentAdminController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/EnvironmentAdminController.java index fb36c4cf..f558435a 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/EnvironmentAdminController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/EnvironmentAdminController.java @@ -27,6 +27,7 @@ public class EnvironmentAdminController { @GetMapping @Operation(summary = "List all environments") + @PreAuthorize("isAuthenticated()") public ResponseEntity> listEnvironments() { return ResponseEntity.ok(environmentService.listAll()); } diff --git a/ui/src/components/LayoutShell.tsx b/ui/src/components/LayoutShell.tsx index b5dfca2b..44b8761f 100644 --- a/ui/src/components/LayoutShell.tsx +++ b/ui/src/components/LayoutShell.tsx @@ -25,6 +25,7 @@ import { useRouteCatalog } from '../api/queries/catalog'; import { useAgents } from '../api/queries/agents'; import { useSearchExecutions, useAttributeKeys } from '../api/queries/executions'; import { useUsers, useGroups, useRoles } from '../api/queries/admin/rbac'; +import { useEnvironments } from '../api/queries/admin/environments'; import type { UserDetail, GroupDetail, RoleDetail } from '../api/queries/admin/rbac'; import { useAuthStore, useIsAdmin } from '../auth/auth-store'; import { useEnvironmentStore } from '../api/environment-store'; @@ -291,16 +292,20 @@ function LayoutContent() { const { data: allAgents } = useAgents(); // unfiltered — for environment discovery const { data: agents } = useAgents(undefined, undefined, selectedEnv); // filtered — for sidebar/search const { data: attributeKeys } = useAttributeKeys(); + const { data: envRecords = [] } = useEnvironments(); - // Extract distinct environments from ALL agents (not filtered by selected env) + // Merge environments from both the environments table and agent heartbeats const environments: string[] = useMemo(() => { - if (!allAgents) return ['default']; const envSet = new Set(); - for (const a of allAgents as any[]) { - envSet.add(a.environmentId || 'default'); + for (const e of envRecords) envSet.add(e.slug); + if (allAgents) { + for (const a of allAgents as any[]) { + envSet.add(a.environmentId || 'default'); + } } + if (envSet.size === 0) envSet.add('default'); return [...envSet].sort(); - }, [allAgents]); + }, [allAgents, envRecords]); // --- Admin search data (only fetched on admin pages) ---------------- const isAdminPage = location.pathname.startsWith('/admin');