mengstack-api/internal/config/config.go
MengStack Dev df809f1045
Some checks failed
CI / Build & Test (push) Failing after 1m31s
feat: user CRUD API + dashboard stats + kernel infrastructure
- Add user management endpoints (list/create/get/update/delete) with pagination and search
- Add dashboard stats endpoint with tenant/user/online counts and growth metrics
- Add tenant resolver middleware for multi-tenant request scoping
- Add i18n kernel with zh/en message files and AcceptLanguage middleware
- Add WebSocket hub/handler for real-time communication
- Add job scheduler kernel with cron support
- Add plugin sandbox for isolated execution
- Add storage kernel (local filesystem)
- Add event bus kernel for pub/sub
- Add cache kernel abstraction
- Add database migration runner and version upgrade checker
- Add rate limiting middleware with Redis backend
- Add SQL migrations for rbac, audit_logs, settings, notifications, examples
- Extend user repository with list/delete/count operations
- Register all module routes with tenant resolver
2026-10-03 03:42:58 +08:00

131 lines
3.5 KiB
Go

package config
import (
"fmt"
"os"
"strings"
"github.com/spf13/viper"
)
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
JWT JWTConfig `mapstructure:"jwt"`
Log LogConfig `mapstructure:"log"`
Plugin PluginConfig `mapstructure:"plugin"`
Tenant TenantConfig `mapstructure:"tenant"`
Storage StorageConfig `mapstructure:"storage"`
}
type ServerConfig struct {
Port int `mapstructure:"port"`
Mode string `mapstructure:"mode"`
}
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbname"`
SSLMode string `mapstructure:"sslmode"`
}
type RedisConfig struct {
Addr string `mapstructure:"addr"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
}
type JWTConfig struct {
Secret string `mapstructure:"secret"`
AccessExpiryMinutes int `mapstructure:"access_expiry_minutes"`
RefreshExpiryDays int `mapstructure:"refresh_expiry_days"`
Issuer string `mapstructure:"issuer"`
}
type LogConfig struct {
Level string `mapstructure:"level"`
Format string `mapstructure:"format"`
}
type PluginConfig struct {
Enabled []string `mapstructure:"enabled"`
}
type TenantConfig struct {
Mode string `mapstructure:"mode"`
DefaultID string `mapstructure:"default_id"`
}
type StorageConfig struct {
Driver string `mapstructure:"driver"`
RootDir string `mapstructure:"root_dir"`
BaseURL string `mapstructure:"base_url"`
}
func Load() (*Config, error) {
v := viper.New()
v.SetConfigName("config")
v.SetConfigType("yaml")
v.AddConfigPath("./configs/")
v.AddConfigPath(".")
v.SetEnvPrefix("MENGSTACK")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AutomaticEnv()
setDefaults(v)
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read config: %w", err)
}
// Check env var for mode override before merging env config
env := v.GetString("server.mode")
if envOverride := os.Getenv("MENGSTACK_SERVER_MODE"); envOverride != "" {
env = envOverride
}
mergeEnvConfig(v, env)
var cfg Config
if err := v.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}
return &cfg, nil
}
func setDefaults(v *viper.Viper) {
v.SetDefault("server.port", 8080)
v.SetDefault("server.mode", "debug")
v.SetDefault("database.host", "127.0.0.1")
v.SetDefault("database.port", 5432)
v.SetDefault("database.user", "postgres")
v.SetDefault("database.dbname", "mengstack")
v.SetDefault("database.sslmode", "disable")
v.SetDefault("redis.addr", "127.0.0.1:6379")
v.SetDefault("redis.db", 0)
v.SetDefault("jwt.access_expiry_minutes", 30)
v.SetDefault("jwt.refresh_expiry_days", 7)
v.SetDefault("jwt.issuer", "mengstack")
v.SetDefault("log.level", "info")
v.SetDefault("log.format", "json")
v.SetDefault("tenant.mode", "multi")
v.SetDefault("tenant.default_id", "00000000-0000-0000-0000-000000000001")
v.SetDefault("storage.driver", "local")
v.SetDefault("storage.root_dir", "./uploads")
v.SetDefault("storage.base_url", "http://localhost:8080")
}
func mergeEnvConfig(v *viper.Viper, env string) {
envFile := fmt.Sprintf("config.%s", env)
v.SetConfigName(envFile)
if err := v.MergeInConfig(); err != nil {
// environment-specific config is optional
}
v.SetConfigName("config")
}