summaryrefslogtreecommitdiffstats
path: root/internal/handler
diff options
context:
space:
mode:
Diffstat (limited to 'internal/handler')
-rw-r--r--internal/handler/grpc/server.go11
-rw-r--r--internal/handler/websocket/handler.go13
-rw-r--r--internal/handler/websocket/nip11.go60
3 files changed, 84 insertions, 0 deletions
diff --git a/internal/handler/grpc/server.go b/internal/handler/grpc/server.go
index b65b527..b1ffd96 100644
--- a/internal/handler/grpc/server.go
+++ b/internal/handler/grpc/server.go
@@ -13,6 +13,7 @@ import (
13type EventStore interface { 13type EventStore interface {
14 StoreEvent(context.Context, *storage.EventData) error 14 StoreEvent(context.Context, *storage.EventData) error
15 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error) 15 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error)
16 ProcessDeletion(context.Context, *pb.Event) error
16} 17}
17 18
18type Server struct { 19type Server struct {
@@ -75,6 +76,16 @@ func (s *Server) PublishEvent(ctx context.Context, req *pb.PublishEventRequest)
75 return nil, fmt.Errorf("failed to store event: %w", err) 76 return nil, fmt.Errorf("failed to store event: %w", err)
76 } 77 }
77 78
79 if req.Event.Kind == 5 {
80 if err := s.store.ProcessDeletion(ctx, req.Event); err != nil {
81 return &pb.PublishEventResponse{
82 Accepted: false,
83 Message: fmt.Sprintf("deletion processing failed: %v", err),
84 CanonicalJson: canonicalJSON,
85 }, nil
86 }
87 }
88
78 s.subs.MatchAndFan(req.Event) 89 s.subs.MatchAndFan(req.Event)
79 90
80 return &pb.PublishEventResponse{ 91 return &pb.PublishEventResponse{
diff --git a/internal/handler/websocket/handler.go b/internal/handler/websocket/handler.go
index cef83dd..4a7db0d 100644
--- a/internal/handler/websocket/handler.go
+++ b/internal/handler/websocket/handler.go
@@ -17,6 +17,7 @@ import (
17type EventStore interface { 17type EventStore interface {
18 StoreEvent(context.Context, *storage.EventData) error 18 StoreEvent(context.Context, *storage.EventData) error
19 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error) 19 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error)
20 ProcessDeletion(context.Context, *pb.Event) error
20} 21}
21 22
22type Handler struct { 23type Handler struct {
@@ -32,6 +33,11 @@ func NewHandler(store EventStore, subs *subscription.Manager) *Handler {
32} 33}
33 34
34func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 35func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
36 if r.Method == "GET" && r.Header.Get("Accept") == "application/nostr+json" {
37 h.ServeNIP11(w, r)
38 return
39 }
40
35 conn, err := websocket.Accept(w, r) 41 conn, err := websocket.Accept(w, r)
36 if err != nil { 42 if err != nil {
37 log.Printf("WebSocket accept failed: %v", err) 43 log.Printf("WebSocket accept failed: %v", err)
@@ -125,6 +131,13 @@ func (h *Handler) handleEvent(ctx context.Context, conn *websocket.Conn, raw []j
125 return nil 131 return nil
126 } 132 }
127 133
134 if pbEvent.Kind == 5 {
135 if err := h.store.ProcessDeletion(ctx, pbEvent); err != nil {
136 h.sendOK(ctx, conn, event.ID, false, fmt.Sprintf("deletion failed: %v", err))
137 return nil
138 }
139 }
140
128 h.subs.MatchAndFan(pbEvent) 141 h.subs.MatchAndFan(pbEvent)
129 142
130 h.sendOK(ctx, conn, event.ID, true, "") 143 h.sendOK(ctx, conn, event.ID, true, "")
diff --git a/internal/handler/websocket/nip11.go b/internal/handler/websocket/nip11.go
new file mode 100644
index 0000000..a5bb9ca
--- /dev/null
+++ b/internal/handler/websocket/nip11.go
@@ -0,0 +1,60 @@
1package websocket
2
3import (
4 "encoding/json"
5 "net/http"
6)
7
8type RelayInfo struct {
9 Name string `json:"name"`
10 Description string `json:"description"`
11 Pubkey string `json:"pubkey,omitempty"`
12 Contact string `json:"contact,omitempty"`
13 SupportedNIPs []int `json:"supported_nips"`
14 Software string `json:"software"`
15 Version string `json:"version"`
16 Limitation *Limits `json:"limitation,omitempty"`
17}
18
19type Limits struct {
20 MaxMessageLength int `json:"max_message_length,omitempty"`
21 MaxSubscriptions int `json:"max_subscriptions,omitempty"`
22 MaxFilters int `json:"max_filters,omitempty"`
23 MaxLimit int `json:"max_limit,omitempty"`
24 MaxSubidLength int `json:"max_subid_length,omitempty"`
25 MaxEventTags int `json:"max_event_tags,omitempty"`
26 MaxContentLength int `json:"max_content_length,omitempty"`
27 MinPowDifficulty int `json:"min_pow_difficulty,omitempty"`
28 AuthRequired bool `json:"auth_required"`
29 PaymentRequired bool `json:"payment_required"`
30 RestrictedWrites bool `json:"restricted_writes"`
31}
32
33func (h *Handler) ServeNIP11(w http.ResponseWriter, r *http.Request) {
34 info := RelayInfo{
35 Name: "nostr-grpc relay",
36 Description: "High-performance Nostr relay with gRPC, Connect, and WebSocket support",
37 SupportedNIPs: []int{1, 9, 11},
38 Software: "northwest.io/nostr-grpc",
39 Version: "0.1.0",
40 Limitation: &Limits{
41 MaxMessageLength: 65536,
42 MaxSubscriptions: 20,
43 MaxFilters: 10,
44 MaxLimit: 5000,
45 MaxSubidLength: 64,
46 MaxEventTags: 2000,
47 MaxContentLength: 65536,
48 AuthRequired: false,
49 PaymentRequired: false,
50 RestrictedWrites: false,
51 },
52 }
53
54 w.Header().Set("Content-Type", "application/nostr+json")
55 w.Header().Set("Access-Control-Allow-Origin", "*")
56 w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept")
57 w.Header().Set("Access-Control-Allow-Methods", "GET")
58
59 json.NewEncoder(w).Encode(info)
60}