package main import ( "flag" "log" "net" "net/http" "os" "os/signal" "syscall" "context" "connectrpc.com/connect" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" "google.golang.org/grpc" pb "northwest.io/nostr-grpc/api/nostr/v1" "northwest.io/nostr-grpc/api/nostr/v1/nostrv1connect" connecthandler "northwest.io/nostr-grpc/internal/handler/connect" grpchandler "northwest.io/nostr-grpc/internal/handler/grpc" wshandler "northwest.io/nostr-grpc/internal/handler/websocket" "northwest.io/nostr-grpc/internal/storage" "northwest.io/nostr-grpc/internal/subscription" ) func main() { var ( grpcAddr = flag.String("grpc-addr", ":50051", "gRPC server address") wsAddr = flag.String("ws-addr", ":8080", "WebSocket 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() subManager := subscription.NewManager() grpcHandler := grpchandler.NewServer(store) grpcHandler.SetSubscriptionManager(subManager) connectHandler := connecthandler.NewHandler(grpcHandler) mux := http.NewServeMux() path, handler := nostrv1connect.NewNostrRelayHandler(connectHandler, connect.WithInterceptors()) mux.Handle(path, handler) wsHandler := wshandler.NewHandler(store, subManager) wsHandler.SetIndexData(*grpcAddr, *wsAddr, *wsAddr) mux.Handle("/", wsHandler) grpcLis, err := net.Listen("tcp", *grpcAddr) if err != nil { log.Fatalf("failed to listen on gRPC port: %v", err) } grpcServer := grpc.NewServer() pb.RegisterNostrRelayServer(grpcServer, grpcHandler) httpServer := &http.Server{ Addr: *wsAddr, Handler: h2c.NewHandler(mux, &http2.Server{}), } log.Printf("gRPC server listening on %s", *grpcAddr) log.Printf("HTTP server listening on %s", *wsAddr) log.Printf(" - Connect (gRPC-Web) at %s/nostr.v1.NostrRelay/*", *wsAddr) log.Printf(" - WebSocket (Nostr) at %s/", *wsAddr) 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() httpServer.Shutdown(context.Background()) }() go func() { if err := grpcServer.Serve(grpcLis); err != nil { log.Fatalf("gRPC server failed: %v", err) } }() if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("WebSocket server failed: %v", err) } }