diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js
index 81173911..7780b28f 100644
--- a/api/resolvers/wallet.js
+++ b/api/resolvers/wallet.js
@@ -7,7 +7,7 @@ import { SELECT } from './item'
import { lnAddrOptions } from '@/lib/lnurl'
import { msatsToSats, msatsToSatsDecimal, ensureB64 } from '@/lib/format'
import { CLNAutowithdrawSchema, LNDAutowithdrawSchema, amountSchema, lnAddrAutowithdrawSchema, lnAddrSchema, ssValidate, withdrawlSchema } from '@/lib/validate'
-import { ANON_BALANCE_LIMIT_MSATS, ANON_INV_PENDING_LIMIT, ANON_USER_ID, BALANCE_LIMIT_MSATS, INVOICE_RETENTION_DAYS, INV_PENDING_LIMIT, USER_IDS_BALANCE_NO_LIMIT } from '@/lib/constants'
+import { ANON_BALANCE_LIMIT_MSATS, ANON_INV_PENDING_LIMIT, ANON_USER_ID, BALANCE_LIMIT_MSATS, INVOICE_RETENTION_DAYS, INV_PENDING_LIMIT, USER_IDS_BALANCE_NO_LIMIT, Wallet } from '@/lib/constants'
import { datePivot } from '@/lib/time'
import assertGofacYourself from './ofac'
import assertApiKeyNotPermitted from './apiKey'
@@ -434,11 +434,11 @@ export default {
data.macaroon = ensureB64(data.macaroon)
data.cert = ensureB64(data.cert)
- const walletType = 'LND'
+ const wallet = Wallet.LND
return await upsertWallet(
{
schema: LNDAutowithdrawSchema,
- walletType,
+ wallet,
testConnect: async ({ cert, macaroon, socket }) => {
try {
const { lnd } = await authenticatedLndGrpc({
@@ -453,12 +453,12 @@ export default {
expires_at: new Date()
})
// we wrap both calls in one try/catch since connection attempts happen on RPC calls
- await addWalletLog({ wallet: walletType, level: 'SUCCESS', message: 'connected to LND' }, { me, models })
+ await addWalletLog({ wallet, level: 'SUCCESS', message: 'connected to LND' }, { me, models })
return inv
} catch (err) {
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
const details = err[2]?.err?.details || err.message || err.toString?.()
- await addWalletLog({ wallet: walletType, level: 'ERROR', message: `could not connect to LND: ${details}` }, { me, models })
+ await addWalletLog({ wallet, level: 'ERROR', message: `could not connect to LND: ${details}` }, { me, models })
throw err
}
}
@@ -468,11 +468,11 @@ export default {
upsertWalletCLN: async (parent, { settings, ...data }, { me, models }) => {
data.cert = ensureB64(data.cert)
- const walletType = 'CLN'
+ const wallet = Wallet.CLN
return await upsertWallet(
{
schema: CLNAutowithdrawSchema,
- walletType,
+ wallet,
testConnect: async ({ socket, rune, cert }) => {
try {
const inv = await createInvoiceCLN({
@@ -483,11 +483,11 @@ export default {
msats: 'any',
expiry: 0
})
- await addWalletLog({ wallet: walletType, level: 'SUCCESS', message: 'connected to CLN' }, { me, models })
+ await addWalletLog({ wallet, level: 'SUCCESS', message: 'connected to CLN' }, { me, models })
return inv
} catch (err) {
const details = err.details || err.message || err.toString?.()
- await addWalletLog({ wallet: walletType, level: 'ERROR', message: `could not connect to CLN: ${details}` }, { me, models })
+ await addWalletLog({ wallet, level: 'ERROR', message: `could not connect to CLN: ${details}` }, { me, models })
throw err
}
}
@@ -495,14 +495,14 @@ export default {
{ settings, data }, { me, models })
},
upsertWalletLNAddr: async (parent, { settings, ...data }, { me, models }) => {
- const walletType = 'LIGHTNING_ADDRESS'
+ const wallet = Wallet.LnAddr
return await upsertWallet(
{
schema: lnAddrAutowithdrawSchema,
- walletType,
+ wallet,
testConnect: async ({ address }) => {
const options = await lnAddrOptions(address)
- await addWalletLog({ wallet: walletType, level: 'SUCCESS', message: 'fetched payment details' }, { me, models })
+ await addWalletLog({ wallet, level: 'SUCCESS', message: 'fetched payment details' }, { me, models })
return options
}
},
@@ -523,6 +523,15 @@ export default {
models.walletLog.create({ data: { userId: me.id, wallet: wallet.type, level: 'SUCCESS', message: 'wallet deleted' } })
])
+ return true
+ },
+ deleteWalletLogs: async (parent, { wallet }, { me, models }) => {
+ if (!me) {
+ throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
+ }
+
+ await models.walletLog.deleteMany({ where: { userId: me.id, wallet } })
+
return true
}
},
@@ -557,14 +566,14 @@ export default {
export const addWalletLog = async ({ wallet, level, message }, { me, models }) => {
try {
- await models.walletLog.create({ data: { userId: me.id, wallet, level, message } })
+ await models.walletLog.create({ data: { userId: me.id, wallet: wallet.type, level, message } })
} catch (err) {
console.error('error creating wallet log:', err)
}
}
async function upsertWallet (
- { schema, walletType, testConnect }, { settings, data }, { me, models }) {
+ { schema, wallet, testConnect }, { settings, data }, { me, models }) {
if (!me) {
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
}
@@ -577,7 +586,7 @@ async function upsertWallet (
await testConnect(data)
} catch (err) {
console.error(err)
- await addWalletLog({ wallet: walletType, level: 'ERROR', message: 'failed to attach wallet' }, { me, models })
+ await addWalletLog({ wallet, level: 'ERROR', message: 'failed to attach wallet' }, { me, models })
throw new GraphQLError('failed to connect to wallet', { extensions: { code: 'BAD_INPUT' } })
}
}
@@ -607,16 +616,13 @@ async function upsertWallet (
}))
}
- const walletName = walletType === 'LND'
- ? 'walletLND'
- : walletType === 'CLN' ? 'walletCLN' : 'walletLightningAddress'
if (id) {
txs.push(
models.wallet.update({
where: { id: Number(id), userId: me.id },
data: {
priority: priority ? 1 : 0,
- [walletName]: {
+ [wallet.field]: {
update: {
where: { walletId: Number(id) },
data: walletData
@@ -624,7 +630,7 @@ async function upsertWallet (
}
}
}),
- models.walletLog.create({ data: { userId: me.id, wallet: walletType, level: 'SUCCESS', message: 'wallet updated' } })
+ models.walletLog.create({ data: { userId: me.id, wallet: wallet.type, level: 'SUCCESS', message: 'wallet updated' } })
)
} else {
txs.push(
@@ -632,13 +638,13 @@ async function upsertWallet (
data: {
priority: Number(priority),
userId: me.id,
- type: walletType,
- [walletName]: {
+ type: wallet.type,
+ [wallet.field]: {
create: walletData
}
}
}),
- models.walletLog.create({ data: { userId: me.id, wallet: walletType, level: 'SUCCESS', message: 'wallet created' } })
+ models.walletLog.create({ data: { userId: me.id, wallet: wallet.type, level: 'SUCCESS', message: 'wallet created' } })
)
}
@@ -751,7 +757,7 @@ export async function sendToLnAddr (parent, { addr, amount, maxFee, comment, ...
if (autoWithdraw && decoded.destination === ourPubkey && process.env.NODE_ENV === 'production') {
// unset lnaddr so we don't trigger another withdrawal with same destination
await models.wallet.deleteMany({
- where: { userId: me.id, type: 'LIGHTNING_ADDRESS' }
+ where: { userId: me.id, type: Wallet.LnAddr.type }
})
throw new Error('automated withdrawals to other stackers are not allowed')
}
diff --git a/api/typeDefs/wallet.js b/api/typeDefs/wallet.js
index ce05cde5..437f9804 100644
--- a/api/typeDefs/wallet.js
+++ b/api/typeDefs/wallet.js
@@ -23,6 +23,7 @@ export default gql`
upsertWalletCLN(id: ID, socket: String!, rune: String!, cert: String, settings: AutowithdrawSettings!): Boolean
upsertWalletLNAddr(id: ID, address: String!, settings: AutowithdrawSettings!): Boolean
removeWallet(id: ID!): Boolean
+ deleteWalletLogs(wallet: String): Boolean
}
type Wallet {
diff --git a/components/logger.js b/components/logger.js
index da94fde9..10f03c0a 100644
--- a/components/logger.js
+++ b/components/logger.js
@@ -1,8 +1,9 @@
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
import { useMe } from './me'
import fancyNames from '@/lib/fancy-names.json'
-import { useQuery } from '@apollo/client'
+import { gql, useMutation, useQuery } from '@apollo/client'
import { WALLET_LOGS } from '@/fragments/wallet'
+import { getWalletBy } from '@/lib/constants'
const generateFancyName = () => {
// 100 adjectives * 100 nouns * 10000 = 100M possible names
@@ -157,21 +158,6 @@ const initIndexedDB = async (storeName) => {
})
}
-const renameWallet = (wallet) => {
- switch (wallet) {
- case 'walletLightningAddress':
- case 'LIGHTNING_ADDRESS':
- return 'lnAddr'
- case 'walletLND':
- case 'LND':
- return 'lnd'
- case 'walletCLN':
- case 'CLN':
- return 'cln'
- }
- return wallet
-}
-
const WalletLoggerProvider = ({ children }) => {
const [logs, setLogs] = useState([])
const idbStoreName = 'wallet_logs'
@@ -187,12 +173,33 @@ const WalletLoggerProvider = ({ children }) => {
const existingIds = prevLogs.map(({ id }) => id)
const logs = walletLogs
.filter(({ id }) => !existingIds.includes(id))
- .map(({ createdAt, wallet, ...log }) => ({ ts: +new Date(createdAt), wallet: renameWallet(wallet), ...log }))
+ .map(({ createdAt, wallet: walletType, ...log }) => {
+ return {
+ ts: +new Date(createdAt),
+ wallet: getWalletBy('type', walletType).logTag,
+ ...log
+ }
+ })
return [...prevLogs, ...logs].sort((a, b) => a.ts - b.ts)
})
}
})
+ const [deleteServerWalletLogs] = useMutation(
+ gql`
+ mutation deleteWalletLogs($wallet: String) {
+ deleteWalletLogs(wallet: $wallet)
+ }
+ `,
+ {
+ onCompleted: (_, { variables: { wallet: walletType } }) => {
+ setLogs((logs) => {
+ return logs.filter(l => walletType ? l.wallet !== getWalletBy('type', walletType).logTag : false)
+ })
+ }
+ }
+ )
+
const saveLog = useCallback((log) => {
if (!idb.current) {
// IDB may not be ready yet
@@ -234,14 +241,36 @@ const WalletLoggerProvider = ({ children }) => {
}, [])
const appendLog = useCallback((wallet, level, message) => {
- const log = { wallet, level, message, ts: +new Date() }
+ const log = { wallet: wallet.logTag, level, message, ts: +new Date() }
saveLog(log)
setLogs((prevLogs) => [...prevLogs, log])
}, [saveLog])
+ const deleteLogs = useCallback(async (wallet) => {
+ if (!wallet || wallet.server) {
+ await deleteServerWalletLogs({ variables: { wallet: wallet?.type } })
+ }
+ if (!wallet || !wallet.server) {
+ const tx = idb.current.transaction(idbStoreName, 'readwrite')
+ const objectStore = tx.objectStore(idbStoreName)
+ const idx = objectStore.index('wallet_ts')
+ const request = wallet ? idx.openCursor(window.IDBKeyRange.bound([wallet.logTag, -Infinity], [wallet.logTag, Infinity])) : idx.openCursor()
+ request.onsuccess = function (event) {
+ const cursor = event.target.result
+ if (cursor) {
+ cursor.delete()
+ cursor.continue()
+ } else {
+ // finished
+ setLogs((logs) => logs.filter(l => wallet ? l.wallet !== wallet.logTag : false))
+ }
+ }
+ }
+ }, [setLogs])
+
return (
-
+
{children}
@@ -249,14 +278,14 @@ const WalletLoggerProvider = ({ children }) => {
}
export function useWalletLogger (wallet) {
- const appendLog = useContext(WalletLoggerContext)
+ const { appendLog, deleteLogs: innerDeleteLogs } = useContext(WalletLoggerContext)
const log = useCallback(level => message => {
// TODO:
// also send this to us if diagnostics was enabled,
// very similar to how the service worker logger works.
appendLog(wallet, level, message)
- console[level !== 'error' ? 'info' : 'error'](`[${wallet}]`, message)
+ console[level !== 'error' ? 'info' : 'error'](`[${wallet.logTag}]`, message)
}, [appendLog, wallet])
const logger = useMemo(() => ({
@@ -265,10 +294,12 @@ export function useWalletLogger (wallet) {
error: (...message) => log('error')(message.join(' '))
}), [log, wallet])
- return logger
+ const deleteLogs = useCallback((w) => innerDeleteLogs(w || wallet), [innerDeleteLogs, wallet])
+
+ return { logger, deleteLogs }
}
export function useWalletLogs (wallet) {
const logs = useContext(WalletLogsContext)
- return logs.filter(l => !wallet || l.wallet === wallet)
+ return logs.filter(l => !wallet || l.wallet === wallet.logTag)
}
diff --git a/components/nav/common.js b/components/nav/common.js
index e5114a65..49881765 100644
--- a/components/nav/common.js
+++ b/components/nav/common.js
@@ -6,7 +6,7 @@ import BackArrow from '../../svgs/arrow-left-line.svg'
import { useCallback, useEffect, useState } from 'react'
import Price from '../price'
import SubSelect from '../sub-select'
-import { ANON_USER_ID, BALANCE_LIMIT_MSATS } from '../../lib/constants'
+import { ANON_USER_ID, BALANCE_LIMIT_MSATS, Wallet } from '../../lib/constants'
import Head from 'next/head'
import NoteIcon from '../../svgs/notification-4-fill.svg'
import { useMe } from '../me'
@@ -22,6 +22,7 @@ import SearchIcon from '../../svgs/search-line.svg'
import classNames from 'classnames'
import SnIcon from '@/svgs/sn.svg'
import { useHasNewNotes } from '../use-has-new-notes'
+import { useWalletLogger } from '../logger'
export function Brand ({ className }) {
return (
@@ -162,6 +163,7 @@ export function NavWalletSummary ({ className }) {
export function MeDropdown ({ me, dropNavKey }) {
if (!me) return null
const { registration: swRegistration, togglePushSubscription } = useServiceWorker()
+ const { deleteLogs } = useWalletLogger()
return (
@@ -202,16 +204,16 @@ export function MeDropdown ({ me, dropNavKey }) {
{
- try {
- // order is important because we need to be logged in to delete push subscription on server
- const pushSubscription = await swRegistration?.pushManager.getSubscription()
- if (pushSubscription) {
- await togglePushSubscription()
- }
- } catch (err) {
+ // order is important because we need to be logged in to delete push subscription on server
+ const pushSubscription = await swRegistration?.pushManager.getSubscription()
+ if (pushSubscription) {
// don't prevent signout because of an unsubscription error
- console.error(err)
+ await togglePushSubscription().catch(console.error)
}
+ // delete client wallet logs to prevent leak of private data if a shared device was used
+ await deleteLogs(Wallet.NWC).catch(console.error)
+ await deleteLogs(Wallet.LNbits).catch(console.error)
+ await deleteLogs(Wallet.LNC).catch(console.error)
await signOut({ callbackUrl: '/' })
}}
>logout
diff --git a/components/wallet-logs.js b/components/wallet-logs.js
index f8f95bf5..c5cb678d 100644
--- a/components/wallet-logs.js
+++ b/components/wallet-logs.js
@@ -1,10 +1,13 @@
import { useRouter } from 'next/router'
import LogMessage from './log-message'
-import { useWalletLogs } from './logger'
+import { useWalletLogger, useWalletLogs } from './logger'
import { useEffect, useRef, useState } from 'react'
import { Checkbox, Form } from './form'
import { useField } from 'formik'
import styles from '@/styles/log.module.css'
+import { Button } from 'react-bootstrap'
+import { useToast } from './toast'
+import { useShowModal } from './modal'
const FollowCheckbox = ({ value, ...props }) => {
const [,, helpers] = useField(props.name)
@@ -26,6 +29,7 @@ export default function WalletLogs ({ wallet, embedded }) {
const [follow, setFollow] = useState(defaultFollow ?? true)
const tableRef = useRef()
const scrollY = useRef()
+ const showModal = useShowModal()
useEffect(() => {
if (follow) {
@@ -57,12 +61,21 @@ export default function WalletLogs ({ wallet, embedded }) {
return (
<>
-
+
+
+ {
+ showModal(onClose => )
+ }}
+ >clear
+
+
------ start of logs ------
{logs.length === 0 &&
empty
}
@@ -75,3 +88,34 @@ export default function WalletLogs ({ wallet, embedded }) {
>
)
}
+
+function DeleteWalletLogsObstacle ({ wallet, onClose }) {
+ const toaster = useToast()
+ const { deleteLogs } = useWalletLogger(wallet)
+
+ const prompt = `Do you really want to delete all ${wallet ? '' : 'wallet'} logs ${wallet ? 'of this wallet' : ''}?`
+ return (
+
+ {prompt}
+
+ cancel
+
+
+
+ )
+}
diff --git a/components/webln/lnbits.js b/components/webln/lnbits.js
index e433e8e4..c2f3009f 100644
--- a/components/webln/lnbits.js
+++ b/components/webln/lnbits.js
@@ -2,6 +2,7 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea
import { useWalletLogger } from '../logger'
import { Status } from '.'
import { bolt11Tags } from '@/lib/bolt11'
+import { Wallet } from '@/lib/constants'
// Reference: https://github.com/getAlby/bitcoin-connect/blob/v3.2.0-alpha/src/connectors/LnbitsConnector.ts
@@ -67,7 +68,7 @@ export function LNbitsProvider ({ children }) {
const [url, setUrl] = useState('')
const [adminKey, setAdminKey] = useState('')
const [status, setStatus] = useState()
- const logger = useWalletLogger('lnbits')
+ const { logger } = useWalletLogger(Wallet.LNbits)
const name = 'LNbits'
const storageKey = 'webln:provider:lnbits'
diff --git a/components/webln/lnc.js b/components/webln/lnc.js
index 7b056f42..7f942197 100644
--- a/components/webln/lnc.js
+++ b/components/webln/lnc.js
@@ -7,6 +7,7 @@ import useModal from '../modal'
import { Form, PasswordInput, SubmitButton } from '../form'
import CancelButton from '../cancel-button'
import { Mutex } from 'async-mutex'
+import { Wallet } from '@/lib/constants'
const LNCContext = createContext()
const mutex = new Mutex()
@@ -32,8 +33,7 @@ function validateNarrowPerms (lnc) {
}
export function LNCProvider ({ children }) {
- const name = 'lnc'
- const logger = useWalletLogger(name)
+ const logger = useWalletLogger(Wallet.LNC)
const [config, setConfig] = useState({})
const [lnc, setLNC] = useState()
const [status, setStatus] = useState()
@@ -188,7 +188,7 @@ export function LNCProvider ({ children }) {
}, [setStatus, setConfig, logger])
return (
-
+
{children}
{modal}
diff --git a/components/webln/nwc.js b/components/webln/nwc.js
index cb403775..a264d665 100644
--- a/components/webln/nwc.js
+++ b/components/webln/nwc.js
@@ -6,6 +6,7 @@ import { parseNwcUrl } from '@/lib/url'
import { useWalletLogger } from '../logger'
import { Status } from '.'
import { bolt11Tags } from '@/lib/bolt11'
+import { Wallet } from '@/lib/constants'
const NWCContext = createContext()
@@ -15,7 +16,7 @@ export function NWCProvider ({ children }) {
const [relayUrl, setRelayUrl] = useState()
const [secret, setSecret] = useState()
const [status, setStatus] = useState()
- const logger = useWalletLogger('nwc')
+ const { logger } = useWalletLogger(Wallet.NWC)
const name = 'NWC'
const storageKey = 'webln:provider:nwc'
diff --git a/lib/constants.js b/lib/constants.js
index f149b9b8..5ac6652a 100644
--- a/lib/constants.js
+++ b/lib/constants.js
@@ -131,3 +131,20 @@ export const FAST_POLL_INTERVAL = Number(process.env.NEXT_PUBLIC_FAST_POLL_INTER
export const NORMAL_POLL_INTERVAL = Number(process.env.NEXT_PUBLIC_NORMAL_POLL_INTERVAL)
export const LONG_POLL_INTERVAL = Number(process.env.NEXT_PUBLIC_LONG_POLL_INTERVAL)
export const EXTRA_LONG_POLL_INTERVAL = Number(process.env.NEXT_PUBLIC_EXTRA_LONG_POLL_INTERVAL)
+
+// attached wallets
+export const Wallet = {
+ LND: { logTag: 'lnd', server: true, type: 'LND', field: 'walletLND' },
+ CLN: { logTag: 'cln', server: true, type: 'CLN', field: 'walletCLN' },
+ LnAddr: { logTag: 'lnAddr', server: true, type: 'LIGHTNING_ADDRESS', field: 'walletLightningAddress' },
+ NWC: { logTag: 'nwc', server: false },
+ LNbits: { logTag: 'lnbits', server: false },
+ LNC: { logTag: 'lnc', server: false }
+}
+
+export const getWalletBy = (key, value) => {
+ for (const w of Object.values(Wallet)) {
+ if (w[key] === value) return w
+ }
+ throw new Error(`wallet not found: ${key}=${value}`)
+}
diff --git a/pages/settings/wallets/cln.js b/pages/settings/wallets/cln.js
index ce517e1f..ea518320 100644
--- a/pages/settings/wallets/cln.js
+++ b/pages/settings/wallets/cln.js
@@ -12,8 +12,9 @@ import { REMOVE_WALLET, UPSERT_WALLET_CLN, WALLET_BY_TYPE } from '@/fragments/wa
import WalletLogs from '@/components/wallet-logs'
import Info from '@/components/info'
import Text from '@/components/text'
+import { Wallet } from '@/lib/constants'
-const variables = { type: 'CLN' }
+const variables = { type: Wallet.CLN.type }
export const getServerSideProps = getGetServerSideProps({ query: WALLET_BY_TYPE, variables, authRequired: true })
export default function CLN ({ ssrData }) {
@@ -118,7 +119,7 @@ export default function CLN ({ ssrData }) {
/>
-
+
)
diff --git a/pages/settings/wallets/index.js b/pages/settings/wallets/index.js
index 25641419..8adbcec5 100644
--- a/pages/settings/wallets/index.js
+++ b/pages/settings/wallets/index.js
@@ -12,6 +12,7 @@ import { useQuery } from '@apollo/client'
import PageLoading from '@/components/page-loading'
import { LNCCard } from './lnc'
import Link from 'next/link'
+import { Wallet as W } from '@/lib/constants'
export const getServerSideProps = getGetServerSideProps({ query: WALLETS, authRequired: true })
@@ -20,9 +21,9 @@ export default function Wallet ({ ssrData }) {
if (!data && !ssrData) return
const { wallets } = data || ssrData
- const lnd = wallets.find(w => w.type === 'LND')
- const lnaddr = wallets.find(w => w.type === 'LIGHTNING_ADDRESS')
- const cln = wallets.find(w => w.type === 'CLN')
+ const lnd = wallets.find(w => w.type === W.LND.type)
+ const lnaddr = wallets.find(w => w.type === W.LnAddr.type)
+ const cln = wallets.find(w => w.type === W.CLN.type)
return (
diff --git a/pages/settings/wallets/lightning-address.js b/pages/settings/wallets/lightning-address.js
index 792cbff0..8dbe3c9c 100644
--- a/pages/settings/wallets/lightning-address.js
+++ b/pages/settings/wallets/lightning-address.js
@@ -10,8 +10,9 @@ import { useRouter } from 'next/router'
import { AutowithdrawSettings, autowithdrawInitial } from '@/components/autowithdraw-shared'
import { REMOVE_WALLET, UPSERT_WALLET_LNADDR, WALLET_BY_TYPE } from '@/fragments/wallet'
import WalletLogs from '@/components/wallet-logs'
+import { Wallet } from '@/lib/constants'
-const variables = { type: 'LIGHTNING_ADDRESS' }
+const variables = { type: Wallet.LnAddr.type }
export const getServerSideProps = getGetServerSideProps({ query: WALLET_BY_TYPE, variables, authRequired: true })
export default function LightningAddress ({ ssrData }) {
@@ -87,7 +88,7 @@ export default function LightningAddress ({ ssrData }) {
/>
-
+
)
diff --git a/pages/settings/wallets/lnbits.js b/pages/settings/wallets/lnbits.js
index dbdbe46d..b1905c0b 100644
--- a/pages/settings/wallets/lnbits.js
+++ b/pages/settings/wallets/lnbits.js
@@ -9,6 +9,7 @@ import { useLNbits } from '@/components/webln/lnbits'
import { WalletSecurityBanner } from '@/components/banners'
import { useWebLNConfigurator } from '@/components/webln'
import WalletLogs from '@/components/wallet-logs'
+import { Wallet } from '@/lib/constants'
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
@@ -79,7 +80,7 @@ export default function LNbits () {
/>
-
+
)
diff --git a/pages/settings/wallets/lnc.js b/pages/settings/wallets/lnc.js
index 508deb20..6f97c191 100644
--- a/pages/settings/wallets/lnc.js
+++ b/pages/settings/wallets/lnc.js
@@ -12,6 +12,7 @@ import { XXX_DEFAULT_PASSWORD, useLNC } from '@/components/webln/lnc'
import { lncSchema } from '@/lib/validate'
import { useRouter } from 'next/router'
import { useEffect, useRef } from 'react'
+import { Wallet } from '@/lib/constants'
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
@@ -102,7 +103,7 @@ export default function LNC () {
/>
-
+
)
diff --git a/pages/settings/wallets/lnd.js b/pages/settings/wallets/lnd.js
index cfa4efd5..1d5d0858 100644
--- a/pages/settings/wallets/lnd.js
+++ b/pages/settings/wallets/lnd.js
@@ -12,8 +12,9 @@ import { REMOVE_WALLET, UPSERT_WALLET_LND, WALLET_BY_TYPE } from '@/fragments/wa
import Info from '@/components/info'
import Text from '@/components/text'
import WalletLogs from '@/components/wallet-logs'
+import { Wallet } from '@/lib/constants'
-const variables = { type: 'LND' }
+const variables = { type: Wallet.LND.type }
export const getServerSideProps = getGetServerSideProps({ query: WALLET_BY_TYPE, variables, authRequired: true })
export default function LND ({ ssrData }) {
@@ -118,7 +119,7 @@ export default function LND ({ ssrData }) {
/>
-
+
)
diff --git a/pages/settings/wallets/nwc.js b/pages/settings/wallets/nwc.js
index f5c12fe3..81800832 100644
--- a/pages/settings/wallets/nwc.js
+++ b/pages/settings/wallets/nwc.js
@@ -9,6 +9,7 @@ import { useNWC } from '@/components/webln/nwc'
import { WalletSecurityBanner } from '@/components/banners'
import { useWebLNConfigurator } from '@/components/webln'
import WalletLogs from '@/components/wallet-logs'
+import { Wallet } from '@/lib/constants'
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
@@ -72,7 +73,7 @@ export default function NWC () {
/>
-
+
)
diff --git a/prisma/migrations/20240424132802_wallet_log_use_enum/migration.sql b/prisma/migrations/20240424132802_wallet_log_use_enum/migration.sql
new file mode 100644
index 00000000..204c49fb
--- /dev/null
+++ b/prisma/migrations/20240424132802_wallet_log_use_enum/migration.sql
@@ -0,0 +1,17 @@
+/*
+ Warnings:
+
+ - Changed the type of `wallet` on the `WalletLog` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
+
+*/
+
+UPDATE "WalletLog"
+SET wallet = CASE
+ WHEN wallet = 'walletLND' THEN 'LND'
+ WHEN wallet = 'walletCLN' THEN 'CLN'
+ WHEN wallet = 'walletLightningAddress' THEN 'LIGHTNING_ADDRESS'
+ ELSE wallet
+END;
+
+-- AlterTable
+ALTER TABLE "WalletLog" ALTER COLUMN "wallet" TYPE "WalletType" USING "wallet"::"WalletType";
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 7a3869b4..4c761cc5 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -166,7 +166,7 @@ model WalletLog {
createdAt DateTime @default(now()) @map("created_at")
userId Int
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- wallet String
+ wallet WalletType
level LogLevel
message String
diff --git a/worker/autowithdraw.js b/worker/autowithdraw.js
index c4c4fe54..5f35c96d 100644
--- a/worker/autowithdraw.js
+++ b/worker/autowithdraw.js
@@ -3,6 +3,7 @@ import { msatsToSats, satsToMsats } from '@/lib/format'
import { datePivot } from '@/lib/time'
import { createWithdrawal, sendToLnAddr, addWalletLog } from '@/api/resolvers/wallet'
import { createInvoice as createInvoiceCLN } from '@/lib/cln'
+import { Wallet } from '@/lib/constants'
export async function autoWithdraw ({ data: { id }, models, lnd }) {
const user = await models.user.findUnique({ where: { id } })
@@ -46,15 +47,15 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
for (const wallet of wallets) {
try {
- if (wallet.type === 'LND') {
+ if (wallet.type === Wallet.LND.type) {
await autowithdrawLND(
{ amount, maxFee },
{ models, me: user, lnd })
- } else if (wallet.type === 'CLN') {
+ } else if (wallet.type === Wallet.CLN.type) {
await autowithdrawCLN(
{ amount, maxFee },
{ models, me: user, lnd })
- } else if (wallet.type === 'LIGHTNING_ADDRESS') {
+ } else if (wallet.type === Wallet.LnAddr.type) {
await autowithdrawLNAddr(
{ amount, maxFee },
{ models, me: user, lnd })
@@ -66,7 +67,7 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
const details = error[2]?.err?.details || error.message || error.toString?.()
await addWalletLog({
- wallet: wallet.type,
+ wallet,
level: 'ERROR',
message: 'autowithdrawal failed: ' + details
}, { me: user, models })
@@ -86,7 +87,7 @@ async function autowithdrawLNAddr (
const wallet = await models.wallet.findFirst({
where: {
userId: me.id,
- type: 'LIGHTNING_ADDRESS'
+ type: Wallet.LnAddr.type
},
include: {
walletLightningAddress: true
@@ -109,7 +110,7 @@ async function autowithdrawLND ({ amount, maxFee }, { me, models, lnd }) {
const wallet = await models.wallet.findFirst({
where: {
userId: me.id,
- type: 'LND'
+ type: Wallet.LND.type
},
include: {
walletLND: true
@@ -145,7 +146,7 @@ async function autowithdrawCLN ({ amount, maxFee }, { me, models, lnd }) {
const wallet = await models.wallet.findFirst({
where: {
userId: me.id,
- type: 'CLN'
+ type: Wallet.CLN.type
},
include: {
walletCLN: true