fix(probes): проверять расписание и сохранять подтверждённые данные
This commit is contained in:
1 parent
1d27127b52
commit
271291a910
10 files changed
+108
-98
No files matched your search
@@ -18,22 +18,8 @@ import { DataGridSortHeader } from "@/components/data-grids/shared/data-grid-sor
|
||||
import { EmptyState } from "@/components/empty-state"
|
||||
import { ClockIcon } from "lucide-react"
|
||||
|
||||
export interface SpeedProbeApiRow {
|
||||
id: string
|
||||
srcServerId: string
|
||||
dstServerId: string
|
||||
srcInterface: string
|
||||
dstInterface: string
|
||||
protocol: string
|
||||
direction: string
|
||||
durationSec: string
|
||||
enabled: boolean
|
||||
lastRunAt: string | null
|
||||
lastTxAvgMbps: number | null
|
||||
lastRxAvgMbps: number | null
|
||||
lastStatus: string | null
|
||||
lastError: string | null
|
||||
}
|
||||
import type {SpeedProbeApiRow} from "@/lib/probe-schedule-response"
|
||||
export type {SpeedProbeApiRow} from "@/lib/probe-schedule-response"
|
||||
|
||||
interface ProbesSpeedProbesDataGridProps {
|
||||
rows: SpeedProbeApiRow[]
|
||||
@@ -123,9 +109,9 @@ function ProbesSpeedProbesDataGrid({ rows, serverName }: ProbesSpeedProbesDataGr
|
||||
return (
|
||||
<span className="text-xs text-muted-foreground truncate block">
|
||||
{r.lastRunAt ?? "—"}
|
||||
{r.lastStatus === "done" && r.lastTxAvgMbps != null && (
|
||||
{r.lastStatus === "done" && (
|
||||
<span className="text-emerald-600 dark:text-emerald-400 ml-1">
|
||||
TX≈{r.lastTxAvgMbps.toFixed(1)} RX≈{(r.lastRxAvgMbps ?? 0).toFixed(1)} Mb/s
|
||||
TX: {r.lastTxAvgMbps==null?"неизвестно":r.lastTxAvgMbps.toFixed(1)+" Мбит/с"} · RX: {r.lastRxAvgMbps==null?"неизвестно":r.lastRxAvgMbps.toFixed(1)+" Мбит/с"}
|
||||
</span>
|
||||
)}
|
||||
{r.lastStatus === "error" && r.lastError && (
|
||||
@@ -157,7 +143,7 @@ function ProbesSpeedProbesDataGrid({ rows, serverName }: ProbesSpeedProbesDataGr
|
||||
<EmptyState
|
||||
icon={<ClockIcon className="size-4" />}
|
||||
title="Нет записей speed-test"
|
||||
description="Настраиваются через API PUT /api/uptime/speed-probes"
|
||||
description="Добавьте пробы в разделе мониторинга."
|
||||
className="border-0 py-10"
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
"use client"
|
||||
import Link from "next/link"
|
||||
import {useCallback,useEffect,useRef,useState} from "react"
|
||||
import {Button} from "@/components/ui/button"
|
||||
import {DataPageCard} from "@/components/data-page-card"
|
||||
import {ProbesSpeedProbesDataGrid} from "@/components/data-grids/probes-speed-probes-data-grid"
|
||||
import {can,ensureAuthConfig} from "@/lib/auth"
|
||||
import {readProbeSchedule,type SpeedProbeApiRow} from "@/lib/probe-schedule-response"
|
||||
import type {Server} from "@/lib/data"
|
||||
import {requestJson} from "@/shared/api/http-client"
|
||||
|
||||
type Snapshot={rows:SpeedProbeApiRow[]|undefined;loading:boolean;error:string|null;receivedAt:string|null}
|
||||
export function ProbesSpeedSchedule({base,serversForName}:{base:string;serversForName:Server[]}){
|
||||
const [state,setState]=useState<Snapshot>({rows:undefined,loading:true,error:null,receivedAt:null})
|
||||
const active=useRef(false),revision=useRef(0),request=useRef<AbortController|null>(null),timer=useRef<ReturnType<typeof setTimeout>|null>(null)
|
||||
const refresh=useCallback(async()=>{
|
||||
if(!active.current||request.current)return
|
||||
const id=++revision.current,controller=new AbortController();request.current=controller
|
||||
const current=()=>active.current&&revision.current===id&&!controller.signal.aborted
|
||||
setState(previous=>({...previous,loading:true,error:null}))
|
||||
timer.current=setTimeout(()=>{if(current()){revision.current++;controller.abort();request.current=null;setState(previous=>({...previous,loading:false,error:"Время ожидания проб скорости истекло. Повторите обновление."}))}},30000)
|
||||
try{
|
||||
await ensureAuthConfig();if(!current())return
|
||||
if(!can("mm:uptime:read")){setState({rows:undefined,loading:false,error:"Нет права читать пробы скорости.",receivedAt:null});return}
|
||||
const rows=readProbeSchedule(await requestJson<unknown>(base,"/api/uptime/speed-probes",{signal:controller.signal,cache:"no-store"}))
|
||||
if(current()){
|
||||
if(!can("mm:uptime:read")){setState({rows:undefined,loading:false,error:"Нет права читать пробы скорости.",receivedAt:null});return}
|
||||
setState({rows,loading:false,error:null,receivedAt:new Date().toISOString()})
|
||||
}
|
||||
}catch(error){if(current())setState(previous=>({...previous,loading:false,error:error instanceof Error?error.message:"Не удалось прочитать пробы скорости."}))}
|
||||
finally{if(revision.current===id){if(timer.current)clearTimeout(timer.current);timer.current=null;request.current=null}}
|
||||
},[base])
|
||||
useEffect(()=>{const generation=revision;active.current=true;let pending=true;queueMicrotask(()=>{if(pending)void refresh()});return()=>{pending=false;active.current=false;generation.current++;request.current?.abort();request.current=null;if(timer.current)clearTimeout(timer.current)}},[refresh])
|
||||
return <section aria-label="Пробы скорости" className="min-w-0 space-y-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2"><p role="status" className="text-xs text-muted-foreground">{state.loading?"Обновление проб скорости…":state.receivedAt?"Последние данные получены: "+new Date(state.receivedAt).toLocaleString("ru-RU"):"Данные проб скорости ещё не получены."}</p><Button type="button" size="sm" variant="outline" disabled={state.loading} onClick={()=>void refresh()}>Обновить пробы скорости</Button></div>
|
||||
{state.error?<p role="alert" className="text-sm text-destructive">{state.error}{state.rows!==undefined?" Показан последний успешный список.":""}</p>:null}
|
||||
{state.rows!==undefined?<DataPageCard><ProbesSpeedProbesDataGrid rows={state.rows} serverName={id=>serversForName.find(s=>s.id===id)?.name??id}/></DataPageCard>:null}
|
||||
<p className="text-xs text-muted-foreground">Показаны пробы скорости коллектора. Периодичность запуска задаётся в сборе данных; отдельного расписания диагностических GRE-тестов в живом режиме нет.</p>
|
||||
<div className="flex flex-wrap gap-3 text-sm"><Link className="underline underline-offset-4" href="/uptime">Настроить пробы в мониторинге</Link><Link className="underline underline-offset-4" href="/data-collection">Открыть сбор данных</Link></div>
|
||||
</section>
|
||||
}
|
||||
Reference in new issue
Block a user