206 lines
5.3 KiB
Go
206 lines
5.3 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type Config struct {
|
|
Server ServerConfig
|
|
Database DatabaseConfig
|
|
Auth AuthConfig
|
|
AI AIConfig
|
|
JWT JWTConfig
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Host string
|
|
Port string
|
|
ReadTimeout time.Duration
|
|
WriteTimeout time.Duration
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
Host string
|
|
Port string
|
|
User string
|
|
Password string
|
|
DBName string
|
|
SSLMode string
|
|
TimeZone string
|
|
}
|
|
|
|
type AuthConfig struct {
|
|
JWTSecret string
|
|
JWTExpirationHours int
|
|
BCryptCost int
|
|
MaxLoginAttempts int
|
|
AccountLockDuration time.Duration
|
|
}
|
|
|
|
type AIConfig struct {
|
|
OpenAI APIConfig
|
|
Local APIConfig
|
|
}
|
|
|
|
type APIConfig struct {
|
|
APIKey string
|
|
Endpoint string
|
|
Model string
|
|
MaxTokens int
|
|
Temperature float64
|
|
TopP float64
|
|
Timeout time.Duration
|
|
}
|
|
|
|
type JWTConfig struct {
|
|
Secret string
|
|
ExpirationHours int
|
|
Issuer string
|
|
Audience string
|
|
}
|
|
|
|
var AppConfig Config
|
|
|
|
// LoadConfig loads configuration from environment variables and config file
|
|
func LoadConfig() error {
|
|
// Set default values
|
|
viper.SetDefault("server.host", "0.0.0.0")
|
|
viper.SetDefault("server.port", "8080")
|
|
viper.SetDefault("server.readTimeout", "15s")
|
|
viper.SetDefault("server.writeTimeout", "15s")
|
|
|
|
viper.SetDefault("database.host", "localhost")
|
|
viper.SetDefault("database.port", "5432")
|
|
viper.SetDefault("database.user", "postgres")
|
|
viper.SetDefault("database.password", "postgres")
|
|
viper.SetDefault("database.dbname", "support")
|
|
viper.SetDefault("database.sslmode", "disable")
|
|
viper.SetDefault("database.timezone", "UTC")
|
|
|
|
viper.SetDefault("auth.jwtSecret", "your-secret-key")
|
|
viper.SetDefault("auth.jwtExpirationHours", 24)
|
|
viper.SetDefault("auth.bcryptCost", 12)
|
|
viper.SetDefault("auth.maxLoginAttempts", 5)
|
|
viper.SetDefault("auth.accountLockDuration", "30m")
|
|
|
|
viper.SetDefault("ai.openai.maxTokens", 4000)
|
|
viper.SetDefault("ai.openai.temperature", 0.7)
|
|
viper.SetDefault("ai.openai.topP", 1.0)
|
|
viper.SetDefault("ai.openai.timeout", "30s")
|
|
|
|
viper.SetDefault("ai.local.maxTokens", 2000)
|
|
viper.SetDefault("ai.local.temperature", 0.7)
|
|
viper.SetDefault("ai.local.topP", 1.0)
|
|
viper.SetDefault("ai.local.timeout", "60s")
|
|
viper.SetDefault("ai.local.endpoint", "http://localhost:11434")
|
|
viper.SetDefault("ai.local.model", "llama2")
|
|
|
|
viper.SetDefault("jwt.secret", "your-jwt-secret")
|
|
viper.SetDefault("jwt.expirationHours", 24)
|
|
viper.SetDefault("jwt.issuer", "support-system")
|
|
viper.SetDefault("jwt.audience", "support-users")
|
|
|
|
// Configure viper to read from environment variables
|
|
viper.AutomaticEnv()
|
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
|
|
// Read config file if it exists
|
|
viper.SetConfigName("config")
|
|
viper.SetConfigType("yaml")
|
|
viper.AddConfigPath("./")
|
|
viper.AddConfigPath("./config")
|
|
viper.AddConfigPath("../config")
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
|
return fmt.Errorf("error reading config file: %w", err)
|
|
}
|
|
// Config file not found; ignore error
|
|
}
|
|
|
|
// Unmarshal config
|
|
if err := viper.Unmarshal(&AppConfig); err != nil {
|
|
return fmt.Errorf("unable to decode config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetDSN returns the database connection string
|
|
func (c *DatabaseConfig) GetDSN() string {
|
|
return fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=%s",
|
|
c.Host, c.User, c.Password, c.DBName, c.Port, c.SSLMode, c.TimeZone)
|
|
}
|
|
|
|
// GetServerAddress returns the server address
|
|
func (c *ServerConfig) GetServerAddress() string {
|
|
return fmt.Sprintf("%s:%s", c.Host, c.Port)
|
|
}
|
|
|
|
// GetJWTSigningKey returns the JWT signing key
|
|
func (c *JWTConfig) GetJWTSigningKey() []byte {
|
|
return []byte(c.Secret)
|
|
}
|
|
|
|
// ParseJWTToken parses a JWT token and returns the claims
|
|
func (c *JWTConfig) ParseJWTToken(tokenString string) (*jwt.MapClaims, error) {
|
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
}
|
|
return c.GetJWTSigningKey(), nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
|
return &claims, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("invalid token")
|
|
}
|
|
|
|
// GetEnv gets an environment variable with a default value
|
|
func GetEnv(key, defaultValue string) string {
|
|
if value, exists := os.LookupEnv(key); exists {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
// GetEnvAsInt gets an environment variable as an integer with a default value
|
|
func GetEnvAsInt(key string, defaultValue int) int {
|
|
valueStr := GetEnv(key, "")
|
|
if value, err := strconv.Atoi(valueStr); err == nil {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
// GetEnvAsBool gets an environment variable as a boolean with a default value
|
|
func GetEnvAsBool(key string, defaultValue bool) bool {
|
|
valueStr := GetEnv(key, "")
|
|
if value, err := strconv.ParseBool(valueStr); err == nil {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
// GetEnvAsDuration gets an environment variable as a duration with a default value
|
|
func GetEnvAsDuration(key string, defaultValue time.Duration) time.Duration {
|
|
valueStr := GetEnv(key, "")
|
|
if value, err := time.ParseDuration(valueStr); err == nil {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|