diff --git a/lib/validate.js b/lib/validate.js index df8db627..3daa88af 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -10,7 +10,7 @@ import { msatsToSats, numWithUnits, abbrNum, ensureB64, B64_URL_REGEX } from './ import * as usersFragments from '@/fragments/users' import * as subsFragments from '@/fragments/subs' import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon' -import { TOR_REGEXP, parseNwcUrl } from './url' +import { parseNwcUrl } from './url' import { datePivot } from './time' import { decodeRune } from '@/lib/cln' import bip39Words from './bip39-words' @@ -600,31 +600,6 @@ export const lnAddrSchema = ({ payerData, min, max, commentAllowed } = {}) => return accum }, {}))) -export const lnbitsSchema = object({ - url: process.env.NODE_ENV === 'development' - ? string() - .or([string().matches(/^(http:\/\/)?localhost:\d+$/), string().url()], 'invalid url') - .required('required').trim() - : string().url().required('required').trim() - .test(async (url, context) => { - if (TOR_REGEXP.test(url)) { - // allow HTTP and HTTPS over Tor - if (!/^https?:\/\//.test(url)) { - return context.createError({ message: 'http or https required' }) - } - return true - } - try { - // force HTTPS over clearnet - await string().https().validate(url) - } catch (err) { - return context.createError({ message: err.message }) - } - return true - }), - adminKey: string().length(32) -}) - export const nwcSchema = object({ nwcUrl: string() .required('required') diff --git a/pages/settings/wallets/[wallet].js b/pages/settings/wallets/[wallet].js index 9eabd3b9..734167cb 100644 --- a/pages/settings/wallets/[wallet].js +++ b/pages/settings/wallets/[wallet].js @@ -10,6 +10,10 @@ import Info from '@/components/info' import Text from '@/components/text' import { AutowithdrawSettings } from '@/components/autowithdraw-shared' import dynamic from 'next/dynamic' +import { object, string } from 'yup' +import { autowithdrawSchemaMembers } from '@/lib/validate' +import { useMe } from '@/components/me' +import { TOR_REGEXP } from '@/lib/url' const WalletButtonBar = dynamic(() => import('@/components/wallet-buttonbar.js'), { ssr: false }) @@ -20,6 +24,7 @@ export default function WalletSettings () { const router = useRouter() const { wallet: name } = router.query const wallet = useWallet(name) + const me = useMe() const initial = wallet.fields.reduce((acc, field) => { // We still need to run over all wallet fields via reduce @@ -33,6 +38,8 @@ export default function WalletSettings () { } }, wallet.config) + const schema = generateSchema(wallet, { me }) + return (

{wallet.card.title}

@@ -40,7 +47,7 @@ export default function WalletSettings () { {!wallet.walletType && }
{ try { const newConfig = !wallet.isConfigured @@ -130,3 +137,67 @@ function WalletFields ({ wallet: { config, fields } }) { return null }) } + +function generateSchema (wallet, { me }) { + if (wallet.schema) return wallet.schema + + const fieldValidator = (field) => { + if (!field.validate) { + // default validation + let validator = string() + if (!field.optional) validator = validator.required('required') + return validator + } + + const { type: validationType } = field.validate + + let validator + + const stringTypes = ['url', 'string'] + + if (stringTypes.includes(validationType)) { + validator = string() + + if (field.validate.length) { + validator = validator.length(field.validate.length) + } + } + + if (validationType === 'url') { + validator = process.env.NODE_ENV === 'development' + ? validator + .or([string().matches(/^(http:\/\/)?localhost:\d+$/), string().url()], 'invalid url') + : validator + .url() + .test(async (url, context) => { + if (field.validate.onionAllowed && TOR_REGEXP.test(url)) { + // allow HTTP and HTTPS over Tor + if (!/^https?:\/\//.test(url)) { + return context.createError({ message: 'http or https required' }) + } + return true + } + try { + // force HTTPS over clearnet + await string().https().validate(url) + } catch (err) { + return context.createError({ message: err.message }) + } + return true + }) + } + + if (!field.optional) validator = validator.required('required') + + return validator + } + + return object( + wallet.fields.reduce((acc, field) => { + return { + ...acc, + [field.name]: fieldValidator(field) + } + }, wallet.walletType ? autowithdrawSchemaMembers({ me }) : {}) + ) +} diff --git a/wallets/lnbits/index.js b/wallets/lnbits/index.js index f3f23aa8..f4a5e940 100644 --- a/wallets/lnbits/index.js +++ b/wallets/lnbits/index.js @@ -1,17 +1,23 @@ -import { lnbitsSchema } from '@/lib/validate' - export const name = 'lnbits' export const fields = [ { name: 'url', label: 'lnbits url', - type: 'text' + type: 'text', + validate: { + type: 'url', + onionAllowed: true + } }, { name: 'adminKey', label: 'admin key', - type: 'password' + type: 'password', + validate: { + type: 'string', + length: 32 + } } ] @@ -20,5 +26,3 @@ export const card = { subtitle: 'use [LNbits](https://lnbits.com/) for payments', badges: ['send only', 'non-custodialish'] } - -export const schema = lnbitsSchema