mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 00:31:35 +00:00
- 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
116 lines
3.6 KiB
Go
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
|
|
}
|