fix
This commit is contained in:
3 files changed
+76
-1
No files matched your search
@@ -100,6 +100,41 @@ export function useImportZones() {
|
|||||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["domains", project?.id] }),
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["domains", project?.id] }),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const CHECK_ALL_CONCURRENCY = 4
|
||||||
|
|
||||||
|
// Ручной check по всем переданным доменам. Бэкенд (handleCheck) сам пишет
|
||||||
|
// статус, поэтому после прогона достаточно перечитать список доменов.
|
||||||
|
// Сбой одной зоны не прерывает остальные — считаем успехи и ошибки.
|
||||||
|
export function useCheckAllDomains() {
|
||||||
|
const { project } = useAuth()
|
||||||
|
const qc = useQueryClient()
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (ids: string[]) => {
|
||||||
|
const pid = requireProjectId(project)
|
||||||
|
let ok = 0
|
||||||
|
let failed = 0
|
||||||
|
const queue = [...ids]
|
||||||
|
const worker = async () => {
|
||||||
|
for (let id = queue.shift(); id !== undefined; id = queue.shift()) {
|
||||||
|
try {
|
||||||
|
await api.checkDomain(pid, id)
|
||||||
|
ok++
|
||||||
|
} catch {
|
||||||
|
failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
Array.from({ length: Math.min(CHECK_ALL_CONCURRENCY, ids.length) }, worker),
|
||||||
|
)
|
||||||
|
return { ok, failed }
|
||||||
|
},
|
||||||
|
onSettled: () => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["domains", project?.id] })
|
||||||
|
qc.invalidateQueries({ queryKey: ["check", project?.id] })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
export function useSetDomainTemplate() {
|
export function useSetDomainTemplate() {
|
||||||
const { project } = useAuth()
|
const { project } = useAuth()
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
|
|||||||
@@ -234,3 +234,16 @@ test("счётчик показывает, сколько доменов отф
|
|||||||
|
|
||||||
expect(screen.getByText(/показано 1 из 2/i)).toBeInTheDocument()
|
expect(screen.getByText(/показано 1 из 2/i)).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("«Проверить все зоны» проверяет только домены с шаблоном и обновляет список", async () => {
|
||||||
|
const check = vi.spyOn(api, "checkDomain").mockResolvedValue({ diffs: [] } as never)
|
||||||
|
const user = userEvent.setup()
|
||||||
|
renderPage()
|
||||||
|
await screen.findByText("example.com.")
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: "Проверить все зоны" }))
|
||||||
|
|
||||||
|
await waitFor(() => expect(check).toHaveBeenCalledTimes(1))
|
||||||
|
expect(check).toHaveBeenCalledWith(PROJECT_ID, "d2")
|
||||||
|
expect(await screen.findByText("Проверено 1")).toBeInTheDocument()
|
||||||
|
})
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
import { Inbox, Loader2, Upload } from "lucide-react"
|
import { GitCompareArrows, Inbox, Loader2, Upload } from "lucide-react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { StatusBadge } from "@/components/StatusBadge"
|
import { StatusBadge } from "@/components/StatusBadge"
|
||||||
import {
|
import {
|
||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
} from "@/components/ui/table"
|
} from "@/components/ui/table"
|
||||||
import {
|
import {
|
||||||
useAccounts,
|
useAccounts,
|
||||||
|
useCheckAllDomains,
|
||||||
useDomains,
|
useDomains,
|
||||||
useImportZones,
|
useImportZones,
|
||||||
useSetDomainTemplate,
|
useSetDomainTemplate,
|
||||||
@@ -56,6 +57,7 @@ export function DomainsPage() {
|
|||||||
const accounts = useAccounts()
|
const accounts = useAccounts()
|
||||||
const templates = useTemplates()
|
const templates = useTemplates()
|
||||||
const importZones = useImportZones()
|
const importZones = useImportZones()
|
||||||
|
const checkAll = useCheckAllDomains()
|
||||||
const setTemplate = useSetDomainTemplate()
|
const setTemplate = useSetDomainTemplate()
|
||||||
|
|
||||||
const accountList = accounts.data ?? []
|
const accountList = accounts.data ?? []
|
||||||
@@ -104,6 +106,13 @@ export function DomainsPage() {
|
|||||||
importZones.mutate(selectedImportAccount)
|
importZones.mutate(selectedImportAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Диф есть только у доменов с шаблоном; фильтры списка на набор не влияют.
|
||||||
|
const checkableIds = domainList.filter((d) => d.templateId).map((d) => d.id)
|
||||||
|
|
||||||
|
function onCheckAll() {
|
||||||
|
checkAll.mutate(checkableIds)
|
||||||
|
}
|
||||||
|
|
||||||
function onTemplateChange(domainId: string, value: unknown) {
|
function onTemplateChange(domainId: string, value: unknown) {
|
||||||
const templateId = value === NO_TEMPLATE ? null : (value as string)
|
const templateId = value === NO_TEMPLATE ? null : (value as string)
|
||||||
setTemplate.mutate({ id: domainId, templateId })
|
setTemplate.mutate({ id: domainId, templateId })
|
||||||
@@ -146,9 +155,27 @@ export function DomainsPage() {
|
|||||||
)}
|
)}
|
||||||
Импортировать зоны
|
Импортировать зоны
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={onCheckAll}
|
||||||
|
disabled={checkableIds.length === 0 || checkAll.isPending}
|
||||||
|
>
|
||||||
|
{checkAll.isPending ? (
|
||||||
|
<Loader2 className="size-4 animate-spin" strokeWidth={1.75} />
|
||||||
|
) : (
|
||||||
|
<GitCompareArrows className="size-4" strokeWidth={1.75} />
|
||||||
|
)}
|
||||||
|
Проверить все зоны
|
||||||
|
</Button>
|
||||||
{importZones.isError && (
|
{importZones.isError && (
|
||||||
<span className="font-dns text-xs text-destructive">{importZones.error.message}</span>
|
<span className="font-dns text-xs text-destructive">{importZones.error.message}</span>
|
||||||
)}
|
)}
|
||||||
|
{checkAll.isSuccess && (
|
||||||
|
<span className="font-dns text-xs text-muted-foreground">
|
||||||
|
Проверено {checkAll.data.ok}
|
||||||
|
{checkAll.data.failed > 0 && `, ошибок ${checkAll.data.failed}`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{importZones.isSuccess && (
|
{importZones.isSuccess && (
|
||||||
<span className="font-dns text-xs text-muted-foreground">
|
<span className="font-dns text-xs text-muted-foreground">
|
||||||
Создано {importZones.data.created.length}, удалено {importZones.data.removed.length}
|
Создано {importZones.data.created.length}, удалено {importZones.data.removed.length}
|
||||||
|
|||||||
Reference in new issue
Block a user