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/validator/v10 v10.22.0 // 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/now v1.1.5 // 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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=

View File

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

View File

@@ -4,28 +4,23 @@ import (
"GoMembership/internal/models"
"GoMembership/internal/services"
"github.com/gin-gonic/gin"
"net/http"
"github.com/gin-gonic/gin"
"GoMembership/pkg/logger"
)
type UserController struct {
service services.UserService
emailService services.EmailService
consentService services.ConsentService
bankAccountService services.BankAccountService
membershipService services.MembershipService
Service services.UserServiceInterface
EmailService *services.EmailService
ConsentService services.ConsentServiceInterface
BankAccountService services.BankAccountServiceInterface
MembershipService services.MembershipServiceInterface
}
type RegistrationData struct {
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}
User models.User `json:"user"`
}
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"})
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
id, token, err := uc.service.RegisterUser(&regData.User)
id, token, err := uc.Service.RegisterUser(&regData.User)
if err != nil {
logger.Error.Printf("Couldn't register User: %v", err)
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
// Register Bank Account
_, err = uc.bankAccountService.RegisterBankAccount(&regData.BankAccount)
/* _, err = uc.BankAccountService.RegisterBankAccount(&regData.User.BankAccount)
if err != nil {
logger.Error.Printf("Couldn't register bank account: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-BankAccount"})
return
}
} */
// Register Consents
var consents = [2]models.Consent{
@@ -72,7 +75,7 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
},
}
for _, consent := range consents {
_, err = uc.consentService.RegisterConsent(&consent)
_, err = uc.ConsentService.RegisterConsent(&consent)
if err != nil {
logger.Error.Printf("Couldn't register consent: %v", err)
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
_, err = uc.membershipService.RegisterMembership(&regData.Membership)
/* _, err = uc.MembershipService.RegisterMembership(&regData.User.Membership)
if err != nil {
logger.Error.Printf("Couldn't register membership: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-membership"})
return
}
} */
// 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)
// Proceed without returning error since user registration is successful
}
// 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)
// Proceed without returning error since user registration is successful
}
@@ -114,21 +117,21 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
return
}
user, err := uc.service.VerifyUser(&token)
user, err := uc.Service.VerifyUser(&token)
if err != nil {
logger.Error.Printf("Cannot verify user: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Cannot verify user"})
return
}
membership, err := uc.membershipService.FindMembershipByUserID(user.ID)
membership, err := uc.MembershipService.FindMembershipByUserID(user.ID)
if err != nil {
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"})
return
}
uc.emailService.SendWelcomeEmail(user, membership)
uc.EmailService.SendWelcomeEmail(user, membership)
c.Status(http.StatusOK)
}
@@ -138,7 +141,7 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
Email string `json:"email"`
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 {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
@@ -153,7 +156,7 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
http.Error(w, "Invalid user ID", http.StatusBadRequest)
return
}
user, err := uc.service.GetUserByID(id)
user, err := services.UserService.GetUserByID(id)
if err != nil {
http.Error(w, "User not found: "+err.Error(), http.StatusNotFound)
}

View File

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

View File

@@ -5,12 +5,12 @@ import "time"
type BankAccount struct {
CreatedAt time.Time
UpdatedAt time.Time
MandateDateSigned time.Time `gorm:"not null" json:"mandate_date_signed"`
Bank string `json:"bank_name"`
AccountHolderName string `json:"account_holder_name"`
IBAN string `gorm:"not null" json:"iban"`
BIC string `json:"bic"`
MandateReference string `gorm:"not null" json:"mandate_reference"`
MandateDateSigned time.Time `gorm:"not null"` // json:"mandate_date_signed"`
Bank string //`json:"bank_name" validate:"omitempty,alphanumunicode"`
AccountHolderName string //`json:"account_holder_name" validate:"omitempty,alphaunicode"`
IBAN string `gorm:"not null" json:"iban" validate:"required,iban"`
BIC string //`json:"bic" validate:"omitempty,bic"`
MandateReference string `gorm:"not null"` //json:"mandate_reference"`
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"`
Children *[]User `gorm:"foreignKey:ParentMemberID"`
Status string `json:"status"`
SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID"`
SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID" json:"subscription_model"`
SubscriptionModelID int64 `json:"subsription_model_id"`
ID int64 `json:"id"`
UserID int64 `json:"user_id"`

View File

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

View File

@@ -8,24 +8,24 @@ import (
type User struct {
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
Salt *string `json:"-"`
Phone *string `json:"phone"`
Phone string `json:"phone" validate:"omitempty,omitnil,e164"`
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"`
Email string `gorm:"unique;not null" json:"email"`
LastName string `gorm:"not null" json:"last_name"`
ProfilePicture string `json:"profile_picture"`
Address string `gorm:"not null" json:"address"`
ZipCode string `gorm:"not null" json:"zip_code"`
City string `form:"not null" json:"city"`
Email string `gorm:"unique;not null" json:"email" validate:"required,email"`
LastName string `gorm:"not null" json:"last_name" validate:"required,alphaunicode"`
ProfilePicture string `json:"profile_picture" validate:"omitempty,image"`
Address string `gorm:"not null" json:"address" validate:"required"`
ZipCode string `gorm:"not null" json:"zip_code" validate:"required,alphanum"`
City string `form:"not null" json:"city" validate:"required,alphaunicode"`
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;"`
Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
ParentMemberID int64 `json:"parent_member_id"`
Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"membership"`
ParentMemberID int64 `json:"parent_member_id" validate:"omitempty,number"`
ID int64 `gorm:"primaryKey"`
PaymentStatus int8 `json:"payment_status"`
Status int8 `json:"status"`

View File

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

View File

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

View File

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

View File

@@ -1,27 +1,40 @@
package repositories
import (
"gorm.io/gorm"
"GoMembership/internal/database"
"GoMembership/internal/models"
)
type SubscriptionModelsRepository interface {
type SubscriptionModelsRepositoryInterface interface {
CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error)
GetMembershipModelNames() ([]string, error)
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
}
type subscriptionModelsRepository struct {
db *gorm.DB
}
type SubscriptionModelsRepository struct{}
func NewSubscriptionModelsRepository(db *gorm.DB) SubscriptionModelsRepository {
return &subscriptionModelsRepository{db}
}
func (sr *SubscriptionModelsRepository) CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error) {
func (repo *subscriptionModelsRepository) CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error) {
result := repo.db.Create(subscriptionModel)
result := database.DB.Create(subscriptionModel)
if result.Error != nil {
return 0, result.Error
}
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 (
"time"
"GoMembership/internal/constants"
"gorm.io/gorm"
"GoMembership/internal/constants"
"GoMembership/internal/database"
"gorm.io/gorm/clause"
"GoMembership/internal/models"
@@ -14,7 +15,7 @@ import (
"GoMembership/pkg/logger"
)
type UserRepository interface {
type UserRepositoryInterface interface {
CreateUser(user *models.User) (int64, error)
UpdateUser(userID int64, user *models.User) error
FindUserByID(id int64) (*models.User, error)
@@ -24,28 +25,22 @@ type UserRepository interface {
VerifyUserOfToken(token *string) (*models.User, error)
}
type userRepository struct {
db *gorm.DB
}
type UserRepository struct{}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db}
}
func (repo *userRepository) CreateUser(user *models.User) (int64, error) {
result := repo.db.Create(user)
func (ur *UserRepository) CreateUser(user *models.User) (int64, error) {
result := database.DB.Create(user)
if result.Error != nil {
return 0, result.Error
}
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)
if user == nil {
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 {
return result.Error
}
@@ -57,9 +52,9 @@ func (repo *userRepository) UpdateUser(userID int64, user *models.User) error {
return nil
}
func (repo *userRepository) FindUserByID(id int64) (*models.User, error) {
func (ur *UserRepository) FindUserByID(id int64) (*models.User, error) {
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 == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound
@@ -69,9 +64,9 @@ func (repo *userRepository) FindUserByID(id int64) (*models.User, error) {
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
result := repo.db.Where("email = ?", email).First(&user)
result := database.DB.Where("email = ?", email).First(&user)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound
@@ -81,9 +76,9 @@ func (repo *userRepository) FindUserByEmail(email string) (*models.User, error)
return &user, nil
}
func (repo *userRepository) IsVerified(userID *int64) (bool, error) {
func (ur *UserRepository) IsVerified(userID *int64) (bool, error) {
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 == gorm.ErrRecordNotFound {
return false, gorm.ErrRecordNotFound
@@ -93,9 +88,9 @@ func (repo *userRepository) IsVerified(userID *int64) (bool, error) {
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
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 == 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
verified, err := repo.IsVerified(&emailVerification.UserID)
verified, err := ur.IsVerified(&emailVerification.UserID)
if err != nil {
return nil, err
}
user, err := repo.FindUserByID(emailVerification.UserID)
user, err := ur.FindUserByID(emailVerification.UserID)
if err != nil {
return nil, err
}
@@ -121,7 +116,7 @@ func (repo *userRepository) VerifyUserOfToken(token *string) (*models.User, erro
user.Status = constants.VerifiedStatus
user.Verification = emailVerification
err = repo.UpdateUser(emailVerification.UserID, user)
err = ur.UpdateUser(emailVerification.UserID, user)
if err != nil {
return nil, err
}
@@ -129,9 +124,9 @@ func (repo *userRepository) VerifyUserOfToken(token *string) (*models.User, erro
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
verified, err := repo.IsVerified(&user.ID)
verified, err := ur.IsVerified(&user.ID)
if err != nil {
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
result := repo.db.Clauses(clause.OnConflict{
result := database.DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"verification_token", "created_at"}),
}).Create(&verification)

View File

@@ -6,10 +6,10 @@ import (
"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.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")
}

View File

@@ -6,8 +6,9 @@ import (
"GoMembership/internal/config"
"GoMembership/internal/controllers"
"GoMembership/internal/database"
// "GoMembership/internal/middlewares"
"GoMembership/internal/repositories"
// "GoMembership/internal/middlewares"
"GoMembership/internal/routes"
"GoMembership/internal/services"
"GoMembership/pkg/logger"
@@ -18,23 +19,27 @@ import (
func Run() {
cfg := config.LoadConfig()
logger.Info.Printf("Config: %+v", cfg)
db, err := database.InitDB(cfg.DB.Path)
err := database.InitDB(cfg.DB.Path)
if err != nil {
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)
consentRepo := repositories.NewConsentRepository(db)
consentService := services.NewConsentService(consentRepo)
bankAccountRepo := repositories.NewBankAccountRepository(db)
bankAccountService := services.NewBankAccountService(bankAccountRepo)
membershipRepo := repositories.NewMembershipRepository(db)
subscriptionRepo := repositories.NewSubscriptionModelsRepository(db)
membershipService := services.NewMembershipService(membershipRepo, subscriptionRepo)
userRepo := repositories.NewUserRepository(db)
userService := services.NewUserService(userRepo)
userController := controllers.NewUserController(userService, emailService, consentService, bankAccountService, membershipService)
membershipController := controllers.NewMembershipController(membershipService)
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
consentService := &services.ConsentService{Repo: consentRepo}
var bankAccountRepo repositories.BankAccountRepositoryInterface = &repositories.BankAccountRepository{}
bankAccountService := &services.BankAccountService{Repo: bankAccountRepo}
var membershipRepo repositories.MembershipRepositoryInterface = &repositories.MembershipRepository{}
var subscriptionRepo repositories.SubscriptionModelsRepositoryInterface = &repositories.SubscriptionModelsRepository{}
membershipService := &services.MembershipService{Repo: membershipRepo, SubscriptionRepo: subscriptionRepo}
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()
// gin.SetMode(gin.ReleaseMode)

View File

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

View File

@@ -1,25 +1,22 @@
package services
import (
"time"
"GoMembership/internal/models"
"GoMembership/internal/repositories"
"time"
)
type ConsentService interface {
type ConsentServiceInterface interface {
RegisterConsent(consent *models.Consent) (int64, error)
}
type consentService struct {
repo repositories.ConsentRepository
type ConsentService struct {
Repo repositories.ConsentRepositoryInterface
}
func NewConsentService(repo repositories.ConsentRepository) ConsentService {
return &consentService{repo}
}
func (service *consentService) RegisterConsent(consent *models.Consent) (int64, error) {
func (service *ConsentService) RegisterConsent(consent *models.Consent) (int64, error) {
consent.CreatedAt = 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,
DateOfBirth: user.DateOfBirth.Format("20060102"),
Email: user.Email,
Phone: *user.Phone,
Phone: user.Phone,
IBAN: user.BankAccount.IBAN,
}

View File

@@ -3,35 +3,50 @@ package services
import (
"GoMembership/internal/models"
"GoMembership/internal/repositories"
"GoMembership/pkg/errors"
"slices"
"time"
)
type MembershipService interface {
type MembershipServiceInterface interface {
RegisterMembership(membership *models.Membership) (int64, error)
FindMembershipByUserID(userID int64) (*models.Membership, error)
RegisterSubscription(subscription *models.SubscriptionModel) (int64, error)
GetMembershipModelNames() ([]string, error)
GetModelByName(modelname *string) (*models.SubscriptionModel, error)
}
type membershipService struct {
repo repositories.MembershipRepository
subscriptionRepo repositories.SubscriptionModelsRepository
type MembershipService struct {
Repo repositories.MembershipRepositoryInterface
SubscriptionRepo repositories.SubscriptionModelsRepositoryInterface
}
func NewMembershipService(repo repositories.MembershipRepository, subscriptionRepo repositories.SubscriptionModelsRepository) MembershipService {
return &membershipService{repo, subscriptionRepo}
}
func (service *membershipService) RegisterMembership(membership *models.Membership) (int64, error) {
func (service *MembershipService) RegisterMembership(membership *models.Membership) (int64, error) {
membership.StartDate = time.Now()
return service.repo.CreateMembership(membership)
return service.Repo.CreateMembership(membership)
}
func (service *membershipService) FindMembershipByUserID(userID int64) (*models.Membership, error) {
return service.repo.FindMembershipByUserID(userID)
func (service *MembershipService) FindMembershipByUserID(userID int64) (*models.Membership, error) {
return service.Repo.FindMembershipByUserID(userID)
}
// Membership_Subscriptions
func (service *membershipService) RegisterSubscription(subscription *models.SubscriptionModel) (int64, error) {
return service.subscriptionRepo.CreateSubscriptionModel(subscription)
func (service *MembershipService) RegisterSubscription(subscription *models.SubscriptionModel) (int64, error) {
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/utils"
"GoMembership/pkg/logger"
"github.com/go-playground/validator/v10"
// "crypto/rand"
// "encoding/base64"
// "golang.org/x/crypto/bcrypt"
"time"
)
type UserService interface {
type UserServiceInterface interface {
RegisterUser(user *models.User) (int64, string, error)
// AuthenticateUser(email, password string) (*models.User, error)A
VerifyUser(token *string) (*models.User, error)
}
type userService struct {
repo repositories.UserRepository
type UserService struct {
Repo repositories.UserRepositoryInterface
}
func NewUserService(repo repositories.UserRepository) UserService {
return &userService{repo}
}
func (service *userService) RegisterUser(user *models.User) (int64, string, error) {
func (service *UserService) RegisterUser(user *models.User) (int64, string, error) {
/* salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
return -1, err
}
user.Salt = base64.StdEncoding.EncodeToString(salt)
hashedPassword, err := HashPassword(user.Password, user.Salt)
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)
*/
err := validateRegistrationData(user)
if err != nil {
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
token, err := utils.GenerateVerificationToken()
@@ -55,21 +52,33 @@ func (service *userService) RegisterUser(user *models.User) (int64, string, erro
}
logger.Info.Printf("TOKEN: %v", token)
_, err = service.repo.SetVerificationToken(user, &token)
_, err = service.Repo.SetVerificationToken(user, &token)
if err != nil {
return -1, "", err
}
return id, token, nil
}
func (service *userService) VerifyUser(token *string) (*models.User, error) {
user, err := service.repo.VerifyUserOfToken(token)
func (service *UserService) VerifyUser(token *string) (*models.User, error) {
user, err := service.Repo.VerifyUserOfToken(token)
if err != nil {
return nil, err
}
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) {
saltedPassword := password + salt
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
} */
/* 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)
if err != nil {
return nil, errors.ErrUserNotFound

View File

@@ -1,9 +1,62 @@
package utils
import "regexp"
// import "regexp"
func IsEmailValid(email string) bool {
regex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
re := regexp.MustCompile(regex)
return re.MatchString(email)
import (
// "reflect"
"time"
"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
}