chore: enhance data source configuration and mock data handling
- Added support for mock data mode in the frontend by introducing a new environment variable in the Dockerfile and workflow configuration. - Updated the settings page to conditionally display options based on the availability of mock data, improving user experience. - Refactored data source logic to include a check for mock data availability, ensuring proper mode handling in the application.
This commit is contained in:
@@ -129,6 +129,7 @@ jobs:
|
||||
BACKEND_INTERNAL_URL=http://backend:8000
|
||||
NEXT_PUBLIC_BACKEND_URL=same-origin
|
||||
NEXT_PUBLIC_DEFAULT_DATA_SOURCE=live
|
||||
NEXT_PUBLIC_ALLOW_MOCK_DATA=false
|
||||
tags: |
|
||||
${{ env.IMAGE_NAME }}:latest
|
||||
${{ env.IMAGE_NAME }}:${{ gitea.sha }}
|
||||
|
||||
@@ -11,9 +11,11 @@ FROM deps AS build
|
||||
ARG BACKEND_INTERNAL_URL=http://backend:8000
|
||||
ARG NEXT_PUBLIC_BACKEND_URL=same-origin
|
||||
ARG NEXT_PUBLIC_DEFAULT_DATA_SOURCE=live
|
||||
ARG NEXT_PUBLIC_ALLOW_MOCK_DATA=false
|
||||
ENV BACKEND_INTERNAL_URL=$BACKEND_INTERNAL_URL
|
||||
ENV NEXT_PUBLIC_BACKEND_URL=$NEXT_PUBLIC_BACKEND_URL
|
||||
ENV NEXT_PUBLIC_DEFAULT_DATA_SOURCE=$NEXT_PUBLIC_DEFAULT_DATA_SOURCE
|
||||
ENV NEXT_PUBLIC_ALLOW_MOCK_DATA=$NEXT_PUBLIC_ALLOW_MOCK_DATA
|
||||
COPY packages/contracts packages/contracts
|
||||
COPY next.config.ts tsconfig.json postcss.config.mjs components.json ./
|
||||
COPY app app
|
||||
|
||||
@@ -825,7 +825,7 @@ export default function SettingsPage() {
|
||||
const [copied, setCopied] = useState<string | null>(null)
|
||||
|
||||
// data source
|
||||
const { mode, setMode, backendUrl, setBackendUrl, backendUrlLocked, backendStatus, checkBackend } = useDataSource()
|
||||
const { mode, setMode, backendUrl, setBackendUrl, backendUrlLocked, mockModeAvailable, backendStatus, checkBackend } = useDataSource()
|
||||
const evo = useEvoBGP()
|
||||
const [evoBaseDraft, setEvoBaseDraft] = useState("")
|
||||
const [evoEnabledDraft, setEvoEnabledDraft] = useState(false)
|
||||
@@ -969,30 +969,40 @@ export default function SettingsPage() {
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Источник данных</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
Переключите между живыми данными от бекенда и суррогатными моками из lib/data.ts
|
||||
{mockModeAvailable
|
||||
? "Переключите между живыми данными от бекенда и суррогатными моками из lib/data.ts"
|
||||
: "В продакшен-образе доступны только живые данные от бекенда"}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="divide-y px-5">
|
||||
|
||||
{/* mode toggle */}
|
||||
<SettingRow label="Режим данных" description="Моковые — статичные тестовые данные; Живые — RouterOS REST API через бекенд">
|
||||
<div className="flex rounded-md border border-input overflow-hidden h-8">
|
||||
{([["mock", "Моковые"], ["live", "Живые"]] as const).map(([v, l], i) => (
|
||||
<button key={v} onClick={() => setMode(v)}
|
||||
className={cn(
|
||||
"px-3 text-xs transition-colors border-input",
|
||||
i === 0 && "border-r",
|
||||
mode === v
|
||||
? v === "live"
|
||||
? "bg-emerald-600 text-white dark:bg-emerald-500"
|
||||
: "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:bg-muted",
|
||||
)}>
|
||||
{l}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</SettingRow>
|
||||
{mockModeAvailable ? (
|
||||
<SettingRow label="Режим данных" description="Моковые — статичные тестовые данные; Живые — RouterOS REST API через бекенд">
|
||||
<div className="flex rounded-md border border-input overflow-hidden h-8">
|
||||
{([["mock", "Моковые"], ["live", "Живые"]] as const).map(([v, l], i) => (
|
||||
<button key={v} onClick={() => setMode(v)}
|
||||
className={cn(
|
||||
"px-3 text-xs transition-colors border-input",
|
||||
i === 0 && "border-r",
|
||||
mode === v
|
||||
? v === "live"
|
||||
? "bg-emerald-600 text-white dark:bg-emerald-500"
|
||||
: "bg-primary text-primary-foreground"
|
||||
: "text-muted-foreground hover:bg-muted",
|
||||
)}>
|
||||
{l}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</SettingRow>
|
||||
) : (
|
||||
<SettingRow label="Режим данных" description="В Docker-образе приложение работает только с живыми данными бекенда">
|
||||
<span className="inline-flex h-8 items-center rounded-md border border-input bg-emerald-600 px-3 text-xs text-white dark:bg-emerald-500">
|
||||
Живые
|
||||
</span>
|
||||
</SettingRow>
|
||||
)}
|
||||
|
||||
{/* backend URL — visible in both modes so user can configure before switching */}
|
||||
<SettingRow
|
||||
|
||||
@@ -13,9 +13,14 @@ export function configuredBackendUrl(): ConfiguredBackendUrl {
|
||||
}
|
||||
|
||||
export function defaultDataSourceMode(): "mock" | "live" {
|
||||
if (!isMockDataSourceAvailable()) return "live"
|
||||
return process.env.NEXT_PUBLIC_DEFAULT_DATA_SOURCE === "live" ? "live" : "mock"
|
||||
}
|
||||
|
||||
export function isMockDataSourceAvailable(): boolean {
|
||||
return process.env.NEXT_PUBLIC_ALLOW_MOCK_DATA !== "false"
|
||||
}
|
||||
|
||||
export function isBackendUrlLocked(): boolean {
|
||||
const cfg = configuredBackendUrl()
|
||||
return cfg.kind === "same-origin" || cfg.kind === "fixed"
|
||||
|
||||
+10
-3
@@ -5,6 +5,7 @@ import {
|
||||
configuredBackendUrl,
|
||||
defaultDataSourceMode,
|
||||
isBackendUrlLocked,
|
||||
isMockDataSourceAvailable,
|
||||
LOCAL_DEFAULT_BACKEND_URL,
|
||||
resolveStoredBackendUrl,
|
||||
} from "@/lib/backend-url"
|
||||
@@ -19,6 +20,7 @@ interface DataSourceContextValue {
|
||||
backendUrl: string
|
||||
setBackendUrl: (url: string) => void
|
||||
backendUrlLocked: boolean
|
||||
mockModeAvailable: boolean
|
||||
/** undefined = не проверялось, true = OK, false = недоступен */
|
||||
backendStatus: boolean | undefined
|
||||
checkBackend: () => Promise<void>
|
||||
@@ -32,9 +34,12 @@ 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
|
||||
return stored === "mock" || stored === "live" ? stored : defaultDataSourceMode()
|
||||
if (stored === "live") return "live"
|
||||
if (stored === "mock") return "mock"
|
||||
return defaultDataSourceMode()
|
||||
}
|
||||
|
||||
function readStoredBackendUrl(): string {
|
||||
@@ -55,6 +60,7 @@ export function DataSourceProvider({ children }: { children: React.ReactNode })
|
||||
)
|
||||
const [backendStatus, setBackendStatus] = useState<boolean | undefined>(undefined)
|
||||
const backendUrlLocked = isBackendUrlLocked()
|
||||
const mockModeAvailable = isMockDataSourceAvailable()
|
||||
|
||||
useEffect(() => {
|
||||
if (configuredBackendUrl().kind !== "same-origin") return
|
||||
@@ -62,9 +68,10 @@ export function DataSourceProvider({ children }: { children: React.ReactNode })
|
||||
}, [])
|
||||
|
||||
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
|
||||
@@ -89,7 +96,7 @@ export function DataSourceProvider({ children }: { children: React.ReactNode })
|
||||
|
||||
return (
|
||||
<DataSourceContext.Provider
|
||||
value={{ mode, setMode, backendUrl, setBackendUrl, backendUrlLocked, backendStatus, checkBackend }}
|
||||
value={{ mode, setMode, backendUrl, setBackendUrl, backendUrlLocked, mockModeAvailable, backendStatus, checkBackend }}
|
||||
>
|
||||
{children}
|
||||
</DataSourceContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user