瀏覽代碼

fix(nodes): "Invalid input" when saving a node with inbound sync mode "all"

NodeFormSchema required inboundTags, but the inboundTags Form.Item is only
mounted when inboundSyncMode is "selected" - antd onFinish omits unmounted
fields, so saving with the default "all" mode failed schema validation with
Zod generic "Invalid input" (regression from #5178; same class as the
earlier pinnedCertSha256 fix).

Also tolerate null inboundTags (Go nil slice) for nodes saved before #5178,
both in the form schema and NodeRecordSchema, and normalize edit-mode values.
MHSanaei 14 小時之前
父節點
當前提交
5c29851be1
共有 4 個文件被更改,包括 10 次插入9 次删除
  1. 1 6
      .gitattributes
  2. 1 0
      .gitignore
  3. 2 0
      frontend/src/pages/nodes/NodeFormModal.tsx
  4. 6 3
      frontend/src/schemas/node.ts

+ 1 - 6
.gitattributes

@@ -1,11 +1,6 @@
-# Shell scripts must stay LF so the Docker build works when the repo is
-# checked out on Windows (CRLF breaks the script shebang -> exit 127).
 *.sh text eol=lf
 DockerInit.sh text eol=lf
 DockerEntrypoint.sh text eol=lf
-
-# Generated files (regenerated from Go) must stay LF so a Windows regen
-# with core.autocrlf=true doesn't show phantom CRLF-only "modified" diffs.
 frontend/src/generated/** text eol=lf
 frontend/public/openapi.json text eol=lf
-frontend\src\test\__snapshots__\** text eol=lf
+frontend/src/test/__snapshots__/** text eol=lf

+ 1 - 0
.gitignore

@@ -36,6 +36,7 @@ Thumbs.db
 x-ui.db
 x-ui.db-shm
 x-ui.db-wal
+system_metrics.gob
 *.dump
 
 # Ignore Docker specific files

+ 2 - 0
frontend/src/pages/nodes/NodeFormModal.tsx

@@ -85,6 +85,8 @@ export default function NodeFormModal({
         ...(node as unknown as Partial<NodeFormValues>),
         id: node.id,
         scheme: (node.scheme as 'http' | 'https') || base.scheme,
+        inboundSyncMode: (node.inboundSyncMode as 'all' | 'selected') || base.inboundSyncMode,
+        inboundTags: node.inboundTags ?? [],
       }
       : base;
     if (next.scheme === 'http') next.tlsVerifyMode = 'skip';

+ 6 - 3
frontend/src/schemas/node.ts

@@ -32,7 +32,8 @@ export const NodeRecordSchema = z.object({
   tlsVerifyMode: z.enum(['verify', 'skip', 'pin']).optional(),
   pinnedCertSha256: z.string().optional(),
   inboundSyncMode: z.enum(['all', 'selected']).optional(),
-  inboundTags: z.array(z.string()).optional(),
+  // Backend serializes a nil []string as null for nodes saved before #5178.
+  inboundTags: z.array(z.string()).nullish(),
   // Multi-hop node tree (#4983): a node's stable GUID, its parent's GUID, and
   // whether it's a read-only transitive sub-node surfaced from a downstream node.
   guid: z.string().optional(),
@@ -65,8 +66,10 @@ export const NodeFormSchema = z.object({
   allowPrivateAddress: z.boolean(),
   tlsVerifyMode: z.enum(['verify', 'skip', 'pin']),
   pinnedCertSha256: z.string().optional().default(''),
-  inboundSyncMode: z.enum(['all', 'selected']),
-  inboundTags: z.array(z.string()),
+  inboundSyncMode: z.enum(['all', 'selected']).optional().default('all'),
+  // Unmounted when sync mode is "all" (absent from antd onFinish values) and
+  // serialized as null by the backend for a nil slice — tolerate both.
+  inboundTags: z.array(z.string()).nullish().transform((tags) => tags ?? []),
 });
 
 export type NodeRecord = z.infer<typeof NodeRecordSchema>;