feat(connection-import): 支持导入 Navicat NCX 连接与密码

- 新增 Navicat NCX XML 解析与 v1/v2 密码解密能力
- 接入后端连接导入链路并补充导入失败提示
- 前端补充 NCX 格式识别、缺失密码提示与定向测试
This commit is contained in:
Syngnat
2026-06-07 13:38:53 +08:00
parent ace6e18da8
commit 79c5bfb3d4
6 changed files with 723 additions and 3 deletions

View File

@@ -2058,14 +2058,14 @@ function App() {
const importKind = detectConnectionImportKind(raw);
if (importKind === 'invalid') {
void message.error('文件格式错误:仅支持 GoNavi 恢复包、历史 JSON 连接数组MySQL Workbench XML');
void message.error('文件格式错误:仅支持 GoNavi 恢复包、历史 JSON 连接数组MySQL Workbench XML 或 Navicat NCX');
return;
}
try {
setPendingConnectionImportPayload(null);
const importedViews = await importConnectionsPayload(raw, '');
if (importKind === 'mysql-workbench-xml' && importedViews.some(v => !v.hasPrimaryPassword)) {
if ((importKind === 'mysql-workbench-xml' || importKind === 'navicat-ncx') && importedViews.some(v => !v.hasPrimaryPassword)) {
void message.warning(`成功导入 ${importedViews.length} 个连接,部分连接未包含密码,请编辑对应连接并输入密码后保存`);
} else {
void message.success(`成功导入 ${importedViews.length} 个连接`);

View File

@@ -88,6 +88,13 @@ describe('connectionExport', () => {
]))).toBe('legacy-json');
});
it('detects Navicat NCX xml exports', () => {
expect(detectConnectionImportKind(`<?xml version="1.0" encoding="UTF-8"?>
<Connections>
<Connection ConnType="MYSQL" ConnectionName="Local MySQL" Host="127.0.0.1" Port="3306" UserName="root" Password="ABCD" SavePassword="true" />
</Connections>`)).toBe('navicat-ncx');
});
it('returns invalid for malformed or unsupported content', () => {
expect(detectConnectionImportKind('{not-json}')).toBe('invalid');
expect(detectConnectionImportKind(JSON.stringify({

View File

@@ -1,6 +1,6 @@
import type { ConnectionConfig, SavedConnection } from '../types';
export type ConnectionImportKind = 'app-managed-package' | 'encrypted-package' | 'legacy-json' | 'mysql-workbench-xml' | 'invalid';
export type ConnectionImportKind = 'app-managed-package' | 'encrypted-package' | 'legacy-json' | 'mysql-workbench-xml' | 'navicat-ncx' | 'invalid';
export type ConnectionPackageDialogSnapshot = {
open: boolean;
mode: 'export' | 'import';
@@ -109,10 +109,19 @@ const isMySQLWorkbenchXML = (raw: string): boolean => (
raw.includes('<data') && raw.includes('grt_format') && raw.includes('db.mgmt.Connection')
);
const isNavicatNCX = (raw: string): boolean => (
raw.includes('<Connection')
&& raw.includes('ConnType=')
&& raw.includes('ConnectionName=')
);
export const detectConnectionImportKind = (raw: unknown): ConnectionImportKind => {
if (typeof raw === 'string' && isMySQLWorkbenchXML(raw)) {
return 'mysql-workbench-xml';
}
if (typeof raw === 'string' && isNavicatNCX(raw)) {
return 'navicat-ncx';
}
const parsed = parseConnectionImportRaw(raw);