mirror of
https://github.com/getAlby/lndhub.go.git
synced 2025-12-22 07:04:56 +01:00
resolve merge conflicts
This commit is contained in:
11
.github/workflows/integration_tests.yaml
vendored
11
.github/workflows/integration_tests.yaml
vendored
@@ -25,6 +25,14 @@ jobs:
|
|||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3.11.8
|
||||||
|
ports:
|
||||||
|
- 5672:5672
|
||||||
|
env:
|
||||||
|
RABBITMQ_DEFAULT_USER: "root"
|
||||||
|
RABBITMQ_DEFAULT_PASS: "password"
|
||||||
|
options: --health-cmd "rabbitmqctl node_health_check" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
@@ -34,3 +42,6 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: go test -p 1 -v -cover -coverpkg=./... ./...
|
run: go test -p 1 -v -cover -coverpkg=./... ./...
|
||||||
|
env:
|
||||||
|
RABBITMQ_URI: amqp://root:password@localhost:5672
|
||||||
|
|
||||||
|
|||||||
24
go.mod
24
go.mod
@@ -14,6 +14,7 @@ require (
|
|||||||
github.com/labstack/echo/v4 v4.10.0
|
github.com/labstack/echo/v4 v4.10.0
|
||||||
github.com/labstack/gommon v0.4.0
|
github.com/labstack/gommon v0.4.0
|
||||||
github.com/lightningnetwork/lnd v0.15.5-beta.rc2
|
github.com/lightningnetwork/lnd v0.15.5-beta.rc2
|
||||||
|
github.com/rabbitmq/amqp091-go v1.6.1
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/uptrace/bun v1.1.10
|
github.com/uptrace/bun v1.1.10
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.10
|
github.com/uptrace/bun/dialect/pgdialect v1.1.10
|
||||||
@@ -31,7 +32,7 @@ require (
|
|||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||||
github.com/aead/siphash v1.0.1 // indirect
|
github.com/aead/siphash v1.0.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/btcsuite/btcd/btcutil v1.1.3 // indirect
|
github.com/btcsuite/btcd/btcutil v1.1.3 // indirect
|
||||||
github.com/btcsuite/btcd/btcutil/psbt v1.1.6 // indirect
|
github.com/btcsuite/btcd/btcutil/psbt v1.1.6 // indirect
|
||||||
@@ -54,12 +55,10 @@ require (
|
|||||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
|
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||||
github.com/decred/dcrd/lru v1.1.1 // indirect
|
github.com/decred/dcrd/lru v1.1.1 // indirect
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6 // indirect
|
github.com/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6 // indirect
|
||||||
github.com/fatih/color v1.13.0 // indirect
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
github.com/fergusstrange/embedded-postgres v1.19.0 // indirect
|
github.com/fergusstrange/embedded-postgres v1.19.0 // indirect
|
||||||
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
|
|
||||||
github.com/go-errors/errors v1.4.2 // indirect
|
github.com/go-errors/errors v1.4.2 // indirect
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
@@ -92,17 +91,7 @@ require (
|
|||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/jrick/logrotate v1.0.0 // indirect
|
github.com/jrick/logrotate v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/juju/clock v0.0.0-20220203021603-d9deb868a28a // indirect
|
|
||||||
github.com/juju/collections v0.0.0-20220203020748-febd7cad8a7a // indirect
|
|
||||||
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b // indirect
|
|
||||||
github.com/juju/loggo v1.0.0 // indirect
|
|
||||||
github.com/juju/mgo/v2 v2.0.0-20220111072304-f200228f1090 // indirect
|
|
||||||
github.com/juju/retry v0.0.0-20220204093819-62423bf33287 // indirect
|
|
||||||
github.com/juju/utils/v3 v3.0.0-20220203023959-c3fbc78a33b0 // indirect
|
|
||||||
github.com/juju/version/v2 v2.0.0-20220204124744-fc9915e3d935 // indirect
|
|
||||||
github.com/kkdai/bstream v1.0.0 // indirect
|
github.com/kkdai/bstream v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.15.14 // indirect
|
|
||||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/lib/pq v1.10.7 // indirect
|
github.com/lib/pq v1.10.7 // indirect
|
||||||
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
|
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
|
||||||
@@ -120,12 +109,9 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mholt/archiver/v3 v3.5.1 // indirect
|
|
||||||
github.com/miekg/dns v1.1.50 // indirect
|
github.com/miekg/dns v1.1.50 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
@@ -141,7 +127,6 @@ require (
|
|||||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
@@ -156,18 +141,13 @@ require (
|
|||||||
go.etcd.io/etcd/pkg/v3 v3.5.6 // indirect
|
go.etcd.io/etcd/pkg/v3 v3.5.6 // indirect
|
||||||
go.etcd.io/etcd/raft/v3 v3.5.6 // indirect
|
go.etcd.io/etcd/raft/v3 v3.5.6 // indirect
|
||||||
go.etcd.io/etcd/server/v3 v3.5.6 // indirect
|
go.etcd.io/etcd/server/v3 v3.5.6 // indirect
|
||||||
go.opentelemetry.io/contrib v1.12.0 // indirect
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.37.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.37.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.11.2 // indirect
|
go.opentelemetry.io/otel v1.11.2 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 // indirect
|
||||||
go.opentelemetry.io/otel/internal/metric v0.27.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v0.34.0 // indirect
|
go.opentelemetry.io/otel/metric v0.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.11.2 // indirect
|
go.opentelemetry.io/otel/sdk v1.11.2 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/export/metric v0.28.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/sdk/metric v0.34.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/trace v1.11.2 // indirect
|
go.opentelemetry.io/otel/trace v1.11.2 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
|
|||||||
164
integration_tests/rabbitmq_test.go
Normal file
164
integration_tests/rabbitmq_test.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package integration_tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/getAlby/lndhub.go/common"
|
||||||
|
"github.com/getAlby/lndhub.go/controllers"
|
||||||
|
"github.com/getAlby/lndhub.go/db/models"
|
||||||
|
"github.com/getAlby/lndhub.go/lib"
|
||||||
|
"github.com/getAlby/lndhub.go/lib/responses"
|
||||||
|
"github.com/getAlby/lndhub.go/lib/service"
|
||||||
|
"github.com/getAlby/lndhub.go/lib/tokens"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RabbitMQTestSuite struct {
|
||||||
|
TestSuite
|
||||||
|
mlnd *MockLND
|
||||||
|
externalLnd *MockLND
|
||||||
|
invoiceUpdateSubCancelFn context.CancelFunc
|
||||||
|
userToken string
|
||||||
|
svc *service.LndhubService
|
||||||
|
testQueueName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RabbitMQTestSuite) SetupSuite() {
|
||||||
|
mlnd := newDefaultMockLND()
|
||||||
|
//needs different pubkey
|
||||||
|
//to allow for "external" payments
|
||||||
|
externalLnd, err := NewMockLND("1234567890abcdef1234", 0, make(chan (*lnrpc.Invoice)))
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
svc, err := LndHubTestServiceInit(mlnd)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("could not initialize test service: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.mlnd = mlnd
|
||||||
|
suite.externalLnd = externalLnd
|
||||||
|
suite.testQueueName = "test_invoice"
|
||||||
|
|
||||||
|
_, userTokens, err := createUsers(svc, 1)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating test users: %v", err)
|
||||||
|
}
|
||||||
|
suite.userToken = userTokens[0]
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
suite.invoiceUpdateSubCancelFn = cancel
|
||||||
|
go svc.InvoiceUpdateSubscription(ctx)
|
||||||
|
suite.svc = svc
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
e.HTTPErrorHandler = responses.HTTPErrorHandler
|
||||||
|
e.Validator = &lib.CustomValidator{Validator: validator.New()}
|
||||||
|
|
||||||
|
suite.echo = e
|
||||||
|
suite.echo.Use(tokens.Middleware(suite.svc.Config.JWTSecret))
|
||||||
|
suite.echo.POST("/addinvoice", controllers.NewAddInvoiceController(suite.svc).AddInvoice)
|
||||||
|
suite.echo.POST("/payinvoice", controllers.NewPayInvoiceController(suite.svc).PayInvoice)
|
||||||
|
go func() {
|
||||||
|
err = svc.StartRabbitMqPublisher(ctx)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RabbitMQTestSuite) TestPublishInvoice() {
|
||||||
|
conn, err := amqp.Dial(suite.svc.Config.RabbitMQUri)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
ch, err := conn.Channel()
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
defer ch.Close()
|
||||||
|
|
||||||
|
q, err := ch.QueueDeclare(
|
||||||
|
suite.testQueueName,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
err = ch.QueueBind(q.Name, "#", suite.svc.Config.RabbitMQInvoiceExchange, false, nil)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
invoice := suite.createAddInvoiceReq(1000, "integration test rabbitmq", suite.userToken)
|
||||||
|
err = suite.mlnd.mockPaidInvoice(invoice, 0, false, nil)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
m, err := ch.Consume(
|
||||||
|
q.Name,
|
||||||
|
"invoice.*.*",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
msg := <-m
|
||||||
|
|
||||||
|
var receivedInvoice models.Invoice
|
||||||
|
r := bytes.NewReader(msg.Body)
|
||||||
|
err = json.NewDecoder(r).Decode(&receivedInvoice)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
assert.Equal(suite.T(), invoice.RHash, receivedInvoice.RHash)
|
||||||
|
assert.Equal(suite.T(), common.InvoiceTypeIncoming, receivedInvoice.Type)
|
||||||
|
|
||||||
|
//check if outgoing invoices also get published
|
||||||
|
outgoingInvoiceValue := 500
|
||||||
|
outgoingInvoiceDescription := "test rabbit outgoing invoice"
|
||||||
|
preimage, err := makePreimageHex()
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
outgoingInv, err := suite.externalLnd.AddInvoice(context.Background(), &lnrpc.Invoice{Value: int64(outgoingInvoiceValue), Memo: outgoingInvoiceDescription, RPreimage: preimage})
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
//pay invoice
|
||||||
|
suite.createPayInvoiceReq(&ExpectedPayInvoiceRequestBody{
|
||||||
|
Invoice: outgoingInv.PaymentRequest,
|
||||||
|
}, suite.userToken)
|
||||||
|
msg = <-m
|
||||||
|
|
||||||
|
var receivedPayment models.Invoice
|
||||||
|
r = bytes.NewReader(msg.Body)
|
||||||
|
err = json.NewDecoder(r).Decode(&receivedPayment)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
assert.Equal(suite.T(), common.InvoiceTypeOutgoing, receivedPayment.Type)
|
||||||
|
assert.Equal(suite.T(), int64(outgoingInvoiceValue), receivedPayment.Amount)
|
||||||
|
assert.Equal(suite.T(), outgoingInvoiceDescription, receivedPayment.Memo)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RabbitMQTestSuite) TearDownSuite() {
|
||||||
|
conn, err := amqp.Dial(suite.svc.Config.RabbitMQUri)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
ch, err := conn.Channel()
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
defer ch.Close()
|
||||||
|
|
||||||
|
_, err = ch.QueueDelete(suite.testQueueName, false, false, false)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
err = ch.ExchangeDelete(suite.svc.Config.RabbitMQInvoiceExchange, true, false)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRabbitMQTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(RabbitMQTestSuite))
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/getAlby/lndhub.go/db"
|
"github.com/getAlby/lndhub.go/db"
|
||||||
@@ -56,6 +57,13 @@ func LndHubTestServiceInit(lndClientMock lnd.LightningClientWrapper) (svc *servi
|
|||||||
LNDAddress: mockLNDAddress,
|
LNDAddress: mockLNDAddress,
|
||||||
LNDMacaroonHex: mockLNDMacaroonHex,
|
LNDMacaroonHex: mockLNDMacaroonHex,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rabbitmqUri, ok := os.LookupEnv("RABBITMQ_URI")
|
||||||
|
if ok {
|
||||||
|
c.RabbitMQUri = rabbitmqUri
|
||||||
|
c.RabbitMQInvoiceExchange = "test_lndhub_invoices"
|
||||||
|
}
|
||||||
|
|
||||||
dbConn, err := db.Open(c)
|
dbConn, err := db.Open(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to database: %w", err)
|
return nil, fmt.Errorf("failed to connect to database: %w", err)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (suite *WebHookTestSuite) SetupSuite() {
|
|||||||
suite.invoiceUpdateSubCancelFn = cancel
|
suite.invoiceUpdateSubCancelFn = cancel
|
||||||
go svc.InvoiceUpdateSubscription(ctx)
|
go svc.InvoiceUpdateSubscription(ctx)
|
||||||
|
|
||||||
go svc.StartWebhookSubscribtion(ctx, svc.Config.WebhookUrl)
|
go svc.StartWebhookSubscription(ctx, svc.Config.WebhookUrl)
|
||||||
|
|
||||||
suite.service = svc
|
suite.service = svc
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ type Config struct {
|
|||||||
MaxReceiveAmount int64 `envconfig:"MAX_RECEIVE_AMOUNT" default:"0"`
|
MaxReceiveAmount int64 `envconfig:"MAX_RECEIVE_AMOUNT" default:"0"`
|
||||||
MaxSendAmount int64 `envconfig:"MAX_SEND_AMOUNT" default:"0"`
|
MaxSendAmount int64 `envconfig:"MAX_SEND_AMOUNT" default:"0"`
|
||||||
MaxAccountBalance int64 `envconfig:"MAX_ACCOUNT_BALANCE" default:"0"`
|
MaxAccountBalance int64 `envconfig:"MAX_ACCOUNT_BALANCE" default:"0"`
|
||||||
|
RabbitMQUri string `envconfig:"RABBITMQ_URI"`
|
||||||
|
RabbitMQInvoiceExchange string `envconfig:"RABBITMQ_INVOICE_EXCHANGE" default:"lndhub_invoice"`
|
||||||
Branding BrandingConfig
|
Branding BrandingConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
113
lib/service/rabbitmq.go
Normal file
113
lib/service/rabbitmq.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/getAlby/lndhub.go/db/models"
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufPool sync.Pool = sync.Pool{
|
||||||
|
New: func() interface{} { return new(bytes.Buffer) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *LndhubService) StartRabbitMqPublisher(ctx context.Context) error {
|
||||||
|
// It is recommended that, when possible, publishers and consumers
|
||||||
|
// use separate connections so that consumers are isolated from potential
|
||||||
|
// flow control messures that may be applied to publishing connections.
|
||||||
|
// We therefore start a single publishing connection here instead of storing
|
||||||
|
// one on the service object.
|
||||||
|
conn, err := amqp.Dial(svc.Config.RabbitMQUri)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
ch, err := conn.Channel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ch.Close()
|
||||||
|
|
||||||
|
err = ch.ExchangeDeclare(
|
||||||
|
// For the time being we simply declare a single exchange and start pushing to it.
|
||||||
|
// Towards the future however this might become a more involved setup.
|
||||||
|
svc.Config.RabbitMQInvoiceExchange,
|
||||||
|
// topic is a type of exchange that allows routing messages to different queue's bases on a routing key
|
||||||
|
"topic",
|
||||||
|
// Durable and Non-Auto-Deleted exchanges will survive server restarts and remain
|
||||||
|
// declared when there are no remaining bindings.
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
// Non-Internal exchange's accept direct publishing
|
||||||
|
false,
|
||||||
|
// Nowait: We set this to false as we want to wait for a server response
|
||||||
|
// to check wether the exchange was created succesfully
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.Logger.Infof("Starting rabbitmq publisher")
|
||||||
|
|
||||||
|
incomingInvoices, outgoingInvoices, err := svc.subscribeIncomingOutgoingInvoices()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return context.Canceled
|
||||||
|
case incoming := <-incomingInvoices:
|
||||||
|
err = svc.publishInvoice(ctx, incoming, ch)
|
||||||
|
if err != nil {
|
||||||
|
svc.Logger.Error(err)
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
}
|
||||||
|
case outgoing := <-outgoingInvoices:
|
||||||
|
err = svc.publishInvoice(ctx, outgoing, ch)
|
||||||
|
if err != nil {
|
||||||
|
svc.Logger.Error(err)
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *LndhubService) publishInvoice(ctx context.Context, invoice models.Invoice, ch *amqp.Channel) error {
|
||||||
|
key := fmt.Sprintf("invoice.%s.%s", invoice.Type, invoice.State)
|
||||||
|
|
||||||
|
user, err := svc.FindUser(context.Background(), invoice.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := bufPool.Get().(*bytes.Buffer)
|
||||||
|
err = json.NewEncoder(payload).Encode(convertPayload(invoice, user))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ch.PublishWithContext(ctx,
|
||||||
|
svc.Config.RabbitMQInvoiceExchange,
|
||||||
|
key,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
amqp.Publishing{
|
||||||
|
ContentType: "application/json",
|
||||||
|
Body: payload.Bytes(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
svc.Logger.Debugf("Succesfully published invoice to rabbitmq with RHash %s", invoice.RHash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -12,16 +12,11 @@ import (
|
|||||||
"github.com/getAlby/lndhub.go/db/models"
|
"github.com/getAlby/lndhub.go/db/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (svc *LndhubService) StartWebhookSubscribtion(ctx context.Context, url string) {
|
func (svc *LndhubService) StartWebhookSubscription(ctx context.Context, url string) {
|
||||||
|
|
||||||
svc.Logger.Infof("Starting webhook subscription with webhook url %s", svc.Config.WebhookUrl)
|
svc.Logger.Infof("Starting webhook subscription with webhook url %s", svc.Config.WebhookUrl)
|
||||||
incomingInvoices, _, err := svc.InvoicePubSub.Subscribe(common.InvoiceTypeIncoming)
|
incomingInvoices, outgoingInvoices, err := svc.subscribeIncomingOutgoingInvoices()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
svc.Logger.Error(err.Error())
|
svc.Logger.Error(err)
|
||||||
}
|
|
||||||
outgoingInvoices, _, err := svc.InvoicePubSub.Subscribe(common.InvoiceTypeOutgoing)
|
|
||||||
if err != nil {
|
|
||||||
svc.Logger.Error(err.Error())
|
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -44,27 +39,7 @@ func (svc *LndhubService) postToWebhook(invoice models.Invoice, url string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload := new(bytes.Buffer)
|
payload := new(bytes.Buffer)
|
||||||
err = json.NewEncoder(payload).Encode(WebhookInvoicePayload{
|
err = json.NewEncoder(payload).Encode(convertPayload(invoice, user))
|
||||||
ID: invoice.ID,
|
|
||||||
Type: invoice.Type,
|
|
||||||
UserLogin: user.Login,
|
|
||||||
Amount: invoice.Amount,
|
|
||||||
Fee: invoice.Fee,
|
|
||||||
Memo: invoice.Memo,
|
|
||||||
DescriptionHash: invoice.DescriptionHash,
|
|
||||||
PaymentRequest: invoice.PaymentRequest,
|
|
||||||
DestinationPubkeyHex: invoice.DestinationPubkeyHex,
|
|
||||||
DestinationCustomRecords: invoice.DestinationCustomRecords,
|
|
||||||
RHash: invoice.RHash,
|
|
||||||
Preimage: invoice.Preimage,
|
|
||||||
Keysend: invoice.Keysend,
|
|
||||||
State: invoice.State,
|
|
||||||
ErrorMessage: invoice.ErrorMessage,
|
|
||||||
CreatedAt: invoice.CreatedAt,
|
|
||||||
ExpiresAt: invoice.ExpiresAt.Time,
|
|
||||||
UpdatedAt: invoice.UpdatedAt.Time,
|
|
||||||
SettledAt: invoice.SettledAt.Time,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
svc.Logger.Error(err)
|
svc.Logger.Error(err)
|
||||||
return
|
return
|
||||||
@@ -76,7 +51,7 @@ func (svc *LndhubService) postToWebhook(invoice models.Invoice, url string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
msg, err := ioutil.ReadAll(resp.Body)
|
msg, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
svc.Logger.Error(err)
|
svc.Logger.Error(err)
|
||||||
}
|
}
|
||||||
@@ -105,3 +80,39 @@ type WebhookInvoicePayload struct {
|
|||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
SettledAt time.Time `json:"settled_at"`
|
SettledAt time.Time `json:"settled_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (svc *LndhubService) subscribeIncomingOutgoingInvoices() (incoming, outgoing chan models.Invoice, err error) {
|
||||||
|
incomingInvoices, _, err := svc.InvoicePubSub.Subscribe(common.InvoiceTypeIncoming)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
outgoingInvoices, _, err := svc.InvoicePubSub.Subscribe(common.InvoiceTypeOutgoing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return incomingInvoices, outgoingInvoices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertPayload(invoice models.Invoice, user *models.User) (result WebhookInvoicePayload) {
|
||||||
|
return WebhookInvoicePayload{
|
||||||
|
ID: invoice.ID,
|
||||||
|
Type: invoice.Type,
|
||||||
|
UserLogin: user.Login,
|
||||||
|
Amount: invoice.Amount,
|
||||||
|
Fee: invoice.Fee,
|
||||||
|
Memo: invoice.Memo,
|
||||||
|
DescriptionHash: invoice.DescriptionHash,
|
||||||
|
PaymentRequest: invoice.PaymentRequest,
|
||||||
|
DestinationPubkeyHex: invoice.DestinationPubkeyHex,
|
||||||
|
DestinationCustomRecords: invoice.DestinationCustomRecords,
|
||||||
|
RHash: invoice.RHash,
|
||||||
|
Preimage: invoice.Preimage,
|
||||||
|
Keysend: invoice.Keysend,
|
||||||
|
State: invoice.State,
|
||||||
|
ErrorMessage: invoice.ErrorMessage,
|
||||||
|
CreatedAt: invoice.CreatedAt,
|
||||||
|
ExpiresAt: invoice.ExpiresAt.Time,
|
||||||
|
UpdatedAt: invoice.UpdatedAt.Time,
|
||||||
|
SettledAt: invoice.SettledAt.Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
15
main.go
15
main.go
@@ -202,11 +202,24 @@ func main() {
|
|||||||
if svc.Config.WebhookUrl != "" {
|
if svc.Config.WebhookUrl != "" {
|
||||||
backgroundWg.Add(1)
|
backgroundWg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
svc.StartWebhookSubscribtion(backGroundCtx, svc.Config.WebhookUrl)
|
svc.StartWebhookSubscription(backGroundCtx, svc.Config.WebhookUrl)
|
||||||
svc.Logger.Info("Webhook routine done")
|
svc.Logger.Info("Webhook routine done")
|
||||||
backgroundWg.Done()
|
backgroundWg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
//Start rabbit publisher
|
||||||
|
if svc.Config.RabbitMQUri != "" {
|
||||||
|
backgroundWg.Add(1)
|
||||||
|
go func() {
|
||||||
|
err = svc.StartRabbitMqPublisher(backGroundCtx)
|
||||||
|
if err != nil {
|
||||||
|
svc.Logger.Error(err)
|
||||||
|
sentry.CaptureException(err)
|
||||||
|
}
|
||||||
|
svc.Logger.Info("Rabbit routine done")
|
||||||
|
backgroundWg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
var grpcServer *grpc.Server
|
var grpcServer *grpc.Server
|
||||||
if svc.Config.EnableGRPC {
|
if svc.Config.EnableGRPC {
|
||||||
|
|||||||
Reference in New Issue
Block a user