mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
292 lines
8.3 KiB
Go
292 lines
8.3 KiB
Go
package controllers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"fotbal-club/internal/models"
|
|
"fotbal-club/pkg/logger"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// AuditLogController handles audit log operations
|
|
type AuditLogController struct {
|
|
DB *gorm.DB
|
|
}
|
|
|
|
// NewAuditLogController creates a new AuditLogController instance
|
|
func NewAuditLogController(db *gorm.DB) *AuditLogController {
|
|
return &AuditLogController{DB: db}
|
|
}
|
|
|
|
// AuditAction represents common audit actions
|
|
const (
|
|
AuditActionCreate = "CREATE"
|
|
AuditActionUpdate = "UPDATE"
|
|
AuditActionDelete = "DELETE"
|
|
AuditActionLogin = "LOGIN"
|
|
AuditActionLogout = "LOGOUT"
|
|
AuditActionView = "VIEW"
|
|
AuditActionExport = "EXPORT"
|
|
)
|
|
|
|
// LogEntry creates an audit log entry
|
|
func (alc *AuditLogController) LogEntry(c *gin.Context, action, entityType string, entityID *uint, description string, changes interface{}) error {
|
|
var userID *uint
|
|
if user, exists := c.Get("user"); exists {
|
|
if u, ok := user.(*models.User); ok && u != nil {
|
|
userID = &u.ID
|
|
}
|
|
}
|
|
|
|
// Serialize changes to JSON
|
|
var changesJSON string
|
|
if changes != nil {
|
|
if b, err := json.Marshal(changes); err == nil {
|
|
changesJSON = string(b)
|
|
}
|
|
}
|
|
|
|
log := models.AuditLog{
|
|
UserID: userID,
|
|
Action: action,
|
|
EntityType: entityType,
|
|
EntityID: entityID,
|
|
Description: description,
|
|
IPAddress: c.ClientIP(),
|
|
UserAgent: c.Request.UserAgent(),
|
|
Changes: changesJSON,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
if err := alc.DB.Create(&log).Error; err != nil {
|
|
logger.Error("Failed to create audit log: %v", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LogCreate logs a CREATE action
|
|
func (alc *AuditLogController) LogCreate(c *gin.Context, entityType string, entityID uint, description string) error {
|
|
id := entityID
|
|
return alc.LogEntry(c, AuditActionCreate, entityType, &id, description, nil)
|
|
}
|
|
|
|
// LogUpdate logs an UPDATE action with before/after changes
|
|
func (alc *AuditLogController) LogUpdate(c *gin.Context, entityType string, entityID uint, description string, before, after interface{}) error {
|
|
id := entityID
|
|
changes := map[string]interface{}{
|
|
"before": before,
|
|
"after": after,
|
|
}
|
|
return alc.LogEntry(c, AuditActionUpdate, entityType, &id, description, changes)
|
|
}
|
|
|
|
// LogDelete logs a DELETE action
|
|
func (alc *AuditLogController) LogDelete(c *gin.Context, entityType string, entityID uint, description string) error {
|
|
id := entityID
|
|
return alc.LogEntry(c, AuditActionDelete, entityType, &id, description, nil)
|
|
}
|
|
|
|
// LogLogin logs a login action
|
|
func (alc *AuditLogController) LogLogin(c *gin.Context, userID uint, success bool) error {
|
|
id := userID
|
|
description := "User logged in successfully"
|
|
if !success {
|
|
description = "Failed login attempt"
|
|
}
|
|
return alc.LogEntry(c, AuditActionLogin, "User", &id, description, nil)
|
|
}
|
|
|
|
// LogLogout logs a logout action
|
|
func (alc *AuditLogController) LogLogout(c *gin.Context, userID uint) error {
|
|
id := userID
|
|
return alc.LogEntry(c, AuditActionLogout, "User", &id, "User logged out", nil)
|
|
}
|
|
|
|
// GetAuditLogs retrieves audit logs with filtering and pagination (admin only)
|
|
func (alc *AuditLogController) GetAuditLogs(c *gin.Context) {
|
|
query := alc.DB.Model(&models.AuditLog{}).Preload("User")
|
|
|
|
// Apply filters
|
|
query = QueryParser.BuildQueryChain(c, query).
|
|
WithSearch("action", "entity_type", "description").
|
|
WithDateRange("created_at").
|
|
Build()
|
|
|
|
// Filter by action
|
|
if action := c.Query("action"); action != "" {
|
|
query = query.Where("action = ?", action)
|
|
}
|
|
|
|
// Filter by entity type
|
|
if entityType := c.Query("entity_type"); entityType != "" {
|
|
query = query.Where("entity_type = ?", entityType)
|
|
}
|
|
|
|
// Filter by user ID
|
|
if userID := c.Query("user_id"); userID != "" {
|
|
query = query.Where("user_id = ?", userID)
|
|
}
|
|
|
|
// Apply sorting (default: newest first)
|
|
query = QueryParser.ApplySortFromContext(c, query, "created_at", "desc")
|
|
|
|
// Paginate
|
|
var logs []models.AuditLog
|
|
meta, err := Paginator.Paginate(c, query, &logs)
|
|
if err != nil {
|
|
Respond.InternalError(c, "Failed to retrieve audit logs")
|
|
return
|
|
}
|
|
|
|
Respond.SuccessWithMeta(c, logs, meta, "Audit logs retrieved successfully")
|
|
}
|
|
|
|
// GetAuditLogByID retrieves a single audit log by ID (admin only)
|
|
func (alc *AuditLogController) GetAuditLogByID(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var log models.AuditLog
|
|
if err := alc.DB.Preload("User").First(&log, id).Error; err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
Respond.NotFound(c, "Audit log not found")
|
|
return
|
|
}
|
|
Respond.InternalError(c, "Failed to retrieve audit log")
|
|
return
|
|
}
|
|
|
|
Respond.Success(c, log, "Audit log retrieved successfully")
|
|
}
|
|
|
|
// GetEntityAuditHistory retrieves audit history for a specific entity
|
|
func (alc *AuditLogController) GetEntityAuditHistory(c *gin.Context) {
|
|
entityType := c.Param("entity_type")
|
|
entityID := c.Param("entity_id")
|
|
|
|
query := alc.DB.Model(&models.AuditLog{}).
|
|
Where("entity_type = ? AND entity_id = ?", entityType, entityID).
|
|
Preload("User").
|
|
Order("created_at DESC")
|
|
|
|
var logs []models.AuditLog
|
|
meta, err := Paginator.Paginate(c, query, &logs)
|
|
if err != nil {
|
|
Respond.InternalError(c, "Failed to retrieve audit history")
|
|
return
|
|
}
|
|
|
|
Respond.SuccessWithMeta(c, logs, meta, fmt.Sprintf("Audit history for %s #%s retrieved successfully", entityType, entityID))
|
|
}
|
|
|
|
// GetUserActivityLog retrieves activity log for a specific user
|
|
func (alc *AuditLogController) GetUserActivityLog(c *gin.Context) {
|
|
userID := c.Param("user_id")
|
|
|
|
query := alc.DB.Model(&models.AuditLog{}).
|
|
Where("user_id = ?", userID).
|
|
Preload("User").
|
|
Order("created_at DESC")
|
|
|
|
var logs []models.AuditLog
|
|
meta, err := Paginator.Paginate(c, query, &logs)
|
|
if err != nil {
|
|
Respond.InternalError(c, "Failed to retrieve user activity")
|
|
return
|
|
}
|
|
|
|
Respond.SuccessWithMeta(c, logs, meta, "User activity log retrieved successfully")
|
|
}
|
|
|
|
// GetAuditStats returns audit statistics (admin only)
|
|
func (alc *AuditLogController) GetAuditStats(c *gin.Context) {
|
|
var stats struct {
|
|
TotalLogs int64 `json:"total_logs"`
|
|
ActionCounts map[string]int64 `json:"action_counts"`
|
|
EntityCounts map[string]int64 `json:"entity_counts"`
|
|
RecentActions []models.AuditLog `json:"recent_actions"`
|
|
}
|
|
|
|
// Total logs
|
|
alc.DB.Model(&models.AuditLog{}).Count(&stats.TotalLogs)
|
|
|
|
// Action counts
|
|
stats.ActionCounts = make(map[string]int64)
|
|
var actionCounts []struct {
|
|
Action string `json:"action"`
|
|
Count int64 `json:"count"`
|
|
}
|
|
alc.DB.Model(&models.AuditLog{}).
|
|
Select("action, COUNT(*) as count").
|
|
Group("action").
|
|
Scan(&actionCounts)
|
|
for _, ac := range actionCounts {
|
|
stats.ActionCounts[ac.Action] = ac.Count
|
|
}
|
|
|
|
// Entity counts
|
|
stats.EntityCounts = make(map[string]int64)
|
|
var entityCounts []struct {
|
|
EntityType string `json:"entity_type"`
|
|
Count int64 `json:"count"`
|
|
}
|
|
alc.DB.Model(&models.AuditLog{}).
|
|
Select("entity_type, COUNT(*) as count").
|
|
Where("entity_type IS NOT NULL AND entity_type != ''").
|
|
Group("entity_type").
|
|
Scan(&entityCounts)
|
|
for _, ec := range entityCounts {
|
|
stats.EntityCounts[ec.EntityType] = ec.Count
|
|
}
|
|
|
|
// Recent actions
|
|
alc.DB.Model(&models.AuditLog{}).
|
|
Preload("User").
|
|
Order("created_at DESC").
|
|
Limit(10).
|
|
Find(&stats.RecentActions)
|
|
|
|
c.JSON(http.StatusOK, stats)
|
|
}
|
|
|
|
// CleanupOldLogs deletes audit logs older than specified days (admin only)
|
|
func (alc *AuditLogController) CleanupOldLogs(c *gin.Context) {
|
|
var req struct {
|
|
DaysOld int `json:"days_old" binding:"required,min=30"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
Respond.BadRequest(c, "Invalid request: days_old must be at least 30")
|
|
return
|
|
}
|
|
|
|
cutoffDate := time.Now().AddDate(0, 0, -req.DaysOld)
|
|
|
|
result := alc.DB.Where("created_at < ?", cutoffDate).Delete(&models.AuditLog{})
|
|
if result.Error != nil {
|
|
logger.Error("Failed to cleanup old audit logs: %v", result.Error)
|
|
Respond.InternalError(c, "Failed to cleanup old audit logs")
|
|
return
|
|
}
|
|
|
|
logger.Info("Cleaned up %d audit logs older than %d days", result.RowsAffected, req.DaysOld)
|
|
Respond.Success(c, gin.H{
|
|
"deleted_count": result.RowsAffected,
|
|
"cutoff_date": cutoffDate,
|
|
}, fmt.Sprintf("Successfully deleted %d audit logs older than %d days", result.RowsAffected, req.DaysOld))
|
|
}
|
|
|
|
// Global AuditLogController instance (needs to be initialized with DB)
|
|
var AuditLogger *AuditLogController
|
|
|
|
// InitAuditLogger initializes the global audit logger
|
|
func InitAuditLogger(db *gorm.DB) {
|
|
AuditLogger = NewAuditLogController(db)
|
|
}
|