From e3b336ab45a6acf8a02a8c0f1b6d22fb3a320826 Mon Sep 17 00:00:00 2001 From: bndw Date: Fri, 13 Feb 2026 17:50:09 -0800 Subject: feat: add relay server and test client Server (cmd/relay): - gRPC server on :50051 (configurable) - SQLite database (default: relay.db) - Graceful shutdown on SIGTERM/SIGINT Test client (cmd/testclient): - Generates key - Publishes event - Queries events back Build: - make build (relay) - make build-client (test client) - make build-all (both) --- cmd/relay/main.go | 56 +++++++++++++++++++++++++++++++++ cmd/testclient/main.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 cmd/relay/main.go create mode 100644 cmd/testclient/main.go (limited to 'cmd') diff --git a/cmd/relay/main.go b/cmd/relay/main.go new file mode 100644 index 0000000..3db466d --- /dev/null +++ b/cmd/relay/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "flag" + "log" + "net" + "os" + "os/signal" + "syscall" + + "google.golang.org/grpc" + + pb "northwest.io/nostr-grpc/api/nostr/v1" + grpchandler "northwest.io/nostr-grpc/internal/handler/grpc" + "northwest.io/nostr-grpc/internal/storage" +) + +func main() { + var ( + grpcAddr = flag.String("grpc-addr", ":50051", "gRPC server address") + dbPath = flag.String("db", "relay.db", "SQLite database path") + ) + flag.Parse() + + store, err := storage.New(*dbPath) + if err != nil { + log.Fatalf("failed to create storage: %v", err) + } + defer store.Close() + + handler := grpchandler.NewServer(store) + + lis, err := net.Listen("tcp", *grpcAddr) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + + grpcServer := grpc.NewServer() + pb.RegisterNostrRelayServer(grpcServer, handler) + + log.Printf("gRPC server listening on %s", *grpcAddr) + log.Printf("Database: %s", *dbPath) + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + + go func() { + <-sigChan + log.Println("Shutting down...") + grpcServer.GracefulStop() + }() + + if err := grpcServer.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} diff --git a/cmd/testclient/main.go b/cmd/testclient/main.go new file mode 100644 index 0000000..571751e --- /dev/null +++ b/cmd/testclient/main.go @@ -0,0 +1,85 @@ +package main + +import ( + "context" + "flag" + "log" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + pb "northwest.io/nostr-grpc/api/nostr/v1" + "northwest.io/nostr-grpc/internal/nostr" +) + +func main() { + addr := flag.String("addr", "localhost:50051", "relay address") + flag.Parse() + + conn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("failed to connect: %v", err) + } + defer conn.Close() + + client := pb.NewNostrRelayClient(conn) + ctx := context.Background() + + key, err := nostr.GenerateKey() + if err != nil { + log.Fatalf("failed to generate key: %v", err) + } + + log.Printf("Generated key: %s", key.Npub()) + + event := &nostr.Event{ + PubKey: key.Public(), + CreatedAt: time.Now().Unix(), + Kind: 1, + Tags: nostr.Tags{}, + Content: "Hello from gRPC client!", + } + + if err := key.Sign(event); err != nil { + log.Fatalf("failed to sign event: %v", err) + } + + pbEvent := &pb.Event{ + Id: event.ID, + Pubkey: event.PubKey, + CreatedAt: event.CreatedAt, + Kind: int32(event.Kind), + Tags: []*pb.Tag{}, + Content: event.Content, + Sig: event.Sig, + } + + log.Println("Publishing event...") + resp, err := client.PublishEvent(ctx, &pb.PublishEventRequest{Event: pbEvent}) + if err != nil { + log.Fatalf("failed to publish: %v", err) + } + + if resp.Accepted { + log.Printf("✓ Event published successfully: %s", event.ID) + } else { + log.Printf("✗ Event rejected: %s", resp.Message) + return + } + + log.Println("Querying events...") + queryResp, err := client.QueryEvents(ctx, &pb.QueryRequest{ + Filters: []*pb.Filter{ + {Authors: []string{key.Public()}}, + }, + }) + if err != nil { + log.Fatalf("failed to query: %v", err) + } + + log.Printf("Found %d events", len(queryResp.Events)) + for _, e := range queryResp.Events { + log.Printf(" - %s: %s", e.Id[:16], e.Content) + } +} -- cgit v1.2.3