Files
terminalShop/pkg/database/db.go
marcus06 60f52f9851 add ci (#1)
* ci(checks): add pull request workflow and golangci-lint config

* refactor(lint): resolve golangci-lint findings

* style(tui): drop traling blank line for gofmt

* fix(cards): handle crypto/rand.Read error in storeRedirect
2026-05-23 21:43:30 +02:00

109 lines
2.2 KiB
Go

package database
import (
"fmt"
"log"
"strings"
"sync"
"time"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/plugin/opentelemetry/tracing"
)
var (
db *gorm.DB
once sync.Once
)
// Connect initializes the database connection (singleton pattern).
// If dsn starts with postgres:// or postgresql://, connects to PostgreSQL.
// Otherwise falls back to SQLite (development only).
func Connect(dsn string) (*gorm.DB, error) {
var err error
once.Do(func() {
config := &gorm.Config{
Logger: logger.Default.LogMode(logger.Warn),
}
isPostgres := strings.HasPrefix(dsn, "postgres://") ||
strings.HasPrefix(dsn, "postgresql://")
if isPostgres {
db, err = gorm.Open(postgres.Open(dsn), config)
} else {
db, err = gorm.Open(sqlite.Open(dsn), config)
}
if err != nil {
err = fmt.Errorf("failed to connect to database: %w", err)
return
}
if pluginErr := db.Use(tracing.NewPlugin(
tracing.WithoutMetrics(),
tracing.WithoutQueryVariables(),
)); pluginErr != nil {
err = fmt.Errorf("failed to register otel plugin: %w", pluginErr)
return
}
// Configure connection pool for PostgreSQL.
if isPostgres {
sqlDB, poolErr := db.DB()
if poolErr != nil {
err = fmt.Errorf("failed to get sql.DB: %w", poolErr)
return
}
sqlDB.SetMaxOpenConns(25)
sqlDB.SetMaxIdleConns(5)
sqlDB.SetConnMaxLifetime(5 * time.Minute)
}
log.Println("Database connection established")
})
return db, err
}
// GetDB returns the database instance (must call Connect first)
func GetDB() *gorm.DB {
if db == nil {
log.Fatal("Database not initialized. Call Connect() first.")
}
return db
}
// Close closes the database connection
func Close() error {
if db == nil {
return nil
}
sqlDB, err := db.DB()
if err != nil {
return fmt.Errorf("failed to get database instance: %w", err)
}
if err := sqlDB.Close(); err != nil {
return fmt.Errorf("failed to close database: %w", err)
}
log.Println("Database connection closed")
return nil
}
// ResetForTesting resets the singleton for testing purposes
// WARNING: Only use this in tests!
func ResetForTesting() {
if db != nil {
_ = Close()
db = nil
once = sync.Once{}
}
}