Files
MusicServer/internal/db/database.go
T
Sansan 98c1948eff feat: Remove global db.Dbpool with dependency injection (Phase 0)
- Add Database struct in internal/db/database.go with Pool, Ctx, and RunMigrations()
- Update server.go to use Database struct with NewServerInstance()
- Add backend.go with InitBackend(), BackendRepo(), BackendCtx(), BackendPool()
- Update music.go and sync.go to use BackendRepo() and BackendCtx() instead of db.Dbpool/db.Ctx
- Update token_handler.go to accept pool parameter
- Update routes.go to use s.db.Pool for middleware
- Update cmd/main.go to use NewServerInstance() and HTTPServer()
- Update test_helpers.go to initialize backend with test database
- Update test files to use backend.BackendPool() and backend.BackendCtx()

Benefits:
- Easier to mock database for unit tests
- Follows Go best practices (dependency injection)
- Better architecture with explicit dependencies
- RunMigrations() replaces old Migrate_db() function

Note: Global db.Dbpool and db.Ctx still exist in dbHelper.go for backward compatibility
with test_helpers.go, but production code no longer uses them.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-01 18:50:05 +02:00

122 lines
3.4 KiB
Go

package db
import (
"context"
"database/sql"
"fmt"
"music-server/internal/logging"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/database/postgres"
"github.com/golang-migrate/migrate/v4/source/iofs"
"github.com/jackc/pgx/v5/pgxpool"
_ "github.com/lib/pq"
"go.uber.org/zap"
)
// Database holds the database connection pool and context
type Database struct {
Pool *pgxpool.Pool
Ctx context.Context
}
// NewDatabase creates a new Database instance with connection pool
func NewDatabase(host, port, user, password, dbname string) (*Database, error) {
ctx := context.Background()
psqlInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
host, port, user, password, dbname)
logging.GetLogger().Debug("Database connection info",
zap.String("host", host),
zap.String("port", port),
zap.String("dbname", dbname))
pool, err := pgxpool.New(ctx, psqlInfo)
if err != nil {
return nil, fmt.Errorf("unable to connect to database: %w", err)
}
// Test connection
var success string
err = pool.QueryRow(ctx, "select 'Successfully connected!'").Scan(&success)
if err != nil {
pool.Close()
return nil, fmt.Errorf("database query failed: %w", err)
}
logging.GetLogger().Info("Database connected", zap.String("status", success))
return &Database{Pool: pool, Ctx: ctx}, nil
}
// Close closes the database connection pool
func (db *Database) Close() {
if db.Pool != nil {
logging.GetLogger().Info("Closing database connection")
db.Pool.Close()
}
}
// RunMigrations runs all pending database migrations to the latest version.
// Uses the existing pool to extract connection details.
func (db *Database) RunMigrations() error {
// Extract connection info from pool config
connConfig := db.Pool.Config().ConnConfig
migrationURL := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable",
connConfig.User,
connConfig.Password,
connConfig.Host,
connConfig.Port,
connConfig.Database)
logging.GetLogger().Debug("Migration info", zap.String("url", migrationURL))
sqlDb, err := sql.Open("postgres", migrationURL)
if err != nil {
return fmt.Errorf("failed to open database for migration: %w", err)
}
defer sqlDb.Close()
driver, err := postgres.WithInstance(sqlDb, &postgres.Config{})
if err != nil {
return fmt.Errorf("failed to create migration driver: %w", err)
}
files, err := iofs.New(MigrationsFs, "migrations")
if err != nil {
return fmt.Errorf("failed to create migration files: %w", err)
}
m, err := migrate.NewWithInstance("iofs", files, "postgres", driver)
if err != nil {
return fmt.Errorf("failed to create migrator: %w", err)
}
// Get current version for logging
version, _, err := m.Version()
if err != nil {
logging.GetLogger().Error("Failed to get migration version", zap.String("error", err.Error()))
}
logging.GetLogger().Info("Migration version before", zap.Uint("version", version))
// Run all pending migrations to latest version
err = m.Up()
if err != nil {
if err == migrate.ErrNoChange {
logging.GetLogger().Info("Database already up to date")
} else {
return fmt.Errorf("migration failed: %w", err)
}
} else {
// Get new version after migration
versionAfter, _, _ := m.Version()
logging.GetLogger().Info("Migrated to version", zap.Uint("version", versionAfter))
}
logging.GetLogger().Info("Migration completed")
return nil
}