From 0949059034383cd9aef74c0dc91c9802d4add144 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 6 Jan 2025 20:25:43 +0100 Subject: [PATCH] zbd send only attachment --- fragments/wallet.js | 3 - .../migration.sql | 24 -------- .../migration.sql | 2 + prisma/schema.prisma | 10 ---- wallets/server.js | 2 +- wallets/zebedee/client.js | 57 +++++++++--------- wallets/zebedee/index.js | 18 +----- wallets/zebedee/server.js | 58 ------------------- 8 files changed, 35 insertions(+), 139 deletions(-) delete mode 100644 prisma/migrations/20241219120508_zebedee_attachment/migration.sql create mode 100644 prisma/migrations/20250106164328_zebedee_sender/migration.sql delete mode 100644 wallets/zebedee/server.js diff --git a/fragments/wallet.js b/fragments/wallet.js index 10e8ac060..6f84f4afd 100644 --- a/fragments/wallet.js +++ b/fragments/wallet.js @@ -169,9 +169,6 @@ export const WALLET_FIELDS = gql` apiKeyRecv currencyRecv } - ... on WalletZebedee { - gamerTagId - } } } ` diff --git a/prisma/migrations/20241219120508_zebedee_attachment/migration.sql b/prisma/migrations/20241219120508_zebedee_attachment/migration.sql deleted file mode 100644 index 416595e3e..000000000 --- a/prisma/migrations/20241219120508_zebedee_attachment/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ --- AlterEnum -ALTER TYPE "WalletType" ADD VALUE 'ZEBEDEE'; - --- CreateTable -CREATE TABLE "WalletZebedee" ( - "id" SERIAL NOT NULL, - "walletId" INTEGER NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "gamerTagId" TEXT, - - CONSTRAINT "WalletZebedee_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "WalletZebedee_walletId_key" ON "WalletZebedee"("walletId"); - --- AddForeignKey -ALTER TABLE "WalletZebedee" ADD CONSTRAINT "WalletZebedee_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- Update wallet json -CREATE TRIGGER wallet_zebedee_as_jsonb -AFTER INSERT OR UPDATE ON "WalletZebedee" -FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb(); \ No newline at end of file diff --git a/prisma/migrations/20250106164328_zebedee_sender/migration.sql b/prisma/migrations/20250106164328_zebedee_sender/migration.sql new file mode 100644 index 000000000..973f61eac --- /dev/null +++ b/prisma/migrations/20250106164328_zebedee_sender/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "WalletType" ADD VALUE 'ZEBEDEE'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0db4ab4bc..af01507e4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -217,7 +217,6 @@ model Wallet { walletNWC WalletNWC? walletPhoenixd WalletPhoenixd? walletBlink WalletBlink? - walletZebedee WalletZebedee? vaultEntries VaultEntry[] @relation("VaultEntries") withdrawals Withdrawl[] @@ -327,15 +326,6 @@ model WalletPhoenixd { secondaryPassword String? } -model WalletZebedee { - id Int @id @default(autoincrement()) - walletId Int @unique - wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade) - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @default(now()) @updatedAt @map("updated_at") - gamerTagId String? -} - model Mute { muterId Int mutedId Int diff --git a/wallets/server.js b/wallets/server.js index 0a39bc96e..fd96aa0cb 100644 --- a/wallets/server.js +++ b/wallets/server.js @@ -6,11 +6,11 @@ import * as lnbits from '@/wallets/lnbits/server' import * as nwc from '@/wallets/nwc/server' import * as phoenixd from '@/wallets/phoenixd/server' import * as blink from '@/wallets/blink/server' -import * as zebedee from '@/wallets/zebedee/server' // we import only the metadata of client side wallets import * as lnc from '@/wallets/lnc' import * as webln from '@/wallets/webln' +import * as zebedee from '@/wallets/zebedee' import { walletLogger } from '@/api/resolvers/wallet' import walletDefs from '@/wallets/server' diff --git a/wallets/zebedee/client.js b/wallets/zebedee/client.js index 900a287c6..8019a554d 100644 --- a/wallets/zebedee/client.js +++ b/wallets/zebedee/client.js @@ -1,8 +1,5 @@ -import { API_URL, PREIMAGE_AWAIT_TIMEOUT_MS } from '@/wallets/zebedee' +import { API_URL } from '@/wallets/zebedee' import { assertContentTypeJson } from '@/lib/url' -import { callWithTimeout } from '@/lib/time' -import { fetchWithTimeout } from '@/lib/fetch' - export * from '@/wallets/zebedee' export async function testSendPayment ({ apiKey }, { signal }) { @@ -19,45 +16,53 @@ export async function sendPayment (bolt11, { apiKey }, { signal }) { } async function waitForPreimage (id, { apiKey }, { signal }) { - return await callWithTimeout(async () => { - let preimage - while (true) { - const res = await apiCall('payments/{id}', { body: { id }, apiKey, method: 'GET' }, { signal }) - preimage = res?.data?.preimage - if (preimage) break - await new Promise(resolve => setTimeout(resolve, 10)) - } - return preimage - }, PREIMAGE_AWAIT_TIMEOUT_MS) + while (!signal.aborted) { + const res = await apiCall('payments/{id}', { body: { id }, apiKey, method: 'GET' }, { signal }) + + // return preimage if it's available + const preimage = res?.data?.preimage + if (preimage) return preimage + + // wait a before checking again + await new Promise(resolve => setTimeout(resolve, 30)) + } + return null } export async function apiCall (api, { body, apiKey, method = 'POST' }, { signal }) { - const headers = { - apikey: apiKey, - 'Content-Type': 'application/json' - } + // if get request, put params into the url if (method === 'GET' && body) { for (const [k, v] of Object.entries(body)) { - api = api.replace('{' + k + '}', v) + api = api.replace(`{${k}}`, v) } } - const res = await fetchWithTimeout(API_URL + api, { + + const res = await fetch(API_URL + api, { method, - headers, + headers: { + apikey: apiKey, + 'Content-Type': 'application/json' + }, signal, body: method === 'POST' ? JSON.stringify(body) : undefined }) - // https://zbd.dev/api-reference/errors - if (res.status !== 200) { + + // Catch errors + // ref: https://zbd.dev/api-reference/errors + if (res.status < 200 || res.status > 299) { + // try to extract the error message from the response let error try { assertContentTypeJson(res) const json = await res.json() if (json?.message) error = json.message } catch (e) { - error = res.statusText || 'error ' + res.status + console.log('failed to parse error', e) } - throw new Error(error) + // throw the error, if we don't have one, we try to use the request status + throw new Error(error ?? (res.statusText || `error ${res.status}`)) } - return res.json() + + assertContentTypeJson(res) + return await res.json() } diff --git a/wallets/zebedee/index.js b/wallets/zebedee/index.js index a88af1225..15b497804 100644 --- a/wallets/zebedee/index.js +++ b/wallets/zebedee/index.js @@ -1,11 +1,7 @@ import { string } from '@/lib/yup' -export const PREIMAGE_AWAIT_TIMEOUT_MS = 1_200 -export const STATIC_CHARGE_URL = 'https://api.zebedee.io/v0/process-static-charges/' export const DASHBOARD_URL = 'https://dashboard.zebedee.io/' -export const GAMER_TAG_LNADDR_BASEURL = 'https://zbd.gg/.well-known/lnurlp/' export const API_URL = 'https://api.zebedee.io/v0/' -export const ZEBEDEE_LNDOMAIN = 'zbd.gg' export const name = 'zebedee' export const walletType = 'ZEBEDEE' @@ -19,18 +15,7 @@ export const fields = [ optional: 'for sending', help: `you can get an API key from [Zebedee Dashboard](${DASHBOARD_URL}) from \n\`Project->API->Live\``, clientOnly: true, - requiredWithout: 'gamerTagId', - validate: string() - }, - { - name: 'gamerTagId', - label: 'gamer tag or id', - type: 'text', - optional: 'for receiving', - help: `you can find your Gamertag in the [Zebedee Dashboard](${DASHBOARD_URL}) under \n\`Account->Gamertag\`\n section, or in the Zebedee app on the Wallet card.\nNote: You can also use your \`@${ZEBEDEE_LNDOMAIN}\` Lightning address here.`, - serverOnly: true, - requiredWithout: 'apiKey', - validate: string() + validate: string().min(8, 'invalid api key').max(64, 'api key is too long') } ] @@ -38,5 +23,4 @@ export const card = { title: 'Zebedee', subtitle: 'use [Zebedee](https://zebedee.io) for payments', image: { src: '/wallets/zbd.svg' } - } diff --git a/wallets/zebedee/server.js b/wallets/zebedee/server.js deleted file mode 100644 index 810b0576a..000000000 --- a/wallets/zebedee/server.js +++ /dev/null @@ -1,58 +0,0 @@ -import { GAMER_TAG_LNADDR_BASEURL, STATIC_CHARGE_URL, ZEBEDEE_LNDOMAIN } from '@/wallets/zebedee' -import { fetchWithTimeout } from '@/lib/fetch' -import { assertContentTypeJson } from '@/lib/url' - -export * from '@/wallets/zebedee' - -async function fetchJson (url, { signal }) { - let res = await fetchWithTimeout(url, { signal }) - assertContentTypeJson(res) - if (!res.ok) { - res.text().catch(() => {}) - throw new Error(res.statusText || 'error ' + res.status) - } - res = await res.json() - if (res.status?.toLowerCase() === 'error') { - throw new Error(res.reason) - } - return res -} - -function isGamerTag (value) { - if (value.endsWith('@' + ZEBEDEE_LNDOMAIN)) return true - return value.length > 0 && value.length < 30 -} - -export async function fetchGamerId (value, { signal }) { - if (isGamerTag(value)) { - const [gamerTag, domain] = value.split('@') - if (domain && domain !== ZEBEDEE_LNDOMAIN) throw new Error(`invalid gamer tag: not a @${ZEBEDEE_LNDOMAIN} lightning address`) - const url = GAMER_TAG_LNADDR_BASEURL + gamerTag - try { - const res = await fetchJson(url, { signal }) - const callback = res.callback - if (!callback) throw new Error('cannot fetch gamer id: ' + (res.statusText || 'error ' + res.status)) - const gamerId = callback.substring(callback.lastIndexOf('/') + 1) - return gamerId - } catch (e) { - throw new Error('cannot fetch gamer id: ' + e.message) - } - } - return value -} - -export async function testCreateInvoice (credentials, { signal }) { - credentials.gamerTagId = await fetchGamerId(credentials.gamerTagId, { signal }) - return await createInvoice({ msats: 1000, expiry: 1 }, credentials, { signal }) -} - -export async function createInvoice ({ msats, description, expiry }, { gamerTagId }, { signal }) { - try { - const url = STATIC_CHARGE_URL + gamerTagId + '?amount=' + msats + '&comment=' + description - const res = await fetchJson(url, { signal }) - if (!res.pr) throw new Error('cannot fetch invoice') - return res.pr - } catch (e) { - throw new Error(e.message) - } -}