feat: increase isolated app balance (#710)

This commit is contained in:
Roland 2024-10-28 13:29:34 +07:00 committed by GitHub
parent 430c9fe9d2
commit 731d248129
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 300 additions and 92 deletions

View file

@ -20,6 +20,7 @@ import (
"golang.org/x/oauth2"
"gorm.io/gorm"
"github.com/getAlby/hub/apps"
"github.com/getAlby/hub/config"
"github.com/getAlby/hub/constants"
"github.com/getAlby/hub/db"
@ -368,9 +369,9 @@ func (svc *albyOAuthService) DrainSharedWallet(ctx context.Context, lnClient lnc
10 // Alby fee reserve (10 sats)
if amountSat < 1 {
return errors.New("Not enough balance remaining")
return errors.New("not enough balance remaining")
}
amount := amountSat * 1000
amount := uint64(amountSat * 1000)
logger.Logger.WithField("amount", amount).WithError(err).Error("Draining Alby shared wallet funds")
@ -526,7 +527,7 @@ func (svc *albyOAuthService) LinkAccount(ctx context.Context, lnClient lnclient.
scopes = append(scopes, constants.NOTIFICATIONS_SCOPE)
}
app, _, err := db.NewDBService(svc.db, svc.eventPublisher).CreateApp(
app, _, err := apps.NewAppsService(svc.db, svc.eventPublisher).CreateApp(
ALBY_ACCOUNT_APP_NAME,
connectionPubkey,
budget,

View file

@ -17,6 +17,7 @@ import (
"gorm.io/gorm"
"github.com/getAlby/hub/alby"
"github.com/getAlby/hub/apps"
"github.com/getAlby/hub/config"
"github.com/getAlby/hub/constants"
"github.com/getAlby/hub/db"
@ -33,7 +34,7 @@ import (
type api struct {
db *gorm.DB
dbSvc db.DBService
appsSvc apps.AppsService
cfg config.Config
svc service.Service
permissionsSvc permissions.PermissionsService
@ -46,7 +47,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,
dbSvc: db.NewDBService(gormDB, eventPublisher),
appsSvc: apps.NewAppsService(gormDB, eventPublisher),
cfg: config,
svc: svc,
permissionsSvc: permissions.NewPermissionsService(gormDB, eventPublisher),
@ -71,7 +72,7 @@ func (api *api) CreateApp(createAppRequest *CreateAppRequest) (*CreateAppRespons
}
}
app, pairingSecretKey, err := api.dbSvc.CreateApp(
app, pairingSecretKey, err := api.appsSvc.CreateApp(
createAppRequest.Name,
createAppRequest.Pubkey,
createAppRequest.MaxAmountSat,

View file

@ -12,9 +12,10 @@ import (
type API interface {
CreateApp(createAppRequest *CreateAppRequest) (*CreateAppResponse, error)
UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) error
DeleteApp(userApp *db.App) error
GetApp(userApp *db.App) *App
UpdateApp(app *db.App, updateAppRequest *UpdateAppRequest) error
TopupIsolatedApp(ctx context.Context, app *db.App, amountMsat uint64) error
DeleteApp(app *db.App) error
GetApp(app *db.App) *App
ListApps() ([]App, error)
ListChannels(ctx context.Context) ([]Channel, error)
GetChannelPeerSuggestions(ctx context.Context) ([]alby.ChannelPeerSuggestion, error)
@ -36,7 +37,7 @@ type API interface {
GetBalances(ctx context.Context) (*BalancesResponse, error)
ListTransactions(ctx context.Context, limit uint64, offset uint64) (*ListTransactionsResponse, error)
SendPayment(ctx context.Context, invoice string) (*SendPaymentResponse, error)
CreateInvoice(ctx context.Context, amount int64, description string) (*MakeInvoiceResponse, error)
CreateInvoice(ctx context.Context, amount uint64, description string) (*MakeInvoiceResponse, error)
LookupInvoice(ctx context.Context, paymentHash string) (*LookupInvoiceResponse, error)
RequestMempoolApi(endpoint string) (interface{}, error)
GetInfo(ctx context.Context) (*InfoResponse, error)
@ -86,6 +87,10 @@ type UpdateAppRequest struct {
Metadata Metadata `json:"metadata,omitempty"`
}
type TopupIsolatedAppRequest struct {
AmountSat uint64 `json:"amountSat"`
}
type CreateAppRequest struct {
Name string `json:"name"`
Pubkey string `json:"pubkey"`
@ -283,7 +288,7 @@ type SignMessageResponse struct {
}
type MakeInvoiceRequest struct {
Amount int64 `json:"amount"`
Amount uint64 `json:"amount"`
Description string `json:"description"`
}

View file

@ -7,12 +7,13 @@ import (
"strings"
"time"
"github.com/getAlby/hub/db"
"github.com/getAlby/hub/logger"
"github.com/getAlby/hub/transactions"
"github.com/sirupsen/logrus"
)
func (api *api) CreateInvoice(ctx context.Context, amount int64, description string) (*MakeInvoiceResponse, error) {
func (api *api) CreateInvoice(ctx context.Context, amount uint64, description string) (*MakeInvoiceResponse, error) {
if api.svc.GetLNClient() == nil {
return nil, errors.New("LNClient not started")
}
@ -116,6 +117,24 @@ func toApiTransaction(transaction *transactions.Transaction) *Transaction {
}
}
func (api *api) TopupIsolatedApp(ctx context.Context, userApp *db.App, amountMsat uint64) error {
if api.svc.GetLNClient() == nil {
return errors.New("LNClient not started")
}
if !userApp.Isolated {
return errors.New("app is not isolated")
}
transaction, err := api.svc.GetTransactionsService().MakeInvoice(ctx, amountMsat, "top up", "", 0, nil, api.svc.GetLNClient(), &userApp.ID, nil)
if err != nil {
return err
}
_, err = api.svc.GetTransactionsService().SendPaymentSync(ctx, transaction.PaymentRequest, api.svc.GetLNClient(), nil, nil)
return err
}
func toApiBoostagram(boostagram *transactions.Boostagram) *Boostagram {
return &Boostagram{
AppName: boostagram.AppName,

View file

@ -1,4 +1,4 @@
package db
package apps
import (
"encoding/hex"
@ -9,6 +9,7 @@ import (
"time"
"github.com/getAlby/hub/constants"
"github.com/getAlby/hub/db"
"github.com/getAlby/hub/events"
"github.com/getAlby/hub/logger"
"github.com/nbd-wtf/go-nostr"
@ -16,19 +17,24 @@ import (
"gorm.io/gorm"
)
type dbService struct {
type AppsService interface {
CreateApp(name string, pubkey string, maxAmountSat uint64, budgetRenewal string, expiresAt *time.Time, scopes []string, isolated bool, metadata map[string]interface{}) (*db.App, string, error)
GetAppByPubkey(pubkey string) *db.App
}
type appsService struct {
db *gorm.DB
eventPublisher events.EventPublisher
}
func NewDBService(db *gorm.DB, eventPublisher events.EventPublisher) *dbService {
return &dbService{
func NewAppsService(db *gorm.DB, eventPublisher events.EventPublisher) *appsService {
return &appsService{
db: db,
eventPublisher: eventPublisher,
}
}
func (svc *dbService) CreateApp(name string, pubkey string, maxAmountSat uint64, budgetRenewal string, expiresAt *time.Time, scopes []string, isolated bool, metadata map[string]interface{}) (*App, string, error) {
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 subaccount
return nil, "", errors.New("isolated app cannot have sign_message scope")
@ -59,7 +65,7 @@ func (svc *dbService) CreateApp(name string, pubkey string, maxAmountSat uint64,
}
}
app := App{Name: name, NostrPubkey: pairingPublicKey, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)}
app := db.App{Name: name, NostrPubkey: pairingPublicKey, Isolated: isolated, Metadata: datatypes.JSON(metadataBytes)}
err := svc.db.Transaction(func(tx *gorm.DB) error {
err := tx.Save(&app).Error
@ -68,7 +74,7 @@ func (svc *dbService) CreateApp(name string, pubkey string, maxAmountSat uint64,
}
for _, scope := range scopes {
appPermission := AppPermission{
appPermission := db.AppPermission{
App: app,
Scope: scope,
ExpiresAt: expiresAt,
@ -100,3 +106,12 @@ func (svc *dbService) CreateApp(name string, pubkey string, maxAmountSat uint64,
return &app, pairingSecretKey, nil
}
func (svc *appsService) GetAppByPubkey(pubkey string) *db.App {
dbApp := db.App{}
findResult := svc.db.Where("nostr_pubkey = ?", pubkey).First(&dbApp)
if findResult.RowsAffected == 0 {
return nil
}
return &dbApp
}

View file

@ -86,10 +86,6 @@ type Transaction struct {
FailureReason string
}
type DBService interface {
CreateApp(name string, pubkey string, maxAmountSat uint64, budgetRenewal string, expiresAt *time.Time, scopes []string, isolated bool, metadata map[string]interface{}) (*App, string, error)
}
const (
REQUEST_EVENT_STATE_HANDLER_EXECUTING = "executing"
REQUEST_EVENT_STATE_HANDLER_EXECUTED = "executed"

View file

@ -0,0 +1,90 @@
import React from "react";
import {
AlertDialog,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "src/components/ui/alert-dialog";
import { Input } from "src/components/ui/input";
import { LoadingButton } from "src/components/ui/loading-button";
import { useToast } from "src/components/ui/use-toast";
import { useApp } from "src/hooks/useApp";
import { handleRequestError } from "src/utils/handleRequestError";
import { request } from "src/utils/request";
type IsolatedAppTopupProps = {
appPubkey: string;
};
export function IsolatedAppTopupDialog({
appPubkey,
children,
}: React.PropsWithChildren<IsolatedAppTopupProps>) {
const { mutate: reloadApp } = useApp(appPubkey);
const [amountSat, setAmountSat] = React.useState("");
const [loading, setLoading] = React.useState(false);
const [open, setOpen] = React.useState(false);
const { toast } = useToast();
async function onSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
try {
await request(`/api/apps/${appPubkey}/topup`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
amountSat: +amountSat,
}),
});
await reloadApp();
toast({
title: "Successfully increased isolated app balance",
});
setOpen(false);
} catch (error) {
handleRequestError(
toast,
"Failed to increase isolated app balance",
error
);
}
setLoading(false);
}
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
<AlertDialogContent>
<form onSubmit={onSubmit}>
<AlertDialogHeader>
<AlertDialogTitle>Increase Isolated App Balance</AlertDialogTitle>
<AlertDialogDescription>
As the owner of your Alby Hub, you must make sure you have enough
funds in your channels for this app to make payments matching its
balance.
</AlertDialogDescription>
<Input
autoFocus
id="amount"
type="number"
required
value={amountSat}
onChange={(e) => {
setAmountSat(e.target.value.trim());
}}
/>
</AlertDialogHeader>
<AlertDialogFooter className="mt-5">
<AlertDialogCancel>Cancel</AlertDialogCancel>
<LoadingButton loading={loading}>Top Up</LoadingButton>
</AlertDialogFooter>
</form>
</AlertDialogContent>
</AlertDialog>
);
}

View file

@ -4,6 +4,7 @@ import { Link, Navigate, useLocation, useNavigate } from "react-router-dom";
import AppHeader from "src/components/AppHeader";
import ExternalLink from "src/components/ExternalLink";
import { IsolatedAppTopupDialog } from "src/components/IsolatedAppTopupDialog";
import Loading from "src/components/Loading";
import QRCode from "src/components/QRCode";
import { SuggestedApp, suggestedApps } from "src/components/SuggestedAppData";
@ -87,22 +88,36 @@ function AppCreatedInternal() {
/>
<div className="flex flex-col gap-3 sensitive">
<div>
<p>
1. Open{" "}
{appstoreApp?.webLink ? (
<ExternalLink
className="font-semibold underline"
to={appstoreApp.webLink}
>
{appstoreApp.title}
</ExternalLink>
) : (
"the app you wish to connect"
)}{" "}
and look for a way to attach a wallet (most apps provide this option
in settings)
</p>
<p>2. Scan or paste the connection secret</p>
<ol className="list-decimal list-inside">
<li>
Open{" "}
{appstoreApp?.webLink ? (
<ExternalLink
className="font-semibold underline"
to={appstoreApp.webLink}
>
{appstoreApp.title}
</ExternalLink>
) : (
"the app you wish to connect"
)}{" "}
and look for a way to attach a wallet (most apps provide this
option in settings)
</li>
{app?.isolated && (
<li>
Optional: Increase isolated balance (
{new Intl.NumberFormat().format(Math.floor(app.balance / 1000))}{" "}
sats){" "}
<IsolatedAppTopupDialog appPubkey={app.nostrPubkey}>
<Button size="sm" variant="secondary">
Increase
</Button>
</IsolatedAppTopupDialog>
</li>
)}
<li>Scan or paste the connection secret</li>
</ol>
</div>
{app && (
<ConnectAppCard

View file

@ -17,6 +17,7 @@ import { request } from "src/utils/request"; // build the project for this to ap
import { PencilIcon } from "lucide-react";
import AppAvatar from "src/components/AppAvatar";
import AppHeader from "src/components/AppHeader";
import { IsolatedAppTopupDialog } from "src/components/IsolatedAppTopupDialog";
import Loading from "src/components/Loading";
import Permissions from "src/components/Permissions";
import {
@ -244,7 +245,12 @@ function AppInternal({ app, refetchApp, capabilities }: AppInternalProps) {
{new Intl.NumberFormat().format(
Math.floor(app.balance / 1000)
)}{" "}
sats
sats{" "}
<IsolatedAppTopupDialog appPubkey={app.nostrPubkey}>
<Button size="sm" variant="secondary">
Increase
</Button>
</IsolatedAppTopupDialog>
</TableCell>
</TableRow>
)}

View file

@ -3,6 +3,7 @@ import React from "react";
import AppHeader from "src/components/AppHeader";
import AppCard from "src/components/connections/AppCard";
import ExternalLink from "src/components/ExternalLink";
import { IsolatedAppTopupDialog } from "src/components/IsolatedAppTopupDialog";
import {
Accordion,
AccordionContent,
@ -282,6 +283,21 @@ export function UncleJim() {
</AccordionContent>
</AccordionItem>
</Accordion>
{app && (
<>
<p className="mt-5">
{name} currently has{" "}
{new Intl.NumberFormat().format(Math.floor(app.balance / 1000))}{" "}
sats
</p>
<IsolatedAppTopupDialog appPubkey={appPublicKey}>
<Button size="sm" variant="secondary">
Increase
</Button>
</IsolatedAppTopupDialog>
</>
)}
</div>
)}
</div>

View file

@ -16,8 +16,8 @@ import (
"github.com/sirupsen/logrus"
"gorm.io/gorm"
"github.com/getAlby/hub/apps"
"github.com/getAlby/hub/config"
"github.com/getAlby/hub/db"
"github.com/getAlby/hub/events"
"github.com/getAlby/hub/logger"
"github.com/getAlby/hub/service"
@ -43,6 +43,7 @@ type HttpService struct {
cfg config.Config
eventPublisher events.EventPublisher
db *gorm.DB
appsSvc apps.AppsService
}
func NewHttpService(svc service.Service, eventPublisher events.EventPublisher) *HttpService {
@ -52,6 +53,7 @@ func NewHttpService(svc service.Service, eventPublisher events.EventPublisher) *
cfg: svc.GetConfig(),
eventPublisher: eventPublisher,
db: svc.GetDB(),
appsSvc: apps.NewAppsService(svc.GetDB(), eventPublisher),
}
}
@ -116,6 +118,7 @@ func (httpSvc *HttpService) RegisterSharedRoutes(e *echo.Echo) {
restrictedGroup.GET("/api/apps/:pubkey", httpSvc.appsShowHandler)
restrictedGroup.PATCH("/api/apps/:pubkey", httpSvc.appsUpdateHandler)
restrictedGroup.DELETE("/api/apps/:pubkey", httpSvc.appsDeleteHandler)
restrictedGroup.POST("/api/apps/:pubkey/topup", httpSvc.isolatedAppTopupHandler)
restrictedGroup.POST("/api/apps", httpSvc.appsCreateHandler)
restrictedGroup.POST("/api/mnemonic", httpSvc.mnemonicHandler)
restrictedGroup.PATCH("/api/backup-reminder", httpSvc.backupReminderHandler)
@ -756,16 +759,15 @@ func (httpSvc *HttpService) appsListHandler(c echo.Context) error {
func (httpSvc *HttpService) appsShowHandler(c echo.Context) error {
// TODO: move this to DB service
dbApp := db.App{}
findResult := httpSvc.db.Where("nostr_pubkey = ?", c.Param("pubkey")).First(&dbApp)
dbApp := httpSvc.appsSvc.GetAppByPubkey(c.Param("pubkey"))
if findResult.RowsAffected == 0 {
if dbApp == nil {
return c.JSON(http.StatusNotFound, ErrorResponse{
Message: "App does not exist",
Message: "App not found",
})
}
response := httpSvc.api.GetApp(&dbApp)
response := httpSvc.api.GetApp(dbApp)
return c.JSON(http.StatusOK, response)
}
@ -778,17 +780,15 @@ func (httpSvc *HttpService) appsUpdateHandler(c echo.Context) error {
})
}
// TODO: move this to DB service
dbApp := db.App{}
findResult := httpSvc.db.Where("nostr_pubkey = ?", c.Param("pubkey")).First(&dbApp)
dbApp := httpSvc.appsSvc.GetAppByPubkey(c.Param("pubkey"))
if findResult.RowsAffected == 0 {
if dbApp == nil {
return c.JSON(http.StatusNotFound, ErrorResponse{
Message: "App does not exist",
Message: "App not found",
})
}
err := httpSvc.api.UpdateApp(&dbApp, &requestData)
err := httpSvc.api.UpdateApp(dbApp, &requestData)
if err != nil {
logger.Logger.WithError(err).Error("Failed to update app")
@ -800,28 +800,43 @@ func (httpSvc *HttpService) appsUpdateHandler(c echo.Context) error {
return c.NoContent(http.StatusNoContent)
}
func (httpSvc *HttpService) appsDeleteHandler(c echo.Context) error {
pubkey := c.Param("pubkey")
if pubkey == "" {
func (httpSvc *HttpService) isolatedAppTopupHandler(c echo.Context) error {
var requestData api.TopupIsolatedAppRequest
if err := c.Bind(&requestData); err != nil {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "Invalid pubkey parameter",
})
}
// TODO: move this to DB service
dbApp := db.App{}
result := httpSvc.db.Where("nostr_pubkey = ?", pubkey).First(&dbApp)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return c.JSON(http.StatusNotFound, ErrorResponse{
Message: "App not found",
})
}
return c.JSON(http.StatusInternalServerError, ErrorResponse{
Message: "Failed to fetch app",
Message: fmt.Sprintf("Bad request: %s", err.Error()),
})
}
if err := httpSvc.api.DeleteApp(&dbApp); err != nil {
dbApp := httpSvc.appsSvc.GetAppByPubkey(c.Param("pubkey"))
if dbApp == nil {
return c.JSON(http.StatusNotFound, ErrorResponse{
Message: "App not found",
})
}
err := httpSvc.api.TopupIsolatedApp(c.Request().Context(), dbApp, requestData.AmountSat*1000)
if err != nil {
logger.Logger.WithError(err).Error("Failed to topup isolated app")
return c.JSON(http.StatusInternalServerError, ErrorResponse{
Message: fmt.Sprintf("Failed to topup isolated app: %v", err),
})
}
return c.NoContent(http.StatusNoContent)
}
func (httpSvc *HttpService) appsDeleteHandler(c echo.Context) error {
dbApp := httpSvc.appsSvc.GetAppByPubkey(c.Param("pubkey"))
if dbApp == nil {
return c.JSON(http.StatusNotFound, ErrorResponse{
Message: "App not found",
})
}
if err := httpSvc.api.DeleteApp(dbApp); err != nil {
return c.JSON(http.StatusInternalServerError, ErrorResponse{
Message: "Failed to delete app",
})

View file

@ -11,10 +11,10 @@ import (
)
type makeInvoiceParams struct {
Amount int64 `json:"amount"`
Amount uint64 `json:"amount"`
Description string `json:"description"`
DescriptionHash string `json:"description_hash"`
Expiry int64 `json:"expiry"`
Expiry uint64 `json:"expiry"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
type makeInvoiceResponse struct {

View file

@ -33,7 +33,7 @@ type transactionsService struct {
type TransactionsService interface {
events.EventSubscriber
MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error)
MakeInvoice(ctx context.Context, amount uint64, description string, descriptionHash string, expiry uint64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error)
LookupTransaction(ctx context.Context, paymentHash string, transactionType *string, lnClient lnclient.LNClient, appId *uint) (*Transaction, error)
ListTransactions(ctx context.Context, from, until, limit, offset uint64, unpaidOutgoing bool, unpaidIncoming bool, transactionType *string, lnClient lnclient.LNClient, appId *uint) (transactions []Transaction, err error)
SendPaymentSync(ctx context.Context, payReq string, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error)
@ -129,7 +129,7 @@ func NewTransactionsService(db *gorm.DB, eventPublisher events.EventPublisher) *
}
}
func (svc *transactionsService) MakeInvoice(ctx context.Context, amount int64, description string, descriptionHash string, expiry int64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error) {
func (svc *transactionsService) MakeInvoice(ctx context.Context, amount uint64, description string, descriptionHash string, expiry uint64, metadata map[string]interface{}, lnClient lnclient.LNClient, appId *uint, requestEventId *uint) (*Transaction, error) {
var metadataBytes []byte
if metadata != nil {
var err error
@ -143,7 +143,7 @@ func (svc *transactionsService) MakeInvoice(ctx context.Context, amount int64, d
}
}
lnClientTransaction, err := lnClient.MakeInvoice(ctx, amount, description, descriptionHash, expiry)
lnClientTransaction, err := lnClient.MakeInvoice(ctx, int64(amount), description, descriptionHash, int64(expiry))
if err != nil {
logger.Logger.WithError(err).Error("Failed to create transaction")
return nil, err

View file

@ -5,6 +5,7 @@ import (
"embed"
"github.com/getAlby/hub/api"
"github.com/getAlby/hub/apps"
"github.com/getAlby/hub/logger"
"github.com/getAlby/hub/service"
"github.com/wailsapp/wails/v2"
@ -16,17 +17,19 @@ import (
)
type WailsApp struct {
ctx context.Context
svc service.Service
api api.API
db *gorm.DB
ctx context.Context
svc service.Service
api api.API
db *gorm.DB
appsSvc apps.AppsService
}
func NewApp(svc service.Service) *WailsApp {
return &WailsApp{
svc: svc,
api: api.NewAPI(svc, svc.GetDB(), svc.GetConfig(), svc.GetKeys(), svc.GetAlbyOAuthSvc(), svc.GetEventPublisher()),
db: svc.GetDB(),
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()),
}
}

View file

@ -13,7 +13,6 @@ import (
"github.com/getAlby/hub/alby"
"github.com/getAlby/hub/api"
"github.com/getAlby/hub/db"
"github.com/getAlby/hub/logger"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
@ -59,18 +58,14 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
switch {
case len(appMatch) > 1:
pubkey := appMatch[1]
// TODO: move this to DB service
dbApp := db.App{}
findResult := app.db.Where("nostr_pubkey = ?", pubkey).First(&dbApp)
if findResult.RowsAffected == 0 {
dbApp := app.appsSvc.GetAppByPubkey(pubkey)
if dbApp == nil {
return WailsRequestRouterResponse{Body: nil, Error: "App does not exist"}
}
switch method {
case "GET":
app := app.api.GetApp(&dbApp)
app := app.api.GetApp(dbApp)
return WailsRequestRouterResponse{Body: app, Error: ""}
case "PATCH":
updateAppRequest := &api.UpdateAppRequest{}
@ -83,13 +78,13 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
}).WithError(err).Error("Failed to decode request to wails router")
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}
err = app.api.UpdateApp(&dbApp, updateAppRequest)
err = app.api.UpdateApp(dbApp, updateAppRequest)
if err != nil {
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}
return WailsRequestRouterResponse{Body: nil, Error: ""}
case "DELETE":
err := app.api.DeleteApp(&dbApp)
err := app.api.DeleteApp(dbApp)
if err != nil {
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}
@ -97,6 +92,37 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
}
}
appTopupRegex := regexp.MustCompile(
`/api/apps/([0-9a-f]+)/topup`,
)
appTopupMatch := appTopupRegex.FindStringSubmatch(route)
switch {
case len(appTopupMatch) > 1:
pubkey := appTopupMatch[1]
dbApp := app.appsSvc.GetAppByPubkey(pubkey)
if dbApp == nil {
return WailsRequestRouterResponse{Body: nil, Error: "App does not exist"}
}
topupIsolatedAppRequest := &api.TopupIsolatedAppRequest{}
err := json.Unmarshal([]byte(body), topupIsolatedAppRequest)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"route": route,
"method": method,
"body": body,
}).WithError(err).Error("Failed to decode request to wails router")
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}
err = app.api.TopupIsolatedApp(ctx, dbApp, topupIsolatedAppRequest.AmountSat*1000)
if err != nil {
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}
return WailsRequestRouterResponse{Body: nil, Error: ""}
}
peerChannelRegex := regexp.MustCompile(
`/api/peers/([^/]+)/channels/([^/]+)`,
)