server side config saves
This commit is contained in:
parent
4826ae5a7b
commit
ccdf346954
|
@ -55,8 +55,8 @@ export default {
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
txs.push(models.vaultEntry.update({
|
txs.push(models.vaultEntry.update({
|
||||||
where: { id: entry.id },
|
where: { userId_key: { userId: me.id, key: entry.key } },
|
||||||
data: { key: entry.key, value: entry.value }
|
data: { value: entry.value }
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
await models.prisma.$transaction(txs)
|
await models.prisma.$transaction(txs)
|
||||||
|
|
|
@ -26,12 +26,13 @@ import { getNodeSockets, getOurPubkey } from '../lnd'
|
||||||
|
|
||||||
function injectResolvers (resolvers) {
|
function injectResolvers (resolvers) {
|
||||||
console.group('injected GraphQL resolvers:')
|
console.group('injected GraphQL resolvers:')
|
||||||
for (const w of walletDefs) {
|
for (const walletDef of walletDefs) {
|
||||||
const resolverName = generateResolverName(w.walletField)
|
const resolverName = generateResolverName(walletDef.walletField)
|
||||||
console.log(resolverName)
|
console.log(resolverName)
|
||||||
resolvers.Mutation[resolverName] = async (parent, { settings, validateLightning, vaultEntries, ...data }, { me, models }) => {
|
resolvers.Mutation[resolverName] = async (parent, { settings, validateLightning, vaultEntries, ...data }, { me, models }) => {
|
||||||
// allow transformation of the data on validation (this is optional ... won't do anything if not implemented)
|
// allow transformation of the data on validation (this is optional ... won't do anything if not implemented)
|
||||||
const validData = await walletValidate(w, { ...data, ...settings, vaultEntries })
|
// TODO: our validation should be improved
|
||||||
|
const validData = await walletValidate(walletDef, { ...data, ...settings, vaultEntries })
|
||||||
if (validData) {
|
if (validData) {
|
||||||
Object.keys(validData).filter(key => key in data).forEach(key => { data[key] = validData[key] })
|
Object.keys(validData).filter(key => key in data).forEach(key => { data[key] = validData[key] })
|
||||||
Object.keys(validData).filter(key => key in settings).forEach(key => { settings[key] = validData[key] })
|
Object.keys(validData).filter(key => key in settings).forEach(key => { settings[key] = validData[key] })
|
||||||
|
@ -39,10 +40,12 @@ function injectResolvers (resolvers) {
|
||||||
|
|
||||||
return await upsertWallet({
|
return await upsertWallet({
|
||||||
wallet: {
|
wallet: {
|
||||||
field: w.walletField,
|
field: walletDef.walletField,
|
||||||
type: w.walletType
|
type: walletDef.walletType
|
||||||
},
|
},
|
||||||
testCreateInvoice: w.testCreateInvoice && validateLightning ? (data) => w.testCreateInvoice(data, { me, models }) : null
|
testCreateInvoice: walletDef.testCreateInvoice && validateLightning
|
||||||
|
? (data) => walletDef.testCreateInvoice(data, { me, models })
|
||||||
|
: null
|
||||||
}, {
|
}, {
|
||||||
settings,
|
settings,
|
||||||
data,
|
data,
|
||||||
|
@ -643,17 +646,13 @@ export const addWalletLog = async ({ wallet, level, message }, { models }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function upsertWallet (
|
async function upsertWallet (
|
||||||
{ wallet, testCreateInvoice },
|
{ wallet, testCreateInvoice }, { settings, data, vaultEntries = [] }, { me, models }) {
|
||||||
{ settings, data, priorityOnly, canSend, canReceive },
|
if (!me) {
|
||||||
{ me, models }
|
throw new GqlAuthenticationError()
|
||||||
) {
|
}
|
||||||
if (!me) throw new GqlAuthenticationError()
|
|
||||||
assertApiKeyNotPermitted({ me })
|
assertApiKeyNotPermitted({ me })
|
||||||
|
|
||||||
const { id, ...walletData } = data
|
if (testCreateInvoice) {
|
||||||
const { autoWithdrawThreshold, autoWithdrawMaxFeePercent, enabled, priority } = settings
|
|
||||||
|
|
||||||
if (testCreateInvoice && !priorityOnly && canReceive && enabled) {
|
|
||||||
try {
|
try {
|
||||||
await testCreateInvoice(data)
|
await testCreateInvoice(data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -666,103 +665,111 @@ async function upsertWallet (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.$transaction(async (tx) => {
|
const { id, enabled, priority, ...walletData } = data
|
||||||
if (canReceive) {
|
const {
|
||||||
await tx.user.update({
|
autoWithdrawThreshold,
|
||||||
where: { id: me.id },
|
autoWithdrawMaxFeePercent,
|
||||||
data: {
|
autoWithdrawMaxFeeTotal
|
||||||
autoWithdrawMaxFeePercent,
|
} = settings
|
||||||
autoWithdrawThreshold
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let updatedWallet
|
const txs = []
|
||||||
if (id) {
|
|
||||||
const existingWalletTypeRecord = canReceive
|
|
||||||
? await tx[wallet.field].findUnique({
|
|
||||||
where: { walletId: Number(id) }
|
|
||||||
})
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
updatedWallet = await tx.wallet.update({
|
if (id) {
|
||||||
|
const oldVaultEntries = await models.vaultEntry.findMany({ where: { userId: me.id, walletId: Number(id) } })
|
||||||
|
|
||||||
|
// createMany is the set difference of the new - old
|
||||||
|
// deleteMany is the set difference of the old - new
|
||||||
|
// updateMany is the intersection of the old and new
|
||||||
|
const difference = (a = [], b = [], key = 'key') => a.filter(x => !b.find(y => y[key] === x[key]))
|
||||||
|
const intersectionMerge = (a = [], b = [], key = 'key') => a.filter(x => b.find(y => y[key] === x[key]))
|
||||||
|
.map(x => ({ [key]: x[key], ...b.find(y => y[key] === x[key]) }))
|
||||||
|
|
||||||
|
txs.push(
|
||||||
|
models.wallet.update({
|
||||||
where: { id: Number(id), userId: me.id },
|
where: { id: Number(id), userId: me.id },
|
||||||
data: {
|
data: {
|
||||||
enabled,
|
enabled,
|
||||||
priority,
|
priority,
|
||||||
canSend,
|
[wallet.field]: {
|
||||||
canReceive,
|
update: {
|
||||||
// if send-only config or priority only, don't update the wallet type record
|
where: { walletId: Number(id) },
|
||||||
...(canReceive && !priorityOnly
|
data: walletData
|
||||||
? {
|
}
|
||||||
[wallet.field]: existingWalletTypeRecord
|
},
|
||||||
? { update: walletData }
|
vaultEntries: {
|
||||||
: { create: walletData }
|
deleteMany: difference(oldVaultEntries, vaultEntries, 'key').map(({ key }) => ({
|
||||||
}
|
userId: me.id, key
|
||||||
: {})
|
})),
|
||||||
|
create: difference(vaultEntries, oldVaultEntries, 'key').map(({ key, value }) => ({
|
||||||
|
key, value, userId: me.id
|
||||||
|
})),
|
||||||
|
update: intersectionMerge(oldVaultEntries, vaultEntries, 'key').map(({ key, value }) => ({
|
||||||
|
where: { userId_key: { userId: me.id, key } },
|
||||||
|
data: { value }
|
||||||
|
}))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
...(canReceive && !priorityOnly ? { [wallet.field]: true } : {})
|
vaultEntries: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
)
|
||||||
updatedWallet = await tx.wallet.create({
|
} else {
|
||||||
|
txs.push(
|
||||||
|
models.wallet.create({
|
||||||
|
include: {
|
||||||
|
vaultEntries: true
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
enabled,
|
enabled,
|
||||||
priority,
|
priority,
|
||||||
canSend,
|
|
||||||
canReceive,
|
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
type: wallet.type,
|
type: wallet.type,
|
||||||
// if send-only config or priority only, don't update the wallet type record
|
[wallet.field]: {
|
||||||
...(canReceive && !priorityOnly
|
create: walletData
|
||||||
? {
|
},
|
||||||
[wallet.field]: {
|
vaultEntries: {
|
||||||
create: walletData
|
createMany: {
|
||||||
}
|
data: vaultEntries.map(({ key, value }) => ({ key, value, userId: me.id }))
|
||||||
}
|
}
|
||||||
: {})
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const logs = []
|
txs.push(
|
||||||
if (canReceive) {
|
models.user.update({
|
||||||
logs.push({
|
where: { id: me.id },
|
||||||
userId: me.id,
|
data: {
|
||||||
wallet: wallet.type,
|
autoWithdrawMaxFeePercent,
|
||||||
level: enabled ? 'SUCCESS' : 'INFO',
|
autoWithdrawThreshold,
|
||||||
message: id ? 'receive details updated' : 'wallet attached for receives'
|
autoWithdrawMaxFeeTotal
|
||||||
})
|
}
|
||||||
logs.push({
|
|
||||||
userId: me.id,
|
|
||||||
wallet: wallet.type,
|
|
||||||
level: enabled ? 'SUCCESS' : 'INFO',
|
|
||||||
message: enabled ? 'receives enabled' : 'receives disabled'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canSend) {
|
|
||||||
logs.push({
|
|
||||||
userId: me.id,
|
|
||||||
wallet: wallet.type,
|
|
||||||
level: enabled ? 'SUCCESS' : 'INFO',
|
|
||||||
message: id ? 'send details updated' : 'wallet attached for sends'
|
|
||||||
})
|
|
||||||
logs.push({
|
|
||||||
userId: me.id,
|
|
||||||
wallet: wallet.type,
|
|
||||||
level: enabled ? 'SUCCESS' : 'INFO',
|
|
||||||
message: enabled ? 'sends enabled' : 'sends disabled'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await tx.walletLog.createMany({
|
|
||||||
data: logs
|
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
|
||||||
return updatedWallet
|
txs.push(
|
||||||
})
|
models.walletLog.createMany({
|
||||||
|
data: {
|
||||||
|
userId: me.id,
|
||||||
|
wallet: wallet.type,
|
||||||
|
level: 'SUCCESS',
|
||||||
|
message: id ? 'wallet details updated' : 'wallet attached'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
models.walletLog.create({
|
||||||
|
data: {
|
||||||
|
userId: me.id,
|
||||||
|
wallet: wallet.type,
|
||||||
|
level: enabled ? 'SUCCESS' : 'INFO',
|
||||||
|
message: enabled ? 'wallet enabled' : 'wallet disabled'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const [upsertedWallet] = await models.$transaction(txs)
|
||||||
|
return upsertedWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createWithdrawal (parent, { invoice, maxFee }, { me, models, lnd, headers, walletId = null }) {
|
export async function createWithdrawal (parent, { invoice, maxFee }, { me, models, lnd, headers, walletId = null }) {
|
||||||
|
|
|
@ -182,11 +182,9 @@ export default gql`
|
||||||
withdrawMaxFeeDefault: Int!
|
withdrawMaxFeeDefault: Int!
|
||||||
autoWithdrawThreshold: Int
|
autoWithdrawThreshold: Int
|
||||||
autoWithdrawMaxFeePercent: Float
|
autoWithdrawMaxFeePercent: Float
|
||||||
<<<<<<< HEAD
|
|
||||||
autoWithdrawMaxFeeTotal: Int
|
autoWithdrawMaxFeeTotal: Int
|
||||||
=======
|
|
||||||
vaultKeyHash: String
|
vaultKeyHash: String
|
||||||
>>>>>>> 002b1d19 (user vault and server side client wallets)
|
walletsUpdatedAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserOptional {
|
type UserOptional {
|
||||||
|
|
|
@ -17,7 +17,7 @@ function mutationTypeDefs () {
|
||||||
.filter(isServerField)
|
.filter(isServerField)
|
||||||
.map(fieldToGqlArgOptional)
|
.map(fieldToGqlArgOptional)
|
||||||
if (serverFields.length > 0) args += serverFields.join(', ') + ','
|
if (serverFields.length > 0) args += serverFields.join(', ') + ','
|
||||||
args += 'settings: AutowithdrawSettings!, priorityOnly: Boolean, canSend: Boolean!, canReceive: Boolean!'
|
args += 'enabled: Boolean, priority: Int, vaultEntries: [VaultEntryInput!], settings: AutowithdrawSettings!, validateLightning: Boolean'
|
||||||
const resolverName = generateResolverName(w.walletField)
|
const resolverName = generateResolverName(w.walletField)
|
||||||
const typeDef = `${resolverName}(${args}): Wallet`
|
const typeDef = `${resolverName}(${args}): Wallet`
|
||||||
console.log(typeDef)
|
console.log(typeDef)
|
||||||
|
@ -91,8 +91,6 @@ const typeDefs = `
|
||||||
enabled: Boolean!
|
enabled: Boolean!
|
||||||
priority: Int!
|
priority: Int!
|
||||||
wallet: WalletDetails!
|
wallet: WalletDetails!
|
||||||
canReceive: Boolean!
|
|
||||||
canSend: Boolean!
|
|
||||||
vaultEntries: [VaultEntry!]!
|
vaultEntries: [VaultEntry!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +98,6 @@ const typeDefs = `
|
||||||
autoWithdrawThreshold: Int!
|
autoWithdrawThreshold: Int!
|
||||||
autoWithdrawMaxFeePercent: Float!
|
autoWithdrawMaxFeePercent: Float!
|
||||||
autoWithdrawMaxFeeTotal: Int!
|
autoWithdrawMaxFeeTotal: Int!
|
||||||
priority: Int
|
|
||||||
enabled: Boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Invoice {
|
type Invoice {
|
||||||
|
|
|
@ -93,7 +93,10 @@ function sortHelper (a, b) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeeButtonProvider ({ baseLineItems = {}, useRemoteLineItems = () => null, children }) {
|
const DEFAULT_BASE_LINE_ITEMS = {}
|
||||||
|
const DEFAULT_USE_REMOTE_LINE_ITEMS = () => null
|
||||||
|
|
||||||
|
export function FeeButtonProvider ({ baseLineItems = DEFAULT_BASE_LINE_ITEMS, useRemoteLineItems = DEFAULT_USE_REMOTE_LINE_ITEMS, children }) {
|
||||||
const [lineItems, setLineItems] = useState({})
|
const [lineItems, setLineItems] = useState({})
|
||||||
const [disabled, setDisabled] = useState(false)
|
const [disabled, setDisabled] = useState(false)
|
||||||
const { me } = useMe()
|
const { me } = useMe()
|
||||||
|
|
|
@ -10,7 +10,10 @@ import { LIMIT } from '@/lib/cursor'
|
||||||
import ItemFull from './item-full'
|
import ItemFull from './item-full'
|
||||||
import { useData } from './use-data'
|
import { useData } from './use-data'
|
||||||
|
|
||||||
export default function Items ({ ssrData, variables = {}, query, destructureData, rank, noMoreText, Footer, filter = () => true }) {
|
const DEFAULT_FILTER = () => true
|
||||||
|
const DEFAULT_VARIABLES = {}
|
||||||
|
|
||||||
|
export default function Items ({ ssrData, variables = DEFAULT_VARIABLES, query, destructureData, rank, noMoreText, Footer, filter = DEFAULT_FILTER }) {
|
||||||
const { data, fetchMore } = useQuery(query || SUB_ITEMS, { variables })
|
const { data, fetchMore } = useQuery(query || SUB_ITEMS, { variables })
|
||||||
const Foooter = Footer || MoreFooter
|
const Foooter = Footer || MoreFooter
|
||||||
const dat = useData(data, ssrData)
|
const dat = useData(data, ssrData)
|
||||||
|
|
|
@ -15,7 +15,11 @@ export function SubSelectInitial ({ sub }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSubs ({ prependSubs = [], sub, filterSubs = () => true, appendSubs = [] }) {
|
const DEFAULT_PREPEND_SUBS = []
|
||||||
|
const DEFAULT_APPEND_SUBS = []
|
||||||
|
const DEFAULT_FILTER_SUBS = () => true
|
||||||
|
|
||||||
|
export function useSubs ({ prependSubs = DEFAULT_PREPEND_SUBS, sub, filterSubs = DEFAULT_FILTER_SUBS, appendSubs = DEFAULT_APPEND_SUBS }) {
|
||||||
const { data } = useQuery(SUBS, SSR
|
const { data } = useQuery(SUBS, SSR
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
|
|
|
@ -17,7 +17,9 @@ export function debounce (fn, time) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useDebounceCallback (fn, time, deps = []) {
|
const DEFAULT_DEPS = []
|
||||||
|
|
||||||
|
export default function useDebounceCallback (fn, time, deps = DEFAULT_DEPS) {
|
||||||
const [args, setArgs] = useState([])
|
const [args, setArgs] = useState([])
|
||||||
const memoFn = useCallback(fn, deps)
|
const memoFn = useCallback(fn, deps)
|
||||||
useNoInitialEffect(debounce(() => memoFn(...args), time), [memoFn, time, args])
|
useNoInitialEffect(debounce(() => memoFn(...args), time), [memoFn, time, args])
|
||||||
|
|
|
@ -4,7 +4,11 @@ export function getDbName (userId, name) {
|
||||||
return `app:storage:${userId ?? ''}${name ? `:${name}` : ''}`
|
return `app:storage:${userId ?? ''}${name ? `:${name}` : ''}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function useIndexedDB ({ dbName, storeName, options = { keyPath: 'id', autoIncrement: true }, indices = [], version = 1 }) {
|
const DEFAULT_OPTIONS = { keyPath: 'id', autoIncrement: true }
|
||||||
|
const DEFAULT_INDICES = []
|
||||||
|
const DEFAULT_VERSION = 1
|
||||||
|
|
||||||
|
function useIndexedDB ({ dbName, storeName, options = DEFAULT_OPTIONS, indices = DEFAULT_INDICES, version = DEFAULT_VERSION }) {
|
||||||
const [db, setDb] = useState(null)
|
const [db, setDb] = useState(null)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [notSupported, setNotSupported] = useState(false)
|
const [notSupported, setNotSupported] = useState(false)
|
||||||
|
@ -28,7 +32,7 @@ function useIndexedDB ({ dbName, storeName, options = { keyPath: 'id', autoIncre
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error)
|
handleError(error)
|
||||||
}
|
}
|
||||||
}, [storeName, handleError])
|
}, [storeName, handleError, operationQueue])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true
|
let isMounted = true
|
||||||
|
@ -81,7 +85,7 @@ function useIndexedDB ({ dbName, storeName, options = { keyPath: 'id', autoIncre
|
||||||
db.close()
|
db.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [dbName, storeName, version, indices, handleError, processQueue])
|
}, [dbName, storeName, version, indices, options, handleError, processQueue])
|
||||||
|
|
||||||
const queueOperation = useCallback((operation) => {
|
const queueOperation = useCallback((operation) => {
|
||||||
if (notSupported) {
|
if (notSupported) {
|
||||||
|
|
|
@ -140,7 +140,9 @@ function UserHidden ({ rank, Embellish }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ListUsers ({ users, rank, statComps = seperate(STAT_COMPONENTS, Seperator), Embellish, nymActionDropdown }) {
|
const DEFAULT_STAT_COMPONENTS = seperate(STAT_COMPONENTS, Seperator)
|
||||||
|
|
||||||
|
export function ListUsers ({ users, rank, statComps = DEFAULT_STAT_COMPONENTS, Embellish, nymActionDropdown }) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
{users.map((user, i) => (
|
{users.map((user, i) => (
|
||||||
|
@ -155,7 +157,7 @@ export function ListUsers ({ users, rank, statComps = seperate(STAT_COMPONENTS,
|
||||||
export default function UserList ({ ssrData, query, variables, destructureData, rank, footer = true, nymActionDropdown, statCompsProp }) {
|
export default function UserList ({ ssrData, query, variables, destructureData, rank, footer = true, nymActionDropdown, statCompsProp }) {
|
||||||
const { data, fetchMore } = useQuery(query, { variables })
|
const { data, fetchMore } = useQuery(query, { variables })
|
||||||
const dat = useData(data, ssrData)
|
const dat = useData(data, ssrData)
|
||||||
const [statComps, setStatComps] = useState(seperate(STAT_COMPONENTS, Seperator))
|
const [statComps, setStatComps] = useState(DEFAULT_STAT_COMPONENTS)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// shift the stat we are sorting by to the front
|
// shift the stat we are sorting by to the front
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useMutation, useQuery } from '@apollo/client'
|
||||||
import { useMe } from '../me'
|
import { useMe } from '../me'
|
||||||
import { useToast } from '../toast'
|
import { useToast } from '../toast'
|
||||||
import useIndexedDB, { getDbName } from '../use-indexeddb'
|
import useIndexedDB, { getDbName } from '../use-indexeddb'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { E_VAULT_KEY_EXISTS } from '@/lib/error'
|
import { E_VAULT_KEY_EXISTS } from '@/lib/error'
|
||||||
import { CLEAR_VAULT, GET_VAULT_ENTRIES, UPDATE_VAULT_KEY } from '@/fragments/vault'
|
import { CLEAR_VAULT, GET_VAULT_ENTRIES, UPDATE_VAULT_KEY } from '@/fragments/vault'
|
||||||
import { toHex } from '@/lib/hex'
|
import { toHex } from '@/lib/hex'
|
||||||
|
@ -21,7 +21,8 @@ const useImperativeQuery = (query) => {
|
||||||
export function useVaultConfigurator () {
|
export function useVaultConfigurator () {
|
||||||
const { me } = useMe()
|
const { me } = useMe()
|
||||||
const toaster = useToast()
|
const toaster = useToast()
|
||||||
const { set, get, remove } = useIndexedDB({ dbName: getDbName(me?.id, 'vault'), storeName: 'vault' })
|
const idbConfig = useMemo(() => ({ dbName: getDbName(me?.id, 'vault'), storeName: 'vault' }), [me?.id])
|
||||||
|
const { set, get, remove } = useIndexedDB(idbConfig)
|
||||||
const [updateVaultKey] = useMutation(UPDATE_VAULT_KEY)
|
const [updateVaultKey] = useMutation(UPDATE_VAULT_KEY)
|
||||||
const getVaultEntries = useImperativeQuery(GET_VAULT_ENTRIES)
|
const getVaultEntries = useImperativeQuery(GET_VAULT_ENTRIES)
|
||||||
const [key, setKey] = useState(null)
|
const [key, setKey] = useState(null)
|
||||||
|
@ -46,7 +47,7 @@ export function useVaultConfigurator () {
|
||||||
// toaster?.danger('error loading vault configuration ' + e.message)
|
// toaster?.danger('error loading vault configuration ' + e.message)
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
}, [me?.privates?.vaultKeyHash, keyHash, get, remove, toaster])
|
}, [me?.privates?.vaultKeyHash, keyHash, get, remove])
|
||||||
|
|
||||||
// clear vault: remove everything and reset the key
|
// clear vault: remove everything and reset the key
|
||||||
const [clearVault] = useMutation(CLEAR_VAULT, {
|
const [clearVault] = useMutation(CLEAR_VAULT, {
|
||||||
|
|
|
@ -92,11 +92,11 @@ function getWalletLogDbName (userId) {
|
||||||
|
|
||||||
function useWalletLogDB () {
|
function useWalletLogDB () {
|
||||||
const { me } = useMe()
|
const { me } = useMe()
|
||||||
const { add, getPage, clear, error, notSupported } = useIndexedDB({
|
// memoize the idb config to avoid re-creating it on every render
|
||||||
dbName: getWalletLogDbName(me?.id),
|
const idbConfig = useMemo(() =>
|
||||||
storeName: 'wallet_logs',
|
({ dbName: getWalletLogDbName(me?.id), storeName: 'wallet_logs', indices: INDICES }), [me?.id])
|
||||||
indices: INDICES
|
const { add, getPage, clear, error, notSupported } = useIndexedDB(idbConfig)
|
||||||
})
|
|
||||||
return { add, getPage, clear, error, notSupported }
|
return { add, getPage, clear, error, notSupported }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,68 +3,12 @@ import { COMMENTS, COMMENTS_ITEM_EXT_FIELDS } from './comments'
|
||||||
import { ITEM_FIELDS, ITEM_FULL_FIELDS } from './items'
|
import { ITEM_FIELDS, ITEM_FULL_FIELDS } from './items'
|
||||||
import { SUB_FULL_FIELDS } from './subs'
|
import { SUB_FULL_FIELDS } from './subs'
|
||||||
|
|
||||||
export const ME = gql`
|
const STREAK_FIELDS = gql`
|
||||||
{
|
fragment StreakFields on User {
|
||||||
me {
|
optional {
|
||||||
id
|
streak
|
||||||
name
|
gunStreak
|
||||||
bioId
|
horseStreak
|
||||||
photoId
|
|
||||||
privates {
|
|
||||||
autoDropBolt11s
|
|
||||||
diagnostics
|
|
||||||
noReferralLinks
|
|
||||||
fiatCurrency
|
|
||||||
satsFilter
|
|
||||||
hideCowboyHat
|
|
||||||
hideFromTopUsers
|
|
||||||
hideGithub
|
|
||||||
hideNostr
|
|
||||||
hideTwitter
|
|
||||||
hideInvoiceDesc
|
|
||||||
hideIsContributor
|
|
||||||
hideWalletBalance
|
|
||||||
hideWelcomeBanner
|
|
||||||
imgproxyOnly
|
|
||||||
showImagesAndVideos
|
|
||||||
lastCheckedJobs
|
|
||||||
nostrCrossposting
|
|
||||||
noteAllDescendants
|
|
||||||
noteCowboyHat
|
|
||||||
noteDeposits
|
|
||||||
noteWithdrawals
|
|
||||||
noteEarning
|
|
||||||
noteForwardedSats
|
|
||||||
noteInvites
|
|
||||||
noteItemSats
|
|
||||||
noteJobIndicator
|
|
||||||
noteMentions
|
|
||||||
noteItemMentions
|
|
||||||
sats
|
|
||||||
tipDefault
|
|
||||||
tipRandom
|
|
||||||
tipRandomMin
|
|
||||||
tipRandomMax
|
|
||||||
tipPopover
|
|
||||||
turboTipping
|
|
||||||
zapUndos
|
|
||||||
upvotePopover
|
|
||||||
wildWestMode
|
|
||||||
withdrawMaxFeeDefault
|
|
||||||
lnAddr
|
|
||||||
autoWithdrawMaxFeePercent
|
|
||||||
autoWithdrawThreshold
|
|
||||||
disableFreebies
|
|
||||||
vaultKeyHash
|
|
||||||
}
|
|
||||||
optional {
|
|
||||||
isContributor
|
|
||||||
stacked
|
|
||||||
streak
|
|
||||||
githubId
|
|
||||||
nostrAuthPubkey
|
|
||||||
twitterId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -104,6 +48,8 @@ ${STREAK_FIELDS}
|
||||||
upvotePopover
|
upvotePopover
|
||||||
wildWestMode
|
wildWestMode
|
||||||
disableFreebies
|
disableFreebies
|
||||||
|
vaultKeyHash
|
||||||
|
walletsUpdatedAt
|
||||||
}
|
}
|
||||||
optional {
|
optional {
|
||||||
isContributor
|
isContributor
|
||||||
|
|
|
@ -106,86 +106,7 @@ mutation removeWallet($id: ID!) {
|
||||||
removeWallet(id: $id)
|
removeWallet(id: $id)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// XXX [WALLET] this needs to be updated if another server wallet is added
|
// XXX [WALLET] this needs to be updated if another server wallet is added
|
||||||
export const WALLET = gql`
|
|
||||||
query Wallet($id: ID!) {
|
|
||||||
wallet(id: $id) {
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
priority
|
|
||||||
type
|
|
||||||
wallet {
|
|
||||||
__typename
|
|
||||||
... on WalletLightningAddress {
|
|
||||||
address
|
|
||||||
}
|
|
||||||
... on WalletLnd {
|
|
||||||
socket
|
|
||||||
macaroon
|
|
||||||
cert
|
|
||||||
}
|
|
||||||
... on WalletCln {
|
|
||||||
socket
|
|
||||||
rune
|
|
||||||
cert
|
|
||||||
}
|
|
||||||
... on WalletLnbits {
|
|
||||||
url
|
|
||||||
invoiceKey
|
|
||||||
}
|
|
||||||
... on WalletNwc {
|
|
||||||
nwcUrlRecv
|
|
||||||
}
|
|
||||||
... on WalletPhoenixd {
|
|
||||||
url
|
|
||||||
secondaryPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// XXX [WALLET] this needs to be updated if another server wallet is added
|
|
||||||
export const WALLET_BY_TYPE = gql`
|
|
||||||
query WalletByType($type: String!) {
|
|
||||||
walletByType(type: $type) {
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
enabled
|
|
||||||
priority
|
|
||||||
type
|
|
||||||
wallet {
|
|
||||||
__typename
|
|
||||||
... on WalletLightningAddress {
|
|
||||||
address
|
|
||||||
}
|
|
||||||
... on WalletLnd {
|
|
||||||
socket
|
|
||||||
macaroon
|
|
||||||
cert
|
|
||||||
}
|
|
||||||
... on WalletCln {
|
|
||||||
socket
|
|
||||||
rune
|
|
||||||
cert
|
|
||||||
}
|
|
||||||
... on WalletLnbits {
|
|
||||||
url
|
|
||||||
invoiceKey
|
|
||||||
}
|
|
||||||
... on WalletNwc {
|
|
||||||
nwcUrlRecv
|
|
||||||
}
|
|
||||||
... on WalletPhoenixd {
|
|
||||||
url
|
|
||||||
secondaryPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const WALLET_FIELDS = gql`
|
export const WALLET_FIELDS = gql`
|
||||||
fragment WalletFields on Wallet {
|
fragment WalletFields on Wallet {
|
||||||
id
|
id
|
||||||
|
@ -197,12 +118,57 @@ export const WALLET_FIELDS = gql`
|
||||||
key
|
key
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
wallet {
|
||||||
|
__typename
|
||||||
|
... on WalletLightningAddress {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
... on WalletLnd {
|
||||||
|
socket
|
||||||
|
macaroon
|
||||||
|
cert
|
||||||
|
}
|
||||||
|
... on WalletCln {
|
||||||
|
socket
|
||||||
|
rune
|
||||||
|
cert
|
||||||
|
}
|
||||||
|
... on WalletLnbits {
|
||||||
|
url
|
||||||
|
invoiceKey
|
||||||
|
}
|
||||||
|
... on WalletNwc {
|
||||||
|
nwcUrlRecv
|
||||||
|
}
|
||||||
|
... on WalletPhoenixd {
|
||||||
|
url
|
||||||
|
secondaryPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const WALLET = gql`
|
||||||
|
${WALLET_FIELDS}
|
||||||
|
query Wallet($id: ID!) {
|
||||||
|
wallet(id: $id) {
|
||||||
|
...WalletFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// XXX [WALLET] this needs to be updated if another server wallet is added
|
||||||
|
export const WALLET_BY_TYPE = gql`
|
||||||
|
${WALLET_FIELDS}
|
||||||
|
query WalletByType($type: String!) {
|
||||||
|
walletByType(type: $type) {
|
||||||
|
...WalletFields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const WALLETS = gql`
|
export const WALLETS = gql`
|
||||||
${WALLET_FIELDS}
|
${WALLET_FIELDS}
|
||||||
|
|
||||||
query Wallets {
|
query Wallets {
|
||||||
wallets {
|
wallets {
|
||||||
...WalletFields
|
...WalletFields
|
||||||
|
|
|
@ -42,11 +42,11 @@ export async function formikValidate (validate, data) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function walletValidate (wallet, data) {
|
export async function walletValidate (walletDef, data) {
|
||||||
if (typeof wallet.def.fieldValidation === 'function') {
|
if (typeof walletDef.fieldValidation === 'function') {
|
||||||
return await formikValidate(wallet.def.fieldValidation, data)
|
return await formikValidate(walletDef.fieldValidation, data)
|
||||||
} else {
|
} else {
|
||||||
return await ssValidate(wallet.def.fieldValidation, data)
|
return await ssValidate(walletDef.fieldValidation, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
"mdast-util-gfm": "^3.0.0",
|
"mdast-util-gfm": "^3.0.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"micromark-extension-gfm": "^3.0.0",
|
"micromark-extension-gfm": "^3.0.0",
|
||||||
"next": "^14.2.15",
|
"next": "^14.2.16",
|
||||||
"next-auth": "^4.24.8",
|
"next-auth": "^4.24.8",
|
||||||
"next-plausible": "^3.12.2",
|
"next-plausible": "^3.12.2",
|
||||||
"next-seo": "^6.6.0",
|
"next-seo": "^6.6.0",
|
||||||
|
@ -4124,9 +4124,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.16.tgz",
|
||||||
"integrity": "sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ=="
|
"integrity": "sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag=="
|
||||||
},
|
},
|
||||||
"node_modules/@next/eslint-plugin-next": {
|
"node_modules/@next/eslint-plugin-next": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.15",
|
||||||
|
@ -4184,9 +4184,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-arm64": {
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.16.tgz",
|
||||||
"integrity": "sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==",
|
"integrity": "sha512-uFT34QojYkf0+nn6MEZ4gIWQ5aqGF11uIZ1HSxG+cSbj+Mg3+tYm8qXYd3dKN5jqKUm5rBVvf1PBRO/MeQ6rxw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4199,9 +4199,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-x64": {
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.16.tgz",
|
||||||
"integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==",
|
"integrity": "sha512-mCecsFkYezem0QiZlg2bau3Xul77VxUD38b/auAjohMA22G9KTJneUYMv78vWoCCFkleFAhY1NIvbyjj1ncG9g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -4214,9 +4214,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.16.tgz",
|
||||||
"integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==",
|
"integrity": "sha512-yhkNA36+ECTC91KSyZcgWgKrYIyDnXZj8PqtJ+c2pMvj45xf7y/HrgI17hLdrcYamLfVt7pBaJUMxADtPaczHA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4229,9 +4229,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-musl": {
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.16.tgz",
|
||||||
"integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==",
|
"integrity": "sha512-X2YSyu5RMys8R2lA0yLMCOCtqFOoLxrq2YbazFvcPOE4i/isubYjkh+JCpRmqYfEuCVltvlo+oGfj/b5T2pKUA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4244,9 +4244,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-gnu": {
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.16.tgz",
|
||||||
"integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==",
|
"integrity": "sha512-9AGcX7VAkGbc5zTSa+bjQ757tkjr6C/pKS7OK8cX7QEiK6MHIIezBLcQ7gQqbDW2k5yaqba2aDtaBeyyZh1i6Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -4259,9 +4259,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-musl": {
|
"node_modules/@next/swc-linux-x64-musl": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.16.tgz",
|
||||||
"integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==",
|
"integrity": "sha512-Klgeagrdun4WWDaOizdbtIIm8khUDQJ/5cRzdpXHfkbY91LxBXeejL4kbZBrpR/nmgRrQvmz4l3OtttNVkz2Sg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -4274,9 +4274,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.16.tgz",
|
||||||
"integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==",
|
"integrity": "sha512-PwW8A1UC1Y0xIm83G3yFGPiOBftJK4zukTmk7DI1CebyMOoaVpd8aSy7K6GhobzhkjYvqS/QmzcfsWG2Dwizdg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
@ -4289,9 +4289,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.16.tgz",
|
||||||
"integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==",
|
"integrity": "sha512-jhPl3nN0oKEshJBNDAo0etGMzv0j3q3VYorTSFqH1o3rwv1MQRdor27u1zhkgsHPNeY1jxcgyx1ZsCkDD1IHgg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
|
@ -4304,9 +4304,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-x64-msvc": {
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.16.tgz",
|
||||||
"integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==",
|
"integrity": "sha512-OA7NtfxgirCjfqt+02BqxC3MIgM/JaGjw9tOe4fyZgPsqfseNiMPnCRP44Pfs+Gpo9zPN+SXaFsgP6vk8d571A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
@ -15494,11 +15494,11 @@
|
||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||||
},
|
},
|
||||||
"node_modules/next": {
|
"node_modules/next": {
|
||||||
"version": "14.2.15",
|
"version": "14.2.16",
|
||||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/next/-/next-14.2.16.tgz",
|
||||||
"integrity": "sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==",
|
"integrity": "sha512-LcO7WnFu6lYSvCzZoo1dB+IO0xXz5uEv52HF1IUN0IqVTUIZGHuuR10I5efiLadGt+4oZqTcNZyVVEem/TM5nA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/env": "14.2.15",
|
"@next/env": "14.2.16",
|
||||||
"@swc/helpers": "0.5.5",
|
"@swc/helpers": "0.5.5",
|
||||||
"busboy": "1.6.0",
|
"busboy": "1.6.0",
|
||||||
"caniuse-lite": "^1.0.30001579",
|
"caniuse-lite": "^1.0.30001579",
|
||||||
|
@ -15513,15 +15513,15 @@
|
||||||
"node": ">=18.17.0"
|
"node": ">=18.17.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@next/swc-darwin-arm64": "14.2.15",
|
"@next/swc-darwin-arm64": "14.2.16",
|
||||||
"@next/swc-darwin-x64": "14.2.15",
|
"@next/swc-darwin-x64": "14.2.16",
|
||||||
"@next/swc-linux-arm64-gnu": "14.2.15",
|
"@next/swc-linux-arm64-gnu": "14.2.16",
|
||||||
"@next/swc-linux-arm64-musl": "14.2.15",
|
"@next/swc-linux-arm64-musl": "14.2.16",
|
||||||
"@next/swc-linux-x64-gnu": "14.2.15",
|
"@next/swc-linux-x64-gnu": "14.2.16",
|
||||||
"@next/swc-linux-x64-musl": "14.2.15",
|
"@next/swc-linux-x64-musl": "14.2.16",
|
||||||
"@next/swc-win32-arm64-msvc": "14.2.15",
|
"@next/swc-win32-arm64-msvc": "14.2.16",
|
||||||
"@next/swc-win32-ia32-msvc": "14.2.15",
|
"@next/swc-win32-ia32-msvc": "14.2.16",
|
||||||
"@next/swc-win32-x64-msvc": "14.2.15"
|
"@next/swc-win32-x64-msvc": "14.2.16"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@opentelemetry/api": "^1.1.0",
|
"@opentelemetry/api": "^1.1.0",
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
"mdast-util-gfm": "^3.0.0",
|
"mdast-util-gfm": "^3.0.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"micromark-extension-gfm": "^3.0.0",
|
"micromark-extension-gfm": "^3.0.0",
|
||||||
"next": "^14.2.15",
|
"next": "^14.2.16",
|
||||||
"next-auth": "^4.24.8",
|
"next-auth": "^4.24.8",
|
||||||
"next-plausible": "^3.12.2",
|
"next-plausible": "^3.12.2",
|
||||||
"next-seo": "^6.6.0",
|
"next-seo": "^6.6.0",
|
||||||
|
|
|
@ -11,16 +11,14 @@ ALTER TYPE "WalletType" ADD VALUE 'LNC';
|
||||||
ALTER TYPE "WalletType" ADD VALUE 'WEBLN';
|
ALTER TYPE "WalletType" ADD VALUE 'WEBLN';
|
||||||
|
|
||||||
-- AlterTable
|
-- AlterTable
|
||||||
ALTER TABLE "Wallet" ADD COLUMN "canReceive" BOOLEAN NOT NULL DEFAULT true,
|
ALTER TABLE "users" ADD COLUMN "vaultKeyHash" TEXT NOT NULL DEFAULT '',
|
||||||
ADD COLUMN "canSend" BOOLEAN NOT NULL DEFAULT false;
|
ADD COLUMN "walletsUpdatedAt" TIMESTAMP(3);
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "users" ADD COLUMN "vaultKeyHash" TEXT NOT NULL DEFAULT '';
|
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE "VaultEntry" (
|
CREATE TABLE "VaultEntry" (
|
||||||
"id" SERIAL NOT NULL,
|
"id" SERIAL NOT NULL,
|
||||||
"key" VARCHAR(64) NOT NULL,
|
"key" TEXT NOT NULL,
|
||||||
|
"iv" TEXT NOT NULL,
|
||||||
"value" TEXT NOT NULL,
|
"value" TEXT NOT NULL,
|
||||||
"userId" INTEGER NOT NULL,
|
"userId" INTEGER NOT NULL,
|
||||||
"walletId" INTEGER,
|
"walletId" INTEGER,
|
||||||
|
@ -30,17 +28,32 @@ CREATE TABLE "VaultEntry" (
|
||||||
CONSTRAINT "VaultEntry_pkey" PRIMARY KEY ("id")
|
CONSTRAINT "VaultEntry_pkey" PRIMARY KEY ("id")
|
||||||
);
|
);
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "VaultEntry_userId_idx" ON "VaultEntry"("userId");
|
|
||||||
|
|
||||||
-- CreateIndex
|
-- CreateIndex
|
||||||
CREATE INDEX "VaultEntry_walletId_idx" ON "VaultEntry"("walletId");
|
CREATE INDEX "VaultEntry_walletId_idx" ON "VaultEntry"("walletId");
|
||||||
|
|
||||||
-- CreateIndex
|
-- CreateIndex
|
||||||
CREATE UNIQUE INDEX "VaultEntry_userId_key_walletId_key" ON "VaultEntry"("userId", "key", "walletId");
|
CREATE UNIQUE INDEX "VaultEntry_userId_key_key" ON "VaultEntry"("userId", "key");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Wallet_priority_idx" ON "Wallet"("priority");
|
||||||
|
|
||||||
-- AddForeignKey
|
-- AddForeignKey
|
||||||
ALTER TABLE "VaultEntry" ADD CONSTRAINT "VaultEntry_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
ALTER TABLE "VaultEntry" ADD CONSTRAINT "VaultEntry_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
-- AddForeignKey
|
-- AddForeignKey
|
||||||
ALTER TABLE "VaultEntry" ADD CONSTRAINT "VaultEntry_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
ALTER TABLE "VaultEntry" ADD CONSTRAINT "VaultEntry_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
CREATE FUNCTION wallet_updated_at_trigger() RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
UPDATE "users" SET "walletsUpdatedAt" = NOW() WHERE "id" = NEW."userId";
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER wallet_updated_at_trigger
|
||||||
|
AFTER INSERT OR UPDATE ON "Wallet"
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE wallet_updated_at_trigger();
|
||||||
|
|
||||||
|
CREATE TRIGGER vault_entry_updated_at_trigger
|
||||||
|
AFTER INSERT OR UPDATE ON "VaultEntry"
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE wallet_updated_at_trigger();
|
|
@ -138,6 +138,7 @@ model User {
|
||||||
oneDayReferrals OneDayReferral[] @relation("OneDayReferral_referrer")
|
oneDayReferrals OneDayReferral[] @relation("OneDayReferral_referrer")
|
||||||
oneDayReferrees OneDayReferral[] @relation("OneDayReferral_referrees")
|
oneDayReferrees OneDayReferral[] @relation("OneDayReferral_referrees")
|
||||||
vaultKeyHash String @default("")
|
vaultKeyHash String @default("")
|
||||||
|
walletsUpdatedAt DateTime?
|
||||||
vaultEntries VaultEntry[] @relation("VaultEntries")
|
vaultEntries VaultEntry[] @relation("VaultEntries")
|
||||||
|
|
||||||
@@index([photoId])
|
@@index([photoId])
|
||||||
|
@ -187,16 +188,14 @@ enum WalletType {
|
||||||
}
|
}
|
||||||
|
|
||||||
model Wallet {
|
model Wallet {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||||
userId Int
|
userId Int
|
||||||
label String?
|
label String?
|
||||||
enabled Boolean @default(true)
|
enabled Boolean @default(true)
|
||||||
priority Int @default(0)
|
priority Int @default(0)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
canReceive Boolean @default(true)
|
|
||||||
canSend Boolean @default(false)
|
|
||||||
|
|
||||||
// NOTE: this denormalized json field exists to make polymorphic joins efficient
|
// NOTE: this denormalized json field exists to make polymorphic joins efficient
|
||||||
// when reading wallets ... it is populated by a trigger when wallet descendants update
|
// when reading wallets ... it is populated by a trigger when wallet descendants update
|
||||||
|
@ -218,11 +217,13 @@ model Wallet {
|
||||||
InvoiceForward InvoiceForward[]
|
InvoiceForward InvoiceForward[]
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
|
@@index([priority])
|
||||||
}
|
}
|
||||||
|
|
||||||
model VaultEntry {
|
model VaultEntry {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
key String @db.VarChar(64)
|
key String @db.Text
|
||||||
|
iv String @db.Text
|
||||||
value String @db.Text
|
value String @db.Text
|
||||||
userId Int
|
userId Int
|
||||||
walletId Int?
|
walletId Int?
|
||||||
|
@ -231,8 +232,7 @@ model VaultEntry {
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||||
|
|
||||||
@@unique([userId, key, walletId])
|
@@unique([userId, key])
|
||||||
@@index([userId])
|
|
||||||
@@index([walletId])
|
@@index([walletId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,24 +74,24 @@ function checkFields ({ fields, config }) {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isConfigured (wallet) {
|
export function isConfigured ({ def, config }) {
|
||||||
return isSendConfigured(wallet) || isReceiveConfigured(wallet)
|
return isSendConfigured({ def, config }) || isReceiveConfigured({ def, config })
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSendConfigured (wallet) {
|
function isSendConfigured ({ def, config }) {
|
||||||
const fields = wallet.def.fields.filter(isClientField)
|
const fields = def.fields.filter(isClientField)
|
||||||
return checkFields({ fields, config: wallet.config })
|
return checkFields({ fields, config })
|
||||||
}
|
}
|
||||||
|
|
||||||
function isReceiveConfigured (wallet) {
|
function isReceiveConfigured ({ def, config }) {
|
||||||
const fields = wallet.def.fields.filter(isServerField)
|
const fields = def.fields.filter(isServerField)
|
||||||
return checkFields({ fields, config: wallet.config })
|
return checkFields({ fields, config })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canSend (wallet) {
|
export function canSend ({ def, config }) {
|
||||||
return !!wallet.def.sendPayment && isSendConfigured(wallet)
|
return !!def.sendPayment && isSendConfigured({ def, config })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canReceive (wallet) {
|
export function canReceive ({ def, config }) {
|
||||||
return !wallet.def.clientOnly && isReceiveConfigured(wallet)
|
return !def.clientOnly && isReceiveConfigured({ def, config })
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,13 +35,12 @@ export function useWalletConfigurator (wallet) {
|
||||||
|
|
||||||
const _validate = useCallback(async (config, validateLightning = true) => {
|
const _validate = useCallback(async (config, validateLightning = true) => {
|
||||||
const { serverWithShared, clientWithShared } = siftConfig(wallet.def.fields, config)
|
const { serverWithShared, clientWithShared } = siftConfig(wallet.def.fields, config)
|
||||||
console.log('sifted', siftConfig(wallet.def.fields, config))
|
|
||||||
|
|
||||||
let clientConfig = clientWithShared
|
let clientConfig = clientWithShared
|
||||||
let serverConfig = serverWithShared
|
let serverConfig = serverWithShared
|
||||||
|
|
||||||
if (canSend(wallet)) {
|
if (canSend({ def: wallet.def, config: clientConfig })) {
|
||||||
let transformedConfig = await walletValidate(wallet, clientWithShared)
|
let transformedConfig = await walletValidate(wallet.def, clientWithShared)
|
||||||
if (transformedConfig) {
|
if (transformedConfig) {
|
||||||
clientConfig = Object.assign(clientConfig, transformedConfig)
|
clientConfig = Object.assign(clientConfig, transformedConfig)
|
||||||
}
|
}
|
||||||
|
@ -51,29 +50,29 @@ export function useWalletConfigurator (wallet) {
|
||||||
clientConfig = Object.assign(clientConfig, transformedConfig)
|
clientConfig = Object.assign(clientConfig, transformedConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (canReceive({ def: wallet.def, config: serverConfig })) {
|
||||||
|
const transformedConfig = await walletValidate(wallet.def, serverConfig)
|
||||||
if (canReceive(wallet)) {
|
|
||||||
const transformedConfig = await walletValidate(wallet, serverConfig)
|
|
||||||
if (transformedConfig) {
|
if (transformedConfig) {
|
||||||
serverConfig = Object.assign(serverConfig, transformedConfig)
|
serverConfig = Object.assign(serverConfig, transformedConfig)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('configuration must be able to send or receive')
|
||||||
}
|
}
|
||||||
|
|
||||||
return { clientConfig, serverConfig }
|
return { clientConfig, serverConfig }
|
||||||
}, [wallet])
|
}, [wallet])
|
||||||
|
|
||||||
const save = useCallback(async (newConfig, validateLightning = true) => {
|
const save = useCallback(async (newConfig, validateLightning = true) => {
|
||||||
const { clientConfig, serverConfig } = _validate(newConfig, validateLightning)
|
const { clientConfig, serverConfig } = await _validate(newConfig, validateLightning)
|
||||||
|
|
||||||
// if vault is active, encrypt and send to server regardless of wallet type
|
// if vault is active, encrypt and send to server regardless of wallet type
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
await _saveToServer(serverConfig, clientConfig, validateLightning)
|
await _saveToServer(serverConfig, clientConfig, validateLightning)
|
||||||
} else {
|
} else {
|
||||||
if (canSend(wallet)) {
|
if (canSend({ def: wallet.def, config: clientConfig })) {
|
||||||
await _saveToLocal(clientConfig)
|
await _saveToLocal(clientConfig)
|
||||||
}
|
}
|
||||||
if (canReceive(wallet)) {
|
if (canReceive({ def: wallet.def, config: serverConfig })) {
|
||||||
await _saveToServer(serverConfig, clientConfig, validateLightning)
|
await _saveToServer(serverConfig, clientConfig, validateLightning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,18 +83,19 @@ export function useWalletConfigurator (wallet) {
|
||||||
}, [wallet.config?.id])
|
}, [wallet.config?.id])
|
||||||
|
|
||||||
const _detachFromLocal = useCallback(async () => {
|
const _detachFromLocal = useCallback(async () => {
|
||||||
// if vault is not active and has a client config, delete from local storage
|
|
||||||
window.localStorage.removeItem(getStorageKey(wallet.def.name, me?.id))
|
window.localStorage.removeItem(getStorageKey(wallet.def.name, me?.id))
|
||||||
}, [me?.id, wallet.def.name])
|
}, [me?.id, wallet.def.name])
|
||||||
|
|
||||||
const detach = useCallback(async () => {
|
const detach = useCallback(async () => {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
|
// if vault is active, detach all wallets from server
|
||||||
await _detachFromServer()
|
await _detachFromServer()
|
||||||
} else {
|
} else {
|
||||||
if (wallet.config.id) {
|
if (wallet.config.id) {
|
||||||
await _detachFromServer()
|
await _detachFromServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if vault is not active and has a client config, delete from local storage
|
||||||
await _detachFromLocal()
|
await _detachFromLocal()
|
||||||
}
|
}
|
||||||
}, [isActive, _detachFromServer, _detachFromLocal])
|
}, [isActive, _detachFromServer, _detachFromLocal])
|
||||||
|
|
|
@ -33,17 +33,18 @@ export function generateMutation (wallet) {
|
||||||
.filter(isServerField)
|
.filter(isServerField)
|
||||||
.map(f => `$${f.name}: String`)
|
.map(f => `$${f.name}: String`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
headerArgs += ', $settings: AutowithdrawSettings!, $validateLightning: Boolean'
|
headerArgs += ', $enabled: Boolean, $priority: Int, $vaultEntries: [VaultEntryInput!], $settings: AutowithdrawSettings!, $validateLightning: Boolean'
|
||||||
|
|
||||||
let inputArgs = 'id: $id, '
|
let inputArgs = 'id: $id, '
|
||||||
inputArgs += wallet.fields
|
inputArgs += wallet.fields
|
||||||
.filter(isServerField)
|
.filter(isServerField)
|
||||||
.map(f => `${f.name}: $${f.name}`).join(', ')
|
.map(f => `${f.name}: $${f.name}`).join(', ')
|
||||||
inputArgs += ', settings: $settings, validateLightning: $validateLightning,'
|
inputArgs += ', enabled: $enabled, priority: $priority, vaultEntries: $vaultEntries, settings: $settings, validateLightning: $validateLightning'
|
||||||
|
|
||||||
return gql`mutation ${resolverName}(${headerArgs}) {
|
return gql`
|
||||||
${WALLET_FIELDS}
|
${WALLET_FIELDS}
|
||||||
${resolverName}(${inputArgs}) {
|
mutation ${resolverName}(${headerArgs}) {
|
||||||
|
${resolverName}(${inputArgs}) {
|
||||||
...WalletFields
|
...WalletFields
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useMe } from '@/components/me'
|
import { useMe } from '@/components/me'
|
||||||
import { WALLETS } from '@/fragments/wallet'
|
import { WALLETS } from '@/fragments/wallet'
|
||||||
import { LONG_POLL_INTERVAL, SSR } from '@/lib/constants'
|
import { SSR } from '@/lib/constants'
|
||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend } from './common'
|
import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend } from './common'
|
||||||
|
@ -41,15 +41,17 @@ const walletDefsOnly = walletDefs.map(w => ({ def: w, config: {} }))
|
||||||
|
|
||||||
export function WalletsProvider ({ children }) {
|
export function WalletsProvider ({ children }) {
|
||||||
const { decrypt } = useVault()
|
const { decrypt } = useVault()
|
||||||
|
const { me } = useMe()
|
||||||
const { wallets: localWallets, reloadLocalWallets } = useLocalWallets()
|
const { wallets: localWallets, reloadLocalWallets } = useLocalWallets()
|
||||||
|
|
||||||
// TODO: instead of polling, this should only be called when the vault key is updated
|
const { data, refetch } = useQuery(WALLETS,
|
||||||
// or a denormalized field on the user 'vaultUpdatedAt' is changed
|
SSR ? {} : { nextFetchPolicy: 'cache-and-network' })
|
||||||
const { data } = useQuery(WALLETS, {
|
|
||||||
pollInterval: LONG_POLL_INTERVAL,
|
useEffect(() => {
|
||||||
nextFetchPolicy: 'cache-and-network',
|
if (me?.privates?.walletsUpdatedAt) {
|
||||||
skip: SSR
|
refetch()
|
||||||
})
|
}
|
||||||
|
}, [me?.privates?.walletsUpdatedAt, me?.privates?.vaultKeyHash, refetch])
|
||||||
|
|
||||||
const wallets = useMemo(() => {
|
const wallets = useMemo(() => {
|
||||||
// form wallets into a list of { config, def }
|
// form wallets into a list of { config, def }
|
||||||
|
@ -60,7 +62,9 @@ export function WalletsProvider ({ children }) {
|
||||||
config[key] = decrypt(value)
|
config[key] = decrypt(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { config, def }
|
// the specific wallet config on the server is stored in wallet.wallet
|
||||||
|
// on the client, it's stored in unnested
|
||||||
|
return { config: { ...config, ...w.wallet }, def }
|
||||||
}) ?? []
|
}) ?? []
|
||||||
|
|
||||||
// merge wallets on name
|
// merge wallets on name
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { parsePaymentRequest } from 'ln-service'
|
||||||
import { toPositiveNumber } from '@/lib/validate'
|
import { toPositiveNumber } from '@/lib/validate'
|
||||||
import { PAID_ACTION_TERMINAL_STATES } from '@/lib/constants'
|
import { PAID_ACTION_TERMINAL_STATES } from '@/lib/constants'
|
||||||
import { withTimeout } from '@/lib/time'
|
import { withTimeout } from '@/lib/time'
|
||||||
|
import { canReceive } from './common'
|
||||||
|
|
||||||
export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink, lnc, webln]
|
export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink, lnc, webln]
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ const MAX_PENDING_INVOICES_PER_WALLET = 25
|
||||||
export async function createInvoice (userId, { msats, description, descriptionHash, expiry = 360 }, { models }) {
|
export async function createInvoice (userId, { msats, description, descriptionHash, expiry = 360 }, { models }) {
|
||||||
// get the wallets in order of priority
|
// get the wallets in order of priority
|
||||||
const wallets = await models.wallet.findMany({
|
const wallets = await models.wallet.findMany({
|
||||||
where: { userId, enabled: true, canReceive: true },
|
where: { userId, enabled: true },
|
||||||
include: {
|
include: {
|
||||||
user: true
|
user: true
|
||||||
},
|
},
|
||||||
|
@ -42,11 +43,14 @@ export async function createInvoice (userId, { msats, description, descriptionHa
|
||||||
const w = walletDefs.find(w => w.walletType === wallet.def.walletType)
|
const w = walletDefs.find(w => w.walletType === wallet.def.walletType)
|
||||||
try {
|
try {
|
||||||
const { walletType, walletField, createInvoice } = w
|
const { walletType, walletField, createInvoice } = w
|
||||||
|
if (!canReceive({ def: w, config: wallet })) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
const walletFull = await models.wallet.findFirst({
|
const walletFull = await models.wallet.findFirst({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
type: wallet.def.walletType
|
type: walletType
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
[walletField]: true
|
[walletField]: true
|
||||||
|
|
Loading…
Reference in New Issue