package app import ( "fmt" "log" "os" "time" "gorm.io/driver/postgres" "gorm.io/gorm" gormlogger "gorm.io/gorm/logger" "github.com/gochat/gochat/internal/config" applogger "github.com/gochat/gochat/pkg/logger" ) // NewDatabase creates a GORM database connection. // Pattern follows Chatwoot's ActiveRecord PostgreSQL setup in config/database.yml func NewDatabase(cfg *config.DatabaseConfig, logLevel string) (*gorm.DB, error) { dsn := cfg.DSN // Map log level to GORM logger level var gormLogLevel gormlogger.LogLevel switch logLevel { case "silent": gormLogLevel = gormlogger.Silent case "error": gormLogLevel = gormlogger.Error case "warn": gormLogLevel = gormlogger.Warn case "info": gormLogLevel = gormlogger.Info default: gormLogLevel = gormlogger.Info } db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: newDatabaseLogger(gormLogLevel), PrepareStmt: true, DisableForeignKeyConstraintWhenMigrating: true, // migrate first, add FK constraints later }) if err != nil { return nil, fmt.Errorf("failed to connect to database: %w", err) } // Configure connection pool (ref: Chatwoot config/database.yml pool settings) sqlDB, err := db.DB() if err != nil { return nil, fmt.Errorf("failed to get underlying sql.DB: %w", err) } sqlDB.SetMaxOpenConns(cfg.MaxOpenConns) sqlDB.SetMaxIdleConns(cfg.MaxIdleConns) sqlDB.SetConnMaxLifetime(5 * time.Minute) sqlDB.SetConnMaxIdleTime(10 * time.Minute) // Set default statement timeout (ref: Chatwoot POSTGRES_STATEMENT_TIMEOUT) db.Set("gorm:statement_timeout", "5000ms") applogger.L().Infof("Connected to PostgreSQL (pool=%d)", cfg.MaxOpenConns) return db, nil } func newDatabaseLogger(level gormlogger.LogLevel) gormlogger.Interface { return gormlogger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), gormlogger.Config{ SlowThreshold: 200 * time.Millisecond, LogLevel: level, IgnoreRecordNotFoundError: true, ParameterizedQueries: true, }) }