diff options
Diffstat (limited to 'internal/handler/grpc/server.go')
| -rw-r--r-- | internal/handler/grpc/server.go | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/internal/handler/grpc/server.go b/internal/handler/grpc/server.go new file mode 100644 index 0000000..a3a3175 --- /dev/null +++ b/internal/handler/grpc/server.go | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | package grpc | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "context" | ||
| 5 | "fmt" | ||
| 6 | |||
| 7 | pb "northwest.io/nostr-grpc/api/nostr/v1" | ||
| 8 | "northwest.io/nostr-grpc/internal/storage" | ||
| 9 | ) | ||
| 10 | |||
| 11 | type EventStore interface { | ||
| 12 | StoreEvent(context.Context, *storage.EventData) error | ||
| 13 | QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error) | ||
| 14 | } | ||
| 15 | |||
| 16 | type Server struct { | ||
| 17 | pb.UnimplementedNostrRelayServer | ||
| 18 | store EventStore | ||
| 19 | } | ||
| 20 | |||
| 21 | func NewServer(store EventStore) *Server { | ||
| 22 | return &Server{store: store} | ||
| 23 | } | ||
| 24 | |||
| 25 | func (s *Server) PublishEvent(ctx context.Context, req *pb.PublishEventRequest) (*pb.PublishEventResponse, error) { | ||
| 26 | if req.Event == nil { | ||
| 27 | return &pb.PublishEventResponse{ | ||
| 28 | Accepted: false, | ||
| 29 | Message: "event is required", | ||
| 30 | }, nil | ||
| 31 | } | ||
| 32 | |||
| 33 | nostrEvent := PBToNostr(req.Event) | ||
| 34 | |||
| 35 | if !nostrEvent.CheckID() { | ||
| 36 | return &pb.PublishEventResponse{ | ||
| 37 | Accepted: false, | ||
| 38 | Message: "invalid event ID", | ||
| 39 | }, nil | ||
| 40 | } | ||
| 41 | |||
| 42 | if !nostrEvent.Verify() { | ||
| 43 | return &pb.PublishEventResponse{ | ||
| 44 | Accepted: false, | ||
| 45 | Message: "invalid signature", | ||
| 46 | }, nil | ||
| 47 | } | ||
| 48 | |||
| 49 | canonicalJSON := nostrEvent.Serialize() | ||
| 50 | |||
| 51 | eventData := &storage.EventData{ | ||
| 52 | Event: req.Event, | ||
| 53 | CanonicalJSON: canonicalJSON, | ||
| 54 | } | ||
| 55 | |||
| 56 | err := s.store.StoreEvent(ctx, eventData) | ||
| 57 | if err == storage.ErrEventExists { | ||
| 58 | return &pb.PublishEventResponse{ | ||
| 59 | Accepted: false, | ||
| 60 | Message: "duplicate: event already exists", | ||
| 61 | CanonicalJson: canonicalJSON, | ||
| 62 | }, nil | ||
| 63 | } | ||
| 64 | if err != nil { | ||
| 65 | return nil, fmt.Errorf("failed to store event: %w", err) | ||
| 66 | } | ||
| 67 | |||
| 68 | return &pb.PublishEventResponse{ | ||
| 69 | Accepted: true, | ||
| 70 | Message: "success", | ||
| 71 | CanonicalJson: canonicalJSON, | ||
| 72 | }, nil | ||
| 73 | } | ||
| 74 | |||
| 75 | func (s *Server) QueryEvents(ctx context.Context, req *pb.QueryRequest) (*pb.QueryResponse, error) { | ||
| 76 | opts := &storage.QueryOptions{ | ||
| 77 | IncludeCanonical: req.IncludeCanonicalJson, | ||
| 78 | Limit: req.PageSize, | ||
| 79 | } | ||
| 80 | |||
| 81 | if opts.Limit == 0 { | ||
| 82 | opts.Limit = 100 | ||
| 83 | } | ||
| 84 | |||
| 85 | events, err := s.store.QueryEvents(ctx, req.Filters, opts) | ||
| 86 | if err != nil { | ||
| 87 | return nil, fmt.Errorf("query failed: %w", err) | ||
| 88 | } | ||
| 89 | |||
| 90 | return &pb.QueryResponse{ | ||
| 91 | Events: events, | ||
| 92 | }, nil | ||
| 93 | } | ||
| 94 | |||
| 95 | func (s *Server) CountEvents(ctx context.Context, req *pb.CountRequest) (*pb.CountResponse, error) { | ||
| 96 | events, err := s.store.QueryEvents(ctx, req.Filters, &storage.QueryOptions{Limit: 0}) | ||
| 97 | if err != nil { | ||
| 98 | return nil, fmt.Errorf("count failed: %w", err) | ||
| 99 | } | ||
| 100 | |||
| 101 | return &pb.CountResponse{ | ||
| 102 | Count: int64(len(events)), | ||
| 103 | }, nil | ||
| 104 | } | ||
| 105 | |||
| 106 | func (s *Server) PublishBatch(ctx context.Context, req *pb.PublishBatchRequest) (*pb.PublishBatchResponse, error) { | ||
| 107 | results := make([]*pb.PublishEventResponse, len(req.Events)) | ||
| 108 | |||
| 109 | for i, event := range req.Events { | ||
| 110 | resp, err := s.PublishEvent(ctx, &pb.PublishEventRequest{Event: event}) | ||
| 111 | if err != nil { | ||
| 112 | return nil, err | ||
| 113 | } | ||
| 114 | results[i] = resp | ||
| 115 | } | ||
| 116 | |||
| 117 | return &pb.PublishBatchResponse{ | ||
| 118 | Results: results, | ||
| 119 | }, nil | ||
| 120 | } | ||
| 121 | |||
| 122 | func (s *Server) Subscribe(req *pb.SubscribeRequest, stream pb.NostrRelay_SubscribeServer) error { | ||
| 123 | return fmt.Errorf("not implemented yet") | ||
| 124 | } | ||
| 125 | |||
| 126 | func (s *Server) Unsubscribe(ctx context.Context, req *pb.UnsubscribeRequest) (*pb.Empty, error) { | ||
| 127 | return nil, fmt.Errorf("not implemented yet") | ||
| 128 | } | ||
