feat: add precise diagnostics and optional client copy logging controls
This commit is contained in:
@@ -70,3 +70,7 @@ func (c *ServiceContext) DatabaseOptions() []mysqladapter.Option {
|
||||
`WithDriverConfig(func(*mysql.Config) error)` 支持高级拨号、驱动参数等,配置在回调后克隆;这是项目明确选择的底层扩展。`NewConnector` 返回标准 driver.Connector,供已有基础设施使用;它不接受启动检查选项。任意驱动仍可用核心 `database.OpenConnector`。
|
||||
|
||||
错误有安全摘要,errors.Is/As 保留 TLS/驱动/项目检查的原因。配置验证不读证书、不连接数据库;Open 才读取证书、创建池并握手。不要自行打印原始配置或展开可能包含内部信息的 driver 字段给用户。
|
||||
|
||||
## 字段级配置错误
|
||||
|
||||
Validate 返回独立字段与安全原因,例如 port(1–65535)、readTimeout、pool.maxIdleConns、tls.keyFile。项目调用 `config.AtField("database", err)` 后,统一加载器补齐 database 前缀、文件、环境名和行列,无需重复写端口/连接池校验。TLS 成对配置会指出缺失的那个字段。连接失败、驱动和证书文件的内部原因仍通过 errors.Is/As 保留,不把凭据放进公开提示。见 [配置诊断](../../CONFIG.md#框架模块的字段诊断)。
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"git.yuntuo.hk/k2safe/go-omni/database"
|
||||
"git.yuntuo.hk/k2safe/go-omni/internal/configdiag"
|
||||
"git.yuntuo.hk/k2safe/go-omni/logx"
|
||||
mysqldriver "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
@@ -61,45 +62,60 @@ var identifier = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)
|
||||
// Validate does not read certificate files or open connections. All defaults
|
||||
// come from DefaultConfig; explicit zero I/O timeouts mean no driver deadline.
|
||||
func (c Config) Validate() error {
|
||||
if c.Socket == "" && (c.Host == "" || strings.ContainsAny(c.Host, "/\r\n\t ") || c.Port < 1 || c.Port > 65535) {
|
||||
return errors.New("mysql: invalid host or port")
|
||||
if c.Socket == "" {
|
||||
if c.Host == "" || strings.ContainsAny(c.Host, "/\r\n\t ") {
|
||||
return configdiag.New("host", "must be a nonempty hostname or IP address")
|
||||
}
|
||||
if c.Port < 1 || c.Port > 65535 {
|
||||
return configdiag.New("port", "must be between 1 and 65535")
|
||||
}
|
||||
}
|
||||
if strings.ContainsRune(c.Socket, 0) || strings.ContainsRune(c.Database, 0) || strings.ContainsRune(c.User, 0) {
|
||||
return errors.New("mysql: invalid connection field")
|
||||
for _, f := range []struct{ name, value string }{{"socket", c.Socket}, {"database", c.Database}, {"user", c.User}} {
|
||||
if strings.ContainsRune(f.value, 0) {
|
||||
return configdiag.New(f.name, "must not contain a NUL character")
|
||||
}
|
||||
}
|
||||
if c.ConnectTimeout < 0 || c.ReadTimeout < 0 || c.WriteTimeout < 0 {
|
||||
return errors.New("mysql: timeouts cannot be negative")
|
||||
for _, f := range []struct {
|
||||
name string
|
||||
value time.Duration
|
||||
}{{"connectTimeout", c.ConnectTimeout}, {"readTimeout", c.ReadTimeout}, {"writeTimeout", c.WriteTimeout}} {
|
||||
if f.value < 0 {
|
||||
return configdiag.New(f.name, "must be nonnegative")
|
||||
}
|
||||
}
|
||||
if err := c.Pool.Validate(); err != nil {
|
||||
return err
|
||||
return configdiag.At("pool", err)
|
||||
}
|
||||
if c.Location != "" {
|
||||
if _, err := time.LoadLocation(c.Location); err != nil {
|
||||
return errors.New("mysql: invalid time location")
|
||||
return configdiag.Wrap("location", "must be an available IANA time zone or UTC", err)
|
||||
}
|
||||
}
|
||||
if c.Charset != "" && !identifier.MatchString(c.Charset) {
|
||||
return errors.New("mysql: invalid charset")
|
||||
return configdiag.New("charset", "must be a valid MySQL identifier")
|
||||
}
|
||||
if c.Collation != "" && !identifier.MatchString(c.Collation) {
|
||||
return errors.New("mysql: invalid collation")
|
||||
return configdiag.New("collation", "must be a valid MySQL identifier")
|
||||
}
|
||||
for key := range c.Session {
|
||||
if !identifier.MatchString(key) {
|
||||
return errors.New("mysql: invalid session variable name")
|
||||
return configdiag.New("session", "keys must be valid MySQL identifiers")
|
||||
}
|
||||
}
|
||||
if c.TLS.Mode != "" && c.TLS.Mode != "required" && c.TLS.Mode != "disabled" {
|
||||
return errors.New("mysql: TLS mode must be required or disabled")
|
||||
return configdiag.New("tls.mode", "must be required or disabled")
|
||||
}
|
||||
if (c.TLS.CertFile == "") != (c.TLS.KeyFile == "") {
|
||||
return errors.New("mysql: TLS client certificate and key must be configured together")
|
||||
if c.TLS.CertFile == "" {
|
||||
return configdiag.New("tls.certFile", "is required when keyFile is configured")
|
||||
}
|
||||
return configdiag.New("tls.keyFile", "is required when certFile is configured")
|
||||
}
|
||||
if c.TLS.Mode == "disabled" && (c.TLS.CAFile != "" || c.TLS.CertFile != "" || c.TLS.ServerName != "") {
|
||||
return errors.New("mysql: TLS fields cannot be used when disabled")
|
||||
return configdiag.New("tls.mode", "must be required when TLS fields are configured")
|
||||
}
|
||||
if c.Socket != "" && c.TLS.Mode != "disabled" && c.TLS.ServerName == "" {
|
||||
return errors.New("mysql: TLS over a unix socket requires serverName")
|
||||
return configdiag.New("tls.serverName", "is required for TLS over a unix socket")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user