mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
dev day #65
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user