package models import ( "encoding/json" "time" "gorm.io/gorm" "golang.org/x/crypto/bcrypt" ) // User represents a user in the system type User struct { ID int64 `gorm:"column:rowid;primaryKey;autoIncrement" json:"id"` // Unique identifier Username string `json:"username" gorm:"uniqueIndex;not null"` // Username Email string `json:"email" gorm:"uniqueIndex;not null"` // User's email address Password string `json:"password,omitempty" gorm:"not null"` // Password (hashed) Role string `json:"role" gorm:"default:'user'"` // User role PreferencesJSON string `json:"-" gorm:"column:preferences;type:text"` // User preferences stored as JSON string Preferences UserPreferences `json:"preferences" gorm:"-"` // User preferences (transient field) CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"` // Creation timestamp UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"` // Last update timestamp DeletedAt gorm.DeletedAt `json:"-" gorm:"index"` // Soft delete support } // UserPreferences represents user-specific settings type UserPreferences struct { Theme string `json:"theme"` // User's theme preference Notifications bool `json:"notifications"` // Notification preference DashboardLayout interface{} `json:"dashboardLayout,omitempty"` // Optional dashboard layout } // BeforeSave hook runs before saving a User - handles password hashing and preferences serialization func (u *User) BeforeSave(tx *gorm.DB) error { // Only hash password if it's provided and not already hashed if u.Password != "" && len(u.Password) < 60 { // bcrypt hashes are typically 60 characters hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost) if err != nil { return err } u.Password = string(hashedPassword) } // Serialize preferences to JSON preferencesData, err := json.Marshal(u.Preferences) if err != nil { return err } u.PreferencesJSON = string(preferencesData) return nil } // AfterFind hook deserializes JSON preferences after fetching from database func (u *User) AfterFind(tx *gorm.DB) error { if u.PreferencesJSON != "" { return json.Unmarshal([]byte(u.PreferencesJSON), &u.Preferences) } return nil } // CheckPassword verifies if the provided password matches the hashed one func (u *User) CheckPassword(password string) bool { err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) return err == nil } type UserRole string const ( Admin UserRole = "admin" Developer UserRole = "developer" Viewer UserRole = "viewer" )