feat: activity log backend foundation
This commit is contained in:
1 parent
8907bb49fe
commit
50b1649965
74 files changed
+5949
-239
No files matched your search
@@ -0,0 +1,144 @@
|
||||
package useCases
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/audit"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
activityRangeToday = "today"
|
||||
activityRange7Days = "7d"
|
||||
activityRange30Days = "30d"
|
||||
activityRangeCustom = "custom"
|
||||
activityDateLayout = "2006-01-02"
|
||||
activityDefaultSize = 25
|
||||
outcomeAll = "all"
|
||||
securityOnly = "true"
|
||||
)
|
||||
|
||||
var activityPageSizes = []int{25, 50, 100}
|
||||
|
||||
type ActivityQuery struct {
|
||||
Q string
|
||||
Actor string
|
||||
Category string
|
||||
Outcome string
|
||||
Security string
|
||||
Range string
|
||||
From string
|
||||
To string
|
||||
Page int
|
||||
Size int
|
||||
}
|
||||
|
||||
type activityWindow struct {
|
||||
from time.Time
|
||||
to time.Time
|
||||
explicit bool
|
||||
}
|
||||
|
||||
func resolveActivityWindow(query ActivityQuery, now time.Time) (activityWindow, error) {
|
||||
utc := now.UTC()
|
||||
end := utc.Add(time.Second)
|
||||
switch query.Range {
|
||||
case "", activityRange7Days:
|
||||
return activityWindow{from: end.AddDate(0, 0, -7), to: end}, nil
|
||||
case activityRange30Days:
|
||||
return activityWindow{from: end.AddDate(0, 0, -30), to: end}, nil
|
||||
case activityRangeToday:
|
||||
return activityWindow{from: time.Date(utc.Year(), utc.Month(), utc.Day(), 0, 0, 0, 0, time.UTC), to: end}, nil
|
||||
case activityRangeCustom:
|
||||
return resolveCustomWindow(query)
|
||||
default:
|
||||
return activityWindow{}, audit.ErrInvalidFilter
|
||||
}
|
||||
}
|
||||
|
||||
func resolveCustomWindow(query ActivityQuery) (activityWindow, error) {
|
||||
from, err := time.Parse(activityDateLayout, query.From)
|
||||
if err != nil {
|
||||
return activityWindow{}, audit.ErrInvalidFilter
|
||||
}
|
||||
last, err := time.Parse(activityDateLayout, query.To)
|
||||
if err != nil || last.Before(from) {
|
||||
return activityWindow{}, audit.ErrInvalidFilter
|
||||
}
|
||||
return activityWindow{from: from, to: last.AddDate(0, 0, 1), explicit: true}, nil
|
||||
}
|
||||
|
||||
func buildActivityFilter(query ActivityQuery, now time.Time) (audit.Filter, error) {
|
||||
window, err := resolveActivityWindow(query, now)
|
||||
if err != nil {
|
||||
return audit.Filter{}, err
|
||||
}
|
||||
filter := audit.Filter{Query: query.Q, From: &window.from}
|
||||
if window.explicit {
|
||||
filter.To = &window.to
|
||||
}
|
||||
if filter.Outcome, err = parseActivityOutcome(query.Outcome); err != nil {
|
||||
return audit.Filter{}, err
|
||||
}
|
||||
if filter.Security, err = parseActivitySecurity(query.Security); err != nil {
|
||||
return audit.Filter{}, err
|
||||
}
|
||||
if query.Category != "" {
|
||||
if filter.Category, err = audit.ParseCategory(query.Category); err != nil {
|
||||
return audit.Filter{}, err
|
||||
}
|
||||
}
|
||||
if query.Actor != "" {
|
||||
actor, err := audit.ParseActor(query.Actor)
|
||||
if err != nil {
|
||||
return audit.Filter{}, err
|
||||
}
|
||||
filter.Actor = &actor
|
||||
}
|
||||
return filter, nil
|
||||
}
|
||||
|
||||
func parseActivityOutcome(raw string) (audit.Outcome, error) {
|
||||
switch raw {
|
||||
case "", outcomeAll:
|
||||
return "", nil
|
||||
case string(audit.OutcomeSuccess):
|
||||
return audit.OutcomeSuccess, nil
|
||||
case string(audit.OutcomeFailed):
|
||||
return audit.OutcomeFailed, nil
|
||||
default:
|
||||
return "", audit.ErrInvalidFilter
|
||||
}
|
||||
}
|
||||
|
||||
func parseActivitySecurity(raw string) (bool, error) {
|
||||
switch raw {
|
||||
case "":
|
||||
return false, nil
|
||||
case securityOnly:
|
||||
return true, nil
|
||||
default:
|
||||
return false, audit.ErrInvalidFilter
|
||||
}
|
||||
}
|
||||
|
||||
func resolveActivityPaging(query ActivityQuery) (int, int, error) {
|
||||
page, size := query.Page, query.Size
|
||||
if page == 0 {
|
||||
page = 1
|
||||
}
|
||||
if size == 0 {
|
||||
size = activityDefaultSize
|
||||
}
|
||||
if page < 1 || !containsInt(activityPageSizes, size) {
|
||||
return 0, 0, audit.ErrInvalidFilter
|
||||
}
|
||||
return page, size, nil
|
||||
}
|
||||
|
||||
func containsInt(values []int, wanted int) bool {
|
||||
for _, value := range values {
|
||||
if value == wanted {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in new issue
Block a user