package database import ( "GoMembership/internal/constants" "GoMembership/internal/models" "GoMembership/pkg/logger" "crypto/rand" "encoding/base64" "errors" "fmt" "time" "gorm.io/driver/sqlite" "gorm.io/gorm" ) var DB *gorm.DB func Open(dbPath string, adminMail string, debug bool) (*gorm.DB, error) { // Add foreign key support and WAL journal mode to DSN dsn := fmt.Sprintf("%s?_foreign_keys=1&_journal_mode=WAL", dbPath) db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{ // Enable PrepareStmt for better performance PrepareStmt: true, }) if err != nil { return nil, fmt.Errorf("failed to connect database: %w", err) } // Verify foreign key support is enabled var foreignKeyEnabled int if err := db.Raw("PRAGMA foreign_keys").Scan(&foreignKeyEnabled).Error; err != nil { return nil, fmt.Errorf("foreign key check failed: %w", err) } if foreignKeyEnabled != 1 { return nil, errors.New("SQLite foreign key constraints not enabled") } if debug { db = db.Debug() } // Configure connection pool sqlDB, err := db.DB() if err != nil { return nil, fmt.Errorf("failed to get DB instance: %w", err) } sqlDB.SetMaxOpenConns(1) // Required for SQLite in production sqlDB.SetMaxIdleConns(1) sqlDB.SetConnMaxLifetime(time.Hour) if err := db.AutoMigrate( &models.User{}, &models.Subscription{}, &models.Membership{}, &models.Consent{}, &models.Verification{}, &models.BankAccount{}, &models.Licence{}, &models.Category{}, &models.Car{}, &models.Location{}, &models.Damage{}, &models.Insurance{}, ); err != nil { return nil, fmt.Errorf("failed to migrate database: %w", err) } logger.Info.Print("Opened DB") DB = db var categoriesCount int64 db.Model(&models.Category{}).Count(&categoriesCount) if categoriesCount == 0 { categories := createLicenceCategories() for _, model := range categories { result := db.Create(&model) if result.Error != nil { return nil, result.Error } } } var subscriptionsCount int64 db.Model(&models.Subscription{}).Count(&subscriptionsCount) subscriptions := createSubscriptions() for _, model := range subscriptions { var exists int64 db. Model(&models.Subscription{}). Where("name = ?", model.Name). Count(&exists) logger.Error.Printf("looked for model.name %v and found %v", model.Name, exists) if exists == 0 { result := db.Create(&model) if result.Error != nil { return nil, result.Error } } } var userCount int64 db.Model(&models.User{}).Count(&userCount) if userCount == 0 { var createdModel models.Subscription if err := db.First(&createdModel).Error; err != nil { return nil, err } admin, err := createAdmin(adminMail) if err != nil { return nil, err } admin.Create(db) } return db, nil } func createSubscriptions() []models.Subscription { return []models.Subscription{ { Name: constants.SupporterSubscriptionName, Details: "Dieses Modell ist für Sponsoren und Nichtmitglieder, die keinen Vereinsmitglied sind.", HourlyRate: 999, MonthlyFee: 0, }, } } func createLicenceCategories() []models.Category { return []models.Category{ {Name: "AM"}, {Name: "A1"}, {Name: "A2"}, {Name: "A"}, {Name: "B"}, {Name: "C1"}, {Name: "C"}, {Name: "D1"}, {Name: "D"}, {Name: "BE"}, {Name: "C1E"}, {Name: "CE"}, {Name: "D1E"}, {Name: "DE"}, {Name: "T"}, {Name: "L"}, } } // TODO: Landing page to create an admin func createAdmin(userMail string) (*models.User, error) { passwordBytes := make([]byte, 12) _, err := rand.Read(passwordBytes) if err != nil { return nil, err } // Encode into a URL-safe base64 string password := base64.URLEncoding.EncodeToString(passwordBytes)[:12] logger.Error.Print("==============================================================") logger.Error.Printf("Admin Email: %v", userMail) logger.Error.Printf("Admin Password: %v", password) logger.Error.Print("==============================================================") return &models.User{ FirstName: "Ad", LastName: "Min", DateOfBirth: time.Now().AddDate(-20, 0, 0), Password: password, Company: "", Address: "", ZipCode: "", City: "", Phone: "", Notes: "", Email: userMail, Status: constants.ActiveStatus, RoleID: constants.Roles.Admin, Consents: nil, Verifications: nil, Membership: nil, BankAccount: nil, Licence: nil, }, nil //"DE49700500000008447644", //fake } func Close(db *gorm.DB) error { logger.Info.Print("Closing DB") database, err := db.DB() if err != nil { return err } return database.Close() }