diff --git a/web/src/components/accounts/RebindEnvironment.tsx b/web/src/components/accounts/RebindEnvironment.tsx index e25f503..83889cb 100644 --- a/web/src/components/accounts/RebindEnvironment.tsx +++ b/web/src/components/accounts/RebindEnvironment.tsx @@ -1,22 +1,20 @@ -// 账号环境出口重绑:账号即环境下唯一的换绑入口(环境跟随账号,不再有独立环境页)。 -// 仅列举检测通过的出口;重绑走 POST /browsers/:alias/rebind(alias=账号 ID,成功后由父级刷新)。 +// 编辑页网络出口切换:仅列举检测通过的出口,以运行环境 alias 请求并返回最新环境状态。 import { useEffect, useMemo, useState } from 'react'; import { Button, Flex, Select, Typography } from 'antd'; import { getList } from '@/services/api'; import { jsonOptions, request } from '@/requestErrorConfig'; interface RebindEnvironmentProps { - accountID: string; binding: any; disabled?: boolean; - onRebound: () => void | Promise; + onRebound: (environment: any) => void | Promise; onError: (error: unknown) => void; } -export default function RebindEnvironment({ accountID, binding, disabled, onRebound, onError }: RebindEnvironmentProps) { +export default function RebindEnvironment({ binding, disabled, onRebound, onError }: RebindEnvironmentProps) { const [exits, setExits] = useState([]); const [exitsError, setExitsError] = useState(false); - const [exitID, setExitID] = useState(''); + const [exitID, setExitID] = useState(binding.network_exit_id ?? ''); const [busy, setBusy] = useState(false); useEffect(() => { @@ -25,32 +23,39 @@ export default function RebindEnvironment({ accountID, binding, disabled, onRebo .then((result) => { if (!cancelled) setExits((result.data ?? []).filter((item: any) => item.health_status === 'healthy')); }) - .catch(() => { - if (!cancelled) setExitsError(true); + .catch((reason) => { + if (!cancelled) { + console.error('[network-exit] Failed to load exits', { environmentAlias: binding.alias, error: reason }); + setExitsError(true); + onError(reason); + } }); return () => { cancelled = true; }; }, []); - // 直连(当前状态)或任一健康出口;已在用的出口不重复出现在选项里。 + // 当前选择回显已绑定出口;直连与检测通过的出口均可选。 const options = useMemo(() => { const choices = [{ value: '', label: '当前机器直连' }]; for (const exit of exits) { - if (exit.id === binding.network_exit_id) continue; choices.push({ value: exit.id, label: `${exit.id}${exit.observed_public_ip ? ` · ${exit.observed_public_ip}` : ''}` }); } return choices; - }, [exits, binding.network_exit_id]); + }, [exits]); const unchanged = exitID === (binding.network_exit_id ?? ''); async function rebind() { setBusy(true); try { - await request(`/browsers/${encodeURIComponent(accountID)}/rebind`, jsonOptions('POST', { network_exit_id: exitID })); - await onRebound(); + const result = await request(`/browsers/${encodeURIComponent(binding.alias)}/rebind`, jsonOptions('POST', { network_exit_id: exitID })); + if (result?.environment?.alias !== binding.alias) { + throw new Error('网络出口切换结果中的运行环境不匹配,请刷新页面确认实际状态。'); + } + await onRebound(result.environment); } catch (reason) { + console.error('[network-exit] Failed to switch exit', { environmentAlias: binding.alias, networkExitID: exitID, error: reason }); onError(reason); } finally { setBusy(false); @@ -60,21 +65,22 @@ export default function RebindEnvironment({ accountID, binding, disabled, onRebo return ( - 切换出口 + 目标出口