tercul-backend/models/user.go
Damir Mukimov 4957117cb6 Initial commit: Tercul Go project with comprehensive architecture
- Core Go application with GraphQL API using gqlgen
- Comprehensive data models for literary works, authors, translations
- Repository pattern with caching layer
- Authentication and authorization system
- Linguistics analysis capabilities with multiple adapters
- Vector search integration with Weaviate
- Docker containerization support
- Python data migration and analysis scripts
- Clean architecture with proper separation of concerns
- Production-ready configuration and middleware
- Proper .gitignore excluding vendor/, database files, and build artifacts
2025-08-13 07:42:32 +02:00

116 lines
3.6 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
}