diff --git a/api/paidAction/receive.js b/api/paidAction/receive.js index 4313ccb8..51945a85 100644 --- a/api/paidAction/receive.js +++ b/api/paidAction/receive.js @@ -1,5 +1,5 @@ import { PAID_ACTION_PAYMENT_METHODS } from '@/lib/constants' -import { toPositiveBigInt, numWithUnits, msatsToSats, satsToMsats } from '@/lib/format' +import { toPositiveBigInt, numWithUnits, msatsToSats } from '@/lib/format' import { notifyDeposit } from '@/lib/webPush' import { getInvoiceableWallets } from '@/wallets/server' @@ -23,10 +23,6 @@ export async function getInvoiceablePeer (_, { me, models, cost, paymentMethod } return null } - if (cost < satsToMsats(me.receiveCreditsBelowSats)) { - return null - } - return me.id } diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js index a8af96df..1bfb66f1 100644 --- a/api/resolvers/wallet.js +++ b/api/resolvers/wallet.js @@ -796,9 +796,9 @@ const logContextFromBolt11 = async (bolt11) => { } } -export const walletLogger = ({ wallet, models }) => { - // no-op logger if wallet is not provided - if (!wallet) { +export const walletLogger = ({ wallet, models, me }) => { + // no-op logger if no wallet or user provided + if (!wallet && !me) { return { ok: () => {}, info: () => {}, @@ -822,8 +822,9 @@ export const walletLogger = ({ wallet, models }) => { await models.walletLog.create({ data: { - userId: wallet.userId, - wallet: wallet.type, + userId: wallet?.userId ?? me.id, + // system logs have no wallet + wallet: wallet?.type, level, message, context, diff --git a/api/typeDefs/wallet.js b/api/typeDefs/wallet.js index efc49130..446462cb 100644 --- a/api/typeDefs/wallet.js +++ b/api/typeDefs/wallet.js @@ -194,7 +194,7 @@ const typeDefs = ` type WalletLogEntry { id: ID! createdAt: Date! - wallet: ID! + wallet: ID level: String! message: String! context: JSONObject diff --git a/components/wallet-logger.js b/components/wallet-logger.js index 473c8a22..decd7451 100644 --- a/components/wallet-logger.js +++ b/components/wallet-logger.js @@ -226,7 +226,7 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) { to = null } - const { data } = await getWalletLogs({ + const { data, error } = await getWalletLogs({ variables: { type: walletDef?.walletType, from, @@ -236,9 +236,14 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) { } }) + if (error) { + console.error('failed to query wallet logs:', error) + return { data: [], hasMore: false } + } + const newLogs = data.walletLogs.entries.map(({ createdAt, wallet: walletType, ...log }) => ({ ts: +new Date(createdAt), - wallet: walletTag(getWalletByType(walletType)), + wallet: walletType ? walletTag(getWalletByType(walletType)) : 'system', ...log, // required to resolve recv status context: { diff --git a/pages/api/lnurlp/[username]/pay.js b/pages/api/lnurlp/[username]/pay.js index 972b83c7..97b30c92 100644 --- a/pages/api/lnurlp/[username]/pay.js +++ b/pages/api/lnurlp/[username]/pay.js @@ -4,10 +4,11 @@ import { lnurlPayDescriptionHashForUser, lnurlPayMetadataString, lnurlPayDescrip import { schnorr } from '@noble/curves/secp256k1' import { createHash } from 'crypto' import { LNURLP_COMMENT_MAX_LENGTH, MAX_INVOICE_DESCRIPTION_LENGTH } from '@/lib/constants' -import { toPositiveBigInt } from '@/lib/format' +import { formatMsats, toPositiveBigInt } from '@/lib/format' import assertGofacYourself from '@/api/resolvers/ofac' import performPaidAction from '@/api/paidAction' import { validateSchema, lud18PayerDataSchema } from '@/lib/validate' +import { walletLogger } from '@/api/resolvers/wallet' export default async ({ query: { username, amount, nostr, comment, payerdata: payerData }, headers }, res) => { const user = await models.user.findUnique({ where: { name: username } }) @@ -15,6 +16,9 @@ export default async ({ query: { username, amount, nostr, comment, payerdata: pa return res.status(400).json({ status: 'ERROR', reason: `user @${username} does not exist` }) } + const logger = walletLogger({ models, me: user }) + logger.info(`${user.name}@stacker.news payment attempt`, { amount: formatMsats(amount), nostr, comment }) + try { await assertGofacYourself({ models, headers }) // if nostr, decode, validate sig, check tags, set description hash @@ -96,6 +100,7 @@ export default async ({ query: { username, amount, nostr, comment, payerdata: pa }) } catch (error) { console.log(error) + logger.error(`${user.name}@stacker.news payment failed: ${error.message}`) res.status(400).json({ status: 'ERROR', reason: 'could not generate invoice to customer\'s attached wallet' }) } } diff --git a/prisma/migrations/20250327192628_system_logger/migration.sql b/prisma/migrations/20250327192628_system_logger/migration.sql new file mode 100644 index 00000000..54f842b3 --- /dev/null +++ b/prisma/migrations/20250327192628_system_logger/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "WalletLog" ALTER COLUMN "wallet" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6346a0dc..caaf5b53 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -264,18 +264,18 @@ model VaultEntry { } model WalletLog { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map("created_at") + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") userId Int - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - wallet WalletType + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + wallet WalletType? level LogLevel message String invoiceId Int? - invoice Invoice? @relation(fields: [invoiceId], references: [id]) + invoice Invoice? @relation(fields: [invoiceId], references: [id]) withdrawalId Int? - withdrawal Withdrawl? @relation(fields: [withdrawalId], references: [id]) - context Json? @db.JsonB + withdrawal Withdrawl? @relation(fields: [withdrawalId], references: [id]) + context Json? @db.JsonB @@index([userId, createdAt]) }