validate generated fields
This commit is contained in:
parent
b1fc341017
commit
3cfbaf4638
|
@ -24,6 +24,7 @@ import { lnAddrOptions } from '@/lib/lnurl'
|
|||
import { GqlAuthenticationError, GqlAuthorizationError, GqlInputError } from '@/lib/error'
|
||||
import { getNodeSockets, getOurPubkey } from '../lnd'
|
||||
import validateWallet from '@/wallets/validate'
|
||||
import { canReceive } from '@/wallets/common'
|
||||
|
||||
function injectResolvers (resolvers) {
|
||||
console.group('injected GraphQL resolvers:')
|
||||
|
@ -43,9 +44,10 @@ function injectResolvers (resolvers) {
|
|||
field: walletDef.walletField,
|
||||
type: walletDef.walletType
|
||||
},
|
||||
testCreateInvoice: walletDef.testCreateInvoice && validateLightning
|
||||
? (data) => walletDef.testCreateInvoice(data, { me, models })
|
||||
: null
|
||||
testCreateInvoice:
|
||||
walletDef.testCreateInvoice && validateLightning && canReceive({ def: walletDef, config: data })
|
||||
? (data) => walletDef.testCreateInvoice(data, { me, models })
|
||||
: null
|
||||
}, {
|
||||
settings,
|
||||
data,
|
||||
|
|
|
@ -7,6 +7,7 @@ import { WELCOME_BANNER_MUTATION } from '@/fragments/users'
|
|||
import { useToast } from '@/components/toast'
|
||||
import { BALANCE_LIMIT_MSATS } from '@/lib/constants'
|
||||
import { msatsToSats, numWithUnits } from '@/lib/format'
|
||||
import Link from 'next/link'
|
||||
|
||||
export function WelcomeBanner ({ Banner }) {
|
||||
const { me } = useMe()
|
||||
|
@ -132,7 +133,7 @@ export function WalletSecurityBanner ({ isActive }) {
|
|||
Listen up, pardner! Put a limit on yer spendin' wallet or hook up a wallet that's only for Stacker News. It'll keep them varmints from cleanin' out yer whole goldmine if they rustle up yer wallet.
|
||||
</p>
|
||||
<p className='line-height-md'>
|
||||
Your spending wallet's credentials are never sent to our servers in plain text. To sync across devices, enable device sync in your settings.
|
||||
Your spending wallet's credentials are never sent to our servers in plain text. To sync across devices, <Alert.Link as={Link} href='/settings/passphrase'>enable device sync in your settings</Alert.Link>.
|
||||
</p>
|
||||
</Alert>
|
||||
)
|
||||
|
|
|
@ -17,7 +17,8 @@ export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
|||
export default function DeviceSync ({ ssrData }) {
|
||||
const { me } = useMe()
|
||||
const { onVaultKeySet, beforeDisconnectVault } = useWallets()
|
||||
const { key, setVaultKey, clearVault, disconnectVault } = useVaultConfigurator({ onVaultKeySet, beforeDisconnectVault })
|
||||
const { key, setVaultKey, clearVault, disconnectVault } =
|
||||
useVaultConfigurator({ onVaultKeySet, beforeDisconnectVault })
|
||||
const [passphrase, setPassphrase] = useState()
|
||||
|
||||
const setSeedPassphrase = useCallback(async (passphrase) => {
|
||||
|
|
|
@ -52,7 +52,8 @@ export default function WalletSettings () {
|
|||
|
||||
const validate = useCallback(async (data) => {
|
||||
try {
|
||||
await validateWallet(wallet.def, data, { yupOptions: { abortEarly: false }, topLevel: false })
|
||||
await validateWallet(wallet.def, data,
|
||||
{ yupOptions: { abortEarly: false }, topLevel: false, skipGenerated: true })
|
||||
} catch (error) {
|
||||
if (error instanceof ValidationError) {
|
||||
return error.inner.reduce((acc, error) => {
|
||||
|
@ -81,7 +82,7 @@ export default function WalletSettings () {
|
|||
values.enabled = true
|
||||
}
|
||||
|
||||
await save(values, true)
|
||||
await save(values, values.enabled)
|
||||
|
||||
toaster.success('saved settings')
|
||||
router.push('/settings/wallets')
|
||||
|
@ -137,7 +138,10 @@ function ReceiveSettings ({ walletDef }) {
|
|||
|
||||
function WalletFields ({ wallet }) {
|
||||
return wallet.def.fields
|
||||
.map(({ name, label = '', type, help, optional, editable, requiredWithout, validate, clientOnly, serverOnly, ...props }, i) => {
|
||||
.map(({
|
||||
name, label = '', type, help, optional, editable, requiredWithout,
|
||||
validate, clientOnly, serverOnly, generated, ...props
|
||||
}, i) => {
|
||||
const rawProps = {
|
||||
...props,
|
||||
name,
|
||||
|
|
|
@ -62,14 +62,14 @@ export function isClientField (f) {
|
|||
function checkFields ({ fields, config }) {
|
||||
// a wallet is configured if all of its required fields are set
|
||||
let val = fields.every(f => {
|
||||
if (f.optional && !f.requiredWithout) return true
|
||||
if ((f.optional || f.generated) && !f.requiredWithout) return true
|
||||
return !!config?.[f.name]
|
||||
})
|
||||
|
||||
// however, a wallet is not configured if all fields are optional and none are set
|
||||
// since that usually means that one of them is required
|
||||
if (val && fields.length > 0) {
|
||||
val = !(fields.every(f => f.optional) && fields.every(f => !config?.[f.name]))
|
||||
val = !(fields.every(f => f.optional || f.generated) && fields.every(f => !config?.[f.name]))
|
||||
}
|
||||
|
||||
return val
|
||||
|
@ -81,12 +81,12 @@ export function isConfigured ({ def, config }) {
|
|||
|
||||
function isSendConfigured ({ def, config }) {
|
||||
const fields = def.fields.filter(isClientField)
|
||||
return checkFields({ fields, config })
|
||||
return (fields.length > 0 || def.isAvailable?.()) && checkFields({ fields, config })
|
||||
}
|
||||
|
||||
function isReceiveConfigured ({ def, config }) {
|
||||
const fields = def.fields.filter(isServerField)
|
||||
return checkFields({ fields, config })
|
||||
return fields.length > 0 && checkFields({ fields, config })
|
||||
}
|
||||
|
||||
export function canSend ({ def, config }) {
|
||||
|
|
|
@ -37,7 +37,7 @@ export function useWalletConfigurator (wallet) {
|
|||
let serverConfig = serverWithShared
|
||||
|
||||
if (canSend({ def: wallet.def, config: clientConfig })) {
|
||||
let transformedConfig = await validateWallet(wallet.def, clientWithShared)
|
||||
let transformedConfig = await validateWallet(wallet.def, clientWithShared, { skipGenerated: true })
|
||||
if (transformedConfig) {
|
||||
clientConfig = Object.assign(clientConfig, transformedConfig)
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ export function useWalletConfigurator (wallet) {
|
|||
if (transformedConfig) {
|
||||
clientConfig = Object.assign(clientConfig, transformedConfig)
|
||||
}
|
||||
// validate again to ensure generated fields are valid
|
||||
await validateWallet(wallet.def, clientConfig)
|
||||
}
|
||||
} else if (canReceive({ def: wallet.def, config: serverConfig })) {
|
||||
const transformedConfig = await validateWallet(wallet.def, serverConfig)
|
||||
|
@ -74,6 +76,7 @@ export function useWalletConfigurator (wallet) {
|
|||
// if vault is active, encrypt and send to server regardless of wallet type
|
||||
if (isActive) {
|
||||
await _saveToServer(serverConfig, clientConfig, validateLightning)
|
||||
await _detachFromLocal()
|
||||
} else {
|
||||
if (canSend({ def: wallet.def, config: clientConfig })) {
|
||||
await _saveToLocal(clientConfig)
|
||||
|
|
|
@ -36,23 +36,26 @@ export const fields = [
|
|||
{
|
||||
name: 'localKey',
|
||||
type: 'text',
|
||||
optional: true,
|
||||
hidden: true,
|
||||
clientOnly: true
|
||||
clientOnly: true,
|
||||
generated: true,
|
||||
validate: string()
|
||||
},
|
||||
{
|
||||
name: 'remoteKey',
|
||||
type: 'text',
|
||||
optional: true,
|
||||
hidden: true,
|
||||
clientOnly: true
|
||||
clientOnly: true,
|
||||
generated: true,
|
||||
validate: string()
|
||||
},
|
||||
{
|
||||
name: 'serverHost',
|
||||
type: 'text',
|
||||
optional: true,
|
||||
hidden: true,
|
||||
clientOnly: true
|
||||
clientOnly: true,
|
||||
generated: true,
|
||||
validate: string()
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ import * as Yup from '@/lib/yup'
|
|||
import { canReceive } from './common'
|
||||
|
||||
export default async function validateWallet (walletDef, data,
|
||||
{ yupOptions = { abortEarly: true }, topLevel = true, serverSide = false } = {}) {
|
||||
let schema = composeWalletSchema(walletDef, serverSide)
|
||||
{ yupOptions = { abortEarly: true }, topLevel = true, serverSide = false, skipGenerated = false } = {}) {
|
||||
let schema = composeWalletSchema(walletDef, serverSide, skipGenerated)
|
||||
|
||||
if (canReceive({ def: walletDef, config: data })) {
|
||||
schema = schema.concat(autowithdrawSchemaMembers)
|
||||
|
@ -58,12 +58,16 @@ function createFieldSchema (name, validate) {
|
|||
}
|
||||
}
|
||||
|
||||
function composeWalletSchema (walletDef, serverSide) {
|
||||
function composeWalletSchema (walletDef, serverSide, skipGenerated) {
|
||||
const { fields } = walletDef
|
||||
|
||||
const vaultEntrySchemas = { required: [], optional: [] }
|
||||
const schemaShape = fields.reduce((acc, field) => {
|
||||
const { name, validate, optional, clientOnly, requiredWithout } = field
|
||||
const { name, validate, optional, generated, clientOnly, requiredWithout } = field
|
||||
|
||||
if (generated && skipGenerated) {
|
||||
return acc
|
||||
}
|
||||
|
||||
if (clientOnly && serverSide) {
|
||||
// For server-side validation, accumulate clientOnly fields as vaultEntries
|
||||
|
|
Loading…
Reference in New Issue