153 lines
3.6 KiB
Go
153 lines
3.6 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
)
|
|
|
|
type Config struct {
|
|
Server ServerConfig `toml:"server"`
|
|
Database DatabaseConfig `toml:"database"`
|
|
JWT JWTConfig `toml:"jwt"`
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
BindAddr string `toml:"bind_addr"`
|
|
Mode string `toml:"mode"` // "debug" or "release"
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
Type string `toml:"type"` // "sqlite", "postgres", "mysql"
|
|
Path string `toml:"path"` // For SQLite
|
|
Host string `toml:"host"` // For PostgreSQL/MySQL
|
|
Port int `toml:"port"` // For PostgreSQL/MySQL
|
|
User string `toml:"user"` // For PostgreSQL/MySQL
|
|
Password string `toml:"password"` // For PostgreSQL/MySQL
|
|
DBName string `toml:"dbname"` // For PostgreSQL/MySQL
|
|
SSLMode string `toml:"sslmode"` // For PostgreSQL
|
|
}
|
|
|
|
type JWTConfig struct {
|
|
Secret string `toml:"secret"`
|
|
Expiration int `toml:"expiration"` // in hours
|
|
}
|
|
|
|
var (
|
|
instance *Config
|
|
once sync.Once
|
|
)
|
|
|
|
// Load loads the configuration from a TOML file
|
|
func Load(configPath string) (*Config, error) {
|
|
var err error
|
|
once.Do(func() {
|
|
instance = &Config{}
|
|
|
|
if _, loadErr := toml.DecodeFile(configPath, instance); loadErr != nil {
|
|
err = fmt.Errorf("failed to load configuration: %w", loadErr)
|
|
return
|
|
}
|
|
|
|
// Default values if not specified
|
|
if instance.Server.BindAddr == "" {
|
|
instance.Server.BindAddr = "0.0.0.0:7000"
|
|
}
|
|
if instance.Server.Mode == "" {
|
|
instance.Server.Mode = "release"
|
|
}
|
|
if instance.Database.Type == "" {
|
|
instance.Database.Type = "sqlite"
|
|
}
|
|
if instance.Database.Path == "" {
|
|
instance.Database.Path = "./oxspeak.db"
|
|
}
|
|
if instance.JWT.Expiration == 0 {
|
|
instance.JWT.Expiration = 24
|
|
}
|
|
|
|
// Validation
|
|
if instance.JWT.Secret == "" {
|
|
err = fmt.Errorf("JWT secret is required")
|
|
return
|
|
}
|
|
|
|
if instance.Database.Type != "sqlite" && instance.Database.Type != "postgres" && instance.Database.Type != "mysql" {
|
|
err = fmt.Errorf("invalid database type: %s (accepted values: sqlite, postgres, mysql)", instance.Database.Type)
|
|
return
|
|
}
|
|
})
|
|
|
|
return instance, err
|
|
}
|
|
|
|
// Get returns the configuration instance (must be loaded first)
|
|
func Get() *Config {
|
|
if instance == nil {
|
|
panic("configuration not loaded. Call Load() first")
|
|
}
|
|
return instance
|
|
}
|
|
|
|
// GetDSN returns the connection string based on the database type
|
|
func (c *Config) GetDSN() string {
|
|
switch c.Database.Type {
|
|
case "sqlite":
|
|
return c.Database.Path
|
|
case "postgres":
|
|
return fmt.Sprintf(
|
|
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
|
c.Database.Host,
|
|
c.Database.Port,
|
|
c.Database.User,
|
|
c.Database.Password,
|
|
c.Database.DBName,
|
|
c.Database.SSLMode,
|
|
)
|
|
case "mysql":
|
|
return fmt.Sprintf(
|
|
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
|
c.Database.User,
|
|
c.Database.Password,
|
|
c.Database.Host,
|
|
c.Database.Port,
|
|
c.Database.DBName,
|
|
)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// GetDialector returns the GORM dialect name
|
|
func (c *Config) GetDialector() string {
|
|
return c.Database.Type
|
|
}
|
|
|
|
// FindConfigFile searches for the config file in multiple locations
|
|
func FindConfigFile() (string, error) {
|
|
locations := []string{
|
|
"./config.toml",
|
|
"./config/config.toml",
|
|
"/etc/oxspeak/config.toml",
|
|
}
|
|
|
|
// Add executable directory
|
|
if exePath, err := os.Executable(); err == nil {
|
|
exeDir := filepath.Dir(exePath)
|
|
locations = append([]string{
|
|
filepath.Join(exeDir, "config.toml"),
|
|
}, locations...)
|
|
}
|
|
|
|
for _, path := range locations {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return path, nil
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("configuration file not found in locations: %v", locations)
|
|
}
|