113 lines
2.3 KiB
Go
113 lines
2.3 KiB
Go
|
|
package database
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"log"
|
||
|
|
|
||
|
|
"attune-heart-therapy/internal/config"
|
||
|
|
"attune-heart-therapy/internal/models"
|
||
|
|
|
||
|
|
"gorm.io/driver/postgres"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
"gorm.io/gorm/logger"
|
||
|
|
)
|
||
|
|
|
||
|
|
// DB holds the database connection
|
||
|
|
type DB struct {
|
||
|
|
*gorm.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
// New creates a new database connection
|
||
|
|
func New(cfg *config.Config) (*DB, error) {
|
||
|
|
dsn := fmt.Sprintf(
|
||
|
|
"host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=UTC",
|
||
|
|
cfg.Database.Host,
|
||
|
|
cfg.Database.User,
|
||
|
|
cfg.Database.Password,
|
||
|
|
cfg.Database.Name,
|
||
|
|
cfg.Database.Port,
|
||
|
|
cfg.Database.SSLMode,
|
||
|
|
)
|
||
|
|
|
||
|
|
// Configure GORM logger
|
||
|
|
gormConfig := &gorm.Config{
|
||
|
|
Logger: logger.Default.LogMode(logger.Info),
|
||
|
|
}
|
||
|
|
|
||
|
|
db, err := gorm.Open(postgres.Open(dsn), gormConfig)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("failed to connect to database: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Configure connection pool
|
||
|
|
sqlDB, err := db.DB()
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("failed to get underlying sql.DB: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set connection pool settings
|
||
|
|
sqlDB.SetMaxIdleConns(10)
|
||
|
|
sqlDB.SetMaxOpenConns(100)
|
||
|
|
|
||
|
|
log.Println("Successfully connected to PostgreSQL database")
|
||
|
|
|
||
|
|
return &DB{db}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Close closes the database connection
|
||
|
|
func (db *DB) Close() error {
|
||
|
|
sqlDB, err := db.DB.DB()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return sqlDB.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Migrate runs database migrations
|
||
|
|
func (db *DB) Migrate() error {
|
||
|
|
log.Println("Running database migrations...")
|
||
|
|
|
||
|
|
// Auto-migrate all models
|
||
|
|
err := db.AutoMigrate(
|
||
|
|
&models.User{},
|
||
|
|
&models.Schedule{},
|
||
|
|
&models.Booking{},
|
||
|
|
&models.Notification{},
|
||
|
|
)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to run migrations: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Println("Database migrations completed successfully")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Seed creates initial data for the database
|
||
|
|
func (db *DB) Seed() error {
|
||
|
|
log.Println("Seeding database with initial data...")
|
||
|
|
|
||
|
|
// Check if we already have data to avoid duplicate seeding
|
||
|
|
var userCount int64
|
||
|
|
db.Model(&models.User{}).Count(&userCount)
|
||
|
|
|
||
|
|
if userCount > 0 {
|
||
|
|
log.Println("Database already contains data, skipping seeding")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add any initial data here if needed
|
||
|
|
// For now, we'll just log that seeding is complete
|
||
|
|
log.Println("Database seeding completed")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Health checks database connectivity
|
||
|
|
func (db *DB) Health() error {
|
||
|
|
sqlDB, err := db.DB.DB()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return sqlDB.Ping()
|
||
|
|
}
|