"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={data:T|undefined;loading:boolean;error:string|null;receivedAt:string|null} type Reader=(base:string,options:{signal:AbortSignal})=>Promise // Each catalog owns its lifetime and revision. The page is keyed by source. function useCatalog(base:string,live:boolean,permission:string,read:Reader){ const [snapshot,setSnapshot]=useState>({data:undefined,loading:false,error:null,receivedAt:null}) const active=useRef(false),revision=useRef(0),controller=useRef(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} }