refactor: restructure database initialization and migration logic, moving to models package

This commit is contained in:
Jean Jacques Avril 2025-03-31 19:21:49 +00:00
parent bcc3aadb85
commit a9c7598862
3 changed files with 79 additions and 36 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/timetracker/backend/internal/config"
"github.com/timetracker/backend/internal/db"
"github.com/timetracker/backend/internal/models"
"gorm.io/gorm/logger"
)
@ -102,7 +103,7 @@ func main() {
// Run migrations
fmt.Println("Running database migrations...")
if err = db.MigrateDB(); err != nil {
if err = models.MigrateDB(); err != nil {
log.Fatalf("Error migrating database: %v", err)
}
fmt.Println("✓ Database migrations completed successfully")

View File

@ -2,6 +2,7 @@ package db
import (
"context"
"errors"
"fmt"
"log"
"time"
@ -15,53 +16,30 @@ import (
// Global variable for the DB connection
var db *gorm.DB
// ErrDBNotInitialized is returned when a database operation is attempted before initialization
var ErrDBNotInitialized = errors.New("database not initialized")
// InitDB initializes the database connection (once at startup)
// with the provided configuration
func InitDB(config config.DatabaseConfig) error {
// Create DSN (Data Source Name)
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
config.Host, config.Port, config.User, config.Password, config.DBName, config.SSLMode)
// Configure GORM logger
gormLogger := logger.New(
log.New(log.Writer(), "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: 200 * time.Millisecond, // Slow SQL threshold
LogLevel: config.LogLevel, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
Colorful: true, // Enable color
},
)
// Establish database connection with custom logger
var err error
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: gormLogger,
})
// Create connection using the default database name
gormDB, err := createConnection(config, config.DBName)
if err != nil {
return fmt.Errorf("error connecting to the database: %w", err)
return err
}
// Set the global db instance
db = gormDB
// Configure connection pool
sqlDB, err := db.DB()
if err != nil {
return fmt.Errorf("error getting database connection: %w", err)
}
// Set connection pool parameters
sqlDB.SetMaxIdleConns(config.MaxIdleConns)
sqlDB.SetMaxOpenConns(config.MaxOpenConns)
sqlDB.SetConnMaxLifetime(config.MaxLifetime)
return nil
return configureConnectionPool(db, config)
}
// GetEngine returns the DB instance, possibly with context
// GetEngine returns the DB instance with context
func GetEngine(ctx context.Context) *gorm.DB {
if db == nil {
panic("database not initialized")
panic(ErrDBNotInitialized)
}
// If a special transaction is in ctx, you could check it here
return db.WithContext(ctx)
}
@ -82,3 +60,67 @@ func CloseDB() error {
return nil
}
// GetGormDB is used for special cases like database creation
func GetGormDB(dbConfig config.DatabaseConfig, dbName string) (*gorm.DB, error) {
return createConnection(dbConfig, dbName)
}
// MigrateDB performs database migrations for all models
// This is a placeholder that will be called by models.MigrateDB
func MigrateDB() error {
if db == nil {
return ErrDBNotInitialized
}
// The actual migration is implemented in models.MigrateDB
// This is just a placeholder to make the migrate/main.go file work
return errors.New("MigrateDB should be called from models package")
}
// createConnection creates a new database connection with the given configuration
func createConnection(dbConfig config.DatabaseConfig, dbName string) (*gorm.DB, error) {
// Create DSN (Data Source Name)
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbName=%s sslmode=%s",
dbConfig.Host, dbConfig.Port, dbConfig.User, dbConfig.Password, dbName, dbConfig.SSLMode)
// Configure GORM logger
gormLogger := createGormLogger(dbConfig)
// Establish database connection with custom logger
gormDB, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: gormLogger,
})
if err != nil {
return nil, fmt.Errorf("error connecting to the database: %w", err)
}
return gormDB, nil
}
// createGormLogger creates a configured GORM logger instance
func createGormLogger(dbConfig config.DatabaseConfig) logger.Interface {
return logger.New(
log.New(log.Writer(), "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: 200 * time.Millisecond, // Slow SQL threshold
LogLevel: dbConfig.LogLevel, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
Colorful: true, // Enable color
},
)
}
// configureConnectionPool sets up the connection pool parameters
func configureConnectionPool(db *gorm.DB, config config.DatabaseConfig) error {
sqlDB, err := db.DB()
if err != nil {
return fmt.Errorf("error getting database connection: %w", err)
}
// Set connection pool parameters
sqlDB.SetMaxIdleConns(config.MaxIdleConns)
sqlDB.SetMaxOpenConns(config.MaxOpenConns)
sqlDB.SetConnMaxLifetime(config.MaxLifetime)
return nil
}

BIN
backend/migrate Executable file

Binary file not shown.