This commit is contained in:
vasyansk committed 2026-11-02 17:41:46 +07:00
1 parent 550427e2d4
commit 8417a144d6
3 files changed
+76 -1

No files matched your search

+35
View File
@@ -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()
+13
View File
@@ -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()
})
+28 -1
View File
@@ -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}