mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-11 17:19:45 +08:00
- 背景与问题 :以前没有支持官方工具mysqlworkbench的xml导入,现在支持了
- 变更点:新增mysqlworkbench的xml文件导入,并当没有密码时,提示用户,而不是直接使用空密码进行直接连接,更友好 - 影响范围:仅导入受到影响 - 验证方式:点击导入,用mysqlworkbench的xml进行导入即可
This commit is contained in:
@@ -2,8 +2,10 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -268,6 +270,21 @@ func (a *App) ImportConnectionsPayload(raw string, password string) ([]connectio
|
||||
return sanitizeSavedConnectionViews(views), nil
|
||||
}
|
||||
|
||||
if isMySQLWorkbenchXML(trimmed) {
|
||||
inputs, err := parseMySQLWorkbenchXML(trimmed)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析 MySQL Workbench XML 失败: %w", err)
|
||||
}
|
||||
if len(inputs) == 0 {
|
||||
return nil, fmt.Errorf("未在 XML 中找到有效的连接配置")
|
||||
}
|
||||
views, err := a.importSavedConnectionsAtomically(inputs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sanitizeSavedConnectionViews(views), nil
|
||||
}
|
||||
|
||||
var legacy []connection.LegacySavedConnection
|
||||
if err := json.Unmarshal([]byte(trimmed), &legacy); err != nil {
|
||||
return nil, errConnectionPackageUnsupported
|
||||
@@ -372,3 +389,126 @@ func (s connectionPackageImportRollbackSnapshot) restore(a *App) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- MySQL Workbench XML import ---
|
||||
|
||||
func isMySQLWorkbenchXML(content string) bool {
|
||||
return strings.Contains(content, "<data") && strings.Contains(content, "grt_format") && strings.Contains(content, "db.mgmt.Connection")
|
||||
}
|
||||
|
||||
// mysqlWorkbenchData is the root XML element.
|
||||
type mysqlWorkbenchData struct {
|
||||
XMLName xml.Name `xml:"data"`
|
||||
Value mysqlWorkbenchTopValue `xml:"value"`
|
||||
}
|
||||
|
||||
type mysqlWorkbenchTopValue struct {
|
||||
Values []mysqlWorkbenchConnection `xml:"value"`
|
||||
}
|
||||
|
||||
type mysqlWorkbenchConnection struct {
|
||||
StructName string `xml:"struct-name,attr"`
|
||||
Values []mysqlWorkbenchValue `xml:"value"`
|
||||
Links []mysqlWorkbenchLinkValue `xml:"link"`
|
||||
}
|
||||
|
||||
type mysqlWorkbenchValue struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Key string `xml:"key,attr"`
|
||||
StructName string `xml:"struct-name,attr"`
|
||||
Content string `xml:",chardata"`
|
||||
Children []mysqlWorkbenchValue `xml:"value"`
|
||||
}
|
||||
|
||||
type mysqlWorkbenchLinkValue struct {
|
||||
Key string `xml:"key,attr"`
|
||||
Content string `xml:",chardata"`
|
||||
}
|
||||
|
||||
func parseMySQLWorkbenchXML(content string) ([]connection.SavedConnectionInput, error) {
|
||||
var data mysqlWorkbenchData
|
||||
if err := xml.Unmarshal([]byte(content), &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var inputs []connection.SavedConnectionInput
|
||||
for _, conn := range data.Value.Values {
|
||||
if conn.StructName != "db.mgmt.Connection" {
|
||||
continue
|
||||
}
|
||||
|
||||
input := parseMySQLWorkbenchConnection(conn)
|
||||
inputs = append(inputs, input)
|
||||
}
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func parseMySQLWorkbenchConnection(conn mysqlWorkbenchConnection) connection.SavedConnectionInput {
|
||||
params := make(map[string]string)
|
||||
connName := ""
|
||||
driverKey := ""
|
||||
|
||||
for _, v := range conn.Values {
|
||||
key := strings.TrimSpace(v.Key)
|
||||
switch {
|
||||
case key == "name" && v.Type == "string":
|
||||
connName = strings.TrimSpace(v.Content)
|
||||
case key == "parameterValues" && v.Type == "dict":
|
||||
for _, child := range v.Children {
|
||||
childKey := strings.TrimSpace(child.Key)
|
||||
if childKey == "" {
|
||||
continue
|
||||
}
|
||||
params[childKey] = strings.TrimSpace(child.Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, link := range conn.Links {
|
||||
if strings.TrimSpace(link.Key) == "driver" {
|
||||
driverKey = strings.TrimSpace(link.Content)
|
||||
}
|
||||
}
|
||||
|
||||
host := params["hostName"]
|
||||
port := 3306
|
||||
if p, err := strconv.Atoi(params["port"]); err == nil && p > 0 {
|
||||
port = p
|
||||
}
|
||||
user := params["userName"]
|
||||
schema := params["schema"]
|
||||
password := params["password"]
|
||||
|
||||
useSSL := false
|
||||
if v, err := strconv.Atoi(params["useSSL"]); err == nil && v > 0 {
|
||||
useSSL = true
|
||||
}
|
||||
|
||||
dbType := "mysql"
|
||||
if strings.Contains(driverKey, "mariadb") {
|
||||
dbType = "mariadb"
|
||||
}
|
||||
|
||||
connID := "conn-" + uuid.New().String()[:8]
|
||||
|
||||
config := connection.ConnectionConfig{
|
||||
ID: connID,
|
||||
Type: dbType,
|
||||
Host: host,
|
||||
Port: port,
|
||||
User: user,
|
||||
Password: password,
|
||||
Database: schema,
|
||||
UseSSL: useSSL,
|
||||
}
|
||||
|
||||
if connName == "" {
|
||||
connName = fmt.Sprintf("%s@%s:%d", user, host, port)
|
||||
}
|
||||
|
||||
return connection.SavedConnectionInput{
|
||||
ID: connID,
|
||||
Name: connName,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,14 @@ func (a *App) resolveConnectionSecrets(config connection.ConnectionConfig) (conn
|
||||
}
|
||||
resolved := mergeConnectionSecretBundleIntoConfig(base, bundle)
|
||||
resolved.ID = view.ID
|
||||
|
||||
if !connectionConfigCarriesInlineSecrets(config) && !bundle.hasAny() {
|
||||
_, dailyExists, _ := repo.dailySecrets().GetConnection(view.ID)
|
||||
if !dailyExists {
|
||||
return resolved, fmt.Errorf("未找到当前连接对应的已保存密文,请编辑当前连接,并输入密码后保存")
|
||||
}
|
||||
}
|
||||
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
@@ -102,9 +110,9 @@ func normalizeConnectionSecretResolutionError(config connection.ConnectionConfig
|
||||
if connectionMetadataLooksEmpty(config) {
|
||||
return fmt.Errorf("未找到已保存连接,可能已被删除,请刷新后重试")
|
||||
}
|
||||
return fmt.Errorf("未找到当前连接对应的已保存密文,请重新填写密码并保存后再试")
|
||||
return fmt.Errorf("未找到当前连接对应的已保存密文,请编辑当前连接,并输入密码后保存")
|
||||
case errors.Is(err, os.ErrNotExist):
|
||||
return fmt.Errorf("未找到当前连接对应的已保存密文,请重新填写密码并保存后再试")
|
||||
return fmt.Errorf("未找到当前连接对应的已保存密文,请编辑当前连接,并输入密码后保存")
|
||||
case strings.Contains(lower, "secret store unavailable"):
|
||||
return fmt.Errorf("系统密文存储当前不可用,请检查系统钥匙串或凭据管理器后再试")
|
||||
default:
|
||||
|
||||
@@ -290,6 +290,10 @@ func (a *App) ImportConfigFile() connection.QueryResult {
|
||||
DisplayName: "JSON Files (*.json)",
|
||||
Pattern: "*.json",
|
||||
},
|
||||
{
|
||||
DisplayName: "MySQL Workbench Connections (*.xml)",
|
||||
Pattern: "*.xml",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user