diff --git a/go-client/config.json b/go-client/config.json
index 48160403..b5a260d6 100644
--- a/go-client/config.json
+++ b/go-client/config.json
@@ -496,6 +496,27 @@
"is_internal": false,
"is_default": false,
"is_set": false
+ },
+ {
+ "name": "mysqlworkbench",
+ "display_name": "MySQL Workbench",
+ "protocol": [
+ "mysql"
+ ],
+ "comment": {
+ "zh": "MySQL Workbench 是一款专为 MySQL 设计的集成可视化工具。\n\n!!!手动下载安装,点击保存启用!!!",
+ "en": "MySQL Workbench is a unified visual tool for database architects, developers, and DBAs.\n\n!!!Manually download and install, click Save to activate!!!"
+ },
+ "download_url": "https://dev.mysql.com/downloads/workbench/",
+ "type": "databases",
+ "path": "C:\\Program Files\\MySQL\\MySQL Workbench 8.0 CE\\MySQLWorkbench.exe",
+ "arg_format": "",
+ "match_first": [
+ "mysql"
+ ],
+ "is_internal": false,
+ "is_default": false,
+ "is_set": false
}
]
},
@@ -894,4 +915,4 @@
}
]
}
-}
+}
\ No newline at end of file
diff --git a/go-client/pkg/awaken/awaken_windows.go b/go-client/pkg/awaken/awaken_windows.go
index a7e0fd13..a9bd5d29 100755
--- a/go-client/pkg/awaken/awaken_windows.go
+++ b/go-client/pkg/awaken/awaken_windows.go
@@ -1,6 +1,7 @@
package awaken
import (
+ "crypto/rand"
"encoding/json"
"fmt"
"go-client/global"
@@ -25,6 +26,14 @@ func EnsureDirExist(path string) {
}
}
+func fileExists(filename string) bool {
+ info, err := os.Stat(filename)
+ if os.IsNotExist(err) {
+ return false
+ }
+ return !info.IsDir()
+}
+
func getNavicatURL(connectInfo map[string]string) string {
re := regexp.MustCompile(`@(.+)$`)
matches := re.FindStringSubmatch(connectInfo["name"])
@@ -293,6 +302,83 @@ func handleSSH(r *Rouse, cfg *config.AppConfig) *exec.Cmd {
}
}
+// prepareWorkbenchConnection writes a minimal connection entry into MySQL Workbench's
+// connections.xml so that the connection succeeds without OS mismatch warnings.
+func prepareWorkbenchConnection(connName, host, port, username string) error {
+ dir, err := os.UserConfigDir()
+ if err != nil {
+ return fmt.Errorf("failed to get user config dir: %w", err)
+ }
+ wbDir := filepath.Join(dir, "MySQL", "Workbench")
+ EnsureDirExist(wbDir)
+ connFile := filepath.Join(wbDir, "connections.xml")
+
+ // Generate a UUID for the connection entry
+ b := make([]byte, 16)
+ rand.Read(b)
+ connID := fmt.Sprintf("{%08X-%04X-%04X-%04X-%012X}",
+ b[0:4], b[4:6], b[6:8], b[8:10], b[10:16])
+
+ // Build the minimal connection XML entry
+ connEntry := fmt.Sprintf(`
+
+ com.mysql.rdbms.mysql.driver.native
+ Mysql@%s:%s
+ 0
+
+
+ %s
+ %s
+ %s
+
+ %s
+ `,
+ connID, host, port, host, port, username, connName)
+
+ var data []byte
+ if !fileExists(connFile) {
+ content := fmt.Sprintf(`
+
+ %s
+
+`, connEntry)
+ data = []byte(content)
+ } else {
+ existing, err := ioutil.ReadFile(connFile)
+ if err != nil {
+ return fmt.Errorf("failed to read connections.xml: %w", err)
+ }
+ content := string(existing)
+
+ // Remove existing JumpServer connection if it exists
+ re := regexp.MustCompile(`(?s)]*>.*?JumpServer.*?`)
+ content = re.ReplaceAllString(content, "")
+
+ insertPos := strings.Index(content, "\n")
+ if insertPos == -1 {
+ insertPos = strings.Index(content, "\r\n")
+ }
+ if insertPos != -1 {
+ content = content[:insertPos] + connEntry + "\n " + content[insertPos:]
+ data = []byte(content)
+ } else {
+ content := fmt.Sprintf(`
+
+ %s
+
+`, connEntry)
+ data = []byte(content)
+ }
+ }
+
+ err = ioutil.WriteFile(connFile, data, 0644)
+ if err != nil {
+ return fmt.Errorf("failed to write connections.xml: %w", err)
+ }
+
+ return nil
+}
+
func handleDB(r *Rouse, cfg *config.AppConfig) *exec.Cmd {
var appItem *config.AppItem
appLst := cfg.Windows.Databases
@@ -375,6 +461,45 @@ func handleDB(r *Rouse, cfg *config.AppConfig) *exec.Cmd {
url := getNavicatURL(connectMap)
connectMap["url"] = url
}
+ if appItem.Name == "mysqlworkbench" {
+ connName := "JumpServer"
+ err := prepareWorkbenchConnection(connName, r.Host, strconv.Itoa(r.Port), r.getUserName())
+ if err != nil {
+ global.LOG.Error("Failed to prepare MySQL Workbench connection: " + err.Error())
+ return nil
+ }
+ global.LOG.Info("MySQL Workbench connection prepared: " + connName)
+
+ autoit.LoadAuto()
+
+ // Run Workbench with --query to directly open the connection tab
+ global.LOG.Info("Launching MySQL Workbench with query: " + connName)
+ autoit.Run(fmt.Sprintf(`"%s" --query "%s"`, appPath, connName))
+
+ // Wait for password prompt dialog
+ // The title is "Connect to MySQL Server"
+ pwdTitle := "[REGEXPTITLE:.*Connect to MySQL Server.*]"
+ active := false
+ for i := 0; i <= 30; i++ {
+ ret := autoit.WinWaitActive(pwdTitle, "", 1)
+ time.Sleep(300 * time.Millisecond)
+ if ret != 0 {
+ active = true
+ break
+ }
+ autoit.WinActivate(pwdTitle, "")
+ }
+ if active {
+ time.Sleep(500 * time.Millisecond)
+ // Send password in raw mode and press Enter
+ autoit.Send(r.Value, 1)
+ time.Sleep(100 * time.Millisecond)
+ autoit.Send("{ENTER}")
+ } else {
+ global.LOG.Error("MySQL Workbench password dialog not detected")
+ }
+ return exec.Command("cmd", "/C", "exit", "0")
+ }
if len(appItem.AutoIt) == 0 {
commands := getCommandFromArgs(connectMap, appItem.ArgFormat)
if appItem.Name == "heidisql" && r.Protocol == "postgresql" {
diff --git a/ui/assets/images/mysqlworkbench.png b/ui/assets/images/mysqlworkbench.png
new file mode 100644
index 00000000..8d928778
Binary files /dev/null and b/ui/assets/images/mysqlworkbench.png differ
diff --git a/ui/components/SettingItems/settingItems.vue b/ui/components/SettingItems/settingItems.vue
index 0f4995ad..f6654936 100644
--- a/ui/components/SettingItems/settingItems.vue
+++ b/ui/components/SettingItems/settingItems.vue
@@ -43,7 +43,8 @@ const imagesMap: Record = {
navicat17: getImageByName("navicat17"),
royalts: getImageByName("royalts"),
windows_rdm: getImageByName("windows_rdm"),
- toad: getImageByName("toad")
+ toad: getImageByName("toad"),
+ mysqlworkbench: getImageByName("mysqlworkbench")
};
const commentText = computed(() => {