switched to gorm..

This commit is contained in:
$(pass /github/name)
2024-07-10 14:22:56 +02:00
parent 87e9f71ceb
commit 6ac5491053
27 changed files with 368 additions and 283 deletions

View File

@@ -1,6 +1,6 @@
{
"db": {
"DBPath": "data/db.sqlite3"
"Path": "data/db.sqlite3"
},
"smtp": {
"Host": "mail.server.com",
@@ -12,5 +12,8 @@
},
"templates": {
"MailDir": "templates/email"
},
"auth": {
"APIKey": ""
}
}

4
go.mod
View File

@@ -10,6 +10,10 @@ require (
)
require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gorm.io/driver/sqlite v1.5.6 // indirect
gorm.io/gorm v1.25.10 // indirect
)

8
go.sum
View File

@@ -2,6 +2,10 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
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=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
@@ -10,3 +14,7 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

View File

@@ -10,12 +10,13 @@ import (
)
type DatabaseConfig struct {
DBPath string `json:"DBPath"`
Path string `json:"Path"`
}
type AuthenticationConfig struct {
JWTSecret string
CSRFSecret string
APIKEY string `json:"APIKey"`
}
type SMTPConfig struct {
@@ -31,10 +32,10 @@ type TemplateConfig struct {
MailDir string `json:"MailDir"`
}
type Config struct {
DB DatabaseConfig `json:"db"`
Auth AuthenticationConfig
SMTP SMTPConfig `json:"smtp"`
Templates TemplateConfig `json:"templates"`
Auth AuthenticationConfig `json:"auth"`
DB DatabaseConfig `json:"db"`
Templates TemplateConfig `json:"templates"`
SMTP SMTPConfig `json:"smtp"`
}
var (

View File

@@ -0,0 +1,12 @@
package constants
const (
UnverifiedStatus = iota + 1
VerifiedStatus
ActiveStatus
PassiveStatus
DisabledStatus
DelayedPaymentStatus
SettledPaymentStatus
AwaitingPaymentStatus
)

View File

@@ -0,0 +1,60 @@
package controllers
import (
"encoding/json"
"GoMembership/internal/config"
"GoMembership/internal/models"
"GoMembership/internal/services"
// "github.com/gorilla/mux"
"net/http"
// "strconv"
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
)
type MembershipController struct {
service services.MembershipService
}
type MembershipData struct {
APIKey string
Model models.SubscriptionModel
}
func NewMembershipController(service services.MembershipService) *MembershipController {
return &MembershipController{service}
}
func (uc *MembershipController) RegisterPlan(w http.ResponseWriter, r *http.Request) {
rh := utils.NewResponseHandler(w)
var regData MembershipData
if err := json.NewDecoder(r.Body).Decode(&regData); err != nil {
logger.Error.Printf("Couldn't decode SubscriptionModel: %v", err)
rh.RespondWithError(http.StatusBadRequest, "Couldn't decode Membershipmodel")
return
}
logger.Info.Printf("Using API key: %v", config.LoadConfig().Auth.APIKEY)
if regData.APIKey == "" {
logger.Error.Println("API Key is missing")
rh.RespondWithError(http.StatusBadRequest, "API Key is required")
return
}
if regData.APIKey != config.LoadConfig().Auth.APIKEY {
logger.Error.Printf("API Key not valid: %v", regData.APIKey)
rh.RespondWithError(http.StatusExpectationFailed, "API Key not valid")
return
}
logger.Info.Printf("registering plan: %+v", regData)
// Register Plan
id, err := uc.service.RegisterPlan(&regData.Model)
if err != nil {
logger.Error.Printf("Couldn't register Membershipmodel: %v", err)
rh.RespondWithError(http.StatusInternalServerError, "Couldn't register Membershipmodel")
return
}
regData.Model.ID = id
}

View File

@@ -1,55 +1,30 @@
package database
import (
"database/sql"
"os"
"GoMembership/internal/config"
"GoMembership/internal/models"
"GoMembership/pkg/logger"
"embed"
_ "github.com/mattn/go-sqlite3"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
//go:embed schema.sql
var fs embed.FS
// var DB *gorm.DB
func initializeDB(dbPath string) error {
func InitDB(dbPath string) (*gorm.DB, error) {
db, err := sql.Open("sqlite3", dbPath)
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
return err
return nil, err
}
defer db.Close()
schema, err := fs.ReadFile("schema.sql")
if err != nil {
return err
if err := db.AutoMigrate(
&models.User{},
&models.SubscriptionModel{},
&models.Membership{},
&models.Consent{},
&models.Verification{},
&models.BankAccount{}); err != nil {
logger.Error.Fatalf("Couldn't create database: %v", err)
return nil, err
}
_, err = db.Exec(string(schema))
if err != nil {
return err
}
return nil
}
func Connect(DBcfg config.DatabaseConfig) *sql.DB {
_, err := os.Stat(DBcfg.DBPath)
if os.IsNotExist(err) {
initErr := initializeDB(DBcfg.DBPath)
if initErr != nil {
logger.Error.Fatalf("Couldn't create database: %v", initErr)
}
logger.Info.Println("Created new database")
}
db, err := sql.Open("sqlite3", DBcfg.DBPath)
if err != nil {
logger.Error.Fatal(err)
}
if err := db.Ping(); err != nil {
logger.Error.Fatal(err)
}
return db
// DB = db
return db, nil
}

View File

@@ -48,9 +48,8 @@ CREATE TABLE membership_plans (
monthly_fee DECIMAL(10, 2),
hourly_rate DECIMAL(10, 2),
included_hours_per_year INT,
remaining_hours_per_year INT,
included_hours_per_month INT,
remaining_hours_per_month INT,
conditions TEXT,
details TEXT
);

View File

@@ -0,0 +1,13 @@
package models
import "time"
type Verification struct {
UpdatedAt time.Time
CreatedAt time.Time
EmailVerifiedAt *time.Time `gorm:"Default:NULL" json:"email_verified_at"`
IDVerifiedAt *time.Time `gorm:"Default:NULL" json:"id_verified_at"`
VerificationToken string `json:"token"`
ID int64 `gorm:"primaryKey"`
UserID int64 `gorm:"unique;" json:"user_id"`
}

View File

@@ -3,11 +3,14 @@ package models
import "time"
type BankAccount struct {
MandateDateSigned time.Time `json:"mandate_date_signed"`
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 `json:"iban"`
IBAN string `gorm:"not null" json:"iban"`
BIC string `json:"bic"`
MandateReference string `json:"mandate_reference"`
UserID int `json:"id"`
MandateReference string `gorm:"not null" json:"mandate_reference"`
ID int64 `gorm:"primaryKey"`
UserID int64 `json:"user_id"`
}

View File

@@ -3,11 +3,12 @@ package models
import "time"
type Consent struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"created_at"`
ConsentType string `json:"consent_type"`
UserID int `json:"id"`
CreatedAt time.Time
UpdatedAt time.Time
FirstName string `gorm:"not null" json:"first_name"`
LastName string `gorm:"not null" json:"last_name"`
Email string `gorm:"unique" json:"email"`
ConsentType string `gorm:"not null" json:"consent_type"`
ID int64 `gorm:"primaryKey"`
UserID int64 `gorm:"not null" json:"user_id"`
}

View File

@@ -3,10 +3,14 @@ package models
import "time"
type Membership struct {
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
Status string `json:"status"`
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
ParentID int64 `json:"parent_id"`
CreatedAt time.Time
UpdatedAt time.Time
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
Status string `json:"status"`
SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID"`
SubscriptionModelID int64 `json:"subsription_model_id"`
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
ParentID int64 `json:"parent_id"`
}

View File

@@ -1,10 +0,0 @@
package models
type MembershipPlan struct {
Name string `json:"name"`
MonthlyFee float32 `json:"monthly_fee"`
HourlyRate float32 `json:"hourly_rate"`
IncludedPerYear int16 `json:"included_hours_per_year"`
IncludedPerMonth int16 `json:"included_hours_per_month"`
Details string `json:"details"`
}

View File

@@ -0,0 +1,18 @@
package models
import (
"time"
)
type SubscriptionModel struct {
CreatedAt time.Time
UpdatedAt time.Time
Name string `json:"name"`
Details string `json:"details"`
Conditions string `json:"conditions"`
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"`
}

View File

@@ -1,23 +1,39 @@
package models
import "time"
import (
"GoMembership/internal/constants"
"gorm.io/gorm"
"time"
)
type User struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DateOfBirth time.Time `json:"date_of_birth"`
Email string `json:"email"`
ProfilePicture string `json:"profile_picture"`
FirstName string `json:"first_name"`
Salt string `json:"-"`
LastName string `json:"last_name"`
Phone string `json:"phone"`
Status string `json:"status"`
Notes string `json:"notes"`
PaymentStatus string `json:"payment_status"`
Password string `json:"password"`
Address string `json:"address"`
ID int64 `json:"id"`
RoleID int8 `json:"role_id"`
DriversIDChecked bool `json:"drivers_id_checked"`
UpdatedAt time.Time
DateOfBirth time.Time `gorm:"not null" json:"date_of_birth"`
CreatedAt time.Time
Salt *string `json:"-"`
Phone *string `json:"phone"`
Notes *string `json:"notes"`
FirstName string `gorm:"not null" json:"first_name"`
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"`
Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"`
BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
ID int64 `gorm:"primaryKey"`
PaymentStatus int8 `json:"payment_status"`
Status int8 `json:"status"`
RoleID int8 `json:"role_id"`
}
// BeforeCreate sets the default value for Status
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if u.Status == 0 { // Assuming 0 is an unset value
u.Status = constants.UnverifiedStatus
u.PaymentStatus = constants.AwaitingPaymentStatus
}
return
}

View File

@@ -2,8 +2,7 @@ package repositories
import (
"GoMembership/internal/models"
// "GoMembership/pkg/errors"
"database/sql"
"gorm.io/gorm"
)
type BankAccountRepository interface {
@@ -11,25 +10,17 @@ type BankAccountRepository interface {
}
type bankAccountRepository struct {
db *sql.DB
db *gorm.DB
}
func NewBankAccountRepository(db *sql.DB) BankAccountRepository {
func NewBankAccountRepository(db *gorm.DB) BankAccountRepository {
return &bankAccountRepository{db}
}
func (repo *bankAccountRepository) CreateBankAccount(account *models.BankAccount) (int64, error) {
query := "INSERT INTO banking (user_id, iban, bic, mandate_reference, mandate_date_signed, bank_name, account_holder_name) VALUES (?, ?, ?, ?, ?, ?, ?)"
result, err := repo.db.Exec(query, account.UserID, account.IBAN, account.BIC, account.MandateReference, account.MandateDateSigned, account.Bank, account.AccountHolderName)
if err != nil {
return -1, err
result := repo.db.Create(account)
if result.Error != nil {
return 0, result.Error
}
lastInsertID, err := result.LastInsertId()
if err != nil {
return -1, err
}
return lastInsertID, err
return account.ID, nil
}

View File

@@ -2,8 +2,7 @@ package repositories
import (
"GoMembership/internal/models"
// "GoMembership/pkg/errors"
"database/sql"
"gorm.io/gorm"
)
type ConsentRepository interface {
@@ -11,25 +10,18 @@ type ConsentRepository interface {
}
type consentRepository struct {
db *sql.DB
db *gorm.DB
}
func NewConsentRepository(db *sql.DB) ConsentRepository {
func NewConsentRepository(db *gorm.DB) ConsentRepository {
return &consentRepository{db}
}
func (repo *consentRepository) CreateConsent(consent *models.Consent) (int64, error) {
result := repo.db.Create(consent)
query := "INSERT INTO consents (user_id, first_name, last_name, email, created_at, updated_at, consent_type) VALUES (?, ?, ?, ?, ?, ?, ?)"
result, err := repo.db.Exec(query, consent.UserID, consent.FirstName, consent.LastName, consent.Email, consent.CreatedAt, consent.UpdatedAt, consent.ConsentType)
if err != nil {
return -1, err
if result.Error != nil {
return 0, result.Error
}
lastInsertID, err := result.LastInsertId()
if err != nil {
return -1, err
}
return lastInsertID, err
return consent.ID, nil
}

View File

@@ -1,53 +1,41 @@
package repositories
import (
"database/sql"
"time"
"gorm.io/gorm"
"GoMembership/internal/models"
"GoMembership/pkg/errors"
)
type MembershipRepository interface {
CreateMembership(account *models.Membership) (int64, error)
CreateMembership(membership *models.Membership) (int64, error)
FindMembershipByUserID(userID int64) (*models.Membership, error)
}
type membershipRepository struct {
db *sql.DB
db *gorm.DB
}
func NewMembershipRepository(db *sql.DB) MembershipRepository {
func NewMembershipRepository(db *gorm.DB) MembershipRepository {
return &membershipRepository{db}
}
func (repo *membershipRepository) CreateMembership(membership *models.Membership) (int64, error) {
query := "INSERT INTO memberships (user_id, model_id, start_date, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
result, err := repo.db.Exec(query, membership.UserID, membership.MonthlyFee, membership.RentalFee, membership.Model, time.Now(), membership.Status)
if err != nil {
return -1, err
result := repo.db.Create(membership)
if result.Error != nil {
return 0, result.Error
}
lastInsertID, err := result.LastInsertId()
if err != nil {
return -1, err
}
return lastInsertID, err
return membership.ID, nil
}
func (repo *membershipRepository) FindMembershipByUserID(userID int64) (*models.Membership, error) {
var membership models.Membership
query := "SELECT id, model_id, start_date, end_date, status FROM memberships where user_id = ?"
err := repo.db.QueryRow(query, userID).Scan(&membership.ID, &membership.MonthlyFee, &membership.RentalFee, &membership.Model, &membership.StartDate, &membership.EndDate, &membership.Status)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.ErrNotFound
}
return nil, err
}
membership.UserID = userID
return &membership, nil
var membership models.Membership
result := repo.db.First(&membership, userID)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound
}
return nil, result.Error
}
return &membership, nil
}

View File

@@ -0,0 +1,27 @@
package repositories
import (
"gorm.io/gorm"
"GoMembership/internal/models"
)
type SubscriptionModelsRepository interface {
CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error)
}
type subscriptionModelsRepository struct {
db *gorm.DB
}
func NewSubscriptionModelsRepository(db *gorm.DB) SubscriptionModelsRepository {
return &subscriptionModelsRepository{db}
}
func (repo *subscriptionModelsRepository) CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (int64, error) {
result := repo.db.Create(subscriptionModel)
if result.Error != nil {
return 0, result.Error
}
return subscriptionModel.ID, nil
}

View File

@@ -1,12 +1,14 @@
package repositories
import (
"GoMembership/internal/constants"
"time"
"gorm.io/gorm"
"GoMembership/internal/models"
"GoMembership/pkg/errors"
"database/sql"
"fmt"
"strings"
"time"
"gorm.io/gorm/clause"
)
type UserRepository interface {
@@ -19,63 +21,32 @@ type UserRepository interface {
}
type userRepository struct {
db *sql.DB
db *gorm.DB
}
func NewUserRepository(db *sql.DB) UserRepository {
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{db}
}
func (repo *userRepository) CreateUser(user *models.User) (int64, error) {
query := "INSERT INTO users (first_name, last_name, email, phone, drivers_id_checked, role_id, payment_status, date_of_birth, address, profile_picture, notes, status, password, salt, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
result, err := repo.db.Exec(query,
user.FirstName,
user.LastName,
user.Email,
user.Phone,
user.DriversIDChecked,
user.RoleID,
user.PaymentStatus,
user.DateOfBirth,
user.Address,
user.ProfilePicture,
user.Notes,
user.Status,
user.Password,
user.Salt,
user.CreatedAt,
user.UpdatedAt)
if err != nil {
return -1, err
result := repo.db.Create(user)
if result.Error != nil {
return 0, result.Error
}
lastInsertID, err := result.LastInsertId()
if err != nil {
return -1, err
}
return lastInsertID, err
return user.ID, nil
}
func (repo *userRepository) UpdateUser(userID int64, updates map[string]interface{}) error {
if len(updates) == 0 {
return errors.ErrNoData
}
// Construct the query
setClauses := make([]string, 0, len(updates))
args := make([]interface{}, 0, len(updates)+1)
for column, value := range updates {
setClauses = append(setClauses, fmt.Sprintf("%s = ?", column))
args = append(args, value)
result := repo.db.Session(&gorm.Session{FullSaveAssociations: true}).Model(&models.User{}).Where("id = ?", userID).Updates(&updates)
if result.Error != nil {
return result.Error
}
args = append(args, userID)
query := fmt.Sprintf("UPDATE users SET %s WHERE id = ?", strings.Join(setClauses, ", "))
// Execute the query
_, err := repo.db.Exec(query, args...)
if err != nil {
return err
if result.RowsAffected == 0 {
return errors.ErrNoRowsAffected
}
return nil
@@ -83,88 +54,76 @@ func (repo *userRepository) UpdateUser(userID int64, updates map[string]interfac
func (repo *userRepository) FindUserByID(id int64) (*models.User, error) {
var user models.User
query := "SELECT id, first_name, last_name, email, phone, drivers_id_checked, role_id, payment_status, date_of_birth, address, profile_picture, notes, status FROM users WHERE id = ?"
err := repo.db.QueryRow(query, id).Scan(&user.ID,
&user.FirstName,
&user.LastName,
&user.Email,
&user.Phone,
&user.DriversIDChecked,
&user.RoleID,
&user.PaymentStatus,
&user.DateOfBirth,
&user.Address,
&user.ProfilePicture,
&user.Notes,
&user.Status)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.ErrUserNotFound
result := repo.db.First(&user, id)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound
}
return nil, err
return nil, result.Error
}
return &user, nil
}
func (repo *userRepository) FindUserByEmail(email string) (*models.User, error) {
var user models.User
query := "SELECT id, first_name, last_name, email FROM users WHERE email = ?"
err := repo.db.QueryRow(query, email).Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.ErrUserNotFound
result := repo.db.Where("email = ?", email).First(&user)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound
}
return nil, err
return nil, result.Error
}
return &user, nil
}
func (repo *userRepository) IsVerified(userID *int64) (bool, error) {
var status string
query := "SELECT status FROM users where id = ?"
err := repo.db.QueryRow(query, userID).Scan(&status)
if err != nil {
if err == sql.ErrNoRows {
return false, errors.ErrUserNotFound
var user models.User
result := repo.db.Select("status").First(&user, userID)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return false, gorm.ErrRecordNotFound
}
return false, err
return false, result.Error
}
return status != "unverified", nil
return user.Status != constants.UnverifiedStatus, nil
}
func (repo *userRepository) VerifyUserOfToken(token *string) (int64, error) {
var userID int64
err := repo.db.QueryRow("SELECT user_id FROM email_verifications WHERE verification_token = $1", token).Scan(&userID)
if err == sql.ErrNoRows {
return -1, errors.ErrTokenNotFound
} else if err != nil {
return -1, err
}
verified, err := repo.IsVerified(&userID)
if err != nil {
return -1, err
}
if verified {
return userID, errors.ErrAlreadyVerified
}
update := map[string]interface{}{
"status": "active",
var emailVerification models.Verification
result := repo.db.Where("verification_token = ?", token).First(&emailVerification)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return 0, gorm.ErrRecordNotFound
}
return 0, result.Error
}
err = repo.UpdateUser(userID, update)
// Check if the user is already verified
verified, err := repo.IsVerified(&emailVerification.UserID)
if err != nil {
return -1, err
return 0, err
}
query := "UPDATE email_verifications SET verified_at = ? WHERE user_id = ?"
_, err = repo.db.Exec(query, time.Now(), userID)
if verified {
return emailVerification.UserID, gorm.ErrRecordNotFound
}
// Update user status to active
t := time.Now()
emailVerification.EmailVerifiedAt = &t
update := map[string]interface{}{
"status": constants.VerifiedStatus,
"verifications": emailVerification,
}
err = repo.UpdateUser(emailVerification.UserID, update)
if err != nil {
return -1, err
return 0, err
}
return userID, nil
return emailVerification.UserID, nil
}
func (repo *userRepository) SetVerificationToken(user *models.User, token *string) (int64, error) {
// check if user already verified
// Check if user is already verified
verified, err := repo.IsVerified(&user.ID)
if err != nil {
return -1, err
@@ -173,15 +132,21 @@ func (repo *userRepository) SetVerificationToken(user *models.User, token *strin
return -1, errors.ErrAlreadyVerified
}
query := "INSERT OR REPLACE INTO email_verifications (user_id, verification_token, created_at) VALUES (?, ?, ?)"
result, err := repo.db.Exec(query, user.ID, token, time.Now())
if err != nil {
return -1, err
}
lastInsertID, err := result.LastInsertId()
if err != nil {
return -1, err
// Prepare the Verification record
verification := models.Verification{
UserID: user.ID,
VerificationToken: *token,
}
return lastInsertID, nil
// Use GORM to insert or update the Verification record
result := repo.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"verification_token", "created_at"}),
}).Create(&verification)
if result.Error != nil {
return -1, result.Error
}
return verification.ID, nil
}

View File

@@ -9,10 +9,11 @@ import (
"github.com/gorilla/mux"
)
func RegisterRoutes(router *mux.Router, userController *controllers.UserController) {
logger.Info.Println("Registering /api/register route")
func RegisterRoutes(router *mux.Router, userController *controllers.UserController, membershipController *controllers.MembershipController) {
logger.Info.Println("Registering backend/api/register route")
router.HandleFunc("/backend/api/verify", userController.VerifyMailHandler).Methods("GET")
router.HandleFunc("/backend/api/register", userController.RegisterUser).Methods("POST")
router.HandleFunc("/backend/api/plans", membershipController.RegisterPlan).Methods("POST")
// router.HandleFunc("/login", userController.LoginUser).Methods("POST")
}

View File

@@ -1,6 +1,8 @@
package server
import (
"net/http"
"GoMembership/internal/config"
"GoMembership/internal/controllers"
"GoMembership/internal/database"
@@ -9,16 +11,17 @@ import (
"GoMembership/internal/routes"
"GoMembership/internal/services"
"GoMembership/pkg/logger"
"net/http"
"github.com/gorilla/mux"
)
func Run() {
cfg := config.LoadConfig()
logger.Info.Printf("Config: %v", cfg)
db := database.Connect(cfg.DB)
defer db.Close()
logger.Info.Printf("Config: %+v", cfg)
db, 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)
@@ -26,11 +29,12 @@ func Run() {
bankAccountRepo := repositories.NewBankAccountRepository(db)
bankAccountService := services.NewBankAccountService(bankAccountRepo)
membershipRepo := repositories.NewMembershipRepository(db)
membershipService := services.NewMembershipService(membershipRepo)
planRepo := repositories.NewSubscriptionModelsRepository(db)
membershipService := services.NewMembershipService(membershipRepo, planRepo)
userRepo := repositories.NewUserRepository(db)
userService := services.NewUserService(userRepo)
userController := controllers.NewUserController(userService, emailService, consentService, bankAccountService, membershipService)
membershipController := controllers.NewMembershipController(membershipService)
router := mux.NewRouter()
// router.Handle("/csrf-token", middlewares.GenerateCSRFTokenHandler()).Methods("GET")
@@ -38,7 +42,7 @@ func Run() {
// router.Use(middlewares.CSRFMiddleware)
router.Use(middlewares.LoggerMiddleware)
routes.RegisterRoutes(router, userController)
routes.RegisterRoutes(router, userController, membershipController)
// create subrouter for teh authenticated area /account
// also pthprefix matches everything below /account
// accountRouter := router.PathPrefix("/account").Subrouter()

View File

@@ -24,7 +24,7 @@ func NewBankAccountService(repo repositories.BankAccountRepository) BankAccountS
func (service *bankAccountService) RegisterBankAccount(bankAccount *models.BankAccount) (int64, error) {
bankAccount.MandateDateSigned = time.Now()
ref, err := generateSEPAMandateReference(strconv.Itoa(bankAccount.UserID))
ref, err := generateSEPAMandateReference(strconv.FormatInt(bankAccount.UserID, 10))
if err != nil {
return -1, err
}

View File

@@ -86,10 +86,10 @@ func (s *EmailService) SendWelcomeEmail(user *models.User, membership *models.Me
RentalFee float32
}{
FirstName: user.FirstName,
MembershipModel: membership.Model,
MembershipModel: membership.SubscriptionModel.Name,
MembershipID: membership.ID,
MembershipFee: float32(membership.MonthlyFee),
RentalFee: float32(membership.RentalFee),
MembershipFee: float32(membership.SubscriptionModel.MonthlyFee),
RentalFee: float32(membership.SubscriptionModel.HourlyRate),
}
subject := "Willkommen beim Dörpsmobil Hasloh e.V."

View File

@@ -9,14 +9,16 @@ import (
type MembershipService interface {
RegisterMembership(membership *models.Membership) (int64, error)
FindMembershipByUserID(userID int64) (*models.Membership, error)
RegisterPlan(plan *models.SubscriptionModel) (int64, error)
}
type membershipService struct {
repo repositories.MembershipRepository
repo repositories.MembershipRepository
planRepo repositories.SubscriptionModelsRepository
}
func NewMembershipService(repo repositories.MembershipRepository) MembershipService {
return &membershipService{repo}
func NewMembershipService(repo repositories.MembershipRepository, planRepo repositories.SubscriptionModelsRepository) MembershipService {
return &membershipService{repo, planRepo}
}
func (service *membershipService) RegisterMembership(membership *models.Membership) (int64, error) {
@@ -27,3 +29,9 @@ func (service *membershipService) RegisterMembership(membership *models.Membersh
func (service *membershipService) FindMembershipByUserID(userID int64) (*models.Membership, error) {
return service.repo.FindMembershipByUserID(userID)
}
// Membership_Plans
func (service *membershipService) RegisterPlan(plan *models.SubscriptionModel) (int64, error) {
return service.planRepo.CreateSubscriptionModel(plan)
}

View File

@@ -1,6 +1,7 @@
package services
import (
"GoMembership/internal/constants"
"GoMembership/internal/models"
"GoMembership/internal/repositories"
"GoMembership/internal/utils"
@@ -38,7 +39,7 @@ func (service *userService) RegisterUser(user *models.User) (int64, string, erro
return -1, err
}
user.Password = string(hashedPassword) */
user.Status = "unverified"
user.Status = constants.UnverifiedStatus
user.CreatedAt = time.Now()
user.UpdatedAt = time.Now()
id, err := service.repo.CreateUser(user)

View File

@@ -11,4 +11,5 @@ var (
ErrTokenNotFound = errors.New("verification token not found")
ErrTokenNotSet = errors.New("verification token has not been set")
ErrNoData = errors.New("no data provided")
ErrNoRowsAffected = errors.New("no rows affected")
)