switched to gorm..
This commit is contained in:
@@ -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
4
go.mod
@@ -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
8
go.sum
@@ -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=
|
||||
|
||||
@@ -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 {
|
||||
Auth AuthenticationConfig `json:"auth"`
|
||||
DB DatabaseConfig `json:"db"`
|
||||
Auth AuthenticationConfig
|
||||
SMTP SMTPConfig `json:"smtp"`
|
||||
Templates TemplateConfig `json:"templates"`
|
||||
SMTP SMTPConfig `json:"smtp"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
12
internal/constants/constants.go
Normal file
12
internal/constants/constants.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
UnverifiedStatus = iota + 1
|
||||
VerifiedStatus
|
||||
ActiveStatus
|
||||
PassiveStatus
|
||||
DisabledStatus
|
||||
DelayedPaymentStatus
|
||||
SettledPaymentStatus
|
||||
AwaitingPaymentStatus
|
||||
)
|
||||
60
internal/controllers/membershipController.go
Normal file
60
internal/controllers/membershipController.go
Normal 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(®Data); 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(®Data.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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
13
internal/models/Verifications.go
Normal file
13
internal/models/Verifications.go
Normal 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"`
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ package models
|
||||
import "time"
|
||||
|
||||
type Membership struct {
|
||||
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"`
|
||||
ParentID int64 `json:"parent_id"`
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
18
internal/models/subscription_model.go
Normal file
18
internal/models/subscription_model.go
Normal 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"`
|
||||
}
|
||||
@@ -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"`
|
||||
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"`
|
||||
Address string `json:"address"`
|
||||
ID int64 `json:"id"`
|
||||
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"`
|
||||
DriversIDChecked bool `json:"drivers_id_checked"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
27
internal/repositories/subscription_model_repository.go
Normal file
27
internal/repositories/subscription_model_repository.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
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
|
||||
}
|
||||
verified, err := repo.IsVerified(&userID)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if verified {
|
||||
return userID, errors.ErrAlreadyVerified
|
||||
}
|
||||
update := map[string]interface{}{
|
||||
"status": "active",
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user