moved repo to user model

This commit is contained in:
Alex
2025-03-11 20:44:29 +01:00
parent 39c060794a
commit ca441d51e7

View File

@@ -1,11 +1,20 @@
package models
import (
"GoMembership/internal/config"
"GoMembership/internal/constants"
"GoMembership/internal/utils"
"GoMembership/pkg/errors"
"GoMembership/pkg/logger"
"fmt"
"slices"
"time"
"github.com/alexedwards/argon2id"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type User struct {
@@ -28,8 +37,7 @@ type User struct {
Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"`
BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bank_account"`
BankAccountID uint
Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
VerificationID uint
Verifications *[]Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"membership"`
MembershipID uint
Licence *Licence `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"licence"`
@@ -69,6 +77,268 @@ func (u *User) PasswordMatches(plaintextPassword string) (bool, error) {
return argon2id.ComparePasswordAndHash(plaintextPassword, u.Password)
}
func (u *User) PasswordExists() bool {
return u.Password != ""
}
func (u *User) Delete(db *gorm.DB) error {
return db.Delete(&User{}, "id = ?", u.ID).Error
}
func (u *User) Create(db *gorm.DB) error {
return db.Transaction(func(tx *gorm.DB) error {
// Create the base User record (omit associations to handle them separately)
if err := tx.Create(u).Error; err != nil {
return err
}
// Replace associated Categories (assumes Categories already exist)
if u.Licence != nil && len(u.Licence.Categories) > 0 {
if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil {
return err
}
}
logger.Info.Printf("user created: %#v", u.Safe())
// Preload all associations to return the fully populated User
return tx.
Preload("Membership").
Preload("Membership.SubscriptionModel").
Preload("Licence").
Preload("Licence.Categories").
First(u, u.ID).Error // Refresh the user object with all associations
})
}
func (u *User) Update(db *gorm.DB) error {
err := db.Transaction(func(tx *gorm.DB) error {
// Check if the user exists in the database
var existingUser User
logger.Info.Printf("updating user: %#v", u)
if err := tx.
Preload("Membership").
Preload("Membership.SubscriptionModel").
Preload("Licence").
Preload("Licence.Categories").
Preload("Verifications").
First(&existingUser, u.ID).Error; err != nil {
return err
}
// Update the user's main fields
result := tx.Session(&gorm.Session{FullSaveAssociations: true}).Omit("Password", "Membership", "Licence", "Verifications").Updates(u)
if result.Error != nil {
logger.Error.Printf("User update error in update user: %#v", result.Error)
return result.Error
}
if result.RowsAffected == 0 {
return errors.ErrNoRowsAffected
}
if u.Password != "" {
if err := tx.Model(&existingUser).
Update("Password", u.Password).Error; err != nil {
logger.Error.Printf("Password update error in update user: %#v", err)
return err
}
}
// Update the Membership if provided
if u.Membership.ID != 0 {
if err := tx.Model(&existingUser.Membership).Updates(u.Membership).Error; err != nil {
logger.Error.Printf("Membership update error in update user: %#v", err)
return err
}
}
if u.Licence != nil {
u.Licence.UserID = existingUser.ID
if err := tx.Save(u.Licence).Error; err != nil {
return err
}
if err := tx.Model(&existingUser).Update("LicenceID", u.Licence.ID).Error; err != nil {
return err
}
if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil {
return err
}
}
// if u.Licence != nil {
// if existingUser.Licence == nil || existingUser.LicenceID == 0 {
// u.Licence.UserID = existingUser.ID // Ensure Licence belongs to User
// if err := tx.Create(u.Licence).Error; err != nil {
// return err
// }
// existingUser.Licence = u.Licence
// existingUser.LicenceID = u.Licence.ID
// if err := tx.Model(&existingUser).Update("LicenceID", u.Licence.ID).Error; err != nil {
// return err
// }
// }
// if err := tx.Model(existingUser.Licence).Updates(u.Licence).Error; err != nil {
// return err
// }
// // Update Categories association
// if err := tx.Model(existingUser.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil {
// return err
// }
// }
if u.Verifications != nil {
if err := tx.Save(*u.Verifications).Error; err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
return db.
Preload("Membership").
Preload("Membership.SubscriptionModel").
Preload("Licence").
Preload("Licence.Categories").
Preload("Verifications").
First(&u, u.ID).Error
}
func (u *User) FromID(db *gorm.DB, userID *uint) error {
var user User
result := db.
Preload(clause.Associations).
Preload("Membership").
Preload("Membership.SubscriptionModel").
Preload("Licence").
Preload("Licence.Categories").
Preload("Verifications").
First(&user, userID)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return gorm.ErrRecordNotFound
}
return result.Error
}
*u = user
return nil
}
func (u *User) FromEmail(db *gorm.DB, email *string) error {
var user User
result := db.
Preload(clause.Associations).
Preload("Membership").
Preload("Membership.SubscriptionModel").
Preload("Licence").
Preload("Licence.Categories").
Preload("Verifications").
Where("email = ?", email).First(&user)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return gorm.ErrRecordNotFound
}
return result.Error
}
*u = user
return nil
}
func (u *User) FromContext(db *gorm.DB, c *gin.Context) error {
tokenString, err := c.Cookie("jwt")
if err != nil {
return err
}
jwtUserID, err := extractUserIDFrom(tokenString)
if err != nil {
return err
}
if err = u.FromID(db, &jwtUserID); err != nil {
return err
}
return nil
}
func (u *User) IsVerified() bool {
return u.Status > constants.DisabledStatus
}
func (u *User) HasPrivilege(privilege int8) bool {
return u.RoleID >= privilege
}
func (u *User) IsAdmin() bool {
return u.RoleID == constants.Roles.Admin
}
func (u *User) IsMember() bool {
return u.RoleID == constants.Roles.Member
}
func (u *User) IsSupporter() bool {
return u.RoleID == constants.Roles.Supporter
}
func (u *User) SetVerification(verificationType string) (*Verification, error) {
if u.Verifications == nil {
u.Verifications = new([]Verification)
}
token, err := utils.GenerateVerificationToken()
if err != nil {
return nil, err
}
v := Verification{
UserID: u.ID,
VerificationToken: token,
Type: verificationType,
}
if vi := slices.IndexFunc(*u.Verifications, func(vsl Verification) bool { return vsl.Type == v.Type }); vi > -1 {
(*u.Verifications)[vi] = v
} else {
*u.Verifications = append(*u.Verifications, v)
}
return &v, nil
}
func (u *User) GetVerification(verificationType string) (*Verification, error) {
if u.Verifications == nil {
return nil, errors.ErrNoData
}
vi := slices.IndexFunc(*u.Verifications, func(vsl Verification) bool { return vsl.Type == verificationType })
if vi == -1 {
return nil, errors.ErrNotFound
}
return &(*u.Verifications)[vi], nil
}
func (u *User) Verify(token string, verificationType string) bool {
if token == "" || verificationType == "" {
logger.Error.Printf("token or verification type are empty in user.Verify")
return false
}
vi := slices.IndexFunc(*u.Verifications, func(vsl Verification) bool {
return vsl.Type == verificationType && vsl.VerificationToken == token
})
if vi == -1 {
logger.Error.Printf("Couldn't find verification in users verifications")
return false
}
if (*u.Verifications)[vi].VerifiedAt != nil {
logger.Error.Printf("VerifiedAt is not nil, already verified?: %#v", (*u.Verifications)[vi])
return false
}
t := time.Now()
(*u.Verifications)[vi].VerifiedAt = &t
return true
}
func (u *User) Safe() map[string]interface{} {
result := map[string]interface{}{
"email": u.Email,
@@ -128,3 +398,61 @@ func (u *User) Safe() map[string]interface{} {
return result
}
func extractUserIDFrom(tokenString string) (uint, error) {
jwtSigningMethod := jwt.SigningMethodHS256
jwtParser := jwt.NewParser(jwt.WithValidMethods([]string{jwtSigningMethod.Alg()}))
token, err := jwtParser.Parse(tokenString, func(_ *jwt.Token) (interface{}, error) {
return []byte(config.Auth.JWTSecret), nil
})
// Handle parsing errors (excluding expiration error)
if err != nil && !errors.Is(err, jwt.ErrTokenExpired) || token == nil {
logger.Error.Printf("Error parsing token: %v", err)
return 0, err
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
logger.Error.Print("Invalid token claims structure")
return 0, fmt.Errorf("invalid token claims format")
}
// Validate required session_id claim
if _, exists := claims["session_id"]; !exists {
logger.Error.Print("Missing session_id in token claims")
return 0, fmt.Errorf("missing session_id claim")
}
// Return token, claims, and original error (might be expiration)
if _, exists := claims["session_id"]; !exists {
logger.Error.Print("Missing session_id in token claims")
return 0, fmt.Errorf("missing session_id claim")
}
id, ok := claims["user_id"]
if !ok {
return 0, fmt.Errorf("missing user_id claim")
}
return uint(id.(float64)), nil
}
func GetUsersWhere(db *gorm.DB, where map[string]interface{}) (*[]User, error) {
var users []User
result := db.
Preload(clause.Associations).
Preload("Membership").
Preload("Membership.SubscriptionModel").
Preload("Licence").
Preload("Licence.Categories").
Preload("Verifications").
Where(where).Find(&users)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound
}
return nil, result.Error
}
return &users, nil
}