Files
bot-pontos/backend/internal/adapters/postgres/audit.go
2026-10-23 22:31:02 -03:00

79 lines
2.7 KiB
Go

package postgres
import (
"context"
"encoding/json"
"time"
"botpontos/internal/domain"
)
func (s *Store) InsertAudit(ctx context.Context, e domain.AuditEntry) error {
details := e.Details
if details == nil {
details = map[string]any{}
}
raw, err := json.Marshal(details)
if err != nil {
return err
}
_, err = s.pool.Exec(ctx, `
INSERT INTO audit_logs (source, actor_user_id, actor_account, target_user_id, event_type, success, details)
VALUES ($1::text::audit_source, $2, $3, $4, $5, $6, $7::text::jsonb)`,
string(e.Source), e.ActorUserID, e.ActorAccount, e.TargetUserID, e.EventType, e.Success, string(raw))
return err
}
type AuditFilter struct {
From *time.Time
To *time.Time
Source *string
EventType *string
Success *bool
TargetUserID *int64
ActorAccount *int64
Before *int64 // cursor: id do último item da página anterior
Limit int
}
func (s *Store) ListAudit(ctx context.Context, f AuditFilter) ([]domain.AuditLog, error) {
if f.Limit <= 0 || f.Limit > 200 {
f.Limit = 50
}
rows, err := s.pool.Query(ctx, `
SELECT a.id, a.occurred_at, a.source, a.actor_user_id, au.display_name, a.actor_account, wa.username::text,
a.target_user_id, tu.display_name, a.event_type, a.success, a.details::text
FROM audit_logs a
LEFT JOIN users au ON au.id = a.actor_user_id
LEFT JOIN web_accounts wa ON wa.id = a.actor_account
LEFT JOIN users tu ON tu.id = a.target_user_id
WHERE ($1::timestamptz IS NULL OR a.occurred_at >= $1)
AND ($2::timestamptz IS NULL OR a.occurred_at < $2)
AND ($3::text IS NULL OR a.source::text = $3)
AND ($4::text IS NULL OR a.event_type = $4)
AND ($5::boolean IS NULL OR a.success = $5)
AND ($6::bigint IS NULL OR a.target_user_id = $6)
AND ($7::bigint IS NULL OR a.actor_account = $7)
AND ($8::bigint IS NULL OR a.id < $8)
ORDER BY a.id DESC
LIMIT $9`,
f.From, f.To, f.Source, f.EventType, f.Success, f.TargetUserID, f.ActorAccount, f.Before, f.Limit)
return collect(rows, err, func(row scanner) (domain.AuditLog, error) {
var l domain.AuditLog
var source, details string
if err := row.Scan(&l.ID, &l.OccurredAt, &source, &l.ActorUserID, &l.ActorUserName, &l.ActorAccount,
&l.ActorUsername, &l.TargetUserID, &l.TargetUserName, &l.EventType, &l.Success, &details); err != nil {
return l, err
}
l.Source = domain.AuditSource(source)
l.Details = map[string]any{}
_ = json.Unmarshal([]byte(details), &l.Details)
return l, nil
})
}
func (s *Store) DeleteAuditBefore(ctx context.Context, before time.Time) (int64, error) {
tag, err := s.pool.Exec(ctx, `DELETE FROM audit_logs WHERE occurred_at < $1`, before)
return tag.RowsAffected(), err
}