tercul-backend/internal/models/user.go
Damir Mukimov fa336cacf3
wip
2025-09-01 00:43:59 +02:00

116 lines
3.5 KiB
Go

package models
import (
"errors"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"time"
)
// User represents a user of the platform
type User struct {
BaseModel
Username string `gorm:"size:50;not null;unique"`
Email string `gorm:"size:100;not null;unique"`
Password string `gorm:"size:255;not null"`
FirstName string `gorm:"size:50"`
LastName string `gorm:"size:50"`
DisplayName string `gorm:"size:100"`
Bio string `gorm:"type:text"`
AvatarURL string `gorm:"size:255"`
Role UserRole `gorm:"size:20;default:'reader'"`
LastLoginAt *time.Time
Verified bool `gorm:"default:false"`
Active bool `gorm:"default:true"`
// Relationships
Translations []*Translation `gorm:"foreignKey:TranslatorID"`
Comments []*Comment `gorm:"foreignKey:UserID"`
Likes []*Like `gorm:"foreignKey:UserID"`
Bookmarks []*Bookmark `gorm:"foreignKey:UserID"`
Collections []*Collection `gorm:"foreignKey:UserID"`
Contributions []*Contribution `gorm:"foreignKey:UserID"`
// Location information
CountryID *uint
Country *Country `gorm:"foreignKey:CountryID"`
CityID *uint
City *City `gorm:"foreignKey:CityID"`
AddressID *uint
Address *Address `gorm:"foreignKey:AddressID"`
}
// UserProfile represents additional profile information for a user
type UserProfile struct {
BaseModel
UserID uint `gorm:"uniqueIndex"`
User *User `gorm:"foreignKey:UserID"`
PhoneNumber string `gorm:"size:20"`
Website string `gorm:"size:255"`
Twitter string `gorm:"size:50"`
Facebook string `gorm:"size:50"`
LinkedIn string `gorm:"size:50"`
Github string `gorm:"size:50"`
Preferences JSONB `gorm:"type:jsonb;default:'{}'"`
Settings JSONB `gorm:"type:jsonb;default:'{}'"`
}
// UserSession represents a user session
type UserSession struct {
BaseModel
UserID uint `gorm:"index"`
User *User `gorm:"foreignKey:UserID"`
Token string `gorm:"size:255;not null;uniqueIndex"`
IP string `gorm:"size:50"`
UserAgent string `gorm:"size:255"`
ExpiresAt time.Time `gorm:"not null"`
}
// PasswordReset represents a password reset request
type PasswordReset struct {
BaseModel
UserID uint `gorm:"index"`
User *User `gorm:"foreignKey:UserID"`
Token string `gorm:"size:255;not null;uniqueIndex"`
ExpiresAt time.Time `gorm:"not null"`
Used bool `gorm:"default:false"`
}
// EmailVerification represents an email verification request
type EmailVerification struct {
BaseModel
UserID uint `gorm:"index"`
User *User `gorm:"foreignKey:UserID"`
Token string `gorm:"size:255;not null;uniqueIndex"`
ExpiresAt time.Time `gorm:"not null"`
Used bool `gorm:"default:false"`
}
// BeforeSave hook for User to hash password if changed
func (u *User) BeforeSave(tx *gorm.DB) error {
// Check if password needs to be hashed
if u.Password == "" {
return nil // No password to hash
}
// Check if password is already hashed
if len(u.Password) >= 60 && u.Password[:4] == "$2a$" {
return nil // Password is already hashed
}
// Hash the password with bcrypt
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
if err != nil {
return errors.New("failed to hash password: " + err.Error())
}
u.Password = string(hashedPassword)
return nil
}
// CheckPassword verifies the provided password against the stored hash
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
return err == nil
}