committtttt

This commit is contained in:
Dev
2025-09-13 06:48:55 +03:00
commit 2d49f69be6
32 changed files with 6478 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
package models
import (
"time"
"gorm.io/gorm"
)
// AIModel represents an AI model configuration
type AIModel struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"not null"`
Type string `json:"type" gorm:"not null"` // 'openai', 'local', 'custom'
Endpoint string `json:"endpoint"`
APIKey string `json:"apiKey"` // Encrypted in database
Model string `json:"model"` // e.g., 'gpt-4', 'llama2', etc.
MaxTokens int `json:"maxTokens" gorm:"default:1000"`
Temperature float64 `json:"temperature" gorm:"default:0.7"`
TopP float64 `json:"topP" gorm:"default:1.0"`
Active bool `json:"active" gorm:"default:true"`
Priority int `json:"priority" gorm:"default:1"` // Higher number = higher priority
Description string `json:"description"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}
// AIInteraction represents an interaction with an AI model
type AIInteraction struct {
ID uint `json:"id" gorm:"primaryKey"`
ConversationID uint `json:"conversationId"`
MessageID uint `json:"messageId"`
AIModelID uint `json:"aiModelId"`
Prompt string `json:"prompt" gorm:"not null"`
Response string `json:"response" gorm:"not null"`
TokensUsed int `json:"tokensUsed"`
ResponseTime int64 `json:"responseTime"` // in milliseconds
Cost float64 `json:"cost"`
Success bool `json:"success"`
ErrorMessage string `json:"errorMessage"`
CreatedAt time.Time `json:"createdAt"`
// Relationships
Conversation Conversation `json:"conversation" gorm:"foreignKey:ConversationID"`
Message Message `json:"message" gorm:"foreignKey:MessageID"`
AIModel AIModel `json:"aiModel" gorm:"foreignKey:AIModelID"`
}
// AIFallback represents a fallback event when an AI model fails
type AIFallback struct {
ID uint `json:"id" gorm:"primaryKey"`
ConversationID uint `json:"conversationId"`
MessageID uint `json:"messageId"`
FromAIModelID uint `json:"fromAiModelId"`
ToAIModelID uint `json:"toAiModelId"`
Reason string `json:"reason" gorm:"not null"`
CreatedAt time.Time `json:"createdAt"`
// Relationships
Conversation Conversation `json:"conversation" gorm:"foreignKey:ConversationID"`
Message Message `json:"message" gorm:"foreignKey:MessageID"`
FromAIModel AIModel `json:"fromAiModel" gorm:"foreignKey:FromAIModelID"`
ToAIModel AIModel `json:"toAiModel" gorm:"foreignKey:ToAIModelID"`
}
// BeforeCreate is a GORM hook that validates the AI model before creation
func (a *AIModel) BeforeCreate(tx *gorm.DB) error {
if a.Priority < 1 {
a.Priority = 1
}
if a.MaxTokens < 1 {
a.MaxTokens = 1000
}
if a.Temperature < 0 {
a.Temperature = 0
} else if a.Temperature > 2 {
a.Temperature = 2
}
if a.TopP < 0 {
a.TopP = 0
} else if a.TopP > 1 {
a.TopP = 1
}
return nil
}
// BeforeUpdate is a GORM hook that validates the AI model before update
func (a *AIModel) BeforeUpdate(tx *gorm.DB) error {
if a.Priority < 1 {
a.Priority = 1
}
if a.MaxTokens < 1 {
a.MaxTokens = 1000
}
if a.Temperature < 0 {
a.Temperature = 0
} else if a.Temperature > 2 {
a.Temperature = 2
}
if a.TopP < 0 {
a.TopP = 0
} else if a.TopP > 1 {
a.TopP = 1
}
return nil
}
// IsActive checks if the AI model is active
func (a *AIModel) IsActive() bool {
return a.Active
}
// IsOpenAI checks if the AI model is an OpenAI model
func (a *AIModel) IsOpenAI() bool {
return a.Type == "openai"
}
// IsLocal checks if the AI model is a local model
func (a *AIModel) IsLocal() bool {
return a.Type == "local"
}
// IsCustom checks if the AI model is a custom model
func (a *AIModel) IsCustom() bool {
return a.Type == "custom"
}

View File

@@ -0,0 +1,63 @@
package models
import (
"time"
"gorm.io/gorm"
)
// Conversation represents a chat conversation between users and/or agents
type Conversation struct {
ID uint `json:"id" gorm:"primaryKey"`
Title string `json:"title"`
UserID uint `json:"userId"`
AgentID *uint `json:"agentId,omitempty"`
Status string `json:"status" gorm:"default:'active'"` // 'active', 'closed', 'escalated'
Department string `json:"department"`
Priority string `json:"priority" gorm:"default:'medium'"` // 'low', 'medium', 'high', 'urgent'
Tags string `json:"tags"` // Comma-separated tags
LastMessageAt time.Time `json:"lastMessageAt"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
// Relationships
User User `json:"user" gorm:"foreignKey:UserID"`
Agent *User `json:"agent,omitempty" gorm:"foreignKey:AgentID"`
Messages []Message `json:"messages" gorm:"foreignKey:ConversationID"`
}
// Message represents a single message in a conversation
type Message struct {
ID uint `json:"id" gorm:"primaryKey"`
ConversationID uint `json:"conversationId"`
UserID uint `json:"userId"`
Content string `json:"content" gorm:"not null"`
Type string `json:"type" gorm:"default:'text'"` // 'text', 'image', 'file', 'system'
Status string `json:"status" gorm:"default:'sent'"` // 'sent', 'delivered', 'read'
Sentiment float64 `json:"sentiment"` // Sentiment score from -1 (negative) to 1 (positive)
IsAI bool `json:"isAI" gorm:"default:false"`
AIModel string `json:"aiModel,omitempty"` // Which AI model generated this message
ReadAt *time.Time `json:"readAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
// Relationships
Conversation Conversation `json:"conversation" gorm:"foreignKey:ConversationID"`
User User `json:"user" gorm:"foreignKey:UserID"`
}
// BeforeCreate is a GORM hook that sets the LastMessageAt field when creating a conversation
func (c *Conversation) BeforeCreate(tx *gorm.DB) error {
c.LastMessageAt = time.Now()
return nil
}
// BeforeCreate is a GORM hook that updates the conversation's LastMessageAt when creating a message
func (m *Message) BeforeCreate(tx *gorm.DB) error {
// Update the conversation's LastMessageAt
tx.Model(&Conversation{}).Where("id = ?", m.ConversationID).Update("last_message_at", time.Now())
return nil
}

View File

@@ -0,0 +1,85 @@
package models
import (
"time"
"gorm.io/gorm"
)
// KnowledgeBase represents a knowledge base entry (FAQ)
type KnowledgeBase struct {
ID uint `json:"id" gorm:"primaryKey"`
Question string `json:"question" gorm:"not null"`
Answer string `json:"answer" gorm:"not null"`
Category string `json:"category" gorm:"not null"`
Tags string `json:"tags"` // Comma-separated tags
Priority int `json:"priority" gorm:"default:1"` // Higher number = higher priority
ViewCount int `json:"viewCount" gorm:"default:0"`
Helpful int `json:"helpful" gorm:"default:0"` // Number of times marked as helpful
NotHelpful int `json:"notHelpful" gorm:"default:0"` // Number of times marked as not helpful
Active bool `json:"active" gorm:"default:true"`
CreatedBy uint `json:"createdBy"`
UpdatedBy uint `json:"updatedBy"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
// Relationships
Creator User `json:"creator" gorm:"foreignKey:CreatedBy"`
Updater User `json:"updater" gorm:"foreignKey:UpdatedBy"`
}
// KnowledgeBaseFeedback represents user feedback on a knowledge base entry
type KnowledgeBaseFeedback struct {
ID uint `json:"id" gorm:"primaryKey"`
KnowledgeBaseID uint `json:"knowledgeBaseId" gorm:"not null"`
UserID uint `json:"userId" gorm:"not null"`
Helpful bool `json:"helpful"` // true if helpful, false if not helpful
Comment string `json:"comment"`
CreatedAt time.Time `json:"createdAt"`
// Relationships
KnowledgeBase KnowledgeBase `json:"knowledgeBase" gorm:"foreignKey:KnowledgeBaseID"`
User User `json:"user" gorm:"foreignKey:UserID"`
}
// BeforeCreate is a GORM hook that validates the knowledge base entry before creation
func (k *KnowledgeBase) BeforeCreate(tx *gorm.DB) error {
if k.Priority < 1 {
k.Priority = 1
}
return nil
}
// BeforeUpdate is a GORM hook that validates the knowledge base entry before update
func (k *KnowledgeBase) BeforeUpdate(tx *gorm.DB) error {
if k.Priority < 1 {
k.Priority = 1
}
return nil
}
// IncrementViewCount increments the view count of a knowledge base entry
func (k *KnowledgeBase) IncrementViewCount(tx *gorm.DB) error {
return tx.Model(k).UpdateColumn("view_count", gorm.Expr("view_count + ?", 1)).Error
}
// MarkHelpful marks a knowledge base entry as helpful
func (k *KnowledgeBase) MarkHelpful(tx *gorm.DB) error {
return tx.Model(k).UpdateColumn("helpful", gorm.Expr("helpful + ?", 1)).Error
}
// MarkNotHelpful marks a knowledge base entry as not helpful
func (k *KnowledgeBase) MarkNotHelpful(tx *gorm.DB) error {
return tx.Model(k).UpdateColumn("not_helpful", gorm.Expr("not_helpful + ?", 1)).Error
}
// GetHelpfulnessPercentage returns the helpfulness percentage
func (k *KnowledgeBase) GetHelpfulnessPercentage() float64 {
total := k.Helpful + k.NotHelpful
if total == 0 {
return 0
}
return float64(k.Helpful) / float64(total) * 100
}

View File

@@ -0,0 +1,143 @@
package models
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestUserModel(t *testing.T) {
// Test user model fields
user := User{
ID: 1,
Username: "testuser",
Email: "test@example.com",
Password: "hashedpassword",
FirstName: "Test",
LastName: "User",
Role: "user",
Active: true,
}
// Verify user fields
assert.Equal(t, uint(1), user.ID)
assert.Equal(t, "testuser", user.Username)
assert.Equal(t, "test@example.com", user.Email)
assert.Equal(t, "hashedpassword", user.Password)
assert.Equal(t, "Test", user.FirstName)
assert.Equal(t, "User", user.LastName)
assert.Equal(t, "user", user.Role)
assert.True(t, user.Active)
}
func TestConversationModel(t *testing.T) {
// Test conversation model fields
conversation := Conversation{
ID: 1,
UserID: 1,
Title: "Test Conversation",
Status: "active",
Department: "support",
Priority: "medium",
Tags: "test,sample",
}
// Verify conversation fields
assert.Equal(t, uint(1), conversation.ID)
assert.Equal(t, uint(1), conversation.UserID)
assert.Equal(t, "Test Conversation", conversation.Title)
assert.Equal(t, "active", conversation.Status)
assert.Equal(t, "support", conversation.Department)
assert.Equal(t, "medium", conversation.Priority)
assert.Equal(t, "test,sample", conversation.Tags)
}
func TestMessageModel(t *testing.T) {
// Test message model fields
message := Message{
ID: 1,
ConversationID: 1,
UserID: 1,
Content: "Test message content",
Type: "text",
Status: "sent",
Sentiment: 0.5,
IsAI: false,
AIModel: "",
}
// Verify message fields
assert.Equal(t, uint(1), message.ID)
assert.Equal(t, uint(1), message.ConversationID)
assert.Equal(t, uint(1), message.UserID)
assert.Equal(t, "Test message content", message.Content)
assert.Equal(t, "text", message.Type)
assert.Equal(t, "sent", message.Status)
assert.Equal(t, 0.5, message.Sentiment)
assert.False(t, message.IsAI)
assert.Equal(t, "", message.AIModel)
}
func TestKnowledgeModel(t *testing.T) {
// Test knowledge model fields
knowledge := KnowledgeBase{
ID: 1,
Question: "Test Question",
Answer: "Test knowledge content",
Category: "general",
Tags: "test,sample",
Priority: 1,
ViewCount: 10,
Helpful: 5,
NotHelpful: 2,
Active: true,
CreatedBy: 1,
UpdatedBy: 1,
}
// Verify knowledge fields
assert.Equal(t, uint(1), knowledge.ID)
assert.Equal(t, "Test Question", knowledge.Question)
assert.Equal(t, "Test knowledge content", knowledge.Answer)
assert.Equal(t, "general", knowledge.Category)
assert.Equal(t, "test,sample", knowledge.Tags)
assert.Equal(t, 1, knowledge.Priority)
assert.Equal(t, 10, knowledge.ViewCount)
assert.Equal(t, 5, knowledge.Helpful)
assert.Equal(t, 2, knowledge.NotHelpful)
assert.True(t, knowledge.Active)
assert.Equal(t, uint(1), knowledge.CreatedBy)
assert.Equal(t, uint(1), knowledge.UpdatedBy)
}
func TestAIModel(t *testing.T) {
// Test AI model fields
aiModel := AIModel{
ID: 1,
Name: "Test AI Model",
Type: "openai",
Endpoint: "https://api.openai.com/v1/chat/completions",
APIKey: "test-api-key",
Model: "gpt-4",
MaxTokens: 4000,
Temperature: 0.7,
TopP: 1.0,
Active: true,
Priority: 1,
Description: "Test AI model description",
}
// Verify AI model fields
assert.Equal(t, uint(1), aiModel.ID)
assert.Equal(t, "Test AI Model", aiModel.Name)
assert.Equal(t, "openai", aiModel.Type)
assert.Equal(t, "https://api.openai.com/v1/chat/completions", aiModel.Endpoint)
assert.Equal(t, "test-api-key", aiModel.APIKey)
assert.Equal(t, "gpt-4", aiModel.Model)
assert.Equal(t, 4000, aiModel.MaxTokens)
assert.Equal(t, 0.7, aiModel.Temperature)
assert.Equal(t, 1.0, aiModel.TopP)
assert.True(t, aiModel.Active)
assert.Equal(t, 1, aiModel.Priority)
assert.Equal(t, "Test AI model description", aiModel.Description)
}

View File

@@ -0,0 +1,261 @@
package models
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// User Request/Response types
type CreateUserRequest struct {
Username string `json:"username" binding:"required,min=3,max=50"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Role string `json:"role"`
}
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
type LoginResponse struct {
Token string `json:"token"`
User SafeUser `json:"user"`
}
type UpdateUserRequest struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email" binding:"omitempty,email"`
Active *bool `json:"active"`
Role string `json:"role"`
}
type ChangePasswordRequest struct {
CurrentPassword string `json:"currentPassword" binding:"required"`
NewPassword string `json:"newPassword" binding:"required,min=6"`
}
// Conversation Request/Response types
type CreateConversationRequest struct {
Title string `json:"title" binding:"required,min=1,max=100"`
Department string `json:"department" binding:"required"`
Priority string `json:"priority" binding:"oneof=low medium high urgent"`
Tags string `json:"tags"`
}
type UpdateConversationRequest struct {
Title string `json:"title"`
Status string `json:"status" binding:"oneof=active closed escalated"`
Department string `json:"department"`
Priority string `json:"priority" binding:"oneof=low medium high urgent"`
Tags string `json:"tags"`
AgentID *uint `json:"agentId"`
}
type CreateMessageRequest struct {
ConversationID uint `json:"conversationId" binding:"required"`
Content string `json:"content" binding:"required,min=1,max=5000"`
Type string `json:"type" binding:"oneof=text image file system"`
}
type UpdateMessageRequest struct {
Content string `json:"content" binding:"omitempty,min=1,max=5000"`
Status string `json:"status" binding:"oneof=sent delivered read"`
}
type ConversationListResponse struct {
Conversations []Conversation `json:"conversations"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type MessageListResponse struct {
Messages []Message `json:"messages"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type ConversationStats struct {
TotalMessages int64 `json:"totalMessages"`
AverageSentiment float64 `json:"averageSentiment"`
ResponseTime int64 `json:"responseTime"` // in seconds
FirstMessageAt time.Time `json:"firstMessageAt"`
LastMessageAt time.Time `json:"lastMessageAt"`
}
// Knowledge Base Request/Response types
type CreateKnowledgeBaseRequest struct {
Question string `json:"question" binding:"required,min=1,max=500"`
Answer string `json:"answer" binding:"required,min=1,max=5000"`
Category string `json:"category" binding:"required,min=1,max=100"`
Tags string `json:"tags"`
Priority int `json:"priority" binding:"min=1,max=10"`
}
type UpdateKnowledgeBaseRequest struct {
Question string `json:"question" binding:"omitempty,min=1,max=500"`
Answer string `json:"answer" binding:"omitempty,min=1,max=5000"`
Category string `json:"category" binding:"omitempty,min=1,max=100"`
Tags string `json:"tags"`
Priority int `json:"priority" binding:"omitempty,min=1,max=10"`
Active *bool `json:"active"`
}
type CreateKnowledgeBaseFeedbackRequest struct {
KnowledgeBaseID uint `json:"knowledgeBaseId" binding:"required"`
Helpful bool `json:"helpful"`
Comment string `json:"comment" binding:"max=500"`
}
type KnowledgeBaseSearchRequest struct {
Query string `json:"query" binding:"required,min=1,max=100"`
Category string `json:"category"`
Tags string `json:"tags"`
Page int `json:"page" binding:"min=1"`
PageSize int `json:"pageSize" binding:"min=1,max=100"`
}
type KnowledgeBaseListResponse struct {
Entries []KnowledgeBase `json:"entries"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type KnowledgeBaseSearchResponse struct {
Results []KnowledgeBaseSearchResult `json:"results"`
Total int64 `json:"total"`
Query string `json:"query"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type KnowledgeBaseSearchResult struct {
KnowledgeBase
RelevanceScore float64 `json:"relevanceScore"`
MatchedFields []string `json:"matchedFields"`
}
type KnowledgeBaseStats struct {
TotalEntries int64 `json:"totalEntries"`
TotalViews int64 `json:"totalViews"`
AverageHelpful float64 `json:"averageHelpful"` // Average helpful rating
TopCategories []CategoryStat `json:"topCategories"`
TopTags []TagStat `json:"topTags"`
}
type CategoryStat struct {
Category string `json:"category"`
Count int64 `json:"count"`
}
type TagStat struct {
Tag string `json:"tag"`
Count int64 `json:"count"`
}
// AI Model Request/Response types
type CreateAIModelRequest struct {
Name string `json:"name" binding:"required,min=1,max=100"`
Type string `json:"type" binding:"required,oneof=openai local custom"`
Endpoint string `json:"endpoint"`
APIKey string `json:"apiKey"`
Model string `json:"model" binding:"required,min=1,max=100"`
MaxTokens int `json:"maxTokens" binding:"min=1,max=100000"`
Temperature float64 `json:"temperature" binding:"min=0,max=2"`
TopP float64 `json:"topP" binding:"min=0,max=1"`
Priority int `json:"priority" binding:"min=1,max=10"`
Description string `json:"description" binding:"max=500"`
}
type UpdateAIModelRequest struct {
Name string `json:"name" binding:"omitempty,min=1,max=100"`
Type string `json:"type" binding:"omitempty,oneof=openai local custom"`
Endpoint string `json:"endpoint"`
APIKey string `json:"apiKey"`
Model string `json:"model" binding:"omitempty,min=1,max=100"`
MaxTokens int `json:"maxTokens" binding:"omitempty,min=1,max=100000"`
Temperature float64 `json:"temperature" binding:"omitempty,min=0,max=2"`
TopP float64 `json:"topP" binding:"omitempty,min=0,max=1"`
Active *bool `json:"active"`
Priority int `json:"priority" binding:"omitempty,min=1,max=10"`
Description string `json:"description" binding:"omitempty,max=500"`
}
type AIModelListResponse struct {
Models []AIModel `json:"models"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type AIInteractionListResponse struct {
Interactions []AIInteraction `json:"interactions"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type AIFallbackListResponse struct {
Fallbacks []AIFallback `json:"fallbacks"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type AIStats struct {
TotalInteractions int64 `json:"totalInteractions"`
TotalFallbacks int64 `json:"totalFallbacks"`
AverageResponseTime float64 `json:"averageResponseTime"` // in milliseconds
TotalTokensUsed int64 `json:"totalTokensUsed"`
TotalCost float64 `json:"totalCost"`
ModelStats []AIModelStats `json:"modelStats"`
SuccessRate float64 `json:"successRate"`
}
type AIModelStats struct {
AIModel AIModel `json:"aiModel"`
InteractionsCount int64 `json:"interactionsCount"`
FallbacksCount int64 `json:"fallbacksCount"`
AverageResponseTime float64 `json:"averageResponseTime"`
SuccessRate float64 `json:"successRate"`
TokensUsed int64 `json:"tokensUsed"`
Cost float64 `json:"cost"`
}
// Common Response types
type ErrorResponse struct {
Error string `json:"error"`
Message string `json:"message"`
Status int `json:"status"`
}
type SuccessResponse struct {
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
Status int `json:"status"`
}
// Response helpers
func NewErrorResponse(c *gin.Context, message string, statusCode int) {
c.JSON(statusCode, ErrorResponse{
Error: http.StatusText(statusCode),
Message: message,
Status: statusCode,
})
}
func NewSuccessResponse(c *gin.Context, message string, data interface{}, statusCode int) {
c.JSON(statusCode, SuccessResponse{
Message: message,
Data: data,
Status: statusCode,
})
}

View File

@@ -0,0 +1,69 @@
package models
import (
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// User represents a user in the system
type User struct {
ID uint `json:"id" gorm:"primaryKey"`
Username string `json:"username" gorm:"uniqueIndex;not null"`
Email string `json:"email" gorm:"uniqueIndex;not null"`
Password string `json:"-" gorm:"not null"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Role string `json:"role" gorm:"default:'user'"` // 'user', 'agent', 'admin'
Active bool `json:"active" gorm:"default:true"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}
// BeforeSave is a GORM hook that hashes the password before saving
func (u *User) BeforeSave(tx *gorm.DB) error {
if u.Password != "" {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
u.Password = string(hashedPassword)
}
return nil
}
// ComparePassword compares a plaintext password with the user's hashed password
func (u *User) ComparePassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
return err == nil
}
// ToSafeUser returns a user object without sensitive information
func (u *User) ToSafeUser() SafeUser {
return SafeUser{
ID: u.ID,
Username: u.Username,
Email: u.Email,
FirstName: u.FirstName,
LastName: u.LastName,
Role: u.Role,
Active: u.Active,
CreatedAt: u.CreatedAt,
UpdatedAt: u.UpdatedAt,
}
}
// SafeUser represents user information that can be safely exposed to clients
type SafeUser struct {
ID uint `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Role string `json:"role"`
Active bool `json:"active"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}