package db import ( "database/sql" "fmt" "net/url" "os" "pinepods/gpodder-api/config" "strings" _ "github.com/lib/pq" ) // PostgresDB represents a connection to the PostgreSQL database type PostgresDB struct { *sql.DB } // NewPostgresDB creates a new PostgreSQL database connection func NewPostgresDB(cfg config.DatabaseConfig) (*PostgresDB, error) { // Print connection details for debugging (hide password for security) fmt.Printf("Connecting to database: host=%s port=%d user=%s dbname=%s sslmode=%s\n", cfg.Host, cfg.Port, cfg.User, cfg.DBName, cfg.SSLMode) // Get password directly from environment to handle special characters password := os.Getenv("DB_PASSWORD") if password == "" { // Fall back to config if env var is empty password = cfg.Password } // Escape special characters in password escapedPassword := url.QueryEscape(password) // Use a connection string without password for logging logConnStr := fmt.Sprintf( "host=%s port=%d user=%s dbname=%s sslmode=%s", cfg.Host, cfg.Port, cfg.User, cfg.DBName, cfg.SSLMode, ) fmt.Printf("Connection string (without password): %s\n", logConnStr) // Build the actual connection string with password connStr := fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", cfg.Host, cfg.Port, cfg.User, password, cfg.DBName, cfg.SSLMode, ) // Try alternate connection string format if the first fails db, err := sql.Open("postgres", connStr) if err != nil { // Try URL format connection string urlConnStr := fmt.Sprintf( "postgres://%s:%s@%s:%d/%s?sslmode=%s", cfg.User, escapedPassword, cfg.Host, cfg.Port, cfg.DBName, cfg.SSLMode, ) fmt.Println("First connection attempt failed, trying URL format...") db, err = sql.Open("postgres", urlConnStr) if err != nil { return nil, fmt.Errorf("failed to open database connection: %w", err) } } // Test the connection if err := db.Ping(); err != nil { db.Close() // Check if error contains password authentication failure if strings.Contains(err.Error(), "password authentication failed") { // Print environment variables (hide password) fmt.Println("Password authentication failed. Environment variables:") fmt.Printf("DB_HOST=%s\n", os.Getenv("DB_HOST")) fmt.Printf("DB_PORT=%s\n", os.Getenv("DB_PORT")) fmt.Printf("DB_USER=%s\n", os.Getenv("DB_USER")) fmt.Printf("DB_NAME=%s\n", os.Getenv("DB_NAME")) fmt.Printf("DB_PASSWORD=*** (length: %d)\n", len(os.Getenv("DB_PASSWORD"))) } return nil, fmt.Errorf("failed to ping database: %w", err) } fmt.Println("Successfully connected to the database") // Migrations are now handled by the Python migration system // Skip Go migrations to avoid conflicts fmt.Println("Skipping Go migrations - now handled by Python migration system") return &PostgresDB{DB: db}, nil } // Close closes the database connection func (db *PostgresDB) Close() error { return db.DB.Close() }