feat: split workbench core mysql access

This commit is contained in:
Your Name
2026-10-06 23:44:37 +08:00
parent 71b49ddcf7
commit 8d32376de8
6 changed files with 245 additions and 77 deletions
+15
View File
@@ -15,6 +15,7 @@ import (
type Config struct {
Listen string `json:"listen"`
MySQL MySQLConfig `json:"mysql"`
CoreMySQL MySQLConfig `json:"core_mysql"`
Redis RedisConfig `json:"redis"`
Kafka KafkaConfig `json:"kafka"`
Auth AuthConfig `json:"auth"`
@@ -155,6 +156,16 @@ func applyEnv(cfg *Config) {
setString(&cfg.MySQL.DSN, "IM_WORKBENCH_MYSQL_DSN")
setInt(&cfg.MySQL.MaxOpenConns, "IM_WORKBENCH_MYSQL_MAX_OPEN_CONNS")
setInt(&cfg.MySQL.MaxIdleConns, "IM_WORKBENCH_MYSQL_MAX_IDLE_CONNS")
setString(&cfg.CoreMySQL.DSN, "IM_WORKBENCH_CORE_MYSQL_DSN")
if strings.TrimSpace(cfg.CoreMySQL.DSN) == "" {
cfg.CoreMySQL.DSN = cfg.MySQL.DSN
}
if cfg.CoreMySQL.MaxOpenConns == 0 {
cfg.CoreMySQL.MaxOpenConns = cfg.MySQL.MaxOpenConns
}
if cfg.CoreMySQL.MaxIdleConns == 0 {
cfg.CoreMySQL.MaxIdleConns = cfg.MySQL.MaxIdleConns
}
setString(&cfg.Redis.Addr, "IM_WORKBENCH_REDIS_ADDR")
setString(&cfg.Redis.Username, "IM_WORKBENCH_REDIS_USERNAME")
setString(&cfg.Redis.Password, "IM_WORKBENCH_REDIS_PASSWORD")
@@ -240,6 +251,10 @@ func (c *Config) validate() error {
if strings.EqualFold(strings.TrimSpace(parsedDSN.DBName), "tinode") {
return errors.New("mysql database tinode is deprecated; use workbench")
}
coreMySQLDSN := strings.TrimPrefix(strings.TrimSpace(c.CoreMySQL.DSN), "mysql://")
if _, err := mysqlDriver.ParseDSN(coreMySQLDSN); err != nil {
return fmt.Errorf("core mysql dsn is invalid: %w", err)
}
if strings.TrimSpace(c.Redis.Addr) == "" && !c.Auth.SkipSessionInDev {
return errors.New("redis addr is required")
}
+59
View File
@@ -1,6 +1,8 @@
package config
import (
"os"
"path/filepath"
"strings"
"testing"
)
@@ -39,3 +41,60 @@ func TestApplyEnvOverridesMySQLPool(t *testing.T) {
t.Fatalf("mysql pool = %d/%d, want 12/4", cfg.MySQL.MaxOpenConns, cfg.MySQL.MaxIdleConns)
}
}
func TestLoadFallsBackCoreMySQLToPrimaryDSN(t *testing.T) {
path := filepath.Join(t.TempDir(), "workbench.json")
if err := os.WriteFile(path, []byte(`{"mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/workbench?parseTime=true"}}`), 0o600); err != nil {
t.Fatal(err)
}
t.Setenv("IM_WORKBENCH_CONFIG", path)
t.Setenv("IM_WORKBENCH_MYSQL_DSN", "")
t.Setenv("IM_WORKBENCH_CORE_MYSQL_DSN", "")
cfg, err := Load()
if err != nil {
t.Fatal(err)
}
if cfg.CoreMySQL.DSN != cfg.MySQL.DSN {
t.Fatalf("core_mysql.dsn = %q, want primary mysql.dsn %q", cfg.CoreMySQL.DSN, cfg.MySQL.DSN)
}
}
func TestLoadUsesCoreMySQLDSNFromEnvironment(t *testing.T) {
path := filepath.Join(t.TempDir(), "workbench.json")
if err := os.WriteFile(path, []byte(`{"mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/workbench?parseTime=true"}}`), 0o600); err != nil {
t.Fatal(err)
}
t.Setenv("IM_WORKBENCH_CONFIG", path)
t.Setenv("IM_WORKBENCH_MYSQL_DSN", "")
t.Setenv("IM_WORKBENCH_CORE_MYSQL_DSN", "root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true")
cfg, err := Load()
if err != nil {
t.Fatal(err)
}
if !strings.Contains(cfg.CoreMySQL.DSN, "/meeting_im?") {
t.Fatalf("core_mysql.dsn = %q, want meeting_im", cfg.CoreMySQL.DSN)
}
}
func TestLoadPreservesCoreMySQLDSNFromJSON(t *testing.T) {
path := filepath.Join(t.TempDir(), "workbench.json")
if err := os.WriteFile(path, []byte(`{"mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/workbench?parseTime=true"},"core_mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true"}}`), 0o600); err != nil {
t.Fatal(err)
}
t.Setenv("IM_WORKBENCH_CONFIG", path)
t.Setenv("IM_WORKBENCH_MYSQL_DSN", "")
t.Setenv("IM_WORKBENCH_CORE_MYSQL_DSN", "")
cfg, err := Load()
if err != nil {
t.Fatal(err)
}
if !strings.Contains(cfg.MySQL.DSN, "/workbench?") {
t.Fatalf("mysql.dsn = %q, want workbench", cfg.MySQL.DSN)
}
if !strings.Contains(cfg.CoreMySQL.DSN, "/meeting_im?") {
t.Fatalf("core_mysql.dsn = %q, want meeting_im", cfg.CoreMySQL.DSN)
}
}