added validation; DB is global now

This commit is contained in:
$(pass /github/name)
2024-07-11 20:59:52 +02:00
parent e4475e2400
commit 03a2b3bdc5
22 changed files with 287 additions and 218 deletions

1
go.mod
View File

@@ -20,6 +20,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/go-playground/validator/v10 v10.22.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect github.com/goccy/go-json v0.10.3 // indirect
github.com/jbub/banking v0.8.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect

2
go.sum
View File

@@ -28,6 +28,8 @@ github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jbub/banking v0.8.0 h1:79kXJj1X2E9dWdWuFNkk2Pw7c6uYPFQS8ev0l+zMFxk=
github.com/jbub/banking v0.8.0/go.mod h1:ctv/bD2EGRR5PobFrJSXZ/FZXCFtUbmVv6v2qf/b/88=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=

View File

@@ -13,18 +13,15 @@ import (
) )
type MembershipController struct { type MembershipController struct {
service services.MembershipService Service services.MembershipService
} }
type MembershipData struct { type MembershipData struct {
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
Model models.SubscriptionModel `json:"model"` Model models.SubscriptionModel `json:"model"`
} }
func NewMembershipController(service services.MembershipService) *MembershipController { func (mc *MembershipController) RegisterSubscription(c *gin.Context) {
return &MembershipController{service}
}
func (uc *MembershipController) RegisterSubscription(c *gin.Context) {
var regData MembershipData var regData MembershipData
if err := c.ShouldBindJSON(&regData); err != nil { if err := c.ShouldBindJSON(&regData); err != nil {
logger.Error.Printf("Couln't decode subscription data: %v", err) logger.Error.Printf("Couln't decode subscription data: %v", err)
@@ -46,7 +43,7 @@ func (uc *MembershipController) RegisterSubscription(c *gin.Context) {
logger.Info.Printf("registering subscription: %+v", regData) logger.Info.Printf("registering subscription: %+v", regData)
// Register Subscription // Register Subscription
id, err := uc.service.RegisterSubscription(&regData.Model) id, err := mc.Service.RegisterSubscription(&regData.Model)
if err != nil { if err != nil {
logger.Error.Printf("Couldn't register Membershipmodel: %v", err) logger.Error.Printf("Couldn't register Membershipmodel: %v", err)
c.JSON(http.StatusInternalServerError, "Couldn't register Membershipmodel") c.JSON(http.StatusInternalServerError, "Couldn't register Membershipmodel")

View File

@@ -4,28 +4,23 @@ import (
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/internal/services" "GoMembership/internal/services"
"github.com/gin-gonic/gin"
"net/http" "net/http"
"github.com/gin-gonic/gin"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
) )
type UserController struct { type UserController struct {
service services.UserService Service services.UserServiceInterface
emailService services.EmailService EmailService *services.EmailService
consentService services.ConsentService ConsentService services.ConsentServiceInterface
bankAccountService services.BankAccountService BankAccountService services.BankAccountServiceInterface
membershipService services.MembershipService MembershipService services.MembershipServiceInterface
} }
type RegistrationData struct { type RegistrationData struct {
User models.User `json:"user"` User models.User `json:"user"`
BankAccount models.BankAccount `json:"bank_account"`
Membership models.Membership `json:"membership"`
}
func NewUserController(service services.UserService, emailService *services.EmailService, consentService services.ConsentService, bankAccountService services.BankAccountService, membershipService services.MembershipService) *UserController {
return &UserController{service, *emailService, consentService, bankAccountService, membershipService}
} }
func (uc *UserController) RegisterUser(c *gin.Context) { func (uc *UserController) RegisterUser(c *gin.Context) {
@@ -37,10 +32,18 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode userdata"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode userdata"})
return return
} }
logger.Info.Printf("registering user: %v", regData.User) logger.Info.Printf("registering user: %#v", regData.User)
selectedModel, err := uc.MembershipService.GetModelByName(&regData.User.Membership.SubscriptionModel.Name)
if err != nil {
logger.Error.Printf("No subscription model found: %#v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Not a valid subscription model"})
return
}
regData.User.Membership.SubscriptionModel = *selectedModel
// Register User // Register User
id, token, err := uc.service.RegisterUser(&regData.User) id, token, err := uc.Service.RegisterUser(&regData.User)
if err != nil { if err != nil {
logger.Error.Printf("Couldn't register User: %v", err) logger.Error.Printf("Couldn't register User: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User"})
@@ -49,12 +52,12 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
regData.User.ID = id regData.User.ID = id
// Register Bank Account // Register Bank Account
_, err = uc.bankAccountService.RegisterBankAccount(&regData.BankAccount) /* _, err = uc.BankAccountService.RegisterBankAccount(&regData.User.BankAccount)
if err != nil { if err != nil {
logger.Error.Printf("Couldn't register bank account: %v", err) logger.Error.Printf("Couldn't register bank account: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-BankAccount"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-BankAccount"})
return return
} } */
// Register Consents // Register Consents
var consents = [2]models.Consent{ var consents = [2]models.Consent{
@@ -72,7 +75,7 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
}, },
} }
for _, consent := range consents { for _, consent := range consents {
_, err = uc.consentService.RegisterConsent(&consent) _, err = uc.ConsentService.RegisterConsent(&consent)
if err != nil { if err != nil {
logger.Error.Printf("Couldn't register consent: %v", err) logger.Error.Printf("Couldn't register consent: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-consent"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-consent"})
@@ -81,21 +84,21 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
} }
// Register Membership // Register Membership
_, err = uc.membershipService.RegisterMembership(&regData.Membership) /* _, err = uc.MembershipService.RegisterMembership(&regData.User.Membership)
if err != nil { if err != nil {
logger.Error.Printf("Couldn't register membership: %v", err) logger.Error.Printf("Couldn't register membership: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-membership"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-membership"})
return return
} } */
// Send notifications // Send notifications
if err := uc.emailService.SendVerificationEmail(&regData.User, &token); err != nil { if err := uc.EmailService.SendVerificationEmail(&regData.User, &token); err != nil {
logger.Error.Printf("Failed to send email verification email to user: %v", err) logger.Error.Printf("Failed to send email verification email to user: %v", err)
// Proceed without returning error since user registration is successful // Proceed without returning error since user registration is successful
} }
// Notify admin of new user registration // Notify admin of new user registration
if err := uc.emailService.NotifyAdminOfNewUser(&regData.User); err != nil { if err := uc.EmailService.NotifyAdminOfNewUser(&regData.User); err != nil {
logger.Error.Printf("Failed to notify admin of new user registration: %v", err) logger.Error.Printf("Failed to notify admin of new user registration: %v", err)
// Proceed without returning error since user registration is successful // Proceed without returning error since user registration is successful
} }
@@ -114,21 +117,21 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
return return
} }
user, err := uc.service.VerifyUser(&token) user, err := uc.Service.VerifyUser(&token)
if err != nil { if err != nil {
logger.Error.Printf("Cannot verify user: %v", err) logger.Error.Printf("Cannot verify user: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Cannot verify user"}) c.JSON(http.StatusUnauthorized, gin.H{"error": "Cannot verify user"})
return return
} }
membership, err := uc.membershipService.FindMembershipByUserID(user.ID) membership, err := uc.MembershipService.FindMembershipByUserID(user.ID)
if err != nil { if err != nil {
logger.Error.Printf("Cannot get membership of user %v: %v", user.ID, err) logger.Error.Printf("Cannot get membership of user %v: %v", user.ID, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Cannot get Membership of user"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Cannot get Membership of user"})
return return
} }
uc.emailService.SendWelcomeEmail(user, membership) uc.EmailService.SendWelcomeEmail(user, membership)
c.Status(http.StatusOK) c.Status(http.StatusOK)
} }
@@ -138,7 +141,7 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"` Password string `json:"password"`
} }
user, err := uc.service.AuthenticateUser(credentials.Email, credentials.Password) user, err := services.UserService.AuthenticateUser(credentials.Email, credentials.Password)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
@@ -153,7 +156,7 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
http.Error(w, "Invalid user ID", http.StatusBadRequest) http.Error(w, "Invalid user ID", http.StatusBadRequest)
return return
} }
user, err := uc.service.GetUserByID(id) user, err := services.UserService.GetUserByID(id)
if err != nil { if err != nil {
http.Error(w, "User not found: "+err.Error(), http.StatusNotFound) http.Error(w, "User not found: "+err.Error(), http.StatusNotFound)
} }

View File

@@ -7,13 +7,13 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
// var DB *gorm.DB var DB *gorm.DB
func InitDB(dbPath string) (*gorm.DB, error) { func InitDB(dbPath string) error {
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil { if err != nil {
return nil, err return err
} }
if err := db.AutoMigrate( if err := db.AutoMigrate(
&models.User{}, &models.User{},
@@ -23,8 +23,8 @@ func InitDB(dbPath string) (*gorm.DB, error) {
&models.Verification{}, &models.Verification{},
&models.BankAccount{}); err != nil { &models.BankAccount{}); err != nil {
logger.Error.Fatalf("Couldn't create database: %v", err) logger.Error.Fatalf("Couldn't create database: %v", err)
return nil, err return err
} }
// DB = db DB = db
return db, nil return nil
} }

View File

@@ -5,12 +5,12 @@ import "time"
type BankAccount struct { type BankAccount struct {
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
MandateDateSigned time.Time `gorm:"not null" json:"mandate_date_signed"` MandateDateSigned time.Time `gorm:"not null"` // json:"mandate_date_signed"`
Bank string `json:"bank_name"` Bank string //`json:"bank_name" validate:"omitempty,alphanumunicode"`
AccountHolderName string `json:"account_holder_name"` AccountHolderName string //`json:"account_holder_name" validate:"omitempty,alphaunicode"`
IBAN string `gorm:"not null" json:"iban"` IBAN string `gorm:"not null" json:"iban" validate:"required,iban"`
BIC string `json:"bic"` BIC string //`json:"bic" validate:"omitempty,bic"`
MandateReference string `gorm:"not null" json:"mandate_reference"` MandateReference string `gorm:"not null"` //json:"mandate_reference"`
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
UserID int64 `json:"user_id"` UserID int64 //`json:"user_id"`
} }

View File

@@ -9,7 +9,7 @@ type Membership struct {
EndDate time.Time `json:"end_date"` EndDate time.Time `json:"end_date"`
Children *[]User `gorm:"foreignKey:ParentMemberID"` Children *[]User `gorm:"foreignKey:ParentMemberID"`
Status string `json:"status"` Status string `json:"status"`
SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID"` SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID" json:"subscription_model"`
SubscriptionModelID int64 `json:"subsription_model_id"` SubscriptionModelID int64 `json:"subsription_model_id"`
ID int64 `json:"id"` ID int64 `json:"id"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`

View File

@@ -7,12 +7,12 @@ import (
type SubscriptionModel struct { type SubscriptionModel struct {
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
Name string `json:"name"` Name string `json:"name" validate:"required,alphaunicode,subscriptionModel"`
Details string `json:"details"` Details string `json:"details" validate:"required"`
Conditions string `json:"conditions"` Conditions string `json:"conditions" validate:"required"`
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
MonthlyFee float32 `json:"monthly_fee"` MonthlyFee float32 `json:"monthly_fee" validate:"required,number"`
HourlyRate float32 `json:"hourly_rate"` HourlyRate float32 `json:"hourly_rate" validate:"required,number"`
IncludedPerYear int16 `json:"included_hours_per_year"` IncludedPerYear int16 `json:"included_hours_per_year" validate:"omitempty,number"`
IncludedPerMonth int16 `json:"included_hours_per_month"` IncludedPerMonth int16 `json:"included_hours_per_month" validate:"omitempty,number"`
} }

View File

@@ -8,24 +8,24 @@ import (
type User struct { type User struct {
UpdatedAt time.Time UpdatedAt time.Time
DateOfBirth time.Time `gorm:"not null" json:"date_of_birth"` DateOfBirth time.Time `gorm:"not null" json:"date_of_birth" validate:"required,age"`
CreatedAt time.Time CreatedAt time.Time
Salt *string `json:"-"` Salt *string `json:"-"`
Phone *string `json:"phone"` Phone string `json:"phone" validate:"omitempty,omitnil,e164"`
Notes *string `json:"notes"` Notes *string `json:"notes"`
FirstName string `gorm:"not null" json:"first_name"` FirstName string `gorm:"not null" json:"first_name" validate:"required,alphaunicode"`
Password string `json:"password"` Password string `json:"password"`
Email string `gorm:"unique;not null" json:"email"` Email string `gorm:"unique;not null" json:"email" validate:"required,email"`
LastName string `gorm:"not null" json:"last_name"` LastName string `gorm:"not null" json:"last_name" validate:"required,alphaunicode"`
ProfilePicture string `json:"profile_picture"` ProfilePicture string `json:"profile_picture" validate:"omitempty,image"`
Address string `gorm:"not null" json:"address"` Address string `gorm:"not null" json:"address" validate:"required"`
ZipCode string `gorm:"not null" json:"zip_code"` ZipCode string `gorm:"not null" json:"zip_code" validate:"required,alphanum"`
City string `form:"not null" json:"city"` City string `form:"not null" json:"city" validate:"required,alphaunicode"`
Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"` Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"`
BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bank_account"`
Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"membership"`
ParentMemberID int64 `json:"parent_member_id"` ParentMemberID int64 `json:"parent_member_id" validate:"omitempty,number"`
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
PaymentStatus int8 `json:"payment_status"` PaymentStatus int8 `json:"payment_status"`
Status int8 `json:"status"` Status int8 `json:"status"`

View File

@@ -1,24 +1,18 @@
package repositories package repositories
import ( import (
"GoMembership/internal/database"
"GoMembership/internal/models" "GoMembership/internal/models"
"gorm.io/gorm"
) )
type BankAccountRepository interface { type BankAccountRepositoryInterface interface {
CreateBankAccount(account *models.BankAccount) (int64, error) CreateBankAccount(account *models.BankAccount) (int64, error)
} }
type bankAccountRepository struct { type BankAccountRepository struct{}
db *gorm.DB
}
func NewBankAccountRepository(db *gorm.DB) BankAccountRepository { func (repo *BankAccountRepository) CreateBankAccount(account *models.BankAccount) (int64, error) {
return &bankAccountRepository{db} result := database.DB.Create(account)
}
func (repo *bankAccountRepository) CreateBankAccount(account *models.BankAccount) (int64, error) {
result := repo.db.Create(account)
if result.Error != nil { if result.Error != nil {
return 0, result.Error return 0, result.Error
} }

View File

@@ -1,24 +1,18 @@
package repositories package repositories
import ( import (
"GoMembership/internal/database"
"GoMembership/internal/models" "GoMembership/internal/models"
"gorm.io/gorm"
) )
type ConsentRepository interface { type ConsentRepositoryInterface interface {
CreateConsent(consent *models.Consent) (int64, error) CreateConsent(consent *models.Consent) (int64, error)
} }
type consentRepository struct { type ConsentRepository struct{}
db *gorm.DB
}
func NewConsentRepository(db *gorm.DB) ConsentRepository { func (repo *ConsentRepository) CreateConsent(consent *models.Consent) (int64, error) {
return &consentRepository{db} result := database.DB.Create(consent)
}
func (repo *consentRepository) CreateConsent(consent *models.Consent) (int64, error) {
result := repo.db.Create(consent)
if result.Error != nil { if result.Error != nil {
return 0, result.Error return 0, result.Error

View File

@@ -1,36 +1,31 @@
package repositories package repositories
import ( import (
"GoMembership/internal/database"
"gorm.io/gorm" "gorm.io/gorm"
"GoMembership/internal/models" "GoMembership/internal/models"
) )
type MembershipRepository interface { type MembershipRepositoryInterface interface {
CreateMembership(membership *models.Membership) (int64, error) CreateMembership(membership *models.Membership) (int64, error)
FindMembershipByUserID(userID int64) (*models.Membership, error) FindMembershipByUserID(userID int64) (*models.Membership, error)
} }
type membershipRepository struct { type MembershipRepository struct{}
db *gorm.DB
}
func NewMembershipRepository(db *gorm.DB) MembershipRepository { func (repo *MembershipRepository) CreateMembership(membership *models.Membership) (int64, error) {
return &membershipRepository{db} result := database.DB.Create(membership)
}
func (repo *membershipRepository) CreateMembership(membership *models.Membership) (int64, error) {
result := repo.db.Create(membership)
if result.Error != nil { if result.Error != nil {
return 0, result.Error return 0, result.Error
} }
return membership.ID, nil return membership.ID, nil
} }
func (repo *membershipRepository) FindMembershipByUserID(userID int64) (*models.Membership, error) { func (repo *MembershipRepository) FindMembershipByUserID(userID int64) (*models.Membership, error) {
var membership models.Membership var membership models.Membership
result := repo.db.First(&membership, userID) result := database.DB.First(&membership, userID)
if result.Error != nil { if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound return nil, gorm.ErrRecordNotFound

View File

@@ -1,27 +1,40 @@
package repositories package repositories
import ( import (
"gorm.io/gorm" "GoMembership/internal/database"
"GoMembership/internal/models" "GoMembership/internal/models"
) )
type SubscriptionModelsRepository interface { type SubscriptionModelsRepositoryInterface interface {
CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error) CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error)
GetMembershipModelNames() ([]string, error)
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
} }
type subscriptionModelsRepository struct { type SubscriptionModelsRepository struct{}
db *gorm.DB
}
func NewSubscriptionModelsRepository(db *gorm.DB) SubscriptionModelsRepository { func (sr *SubscriptionModelsRepository) CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error) {
return &subscriptionModelsRepository{db}
}
func (repo *subscriptionModelsRepository) CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error) { result := database.DB.Create(subscriptionModel)
result := repo.db.Create(subscriptionModel)
if result.Error != nil { if result.Error != nil {
return 0, result.Error return 0, result.Error
} }
return subscriptionModel.ID, nil return subscriptionModel.ID, nil
} }
func (sr *SubscriptionModelsRepository) GetModelByName(modelname *string) (*models.SubscriptionModel, error) {
var model models.SubscriptionModel
if err := database.DB.Where("name = ?", modelname).First(&model).Error; err != nil {
return nil, err
}
return &model, nil
}
func (sr *SubscriptionModelsRepository) GetMembershipModelNames() ([]string, error) {
var names []string
if err := database.DB.Model(&models.SubscriptionModel{}).Pluck("name", &names).Error; err != nil {
return []string{}, err
}
return names, nil
}

View File

@@ -3,10 +3,11 @@ package repositories
import ( import (
"time" "time"
"GoMembership/internal/constants"
"gorm.io/gorm" "gorm.io/gorm"
"GoMembership/internal/constants"
"GoMembership/internal/database"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
"GoMembership/internal/models" "GoMembership/internal/models"
@@ -14,7 +15,7 @@ import (
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
) )
type UserRepository interface { type UserRepositoryInterface interface {
CreateUser(user *models.User) (int64, error) CreateUser(user *models.User) (int64, error)
UpdateUser(userID int64, user *models.User) error UpdateUser(userID int64, user *models.User) error
FindUserByID(id int64) (*models.User, error) FindUserByID(id int64) (*models.User, error)
@@ -24,28 +25,22 @@ type UserRepository interface {
VerifyUserOfToken(token *string) (*models.User, error) VerifyUserOfToken(token *string) (*models.User, error)
} }
type userRepository struct { type UserRepository struct{}
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) UserRepository { func (ur *UserRepository) CreateUser(user *models.User) (int64, error) {
return &userRepository{db} result := database.DB.Create(user)
}
func (repo *userRepository) CreateUser(user *models.User) (int64, error) {
result := repo.db.Create(user)
if result.Error != nil { if result.Error != nil {
return 0, result.Error return 0, result.Error
} }
return user.ID, nil return user.ID, nil
} }
func (repo *userRepository) UpdateUser(userID int64, user *models.User) error { func (ur *UserRepository) UpdateUser(userID int64, user *models.User) error {
logger.Info.Printf("Updating User: %#v\n", user) logger.Info.Printf("Updating User: %#v\n", user)
if user == nil { if user == nil {
return errors.ErrNoData return errors.ErrNoData
} }
result := repo.db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user) result := database.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
if result.Error != nil { if result.Error != nil {
return result.Error return result.Error
} }
@@ -57,9 +52,9 @@ func (repo *userRepository) UpdateUser(userID int64, user *models.User) error {
return nil return nil
} }
func (repo *userRepository) FindUserByID(id int64) (*models.User, error) { func (ur *UserRepository) FindUserByID(id int64) (*models.User, error) {
var user models.User var user models.User
result := repo.db.Preload("Consents").Preload("BankAccount").Preload("Verification").Preload("Membership").First(&user, id) result := database.DB.Preload("Consents").Preload("BankAccount").Preload("Verification").Preload("Membership").First(&user, id)
if result.Error != nil { if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound return nil, gorm.ErrRecordNotFound
@@ -69,9 +64,9 @@ func (repo *userRepository) FindUserByID(id int64) (*models.User, error) {
return &user, nil return &user, nil
} }
func (repo *userRepository) FindUserByEmail(email string) (*models.User, error) { func (ur *UserRepository) FindUserByEmail(email string) (*models.User, error) {
var user models.User var user models.User
result := repo.db.Where("email = ?", email).First(&user) result := database.DB.Where("email = ?", email).First(&user)
if result.Error != nil { if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound return nil, gorm.ErrRecordNotFound
@@ -81,9 +76,9 @@ func (repo *userRepository) FindUserByEmail(email string) (*models.User, error)
return &user, nil return &user, nil
} }
func (repo *userRepository) IsVerified(userID *int64) (bool, error) { func (ur *UserRepository) IsVerified(userID *int64) (bool, error) {
var user models.User var user models.User
result := repo.db.Select("status").First(&user, userID) result := database.DB.Select("status").First(&user, userID)
if result.Error != nil { if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {
return false, gorm.ErrRecordNotFound return false, gorm.ErrRecordNotFound
@@ -93,9 +88,9 @@ func (repo *userRepository) IsVerified(userID *int64) (bool, error) {
return user.Status != constants.UnverifiedStatus, nil return user.Status != constants.UnverifiedStatus, nil
} }
func (repo *userRepository) VerifyUserOfToken(token *string) (*models.User, error) { func (ur *UserRepository) VerifyUserOfToken(token *string) (*models.User, error) {
var emailVerification models.Verification var emailVerification models.Verification
result := repo.db.Where("verification_token = ?", token).First(&emailVerification) result := database.DB.Where("verification_token = ?", token).First(&emailVerification)
if result.Error != nil { if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound return nil, gorm.ErrRecordNotFound
@@ -104,11 +99,11 @@ func (repo *userRepository) VerifyUserOfToken(token *string) (*models.User, erro
} }
// Check if the user is already verified // Check if the user is already verified
verified, err := repo.IsVerified(&emailVerification.UserID) verified, err := ur.IsVerified(&emailVerification.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
user, err := repo.FindUserByID(emailVerification.UserID) user, err := ur.FindUserByID(emailVerification.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -121,7 +116,7 @@ func (repo *userRepository) VerifyUserOfToken(token *string) (*models.User, erro
user.Status = constants.VerifiedStatus user.Status = constants.VerifiedStatus
user.Verification = emailVerification user.Verification = emailVerification
err = repo.UpdateUser(emailVerification.UserID, user) err = ur.UpdateUser(emailVerification.UserID, user)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -129,9 +124,9 @@ func (repo *userRepository) VerifyUserOfToken(token *string) (*models.User, erro
return user, nil return user, nil
} }
func (repo *userRepository) SetVerificationToken(user *models.User, token *string) (int64, error) { func (ur *UserRepository) SetVerificationToken(user *models.User, token *string) (int64, error) {
// Check if user is already verified // Check if user is already verified
verified, err := repo.IsVerified(&user.ID) verified, err := ur.IsVerified(&user.ID)
if err != nil { if err != nil {
return -1, err return -1, err
} }
@@ -146,7 +141,7 @@ func (repo *userRepository) SetVerificationToken(user *models.User, token *strin
} }
// Use GORM to insert or update the Verification record // Use GORM to insert or update the Verification record
result := repo.db.Clauses(clause.OnConflict{ result := database.DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}}, Columns: []clause.Column{{Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"verification_token", "created_at"}), DoUpdates: clause.AssignmentColumns([]string{"verification_token", "created_at"}),
}).Create(&verification) }).Create(&verification)

View File

@@ -6,10 +6,10 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func RegisterRoutes(router *gin.Engine, userController *controllers.UserController, membershipController *controllers.MembershipController) { func RegisterRoutes(router *gin.Engine, userController *controllers.UserController, membershipcontroller *controllers.MembershipController) {
router.GET("/backend/verify", userController.VerifyMailHandler) router.GET("/backend/verify", userController.VerifyMailHandler)
router.POST("/backend/api/register", userController.RegisterUser) router.POST("/backend/api/register", userController.RegisterUser)
router.POST("/backend/api/register/subscription", membershipController.RegisterSubscription) router.POST("/backend/api/register/subscription", membershipcontroller.RegisterSubscription)
// router.HandleFunc("/login", userController.LoginUser).Methods("POST") // router.HandleFunc("/login", userController.LoginUser).Methods("POST")
} }

View File

@@ -6,8 +6,9 @@ import (
"GoMembership/internal/config" "GoMembership/internal/config"
"GoMembership/internal/controllers" "GoMembership/internal/controllers"
"GoMembership/internal/database" "GoMembership/internal/database"
// "GoMembership/internal/middlewares"
"GoMembership/internal/repositories" "GoMembership/internal/repositories"
// "GoMembership/internal/middlewares"
"GoMembership/internal/routes" "GoMembership/internal/routes"
"GoMembership/internal/services" "GoMembership/internal/services"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
@@ -18,23 +19,27 @@ import (
func Run() { func Run() {
cfg := config.LoadConfig() cfg := config.LoadConfig()
logger.Info.Printf("Config: %+v", cfg) logger.Info.Printf("Config: %+v", cfg)
db, err := database.InitDB(cfg.DB.Path) err := database.InitDB(cfg.DB.Path)
if err != nil { if err != nil {
logger.Error.Fatalf("Couldn't init database: %v", err) logger.Error.Fatalf("Couldn't init database: %v", err)
} }
emailService := services.NewEmailService(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.User, cfg.SMTP.Password, cfg.SMTP.AdminEmail) emailService := services.NewEmailService(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.User, cfg.SMTP.Password, cfg.SMTP.AdminEmail)
consentRepo := repositories.NewConsentRepository(db) var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
consentService := services.NewConsentService(consentRepo) consentService := &services.ConsentService{Repo: consentRepo}
bankAccountRepo := repositories.NewBankAccountRepository(db)
bankAccountService := services.NewBankAccountService(bankAccountRepo) var bankAccountRepo repositories.BankAccountRepositoryInterface = &repositories.BankAccountRepository{}
membershipRepo := repositories.NewMembershipRepository(db) bankAccountService := &services.BankAccountService{Repo: bankAccountRepo}
subscriptionRepo := repositories.NewSubscriptionModelsRepository(db)
membershipService := services.NewMembershipService(membershipRepo, subscriptionRepo) var membershipRepo repositories.MembershipRepositoryInterface = &repositories.MembershipRepository{}
userRepo := repositories.NewUserRepository(db) var subscriptionRepo repositories.SubscriptionModelsRepositoryInterface = &repositories.SubscriptionModelsRepository{}
userService := services.NewUserService(userRepo) membershipService := &services.MembershipService{Repo: membershipRepo, SubscriptionRepo: subscriptionRepo}
userController := controllers.NewUserController(userService, emailService, consentService, bankAccountService, membershipService)
membershipController := controllers.NewMembershipController(membershipService) var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
userService := &services.UserService{Repo: userRepo}
userController := &controllers.UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
membershipController := &controllers.MembershipController{Service: *membershipService}
router := gin.Default() router := gin.Default()
// gin.SetMode(gin.ReleaseMode) // gin.SetMode(gin.ReleaseMode)

View File

@@ -10,19 +10,15 @@ import (
"time" "time"
) )
type BankAccountService interface { type BankAccountServiceInterface interface {
RegisterBankAccount(bankAccount *models.BankAccount) (int64, error) RegisterBankAccount(bankAccount *models.BankAccount) (int64, error)
} }
type bankAccountService struct { type BankAccountService struct {
repo repositories.BankAccountRepository Repo repositories.BankAccountRepositoryInterface
} }
func NewBankAccountService(repo repositories.BankAccountRepository) BankAccountService { func (service *BankAccountService) RegisterBankAccount(bankAccount *models.BankAccount) (int64, error) {
return &bankAccountService{repo}
}
func (service *bankAccountService) RegisterBankAccount(bankAccount *models.BankAccount) (int64, error) {
bankAccount.MandateDateSigned = time.Now() bankAccount.MandateDateSigned = time.Now()
ref, err := generateSEPAMandateReference(strconv.FormatInt(bankAccount.UserID, 10)) ref, err := generateSEPAMandateReference(strconv.FormatInt(bankAccount.UserID, 10))
if err != nil { if err != nil {
@@ -30,7 +26,7 @@ func (service *bankAccountService) RegisterBankAccount(bankAccount *models.BankA
} }
bankAccount.MandateReference = ref bankAccount.MandateReference = ref
return service.repo.CreateBankAccount(bankAccount) return service.Repo.CreateBankAccount(bankAccount)
} }
func generateUniqueID(length int) (string, error) { func generateUniqueID(length int) (string, error) {

View File

@@ -1,25 +1,22 @@
package services package services
import ( import (
"time"
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/internal/repositories" "GoMembership/internal/repositories"
"time"
) )
type ConsentService interface { type ConsentServiceInterface interface {
RegisterConsent(consent *models.Consent) (int64, error) RegisterConsent(consent *models.Consent) (int64, error)
} }
type consentService struct { type ConsentService struct {
repo repositories.ConsentRepository Repo repositories.ConsentRepositoryInterface
} }
func NewConsentService(repo repositories.ConsentRepository) ConsentService { func (service *ConsentService) RegisterConsent(consent *models.Consent) (int64, error) {
return &consentService{repo}
}
func (service *consentService) RegisterConsent(consent *models.Consent) (int64, error) {
consent.CreatedAt = time.Now() consent.CreatedAt = time.Now()
consent.UpdatedAt = time.Now() consent.UpdatedAt = time.Now()
return service.repo.CreateConsent(consent) return service.Repo.CreateConsent(consent)
} }

View File

@@ -129,7 +129,7 @@ func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error {
City: user.City, City: user.City,
DateOfBirth: user.DateOfBirth.Format("20060102"), DateOfBirth: user.DateOfBirth.Format("20060102"),
Email: user.Email, Email: user.Email,
Phone: *user.Phone, Phone: user.Phone,
IBAN: user.BankAccount.IBAN, IBAN: user.BankAccount.IBAN,
} }

View File

@@ -3,35 +3,50 @@ package services
import ( import (
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/internal/repositories" "GoMembership/internal/repositories"
"GoMembership/pkg/errors"
"slices"
"time" "time"
) )
type MembershipService interface { type MembershipServiceInterface interface {
RegisterMembership(membership *models.Membership) (int64, error) RegisterMembership(membership *models.Membership) (int64, error)
FindMembershipByUserID(userID int64) (*models.Membership, error) FindMembershipByUserID(userID int64) (*models.Membership, error)
RegisterSubscription(subscription *models.SubscriptionModel) (int64, error) RegisterSubscription(subscription *models.SubscriptionModel) (int64, error)
GetMembershipModelNames() ([]string, error)
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
} }
type membershipService struct { type MembershipService struct {
repo repositories.MembershipRepository Repo repositories.MembershipRepositoryInterface
subscriptionRepo repositories.SubscriptionModelsRepository SubscriptionRepo repositories.SubscriptionModelsRepositoryInterface
} }
func NewMembershipService(repo repositories.MembershipRepository, subscriptionRepo repositories.SubscriptionModelsRepository) MembershipService { func (service *MembershipService) RegisterMembership(membership *models.Membership) (int64, error) {
return &membershipService{repo, subscriptionRepo}
}
func (service *membershipService) RegisterMembership(membership *models.Membership) (int64, error) {
membership.StartDate = time.Now() membership.StartDate = time.Now()
return service.repo.CreateMembership(membership) return service.Repo.CreateMembership(membership)
} }
func (service *membershipService) FindMembershipByUserID(userID int64) (*models.Membership, error) { func (service *MembershipService) FindMembershipByUserID(userID int64) (*models.Membership, error) {
return service.repo.FindMembershipByUserID(userID) return service.Repo.FindMembershipByUserID(userID)
} }
// Membership_Subscriptions // Membership_Subscriptions
func (service *MembershipService) RegisterSubscription(subscription *models.SubscriptionModel) (int64, error) {
func (service *membershipService) RegisterSubscription(subscription *models.SubscriptionModel) (int64, error) { return service.SubscriptionRepo.CreateSubscriptionModel(subscription)
return service.subscriptionRepo.CreateSubscriptionModel(subscription) }
func (service *MembershipService) GetMembershipModelNames() ([]string, error) {
return service.SubscriptionRepo.GetMembershipModelNames()
}
func (service *MembershipService) GetModelByName(modelname *string) (*models.SubscriptionModel, error) {
sModelNames, err := service.SubscriptionRepo.GetMembershipModelNames()
if err != nil {
return nil, err
}
if !slices.Contains(sModelNames, *modelname) {
return nil, errors.ErrNotFound
}
return service.SubscriptionRepo.GetModelByName(modelname)
} }

View File

@@ -6,47 +6,44 @@ import (
"GoMembership/internal/repositories" "GoMembership/internal/repositories"
"GoMembership/internal/utils" "GoMembership/internal/utils"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
"github.com/go-playground/validator/v10"
// "crypto/rand" // "crypto/rand"
// "encoding/base64" // "encoding/base64"
// "golang.org/x/crypto/bcrypt" // "golang.org/x/crypto/bcrypt"
"time" "time"
) )
type UserService interface { type UserServiceInterface interface {
RegisterUser(user *models.User) (int64, string, error) RegisterUser(user *models.User) (int64, string, error)
// AuthenticateUser(email, password string) (*models.User, error)A // AuthenticateUser(email, password string) (*models.User, error)A
VerifyUser(token *string) (*models.User, error) VerifyUser(token *string) (*models.User, error)
} }
type userService struct { type UserService struct {
repo repositories.UserRepository Repo repositories.UserRepositoryInterface
} }
func NewUserService(repo repositories.UserRepository) UserService { func (service *UserService) RegisterUser(user *models.User) (int64, string, error) {
return &userService{repo}
}
func (service *userService) RegisterUser(user *models.User) (int64, string, error) {
/* salt := make([]byte, 16) /* salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil { if _, err := rand.Read(salt); err != nil {
return -1, err return -1, err
} }
user.Salt = base64.StdEncoding.EncodeToString(salt) user.Salt = base64.StdEncoding.EncodeToString(salt)
*/
hashedPassword, err := HashPassword(user.Password, user.Salt) err := validateRegistrationData(user)
if err != nil {
return -1, err
}
user.Password = string(hashedPassword) */
// TODO: Validate Data
user.Status = constants.UnverifiedStatus
user.CreatedAt = time.Now()
user.UpdatedAt = time.Now()
id, err := service.repo.CreateUser(user)
if err != nil { if err != nil {
return -1, "", err return -1, "", err
} }
user.Status = constants.UnverifiedStatus
user.CreatedAt = time.Now()
user.UpdatedAt = time.Now()
id, err := service.Repo.CreateUser(user)
if err != nil {
return -1, "", err
}
user.ID = id user.ID = id
token, err := utils.GenerateVerificationToken() token, err := utils.GenerateVerificationToken()
@@ -55,21 +52,33 @@ func (service *userService) RegisterUser(user *models.User) (int64, string, erro
} }
logger.Info.Printf("TOKEN: %v", token) logger.Info.Printf("TOKEN: %v", token)
_, err = service.repo.SetVerificationToken(user, &token)
_, err = service.Repo.SetVerificationToken(user, &token)
if err != nil { if err != nil {
return -1, "", err return -1, "", err
} }
return id, token, nil return id, token, nil
} }
func (service *userService) VerifyUser(token *string) (*models.User, error) { func (service *UserService) VerifyUser(token *string) (*models.User, error) {
user, err := service.repo.VerifyUserOfToken(token) user, err := service.Repo.VerifyUserOfToken(token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return user, nil return user, nil
} }
func validateRegistrationData(user *models.User) error {
validate := validator.New()
validate.RegisterValidation("age", utils.AgeValidator)
validate.RegisterValidation("bic", utils.BICValidator)
validate.RegisterValidation("iban", utils.IBANValidator)
validate.RegisterValidation("subscriptionModel", utils.SubscriptionModelValidator)
return validate.Struct(user)
}
/* func HashPassword(password string, salt string) (string, error) { /* func HashPassword(password string, salt string) (string, error) {
saltedPassword := password + salt saltedPassword := password + salt
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(saltedPassword), bcrypt.DefaultCost) hashedPassword, err := bcrypt.GenerateFromPassword([]byte(saltedPassword), bcrypt.DefaultCost)
@@ -80,7 +89,7 @@ func (service *userService) VerifyUser(token *string) (*models.User, error) {
return base64.StdEncoding.EncodeToString(hashedPassword), nil return base64.StdEncoding.EncodeToString(hashedPassword), nil
} */ } */
/* func (s *userService) AuthenticateUser(email, password string) (*models.User, error) { /* func (s *UserService) AuthenticateUser(email, password string) (*models.User, error) {
user, err := s.repo.FindUserByEmail(email) user, err := s.repo.FindUserByEmail(email)
if err != nil { if err != nil {
return nil, errors.ErrUserNotFound return nil, errors.ErrUserNotFound

View File

@@ -1,9 +1,62 @@
package utils package utils
import "regexp" // import "regexp"
func IsEmailValid(email string) bool { import (
regex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$` // "reflect"
re := regexp.MustCompile(regex) "time"
return re.MatchString(email)
"GoMembership/internal/database"
"GoMembership/internal/models"
"GoMembership/pkg/logger"
"github.com/go-playground/validator/v10"
"github.com/jbub/banking/iban"
"github.com/jbub/banking/swift"
"slices"
)
//
// func IsEmailValid(email string) bool {
// regex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
// re := regexp.MustCompile(regex)
// return re.MatchString(email)
// }
func AgeValidator(fl validator.FieldLevel) bool {
fieldValue := fl.Field()
// Ensure the field is of type time.Time
// if fieldValue.Kind() != reflect.Struct || !fieldValue.Type().ConvertibleTo(reflect.TypeOf(time.Time{})) {
// return false
// }
dateOfBirth := fieldValue.Interface().(time.Time)
now := time.Now()
age := now.Year() - dateOfBirth.Year()
if now.YearDay() < dateOfBirth.YearDay() {
age-- // if birthday is in the future..
}
return age >= 18
}
func SubscriptionModelValidator(fl validator.FieldLevel) bool {
fieldValue := fl.Field().String()
var names []string
if err := database.DB.Model(&models.SubscriptionModel{}).Pluck("name", &names).Error; err != nil {
logger.Error.Fatalf("Couldn't get SubscriptionModel names: %#v", err)
return false
}
return slices.Contains(names, fieldValue)
}
func IBANValidator(fl validator.FieldLevel) bool {
fieldValue := fl.Field().String()
return iban.Validate(fieldValue) == nil
}
func BICValidator(fl validator.FieldLevel) bool {
fieldValue := fl.Field().String()
return swift.Validate(fieldValue) == nil
} }