mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-11 18:29:44 +08:00
错误消息中文化: - 19 个驱动实现文件中 connection not open / table name required 等英文消息替换为中文 - methods_file.go / methods_db.go / methods_driver.go 英文消息中文化 - 前端 App/Sidebar/DataGrid/ConnectionModal/DriverManagerModal 同步替换 "Cancelled" → "已取消" 文档与测试: - database.go Database/BatchApplier 接口、types.go 12 个类型、driver_support.go 导出函数补充中文 godoc - 新增 logger_test.go(ErrorChain 5 个用例)和 methods_db_conn_test.go(连接管理 7 个用例) Bug 修复: - DataGrid: 将 liveTargets 空检查移至非虚拟路径,修复外部横向滚动条拖动时内容不跟随 - vite.config.ts: server.host 指定 127.0.0.1,修复 IPv6 回环被拦截导致 Wails 代理 502 基础设施: - .gitignore 新增 .gemini/ 规则,tmpclaude-* 改为 **/tmpclaude-* 覆盖子目录
235 lines
6.6 KiB
Go
235 lines
6.6 KiB
Go
package db
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
// coreBuiltinDrivers 是始终内置可用的核心驱动,无需额外安装即可使用。
|
||
var coreBuiltinDrivers = map[string]struct{}{
|
||
"mysql": {},
|
||
"redis": {},
|
||
"oracle": {},
|
||
"postgres": {},
|
||
}
|
||
|
||
// optionalGoDrivers 表示需要用户“安装启用”后才能使用的纯 Go 驱动。
|
||
// 注意:这是一种运行时门控(installed.json 标记),并不减少主二进制体积。
|
||
var optionalGoDrivers = map[string]struct{}{
|
||
"mariadb": {},
|
||
"diros": {},
|
||
"sphinx": {},
|
||
"sqlserver": {},
|
||
"sqlite": {},
|
||
"duckdb": {},
|
||
"dameng": {},
|
||
"kingbase": {},
|
||
"highgo": {},
|
||
"vastbase": {},
|
||
"mongodb": {},
|
||
"tdengine": {},
|
||
"clickhouse": {},
|
||
}
|
||
|
||
var (
|
||
externalDriverDirMu sync.RWMutex
|
||
externalDriverDir string
|
||
)
|
||
|
||
func normalizeRuntimeDriverType(driverType string) string {
|
||
normalized := strings.ToLower(strings.TrimSpace(driverType))
|
||
switch normalized {
|
||
case "doris":
|
||
return "diros"
|
||
case "postgresql":
|
||
return "postgres"
|
||
default:
|
||
return normalized
|
||
}
|
||
}
|
||
|
||
func driverDisplayName(driverType string) string {
|
||
switch normalizeRuntimeDriverType(driverType) {
|
||
case "mysql":
|
||
return "MySQL"
|
||
case "oracle":
|
||
return "Oracle"
|
||
case "redis":
|
||
return "Redis"
|
||
case "mariadb":
|
||
return "MariaDB"
|
||
case "diros":
|
||
return "Doris"
|
||
case "sphinx":
|
||
return "Sphinx"
|
||
case "postgres":
|
||
return "PostgreSQL"
|
||
case "sqlserver":
|
||
return "SQL Server"
|
||
case "sqlite":
|
||
return "SQLite"
|
||
case "duckdb":
|
||
return "DuckDB"
|
||
case "dameng":
|
||
return "Dameng"
|
||
case "kingbase":
|
||
return "Kingbase"
|
||
case "highgo":
|
||
return "HighGo"
|
||
case "vastbase":
|
||
return "Vastbase"
|
||
case "mongodb":
|
||
return "MongoDB"
|
||
case "tdengine":
|
||
return "TDengine"
|
||
case "clickhouse":
|
||
return "ClickHouse"
|
||
default:
|
||
return strings.ToUpper(strings.TrimSpace(driverType))
|
||
}
|
||
}
|
||
|
||
// IsOptionalGoDriver 返回指定驱动类型是否为可选的纯 Go 驱动。
|
||
// 可选驱动需要用户在驱动管理界面点击“安装启用”后才能使用。
|
||
func IsOptionalGoDriver(driverType string) bool {
|
||
_, ok := optionalGoDrivers[normalizeRuntimeDriverType(driverType)]
|
||
return ok
|
||
}
|
||
|
||
func IsOptionalGoDriverBuildIncluded(driverType string) bool {
|
||
return optionalGoDriverBuildIncluded(normalizeRuntimeDriverType(driverType))
|
||
}
|
||
|
||
// IsBuiltinDriver 返回指定驱动类型是否为核心内置驱动(始终可用,无需安装)。
|
||
func IsBuiltinDriver(driverType string) bool {
|
||
_, ok := coreBuiltinDrivers[normalizeRuntimeDriverType(driverType)]
|
||
return ok
|
||
}
|
||
|
||
func defaultExternalDriverDownloadDirectory() string {
|
||
if home, err := os.UserHomeDir(); err == nil && strings.TrimSpace(home) != "" {
|
||
return filepath.Join(home, ".gonavi", "drivers")
|
||
}
|
||
if wd, err := os.Getwd(); err == nil && strings.TrimSpace(wd) != "" {
|
||
return filepath.Join(wd, ".gonavi-drivers")
|
||
}
|
||
return ".gonavi-drivers"
|
||
}
|
||
|
||
func resolveExternalDriverRoot(downloadDir string) (string, error) {
|
||
root := strings.TrimSpace(downloadDir)
|
||
if root == "" {
|
||
root = currentExternalDriverDownloadDirectory()
|
||
}
|
||
if root == "" {
|
||
root = defaultExternalDriverDownloadDirectory()
|
||
}
|
||
if !filepath.IsAbs(root) {
|
||
abs, err := filepath.Abs(root)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
root = abs
|
||
}
|
||
if err := os.MkdirAll(root, 0o755); err != nil {
|
||
return "", fmt.Errorf("创建驱动目录失败:%w", err)
|
||
}
|
||
return root, nil
|
||
}
|
||
|
||
func currentExternalDriverDownloadDirectory() string {
|
||
externalDriverDirMu.RLock()
|
||
current := strings.TrimSpace(externalDriverDir)
|
||
externalDriverDirMu.RUnlock()
|
||
if current != "" {
|
||
return current
|
||
}
|
||
return defaultExternalDriverDownloadDirectory()
|
||
}
|
||
|
||
// SetExternalDriverDownloadDirectory 设置可选驱动的下载存储目录。
|
||
// 如果路径解析失败,会回退到默认目录(~/.gonavi/drivers)。
|
||
func SetExternalDriverDownloadDirectory(downloadDir string) {
|
||
root, err := resolveExternalDriverRoot(downloadDir)
|
||
if err != nil {
|
||
root = defaultExternalDriverDownloadDirectory()
|
||
}
|
||
externalDriverDirMu.Lock()
|
||
externalDriverDir = root
|
||
externalDriverDirMu.Unlock()
|
||
}
|
||
|
||
func ResolveExternalDriverRoot(downloadDir string) (string, error) {
|
||
return resolveExternalDriverRoot(downloadDir)
|
||
}
|
||
|
||
func ResolveOptionalGoDriverMarkerPath(downloadDir string, driverType string) (string, error) {
|
||
normalized := normalizeRuntimeDriverType(driverType)
|
||
if !IsOptionalGoDriver(normalized) {
|
||
return "", fmt.Errorf("%s 不是可选 Go 驱动", driverDisplayName(normalized))
|
||
}
|
||
root, err := resolveExternalDriverRoot(downloadDir)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return filepath.Join(root, normalized, "installed.json"), nil
|
||
}
|
||
|
||
func optionalGoDriverInstalled(driverType string) bool {
|
||
markerPath, err := ResolveOptionalGoDriverMarkerPath("", driverType)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
info, statErr := os.Stat(markerPath)
|
||
return statErr == nil && !info.IsDir()
|
||
}
|
||
|
||
func optionalGoDriverRuntimeReady(driverType string) (bool, string) {
|
||
normalized := normalizeRuntimeDriverType(driverType)
|
||
if !IsOptionalGoDriver(normalized) {
|
||
return true, ""
|
||
}
|
||
executablePath, err := ResolveOptionalDriverAgentExecutablePath("", normalized)
|
||
if err != nil {
|
||
return false, fmt.Sprintf("%s 驱动代理路径解析失败,请在驱动管理中重新安装启用", driverDisplayName(normalized))
|
||
}
|
||
info, statErr := os.Stat(executablePath)
|
||
if statErr != nil || info.IsDir() {
|
||
return false, fmt.Sprintf("%s 驱动代理缺失,请在驱动管理中重新安装启用", driverDisplayName(normalized))
|
||
}
|
||
if validateErr := ValidateOptionalDriverAgentExecutable(normalized, executablePath); validateErr != nil {
|
||
return false, fmt.Sprintf("%s;请在驱动管理中重新安装启用", validateErr.Error())
|
||
}
|
||
return true, ""
|
||
}
|
||
|
||
// DriverRuntimeSupportStatus 返回当前构建下驱动是否可用(可直接用于连接)。
|
||
func DriverRuntimeSupportStatus(driverType string) (bool, string) {
|
||
normalized := normalizeRuntimeDriverType(driverType)
|
||
if normalized == "" {
|
||
return false, "未识别的数据源类型"
|
||
}
|
||
if normalized == "custom" {
|
||
return true, ""
|
||
}
|
||
if IsBuiltinDriver(normalized) {
|
||
return true, ""
|
||
}
|
||
if IsOptionalGoDriver(normalized) {
|
||
if !IsOptionalGoDriverBuildIncluded(normalized) {
|
||
return false, fmt.Sprintf("%s 当前发行包为精简构建,未内置该驱动;如需使用请安装 Full 版", driverDisplayName(normalized))
|
||
}
|
||
if optionalGoDriverInstalled(normalized) {
|
||
if ready, reason := optionalGoDriverRuntimeReady(normalized); !ready {
|
||
return false, reason
|
||
}
|
||
return true, ""
|
||
}
|
||
return false, fmt.Sprintf("%s 纯 Go 驱动未启用,请先在驱动管理中点击“安装启用”", driverDisplayName(normalized))
|
||
}
|
||
return true, ""
|
||
}
|