feat: scaffold independent workbench service
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Listen string `json:"listen"`
|
||||
MySQL MySQLConfig `json:"mysql"`
|
||||
Redis RedisConfig `json:"redis"`
|
||||
Kafka KafkaConfig `json:"kafka"`
|
||||
Auth AuthConfig `json:"auth"`
|
||||
ReadTimeout time.Duration `json:"-"`
|
||||
WriteTimeout time.Duration `json:"-"`
|
||||
}
|
||||
|
||||
type MySQLConfig struct {
|
||||
DSN string `json:"dsn"`
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
DB int `json:"db"`
|
||||
KeyPrefix string `json:"key_prefix"`
|
||||
}
|
||||
|
||||
type KafkaConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Brokers []string `json:"brokers"`
|
||||
Topic string `json:"topic"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
SASL string `json:"sasl"`
|
||||
ClientID string `json:"client_id"`
|
||||
BatchSize int `json:"batch_size"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
APIKeys []string `json:"api_keys"`
|
||||
SessionKeyPattern string `json:"session_key_pattern"`
|
||||
AllowDevHeaderAuth bool `json:"allow_dev_header_auth"`
|
||||
RequiredTenantCode bool `json:"required_tenant_code"`
|
||||
RequiredAPIKey bool `json:"required_api_key"`
|
||||
SkipSessionInDev bool `json:"skip_session_in_dev"`
|
||||
TrustedInternalAddr string `json:"trusted_internal_addr"`
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
cfg := defaultConfig()
|
||||
if path := strings.TrimSpace(os.Getenv("IM_WORKBENCH_CONFIG")); path != "" {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(data, cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
applyEnv(cfg)
|
||||
if err := cfg.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
Listen: ":6061",
|
||||
MySQL: MySQLConfig{
|
||||
DSN: "root:root@tcp(127.0.0.1:3306)/tinode?parseTime=true&charset=utf8mb4,utf8&loc=Local",
|
||||
},
|
||||
Redis: RedisConfig{
|
||||
Addr: "127.0.0.1:6379",
|
||||
KeyPrefix: "im:",
|
||||
},
|
||||
Kafka: KafkaConfig{
|
||||
Topic: "im.workbench.event",
|
||||
ClientID: "im_workbench",
|
||||
},
|
||||
Auth: AuthConfig{
|
||||
SessionKeyPattern: "im:session:token:%s",
|
||||
RequiredTenantCode: true,
|
||||
RequiredAPIKey: true,
|
||||
},
|
||||
ReadTimeout: 8 * time.Second,
|
||||
WriteTimeout: 8 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func applyEnv(cfg *Config) {
|
||||
setString(&cfg.Listen, "IM_WORKBENCH_LISTEN")
|
||||
setString(&cfg.MySQL.DSN, "IM_WORKBENCH_MYSQL_DSN")
|
||||
setString(&cfg.Redis.Addr, "IM_WORKBENCH_REDIS_ADDR")
|
||||
setString(&cfg.Redis.Username, "IM_WORKBENCH_REDIS_USERNAME")
|
||||
setString(&cfg.Redis.Password, "IM_WORKBENCH_REDIS_PASSWORD")
|
||||
setString(&cfg.Redis.KeyPrefix, "IM_WORKBENCH_REDIS_KEY_PREFIX")
|
||||
setString(&cfg.Kafka.Topic, "IM_WORKBENCH_KAFKA_TOPIC")
|
||||
setString(&cfg.Kafka.Username, "IM_WORKBENCH_KAFKA_USERNAME")
|
||||
setString(&cfg.Kafka.Password, "IM_WORKBENCH_KAFKA_PASSWORD")
|
||||
setString(&cfg.Kafka.SASL, "IM_WORKBENCH_KAFKA_SASL")
|
||||
setString(&cfg.Kafka.ClientID, "IM_WORKBENCH_KAFKA_CLIENT_ID")
|
||||
setString(&cfg.Auth.SessionKeyPattern, "IM_WORKBENCH_SESSION_KEY_PATTERN")
|
||||
setString(&cfg.Auth.TrustedInternalAddr, "IM_WORKBENCH_TRUSTED_INTERNAL_ADDR")
|
||||
if brokers := strings.TrimSpace(os.Getenv("IM_WORKBENCH_KAFKA_BROKERS")); brokers != "" {
|
||||
cfg.Kafka.Brokers = splitCSV(brokers)
|
||||
}
|
||||
if keys := strings.TrimSpace(os.Getenv("IM_WORKBENCH_API_KEYS")); keys != "" {
|
||||
cfg.Auth.APIKeys = splitCSV(keys)
|
||||
}
|
||||
if value := strings.TrimSpace(os.Getenv("IM_WORKBENCH_KAFKA_BATCH_SIZE")); value != "" {
|
||||
if parsed, err := strconv.Atoi(value); err == nil {
|
||||
cfg.Kafka.BatchSize = parsed
|
||||
}
|
||||
}
|
||||
if value := strings.TrimSpace(os.Getenv("IM_WORKBENCH_REDIS_DB")); value != "" {
|
||||
if parsed, err := strconv.Atoi(value); err == nil {
|
||||
cfg.Redis.DB = parsed
|
||||
}
|
||||
}
|
||||
setBool(&cfg.Auth.AllowDevHeaderAuth, "IM_WORKBENCH_ALLOW_DEV_HEADER_AUTH")
|
||||
setBool(&cfg.Auth.RequiredTenantCode, "IM_WORKBENCH_REQUIRED_TENANT_CODE")
|
||||
setBool(&cfg.Auth.RequiredAPIKey, "IM_WORKBENCH_REQUIRED_API_KEY")
|
||||
setBool(&cfg.Auth.SkipSessionInDev, "IM_WORKBENCH_SKIP_SESSION_IN_DEV")
|
||||
setBool(&cfg.Kafka.Enabled, "IM_WORKBENCH_KAFKA_ENABLED")
|
||||
}
|
||||
|
||||
func (c *Config) validate() error {
|
||||
if strings.TrimSpace(c.Listen) == "" {
|
||||
return errors.New("listen is required")
|
||||
}
|
||||
if strings.TrimSpace(c.MySQL.DSN) == "" {
|
||||
return errors.New("mysql dsn is required")
|
||||
}
|
||||
if strings.TrimSpace(c.Redis.Addr) == "" && !c.Auth.SkipSessionInDev {
|
||||
return errors.New("redis addr is required")
|
||||
}
|
||||
if strings.TrimSpace(c.Auth.SessionKeyPattern) == "" {
|
||||
return errors.New("session key pattern is required")
|
||||
}
|
||||
if !strings.Contains(c.Auth.SessionKeyPattern, "%s") {
|
||||
return fmt.Errorf("session key pattern must include %%s")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setString(target *string, key string) {
|
||||
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
||||
*target = value
|
||||
}
|
||||
}
|
||||
|
||||
func setBool(target *bool, key string) {
|
||||
switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) {
|
||||
case "1", "true", "yes", "on":
|
||||
*target = true
|
||||
case "0", "false", "no", "off":
|
||||
*target = false
|
||||
}
|
||||
}
|
||||
|
||||
func splitCSV(value string) []string {
|
||||
parts := strings.Split(value, ",")
|
||||
items := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if item := strings.TrimSpace(part); item != "" {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
Reference in New Issue
Block a user