241e3b5f2d
- Introduced a new `auditlog` package to handle audit logging for user actions, including creation, updates, deletions, and authentication events. - Enhanced existing services (customer, user) to log relevant actions using the new audit logger, capturing details such as actor ID, action type, target type, and success status. - Added middleware to enrich request context with metadata for audit logging, ensuring comprehensive tracking of user interactions. - Integrated Elasticsearch for persistent storage of audit logs, with fallback to file-only logging if Elasticsearch is unavailable. - Updated API documentation to include new audit log endpoints for administrative access. This update significantly improves the system's ability to track and audit user actions, enhancing security and accountability within the application.
89 lines
2.0 KiB
Go
89 lines
2.0 KiB
Go
package auditlog
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
|
|
"tm/pkg/audit"
|
|
"tm/pkg/logger"
|
|
"tm/pkg/response"
|
|
)
|
|
|
|
// Service defines audit log query operations.
|
|
type Service interface {
|
|
Search(ctx context.Context, form SearchForm, pagination *response.Pagination) (*ListResponse, error)
|
|
}
|
|
|
|
type service struct {
|
|
repository Repository
|
|
logger logger.Logger
|
|
}
|
|
|
|
// NewService creates an audit log service.
|
|
func NewService(repository Repository, log logger.Logger) Service {
|
|
return &service{
|
|
repository: repository,
|
|
logger: log,
|
|
}
|
|
}
|
|
|
|
func (s *service) Search(ctx context.Context, form SearchForm, pagination *response.Pagination) (*ListResponse, error) {
|
|
filter := audit.SearchFilter{
|
|
ActorID: form.ActorID,
|
|
ActorType: form.ActorType,
|
|
Action: form.Action,
|
|
TargetType: form.TargetType,
|
|
TargetID: form.TargetID,
|
|
From: form.From,
|
|
To: form.To,
|
|
Limit: pagination.Limit,
|
|
Offset: pagination.Offset,
|
|
}
|
|
|
|
result, err := s.repository.Search(ctx, filter)
|
|
if err != nil {
|
|
s.logger.Error("Failed to search audit logs", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
entries := make([]*Entry, 0, len(result.Items))
|
|
for _, item := range result.Items {
|
|
entries = append(entries, &Entry{
|
|
ActorID: item.ActorID,
|
|
ActorType: item.ActorType,
|
|
Action: item.Action,
|
|
TargetType: item.TargetType,
|
|
TargetID: item.TargetID,
|
|
At: item.At,
|
|
IP: item.IP,
|
|
RequestID: item.RequestID,
|
|
UserAgent: item.UserAgent,
|
|
Success: item.Success,
|
|
Metadata: item.Metadata,
|
|
})
|
|
}
|
|
|
|
total := result.TotalCount
|
|
pages := 0
|
|
if pagination.Limit > 0 {
|
|
pages = int(math.Ceil(float64(total) / float64(pagination.Limit)))
|
|
}
|
|
page := 1
|
|
if pagination.Limit > 0 {
|
|
page = (pagination.Offset / pagination.Limit) + 1
|
|
}
|
|
|
|
return &ListResponse{
|
|
Logs: entries,
|
|
Meta: &ListMeta{
|
|
Total: total,
|
|
Limit: pagination.Limit,
|
|
Offset: pagination.Offset,
|
|
Page: page,
|
|
Pages: pages,
|
|
},
|
|
}, nil
|
|
}
|