package database import ( "database/sql" "fmt" "log" "os" _ "github.com/lib/pq" ) var DB *sql.DB func InitDB() { var err error host := os.Getenv("DB_HOST") if host == "" { log.Fatal("DB_HOST environment variable not set") } user := os.Getenv("DB_USER") if user == "" { log.Fatal("DB_USER environment variable not set") } password := os.Getenv("DB_PASSWORD") if password == "" { log.Fatal("DB_PASSWORD environment variable not set") } dbname := os.Getenv("DB_NAME") if dbname == "" { log.Fatal("DB_NAME environment variable not set") } port := os.Getenv("DB_PORT") if port == "" { port = "5432" } sslmode := os.Getenv("DB_SSLMODE") if sslmode == "" { sslmode = "require" // Default to require for production security } connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", host, port, user, password, dbname, sslmode) DB, err = sql.Open("postgres", connStr) if err != nil { log.Fatalf("Error opening database: %v", err) } if err = DB.Ping(); err != nil { log.Fatalf("Error connecting to database: %v", err) } log.Println("Successfully connected to the database") } func RunMigrations() { files, err := os.ReadDir("migrations") if err != nil { // Try fallback to relative path if running from cmd/api files, err = os.ReadDir("../../migrations") if err != nil { log.Printf("Warning: Could not list migrations directory: %v", err) return } } // Sort files by name to ensure order (001, 002, ...) // ReadDir returns sorted by name automatically, but let's be safe if logic changes? // Actually ReadDir result is sorted by filename. for _, file := range files { if file.IsDir() { continue } log.Printf("Running migration: %s", file.Name()) content, err := os.ReadFile("migrations/" + file.Name()) if err != nil { // Try fallback content, err = os.ReadFile("../../migrations/" + file.Name()) if err != nil { log.Fatalf("Error reading migration file %s: %v", file.Name(), err) } } _, err = DB.Exec(string(content)) if err != nil { // Log warning but don't crash on "already exists" errors if possible, // but pure SQL Exec might fail hard. // Given the SQL files use IF NOT EXISTS, we should be fine. // If one fails, it might be syntax. log.Printf("Error running migration %s: %v", file.Name(), err) // Continue or Fail? User wants robustness. Let's Warn and continue for now to avoid single failure blocking all } else { log.Printf("Migration %s executed successfully", file.Name()) } } log.Println("All migrations processed") }