62 lines
2.8 KiB
TypeScript
62 lines
2.8 KiB
TypeScript
"use client"
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
import { serverListSchema, type ServerRead } from "@mmapp/contracts/servers"
|
|
import { readCertificatesState } from "@/shared/api/certificates"
|
|
import { requestJson } from "@/shared/api/http-client"
|
|
import { mergeCertificateSnapshots, type CertificateSnapshots } from "@/lib/certificate-snapshots"
|
|
|
|
export function useCertificateCatalog(backendUrl: string, live: boolean, canRead: boolean, canReadServers: boolean) {
|
|
const [snapshots, setSnapshots] = useState<CertificateSnapshots>({})
|
|
const [servers, setServers] = useState<ServerRead[]>([])
|
|
const [hasState, setHasState] = useState(false)
|
|
const [hasServers, setHasServers] = useState(false)
|
|
const [loading, setLoading] = useState(live)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [serverError, setServerError] = useState<string | null>(null)
|
|
const [collectedAt, setCollectedAt] = useState<string | null>(null)
|
|
const controller = useRef<AbortController | null>(null)
|
|
const revision = useRef(0)
|
|
const alive = useRef(true)
|
|
|
|
const reload = useCallback(async () => {
|
|
if (!live || !alive.current) return
|
|
controller.current?.abort()
|
|
const current = ++revision.current
|
|
const abort = new AbortController()
|
|
controller.current = abort
|
|
const signal = AbortSignal.any([abort.signal, AbortSignal.timeout(30_000)])
|
|
const active = () => alive.current && !abort.signal.aborted && revision.current === current
|
|
setLoading(true)
|
|
await Promise.allSettled([
|
|
canRead ? readCertificatesState(backendUrl, { signal }).then(result => {
|
|
if (!active()) return
|
|
setSnapshots(previous => mergeCertificateSnapshots(previous, result))
|
|
setHasState(true)
|
|
setCollectedAt(result.collectedAt)
|
|
setError(null)
|
|
}).catch(reason => {
|
|
if (active()) setError(reason instanceof Error ? reason.message : "Ошибка каталога сертификатов")
|
|
}) : Promise.resolve(),
|
|
canReadServers ? requestJson<unknown>(backendUrl, "/api/servers", { signal }).then(payload => {
|
|
const result = serverListSchema.parse(payload)
|
|
if (!active()) return
|
|
setServers(result)
|
|
setHasServers(true)
|
|
setServerError(null)
|
|
}).catch(reason => {
|
|
if (active()) setServerError(reason instanceof Error ? reason.message : "Ошибка каталога серверов")
|
|
}) : Promise.resolve(),
|
|
])
|
|
if (active()) setLoading(false)
|
|
}, [backendUrl, live, canRead, canReadServers])
|
|
|
|
useEffect(() => {
|
|
alive.current = true
|
|
queueMicrotask(() => { if (alive.current) void reload() })
|
|
return () => { alive.current = false; controller.current?.abort() }
|
|
}, [reload])
|
|
|
|
return { snapshots, servers, hasState, hasServers, loading, error, serverError, collectedAt, reload }
|
|
}
|