141 lines
3.8 KiB
Go
141 lines
3.8 KiB
Go
// 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"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
const (
|
||
envHost = "CLICKHOUSE_HOST"
|
||
envPort = "CLICKHOUSE_PORT"
|
||
envUser = "CLICKHOUSE_USER"
|
||
envPassword = "CLICKHOUSE_PASSWORD"
|
||
envDatabase = "CLICKHOUSE_DATABASE"
|
||
envScheme = "CLICKHOUSE_SCHEME"
|
||
envTLSSkipVerify = "CLICKHOUSE_TLS_SKIP_VERIFY"
|
||
envTLSServerName = "CLICKHOUSE_TLS_SERVER_NAME"
|
||
defaultPort = 8123
|
||
defaultDB = "default"
|
||
defaultScheme = "http"
|
||
)
|
||
|
||
var (
|
||
sharedConfig *Config
|
||
configOnce sync.Once
|
||
configLocker sync.Mutex
|
||
)
|
||
|
||
// Config ClickHouse 连接配置(仅查询,不从代码写库)
|
||
type Config struct {
|
||
Host string
|
||
Port int
|
||
User string
|
||
Password string
|
||
Database string
|
||
Scheme string
|
||
TLSSkipVerify bool
|
||
TLSServerName 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, Scheme: defaultScheme}
|
||
// 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
|
||
cfg.Scheme = normalizeScheme(dbCfg.Scheme)
|
||
cfg.TLSSkipVerify = dbCfg.TLSSkipVerify
|
||
cfg.TLSServerName = dbCfg.TLSServerName
|
||
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
|
||
cfg.Scheme = normalizeScheme(ch.Scheme)
|
||
cfg.TLSSkipVerify = ch.TLSSkipVerify
|
||
cfg.TLSServerName = ch.TLSServerName
|
||
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
|
||
}
|
||
cfg.Scheme = normalizeScheme(os.Getenv(envScheme))
|
||
cfg.TLSServerName = os.Getenv(envTLSServerName)
|
||
if p := os.Getenv(envPort); p != "" {
|
||
if v, err := strconv.Atoi(p); err == nil {
|
||
cfg.Port = v
|
||
}
|
||
}
|
||
if v := os.Getenv(envTLSSkipVerify); v != "" {
|
||
if b, err := strconv.ParseBool(v); err == nil {
|
||
cfg.TLSSkipVerify = b
|
||
}
|
||
}
|
||
return cfg
|
||
}
|
||
|
||
func normalizeScheme(scheme string) string {
|
||
s := strings.ToLower(strings.TrimSpace(scheme))
|
||
if s == "https" {
|
||
return "https"
|
||
}
|
||
return defaultScheme
|
||
}
|
||
|
||
// IsConfigured 是否已配置(Host 非空即视为启用 ClickHouse 查询)
|
||
func (c *Config) IsConfigured() bool {
|
||
return c != nil && c.Host != ""
|
||
}
|