fix(api): сохранять каталог speed-проб атомарно
This commit is contained in:
1 parent
d4f2961f76
commit
908974302e
9 files changed
+204
-86
No files matched your search
+15
-24
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { speedHistoryResponseSchema, speedProbesResponseSchema } from "@mmapp/contracts"
|
||||
import { speedHistoryResponseSchema, speedProbesResponseSchema, speedProbesWriteSchema, speedProbesSavedSchema } from "@mmapp/contracts"
|
||||
|
||||
import probeStyles from "./probe-rows.module.css"
|
||||
|
||||
@@ -1074,6 +1074,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
const [deleteTarget, setDeleteTarget] = useState<{ kind: "ping" | "speed"; id: string; label: string } | null>(null)
|
||||
const draftProbeId = useRef("")
|
||||
const [probeSaving, setProbeSaving] = useState(false)
|
||||
const [probeSaveWarning, setProbeSaveWarning] = useState<string | null>(null)
|
||||
const [probeSaveError, setProbeSaveError] = useState<string | null>(null)
|
||||
const savingRef = useRef(false)
|
||||
const saveContext = useRef(0)
|
||||
@@ -1524,34 +1525,23 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
const persistSpeedProbes = useCallback(async (rows: SpeedProbeRow[]) => {
|
||||
if (!liveApi) return
|
||||
if (!speedCatalogReady) throw new Error("Каталог speed-проб не подтверждён. Обновите данные перед сохранением.")
|
||||
await apiFetch("/api/uptime/speed-probes", {
|
||||
method: "PUT",
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
body: JSON.stringify({
|
||||
probes: rows.map((p) => ({
|
||||
id: p.id,
|
||||
srcServerId: p.srcServerId,
|
||||
dstServerId: p.dstServerId,
|
||||
srcInterface: p.srcInterface || "",
|
||||
dstInterface: p.dstInterface || "",
|
||||
protocol: p.protocol,
|
||||
direction: p.direction,
|
||||
durationSec: Number.parseInt(p.durationSec, 10) || 10,
|
||||
enabled: p.enabled,
|
||||
lastRunAt: p.lastRunAt ?? null,
|
||||
lastTxAvgMbps: p.lastTxAvgMbps ?? null,
|
||||
lastRxAvgMbps: p.lastRxAvgMbps ?? null,
|
||||
lastStatus: p.lastStatus ?? null,
|
||||
lastError: p.lastError ?? null,
|
||||
})),
|
||||
}),
|
||||
})
|
||||
const payload = speedProbesWriteSchema.safeParse({ probes: rows.map(p => ({
|
||||
id: p.id, srcServerId: p.srcServerId, dstServerId: p.dstServerId,
|
||||
srcInterface: p.srcInterface || "", dstInterface: p.dstInterface || "",
|
||||
protocol: p.protocol, direction: p.direction, durationSec: p.durationSec, enabled: p.enabled,
|
||||
})) })
|
||||
if (!payload.success) throw new Error("Проверьте speed-пробы: источник и назначение должны отличаться, длительность — целое число от 3 секунд.")
|
||||
const saved = speedProbesSavedSchema.safeParse(await apiFetch<unknown>("/api/uptime/speed-probes", {
|
||||
method: "PUT", signal: AbortSignal.timeout(30_000), body: JSON.stringify(payload.data),
|
||||
}))
|
||||
if (!saved.success) throw new Error("Ответ о сохранении speed-проб не подтверждён. Обновите список перед повторной попыткой.")
|
||||
setProbeSaveWarning(saved.data.warning ?? null)
|
||||
}, [apiFetch, liveApi, speedCatalogReady])
|
||||
|
||||
async function saveProbeForm(save: () => Promise<void>, confirm: () => void) {
|
||||
if (!requireWrite()) return
|
||||
savingRef.current = true
|
||||
setProbeSaving(true); setProbeSaveError(null)
|
||||
setProbeSaving(true); setProbeSaveError(null); setProbeSaveWarning(null)
|
||||
overviewReqRef.current++
|
||||
const context = ++saveContext.current
|
||||
try {
|
||||
@@ -2014,6 +2004,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
{!canWrite && <ContextAlert variant="info" role="status"><ContextTitle>Только просмотр</ContextTitle><ContextDescription>Изменение проб и запуск измерений требуют разрешения на запись. Обновление данных загружает сохранённые результаты.</ContextDescription></ContextAlert>}
|
||||
{permissionError && !sheetOpen && !speedSheetOpen && !deleteTarget && <ContextAlert variant="destructive" role="alert"><ContextDescription>{permissionError}</ContextDescription></ContextAlert>}
|
||||
|
||||
{probeSaveWarning && <ContextAlert variant="warning" role="status"><ContextDescription>{probeSaveWarning}</ContextDescription></ContextAlert>}
|
||||
{probeSaveError && !sheetOpen && !speedSheetOpen && !deleteTarget && <ContextAlert variant="destructive" role="alert"><ContextDescription>{probeSaveError}</ContextDescription></ContextAlert>}
|
||||
|
||||
{urlError && <ContextAlert variant="warning" role="alert"><ContextTitle>Параметры ссылки требуют внимания</ContextTitle><ContextDescription>{urlError}<Button type="button" variant="outline" size="sm" onClick={resetUrl}>Сбросить параметры мониторинга</Button></ContextDescription></ContextAlert>}
|
||||
|
||||
Reference in new issue
Block a user