summaryrefslogtreecommitdiffstats
path: root/internal/handler/grpc/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/handler/grpc/server.go')
-rw-r--r--internal/handler/grpc/server.go128
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 @@
1package grpc
2
3import (
4 "context"
5 "fmt"
6
7 pb "northwest.io/nostr-grpc/api/nostr/v1"
8 "northwest.io/nostr-grpc/internal/storage"
9)
10
11type EventStore interface {
12 StoreEvent(context.Context, *storage.EventData) error
13 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error)
14}
15
16type Server struct {
17 pb.UnimplementedNostrRelayServer
18 store EventStore
19}
20
21func NewServer(store EventStore) *Server {
22 return &Server{store: store}
23}
24
25func (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
75func (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
95func (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
106func (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
122func (s *Server) Subscribe(req *pb.SubscribeRequest, stream pb.NostrRelay_SubscribeServer) error {
123 return fmt.Errorf("not implemented yet")
124}
125
126func (s *Server) Unsubscribe(ctx context.Context, req *pb.UnsubscribeRequest) (*pb.Empty, error) {
127 return nil, fmt.Errorf("not implemented yet")
128}