chore: enhance data source configuration and mock data handling
Docker images / backend-image (push) Successful in 39s
Docker images / frontend-image (push) Successful in 1m38s
Docker images / updater-image (push) Successful in 35s
Docker images / notify-webhook (push) Has been skipped

- 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:
Denozordec
2026-05-12 15:38:27 +07:00
parent 4489dfb2d6
commit 1fefed2aa0
5 changed files with 48 additions and 23 deletions
+1
View File
@@ -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 }}
+2
View File
@@ -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
+30 -20
View File
@@ -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
+5
View File
@@ -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
View File
@@ -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>