package config import ( "fmt" "os" "path/filepath" "gopkg.in/yaml.v3" ) func (c *Config) ToFile() File { if c == nil { return File{} } return File{ Listen: ListenFile{Host: c.ListenHost, Port: c.ListenPort}, LocalAuth: LocalAuthFile{Username: c.LocalUsername, Password: c.LocalPassword}, Tunnel: TunnelFile{ URL: c.TunnelURL, EncodeBase64: c.EncodeBase64, TLSInsecureSkipVerify: c.TLSInsecureSkipVerify, }, Remote: RemoteFile{ Host: c.RemoteHost, Port: c.RemotePort, Username: c.RemoteUsername, Password: c.RemotePassword, Database: c.RemoteDatabase, Charset: c.RemoteCharset, }, Options: OptionsFile{ RequestTimeout: int(c.RequestTimeout.Seconds()), MaxConnections: c.MaxConnections, LogLevel: c.LogLevel, LogSQL: c.LogSQL, LogFile: relativeLogFile(c.Path, c.LogFile), }, } } func relativeLogFile(cfgPath, logFile string) string { if logFile == "" { return DefaultLogFile } if !filepath.IsAbs(logFile) { return filepath.ToSlash(filepath.Clean(logFile)) } dir := filepath.Dir(cfgPath) rel, err := filepath.Rel(dir, logFile) if err != nil { return logFile } return filepath.ToSlash(rel) } func Save(path string, c *Config) error { if path == "" { return fmt.Errorf("config path is empty") } if dir := filepath.Dir(path); dir != "" && dir != "." { if err := os.MkdirAll(dir, 0o750); err != nil { return err } } raw, err := yaml.Marshal(c.ToFile()) if err != nil { return err } out := append([]byte("# Written by mysql-tunnel. Contains secrets; do not commit.\n"), raw...) return os.WriteFile(path, out, 0o600) } func DefaultSavePath() string { if exe, err := os.Executable(); err == nil { return filepath.Join(filepath.Dir(exe), "config.yaml") } return "config.yaml" } // LoadOrEmpty returns a parsed config, or defaults if the file is missing or invalid. func LoadOrEmpty(path string) *Config { if path == "" { path = DefaultSavePath() } if _, err := os.Stat(path); err != nil { c := Defaults() c.Path = path return &c } cfg, err := Parse(path, Overrides{}) if err != nil { c := Defaults() c.Path = path c.Warnings = append(c.Warnings, err.Error()) return &c } cfg.ResolvePaths() return cfg }