Documentation
¶
Overview ¶
audit/diff.go
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Diff ¶
Diff returns the field names that differ between two structs. Both must be the same type. Uses json tags as field names when present.
Fields tagged with audit:"-" are always excluded from the diff, which is useful for system-managed fields like updated_at and updated_by that change on every write but carry no semantic diff value.
func Mask ¶
Mask takes any value, marshals it to JSON, and returns a copy where sensitive fields (by key name) are replaced with "<redacted>". This approach is intentionally minimal: it operates on the JSON representation and thus does not require constructing typed copies.
Types ¶
type AsyncRepository ¶
type AsyncRepository struct {
OnError func(Entry, error) // called on failed write
// contains filtered or unexported fields
}
AsyncRepository writes audit entries to an underlying Repository asynchronously. It buffers entries in a channel and has a Shutdown method to flush remaining items.
func NewAsyncRepository ¶
func NewAsyncRepository(underlying Repository, buffer int, workers int) *AsyncRepository
NewAsyncRepository creates an AsyncRepository with the given buffer size and number of concurrent drain goroutines. If workers < 1 it defaults to 1.
func (*AsyncRepository) Shutdown ¶
func (a *AsyncRepository) Shutdown(ctx context.Context) error
Shutdown closes the queue and waits for pending items to be processed. If the provided context expires before flush completes an AppError is returned.
closed is set under mu (same mutex Write uses) so that no new wg.Add(1) calls can race with wg.Wait().
func (*AsyncRepository) Write ¶
func (a *AsyncRepository) Write(ctx context.Context, entry Entry) error
Write enqueues an audit entry for asynchronous persistence. Returns an AppError if the repository is shut down or the internal buffer is full.
The mutex is held across the closed-check, wg.Add(1), and the non-blocking channel send so that Shutdown cannot close the channel between any of those three steps (which would cause a WaitGroup panic or a send-on-closed panic).
type AuditLog ¶
type AuditLog struct {
ID uint `json:"id"`
EntityType string `json:"entity_type"`
EntityID int `json:"entity_id"`
Action string `json:"action"`
ActorID int `json:"actor_id"`
Metadata json.RawMessage `json:"metadata"`
BeforeState json.RawMessage `json:"before_state"`
AfterState json.RawMessage `json:"after_state"`
ChangedFields json.RawMessage `json:"changed_fields"`
CreatedAt time.Time `json:"created_at"`
}
AuditLog represents a persisted audit entry. Tags are intentionally generic (no GORM-specific tags) so runtime packages do not import GORM directly. Note: Metadata is stored as raw JSON bytes to ensure GORM AutoMigrate can create the correct column type across dialects.
type Entry ¶
type Entry struct {
EntityType string
EntityID int
Action string
ActorID int
Metadata map[string]any
BeforeState any // will be marshalled to JSON
AfterState any
ChangedFields []string
}
Entry represents a logical audit event to persist.
func (Entry) WithBefore ¶
type Repository ¶
Repository defines the minimal audit persistence API.
func NewRepository ¶
func NewRepository(t database.Transactor) Repository
NewRepository constructs a Repository using the provided Transactor.
func NewRepositoryWithHook ¶
func NewRepositoryWithHook(t database.Transactor, hook func(ctx context.Context, log AuditLog) error) Repository
NewRepositoryWithHook constructs a Repository and installs a test hook used in unit tests.