From 8f0bd61c141fadc2bfd15171a19cfb635fc76407 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Fri, 12 Jun 2026 02:58:05 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(connection):=20?= =?UTF-8?q?=E6=8B=86=E5=88=86=20MongoDB=20=E8=BF=9E=E6=8E=A5=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 MongoDB 拓扑、SRV、策略和副本集表单拆入独立组件 - 更新连接弹窗源码断言覆盖拆分后的 Mongo 字段 - 已通过前端定向测试、build 和浏览器 Mongo 表单冒烟 --- .../ConnectionModal.edit-password.test.tsx | 15 +- frontend/src/components/ConnectionModal.tsx | 321 +-------------- .../ConnectionModalMongoSections.tsx | 364 ++++++++++++++++++ 3 files changed, 397 insertions(+), 303 deletions(-) create mode 100644 frontend/src/components/ConnectionModalMongoSections.tsx diff --git a/frontend/src/components/ConnectionModal.edit-password.test.tsx b/frontend/src/components/ConnectionModal.edit-password.test.tsx index e683429..2e6b86c 100644 --- a/frontend/src/components/ConnectionModal.edit-password.test.tsx +++ b/frontend/src/components/ConnectionModal.edit-password.test.tsx @@ -3,7 +3,8 @@ import { readFileSync } from 'node:fs'; const connectionModalSource = readFileSync(new URL('./ConnectionModal.tsx', import.meta.url), 'utf8'); const redisSectionsSource = readFileSync(new URL('./ConnectionModalRedisSections.tsx', import.meta.url), 'utf8'); -const source = `${connectionModalSource}\n${redisSectionsSource}`; +const mongoSectionsSource = readFileSync(new URL('./ConnectionModalMongoSections.tsx', import.meta.url), 'utf8'); +const source = `${connectionModalSource}\n${redisSectionsSource}\n${mongoSectionsSource}`; describe('ConnectionModal edit password behavior', () => { it('keeps the prefilled primary password masked by default', () => { @@ -51,3 +52,15 @@ describe('ConnectionModal Redis Sentinel configuration', () => { expect(source).toContain('form.setFieldValue("port", 6379)'); }); }); + +describe('ConnectionModal MongoDB configuration', () => { + it('keeps replica, SRV, and read preference fields in the split Mongo sections', () => { + expect(source).toContain('ConnectionModalMongoSections'); + expect(source).toContain('name="mongoSrv"'); + expect(source).toContain('SRV 与 SSH 隧道同时启用'); + expect(source).toContain('name="mongoReplicaPassword"'); + expect(source).toContain('clearKey: "mongoReplicaPassword"'); + expect(source).toContain('自动发现成员'); + expect(source).toContain('fieldName: "mongoReadPreference"'); + }); +}); diff --git a/frontend/src/components/ConnectionModal.tsx b/frontend/src/components/ConnectionModal.tsx index c39891d..f923bc4 100644 --- a/frontend/src/components/ConnectionModal.tsx +++ b/frontend/src/components/ConnectionModal.tsx @@ -97,6 +97,7 @@ import { TestJVMConnection, } from "../../wailsjs/go/app/App"; import { ConnectionConfig, MongoMemberInfo, SavedConnection } from "../types"; +import ConnectionModalMongoSections from "./ConnectionModalMongoSections"; import ConnectionModalRedisSections from "./ConnectionModalRedisSections"; const { Text } = Typography; @@ -5335,308 +5336,24 @@ const ConnectionModal: React.FC<{ ), })} - {dbType === "mongodb" && - renderConfigSectionCard({ - sectionKey: "connectionMode", - icon: , - children: renderChoiceCards({ - fieldName: "mongoTopology", - value: String(mongoTopology), - options: [ - { - value: "single", - label: "单机模式", - description: "只连接一个 MongoDB 节点。", - }, - { - value: "replica", - label: "副本集 / 多节点", - description: "配置副本集名称和多个候选节点。", - }, - ], - }), - })} - - {dbType === "mongodb" && - renderConfigSectionCard({ - sectionKey: "mongoDiscovery", - icon: , - children: ( - <> - -
- {[ - { - value: false, - label: "标准地址", - description: "使用 host:port 直连或副本集节点列表。", - }, - { - value: true, - label: "SRV 地址", - description: - "使用 mongodb+srv,由 DNS 发现目标节点。", - }, - ].map((option) => { - const active = mongoSrv === option.value; - return ( - - ); - })} -
- {mongoSrv && useSSH && ( - - )} - - ), - })} - - {dbType === "mongodb" && - mongoTopology === "replica" && - renderConfigSectionCard({ - sectionKey: "replica", - icon: , - children: ( - <> - - - - - - - - - - - {renderStoredSecretControls({ - fieldName: "mongoReplicaPassword", - clearKey: "mongoReplicaPassword", - hasStoredSecret: initialValues?.hasMongoReplicaPassword, - clearLabel: "清除已保存副本集密码", - description: - "当前已保存副本集密码。留空表示继续沿用,输入新值表示替换。", - })} - - - - {mongoMembers.length > 0 && ( - record.host} - pagination={false} - dataSource={mongoMembers} - style={{ marginBottom: 12 }} - columns={[ - { title: "Host", dataIndex: "host", width: "48%" }, - { - title: "角色", - dataIndex: "role", - width: "32%", - render: ( - value: string, - record: MongoMemberInfo, - ) => ( - - {value || "UNKNOWN"} - - ), - }, - { - title: "健康", - dataIndex: "healthy", - width: "20%", - render: (value: boolean) => ( - - {value ? "正常" : "异常"} - - ), - }, - ]} - /> - )} - - ), - })} - - {dbType === "mongodb" && - renderConfigSectionCard({ - sectionKey: "mongoPolicy", - icon: , - children: ( -
- - - -
- 读偏好 (readPreference) - {renderChoiceCards({ - fieldName: "mongoReadPreference", - value: String(mongoReadPreference), - minWidth: 130, - options: [ - { - value: "primary", - label: "primary", - description: "只读主节点。", - }, - { - value: "primaryPreferred", - label: "primaryPreferred", - description: "主节点优先。", - }, - { - value: "secondary", - label: "secondary", - description: "只读从节点。", - }, - { - value: "secondaryPreferred", - label: "secondaryPreferred", - description: "从节点优先。", - }, - { - value: "nearest", - label: "nearest", - description: "选择最近节点。", - }, - ], - })} -
-
- ), - })} + {dbType === "mongodb" && ( + + )} {isRedis && ( void; +}) => React.ReactNode; + +type RenderConfigSectionCard = (params: { + sectionKey: ConnectionConfigSectionKey; + icon: React.ReactNode; + children: React.ReactNode; + badge?: React.ReactNode; +}) => React.ReactNode; + +type RenderStoredSecretControls = (params: { + fieldName: string; + clearKey: "mongoReplicaPassword"; + hasStoredSecret?: boolean; + clearLabel: string; + description: string; +}) => React.ReactNode; + +interface ConnectionModalMongoSectionsProps { + mongoTopology: string; + mongoSrv: boolean; + useSSH: boolean; + darkMode: boolean; + modalMutedTextStyle: React.CSSProperties; + mongoReadPreference: string; + mongoMembers: MongoMemberInfo[]; + discoveringMembers: boolean; + initialValues?: SavedConnection | null; + renderChoiceCards: RenderChoiceCards; + renderConfigSectionCard: RenderConfigSectionCard; + renderStoredSecretControls: RenderStoredSecretControls; + setChoiceFieldValue: (fieldName: string, value: string | boolean) => void; + handleDiscoverMongoMembers: () => void; +} + +const ConnectionModalMongoSections: React.FC = ({ + mongoTopology, + mongoSrv, + useSSH, + darkMode, + modalMutedTextStyle, + mongoReadPreference, + mongoMembers, + discoveringMembers, + initialValues, + renderChoiceCards, + renderConfigSectionCard, + renderStoredSecretControls, + setChoiceFieldValue, + handleDiscoverMongoMembers, +}) => ( + <> + {renderConfigSectionCard({ + sectionKey: "connectionMode", + icon: , + children: renderChoiceCards({ + fieldName: "mongoTopology", + value: String(mongoTopology), + options: [ + { + value: "single", + label: "单机模式", + description: "只连接一个 MongoDB 节点。", + }, + { + value: "replica", + label: "副本集 / 多节点", + description: "配置副本集名称和多个候选节点。", + }, + ], + }), + })} + + {renderConfigSectionCard({ + sectionKey: "mongoDiscovery", + icon: , + children: ( + <> + +
+ {[ + { + value: false, + label: "标准地址", + description: "使用 host:port 直连或副本集节点列表。", + }, + { + value: true, + label: "SRV 地址", + description: "使用 mongodb+srv,由 DNS 发现目标节点。", + }, + ].map((option) => { + const active = mongoSrv === option.value; + return ( + + ); + })} +
+ {mongoSrv && useSSH && ( + + )} + + ), + })} + + {mongoTopology === "replica" && + renderConfigSectionCard({ + sectionKey: "replica", + icon: , + children: ( + <> + + + + + + + + + + + {renderStoredSecretControls({ + fieldName: "mongoReplicaPassword", + clearKey: "mongoReplicaPassword", + hasStoredSecret: initialValues?.hasMongoReplicaPassword, + clearLabel: "清除已保存副本集密码", + description: + "当前已保存副本集密码。留空表示继续沿用,输入新值表示替换。", + })} + + + + {mongoMembers.length > 0 && ( +
record.host} + pagination={false} + dataSource={mongoMembers} + style={{ marginBottom: 12 }} + columns={[ + { title: "Host", dataIndex: "host", width: "48%" }, + { + title: "角色", + dataIndex: "role", + width: "32%", + render: (value: string, record: MongoMemberInfo) => ( + + {value || "UNKNOWN"} + + ), + }, + { + title: "健康", + dataIndex: "healthy", + width: "20%", + render: (value: boolean) => ( + + {value ? "正常" : "异常"} + + ), + }, + ]} + /> + )} + + ), + })} + + {renderConfigSectionCard({ + sectionKey: "mongoPolicy", + icon: , + children: ( +
+ + + +
+ 读偏好 (readPreference) + {renderChoiceCards({ + fieldName: "mongoReadPreference", + value: String(mongoReadPreference), + minWidth: 130, + options: [ + { + value: "primary", + label: "primary", + description: "只读主节点。", + }, + { + value: "primaryPreferred", + label: "primaryPreferred", + description: "主节点优先。", + }, + { + value: "secondary", + label: "secondary", + description: "只读从节点。", + }, + { + value: "secondaryPreferred", + label: "secondaryPreferred", + description: "从节点优先。", + }, + { + value: "nearest", + label: "nearest", + description: "选择最近节点。", + }, + ], + })} +
+
+ ), + })} + +); + +export default ConnectionModalMongoSections;