refactor: centralize polling intervals — replace hardcoded ms with POLL.* constants
Every hook that polled had its own magic number. POLL tiers (LIVE/DASHBOARD/ DETAIL/OVERVIEW/STATIC/HEALTH) declared once in polling.ts, consumed everywhere. Mechanical change, no behavior shift.
This commit is contained in:
parent
488cca2dd5
commit
8811ec9100
13 changed files with 66 additions and 35 deletions
|
|
@ -1,17 +1,18 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import { healthApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import type { AdvisoryHealth } from '@/lib/api';
|
||||
import type { UseQueryResult } from '@tanstack/react-query';
|
||||
|
||||
// Polls advisory-feed health for the degradation banner. The breaker heals on its
|
||||
// own and packages re-vet on the next cycle, so a 30s cadence is plenty — this is
|
||||
// own and packages re-vet on the next cycle, so DETAIL cadence is plenty — this is
|
||||
// "is the feed up" awareness, not a real-time dashboard.
|
||||
export const useAdvisoryHealth = (): UseQueryResult<AdvisoryHealth, Error> => {
|
||||
return useQuery({
|
||||
queryKey: ['advisory-health'],
|
||||
queryFn: healthApi.getAdvisory,
|
||||
refetchInterval: 30000,
|
||||
staleTime: 15000,
|
||||
retry: false, // a transient failure shouldn't itself raise the alarm
|
||||
refetchInterval: POLL.DETAIL,
|
||||
staleTime: POLL.DETAIL * 0.5,
|
||||
retry: false,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useEffect } from 'react';
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import api from '@/lib/api';
|
||||
import { useRealtimeStore } from '@/lib/store';
|
||||
import { POLL } from '@/lib/polling';
|
||||
|
||||
// SystemEvent interface matching the backend model
|
||||
interface SystemEvent {
|
||||
|
|
@ -33,7 +34,7 @@ export const useAgentEvents = (
|
|||
const {
|
||||
severity = 'error,critical,warning',
|
||||
limit = 50,
|
||||
pollingInterval = 30000,
|
||||
pollingInterval = POLL.DETAIL,
|
||||
} = options;
|
||||
|
||||
const { data, isLoading, error, refetch } = useQuery({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { agentApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import type { Agent, ListQueryParams, AgentListResponse, ScanRequest } from '@/types';
|
||||
import type { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
|
||||
|
||||
|
|
@ -7,10 +8,10 @@ export const useAgents = (params?: ListQueryParams): UseQueryResult<AgentListRes
|
|||
return useQuery({
|
||||
queryKey: ['agents', params],
|
||||
queryFn: () => agentApi.getAgents(params),
|
||||
staleTime: 30 * 1000, // Consider data fresh for 30 seconds
|
||||
refetchInterval: 60 * 1000, // Poll every 60 seconds
|
||||
refetchIntervalInBackground: false, // Don't poll when tab is inactive
|
||||
refetchOnWindowFocus: true, // Refresh when window gains focus
|
||||
staleTime: POLL.OVERVIEW * 0.5,
|
||||
refetchInterval: POLL.OVERVIEW,
|
||||
refetchIntervalInBackground: false,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -19,10 +20,10 @@ export const useAgent = (id: string, enabled: boolean = true): UseQueryResult<Ag
|
|||
queryKey: ['agent', id],
|
||||
queryFn: () => agentApi.getAgent(id),
|
||||
enabled: enabled && !!id,
|
||||
staleTime: 30 * 1000, // Consider data fresh for 30 seconds
|
||||
refetchInterval: 30 * 1000, // Poll every 30 seconds for selected agent
|
||||
refetchIntervalInBackground: false, // Don't poll when tab is inactive
|
||||
refetchOnWindowFocus: true, // Refresh when window gains focus
|
||||
staleTime: POLL.DETAIL * 0.5,
|
||||
refetchInterval: POLL.DETAIL,
|
||||
refetchIntervalInBackground: false,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { updateApi, agentApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import type { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
|
||||
|
||||
interface ActiveCommand {
|
||||
|
|
@ -19,8 +20,8 @@ export const useActiveCommands = (autoRefresh: boolean = true): UseQueryResult<{
|
|||
return useQuery({
|
||||
queryKey: ['activeCommands'],
|
||||
queryFn: () => updateApi.getActiveCommands(),
|
||||
refetchInterval: autoRefresh ? 5000 : false, // Auto-refresh every 5 seconds when enabled
|
||||
staleTime: 0, // Override global staleTime to allow refetchInterval to work
|
||||
refetchInterval: autoRefresh ? POLL.LIVE : false,
|
||||
staleTime: 0,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { dockerApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
// Hook for fetching all Docker containers/images across all agents
|
||||
|
|
@ -128,7 +129,7 @@ export const useAgentRuntimeContainers = (agentId: string) => {
|
|||
queryKey: ['agent-runtime-containers', agentId],
|
||||
queryFn: () => dockerApi.getAgentRuntimeContainers(agentId),
|
||||
enabled: !!agentId,
|
||||
refetchInterval: 30000,
|
||||
refetchInterval: POLL.DETAIL,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ export const useAgentRuntimeStacks = (agentId: string) => {
|
|||
queryKey: ['agent-runtime-stacks', agentId],
|
||||
queryFn: () => dockerApi.getAgentRuntimeStacks(agentId),
|
||||
enabled: !!agentId,
|
||||
refetchInterval: 30000,
|
||||
refetchInterval: POLL.DETAIL,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -145,7 +146,7 @@ export const useFleetRuntimeContainers = () => {
|
|||
return useQuery({
|
||||
queryKey: ['fleet-runtime-containers'],
|
||||
queryFn: () => dockerApi.getFleetRuntimeContainers(),
|
||||
refetchInterval: 30000,
|
||||
refetchInterval: POLL.DETAIL,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ export const useFleetRuntimeStacks = () => {
|
|||
return useQuery({
|
||||
queryKey: ['fleet-runtime-stacks'],
|
||||
queryFn: () => dockerApi.getFleetRuntimeStacks(),
|
||||
refetchInterval: 30000,
|
||||
refetchInterval: POLL.DETAIL,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { adminApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import { CreateMaintenanceWindowRequest } from '@/types';
|
||||
|
||||
export const maintenanceWindowKeys = {
|
||||
|
|
@ -16,7 +17,7 @@ export const useMaintenanceWindows = () => {
|
|||
return useQuery({
|
||||
queryKey: maintenanceWindowKeys.list(),
|
||||
queryFn: () => adminApi.maintenanceWindows.list(),
|
||||
staleTime: 1000 * 60,
|
||||
staleTime: POLL.OVERVIEW * 0.5,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ export const useMaintenanceWindow = (id: string) => {
|
|||
queryKey: maintenanceWindowKeys.detail(id),
|
||||
queryFn: () => adminApi.maintenanceWindows.get(id),
|
||||
enabled: !!id,
|
||||
staleTime: 1000 * 60,
|
||||
staleTime: POLL.OVERVIEW * 0.5,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -33,8 +34,8 @@ export const useMaintenanceWindowCheck = () => {
|
|||
return useQuery({
|
||||
queryKey: maintenanceWindowKeys.check(),
|
||||
queryFn: () => adminApi.maintenanceWindows.check(),
|
||||
staleTime: 1000 * 30,
|
||||
refetchInterval: 1000 * 60,
|
||||
staleTime: POLL.DETAIL * 0.5,
|
||||
refetchInterval: POLL.OVERVIEW,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { adminApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import { RateLimitSettings } from '@/types';
|
||||
|
||||
export const rateLimitKeys = {
|
||||
|
|
@ -13,7 +14,7 @@ export const useRateLimitSettings = () => {
|
|||
return useQuery({
|
||||
queryKey: rateLimitKeys.settings(),
|
||||
queryFn: () => adminApi.rateLimits.getSettings(),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
staleTime: POLL.STATIC * 0.2,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -21,8 +22,8 @@ export const useRateLimitStats = () => {
|
|||
return useQuery({
|
||||
queryKey: rateLimitKeys.stats(),
|
||||
queryFn: () => adminApi.rateLimits.getStats(),
|
||||
staleTime: 1000 * 30,
|
||||
refetchInterval: 1000 * 30,
|
||||
staleTime: POLL.DETAIL * 0.5,
|
||||
refetchInterval: POLL.DETAIL,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { adminApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import {
|
||||
CreateRegistrationTokenRequest,
|
||||
} from '@/types';
|
||||
|
|
@ -42,8 +43,8 @@ export const useRegistrationTokenStats = () => {
|
|||
return useQuery({
|
||||
queryKey: registrationTokenKeys.stats(),
|
||||
queryFn: () => adminApi.tokens.getStats(),
|
||||
staleTime: 1000 * 60, // 1 minute
|
||||
refetchInterval: 1000 * 60 * 5, // Refresh every 5 minutes
|
||||
staleTime: POLL.STATIC * 0.2,
|
||||
refetchInterval: POLL.STATIC,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { api, securityApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import {
|
||||
SecuritySettings,
|
||||
SecuritySettingsResponse,
|
||||
|
|
@ -167,7 +168,7 @@ export const useSecuritySettings = () => {
|
|||
queryKey: ['security', 'overview'],
|
||||
queryFn: () => securityApi.getOverview(),
|
||||
staleTime: 60 * 1000, // 1 minute
|
||||
refetchInterval: 60 * 1000, // Auto-refresh every minute
|
||||
refetchInterval: POLL.OVERVIEW,
|
||||
});
|
||||
|
||||
// Update single setting mutation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import { statsApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import type { DashboardStats } from '@/types';
|
||||
import type { UseQueryResult } from '@tanstack/react-query';
|
||||
|
||||
|
|
@ -7,7 +8,7 @@ export const useDashboardStats = (): UseQueryResult<DashboardStats, Error> => {
|
|||
return useQuery({
|
||||
queryKey: ['dashboard-stats'],
|
||||
queryFn: statsApi.getDashboardStats,
|
||||
refetchInterval: 15000, // Refresh every 15 seconds (dashboard is the operator's primary view)
|
||||
staleTime: 10000, // Consider data stale after 10 seconds
|
||||
refetchInterval: POLL.DASHBOARD,
|
||||
staleTime: POLL.DASHBOARD * 0.67,
|
||||
});
|
||||
};
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { updateApi, capabilityTokenApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import type { UpdatePackage, ListQueryParams, UpdateApprovalRequest, UpdateListResponse, PackageListResponse, PackageFleetResponse, PackageVersionsResponse, CapabilityTokenStatusResponse, PackageSummary, PackageAgentsResponse, PackageVulnerabilitiesResponse } from '@/types';
|
||||
import type { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
|
||||
|
||||
|
|
@ -54,7 +55,7 @@ export const usePackageAgents = (pkgType: string, pkgName: string, enabled: bool
|
|||
queryKey: ['package-agents', pkgType, pkgName],
|
||||
queryFn: () => updateApi.getPackageAgentsByCoords(pkgType, pkgName),
|
||||
enabled: enabled && !!pkgType && !!pkgName,
|
||||
refetchInterval: 10000,
|
||||
refetchInterval: POLL.DASHBOARD,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { adminApi } from '@/lib/api';
|
||||
import { POLL } from '@/lib/polling';
|
||||
import { CreateTrackedSoftwareRequest } from '@/types';
|
||||
|
||||
export const upstreamKeys = {
|
||||
|
|
@ -16,7 +17,7 @@ export const useTrackedSoftware = () => {
|
|||
return useQuery({
|
||||
queryKey: upstreamKeys.list(),
|
||||
queryFn: () => adminApi.upstream.list(),
|
||||
staleTime: 1000 * 60,
|
||||
staleTime: POLL.STATIC * 0.2,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -24,8 +25,8 @@ export const useDriftedSoftware = () => {
|
|||
return useQuery({
|
||||
queryKey: upstreamKeys.drift(),
|
||||
queryFn: () => adminApi.upstream.listDrifted(),
|
||||
staleTime: 1000 * 60,
|
||||
refetchInterval: 1000 * 60 * 5,
|
||||
staleTime: POLL.STATIC * 0.2,
|
||||
refetchInterval: POLL.STATIC,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
19
web/src/lib/polling.ts
Normal file
19
web/src/lib/polling.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Centralized polling intervals. Every hook that polls uses these constants
|
||||
// instead of hardcoded magic numbers. The tiers are based on how volatile the
|
||||
// data is and how directly the operator is watching it.
|
||||
//
|
||||
// LIVE — real-time operations the operator is actively watching (commands)
|
||||
// DASHBOARD — primary operator view, needs to feel responsive (stats, updates)
|
||||
// DETAIL — single-entity pages the operator opened on purpose (agent, package)
|
||||
// OVERVIEW — fleet-wide lists, less time-critical (agent list, settings)
|
||||
// STATIC — admin data that almost never changes (tokens, upstream tracking)
|
||||
// HEALTH — fallback connection check (only when disconnected + unauthenticated)
|
||||
|
||||
export const POLL = {
|
||||
LIVE: 5_000,
|
||||
DASHBOARD: 15_000,
|
||||
DETAIL: 30_000,
|
||||
OVERVIEW: 60_000,
|
||||
STATIC: 300_000,
|
||||
HEALTH: 10_000,
|
||||
} as const;
|
||||
Loading…
Reference in a new issue