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:
@@ -0,0 +1,32 @@
|
||||
export const LOCAL_DEFAULT_BACKEND_URL = "http://localhost:8000"
|
||||
|
||||
export type ConfiguredBackendUrl =
|
||||
| { kind: "local" }
|
||||
| { kind: "fixed"; url: string }
|
||||
| { kind: "same-origin" }
|
||||
|
||||
export function configuredBackendUrl(): ConfiguredBackendUrl {
|
||||
const raw = process.env.NEXT_PUBLIC_BACKEND_URL
|
||||
if (raw === undefined || raw === "local") return { kind: "local" }
|
||||
if (raw === "" || raw === "same-origin") return { kind: "same-origin" }
|
||||
return { kind: "fixed", url: raw.replace(/\/$/, "") }
|
||||
}
|
||||
|
||||
export function defaultDataSourceMode(): "mock" | "live" {
|
||||
return process.env.NEXT_PUBLIC_DEFAULT_DATA_SOURCE === "live" ? "live" : "mock"
|
||||
}
|
||||
|
||||
export function isBackendUrlLocked(): boolean {
|
||||
const cfg = configuredBackendUrl()
|
||||
return cfg.kind === "same-origin" || cfg.kind === "fixed"
|
||||
}
|
||||
|
||||
export function resolveStoredBackendUrl(stored: string | null): string {
|
||||
const cfg = configuredBackendUrl()
|
||||
if (cfg.kind === "fixed") return cfg.url
|
||||
if (cfg.kind === "same-origin" && typeof window !== "undefined") {
|
||||
return window.location.origin
|
||||
}
|
||||
const trimmed = stored?.trim().replace(/\/$/, "")
|
||||
return trimmed || LOCAL_DEFAULT_BACKEND_URL
|
||||
}
|
||||
+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