mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 15:06:37 +08:00
[优化] 添加节点 IP 手动覆盖功能,更新相关文档和测试用例
This commit is contained in:
@@ -1654,7 +1654,10 @@ export function NodeDetailPage({ nodeId }: { nodeId: string }) {
|
||||
<p className="font-medium text-[var(--foreground-primary)]">
|
||||
IP 地址
|
||||
</p>
|
||||
<p className="mt-1">{node.ip || '暂无'}</p>
|
||||
<p className="mt-1">
|
||||
{node.ip || '暂无'}
|
||||
{node.ip_manual_override ? '(管理端锁定)' : ''}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-[var(--foreground-primary)]">
|
||||
|
||||
@@ -43,6 +43,7 @@ const nodeEditorSchema = z
|
||||
.min(1, '请输入节点名')
|
||||
.max(128, '节点名不能超过 128 个字符'),
|
||||
ip: z.string().trim().max(64, '节点 IP 不能超过 64 个字符'),
|
||||
ip_manual_override: z.boolean(),
|
||||
auto_update_enabled: z.boolean(),
|
||||
geo_manual_override: z.boolean(),
|
||||
geo_region: z.string(),
|
||||
@@ -51,6 +52,14 @@ const nodeEditorSchema = z
|
||||
geo_longitude: z.string().trim(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
if (values.ip_manual_override && values.ip.trim() === '') {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['ip'],
|
||||
message: '锁定节点 IP 时必须填写节点 IP',
|
||||
});
|
||||
}
|
||||
|
||||
if (!values.geo_manual_override) {
|
||||
return;
|
||||
}
|
||||
@@ -100,6 +109,7 @@ type NodeEditorValues = z.infer<typeof nodeEditorSchema>;
|
||||
const defaultValues: NodeEditorValues = {
|
||||
name: '',
|
||||
ip: '',
|
||||
ip_manual_override: false,
|
||||
auto_update_enabled: false,
|
||||
geo_manual_override: false,
|
||||
geo_region: '',
|
||||
@@ -184,6 +194,7 @@ function buildFormValues(node?: Partial<NodeItem> | null): NodeEditorValues {
|
||||
return {
|
||||
name: node.name ?? '',
|
||||
ip: node.ip ?? '',
|
||||
ip_manual_override: node.ip_manual_override ?? false,
|
||||
auto_update_enabled: node.auto_update_enabled ?? false,
|
||||
geo_manual_override: node.geo_manual_override ?? false,
|
||||
geo_region: node.geo_manual_override ? (node.geo_name ?? '') : '',
|
||||
@@ -204,6 +215,7 @@ function toPayload(values: NodeEditorValues): NodeMutationPayload {
|
||||
return {
|
||||
name: values.name.trim(),
|
||||
ip: values.ip.trim(),
|
||||
ip_manual_override: values.ip_manual_override,
|
||||
auto_update_enabled: values.auto_update_enabled,
|
||||
geo_manual_override: false,
|
||||
geo_name: '',
|
||||
@@ -215,6 +227,7 @@ function toPayload(values: NodeEditorValues): NodeMutationPayload {
|
||||
return {
|
||||
name: values.name.trim(),
|
||||
ip: values.ip.trim(),
|
||||
ip_manual_override: values.ip_manual_override,
|
||||
auto_update_enabled: values.auto_update_enabled,
|
||||
geo_manual_override: true,
|
||||
geo_name: values.geo_name.trim(),
|
||||
@@ -253,6 +266,10 @@ export function NodeEditorModal({
|
||||
control: form.control,
|
||||
name: 'auto_update_enabled',
|
||||
});
|
||||
const watchedIPManualOverride = useWatch({
|
||||
control: form.control,
|
||||
name: 'ip_manual_override',
|
||||
});
|
||||
const watchedGeoManualOverride = useWatch({
|
||||
control: form.control,
|
||||
name: 'geo_manual_override',
|
||||
@@ -266,6 +283,7 @@ export function NodeEditorModal({
|
||||
form.reset(buildFormValues(node));
|
||||
}, [form, isOpen, node]);
|
||||
|
||||
const ipField = form.register('ip');
|
||||
const handleSubmit = form.handleSubmit((values) => {
|
||||
onSubmit(toPayload(values));
|
||||
});
|
||||
@@ -309,12 +327,42 @@ export function NodeEditorModal({
|
||||
|
||||
<ResourceField
|
||||
label="节点 IP"
|
||||
hint="可手动维护节点当前对外 IP;留空则等待 Agent 注册或心跳自动回填。"
|
||||
hint={
|
||||
watchedIPManualOverride
|
||||
? '已锁定为指定值,不会被更新。'
|
||||
: '留空或关闭锁定时,等待 Agent 自动更新。'
|
||||
}
|
||||
error={form.formState.errors.ip?.message}
|
||||
>
|
||||
<ResourceInput placeholder="203.0.113.10" {...form.register('ip')} />
|
||||
<ResourceInput
|
||||
placeholder="203.0.113.10"
|
||||
{...ipField}
|
||||
onChange={(event) => {
|
||||
ipField.onChange(event);
|
||||
const nextIP = event.target.value.trim();
|
||||
const previousIP = (node?.ip ?? '').trim();
|
||||
if (nextIP !== '' && nextIP !== previousIP) {
|
||||
form.setValue('ip_manual_override', true, {
|
||||
shouldDirty: true,
|
||||
shouldValidate: true,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ResourceField>
|
||||
|
||||
<ToggleField
|
||||
label="锁定节点 IP"
|
||||
description="开启后以管理端填写的 IP 为准,Agent 后续上报不会覆盖该字段。"
|
||||
checked={watchedIPManualOverride}
|
||||
onChange={(checked) =>
|
||||
form.setValue('ip_manual_override', checked, {
|
||||
shouldDirty: true,
|
||||
shouldValidate: true,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<ToggleField
|
||||
label="启用自动更新"
|
||||
description="开启后 Agent 心跳返回会提示节点自动执行自更新。"
|
||||
|
||||
@@ -309,6 +309,7 @@ export function NodesPage() {
|
||||
</p>
|
||||
<p className="text-xs text-[var(--foreground-secondary)]">
|
||||
IP:{node.ip || 'null'}
|
||||
{node.ip_manual_override ? '(已锁定)' : ''}
|
||||
</p>
|
||||
<p className="text-xs text-[var(--foreground-secondary)]">
|
||||
位置:{node.geo_name || '未配置地图点位'}
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface NodeItem {
|
||||
node_id: string;
|
||||
name: string;
|
||||
ip: string;
|
||||
ip_manual_override: boolean;
|
||||
geo_name: string;
|
||||
geo_latitude?: number | null;
|
||||
geo_longitude?: number | null;
|
||||
@@ -41,6 +42,7 @@ export interface NodeBootstrapToken {
|
||||
export interface NodeMutationPayload {
|
||||
name: string;
|
||||
ip: string;
|
||||
ip_manual_override: boolean;
|
||||
auto_update_enabled: boolean;
|
||||
geo_name: string;
|
||||
geo_latitude?: number | null;
|
||||
|
||||
Reference in New Issue
Block a user