chore: use monorepo organization

This commit is contained in:
Rafael Taranto 2025-05-12 10:52:54 +01:00
parent deaf7d6ecc
commit a687827f7e
1099 changed files with 8184 additions and 11535 deletions

View file

@ -0,0 +1,86 @@
import classnames from 'classnames'
import { Formik, Form, FastField } from 'formik'
import * as R from 'ramda'
import React, { useState } from 'react'
import ErrorMessage from 'src/components/ErrorMessage'
import { Button } from 'src/components/buttons'
import { SecretInput } from 'src/components/inputs/formik'
const FormRenderer = ({
validationSchema,
elements,
value,
save,
buttonLabel = 'Save changes',
buttonClass,
xs = 12
}) => {
const initialValues = R.compose(
R.mergeAll,
R.map(({ code }) => ({ [code]: (value && value[code]) ?? '' }))
)(elements)
const values = R.merge(initialValues, value)
const [saveError, setSaveError] = useState([])
const saveNonEmptySecret = it => {
const emptySecretFields = R.compose(
R.map(R.prop('code')),
R.filter(
elem =>
R.prop('component', elem) === SecretInput &&
R.isEmpty(it[R.prop('code', elem)])
)
)(elements)
return save(R.omit(emptySecretFields, it)).catch(s => {
setSaveError({ save: 'Failed to save changes' })
})
}
return (
<Formik
validateOnBlur={false}
validateOnChange={false}
enableReinitialize
initialValues={values}
validationSchema={validationSchema}
onSubmit={saveNonEmptySecret}>
{({ errors }) => (
<Form className="flex flex-col flex-1">
<div className="flex flex-col gap-3 mb-6 mt-3">
{elements.map(
({ component, code, display, settings, inputProps }) => (
<div key={code}>
<FastField
component={component}
{...inputProps}
name={code}
label={display}
settings={settings}
fullWidth={true}
/>
</div>
)
)}
</div>
<div className="flex flex-row mt-auto mb-8">
{!R.isEmpty(R.mergeRight(errors, saveError)) && (
<ErrorMessage>
{R.head(R.values(R.mergeRight(errors, saveError)))}
</ErrorMessage>
)}
<Button
className={classnames('mt-auto ml-auto', buttonClass)}
type="submit">
{buttonLabel}
</Button>
</div>
</Form>
)}
</Formik>
)
}
export default FormRenderer

View file

@ -0,0 +1,141 @@
import { useQuery, useMutation, gql } from '@apollo/client'
import * as R from 'ramda'
import React, { useState } from 'react'
import Modal from 'src/components/Modal'
import CheckboxInput from 'src/components/inputs/formik/Checkbox'
import TitleSection from 'src/components/layout/TitleSection'
import SingleRowTable from 'src/components/single-row-table/SingleRowTable'
import { SecretInput } from 'src/components/inputs/formik'
import { formatLong } from 'src/utils/string'
import FormRenderer from './FormRenderer'
import _schemas from './schemas'
const GET_INFO = gql`
query getData {
accounts
config
}
`
const GET_MARKETS = gql`
query getMarkets {
getMarkets
}
`
const SAVE_ACCOUNT = gql`
mutation Save($accounts: JSONObject) {
saveAccounts(accounts: $accounts)
}
`
const Services = () => {
const [editingSchema, setEditingSchema] = useState(null)
const { data, loading: configLoading } = useQuery(GET_INFO)
const { data: marketsData, loading: marketsLoading } = useQuery(GET_MARKETS)
const [saveAccount] = useMutation(SAVE_ACCOUNT, {
onCompleted: () => setEditingSchema(null),
refetchQueries: ['getData']
})
const markets = marketsData?.getMarkets
const schemas = _schemas(markets)
const accounts = data?.accounts ?? {}
const getItems = (code, elements) => {
const faceElements = R.filter(R.prop('face'))(elements)
const values = accounts[code] || {}
return R.map(({ display, code, long }) => ({
label: display,
value: long ? formatLong(values[code]) : values[code]
}))(faceElements)
}
const updateSettings = element => {
const settings = element.settings
const field = R.lensPath(['config', settings.field])
const isEnabled = R.isNil(settings.requirement)
? true
: R.equals(R.view(field, data), settings.requirement)
settings.enabled = isEnabled
return element
}
const getElements = ({ code, elements }) => {
return R.map(elem => {
if (elem.component === CheckboxInput) return updateSettings(elem)
if (elem.component !== SecretInput) return elem
return {
...elem,
inputProps: {
isPasswordFilled:
!R.isNil(accounts[code]) &&
!R.isNil(R.path([elem.code], accounts[code]))
}
}
}, elements)
}
const getAccounts = ({ elements, code }) => {
const account = accounts[code]
const filterBySecretComponent = R.filter(R.propEq('component', SecretInput))
const mapToCode = R.map(R.prop(['code']))
const passwordFields = R.compose(
mapToCode,
filterBySecretComponent
)(elements)
return R.mapObjIndexed(
(value, key) => (R.includes(key, passwordFields) ? '' : value),
account
)
}
const getValidationSchema = ({ code, getValidationSchema }) =>
getValidationSchema(accounts[code])
const loading = marketsLoading || configLoading
return (
!loading && (
<div>
<TitleSection title="Third-Party services" />
<div className="grid grid-cols-3 gap-5">
{R.values(schemas).map(schema => (
<SingleRowTable
key={schema.code}
editMessage={'Configure ' + schema.title}
title={schema.title}
onEdit={() => setEditingSchema(schema)}
items={getItems(schema.code, schema.elements)}
/>
))}
</div>
{editingSchema && (
<Modal
title={`Edit ${editingSchema.name}`}
width={525}
handleClose={() => setEditingSchema(null)}
open={true}>
<FormRenderer
save={it =>
saveAccount({
variables: { accounts: { [editingSchema.code]: it } }
})
}
elements={getElements(editingSchema)}
validationSchema={getValidationSchema(editingSchema)}
value={getAccounts(editingSchema)}
/>
</Modal>
)}
</div>
)
)
}
export default Services

View file

@ -0,0 +1,57 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest, buildCurrencyOptions } from './helper'
const schema = markets => {
return {
code: 'binance',
name: 'Binance',
title: 'Binance (Exchange)',
elements: [
{
code: 'apiKey',
display: 'API key',
component: TextInput,
face: true,
long: true
},
{
code: 'privateKey',
display: 'Private key',
component: SecretInput
},
{
code: 'currencyMarket',
display: 'Currency market',
component: Autocomplete,
inputProps: {
options: buildCurrencyOptions(markets),
labelProp: 'display',
valueProp: 'code'
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(100, 'The API key is too long')
.required('The API key is required'),
privateKey: Yup.string('The private key must be a string')
.max(100, 'The private key is too long')
.test(secretTest(account?.privateKey, 'private key')),
currencyMarket: Yup.string(
'The currency market must be a string'
).required('The currency market is required')
})
}
}
}
export default schema

View file

@ -0,0 +1,57 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest, buildCurrencyOptions } from './helper'
const schema = markets => {
return {
code: 'binanceus',
name: 'Binance.us',
title: 'Binance.us (Exchange)',
elements: [
{
code: 'apiKey',
display: 'API key',
component: TextInput,
face: true,
long: true
},
{
code: 'privateKey',
display: 'Private key',
component: SecretInput
},
{
code: 'currencyMarket',
display: 'Currency market',
component: Autocomplete,
inputProps: {
options: buildCurrencyOptions(markets),
labelProp: 'display',
valueProp: 'code'
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(100, 'The API key is too long')
.required('The API key is required'),
privateKey: Yup.string('The private key must be a string')
.max(100, 'The private key is too long')
.test(secretTest(account?.privateKey, 'private key')),
currencyMarket: Yup.string(
'The currency market must be a string'
).required('The currency market is required')
})
}
}
}
export default schema

View file

@ -0,0 +1,57 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest, buildCurrencyOptions } from './helper'
const schema = markets => {
return {
code: 'bitfinex',
name: 'Bitfinex',
title: 'Bitfinex (Exchange)',
elements: [
{
code: 'key',
display: 'API key',
component: TextInput,
face: true,
long: true
},
{
code: 'secret',
display: 'API secret',
component: SecretInput
},
{
code: 'currencyMarket',
display: 'Currency Market',
component: Autocomplete,
inputProps: {
options: buildCurrencyOptions(markets),
labelProp: 'display',
valueProp: 'code'
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
key: Yup.string('The API key must be a string')
.max(100, 'The API key is too long')
.required('The API key is required'),
secret: Yup.string('The API secret must be a string')
.max(100, 'The API secret is too long')
.test(secretTest(account?.secret, 'API secret')),
currencyMarket: Yup.string(
'The currency market must be a string'
).required('The currency market is required')
})
}
}
}
export default schema

View file

@ -0,0 +1,149 @@
import * as Yup from 'yup'
import {
TextInput,
SecretInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest } from './helper'
const isDefined = it => it && it.length
const buildTestValidation = (id, passphrase) => {
return Yup.string()
.max(100, 'Too long')
.when(id, {
is: isDefined,
then: schema => schema.test(secretTest(passphrase))
})
}
export default {
code: 'bitgo',
name: 'BitGo',
title: 'BitGo (Wallet)',
elements: [
{
code: 'token',
display: 'API token',
component: TextInput,
face: true,
long: true
},
{
code: 'environment',
display: 'Environment',
component: Autocomplete,
inputProps: {
options: [
{ code: 'prod', display: 'prod' },
{ code: 'test', display: 'test' }
],
labelProp: 'display',
valueProp: 'code'
},
face: true
},
{
code: 'BTCWalletId',
display: 'BTC wallet ID',
component: TextInput
},
{
code: 'BTCWalletPassphrase',
display: 'BTC wallet passphrase',
component: SecretInput
},
{
code: 'LTCWalletId',
display: 'LTC wallet ID',
component: TextInput
},
{
code: 'LTCWalletPassphrase',
display: 'LTC wallet passphrase',
component: SecretInput
},
{
code: 'ZECWalletId',
display: 'ZEC wallet ID',
component: TextInput
},
{
code: 'ZECWalletPassphrase',
display: 'ZEC wallet passphrase',
component: SecretInput
},
{
code: 'BCHWalletId',
display: 'BCH wallet ID',
component: TextInput
},
{
code: 'BCHWalletPassphrase',
display: 'BCH wallet passphrase',
component: SecretInput
},
{
code: 'DASHWalletId',
display: 'DASH wallet ID',
component: TextInput
},
{
code: 'DASHWalletPassphrase',
display: 'DASH wallet passphrase',
component: SecretInput
}
],
getValidationSchema: account => {
return Yup.object().shape({
token: Yup.string('The token must be a string')
.max(100, 'The token is too long')
.required('The token is required'),
BTCWalletId: Yup.string('The BTC wallet ID must be a string').max(
100,
'The BTC wallet ID is too long'
),
BTCWalletPassphrase: buildTestValidation(
'BTCWalletId',
account?.BTCWalletPassphrase
),
LTCWalletId: Yup.string('The LTC wallet ID must be a string').max(
100,
'The LTC wallet ID is too long'
),
LTCWalletPassphrase: buildTestValidation(
'LTCWalletId',
account?.LTCWalletPassphrase
),
ZECWalletId: Yup.string('The ZEC wallet ID must be a string').max(
100,
'The ZEC wallet ID is too long'
),
ZECWalletPassphrase: buildTestValidation(
'ZECWalletId',
account?.ZECWalletPassphrase
),
BCHWalletId: Yup.string('The BCH wallet ID must be a string').max(
100,
'The BCH wallet ID is too long'
),
BCHWalletPassphrase: buildTestValidation(
'BCHWalletId',
account?.BCHWalletPassphrase
),
DASHWalletId: Yup.string('The DASH wallet ID must be a string').max(
100,
'The DASH wallet ID is too long'
),
DASHWalletPassphrase: buildTestValidation(
'DASHWalletId',
account?.DASHWalletPassphrase
),
environment: Yup.string('The environment must be a string')
.matches(/(prod|test)/)
.required('The environment is required')
});
}
}

View file

@ -0,0 +1,67 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest, buildCurrencyOptions } from './helper'
const schema = markets => {
return {
code: 'bitstamp',
name: 'Bitstamp',
title: 'Bitstamp (Exchange)',
elements: [
{
code: 'clientId',
display: 'Client ID',
component: TextInput,
face: true,
long: true
},
{
code: 'key',
display: 'API key',
component: TextInput,
face: true,
long: true
},
{
code: 'secret',
display: 'API secret',
component: SecretInput
},
{
code: 'currencyMarket',
display: 'Currency market',
component: Autocomplete,
inputProps: {
options: buildCurrencyOptions(markets),
labelProp: 'display',
valueProp: 'code'
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
clientId: Yup.string('The client ID must be a string')
.max(100, 'The client ID is too long')
.required('The client ID is required'),
key: Yup.string('The API key must be a string')
.max(100, 'The API key is too long')
.required('The API key is required'),
secret: Yup.string('The API secret must be a string')
.max(100, 'The API secret is too long')
.test(secretTest(account?.secret, 'API secret')),
currencyMarket: Yup.string(
'The currency market must be a string'
).required('The currency market is required')
})
}
}
}
export default schema

View file

@ -0,0 +1,50 @@
import * as Yup from 'yup'
import { Checkbox, TextInput, NumberInput } from 'src/components/inputs/formik'
export default {
code: 'blockcypher',
name: 'Blockcypher',
title: 'Blockcypher (Payments)',
elements: [
{
code: 'token',
display: 'API token',
component: TextInput,
face: true,
long: true
},
{
code: 'confidenceFactor',
display: 'Confidence factor',
component: NumberInput,
face: true
},
{
code: 'rbf',
component: Checkbox,
settings: {
field: 'wallets_BTC_wallet',
enabled: true,
disabledMessage:
'Lower the confidence of RBF transactions (Available when using bitcoind.)',
label: 'Lower the confidence of RBF transactions',
requirement: 'bitcoind',
rightSideLabel: true
},
face: true
}
],
getValidationSchema: () => {
return Yup.object().shape({
token: Yup.string('The token must be a string')
.max(100, 'The token is too long')
.required('The token is required'),
confidenceFactor: Yup.number('The confidence factor must be a number')
.integer('The confidence factor must be an integer')
.min(0, 'The confidence factor must be between 0 and 100')
.max(100, 'The confidence factor must be between 0 and 100')
.required('The confidence factor is required')
})
}
}

View file

@ -0,0 +1,67 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest, buildCurrencyOptions } from './helper'
const schema = markets => {
return {
code: 'cex',
name: 'CEX.IO',
title: 'CEX.IO (Exchange)',
elements: [
{
code: 'apiKey',
display: 'API key',
component: TextInput,
face: true,
long: true
},
{
code: 'uid',
display: 'User ID',
component: TextInput,
face: true,
long: true
},
{
code: 'privateKey',
display: 'Private key',
component: SecretInput
},
{
code: 'currencyMarket',
display: 'Currency Market',
component: Autocomplete,
inputProps: {
options: buildCurrencyOptions(markets),
labelProp: 'display',
valueProp: 'code'
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(100, 'The API key is too long')
.required('The API key is required'),
uid: Yup.string('The User ID must be a string')
.max(100, 'The User ID is too long')
.required('The User ID is required'),
privateKey: Yup.string('The private key must be a string')
.max(100, 'The private key is too long')
.test(secretTest(account?.privateKey, 'private key')),
currencyMarket: Yup.string(
'The currency market must be a string'
).required('The currency market is required')
})
}
}
}
export default schema

View file

@ -0,0 +1,63 @@
import CheckboxFormik from 'src/components/inputs/formik/Checkbox'
import NumberInputFormik from 'src/components/inputs/formik/NumberInput'
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import * as Yup from 'yup'
import { secretTest, leadingZerosTest } from './helper'
export default {
code: 'elliptic',
name: 'Elliptic',
title: 'Elliptic (Scoring)',
elements: [
{
code: 'apiKey',
display: 'API Key',
component: SecretInputFormik
},
{
code: 'apiSecret',
display: 'API Secret',
component: SecretInputFormik
},
{
code: 'scoreThreshold',
display: 'Score threshold',
component: NumberInputFormik,
face: true,
long: false
},
{
code: 'enabled',
component: CheckboxFormik,
settings: {
enabled: true,
disabledMessage: 'This plugin is disabled',
label: 'Enabled',
requirement: null,
rightSideLabel: true
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(100, 'Too long')
.test(secretTest(account?.apiKey, 'API key')),
apiSecret: Yup.string('The API secret must be a string')
.max(100, 'Too long')
.test(secretTest(account?.apiKey, 'API key')),
scoreThreshold: Yup.number('The score threshold must be a number')
.required('A score threshold is required')
.min(1, 'The score threshold must be between 1 and 100')
.max(100, 'The score threshold must be between 1 and 100')
.integer('The score threshold must be an integer')
.test(
'no-leading-zeros',
'The score threshold must not have leading zeros',
leadingZerosTest
)
})
}
}

View file

@ -0,0 +1,62 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest } from './helper'
export default {
code: 'galoy',
name: 'Galoy',
title: 'Galoy (Wallet)',
elements: [
{
code: 'apiSecret',
display: 'API Secret',
component: SecretInput
},
{
code: 'environment',
display: 'Environment',
component: Autocomplete,
inputProps: {
options: [
{ code: 'main', display: 'prod' },
{ code: 'test', display: 'test' }
],
labelProp: 'display',
valueProp: 'code'
},
face: true
},
{
code: 'endpoint',
display: 'Endpoint',
component: TextInput
},
{
code: 'walletId',
display: 'Wallet ID',
component: SecretInput
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiSecret: Yup.string('The API Secret must be a string')
.max(200, 'The API Secret is too long')
.test(secretTest(account?.apiSecret)),
walletId: Yup.string('The wallet id must be a string')
.max(100, 'The wallet id is too long')
.test(secretTest(account?.walletId)),
environment: Yup.string('The environment must be a string')
.matches(/(main|test)/)
.required('The environment is required'),
endpoint: Yup.string('The endpoint must be a string')
.max(100, 'The endpoint is too long')
.required('The endpoint is required')
});
}
}

View file

@ -0,0 +1,64 @@
import { ALL_CRYPTOS } from '@lamassu/coins/config/consts'
import { getEquivalentCode } from '@lamassu/coins/lightUtils'
import * as R from 'ramda'
const WARNING_LEVELS = {
CLEAN: 'clean',
PARTIAL: 'partial',
IMPORTANT: 'important'
}
const secretTest = (secret, message) => ({
name: 'secret-test',
message: message ? `The ${message} is invalid` : 'Invalid field',
test(val) {
if (R.isNil(secret) && R.isNil(val)) {
return this.createError()
}
return true
}
})
const leadingZerosTest = (value, context) => {
if (
R.startsWith('0', context.originalValue) &&
R.length(context.originalValue) > 1
) {
return context.createError()
}
return true
}
const buildCurrencyOptions = markets => {
const prunedCoins = R.compose(R.uniq, R.map(getEquivalentCode))(ALL_CRYPTOS)
return R.map(it => {
const unavailableCryptos = R.difference(prunedCoins, markets[it])
const unavailableCryptosFiltered = R.difference(unavailableCryptos, [it]) // As the markets can have stablecoins to trade against other crypto, filter them out, as there can't be pairs such as USDT/USDT
const unavailableMarketsStr =
R.length(unavailableCryptosFiltered) > 1
? `${R.join(
', ',
R.slice(0, -1, unavailableCryptosFiltered)
)} and ${R.last(unavailableCryptosFiltered)}`
: unavailableCryptosFiltered[0]
const warningLevel = R.isEmpty(unavailableCryptosFiltered)
? WARNING_LEVELS.CLEAN
: !R.isEmpty(unavailableCryptosFiltered) &&
R.length(unavailableCryptosFiltered) < R.length(prunedCoins)
? WARNING_LEVELS.PARTIAL
: WARNING_LEVELS.IMPORTANT
return {
code: R.toUpper(it),
display: R.toUpper(it),
warning: warningLevel,
warningMessage: !R.isEmpty(unavailableCryptosFiltered)
? `No market pairs available for ${unavailableMarketsStr}`
: `All market pairs are available`
}
}, R.keys(markets))
}
export { secretTest, leadingZerosTest, buildCurrencyOptions }

View file

@ -0,0 +1,55 @@
import _binance from './binance'
import _binanceus from './binanceus'
import _bitfinex from './bitfinex'
import bitgo from './bitgo'
import _bitstamp from './bitstamp'
import blockcypher from './blockcypher'
import _cex from './cex'
import elliptic from './elliptic'
import galoy from './galoy'
import inforu from './inforu'
import infura from './infura'
import _itbit from './itbit'
import _kraken from './kraken'
import mailgun from './mailgun'
import scorechain from './scorechain'
import sumsub from './sumsub'
import telnyx from './telnyx'
import trongrid from './trongrid'
import twilio from './twilio'
import vonage from './vonage'
const schemas = (markets = {}) => {
const binance = _binance(markets?.binance)
const bitfinex = _bitfinex(markets?.bitfinex)
const binanceus = _binanceus(markets?.binanceus)
const bitstamp = _bitstamp(markets?.bitstamp)
const cex = _cex(markets?.cex)
const itbit = _itbit(markets?.itbit)
const kraken = _kraken(markets?.kraken)
return {
[bitgo.code]: bitgo,
[galoy.code]: galoy,
[bitstamp.code]: bitstamp,
[blockcypher.code]: blockcypher,
[elliptic.code]: elliptic,
[inforu.code]: inforu,
[infura.code]: infura,
[itbit.code]: itbit,
[kraken.code]: kraken,
[mailgun.code]: mailgun,
[telnyx.code]: telnyx,
[vonage.code]: vonage,
[twilio.code]: twilio,
[binanceus.code]: binanceus,
[cex.code]: cex,
[scorechain.code]: scorechain,
[trongrid.code]: trongrid,
[binance.code]: binance,
[bitfinex.code]: bitfinex,
[sumsub.code]: sumsub
}
}
export default schemas

View file

@ -0,0 +1,52 @@
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput'
import * as Yup from 'yup'
import { secretTest } from './helper'
export default {
code: 'inforu',
name: 'InforU',
title: 'InforU (SMS)',
elements: [
{
code: 'username',
display: 'InforU username',
component: TextInputFormik,
face: true
},
{
code: 'apiKey',
display: 'API Key',
component: SecretInputFormik
},
{
code: 'fromNumber',
display: 'InforU sender',
component: TextInputFormik,
face: true
},
{
code: 'toNumber',
display: 'Notifications Number (international format)',
component: TextInputFormik,
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
username: Yup.string('The InforU username must be a string')
.max(100, 'The InforU username is too long')
.required('The InforU username is required'),
apiKey: Yup.string('The API key must be a string')
.max(200, 'The API key is too long')
.test(secretTest(account?.apiKey, 'API key')),
fromNumber: Yup.string('The InforU sender must be a string')
.max(11, 'The InforU sender is too long')
.required('The InforU sender is required'),
toNumber: Yup.string('The notifications number must be a string')
.max(100, 'The notifications number is too long')
.required('The notifications number is required')
})
}
}

View file

@ -0,0 +1,23 @@
import TextInputFormik from 'src/components/inputs/formik/TextInput'
import * as Yup from 'yup'
export default {
code: 'infura',
name: 'Infura/Alchemy',
title: 'Infura/Alchemy (Wallet)',
elements: [
{
code: 'endpoint',
display: 'Endpoint',
component: TextInputFormik,
face: true
}
],
getValidationSchema: () => {
return Yup.object().shape({
endpoint: Yup.string('The endpoint must be a string')
.max(100, 'The endpoint is too long')
.required('The endpoint is required')
})
}
}

View file

@ -0,0 +1,75 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { buildCurrencyOptions, secretTest } from './helper'
const schema = markets => {
return {
code: 'itbit',
name: 'itBit',
title: 'itBit (Exchange)',
elements: [
{
code: 'userId',
display: 'User ID',
component: TextInput,
face: true,
long: true
},
{
code: 'walletId',
display: 'Wallet ID',
component: TextInput,
face: true,
long: true
},
{
code: 'clientKey',
display: 'Client key',
component: TextInput
},
{
code: 'clientSecret',
display: 'Client secret',
component: SecretInput
},
{
code: 'currencyMarket',
display: 'Currency market',
component: Autocomplete,
inputProps: {
options: buildCurrencyOptions(markets),
labelProp: 'display',
valueProp: 'code'
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
userId: Yup.string('The user ID must be a string')
.max(100, 'The user ID is too long')
.required('The user ID is required'),
walletId: Yup.string('The wallet ID must be a string')
.max(100, 'The wallet ID is too long')
.required('The wallet ID is required'),
clientKey: Yup.string('The client key must be a string')
.max(100, 'The client key is too long')
.required('The client key is required'),
clientSecret: Yup.string('The client secret must be a string')
.max(100, 'The client secret is too long')
.test(secretTest(account?.clientSecret, 'client secret')),
currencyMarket: Yup.string(
'The currency market must be a string'
).required('The currency market is required')
})
}
}
}
export default schema

View file

@ -0,0 +1,57 @@
import * as Yup from 'yup'
import {
SecretInput,
TextInput,
Autocomplete
} from 'src/components/inputs/formik'
import { secretTest, buildCurrencyOptions } from './helper'
const schema = markets => {
return {
code: 'kraken',
name: 'Kraken',
title: 'Kraken (Exchange)',
elements: [
{
code: 'apiKey',
display: 'API key',
component: TextInput,
face: true,
long: true
},
{
code: 'privateKey',
display: 'Private key',
component: SecretInput
},
{
code: 'currencyMarket',
display: 'Currency market',
component: Autocomplete,
inputProps: {
options: buildCurrencyOptions(markets),
labelProp: 'display',
valueProp: 'code'
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(100, 'The API key is too long')
.required('The API key is required'),
privateKey: Yup.string('The private key must be a string')
.max(100, 'The private key is too long')
.test(secretTest(account?.privateKey, 'private key')),
currencyMarket: Yup.string(
'The currency market must be a string'
).required('The currency market is required')
})
}
}
}
export default schema

View file

@ -0,0 +1,50 @@
import TextInputFormik from 'src/components/inputs/formik/TextInput'
import * as Yup from 'yup'
export default {
code: 'mailgun',
name: 'Mailgun',
title: 'Mailgun (Email)',
elements: [
{
code: 'apiKey',
display: 'API key',
component: TextInputFormik
},
{
code: 'domain',
display: 'Domain',
component: TextInputFormik
},
{
code: 'fromEmail',
display: 'From email',
component: TextInputFormik,
face: true
},
{
code: 'toEmail',
display: 'To email',
component: TextInputFormik,
face: true
}
],
getValidationSchema: () => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(100, 'The API key is too long')
.required('The API key is required'),
domain: Yup.string('The domain must be a string')
.max(100, 'The domain is too long')
.required('The domain is required'),
fromEmail: Yup.string('The from email must be a string')
.max(100, 'The from email is too long')
.email('The from email must be a valid email address')
.required('The from email is required'),
toEmail: Yup.string('The to email must be a string')
.max(100, 'The to email is too long')
.email('The to email must be a valid email address')
.required('The to email is required')
})
}
}

View file

@ -0,0 +1,55 @@
import CheckboxFormik from 'src/components/inputs/formik/Checkbox'
import NumberInputFormik from 'src/components/inputs/formik/NumberInput'
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import * as Yup from 'yup'
import { secretTest, leadingZerosTest } from './helper'
export default {
code: 'scorechain',
name: 'Scorechain',
title: 'Scorechain (Scoring)',
elements: [
{
code: 'apiKey',
display: 'API Key',
component: SecretInputFormik
},
{
code: 'scoreThreshold',
display: 'Score threshold',
component: NumberInputFormik,
face: true,
long: false
},
{
code: 'enabled',
component: CheckboxFormik,
settings: {
enabled: true,
disabledMessage: 'This plugin is disabled',
label: 'Enabled',
requirement: null,
rightSideLabel: true
},
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(100, 'Too long')
.test(secretTest(account?.apiKey, 'API key')),
scoreThreshold: Yup.number('The score threshold must be a number')
.required('A score threshold is required')
.min(1, 'The score threshold must be between 1 and 100')
.max(100, 'The score threshold must be between 1 and 100')
.integer('The score threshold must be an integer')
.test(
'no-leading-zeros',
'The score threshold must not have leading zeros',
leadingZerosTest
)
})
}
}

View file

@ -0,0 +1,64 @@
import * as Yup from 'yup'
import {
TextInput,
SecretInput,
Autocomplete
} from 'src/components/inputs/formik'
const singleBitgo = code => ({
code: 'bitgo',
name: 'BitGo',
title: 'BitGo (Wallet)',
elements: [
{
code: 'token',
display: 'API token',
component: TextInput,
face: true,
long: true
},
{
code: 'environment',
display: 'Environment',
component: Autocomplete,
inputProps: {
options: [
{ code: 'prod', display: 'prod' },
{ code: 'test', display: 'test' }
],
labelProp: 'display',
valueProp: 'code'
},
face: true
},
{
code: `${code}WalletId`,
display: `${code} wallet ID`,
component: TextInput
},
{
code: `${code}WalletPassphrase`,
display: `${code} wallet passphrase`,
component: SecretInput
}
],
validationSchema: Yup.object().shape({
token: Yup.string('The token must be a string')
.max(100, 'The token is too long')
.required('The token is required'),
environment: Yup.string('The environment must be a string')
.matches(/(prod|test)/)
.required('The environment is required'),
[`${code}WalletId`]: Yup.string(`The ${code} wallet ID must be a string`)
.max(100, `The ${code} wallet ID is too long`)
.required(`The ${code} wallet ID is required`),
[`${code}WalletPassphrase`]: Yup.string(
`The ${code} passphrase must be a string`
)
.max(100, `The ${code} wallet passphrase is too long`)
.required(`The ${code} wallet passphrase is required`)
})
})
export default singleBitgo

View file

@ -0,0 +1,44 @@
import * as Yup from 'yup'
import { SecretInput, TextInput } from 'src/components/inputs/formik'
import { secretTest } from './helper'
const schema = {
code: 'sumsub',
name: 'Sumsub',
title: 'Sumsub (Compliance)',
elements: [
{
code: 'apiToken',
display: 'API Token',
component: SecretInput
},
{
code: 'secretKey',
display: 'Secret Key',
component: SecretInput
},
{
code: 'applicantLevel',
display: 'Applicant Level',
component: TextInput,
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiToken: Yup.string('The API token must be a string')
.max(100, 'The API token is too long')
.test(secretTest(account?.apiToken, 'API token')),
secretKey: Yup.string('The secret key must be a string')
.max(100, 'The secret key is too long')
.test(secretTest(account?.secretKey, 'secret key')),
applicantLevel: Yup.string('The applicant level must be a string')
.max(100, 'The applicant level is too long')
.required('The applicant level is required')
})
}
}
export default schema

View file

@ -0,0 +1,43 @@
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput'
import * as Yup from 'yup'
import { secretTest } from './helper'
export default {
code: 'telnyx',
name: 'Telnyx',
title: 'Telnyx (SMS)',
elements: [
{
code: 'apiKey',
display: 'API Key',
component: SecretInputFormik
},
{
code: 'fromNumber',
display: 'Telnyx Number (international format)',
component: TextInputFormik,
face: true
},
{
code: 'toNumber',
display: 'Notifications Number (international format)',
component: TextInputFormik,
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(200, 'The API key is too long')
.test(secretTest(account?.apiKey, 'API key')),
fromNumber: Yup.string('The Telnyx number must be a string')
.max(100, 'The Telnyx number is too long')
.required('The Telnyx number is required'),
toNumber: Yup.string('The notifications number must be a string')
.max(100, 'The notifications number is too long')
.required('The notifications number is required')
})
}
}

View file

@ -0,0 +1,24 @@
import TextInputFormik from 'src/components/inputs/formik/TextInput'
import * as Yup from 'yup'
export default {
code: 'trongrid',
name: 'Trongrid',
title: 'Trongrid (Wallet)',
elements: [
{
code: 'apiKey',
display: 'API Key',
component: TextInputFormik,
face: true,
long: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The project ID must be a string')
.max(100, 'The project ID is too long')
.required('The project ID is required')
})
}
}

View file

@ -0,0 +1,51 @@
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput'
import * as Yup from 'yup'
import { secretTest } from './helper'
export default {
code: 'twilio',
name: 'Twilio',
title: 'Twilio (SMS)',
elements: [
{
code: 'accountSid',
display: 'Account SID',
component: TextInputFormik
},
{
code: 'authToken',
display: 'Auth token',
component: SecretInputFormik
},
{
code: 'fromNumber',
display: 'Twilio number (international format)',
component: TextInputFormik,
face: true
},
{
code: 'toNumber',
display: 'Notifications number (international format)',
component: TextInputFormik,
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
accountSid: Yup.string('The account SID must be a string')
.max(100, 'The account SID is too long')
.required('The account SID is required'),
authToken: Yup.string('The auth token must be a string')
.max(100, 'The auth token is too long')
.test(secretTest(account?.authToken, 'auth token')),
fromNumber: Yup.string('The Twilio number must be a string')
.max(100, 'The Twilio number is too long')
.required('The Twilio number is required'),
toNumber: Yup.string('The notifications number must be a string')
.max(100, 'The notifications number is too long')
.required('The notifications number is required')
})
}
}

View file

@ -0,0 +1,51 @@
import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import TextInputFormik from 'src/components/inputs/formik/TextInput'
import * as Yup from 'yup'
import { secretTest } from './helper'
export default {
code: 'vonage',
name: 'Vonage',
title: 'Vonage (SMS)',
elements: [
{
code: 'apiKey',
display: 'API Key',
component: TextInputFormik
},
{
code: 'apiSecret',
display: 'API Secret',
component: SecretInputFormik
},
{
code: 'fromNumber',
display: 'Vonage Number (international format)',
component: TextInputFormik,
face: true
},
{
code: 'toNumber',
display: 'Notifications Number (international format)',
component: TextInputFormik,
face: true
}
],
getValidationSchema: account => {
return Yup.object().shape({
apiKey: Yup.string('The API key must be a string')
.max(200, 'The API key is too long')
.required('The Vonage number is required'),
apiSecret: Yup.string('The API key must be a string')
.max(200, 'The API secret is too long')
.test(secretTest(account?.apiKey, 'API secret')),
fromNumber: Yup.string('The Vonage number must be a string')
.max(100, 'The Vonage number is too long')
.required('The Vonage number is required'),
toNumber: Yup.string('The notifications number must be a string')
.max(100, 'The notifications number is too long')
.required('The notifications number is required')
})
}
}