143 lines
3.9 KiB
Go
143 lines
3.9 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"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()
|
|
}
|
|
}
|
|
|
|
// Health checks the health of the database connection by pinging the database.
|
|
// It returns a map with keys indicating various health statistics.
|
|
func (db *Database) Health() map[string]string {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
stats := make(map[string]string)
|
|
|
|
// Ping the database
|
|
err := db.Pool.Ping(ctx)
|
|
if err != nil {
|
|
stats["status"] = "down"
|
|
stats["error"] = err.Error()
|
|
return stats
|
|
}
|
|
|
|
stats["status"] = "up"
|
|
return stats
|
|
}
|
|
|
|
// 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
|
|
}
|