191 lines
7.4 KiB
Go
191 lines
7.4 KiB
Go
package users
|
|
|
|
import (
|
|
"server/internal/auth"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type User struct {
|
|
ID int `gorm:"primaryKey"`
|
|
Email string `json:"email" gorm:"uniqueIndex;size:255"`
|
|
Name string `json:"name" gorm:"size:255"`
|
|
Password string `json:"-" gorm:"size:255"`
|
|
Permission auth.Permission `json:"permission"`
|
|
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=Nullable<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,omitempty" ts:"type=Date"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty" ts:"type=Date"`
|
|
DeletedAt *gorm.DeletedAt `json:"-" gorm:"index" `
|
|
}
|
|
|
|
// 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"`
|
|
Permission auth.Permission `json:"permission"`
|
|
Types UserTypes `json:"types"`
|
|
Status UserStatus `json:"status"`
|
|
ActivatedAt *time.Time `json:"activatedAt" ts:"type=Nullable<Date>"`
|
|
UUID string `json:"uuid"`
|
|
Details *UserDetails `json:"details"`
|
|
Preferences *UserPreferences `json:"preferences"`
|
|
Avatar *string `json:"avatar"`
|
|
CreatedAt *time.Time `json:"createdAt,omitempty" ts:"type=Date"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty" 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,
|
|
Permission: u.Permission,
|
|
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,omitempty" ts:"type=Date"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty" 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,omitempty" ts:"type=Date"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty" ts:"type=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=Nullable<Date>" gorm:"index"`
|
|
IPAddress string `json:"ipAddress" gorm:"size:64"`
|
|
UserAgent string `json:"userAgent" gorm:"size:512"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty" ts:"type=Date"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty" 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,omitempty" ts:"type=Date" gorm:"index"`
|
|
UsedAt *time.Time `json:"usedAt,omitempty" ts:"type=Date"`
|
|
CreatedAt *time.Time `json:"createdAt" ts:"type=Date"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty" 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 UpdatePasswordRequest struct {
|
|
Password string `json:"password" validate:"required,min=8,max=128"`
|
|
}
|
|
|
|
type UpdateUserRequest struct {
|
|
UUID string `json:"uuid" validate:"required,uuid4"`
|
|
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"`
|
|
Permission auth.Permission `json:"permission"`
|
|
Status UserStatus `json:"status"`
|
|
Types UserTypes `json:"types"`
|
|
Avatar *string `json:"avatar,omitempty"`
|
|
Details *UserDetails `json:"details,omitempty"`
|
|
Preferences *UserPreferences `json:"preferences,omitempty"`
|
|
}
|
|
|
|
// 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"`
|
|
Permission auth.Permission `json:"permission"`
|
|
Status UserStatus `json:"status"`
|
|
Types UserTypes `json:"types"`
|
|
Avatar *string `json:"avatar,omitempty"`
|
|
Details *UserDetails `json:"details,omitempty"`
|
|
Preferences *UserPreferences `json:"preferences,omitempty"`
|
|
}
|