272 lines
8.6 KiB
Go
272 lines
8.6 KiB
Go
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// AuditLog represents an audit log entry in the system
|
|
type AuditLog struct {
|
|
BaseModel
|
|
UserID string `json:"userId" gorm:"type:varchar(36);index"`
|
|
Action string `json:"action" gorm:"not null;type:varchar(100);index"`
|
|
Resource string `json:"resource" gorm:"not null;type:varchar(100);index"`
|
|
ResourceID string `json:"resourceId" gorm:"type:varchar(36);index"`
|
|
Details map[string]interface{} `json:"details" gorm:"type:text"`
|
|
IPAddress string `json:"ipAddress" gorm:"type:varchar(45)"`
|
|
UserAgent string `json:"userAgent" gorm:"type:text"`
|
|
Success bool `json:"success" gorm:"default:true;index"`
|
|
ErrorMsg string `json:"errorMsg,omitempty" gorm:"type:text"`
|
|
Duration int64 `json:"duration,omitempty"` // Duration in milliseconds
|
|
SessionID string `json:"sessionId,omitempty" gorm:"type:varchar(255)"`
|
|
RequestID string `json:"requestId,omitempty" gorm:"type:varchar(255)"`
|
|
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
|
}
|
|
|
|
// AuditLogCreateRequest represents the request to create a new audit log
|
|
type AuditLogCreateRequest struct {
|
|
UserID string `json:"userId"`
|
|
Action string `json:"action" validate:"required,max=100"`
|
|
Resource string `json:"resource" validate:"required,max=100"`
|
|
ResourceID string `json:"resourceId"`
|
|
Details map[string]interface{} `json:"details"`
|
|
IPAddress string `json:"ipAddress" validate:"max=45"`
|
|
UserAgent string `json:"userAgent"`
|
|
Success bool `json:"success"`
|
|
ErrorMsg string `json:"errorMsg"`
|
|
Duration int64 `json:"duration"`
|
|
SessionID string `json:"sessionId"`
|
|
RequestID string `json:"requestId"`
|
|
}
|
|
|
|
// AuditLogInfo represents public audit log information
|
|
type AuditLogInfo struct {
|
|
ID string `json:"id"`
|
|
UserID string `json:"userId"`
|
|
UserEmail string `json:"userEmail,omitempty"`
|
|
UserName string `json:"userName,omitempty"`
|
|
Action string `json:"action"`
|
|
Resource string `json:"resource"`
|
|
ResourceID string `json:"resourceId"`
|
|
Details map[string]interface{} `json:"details"`
|
|
IPAddress string `json:"ipAddress"`
|
|
UserAgent string `json:"userAgent"`
|
|
Success bool `json:"success"`
|
|
ErrorMsg string `json:"errorMsg,omitempty"`
|
|
Duration int64 `json:"duration,omitempty"`
|
|
SessionID string `json:"sessionId,omitempty"`
|
|
RequestID string `json:"requestId,omitempty"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
// BeforeCreate is called before creating an audit log
|
|
func (al *AuditLog) BeforeCreate(tx *gorm.DB) error {
|
|
al.BaseModel.BeforeCreate()
|
|
|
|
// Normalize fields
|
|
al.Action = strings.ToLower(strings.TrimSpace(al.Action))
|
|
al.Resource = strings.ToLower(strings.TrimSpace(al.Resource))
|
|
al.IPAddress = strings.TrimSpace(al.IPAddress)
|
|
al.UserAgent = strings.TrimSpace(al.UserAgent)
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetDetails sets the details field from a map
|
|
func (al *AuditLog) SetDetails(details map[string]interface{}) error {
|
|
al.Details = details
|
|
return nil
|
|
}
|
|
|
|
// GetDetails returns the details as a map
|
|
func (al *AuditLog) GetDetails() map[string]interface{} {
|
|
if al.Details == nil {
|
|
return make(map[string]interface{})
|
|
}
|
|
return al.Details
|
|
}
|
|
|
|
// SetDetailsFromJSON sets the details field from a JSON string
|
|
func (al *AuditLog) SetDetailsFromJSON(jsonStr string) error {
|
|
if jsonStr == "" {
|
|
al.Details = make(map[string]interface{})
|
|
return nil
|
|
}
|
|
|
|
var details map[string]interface{}
|
|
if err := json.Unmarshal([]byte(jsonStr), &details); err != nil {
|
|
return err
|
|
}
|
|
|
|
al.Details = details
|
|
return nil
|
|
}
|
|
|
|
// GetDetailsAsJSON returns the details as a JSON string
|
|
func (al *AuditLog) GetDetailsAsJSON() (string, error) {
|
|
if al.Details == nil || len(al.Details) == 0 {
|
|
return "{}", nil
|
|
}
|
|
|
|
bytes, err := json.Marshal(al.Details)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(bytes), nil
|
|
}
|
|
|
|
// ToAuditLogInfo converts AuditLog to AuditLogInfo (public information)
|
|
func (al *AuditLog) ToAuditLogInfo() AuditLogInfo {
|
|
info := AuditLogInfo{
|
|
ID: al.ID,
|
|
UserID: al.UserID,
|
|
Action: al.Action,
|
|
Resource: al.Resource,
|
|
ResourceID: al.ResourceID,
|
|
Details: al.GetDetails(),
|
|
IPAddress: al.IPAddress,
|
|
UserAgent: al.UserAgent,
|
|
Success: al.Success,
|
|
ErrorMsg: al.ErrorMsg,
|
|
Duration: al.Duration,
|
|
SessionID: al.SessionID,
|
|
RequestID: al.RequestID,
|
|
CreatedAt: al.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
|
}
|
|
|
|
// Include user information if available
|
|
if al.User != nil {
|
|
info.UserEmail = al.User.Email
|
|
info.UserName = al.User.FullName
|
|
}
|
|
|
|
return info
|
|
}
|
|
|
|
// AddDetail adds a single detail to the details map
|
|
func (al *AuditLog) AddDetail(key string, value interface{}) {
|
|
if al.Details == nil {
|
|
al.Details = make(map[string]interface{})
|
|
}
|
|
al.Details[key] = value
|
|
}
|
|
|
|
// GetDetail gets a single detail from the details map
|
|
func (al *AuditLog) GetDetail(key string) (interface{}, bool) {
|
|
if al.Details == nil {
|
|
return nil, false
|
|
}
|
|
value, exists := al.Details[key]
|
|
return value, exists
|
|
}
|
|
|
|
// Common audit log actions
|
|
const (
|
|
AuditActionCreate = "create"
|
|
AuditActionRead = "read"
|
|
AuditActionUpdate = "update"
|
|
AuditActionDelete = "delete"
|
|
AuditActionLogin = "login"
|
|
AuditActionLogout = "logout"
|
|
AuditActionAccess = "access"
|
|
AuditActionExport = "export"
|
|
AuditActionImport = "import"
|
|
AuditActionConfig = "config"
|
|
)
|
|
|
|
// Common audit log resources
|
|
const (
|
|
AuditResourceUser = "user"
|
|
AuditResourceRole = "role"
|
|
AuditResourcePermission = "permission"
|
|
AuditResourceSystemConfig = "system_config"
|
|
AuditResourceAuth = "auth"
|
|
AuditResourceAPI = "api"
|
|
AuditResourceFile = "file"
|
|
AuditResourceDatabase = "database"
|
|
AuditResourceSystem = "system"
|
|
)
|
|
|
|
// CreateAuditLog creates a new audit log entry
|
|
func CreateAuditLog(userID, action, resource, resourceID string, success bool) *AuditLog {
|
|
auditLog := &AuditLog{
|
|
UserID: userID,
|
|
Action: action,
|
|
Resource: resource,
|
|
ResourceID: resourceID,
|
|
Success: success,
|
|
Details: make(map[string]interface{}),
|
|
}
|
|
auditLog.Init()
|
|
return auditLog
|
|
}
|
|
|
|
// CreateAuditLogWithDetails creates a new audit log entry with details
|
|
func CreateAuditLogWithDetails(userID, action, resource, resourceID string, success bool, details map[string]interface{}) *AuditLog {
|
|
auditLog := CreateAuditLog(userID, action, resource, resourceID, success)
|
|
auditLog.Details = details
|
|
return auditLog
|
|
}
|
|
|
|
// CreateAuditLogWithError creates a new audit log entry for an error
|
|
func CreateAuditLogWithError(userID, action, resource, resourceID, errorMsg string) *AuditLog {
|
|
auditLog := CreateAuditLog(userID, action, resource, resourceID, false)
|
|
auditLog.ErrorMsg = errorMsg
|
|
return auditLog
|
|
}
|
|
|
|
// SetRequestInfo sets request-related information
|
|
func (al *AuditLog) SetRequestInfo(ipAddress, userAgent, sessionID, requestID string) {
|
|
al.IPAddress = ipAddress
|
|
al.UserAgent = userAgent
|
|
al.SessionID = sessionID
|
|
al.RequestID = requestID
|
|
}
|
|
|
|
// SetDuration sets the operation duration
|
|
func (al *AuditLog) SetDuration(start time.Time) {
|
|
al.Duration = time.Since(start).Milliseconds()
|
|
}
|
|
|
|
// IsSuccess returns whether the audit log represents a successful operation
|
|
func (al *AuditLog) IsSuccess() bool {
|
|
return al.Success
|
|
}
|
|
|
|
// IsFailure returns whether the audit log represents a failed operation
|
|
func (al *AuditLog) IsFailure() bool {
|
|
return !al.Success
|
|
}
|
|
|
|
// GetActionDescription returns a human-readable description of the action
|
|
func (al *AuditLog) GetActionDescription() string {
|
|
switch al.Action {
|
|
case AuditActionCreate:
|
|
return "Created " + al.Resource
|
|
case AuditActionRead:
|
|
return "Viewed " + al.Resource
|
|
case AuditActionUpdate:
|
|
return "Updated " + al.Resource
|
|
case AuditActionDelete:
|
|
return "Deleted " + al.Resource
|
|
case AuditActionLogin:
|
|
return "Logged in"
|
|
case AuditActionLogout:
|
|
return "Logged out"
|
|
case AuditActionAccess:
|
|
return "Accessed " + al.Resource
|
|
case AuditActionExport:
|
|
return "Exported " + al.Resource
|
|
case AuditActionImport:
|
|
return "Imported " + al.Resource
|
|
case AuditActionConfig:
|
|
return "Configured " + al.Resource
|
|
default:
|
|
return strings.Title(al.Action) + " " + al.Resource
|
|
}
|
|
}
|