145 lines
3.4 KiB
Go
145 lines
3.4 KiB
Go
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
|
|
}
|