Update wallet logging + other stuff
* add canPay and canSend to wallet definition * rename 'default payment method' to 'enabled' and add enable + disable method
This commit is contained in:
parent
a1b343ac89
commit
6ac8785c51
|
@ -228,8 +228,13 @@ export const WalletLoggerProvider = ({ children }) => {
|
||||||
const index = store.index('ts')
|
const index = store.index('ts')
|
||||||
const request = index.getAll()
|
const request = index.getAll()
|
||||||
request.onsuccess = () => {
|
request.onsuccess = () => {
|
||||||
const logs = request.result
|
let logs = request.result
|
||||||
setLogs((prevLogs) => {
|
setLogs((prevLogs) => {
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
// in dev mode, useEffect runs twice, so we filter out duplicates here
|
||||||
|
const existingIds = prevLogs.map(({ id }) => id)
|
||||||
|
logs = logs.filter(({ id }) => !existingIds.includes(id))
|
||||||
|
}
|
||||||
// sort oldest first to keep same order as logs are appended
|
// sort oldest first to keep same order as logs are appended
|
||||||
return [...prevLogs, ...logs].sort((a, b) => a.ts - b.ts)
|
return [...prevLogs, ...logs].sort((a, b) => a.ts - b.ts)
|
||||||
})
|
})
|
||||||
|
@ -254,16 +259,16 @@ export const WalletLoggerProvider = ({ children }) => {
|
||||||
}, [saveLog])
|
}, [saveLog])
|
||||||
|
|
||||||
const deleteLogs = useCallback(async (walletName) => {
|
const deleteLogs = useCallback(async (walletName) => {
|
||||||
const wallet = getWalletByName(walletName, me)
|
const wallet = walletName ? getWalletByName(walletName, me) : null
|
||||||
|
|
||||||
if (!walletName || wallet.server) {
|
if (!wallet || wallet.canReceive) {
|
||||||
await deleteServerWalletLogs({ variables: { wallet: wallet?.type } })
|
await deleteServerWalletLogs({ variables: { wallet: wallet?.type } })
|
||||||
}
|
}
|
||||||
if (!walletName || !wallet.server) {
|
if (!wallet || wallet.canPay) {
|
||||||
const tx = idb.current.transaction(idbStoreName, 'readwrite')
|
const tx = idb.current.transaction(idbStoreName, 'readwrite')
|
||||||
const objectStore = tx.objectStore(idbStoreName)
|
const objectStore = tx.objectStore(idbStoreName)
|
||||||
const idx = objectStore.index('wallet_ts')
|
const idx = objectStore.index('wallet_ts')
|
||||||
const request = walletName ? idx.openCursor(window.IDBKeyRange.bound([walletName, -Infinity], [walletName, Infinity])) : idx.openCursor()
|
const request = wallet ? idx.openCursor(window.IDBKeyRange.bound([wallet.name, -Infinity], [wallet.name, Infinity])) : idx.openCursor()
|
||||||
request.onsuccess = function (event) {
|
request.onsuccess = function (event) {
|
||||||
const cursor = event.target.result
|
const cursor = event.target.result
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
|
@ -271,7 +276,7 @@ export const WalletLoggerProvider = ({ children }) => {
|
||||||
cursor.continue()
|
cursor.continue()
|
||||||
} else {
|
} else {
|
||||||
// finished
|
// finished
|
||||||
setLogs((logs) => logs.filter(l => walletName ? l.wallet !== walletName : false))
|
setLogs((logs) => logs.filter(l => wallet ? l.wallet !== wallet.name : false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,29 +291,33 @@ export const WalletLoggerProvider = ({ children }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWalletLogger (walletName) {
|
export function useWalletLogger (wallet) {
|
||||||
const { appendLog, deleteLogs: innerDeleteLogs } = useContext(WalletLoggerContext)
|
const { appendLog, deleteLogs: innerDeleteLogs } = useContext(WalletLoggerContext)
|
||||||
|
|
||||||
const log = useCallback(level => message => {
|
const log = useCallback(level => message => {
|
||||||
|
if (!wallet) {
|
||||||
|
console.error('cannot log: no wallet set')
|
||||||
|
return
|
||||||
|
}
|
||||||
// TODO:
|
// TODO:
|
||||||
// also send this to us if diagnostics was enabled,
|
// also send this to us if diagnostics was enabled,
|
||||||
// very similar to how the service worker logger works.
|
// very similar to how the service worker logger works.
|
||||||
appendLog(walletName, level, message)
|
appendLog(wallet.name, level, message)
|
||||||
console[level !== 'error' ? 'info' : 'error'](`[${walletName}]`, message)
|
console[level !== 'error' ? 'info' : 'error'](`[${wallet.name}]`, message)
|
||||||
}, [appendLog, walletName])
|
}, [appendLog, wallet?.name])
|
||||||
|
|
||||||
const logger = useMemo(() => ({
|
const logger = useMemo(() => ({
|
||||||
ok: (...message) => log('ok')(message.join(' ')),
|
ok: (...message) => log('ok')(message.join(' ')),
|
||||||
info: (...message) => log('info')(message.join(' ')),
|
info: (...message) => log('info')(message.join(' ')),
|
||||||
error: (...message) => log('error')(message.join(' '))
|
error: (...message) => log('error')(message.join(' '))
|
||||||
}), [log, walletName])
|
}), [log, wallet?.name])
|
||||||
|
|
||||||
const deleteLogs = useCallback((w) => innerDeleteLogs(w || walletName), [innerDeleteLogs, walletName])
|
const deleteLogs = useCallback((w) => innerDeleteLogs(w?.name || wallet?.name), [innerDeleteLogs, wallet?.name])
|
||||||
|
|
||||||
return { logger, deleteLogs }
|
return { logger, deleteLogs }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWalletLogs (walletName) {
|
export function useWalletLogs (wallet) {
|
||||||
const logs = useContext(WalletLogsContext)
|
const logs = useContext(WalletLogsContext)
|
||||||
return logs.filter(l => !walletName || l.wallet === walletName)
|
return logs.filter(l => !wallet || l.wallet === wallet.name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useMe } from '@/components/me'
|
||||||
import useLocalState from '@/components/use-local-state'
|
import useLocalState from '@/components/use-local-state'
|
||||||
import { useWalletLogger } from '@/components/wallet-logger'
|
import { useWalletLogger } from '@/components/wallet-logger'
|
||||||
import { SSR } from '@/lib/constants'
|
import { SSR } from '@/lib/constants'
|
||||||
|
import { bolt11Tags } from '@/lib/bolt11'
|
||||||
|
|
||||||
// wallet definitions
|
// wallet definitions
|
||||||
export const WALLET_DEFS = [
|
export const WALLET_DEFS = [
|
||||||
|
@ -18,25 +19,45 @@ export const Status = {
|
||||||
|
|
||||||
export function useWallet (name) {
|
export function useWallet (name) {
|
||||||
const me = useMe()
|
const me = useMe()
|
||||||
const { logger } = useWalletLogger(name)
|
|
||||||
|
|
||||||
const wallet = getWalletByName(name, me)
|
const wallet = name ? getWalletByName(name, me) : getEnabledWallet(me)
|
||||||
|
const { logger } = useWalletLogger(wallet)
|
||||||
const storageKey = getStorageKey(wallet?.name, me)
|
const storageKey = getStorageKey(wallet?.name, me)
|
||||||
const [config, saveConfig, clearConfig] = useLocalState(storageKey)
|
const [config, saveConfig, clearConfig] = useLocalState(storageKey)
|
||||||
|
|
||||||
const isConfigured = !!config
|
|
||||||
|
|
||||||
const sendPayment = useCallback(async (bolt11) => {
|
const sendPayment = useCallback(async (bolt11) => {
|
||||||
return await wallet.sendPayment({ bolt11, config, logger })
|
const hash = bolt11Tags(bolt11).payment_hash
|
||||||
|
logger.info('sending payment:', `payment_hash=${hash}`)
|
||||||
|
try {
|
||||||
|
const { preimage } = await wallet.sendPayment({ bolt11, config, logger })
|
||||||
|
logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
|
||||||
|
} catch (err) {
|
||||||
|
const message = err.message || err.toString?.()
|
||||||
|
logger.error('payment failed:', `payment_hash=${hash}`, message)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
}, [wallet, config, logger])
|
}, [wallet, config, logger])
|
||||||
|
|
||||||
const validate = useCallback(async (values) => {
|
const validate = useCallback(async (values) => {
|
||||||
|
try {
|
||||||
|
// validate should log custom INFO and OK message
|
||||||
return await wallet.validate({ logger, ...values })
|
return await wallet.validate({ logger, ...values })
|
||||||
}, [logger])
|
} catch (err) {
|
||||||
|
const message = err.message || err.toString?.()
|
||||||
|
logger.error(message)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}, [wallet, logger])
|
||||||
|
|
||||||
const enable = useCallback(() => {
|
const enable = useCallback(() => {
|
||||||
enableWallet(name, me)
|
enableWallet(name, me)
|
||||||
}, [name, me])
|
logger.ok('wallet enabled')
|
||||||
|
}, [name, me, logger])
|
||||||
|
|
||||||
|
const disable = useCallback(() => {
|
||||||
|
disableWallet(name, me)
|
||||||
|
logger.ok('wallet disabled')
|
||||||
|
}, [name, me, logger])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...wallet,
|
...wallet,
|
||||||
|
@ -46,15 +67,22 @@ export function useWallet (name) {
|
||||||
saveConfig,
|
saveConfig,
|
||||||
clearConfig,
|
clearConfig,
|
||||||
enable,
|
enable,
|
||||||
isConfigured,
|
disable,
|
||||||
status: config?.enabled ? Status.Enabled : Status.Initialized
|
isConfigured: !!config,
|
||||||
|
status: config?.enabled ? Status.Enabled : Status.Initialized,
|
||||||
|
logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWalletByName (name, me) {
|
export function getWalletByName (name, me) {
|
||||||
return name
|
return WALLET_DEFS.find(def => def.name === name)
|
||||||
? WALLET_DEFS.find(def => def.name === name)
|
}
|
||||||
: WALLET_DEFS.find(def => {
|
|
||||||
|
export function getEnabledWallet (me) {
|
||||||
|
// TODO: handle multiple enabled wallets
|
||||||
|
return WALLET_DEFS
|
||||||
|
.filter(def => def.canPay)
|
||||||
|
.find(def => {
|
||||||
const key = getStorageKey(def.name, me)
|
const key = getStorageKey(def.name, me)
|
||||||
const config = SSR ? null : JSON.parse(window?.localStorage.getItem(key))
|
const config = SSR ? null : JSON.parse(window?.localStorage.getItem(key))
|
||||||
return config?.enabled
|
return config?.enabled
|
||||||
|
@ -70,6 +98,7 @@ function getStorageKey (name, me) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableWallet (name, me) {
|
function enableWallet (name, me) {
|
||||||
|
// mark all wallets as disabled except the one to enable
|
||||||
for (const walletDef of WALLET_DEFS) {
|
for (const walletDef of WALLET_DEFS) {
|
||||||
const toEnable = walletDef.name === name
|
const toEnable = walletDef.name === name
|
||||||
const key = getStorageKey(name, me)
|
const key = getStorageKey(name, me)
|
||||||
|
@ -80,3 +109,11 @@ function enableWallet (name, me) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function disableWallet (name, me) {
|
||||||
|
const key = getStorageKey(name, me)
|
||||||
|
const config = JSON.parse(window.localStorage.getItem(key))
|
||||||
|
if (!config) return
|
||||||
|
config.enabled = false
|
||||||
|
window.localStorage.setItem(key, JSON.stringify(config))
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { bolt11Tags } from '@/lib/bolt11'
|
|
||||||
|
|
||||||
export const name = 'lnbits'
|
export const name = 'lnbits'
|
||||||
|
|
||||||
|
export const canPay = true
|
||||||
|
export const canReceive = false
|
||||||
|
|
||||||
export const fields = [
|
export const fields = [
|
||||||
{
|
{
|
||||||
name: 'url',
|
name: 'url',
|
||||||
|
@ -25,7 +26,9 @@ export async function validate ({ logger, ...config }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getInfo ({ logger, ...config }) {
|
async function getInfo ({ logger, ...config }) {
|
||||||
|
logger.info('trying to fetch wallet')
|
||||||
const response = await getWallet(config.url, config.adminKey)
|
const response = await getWallet(config.url, config.adminKey)
|
||||||
|
logger.ok('wallet found')
|
||||||
return {
|
return {
|
||||||
node: {
|
node: {
|
||||||
alias: response.name,
|
alias: response.name,
|
||||||
|
@ -41,13 +44,9 @@ async function getInfo ({ logger, ...config }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendPayment ({ bolt11, config, logger }) {
|
export async function sendPayment ({ bolt11, config }) {
|
||||||
const { url, adminKey } = config
|
const { url, adminKey } = config
|
||||||
|
|
||||||
const hash = bolt11Tags(bolt11).payment_hash
|
|
||||||
logger.info('sending payment:', `payment_hash=${hash}`)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await postPayment(url, adminKey, bolt11)
|
const response = await postPayment(url, adminKey, bolt11)
|
||||||
|
|
||||||
const checkResponse = await getPayment(url, adminKey, response.payment_hash)
|
const checkResponse = await getPayment(url, adminKey, response.payment_hash)
|
||||||
|
@ -56,12 +55,7 @@ export async function sendPayment ({ bolt11, config, logger }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const preimage = checkResponse.preimage
|
const preimage = checkResponse.preimage
|
||||||
logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
|
|
||||||
return { preimage }
|
return { preimage }
|
||||||
} catch (err) {
|
|
||||||
logger.error('payment failed:', `payment_hash=${hash}`, err.message || err.toString?.())
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWallet (baseUrl, adminKey) {
|
async function getWallet (baseUrl, adminKey) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { WalletSecurityBanner } from '@/components/banners'
|
||||||
import { WalletLogs } from '@/components/wallet-logger'
|
import { WalletLogs } from '@/components/wallet-logger'
|
||||||
import { useToast } from '@/components/toast'
|
import { useToast } from '@/components/toast'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useWallet } from '@/components/wallet'
|
import { useWallet, Status } from '@/components/wallet'
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
||||||
|
|
||||||
|
@ -34,25 +34,27 @@ export default function WalletSettings () {
|
||||||
<Form
|
<Form
|
||||||
initial={initial}
|
initial={initial}
|
||||||
schema={lnbitsSchema}
|
schema={lnbitsSchema}
|
||||||
onSubmit={async ({ isDefault, ...values }) => {
|
onSubmit={async ({ enabled, ...values }) => {
|
||||||
try {
|
try {
|
||||||
await wallet.validate(values)
|
await wallet.validate(values)
|
||||||
wallet.saveConfig(values)
|
wallet.saveConfig(values)
|
||||||
wallet.enable()
|
if (enabled) wallet.enable()
|
||||||
|
else wallet.disable()
|
||||||
toaster.success('saved settings')
|
toaster.success('saved settings')
|
||||||
router.push('/settings/wallets')
|
router.push('/settings/wallets')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
toaster.danger('failed to attach: ' + err.message || err.toString?.())
|
const message = 'failed to attach: ' + err.message || err.toString?.()
|
||||||
|
toaster.danger(message)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WalletFields wallet={wallet} />
|
<WalletFields wallet={wallet} />
|
||||||
<ClientCheckbox
|
<ClientCheckbox
|
||||||
disabled={false}
|
disabled={false}
|
||||||
initialValue={false}
|
initialValue={wallet.status === Status.Enabled}
|
||||||
label='default payment method'
|
label='enabled'
|
||||||
name='isDefault'
|
name='enabled'
|
||||||
/>
|
/>
|
||||||
<WalletButtonBar
|
<WalletButtonBar
|
||||||
wallet={wallet} onDelete={async () => {
|
wallet={wallet} onDelete={async () => {
|
||||||
|
@ -62,7 +64,9 @@ export default function WalletSettings () {
|
||||||
router.push('/settings/wallets')
|
router.push('/settings/wallets')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
toaster.danger('failed to detach: ' + err.message || err.toString?.())
|
const message = 'failed to detach: ' + err.message || err.toString?.()
|
||||||
|
wallet.logger.error(message)
|
||||||
|
toaster.danger(message)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { CenterLayout } from '@/components/layout'
|
import { CenterLayout } from '@/components/layout'
|
||||||
import { getGetServerSideProps } from '@/api/ssrApollo'
|
import { getGetServerSideProps } from '@/api/ssrApollo'
|
||||||
import { WalletLogs } from '@/components/wallet-logs'
|
import { WalletLogs } from '@/components/wallet-logger'
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps({ query: null })
|
export const getServerSideProps = getGetServerSideProps({ query: null })
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue