package store import ( "context" "fmt" "time" ) // AuditEntry ist ein Protokolleintrag einer Admin-Aktion. Append-only: // siehe Migration 0004 — ein Protokoll, das man ändern kann, ist kein // Nachweis mehr, aus demselben Grund wie bei finding/extraction. type AuditEntry struct { ID string ActorUserID string Action string TargetType string TargetID string Details string CreatedAt time.Time } // CreateAuditEntry protokolliert eine Admin-Aktion. func (s *Store) CreateAuditEntry(ctx context.Context, actorUserID, action, targetType, targetID, details string) (AuditEntry, error) { var e AuditEntry err := s.db(ctx).QueryRow(ctx, ` INSERT INTO audit_log (actor_user_id, action, target_type, target_id, details) VALUES ($1, $2, $3, $4, $5) RETURNING id, actor_user_id, action, target_type, target_id, details, created_at `, actorUserID, action, targetType, targetID, details).Scan( &e.ID, &e.ActorUserID, &e.Action, &e.TargetType, &e.TargetID, &e.Details, &e.CreatedAt, ) if err != nil { return AuditEntry{}, fmt.Errorf("store: create audit entry: %w", err) } return e, nil } // ListAuditLog liefert die letzten Protokolleinträge, neueste zuerst. func (s *Store) ListAuditLog(ctx context.Context, limit int) ([]AuditEntry, error) { rows, err := s.db(ctx).Query(ctx, ` SELECT id, actor_user_id, action, target_type, target_id, details, created_at FROM audit_log ORDER BY created_at DESC LIMIT $1 `, limit) if err != nil { return nil, fmt.Errorf("store: list audit log: %w", err) } defer rows.Close() var out []AuditEntry for rows.Next() { var e AuditEntry if err := rows.Scan(&e.ID, &e.ActorUserID, &e.Action, &e.TargetType, &e.TargetID, &e.Details, &e.CreatedAt); err != nil { return nil, fmt.Errorf("store: scan audit entry: %w", err) } out = append(out, e) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("store: list audit log: %w", err) } return out, nil }