package users import ( "time" "gorm.io/gorm" ) // UserRoles is stored as JSON array of strings. type UserRoles []string type User struct { ID int `json:"id" gorm:"primaryKey"` Email string `json:"email" gorm:"uniqueIndex;size:255"` Name string `json:"name" gorm:"size:255"` Password string `json:"-" gorm:"size:255"` Roles UserRoles `json:"roles" gorm:"type:text;serializer:json"` Types UserTypes `json:"types" gorm:"type:text;serializer:json"` Status UserStatus `json:"status" gorm:"type:text;default:'pending'"` ActivatedAt *time.Time `json:"activatedAt" ts:"type=Date"` UUID string `json:"uuid" gorm:"size:36"` Details *UserDetails `json:"details" gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` Preferences *UserPreferences `json:"preferences" gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` Avatar *string `json:"avatar" gorm:"size:512"` CreatedAt *time.Time `json:"createdAt" ts:"type=Date"` UpdatedAt *time.Time `json:"updatedAt" ts:"type=Date"` DeletedAt *gorm.DeletedAt `json:"-" gorm:"index" ts:"type=Date"` } // UserTypes is stored as JSON array (e.g. ["internal","external"]). type UserTypes []string // UserProfile is the safe full representation of a user returned by CRUD endpoints. type UserProfile struct { ID int `json:"id"` Email string `json:"email"` Name string `json:"name"` Roles UserRoles `json:"roles"` Types UserTypes `json:"types"` Status UserStatus `json:"status"` ActivatedAt *time.Time `json:"activatedAt" ts:"type=Date"` UUID string `json:"uuid"` Details *UserDetails `json:"details"` Preferences *UserPreferences `json:"preferences"` Avatar *string `json:"avatar"` CreatedAt *time.Time `json:"createdAt" ts:"type=Date"` UpdatedAt *time.Time `json:"updatedAt" ts:"type=Date"` } // ToUserProfile maps a User to a full response without exposing the password hash. func ToUserProfile(u *User) UserProfile { if u == nil { return UserProfile{} } return UserProfile{ ID: u.ID, Email: u.Email, Name: u.Name, Roles: u.Roles, Types: u.Types, Status: u.Status, ActivatedAt: u.ActivatedAt, UUID: u.UUID, Details: u.Details, Preferences: u.Preferences, Avatar: u.Avatar, CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, } } // UserPreferences holds per-user settings stored as JSON. type UserPreferences struct { ID int `json:"id" gorm:"primaryKey"` UserID int `json:"userId" gorm:"index"` UseIdle bool `json:"useIdle"` IdleTimeout int `json:"idleTimeout"` UseIdlePassword bool `json:"useIdlePassword"` IdlePin string `json:"idlePin"` UseDirectLogin bool `json:"useDirectLogin"` UseQuadcodeLogin bool `json:"useQuadcodeLogin"` SendNoticesMail bool `json:"sendNoticesMail"` Language string `json:"language"` CreatedAt time.Time `json:"createdAt" ts:"type=Date"` UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"` } // UserDetails holds optional profile data. type UserDetails struct { ID int `json:"id" gorm:"primaryKey"` UserID int `json:"userId" gorm:"index"` Title string `json:"title"` FirstName string `json:"firstName"` LastName string `json:"lastName"` Address string `json:"address"` City string `json:"city"` ZipCode string `json:"zipCode"` Country string `json:"country"` Phone string `json:"phone"` CreatedAt time.Time `json:"createdAt" ts:"type=Nullable"` UpdatedAt time.Time `json:"updatedAt" ts:"type=Nullable"` } // UserDetails holds optional profile data. // Session tracks logins with browser metadata. type Session struct { ID int `json:"id" gorm:"primaryKey"` UserID *int `json:"userId" gorm:"index"` Username string `json:"username" gorm:"size:255"` AccessTokenHash string `json:"-" gorm:"size:128;index"` RefreshTokenHash string `json:"-" gorm:"size:128;index"` ExpiresAt time.Time `json:"expiresAt" ts:"type=Date" gorm:"index"` IPAddress string `json:"ipAddress" gorm:"size:64"` UserAgent string `json:"userAgent" gorm:"size:512"` CreatedAt time.Time `json:"createdAt" ts:"type=Date"` UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"` DeletedAt gorm.DeletedAt `json:"-" gorm:"index" ` } type PasswordResetToken struct { ID int `json:"id" gorm:"primaryKey"` UserID int `json:"userId" gorm:"index"` TokenHash string `json:"-" gorm:"size:64;uniqueIndex"` ExpiresAt time.Time `json:"expiresAt" ts:"type=Date" gorm:"index"` UsedAt *time.Time `json:"usedAt" ts:"type=Date"` CreatedAt time.Time `json:"createdAt" ts:"type=Date"` UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"` DeletedAt gorm.DeletedAt `json:"-" gorm:"index"` } // UserStatus represents lifecycle state of a user. type UserStatus string // Typescript: enum=UserStatus const ( UserStatusPending UserStatus = "pending" UserStatusActive UserStatus = "active" UserStatusDisabled UserStatus = "disabled" ) type LoginRequest struct { Username string `json:"username" validate:"required,email"` Password string `json:"password" validate:"required,min=8,max=128"` } type RefreshRequest struct { RefreshToken string `json:"refresh_token"` } type ForgotPasswordRequest struct { Email string `json:"email" validate:"required,email"` } type ResetPasswordRequest struct { Token string `json:"token" validate:"required,min=20,max=255"` Password string `json:"password" validate:"required,min=8,max=128"` } type UpdateUserRequest struct { Name string `json:"name" validate:"required,min=1,max=255"` Email string `json:"email" validate:"required,email"` Password string `json:"password" validate:"omitempty,min=8,max=128"` Roles UserRoles `json:"roles"` Status UserStatus `json:"status"` Types UserTypes `json:"types"` Avatar *string `json:"avatar"` Details *UserDetails `json:"details"` Preferences *UserPreferences `json:"preferences"` } // UserCreateRequest captures the minimal payload to create a user. type UserCreateRequest struct { Name string `json:"name" validate:"required,min=1,max=255"` Email string `json:"email" validate:"required,email"` Password string `json:"password" validate:"required,min=8,max=128"` Roles UserRoles `json:"roles"` Status UserStatus `json:"status"` Types UserTypes `json:"types"` Avatar *string `json:"avatar"` Details *UserDetails `json:"details" ` Preferences *UserPreferences `json:"preferences" ` }