diff --git a/.do/deploy.template.yaml b/.do/deploy.template.yaml index 53f03299..f27e6605 100644 --- a/.do/deploy.template.yaml +++ b/.do/deploy.template.yaml @@ -5,10 +5,10 @@ spec: image: registry_type: GHCR registry: getalby - repository: nostr-wallet-connect-next + repository: hub instance_size_slug: basic-xxs envs: - - key: LDK_ESPLORA_SERVER - value: "https://electrs.albylabs.com" - - key: LOG_EVENTS - value: "true" + - key: LDK_ESPLORA_SERVER + value: "https://electrs.getalby.com" + - key: LOG_EVENTS + value: "true" diff --git a/.github/workflows/http.yml b/.github/workflows/http.yml index f7831940..947e2c6e 100644 --- a/.github/workflows/http.yml +++ b/.github/workflows/http.yml @@ -94,7 +94,7 @@ jobs: env: CGO_ENABLED: 1 TAG: ${{ github.ref_name }} - run: go build ${{ env.GOTAGS }} -o build/bin/${{ env.PACKAGE_NAME }}/bin/${{ env.EXEC_NAME }} -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" cmd/http/main.go + run: go build ${{ env.GOTAGS }} -o build/bin/${{ env.PACKAGE_NAME }}/bin/${{ env.EXEC_NAME }} -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" cmd/http/main.go - name: Copy shared libraries to the output directory run: | diff --git a/.github/workflows/wails.yml b/.github/workflows/wails.yml index 3b89a147..e19c6967 100644 --- a/.github/workflows/wails.yml +++ b/.github/workflows/wails.yml @@ -54,7 +54,7 @@ jobs: - name: Setup GoLang uses: actions/setup-go@v5 with: - go-version-file: './go.mod' + go-version-file: "./go.mod" - name: Get dependencies run: go get -v -t -d ./... @@ -65,7 +65,7 @@ jobs: node-version: "20.x" - name: Install Wails - run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.8.2 + run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.9.1 shell: bash - name: Install Linux Wails deps @@ -101,17 +101,17 @@ jobs: - name: Build App if: runner.os == 'macOS' - run: wails build --platform darwin/universal -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" + run: wails build --platform darwin/universal -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" shell: bash - name: Build App if: runner.os == 'Linux' - run: wails build --platform linux/amd64 -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" + run: wails build --platform linux/amd64 -webview2 embed -o ${{ env.EXEC_NAME }} -tags "wails" -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" shell: bash - name: Build Windows App if: runner.os == 'Windows' - run: wails build --platform windows/amd64 -webview2 embed -o ${{ env.EXEC_NAME }}.exe -tags "wails" -ldflags "-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=${{ env.TAG }}'" + run: wails build --platform windows/amd64 -webview2 embed -o ${{ env.EXEC_NAME }}.exe -tags "wails" -ldflags "-X 'github.com/getAlby/hub/version.Tag=${{ env.TAG }}'" shell: bash - name: Import Code-Signing Certificates for macOS diff --git a/Dockerfile b/Dockerfile index f72de98a..386c383f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ COPY . . COPY --from=frontend /build/frontend/dist ./frontend/dist RUN GOARCH=$(echo "$TARGETPLATFORM" | cut -d'/' -f2) go build \ - -ldflags="-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=$TAG'" \ + -ldflags="-X 'github.com/getAlby/hub/version.Tag=$TAG'" \ -o main cmd/http/main.go COPY ./build/docker/copy_dylibs.sh . diff --git a/README.md b/README.md index 23563a16..5554dfe0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Nostr Wallet Connect (Next) +# Alby Hub -This is a self-sovereign, self-custodial, single-user rewrite of NWC currently in an experimental phase development. **❗This version is not backwards compatible with NWC - it requires a fresh database and connections to be re-added** +This is a self-sovereign, self-custodial, single-user rewrite of the original [Nostr Wallet Connect](https://github.com/getAlby/nostr-wallet-connect) app. **❗This version is not backwards compatible with the original app - it requires a fresh database and connections to be re-added** This application allows you to control your Lightning node or wallet from any other application that supports [NWC](https://nwc.dev/). Connect apps like [Damus](https://damus.io/) or [Amethyst](https://linktr.ee/amethyst.social) to your node. There are many more available on https://nwc.dev/. @@ -16,10 +16,12 @@ Ideally the app runs 24/7 (on a node, VPS or always-online desktop/laptop machin ## Supported Backends -- LND (see: lnd.go) -- Breez (see: breez.go) -- Greenlight (see: greenlight.go) -- LDK (see: ldk.go) +- LND +- Breez +- Greenlight +- LDK +- Pheonixd +- Cashu - want more? please open an issue. ## Installation @@ -123,7 +125,7 @@ For more information refer to: ### Versioning - $ go run -ldflags="-X 'github.com/getAlby/nostr-wallet-connect/version.Tag=v0.6.0'" cmd/http/main.go + $ go run -ldflags="-X 'github.com/getAlby/hub/version.Tag=v0.6.0'" cmd/http/main.go ### Windows @@ -135,9 +137,9 @@ Breez SDK requires gcc to build the Breez bindings. Run `choco install mingw` an - `CLIENT_NOSTR_PUBKEY`: if set, this service will only listen to events authored by this public key. You can set this to your own nostr public key. - `RELAY`: default: "wss://relay.getalby.com/v1" - `COOKIE_SECRET`: a randomly generated secret string. (only needed in http mode) -- `DATABASE_URI`: a sqlite filename. Default: $XDG_DATA_HOME/nostr-wallet-connect/nwc.db +- `DATABASE_URI`: a sqlite filename. Default: $XDG_DATA_HOME/albyhub/nwc.db - `PORT`: the port on which the app should listen on (default: 8080) -- `WORK_DIR`: directory to store NWC data files. Default: $XDG_DATA_HOME/nostr-wallet-connect +- `WORK_DIR`: directory to store NWC data files. Default: $XDG_DATA_HOME/albyhub - `LOG_LEVEL`: log level for the application. Higher is more verbose. Default: 4 (info) ### LND Backend parameters @@ -179,9 +181,9 @@ Create an OAuth client at the [Alby Developer Portal](https://getalby.com/develo Follow the steps to integrate Mutinynet with your NWC Next setup: -1. Configure your environment with the [Mutinynet LDK parameters](https://github.com/getAlby/nostr-wallet-connect-next#mutinynet) +1. Configure your environment with the [Mutinynet LDK parameters](https://github.com/getAlby/hub#mutinynet) -2. Proceed as described in the [Development](https://github.com/getAlby/nostr-wallet-connect-next#Development) section to run the frontend and backend +2. Proceed as described in the [Development](https://github.com/getAlby/hub#Development) section to run the frontend and backend 3. During onboarding, after setting your password and authorizing via Alby OAuth, you'll be directed to `/onboarding/lightning/migrate-alby`. Click "Skip For Now" to access your wallet interface @@ -332,11 +334,11 @@ Run NWC on your own node! ### Digital Ocean -[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/getAlby/nostr-wallet-connect-next/tree/master) +[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/getAlby/hub/tree/master) ### Render -[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/getAlby/nostr-wallet-connect-next) +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/getAlby/hub) ### Fly @@ -378,10 +380,16 @@ LDK logs: _Tested on Linux only_ -`docker run -v ~/.local/share/alby-nwc:/data -e WORK_DIR='/data' -p 8080:8080 ghcr.io/getalby/nostr-wallet-connect-next:latest` +`docker run -v ~/.local/share/albyhub:/data -e WORK_DIR='/data' -p 8080:8080 ghcr.io/getalby/hub:latest` #### From Source _Tested on Linux only_ -`docker run -v ~/.local/share/alby-nwc:/data -e WORK_DIR='/data' -p 8080:8080 $(docker build -q .)` +##### Docker Compose + +`docker compose up` + +##### Manually + +`docker run -v ~/.local/share/albyhub:/data -e WORK_DIR='/data' -p 8080:8080 $(docker build -q .)` diff --git a/alby/alby_oauth_service.go b/alby/alby_oauth_service.go index dcfd9b89..a2eb32ba 100644 --- a/alby/alby_oauth_service.go +++ b/alby/alby_oauth_service.go @@ -16,13 +16,13 @@ import ( "golang.org/x/oauth2" "gorm.io/gorm" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - nip47 "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/service/keys" ) type albyOAuthService struct { @@ -258,15 +258,18 @@ func (svc *albyOAuthService) DrainSharedWallet(ctx context.Context, lnClient lnc return err } - amount := int64(math.Floor( - float64(balance.Balance)*1000* // Alby shared node balance in sats - (1-8/1000)* // Alby service fee (0.8%) - 0.99)) - // Maximum potential routing fees (1%) - 10000 // Alby fee reserve (10 sats) + balanceSat := float64(balance.Balance) - if amount < 1000 { + amountSat := int64(math.Floor( + balanceSat- // Alby shared node balance in sats + (balanceSat*(8.0/1000.0))- // Alby service fee (0.8%) + (balanceSat*0.01))) - // Maximum potential routing fees (1%) + 10 // Alby fee reserve (10 sats) + + if amountSat < 1 { return errors.New("Not enough balance remaining") } + amount := amountSat * 1000 logger.Logger.WithField("amount", amount).WithError(err).Error("Draining Alby shared wallet funds") @@ -376,20 +379,30 @@ func (svc *albyOAuthService) GetAuthUrl() string { return svc.oauthConf.AuthCodeURL("unused") } -func (svc *albyOAuthService) LinkAccount(ctx context.Context, lnClient lnclient.LNClient) error { +func (svc *albyOAuthService) LinkAccount(ctx context.Context, lnClient lnclient.LNClient, budget uint64, renewal string) error { connectionPubkey, err := svc.createAlbyAccountNWCNode(ctx) if err != nil { logger.Logger.WithError(err).Error("Failed to create alby account nwc node") return err } + scopes, err := permissions.RequestMethodsToScopes(lnClient.GetSupportedNIP47Methods()) + if err != nil { + logger.Logger.WithError(err).Error("Failed to get scopes from LNClient request methods") + return err + } + notificationTypes := lnClient.GetSupportedNIP47NotificationTypes() + if len(notificationTypes) > 0 { + scopes = append(scopes, permissions.NOTIFICATIONS_SCOPE) + } + app, _, err := db.NewDBService(svc.db, svc.eventPublisher).CreateApp( "getalby.com", connectionPubkey, - 1_000_000, - nip47.BUDGET_RENEWAL_MONTHLY, + budget, + renewal, nil, - lnClient.GetSupportedNIP47Methods(), + scopes, ) if err != nil { diff --git a/alby/models.go b/alby/models.go index 62073afc..c773e7d7 100644 --- a/alby/models.go +++ b/alby/models.go @@ -3,8 +3,8 @@ package alby import ( "context" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" ) type AlbyOAuthService interface { @@ -13,7 +13,7 @@ type AlbyOAuthService interface { GetAuthUrl() string GetUserIdentifier() (string, error) IsConnected(ctx context.Context) bool - LinkAccount(ctx context.Context, lnClient lnclient.LNClient) error + LinkAccount(ctx context.Context, lnClient lnclient.LNClient, budget uint64, renewal string) error CallbackHandler(ctx context.Context, code string) error GetBalance(ctx context.Context) (*AlbyBalance, error) GetMe(ctx context.Context) (*AlbyMe, error) @@ -29,15 +29,24 @@ type AlbyPayRequest struct { Invoice string `json:"invoice"` } +type AlbyLinkAccountRequest struct { + Budget uint64 `json:"budget"` + Renewal string `json:"renewal"` +} + +type AlbyMeHub struct { + LatestVersion string `json:"latest_version"` +} type AlbyMe struct { - Identifier string `json:"identifier"` - NPub string `json:"nostr_pubkey"` - LightningAddress string `json:"lightning_address"` - Email string `json:"email"` - Name string `json:"name"` - Avatar string `json:"avatar"` - KeysendPubkey string `json:"keysend_pubkey"` - SharedNode bool `json:"shared_node"` + Identifier string `json:"identifier"` + NPub string `json:"nostr_pubkey"` + LightningAddress string `json:"lightning_address"` + Email string `json:"email"` + Name string `json:"name"` + Avatar string `json:"avatar"` + KeysendPubkey string `json:"keysend_pubkey"` + SharedNode bool `json:"shared_node"` + Hub AlbyMeHub `json:"hub"` } type AlbyBalance struct { @@ -52,6 +61,7 @@ type ChannelPeerSuggestion struct { Pubkey string `json:"pubkey"` Host string `json:"host"` MinimumChannelSize uint64 `json:"minimumChannelSize"` + MaximumChannelSize uint64 `json:"maximumChannelSize"` Name string `json:"name"` Image string `json:"image"` BrokenLspUrl string `json:"lsp_url"` diff --git a/api/api.go b/api/api.go index d96473d8..38443aa1 100644 --- a/api/api.go +++ b/api/api.go @@ -15,17 +15,17 @@ import ( "github.com/sirupsen/logrus" "gorm.io/gorm" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - permissions "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/service" - "github.com/getAlby/nostr-wallet-connect/service/keys" - "github.com/getAlby/nostr-wallet-connect/utils" - "github.com/getAlby/nostr-wallet-connect/version" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + permissions "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/service" + "github.com/getAlby/hub/service/keys" + "github.com/getAlby/hub/utils" + "github.com/getAlby/hub/version" ) type api struct { @@ -487,12 +487,11 @@ func (api *api) GetBalances(ctx context.Context) (*BalancesResponse, error) { return balances, nil } -// TODO: accept offset, limit params for pagination -func (api *api) ListTransactions(ctx context.Context) (*ListTransactionsResponse, error) { +func (api *api) ListTransactions(ctx context.Context, limit uint64, offset uint64) (*ListTransactionsResponse, error) { if api.svc.GetLNClient() == nil { return nil, errors.New("LNClient not started") } - transactions, err := api.svc.GetLNClient().ListTransactions(ctx, 0, 0, 20, 0, false, "") + transactions, err := api.svc.GetLNClient().ListTransactions(ctx, 0, 0, limit, offset, false, "") if err != nil { return nil, err } @@ -581,7 +580,6 @@ func (api *api) GetInfo(ctx context.Context) (*InfoResponse, error) { info.AlbyAuthUrl = api.albyOAuthSvc.GetAuthUrl() info.OAuthRedirect = !api.cfg.GetEnv().IsDefaultClientId() info.Version = version.Tag - info.LatestVersion = version.GetLatestReleaseTag() albyUserIdentifier, err := api.albyOAuthSvc.GetUserIdentifier() if err != nil { logger.Logger.WithError(err).Error("Failed to get alby user identifier") diff --git a/api/backup.go b/api/backup.go index f264a2cf..d38f9023 100644 --- a/api/backup.go +++ b/api/backup.go @@ -16,9 +16,9 @@ import ( "crypto/rand" "crypto/sha256" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/utils" "golang.org/x/crypto/pbkdf2" ) diff --git a/api/esplora.go b/api/esplora.go index 8dd0e31a..d7c4af8d 100644 --- a/api/esplora.go +++ b/api/esplora.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" ) diff --git a/api/lsp.go b/api/lsp.go index cf85d5ae..2d3f0e2e 100644 --- a/api/lsp.go +++ b/api/lsp.go @@ -13,10 +13,10 @@ import ( "strings" "time" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/lsp" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/lsp" + "github.com/getAlby/hub/utils" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" ) diff --git a/api/models.go b/api/models.go index 1f82c075..baa5be0d 100644 --- a/api/models.go +++ b/api/models.go @@ -5,9 +5,9 @@ import ( "io" "time" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/lnclient" ) type API interface { @@ -34,7 +34,7 @@ type API interface { SignMessage(ctx context.Context, message string) (*SignMessageResponse, error) RedeemOnchainFunds(ctx context.Context, toAddress string) (*RedeemOnchainFundsResponse, error) GetBalances(ctx context.Context) (*BalancesResponse, error) - ListTransactions(ctx context.Context) (*ListTransactionsResponse, error) + ListTransactions(ctx context.Context, limit uint64, offset uint64) (*ListTransactionsResponse, error) SendPayment(ctx context.Context, invoice string) (*SendPaymentResponse, error) CreateInvoice(ctx context.Context, amount int64, description string) (*MakeInvoiceResponse, error) LookupInvoice(ctx context.Context, paymentHash string) (*LookupInvoiceResponse, error) @@ -154,7 +154,6 @@ type InfoResponse struct { AlbyUserIdentifier string `json:"albyUserIdentifier"` AlbyAccountConnected bool `json:"albyAccountConnected"` Version string `json:"version"` - LatestVersion string `json:"latestVersion"` Network string `json:"network"` } diff --git a/cmd/http/main.go b/cmd/http/main.go index a953049e..929dec5d 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -10,9 +10,9 @@ import ( "time" echologrus "github.com/davrux/echo-logrus/v4" - "github.com/getAlby/nostr-wallet-connect/http" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" + "github.com/getAlby/hub/http" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" "github.com/labstack/echo/v4" log "github.com/sirupsen/logrus" ) diff --git a/config/config.go b/config/config.go index 0fad5cd4..54f956fd 100644 --- a/config/config.go +++ b/config/config.go @@ -7,8 +7,8 @@ import ( "fmt" "os" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/logger" "gorm.io/gorm" "gorm.io/gorm/clause" ) diff --git a/config/models.go b/config/models.go index a8a704c3..b20cb9a5 100644 --- a/config/models.go +++ b/config/models.go @@ -25,7 +25,7 @@ type AppConfig struct { CookieSecret string `envconfig:"COOKIE_SECRET"` LogLevel string `envconfig:"LOG_LEVEL"` LDKNetwork string `envconfig:"LDK_NETWORK" default:"bitcoin"` - LDKEsploraServer string `envconfig:"LDK_ESPLORA_SERVER" default:"https://electrs.albylabs.com"` // TODO: remove LDK prefix + LDKEsploraServer string `envconfig:"LDK_ESPLORA_SERVER" default:"https://electrs.getalby.com"` // TODO: remove LDK prefix LDKGossipSource string `envconfig:"LDK_GOSSIP_SOURCE"` LDKLogLevel string `envconfig:"LDK_LOG_LEVEL"` MempoolApi string `envconfig:"MEMPOOL_API" default:"https://mempool.space/api"` diff --git a/db/db.go b/db/db.go index d6cbbbb3..acc908ed 100644 --- a/db/db.go +++ b/db/db.go @@ -3,8 +3,8 @@ package db import ( "fmt" - "github.com/getAlby/nostr-wallet-connect/db/migrations" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/db/migrations" + "github.com/getAlby/hub/logger" "github.com/glebarez/sqlite" "gorm.io/gorm" ) diff --git a/db/db_service.go b/db/db_service.go index 8f6b159f..5779b603 100644 --- a/db/db_service.go +++ b/db/db_service.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" "github.com/nbd-wtf/go-nostr" "gorm.io/gorm" ) diff --git a/docker-compose.yml b/docker-compose.yml index ad8d54b2..8c5ec501 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: albyhub: platform: linux/amd64 container_name: albyhub - image: ghcr.io/getalby/nostr-wallet-connect-next:latest + image: ghcr.io/getalby/hub:latest volumes: - ./albyhub-data:/data ports: diff --git a/events/events.go b/events/events.go index ac7ff3c7..48d768a0 100644 --- a/events/events.go +++ b/events/events.go @@ -5,7 +5,7 @@ import ( "slices" "sync" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" ) diff --git a/fly.toml b/fly.toml index 48a7d18e..59d73ca3 100644 --- a/fly.toml +++ b/fly.toml @@ -7,7 +7,7 @@ app = 'nwc' primary_region = 'lax' [build] - image = 'ghcr.io/getalby/nostr-wallet-connect-next:latest' + image = 'ghcr.io/getalby/hub:latest' [env] DATABASE_URI = '/data/nwc.db' diff --git a/frontend/package.json b/frontend/package.json index bb407857..2354ae04 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -57,6 +57,7 @@ "swr": "^2.2.4", "tailwind-merge": "^2.2.2", "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.1", "zustand": "^4.5.0" }, "devDependencies": { diff --git a/frontend/public/images/illustrations/link-account.png b/frontend/public/images/illustrations/link-account.png new file mode 100644 index 00000000..881147a1 Binary files /dev/null and b/frontend/public/images/illustrations/link-account.png differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d1b03f9d..cd8eed83 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -13,7 +13,11 @@ function App() { return ( <> - + diff --git a/frontend/src/assets/suggested-apps/bitcoin-connect.png b/frontend/src/assets/suggested-apps/bitcoin-connect.png deleted file mode 100644 index 81859503..00000000 Binary files a/frontend/src/assets/suggested-apps/bitcoin-connect.png and /dev/null differ diff --git a/frontend/src/components/AppHeader.tsx b/frontend/src/components/AppHeader.tsx index a4e59af4..8bcb3e53 100644 --- a/frontend/src/components/AppHeader.tsx +++ b/frontend/src/components/AppHeader.tsx @@ -5,12 +5,18 @@ type Props = { title: string | ReactElement; description: string | ReactElement; contentRight?: React.ReactNode; + breadcrumb?: boolean; }; -function AppHeader({ title, description, contentRight }: Props) { +function AppHeader({ + title, + description, + contentRight, + breadcrumb = true, +}: Props) { return ( <> - + {breadcrumb && }

{title}

diff --git a/frontend/src/components/Breadcrumbs.tsx b/frontend/src/components/Breadcrumbs.tsx index ed0085df..1ab01725 100644 --- a/frontend/src/components/Breadcrumbs.tsx +++ b/frontend/src/components/Breadcrumbs.tsx @@ -38,8 +38,8 @@ function Breadcrumbs() { // Remove the last item if it's an index route to prevent e.g. Wallet > Wallet const filteredCrumbs = isIndexRoute ? crumbs.slice(0, -1) : crumbs; - // Don't render anything if there is only one item - if (filteredCrumbs.length < 2) { + // Skip rendering for breadcrumbs consisting of 2 (or less) items + if (filteredCrumbs.length < 3) { return null; } diff --git a/frontend/src/components/BudgetAmountSelect.tsx b/frontend/src/components/BudgetAmountSelect.tsx new file mode 100644 index 00000000..019c1cc1 --- /dev/null +++ b/frontend/src/components/BudgetAmountSelect.tsx @@ -0,0 +1,96 @@ +import React from "react"; +import { Input } from "src/components/ui/input"; +import { Label } from "src/components/ui/label"; +import { cn } from "src/lib/utils"; +import { budgetOptions } from "src/types"; + +function BudgetAmountSelect({ + value, + onChange, +}: { + value: number; + onChange: (value: number) => void; +}) { + const [customBudget, setCustomBudget] = React.useState( + value ? !Object.values(budgetOptions).includes(value) : false + ); + return ( + <> +
+ {Object.keys(budgetOptions).map((budget) => { + return ( + // replace with something else and then remove dark prefixes +
{ + setCustomBudget(false); + onChange(budgetOptions[budget]); + }} + className={cn( + "cursor-pointer rounded text-nowrap border-2 text-center p-4 dark:text-white", + !customBudget && + (Number.isNaN(value) ? 100000 : value) == + budgetOptions[budget] + ? "border-primary" + : "border-muted" + )} + > + {`${budget} ${budgetOptions[budget] ? " sats" : ""}`} +
+ ); + })} +
{ + setCustomBudget(true); + onChange(0); + }} + className={cn( + "cursor-pointer rounded border-2 text-center p-4 dark:text-white", + customBudget ? "border-primary" : "border-muted" + )} + > + Custom... +
+
+ {customBudget && ( +
+ + { + onChange(parseInt(e.target.value)); + }} + /> +
+ )} + {/* + + + + + + + + + + +
Budget Allowance: + {value ? new Intl.NumberFormat().format(value) : "∞"} + {" sats "} + ({new Intl.NumberFormat().format(budgetUsage || 0)} sats + used) +
Renews: + {permissions.budgetRenewal || "Never"} +
*/} + + ); +} + +export default BudgetAmountSelect; diff --git a/frontend/src/components/BudgetRenewalSelect.tsx b/frontend/src/components/BudgetRenewalSelect.tsx new file mode 100644 index 00000000..33c5173b --- /dev/null +++ b/frontend/src/components/BudgetRenewalSelect.tsx @@ -0,0 +1,47 @@ +import { XIcon } from "lucide-react"; +import React from "react"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "src/components/ui/select"; +import { BudgetRenewalType, validBudgetRenewals } from "src/types"; + +interface BudgetRenewalProps { + value: BudgetRenewalType; + onChange: (value: BudgetRenewalType) => void; + disabled?: boolean; +} + +const BudgetRenewalSelect: React.FC = ({ + value, + onChange, + disabled, +}) => { + return ( + + ); +}; + +export default BudgetRenewalSelect; diff --git a/frontend/src/components/MnemonicInputs.tsx b/frontend/src/components/MnemonicInputs.tsx index bab01c8e..0d0d39a0 100644 --- a/frontend/src/components/MnemonicInputs.tsx +++ b/frontend/src/components/MnemonicInputs.tsx @@ -40,7 +40,7 @@ export default function MnemonicInputs({ Recovery phrase to your wallet -
+
{words.map((word, i) => { const isRevealed = revealedIndex === i; const inputId = `mnemonic-word-${i}`; diff --git a/frontend/src/components/Permissions.tsx b/frontend/src/components/Permissions.tsx index 084e0d74..8557201b 100644 --- a/frontend/src/components/Permissions.tsx +++ b/frontend/src/components/Permissions.tsx @@ -1,23 +1,16 @@ import { format } from "date-fns"; -import { CalendarIcon, PlusCircle, XIcon } from "lucide-react"; +import { CalendarIcon, PlusCircle } from "lucide-react"; import React, { useState } from "react"; +import BudgetAmountSelect from "src/components/BudgetAmountSelect"; +import BudgetRenewalSelect from "src/components/BudgetRenewalSelect"; import Scopes from "src/components/Scopes"; import { Button } from "src/components/ui/button"; import { Calendar } from "src/components/ui/calendar"; -import { Input } from "src/components/ui/input"; -import { Label } from "src/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger, } from "src/components/ui/popover"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "src/components/ui/select"; import { cn } from "src/lib/utils"; import { AppPermissions, @@ -25,11 +18,9 @@ import { NIP_47_PAY_INVOICE_METHOD, Scope, WalletCapabilities, - budgetOptions, expiryOptions, iconMap, scopeDescriptions, - validBudgetRenewals, } from "src/types"; const getTimeZoneDirection = () => { @@ -82,11 +73,11 @@ const Permissions: React.FC = ({ const [budgetOption, setBudgetOption] = useState( isNewConnection ? !!permissions.maxAmount : true ); - const [customBudget, setCustomBudget] = useState( - permissions.maxAmount - ? !Object.values(budgetOptions).includes(permissions.maxAmount) - : false - ); + // const [customBudget, setCustomBudget] = useState( + // permissions.maxAmount + // ? !Object.values(budgetOptions).includes(permissions.maxAmount) + // : false + // ); const [expireOption, setExpireOption] = useState( isNewConnection ? !!permissions.expiresAt : true ); @@ -102,11 +93,11 @@ const Permissions: React.FC = ({ React.useEffect(() => { setPermissions(initialPermissions); setExpiryDays(daysFromNow(initialPermissions.expiresAt)); - setCustomBudget( - initialPermissions.maxAmount - ? !Object.values(budgetOptions).includes(initialPermissions.maxAmount) - : false - ); + // setCustomBudget( + // initialPermissions.maxAmount + // ? !Object.values(budgetOptions).includes(initialPermissions.maxAmount) + // : false + // ); setCustomExpiry( daysFromNow(initialPermissions.expiresAt) ? !Object.values(expiryOptions).includes( @@ -231,83 +222,16 @@ const Permissions: React.FC = ({ <>

Budget Renewal

- -
-
- {Object.keys(budgetOptions).map((budget) => { - return ( - // replace with something else and then remove dark prefixes -
{ - setCustomBudget(false); - handleBudgetMaxAmountChange(budgetOptions[budget]); - }} - className={cn( - "cursor-pointer rounded text-nowrap border-2 text-center p-4 dark:text-white", - !customBudget && - (Number.isNaN(permissions.maxAmount) - ? 100000 - : +permissions.maxAmount) == budgetOptions[budget] - ? "border-primary" - : "border-muted" - )} - > - {`${budget} ${budgetOptions[budget] ? " sats" : ""}`} -
- ); - })} -
{ - setCustomBudget(true); - handleBudgetMaxAmountChange(0); - }} - className={cn( - "cursor-pointer rounded border-2 text-center p-4 dark:text-white", - customBudget ? "border-primary" : "border-muted" - )} - > - Custom... -
+
- {customBudget && ( -
- - { - handleBudgetMaxAmountChange(parseInt(e.target.value)); - }} - /> -
- )} + )} diff --git a/frontend/src/components/SidebarHint.tsx b/frontend/src/components/SidebarHint.tsx index 8d4d9d1e..3aa1a1c4 100644 --- a/frontend/src/components/SidebarHint.tsx +++ b/frontend/src/components/SidebarHint.tsx @@ -49,6 +49,7 @@ function SidebarHint() { // User has funds to migrate if ( hasChannelManagement && + info?.backendType === "LDK" && albyBalance && albyBalance.sats * (1 - ALBY_SERVICE_FEE) > ALBY_MIN_BALANCE + 50000 /* accomodate for onchain fees */ @@ -87,9 +88,9 @@ function SidebarHint() { return ( ); diff --git a/frontend/src/components/SuggestedAppData.tsx b/frontend/src/components/SuggestedAppData.tsx index 7fb22672..159c1c93 100644 --- a/frontend/src/components/SuggestedAppData.tsx +++ b/frontend/src/components/SuggestedAppData.tsx @@ -1,6 +1,5 @@ import alby from "src/assets/suggested-apps/alby.png"; import amethyst from "src/assets/suggested-apps/amethyst.png"; -import bc from "src/assets/suggested-apps/bitcoin-connect.png"; import damus from "src/assets/suggested-apps/damus.png"; import hablanews from "src/assets/suggested-apps/habla-news.png"; import kiwi from "src/assets/suggested-apps/kiwi.png"; @@ -126,13 +125,6 @@ export const suggestedApps: SuggestedApp[] = [ webLink: "https://lume.nu/", logo: lume, }, - { - id: "bitcoin-connect", - title: "Bitcoin Connect", - description: "Connect to apps", - webLink: "https://bitcoin-connect.com/", - logo: bc, - }, { id: "kiwi", title: "Kiwi", diff --git a/frontend/src/components/TransactionItem.tsx b/frontend/src/components/TransactionItem.tsx new file mode 100644 index 00000000..1eae0234 --- /dev/null +++ b/frontend/src/components/TransactionItem.tsx @@ -0,0 +1,229 @@ +import dayjs from "dayjs"; +import timezone from "dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; +import { + ArrowDownIcon, + ArrowUpIcon, + ChevronDown, + ChevronUp, + CopyIcon, +} from "lucide-react"; +import React from "react"; +import { + Credenza, + CredenzaBody, + CredenzaContent, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle, + CredenzaTrigger, +} from "src/components/ui/credenza"; +import { toast } from "src/components/ui/use-toast"; +import { copyToClipboard } from "src/lib/clipboard"; +import { cn } from "src/lib/utils"; +import { Transaction } from "src/types"; + +dayjs.extend(utc); +dayjs.extend(timezone); + +type Props = { + tx: Transaction; +}; + +function TransactionItem({ tx }: Props) { + const [showDetails, setShowDetails] = React.useState(false); + const type = tx.type; + const Icon = tx.type == "outgoing" ? ArrowUpIcon : ArrowDownIcon; + + const copy = (text: string) => { + copyToClipboard(text); + toast({ title: "Copied to clipboard." }); + }; + + return ( + { + if (!open) { + setShowDetails(false); + } + }} + > + +
+
+
+ +
+
+
+
+

+ {type == "incoming" ? "Received" : "Sent"} +

+

+ {dayjs(tx.settled_at * 1000).fromNow()} +

+
+

+ {tx.description || "Lightning invoice"} +

+
+
+
+

+ {type == "outgoing" ? "-" : "+"} + {new Intl.NumberFormat(undefined, {}).format( + Math.floor(tx.amount / 1000) + )}{" "} +

+

+ {Math.floor(tx.amount / 1000) == 1 ? "sat" : "sats"} +

+ + {/* {!!tx.totalAmountFiat && ( +

+ ~{tx.totalAmountFiat} +

+ )} */} +
+
+
+
+ + + + {`${type == "outgoing" ? "Sent" : "Received"} Bitcoin`} + + + +
+
+ +
+
+

+ {new Intl.NumberFormat(undefined, {}).format( + Math.floor(tx.amount / 1000) + )}{" "} + {Math.floor(tx.amount / 1000) == 1 ? "sat" : "sats"} +

+ {/*

+ Fiat Amount +

*/} +
+
+
+

Date & Time

+

+ {dayjs(tx.settled_at * 1000) + .tz(dayjs.tz.guess()) + .format("D MMMM YYYY, HH:mm")} +

+
+ {type == "outgoing" && ( +
+

Fee

+

+ {new Intl.NumberFormat(undefined, {}).format( + Math.floor(tx.fees_paid / 1000) + )}{" "} + {Math.floor(tx.fees_paid / 1000) == 1 ? "sat" : "sats"} +

+
+ )} + {tx.description && ( +
+

Description

+

{tx.description}

+
+ )} +
+ +
setShowDetails(!showDetails)} + > + Details + {showDetails ? ( + + ) : ( + + )} +
+ {showDetails && ( + <> +
+

Preimage

+
+

+ {tx.preimage} +

+ { + copy(tx.preimage); + }} + /> +
+
+
+

Hash

+
+

+ {tx.payment_hash} +

+ { + copy(tx.payment_hash); + }} + /> +
+
+ + )} +
+
+
+ ); +} + +export default TransactionItem; diff --git a/frontend/src/components/TransactionsList.tsx b/frontend/src/components/TransactionsList.tsx index b7eaa0e2..0bc7ba0e 100644 --- a/frontend/src/components/TransactionsList.tsx +++ b/frontend/src/components/TransactionsList.tsx @@ -1,8 +1,7 @@ -import dayjs from "dayjs"; -import { ArrowDownIcon, ArrowUpIcon, Drum } from "lucide-react"; +import { Drum } from "lucide-react"; import EmptyState from "src/components/EmptyState"; - import Loading from "src/components/Loading"; +import TransactionItem from "src/components/TransactionItem"; import { useTransactions } from "src/hooks/useTransactions"; function TransactionsList() { @@ -13,7 +12,7 @@ function TransactionsList() { } return ( -
+
{!transactions?.length ? ( ) : ( <> - {transactions?.map((tx, i) => { - const type = tx.type; - - return ( -
-
-
- {type == "outgoing" ? ( -
- -
- ) : ( -
- -
- )} -
-
-
-

- {type == "incoming" ? "Received" : "Sent"} -

-

- {dayjs(tx.settled_at * 1000).fromNow()} -

-
-

- {tx.description || "Lightning invoice"} -

-
-
-
-

- {type == "outgoing" ? "-" : "+"}{" "} - {Math.floor(tx.amount / 1000)} -

-

sats

- - {/* {!!tx.totalAmountFiat && ( -

- ~{tx.totalAmountFiat} -

- )} */} -
-
-
-
- ); + {transactions?.map((tx) => { + return ; })} - {/* {transaction && ( - { - setModalOpen(false); - }} - /> - )} */} )}
diff --git a/frontend/src/components/connections/AlbyConnectionCard.tsx b/frontend/src/components/connections/AlbyConnectionCard.tsx index 2c3639a8..da7302f2 100644 --- a/frontend/src/components/connections/AlbyConnectionCard.tsx +++ b/frontend/src/components/connections/AlbyConnectionCard.tsx @@ -6,7 +6,11 @@ import { Link2Icon, ZapIcon, } from "lucide-react"; +import { useState } from "react"; import { Link } from "react-router-dom"; + +import BudgetAmountSelect from "src/components/BudgetAmountSelect"; +import BudgetRenewalSelect from "src/components/BudgetRenewalSelect"; import ExternalLink from "src/components/ExternalLink"; import Loading from "src/components/Loading"; import UserAvatar from "src/components/UserAvatar"; @@ -20,22 +24,36 @@ import { CardHeader, CardTitle, } from "src/components/ui/card"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTrigger, +} from "src/components/ui/dialog"; +import { Label } from "src/components/ui/label"; import { LoadingButton } from "src/components/ui/loading-button"; import { Separator } from "src/components/ui/separator"; import { useAlbyMe } from "src/hooks/useAlbyMe"; import { LinkStatus, useLinkAccount } from "src/hooks/useLinkAccount"; -import { App } from "src/types"; +import { App, BudgetRenewalType } from "src/types"; +import linkAccountIllustration from "/images/illustrations/link-account.png"; function AlbyConnectionCard({ connection }: { connection?: App }) { const { data: albyMe } = useAlbyMe(); const { loading, linkStatus, loadingLinkStatus, linkAccount } = useLinkAccount(); + const [maxAmount, setMaxAmount] = useState(1_000_000); + const [budgetRenewal, setBudgetRenewal] = + useState("monthly"); + return ( - Alby Account + Linked Alby Account {connection && } @@ -47,7 +65,7 @@ function AlbyConnectionCard({ connection }: { connection?: App }) {
-
+
@@ -62,10 +80,47 @@ function AlbyConnectionCard({ connection }: { connection?: App }) {
{loadingLinkStatus && } {!connection || linkStatus === LinkStatus.SharedNode ? ( - - {!loading && } - Link your Alby Account - + + + + {!loading && } + Link your Alby Account + + + + Link to Alby Account + + After you link your account, your lightning address and + every app you access through your Alby Account will handle + payments via the Hub. + + You can add a budget that will restrict how much can be + spent from the Hub with your Alby Account. + +
+ + +
+ + + linkAccount(maxAmount, budgetRenewal)} + loading={loading} + > + Link to Alby Account + + +
+
) : linkStatus === LinkStatus.ThisNode ? ( +
+
+ {connection.lastEventAt && ( + <>Last used: {dayjs(connection.lastEventAt).fromNow()} + )} +
+ e.stopPropagation()} + > + +
)} diff --git a/frontend/src/components/layouts/AppLayout.tsx b/frontend/src/components/layouts/AppLayout.tsx index df0bb092..bde01f65 100644 --- a/frontend/src/components/layouts/AppLayout.tsx +++ b/frontend/src/components/layouts/AppLayout.tsx @@ -2,7 +2,6 @@ import { Cable, EllipsisVertical, ExternalLinkIcon, - FlaskRound, Home, Lock, Megaphone, @@ -15,6 +14,7 @@ import { Wallet, } from "lucide-react"; +import { CubeIcon } from "@radix-ui/react-icons"; import React from "react"; import { Link, @@ -140,13 +140,10 @@ export default function AppLayout() { const { hasChannelManagement } = useInfo(); return (
diff --git a/frontend/src/components/redirects/SetupRedirect.tsx b/frontend/src/components/redirects/SetupRedirect.tsx index 9bf61152..207ed1bf 100644 --- a/frontend/src/components/redirects/SetupRedirect.tsx +++ b/frontend/src/components/redirects/SetupRedirect.tsx @@ -3,7 +3,6 @@ import { Outlet, useLocation, useNavigate } from "react-router-dom"; import Loading from "src/components/Loading"; import { useInfo } from "src/hooks/useInfo"; -let didSetupThisSession = false; export function SetupRedirect() { const { data: info } = useInfo(); const location = useLocation(); @@ -13,17 +12,10 @@ export function SetupRedirect() { if (!info) { return; } - if (didSetupThisSession) { - // ensure redirect does not happen as node may still be starting - // which would then incorrectly redirect to the login page - console.info("Skipping setup redirect on initial setup"); - return; - } - if (info.setupCompleted) { + if (info.setupCompleted && info.running) { navigate("/"); return; } - didSetupThisSession = true; }, [info, location, navigate]); if (!info) { diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx index 0c170b2d..84922957 100644 --- a/frontend/src/components/ui/button.tsx +++ b/frontend/src/components/ui/button.tsx @@ -18,7 +18,7 @@ const buttonVariants = cva( secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", + link: "text-foreground hover:text-accent-foreground underline-offset-4 hover:underline", positive: "bg-positive text-positive-foreground shadow-sm hover:bg-positive/90", }, diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx index ed176639..0f76508b 100644 --- a/frontend/src/components/ui/card.tsx +++ b/frontend/src/components/ui/card.tsx @@ -8,10 +8,7 @@ const Card = React.forwardRef< >(({ className, ...props }, ref) => (
)); @@ -75,9 +72,9 @@ CardFooter.displayName = "CardFooter"; export { Card, - CardHeader, + CardContent, + CardDescription, CardFooter, + CardHeader, CardTitle, - CardDescription, - CardContent, }; diff --git a/frontend/src/components/ui/credenza.tsx b/frontend/src/components/ui/credenza.tsx new file mode 100644 index 00000000..587515fd --- /dev/null +++ b/frontend/src/components/ui/credenza.tsx @@ -0,0 +1,148 @@ +import * as React from "react"; + +import { useMediaQuery } from "src/hooks/useMediaQuery"; +import { cn } from "src/lib/utils"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "./dialog"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "./drawer"; + +interface BaseProps { + children: React.ReactNode; +} + +interface RootCredenzaProps extends BaseProps { + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +interface CredenzaProps extends BaseProps { + className?: string; + asChild?: true; +} + +const desktop = "(min-width: 768px)"; + +const Credenza = ({ children, ...props }: RootCredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const Credenza = isDesktop ? Dialog : Drawer; + + return {children}; +}; + +const CredenzaTrigger = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaTrigger = isDesktop ? DialogTrigger : DrawerTrigger; + + return ( + + {children} + + ); +}; + +const CredenzaClose = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaClose = isDesktop ? DialogClose : DrawerClose; + + return ( + + {children} + + ); +}; + +const CredenzaContent = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaContent = isDesktop ? DialogContent : DrawerContent; + + return ( + + {children} + + ); +}; + +const CredenzaDescription = ({ + className, + children, + ...props +}: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaDescription = isDesktop ? DialogDescription : DrawerDescription; + + return ( + + {children} + + ); +}; + +const CredenzaHeader = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaHeader = isDesktop ? DialogHeader : DrawerHeader; + + return ( + + {children} + + ); +}; + +const CredenzaTitle = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaTitle = isDesktop ? DialogTitle : DrawerTitle; + + return ( + + {children} + + ); +}; + +const CredenzaBody = ({ className, children, ...props }: CredenzaProps) => { + return ( +
+ {children} +
+ ); +}; + +const CredenzaFooter = ({ className, children, ...props }: CredenzaProps) => { + const isDesktop = useMediaQuery(desktop); + const CredenzaFooter = isDesktop ? DialogFooter : DrawerFooter; + + return ( + + {children} + + ); +}; + +export { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle, + CredenzaTrigger, +}; diff --git a/frontend/src/components/ui/drawer.tsx b/frontend/src/components/ui/drawer.tsx new file mode 100644 index 00000000..36f39019 --- /dev/null +++ b/frontend/src/components/ui/drawer.tsx @@ -0,0 +1,116 @@ +import * as React from "react"; +import { Drawer as DrawerPrimitive } from "vaul"; + +import { cn } from "src/lib/utils"; + +const Drawer = ({ + shouldScaleBackground = true, + ...props +}: React.ComponentProps) => ( + +); +Drawer.displayName = "Drawer"; + +const DrawerTrigger = DrawerPrimitive.Trigger; + +const DrawerPortal = DrawerPrimitive.Portal; + +const DrawerClose = DrawerPrimitive.Close; + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName; + +const DrawerContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + +
+ {children} + + +)); +DrawerContent.displayName = "DrawerContent"; + +const DrawerHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DrawerHeader.displayName = "DrawerHeader"; + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DrawerFooter.displayName = "DrawerFooter"; + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerTitle.displayName = DrawerPrimitive.Title.displayName; + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerDescription.displayName = DrawerPrimitive.Description.displayName; + +export { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerOverlay, + DrawerPortal, + DrawerTitle, + DrawerTrigger, +}; diff --git a/frontend/src/components/ui/mode-toggle.tsx b/frontend/src/components/ui/mode-toggle.tsx deleted file mode 100644 index a9ea84e5..00000000 --- a/frontend/src/components/ui/mode-toggle.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Moon, Sun } from "lucide-react"; -import { Switch } from "src/components/ui/switch"; -import { useTheme } from "src/components/ui/theme-provider"; - -export function ModeToggle() { - const { theme, setTheme } = useTheme(); - - return ( - <> -
- - { - setTheme(theme === "dark" ? "light" : "dark"); - }} - checked={theme === "light"} - /> - -
- - ); -} diff --git a/frontend/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx index 336ac732..f7bf8c8a 100644 --- a/frontend/src/components/ui/progress.tsx +++ b/frontend/src/components/ui/progress.tsx @@ -1,5 +1,5 @@ -import * as React from "react"; import * as ProgressPrimitive from "@radix-ui/react-progress"; +import * as React from "react"; import { cn } from "src/lib/utils"; @@ -23,4 +23,23 @@ const Progress = React.forwardRef< )); Progress.displayName = ProgressPrimitive.Root.displayName; -export { Progress }; +const CircleProgress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + {props.children ||
{`${value || 0}%`}
} +
+)); + +export { CircleProgress, Progress }; diff --git a/frontend/src/components/ui/theme-provider.tsx b/frontend/src/components/ui/theme-provider.tsx index 84596d7f..d9be4651 100644 --- a/frontend/src/components/ui/theme-provider.tsx +++ b/frontend/src/components/ui/theme-provider.tsx @@ -1,51 +1,77 @@ import { createContext, useContext, useEffect, useState } from "react"; -type Theme = "dark" | "light" | "system"; +export type DarkMode = "system" | "light" | "dark"; +export const Themes = ["default", "alby", "bitcoin", "nostr"] as const; +export type Theme = (typeof Themes)[number]; type ThemeProviderProps = { children: React.ReactNode; defaultTheme?: Theme; + defaultDarkMode?: DarkMode; storageKey?: string; }; type ThemeProviderState = { - theme: Theme; + theme: string; + darkMode: string; setTheme: (theme: Theme) => void; + setDarkMode: (mode: DarkMode) => void; }; const initialState: ThemeProviderState = { - theme: "system", + theme: "default", setTheme: () => null, + darkMode: "system", + setDarkMode: () => null, }; const ThemeProviderContext = createContext(initialState); export function ThemeProvider({ children, - defaultTheme = "system", + defaultTheme = "default", + defaultDarkMode = "system", storageKey = "vite-ui-theme", ...props }: ThemeProviderProps) { const [theme, setTheme] = useState(() => { - return (localStorage.getItem(storageKey) as Theme) || defaultTheme; + const themeFromStorage = localStorage.getItem(storageKey) as Theme; + return Themes.includes(themeFromStorage) ? themeFromStorage : defaultTheme; }); + + const [darkMode, setDarkMode] = useState(() => { + return ( + (localStorage.getItem(storageKey + "-darkmode") as DarkMode) || + defaultDarkMode + ); + }); + useEffect(() => { const root = window.document.documentElement; - root.classList.remove("light", "dark"); - if (theme === "system") { - const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") - .matches - ? "dark" - : "light"; + // Find and remove classes that start with 'theme-' + const classList = root.classList; + classList.forEach((className) => { + if (className.startsWith("theme-")) { + classList.remove(className); + } + }); - root.classList.add(systemTheme); - setTheme(systemTheme); - return; + classList.add(`theme-${theme}`); + + let prefersDark = false; + if (darkMode == "system") { + prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + } else { + prefersDark = darkMode === "dark"; } - root.classList.add(theme); - }, [theme]); + if (prefersDark) { + classList.add("dark"); + } else { + classList.remove("dark"); + } + }, [theme, darkMode]); const value = { theme, @@ -53,6 +79,11 @@ export function ThemeProvider({ localStorage.setItem(storageKey, theme); setTheme(theme); }, + darkMode, + setDarkMode: (darkMode: DarkMode) => { + localStorage.setItem(storageKey + "-darkmode", darkMode); + setDarkMode(darkMode); + }, }; return ( diff --git a/frontend/src/hooks/useLinkAccount.ts b/frontend/src/hooks/useLinkAccount.ts index 9a858982..197704d7 100644 --- a/frontend/src/hooks/useLinkAccount.ts +++ b/frontend/src/hooks/useLinkAccount.ts @@ -4,6 +4,7 @@ import { useAlbyMe } from "src/hooks/useAlbyMe"; import { useApps } from "src/hooks/useApps"; import { useCSRF } from "src/hooks/useCSRF"; import { useNodeConnectionInfo } from "src/hooks/useNodeConnectionInfo"; +import { BudgetRenewalType } from "src/types"; import { request } from "src/utils/request"; export enum LinkStatus { @@ -32,7 +33,7 @@ export function useLinkAccount() { const loadingLinkStatus = linkStatus === undefined; - async function linkAccount() { + async function linkAccount(budget: number, renewal: BudgetRenewalType) { try { setLoading(true); if (!csrf) { @@ -44,6 +45,10 @@ export function useLinkAccount() { "X-CSRF-Token": csrf, "Content-Type": "application/json", }, + body: JSON.stringify({ + budget, + renewal, + }), }); // update the link status and get the newly-created Alby Account app await Promise.all([reloadAlbyMe(), reloadApps()]); diff --git a/frontend/src/hooks/useMediaQuery.ts b/frontend/src/hooks/useMediaQuery.ts new file mode 100644 index 00000000..48db609e --- /dev/null +++ b/frontend/src/hooks/useMediaQuery.ts @@ -0,0 +1,19 @@ +import * as React from "react"; + +export function useMediaQuery(query: string) { + const [value, setValue] = React.useState(window.matchMedia(query).matches); + + React.useEffect(() => { + function onChange(event: MediaQueryListEvent) { + setValue(event.matches); + } + + const result = matchMedia(query); + result.addEventListener("change", onChange); + setValue(result.matches); + + return () => result.removeEventListener("change", onChange); + }, [query]); + + return value; +} diff --git a/frontend/src/hooks/useTransactions.ts b/frontend/src/hooks/useTransactions.ts index 394768fd..6b550023 100644 --- a/frontend/src/hooks/useTransactions.ts +++ b/frontend/src/hooks/useTransactions.ts @@ -7,9 +7,10 @@ const pollConfiguration: SWRConfiguration = { refreshInterval: 3000, }; -export function useTransactions(poll = false) { +export function useTransactions(poll = false, limit = 100, page = 1) { + const offset = (page - 1) * limit; return useSWR( - "/api/transactions", + `/api/transactions?limit=${limit}&offset=${offset}`, swrFetcher, poll ? pollConfiguration : undefined ); diff --git a/frontend/src/index.css b/frontend/src/index.css index 6590f298..7adbc878 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,56 +1,12 @@ +@import "themes/default.css"; +@import "themes/alby.css"; +@import "themes/bitcoin.css"; +@import "themes/nostr.css"; + @tailwind base; @tailwind components; @tailwind utilities; -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 5.9% 10%; - --radius: 0.5rem; - --positive: 138, 68%, 96%; - --positive-foreground: 142 76% 36%; - } - - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; - } -} - @layer base { * { @apply border-border; @@ -65,9 +21,3 @@ input[type="text"]::-webkit-calendar-picker-indicator { display: none !important; } - -html { - --bc-color-brand: #000000; /* use a different brand color in dark mode */ - --bc-color-brand-dark: #ffffff; /* use a different brand color in dark mode */ - --bc-brand-mix: 100%; /* how much to mix the brand color with default foreground color */ -} diff --git a/frontend/src/lib/clipboard.ts b/frontend/src/lib/clipboard.ts index a35baa50..b7f16bdb 100644 --- a/frontend/src/lib/clipboard.ts +++ b/frontend/src/lib/clipboard.ts @@ -11,12 +11,15 @@ export async function copyToClipboard(content: string) { textArea.style.position = "absolute"; textArea.style.opacity = "0"; document.body.appendChild(textArea); - selectElement(textArea); + textArea.focus(); + textArea.select(); + if (document.execCommand("copy")) { resolve(content); } else { reject(); } + textArea.remove(); } }); @@ -31,13 +34,3 @@ export async function copyToClipboard(content: string) { }); } } - -function selectElement(element: Element) { - const selection = window.getSelection(); - if (selection) { - selection.removeAllRanges(); - const range = document.createRange(); - range.selectNode(element); - selection.addRange(range); - } -} diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx index 214ed395..06cabff9 100644 --- a/frontend/src/routes.tsx +++ b/frontend/src/routes.tsx @@ -102,26 +102,32 @@ const routes = [ }, { path: "settings", - element: , + element: , handle: { crumb: () => "Settings" }, children: [ { - index: true, - element: , - }, - { - path: "change-unlock-password", - element: , - handle: { crumb: () => "Unlock Password" }, - }, - { - path: "key-backup", - element: , - handle: { crumb: () => "Key Backup" }, - }, - { - path: "node-backup", - element: , + path: "", + element: , + children: [ + { + index: true, + element: , + }, + { + path: "change-unlock-password", + element: , + handle: { crumb: () => "Unlock Password" }, + }, + { + path: "key-backup", + element: , + handle: { crumb: () => "Key Backup" }, + }, + { + path: "node-backup", + element: , + }, + ], }, ], }, @@ -164,7 +170,7 @@ const routes = [ { path: "channels", element: , - handle: { crumb: () => "Liquidity" }, + handle: { crumb: () => "Node" }, children: [ { index: true, diff --git a/frontend/src/screens/Intro.tsx b/frontend/src/screens/Intro.tsx index 12e8f00c..f70d9267 100644 --- a/frontend/src/screens/Intro.tsx +++ b/frontend/src/screens/Intro.tsx @@ -27,7 +27,16 @@ export function Intro() { const navigate = useNavigate(); const [api, setApi] = React.useState(); const [progress, setProgress] = React.useState(0); - const { theme } = useTheme(); + const { setDarkMode } = useTheme(); + + React.useEffect(() => { + // Force dark mode on intro screen + setDarkMode("dark"); + return () => { + // Revert to default after exiting intro + setDarkMode("system"); + }; + }, [setDarkMode]); React.useEffect(() => { if (!info?.setupCompleted) { @@ -49,7 +58,6 @@ export function Intro() { style={{ backgroundImage: `url(${Cloud})`, backgroundPositionX: `${-Math.max(progress, 0) * 40}%`, - filter: theme === "light" ? "invert(0.3)" : undefined, }} />
diff --git a/frontend/src/screens/Start.tsx b/frontend/src/screens/Start.tsx index fcd2693f..58a52fe4 100644 --- a/frontend/src/screens/Start.tsx +++ b/frontend/src/screens/Start.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { useNavigate } from "react-router-dom"; import Container from "src/components/Container"; import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader"; import { Input } from "src/components/ui/input"; @@ -11,18 +10,61 @@ import { useInfo } from "src/hooks/useInfo"; import { handleRequestError } from "src/utils/handleRequestError"; import { request } from "src/utils/request"; +const messages: string[] = [ + "Unlocking", + "Starting the wallet", + "Connecting to the network", + "Syncing", + "Still syncing, please wait...", +]; + export default function Start() { const [unlockPassword, setUnlockPassword] = React.useState(""); const [loading, setLoading] = React.useState(false); - const navigate = useNavigate(); + const [buttonText, setButtonText] = React.useState("Login"); + useInfo(true); // poll the info endpoint to auto-redirect when app is running const { data: csrf } = useCSRF(); - const { mutate: refetchInfo } = useInfo(); const { toast } = useToast(); + React.useEffect(() => { + if (!loading) { + return; + } + let messageIndex = 1; + const intervalId = setInterval(() => { + if (messageIndex < messages.length) { + setButtonText(messages[messageIndex]); + messageIndex++; + } else { + clearInterval(intervalId); + } + }, 5000); + + const timeoutId = setTimeout(() => { + // if redirection didn't happen in 3 minutes info.running is false + toast({ + title: "Failed to start", + description: "Please try starting the node again.", + variant: "destructive", + }); + + setLoading(false); + setButtonText("Login"); + setUnlockPassword(""); + return; + }, 180000); // wait for 3 minutes + + return () => { + clearInterval(intervalId); + clearTimeout(timeoutId); + }; + }, [loading, toast]); + async function onSubmit(e: React.FormEvent) { e.preventDefault(); try { setLoading(true); + setButtonText(messages[0]); if (!csrf) { throw new Error("csrf not loaded"); } @@ -36,13 +78,11 @@ export default function Start() { unlockPassword, }), }); - await refetchInfo(); - - navigate("/"); } catch (error) { handleRequestError(toast, "Failed to connect", error); - } finally { setLoading(false); + setButtonText("Login"); + setUnlockPassword(""); } } @@ -68,7 +108,7 @@ export default function Start() { />
- Login + {buttonText}
diff --git a/frontend/src/screens/Welcome.tsx b/frontend/src/screens/Welcome.tsx index 4446dd16..d319d02d 100644 --- a/frontend/src/screens/Welcome.tsx +++ b/frontend/src/screens/Welcome.tsx @@ -166,7 +166,7 @@ export function Welcome() { Subject to the following terms, Alby grants to you a perpetual, worldwide license to the Application pursuant to the Apache-2.0 license. - (https://github.com/getAlby/nostr-wallet-connect-next?tab=Apache-2.0-1-ov-file#readme). + (https://github.com/getAlby/hub?tab=Apache-2.0-1-ov-file#readme). This EULA pertains solely to the Application and does not limit your rights under, or grant you rights that supersede, the license terms of any particular component. diff --git a/frontend/src/screens/apps/AppCreated.tsx b/frontend/src/screens/apps/AppCreated.tsx index 7ff69330..e03e494d 100644 --- a/frontend/src/screens/apps/AppCreated.tsx +++ b/frontend/src/screens/apps/AppCreated.tsx @@ -1,4 +1,4 @@ -import { CopyIcon } from "lucide-react"; +import { CopyIcon, EyeIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { Link, Navigate, useLocation, useNavigate } from "react-router-dom"; @@ -29,6 +29,7 @@ export default function AppCreated() { const appstoreApp = suggestedApps.find((app) => app.id === appId); const [timeout, setTimeout] = useState(false); + const [isQRCodeVisible, setIsQRCodeVisible] = useState(false); const createAppResponse = state as CreateAppResponse; const pairingUri = createAppResponse.pairingUri; const { data: app } = useApp(createAppResponse.pairingPublicKey, true); @@ -41,7 +42,7 @@ export default function AppCreated() { useEffect(() => { const timeoutId = window.setTimeout(() => { setTimeout(true); - }, 10000); + }, 30000); return () => window.clearTimeout(timeoutId); }, []); @@ -87,7 +88,7 @@ export default function AppCreated() { title={`Connect to ${createAppResponse.name}`} description="Configure wallet permissions for the app and follow instructions to finalise the connection" /> -
+

1. Open{" "} @@ -124,12 +125,26 @@ export default function AppCreated() {

)} - - {appstoreApp && ( - +
+ + {appstoreApp && ( + + )} +
+ {!isQRCodeVisible && ( + )}
diff --git a/frontend/src/screens/apps/AppList.tsx b/frontend/src/screens/apps/AppList.tsx index 8d417669..cc4e8270 100644 --- a/frontend/src/screens/apps/AppList.tsx +++ b/frontend/src/screens/apps/AppList.tsx @@ -52,7 +52,7 @@ function AppList() { )} {otherApps.length > 0 && ( -
+
{otherApps.map((app, index) => ( ))} diff --git a/frontend/src/screens/appstore/AppStore.tsx b/frontend/src/screens/appstore/AppStore.tsx index f25904c3..966bddc5 100644 --- a/frontend/src/screens/appstore/AppStore.tsx +++ b/frontend/src/screens/appstore/AppStore.tsx @@ -7,7 +7,7 @@ function AppStore() { return ( <> diff --git a/frontend/src/screens/channels/Channels.tsx b/frontend/src/screens/channels/Channels.tsx index 2105308d..1356a7a7 100644 --- a/frontend/src/screens/channels/Channels.tsx +++ b/frontend/src/screens/channels/Channels.tsx @@ -7,6 +7,7 @@ import { CopyIcon, ExternalLinkIcon, HandCoins, + Heart, Hotel, InfoIcon, MoreHorizontal, @@ -43,7 +44,7 @@ import { DropdownMenuTrigger, } from "src/components/ui/dropdown-menu.tsx"; import { LoadingButton } from "src/components/ui/loading-button.tsx"; -import { Progress } from "src/components/ui/progress.tsx"; +import { CircleProgress, Progress } from "src/components/ui/progress.tsx"; import { Table, TableBody, @@ -95,6 +96,8 @@ export default function Channels() { const [drainingAlbySharedFunds, setDrainingAlbySharedFunds] = React.useState(false); + const nodeHealth = channels ? getNodeHealth(channels) : 0; + // TODO: move to NWC backend const loadNodeStats = React.useCallback(async () => { if (!channels) { @@ -284,10 +287,10 @@ export default function Channels() { return ( <> +
- */} - + + + + + + + {nodeHealth === 100 && ( +
+
+
+ )} + + + + Node health: {nodeHealth}% + + + +
} > @@ -414,14 +446,14 @@ export default function Channels() { )} > {showHostedBalance && ( - + Alby Hosted Balance - +
{new Intl.NumberFormat().format(albyBalance?.sats)} sats
@@ -476,14 +508,14 @@ export default function Channels() {
)} - + Savings Balance - + {!balances && (
@@ -491,7 +523,7 @@ export default function Channels() {
)} -
+
{balances && ( <> {new Intl.NumberFormat().format(balances.onchain.spendable)}{" "} @@ -519,14 +551,14 @@ export default function Channels() { - + Spending Balance - + {!balances && (
@@ -535,7 +567,7 @@ export default function Channels() {
)} {balances && ( -
+
{new Intl.NumberFormat(undefined, {}).format( Math.floor(balances.lightning.totalSpendable / 1000) )}{" "} @@ -549,15 +581,15 @@ export default function Channels() { - + Receiving Capacity - -
+ +
{balances && new Intl.NumberFormat().format( Math.floor(balances.lightning.totalReceivable / 1000) @@ -585,7 +617,7 @@ export default function Channels() { {!channels || (channels.length > 0 && ( - +
Status @@ -648,7 +680,7 @@ export default function Channels() { } return ( - + {channel.active ? ( Online @@ -797,3 +829,38 @@ export default function Channels() { ); } + +function getNodeHealth(channels: Channel[]) { + const totalChannelCapacitySats = channels + .map((channel) => (channel.localBalance + channel.remoteBalance) / 1000) + .reduce((a, b) => a + b, 0); + const averageChannelBalance = + channels + .map((channel) => { + const totalBalance = channel.localBalance + channel.remoteBalance; + const expectedBalance = totalBalance / 2; + const actualBalance = + Math.min(channel.localBalance, channel.remoteBalance) / + expectedBalance; + return actualBalance; + }) + .reduce((a, b) => a + b, 0) / (channels.length || 1); + + const numUniqueChannelPartners = new Set( + channels.map((channel) => channel.remotePubkey) + ).size; + + const nodeHealth = Math.ceil( + numUniqueChannelPartners * + (100 / 2) * // 2 or more channels is great + (Math.min(totalChannelCapacitySats, 1_000_000) / 1_000_000) * // 1 million sats or more is great + (0.9 + averageChannelBalance * 0.1) // +10% for perfectly balanced channels + ); + + if (nodeHealth > 95) { + // prevent OCD + return 100; + } + + return nodeHealth; +} diff --git a/frontend/src/screens/channels/CurrentChannelOrder.tsx b/frontend/src/screens/channels/CurrentChannelOrder.tsx index 47149a5e..a18f5ce2 100644 --- a/frontend/src/screens/channels/CurrentChannelOrder.tsx +++ b/frontend/src/screens/channels/CurrentChannelOrder.tsx @@ -356,6 +356,11 @@ function PayBitcoinChannelOrderTopup({ order }: { order: NewChannelOrder }) { Topup with your credit card or bank account + + + ); diff --git a/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx b/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx index fe55ea01..7296d8fd 100644 --- a/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx +++ b/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx @@ -128,6 +128,7 @@ function NewChannelInternal({ network }: { network: Network }) { const okPartners = channelPeerSuggestions.filter( (partner) => amount >= partner.minimumChannelSize && + amount <= partner.maximumChannelSize && partner.network === network && partner.paymentMethod === "lightning" && partner.lspType === "LSPS1" && @@ -264,15 +265,19 @@ function NewChannelInternal({ network }: { network: Network }) { )}
{peer.name} - {peer.minimumChannelSize > 0 && ( - - Min.{" "} - {new Intl.NumberFormat().format( - peer.minimumChannelSize - )}{" "} - sats - - )} + + Min.{" "} + {new Intl.NumberFormat().format( + peer.minimumChannelSize + )} + sats + + Max.{" "} + {new Intl.NumberFormat().format( + peer.maximumChannelSize + )}{" "} + sats +
diff --git a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx index 2fce2778..242f478b 100644 --- a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx +++ b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx @@ -1,4 +1,4 @@ -import { Box, ChevronDown, Zap } from "lucide-react"; +import { ChevronDown } from "lucide-react"; import React, { FormEvent } from "react"; import { Link, useNavigate } from "react-router-dom"; import AppHeader from "src/components/AppHeader"; @@ -70,6 +70,7 @@ function NewChannelInternal({ network }: { network: Network }) { network, paymentMethod: "onchain", minimumChannelSize: 0, + maximumChannelSize: 0, pubkey: "", host: "", image: "", @@ -77,21 +78,13 @@ function NewChannelInternal({ network }: { network: Network }) { return _channelPeerSuggestions ? [ ..._channelPeerSuggestions.filter( - (peer) => - peer.paymentMethod !== "lightning" || peer.lspType !== "LSPS1" + (peer) => peer.paymentMethod !== "lightning" ), customOption, ] : undefined; }, [_channelPeerSuggestions, network]); - function setPaymentMethod(paymentMethod: "onchain" | "lightning") { - setOrder((current) => ({ - ...current, - paymentMethod, - })); - } - function setPublic(isPublic: boolean) { setOrder((current) => ({ ...current, @@ -130,21 +123,9 @@ function NewChannelInternal({ network }: { network: Network }) { host: selectedPeer.host, })); } - if ( - selectedPeer.paymentMethod === "lightning" && - order.paymentMethod === "lightning" - ) { - setOrder((current) => ({ - ...current, - lspType: selectedPeer.lspType, - lspUrl: selectedPeer.lspUrl, - })); - } } }, [order.paymentMethod, selectedPeer]); - const selectedCardStyles = "border-primary border-2 font-medium"; - const [showAdvanced, setShowAdvanced] = React.useState(false); function onSubmit(e: FormEvent) { @@ -218,6 +199,15 @@ function NewChannelInternal({ network }: { network: Network }) { + + + + + } />
{showAdvanced && ( <> -
- -
- setPaymentMethod("onchain")} - className="flex-1" - > -
- - Onchain -
- - setPaymentMethod("lightning")}> -
- - Lightning -
- -
-
{selectedPeer && - (selectedPeer.paymentMethod === "lightning" || - (order.paymentMethod === "onchain" && - selectedPeer.pubkey === order.pubkey)) && ( + order.paymentMethod === "onchain" && + selectedPeer.pubkey === order.pubkey && (
{ + setTheme(value as Theme); + toast({ title: "Theme updated." }); + }} + > + + + + + {Themes.map((theme) => ( + + {theme.charAt(0).toUpperCase() + theme.substring(1)} + + ))} + + +
+
+ + +
+ ); } diff --git a/frontend/src/screens/setup/SetupFinish.tsx b/frontend/src/screens/setup/SetupFinish.tsx index dfcfa81e..9ccda8b2 100644 --- a/frontend/src/screens/setup/SetupFinish.tsx +++ b/frontend/src/screens/setup/SetupFinish.tsx @@ -15,9 +15,9 @@ import { request } from "src/utils/request"; export function SetupFinish() { const navigate = useNavigate(); const { nodeInfo, unlockPassword } = useSetupStore(); - - const { mutate: refetchInfo } = useInfo(); + useInfo(true); // poll the info endpoint to auto-redirect when app is running const { data: csrf } = useCSRF(); + const [loading, setLoading] = React.useState(false); const [connectionError, setConnectionError] = React.useState(false); const hasFetchedRef = React.useRef(false); @@ -30,6 +30,22 @@ export function SetupFinish() { }, }; + useEffect(() => { + if (!loading) { + return; + } + const timer = setTimeout(() => { + // SetupRedirect takes care of redirection once info.running is true + // if it still didn't redirect after 3 minutes, we show an error + setLoading(false); + setConnectionError(true); + }, 180000); + + return () => { + clearTimeout(timer); + }; + }, [loading]); + useEffect(() => { // ensure setup call is only called once if (!csrf || hasFetchedRef.current) { @@ -38,18 +54,15 @@ export function SetupFinish() { hasFetchedRef.current = true; (async () => { + setLoading(true); const succeeded = await finishSetup(csrf, nodeInfo, unlockPassword); - if (succeeded) { - const info = await refetchInfo(); - if (!info) { - throw new Error("Failed to re-fetch info"); - } - navigate("/"); - } else { + // only setup call is successful as start is async + if (!succeeded) { + setLoading(false); setConnectionError(true); } })(); - }, [csrf, nodeInfo, refetchInfo, navigate, unlockPassword]); + }, [csrf, nodeInfo, navigate, unlockPassword]); if (connectionError) { return ( @@ -57,7 +70,7 @@ export function SetupFinish() {

Connection Failed

-

Please check your node configuration.

+

Please check your node configuration and try again.

-
@@ -215,7 +228,7 @@ export default function Send() {
)} {balances && ( -
+
{new Intl.NumberFormat(undefined, {}).format( Math.floor(balances.lightning.totalSpendable / 1000) )}{" "} diff --git a/frontend/src/screens/wallet/index.tsx b/frontend/src/screens/wallet/index.tsx index 4f733e31..eb35a6d8 100644 --- a/frontend/src/screens/wallet/index.tsx +++ b/frontend/src/screens/wallet/index.tsx @@ -22,7 +22,7 @@ function Wallet() {
-
+
{new Intl.NumberFormat().format( Math.floor(balances.lightning.totalSpendable / 1000) )}{" "} diff --git a/frontend/src/themes/alby.css b/frontend/src/themes/alby.css new file mode 100644 index 00000000..db52a422 --- /dev/null +++ b/frontend/src/themes/alby.css @@ -0,0 +1,52 @@ +.theme-alby { + --background: 0 0% 100%; + --foreground: 0 0% 5%; + --card: 0 0% 100%; + --card-foreground: 0 0% 5%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 5%; + --primary: 47 100% 72%; + --primary-foreground: 0 0% 2%; + --secondary: 0 0% 96%; + --secondary-foreground: 0 0% 5%; + --muted: 0 0% 96%; + --muted-foreground: 0 0% 45%; + --accent: 0 0% 96%; + --accent-foreground: 0 0% 5%; + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 92%; + --input: 0 0% 85%; + --ring: 0 0% 76%; + --radius: 0.5rem; +} + +.theme-alby.dark { + --background: 0 0% 3%; + --foreground: 0 0% 98%; + + --card: 0 0% 3%; + --card-foreground: 0 0% 98%; + + --popover: 0 0% 0%; + --popover-foreground: 0 0% 98%; + + --primary: 47 100% 72%; + --primary-foreground: 0 0% 2%; + + --secondary: 0 0% 0%; + --secondary-foreground: 0 0% 98%; + + --muted: 0 0% 10%; + --muted-foreground: 0 0% 49%; + + --accent: 0 0% 0%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 15%; + --input: 0 0% 15%; + --ring: 47 100% 40%; +} diff --git a/frontend/src/themes/bitcoin.css b/frontend/src/themes/bitcoin.css new file mode 100644 index 00000000..eb7cd837 --- /dev/null +++ b/frontend/src/themes/bitcoin.css @@ -0,0 +1,61 @@ +.theme-bitcoin { + --background: 0 0% 100%; + --foreground: 0 0% 5%; + + --card: 0 0% 100%; + --card-foreground: 0 0% 5%; + + --popover: 0 0% 100%; + --popover-foreground: 0 0% 5%; + + --primary: 42 100% 47%; + --primary-foreground: 0 0% 2%; + + --secondary: 0 0% 96%; + --secondary-foreground: 0 0% 5%; + + --muted: 0 0% 96%; + --muted-foreground: 0 0% 45%; + + --accent: 0 0% 96%; + --accent-foreground: 0 0% 5%; + + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 92%; + --input: 0 0% 85%; + --ring: 0 0% 76%; + + --radius: 0.5rem; +} + +.theme-bitcoin.dark { + --background: 60 3% 6%; + --foreground: 0 0% 98%; + + --card: 60 3% 6%; + --card-foreground: 0 0% 98%; + + --popover: 60 3% 6%; + --popover-foreground: 0 0% 98%; + + --primary: 42 100% 47%; + --primary-foreground: 0 0% 2%; + + --secondary: 40 5% 12%; + --secondary-foreground: 0 0% 98%; + + --muted: 45 4% 18%; + --muted-foreground: 40 5% 49%; + + --accent: 42 44% 12%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 98%; + + --border: 40 5% 12%; + --input: 45 4% 18%; + --ring: 42 100% 40%; +} diff --git a/frontend/src/themes/default.css b/frontend/src/themes/default.css new file mode 100644 index 00000000..64a4a1bd --- /dev/null +++ b/frontend/src/themes/default.css @@ -0,0 +1,52 @@ +.theme-default { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 5.9% 10%; + --positive: 138, 68%, 96%; + --positive-foreground: 142 76% 36%; + --radius: 0.5rem; +} + +.theme-default.dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; +} + +html { + --bc-color-brand: #000000; /* use a different brand color in dark mode */ + --bc-color-brand-dark: #ffffff; /* use a different brand color in dark mode */ + --bc-brand-mix: 100%; /* how much to mix the brand color with default foreground color */ +} diff --git a/frontend/src/themes/nostr.css b/frontend/src/themes/nostr.css new file mode 100644 index 00000000..01b9c732 --- /dev/null +++ b/frontend/src/themes/nostr.css @@ -0,0 +1,61 @@ +.theme-nostr { + --background: 270 8% 95%; + --foreground: 270 8% 5%; + + --card: 270 8% 95%; + --card-foreground: 270 8% 5%; + + --popover: 270 8% 95%; + --popover-foreground: 270 8% 5%; + + --primary: 273 100% 29%; + --primary-foreground: 300 9% 98%; + + --secondary: 276 11% 91%; + --secondary-foreground: 270 8% 5%; + + --muted: 270 9% 91%; + --muted-foreground: 274 9% 45%; + + --accent: 276 11% 91%; + --accent-foreground: 270 8% 5%; + + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 98%; + + --border: 270 9% 87%; + --input: 267 9% 81%; + --ring: 273 36% 72%; + + --radius: 0.5rem; +} + +.theme-nostr.dark { + --background: 300 4% 5%; + --foreground: 0 0% 98%; + + --card: 300 4% 5%; + --card-foreground: 0 0% 98%; + + --popover: 300 4% 5%; + --popover-foreground: 0 0% 98%; + + --primary: 273 42% 47%; + --primary-foreground: 0 0% 98%; + + --secondary: 300 2% 10%; + --secondary-foreground: 0 0% 98%; + + --muted: 270 3% 15%; + --muted-foreground: 276 2% 49%; + + --accent: 273 18% 10%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 98%; + + --border: 300 2% 12%; + --input: 270 2% 18%; + --ring: 272 41% 40%; +} diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 813b3793..3dc24c5e 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -159,7 +159,7 @@ export interface App { scopes: Scope[]; maxAmount: number; budgetUsage: number; - budgetRenewal: string; + budgetRenewal: BudgetRenewalType; } export interface AppPermissions { @@ -181,7 +181,6 @@ export interface InfoResponse { albyUserIdentifier: string; network?: Network; version: string; - latestVersion: string; } export type Network = "bitcoin" | "testnet" | "signet"; @@ -323,6 +322,7 @@ export type RecommendedChannelPeer = { image: string; name: string; minimumChannelSize: number; + maximumChannelSize: number; } & ( | { paymentMethod: "onchain"; @@ -347,6 +347,9 @@ export type AlbyMe = { avatar: string; keysend_pubkey: string; shared_node: boolean; + hub: { + latest_version: string; + }; }; export type AlbyBalance = { diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 7c74ebbc..afa21e7a 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -2,7 +2,7 @@ const defaultTheme = require("tailwindcss/defaultTheme"); /** @type {import('tailwindcss').Config} */ module.exports = { - darkMode: ["class"], + darkMode: ["selector"], content: [ "./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 30cbe0cf..14264eb0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -644,6 +644,26 @@ aria-hidden "^1.1.1" react-remove-scroll "2.5.5" +"@radix-ui/react-dialog@^1.0.4": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz#4906507f7b4ad31e22d7dad69d9330c87c431d44" + integrity sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-dismissable-layer" "1.1.0" + "@radix-ui/react-focus-guards" "1.1.0" + "@radix-ui/react-focus-scope" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-portal" "1.1.1" + "@radix-ui/react-presence" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.7" + "@radix-ui/react-direction@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b" @@ -1606,9 +1626,9 @@ camelcase-css@^2.0.1: integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001565: - version "1.0.30001570" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz" - integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== + version "1.0.30001640" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz" + integrity sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA== canvas-confetti@^1.9.2: version "1.9.2" @@ -3594,6 +3614,13 @@ util-deprecate@^1.0.2: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +vaul@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/vaul/-/vaul-0.9.1.tgz#3640198e04636b209b1f907fcf3079bec6ecc66b" + integrity sha512-fAhd7i4RNMinx+WEm6pF3nOl78DFkAazcN04ElLPFF9BMCNGbY/kou8UMhIcicm0rJCNePJP0Yyza60gGOD0Jw== + dependencies: + "@radix-ui/react-dialog" "^1.0.4" + vite-tsconfig-paths@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz#321f02e4b736a90ff62f9086467faf4e2da857a9" diff --git a/go.mod b/go.mod index 6a92bd48..ba350e9e 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/getAlby/nostr-wallet-connect +module github.com/getAlby/hub go 1.22.2 @@ -10,18 +10,18 @@ require ( github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59 github.com/getAlby/ldk-node-go v0.0.0-20240624140557-d51c707f10d9 github.com/go-gormigrate/gormigrate/v2 v2.1.2 - github.com/gorilla/sessions v1.2.2 + github.com/gorilla/sessions v1.3.0 github.com/labstack/echo-contrib v0.17.1 github.com/labstack/echo/v4 v4.12.0 - github.com/nbd-wtf/go-nostr v0.32.0 + github.com/nbd-wtf/go-nostr v0.34.1 github.com/nbd-wtf/ln-decodepay v1.12.1 github.com/orandin/lumberjackrus v1.0.1 github.com/stretchr/testify v1.9.0 - github.com/wailsapp/wails/v2 v2.8.2 - golang.org/x/crypto v0.24.0 + github.com/wailsapp/wails/v2 v2.9.1 + golang.org/x/crypto v0.25.0 golang.org/x/oauth2 v0.21.0 - google.golang.org/grpc v1.64.0 - gopkg.in/DataDog/dd-trace-go.v1 v1.65.0 + google.golang.org/grpc v1.65.0 + gopkg.in/DataDog/dd-trace-go.v1 v1.65.1 gopkg.in/macaroon.v2 v2.1.0 gorm.io/gorm v1.25.10 ) @@ -199,14 +199,14 @@ require ( golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/errgo.v1 v1.0.1 // indirect gopkg.in/macaroon-bakery.v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 1921c22a..f7ad1606 100644 --- a/go.sum +++ b/go.sum @@ -278,8 +278,8 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= -github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= -github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg= +github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -498,8 +498,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/nbd-wtf/go-nostr v0.32.0 h1:ShRerjhXvqZbiVUc11iPqxLuOImxqbJQ0zTz4t6Tjps= -github.com/nbd-wtf/go-nostr v0.32.0/go.mod h1:NZQkxl96ggbO8rvDpVjcsojJqKTPwqhP4i82O7K5DJs= +github.com/nbd-wtf/go-nostr v0.34.1 h1:wagHkPL0xHu8Tfnaybw3zERItu5ScoR3fKLQIPdCpi8= +github.com/nbd-wtf/go-nostr v0.34.1/go.mod h1:NZQkxl96ggbO8rvDpVjcsojJqKTPwqhP4i82O7K5DJs= github.com/nbd-wtf/ln-decodepay v1.12.1 h1:GDBIDZPm35DtRadhO9qBT+OebXgm33+8BpANq0QcwLA= github.com/nbd-wtf/ln-decodepay v1.12.1/go.mod h1:+VRpg00geUGDEaBx/9+P5nt2RVmyMCNsKnaFxErYUgo= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= @@ -649,8 +649,8 @@ github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhy github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v2 v2.8.2 h1:rYOn9p+7bJiZuFSi2wDyq8rBLHrIX/FoUxov+RpdUOI= -github.com/wailsapp/wails/v2 v2.8.2/go.mod h1:5pTURIST4yZ/wRcmqDUtnM0Mk+caNax/oS610hFiy74= +github.com/wailsapp/wails/v2 v2.9.1 h1:irsXnoQrCpeKzKTYZ2SUVlRRyeMR6I0vCO9Q1cvlEdc= +github.com/wailsapp/wails/v2 v2.9.1/go.mod h1:7maJV2h+Egl11Ak8QZN/jlGLj2wg05bsQS+ywJPT0gI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -739,8 +739,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= @@ -837,13 +837,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -891,20 +891,20 @@ google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -gopkg.in/DataDog/dd-trace-go.v1 v1.65.0 h1:mMix4feEsbn2/wONR8e68JLob2QSdpiAMINhpG/8s7k= -gopkg.in/DataDog/dd-trace-go.v1 v1.65.0/go.mod h1:beNFIWd/H04d0k96cfltgiDH2+t0T5sDbyYLF3VTXqk= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +gopkg.in/DataDog/dd-trace-go.v1 v1.65.1 h1:Ne7kzWr/br/jwhUJR7CnqPl/mUpNxa6LfgZs0S4htZM= +gopkg.in/DataDog/dd-trace-go.v1 v1.65.1/go.mod h1:beNFIWd/H04d0k96cfltgiDH2+t0T5sDbyYLF3VTXqk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/http/alby_http_service.go b/http/alby_http_service.go index 83a03823..2493ea64 100644 --- a/http/alby_http_service.go +++ b/http/alby_http_service.go @@ -4,10 +4,10 @@ import ( "fmt" "net/http" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" "github.com/labstack/echo/v4" ) @@ -119,7 +119,14 @@ func (albyHttpSvc *AlbyHttpService) albyDrainHandler(c echo.Context) error { } func (albyHttpSvc *AlbyHttpService) albyLinkAccountHandler(c echo.Context) error { - err := albyHttpSvc.albyOAuthSvc.LinkAccount(c.Request().Context(), albyHttpSvc.svc.GetLNClient()) + var linkAccountRequest alby.AlbyLinkAccountRequest + if err := c.Bind(&linkAccountRequest); err != nil { + return c.JSON(http.StatusBadRequest, ErrorResponse{ + Message: fmt.Sprintf("Bad request: %s", err.Error()), + }) + } + + err := albyHttpSvc.albyOAuthSvc.LinkAccount(c.Request().Context(), albyHttpSvc.svc.GetLNClient(), linkAccountRequest.Budget, linkAccountRequest.Renewal) if err != nil { logger.Logger.WithError(err).Error("Failed to connect alby account") return err diff --git a/http/http_service.go b/http/http_service.go index 2038d352..949f9819 100644 --- a/http/http_service.go +++ b/http/http_service.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net/http" + "strconv" "strings" echologrus "github.com/davrux/echo-logrus/v4" @@ -14,14 +15,14 @@ import ( "github.com/labstack/echo/v4/middleware" "gorm.io/gorm" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" - "github.com/getAlby/nostr-wallet-connect/api" - "github.com/getAlby/nostr-wallet-connect/frontend" + "github.com/getAlby/hub/api" + "github.com/getAlby/hub/frontend" ) type HttpService struct { @@ -180,14 +181,13 @@ func (httpSvc *HttpService) startHandler(c echo.Context) error { }) } - err := httpSvc.api.Start(&startRequest) - if err != nil { - return c.JSON(http.StatusInternalServerError, ErrorResponse{ - Message: fmt.Sprintf("Failed to start node: %s", err.Error()), + if !httpSvc.cfg.CheckUnlockPassword(startRequest.UnlockPassword) { + return c.JSON(http.StatusUnauthorized, ErrorResponse{ + Message: "Invalid password", }) } - err = httpSvc.saveSessionCookie(c) + err := httpSvc.saveSessionCookie(c) if err != nil { return c.JSON(http.StatusInternalServerError, ErrorResponse{ @@ -195,6 +195,13 @@ func (httpSvc *HttpService) startHandler(c echo.Context) error { }) } + go func() { + err := httpSvc.api.Start(&startRequest) + if err != nil { + logger.Logger.WithError(err).Error("Failed to start node") + } + }() + return c.NoContent(http.StatusNoContent) } @@ -447,7 +454,22 @@ func (httpSvc *HttpService) lookupTransactionHandler(c echo.Context) error { func (httpSvc *HttpService) listTransactionsHandler(c echo.Context) error { ctx := c.Request().Context() - transactions, err := httpSvc.api.ListTransactions(ctx) + limit := uint64(20) + offset := uint64(0) + + if limitParam := c.QueryParam("limit"); limitParam != "" { + if parsedLimit, err := strconv.ParseUint(limitParam, 10, 64); err == nil { + limit = parsedLimit + } + } + + if offsetParam := c.QueryParam("offset"); offsetParam != "" { + if parsedOffset, err := strconv.ParseUint(offsetParam, 10, 64); err == nil { + offset = parsedOffset + } + } + + transactions, err := httpSvc.api.ListTransactions(ctx, limit, offset) if err != nil { return c.JSON(http.StatusInternalServerError, ErrorResponse{ diff --git a/lnclient/breez/breez.go b/lnclient/breez/breez.go index cefdbf8e..7b6245bd 100644 --- a/lnclient/breez/breez.go +++ b/lnclient/breez/breez.go @@ -16,8 +16,8 @@ import ( decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" ) type BreezService struct { @@ -119,9 +119,13 @@ func (bs *BreezService) SendPaymentSync(ctx context.Context, payReq string) (*ln func (bs *BreezService) SendKeysend(ctx context.Context, amount uint64, destination, preimage string, custom_records []lnclient.TLVRecord) (preImage string, err error) { extraTlvs := []breez_sdk.TlvEntry{} for _, record := range custom_records { + decodedValue, err := hex.DecodeString(record.Value) + if err != nil { + return "", err + } extraTlvs = append(extraTlvs, breez_sdk.TlvEntry{ FieldNumber: record.Type, - Value: []uint8(record.Value), + Value: decodedValue, }) } diff --git a/lnclient/breez/breez_stub.go b/lnclient/breez/breez_stub.go index 9692e0ef..e7169c88 100644 --- a/lnclient/breez/breez_stub.go +++ b/lnclient/breez/breez_stub.go @@ -3,7 +3,7 @@ package breez import ( - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/lnclient" ) func NewBreezService(mnemonic, apiKey, inviteCode, workDir string) (result lnclient.LNClient, err error) { diff --git a/lnclient/cashu/cashu.go b/lnclient/cashu/cashu.go index 4128d16c..d858a59f 100644 --- a/lnclient/cashu/cashu.go +++ b/lnclient/cashu/cashu.go @@ -11,8 +11,8 @@ import ( "github.com/elnosh/gonuts/wallet" "github.com/elnosh/gonuts/wallet/storage" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" ) diff --git a/lnclient/greenlight/greenlight.go b/lnclient/greenlight/greenlight.go index 7b2f1a12..93f60fb7 100644 --- a/lnclient/greenlight/greenlight.go +++ b/lnclient/greenlight/greenlight.go @@ -2,7 +2,6 @@ package greenlight import ( "context" - "encoding/hex" "errors" "log" "math/rand" @@ -13,15 +12,15 @@ import ( "strings" "time" - //"github.com/getAlby/nostr-wallet-connect/glalby" // for local development only + //"github.com/getAlby/hub/glalby" // for local development only "github.com/getAlby/glalby-go/glalby" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" ) type GreenlightService struct { @@ -133,7 +132,7 @@ func (gs *GreenlightService) SendKeysend(ctx context.Context, amount uint64, des for _, customRecord := range custom_records { extraTlvs = append(extraTlvs, glalby.TlvEntry{ Ty: customRecord.Type, - Value: hex.EncodeToString([]byte(customRecord.Value)), + Value: customRecord.Value, // glalby expects hex-encoded TLV values }) } diff --git a/lnclient/greenlight/models.go b/lnclient/greenlight/models.go index 4e9fddce..50e06097 100644 --- a/lnclient/greenlight/models.go +++ b/lnclient/greenlight/models.go @@ -1,6 +1,6 @@ package greenlight -import "github.com/getAlby/nostr-wallet-connect/lnclient" +import "github.com/getAlby/hub/lnclient" type NodeInfo struct { ID string `json:"id"` diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index 82b949fc..c37b1551 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -16,19 +16,19 @@ import ( "time" "github.com/getAlby/ldk-node-go/ldk_node" - // "github.com/getAlby/nostr-wallet-connect/ldk_node" + // "github.com/getAlby/hub/ldk_node" "encoding/hex" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/lsp" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/lsp" + "github.com/getAlby/hub/utils" ) type LDKService struct { @@ -61,12 +61,12 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events logDirPath := filepath.Join(newpath, "./logs") - config := ldk_node.DefaultConfig() + ldkConfig := ldk_node.DefaultConfig() listeningAddresses := []string{ "0.0.0.0:9735", "[::]:9735", } - config.TrustedPeers0conf = []string{ + ldkConfig.TrustedPeers0conf = []string{ lsp.OlympusLSP().Pubkey, lsp.AlbyPlebsLSP().Pubkey, lsp.MegalithLSP().Pubkey, @@ -76,10 +76,11 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events lsp.OlympusMutinynetLSP().Pubkey, lsp.MegalithMutinynetLSP().Pubkey, } - config.AnchorChannelsConfig.TrustedPeersNoReserve = []string{ + ldkConfig.AnchorChannelsConfig.TrustedPeersNoReserve = []string{ lsp.OlympusLSP().Pubkey, lsp.AlbyPlebsLSP().Pubkey, lsp.MegalithLSP().Pubkey, + "02b4552a7a85274e4da01a7c71ca57407181752e8568b31d51f13c111a2941dce3", // LNServer_Wave "0296b2db342fcf87ea94d981757fdf4d3e545bd5cef4919f58b5d38dfdd73bf5c9", // blocktank // Mutinynet @@ -88,13 +89,13 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events lsp.MegalithMutinynetLSP().Pubkey, } - config.ListeningAddresses = &listeningAddresses - config.LogDirPath = &logDirPath + ldkConfig.ListeningAddresses = &listeningAddresses + ldkConfig.LogDirPath = &logDirPath logLevel, err := strconv.Atoi(cfg.GetEnv().LDKLogLevel) if err == nil { - config.LogLevel = ldk_node.LogLevel(logLevel) + ldkConfig.LogLevel = ldk_node.LogLevel(logLevel) } - builder := ldk_node.BuilderFromConfig(config) + builder := ldk_node.BuilderFromConfig(ldkConfig) builder.SetEntropyBip39Mnemonic(mnemonic, nil) builder.SetNetwork(network) builder.SetEsploraServer(esploraServer) @@ -188,6 +189,16 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events err = node.SyncWallets() if err != nil { logger.Logger.WithError(err).Error("Failed to sync LDK wallets") + ls.eventPublisher.Publish(&events.Event{ + Event: "nwc_node_sync_failed", + Properties: map[string]interface{}{ + "error": err.Error(), + "sync_type": "full", + "initial_sync": true, + "node_type": config.LDKBackendType, + }, + }) + shutdownErr := ls.Shutdown() if shutdownErr != nil { logger.Logger.WithError(shutdownErr).Error("Failed to shutdown LDK node") @@ -246,6 +257,14 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events err = node.UpdateFeeEstimates() if err != nil { logger.Logger.WithError(err).Error("Failed to update fee estimates") + ls.eventPublisher.Publish(&events.Event{ + Event: "nwc_node_sync_failed", + Properties: map[string]interface{}{ + "error": err.Error(), + "sync_type": "fee_estimates", + "node_type": config.LDKBackendType, + }, + }) } if time.Since(ls.lastWalletSyncRequest) > MIN_SYNC_INTERVAL && time.Since(ls.lastSync) < MAX_SYNC_INTERVAL { @@ -259,6 +278,15 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events if err != nil { logger.Logger.WithError(err).Error("Failed to sync LDK wallets") + ls.eventPublisher.Publish(&events.Event{ + Event: "nwc_node_sync_failed", + Properties: map[string]interface{}{ + "error": err.Error(), + "sync_type": "full", + "node_type": config.LDKBackendType, + }, + }) + // try again at next MIN_SYNC_INTERVAL continue } @@ -498,9 +526,13 @@ func (ls *LDKService) SendKeysend(ctx context.Context, amount uint64, destinatio customTlvs := []ldk_node.TlvEntry{} for _, customRecord := range custom_records { + decodedValue, err := hex.DecodeString(customRecord.Value) + if err != nil { + return "", err + } customTlvs = append(customTlvs, ldk_node.TlvEntry{ Type: customRecord.Type, - Value: []uint8(customRecord.Value), + Value: decodedValue, }) } diff --git a/lnclient/ldk/ldk_event_broadcaster.go b/lnclient/ldk/ldk_event_broadcaster.go index 4b734b08..2dd46f40 100644 --- a/lnclient/ldk/ldk_event_broadcaster.go +++ b/lnclient/ldk/ldk_event_broadcaster.go @@ -6,8 +6,8 @@ import ( "time" "github.com/getAlby/ldk-node-go/ldk_node" - // "github.com/getAlby/nostr-wallet-connect/ldk_node" - "github.com/getAlby/nostr-wallet-connect/logger" + // "github.com/getAlby/hub/ldk_node" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" ) diff --git a/lnclient/lnd/lnd.go b/lnclient/lnd/lnd.go index 9c060c7b..e307925e 100644 --- a/lnclient/lnd/lnd.go +++ b/lnclient/lnd/lnd.go @@ -16,9 +16,9 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" decodepay "github.com/nbd-wtf/ln-decodepay" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/lnclient/lnd/wrapper" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/lnclient/lnd/wrapper" + "github.com/getAlby/hub/logger" "github.com/sirupsen/logrus" // "gorm.io/gorm" @@ -40,6 +40,7 @@ func (svc *LNDService) GetBalance(ctx context.Context) (balance int64, err error return int64(resp.LocalBalance.Msat), nil } +// FIXME: this always returns limit * 2 transactions and offset is not used correctly func (svc *LNDService) ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []lnclient.Transaction, err error) { // Fetch invoices var invoices []*lnrpc.Invoice @@ -358,7 +359,11 @@ func (svc *LNDService) SendKeysend(ctx context.Context, amount uint64, destinati destCustomRecords := map[uint64][]byte{} for _, record := range custom_records { - destCustomRecords[record.Type] = []byte(record.Value) + decodedValue, err := hex.DecodeString(record.Value) + if err != nil { + return "", err + } + destCustomRecords[record.Type] = decodedValue } const KEYSEND_CUSTOM_RECORD = 5482373484 destCustomRecords[KEYSEND_CUSTOM_RECORD] = preImageBytes @@ -726,7 +731,6 @@ func lndInvoiceToTransaction(invoice *lnrpc.Invoice) *lnclient.Transaction { Preimage: preimage, PaymentHash: hex.EncodeToString(invoice.RHash), Amount: invoice.ValueMsat, - FeesPaid: invoice.AmtPaidMsat, CreatedAt: invoice.CreationDate, SettledAt: settledAt, ExpiresAt: expiresAt, diff --git a/lnclient/phoenixd/phoenixd.go b/lnclient/phoenixd/phoenixd.go index a1820813..b40f5a1c 100644 --- a/lnclient/phoenixd/phoenixd.go +++ b/lnclient/phoenixd/phoenixd.go @@ -12,8 +12,8 @@ import ( "strings" "time" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/main_wails.go b/main_wails.go index 143fb644..0b021d42 100644 --- a/main_wails.go +++ b/main_wails.go @@ -7,9 +7,9 @@ import ( "context" "embed" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" - "github.com/getAlby/nostr-wallet-connect/wails" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" + "github.com/getAlby/hub/wails" log "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/decode_request.go b/nip47/controllers/decode_request.go index 2fba4c07..0daedc92 100644 --- a/nip47/controllers/decode_request.go +++ b/nip47/controllers/decode_request.go @@ -3,8 +3,8 @@ package controllers import ( "encoding/json" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/get_balance_controller.go b/nip47/controllers/get_balance_controller.go index a9a4f888..d1c3172b 100644 --- a/nip47/controllers/get_balance_controller.go +++ b/nip47/controllers/get_balance_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/get_balance_controller_test.go b/nip47/controllers/get_balance_controller_test.go index a2ab671f..65bd3378 100644 --- a/nip47/controllers/get_balance_controller_test.go +++ b/nip47/controllers/get_balance_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47GetBalanceJson = ` diff --git a/nip47/controllers/get_info_controller.go b/nip47/controllers/get_info_controller.go index 2073e500..7e4e69ea 100644 --- a/nip47/controllers/get_info_controller.go +++ b/nip47/controllers/get_info_controller.go @@ -3,11 +3,11 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - permissions "github.com/getAlby/nostr-wallet-connect/nip47/permissions" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" + permissions "github.com/getAlby/hub/nip47/permissions" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/get_info_controller_test.go b/nip47/controllers/get_info_controller_test.go index 835dc360..26c66759 100644 --- a/nip47/controllers/get_info_controller_test.go +++ b/nip47/controllers/get_info_controller_test.go @@ -8,10 +8,10 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/tests" ) const nip47GetInfoJson = ` diff --git a/nip47/controllers/list_transactions_controller.go b/nip47/controllers/list_transactions_controller.go index e5d7c261..914e28d1 100644 --- a/nip47/controllers/list_transactions_controller.go +++ b/nip47/controllers/list_transactions_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/list_transactions_controller_test.go b/nip47/controllers/list_transactions_controller_test.go index b838b73d..dfb61857 100644 --- a/nip47/controllers/list_transactions_controller_test.go +++ b/nip47/controllers/list_transactions_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47ListTransactionsJson = ` diff --git a/nip47/controllers/lookup_invoice_controller.go b/nip47/controllers/lookup_invoice_controller.go index 924d127f..de932438 100644 --- a/nip47/controllers/lookup_invoice_controller.go +++ b/nip47/controllers/lookup_invoice_controller.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/nip47/controllers/lookup_invoice_controller_test.go b/nip47/controllers/lookup_invoice_controller_test.go index 2434b6c2..9d8b396a 100644 --- a/nip47/controllers/lookup_invoice_controller_test.go +++ b/nip47/controllers/lookup_invoice_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47LookupInvoiceJson = ` diff --git a/nip47/controllers/make_invoice_controller.go b/nip47/controllers/make_invoice_controller.go index 7998b936..29d2746a 100644 --- a/nip47/controllers/make_invoice_controller.go +++ b/nip47/controllers/make_invoice_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/controllers/make_invoice_controller_test.go b/nip47/controllers/make_invoice_controller_test.go index fc7fb436..2e4dd6e0 100644 --- a/nip47/controllers/make_invoice_controller_test.go +++ b/nip47/controllers/make_invoice_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47MakeInvoiceJson = ` diff --git a/nip47/controllers/models.go b/nip47/controllers/models.go index 7cc6ef60..9ccec5c3 100644 --- a/nip47/controllers/models.go +++ b/nip47/controllers/models.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" ) diff --git a/nip47/controllers/multi_pay_invoice_controller.go b/nip47/controllers/multi_pay_invoice_controller.go index 90b2c92c..f5736a44 100644 --- a/nip47/controllers/multi_pay_invoice_controller.go +++ b/nip47/controllers/multi_pay_invoice_controller.go @@ -6,11 +6,11 @@ import ( "strings" "sync" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/nip47/controllers/multi_pay_invoice_controller_test.go b/nip47/controllers/multi_pay_invoice_controller_test.go index 651484e4..ad5aeaf0 100644 --- a/nip47/controllers/multi_pay_invoice_controller_test.go +++ b/nip47/controllers/multi_pay_invoice_controller_test.go @@ -9,9 +9,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47MultiPayJson = ` diff --git a/nip47/controllers/multi_pay_keysend_controller.go b/nip47/controllers/multi_pay_keysend_controller.go index 29d9da58..7f5bb225 100644 --- a/nip47/controllers/multi_pay_keysend_controller.go +++ b/nip47/controllers/multi_pay_keysend_controller.go @@ -4,10 +4,10 @@ import ( "context" "sync" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "gorm.io/gorm" ) diff --git a/nip47/controllers/multi_pay_keysend_controller_test.go b/nip47/controllers/multi_pay_keysend_controller_test.go index e6279433..a1bab5a4 100644 --- a/nip47/controllers/multi_pay_keysend_controller_test.go +++ b/nip47/controllers/multi_pay_keysend_controller_test.go @@ -9,9 +9,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47MultiPayKeysendJson = ` diff --git a/nip47/controllers/pay_invoice_controller.go b/nip47/controllers/pay_invoice_controller.go index 1d3aa0c3..6a569e06 100644 --- a/nip47/controllers/pay_invoice_controller.go +++ b/nip47/controllers/pay_invoice_controller.go @@ -5,11 +5,11 @@ import ( "fmt" "strings" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" decodepay "github.com/nbd-wtf/ln-decodepay" "github.com/sirupsen/logrus" diff --git a/nip47/controllers/pay_invoice_controller_test.go b/nip47/controllers/pay_invoice_controller_test.go index 92b17502..683a3344 100644 --- a/nip47/controllers/pay_invoice_controller_test.go +++ b/nip47/controllers/pay_invoice_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47PayInvoiceJson = ` diff --git a/nip47/controllers/pay_keysend_controller.go b/nip47/controllers/pay_keysend_controller.go index 734f3794..6aab0d29 100644 --- a/nip47/controllers/pay_keysend_controller.go +++ b/nip47/controllers/pay_keysend_controller.go @@ -3,11 +3,11 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" "gorm.io/gorm" diff --git a/nip47/controllers/pay_keysend_controller_test.go b/nip47/controllers/pay_keysend_controller_test.go index d973e81a..4bd72420 100644 --- a/nip47/controllers/pay_keysend_controller_test.go +++ b/nip47/controllers/pay_keysend_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/stretchr/testify/assert" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" ) const nip47KeysendJson = ` diff --git a/nip47/controllers/sign_message_controller.go b/nip47/controllers/sign_message_controller.go index 374ad633..10ea0ed2 100644 --- a/nip47/controllers/sign_message_controller.go +++ b/nip47/controllers/sign_message_controller.go @@ -3,9 +3,9 @@ package controllers import ( "context" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" "github.com/sirupsen/logrus" ) diff --git a/nip47/event_handler.go b/nip47/event_handler.go index e606a655..c84cbb62 100644 --- a/nip47/event_handler.go +++ b/nip47/event_handler.go @@ -7,13 +7,13 @@ import ( "fmt" "time" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - controllers "github.com/getAlby/nostr-wallet-connect/nip47/controllers" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + controllers "github.com/getAlby/hub/nip47/controllers" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/nip47/permissions" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/sirupsen/logrus" diff --git a/nip47/event_handler_test.go b/nip47/event_handler_test.go index b1e874f6..2ce7545a 100644 --- a/nip47/event_handler_test.go +++ b/nip47/event_handler_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/stretchr/testify/assert" diff --git a/nip47/models/models.go b/nip47/models/models.go index c74d49e5..524a0aad 100644 --- a/nip47/models/models.go +++ b/nip47/models/models.go @@ -3,7 +3,7 @@ package models import ( "encoding/json" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/lnclient" ) const ( diff --git a/nip47/nip47_service.go b/nip47/nip47_service.go index fee56a5b..aa365bbb 100644 --- a/nip47/nip47_service.go +++ b/nip47/nip47_service.go @@ -3,12 +3,12 @@ package nip47 import ( "context" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47/notifications" - permissions "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47/notifications" + permissions "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/service/keys" "github.com/nbd-wtf/go-nostr" "gorm.io/gorm" ) diff --git a/nip47/notifications/models.go b/nip47/notifications/models.go index 22b416c8..888c81d8 100644 --- a/nip47/notifications/models.go +++ b/nip47/notifications/models.go @@ -1,6 +1,6 @@ package notifications -import "github.com/getAlby/nostr-wallet-connect/nip47/models" +import "github.com/getAlby/hub/nip47/models" type Notification struct { Notification interface{} `json:"notification,omitempty"` diff --git a/nip47/notifications/nip47_notification_queue.go b/nip47/notifications/nip47_notification_queue.go index 59bf954b..e5bb5b4b 100644 --- a/nip47/notifications/nip47_notification_queue.go +++ b/nip47/notifications/nip47_notification_queue.go @@ -4,8 +4,8 @@ import ( "context" "errors" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" ) type Nip47NotificationQueue interface { diff --git a/nip47/notifications/nip47_notifier.go b/nip47/notifications/nip47_notifier.go index 49b36b8a..e0e7dbd4 100644 --- a/nip47/notifications/nip47_notifier.go +++ b/nip47/notifications/nip47_notifier.go @@ -5,14 +5,14 @@ import ( "encoding/json" "errors" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/service/keys" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/sirupsen/logrus" diff --git a/nip47/notifications/nip47_notifier_test.go b/nip47/notifications/nip47_notifier_test.go index 08bc9e1b..e35b31f2 100644 --- a/nip47/notifications/nip47_notifier_test.go +++ b/nip47/notifications/nip47_notifier_test.go @@ -6,10 +6,10 @@ import ( "log" "testing" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/nip47/permissions" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/nip47/permissions" + "github.com/getAlby/hub/tests" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" "github.com/stretchr/testify/assert" diff --git a/nip47/permissions/permissions.go b/nip47/permissions/permissions.go index 523f828f..7040c5f4 100644 --- a/nip47/permissions/permissions.go +++ b/nip47/permissions/permissions.go @@ -5,12 +5,12 @@ import ( "slices" "time" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/utils" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/utils" "github.com/sirupsen/logrus" "gorm.io/gorm" ) diff --git a/nip47/permissions/permissions_test.go b/nip47/permissions/permissions_test.go index 7e53146b..a5224bcc 100644 --- a/nip47/permissions/permissions_test.go +++ b/nip47/permissions/permissions_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/nip47/models" - "github.com/getAlby/nostr-wallet-connect/tests" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/nip47/models" + "github.com/getAlby/hub/tests" "github.com/stretchr/testify/assert" ) diff --git a/nip47/publish_nip47_info.go b/nip47/publish_nip47_info.go index bb877a9e..fe7877ae 100644 --- a/nip47/publish_nip47_info.go +++ b/nip47/publish_nip47_info.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47/models" "github.com/nbd-wtf/go-nostr" ) diff --git a/render.yaml b/render.yaml index d6f09e8b..26f796bc 100644 --- a/render.yaml +++ b/render.yaml @@ -3,7 +3,7 @@ services: runtime: image name: albyhub image: - url: ghcr.io/getalby/nostr-wallet-connect-next:latest + url: ghcr.io/getalby/hub:latest numInstances: 1 region: frankfurt # Default: oregon plan: starter @@ -18,4 +18,4 @@ services: - key: LOG_EVENTS value: true - key: LDK_ESPLORA_SERVER - value: "https://electrs.albylabs.com" + value: "https://electrs.getalby.com" diff --git a/scripts/README.md b/scripts/README.md index 781859b7..5453f825 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,11 +1,11 @@ ### Installation on a Raspberry Pi Zero ```shell -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/install.sh)" +/bin/bash -c "$(curl -fsSL https://getalby.com/zero/install.sh)" ``` ### Updating a running instance ```shell -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/update.sh)" +/bin/bash -c "$(curl -fsSL https://getalby.com/zero/update.sh)" ``` diff --git a/scripts/install.sh b/scripts/install.sh index bd03dc89..72813d9a 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -34,7 +34,7 @@ CPUQuota=90% Environment="PORT=80" Environment="WORK_DIR=/opt/albyhub/data" -Environment="LDK_ESPLORA_SERVER=https://electrs.albylabs.com" +Environment="LDK_ESPLORA_SERVER=https://electrs.getalby.com" Environment="LOG_EVENTS=true" Environment="LDK_GOSSIP_SOURCE=" diff --git a/scripts/linux-x86_64/phoenixd/README.md b/scripts/linux-x86_64/phoenixd/README.md index f1472322..79ecddc0 100644 --- a/scripts/linux-x86_64/phoenixd/README.md +++ b/scripts/linux-x86_64/phoenixd/README.md @@ -4,14 +4,14 @@ Run your Alby Hub with phoenixd as a backend. ## Requirements -+ Linux distribution -+ Runs pretty much on any VPS or server +- Linux distribution +- Runs pretty much on any VPS or server ## Docker -To run Alby Hub with phoenixd use [docker-compose](https://docs.docker.com/compose/) using the [docker-compose.yml file](https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/linux-x86_64/phoenixd/docker-compose.yml). +To run Alby Hub with phoenixd use [docker-compose](https://docs.docker.com/compose/) using the [docker-compose.yml file](https://raw.githubusercontent.com/getAlby/hub/master/scripts/linux-x86_64/phoenixd/docker-compose.yml). - $ wget https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/linux-x86_64/phoenixd/docker-compose.yml + $ wget https://raw.githubusercontent.com/getAlby/hub/master/scripts/linux-x86_64/phoenixd/docker-compose.yml $ docker-compose up # or docker-compose up --pull=always <- to make sure you get the latest images It will run on localhost:8080 by default. You can configure the port by editing the docker-compose.yml file. @@ -26,7 +26,7 @@ Make sure to backup the `albyhub-phoenixd` which is used as volume for albyhub a ### Installation (non-Docker) - $ wget https://raw.githubusercontent.com/getAlby/nostr-wallet-connect-next/master/scripts/linux-x86_64/phoenixd/install.sh + $ wget https://raw.githubusercontent.com/getAlby/hub/master/scripts/linux-x86_64/phoenixd/install.sh $ ./install.sh The install script will prompt you for a installation folder and will install phoenixd and Alby Hub there. @@ -47,10 +47,9 @@ Or us the start scripts: $ [your install path]/phoenixd/start.sh $ [your install path]/albyhub/start.sh - ### Backup Make sure to backup your data directories: -+ `[your install path]/phoenixd/data` -+ `[your install path]/albyhub/data` +- `[your install path]/phoenixd/data` +- `[your install path]/albyhub/data` diff --git a/scripts/linux-x86_64/phoenixd/docker-compose.yml b/scripts/linux-x86_64/phoenixd/docker-compose.yml index 794e03b9..9f7b5ef0 100644 --- a/scripts/linux-x86_64/phoenixd/docker-compose.yml +++ b/scripts/linux-x86_64/phoenixd/docker-compose.yml @@ -2,7 +2,7 @@ services: albyhub: platform: linux/amd64 container_name: albyhub - image: ghcr.io/getalby/nostr-wallet-connect-next:latest + image: ghcr.io/getalby/hub:latest volumes: - ./albyhub-phoenixd:/data ports: diff --git a/service/keys/keys.go b/service/keys/keys.go index 7135db4d..cde80dd9 100644 --- a/service/keys/keys.go +++ b/service/keys/keys.go @@ -1,8 +1,8 @@ package keys import ( - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/logger" "github.com/nbd-wtf/go-nostr" ) diff --git a/service/models.go b/service/models.go index 8bdb7c3f..bd056b53 100644 --- a/service/models.go +++ b/service/models.go @@ -1,11 +1,11 @@ package service import ( - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/service/keys" "gorm.io/gorm" ) diff --git a/service/service.go b/service/service.go index f1e30ad2..63a4af84 100644 --- a/service/service.go +++ b/service/service.go @@ -16,17 +16,17 @@ import ( "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service/keys" - "github.com/getAlby/nostr-wallet-connect/version" - - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/nip47" - "github.com/getAlby/nostr-wallet-connect/nip47/models" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service/keys" + "github.com/getAlby/hub/version" + + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/nip47" + "github.com/getAlby/hub/nip47/models" ) type service struct { @@ -56,7 +56,7 @@ func NewService(ctx context.Context) (*service, error) { logger.Logger.Info("AlbyHub " + version.Tag) if appConfig.Workdir == "" { - appConfig.Workdir = filepath.Join(xdg.DataHome, "/alby-nwc") + appConfig.Workdir = filepath.Join(xdg.DataHome, "/albyhub") logger.Logger.WithField("workdir", appConfig.Workdir).Info("No workdir specified, using default") } // make sure workdir exists diff --git a/service/start.go b/service/start.go index 2f44774c..83715e41 100644 --- a/service/start.go +++ b/service/start.go @@ -10,16 +10,16 @@ import ( "github.com/nbd-wtf/go-nostr/nip19" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/lnclient/breez" - "github.com/getAlby/nostr-wallet-connect/lnclient/cashu" - "github.com/getAlby/nostr-wallet-connect/lnclient/greenlight" - "github.com/getAlby/nostr-wallet-connect/lnclient/ldk" - "github.com/getAlby/nostr-wallet-connect/lnclient/lnd" - "github.com/getAlby/nostr-wallet-connect/lnclient/phoenixd" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/lnclient/breez" + "github.com/getAlby/hub/lnclient/cashu" + "github.com/getAlby/hub/lnclient/greenlight" + "github.com/getAlby/hub/lnclient/ldk" + "github.com/getAlby/hub/lnclient/lnd" + "github.com/getAlby/hub/lnclient/phoenixd" + "github.com/getAlby/hub/logger" ) func (svc *service) StartNostr(ctx context.Context, encryptionKey string) error { @@ -39,7 +39,7 @@ func (svc *service) StartNostr(ctx context.Context, encryptionKey string) error logger.Logger.WithFields(logrus.Fields{ "npub": npub, "hex": svc.keys.GetNostrPublicKey(), - }).Info("Starting nostr-wallet-connect") + }).Info("Starting Alby Hub") svc.wg.Add(1) go func() { //Start infinite loop which will be only broken by canceling ctx (SIGINT) diff --git a/service/stop.go b/service/stop.go index cdff012c..2ec0fe03 100644 --- a/service/stop.go +++ b/service/stop.go @@ -3,8 +3,8 @@ package service import ( "fmt" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/logger" ) // TODO: this should happen on ctx.Done() rather than having to call manually diff --git a/tests/create_app.go b/tests/create_app.go index 7acb676a..9a72ba10 100644 --- a/tests/create_app.go +++ b/tests/create_app.go @@ -1,7 +1,7 @@ package tests import ( - "github.com/getAlby/nostr-wallet-connect/db" + "github.com/getAlby/hub/db" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip04" ) diff --git a/tests/mock_ln_client.go b/tests/mock_ln_client.go index d7f5207e..b1e5d625 100644 --- a/tests/mock_ln_client.go +++ b/tests/mock_ln_client.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/getAlby/nostr-wallet-connect/lnclient" + "github.com/getAlby/hub/lnclient" ) // for the invoice: diff --git a/tests/test_service.go b/tests/test_service.go index 2d3c480a..ec3a5b6b 100644 --- a/tests/test_service.go +++ b/tests/test_service.go @@ -3,12 +3,12 @@ package tests import ( "os" - "github.com/getAlby/nostr-wallet-connect/config" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/events" - "github.com/getAlby/nostr-wallet-connect/lnclient" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service/keys" + "github.com/getAlby/hub/config" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/events" + "github.com/getAlby/hub/lnclient" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service/keys" "gorm.io/gorm" ) diff --git a/version/version.go b/version/version.go index e2c5b151..ec4069bc 100644 --- a/version/version.go +++ b/version/version.go @@ -1,82 +1,3 @@ package version -import ( - "encoding/json" - "io" - "net/http" - "time" - - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/sirupsen/logrus" -) - -var Tag string = "Unknown Version" - -type githubRelease struct { - TagName string `json:"tag_name"` -} - -var latestRelease = "" -var lastVersionCheck = time.Time{} - -func GetLatestReleaseTag() string { - if latestRelease != "" && time.Since(lastVersionCheck) < 5*time.Minute { - return latestRelease - } - url := "https://api.github.com/repos/getAlby/nostr-wallet-connect-next/releases" - - client := http.Client{ - Timeout: time.Second * 10, - } - - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - logger.Logger.WithError(err).WithFields(logrus.Fields{ - "url": url, - }).Error("Failed to create http request") - return "" - } - - res, err := client.Do(req) - if err != nil { - logger.Logger.WithError(err).WithFields(logrus.Fields{ - "url": url, - }).Error("Failed to send request") - return "" - } - - defer res.Body.Close() - - body, readErr := io.ReadAll(res.Body) - if readErr != nil { - logger.Logger.WithError(err).WithFields(logrus.Fields{ - "url": url, - }).Error("Failed to read response body") - return "" - } - - releases := []githubRelease{} - jsonErr := json.Unmarshal(body, &releases) - if jsonErr != nil { - logger.Logger.WithError(jsonErr).WithFields(logrus.Fields{ - "url": url, - }).Error("Failed to deserialize json") - return "" - } - - if len(releases) < 1 { - logger.Logger.Error("no github releases found") - return "" - } - - latestRelease = releases[0].TagName - - logger.Logger.WithFields(logrus.Fields{ - "latest": latestRelease, - "current": Tag, - }).Info("Found latest github release") - - lastVersionCheck = time.Now() - - return latestRelease -} +var Tag string = "" diff --git a/wails.json b/wails.json index 924d8e75..64b30e3c 100644 --- a/wails.json +++ b/wails.json @@ -1,7 +1,7 @@ { "$schema": "https://wails.io/schemas/config.v2.json", - "name": "AlbyHub", - "outputfilename": "Nostr-Wallet-Connect", + "name": "albyhub", + "outputfilename": "albyhub", "frontend:install": "yarn install", "frontend:build": "yarn build:wails", "frontend:dev:watcher": "yarn dev:wails", diff --git a/wails/wails_app.go b/wails/wails_app.go index 001c109e..4425476e 100644 --- a/wails/wails_app.go +++ b/wails/wails_app.go @@ -5,9 +5,9 @@ import ( "embed" "log" - "github.com/getAlby/nostr-wallet-connect/api" - "github.com/getAlby/nostr-wallet-connect/logger" - "github.com/getAlby/nostr-wallet-connect/service" + "github.com/getAlby/hub/api" + "github.com/getAlby/hub/logger" + "github.com/getAlby/hub/service" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" diff --git a/wails/wails_handlers.go b/wails/wails_handlers.go index 5b9f4109..d5e27969 100644 --- a/wails/wails_handlers.go +++ b/wails/wails_handlers.go @@ -5,14 +5,15 @@ import ( "fmt" "os" "regexp" + "strconv" "strings" "github.com/sirupsen/logrus" - "github.com/getAlby/nostr-wallet-connect/alby" - "github.com/getAlby/nostr-wallet-connect/api" - "github.com/getAlby/nostr-wallet-connect/db" - "github.com/getAlby/nostr-wallet-connect/logger" + "github.com/getAlby/hub/alby" + "github.com/getAlby/hub/api" + "github.com/getAlby/hub/db" + "github.com/getAlby/hub/logger" "github.com/wailsapp/wails/v2/pkg/runtime" ) @@ -201,6 +202,38 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string return WailsRequestRouterResponse{Body: paymentInfo, Error: ""} } + listTransactionsRegex := regexp.MustCompile( + `/api/transactions`, + ) + + switch { + case listTransactionsRegex.MatchString(route): + limit := uint64(20) + offset := uint64(0) + + // Extract limit and offset parameters + paramRegex := regexp.MustCompile(`[?&](limit|offset)=([^&]+)`) + paramMatches := paramRegex.FindAllStringSubmatch(route, -1) + for _, match := range paramMatches { + switch match[1] { + case "limit": + if parsedLimit, err := strconv.ParseUint(match[2], 10, 64); err == nil { + limit = parsedLimit + } + case "offset": + if parsedOffset, err := strconv.ParseUint(match[2], 10, 64); err == nil { + offset = parsedOffset + } + } + } + + transactions, err := app.api.ListTransactions(ctx, limit, offset) + if err != nil { + return WailsRequestRouterResponse{Body: nil, Error: err.Error()} + } + return WailsRequestRouterResponse{Body: transactions, Error: ""} + } + paymentRegex := regexp.MustCompile( `/api/payments/([0-9a-zA-Z]+)`, ) @@ -371,12 +404,6 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string } res := WailsRequestRouterResponse{Body: invoice, Error: ""} return res - case "/api/transactions": - transactions, err := app.api.ListTransactions(ctx) - if err != nil { - return WailsRequestRouterResponse{Body: nil, Error: err.Error()} - } - return WailsRequestRouterResponse{Body: transactions, Error: ""} case "/api/wallet/sync": app.api.SyncWallet() return WailsRequestRouterResponse{Body: nil, Error: ""} @@ -478,7 +505,17 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string res := WailsRequestRouterResponse{Body: *infoResponse, Error: ""} return res case "/api/alby/link-account": - err := app.svc.GetAlbyOAuthSvc().LinkAccount(ctx, app.svc.GetLNClient()) + linkAccountRequest := &alby.AlbyLinkAccountRequest{} + err := json.Unmarshal([]byte(body), linkAccountRequest) + if err != nil { + logger.Logger.WithFields(logrus.Fields{ + "route": route, + "method": method, + "body": body, + }).WithError(err).Error("Failed to decode request to wails router") + return WailsRequestRouterResponse{Body: nil, Error: err.Error()} + } + err = app.svc.GetAlbyOAuthSvc().LinkAccount(ctx, app.svc.GetLNClient(), linkAccountRequest.Budget, linkAccountRequest.Renewal) if err != nil { return WailsRequestRouterResponse{Body: nil, Error: err.Error()} }