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,88 @@
|
||||
package useCases
|
||||
|
||||
import (
|
||||
"RenewCMS/internal/domain/audit"
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const exportBatchSize = 500
|
||||
|
||||
const formulaGuard = "'"
|
||||
const formulaStarts = "=+-@"
|
||||
|
||||
var exportHeader = []string{"when", "actor", "action", "target", "outcome", "source", "ip"}
|
||||
|
||||
type ExportActivityUseCase struct {
|
||||
events audit.Repository
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
type ActivityExport struct {
|
||||
events audit.Repository
|
||||
filter audit.Filter
|
||||
}
|
||||
|
||||
func NewExportActivityUseCase(events audit.Repository, now func() time.Time) *ExportActivityUseCase {
|
||||
return &ExportActivityUseCase{events, now}
|
||||
}
|
||||
|
||||
func (e *ExportActivityUseCase) Prepare(query ActivityQuery) (*ActivityExport, error) {
|
||||
filter, err := buildActivityFilter(query, e.now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ActivityExport{events: e.events, filter: filter}, nil
|
||||
}
|
||||
|
||||
func (a *ActivityExport) WriteCSV(w io.Writer) error {
|
||||
writer := csv.NewWriter(w)
|
||||
if err := writer.Write(exportHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
for page := 1; ; page++ {
|
||||
filter := a.filter
|
||||
filter.Page, filter.Size = page, exportBatchSize
|
||||
found, err := a.events.List(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, event := range found.Items {
|
||||
if err := writer.Write(exportRow(event)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
writer.Flush()
|
||||
if err := writer.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(found.Items) < exportBatchSize || page*exportBatchSize >= found.Total {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func exportRow(event audit.Event) []string {
|
||||
ip := ""
|
||||
if event.IP != nil {
|
||||
ip = *event.IP
|
||||
}
|
||||
return []string{
|
||||
event.CreatedAt.UTC().Format(time.RFC3339),
|
||||
guardCell(event.Actor.Name),
|
||||
guardCell(event.Action),
|
||||
guardCell(event.Target.Name),
|
||||
string(event.Outcome),
|
||||
string(event.Source),
|
||||
guardCell(ip),
|
||||
}
|
||||
}
|
||||
|
||||
func guardCell(value string) string {
|
||||
if value != "" && strings.ContainsRune(formulaStarts, rune(value[0])) {
|
||||
return formulaGuard + value
|
||||
}
|
||||
return value
|
||||
}
|
||||
Reference in new issue
Block a user