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 }