refactor(ui): server metrics page uses global time range
Drop the page-local DS Select window picker. Drive from() / to() off
useGlobalFilters().timeRange so the dashboard tracks the same TopBar range
as Exchanges / Dashboard / Runtime. Bucket size auto-scales via
stepSecondsFor(windowSeconds) (10 s for ≤30 min → 1 h for >48 h). Query
hooks now take ServerMetricsRange = { from: Date; to: Date } instead of a
windowSeconds number, so they support arbitrary absolute or rolling ranges
the TopBar may supply (not just "now − N"). Toolbar collapses to just the
server-instance badges.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,30 +49,47 @@ export interface ServerMetricQueryRequest {
|
||||
serverInstanceIds?: string[] | null;
|
||||
}
|
||||
|
||||
// ── Range helper ───────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Time range driving every hook below. Callers pass the window they want
|
||||
* to render; the hooks never invent their own "now" — that's the job of
|
||||
* the global time-range control.
|
||||
*/
|
||||
export interface ServerMetricsRange {
|
||||
from: Date;
|
||||
to: Date;
|
||||
}
|
||||
|
||||
function serializeRange(range: ServerMetricsRange) {
|
||||
return {
|
||||
from: range.from.toISOString(),
|
||||
to: range.to.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
// ── Query Hooks ────────────────────────────────────────────────────────
|
||||
|
||||
export function useServerMetricsCatalog(windowSeconds = 3600) {
|
||||
export function useServerMetricsCatalog(range: ServerMetricsRange) {
|
||||
const refetchInterval = useRefreshInterval(60_000);
|
||||
const { from, to } = serializeRange(range);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'server-metrics', 'catalog', windowSeconds],
|
||||
queryFn: async () => {
|
||||
const to = new Date();
|
||||
const from = new Date(to.getTime() - windowSeconds * 1000);
|
||||
const params = new URLSearchParams({ from: from.toISOString(), to: to.toISOString() });
|
||||
queryKey: ['admin', 'server-metrics', 'catalog', from, to],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams({ from, to });
|
||||
return adminFetch<ServerMetricCatalogEntry[]>(`/server-metrics/catalog?${params}`);
|
||||
},
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
export function useServerMetricsInstances(windowSeconds = 3600) {
|
||||
export function useServerMetricsInstances(range: ServerMetricsRange) {
|
||||
const refetchInterval = useRefreshInterval(60_000);
|
||||
const { from, to } = serializeRange(range);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'server-metrics', 'instances', windowSeconds],
|
||||
queryFn: async () => {
|
||||
const to = new Date();
|
||||
const from = new Date(to.getTime() - windowSeconds * 1000);
|
||||
const params = new URLSearchParams({ from: from.toISOString(), to: to.toISOString() });
|
||||
queryKey: ['admin', 'server-metrics', 'instances', from, to],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams({ from, to });
|
||||
return adminFetch<ServerInstanceInfo[]>(`/server-metrics/instances?${params}`);
|
||||
},
|
||||
refetchInterval,
|
||||
@@ -80,28 +97,23 @@ export function useServerMetricsInstances(windowSeconds = 3600) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a time-series query against the server_metrics table.
|
||||
* Generic time-series query against the server_metrics table.
|
||||
*
|
||||
* The window [from, to) is supplied in seconds of "now minus N" so the panel
|
||||
* refreshes automatically at the polling interval without the caller
|
||||
* recomputing timestamps.
|
||||
* The caller owns the window — passing the globally-selected range keeps
|
||||
* every panel aligned with the app-wide time control and allows inspection
|
||||
* of historical windows, not just "last N seconds from now".
|
||||
*/
|
||||
export function useServerMetricsSeries(
|
||||
request: Omit<ServerMetricQueryRequest, 'from' | 'to'>,
|
||||
windowSeconds: number,
|
||||
range: ServerMetricsRange,
|
||||
opts?: { enabled?: boolean },
|
||||
) {
|
||||
const refetchInterval = useRefreshInterval(30_000);
|
||||
const { from, to } = serializeRange(range);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'server-metrics', 'query', request, windowSeconds],
|
||||
queryFn: async () => {
|
||||
const to = new Date();
|
||||
const from = new Date(to.getTime() - windowSeconds * 1000);
|
||||
const body: ServerMetricQueryRequest = {
|
||||
...request,
|
||||
from: from.toISOString(),
|
||||
to: to.toISOString(),
|
||||
};
|
||||
queryKey: ['admin', 'server-metrics', 'query', request, from, to],
|
||||
queryFn: () => {
|
||||
const body: ServerMetricQueryRequest = { ...request, from, to };
|
||||
return adminFetch<ServerMetricQueryResponse>('/server-metrics/query', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
|
||||
Reference in New Issue
Block a user