feat(accounts): consolidate detail resources and move exit switching to edit
This commit is contained in:
@@ -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<void>;
|
||||
onRebound: (environment: any) => void | Promise<void>;
|
||||
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<any[]>([]);
|
||||
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 (
|
||||
<Flex vertical gap={8}>
|
||||
<Flex align="center" gap={8} wrap="wrap">
|
||||
<Typography.Text type="secondary">切换出口</Typography.Text>
|
||||
<Typography.Text type="secondary">目标出口</Typography.Text>
|
||||
<Select
|
||||
style={{ minWidth: 220 }}
|
||||
value={exitID}
|
||||
onChange={setExitID}
|
||||
options={options}
|
||||
placeholder={exitsError ? '出口列表不可用' : '选择目标出口'}
|
||||
disabled={disabled || exitsError}
|
||||
disabled={disabled || exitsError || busy}
|
||||
aria-label="目标网络出口"
|
||||
size="small"
|
||||
/>
|
||||
<Button size="small" type="primary" disabled={disabled || unchanged || busy} loading={busy} onClick={rebind}>
|
||||
重绑
|
||||
<Button size="small" type="primary" disabled={disabled || exitsError || unchanged || busy} loading={busy} onClick={rebind}>
|
||||
保存网络出口
|
||||
</Button>
|
||||
</Flex>
|
||||
{exitsError ? <Typography.Text type="secondary" style={{ fontSize: 12 }}>出口状态未知,请稍后重试。</Typography.Text> : null}
|
||||
{exitsError ? <Typography.Text type="secondary" style={{ fontSize: 12 }}>出口状态未知,请刷新页面后重试。</Typography.Text> : null}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user