mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
Feat: NWA auth for self-hosted hubs (#1016)
* feat: nwc create_connection command (WIP) * feat: allow creating superuser apps from the ui * fix: pass methods rather than scopes in create_connection method * chore: add extra tests * fix: use browser router in http mode * fix: update links to not use hash router * fix: add redirect from hash router url * fix: return nostrWalletConnectUrl in nwc connection success event and message * feat: publish nwa event * chore: use nwc info event instead of nwa event * feat: create custom alby go detail page * chore: allow creating/editing apps with the same name * fix: convert budget from msats to sats (#1111) * chore: address NWA feedback * chore: address feedback - adjust button copy - make create_connection methods consistent with http deeplink flow - remove unused constant - add empty string check before adding lud16 tag - fix test * feat: add support for notification_types in create_connection method * fix: shorter button copy * fix: do not include lud16 tag in published info event * Feat: add lud16 to get_info response (#1128) feat: add lud16 to get_info response * chore: minor alby go screen improvements * fix: incorrect unlock password error message to create app with superuser access * chore: minor ui improvements on alby go detail page * chore: avoid duplicate app names by adding a suffix * chore: move scopes check to apps service, add new tests, DRY controller test setup * fix: scopes component full access scopes and isolated scope group check * fix: use supported capabilities for Alby Go * fix: return correct app name * chore: address minor comments --------- Co-authored-by: im-adithya <imadithyavardhan@gmail.com>
This commit is contained in:
parent
1d5730ad15
commit
3e1d16d423
49 changed files with 1856 additions and 297 deletions
|
|
@ -14,3 +14,6 @@ packages:
|
|||
github.com/getAlby/hub/lnclient:
|
||||
interfaces:
|
||||
LNClient:
|
||||
github.com/getAlby/hub/config:
|
||||
interfaces:
|
||||
Config:
|
||||
|
|
|
|||
|
|
@ -673,7 +673,7 @@ func (svc *albyOAuthService) LinkAccount(ctx context.Context, lnClient lnclient.
|
|||
scopes = append(scopes, constants.NOTIFICATIONS_SCOPE)
|
||||
}
|
||||
|
||||
app, _, err := apps.NewAppsService(svc.db, svc.eventPublisher, svc.keys).CreateApp(
|
||||
app, _, err := apps.NewAppsService(svc.db, svc.eventPublisher, svc.keys, svc.cfg).CreateApp(
|
||||
ALBY_ACCOUNT_APP_NAME,
|
||||
connectionPubkey,
|
||||
budget,
|
||||
|
|
|
|||
37
api/api.go
37
api/api.go
|
|
@ -48,7 +48,7 @@ type api struct {
|
|||
func NewAPI(svc service.Service, gormDB *gorm.DB, config config.Config, keys keys.Keys, albyOAuthSvc alby.AlbyOAuthService, eventPublisher events.EventPublisher) *api {
|
||||
return &api{
|
||||
db: gormDB,
|
||||
appsSvc: apps.NewAppsService(gormDB, eventPublisher, keys),
|
||||
appsSvc: apps.NewAppsService(gormDB, eventPublisher, keys, config),
|
||||
cfg: config,
|
||||
svc: svc,
|
||||
permissionsSvc: permissions.NewPermissionsService(gormDB, eventPublisher),
|
||||
|
|
@ -58,13 +58,11 @@ func NewAPI(svc service.Service, gormDB *gorm.DB, config config.Config, keys key
|
|||
}
|
||||
|
||||
func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppResponse, error) {
|
||||
backendType, _ := api.cfg.Get("LNBackendType", "")
|
||||
if createAppRequest.Isolated &&
|
||||
backendType != config.LDKBackendType &&
|
||||
backendType != config.LNDBackendType &&
|
||||
backendType != config.PhoenixBackendType {
|
||||
return nil, fmt.Errorf(
|
||||
"sub-wallets are currently not supported on your node backend. Try LDK or LND")
|
||||
if slices.Contains(createAppRequest.Scopes, constants.SUPERUSER_SCOPE) {
|
||||
if !api.cfg.CheckUnlockPassword(createAppRequest.UnlockPassword) {
|
||||
return nil, fmt.Errorf(
|
||||
"incorrect unlock password to create app with superuser permission")
|
||||
}
|
||||
}
|
||||
|
||||
expiresAt, err := api.parseExpiresAt(createAppRequest.ExpiresAt)
|
||||
|
|
@ -72,10 +70,6 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons
|
|||
return nil, fmt.Errorf("invalid expiresAt: %v", err)
|
||||
}
|
||||
|
||||
if len(createAppRequest.Scopes) == 0 {
|
||||
return nil, fmt.Errorf("won't create an app without scopes")
|
||||
}
|
||||
|
||||
for _, scope := range createAppRequest.Scopes {
|
||||
if !slices.Contains(permissions.AllScopes(), scope) {
|
||||
return nil, fmt.Errorf("did not recognize requested scope: %s", scope)
|
||||
|
|
@ -106,7 +100,7 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons
|
|||
|
||||
responseBody := &CreateAppResponse{}
|
||||
responseBody.Id = app.ID
|
||||
responseBody.Name = createAppRequest.Name
|
||||
responseBody.Name = app.Name
|
||||
responseBody.Pubkey = app.AppPubkey
|
||||
responseBody.PairingSecret = pairingSecretKey
|
||||
responseBody.WalletPubkey = *app.WalletPubkey
|
||||
|
|
@ -208,12 +202,17 @@ func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) e
|
|||
existingScopeMap[perm.Scope] = true
|
||||
}
|
||||
|
||||
if slices.Contains(newScopes, constants.SUPERUSER_SCOPE) && !existingScopeMap[constants.SUPERUSER_SCOPE] {
|
||||
return fmt.Errorf(
|
||||
"cannot update app to add superuser permission")
|
||||
}
|
||||
|
||||
// Add new permissions
|
||||
for _, method := range newScopes {
|
||||
if !existingScopeMap[method] {
|
||||
for _, scope := range newScopes {
|
||||
if !existingScopeMap[scope] {
|
||||
perm := db.AppPermission{
|
||||
App: *userApp,
|
||||
Scope: method,
|
||||
Scope: scope,
|
||||
ExpiresAt: expiresAt,
|
||||
MaxAmountSat: int(maxAmount),
|
||||
BudgetRenewal: budgetRenewal,
|
||||
|
|
@ -222,12 +221,12 @@ func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) e
|
|||
return err
|
||||
}
|
||||
}
|
||||
delete(existingScopeMap, method)
|
||||
delete(existingScopeMap, scope)
|
||||
}
|
||||
|
||||
// Remove old permissions
|
||||
for method := range existingScopeMap {
|
||||
if err := tx.Where("app_id = ? AND scope = ?", userApp.ID, method).Delete(&db.AppPermission{}).Error; err != nil {
|
||||
for scope := range existingScopeMap {
|
||||
if err := tx.Where("app_id = ? AND scope = ?", userApp.ID, scope).Delete(&db.AppPermission{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
api/apps_test.go
Normal file
23
api/apps_test.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/tests/mocks"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateApp_SuperuserScopeIncorrectPassword(t *testing.T) {
|
||||
cfg := mocks.NewMockConfig(t)
|
||||
cfg.On("CheckUnlockPassword", "").Return(false)
|
||||
theAPI := &api{svc: mocks.NewMockService(t), cfg: cfg}
|
||||
response, err := theAPI.CreateApp(&CreateAppRequest{
|
||||
Scopes: []string{constants.SUPERUSER_SCOPE},
|
||||
})
|
||||
|
||||
assert.Nil(t, response)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, "incorrect unlock password to create app with superuser permission", err.Error())
|
||||
}
|
||||
|
|
@ -99,15 +99,16 @@ type TopupIsolatedAppRequest struct {
|
|||
}
|
||||
|
||||
type CreateAppRequest struct {
|
||||
Name string `json:"name"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
MaxAmountSat uint64 `json:"maxAmount"`
|
||||
BudgetRenewal string `json:"budgetRenewal"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
Scopes []string `json:"scopes"`
|
||||
ReturnTo string `json:"returnTo"`
|
||||
Isolated bool `json:"isolated"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
MaxAmountSat uint64 `json:"maxAmount"`
|
||||
BudgetRenewal string `json:"budgetRenewal"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
Scopes []string `json:"scopes"`
|
||||
ReturnTo string `json:"returnTo"`
|
||||
Isolated bool `json:"isolated"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
UnlockPassword string `json:"unlockPassword"`
|
||||
}
|
||||
|
||||
type StartRequest struct {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/hub/config"
|
||||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/events"
|
||||
|
|
@ -28,20 +30,45 @@ type appsService struct {
|
|||
db *gorm.DB
|
||||
eventPublisher events.EventPublisher
|
||||
keys keys.Keys
|
||||
cfg config.Config
|
||||
}
|
||||
|
||||
func NewAppsService(db *gorm.DB, eventPublisher events.EventPublisher, keys keys.Keys) *appsService {
|
||||
func NewAppsService(db *gorm.DB, eventPublisher events.EventPublisher, keys keys.Keys, cfg config.Config) *appsService {
|
||||
return &appsService{
|
||||
db: db,
|
||||
eventPublisher: eventPublisher,
|
||||
keys: keys,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *appsService) CreateApp(name string, pubkey string, maxAmountSat uint64, budgetRenewal string, expiresAt *time.Time, scopes []string, isolated bool, metadata map[string]interface{}) (*db.App, string, error) {
|
||||
if isolated && (slices.Contains(scopes, constants.SIGN_MESSAGE_SCOPE)) {
|
||||
// cannot sign messages because the isolated app is a custodial sub-wallet
|
||||
return nil, "", errors.New("Sub-wallet app connection cannot have sign_message scope")
|
||||
if isolated {
|
||||
if slices.Contains(scopes, constants.SIGN_MESSAGE_SCOPE) {
|
||||
// cannot sign messages because the isolated app is a custodial sub-wallet
|
||||
return nil, "", errors.New("Sub-wallet app connection cannot have sign_message scope")
|
||||
}
|
||||
|
||||
backendType, _ := svc.cfg.Get("LNBackendType", "")
|
||||
if backendType != config.LDKBackendType &&
|
||||
backendType != config.LNDBackendType &&
|
||||
backendType != config.PhoenixBackendType {
|
||||
return nil, "", fmt.Errorf(
|
||||
"sub-wallets are currently not supported on your node backend. Try LDK or LND")
|
||||
}
|
||||
}
|
||||
|
||||
if budgetRenewal == "" {
|
||||
budgetRenewal = constants.BUDGET_RENEWAL_NEVER
|
||||
}
|
||||
|
||||
if !slices.Contains(constants.GetBudgetRenewals(), budgetRenewal) {
|
||||
return nil, "", fmt.Errorf("invalid budget renewal. Must be one of %s", strings.Join(constants.GetBudgetRenewals(), ","))
|
||||
}
|
||||
|
||||
// ensure there is at least one scope
|
||||
if scopes == nil || len(scopes) == 0 {
|
||||
return nil, "", errors.New("no scopes provided")
|
||||
}
|
||||
|
||||
var pairingPublicKey string
|
||||
|
|
@ -69,7 +96,21 @@ func (svc *appsService) CreateApp(name string, pubkey string, maxAmountSat uint6
|
|||
}
|
||||
}
|
||||
|
||||
app := db.App{Name: name, AppPubkey: pairingPublicKey, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)}
|
||||
// use a suffix to avoid duplicate names
|
||||
nameIndex := 0
|
||||
var freeName string
|
||||
for ; ; nameIndex++ {
|
||||
freeName = name
|
||||
if nameIndex > 0 {
|
||||
freeName += fmt.Sprintf(" (%d)", nameIndex)
|
||||
}
|
||||
existingApp := svc.GetAppByName(freeName)
|
||||
if existingApp == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
app := db.App{Name: freeName, AppPubkey: pairingPublicKey, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)}
|
||||
|
||||
err := svc.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Save(&app).Error
|
||||
|
|
@ -151,3 +192,12 @@ func (svc *appsService) GetAppByPubkey(pubkey string) *db.App {
|
|||
}
|
||||
return &dbApp
|
||||
}
|
||||
|
||||
func (svc *appsService) GetAppByName(name string) *db.App {
|
||||
dbApp := db.App{}
|
||||
findResult := svc.db.Where("name = ?", name).First(&dbApp)
|
||||
if findResult.RowsAffected == 0 {
|
||||
return nil
|
||||
}
|
||||
return &dbApp
|
||||
}
|
||||
|
|
|
|||
58
apps/tests/apps_service_test.go
Normal file
58
apps/tests/apps_service_test.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/getAlby/hub/apps"
|
||||
"github.com/getAlby/hub/config"
|
||||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHandleCreateApp_NilScopes(t *testing.T) {
|
||||
// ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
appsService := apps.NewAppsService(svc.DB, svc.EventPublisher, svc.Keys, svc.Cfg)
|
||||
app, secretKey, err := appsService.CreateApp("Test", "", 0, "monthly", nil, nil, false, nil)
|
||||
|
||||
assert.Nil(t, app)
|
||||
assert.Equal(t, "", secretKey)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, "no scopes provided", err.Error())
|
||||
}
|
||||
|
||||
func TestHandleCreateApp_EmptyScopes(t *testing.T) {
|
||||
// ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
appsService := apps.NewAppsService(svc.DB, svc.EventPublisher, svc.Keys, svc.Cfg)
|
||||
app, secretKey, err := appsService.CreateApp("Test", "", 0, "monthly", nil, []string{}, false, nil)
|
||||
|
||||
assert.Nil(t, app)
|
||||
assert.Equal(t, "", secretKey)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, "no scopes provided", err.Error())
|
||||
}
|
||||
|
||||
func TestHandleCreateApp_IsolatedUnsupportedBackendType(t *testing.T) {
|
||||
// ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
svc.Cfg.SetUpdate("BackendType", config.CashuBackendType, "")
|
||||
|
||||
appsService := apps.NewAppsService(svc.DB, svc.EventPublisher, svc.Keys, svc.Cfg)
|
||||
app, secretKey, err := appsService.CreateApp("Test", "", 0, "monthly", nil, []string{constants.GET_INFO_SCOPE}, true, nil)
|
||||
|
||||
assert.Nil(t, app)
|
||||
assert.Equal(t, "", secretKey)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, "sub-wallets are currently not supported on your node backend. Try LDK or LND", err.Error())
|
||||
}
|
||||
|
|
@ -19,6 +19,16 @@ const (
|
|||
BUDGET_RENEWAL_NEVER = "never"
|
||||
)
|
||||
|
||||
func GetBudgetRenewals() []string {
|
||||
return []string{
|
||||
BUDGET_RENEWAL_DAILY,
|
||||
BUDGET_RENEWAL_WEEKLY,
|
||||
BUDGET_RENEWAL_MONTHLY,
|
||||
BUDGET_RENEWAL_YEARLY,
|
||||
BUDGET_RENEWAL_NEVER,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
PAY_INVOICE_SCOPE = "pay_invoice" // also covers pay_keysend and multi_* payment methods
|
||||
GET_BALANCE_SCOPE = "get_balance"
|
||||
|
|
@ -28,6 +38,7 @@ const (
|
|||
LIST_TRANSACTIONS_SCOPE = "list_transactions"
|
||||
SIGN_MESSAGE_SCOPE = "sign_message"
|
||||
NOTIFICATIONS_SCOPE = "notifications" // covers all notification types
|
||||
SUPERUSER_SCOPE = "superuser"
|
||||
)
|
||||
|
||||
// limit encoded metadata length, otherwise relays may have trouble listing multiple transactions
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BrickWall, PlusCircle } from "lucide-react";
|
||||
import { AlertTriangleIcon, BrickWall, PlusCircle } from "lucide-react";
|
||||
import React from "react";
|
||||
import BudgetAmountSelect from "src/components/BudgetAmountSelect";
|
||||
import BudgetRenewalSelect from "src/components/BudgetRenewalSelect";
|
||||
|
|
@ -227,6 +227,20 @@ const Permissions: React.FC<PermissionsProps> = ({
|
|||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
{permissions.scopes.includes("superuser") && (
|
||||
<>
|
||||
<div className="flex items-center gap-2 mt-4">
|
||||
<AlertTriangleIcon className="w-4 h-4" />
|
||||
<p className="text-sm font-medium">
|
||||
This app can create other app connections
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Make sure to set budgets on connections created by this app.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -87,10 +87,17 @@ const Scopes: React.FC<ScopesProps> = ({
|
|||
}, [capabilities.scopes]);
|
||||
|
||||
const [scopeGroup, setScopeGroup] = React.useState<ScopeGroup>(() => {
|
||||
if (isolated && scopes.length === capabilities.scopes.length) {
|
||||
if (
|
||||
isolated &&
|
||||
scopes.length === isolatedScopes.length &&
|
||||
scopes.every((scope) => isolatedScopes.includes(scope))
|
||||
) {
|
||||
return "isolated";
|
||||
}
|
||||
if (scopes.length === capabilities.scopes.length) {
|
||||
if (
|
||||
scopes.length === fullAccessScopes.length &&
|
||||
scopes.every((scope) => fullAccessScopes.includes(scope))
|
||||
) {
|
||||
return "full_access";
|
||||
}
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -63,6 +63,19 @@ export const suggestedApps: SuggestedApp[] = [
|
|||
internal: true,
|
||||
logo: uncleJim,
|
||||
},
|
||||
{
|
||||
id: "alby-go",
|
||||
title: "Alby Go",
|
||||
description: "A simple mobile wallet that works great with Alby Hub",
|
||||
webLink: "https://albygo.com",
|
||||
playLink:
|
||||
"https://play.google.com/store/apps/details?id=com.getalby.mobile",
|
||||
appleLink: "https://apps.apple.com/us/app/alby-go/id6471335774",
|
||||
zapStoreLink: "https://zapstore.dev/download/",
|
||||
logo: albyGo,
|
||||
extendedDescription: "Sends and receives payments seamlessly from your Hub",
|
||||
internal: true,
|
||||
},
|
||||
{
|
||||
id: "buzzpay",
|
||||
title: "BuzzPay PoS",
|
||||
|
|
@ -1623,59 +1636,6 @@ export const suggestedApps: SuggestedApp[] = [
|
|||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "alby-go",
|
||||
title: "Alby Go",
|
||||
description: "A simple mobile wallet that works great with Alby Hub",
|
||||
webLink: "https://albygo.com",
|
||||
playLink:
|
||||
"https://play.google.com/store/apps/details?id=com.getalby.mobile",
|
||||
appleLink: "https://apps.apple.com/us/app/alby-go/id6471335774",
|
||||
zapStoreLink: "https://zapstore.dev/download/",
|
||||
logo: albyGo,
|
||||
extendedDescription: "Sends and receives payments seamlessly from your Hub",
|
||||
guide: (
|
||||
<>
|
||||
<div>
|
||||
<h3 className="font-medium">In Alby Go</h3>
|
||||
<ul className="list-inside text-muted-foreground">
|
||||
<li>
|
||||
1. Download and open{" "}
|
||||
<span className="font-medium text-foreground">Alby Go</span> on
|
||||
your Android or iOS device
|
||||
</li>
|
||||
<li>
|
||||
2. Click on{" "}
|
||||
<span className="font-medium text-foreground">
|
||||
Connect Wallet
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium">In Alby Hub</h3>
|
||||
<ul className="list-inside text-muted-foreground">
|
||||
<li>
|
||||
4. Click{" "}
|
||||
<Link
|
||||
to="/apps/new?app=alby-go"
|
||||
className="font-medium text-foreground underline"
|
||||
>
|
||||
Connect to Alby Go
|
||||
</Link>
|
||||
</li>
|
||||
<li>5. Set app's wallet permissions (full access recommended)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium">In Alby Go</h3>
|
||||
<ul className="list-inside text-muted-foreground">
|
||||
<li>6. Scan or paste the connection secret from Alby Hub</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "pullthatupjamie-ai",
|
||||
title: "Pull That Up Jamie!",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import { OpeningAutoChannel } from "src/screens/channels/auto/OpeningAutoChannel
|
|||
import { FirstChannel } from "src/screens/channels/first/FirstChannel";
|
||||
import { OpenedFirstChannel } from "src/screens/channels/first/OpenedFirstChannel";
|
||||
import { OpeningFirstChannel } from "src/screens/channels/first/OpeningFirstChannel";
|
||||
import { AlbyGo } from "src/screens/internal-apps/AlbyGo";
|
||||
import { BuzzPay } from "src/screens/internal-apps/BuzzPay";
|
||||
import { SimpleBoost } from "src/screens/internal-apps/SimpleBoost";
|
||||
import { UncleJim } from "src/screens/internal-apps/UncleJim";
|
||||
|
|
@ -241,6 +242,10 @@ const routes = [
|
|||
path: "uncle-jim",
|
||||
element: <UncleJim />,
|
||||
},
|
||||
{
|
||||
path: "alby-go",
|
||||
element: <AlbyGo />,
|
||||
},
|
||||
{
|
||||
path: "buzzpay",
|
||||
element: <BuzzPay />,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import { Label } from "src/components/ui/label";
|
|||
import { LoadingButton } from "src/components/ui/loading-button";
|
||||
import { Separator } from "src/components/ui/separator";
|
||||
import { useToast } from "src/components/ui/use-toast";
|
||||
import { useApps } from "src/hooks/useApps";
|
||||
import { useCapabilities } from "src/hooks/useCapabilities";
|
||||
import { createApp } from "src/requests/createApp";
|
||||
import { handleRequestError } from "src/utils/handleRequestError";
|
||||
|
|
@ -45,7 +44,6 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => {
|
|||
|
||||
const { toast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const { data: apps } = useApps();
|
||||
const [unsupportedError, setUnsupportedError] = useState<string>();
|
||||
const [isLoading, setLoading] = React.useState(false);
|
||||
|
||||
|
|
@ -187,10 +185,6 @@ const NewAppInternal = ({ capabilities }: NewAppInternalProps) => {
|
|||
|
||||
setLoading(true);
|
||||
try {
|
||||
if (apps?.some((existingApp) => existingApp.name === appName)) {
|
||||
throw new Error("A connection with the same name already exists.");
|
||||
}
|
||||
|
||||
const createAppRequest: CreateAppRequest = {
|
||||
name: appName,
|
||||
pubkey,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ import {
|
|||
import { Input } from "src/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableRow } from "src/components/ui/table";
|
||||
import { useToast } from "src/components/ui/use-toast";
|
||||
import { useApps } from "src/hooks/useApps";
|
||||
import { useCapabilities } from "src/hooks/useCapabilities";
|
||||
|
||||
function ShowApp() {
|
||||
|
|
@ -77,7 +76,6 @@ function AppInternal({ app, refetchApp, capabilities }: AppInternalProps) {
|
|||
const { toast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { data: apps } = useApps();
|
||||
const [isEditingName, setIsEditingName] = React.useState(false);
|
||||
const [isEditingPermissions, setIsEditingPermissions] = React.useState(false);
|
||||
|
||||
|
|
@ -102,16 +100,6 @@ function AppInternal({ app, refetchApp, capabilities }: AppInternalProps) {
|
|||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
if (
|
||||
isEditingName &&
|
||||
apps?.some(
|
||||
(existingApp) =>
|
||||
existingApp.name === name && existingApp.id !== app.id
|
||||
)
|
||||
) {
|
||||
throw new Error("A connection with the same name already exists.");
|
||||
}
|
||||
|
||||
const updateAppRequest: UpdateAppRequest = {
|
||||
name,
|
||||
scopes: Array.from(permissions.scopes),
|
||||
|
|
|
|||
329
frontend/src/screens/internal-apps/AlbyGo.tsx
Normal file
329
frontend/src/screens/internal-apps/AlbyGo.tsx
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
import { Globe } from "lucide-react";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import AppHeader from "src/components/AppHeader";
|
||||
import ExternalLink from "src/components/ExternalLink";
|
||||
import { AppleIcon } from "src/components/icons/Apple";
|
||||
import { ChromeIcon } from "src/components/icons/Chrome";
|
||||
import { FirefoxIcon } from "src/components/icons/Firefox";
|
||||
import { NostrWalletConnectIcon } from "src/components/icons/NostrWalletConnectIcon";
|
||||
import { PlayStoreIcon } from "src/components/icons/PlayStore";
|
||||
import { ZapStoreIcon } from "src/components/icons/ZapStore";
|
||||
import Loading from "src/components/Loading";
|
||||
import { suggestedApps } from "src/components/SuggestedAppData";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "src/components/ui/alert-dialog";
|
||||
import { Button } from "src/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "src/components/ui/card";
|
||||
import { Input } from "src/components/ui/input";
|
||||
import { Label } from "src/components/ui/label";
|
||||
import { LoadingButton } from "src/components/ui/loading-button";
|
||||
import { useToast } from "src/components/ui/use-toast";
|
||||
import { useApp } from "src/hooks/useApp";
|
||||
import { useCapabilities } from "src/hooks/useCapabilities";
|
||||
import { createApp } from "src/requests/createApp";
|
||||
import { ConnectAppCard } from "src/screens/apps/AppCreated";
|
||||
|
||||
export function AlbyGo() {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [appPubkey, setAppPubkey] = React.useState<string>();
|
||||
const [connectionSecret, setConnectionSecret] = React.useState<string>("");
|
||||
const [unlockPassword, setUnlockPassword] = React.useState("");
|
||||
const [showCreateConnectionDialog, setShowCreateConnectionDialog] =
|
||||
React.useState(false);
|
||||
const { data: createdApp } = useApp(appPubkey, true);
|
||||
const { toast } = useToast();
|
||||
const { data: capabilities } = useCapabilities();
|
||||
|
||||
const app = suggestedApps.find((app) => app.id === "alby-go");
|
||||
if (!app) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function onClickCreateConnection() {
|
||||
setShowCreateConnectionDialog(true);
|
||||
}
|
||||
|
||||
async function onSubmitCreateConnection(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
try {
|
||||
if (!capabilities) {
|
||||
throw new Error("capabilities not loaded");
|
||||
}
|
||||
|
||||
// TODO: fetch scopes from useCapabilities
|
||||
const createAppResponse = await createApp({
|
||||
name: "Alby Go",
|
||||
scopes: [...capabilities.scopes, "superuser"],
|
||||
isolated: false,
|
||||
metadata: {
|
||||
app_store_app_id: "alby-go",
|
||||
},
|
||||
unlockPassword,
|
||||
maxAmount: 100_000,
|
||||
budgetRenewal: "monthly",
|
||||
});
|
||||
setConnectionSecret(createAppResponse.pairingUri);
|
||||
setAppPubkey(createAppResponse.pairingPublicKey);
|
||||
toast({ title: "Alby Go connection created" });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Something went wrong: " + error,
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
setShowCreateConnectionDialog(false);
|
||||
setUnlockPassword("");
|
||||
}
|
||||
|
||||
if (!capabilities) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid gap-5">
|
||||
<AlertDialog open={showCreateConnectionDialog}>
|
||||
<AlertDialogContent>
|
||||
<form onSubmit={onSubmitCreateConnection}>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Confirm New Connection</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
<div className="flex flex-col">
|
||||
<p>
|
||||
Alby Go will be given permission to create other app
|
||||
connections which can spend your balance.
|
||||
</p>
|
||||
|
||||
<p className="mt-4">
|
||||
Alby Go will be given a 100k sat / month budget by default
|
||||
which you can edit after creating the connection.
|
||||
</p>
|
||||
|
||||
<p className="mt-4">
|
||||
Warning: Alby Go can create connections with a larger budget
|
||||
than the one set for Alby Go. Make sure to always set a
|
||||
budget.
|
||||
</p>
|
||||
|
||||
<p className="mt-4">
|
||||
Please enter your unlock password to continue.
|
||||
</p>
|
||||
<div className="grid gap-1.5 mt-2">
|
||||
<Label htmlFor="password">Unlock Password</Label>
|
||||
<Input
|
||||
autoFocus
|
||||
type="password"
|
||||
name="password"
|
||||
required
|
||||
onChange={(e) => setUnlockPassword(e.target.value)}
|
||||
value={unlockPassword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter className="mt-3">
|
||||
<AlertDialogCancel
|
||||
onClick={() => setShowCreateConnectionDialog(false)}
|
||||
>
|
||||
Cancel
|
||||
</AlertDialogCancel>
|
||||
<LoadingButton loading={loading} type="submit">
|
||||
Confirm
|
||||
</LoadingButton>
|
||||
</AlertDialogFooter>
|
||||
</form>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
<AppHeader
|
||||
title={
|
||||
<>
|
||||
<div className="flex flex-row items-center">
|
||||
<img src={app.logo} className="w-14 h-14 rounded-lg mr-4" />
|
||||
<div className="flex flex-col">
|
||||
<div>{app.title}</div>
|
||||
<div className="text-sm font-normal text-muted-foreground">
|
||||
{app.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
description=""
|
||||
contentRight={
|
||||
!createdApp && (
|
||||
<Link to={`/apps/new?app=${app.id}`}>
|
||||
<Button>
|
||||
<NostrWalletConnectIcon className="w-4 h-4 mr-2" />
|
||||
Connect to {app.title}
|
||||
</Button>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="flex flex-col w-full gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">About the App</CardTitle>
|
||||
</CardHeader>
|
||||
{app.extendedDescription && (
|
||||
<CardContent className="flex flex-col gap-3">
|
||||
<p className="text-muted-foreground">
|
||||
{app.extendedDescription}
|
||||
</p>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">How to Connect</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-3">
|
||||
<>
|
||||
<div>
|
||||
<h3 className="font-medium">In Alby Go</h3>
|
||||
<ul className="list-inside text-muted-foreground">
|
||||
<li>
|
||||
1. Download and open{" "}
|
||||
<span className="font-medium text-foreground">
|
||||
Alby Go
|
||||
</span>{" "}
|
||||
on your Android or iOS device
|
||||
</li>
|
||||
<li>
|
||||
2. Click on{" "}
|
||||
<span className="font-medium text-foreground">
|
||||
Connect Wallet
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
3.{" "}
|
||||
<span className="font-medium text-foreground">
|
||||
Scan or paste
|
||||
</span>{" "}
|
||||
the connection secret from Alby Hub that will be revealed
|
||||
once you create the connection below.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="flex flex-col w-full gap-6">
|
||||
{(app.appleLink ||
|
||||
app.playLink ||
|
||||
app.zapStoreLink ||
|
||||
app.chromeLink ||
|
||||
app.firefoxLink) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Get This App</CardTitle>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex flex-row gap-2">
|
||||
{app.playLink && (
|
||||
<ExternalLink to={app.playLink}>
|
||||
<Button variant="outline">
|
||||
<PlayStoreIcon className="w-4 h-4 mr-2" />
|
||||
Play Store
|
||||
</Button>
|
||||
</ExternalLink>
|
||||
)}
|
||||
{app.appleLink && (
|
||||
<ExternalLink to={app.appleLink}>
|
||||
<Button variant="outline">
|
||||
<AppleIcon className="w-4 h-4 mr-2" />
|
||||
App Store
|
||||
</Button>
|
||||
</ExternalLink>
|
||||
)}
|
||||
{app.zapStoreLink && (
|
||||
<ExternalLink to={app.zapStoreLink}>
|
||||
<Button variant="outline">
|
||||
<ZapStoreIcon className="w-4 h-4 mr-2" />
|
||||
Zapstore
|
||||
</Button>
|
||||
</ExternalLink>
|
||||
)}
|
||||
{app.chromeLink && (
|
||||
<ExternalLink to={app.chromeLink}>
|
||||
<Button variant="outline">
|
||||
<ChromeIcon className="w-4 h-4 mr-2" />
|
||||
Chrome Web Store
|
||||
</Button>
|
||||
</ExternalLink>
|
||||
)}
|
||||
{app.firefoxLink && (
|
||||
<ExternalLink to={app.firefoxLink}>
|
||||
<Button variant="outline">
|
||||
<FirefoxIcon className="w-4 h-4 mr-2" />
|
||||
Firefox Add-Ons
|
||||
</Button>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)}
|
||||
{app.webLink && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Links</CardTitle>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex flex-row gap-2">
|
||||
{app.webLink && (
|
||||
<ExternalLink to={app.webLink}>
|
||||
<Button variant="outline">
|
||||
<Globe className="w-4 h-4 mr-2" />
|
||||
Website
|
||||
</Button>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)}
|
||||
{createdApp && connectionSecret && (
|
||||
<div className="mb-16 w-full">
|
||||
<ConnectAppCard app={createdApp} pairingUri={connectionSecret} />
|
||||
</div>
|
||||
)}
|
||||
{!createdApp && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">One Tap Connections</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground">
|
||||
Use Alby Go to quickly connect other apps to your hub with one
|
||||
tap on mobile.
|
||||
</p>
|
||||
{
|
||||
<Button className="mt-8" onClick={onClickCreateConnection}>
|
||||
<NostrWalletConnectIcon className="w-4 h-4 mr-2" />
|
||||
Connect with One Tap Connections
|
||||
</Button>
|
||||
}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -37,10 +37,6 @@ export function BuzzPay() {
|
|||
setLoading(true);
|
||||
(async () => {
|
||||
try {
|
||||
if (apps?.some((existingApp) => existingApp.name === name)) {
|
||||
throw new Error("A connection with the same name already exists.");
|
||||
}
|
||||
|
||||
const createAppResponse = await createApp({
|
||||
name,
|
||||
scopes: ["get_info", "lookup_invoice", "make_invoice"],
|
||||
|
|
|
|||
|
|
@ -44,10 +44,6 @@ export function SimpleBoost() {
|
|||
setLoading(true);
|
||||
(async () => {
|
||||
try {
|
||||
if (apps?.some((existingApp) => existingApp.name === name)) {
|
||||
throw new Error("A connection with the same name already exists.");
|
||||
}
|
||||
|
||||
const createAppResponse = await createApp({
|
||||
name,
|
||||
scopes: ["lookup_invoice", "make_invoice"],
|
||||
|
|
|
|||
|
|
@ -41,10 +41,6 @@ export function UncleJim() {
|
|||
setLoading(true);
|
||||
|
||||
try {
|
||||
if (apps?.some((existingApp) => existingApp.name === name)) {
|
||||
throw new Error("A connection with the same name already exists.");
|
||||
}
|
||||
|
||||
const createAppRequest: CreateAppRequest = {
|
||||
name,
|
||||
scopes: [
|
||||
|
|
|
|||
|
|
@ -103,10 +103,6 @@ export function ZapPlanner() {
|
|||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
try {
|
||||
if (apps?.some((existingApp) => existingApp.name === recipientName)) {
|
||||
throw new Error("A connection with the same name already exists.");
|
||||
}
|
||||
|
||||
// validate lighning address
|
||||
const ln = new LightningAddress(recipientLightningAddress);
|
||||
await ln.fetch();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
Bell,
|
||||
CirclePlus,
|
||||
Crown,
|
||||
HandCoins,
|
||||
Info,
|
||||
LucideIcon,
|
||||
|
|
@ -47,7 +48,8 @@ export type Scope =
|
|||
| "lookup_invoice"
|
||||
| "list_transactions"
|
||||
| "sign_message"
|
||||
| "notifications"; // covers all notification types
|
||||
| "notifications" // covers all notification types
|
||||
| "superuser";
|
||||
|
||||
export type Nip47NotificationType = "payment_received" | "payment_sent";
|
||||
|
||||
|
|
@ -64,6 +66,7 @@ export const scopeIconMap: ScopeIconMap = {
|
|||
pay_invoice: HandCoins,
|
||||
sign_message: PenLine,
|
||||
notifications: Bell,
|
||||
superuser: Crown,
|
||||
};
|
||||
|
||||
export type WalletCapabilities = {
|
||||
|
|
@ -89,6 +92,7 @@ export const scopeDescriptions: Record<Scope, string> = {
|
|||
pay_invoice: "Send payments",
|
||||
sign_message: "Sign messages",
|
||||
notifications: "Receive wallet notifications",
|
||||
superuser: "Create other app connections",
|
||||
};
|
||||
|
||||
export const expiryOptions: Record<string, number> = {
|
||||
|
|
@ -195,6 +199,7 @@ export interface CreateAppRequest {
|
|||
returnTo?: string;
|
||||
isolated?: boolean;
|
||||
metadata?: AppMetadata;
|
||||
unlockPassword?: string; // required to create superuser apps
|
||||
}
|
||||
|
||||
export interface CreateAppResponse {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func NewHttpService(svc service.Service, eventPublisher events.EventPublisher) *
|
|||
cfg: svc.GetConfig(),
|
||||
eventPublisher: eventPublisher,
|
||||
db: svc.GetDB(),
|
||||
appsSvc: apps.NewAppsService(svc.GetDB(), eventPublisher, svc.GetKeys()),
|
||||
appsSvc: apps.NewAppsService(svc.GetDB(), eventPublisher, svc.GetKeys(), svc.GetConfig()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
15
nip47/controllers/controllers_test.go
Normal file
15
nip47/controllers/controllers_test.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/getAlby/hub/alby"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
func NewTestNip47Controller(svc *tests.TestService) *nip47Controller {
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
return NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc, svc.AppsService, albyOAuthSvc)
|
||||
}
|
||||
135
nip47/controllers/create_connection_controller.go
Normal file
135
nip47/controllers/create_connection_controller.go
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/logger"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type createConnectionParams struct {
|
||||
Pubkey string `json:"pubkey"` // pubkey of the app connection
|
||||
Name string `json:"name"`
|
||||
RequestMethods []string `json:"request_methods"`
|
||||
NotificationTypes []string `json:"notification_types"`
|
||||
MaxAmount uint64 `json:"max_amount"`
|
||||
BudgetRenewal string `json:"budget_renewal"`
|
||||
ExpiresAt *uint64 `json:"expires_at"` // unix timestamp
|
||||
Isolated bool `json:"isolated"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type createConnectionResponse struct {
|
||||
// pubkey is given, user requesting already knows relay.
|
||||
WalletPubkey string `json:"wallet_pubkey"`
|
||||
}
|
||||
|
||||
func (controller *nip47Controller) HandleCreateConnectionEvent(ctx context.Context, nip47Request *models.Request, requestEventId uint, publishResponse publishFunc) {
|
||||
params := &createConnectionParams{}
|
||||
resp := decodeRequest(nip47Request, params)
|
||||
if resp != nil {
|
||||
publishResponse(resp, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"request_event_id": requestEventId,
|
||||
"params": params,
|
||||
}).Info("creating app")
|
||||
|
||||
var expiresAt *time.Time
|
||||
if params.ExpiresAt != nil {
|
||||
expiresAtUnsigned := *params.ExpiresAt
|
||||
expiresAtValue := time.Unix(int64(expiresAtUnsigned), 0)
|
||||
expiresAt = &expiresAtValue
|
||||
}
|
||||
|
||||
maxAmountSat := params.MaxAmount / 1000
|
||||
|
||||
// explicitly do not allow creating an app with create_connection permission
|
||||
if slices.Contains(params.RequestMethods, models.CREATE_CONNECTION_METHOD) {
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &models.Error{
|
||||
Code: constants.ERROR_INTERNAL,
|
||||
Message: "cannot create a new app that has create_connection permission via NWC",
|
||||
},
|
||||
}, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
|
||||
// ensure there is at least one request method
|
||||
if len(params.RequestMethods) == 0 {
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &models.Error{
|
||||
Code: constants.ERROR_INTERNAL,
|
||||
Message: "No request methods provided",
|
||||
},
|
||||
}, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
|
||||
supportedMethods := controller.lnClient.GetSupportedNIP47Methods()
|
||||
if slices.ContainsFunc(params.RequestMethods, func(method string) bool {
|
||||
return !slices.Contains(supportedMethods, method)
|
||||
}) {
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &models.Error{
|
||||
Code: constants.ERROR_INTERNAL,
|
||||
Message: "One or more methods are not supported by the current LNClient",
|
||||
},
|
||||
}, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
|
||||
scopes, err := permissions.RequestMethodsToScopes(params.RequestMethods)
|
||||
|
||||
supportedNotificationTypes := controller.lnClient.GetSupportedNIP47NotificationTypes()
|
||||
if len(params.NotificationTypes) > 0 {
|
||||
if slices.ContainsFunc(params.NotificationTypes, func(method string) bool {
|
||||
return !slices.Contains(supportedNotificationTypes, method)
|
||||
}) {
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &models.Error{
|
||||
Code: constants.ERROR_INTERNAL,
|
||||
Message: "One or more notification types are not supported by the current LNClient",
|
||||
},
|
||||
}, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
scopes = append(scopes, constants.NOTIFICATIONS_SCOPE)
|
||||
}
|
||||
|
||||
app, _, err := controller.appsService.CreateApp(params.Name, params.Pubkey, maxAmountSat, params.BudgetRenewal, expiresAt, scopes, params.Isolated, params.Metadata)
|
||||
if err != nil {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"request_event_id": requestEventId,
|
||||
}).WithError(err).Error("Failed to create app")
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: &models.Error{
|
||||
Code: constants.ERROR_INTERNAL,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
|
||||
responsePayload := createConnectionResponse{
|
||||
WalletPubkey: *app.WalletPubkey,
|
||||
}
|
||||
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Result: responsePayload,
|
||||
}, nostr.Tags{})
|
||||
}
|
||||
363
nip47/controllers/create_connection_controller_test.go
Normal file
363
nip47/controllers/create_connection_controller_test.go
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/getAlby/hub/apps"
|
||||
"github.com/getAlby/hub/config"
|
||||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/tests"
|
||||
)
|
||||
|
||||
func TestHandleCreateConnectionEvent(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
svc.Cfg.SetUpdate("LNBackendType", config.LDKBackendType, "")
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
pairingSecretKey := nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, err := nostr.GetPublicKey(pairingSecretKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
nip47CreateConnectionJson := fmt.Sprintf(`
|
||||
{
|
||||
"method": "create_connection",
|
||||
"params": {
|
||||
"pubkey": "%s",
|
||||
"name": "Test 123",
|
||||
"request_methods": ["get_info", "pay_invoice"],
|
||||
"notification_types": ["payment_received"],
|
||||
"max_amount": 100000000,
|
||||
"budget_renewal": "monthly",
|
||||
"isolated": true
|
||||
}
|
||||
}
|
||||
`, pairingPublicKey)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
err = json.Unmarshal([]byte(nip47CreateConnectionJson), nip47Request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbRequestEvent := &db.RequestEvent{}
|
||||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
var publishedResponse *models.Response
|
||||
|
||||
publishResponse := func(response *models.Response, tags nostr.Tags) {
|
||||
publishedResponse = response
|
||||
}
|
||||
|
||||
NewTestNip47Controller(svc).
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, dbRequestEvent.ID, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
assert.Equal(t, models.CREATE_CONNECTION_METHOD, publishedResponse.ResultType)
|
||||
createAppResult := publishedResponse.Result.(createConnectionResponse)
|
||||
|
||||
assert.NotNil(t, createAppResult.WalletPubkey)
|
||||
app := db.App{}
|
||||
err = svc.DB.First(&app).Error
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pairingPublicKey, app.AppPubkey)
|
||||
assert.Equal(t, createAppResult.WalletPubkey, *app.WalletPubkey)
|
||||
|
||||
permissions := []db.AppPermission{}
|
||||
err = svc.DB.Find(&permissions).Error
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(permissions))
|
||||
assert.Equal(t, constants.GET_INFO_SCOPE, permissions[0].Scope)
|
||||
assert.Equal(t, constants.PAY_INVOICE_SCOPE, permissions[1].Scope)
|
||||
assert.Equal(t, constants.NOTIFICATIONS_SCOPE, permissions[2].Scope)
|
||||
|
||||
assert.True(t, app.Isolated)
|
||||
assert.Equal(t, 100_000, permissions[1].MaxAmountSat)
|
||||
assert.Equal(t, constants.BUDGET_RENEWAL_MONTHLY, permissions[1].BudgetRenewal)
|
||||
}
|
||||
|
||||
func TestHandleCreateConnectionEvent_IsolatedUnsupportedBackendType(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
svc.Cfg.SetUpdate("BackendType", config.CashuBackendType, "")
|
||||
|
||||
pairingSecretKey := nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, err := nostr.GetPublicKey(pairingSecretKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
nip47CreateConnectionJson := fmt.Sprintf(`
|
||||
{
|
||||
"method": "create_connection",
|
||||
"params": {
|
||||
"pubkey": "%s",
|
||||
"name": "Test 123",
|
||||
"request_methods": ["get_info", "pay_invoice"],
|
||||
"notification_types": ["payment_received"],
|
||||
"max_amount": 100000000,
|
||||
"budget_renewal": "monthly",
|
||||
"isolated": true
|
||||
}
|
||||
}
|
||||
`, pairingPublicKey)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
err = json.Unmarshal([]byte(nip47CreateConnectionJson), nip47Request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbRequestEvent := &db.RequestEvent{}
|
||||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
var publishedResponse *models.Response
|
||||
|
||||
publishResponse := func(response *models.Response, tags nostr.Tags) {
|
||||
publishedResponse = response
|
||||
}
|
||||
|
||||
NewTestNip47Controller(svc).
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, dbRequestEvent.ID, publishResponse)
|
||||
|
||||
assert.NotNil(t, publishedResponse.Error)
|
||||
assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code)
|
||||
assert.Equal(t, "sub-wallets are currently not supported on your node backend. Try LDK or LND", publishedResponse.Error.Message)
|
||||
assert.Equal(t, models.CREATE_CONNECTION_METHOD, publishedResponse.ResultType)
|
||||
}
|
||||
|
||||
func TestHandleCreateConnectionEvent_PubkeyAlreadyExists(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
pairingSecretKey := nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, err := nostr.GetPublicKey(pairingSecretKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
appsSvc := apps.NewAppsService(svc.DB, svc.EventPublisher, svc.Keys, svc.Cfg)
|
||||
_, _, err = appsSvc.CreateApp("Existing App", pairingPublicKey, 0, constants.BUDGET_RENEWAL_NEVER, nil, []string{models.GET_INFO_METHOD}, false, nil)
|
||||
|
||||
nip47CreateConnectionJson := fmt.Sprintf(`
|
||||
{
|
||||
"method": "create_connection",
|
||||
"params": {
|
||||
"pubkey": "%s",
|
||||
"name": "Test 123",
|
||||
"request_methods": ["get_info"]
|
||||
}
|
||||
}
|
||||
`, pairingPublicKey)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
err = json.Unmarshal([]byte(nip47CreateConnectionJson), nip47Request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbRequestEvent := &db.RequestEvent{}
|
||||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
var publishedResponse *models.Response
|
||||
|
||||
publishResponse := func(response *models.Response, tags nostr.Tags) {
|
||||
publishedResponse = response
|
||||
}
|
||||
|
||||
NewTestNip47Controller(svc).
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, dbRequestEvent.ID, publishResponse)
|
||||
|
||||
assert.NotNil(t, publishedResponse.Error)
|
||||
assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code)
|
||||
assert.Equal(t, "duplicated key not allowed", publishedResponse.Error.Message)
|
||||
assert.Equal(t, models.CREATE_CONNECTION_METHOD, publishedResponse.ResultType)
|
||||
assert.Nil(t, publishedResponse.Result)
|
||||
}
|
||||
|
||||
func TestHandleCreateConnectionEvent_NoMethods(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
pairingSecretKey := nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, err := nostr.GetPublicKey(pairingSecretKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
nip47CreateConnectionJson := fmt.Sprintf(`
|
||||
{
|
||||
"method": "create_connection",
|
||||
"params": {
|
||||
"pubkey": "%s",
|
||||
"name": "Test 123"
|
||||
}
|
||||
}
|
||||
`, pairingPublicKey)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
err = json.Unmarshal([]byte(nip47CreateConnectionJson), nip47Request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbRequestEvent := &db.RequestEvent{}
|
||||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
var publishedResponse *models.Response
|
||||
|
||||
publishResponse := func(response *models.Response, tags nostr.Tags) {
|
||||
publishedResponse = response
|
||||
}
|
||||
|
||||
NewTestNip47Controller(svc).
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, dbRequestEvent.ID, publishResponse)
|
||||
|
||||
assert.NotNil(t, publishedResponse.Error)
|
||||
assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code)
|
||||
assert.Equal(t, "No request methods provided", publishedResponse.Error.Message)
|
||||
assert.Equal(t, models.CREATE_CONNECTION_METHOD, publishedResponse.ResultType)
|
||||
assert.Nil(t, publishedResponse.Result)
|
||||
}
|
||||
|
||||
func TestHandleCreateConnectionEvent_UnsupportedMethod(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
pairingSecretKey := nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, err := nostr.GetPublicKey(pairingSecretKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
nip47CreateConnectionJson := fmt.Sprintf(`
|
||||
{
|
||||
"method": "create_connection",
|
||||
"params": {
|
||||
"pubkey": "%s",
|
||||
"name": "Test 123",
|
||||
"request_methods": ["non_existent"]
|
||||
}
|
||||
}
|
||||
`, pairingPublicKey)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
err = json.Unmarshal([]byte(nip47CreateConnectionJson), nip47Request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbRequestEvent := &db.RequestEvent{}
|
||||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
var publishedResponse *models.Response
|
||||
|
||||
publishResponse := func(response *models.Response, tags nostr.Tags) {
|
||||
publishedResponse = response
|
||||
}
|
||||
|
||||
NewTestNip47Controller(svc).
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, dbRequestEvent.ID, publishResponse)
|
||||
|
||||
assert.NotNil(t, publishedResponse.Error)
|
||||
assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code)
|
||||
assert.Equal(t, "One or more methods are not supported by the current LNClient", publishedResponse.Error.Message)
|
||||
assert.Equal(t, models.CREATE_CONNECTION_METHOD, publishedResponse.ResultType)
|
||||
assert.Nil(t, publishedResponse.Result)
|
||||
}
|
||||
|
||||
func TestHandleCreateConnectionEvent_UnsupportedNotificationType(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
pairingSecretKey := nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, err := nostr.GetPublicKey(pairingSecretKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
nip47CreateConnectionJson := fmt.Sprintf(`
|
||||
{
|
||||
"method": "create_connection",
|
||||
"params": {
|
||||
"pubkey": "%s",
|
||||
"name": "Test 123",
|
||||
"request_methods": ["get_info"],
|
||||
"notification_types": ["non_existent"]
|
||||
}
|
||||
}
|
||||
`, pairingPublicKey)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
err = json.Unmarshal([]byte(nip47CreateConnectionJson), nip47Request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbRequestEvent := &db.RequestEvent{}
|
||||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
var publishedResponse *models.Response
|
||||
|
||||
publishResponse := func(response *models.Response, tags nostr.Tags) {
|
||||
publishedResponse = response
|
||||
}
|
||||
|
||||
NewTestNip47Controller(svc).
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, dbRequestEvent.ID, publishResponse)
|
||||
|
||||
assert.NotNil(t, publishedResponse.Error)
|
||||
assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code)
|
||||
assert.Equal(t, "One or more notification types are not supported by the current LNClient", publishedResponse.Error.Message)
|
||||
assert.Equal(t, models.CREATE_CONNECTION_METHOD, publishedResponse.ResultType)
|
||||
assert.Nil(t, publishedResponse.Result)
|
||||
}
|
||||
|
||||
func TestHandleCreateConnectionEvent_DoNotAllowCreateConnectionMethod(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
pairingSecretKey := nostr.GeneratePrivateKey()
|
||||
pairingPublicKey, err := nostr.GetPublicKey(pairingSecretKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
nip47CreateConnectionJson := fmt.Sprintf(`
|
||||
{
|
||||
"method": "create_connection",
|
||||
"params": {
|
||||
"pubkey": "%s",
|
||||
"name": "Test 123",
|
||||
"request_methods": ["create_connection"]
|
||||
}
|
||||
}
|
||||
`, pairingPublicKey)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
err = json.Unmarshal([]byte(nip47CreateConnectionJson), nip47Request)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbRequestEvent := &db.RequestEvent{}
|
||||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
var publishedResponse *models.Response
|
||||
|
||||
publishResponse := func(response *models.Response, tags nostr.Tags) {
|
||||
publishedResponse = response
|
||||
}
|
||||
|
||||
NewTestNip47Controller(svc).
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, dbRequestEvent.ID, publishResponse)
|
||||
|
||||
assert.NotNil(t, publishedResponse.Error)
|
||||
assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code)
|
||||
assert.Equal(t, "cannot create a new app that has create_connection permission via NWC", publishedResponse.Error.Message)
|
||||
assert.Equal(t, models.CREATE_CONNECTION_METHOD, publishedResponse.ResultType)
|
||||
assert.Nil(t, publishedResponse.Result)
|
||||
}
|
||||
|
|
@ -12,9 +12,7 @@ import (
|
|||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
const nip47GetBalanceJson = `
|
||||
|
|
@ -46,9 +44,7 @@ func TestHandleGetBalanceEvent(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBalanceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, int64(21000), publishedResponse.Result.(*getBalanceResponse).Balance)
|
||||
|
|
@ -80,9 +76,7 @@ func TestHandleGetBalanceEvent_IsolatedApp_NoTransactions(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBalanceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, int64(0), publishedResponse.Result.(*getBalanceResponse).Balance)
|
||||
|
|
@ -127,9 +121,7 @@ func TestHandleGetBalanceEvent_IsolatedApp_Transactions(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBalanceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, int64(1000), publishedResponse.Result.(*getBalanceResponse).Balance)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ import (
|
|||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
const nip47GetBudgetJson = `
|
||||
|
|
@ -57,9 +55,7 @@ func TestHandleGetBudgetEvent_NoRenewal(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBudgetEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, uint64(400000), publishedResponse.Result.(*getBudgetResponse).TotalBudget)
|
||||
|
|
@ -103,9 +99,7 @@ func TestHandleGetBudgetEvent_NoneUsed(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBudgetEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, uint64(400000), publishedResponse.Result.(*getBudgetResponse).TotalBudget)
|
||||
|
|
@ -157,9 +151,7 @@ func TestHandleGetBudgetEvent_HalfUsed(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBudgetEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, uint64(400000), publishedResponse.Result.(*getBudgetResponse).TotalBudget)
|
||||
|
|
@ -208,9 +200,7 @@ func TestHandleGetBudgetEvent_NoBudget(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBudgetEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, struct{}{}, publishedResponse.Result)
|
||||
|
|
@ -240,9 +230,7 @@ func TestHandleGetBudgetEvent_NoPayInvoicePermission(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetBudgetEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, struct{}{}, publishedResponse.Result)
|
||||
|
|
|
|||
|
|
@ -13,15 +13,16 @@ import (
|
|||
)
|
||||
|
||||
type getInfoResponse struct {
|
||||
Alias string `json:"alias"`
|
||||
Color string `json:"color"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Network string `json:"network"`
|
||||
BlockHeight uint32 `json:"block_height"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Methods []string `json:"methods"`
|
||||
Notifications []string `json:"notifications"`
|
||||
Metadata interface{} `json:"metadata,omitempty"`
|
||||
Alias string `json:"alias"`
|
||||
Color string `json:"color"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
Network string `json:"network"`
|
||||
BlockHeight uint32 `json:"block_height"`
|
||||
BlockHash string `json:"block_hash"`
|
||||
Methods []string `json:"methods"`
|
||||
Notifications []string `json:"notifications"`
|
||||
Metadata interface{} `json:"metadata,omitempty"`
|
||||
LightningAddress string `json:"lud16"`
|
||||
}
|
||||
|
||||
func (controller *nip47Controller) HandleGetInfoEvent(ctx context.Context, nip47Request *models.Request, requestEventId uint, app *db.App, publishResponse publishFunc) {
|
||||
|
|
@ -90,6 +91,10 @@ func (controller *nip47Controller) HandleGetInfoEvent(ctx context.Context, nip47
|
|||
if metadata["name"] == nil {
|
||||
metadata["name"] = app.Name
|
||||
}
|
||||
if !app.Isolated {
|
||||
lightningAddress, _ := controller.albyOAuthService.GetLightningAddress()
|
||||
responsePayload.LightningAddress = lightningAddress
|
||||
}
|
||||
|
||||
responsePayload.Metadata = metadata
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ import (
|
|||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
const nip47GetInfoJson = `
|
||||
|
|
@ -40,6 +38,9 @@ func TestHandleGetInfoEvent_NoPermission(t *testing.T) {
|
|||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
// delete the existing app permissions (the app was created with get_info scope)
|
||||
svc.DB.Exec("delete from app_permissions")
|
||||
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
Scope: constants.GET_BALANCE_SCOPE,
|
||||
|
|
@ -54,9 +55,7 @@ func TestHandleGetInfoEvent_NoPermission(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetInfoEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -103,9 +102,7 @@ func TestHandleGetInfoEvent_WithPermission(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetInfoEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -130,7 +127,7 @@ func TestHandleGetInfoEvent_WithMetadata(t *testing.T) {
|
|||
"a": 123,
|
||||
}
|
||||
|
||||
app, _, err := svc.AppsService.CreateApp("test", "", 0, "monthly", nil, nil, false, metadata)
|
||||
app, _, err := svc.AppsService.CreateApp("test", "", 0, "monthly", nil, []string{constants.GET_INFO_SCOPE}, false, metadata)
|
||||
assert.NoError(t, err)
|
||||
|
||||
nip47Request := &models.Request{}
|
||||
|
|
@ -155,9 +152,7 @@ func TestHandleGetInfoEvent_WithMetadata(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetInfoEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -214,9 +209,7 @@ func TestHandleGetInfoEvent_WithNotifications(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleGetInfoEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ import (
|
|||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
func TestHandleListTransactionsEvent(t *testing.T) {
|
||||
|
|
@ -75,9 +73,7 @@ func TestHandleListTransactionsEvent(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -146,9 +142,7 @@ func TestHandleListTransactionsEvent_UnpaidOutgoingOnly(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -208,9 +202,7 @@ func TestHandleListTransactionsEvent_UnpaidIncomingOnly(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -270,9 +262,7 @@ func TestHandleListTransactionsEvent_Unpaid(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -338,9 +328,7 @@ func TestHandleListTransactionsEvent_Paid(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ import (
|
|||
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
var nip47LookupInvoiceJson = `
|
||||
|
|
@ -66,9 +64,7 @@ func TestHandleLookupInvoiceEvent(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleLookupInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ import (
|
|||
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
const nip47MakeInvoiceJson = `
|
||||
|
|
@ -64,9 +62,7 @@ func TestHandleMakeInvoiceEvent(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMakeInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)
|
||||
|
||||
expectedMetadata := map[string]interface{}{
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@ import (
|
|||
"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/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
const nip47MultiPayJson = `
|
||||
|
|
@ -117,9 +115,7 @@ func TestHandleMultiPayInvoiceEvent_Success(t *testing.T) {
|
|||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMultiPayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
var paymentHashes = []string{
|
||||
|
|
@ -185,9 +181,7 @@ func TestHandleMultiPayInvoiceEvent_OneMalformedInvoice(t *testing.T) {
|
|||
requestEvent := &db.RequestEvent{}
|
||||
svc.DB.Save(requestEvent)
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMultiPayInvoiceEvent(ctx, nip47Request, requestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses))
|
||||
|
|
@ -246,9 +240,7 @@ func TestHandleMultiPayInvoiceEvent_OneExpiredInvoice(t *testing.T) {
|
|||
requestEvent := &db.RequestEvent{}
|
||||
svc.DB.Save(requestEvent)
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMultiPayInvoiceEvent(ctx, nip47Request, requestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses))
|
||||
|
|
@ -319,9 +311,7 @@ func TestHandleMultiPayInvoiceEvent_IsolatedApp_OneBudgetExceeded(t *testing.T)
|
|||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMultiPayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses))
|
||||
|
|
@ -399,9 +389,7 @@ func TestHandleMultiPayInvoiceEvent_LNClient_OnePaymentFailed(t *testing.T) {
|
|||
err = svc.DB.Create(&dbRequestEvent).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMultiPayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses))
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ import (
|
|||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
const nip47MultiPayKeysendJson = `
|
||||
|
|
@ -106,9 +104,7 @@ func TestHandleMultiPayKeysendEvent_Success(t *testing.T) {
|
|||
dTags = append(dTags, tags)
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMultiPayKeysendEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
assert.Equal(t, 2, len(responses))
|
||||
|
|
@ -158,9 +154,7 @@ func TestHandleMultiPayKeysendEvent_OneBudgetExceeded(t *testing.T) {
|
|||
dTags = append(dTags, tags)
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandleMultiPayKeysendEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse)
|
||||
|
||||
// we can't guarantee which request was processed first
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/getAlby/hub/alby"
|
||||
"github.com/getAlby/hub/apps"
|
||||
"github.com/getAlby/hub/events"
|
||||
"github.com/getAlby/hub/lnclient"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
|
|
@ -14,14 +16,25 @@ type nip47Controller struct {
|
|||
eventPublisher events.EventPublisher
|
||||
permissionsService permissions.PermissionsService
|
||||
transactionsService transactions.TransactionsService
|
||||
appsService apps.AppsService
|
||||
albyOAuthService alby.AlbyOAuthService
|
||||
}
|
||||
|
||||
func NewNip47Controller(lnClient lnclient.LNClient, db *gorm.DB, eventPublisher events.EventPublisher, permissionsService permissions.PermissionsService, transactionsService transactions.TransactionsService) *nip47Controller {
|
||||
func NewNip47Controller(
|
||||
lnClient lnclient.LNClient,
|
||||
db *gorm.DB,
|
||||
eventPublisher events.EventPublisher,
|
||||
permissionsService permissions.PermissionsService,
|
||||
transactionsService transactions.TransactionsService,
|
||||
appsService apps.AppsService,
|
||||
albyOAuthService alby.AlbyOAuthService) *nip47Controller {
|
||||
return &nip47Controller{
|
||||
lnClient: lnClient,
|
||||
db: db,
|
||||
eventPublisher: eventPublisher,
|
||||
permissionsService: permissionsService,
|
||||
transactionsService: transactionsService,
|
||||
appsService: appsService,
|
||||
albyOAuthService: albyOAuthService,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
|
@ -85,14 +84,13 @@ func TestHandlePayInvoiceEvent(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
|
||||
|
||||
assert.Equal(t, "123preimage", publishedResponse.Result.(payResponse).Preimage)
|
||||
|
||||
transactionType := constants.TRANSACTION_TYPE_OUTGOING
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
transaction, err := transactionsSvc.LookupTransaction(ctx, "23277d5e13fce5534f9752c62fcf9337a2a6b0ebea9d21fa816a4c9d054cf93b", &transactionType, svc.LNClient, &app.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -136,14 +134,13 @@ func TestHandlePayInvoiceEvent_0Amount(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
|
||||
|
||||
assert.Equal(t, "123preimage", publishedResponse.Result.(payResponse).Preimage)
|
||||
|
||||
transactionType := constants.TRANSACTION_TYPE_OUTGOING
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
transaction, err := transactionsSvc.LookupTransaction(ctx, tests.Mock0AmountPaymentHash, &transactionType, svc.LNClient, &app.ID)
|
||||
assert.NoError(t, err)
|
||||
// from the request amount
|
||||
|
|
@ -181,9 +178,7 @@ func TestHandlePayInvoiceEvent_MalformedInvoice(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
|
||||
|
||||
assert.Nil(t, publishedResponse.Result)
|
||||
|
|
@ -222,9 +217,7 @@ func TestHandlePayInvoiceEvent_ExpiredInvoice(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
|
||||
|
||||
assert.Nil(t, publishedResponse.Result)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ import (
|
|||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/models"
|
||||
"github.com/getAlby/hub/nip47/permissions"
|
||||
"github.com/getAlby/hub/tests"
|
||||
"github.com/getAlby/hub/transactions"
|
||||
)
|
||||
|
||||
const nip47KeysendJson = `
|
||||
|
|
@ -78,9 +76,7 @@ func TestHandlePayKeysendEvent(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandlePayKeysendEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
@ -119,9 +115,7 @@ func TestHandlePayKeysendEvent_WithPreimage(t *testing.T) {
|
|||
publishedResponse = response
|
||||
}
|
||||
|
||||
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
|
||||
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
|
||||
NewTestNip47Controller(svc).
|
||||
HandlePayKeysendEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
|
||||
|
||||
assert.Nil(t, publishedResponse.Error)
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela
|
|||
}
|
||||
}
|
||||
|
||||
controller := controllers.NewNip47Controller(lnClient, svc.db, svc.eventPublisher, svc.permissionsService, svc.transactionsService)
|
||||
controller := controllers.NewNip47Controller(lnClient, svc.db, svc.eventPublisher, svc.permissionsService, svc.transactionsService, svc.appsService, svc.albyOAuthSvc)
|
||||
|
||||
switch nip47Request.Method {
|
||||
case models.MULTI_PAY_INVOICE_METHOD:
|
||||
|
|
@ -334,6 +334,9 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, relay nostrmodels.Rela
|
|||
case models.SIGN_MESSAGE_METHOD:
|
||||
controller.
|
||||
HandleSignMessageEvent(ctx, nip47Request, requestEvent.ID, publishResponse)
|
||||
case models.CREATE_CONNECTION_METHOD:
|
||||
controller.
|
||||
HandleCreateConnectionEvent(ctx, nip47Request, requestEvent.ID, publishResponse)
|
||||
default:
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/getAlby/hub/alby"
|
||||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/nip47/cipher"
|
||||
|
|
@ -66,7 +67,8 @@ func doTestCreateResponse(t *testing.T, svc *tests.TestService, nip47Version str
|
|||
},
|
||||
}
|
||||
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
res, err := nip47svc.CreateResponse(reqEvent, nip47Response, nostr.Tags{}, nip47Cipher, svc.Keys.GetNostrSecretKey())
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -104,7 +106,8 @@ func TestHandleResponse_Nip44_WithPermission(t *testing.T) {
|
|||
}
|
||||
|
||||
func doTestHandleResponse_WithPermission(t *testing.T, svc *tests.TestService, createAppFn tests.CreateAppFn, version string) {
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
reqPrivateKey := nostr.GeneratePrivateKey()
|
||||
reqPubkey, err := nostr.GetPublicKey(reqPrivateKey)
|
||||
|
|
@ -172,7 +175,7 @@ func doTestHandleResponse_WithPermission(t *testing.T, svc *tests.TestService, c
|
|||
assert.Nil(t, unmarshalledResponse.Error)
|
||||
assert.Equal(t, models.GET_INFO_METHOD, unmarshalledResponse.ResultType)
|
||||
expectedMethods := slices.Concat([]string{constants.GET_BALANCE_SCOPE}, permissions.GetAlwaysGrantedMethods())
|
||||
assert.Equal(t, expectedMethods, unmarshalledResponse.Result.Methods)
|
||||
assert.ElementsMatch(t, expectedMethods, unmarshalledResponse.Result.Methods)
|
||||
}
|
||||
|
||||
func TestHandleResponse_Nip04_DuplicateRequest(t *testing.T) {
|
||||
|
|
@ -192,7 +195,8 @@ func TestHandleResponse_Nip44_DuplicateRequest(t *testing.T) {
|
|||
}
|
||||
|
||||
func doTestHandleResponse_DuplicateRequest(t *testing.T, svc *tests.TestService, createAppFn tests.CreateAppFn, version string) {
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
reqPrivateKey := nostr.GeneratePrivateKey()
|
||||
reqPubkey, err := nostr.GetPublicKey(reqPrivateKey)
|
||||
|
|
@ -266,7 +270,8 @@ func TestHandleResponse_Nip44_NoPermission(t *testing.T) {
|
|||
}
|
||||
|
||||
func doTestHandleResponse_NoPermission(t *testing.T, svc *tests.TestService, createAppFn tests.CreateAppFn, version string) {
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
reqPrivateKey := nostr.GeneratePrivateKey()
|
||||
reqPubkey, err := nostr.GetPublicKey(reqPrivateKey)
|
||||
|
|
@ -337,7 +342,8 @@ func TestHandleResponse_Nip44_OldRequestForPayment(t *testing.T) {
|
|||
}
|
||||
|
||||
func doTestHandleResponse_OldRequestForPayment(t *testing.T, svc *tests.TestService, createAppFn tests.CreateAppFn, version string) {
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
reqPrivateKey := nostr.GeneratePrivateKey()
|
||||
reqPubkey, err := nostr.GetPublicKey(reqPrivateKey)
|
||||
|
|
@ -412,7 +418,8 @@ func TestHandleResponse_Nip44_IncorrectPubkey(t *testing.T) {
|
|||
}
|
||||
|
||||
func doTestHandleResponse_IncorrectPubkey(t *testing.T, svc *tests.TestService, createAppFn tests.CreateAppFn, version string) {
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
reqPrivateKey := nostr.GeneratePrivateKey()
|
||||
reqPubkey, err := nostr.GetPublicKey(reqPrivateKey)
|
||||
|
|
@ -469,7 +476,8 @@ func TestHandleResponse_NoApp(t *testing.T) {
|
|||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
reqPrivateKey := nostr.GeneratePrivateKey()
|
||||
reqPubkey, err := nostr.GetPublicKey(reqPrivateKey)
|
||||
|
|
@ -522,7 +530,8 @@ func TestHandleResponse_IncorrectVersions(t *testing.T) {
|
|||
}
|
||||
|
||||
func doTestHandleResponse_IncorrectVersion(t *testing.T, svc *tests.TestService, appVersion, requestVersion string) {
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher)
|
||||
nip47svc := NewNip47Service(svc.DB, svc.Cfg, svc.Keys, svc.EventPublisher, albyOAuthSvc)
|
||||
|
||||
reqPrivateKey := nostr.GeneratePrivateKey()
|
||||
reqPubkey, err := nostr.GetPublicKey(reqPrivateKey)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ const (
|
|||
MULTI_PAY_INVOICE_METHOD = "multi_pay_invoice"
|
||||
MULTI_PAY_KEYSEND_METHOD = "multi_pay_keysend"
|
||||
SIGN_MESSAGE_METHOD = "sign_message"
|
||||
CREATE_CONNECTION_METHOD = "create_connection"
|
||||
)
|
||||
|
||||
type Transaction struct {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package nip47
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getAlby/hub/alby"
|
||||
"github.com/getAlby/hub/apps"
|
||||
"github.com/getAlby/hub/config"
|
||||
"github.com/getAlby/hub/events"
|
||||
"github.com/getAlby/hub/lnclient"
|
||||
|
|
@ -19,6 +21,8 @@ import (
|
|||
type nip47Service struct {
|
||||
permissionsService permissions.PermissionsService
|
||||
transactionsService transactions.TransactionsService
|
||||
appsService apps.AppsService
|
||||
albyOAuthSvc alby.AlbyOAuthService
|
||||
nip47NotificationQueue notifications.Nip47NotificationQueue
|
||||
cfg config.Config
|
||||
keys keys.Keys
|
||||
|
|
@ -36,15 +40,17 @@ type Nip47Service interface {
|
|||
CreateResponse(initialEvent *nostr.Event, content interface{}, tags nostr.Tags, cipher *cipher.Nip47Cipher, walletPrivKey string) (result *nostr.Event, err error)
|
||||
}
|
||||
|
||||
func NewNip47Service(db *gorm.DB, cfg config.Config, keys keys.Keys, eventPublisher events.EventPublisher) *nip47Service {
|
||||
func NewNip47Service(db *gorm.DB, cfg config.Config, keys keys.Keys, eventPublisher events.EventPublisher, albyOAuthSvc alby.AlbyOAuthService) *nip47Service {
|
||||
return &nip47Service{
|
||||
nip47NotificationQueue: notifications.NewNip47NotificationQueue(),
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
permissionsService: permissions.NewPermissionsService(db, eventPublisher),
|
||||
transactionsService: transactions.NewTransactionsService(db, eventPublisher),
|
||||
appsService: apps.NewAppsService(db, eventPublisher, keys, cfg),
|
||||
eventPublisher: eventPublisher,
|
||||
keys: keys,
|
||||
albyOAuthSvc: albyOAuthSvc,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ func (svc *permissionsService) GetPermittedMethods(app *db.App, lnClient lnclien
|
|||
// only return methods supported by the lnClient
|
||||
lnClientSupportedMethods := lnClient.GetSupportedNIP47Methods()
|
||||
requestMethods = utils.Filter(requestMethods, func(requestMethod string) bool {
|
||||
// TODO: better way to exclude methods unrelated to the lnclient
|
||||
if requestMethod == models.CREATE_CONNECTION_METHOD {
|
||||
return true
|
||||
}
|
||||
|
||||
return slices.Contains(lnClientSupportedMethods, requestMethod)
|
||||
})
|
||||
|
||||
|
|
@ -121,6 +126,8 @@ func scopeToRequestMethods(scope string) []string {
|
|||
return []string{models.LIST_TRANSACTIONS_METHOD}
|
||||
case constants.SIGN_MESSAGE_SCOPE:
|
||||
return []string{models.SIGN_MESSAGE_METHOD}
|
||||
case constants.SUPERUSER_SCOPE:
|
||||
return []string{models.CREATE_CONNECTION_METHOD}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
|
@ -158,6 +165,8 @@ func RequestMethodToScope(requestMethod string) (string, error) {
|
|||
return constants.LIST_TRANSACTIONS_SCOPE, nil
|
||||
case models.SIGN_MESSAGE_METHOD:
|
||||
return constants.SIGN_MESSAGE_SCOPE, nil
|
||||
case models.CREATE_CONNECTION_METHOD:
|
||||
return constants.SUPERUSER_SCOPE, nil
|
||||
}
|
||||
logger.Logger.WithField("request_method", requestMethod).Error("Unsupported request method")
|
||||
return "", fmt.Errorf("unsupported request method: %s", requestMethod)
|
||||
|
|
@ -173,6 +182,7 @@ func AllScopes() []string {
|
|||
constants.LIST_TRANSACTIONS_SCOPE,
|
||||
constants.SIGN_MESSAGE_SCOPE,
|
||||
constants.NOTIFICATIONS_SCOPE,
|
||||
constants.SUPERUSER_SCOPE,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,35 +56,6 @@ func TestHasPermission_Expired(t *testing.T) {
|
|||
assert.Equal(t, "This app has expired", message)
|
||||
}
|
||||
|
||||
// TODO: move to transactions service
|
||||
/*func TestHasPermission_Exceeded(t *testing.T) {
|
||||
defer tests.RemoveTestService()
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
|
||||
app, _, err := tests.CreateApp(svc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
budgetRenewal := "never"
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
Scope: constants.PAY_INVOICE_SCOPE,
|
||||
MaxAmountSat: 10,
|
||||
BudgetRenewal: budgetRenewal,
|
||||
ExpiresAt: &expiresAt,
|
||||
}
|
||||
err = svc.DB.Create(appPermission).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
permissionsSvc := NewPermissionsService(svc.DB, svc.EventPublisher)
|
||||
result, code, message := permissionsSvc.HasPermission(app, PAY_INVOICE_SCOPE, 100*1000)
|
||||
assert.False(t, result)
|
||||
assert.Equal(t, constants.ERROR_QUOTA_EXCEEDED, code)
|
||||
assert.Equal(t, "Insufficient budget remaining to make payment", message)
|
||||
}*/
|
||||
|
||||
func TestHasPermission_OK(t *testing.T) {
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -145,6 +116,16 @@ func TestRequestMethodsToScopes_GetInfo(t *testing.T) {
|
|||
assert.Equal(t, []string{constants.GET_INFO_SCOPE}, scopes)
|
||||
}
|
||||
|
||||
func TestRequestMethodToScope_CreateConnection(t *testing.T) {
|
||||
scope, err := RequestMethodToScope(models.CREATE_CONNECTION_METHOD)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, constants.SUPERUSER_SCOPE, scope)
|
||||
}
|
||||
func TestScopeToRequestMethods_Superuser(t *testing.T) {
|
||||
methods := scopeToRequestMethods(constants.SUPERUSER_SCOPE)
|
||||
assert.Equal(t, []string{models.CREATE_CONNECTION_METHOD}, methods)
|
||||
}
|
||||
|
||||
func TestGetPermittedMethods_AlwaysGranted(t *testing.T) {
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ func (svc *nip47Service) PublishNip47Info(ctx context.Context, relay nostrmodels
|
|||
}
|
||||
capabilities = svc.permissionsService.GetPermittedMethods(&app, lnClient)
|
||||
permitsNotifications = svc.permissionsService.PermitsNotifications(&app)
|
||||
|
||||
// NWA: associate the info event with the app so that the app can receive the wallet pubkey
|
||||
tags = append(tags, []string{"p", app.AppPubkey})
|
||||
}
|
||||
if permitsNotifications && len(lnClient.GetSupportedNIP47NotificationTypes()) > 0 {
|
||||
capabilities = append(capabilities, "notifications")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/events"
|
||||
"github.com/getAlby/hub/logger"
|
||||
)
|
||||
|
|
@ -33,6 +34,18 @@ func (s *createAppConsumer) ConsumeEvent(ctx context.Context, event *events.Even
|
|||
logger.Logger.WithField("event", event).Error("Failed to get app id")
|
||||
return
|
||||
}
|
||||
|
||||
app := db.App{}
|
||||
err := s.svc.db.First(&app, &db.App{
|
||||
ID: id,
|
||||
}).Error
|
||||
if err != nil {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"id": id,
|
||||
}).WithError(err).Error("Failed to find app for id")
|
||||
return
|
||||
}
|
||||
|
||||
walletPrivKey, err := s.svc.keys.GetAppWalletKey(id)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).Error("Failed to calculate app wallet priv key")
|
||||
|
|
|
|||
|
|
@ -113,14 +113,16 @@ func NewService(ctx context.Context) (*service, error) {
|
|||
|
||||
keys := keys.NewKeys()
|
||||
|
||||
albyOAuthSvc := alby.NewAlbyOAuthService(gormDB, cfg, keys, eventPublisher)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
svc := &service{
|
||||
cfg: cfg,
|
||||
ctx: ctx,
|
||||
wg: &wg,
|
||||
eventPublisher: eventPublisher,
|
||||
albyOAuthSvc: alby.NewAlbyOAuthService(gormDB, cfg, keys, eventPublisher),
|
||||
nip47Service: nip47.NewNip47Service(gormDB, cfg, keys, eventPublisher),
|
||||
albyOAuthSvc: albyOAuthSvc,
|
||||
nip47Service: nip47.NewNip47Service(gormDB, cfg, keys, eventPublisher, albyOAuthSvc),
|
||||
transactionsService: transactions.NewTransactionsService(gormDB, eventPublisher),
|
||||
db: gormDB,
|
||||
keys: keys,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package tests
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/getAlby/hub/constants"
|
||||
db "github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/events"
|
||||
"github.com/getAlby/hub/nip47/cipher"
|
||||
|
|
@ -27,7 +28,7 @@ func CreateAppWithPrivateKey(svc *TestService, senderPrivkey, nip47Version strin
|
|||
}
|
||||
|
||||
var expiresAt *time.Time
|
||||
app, pairingSecretKey, err := svc.AppsService.CreateApp("test", senderPubkey, 0, "monthly", expiresAt, nil, false, nil)
|
||||
app, pairingSecretKey, err := svc.AppsService.CreateApp("test", senderPubkey, 0, "monthly", expiresAt, []string{constants.GET_INFO_SCOPE}, false, nil)
|
||||
if pairingSecretKey == "" {
|
||||
pairingSecretKey = senderPrivkey
|
||||
}
|
||||
|
|
|
|||
646
tests/mocks/Config.go
Normal file
646
tests/mocks/Config.go
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
// Code generated by mockery v2.52.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
config "github.com/getAlby/hub/config"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockConfig is an autogenerated mock type for the Config type
|
||||
type MockConfig struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockConfig_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockConfig) EXPECT() *MockConfig_Expecter {
|
||||
return &MockConfig_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// ChangeUnlockPassword provides a mock function with given fields: currentUnlockPassword, newUnlockPassword
|
||||
func (_m *MockConfig) ChangeUnlockPassword(currentUnlockPassword string, newUnlockPassword string) error {
|
||||
ret := _m.Called(currentUnlockPassword, newUnlockPassword)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ChangeUnlockPassword")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) error); ok {
|
||||
r0 = rf(currentUnlockPassword, newUnlockPassword)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_ChangeUnlockPassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChangeUnlockPassword'
|
||||
type MockConfig_ChangeUnlockPassword_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ChangeUnlockPassword is a helper method to define mock.On call
|
||||
// - currentUnlockPassword string
|
||||
// - newUnlockPassword string
|
||||
func (_e *MockConfig_Expecter) ChangeUnlockPassword(currentUnlockPassword interface{}, newUnlockPassword interface{}) *MockConfig_ChangeUnlockPassword_Call {
|
||||
return &MockConfig_ChangeUnlockPassword_Call{Call: _e.mock.On("ChangeUnlockPassword", currentUnlockPassword, newUnlockPassword)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_ChangeUnlockPassword_Call) Run(run func(currentUnlockPassword string, newUnlockPassword string)) *MockConfig_ChangeUnlockPassword_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_ChangeUnlockPassword_Call) Return(_a0 error) *MockConfig_ChangeUnlockPassword_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_ChangeUnlockPassword_Call) RunAndReturn(run func(string, string) error) *MockConfig_ChangeUnlockPassword_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CheckUnlockPassword provides a mock function with given fields: password
|
||||
func (_m *MockConfig) CheckUnlockPassword(password string) bool {
|
||||
ret := _m.Called(password)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CheckUnlockPassword")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(string) bool); ok {
|
||||
r0 = rf(password)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_CheckUnlockPassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckUnlockPassword'
|
||||
type MockConfig_CheckUnlockPassword_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CheckUnlockPassword is a helper method to define mock.On call
|
||||
// - password string
|
||||
func (_e *MockConfig_Expecter) CheckUnlockPassword(password interface{}) *MockConfig_CheckUnlockPassword_Call {
|
||||
return &MockConfig_CheckUnlockPassword_Call{Call: _e.mock.On("CheckUnlockPassword", password)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_CheckUnlockPassword_Call) Run(run func(password string)) *MockConfig_CheckUnlockPassword_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_CheckUnlockPassword_Call) Return(_a0 bool) *MockConfig_CheckUnlockPassword_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_CheckUnlockPassword_Call) RunAndReturn(run func(string) bool) *MockConfig_CheckUnlockPassword_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: key, encryptionKey
|
||||
func (_m *MockConfig) Get(key string, encryptionKey string) (string, error) {
|
||||
ret := _m.Called(key, encryptionKey)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Get")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok {
|
||||
return rf(key, encryptionKey)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string) string); ok {
|
||||
r0 = rf(key, encryptionKey)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(key, encryptionKey)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockConfig_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
|
||||
type MockConfig_Get_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Get is a helper method to define mock.On call
|
||||
// - key string
|
||||
// - encryptionKey string
|
||||
func (_e *MockConfig_Expecter) Get(key interface{}, encryptionKey interface{}) *MockConfig_Get_Call {
|
||||
return &MockConfig_Get_Call{Call: _e.mock.On("Get", key, encryptionKey)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_Get_Call) Run(run func(key string, encryptionKey string)) *MockConfig_Get_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_Get_Call) Return(_a0 string, _a1 error) *MockConfig_Get_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_Get_Call) RunAndReturn(run func(string, string) (string, error)) *MockConfig_Get_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetCurrency provides a mock function with no fields
|
||||
func (_m *MockConfig) GetCurrency() string {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetCurrency")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_GetCurrency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrency'
|
||||
type MockConfig_GetCurrency_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetCurrency is a helper method to define mock.On call
|
||||
func (_e *MockConfig_Expecter) GetCurrency() *MockConfig_GetCurrency_Call {
|
||||
return &MockConfig_GetCurrency_Call{Call: _e.mock.On("GetCurrency")}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetCurrency_Call) Run(run func()) *MockConfig_GetCurrency_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetCurrency_Call) Return(_a0 string) *MockConfig_GetCurrency_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetCurrency_Call) RunAndReturn(run func() string) *MockConfig_GetCurrency_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetEnv provides a mock function with no fields
|
||||
func (_m *MockConfig) GetEnv() *config.AppConfig {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetEnv")
|
||||
}
|
||||
|
||||
var r0 *config.AppConfig
|
||||
if rf, ok := ret.Get(0).(func() *config.AppConfig); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*config.AppConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_GetEnv_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEnv'
|
||||
type MockConfig_GetEnv_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetEnv is a helper method to define mock.On call
|
||||
func (_e *MockConfig_Expecter) GetEnv() *MockConfig_GetEnv_Call {
|
||||
return &MockConfig_GetEnv_Call{Call: _e.mock.On("GetEnv")}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetEnv_Call) Run(run func()) *MockConfig_GetEnv_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetEnv_Call) Return(_a0 *config.AppConfig) *MockConfig_GetEnv_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetEnv_Call) RunAndReturn(run func() *config.AppConfig) *MockConfig_GetEnv_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetJWTSecret provides a mock function with no fields
|
||||
func (_m *MockConfig) GetJWTSecret() string {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetJWTSecret")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_GetJWTSecret_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetJWTSecret'
|
||||
type MockConfig_GetJWTSecret_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetJWTSecret is a helper method to define mock.On call
|
||||
func (_e *MockConfig_Expecter) GetJWTSecret() *MockConfig_GetJWTSecret_Call {
|
||||
return &MockConfig_GetJWTSecret_Call{Call: _e.mock.On("GetJWTSecret")}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetJWTSecret_Call) Run(run func()) *MockConfig_GetJWTSecret_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetJWTSecret_Call) Return(_a0 string) *MockConfig_GetJWTSecret_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetJWTSecret_Call) RunAndReturn(run func() string) *MockConfig_GetJWTSecret_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetRelayUrl provides a mock function with no fields
|
||||
func (_m *MockConfig) GetRelayUrl() string {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRelayUrl")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_GetRelayUrl_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRelayUrl'
|
||||
type MockConfig_GetRelayUrl_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetRelayUrl is a helper method to define mock.On call
|
||||
func (_e *MockConfig_Expecter) GetRelayUrl() *MockConfig_GetRelayUrl_Call {
|
||||
return &MockConfig_GetRelayUrl_Call{Call: _e.mock.On("GetRelayUrl")}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetRelayUrl_Call) Run(run func()) *MockConfig_GetRelayUrl_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetRelayUrl_Call) Return(_a0 string) *MockConfig_GetRelayUrl_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_GetRelayUrl_Call) RunAndReturn(run func() string) *MockConfig_GetRelayUrl_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SaveUnlockPasswordCheck provides a mock function with given fields: encryptionKey
|
||||
func (_m *MockConfig) SaveUnlockPasswordCheck(encryptionKey string) error {
|
||||
ret := _m.Called(encryptionKey)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SaveUnlockPasswordCheck")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(encryptionKey)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_SaveUnlockPasswordCheck_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveUnlockPasswordCheck'
|
||||
type MockConfig_SaveUnlockPasswordCheck_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SaveUnlockPasswordCheck is a helper method to define mock.On call
|
||||
// - encryptionKey string
|
||||
func (_e *MockConfig_Expecter) SaveUnlockPasswordCheck(encryptionKey interface{}) *MockConfig_SaveUnlockPasswordCheck_Call {
|
||||
return &MockConfig_SaveUnlockPasswordCheck_Call{Call: _e.mock.On("SaveUnlockPasswordCheck", encryptionKey)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SaveUnlockPasswordCheck_Call) Run(run func(encryptionKey string)) *MockConfig_SaveUnlockPasswordCheck_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SaveUnlockPasswordCheck_Call) Return(_a0 error) *MockConfig_SaveUnlockPasswordCheck_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SaveUnlockPasswordCheck_Call) RunAndReturn(run func(string) error) *MockConfig_SaveUnlockPasswordCheck_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetAutoUnlockPassword provides a mock function with given fields: unlockPassword
|
||||
func (_m *MockConfig) SetAutoUnlockPassword(unlockPassword string) error {
|
||||
ret := _m.Called(unlockPassword)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SetAutoUnlockPassword")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(unlockPassword)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_SetAutoUnlockPassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAutoUnlockPassword'
|
||||
type MockConfig_SetAutoUnlockPassword_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SetAutoUnlockPassword is a helper method to define mock.On call
|
||||
// - unlockPassword string
|
||||
func (_e *MockConfig_Expecter) SetAutoUnlockPassword(unlockPassword interface{}) *MockConfig_SetAutoUnlockPassword_Call {
|
||||
return &MockConfig_SetAutoUnlockPassword_Call{Call: _e.mock.On("SetAutoUnlockPassword", unlockPassword)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetAutoUnlockPassword_Call) Run(run func(unlockPassword string)) *MockConfig_SetAutoUnlockPassword_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetAutoUnlockPassword_Call) Return(_a0 error) *MockConfig_SetAutoUnlockPassword_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetAutoUnlockPassword_Call) RunAndReturn(run func(string) error) *MockConfig_SetAutoUnlockPassword_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetCurrency provides a mock function with given fields: value
|
||||
func (_m *MockConfig) SetCurrency(value string) error {
|
||||
ret := _m.Called(value)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SetCurrency")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(value)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_SetCurrency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetCurrency'
|
||||
type MockConfig_SetCurrency_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SetCurrency is a helper method to define mock.On call
|
||||
// - value string
|
||||
func (_e *MockConfig_Expecter) SetCurrency(value interface{}) *MockConfig_SetCurrency_Call {
|
||||
return &MockConfig_SetCurrency_Call{Call: _e.mock.On("SetCurrency", value)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetCurrency_Call) Run(run func(value string)) *MockConfig_SetCurrency_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetCurrency_Call) Return(_a0 error) *MockConfig_SetCurrency_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetCurrency_Call) RunAndReturn(run func(string) error) *MockConfig_SetCurrency_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetIgnore provides a mock function with given fields: key, value, encryptionKey
|
||||
func (_m *MockConfig) SetIgnore(key string, value string, encryptionKey string) error {
|
||||
ret := _m.Called(key, value, encryptionKey)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SetIgnore")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, string) error); ok {
|
||||
r0 = rf(key, value, encryptionKey)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_SetIgnore_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetIgnore'
|
||||
type MockConfig_SetIgnore_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SetIgnore is a helper method to define mock.On call
|
||||
// - key string
|
||||
// - value string
|
||||
// - encryptionKey string
|
||||
func (_e *MockConfig_Expecter) SetIgnore(key interface{}, value interface{}, encryptionKey interface{}) *MockConfig_SetIgnore_Call {
|
||||
return &MockConfig_SetIgnore_Call{Call: _e.mock.On("SetIgnore", key, value, encryptionKey)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetIgnore_Call) Run(run func(key string, value string, encryptionKey string)) *MockConfig_SetIgnore_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetIgnore_Call) Return(_a0 error) *MockConfig_SetIgnore_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetIgnore_Call) RunAndReturn(run func(string, string, string) error) *MockConfig_SetIgnore_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetUpdate provides a mock function with given fields: key, value, encryptionKey
|
||||
func (_m *MockConfig) SetUpdate(key string, value string, encryptionKey string) error {
|
||||
ret := _m.Called(key, value, encryptionKey)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SetUpdate")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, string) error); ok {
|
||||
r0 = rf(key, value, encryptionKey)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_SetUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetUpdate'
|
||||
type MockConfig_SetUpdate_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SetUpdate is a helper method to define mock.On call
|
||||
// - key string
|
||||
// - value string
|
||||
// - encryptionKey string
|
||||
func (_e *MockConfig_Expecter) SetUpdate(key interface{}, value interface{}, encryptionKey interface{}) *MockConfig_SetUpdate_Call {
|
||||
return &MockConfig_SetUpdate_Call{Call: _e.mock.On("SetUpdate", key, value, encryptionKey)}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetUpdate_Call) Run(run func(key string, value string, encryptionKey string)) *MockConfig_SetUpdate_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string), args[1].(string), args[2].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetUpdate_Call) Return(_a0 error) *MockConfig_SetUpdate_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetUpdate_Call) RunAndReturn(run func(string, string, string) error) *MockConfig_SetUpdate_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// SetupCompleted provides a mock function with no fields
|
||||
func (_m *MockConfig) SetupCompleted() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for SetupCompleted")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// MockConfig_SetupCompleted_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetupCompleted'
|
||||
type MockConfig_SetupCompleted_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// SetupCompleted is a helper method to define mock.On call
|
||||
func (_e *MockConfig_Expecter) SetupCompleted() *MockConfig_SetupCompleted_Call {
|
||||
return &MockConfig_SetupCompleted_Call{Call: _e.mock.On("SetupCompleted")}
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetupCompleted_Call) Run(run func()) *MockConfig_SetupCompleted_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetupCompleted_Call) Return(_a0 bool) *MockConfig_SetupCompleted_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockConfig_SetupCompleted_Call) RunAndReturn(run func() bool) *MockConfig_SetupCompleted_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockConfig creates a new instance of MockConfig. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockConfig(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockConfig {
|
||||
mock := &MockConfig{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ func CreateTestServiceWithMnemonic(t *testing.T, mnemonic string, unlockPassword
|
|||
|
||||
eventPublisher := events.NewEventPublisher()
|
||||
|
||||
appsService := apps.NewAppsService(gormDb, eventPublisher, keys)
|
||||
appsService := apps.NewAppsService(gormDb, eventPublisher, keys, cfg)
|
||||
|
||||
return &TestService{
|
||||
Cfg: cfg,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func NewApp(svc service.Service) *WailsApp {
|
|||
svc: svc,
|
||||
api: api.NewAPI(svc, svc.GetDB(), svc.GetConfig(), svc.GetKeys(), svc.GetAlbyOAuthSvc(), svc.GetEventPublisher()),
|
||||
db: svc.GetDB(),
|
||||
appsSvc: apps.NewAppsService(svc.GetDB(), svc.GetEventPublisher(), svc.GetKeys()),
|
||||
appsSvc: apps.NewAppsService(svc.GetDB(), svc.GetEventPublisher(), svc.GetKeys(), svc.GetConfig()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue