chore: enhance frontend configuration for backend integration
- Added environment variables for backend URLs in the Dockerfile to facilitate dynamic configuration. - Updated Next.js configuration to include rewrites for health and API endpoints, improving backend connectivity. - Modified settings page to handle backend URL locking and normalization, enhancing user experience and data integrity.
This commit is contained in:
+35
-20
@@ -1,6 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, useContext, useEffect, useState, useCallback } from "react"
|
||||
import {
|
||||
configuredBackendUrl,
|
||||
defaultDataSourceMode,
|
||||
isBackendUrlLocked,
|
||||
LOCAL_DEFAULT_BACKEND_URL,
|
||||
resolveStoredBackendUrl,
|
||||
} from "@/lib/backend-url"
|
||||
|
||||
// ── types ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -11,6 +18,7 @@ interface DataSourceContextValue {
|
||||
setMode: (m: DataSourceMode) => void
|
||||
backendUrl: string
|
||||
setBackendUrl: (url: string) => void
|
||||
backendUrlLocked: boolean
|
||||
/** undefined = не проверялось, true = OK, false = недоступен */
|
||||
backendStatus: boolean | undefined
|
||||
checkBackend: () => Promise<void>
|
||||
@@ -22,31 +30,35 @@ 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"
|
||||
if (typeof window === "undefined") return defaultDataSourceMode()
|
||||
const stored = localStorage.getItem(LS_MODE) as DataSourceMode | null
|
||||
return stored === "mock" || stored === "live" ? stored : "mock"
|
||||
return stored === "mock" || stored === "live" ? stored : defaultDataSourceMode()
|
||||
}
|
||||
|
||||
function readStoredBackendUrl(): string {
|
||||
if (typeof window === "undefined") return DEFAULT_URL
|
||||
return localStorage.getItem(LS_BACKEND) ?? DEFAULT_URL
|
||||
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>("mock")
|
||||
const [backendUrl, setBackendUrlState] = useState(DEFAULT_URL)
|
||||
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()
|
||||
|
||||
useEffect(() => {
|
||||
queueMicrotask(() => {
|
||||
const nextMode = readStoredMode()
|
||||
const nextBackendUrl = readStoredBackendUrl()
|
||||
setModeState(nextMode)
|
||||
setBackendUrlState(nextBackendUrl)
|
||||
})
|
||||
if (configuredBackendUrl().kind !== "same-origin") return
|
||||
setBackendUrlState(window.location.origin)
|
||||
}, [])
|
||||
|
||||
const setMode = useCallback((m: DataSourceMode) => {
|
||||
@@ -55,27 +67,30 @@ export function DataSourceProvider({ children }: { children: React.ReactNode })
|
||||
}, [])
|
||||
|
||||
const setBackendUrl = useCallback((url: string) => {
|
||||
setBackendUrlState(url)
|
||||
localStorage.setItem(LS_BACKEND, url)
|
||||
}, [])
|
||||
if (backendUrlLocked) return
|
||||
const normalized = normalizeBackendUrl(url)
|
||||
setBackendUrlState(normalized)
|
||||
localStorage.setItem(LS_BACKEND, normalized)
|
||||
}, [backendUrlLocked])
|
||||
|
||||
const checkBackend = useCallback(async () => {
|
||||
const url = (localStorage.getItem(LS_BACKEND) ?? DEFAULT_URL).replace(/\/$/, "")
|
||||
const url = normalizeBackendUrl(backendUrl)
|
||||
try {
|
||||
const res = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3000) })
|
||||
setBackendStatus(res.ok)
|
||||
} catch {
|
||||
setBackendStatus(false)
|
||||
}
|
||||
}, [])
|
||||
}, [backendUrl])
|
||||
|
||||
// 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 }}>
|
||||
<DataSourceContext.Provider
|
||||
value={{ mode, setMode, backendUrl, setBackendUrl, backendUrlLocked, backendStatus, checkBackend }}
|
||||
>
|
||||
{children}
|
||||
</DataSourceContext.Provider>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user