From b06e07b0f82ce8471c35206014b36f5bdb87e175 Mon Sep 17 00:00:00 2001 From: Nabarun Date: Sat, 27 Jul 2024 13:07:17 +0000 Subject: [PATCH] Add CLI command to get applicant details (#2) Part of [laconicd testnet validator enrollment](https://www.notion.so/laconicd-testnet-validator-enrollment-6fc1d3cafcc64fef8c5ed3affa27c675) Co-authored-by: IshaVenikar Reviewed-on: https://git.vdb.to/cerc-io/testnet-onboarding-api/pulls/2 Co-authored-by: Nabarun Co-committed-by: Nabarun --- bin/testnet-onboarding | 3 + package.json | 4 +- src/cli/cmds/sumsub-cmds/applicant-details.ts | 54 ++++++++ src/cli/cmds/sumsub.ts | 12 ++ src/cli/index.ts | 8 ++ src/index.ts | 34 +---- src/utils.ts | 32 +++++ yarn.lock | 118 ++++++++++++++++++ 8 files changed, 233 insertions(+), 32 deletions(-) create mode 100755 bin/testnet-onboarding create mode 100644 src/cli/cmds/sumsub-cmds/applicant-details.ts create mode 100644 src/cli/cmds/sumsub.ts create mode 100644 src/cli/index.ts create mode 100644 src/utils.ts diff --git a/bin/testnet-onboarding b/bin/testnet-onboarding new file mode 100755 index 0000000..3f3e92f --- /dev/null +++ b/bin/testnet-onboarding @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require('../dist/cli/index.js'); diff --git a/package.json b/package.json index 5c403e1..ee1b924 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,14 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", - "form-data": "^4.0.0" + "form-data": "^4.0.0", + "yargs": "^17.7.2" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^20.14.12", + "@types/yargs": "^17.0.32", "ts-node": "^10.9.2", "typescript": "^5.5.4" }, diff --git a/src/cli/cmds/sumsub-cmds/applicant-details.ts b/src/cli/cmds/sumsub-cmds/applicant-details.ts new file mode 100644 index 0000000..90ce2c3 --- /dev/null +++ b/src/cli/cmds/sumsub-cmds/applicant-details.ts @@ -0,0 +1,54 @@ +import { Arguments } from 'yargs'; +import dotenv from 'dotenv'; + +import { sumsubAxios } from '../../../utils'; + +dotenv.config(); + +export const command = 'applicant-details'; + +export const desc = 'Get applicant details.'; + +const SUMSUB_BASE_URL = 'https://api.sumsub.com'; +const SUMSUB_APP_TOKEN = process.env.SUMSUB_APP_TOKEN || ''; + +export const builder = (yargs: any) => { + return yargs.positional('applicantId', { + describe: 'The applicant ID to query', + type: 'string', + }); +}; + +function getApplicantDetails (applicantId: string | number) { + const config: any = { baseURL: SUMSUB_BASE_URL}; + + var method = 'GET'; + var url = `/resources/applicants/${applicantId}/one` + + var headers = { + 'Accept': 'application/json', + 'X-App-Token': SUMSUB_APP_TOKEN + }; + + config.method = method; + config.url = url; + config.headers = headers; + config.data = null; + + return config; +} + +export const handler = async (argv: Arguments) => { + const applicantId = argv._[2]; + + if (!applicantId) { + console.error('Applicant ID is required'); + } + + try { + const response = await sumsubAxios(getApplicantDetails(applicantId)); + console.log('Applicant Data:', response.data); + } catch (error: any) { + console.error('Failed to fetch applicant data:', error.response ? error.response.data : error.message); + } +} diff --git a/src/cli/cmds/sumsub.ts b/src/cli/cmds/sumsub.ts new file mode 100644 index 0000000..66668ae --- /dev/null +++ b/src/cli/cmds/sumsub.ts @@ -0,0 +1,12 @@ +import yargs from 'yargs'; + +export const command = 'sumsub'; + +export const desc = 'sumsub'; + +exports.builder = (yargs: yargs.Argv) => { + return yargs + .commandDir('sumsub-cmds') + .demandCommand() + .help(); +}; diff --git a/src/cli/index.ts b/src/cli/index.ts new file mode 100644 index 0000000..ecd73b5 --- /dev/null +++ b/src/cli/index.ts @@ -0,0 +1,8 @@ +import yargs from 'yargs/yargs'; +import { hideBin } from 'yargs/helpers'; + +yargs(hideBin(process.argv)) + .commandDir('cmds') + .demandCommand() + .help() + .argv; diff --git a/src/index.ts b/src/index.ts index 5785a21..923129d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,43 +1,15 @@ import express from 'express'; -import axios, { InternalAxiosRequestConfig } from 'axios'; import dotenv from 'dotenv'; -import FormData from 'form-data'; -import crypto from 'crypto'; -import assert from 'assert'; import cors from 'cors'; +import { sumsubAxios } from './utils'; + dotenv.config(); const SUMSUB_BASE_URL = 'https://api.sumsub.com'; const DEFAULT_KYC_LEVEL = process.env.KYC_LEVEL || 'basic-kyc-level'; const DEFAULT_TOKEN_TTL_IN_SECS = process.env.TTL_IN_SECS || 600; -axios.interceptors.request.use(createSignature, function (error) { - return Promise.reject(error); -}) - -// This function creates signature for the request as described here: https://developers.sumsub.com/api-reference/#app-tokens -function createSignature(config: InternalAxiosRequestConfig) { - assert(config.method); - - console.log('Creating a signature for the request...'); - - var ts = Math.floor(Date.now() / 1000); - const signature = crypto.createHmac('sha256', process.env.SUMSUB_SECRET_KEY || ""); - signature.update(ts + config.method.toUpperCase() + config.url); - - if (config.data instanceof FormData) { - signature.update(config.data.getBuffer()); - } else if (config.data) { - signature.update(config.data); - } - - config.headers['X-App-Access-Ts'] = ts; - config.headers['X-App-Access-Sig'] = signature.digest('hex'); - - return config; -} - // https://developers.sumsub.com/api-reference/#access-tokens-for-sdks function createAccessToken (externalUserId: string, levelName: string, ttlInSecs: number) { const config: any = { baseURL: SUMSUB_BASE_URL}; @@ -71,7 +43,7 @@ app.post('/generate-token', async (req, res) => { } try { - const response = await axios(createAccessToken(userId, levelName, ttlInSecs)); + const response = await sumsubAxios(createAccessToken(userId, levelName, ttlInSecs)); console.log("Created token:\n", response.data); const { token } = response.data; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..2d5caf6 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,32 @@ +import axios, { InternalAxiosRequestConfig } from 'axios'; +import FormData from 'form-data'; +import crypto from 'crypto'; +import assert from 'assert'; + +const sumsubAxios = axios.create(); + +sumsubAxios.interceptors.request.use(createSignature, function (error) { + return Promise.reject(error); +}) + +export { sumsubAxios }; + +// This function creates signature for the request as described here: https://developers.sumsub.com/api-reference/#app-tokens +function createSignature(config: InternalAxiosRequestConfig) { + assert(config.method); + + var ts = Math.floor(Date.now() / 1000); + const signature = crypto.createHmac('sha256', process.env.SUMSUB_SECRET_KEY || ""); + signature.update(ts + config.method.toUpperCase() + config.url); + + if (config.data instanceof FormData) { + signature.update(config.data.getBuffer()); + } else if (config.data) { + signature.update(config.data); + } + + config.headers['X-App-Access-Ts'] = ts; + config.headers['X-App-Access-Sig'] = signature.digest('hex'); + + return config; +} diff --git a/yarn.lock b/yarn.lock index 20dd300..3eb5a55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -133,6 +133,18 @@ "@types/node" "*" "@types/send" "*" +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.32": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -153,6 +165,18 @@ acorn@^8.11.0, acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -211,6 +235,27 @@ call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -299,6 +344,11 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -316,6 +366,11 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -405,6 +460,11 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" @@ -475,6 +535,11 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -589,6 +654,11 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -660,6 +730,22 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -722,6 +808,38 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"