From 3481c3273f8764bd0a0ab51183dc57f592fb616c Mon Sep 17 00:00:00 2001 From: bndw Date: Fri, 13 Feb 2026 18:17:37 -0800 Subject: feat: add WebSocket server with full NIP-01 support WebSocket handler: - NIP-01 protocol (EVENT, REQ, CLOSE, OK, EOSE, NOTICE) - JSON envelope parsing - Shares subscription manager with gRPC (unified event fan-out) - Standard Nostr client compatibility Relay now serves dual protocols: - gRPC on :50051 (binary, high performance) - WebSocket on :8080 (JSON, Nostr standard) Both protocols share: - Same storage layer - Same subscription manager - Same validation logic Compatible with all Nostr clients! --- internal/handler/websocket/convert.go | 76 +++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 internal/handler/websocket/convert.go (limited to 'internal/handler/websocket/convert.go') diff --git a/internal/handler/websocket/convert.go b/internal/handler/websocket/convert.go new file mode 100644 index 0000000..0458ee4 --- /dev/null +++ b/internal/handler/websocket/convert.go @@ -0,0 +1,76 @@ +package websocket + +import ( + pb "northwest.io/nostr-grpc/api/nostr/v1" + "northwest.io/nostr-grpc/internal/nostr" +) + +func NostrToPB(n *nostr.Event) *pb.Event { + tags := make([]*pb.Tag, len(n.Tags)) + for i, tag := range n.Tags { + tags[i] = &pb.Tag{Values: tag} + } + + return &pb.Event{ + Id: n.ID, + Pubkey: n.PubKey, + CreatedAt: n.CreatedAt, + Kind: int32(n.Kind), + Tags: tags, + Content: n.Content, + Sig: n.Sig, + } +} + +func PBToNostr(e *pb.Event) *nostr.Event { + tags := make(nostr.Tags, len(e.Tags)) + for i, tag := range e.Tags { + tags[i] = tag.Values + } + + return &nostr.Event{ + ID: e.Id, + PubKey: e.Pubkey, + CreatedAt: e.CreatedAt, + Kind: int(e.Kind), + Tags: tags, + Content: e.Content, + Sig: e.Sig, + } +} + +func NostrFilterToPB(f *nostr.Filter) *pb.Filter { + pbFilter := &pb.Filter{ + Ids: f.IDs, + Authors: f.Authors, + Kinds: make([]int32, len(f.Kinds)), + } + + for i, kind := range f.Kinds { + pbFilter.Kinds[i] = int32(kind) + } + + if f.Since != nil { + since := int64(*f.Since) + pbFilter.Since = &since + } + + if f.Until != nil { + until := int64(*f.Until) + pbFilter.Until = &until + } + + if f.Limit > 0 { + limit := int32(f.Limit) + pbFilter.Limit = &limit + } + + if len(f.Tags) > 0 { + pbFilter.TagFilters = make(map[string]*pb.TagFilter) + for tagName, values := range f.Tags { + pbFilter.TagFilters[tagName] = &pb.TagFilter{Values: values} + } + } + + return pbFilter +} -- cgit v1.2.3