feat: Refactor database configuration loading and seeding logic for improved clarity and maintainability

This commit is contained in:
2025-03-11 12:35:04 +00:00
parent a0b0b98624
commit ec250570a6
8 changed files with 164 additions and 132 deletions
+86
View File
@@ -0,0 +1,86 @@
package config
import (
"errors"
"fmt"
"log"
"os"
"strconv"
"github.com/joho/godotenv"
"github.com/timetracker/backend/internal/models"
"gorm.io/gorm/logger"
)
// Config represents the application configuration
type Config struct {
Database models.DatabaseConfig
}
// LoadConfig loads configuration from environment variables and .env file
func LoadConfig() (*Config, error) {
// Try loading .env file, but don't fail if it doesn't exist
_ = godotenv.Load()
cfg := &Config{
Database: models.DefaultDatabaseConfig(),
}
// Load database configuration
if err := loadDatabaseConfig(cfg); err != nil {
return nil, fmt.Errorf("failed to load database config: %w", err)
}
return cfg, nil
}
// loadDatabaseConfig loads database configuration from environment
func loadDatabaseConfig(cfg *Config) error {
// Required fields
cfg.Database.Host = getEnv("DB_HOST", cfg.Database.Host)
cfg.Database.User = getEnv("DB_USER", cfg.Database.User)
cfg.Database.Password = getEnv("DB_PASSWORD", cfg.Database.Password)
cfg.Database.DBName = getEnv("DB_NAME", cfg.Database.DBName)
cfg.Database.SSLMode = getEnv("DB_SSLMODE", cfg.Database.SSLMode)
// Optional fields with parsing
if port := getEnv("DB_PORT", ""); port != "" {
portInt, err := strconv.Atoi(port)
if err != nil || portInt <= 0 {
return errors.New("invalid DB_PORT value")
}
cfg.Database.Port = portInt
}
// Log level based on environment
if os.Getenv("ENVIRONMENT") == "production" {
cfg.Database.LogLevel = logger.Error
} else {
cfg.Database.LogLevel = logger.Info
}
// Validate required fields
if cfg.Database.Host == "" || cfg.Database.User == "" ||
cfg.Database.Password == "" || cfg.Database.DBName == "" {
return errors.New("missing required database configuration")
}
return nil
}
// getEnv gets an environment variable with fallback
func getEnv(key, fallback string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
return fallback
}
// MustLoadConfig loads configuration or panics on failure
func MustLoadConfig() *Config {
cfg, err := LoadConfig()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
return cfg
}
-58
View File
@@ -114,64 +114,6 @@ func MigrateDB() error {
return nil
}
// SeedDB seeds the database with initial data if needed
func SeedDB(ctx context.Context) error {
if defaultDB == nil {
return errors.New("database not initialized")
}
log.Println("Checking if database seeding is needed...")
// Check if we need to seed (e.g., no companies exist)
var count int64
if err := defaultDB.Model(&Company{}).Count(&count).Error; err != nil {
return fmt.Errorf("error checking if seeding is needed: %w", err)
}
// If data already exists, skip seeding
if count > 0 {
log.Println("Database already contains data, skipping seeding")
return nil
}
log.Println("Seeding database with initial data...")
// Start a transaction for all seed operations
return defaultDB.Transaction(func(tx *gorm.DB) error {
// Create a default company
defaultCompany := Company{
Name: "Default Company",
}
if err := tx.Create(&defaultCompany).Error; err != nil {
return fmt.Errorf("error creating default company: %w", err)
}
// Create an admin user
adminUser := User{
Email: "admin@example.com",
Role: RoleAdmin,
CompanyID: defaultCompany.ID,
HourlyRate: 100.0,
}
// Hash a default password
pwData, err := HashPassword("Admin@123456")
if err != nil {
return fmt.Errorf("error hashing password: %w", err)
}
adminUser.Salt = pwData.Salt
adminUser.Hash = pwData.Hash
if err := tx.Create(&adminUser).Error; err != nil {
return fmt.Errorf("error creating admin user: %w", err)
}
log.Println("Database seeding completed successfully")
return nil
})
}
// GetEngine returns the DB instance, possibly with context
func GetEngine(ctx context.Context) *gorm.DB {
if defaultDB == nil {