Files
MikrotikManager/lib/data-source.tsx
T
2026-05-03 11:16:07 +07:00

89 lines
3.2 KiB
TypeScript

"use client"
import { createContext, useContext, useEffect, useState, useCallback } from "react"
// ── types ─────────────────────────────────────────────────────────────────────
export type DataSourceMode = "mock" | "live"
interface DataSourceContextValue {
mode: DataSourceMode
setMode: (m: DataSourceMode) => void
backendUrl: string
setBackendUrl: (url: string) => void
/** undefined = не проверялось, true = OK, false = недоступен */
backendStatus: boolean | undefined
checkBackend: () => Promise<void>
}
// ── context ───────────────────────────────────────────────────────────────────
const DataSourceContext = createContext<DataSourceContextValue | null>(null)
const LS_MODE = "routerlists:data-source"
const LS_BACKEND = "routerlists:backend-url"
const DEFAULT_URL = "http://localhost:8000"
function readStoredMode(): DataSourceMode {
if (typeof window === "undefined") return "mock"
const stored = localStorage.getItem(LS_MODE) as DataSourceMode | null
return stored === "mock" || stored === "live" ? stored : "mock"
}
function readStoredBackendUrl(): string {
if (typeof window === "undefined") return DEFAULT_URL
return localStorage.getItem(LS_BACKEND) ?? DEFAULT_URL
}
export function DataSourceProvider({ children }: { children: React.ReactNode }) {
const [mode, setModeState] = useState<DataSourceMode>("mock")
const [backendUrl, setBackendUrlState] = useState(DEFAULT_URL)
const [backendStatus, setBackendStatus] = useState<boolean | undefined>(undefined)
useEffect(() => {
queueMicrotask(() => {
const nextMode = readStoredMode()
const nextBackendUrl = readStoredBackendUrl()
setModeState(nextMode)
setBackendUrlState(nextBackendUrl)
})
}, [])
const setMode = useCallback((m: DataSourceMode) => {
setModeState(m)
localStorage.setItem(LS_MODE, m)
}, [])
const setBackendUrl = useCallback((url: string) => {
setBackendUrlState(url)
localStorage.setItem(LS_BACKEND, url)
}, [])
const checkBackend = useCallback(async () => {
const url = (localStorage.getItem(LS_BACKEND) ?? DEFAULT_URL).replace(/\/$/, "")
try {
const res = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3000) })
setBackendStatus(res.ok)
} catch {
setBackendStatus(false)
}
}, [])
// Auto-check when mode switches to live (defer to avoid set-state-in-effect rule on async loader)
useEffect(() => {
if (mode === "live") queueMicrotask(() => { void checkBackend() })
}, [mode, checkBackend])
return (
<DataSourceContext.Provider value={{ mode, setMode, backendUrl, setBackendUrl, backendStatus, checkBackend }}>
{children}
</DataSourceContext.Provider>
)
}
export function useDataSource(): DataSourceContextValue {
const ctx = useContext(DataSourceContext)
if (!ctx) throw new Error("useDataSource must be used inside <DataSourceProvider>")
return ctx
}