go-quasar-partial-ssr/backend/internal/user/model.go

188 lines
7.0 KiB
Go

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<Date>"`
UpdatedAt time.Time `json:"updatedAt" ts:"type=Nullable<Date>"`
}
// 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" `
}