summaryrefslogtreecommitdiff
path: root/internal/controlplane/audit.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--internal/controlplane/audit.go52
1 files changed, 52 insertions, 0 deletions
diff --git a/internal/controlplane/audit.go b/internal/controlplane/audit.go
new file mode 100644
index 0000000..93a4f58
--- /dev/null
+++ b/internal/controlplane/audit.go
@@ -0,0 +1,52 @@
+package controlplane
+
+import (
+ "context"
+ "fmt"
+ "strings"
+)
+
+func (s *Store) WriteAudit(ctx context.Context, input AuditInput) error {
+ actorType := "console_user"
+ if input.Actor.Bootstrap {
+ actorType = "bootstrap"
+ }
+ _, err := s.db.Exec(ctx, `INSERT INTO audit_logs
+ (actor_id, actor_type, actor_role, tenant_id, request_id, method, path, action, status_code, remote_ip, user_agent)
+ VALUES (NULLIF($1,'')::uuid,$2,$3,NULLIF($4,'')::uuid,$5,$6,$7,$8,$9,NULLIF($10,'')::inet,$11)`,
+ input.Actor.ID, actorType, input.Actor.Role, input.Actor.TenantID, input.RequestID, input.Method,
+ input.Path, input.Action, input.StatusCode, input.RemoteIP, input.UserAgent)
+ if err != nil {
+ return fmt.Errorf("write audit log: %w", err)
+ }
+ return nil
+}
+
+func (s *Store) ListAudit(ctx context.Context, tenantID string, limit int) ([]AuditLog, error) {
+ if limit < 1 || limit > 1000 {
+ limit = 200
+ }
+ query := `SELECT id, COALESCE(actor_id::text,''), actor_type, actor_role, COALESCE(tenant_id::text,''),
+ request_id, method, path, action, status_code, COALESCE(host(remote_ip),''), user_agent, created_at FROM audit_logs`
+ args := []any{}
+ if strings.TrimSpace(tenantID) != "" {
+ query += ` WHERE tenant_id=$1`
+ args = append(args, tenantID)
+ }
+ args = append(args, limit)
+ query += fmt.Sprintf(` ORDER BY created_at DESC LIMIT $%d`, len(args))
+ rows, err := s.db.Query(ctx, query, args...)
+ if err != nil {
+ return nil, fmt.Errorf("query audit logs: %w", err)
+ }
+ defer rows.Close()
+ result := make([]AuditLog, 0)
+ for rows.Next() {
+ var item AuditLog
+ if err := rows.Scan(&item.ID, &item.ActorID, &item.ActorType, &item.ActorRole, &item.TenantID, &item.RequestID, &item.Method, &item.Path, &item.Action, &item.StatusCode, &item.RemoteIP, &item.UserAgent, &item.CreatedAt); err != nil {
+ return nil, fmt.Errorf("scan audit log: %w", err)
+ }
+ result = append(result, item)
+ }
+ return result, rows.Err()
+}