client.go raw

   1  // Package grpc provides a gRPC client for the relay group service.
   2  package grpc
   3  
   4  import (
   5  	"context"
   6  	"time"
   7  
   8  	"google.golang.org/grpc"
   9  	"google.golang.org/grpc/credentials/insecure"
  10  	"next.orly.dev/pkg/lol/log"
  11  
  12  	commonv1 "next.orly.dev/pkg/proto/orlysync/common/v1"
  13  	relaygroupv1 "next.orly.dev/pkg/proto/orlysync/relaygroup/v1"
  14  )
  15  
  16  // Client is a gRPC client for the relay group service.
  17  type Client struct {
  18  	conn   *grpc.ClientConn
  19  	client relaygroupv1.RelayGroupServiceClient
  20  	ready  chan struct{}
  21  }
  22  
  23  // ClientConfig holds configuration for the gRPC client.
  24  type ClientConfig struct {
  25  	ServerAddress  string
  26  	ConnectTimeout time.Duration
  27  }
  28  
  29  // New creates a new gRPC relay group client.
  30  func New(ctx context.Context, cfg *ClientConfig) (*Client, error) {
  31  	timeout := cfg.ConnectTimeout
  32  	if timeout == 0 {
  33  		timeout = 10 * time.Second
  34  	}
  35  
  36  	dialCtx, cancel := context.WithTimeout(ctx, timeout)
  37  	defer cancel()
  38  
  39  	conn, err := grpc.DialContext(dialCtx, cfg.ServerAddress,
  40  		grpc.WithTransportCredentials(insecure.NewCredentials()),
  41  		grpc.WithDefaultCallOptions(
  42  			grpc.MaxCallRecvMsgSize(16<<20), // 16MB
  43  			grpc.MaxCallSendMsgSize(16<<20), // 16MB
  44  		),
  45  	)
  46  	if err != nil {
  47  		return nil, err
  48  	}
  49  
  50  	c := &Client{
  51  		conn:   conn,
  52  		client: relaygroupv1.NewRelayGroupServiceClient(conn),
  53  		ready:  make(chan struct{}),
  54  	}
  55  
  56  	go c.waitForReady(ctx)
  57  
  58  	return c, nil
  59  }
  60  
  61  func (c *Client) waitForReady(ctx context.Context) {
  62  	for {
  63  		select {
  64  		case <-ctx.Done():
  65  			return
  66  		default:
  67  			resp, err := c.client.Ready(ctx, &commonv1.Empty{})
  68  			if err == nil && resp.Ready {
  69  				close(c.ready)
  70  				log.I.F("gRPC relay group client connected and ready")
  71  				return
  72  			}
  73  			time.Sleep(100 * time.Millisecond)
  74  		}
  75  	}
  76  }
  77  
  78  // Close closes the gRPC connection.
  79  func (c *Client) Close() error {
  80  	if c.conn != nil {
  81  		return c.conn.Close()
  82  	}
  83  	return nil
  84  }
  85  
  86  // Ready returns a channel that closes when the client is ready.
  87  func (c *Client) Ready() <-chan struct{} {
  88  	return c.ready
  89  }
  90  
  91  // FindAuthoritativeConfig finds the authoritative relay group configuration.
  92  func (c *Client) FindAuthoritativeConfig(ctx context.Context) (*relaygroupv1.RelayGroupConfigResponse, error) {
  93  	return c.client.FindAuthoritativeConfig(ctx, &commonv1.Empty{})
  94  }
  95  
  96  // GetRelays returns the list of relays from the authoritative config.
  97  func (c *Client) GetRelays(ctx context.Context) ([]string, error) {
  98  	resp, err := c.client.GetRelays(ctx, &commonv1.Empty{})
  99  	if err != nil {
 100  		return nil, err
 101  	}
 102  	return resp.Relays, nil
 103  }
 104  
 105  // IsAuthorizedPublisher checks if a pubkey can publish relay group configs.
 106  func (c *Client) IsAuthorizedPublisher(ctx context.Context, pubkey []byte) (bool, error) {
 107  	resp, err := c.client.IsAuthorizedPublisher(ctx, &relaygroupv1.AuthorizedPublisherRequest{
 108  		Pubkey: pubkey,
 109  	})
 110  	if err != nil {
 111  		return false, err
 112  	}
 113  	return resp.Authorized, nil
 114  }
 115  
 116  // GetAuthorizedPubkeys returns all authorized publisher pubkeys.
 117  func (c *Client) GetAuthorizedPubkeys(ctx context.Context) ([][]byte, error) {
 118  	resp, err := c.client.GetAuthorizedPubkeys(ctx, &commonv1.Empty{})
 119  	if err != nil {
 120  		return nil, err
 121  	}
 122  	return resp.Pubkeys, nil
 123  }
 124  
 125  // ValidateRelayGroupEvent validates a relay group configuration event.
 126  func (c *Client) ValidateRelayGroupEvent(ctx context.Context, event *commonv1.Event) (bool, string, error) {
 127  	resp, err := c.client.ValidateRelayGroupEvent(ctx, &relaygroupv1.ValidateEventRequest{
 128  		Event: event,
 129  	})
 130  	if err != nil {
 131  		return false, "", err
 132  	}
 133  	return resp.Valid, resp.Error, nil
 134  }
 135  
 136  // HandleRelayGroupEvent processes a relay group event and triggers peer updates.
 137  func (c *Client) HandleRelayGroupEvent(ctx context.Context, event *commonv1.Event) error {
 138  	_, err := c.client.HandleRelayGroupEvent(ctx, &relaygroupv1.HandleEventRequest{
 139  		Event: event,
 140  	})
 141  	return err
 142  }
 143