package main import ( "context" "encoding/json" "flag" "io" "log" "os" "strings" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "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() // Auto-detect TLS: use TLS for port 443 or non-localhost addresses var opts []grpc.DialOption useTLS := strings.HasSuffix(*addr, ":443") || (!strings.HasPrefix(*addr, "localhost") && !strings.HasPrefix(*addr, "127.0.0.1") && !strings.HasPrefix(*addr, ":")) if useTLS { opts = append(opts, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) log.Printf("Connecting with TLS to %s", *addr) } else { opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) log.Printf("Connecting without TLS to %s", *addr) } conn, err := grpc.NewClient(*addr, opts...) if err != nil { log.Fatalf("failed to connect: %v", err) } defer conn.Close() client := pb.NewNostrRelayClient(conn) ctx := context.Background() var event *nostr.Event stat, _ := os.Stdin.Stat() if (stat.Mode() & os.ModeCharDevice) == 0 { data, err := io.ReadAll(os.Stdin) if err != nil { log.Fatalf("failed to read stdin: %v", err) } event = &nostr.Event{} if err := json.Unmarshal(data, event); err != nil { log.Fatalf("failed to parse JSON event: %v", err) } log.Printf("Read event from stdin: %s", event.ID[:16]) } else { 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) } } tags := make([]*pb.Tag, len(event.Tags)) for i, tag := range event.Tags { tags[i] = &pb.Tag{Values: tag} } pbEvent := &pb.Event{ Id: event.ID, Pubkey: event.PubKey, CreatedAt: event.CreatedAt, Kind: int32(event.Kind), Tags: tags, 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{event.PubKey}}, }, }) if err != nil { log.Fatalf("failed to query: %v", err) } log.Printf("Found %d events from author %s", len(queryResp.Events), event.PubKey[:16]) for _, e := range queryResp.Events { log.Printf(" - %s: %s", e.Id[:16], e.Content) } }