-
-
- 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 @@