From 725ab0bf6e4b7bc4711c74dd6b013d34a91ec240 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Mon, 30 Mar 2026 20:04:33 +0700 Subject: [PATCH] Implement alias filtering across multiple pages - Introduced an `AliasFilterSelect` component to streamline alias selection in various routes, enhancing user experience. - Refactored alias handling logic to use a single filter mechanism, improving consistency and reducing code duplication. - Updated API calls to utilize the new alias filtering logic, ensuring accurate data retrieval based on user-selected aliases. - Enhanced the loading of available aliases dynamically from aggregated summary data, keeping the interface up-to-date. --- .../lib/components/alias-filter-select.svelte | 32 +++++++++ web/src/routes/+page.svelte | 48 ++++++++------ web/src/routes/incidents/+page.svelte | 66 ++++--------------- web/src/routes/ips/+page.svelte | 48 +++++++++----- web/src/routes/live/+page.svelte | 63 ++++-------------- web/src/routes/users/+page.svelte | 45 ++++++++----- 6 files changed, 144 insertions(+), 158 deletions(-) create mode 100644 web/src/lib/components/alias-filter-select.svelte diff --git a/web/src/lib/components/alias-filter-select.svelte b/web/src/lib/components/alias-filter-select.svelte new file mode 100644 index 0000000..62dbbc7 --- /dev/null +++ b/web/src/lib/components/alias-filter-select.svelte @@ -0,0 +1,32 @@ + + + diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index 919e2ee..a60226c 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -17,6 +17,7 @@ import { Alert, AlertDescription, AlertTitle } from '$lib/components/ui/alert/index.js'; import * as Table from '$lib/components/ui/table/index.js'; import { Button } from '$lib/components/ui/button/index.js'; + import AliasFilterSelect from '$lib/components/alias-filter-select.svelte'; import { cn } from '$lib/utils.js'; import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw'; import AlertTriangleIcon from '@lucide/svelte/icons/triangle-alert'; @@ -75,8 +76,8 @@ let uniqueIps = $state(null); let generatedAt = $state(null); let partial = $state(false); - let aliases = $state(''); - let aliasesInput = $state(''); + let aliasFilter = $state('all'); + let availableAliases = $state([]); let refreshSeconds = $state(30); let refreshInput = $state('30'); let lastSuccessAtMs = $state(null); @@ -90,20 +91,39 @@ > >({}); + function parseAliasFilter(raw: string | null): string { + const value = raw?.trim() ?? ''; + return !value || value.includes(',') ? 'all' : value; + } + + function collectAliases(...lists: Array | null | undefined>): string[] { + const out = new Set(); + for (const list of lists) { + for (const item of list ?? []) { + if (typeof item === 'string' && item.trim()) out.add(item); + } + } + return Array.from(out).sort((a, b) => a.localeCompare(b)); + } + async function load() { loading = true; err = null; try { const [s, f, u] = await Promise.all([ - fetchAggSummary({ top_n: 15, aliases: aliases || undefined }), - fetchAggFleetStatus({ aliases: aliases || undefined }), - fetchAggUniqueIps({ aliases: aliases || undefined, geo: true }) + fetchAggSummary({ top_n: 15, aliases: aliasFilter === 'all' ? undefined : aliasFilter }), + fetchAggFleetStatus({ aliases: aliasFilter === 'all' ? undefined : aliasFilter }), + fetchAggUniqueIps({ aliases: aliasFilter === 'all' ? undefined : aliasFilter, geo: true }) ]); generatedAt = s.generated_at; partial = !!(s.partial || f.partial || u.partial); summary = s.data; fleet = f.data ?? null; uniqueIps = u.data ?? null; + availableAliases = collectAliases( + (fleet?.servers ?? []).map((x) => x.alias), + (summary?.servers ?? []).map((x) => x.alias) + ); lastSuccessAtMs = Date.now(); // Как бейдж «OK» в таблице: только health + system/info (без x.ok — в JSON поле ok опционально). @@ -163,10 +183,9 @@ async function applyControls() { const q = new URLSearchParams(page.url.searchParams); - const normalizedAliases = aliasesInput.trim(); const parsedRefresh = parseRefresh(refreshInput); - if (normalizedAliases) q.set('aliases', normalizedAliases); - else q.delete('aliases'); + if (aliasFilter === 'all') q.delete('aliases'); + else q.set('aliases', aliasFilter); if (parsedRefresh === 30) q.delete('refresh'); else q.set('refresh', String(parsedRefresh)); const qs = q.toString(); @@ -181,9 +200,8 @@ $effect(() => { queryKey; if (typeof window === 'undefined') return; - aliases = page.url.searchParams.get('aliases')?.trim() ?? ''; + aliasFilter = parseAliasFilter(page.url.searchParams.get('aliases')); refreshSeconds = parseRefresh(page.url.searchParams.get('refresh')); - aliasesInput = aliases; refreshInput = String(refreshSeconds); restartPolling(); void load(); @@ -370,15 +388,7 @@
- +
-
- - Aliases - {aliasesLabel()} - -
- {#if availableAliases.length === 0} -
Список нод не загружен
- {:else} -
- {#each availableAliases as a (a)} - - {/each} -
- {/if} -
-
+
- +
-
- - Aliases - {aliasesLabel()} - -
- {#if availableAliases.length === 0} -
Список нод не загружен
- {:else} -
- {#each availableAliases as a (a)} - - {/each} -
- {/if} -
-
+ diff --git a/web/src/routes/users/+page.svelte b/web/src/routes/users/+page.svelte index 7b3b717..55b1015 100644 --- a/web/src/routes/users/+page.svelte +++ b/web/src/routes/users/+page.svelte @@ -2,7 +2,7 @@ import { onMount } from 'svelte'; import { page } from '$app/state'; import { goto } from '$app/navigation'; - import { fetchAggUsers, ApiError } from '$lib/api/client.js'; + import { fetchAggUsers, fetchAggSummary, ApiError } from '$lib/api/client.js'; import type { components } from '$lib/api/aggregate.gen.js'; import { formatMiB } from '$lib/format.js'; import { Badge } from '$lib/components/ui/badge/index.js'; @@ -10,6 +10,7 @@ import * as Table from '$lib/components/ui/table/index.js'; import * as Card from '$lib/components/ui/card/index.js'; import { Alert, AlertDescription, AlertTitle } from '$lib/components/ui/alert/index.js'; + import AliasFilterSelect from '$lib/components/alias-filter-select.svelte'; import CopyIcon from '@lucide/svelte/icons/copy'; import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw'; @@ -18,8 +19,8 @@ let rows = $state([]); let partial = $state(false); let includeLinks = $state(false); - let aliases = $state(''); - let aliasesInput = $state(''); + let aliasFilter = $state('all'); + let availableAliases = $state([]); let refreshSeconds = $state(30); let refreshInput = $state('30'); let lastSuccessAtMs = $state(null); @@ -27,6 +28,11 @@ let pollTimer: ReturnType | null = null; let staleTimer: ReturnType | null = null; + function parseAliasFilter(raw: string | null): string { + const value = raw?.trim() ?? ''; + return !value || value.includes(',') ? 'all' : value; + } + function parseRefresh(raw: string | null): number { if (raw == null || raw.trim() === '') return 30; const n = Number(raw); @@ -45,7 +51,7 @@ err = null; try { const usersEnv = await fetchAggUsers({ - aliases: aliases || undefined, + aliases: aliasFilter === 'all' ? undefined : aliasFilter, include_links: includeLinks }); partial = !!usersEnv.partial; @@ -72,10 +78,9 @@ async function applyControls() { const q = new URLSearchParams(page.url.searchParams); - const normalizedAliases = aliasesInput.trim(); const parsedRefresh = parseRefresh(refreshInput); - if (normalizedAliases) q.set('aliases', normalizedAliases); - else q.delete('aliases'); + if (aliasFilter === 'all') q.delete('aliases'); + else q.set('aliases', aliasFilter); if (parsedRefresh === 30) q.delete('refresh'); else q.set('refresh', String(parsedRefresh)); q.set('include_links', includeLinks ? '1' : '0'); @@ -91,10 +96,9 @@ $effect(() => { queryKey; if (typeof window === 'undefined') return; - aliases = page.url.searchParams.get('aliases')?.trim() ?? ''; + aliasFilter = parseAliasFilter(page.url.searchParams.get('aliases')); refreshSeconds = parseRefresh(page.url.searchParams.get('refresh')); includeLinks = parse01(page.url.searchParams.get('include_links'), false); - aliasesInput = aliases; refreshInput = String(refreshSeconds); restartPolling(); void load(); @@ -108,6 +112,19 @@ ); onMount(() => { + void (async () => { + try { + const env = await fetchAggSummary({ top_n: 10 }); + const aliases = (env.data.servers ?? []) + .map((s) => s.alias) + .filter((a): a is string => typeof a === 'string' && a.length > 0) + .sort((a, b) => a.localeCompare(b)); + availableAliases = aliases; + if (aliasFilter !== 'all' && !aliases.includes(aliasFilter)) aliasFilter = 'all'; + } catch { + availableAliases = []; + } + })(); staleTimer = setInterval(() => { nowMs = Date.now(); }, 1000); @@ -180,15 +197,7 @@
- +