Files
warmbly/internal/app/audit/service.go
T
Máté Mészáros (Laptop) 5ac159d2f8 Realtime, api keys & more
2026-01-26 16:04:42 +01:00

119 lines
4.2 KiB
Go

package audit
import (
"context"
"time"
"github.com/google/uuid"
"github.com/warmbly/warmbly/internal/errx"
"github.com/warmbly/warmbly/internal/models"
"github.com/warmbly/warmbly/internal/repository"
)
type AuditService interface {
// Log a new audit entry
Log(ctx context.Context, log *models.CreateAuditLog) error
// Log from gin context (extracts IP, user agent)
LogAction(ctx context.Context, userID uuid.UUID, action models.AuditAction, entityType models.AuditEntityType, entityID *uuid.UUID, ipAddress, userAgent string, changes, metadata map[string]string)
// Query logs
GetUserLogs(ctx context.Context, userID uuid.UUID, params *models.AuditLogSearch) (*models.AuditLogsResult, *errx.Error)
GetEntityLogs(ctx context.Context, entityType models.AuditEntityType, entityID uuid.UUID, limit int, cursor string) (*models.AuditLogsResult, *errx.Error)
}
type auditService struct {
repo repository.AuditRepository
}
func NewService(repo repository.AuditRepository) AuditService {
return &auditService{
repo: repo,
}
}
func (s *auditService) Log(ctx context.Context, log *models.CreateAuditLog) error {
return s.repo.Log(ctx, log)
}
func (s *auditService) LogAction(ctx context.Context, userID uuid.UUID, action models.AuditAction, entityType models.AuditEntityType, entityID *uuid.UUID, ipAddress, userAgent string, changes, metadata map[string]string) {
// Fire and forget - don't block the request
go func() {
log := &models.CreateAuditLog{
UserID: userID,
Action: action,
EntityType: entityType,
EntityID: entityID,
IPAddress: ipAddress,
UserAgent: userAgent,
Changes: changes,
Metadata: metadata,
}
_ = s.repo.Log(context.Background(), log)
}()
}
func (s *auditService) GetUserLogs(ctx context.Context, userID uuid.UUID, params *models.AuditLogSearch) (*models.AuditLogsResult, *errx.Error) {
if params == nil {
params = &models.AuditLogSearch{}
}
params.UserID = &userID
if params.Limit <= 0 || params.Limit > 100 {
params.Limit = 50
}
// Default to today if no date range
if params.Since == nil {
today := time.Now().Truncate(24 * time.Hour)
params.Since = &today
}
result, err := s.repo.Search(ctx, params)
if err != nil {
return nil, errx.InternalError()
}
return result, nil
}
func (s *auditService) GetEntityLogs(ctx context.Context, entityType models.AuditEntityType, entityID uuid.UUID, limit int, cursor string) (*models.AuditLogsResult, *errx.Error) {
if limit <= 0 || limit > 100 {
limit = 50
}
result, err := s.repo.GetByEntity(ctx, string(entityType), entityID, limit, cursor)
if err != nil {
return nil, errx.InternalError()
}
return result, nil
}
// Helper functions for common audit log scenarios
// LogCreate logs a create action
func LogCreate(s AuditService, ctx context.Context, userID uuid.UUID, entityType models.AuditEntityType, entityID uuid.UUID, ipAddress, userAgent string, metadata map[string]string) {
s.LogAction(ctx, userID, models.AuditActionCreate, entityType, &entityID, ipAddress, userAgent, nil, metadata)
}
// LogUpdate logs an update action with changes
func LogUpdate(s AuditService, ctx context.Context, userID uuid.UUID, entityType models.AuditEntityType, entityID uuid.UUID, ipAddress, userAgent string, changes map[string]string) {
s.LogAction(ctx, userID, models.AuditActionUpdate, entityType, &entityID, ipAddress, userAgent, changes, nil)
}
// LogDelete logs a delete action
func LogDelete(s AuditService, ctx context.Context, userID uuid.UUID, entityType models.AuditEntityType, entityID uuid.UUID, ipAddress, userAgent string) {
s.LogAction(ctx, userID, models.AuditActionDelete, entityType, &entityID, ipAddress, userAgent, nil, nil)
}
// LogLogin logs a login action
func LogLogin(s AuditService, ctx context.Context, userID uuid.UUID, ipAddress, userAgent string, metadata map[string]string) {
s.LogAction(ctx, userID, models.AuditActionLogin, models.AuditEntitySession, nil, ipAddress, userAgent, nil, metadata)
}
// LogLogout logs a logout action
func LogLogout(s AuditService, ctx context.Context, userID uuid.UUID, ipAddress, userAgent string) {
s.LogAction(ctx, userID, models.AuditActionLogout, models.AuditEntitySession, nil, ipAddress, userAgent, nil, nil)
}