Implement bad connections baseline reset functionality
- Added a new field `badConnBaseline` in the `Handler` struct to store the baseline for bad connections. - Updated the `BuildIncidents` function to utilize the effective bad connections count for incident generation. - Introduced a new API endpoint `/api/agg/bad-connections/reset` to reset the bad connections baseline. - Enhanced the frontend with a button to trigger the reset action, providing user feedback through toast notifications. - Updated Svelte components to handle the reset functionality and display appropriate messages based on the operation's success or failure.
This commit is contained in:
@@ -282,6 +282,35 @@ export async function fetchAggIncidents(params?: { aliases?: string }): Promise<
|
||||
return body as AggEnvelope<IncidentsData>;
|
||||
}
|
||||
|
||||
export type ResetBadConnectionsRow = {
|
||||
alias: string;
|
||||
ok: boolean;
|
||||
baseline?: number;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export type ResetBadConnectionsData = {
|
||||
servers: ResetBadConnectionsRow[];
|
||||
};
|
||||
|
||||
/** POST: зафиксировать текущий connections_bad_total как «ноль» для алертов bad connections на шлюзе. */
|
||||
export async function postAggResetBadConnectionsBaseline(params?: {
|
||||
aliases?: string;
|
||||
}): Promise<AggEnvelope<ResetBadConnectionsData>> {
|
||||
const q = new URLSearchParams();
|
||||
if (params?.aliases) q.set('aliases', params.aliases);
|
||||
const url = `${gatewayBase()}/api/agg/bad-connections/reset${q.toString() ? `?${q}` : ''}`;
|
||||
const res = await fetch(url, { method: 'POST' });
|
||||
const body = (await parseJson(res)) as Record<string, unknown> | null;
|
||||
if (!res.ok) {
|
||||
throw new ApiError(`bad-connections/reset HTTP ${res.status}`, res.status, body);
|
||||
}
|
||||
if (!body || body.ok !== true) {
|
||||
throw new ApiError('bad-connections/reset: ok !== true', res.status, body);
|
||||
}
|
||||
return body as AggEnvelope<ResetBadConnectionsData>;
|
||||
}
|
||||
|
||||
export function liveEventsUrl(params?: { aliases?: string }): string {
|
||||
const q = new URLSearchParams();
|
||||
if (params?.aliases) q.set('aliases', params.aliases);
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
ApiError,
|
||||
fetchAggIncidents,
|
||||
fetchAggSummary,
|
||||
postAggResetBadConnectionsBaseline,
|
||||
type IncidentItem,
|
||||
type IncidentsData
|
||||
} from '$lib/api/client.js';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import KpiStatCard from '$lib/components/kpi-stat-card.svelte';
|
||||
import DataTableCard from '$lib/components/data-table/data-table-card.svelte';
|
||||
@@ -33,6 +35,7 @@
|
||||
import SirenIcon from '@lucide/svelte/icons/siren';
|
||||
import TriangleAlertIcon from '@lucide/svelte/icons/triangle-alert';
|
||||
import InfoIconBadge from '@lucide/svelte/icons/info';
|
||||
import EraserIcon from '@lucide/svelte/icons/eraser';
|
||||
|
||||
type TriageState = {
|
||||
ack?: boolean;
|
||||
@@ -61,6 +64,7 @@
|
||||
let incSearch = $state('');
|
||||
let incSortKey = $state<'sev' | 'title' | null>(null);
|
||||
let incSortDir = $state<SortDir | null>(null);
|
||||
let resetBadBusy = $state(false);
|
||||
|
||||
function parseRefresh(raw: string | null): number {
|
||||
if (raw == null || raw.trim() === '') return 30;
|
||||
@@ -274,6 +278,31 @@
|
||||
incSortKey = next === null ? null : key;
|
||||
incSortDir = next;
|
||||
}
|
||||
|
||||
async function resetBadConnectionsBaseline() {
|
||||
if (resetBadBusy) return;
|
||||
resetBadBusy = true;
|
||||
try {
|
||||
const env = await postAggResetBadConnectionsBaseline({
|
||||
aliases: aliasFilter === 'all' ? undefined : aliasFilter
|
||||
});
|
||||
const failed = env.data.servers.filter((s) => !s.ok);
|
||||
if (failed.length > 0) {
|
||||
toast.warning(
|
||||
`Не на всех нодах удалось прочитать stats/summary: ${failed.map((f) => `${f.alias}: ${f.error ?? 'ошибка'}`).join('; ')}`
|
||||
);
|
||||
} else {
|
||||
toast.success(
|
||||
'Для алертов bad connections зафиксированы текущие значения счётчиков (отметка на шлюзе).'
|
||||
);
|
||||
}
|
||||
await load();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof ApiError ? e.message : String(e));
|
||||
} finally {
|
||||
resetBadBusy = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mb-6 flex flex-wrap items-center justify-between gap-4">
|
||||
@@ -286,6 +315,25 @@
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<FleetLiveStaleBadge {isStale} {staleSeconds} refreshSeconds={refreshSeconds} />
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger class="inline-flex">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={resetBadBusy || loading}
|
||||
onclick={() => void resetBadConnectionsBaseline()}
|
||||
>
|
||||
<EraserIcon class="mr-1 size-4 {resetBadBusy ? 'animate-pulse' : ''}" />
|
||||
Сброс bad connections
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content class="max-w-xs">
|
||||
<p class="text-xs">
|
||||
Запомнить текущий <span class="font-mono">connections_bad_total</span> на выбранных нодах как ноль для
|
||||
инцидентов на этом шлюзе (память процесса). Сырые счётчики на нодах Telemt не меняются.
|
||||
</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
<Button variant="outline" size="sm" onclick={() => void load()} disabled={loading}>
|
||||
<RefreshCwIcon class="mr-1 size-4 {loading ? 'animate-spin' : ''}" />
|
||||
Обновить
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
import {
|
||||
liveEventsUrl,
|
||||
fetchAggSummary,
|
||||
postAggResetBadConnectionsBaseline,
|
||||
ApiError,
|
||||
type IncidentItem,
|
||||
type IncidentSeverity,
|
||||
type IncidentStatus
|
||||
} from '$lib/api/client.js';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import KpiStatCard from '$lib/components/kpi-stat-card.svelte';
|
||||
import DataTableCard from '$lib/components/data-table/data-table-card.svelte';
|
||||
import DataTableToolbar from '$lib/components/data-table/data-table-toolbar.svelte';
|
||||
@@ -30,6 +33,7 @@
|
||||
import FleetLiveStaleBadge from '$lib/components/fleet/fleet-live-stale-badge.svelte';
|
||||
import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw';
|
||||
import InfoIcon from '@lucide/svelte/icons/info';
|
||||
import EraserIcon from '@lucide/svelte/icons/eraser';
|
||||
|
||||
type LiveStatus = 'healthy' | 'degraded' | 'critical';
|
||||
|
||||
@@ -64,6 +68,7 @@
|
||||
let liveIncSearch = $state('');
|
||||
let liveSortKey = $state<'sev' | 'title' | null>(null);
|
||||
let liveSortDir = $state<SortDir | null>(null);
|
||||
let resetBadBusy = $state(false);
|
||||
|
||||
function liveSevRank(s: IncidentSeverity): number {
|
||||
if (s === 'critical') return 3;
|
||||
@@ -94,6 +99,31 @@
|
||||
return out;
|
||||
});
|
||||
|
||||
async function resetBadConnectionsBaseline() {
|
||||
if (resetBadBusy) return;
|
||||
resetBadBusy = true;
|
||||
try {
|
||||
const env = await postAggResetBadConnectionsBaseline({
|
||||
aliases: aliasFilter === 'all' ? undefined : aliasFilter
|
||||
});
|
||||
const failed = env.data.servers.filter((s) => !s.ok);
|
||||
if (failed.length > 0) {
|
||||
toast.warning(
|
||||
`Не на всех нодах удалось прочитать stats/summary: ${failed.map((f) => `${f.alias}: ${f.error ?? 'ошибка'}`).join('; ')}`
|
||||
);
|
||||
} else {
|
||||
toast.success(
|
||||
'Для алертов bad connections зафиксированы текущие значения счётчиков (отметка на шлюзе). Следующий снимок потока обновит список.'
|
||||
);
|
||||
}
|
||||
connectStream();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof ApiError ? e.message : String(e));
|
||||
} finally {
|
||||
resetBadBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleLiveSort(key: 'sev' | 'title') {
|
||||
const next = nextSortDir(liveSortDir, key, liveSortKey);
|
||||
liveSortKey = next === null ? null : key;
|
||||
@@ -339,6 +369,25 @@
|
||||
? 'Нет событий SSE дольше 12 с — снимок считается устаревшим.'
|
||||
: `Последнее событие потока: ${staleSeconds ?? 0} с назад.`}
|
||||
/>
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger class="inline-flex">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={resetBadBusy}
|
||||
onclick={() => void resetBadConnectionsBaseline()}
|
||||
>
|
||||
<EraserIcon class="mr-1 size-4 {resetBadBusy ? 'animate-pulse' : ''}" />
|
||||
Сброс bad connections
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content class="max-w-xs">
|
||||
<p class="text-xs">
|
||||
Запомнить текущий <span class="font-mono">connections_bad_total</span> на выбранных нодах как ноль для
|
||||
инцидентов на этом шлюзе (память процесса). Сырые счётчики на нодах Telemt не меняются.
|
||||
</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
<Button variant="outline" size="sm" onclick={() => connectStream()}>
|
||||
<RefreshCwIcon class="mr-1 size-4" />
|
||||
Переподключить
|
||||
|
||||
Reference in New Issue
Block a user