Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
feat: use transactions service in NIP-47 handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Jul 3, 2024
1 parent 1a3479e commit d8228ae
Show file tree
Hide file tree
Showing 18 changed files with 113 additions and 62 deletions.
35 changes: 18 additions & 17 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,15 @@ func (api *api) DeleteApp(userApp *db.App) error {
return api.db.Delete(userApp).Error
}

func (api *api) GetApp(userApp *db.App) *App {
func (api *api) GetApp(dbApp *db.App) *App {

var lastEvent db.RequestEvent
lastEventResult := api.db.Where("app_id = ?", userApp.ID).Order("id desc").Limit(1).Find(&lastEvent)
lastEventResult := api.db.Where("app_id = ?", dbApp.ID).Order("id desc").Limit(1).Find(&lastEvent)

paySpecificPermission := db.AppPermission{}
appPermissions := []db.AppPermission{}
var expiresAt *time.Time
api.db.Where("app_id = ?", userApp.ID).Find(&appPermissions)
api.db.Where("app_id = ?", dbApp.ID).Find(&appPermissions)

requestMethods := []string{}
for _, appPerm := range appPermissions {
Expand All @@ -199,11 +199,12 @@ func (api *api) GetApp(userApp *db.App) *App {
}

response := App{
Name: userApp.Name,
Description: userApp.Description,
CreatedAt: userApp.CreatedAt,
UpdatedAt: userApp.UpdatedAt,
NostrPubkey: userApp.NostrPubkey,
ID: dbApp.ID,
Name: dbApp.Name,
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
NostrPubkey: dbApp.NostrPubkey,
ExpiresAt: expiresAt,
MaxAmount: maxAmount,
Scopes: requestMethods,
Expand Down Expand Up @@ -233,17 +234,17 @@ func (api *api) ListApps() ([]App, error) {
}

apiApps := []App{}
for _, userApp := range dbApps {
for _, dbApp := range dbApps {
apiApp := App{
// ID: app.ID,
Name: userApp.Name,
Description: userApp.Description,
CreatedAt: userApp.CreatedAt,
UpdatedAt: userApp.UpdatedAt,
NostrPubkey: userApp.NostrPubkey,
ID: dbApp.ID,
Name: dbApp.Name,
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
NostrPubkey: dbApp.NostrPubkey,
}

for _, appPermission := range permissionsMap[userApp.ID] {
for _, appPermission := range permissionsMap[dbApp.ID] {
apiApp.Scopes = append(apiApp.Scopes, appPermission.Scope)
apiApp.ExpiresAt = appPermission.ExpiresAt
if appPermission.Scope == permissions.PAY_INVOICE_SCOPE {
Expand All @@ -256,7 +257,7 @@ func (api *api) ListApps() ([]App, error) {
}

var lastEvent db.RequestEvent
lastEventResult := api.db.Where("app_id = ?", userApp.ID).Order("id desc").Limit(1).Find(&lastEvent)
lastEventResult := api.db.Where("app_id = ?", dbApp.ID).Order("id desc").Limit(1).Find(&lastEvent)
if lastEventResult.RowsAffected > 0 {
apiApp.LastEventAt = &lastEvent.CreatedAt
}
Expand Down
15 changes: 8 additions & 7 deletions api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,12 @@ type API interface {
}

type App struct {
// ID uint `json:"id"` // ID unused - pubkey is used as ID
Name string `json:"name"`
Description string `json:"description"`
NostrPubkey string `json:"nostrPubkey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`

ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
NostrPubkey string `json:"nostrPubkey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEventAt *time.Time `json:"lastEventAt"`
ExpiresAt *time.Time `json:"expiresAt"`
Scopes []string `json:"scopes"`
Expand Down Expand Up @@ -189,6 +188,7 @@ type MakeInvoiceResponse = Transaction
type LookupInvoiceResponse = Transaction
type ListTransactionsResponse = []Transaction

// TODO: camelCase
type Transaction struct {
Type string `json:"type"`
Invoice string `json:"invoice"`
Expand All @@ -200,6 +200,7 @@ type Transaction struct {
FeesPaid uint64 `json:"fees_paid"`
CreatedAt string `json:"created_at"`
SettledAt *string `json:"settled_at"`
AppId *uint `json:"app_id"`
Metadata interface{} `json:"metadata,omitempty"`
}

Expand Down
3 changes: 2 additions & 1 deletion api/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (api *api) LookupInvoice(ctx context.Context, paymentHash string) (*LookupI
if api.svc.GetLNClient() == nil {
return nil, errors.New("LNClient not started")
}
transaction, err := api.svc.GetTransactionsService().LookupTransaction(ctx, paymentHash, transactions.TRANSACTION_TYPE_INCOMING, api.svc.GetLNClient())
transaction, err := api.svc.GetTransactionsService().LookupTransaction(ctx, paymentHash, api.svc.GetLNClient(), nil)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -80,6 +80,7 @@ func toApiTransaction(transaction *transactions.Transaction) *Transaction {
Preimage: transaction.Preimage,
PaymentHash: transaction.PaymentHash,
Amount: transaction.Amount,
AppId: transaction.AppId,
FeesPaid: fee,
CreatedAt: createdAt,
SettledAt: settledAt,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/AppAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function AppAvatar({ appName, className }: Props) {
<img
src={`data:image/svg+xml;base64,${btoa(gradientAvatar(appName))}`}
alt={appName}
className="block w-full h-full rounded-lg p-1"
className={cn("block w-full h-full rounded-lg p-1", className)}
/>
<span className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white text-xl font-medium capitalize">
{appName.charAt(0)}
Expand Down
17 changes: 15 additions & 2 deletions frontend/src/components/TransactionsList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import dayjs from "dayjs";
import { ArrowDownIcon, ArrowUpIcon, Drum } from "lucide-react";
import AppAvatar from "src/components/AppAvatar";
import EmptyState from "src/components/EmptyState";

import Loading from "src/components/Loading";
import { useApps } from "src/hooks/useApps";
import { useTransactions } from "src/hooks/useTransactions";

function TransactionsList() {
const { data: transactions, isLoading } = useTransactions();
const { data: apps } = useApps();

if (isLoading) {
return <Loading />;
Expand All @@ -26,6 +29,7 @@ function TransactionsList() {
<>
{transactions?.map((tx, i) => {
const type = tx.type;
const app = tx.app_id && apps?.find((app) => app.id === tx.app_id);

return (
<div
Expand All @@ -35,7 +39,12 @@ function TransactionsList() {
>
<div className="flex gap-3">
<div className="flex items-center">
{type == "outgoing" ? (
{app ? (
<AppAvatar
appName={app.name}
className="border-none p-0 rounded-full w-10 h-10 md:w-14 md:h-14"
/>
) : type == "outgoing" ? (
<div
className={
"flex justify-center items-center bg-orange-100 dark:bg-orange-950 rounded-full w-10 h-10 md:w-14 md:h-14"
Expand All @@ -58,7 +67,11 @@ function TransactionsList() {
<div className="overflow-hidden mr-3">
<div className="flex items-center gap-2 truncate dark:text-white">
<p className="text-lg md:text-xl font-semibold">
{type == "incoming" ? "Received" : "Sent"}
{app
? app.name
: type == "incoming"
? "Received"
: "Sent"}
</p>
<p className="text-sm md:text-base truncate text-muted-foreground">
{dayjs(tx.settled_at).fromNow()}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ export type BalancesResponse = {

export type Transaction = {
type: "incoming" | "outgoing";
app_id: number | undefined;
invoice: string;
description: string;
description_hash: string;
Expand Down
7 changes: 6 additions & 1 deletion nip47/controllers/list_transactions_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (controller *nip47Controller) HandleListTransactionsEvent(ctx context.Conte
// make sure a sensible limit is passed
limit = maxLimit
}
transactions, err := controller.lnClient.ListTransactions(ctx, listParams.From, listParams.Until, limit, listParams.Offset, listParams.Unpaid, listParams.Type)
dbTransactions, err := controller.transactionsService.ListTransactions(ctx, listParams.From, listParams.Until, limit, listParams.Offset, listParams.Unpaid, listParams.Type, controller.lnClient)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"params": listParams,
Expand All @@ -65,6 +65,11 @@ func (controller *nip47Controller) HandleListTransactionsEvent(ctx context.Conte
return
}

transactions := []models.Transaction{}
for _, dbTransaction := range dbTransactions {
transactions = append(transactions, *models.ToNip47Transaction(&dbTransaction))
}

responsePayload := &listTransactionsResponse{
Transactions: transactions,
}
Expand Down
6 changes: 3 additions & 3 deletions nip47/controllers/lookup_invoice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type lookupInvoiceResponse struct {
models.Transaction
}

func (controller *nip47Controller) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *models.Request, requestEventId uint, checkPermission checkPermissionFunc, publishResponse publishFunc) {
func (controller *nip47Controller) HandleLookupInvoiceEvent(ctx context.Context, nip47Request *models.Request, requestEventId uint, appId uint, checkPermission checkPermissionFunc, publishResponse publishFunc) {
// basic permissions check
resp := checkPermission(0)
if resp != nil {
Expand Down Expand Up @@ -64,7 +64,7 @@ func (controller *nip47Controller) HandleLookupInvoiceEvent(ctx context.Context,
paymentHash = paymentRequest.PaymentHash
}

transaction, err := controller.lnClient.LookupInvoice(ctx, paymentHash)
dbTransaction, err := controller.transactionsService.LookupTransaction(ctx, paymentHash, controller.lnClient, &appId)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"request_event_id": requestEventId,
Expand All @@ -83,7 +83,7 @@ func (controller *nip47Controller) HandleLookupInvoiceEvent(ctx context.Context,
}

responsePayload := &lookupInvoiceResponse{
Transaction: *transaction,
Transaction: *models.ToNip47Transaction(dbTransaction),
}

publishResponse(&models.Response{
Expand Down
18 changes: 14 additions & 4 deletions nip47/controllers/lookup_invoice_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ func TestHandleLookupInvoiceEvent_NoPermission(t *testing.T) {
err = json.Unmarshal([]byte(nip47LookupInvoiceJson), nip47Request)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{}
app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

Expand All @@ -56,7 +61,7 @@ func TestHandleLookupInvoiceEvent_NoPermission(t *testing.T) {
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleLookupInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, checkPermission, publishResponse)
HandleLookupInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, checkPermission, publishResponse)

assert.Nil(t, publishedResponse.Result)
assert.Equal(t, models.ERROR_RESTRICTED, publishedResponse.Error.Code)
Expand All @@ -72,7 +77,12 @@ func TestHandleLookupInvoiceEvent_WithPermission(t *testing.T) {
err = json.Unmarshal([]byte(nip47LookupInvoiceJson), nip47Request)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{}
app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

Expand All @@ -89,7 +99,7 @@ func TestHandleLookupInvoiceEvent_WithPermission(t *testing.T) {
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleLookupInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, checkPermission, publishResponse)
HandleLookupInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, checkPermission, publishResponse)

assert.Nil(t, publishedResponse.Error)
transaction := publishedResponse.Result.(*lookupInvoiceResponse)
Expand Down
2 changes: 1 addition & 1 deletion nip47/controllers/make_invoice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (controller *nip47Controller) HandleMakeInvoiceEvent(ctx context.Context, n
return
}

nip47Transaction := toNip47Transaction(transaction)
nip47Transaction := models.ToNip47Transaction(transaction)
responsePayload := &makeInvoiceResponse{
Transaction: *nip47Transaction,
}
Expand Down
18 changes: 14 additions & 4 deletions nip47/controllers/make_invoice_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ func TestHandleMakeInvoiceEvent_NoPermission(t *testing.T) {
err = json.Unmarshal([]byte(nip47MakeInvoiceJson), nip47Request)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{}
app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

Expand All @@ -58,7 +63,7 @@ func TestHandleMakeInvoiceEvent_NoPermission(t *testing.T) {
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleMakeInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, checkPermission, publishResponse)
HandleMakeInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, checkPermission, publishResponse)

assert.Nil(t, publishedResponse.Result)
assert.Equal(t, models.ERROR_RESTRICTED, publishedResponse.Error.Code)
Expand All @@ -74,7 +79,12 @@ func TestHandleMakeInvoiceEvent_WithPermission(t *testing.T) {
err = json.Unmarshal([]byte(nip47MakeInvoiceJson), nip47Request)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{}
app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

Expand All @@ -91,7 +101,7 @@ func TestHandleMakeInvoiceEvent_WithPermission(t *testing.T) {
permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleMakeInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, checkPermission, publishResponse)
HandleMakeInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, checkPermission, publishResponse)

assert.Nil(t, publishedResponse.Error)
assert.Equal(t, tests.MockTransaction.Invoice, publishedResponse.Result.(*makeInvoiceResponse).Invoice)
Expand Down
6 changes: 3 additions & 3 deletions nip47/controllers/pay_invoice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (controller *nip47Controller) pay(ctx context.Context, bolt11 string, payme
"bolt11": bolt11,
}).Info("Sending payment")

response, err := controller.lnClient.SendPaymentSync(ctx, bolt11)
response, err := controller.transactionsService.SendPaymentSync(ctx, bolt11, controller.lnClient, &app.ID, &requestEventId)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"request_event_id": requestEventId,
Expand All @@ -100,7 +100,7 @@ func (controller *nip47Controller) pay(ctx context.Context, bolt11 string, payme
}, tags)
return
}
payment.Preimage = &response.Preimage
payment.Preimage = response.Preimage
// TODO: save payment fee
controller.db.Save(&payment)

Expand All @@ -115,7 +115,7 @@ func (controller *nip47Controller) pay(ctx context.Context, bolt11 string, payme
publishResponse(&models.Response{
ResultType: nip47Request.Method,
Result: payResponse{
Preimage: response.Preimage,
Preimage: *response.Preimage,
FeesPaid: response.Fee,
},
}, tags)
Expand Down
4 changes: 2 additions & 2 deletions nip47/event_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,10 @@ func (svc *nip47Service) HandleEvent(ctx context.Context, sub *nostr.Subscriptio
HandleGetBalanceEvent(ctx, nip47Request, requestEvent.ID, checkPermission, publishResponse)
case models.MAKE_INVOICE_METHOD:
controller.
HandleMakeInvoiceEvent(ctx, nip47Request, requestEvent.ID, checkPermission, publishResponse)
HandleMakeInvoiceEvent(ctx, nip47Request, requestEvent.ID, app.ID, checkPermission, publishResponse)
case models.LOOKUP_INVOICE_METHOD:
controller.
HandleLookupInvoiceEvent(ctx, nip47Request, requestEvent.ID, checkPermission, publishResponse)
HandleLookupInvoiceEvent(ctx, nip47Request, requestEvent.ID, app.ID, checkPermission, publishResponse)
case models.LIST_TRANSACTIONS_METHOD:
controller.
HandleListTransactionsEvent(ctx, nip47Request, requestEvent.ID, checkPermission, publishResponse)
Expand Down
Loading

0 comments on commit d8228ae

Please sign in to comment.