internal/store/audit.go

ba046389a8614e133b3fdd33d25fa3055bca7e7d
gitbay/internal/store/audit.go history · blame · raw

47 lines · 1323 bytes

 1package store
 2
 3import "encoding/json"
 4
 5// Audit appends to the security feed. Events are the product feed; this
 6// records who did what, from where, for an operator. actorID 0 means the
 7// host admin (gitbayd admin commands) or an unauthenticated source.
 8func (s *Store) Audit(actorID int64, action string, data map[string]any) {
 9	var actor any
10	if actorID != 0 {
11		actor = actorID
12	}
13	raw, err := json.Marshal(data)
14	if err != nil {
15		raw = []byte("{}")
16	}
17	s.DB.Exec("INSERT INTO audit_log (actor_id, action, data_json) VALUES (?, ?, ?)",
18		actor, action, string(raw))
19}
20
21type AuditEntry struct {
22	ID        int64  `json:"id"`
23	Actor     string `json:"actor,omitempty"`
24	Action    string `json:"action"`
25	Data      string `json:"data"`
26	CreatedAt string `json:"created_at"`
27}
28
29func (s *Store) AuditEntries(limit int) ([]AuditEntry, error) {
30	rows, err := s.DB.Query(`
31		SELECT a.id, COALESCE(u.username, ''), a.action, a.data_json, a.created_at
32		FROM audit_log a LEFT JOIN users u ON u.id = a.actor_id
33		ORDER BY a.id DESC LIMIT ?`, limit)
34	if err != nil {
35		return nil, err
36	}
37	defer rows.Close()
38	var out []AuditEntry
39	for rows.Next() {
40		var e AuditEntry
41		if err := rows.Scan(&e.ID, &e.Actor, &e.Action, &e.Data, &e.CreatedAt); err != nil {
42			return nil, err
43		}
44		out = append(out, e)
45	}
46	return out, rows.Err()
47}