feat: Refactor database configuration loading and seeding logic for improved clarity and maintainability
This commit is contained in:
		
							parent
							
								
									a0b0b98624
								
							
						
					
					
						commit
						ec250570a6
					
				
							
								
								
									
										13
									
								
								.clinerules
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								.clinerules
									
									
									
									
									
								
							@ -33,14 +33,17 @@
 | 
				
			|||||||
- Client components must use TanStack Query
 | 
					- Client components must use TanStack Query
 | 
				
			||||||
- UI state management via Zustand
 | 
					- UI state management via Zustand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
7. DEVOPS
 | 
					 | 
				
			||||||
- Docker builds must pass Hadolint checks
 | 
					 | 
				
			||||||
- Kubernetes manifests in gitops/ directory
 | 
					 | 
				
			||||||
- Monitoring via OpenTelemetry instrumentation
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
8. DEVELOPMENT WORKFLOW
 | 
					8. DEVELOPMENT WORKFLOW
 | 
				
			||||||
- Use Makefile commands for common tasks:
 | 
					- Makefile commands are only available in the backend folder
 | 
				
			||||||
 | 
					- Common make commands:
 | 
				
			||||||
  - make generate: Run code generation (tygo, swagger, etc.)
 | 
					  - make generate: Run code generation (tygo, swagger, etc.)
 | 
				
			||||||
  - make test: Run all tests
 | 
					  - make test: Run all tests
 | 
				
			||||||
  - make build: Build the application
 | 
					  - make build: Build the application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					9. CUSTOM RULES
 | 
				
			||||||
 | 
					- Add custom rules to .clinerules if:
 | 
				
			||||||
 | 
					  - Unexpected behavior is encountered
 | 
				
			||||||
 | 
					  - Specific conditions require warnings
 | 
				
			||||||
 | 
					  - New patterns emerge that need documentation
 | 
				
			||||||
  - make run: Start the development server
 | 
					  - make run: Start the development server
 | 
				
			||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Time Tracker Backend Makefile
 | 
					# Time Tracker Backend Makefile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: db-start db-stop db-test model-test run build clean migrate seed help
 | 
					.PHONY: db-start db-stop db-test model-test run build clean migrate seed help
 | 
				
			||||||
 | 
				
			|||||||
@ -86,12 +86,6 @@ func main() {
 | 
				
			|||||||
		log.Fatalf("Error migrating database: %v", err)
 | 
							log.Fatalf("Error migrating database: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Seed database with initial data if needed
 | 
					 | 
				
			||||||
	ctx := context.Background()
 | 
					 | 
				
			||||||
	if err := models.SeedDB(ctx); err != nil {
 | 
					 | 
				
			||||||
		log.Fatalf("Error seeding database: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create Gin router
 | 
						// Create Gin router
 | 
				
			||||||
	r := gin.Default()
 | 
						r := gin.Default()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,56 +2,28 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/timetracker/backend/internal/config"
 | 
				
			||||||
	"github.com/timetracker/backend/internal/models"
 | 
						"github.com/timetracker/backend/internal/models"
 | 
				
			||||||
	"gorm.io/gorm/logger"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	// Parse command line flags
 | 
						// Parse CLI flags
 | 
				
			||||||
	force := false
 | 
						_ = flag.String("config", "", "Path to .env config file")
 | 
				
			||||||
	for _, arg := range os.Args[1:] {
 | 
						flag.Parse()
 | 
				
			||||||
		if arg == "--force" || arg == "-f" {
 | 
					 | 
				
			||||||
			force = true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get database configuration with sensible defaults
 | 
						// Load configuration
 | 
				
			||||||
	dbConfig := models.DefaultDatabaseConfig()
 | 
						cfg, err := config.LoadConfig()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
	// Override with environment variables if provided
 | 
							log.Fatalf("Failed to load config: %v", err)
 | 
				
			||||||
	if host := os.Getenv("DB_HOST"); host != "" {
 | 
					 | 
				
			||||||
		dbConfig.Host = host
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if port := os.Getenv("DB_PORT"); port != "" {
 | 
					 | 
				
			||||||
		var portInt int
 | 
					 | 
				
			||||||
		if _, err := fmt.Sscanf(port, "%d", &portInt); err == nil && portInt > 0 {
 | 
					 | 
				
			||||||
			dbConfig.Port = portInt
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if user := os.Getenv("DB_USER"); user != "" {
 | 
					 | 
				
			||||||
		dbConfig.User = user
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if password := os.Getenv("DB_PASSWORD"); password != "" {
 | 
					 | 
				
			||||||
		dbConfig.Password = password
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if dbName := os.Getenv("DB_NAME"); dbName != "" {
 | 
					 | 
				
			||||||
		dbConfig.DBName = dbName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if sslMode := os.Getenv("DB_SSLMODE"); sslMode != "" {
 | 
					 | 
				
			||||||
		dbConfig.SSLMode = sslMode
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Set log level
 | 
					 | 
				
			||||||
	dbConfig.LogLevel = logger.Info
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Initialize database
 | 
						// Initialize database
 | 
				
			||||||
	fmt.Println("Connecting to database...")
 | 
						if err := models.InitDB(cfg.Database); err != nil {
 | 
				
			||||||
	if err := models.InitDB(dbConfig); err != nil {
 | 
					 | 
				
			||||||
		log.Fatalf("Error initializing database: %v", err)
 | 
							log.Fatalf("Error initializing database: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
@ -59,31 +31,62 @@ func main() {
 | 
				
			|||||||
			log.Printf("Error closing database connection: %v", err)
 | 
								log.Printf("Error closing database connection: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	fmt.Println("✓ Database connection successful")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create context with timeout
 | 
						// Execute seed operation
 | 
				
			||||||
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
 | 
						if err := seedDatabase(context.Background()); err != nil {
 | 
				
			||||||
	defer cancel()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if we need to seed (e.g., no companies exist)
 | 
					 | 
				
			||||||
	if !force {
 | 
					 | 
				
			||||||
		var count int64
 | 
					 | 
				
			||||||
		db := models.GetEngine(ctx)
 | 
					 | 
				
			||||||
		if err := db.Model(&models.Company{}).Count(&count).Error; err != nil {
 | 
					 | 
				
			||||||
			log.Fatalf("Error checking if seeding is needed: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// If data already exists, skip seeding
 | 
					 | 
				
			||||||
		if count > 0 {
 | 
					 | 
				
			||||||
			fmt.Println("Database already contains data. Use --force to override.")
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Seed the database
 | 
					 | 
				
			||||||
	fmt.Println("Seeding database with initial data...")
 | 
					 | 
				
			||||||
	if err := models.SeedDB(ctx); err != nil {
 | 
					 | 
				
			||||||
		log.Fatalf("Error seeding database: %v", err)
 | 
							log.Fatalf("Error seeding database: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fmt.Println("✓ Database seeding completed successfully")
 | 
					
 | 
				
			||||||
 | 
						log.Println("Database seeding completed successfully")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// seedDatabase performs the database seeding operation
 | 
				
			||||||
 | 
					func seedDatabase(ctx context.Context) error {
 | 
				
			||||||
 | 
						// Check if seeding is needed
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
						if err := models.GetEngine(ctx).Model(&models.Company{}).Count(&count).Error; err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("error checking if seeding is needed: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If data exists, skip seeding
 | 
				
			||||||
 | 
						if count > 0 {
 | 
				
			||||||
 | 
							log.Println("Database already contains data, skipping seeding")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Println("Seeding database with initial data...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start transaction
 | 
				
			||||||
 | 
						return models.GetEngine(ctx).Transaction(func(tx *gorm.DB) error {
 | 
				
			||||||
 | 
							// Create default company
 | 
				
			||||||
 | 
							defaultCompany := models.Company{
 | 
				
			||||||
 | 
								Name: "Default Company",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := tx.Create(&defaultCompany).Error; err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("error creating default company: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Create admin user
 | 
				
			||||||
 | 
							adminUser := models.User{
 | 
				
			||||||
 | 
								Email:      "admin@example.com",
 | 
				
			||||||
 | 
								Role:       models.RoleAdmin,
 | 
				
			||||||
 | 
								CompanyID:  defaultCompany.ID,
 | 
				
			||||||
 | 
								HourlyRate: 100.0,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Hash password
 | 
				
			||||||
 | 
							pwData, err := models.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)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ go 1.23.6
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/gin-gonic/gin v1.10.0
 | 
						github.com/gin-gonic/gin v1.10.0
 | 
				
			||||||
 | 
						github.com/joho/godotenv v1.5.1
 | 
				
			||||||
	github.com/oklog/ulid/v2 v2.1.0
 | 
						github.com/oklog/ulid/v2 v2.1.0
 | 
				
			||||||
	github.com/swaggo/files v1.0.1
 | 
						github.com/swaggo/files v1.0.1
 | 
				
			||||||
	github.com/swaggo/gin-swagger v1.6.0
 | 
						github.com/swaggo/gin-swagger v1.6.0
 | 
				
			||||||
 | 
				
			|||||||
@ -54,6 +54,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
 | 
				
			|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
					github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
				
			||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 | 
					github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 | 
				
			||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
					github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
				
			||||||
 | 
					github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 | 
				
			||||||
 | 
					github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 | 
				
			||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 | 
					github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 | 
				
			||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 | 
					github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 | 
				
			||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
					github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										86
									
								
								backend/internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								backend/internal/config/config.go
									
									
									
									
									
										Normal 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -114,64 +114,6 @@ func MigrateDB() error {
 | 
				
			|||||||
	return nil
 | 
						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
 | 
					// GetEngine returns the DB instance, possibly with context
 | 
				
			||||||
func GetEngine(ctx context.Context) *gorm.DB {
 | 
					func GetEngine(ctx context.Context) *gorm.DB {
 | 
				
			||||||
	if defaultDB == nil {
 | 
						if defaultDB == nil {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user