package main import ( "clapclap/internal/models" "fmt" "log" "os" "time" "gorm.io/driver/postgres" "gorm.io/gorm" ) func connectDB(user, pass string) *gorm.DB { dsn := fmt.Sprintf( "host=postgres port=5432 user=%s password=%s dbname=musicapp sslmode=disable", user, pass, ) var db *gorm.DB var err error for i := 1; i <= 30; i++ { log.Printf("Connecting to database (attempt %d/30)...", i) db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err == nil { sqlDB, err := db.DB() if err == nil { if err = sqlDB.Ping(); err == nil { log.Println("Database connected") return db } } } log.Printf("Database not ready: %v", err) time.Sleep(2 * time.Second) } log.Fatal("Could not connect to database after retries") return nil } func setupPermissions(db *gorm.DB, ownerRole string) error { stmts := []string{ // Ensure group role exists ` DO $$ BEGIN IF NOT EXISTS ( SELECT FROM pg_roles WHERE rolname = 'musicapp_rw' ) THEN CREATE ROLE musicapp_rw; END IF; END $$; `, // Database access `GRANT CONNECT ON DATABASE musicapp TO musicapp_rw;`, // Schema access `GRANT USAGE ON SCHEMA public TO musicapp_rw;`, // Existing tables ` GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO musicapp_rw; `, // Existing sequences ` GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO musicapp_rw; `, // Future tables created by ownerRole fmt.Sprintf(` ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO musicapp_rw; `, ownerRole), // Future sequences created by ownerRole fmt.Sprintf(` ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO musicapp_rw; `, ownerRole), } for _, stmt := range stmts { if err := db.Exec(stmt).Error; err != nil { return fmt.Errorf("permission bootstrap failed: %w", err) } log.Println("Permission statement executed successfully") } return nil } func runMigrations(db *gorm.DB, dbUser string) { if err := db.Exec(`CREATE EXTENSION IF NOT EXISTS citext`).Error; err != nil { log.Fatal(err) } if err := setupPermissions(db, dbUser); err != nil { log.Fatal(err) } if err := db.AutoMigrate( &models.FriendRequest{}, &models.Genre{}, &models.User{}, &models.Track{}, &models.Comment{}, &models.Rating{}, &models.Project{}, &models.Upload{}, ); err != nil { log.Fatal("Migration failed:", err) } log.Println("Migration succeeded") } func main() { dbUser, ok := os.LookupEnv("POSTGRES_USER") if !ok { log.Fatal("POSTGRES_USER is not set") } dbPass, ok := os.LookupEnv("POSTGRES_PASSWORD") if !ok { log.Fatal("POSTGRES_PASSWORD is not set") } db := connectDB(dbUser, dbPass) runMigrations(db, dbUser) }