🐛 fix(driver): 明确JDBC Jar导入限制并补充Kingbase指引

- 后端在驱动包选择与本地导入前拦截 JDBC Jar,并返回替代说明
- 驱动管理统一改为“导入驱动包”,补充不支持 JDBC Jar 的提示
- 自定义连接补充 kingbase8 等驱动别名与 Go 驱动说明
- 新增后端与前端回归测试

Refs #317
This commit is contained in:
Syngnat
2026-04-17 20:41:58 +08:00
parent 0bccdeed8c
commit f3b78f9763
6 changed files with 97 additions and 5 deletions

View File

@@ -12,6 +12,7 @@ import {
} from '../utils/connectionModalPresentation';
import { resolveConnectionSecretDraft } from '../utils/connectionSecretDraft';
import { getCustomConnectionDsnValidationMessage } from '../utils/customConnectionDsn';
import { CUSTOM_CONNECTION_DRIVER_HELP } from '../utils/driverImportGuidance';
import { applyNoAutoCapAttributes, noAutoCapInputProps } from '../utils/inputAutoCap';
import { DBGetDatabases, GetDriverStatusList, MongoDiscoverMembers, TestConnection, RedisConnect, SelectDatabaseFile, SelectSSHKeyFile } from '../../wailsjs/go/app/App';
import { ConnectionConfig, MongoMemberInfo, SavedConnection } from '../types';
@@ -2163,7 +2164,7 @@ const ConnectionModal: React.FC<{
{isCustom ? (
<>
<Form.Item name="driver" label="驱动名称 (Driver Name)" rules={[{ required: true, message: '请输入驱动名称' }]} help="已支持: mysql, postgres, sqlite, oracle, dm, kingbase">
<Form.Item name="driver" label="驱动名称 (Driver Name)" rules={[{ required: true, message: '请输入驱动名称' }]} help={CUSTOM_CONNECTION_DRIVER_HELP}>
<Input {...noAutoCapInputProps} placeholder="例如: mysql, postgres" />
</Form.Item>
<Form.Item name="dsn" label="连接字符串 (DSN)" rules={[createCustomDsnRule()]}>

View File

@@ -4,6 +4,11 @@ import { DeleteOutlined, DownloadOutlined, FileSearchOutlined, FolderOpenOutline
import { EventsOn } from '../../wailsjs/runtime/runtime';
import { useStore } from '../store';
import { normalizeOpacityForPlatform, resolveAppearanceValues } from '../utils/appearance';
import {
DRIVER_LOCAL_IMPORT_BUTTON_LABEL,
DRIVER_LOCAL_IMPORT_DIRECTORY_HELP,
DRIVER_LOCAL_IMPORT_SINGLE_FILE_HELP,
} from '../utils/driverImportGuidance';
import {
CheckDriverNetworkStatus,
DownloadDriverPackage,
@@ -1171,7 +1176,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
loading={loadingLocal}
onClick={() => installDriverFromLocalFile(row)}
>
{DRIVER_LOCAL_IMPORT_BUTTON_LABEL}
</Button>
<Button
type={hasLogs ? 'default' : 'text'}
@@ -1373,8 +1378,8 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
children: (
<Space direction="vertical" size={6} style={{ width: '100%' }}>
<Text type="secondary"></Text>
<Text type="secondary">使</Text>
<Text type="secondary">/ `mariadb-driver-agent``mariadb-driver-agent.exe``GoNavi-DriverAgents.zip`使</Text>
<Text type="secondary">{DRIVER_LOCAL_IMPORT_DIRECTORY_HELP}</Text>
<Text type="secondary">{DRIVER_LOCAL_IMPORT_SINGLE_FILE_HELP}</Text>
<Paragraph copyable={{ text: downloadDir || '-' }} style={{ marginBottom: 0 }}>
{downloadDir || '-'}
</Paragraph>

View File

@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest';
import {
CUSTOM_CONNECTION_DRIVER_HELP,
DRIVER_LOCAL_IMPORT_BUTTON_LABEL,
DRIVER_LOCAL_IMPORT_DIRECTORY_HELP,
DRIVER_LOCAL_IMPORT_SINGLE_FILE_HELP,
} from './driverImportGuidance';
describe('driver import guidance', () => {
it('keeps local import copy focused on driver packages instead of JDBC jars', () => {
expect(DRIVER_LOCAL_IMPORT_BUTTON_LABEL).toBe('导入驱动包');
expect(DRIVER_LOCAL_IMPORT_DIRECTORY_HELP).toContain('导入驱动目录');
expect(DRIVER_LOCAL_IMPORT_SINGLE_FILE_HELP).toContain('JDBC Jar');
});
it('documents custom driver aliases for kingbase and related fallbacks', () => {
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('kingbase8');
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('pgx');
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('JDBC Jar');
});
});

View File

@@ -0,0 +1,10 @@
export const DRIVER_LOCAL_IMPORT_BUTTON_LABEL = '导入驱动包';
export const DRIVER_LOCAL_IMPORT_DIRECTORY_HELP =
'如果应用内下载链路失败,可先手动下载驱动包到该目录,再使用“导入驱动包”或“导入驱动目录”完成安装。';
export const DRIVER_LOCAL_IMPORT_SINGLE_FILE_HELP =
'行内“导入驱动包”仅用于单个驱动文件/总包(如 `mariadb-driver-agent`、`mariadb-driver-agent.exe`、`GoNavi-DriverAgents.zip`),不支持直接导入 JDBC Jar批量导入请使用上方“导入驱动目录”。';
export const CUSTOM_CONNECTION_DRIVER_HELP =
'已支持: mysql, postgres, sqlite, oracle, dm, kingbase别名支持 postgresql/pgx、dm8、kingbase8/kingbasees/kingbasev8。当前不支持通过 JDBC Jar 扩展驱动。';

View File

@@ -486,6 +486,17 @@ func (a *App) SelectDriverDownloadDirectory(currentDir string) connection.QueryR
}
}
func validateLocalDriverPackagePath(path string) error {
pathText := strings.TrimSpace(path)
if pathText == "" {
return nil
}
if strings.EqualFold(filepath.Ext(pathText), ".jar") {
return fmt.Errorf("当前驱动管理不支持直接导入 JDBC Jar。GoNavi 使用 Go 驱动与可选 driver-agent请改用驱动包/驱动目录如需连接人大金仓请优先使用“Kingbase”连接类型或在自定义连接中填写 kingbase / kingbase8")
}
return nil
}
func (a *App) SelectDriverPackageFile(currentPath string) connection.QueryResult {
defaultDir := strings.TrimSpace(currentPath)
if defaultDir == "" {
@@ -501,7 +512,7 @@ func (a *App) SelectDriverPackageFile(currentPath string) connection.QueryResult
}
selection, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Title: "选择驱动包文件",
Title: "选择驱动包文件(非 JDBC Jar",
DefaultDirectory: defaultDir,
})
if err != nil {
@@ -514,6 +525,9 @@ func (a *App) SelectDriverPackageFile(currentPath string) connection.QueryResult
if abs, err := filepath.Abs(selection); err == nil {
selection = abs
}
if err := validateLocalDriverPackagePath(selection); err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}
return connection.QueryResult{Success: true, Data: map[string]interface{}{"path": selection}}
}
@@ -900,6 +914,9 @@ func (a *App) InstallLocalDriverPackage(driverType string, filePath string, down
if definition.BuiltIn {
return connection.QueryResult{Success: false, Message: "内置驱动无需安装扩展包"}
}
if err := validateLocalDriverPackagePath(filePath); err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}
if err := ensureOptionalDriverBuildAvailable(definition); err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}

View File

@@ -0,0 +1,37 @@
package app
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestValidateLocalDriverPackagePathRejectsJdbcJar(t *testing.T) {
err := validateLocalDriverPackagePath(filepath.Join("tmp", "kingbase8.JAR"))
if err == nil {
t.Fatal("expected JDBC jar path to be rejected")
}
if !strings.Contains(err.Error(), "JDBC Jar") {
t.Fatalf("expected JDBC jar hint, got %q", err.Error())
}
if err := validateLocalDriverPackagePath(filepath.Join("tmp", "kingbase-driver-agent.zip")); err != nil {
t.Fatalf("expected zip package to stay supported, got %v", err)
}
}
func TestInstallLocalDriverPackageRejectsJdbcJarBeforeBuildChecks(t *testing.T) {
jarPath := filepath.Join(t.TempDir(), "kingbase8.jar")
if err := os.WriteFile(jarPath, []byte("fake-jar"), 0o644); err != nil {
t.Fatalf("write temp jar: %v", err)
}
app := &App{}
result := app.InstallLocalDriverPackage("kingbase", jarPath, t.TempDir(), "")
if result.Success {
t.Fatal("expected local jar import to fail")
}
if !strings.Contains(result.Message, "JDBC Jar") {
t.Fatalf("expected JDBC jar guidance, got %q", result.Message)
}
}