package database import ( "errors" "fmt" "log" "strings" "time" "golang.org/x/crypto/bcrypt" "social-raiting.nekiiinkognito.ru/internal/config" "social-raiting.nekiiinkognito.ru/internal/models" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/clause" ) func Connect(cfg config.Config) *gorm.DB { dsn := fmt.Sprintf( "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", cfg.DBUser, cfg.DBPassword, cfg.DBHost, cfg.DBPort, cfg.DBName, ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatalf("failed to connect to mysql: %v", err) } sqlDB, err := db.DB() if err != nil { log.Fatalf("failed to create sql database handle: %v", err) } sqlDB.SetConnMaxLifetime(5 * time.Minute) sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(25) if err := sqlDB.Ping(); err != nil { log.Fatalf("failed to ping mysql: %v", err) } if err := db.AutoMigrate(&models.User{}); err != nil { log.Fatalf("failed to migrate database: %v", err) } if err := db.AutoMigrate(&models.UserSocialRating{}, &models.SocialRatingOperation{}); err != nil { log.Fatalf("failed to migrate database: %v", err) } ensureDefaultAdmin(db, cfg) return db } func ensureDefaultAdmin(db *gorm.DB, cfg config.Config) { email := strings.ToLower(strings.TrimSpace(cfg.DefaultAdminEmail)) password := strings.TrimSpace(cfg.DefaultAdminPassword) if email == "" || password == "" { log.Println("skipping default admin bootstrap because admin credentials are empty") return } var user models.User err := db.Where("email = ?", email).First(&user).Error if err == nil { if !user.IsAdmin { if updateErr := db.Model(&user).Update("is_admin", true).Error; updateErr != nil { log.Fatalf("failed to promote default admin user: %v", updateErr) } } return } if !errors.Is(err, gorm.ErrRecordNotFound) { log.Fatalf("failed to check default admin user: %v", err) } passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { log.Fatalf("failed to hash default admin password: %v", err) } user = models.User{ Email: email, PasswordHash: string(passwordHash), IsAdmin: true, } if err := db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "email"}}, DoNothing: true, }).Create(&user).Error; err != nil { log.Fatalf("failed to create default admin user: %v", err) } if err := db.Where("email = ?", email).First(&user).Error; err != nil { log.Fatalf("failed to load default admin user after bootstrap: %v", err) } if !user.IsAdmin { if err := db.Model(&user).Update("is_admin", true).Error; err != nil { log.Fatalf("failed to ensure default admin privileges: %v", err) } } }