diff --git a/config/default.json b/config/default.json index dd9fffe..ab880c9 100644 --- a/config/default.json +++ b/config/default.json @@ -13,7 +13,8 @@ "add_to_fee": "1.000 GOLOS", "signing_key": "5K67PNheLkmxkgJ5UjvR8Nyt3GVPoLEN1dMZjFuNETzrNyMecPG", "delegation": "75000.000000 GESTS", - "free_regs_per_day": 10 + "free_regs_per_day": 10, + "uias": ["YMUSDT", "YMPZM", "YMHIVE"] }, "server_session_secret": "exiKdyF+IwRIXJDmtGIl4vWUz4i3eVSISpfZoeYc0s4=", "session_cookie_key": "X-Reg-ISession", diff --git a/package.json b/package.json index 4ea011d..fd1002f 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "passport-yandex": "^0.0.5", "querystring": "^0.2.1", "react": "^17.0.2", + "react-copy-to-clipboard": "^5.1.0", "react-dom": "^17.0.2", "react-foundation-components": "git+https://github.com/golos-blockchain/react-foundation-components.git#5dbfb800aff45988c57bb7d09c1c235a8b49b418", "react-google-recaptcha": "^2.1.0", diff --git a/src/App.scss b/src/App.scss index 43ac1a1..74dd489 100644 --- a/src/App.scss +++ b/src/App.scss @@ -14,6 +14,7 @@ @import "./modules/LoginForm"; @import "./modules/PendingTx"; @import "./modules/PermissionsList"; +@import "./modules/register/UIARegister"; @import "./pages/_common"; @import "./pages/index"; diff --git a/src/elements/register/VerifyWayTabs.jsx b/src/elements/register/VerifyWayTabs.jsx index 7823c0f..9983c68 100644 --- a/src/elements/register/VerifyWayTabs.jsx +++ b/src/elements/register/VerifyWayTabs.jsx @@ -42,12 +42,21 @@ class VerifyWayTabs extends React.Component { transfer = {transfer} } + let uia = tt('verify_way_tabs_jsx.with_uia') + if (currentWay === 'uia') { + uia = {uia} + } else { + uia = {uia} + } + return
{social}  |  {invite}  |  {transfer} +  |  + {uia}
} } diff --git a/src/locales/en.json b/src/locales/en.json index 2696007..20572ff 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -93,6 +93,18 @@ "cannot_reset": "Unfortunately, something went wrong. We cannot set your password. But everything is under control - it is your temporary key. Save it, and contact us: ", "unload_warning": "Do you saved the key? Or you want to cancel registration? If transfer already sent, there are no refund possible." }, + "uia_register_jsx": { + "select_uia": "Choose cryptocurrencу", + "register_with": "Register with...", + "no_such_asset": " - no such UIA.", + "deposit_unavailable": " - deposit temporarily unavailable.", + "min_amount": "Min amount", + "fee": "Fee", + "memo_fixed": "Memo", + "to": "Send tokens to address/account", + "api_error": "Cannot get address. Try again later. If problem still occurs, contact the issuer of ", + "api_error_details": "and send the error details:" + }, "invites_jsx": { "claim_wrong_secret": "Wrong secret", "claim_wrong_secret_fatal": "No such invite check", @@ -239,8 +251,7 @@ "worker_request_vote": "Voting for worker requests", "proposal_create": "Creating transaction proposals", "proposal_delete": "Removing transaction proposals", - "proposal_update": "Voting in transaction proposals", - "proposal_update_active": "Voting in transaction proposals with active authorities" + "proposal_update": "Voting in transaction proposals" }, "recovery": { "change_title": "Change recovery account", @@ -311,7 +322,8 @@ "verify_way_tabs_jsx": { "social": "Social", "invite_code": "Invite-code", - "transfer": "Transfer" + "transfer": "Transfer", + "with_uia": "With cryptocurrencies" }, "g": { "APP_NAME": "Golos", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index 7e3383d..2b771f2 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -93,6 +93,18 @@ "cannot_reset": "К сожалению, случилась какая-то ошибка и мы не смогли установить пароль. Но все под контролем - вот ваш временный ключ. Сохраните его и обратитесь к нам за помощью: ", "unload_warning": "Вы точно сохранили ключ, или вы хотите отменить регистрацию? Если перевод уже сделан, то автоматического возврата средств не будет." }, + "uia_register_jsx": { + "select_uia": "Выберите криптовалюту", + "register_with": "Регистрация с помощью...", + "no_such_asset": " - такого UIA не существует.", + "deposit_unavailable": " - депозит временно недоступен.", + "min_amount": "Минимальная сумма", + "fee": "Комиссия", + "memo_fixed": "Заметка/memo", + "to": "Отправьте токены на адрес/аккаунт", + "api_error": "Не удается получить адрес. Попробуйте позднее. Если проблема сохраняется, свяжитесь с эмитентом ", + "api_error_details": "и сообщите подробности ошибки:" + }, "invites_jsx": { "claim_wrong_secret": "Неверно указан ключ", "claim_wrong_secret_fatal": "Такого чека нет", @@ -239,8 +251,7 @@ "worker_request_vote": "Голосование за заявки на работу (воркеры)", "proposal_create": "Создание пропозалов", "proposal_delete": "Удаление пропозалов", - "proposal_update": "Голосование в пропозалах", - "proposal_update_active": "Голосование в пропозалах с активным ключом" + "proposal_update": "Голосование в пропозалах" }, "recovery": { "change_title": "Задать аккаунт для восстановления", @@ -311,7 +322,8 @@ "verify_way_tabs_jsx": { "social": "Соцсети", "invite_code": "Инвайт-код", - "transfer": "Перевод с биржи" + "transfer": "Перевод с биржи", + "with_uia": "С помощью криптовалюты" }, "g": { "APP_NAME": "Голос", diff --git a/src/modules/register/TransferWaiter.jsx b/src/modules/register/TransferWaiter.jsx new file mode 100644 index 0000000..1804d00 --- /dev/null +++ b/src/modules/register/TransferWaiter.jsx @@ -0,0 +1,100 @@ +import React from 'react' +import tt from 'counterpart' +import { Asset } from 'golos-lib-js/lib/utils'; + +import LoadingIndicator from '@/elements/LoadingIndicator' +import { callApi, } from '@/utils/RegApiClient' + +class TransferWaiter extends React.Component { + state = { + } + + constructor(props) { + super(props) + } + + poll = async (sym) => { + const retry = async () => { + await new Promise(resolve => setTimeout(resolve, 1000)) + if (!this.state.stopped) + this.poll(sym) + } + try { + let res = await callApi('/api/reg/wait_for_transfer/' + sym) + res = await res.json() + if (res.status === 'ok') { + const { onTransfer } = this.props + onTransfer(Asset(res.delta)) + } else { + console.error(res) + await retry() + } + } catch (err) { + console.error('TransferWaiter', err) + await retry() + } + } + + start = async () => { + this.setState({ + seconds: 30*60, + stopped: false + }) + + this.countdown = setInterval(() => { + const { seconds } = this.state + if (seconds === 0) { + console.log('Countdown reached, stop.') + this.stop() + return + } + this.setState({ + seconds: seconds - 1 + }) + }, 1000) + + const { sym, } = this.props + + this.poll(sym) + } + + componentDidMount() { + this.start() + } + + stop = () => { + if (this.countdown) clearInterval(this.countdown) + this.setState({ + stopped: true + }) + } + + componentWillUnmount() { + this.stop() + } + + componentDidUpdate(prevProps) { + if (this.props.sym !== prevProps.sym) { + this.stop() + this.start() + } + } + + render() { + const { seconds } = this.state + if (!seconds) return null + const min = Math.floor(seconds / 60) + const sec = seconds % 60 + const remaining = min.toString().padStart(2, '0') + ':' + sec.toString().padStart(2, '0') + const { sym, title } = this.props + return
+ {title} +
+ + {remaining} +
+
+ } +} + +export default TransferWaiter diff --git a/src/modules/register/UIARegister.jsx b/src/modules/register/UIARegister.jsx new file mode 100644 index 0000000..e702cbb --- /dev/null +++ b/src/modules/register/UIARegister.jsx @@ -0,0 +1,517 @@ +import React from 'react' +import CopyToClipboard from 'react-copy-to-clipboard' +import tt from 'counterpart' +import cn from 'classnames' +import golos, { api, broadcast } from 'golos-lib-js' +import { Asset } from 'golos-lib-js/lib/utils'; +import { key_utils, PrivateKey } from 'golos-lib-js/lib/auth/ecc' +import Link from 'next/link' + +import LoadingIndicator from '@/elements/LoadingIndicator' +import AccountName from '@/elements/register/AccountName' +import VerifyWayTabs from '@/elements/register/VerifyWayTabs' +import TransferWaiter from '@/modules/register/TransferWaiter' +import KeyFile from '@/utils/KeyFile' +import { emptyAuthority } from '@/utils/RecoveryUtils' +import { callApi, } from '@/utils/RegApiClient' +import { withRouterHelpers, } from '@/utils/routing' + +function getAssetMeta(asset) { + let sym + try { + sym = asset.supply && asset.supply.split(' ')[1] + } catch (err) { + console.warn(err) + } + let res = {} + try { + let obj = JSON.parse(asset.json_metadata) + if (typeof(obj) === 'object' && obj && !Array.isArray(obj)) { + res = obj + } + } catch (err) { + } + if (sym === 'GOLOS') { + res.image_url = '/images/golos.png' + } else if (sym === 'GBG') { + res.image_url = '/images/gold-golos.png' + } + return res +} + +const TransferState = { + initial: 0, + transferring: 1, + waiting: 2, + received: 3, + timeouted: 4, +}; + +class APIError extends Error { + constructor(errReason, errData) { + super('API Error') + this.reason = errReason + this.data = errData + } +} + +class UIARegister extends React.Component { + state = { + loading: true, + error: '', + } + + async componentDidMount() { + const { clientCfg } = this.props + + golos.config.set('websocket', clientCfg.config.ws_connection_client) + if (clientCfg.config.chain_id) + golos.config.set('chain_id', clientCfg.config.chain_id) + + const { uias } = clientCfg.config.registrar + + const path = this.getPath() + if (path[1]) { + const params = new URLSearchParams(path[1]) + const sym = params.get('uia') + if (sym) { + let assets + assets = await golos.api.getAssetsAsync('', uias) + + let error + if (!uias.includes(sym)) { + error = sym + tt('uia_register_jsx.no_such_asset') + } else { + for (const asset of assets) { + const symbol = Asset(asset.supply).symbol + if (sym === symbol) { + const meta = getAssetMeta(asset) + const { deposit, telegram } = meta + if (deposit.unavailable) { + error = sym + tt('uia_register_jsx.deposit_unavailable') + break + } + + const accName = clientCfg.config.registrar.account + let registrar = await golos.api.getAccounts([accName]) + registrar = registrar[0] + + const { to_type, to_api, } = deposit + if (to_type === 'transfer') { + /*clearOldAddresses(); + const addr = loadAddress(sym, asset.creator); + if (addr) { + this.setState({ + transferState: TransferState.received, + receivedTransfer: { + memo: addr, + }, + }); + }*/ + } + + this.setState({ + transferState: TransferState.initial, + rules: { ...deposit, creator: asset.creator, telegram }, + registrar, + sym, + copied_addr: false, + copied_memo: false, + }, () => { + if (to_type === 'api') { + this.doAPI() + } + }) + break + } + } + } + + this.setState({ + loading: false, + assets, + sym, + error + }) + return + } + } + + let assets = [] + if (uias.length) { + assets = await golos.api.getAssetsAsync('', uias) + } + this.setState({ + loading: false, + assets, + error: null + }) + } + + getPath = () => { + const { router } = this.props + let path = (router.asPath.split('#')[0]) + return path.split('?') + } + + doReq = async (acc, sym) => { + const url = '/api/reg/uia_address/' + sym + '/' + acc + let res = await callApi(url) + res = await res.json() + return res + } + + async doAPI() { + const { clientCfg } = this.props + + const { sym, registrar, } = this.state + + try { + const acc = registrar.name + let retried = 0 + const retryReq = async () => { + let res = await this.doReq(acc, sym) + if (res.status === 'err') { + if (retried < 3 && + (res.error === 'too_many_requests' + || res.error === 'cannot_connect_gateway')) { + console.error('Repeating /uia_address', res) + ++retried + await new Promise(resolve => setTimeout(resolve, 1100)) + await retryReq() + return + } + throw new APIError(res.error, res.error_data) + } + + this.setState({ + apiLoaded: { + address: res.address + } + }) + } + await retryReq() + } catch (err) { + console.error('/uia_address', err) + if (err instanceof APIError) { + this.setState({ + apiLoaded: { + error: err.reason, + errData: err.data + } + }) + } else { + this.setState({ + apiLoaded: { + error: 'error_on_golos_blockchain_side', + } + }) + } + } + } + + balanceValue = () => { + const { registrar, } = this.state + if (registrar) { + return registrar.balance + } + return '0.000 GOLOS' + } + + enoughBalance = () => { + return Asset(this.balanceValue()).gte(Asset('0.001 GOLOS')); + } + + transfer = async () => { + this.setState({ + transferState: TransferState.transferring, + }, () => { + this.transferAndWait() + }) + } + + waitingTimeout = (10 + 1) * 60 * 1000 + + transferAndWait = async () => { + const { sym, rules, registrar } = this.state + const { to_transfer, memo_transfer, } = rules + let stopper + let stopStream = api.streamOperations((err, op) => { + if (op[0] === 'transfer' && op[1].from === to_transfer + && op[1].to === registrar.name) { + stopStream(); + clearTimeout(stopper); + saveAddress(sym, rules.creator, op[1].memo); + this.setState({ + transferState: TransferState.received, + receivedTransfer: op[1], + }); + } + }) + + try { + const res = await broadcast.transferAsync(registrar.name, to_transfer, '0.001 GOLOS', memo_transfer) + } catch (err) { + console.error(err) + this.setState({ + transferState: TransferState.initial, + }) + stopStream() + return + } + + this.setState({ + transferState: TransferState.waiting, + }); + stopper = setTimeout(() => { + if (stopStream) stopStream(); + this.setState({ + transferState: TransferState.timeouted, + }) + }, this.waitingTimeout) + } + + _renderTo = (to, to_fixed, username) => { + let addr = to || to_fixed; + if (username) + addr = {addr} + return addr ?
+ {tt('uia_register_jsx.to')}
+ + {addr} + + this.setState({copied_addr: true})}> + + + {this.state.copied_addr ? : null} + + +
+
: null; + } + + _renderParams = () => { + const { rules, sym, registrar } = this.state + const username = registrar.name + const { min_amount, fee, memo_fixed } = rules + let details = rules.details + if (memo_fixed) { + details = details.split('').join(username) + } + return
+
+ {details &&
+ {details} +

} + {min_amount &&
+ {tt('uia_register_jsx.min_amount')} {min_amount} {sym || ''}
} + {fee &&
+ {tt('uia_register_jsx.fee') + ': '}{fee} {sym || ''}
} +
; + } + + _renderApi = () => { + const { sym, apiLoaded } = this.state + if (!apiLoaded) { + return (
+
+
+ +
+
+
); + } + if (apiLoaded.error) { + const { rules } = this.state + let { creator, telegram } = rules + if (telegram) { + telegram = 'https://t.me/' + encodeURIComponent(telegram) + telegram = + + + } + return (
+ {tt('uia_register_jsx.api_error') + sym + ':'} +

+ {creator}{telegram} +

+ {tt('uia_register_jsx.api_error_details')} +
+                    {apiLoaded.error}
+                    {'\n'}
+                    {apiLoaded.errData ? JSON.stringify(apiLoaded.errData) : null}
+                
+
) + } + const { address } = apiLoaded + return (
+ {this._renderTo(address, null)} + {this._renderParams(false)} + {this._renderWaiter()} +
) + } + + _renderTransfer = () => { + const { rules, sym, transferState, receivedTransfer, } = this.state + const { to_transfer, memo_transfer, } = rules + + const transferring = transferState === TransferState.transferring + + const enough = this.enoughBalance() + + if (transferState === TransferState.received) { + const { registrar, } = this.state + const { memo, } = receivedTransfer; + return (
+ {this._renderTo(receivedTransfer.memo, null, registrar.name)} + {this._renderParams(false)} +
); + } + + if (transferState === TransferState.timeouted) { + return (
+ {tt('asset_edit_deposit_jsx.timeouted')} + {sym || ''} + . +
); + } + + if (transferState === TransferState.waiting) { + return (
+ {tt('asset_edit_deposit_jsx.waiting')} +
+
+
+ +
+
+
); + } + + return (
+ {tt('uia_register_jsx.transfer_desc')} + {to_transfer || ''} + {tt('uia_register_jsx.transfer_desc_2')} + {memo_transfer || ''} + {transferring ? + : null} + + {!enough ?
+ {tt('transfer_jsx.insufficient_funds')} +
: null} + {this._renderParams()} +
); + } + + _renderWaiter = () => { + const { sym, registrar, onTransfer } = this.state + if (!onTransfer) { + onTransfer = (delta) => { + this.setState({ + deposited: delta + }) + } + } + return + } + + render() { + let content + + const { loading, error } = this.state + + if (loading) { + content = + } else { + const { assets, sym } = this.state + + const path = this.getPath()[0] + + let syms = [] + for (const asset of assets) { + const meta = getAssetMeta(asset) + if (meta.deposit) { + const symbol = Asset(asset.supply).symbol + syms.push( + + {symbol} + + ) + } + } + + let form + if (error) { + form =
{error}
+ } else if (sym) { + const { deposited } = this.state + if (deposited) { + form =
+
+ {!embed ?

+ {tt('asset_edit_deposit_jsx.transfer_title_SYM', { + SYM: sym || ' ', + })} +

: null} +

+ {tt('asset_edit_deposit_jsx.you_received')} + {deposited.toString()}. {tt('asset_edit_deposit_jsx.you_received2')} +
+
+ } else { + const { rules, registrar, } = this.state + const { to, to_type, to_fixed, to_transfer, + min_amount, fee, details, } = rules + if (to_type === 'api') { + form = this._renderApi() + } else if (to_type === 'transfer') { + form = this._renderTransfer() + } else { + let memo_fixed = rules.memo_fixed + if (memo_fixed) { + const username = registrar.name + memo_fixed = memo_fixed.split('').join(username) + } + form =
+ {this._renderTo(to, to_fixed)} + {memo_fixed ?
+ {tt('uia_register_jsx.memo_fixed')}:
+ + {memo_fixed} + + this.setState({copied_memo: true})}> + + {this.state.copied_memo ? : null} + + +
+
: null} + {this._renderParams()} + {this._renderWaiter()} +
+ } + } + } + + content =
+ {this.state.sym ?

{tt('uia_register_jsx.register_with')}

: +

{tt('uia_register_jsx.select_uia')}

} + {syms} + {this.state.sym &&
} + {form} +
+ } + + return
+ + {content} +
+ } +} + +export default withRouterHelpers(UIARegister) diff --git a/src/modules/register/UIARegister.scss b/src/modules/register/UIARegister.scss new file mode 100644 index 0000000..cd625ba --- /dev/null +++ b/src/modules/register/UIARegister.scss @@ -0,0 +1,27 @@ +.UIARegister { + .uia { + display: inline-block; + border: 1px solid rgba(128,128,128,0.45); + border-radius: 5px; + margin-right: 0.5rem; + margin-bottom: 0.5rem; + padding: 0.5rem; + cursor: pointer; + + &.selected { + background-color: rgba(208,208,208,0.45); + border: 1px solid rgba(128,128,128,0.45); + } + + img { + width: 28px; + height: 28px; + margin-right: 0.5rem; + } + } + + .uia:hover { + background-color: rgba(208,208,208,0.45); + border: 1px solid rgba(128,128,128,0.45); + } +} diff --git a/src/pages/api/reg/[...all].js b/src/pages/api/reg/[...all].js index 61ba419..2447f4c 100644 --- a/src/pages/api/reg/[...all].js +++ b/src/pages/api/reg/[...all].js @@ -1,7 +1,8 @@ import config from 'config'; import gmailSend from 'gmail-send'; -import golos from 'golos-lib-js'; +import golos, { api } from 'golos-lib-js' import { hash, } from 'golos-lib-js/lib/auth/ecc'; +import { Asset } from 'golos-lib-js/lib/utils' import secureRandom from 'secure-random'; import nextConnect from '@/server/nextConnect'; import { throwErr, } from '@/server/error'; @@ -15,6 +16,8 @@ import Tarantool from '@/server/tarantool'; initGolos(); +global.pollIntervals = {} + let handler = nextConnect({ attachParams: true, }) .use(regSessionMiddleware) .use(passport.initialize()) @@ -304,6 +307,65 @@ let handler = nextConnect({ attachParams: true, }) }); }) + .get('/api/reg/wait_for_transfer/:sym', async (req, res) => { + const { sym } = req.params + + if (global.pollIntervals[sym]) throwErr(req, 400, ['Someone already waits for the transfer']) + + const username = config.get('registrar.account') + if (!username) throwErr(req, 400, ['No registrar.account in config']) + + const getBalance = async () => { + const balances = await api.getAccountsBalancesAsync([username], { + symbols: [sym] + }) + let bal = balances[0][sym] + if (bal) { + bal = bal.balance + return Asset(bal) + } else { + const assets = await api.getAssetsAsync('', [sym]) + if (!assets[0]) throwErr(req, 400, ['No such asset']) + bal = Asset(assets[0].supply) + bal.amount = 0 + return bal + } + } + + const stop = () => { + if (global.pollIntervals[sym]) { + clearInterval(global.pollIntervals[sym].interval) + delete global.pollIntervals[sym] + } + } + + const initBal = await getBalance() + const pollMsec = process.env.NODE_ENV === 'development' ? 1000 : 30000 + let tries = 0 + global.pollIntervals[sym] = { created: Date.now(), interval: setInterval(async () => { + if (tries > 2) { + stop() + res.json({ + status: 'err', + error: 'Timeouted' + }) + return + } + ++tries + + const bal = await getBalance() + console.log('wait_for_transfer', initBal.toString(), bal.toString()) + if (bal.amount > initBal.amount) { + stop() + const delta = Asset(bal.amount - initBal.amount, bal.precision, bal.symbol) + res.json({ + status: 'ok', + delta: delta.toString() + }) + } + }, pollMsec) } + }) + handler = addModalRoutes(handler); export default handler; diff --git a/src/pages/api/reg/uia_address/[symbol]/[account].js b/src/pages/api/reg/uia_address/[symbol]/[account].js new file mode 100644 index 0000000..d1b1b5a --- /dev/null +++ b/src/pages/api/reg/uia_address/[symbol]/[account].js @@ -0,0 +1,31 @@ +import nextConnect from '@/server/nextConnect' + +import getUIAAddress from '@/server/getUIAAddress' + +let handler = nextConnect({ attachParams: true, }) + .get('/api/reg/uia_address/:symbol/:account', async (req, res) => { + const { symbol, account, } = req.params; + + const errResp = (errorName, logData, errorData) => { + let logErr = logData + if (!Array.isArray(logErr)) { + logErr = [logErr] + } + console.error('/uia_address', errorName, symbol, ...logErr) + res.json({ + status: 'err', + error: errorName, + symbol, + error_data: errorData, + }) + } + + await getUIAAddress(account, symbol, (address) => { + res.json({ + status: 'ok', + address, + }) + }, errResp) + }) + +export default handler diff --git a/src/pages/register/[[...client]].jsx b/src/pages/register/[[...client]].jsx index 2a0bebe..0f8b07f 100644 --- a/src/pages/register/[[...client]].jsx +++ b/src/pages/register/[[...client]].jsx @@ -16,6 +16,7 @@ import VerifyWayTabs from '@/elements/register/VerifyWayTabs' import Tooltip from '@/elements/Tooltip'; import Header from '@/modules/Header'; import TransferRegister from '@/modules/register/TransferRegister' +import UIARegister from '@/modules/register/UIARegister' import { obtainUid, getClientCfg, getDailyLimit, } from '@/server/reg'; import { initRegSession, } from '@/server/regSession'; import { withSecureHeadersSSR, } from '@/server/security'; @@ -92,6 +93,12 @@ class Register extends React.Component { this.setState({ verificationWay, }) + } else if (params.has('uia')) { + const verificationWay = 'uia' + if (this.state.verificationWay !== verificationWay) + this.setState({ + verificationWay, + }) } else { const verificationWay = 'social' if (!this.state.verificationWay.startsWith(verificationWay)) @@ -397,6 +404,11 @@ class Register extends React.Component { } + } else if (state.verificationWay === 'uia') { + form = } form = form || (
')) + return errResp('url_template_not_contains_place_for_account_name', [meta]) + apiURL = apiURL.replace(//g, accName) + + let resp + try { + resp = await fetchEx(apiURL, { timeout: 10000 }) + } catch (err) { + return errResp('cannot_connect_gateway', [meta.deposit, err]) + } + try { + resp = await resp.text() + } catch (err) { + return errResp('cannot_get_address_from_gateway', [meta.deposit, err]) + } + try { + resp = JSON.parse(resp) + } catch (err) { + resp = resp.substring(0, 100) + return errResp('invalid_json_from_gateway', [meta.deposit, err], resp) + } + + if (!resp.address) + return errResp('no_address_field_in_response', [resp], resp) + + return okResp(resp.address) + } catch (err) { + return errResp('internal_error', err) + } +} diff --git a/src/server/reg.js b/src/server/reg.js index b2e9863..698d418 100644 --- a/src/server/reg.js +++ b/src/server/reg.js @@ -130,6 +130,10 @@ export function getClientCfg(req, params, locale = '') { cfg.fake_emails_allowed = config.has('fake_emails_allowed') && config.get('fake_emails_allowed'); + cfg.registrar = config.has('registrar') ? config.get('registrar') : {} + if (!cfg.registrar.uias) + cfg.registrar.uias = [] + let data = { config: cfg, oauthEnabled: config.has('oauth'), diff --git a/yarn.lock b/yarn.lock index 92846be..ca30db4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1688,6 +1688,13 @@ cookie@^0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== +copy-to-clipboard@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + core-js-pure@^3.16.0: version "3.16.4" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.16.4.tgz#8b23122628d88c560f209812b9b2d9ebbce5e29c" @@ -4623,7 +4630,7 @@ prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" -prop-types@^15.6.2: +prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -4759,6 +4766,14 @@ react-async-script@^1.1.1: hoist-non-react-statics "^3.3.0" prop-types "^15.5.0" +react-copy-to-clipboard@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz#09aae5ec4c62750ccb2e6421a58725eabc41255c" + integrity sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A== + dependencies: + copy-to-clipboard "^3.3.1" + prop-types "^15.8.1" + react-dom@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" @@ -5674,6 +5689,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"