Files
MikrotikManager/lib/data-source.tsx
T
Denozordec bfceb07a23
Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 1m32s
Docker images / frontend-image (push) Successful in 1m39s
Docker images / updater-image (push) Successful in 37s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
fix(ui): убрать проверку статуса бэкенда из компонентов
2026-05-12 18:58:38 +07:00

116 lines
4.1 KiB
TypeScript

"use client"
import { createContext, useContext, useEffect, useState, useCallback } from "react"
import {
configuredBackendUrl,
defaultDataSourceMode,
isBackendUrlLocked,
isMockDataSourceAvailable,
LOCAL_DEFAULT_BACKEND_URL,
resolveStoredBackendUrl,
} from "@/lib/backend-url"
// ── types ─────────────────────────────────────────────────────────────────────
export type DataSourceMode = "mock" | "live"
/** Режим «Живые» в настройках: моковые данные не подставлять. */
export function isLiveDataSourceMode(mode: DataSourceMode): boolean {
return mode === "live"
}
interface DataSourceContextValue {
mode: DataSourceMode
setMode: (m: DataSourceMode) => void
backendUrl: string
setBackendUrl: (url: string) => void
backendUrlLocked: boolean
mockModeAvailable: boolean
/** 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"
function readStoredMode(): DataSourceMode {
if (!isMockDataSourceAvailable()) return "live"
if (typeof window === "undefined") return defaultDataSourceMode()
const stored = localStorage.getItem(LS_MODE) as DataSourceMode | null
if (stored === "live") return "live"
if (stored === "mock") return "mock"
return defaultDataSourceMode()
}
function readStoredBackendUrl(): string {
if (typeof window === "undefined") return LOCAL_DEFAULT_BACKEND_URL
return resolveStoredBackendUrl(localStorage.getItem(LS_BACKEND))
}
function normalizeBackendUrl(url: string): string {
return url.trim().replace(/\/$/, "")
}
export function DataSourceProvider({ children }: { children: React.ReactNode }) {
const [mode, setModeState] = useState<DataSourceMode>(() =>
typeof window === "undefined" ? defaultDataSourceMode() : readStoredMode(),
)
const [backendUrl, setBackendUrlState] = useState(() =>
typeof window === "undefined" ? LOCAL_DEFAULT_BACKEND_URL : readStoredBackendUrl(),
)
const [backendStatus, setBackendStatus] = useState<boolean | undefined>(undefined)
const backendUrlLocked = isBackendUrlLocked()
const mockModeAvailable = isMockDataSourceAvailable()
useEffect(() => {
if (configuredBackendUrl().kind !== "same-origin") return
setBackendUrlState(window.location.origin)
}, [])
const setMode = useCallback((m: DataSourceMode) => {
if (!mockModeAvailable && m === "mock") return
setModeState(m)
localStorage.setItem(LS_MODE, m)
}, [mockModeAvailable])
const setBackendUrl = useCallback((url: string) => {
if (backendUrlLocked) return
const normalized = normalizeBackendUrl(url)
setBackendUrlState(normalized)
localStorage.setItem(LS_BACKEND, normalized)
}, [backendUrlLocked])
const checkBackend = useCallback(async () => {
const url = normalizeBackendUrl(backendUrl)
try {
const res = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3000) })
setBackendStatus(res.ok)
} catch {
setBackendStatus(false)
}
}, [backendUrl])
useEffect(() => {
if (mode === "live") queueMicrotask(() => { void checkBackend() })
}, [mode, checkBackend])
return (
<DataSourceContext.Provider
value={{ mode, setMode, backendUrl, setBackendUrl, backendUrlLocked, mockModeAvailable, 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
}