package httpapi import ( "context" "encoding/json" "github.com/dtoro/oikos/internal/httpapi/gen" ) func (s *Server) QueryAudit(ctx context.Context, req gen.QueryAuditRequestObject) (gen.QueryAuditResponseObject, error) { limit := clampLimit(req.Params.Limit) var actorType, actorID, action, entityID, correlationID *string if req.Params.ActorType != nil { actorType = req.Params.ActorType } if req.Params.ActorId != nil { actorID = req.Params.ActorId } if req.Params.Action != nil { action = req.Params.Action } if req.Params.EntityId != nil { entityID = req.Params.EntityId } if req.Params.CorrelationId != nil { correlationID = req.Params.CorrelationId } rows, err := s.pool.Query(ctx, ` SELECT id, ts, actor_type, actor_id::text, action, entity_id::text, method, path, status_code, detail, source_ip, correlation_id, session_id::text FROM audit_log WHERE ($1::text IS NULL OR actor_type = $1) AND ($2::text IS NULL OR actor_id::text = $2) AND ($3::text IS NULL OR action = $3) AND ($4::text IS NULL OR entity_id::text = $4) AND ($5::text IS NULL OR correlation_id = $5) AND ($6::timestamptz IS NULL OR ts >= $6) AND ($7::timestamptz IS NULL OR ts <= $7) ORDER BY ts DESC LIMIT $8`, actorType, actorID, action, entityID, correlationID, req.Params.From, req.Params.To, limit) if err != nil { return nil, err } defer rows.Close() items := []gen.AuditEntry{} for rows.Next() { var a gen.AuditEntry var detailBytes []byte var actID, entID, method, path, sourceIP, corrID, sessionID *string var statusCode *int if err := rows.Scan(&a.Id, &a.Ts, &a.ActorType, &actID, &a.Action, &entID, &method, &path, &statusCode, &detailBytes, &sourceIP, &corrID, &sessionID); err != nil { return nil, err } a.ActorId = actID a.EntityId = entID a.Method = method a.Path = path a.StatusCode = statusCode a.SourceIp = sourceIP a.CorrelationId = corrID var detail map[string]any if json.Unmarshal(detailBytes, &detail) == nil { a.Detail = &detail } items = append(items, a) } return gen.QueryAudit200JSONResponse{Items: items}, rows.Err() }