* HH-469: close websocket fanout contract gaps * fix: unify message sender contracts --------- Co-authored-by: Rogee <rogee@ipao.vip>
93 lines
3.6 KiB
Go
93 lines
3.6 KiB
Go
package model
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"time"
|
|
|
|
"gorm.io/datatypes"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// User represents an agent/admin user in the system.
|
|
type User struct {
|
|
Base
|
|
AccountID uint `gorm:"index;not null" json:"account_id"`
|
|
Name string `gorm:"size:255;not null" json:"name"`
|
|
Email string `gorm:"size:255;uniqueIndex;not null" json:"email"`
|
|
Password string `gorm:"size:255;not null" json:"-"` // hashed password (bcrypt)
|
|
PasswordDigest string `gorm:"size:255" json:"-"` // alias used by auth service
|
|
Provider string `gorm:"size:50;default:email" json:"provider"` // email, google, oidc
|
|
UID string `gorm:"size:255" json:"uid,omitempty"` // external ID for OAuth providers
|
|
AvatarURL string `gorm:"size:512" json:"avatar_url"`
|
|
DisplayName string `gorm:"size:255" json:"display_name"`
|
|
MessageSignature string `gorm:"type:text" json:"message_signature"`
|
|
PubsubToken string `gorm:"size:255;uniqueIndex" json:"pubsub_token"`
|
|
UISettings datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"ui_settings"`
|
|
CustomAttributes datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"custom_attributes,omitempty"`
|
|
Role string `gorm:"size:50;default:agent" json:"role"` // agent, admin
|
|
Type string `gorm:"size:50;default:user" json:"type"`
|
|
Active bool `gorm:"default:true" json:"active"`
|
|
Available bool `gorm:"default:false" json:"available"`
|
|
// Deprecated: MFA/TOTP functionality has been removed. These fields are kept
|
|
// only to avoid GORM schema mismatch with existing DB columns.
|
|
TOTPSecret string `gorm:"size:512" json:"-" secure:"totp_secret"`
|
|
TOTPEnabled bool `gorm:"default:false" json:"totp_enabled"`
|
|
CustomRoleID *uint `gorm:"index" json:"custom_role_id,omitempty"`
|
|
SignInCount int `gorm:"default:0" json:"sign_in_count"`
|
|
ResetPasswordToken string `gorm:"size:255;index" json:"-"`
|
|
ResetPasswordSentAt *time.Time `json:"-"`
|
|
ConfirmationToken string `gorm:"size:255;index" json:"-"`
|
|
ConfirmationSentAt *time.Time `json:"-"`
|
|
UnconfirmedEmail string `gorm:"size:255" json:"unconfirmed_email,omitempty"`
|
|
ConfirmedAt *time.Time `json:"confirmed_at,omitempty"`
|
|
LastSignInAt *time.Time `json:"last_sign_in_at,omitempty"`
|
|
CurrentSignInAt *time.Time `json:"current_sign_in_at,omitempty"`
|
|
}
|
|
|
|
func (User) TableName() string { return "users" }
|
|
|
|
// PushEventData matches Chatwoot's public message-sender contract.
|
|
func (u *User) PushEventData() map[string]any {
|
|
availableName := u.DisplayName
|
|
if availableName == "" {
|
|
availableName = u.Name
|
|
}
|
|
availabilityStatus := "offline"
|
|
if u.Available {
|
|
availabilityStatus = "online"
|
|
}
|
|
return map[string]any{
|
|
"id": u.ID,
|
|
"name": u.Name,
|
|
"available_name": availableName,
|
|
"avatar_url": u.AvatarURL,
|
|
"type": "user",
|
|
"availability_status": availabilityStatus,
|
|
"thumbnail": u.AvatarURL,
|
|
}
|
|
}
|
|
|
|
// BeforeCreate mirrors Chatwoot's Pubsubable concern by assigning every user a cable token.
|
|
func (u *User) BeforeCreate(tx *gorm.DB) error {
|
|
if u.PubsubToken == "" {
|
|
token, err := generateSecureToken(32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
u.PubsubToken = token
|
|
}
|
|
if u.Type == "" {
|
|
u.Type = "User"
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func generateSecureToken(size int) (string, error) {
|
|
b := make([]byte, size)
|
|
if _, err := rand.Read(b); err != nil {
|
|
return "", err
|
|
}
|
|
return hex.EncodeToString(b), nil
|
|
}
|