Skip to content

Commit

Permalink
Merge pull request #16 from getAlby/task-single-relay
Browse files Browse the repository at this point in the history
chore: stay connected to a default relay
  • Loading branch information
im-adithya authored Feb 16, 2024
2 parents 1a4b131 + 4674569 commit 9f45cdf
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 45 deletions.
16 changes: 13 additions & 3 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func main() {
type config struct {
SentryDSN string `envconfig:"SENTRY_DSN"`
DatadogAgentUrl string `envconfig:"DATADOG_AGENT_URL"`
DefaultRelayURL string `envconfig:"DEFAULT_RELAY_URL"`
Port int `default:"8080"`
}
globalConf := &config{}
Expand All @@ -53,10 +54,19 @@ func main() {
e.Use(ddEcho.Middleware(ddEcho.WithServiceName("http-nostr")))
}

e.GET("/info", nostr.InfoHandler)
e.POST("/nip47", nostr.NIP47Handler)
service, err := nostr.NewNostrService(&nostr.Config{
DefaultRelayURL: globalConf.DefaultRelayURL,
})
if err != nil {
logrus.Fatalf("Failed to initialize NostrService: %v", err)
}

e.GET("/info", service.InfoHandler)
e.POST("/nip47", service.NIP47Handler)
// r.Use(loggingMiddleware)

logrus.Infof("Server starting on port %d", globalConf.Port)
logrus.Fatal(e.Start(fmt.Sprintf(":%d", globalConf.Port)))
if err := e.Start(fmt.Sprintf(":%d", globalConf.Port)); err != nil {
logrus.Fatalf("Server failed to start: %v", err)
}
}
31 changes: 31 additions & 0 deletions internal/nostr/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nostr

import "github.com/nbd-wtf/go-nostr"

const (
NIP_47_INFO_EVENT_KIND = 13194
NIP_47_REQUEST_KIND = 23194
NIP_47_RESPONSE_KIND = 23195
)

type WalletConnectInfo struct {
RelayURL string
WalletPubkey string
Secret string
}

type ErrorResponse struct {
Message string `json:"message"`
}

type InfoRequest struct {
RelayURL string `json:"relayUrl"`
WalletPubkey string `json:"walletPubkey"`
}

type NIP47Request struct {
RelayURL string `json:"relayUrl"`
WalletPubkey string `json:"walletPubkey"`
SignedEvent *nostr.Event `json:"event"`
WebhookURL string `json:"webhookURL"`
}
100 changes: 58 additions & 42 deletions internal/nostr/nostr.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,53 +13,45 @@ import (
"github.com/sirupsen/logrus"
)

const (
NIP_47_INFO_EVENT_KIND = 13194
NIP_47_REQUEST_KIND = 23194
NIP_47_RESPONSE_KIND = 23195
)

type WalletConnectInfo struct {
RelayURL string
WalletPubkey string
Secret string
type Config struct {
DefaultRelayURL string
}

type ErrorResponse struct {
Message string `json:"message"`
type NostrService struct {
config *Config
defaultRelay *nostr.Relay
}

type InfoRequest struct {
RelayURL string `json:"relayUrl"`
WalletPubkey string `json:"walletPubkey"`
}

type NIP47Request struct {
RelayURL string `json:"relayUrl"`
WalletPubkey string `json:"walletPubkey"`
SignedEvent *nostr.Event `json:"event"`
WebhookURL string `json:"webhookURL"`
}
func NewNostrService(config *Config) (*NostrService, error) {
logrus.Info("connecting to the relay...")
relay, err := nostr.RelayConnect(context.Background(), config.DefaultRelayURL)
if err != nil {
return nil, fmt.Errorf("failed to connect to default relay: %w", err)
}

func handleError(w http.ResponseWriter, err error, message string, httpStatusCode int) {
logrus.WithError(err).Error(message)
http.Error(w, message, httpStatusCode)
return &NostrService{
config: config,
defaultRelay: relay,
}, nil
}

func InfoHandler(c echo.Context) error {
func (svc *NostrService) InfoHandler(c echo.Context) error {
var requestData InfoRequest
if err := c.Bind(&requestData); err != nil {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: fmt.Sprintf("error decoding info request: %s", err.Error()),
})
}

logrus.Info("connecting to the relay...")
relay, err := nostr.RelayConnect(c.Request().Context(), requestData.RelayURL)
relay, isCustomRelay, err := svc.getRelayConnection(c.Request().Context(), requestData.RelayURL)
if err != nil {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: fmt.Sprintf("error connecting to relay: %s", err.Error()),
})
if isCustomRelay {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("error connecting to relay: %s", err))
}
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("error connecting to default relay: %s", err))
}
if isCustomRelay {
defer relay.Close()
}

logrus.Info("subscribing to info event...")
Expand Down Expand Up @@ -88,35 +80,35 @@ func InfoHandler(c echo.Context) error {
}
}

func NIP47Handler(c echo.Context) error {
func (svc *NostrService) NIP47Handler(c echo.Context) error {
var requestData NIP47Request
if err := c.Bind(&requestData); err != nil {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: fmt.Sprintf("error decoding nip47 request: %s", err.Error()),
})
}

if (requestData.RelayURL == "" || requestData.WalletPubkey == "") {
if (requestData.WalletPubkey == "") {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "relay url or wallet pubkey is/are empty",
Message: "wallet pubkey is empty",
})
}

if requestData.WebhookURL != "" {
go func() {
event, _, err := processRequest(context.Background(), &requestData)
event, _, err := svc.processRequest(context.Background(), &requestData)
if err != nil {
logrus.WithError(err).Error("failed to process request for webhook")
// what to pass to the webhook?
return
}
postEventToWebhook(event, requestData.WebhookURL)
svc.postEventToWebhook(event, requestData.WebhookURL)
}()
return c.JSON(http.StatusOK, "webhook received")
} else {
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()
event, code, err := processRequest(ctx, &requestData)
event, code, err := svc.processRequest(ctx, &requestData)
if err != nil {
return c.JSON(code, ErrorResponse{
Message: err.Error(),
Expand All @@ -126,11 +118,32 @@ func NIP47Handler(c echo.Context) error {
}
}

func processRequest(ctx context.Context, requestData *NIP47Request) (*nostr.Event, int, error) {
relay, err := nostr.RelayConnect(ctx, requestData.RelayURL)
func (svc *NostrService) getRelayConnection(ctx context.Context, customRelayURL string) (*nostr.Relay, bool, error) {
if customRelayURL != "" && customRelayURL != svc.config.DefaultRelayURL {
logrus.WithFields(logrus.Fields{
"customRelayURL": customRelayURL,
}).Infof("connecting to custom relay")
relay, err := nostr.RelayConnect(ctx, customRelayURL)
return relay, true, err // true means custom and the relay should be closed
}
// check if the default relay is active
if svc.defaultRelay.IsConnected() {
return svc.defaultRelay, false, nil
} else {
logrus.Info("lost connection to default relay, reconnecting...")
relay, err := nostr.RelayConnect(context.Background(), svc.config.DefaultRelayURL)
return relay, false, err
}
}

func (svc *NostrService) processRequest(ctx context.Context, requestData *NIP47Request) (*nostr.Event, int, error) {
relay, isCustomRelay, err := svc.getRelayConnection(ctx, requestData.RelayURL)
if err != nil {
return &nostr.Event{}, http.StatusBadRequest, fmt.Errorf("error connecting to relay: %w", err)
}
if isCustomRelay {
defer relay.Close()
}

logrus.WithFields(logrus.Fields{
"e": requestData.SignedEvent.ID,
Expand Down Expand Up @@ -175,12 +188,15 @@ func processRequest(ctx context.Context, requestData *NIP47Request) (*nostr.Even
case <-ctx.Done():
return &nostr.Event{}, http.StatusRequestTimeout, fmt.Errorf("request canceled or timed out")
case event := <-sub.Events:
logrus.Infof("successfully received event: %s", event.ID)
logrus.WithFields(logrus.Fields{
"eventId": event.ID,
"eventKind": event.Kind,
}).Infof("successfully received event")
return event, http.StatusOK, nil
}
}

func postEventToWebhook(event *nostr.Event, webhookURL string) {
func (svc *NostrService) postEventToWebhook(event *nostr.Event, webhookURL string) {
eventData, err := json.Marshal(event)
if err != nil {
logrus.WithError(err).Error("failed to marshal event for webhook")
Expand Down

0 comments on commit 9f45cdf

Please sign in to comment.