Move project files from uzdb/ subdirectory to root directory for cleaner project structure.
Changes:
- Move frontend/ to root
- Move internal/ to root
- Move build/ to root
- Move all config files (go.mod, wails.json, etc.) to root
- Remove redundant uzdb/ subdirectory nesting
Project structure is now:
├── frontend/ # React application
├── internal/ # Go backend
├── build/ # Wails build assets
├── doc/ # Design documentation
├── main.go # Entry point
└── ...
🤖 Generated with Qoder
262 lines
5.6 KiB
Go
262 lines
5.6 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
)
|
|
|
|
// Environment represents the application environment type
|
|
type Environment string
|
|
|
|
const (
|
|
// EnvDevelopment represents development environment
|
|
EnvDevelopment Environment = "development"
|
|
// EnvProduction represents production environment
|
|
EnvProduction Environment = "production"
|
|
)
|
|
|
|
// Config holds all configuration for the application
|
|
type Config struct {
|
|
// App settings
|
|
AppName string `json:"app_name"`
|
|
Version string `json:"version"`
|
|
Environment Environment `json:"environment"`
|
|
|
|
// Database settings (SQLite for app data)
|
|
Database DatabaseConfig `json:"database"`
|
|
|
|
// Encryption settings
|
|
Encryption EncryptionConfig `json:"encryption"`
|
|
|
|
// Logger settings
|
|
Logger LoggerConfig `json:"logger"`
|
|
|
|
// API settings (for debug HTTP server)
|
|
API APIConfig `json:"api"`
|
|
|
|
// File paths
|
|
DataDir string `json:"-"`
|
|
}
|
|
|
|
// DatabaseConfig holds database configuration
|
|
type DatabaseConfig struct {
|
|
// SQLite database file path for app data
|
|
SQLitePath string `json:"sqlite_path"`
|
|
// Max open connections
|
|
MaxOpenConns int `json:"max_open_conns"`
|
|
// Max idle connections
|
|
MaxIdleConns int `json:"max_idle_conns"`
|
|
// Connection max lifetime in minutes
|
|
MaxLifetime int `json:"max_lifetime"`
|
|
}
|
|
|
|
// EncryptionConfig holds encryption configuration
|
|
type EncryptionConfig struct {
|
|
// Key for encrypting sensitive data (passwords, etc.)
|
|
// In production, this should be loaded from secure storage
|
|
Key string `json:"-"`
|
|
// KeyFile path to load encryption key from
|
|
KeyFile string `json:"key_file"`
|
|
}
|
|
|
|
// LoggerConfig holds logger configuration
|
|
type LoggerConfig struct {
|
|
// Log level: debug, info, warn, error
|
|
Level string `json:"level"`
|
|
// Log format: json, console
|
|
Format string `json:"format"`
|
|
// Output file path (empty for stdout)
|
|
OutputPath string `json:"output_path"`
|
|
}
|
|
|
|
// APIConfig holds HTTP API configuration
|
|
type APIConfig struct {
|
|
// Enable HTTP API server (for debugging)
|
|
Enabled bool `json:"enabled"`
|
|
// Port for HTTP API server
|
|
Port string `json:"port"`
|
|
}
|
|
|
|
var (
|
|
instance *Config
|
|
once sync.Once
|
|
logger *zap.Logger
|
|
)
|
|
|
|
// Get returns the singleton config instance
|
|
func Get() *Config {
|
|
return instance
|
|
}
|
|
|
|
// GetLogger returns the zap logger
|
|
func GetLogger() *zap.Logger {
|
|
return logger
|
|
}
|
|
|
|
// Init initializes the configuration
|
|
// If config file doesn't exist, creates default config
|
|
func Init(dataDir string) (*Config, error) {
|
|
var err error
|
|
once.Do(func() {
|
|
instance = &Config{
|
|
DataDir: dataDir,
|
|
}
|
|
err = instance.load(dataDir)
|
|
})
|
|
return instance, err
|
|
}
|
|
|
|
// load loads configuration from file or creates default
|
|
func (c *Config) load(dataDir string) error {
|
|
configPath := filepath.Join(dataDir, "config.json")
|
|
|
|
// Try to load existing config
|
|
if _, err := os.Stat(configPath); err == nil {
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := json.Unmarshal(data, c); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// Create default config
|
|
c.setDefaults()
|
|
if err := c.save(configPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Override with environment variables
|
|
c.loadEnv()
|
|
|
|
// Initialize logger
|
|
if err := c.initLogger(); err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.Info("configuration loaded",
|
|
zap.String("environment", string(c.Environment)),
|
|
zap.String("data_dir", c.DataDir),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// setDefaults sets default configuration values
|
|
func (c *Config) setDefaults() {
|
|
c.AppName = "uzdb"
|
|
c.Version = "1.0.0"
|
|
c.Environment = EnvDevelopment
|
|
|
|
c.Database = DatabaseConfig{
|
|
SQLitePath: filepath.Join(c.DataDir, "uzdb.db"),
|
|
MaxOpenConns: 25,
|
|
MaxIdleConns: 5,
|
|
MaxLifetime: 5,
|
|
}
|
|
|
|
c.Encryption = EncryptionConfig{
|
|
Key: "", // Will be generated if empty
|
|
KeyFile: filepath.Join(c.DataDir, "encryption.key"),
|
|
}
|
|
|
|
c.Logger = LoggerConfig{
|
|
Level: "debug",
|
|
Format: "console",
|
|
OutputPath: "",
|
|
}
|
|
|
|
c.API = APIConfig{
|
|
Enabled: true,
|
|
Port: "8080",
|
|
}
|
|
}
|
|
|
|
// loadEnv loads configuration from environment variables
|
|
func (c *Config) loadEnv() {
|
|
if env := os.Getenv("UZDB_ENV"); env != "" {
|
|
c.Environment = Environment(env)
|
|
}
|
|
|
|
if port := os.Getenv("UZDB_API_PORT"); port != "" {
|
|
c.API.Port = port
|
|
}
|
|
|
|
if logLevel := os.Getenv("UZDB_LOG_LEVEL"); logLevel != "" {
|
|
c.Logger.Level = logLevel
|
|
}
|
|
|
|
if dbPath := os.Getenv("UZDB_DB_PATH"); dbPath != "" {
|
|
c.Database.SQLitePath = dbPath
|
|
}
|
|
}
|
|
|
|
// save saves configuration to file
|
|
func (c *Config) save(path string) error {
|
|
data, err := json.MarshalIndent(c, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(path, data, 0600)
|
|
}
|
|
|
|
// initLogger initializes the zap logger
|
|
func (c *Config) initLogger() error {
|
|
var cfg zap.Config
|
|
|
|
switch c.Logger.Format {
|
|
case "json":
|
|
cfg = zap.NewProductionConfig()
|
|
default:
|
|
cfg = zap.NewDevelopmentConfig()
|
|
}
|
|
|
|
// Set log level
|
|
level, parseErr := zapcore.ParseLevel(c.Logger.Level)
|
|
if parseErr != nil {
|
|
level = zapcore.InfoLevel
|
|
}
|
|
cfg.Level.SetLevel(level)
|
|
|
|
// Configure output
|
|
if c.Logger.OutputPath != "" {
|
|
cfg.OutputPaths = []string{c.Logger.OutputPath}
|
|
cfg.ErrorOutputPaths = []string{c.Logger.OutputPath}
|
|
}
|
|
|
|
var buildErr error
|
|
logger, buildErr = cfg.Build(
|
|
zap.AddCaller(),
|
|
zap.AddStacktrace(zapcore.ErrorLevel),
|
|
)
|
|
if buildErr != nil {
|
|
return buildErr
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsDevelopment returns true if running in development mode
|
|
func (c *Config) IsDevelopment() bool {
|
|
return c.Environment == EnvDevelopment
|
|
}
|
|
|
|
// IsProduction returns true if running in production mode
|
|
func (c *Config) IsProduction() bool {
|
|
return c.Environment == EnvProduction
|
|
}
|
|
|
|
// Sync flushes any buffered log entries
|
|
func Sync() error {
|
|
if logger != nil {
|
|
return logger.Sync()
|
|
}
|
|
return nil
|
|
}
|