// Package api provides gRPC server implementation for Audit Service. package api import ( "math" "context" auditv1 "git.dcentral.systems/toolz/goplt/api/proto/generated/audit/v1" "git.dcentral.systems/toolz/goplt/services/audit/internal/service" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // Server implements the AuditService gRPC server. type Server struct { auditv1.UnimplementedAuditServiceServer service *service.AuditService logger *zap.Logger } // NewServer creates a new Audit Service gRPC server. func NewServer(auditService *service.AuditService, logger *zap.Logger) *Server { return &Server{ service: auditService, logger: logger, } } // Record records an audit log entry. func (s *Server) Record(ctx context.Context, req *auditv1.RecordRequest) (*auditv1.RecordResponse, error) { if req.Entry == nil { return nil, status.Error(codes.InvalidArgument, "entry is required") } entry := req.Entry // Convert proto entry to service entry serviceEntry := &service.AuditLogEntry{ UserID: entry.UserId, Action: entry.Action, Resource: entry.Resource, ResourceID: entry.ResourceId, IPAddress: entry.IpAddress, UserAgent: entry.UserAgent, Metadata: entry.Metadata, Timestamp: entry.Timestamp, } // Record the audit log if err := s.service.Record(ctx, serviceEntry); err != nil { s.logger.Error("Failed to record audit log", zap.Error(err), zap.String("user_id", entry.UserId), zap.String("action", entry.Action), ) return nil, status.Errorf(codes.Internal, "failed to record audit log: %v", err) } return &auditv1.RecordResponse{ Success: true, }, nil } // Query queries audit logs based on filters. func (s *Server) Query(ctx context.Context, req *auditv1.QueryRequest) (*auditv1.QueryResponse, error) { // Convert proto filters to service filters filters := &service.AuditLogFilters{ Limit: int(req.Limit), Offset: int(req.Offset), } if req.UserId != nil { userID := *req.UserId filters.UserID = &userID } if req.Action != nil { action := *req.Action filters.Action = &action } if req.Resource != nil { resource := *req.Resource filters.Resource = &resource } if req.ResourceId != nil { resourceID := *req.ResourceId filters.ResourceID = &resourceID } if req.StartTime != nil { startTime := *req.StartTime filters.StartTime = &startTime } if req.EndTime != nil { endTime := *req.EndTime filters.EndTime = &endTime } // Query audit logs entries, err := s.service.Query(ctx, filters) if err != nil { s.logger.Error("Failed to query audit logs", zap.Error(err), ) return nil, status.Errorf(codes.Internal, "failed to query audit logs: %v", err) } // Convert service entries to proto entries protoEntries := make([]*auditv1.AuditLogEntry, 0, len(entries)) for _, entry := range entries { protoEntries = append(protoEntries, &auditv1.AuditLogEntry{ UserId: entry.UserID, Action: entry.Action, Resource: entry.Resource, ResourceId: entry.ResourceID, IpAddress: entry.IPAddress, UserAgent: entry.UserAgent, Metadata: entry.Metadata, Timestamp: entry.Timestamp, }) } total := len(protoEntries) var totalInt32 int32 if total > math.MaxInt32 { totalInt32 = math.MaxInt32 } else { totalInt32 = int32(total) } return &auditv1.QueryResponse{ Entries: protoEntries, Total: totalInt32, // Note: This is a simplified total, actual total would require a count query }, nil }