Files
MikrotikManager/hooks/use-alerts-targets.ts
T

45 lines
2.5 KiB
TypeScript

"use client"
import {useCallback,useEffect,useRef,useState} from "react"
import {can} from "@/lib/auth"
import {getAlertsBgpTargets,getAlertsServerTargets,getAlertsGreTargets} from "@/shared/api/alerts-targets"
type Snapshot<T>={data:T|undefined;loading:boolean;error:string|null;receivedAt:string|null}
type Reader<T>=(base:string,options:{signal:AbortSignal})=>Promise<T>
// Each catalog owns its lifetime and revision. The page is keyed by source.
function useCatalog<T>(base:string,live:boolean,permission:string,read:Reader<T>){
const [snapshot,setSnapshot]=useState<Snapshot<T>>({data:undefined,loading:false,error:null,receivedAt:null})
const active=useRef(false),revision=useRef(0),controller=useRef<AbortController|null>(null)
const refresh=useCallback(async()=>{
if(!active.current||!live)return
controller.current?.abort();const requestRevision=++revision.current
if(!can(permission)){setSnapshot({data:undefined,loading:false,error:"Нет доступа к каталогу",receivedAt:null});return}
const request=new AbortController();controller.current=request
setSnapshot(previous=>({...previous,loading:true,error:null}))
const current=()=>active.current&&requestRevision===revision.current&&can(permission)
try{
const data=await read(base,{signal:request.signal})
if(current())setSnapshot({data,loading:false,error:null,receivedAt:new Date().toISOString()})
}catch(error){
if(current())setSnapshot(previous=>({...previous,loading:false,error:error instanceof Error?error.message:"Не удалось получить каталог"}))
}
},[base,live,permission,read])
useEffect(()=>{const generation=revision;active.current=true;let pending=true;queueMicrotask(()=>{if(pending)void refresh()});return()=>{pending=false;active.current=false;generation.current++;controller.current?.abort()}},[refresh])
return {...snapshot,refresh}
}
async function readGre(base:string,options:{signal:AbortSignal}){
const result=await getAlertsGreTargets(base,options)
if(result.failures?.length)throw Error("Неполный каталог GRE: не ответили устройства — "+result.failures.map(f=>f.serverName||f.serverId).join(", "))
return result
}
export function useAlertsTargets(base:string,live:boolean){
const bgp=useCatalog(base,live,"mm:bgp:read",getAlertsBgpTargets)
const servers=useCatalog(base,live,"mm:servers:read",getAlertsServerTargets)
const gre=useCatalog(base,live,"mm:filters:read",readGre)
const refresh=()=>{void bgp.refresh();void servers.refresh();void gre.refresh()}
return {bgp,servers,gre,refresh}
}