87 lines
3.3 KiB
TypeScript
87 lines
3.3 KiB
TypeScript
// 编辑页网络出口切换:仅列举检测通过的出口,以运行环境 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 {
|
|
binding: any;
|
|
disabled?: boolean;
|
|
onRebound: (environment: any) => void | Promise<void>;
|
|
onError: (error: unknown) => void;
|
|
}
|
|
|
|
export default function RebindEnvironment({ binding, disabled, onRebound, onError }: RebindEnvironmentProps) {
|
|
const [exits, setExits] = useState<any[]>([]);
|
|
const [exitsError, setExitsError] = useState(false);
|
|
const [exitID, setExitID] = useState(binding.network_exit_id ?? '');
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
getList({ resource: 'network-exits' })
|
|
.then((result) => {
|
|
if (!cancelled) setExits((result.data ?? []).filter((item: any) => item.health_status === 'healthy'));
|
|
})
|
|
.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) {
|
|
choices.push({ value: exit.id, label: `${exit.id}${exit.observed_public_ip ? ` · ${exit.observed_public_ip}` : ''}` });
|
|
}
|
|
return choices;
|
|
}, [exits]);
|
|
|
|
const unchanged = exitID === (binding.network_exit_id ?? '');
|
|
|
|
async function rebind() {
|
|
setBusy(true);
|
|
try {
|
|
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);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Flex vertical gap={8}>
|
|
<Flex align="center" gap={8} wrap="wrap">
|
|
<Typography.Text type="secondary">目标出口</Typography.Text>
|
|
<Select
|
|
style={{ minWidth: 220 }}
|
|
value={exitID}
|
|
onChange={setExitID}
|
|
options={options}
|
|
placeholder={exitsError ? '出口列表不可用' : '选择目标出口'}
|
|
disabled={disabled || exitsError || busy}
|
|
aria-label="目标网络出口"
|
|
size="small"
|
|
/>
|
|
<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}
|
|
</Flex>
|
|
);
|
|
}
|