Files
GoMembership/go-backend/internal/database/db.go
2025-04-10 15:40:22 +02:00

200 lines
4.6 KiB
Go

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()
}