// Package clickhouse 提供 ClickHouse 只读客户端,用于查询 logs_ingest(Fluent Bit 写入)。 // 配置优先从后台页面(edgeSysSettings.clickhouseConfig)读取,其次 api.yaml,最后环境变量。 package clickhouse import ( "github.com/TeaOSLab/EdgeAPI/internal/configs" "github.com/TeaOSLab/EdgeAPI/internal/db/models" "os" "strconv" "sync" ) const ( envHost = "CLICKHOUSE_HOST" envPort = "CLICKHOUSE_PORT" envUser = "CLICKHOUSE_USER" envPassword = "CLICKHOUSE_PASSWORD" envDatabase = "CLICKHOUSE_DATABASE" defaultPort = 8123 defaultDB = "default" ) var ( sharedConfig *Config configOnce sync.Once configLocker sync.Mutex ) // Config ClickHouse 连接配置(仅查询,不从代码写库) type Config struct { Host string Port int User string Password string Database string } // SharedConfig 返回全局配置(优先从后台 DB 读取,其次 api.yaml,最后环境变量) func SharedConfig() *Config { configLocker.Lock() defer configLocker.Unlock() if sharedConfig != nil { return sharedConfig } sharedConfig = loadConfig() return sharedConfig } // ResetSharedConfig 清空缓存,下次 SharedConfig() 时重新从 DB/文件/环境变量加载(后台保存 ClickHouse 配置后调用) func ResetSharedConfig() { configLocker.Lock() defer configLocker.Unlock() sharedConfig = nil } func loadConfig() *Config { cfg := &Config{Port: defaultPort, Database: defaultDB} // 1) 优先从后台页面配置(DB)读取 if models.SharedSysSettingDAO != nil { if dbCfg, err := models.SharedSysSettingDAO.ReadClickHouseConfig(nil); err == nil && dbCfg != nil && dbCfg.Host != "" { cfg.Host = dbCfg.Host cfg.Port = dbCfg.Port cfg.User = dbCfg.User cfg.Password = dbCfg.Password cfg.Database = dbCfg.Database if cfg.Port <= 0 { cfg.Port = defaultPort } if cfg.Database == "" { cfg.Database = defaultDB } return cfg } } // 2) 其次 api.yaml apiConfig, err := configs.SharedAPIConfig() if err == nil && apiConfig != nil && apiConfig.ClickHouse != nil && apiConfig.ClickHouse.Host != "" { ch := apiConfig.ClickHouse cfg.Host = ch.Host cfg.Port = ch.Port cfg.User = ch.User cfg.Password = ch.Password cfg.Database = ch.Database if cfg.Port <= 0 { cfg.Port = defaultPort } if cfg.Database == "" { cfg.Database = defaultDB } return cfg } // 3) 最后环境变量 cfg.Host = os.Getenv(envHost) cfg.User = os.Getenv(envUser) cfg.Password = os.Getenv(envPassword) cfg.Database = os.Getenv(envDatabase) if cfg.Database == "" { cfg.Database = defaultDB } if p := os.Getenv(envPort); p != "" { if v, err := strconv.Atoi(p); err == nil { cfg.Port = v } } return cfg } // IsConfigured 是否已配置(Host 非空即视为启用 ClickHouse 查询) func (c *Config) IsConfigured() bool { return c != nil && c.Host != "" }