// Package grpc provides gRPC client implementations for service clients. package grpc import ( "context" "fmt" "math" auditv1 "git.dcentral.systems/toolz/goplt/api/proto/generated/audit/v1" "git.dcentral.systems/toolz/goplt/pkg/registry" "git.dcentral.systems/toolz/goplt/pkg/services" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) // AuditClient implements AuditServiceClient using gRPC. type AuditClient struct { registry registry.ServiceRegistry conn *grpc.ClientConn client auditv1.AuditServiceClient } // NewAuditClient creates a new gRPC client for the Audit Service. func NewAuditClient(reg registry.ServiceRegistry) (services.AuditServiceClient, error) { client := &AuditClient{ registry: reg, } return client, nil } // connect connects to the Audit Service. func (c *AuditClient) connect(ctx context.Context) error { if c.conn != nil { return nil } instances, err := c.registry.Discover(ctx, "audit-service") if err != nil { return fmt.Errorf("failed to discover audit service: %w", err) } if len(instances) == 0 { return fmt.Errorf("no instances found for audit-service") } instance := instances[0] address := fmt.Sprintf("%s:%d", instance.Address, instance.Port) conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return fmt.Errorf("failed to connect to audit-service at %s: %w", address, err) } c.conn = conn c.client = auditv1.NewAuditServiceClient(conn) return nil } // Record records an audit log entry. func (c *AuditClient) Record(ctx context.Context, entry *services.AuditLogEntry) error { if err := c.connect(ctx); err != nil { return err } _, err := c.client.Record(ctx, &auditv1.RecordRequest{ Entry: &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, }, }) if err != nil { return fmt.Errorf("record audit log failed: %w", err) } return nil } // Query queries audit logs based on filters. func (c *AuditClient) Query(ctx context.Context, filters *services.AuditLogFilters) ([]services.AuditLogEntry, error) { if err := c.connect(ctx); err != nil { return nil, err } limit := filters.Limit if limit > math.MaxInt32 { limit = math.MaxInt32 } offset := filters.Offset if offset > math.MaxInt32 { offset = math.MaxInt32 } req := &auditv1.QueryRequest{ Limit: int32(limit), Offset: int32(offset), } if filters.UserID != nil { req.UserId = filters.UserID } if filters.Action != nil { req.Action = filters.Action } if filters.Resource != nil { req.Resource = filters.Resource } if filters.ResourceID != nil { req.ResourceId = filters.ResourceID } if filters.StartTime != nil { req.StartTime = filters.StartTime } if filters.EndTime != nil { req.EndTime = filters.EndTime } resp, err := c.client.Query(ctx, req) if err != nil { return nil, fmt.Errorf("query audit logs failed: %w", err) } entries := make([]services.AuditLogEntry, 0, len(resp.Entries)) for _, e := range resp.Entries { entries = append(entries, services.AuditLogEntry{ UserID: e.UserId, Action: e.Action, Resource: e.Resource, ResourceID: e.ResourceId, IPAddress: e.IpAddress, UserAgent: e.UserAgent, Metadata: e.Metadata, Timestamp: e.Timestamp, }) } return entries, nil }