From 3d930aa73b6cbd96df1ab97209c381fb36fc83c2 Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Mon, 12 May 2025 14:21:39 +0100 Subject: [PATCH 1/3] chore: adding eslint and prettier config --- .husky/pre-commit | 4 + packages/admin-ui/.prettierrc => .prettierrc | 0 eslint.config.mjs | 53 +++ package-lock.json | 383 +++++++++++-------- package.json | 29 +- packages/admin-ui/eslint.config.js | 39 -- packages/admin-ui/jsconfig.json | 6 - packages/admin-ui/package.json | 6 - 8 files changed, 304 insertions(+), 216 deletions(-) create mode 100755 .husky/pre-commit rename packages/admin-ui/.prettierrc => .prettierrc (100%) create mode 100644 eslint.config.mjs delete mode 100644 packages/admin-ui/eslint.config.js delete mode 100644 packages/admin-ui/jsconfig.json diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..d24fdfc6 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx lint-staged diff --git a/packages/admin-ui/.prettierrc b/.prettierrc similarity index 100% rename from packages/admin-ui/.prettierrc rename to .prettierrc diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..15315c76 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,53 @@ +import js from '@eslint/js' +import globals from 'globals' +import pluginReact from 'eslint-plugin-react' +import json from '@eslint/json' +import css from '@eslint/css' +import { defineConfig, globalIgnores } from 'eslint/config' +import reactCompiler from 'eslint-plugin-react-compiler' +import eslintConfigPrettier from 'eslint-config-prettier/flat' + +export default defineConfig([ + globalIgnores(['**/build', '**/package.json', '**/package-lock.json']), + { + files: ['**/*.{js,mjs,cjs,jsx}'], + plugins: { js }, + extends: ['js/recommended'] + }, + { + files: ['packages/admin-ui/**/*.{js,mjs,jsx}'], + languageOptions: { sourceType: 'module', globals: globals.browser } + }, + { + files: ['packages/server/**/*.{js,cjs}'], + languageOptions: { sourceType: 'commonjs', globals: globals.node } + }, + { + ...pluginReact.configs.flat.recommended, + settings: { react: { version: 'detect' } }, + files: ['packages/admin-ui/**/*.{jsx,js}'] + }, + { ...reactCompiler.configs.recommended }, + eslintConfigPrettier, + { + files: ['**/*.json'], + plugins: { json }, + language: 'json/json', + extends: ['json/recommended'] + }, + { + files: ['**/*.css'], + plugins: { css }, + language: 'css/css', + extends: ['css/recommended'] + }, + { + rules: { + // 'no-unused-vars': 'on', + 'react/prop-types': 'off', + 'react/display-name': 'off', + 'react/no-unescaped-entities': 'off', + 'react-compiler/react-compiler': 'warn' + } + } +]) diff --git a/package-lock.json b/package-lock.json index 70bc0f70..a14199a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,20 @@ "workspaces": [ "packages/server", "packages/admin-ui" - ] + ], + "devDependencies": { + "@eslint/css": "^0.7.0", + "@eslint/js": "^9.26.0", + "@eslint/json": "^0.12.0", + "eslint": "^9.26.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-compiler": "^19.1.0-rc.1", + "globals": "^16.1.0", + "husky": "^8.0.0", + "lint-staged": "^16.0.0", + "prettier": "^3.5.3" + } }, "node_modules/@adraffy/ens-normalize": { "version": "1.10.1", @@ -3383,6 +3396,35 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/css": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/css/-/css-0.7.0.tgz", + "integrity": "sha512-d6mo8etv4igrTGxgvWSgA5+TsppfObM/Xhlu8JWbkqNBiaJXztUNH45R1B4i1GL2PNIFMLREI3Kh9lTBi19l7g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "@eslint/css-tree": "^3.3.3", + "@eslint/plugin-kit": "^0.2.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/css-tree": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.5.0.tgz", + "integrity": "sha512-RtpdRxIq6AVf078nY0oqZ+x5UTzuxdt/Q2n3RmA4mcfTQVtsvdoIvOxo+4qikydlr6WskqIIofXc5KeGwTYLeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.20.0", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", @@ -3446,6 +3488,36 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz", "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==", "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/json": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.12.0.tgz", + "integrity": "sha512-n/7dz8HFStpEe4o5eYk0tdkBdGUS/ZGb0GQCeDWN1ZmRq67HMHK4vC33b0rQlTT6xdZoX935P4vstiWVk5Ying==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanwhocodes/momoa": "^3.3.4", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/json/node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -4013,6 +4085,16 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@humanwhocodes/momoa": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.8.tgz", + "integrity": "sha512-/3PZzor2imi/RLLcnHztkwA79txiVvW145Ve2cp5dxRcH5qOUNJPToasqLFHniTfw4B4lT7jGDdBOPXbXYlIMQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", @@ -9895,6 +9977,7 @@ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, + "license": "MIT", "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" @@ -9911,6 +9994,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -9923,6 +10007,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -9940,6 +10025,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -10110,7 +10196,8 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colors": { "version": "1.0.3", @@ -11535,7 +11622,8 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -11582,6 +11670,7 @@ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -11910,6 +11999,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz", "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -11968,13 +12058,17 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, + "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } @@ -12155,6 +12249,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -12183,10 +12278,11 @@ } }, "node_modules/eslint-plugin-react-compiler": { - "version": "19.0.0-beta-ebf51a3-20250411", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-ebf51a3-20250411.tgz", - "integrity": "sha512-R7ncuwbCPFAoeMlS56DGGSJFxmRtlWafYH/iWyep5Ks0RaPqTCL4k5gA87axUBBcITsaIgUGkbqAxDxl8Xfm5A==", + "version": "19.1.0-rc.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.1.0-rc.1.tgz", + "integrity": "sha512-3umw5eqZXapBl7aQGmvcjheKhUbsElb9jTETxRZg371e1LG4EPs/zCHt2JzP+wNcdaZWzjU/R730zPUJblY2zw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", @@ -12783,7 +12879,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eventsource": { "version": "3.0.7", @@ -13786,6 +13883,7 @@ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -13964,10 +14062,11 @@ } }, "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -14745,6 +14844,22 @@ "node": ">=8.12.0" } }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/iconv/-/iconv-2.3.5.tgz", @@ -15290,6 +15405,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -18446,6 +18562,7 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -18459,27 +18576,28 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/lint-staged": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", - "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.0.0.tgz", + "integrity": "sha512-sUCprePs6/rbx4vKC60Hez6X10HPkpDJaGcy3D1NdwR7g1RcNkWL8q9mJMreOqmHBTs+1sNFp+wOiX9fr+hoOQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0", "debug": "^4.4.0", - "execa": "^8.0.1", "lilconfig": "^3.1.3", - "listr2": "^8.2.5", + "listr2": "^8.3.3", "micromatch": "^4.0.8", + "nano-spawn": "^1.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", - "yaml": "^2.7.0" + "yaml": "^2.7.1" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": ">=18.12.0" + "node": ">=20.18" }, "funding": { "url": "https://opencollective.com/lint-staged" @@ -18490,6 +18608,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -18502,6 +18621,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } @@ -18511,6 +18631,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -18523,151 +18644,19 @@ } } }, - "node_modules/lint-staged/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/lint-staged/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lint-staged/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, "node_modules/listr2": { "version": "8.3.3", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", "dev": true, + "license": "MIT", "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", @@ -18810,6 +18799,7 @@ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, + "license": "MIT", "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", @@ -18829,6 +18819,7 @@ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, + "license": "MIT", "dependencies": { "environment": "^1.0.0" }, @@ -18844,6 +18835,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -18856,6 +18848,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -18868,6 +18861,7 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^5.0.0" }, @@ -18883,6 +18877,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": true, + "license": "MIT", "dependencies": { "get-east-asian-width": "^1.0.0" }, @@ -18898,6 +18893,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-function": "^5.0.0" }, @@ -18913,6 +18909,7 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" @@ -18929,6 +18926,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -18941,6 +18939,7 @@ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" @@ -18957,6 +18956,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -19524,6 +19524,13 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/mdn-data": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.20.0.tgz", + "integrity": "sha512-/d3otgvmquUkAN2RVxSg6lIbQrYX7isR4aC5Hvw8JuHvzctR3eUG50WmsAZjb9MkbJ5LbijPSy7uIxEtQDGI0w==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -19698,6 +19705,7 @@ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -19983,6 +19991,19 @@ "resolved": "https://registry.npmjs.org/nano-markdown/-/nano-markdown-1.2.2.tgz", "integrity": "sha512-xp0zc42GsE0JVpcTxICpANgNbVfyhk2pUdRV3cDDxzqVZeXSZgrPGruwlj+umFQxo10BKD1qmWdEdxj1x+A0QQ==" }, + "node_modules/nano-spawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.1.tgz", + "integrity": "sha512-BfcvzBlUTxSDWfT+oH7vd6CbUV+rThLLHCIym/QO6GGLBsyVXleZs00fto2i2jzC/wPiBYk5jyOmpXWg4YopiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, "node_modules/nanoassert": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", @@ -21379,6 +21400,7 @@ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, + "license": "MIT", "bin": { "pidtree": "bin/pidtree.js" }, @@ -21689,10 +21711,11 @@ } }, "node_modules/prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", - "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -22974,7 +22997,8 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", @@ -24157,6 +24181,7 @@ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" @@ -24173,6 +24198,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -25283,6 +25309,7 @@ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.19" } @@ -28012,6 +28039,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -28029,6 +28057,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -28041,6 +28070,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -28053,6 +28083,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -28070,6 +28101,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -28560,22 +28592,45 @@ "yup": "1.6.1" }, "devDependencies": { - "@eslint/js": "^9.16.0", "@tailwindcss/vite": "^4.1.4", "@vitejs/plugin-react-swc": "^3.7.2", "esbuild-plugin-react-virtualized": "^1.0.4", - "eslint": "^9.16.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124", "globals": "^15.13.0", - "lint-staged": "^15.2.10", "prettier": "3.4.1", "tailwindcss": "^4.1.4", "vite": "^6.0.1", "vite-plugin-svgr": "^4.3.0" } }, + "packages/admin-ui/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/admin-ui/node_modules/prettier": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "packages/server": { "name": "lamassu-server", "version": "11.0.0-beta.0", diff --git a/package.json b/package.json index e296472d..a05fb6b9 100644 --- a/package.json +++ b/package.json @@ -11,5 +11,32 @@ "workspaces": [ "packages/server", "packages/admin-ui" - ] + ], + "devDependencies": { + "@eslint/css": "^0.7.0", + "@eslint/js": "^9.26.0", + "@eslint/json": "^0.12.0", + "eslint": "^9.26.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-compiler": "^19.1.0-rc.1", + "globals": "^16.1.0", + "husky": "^8.0.0", + "lint-staged": "^16.0.0", + "prettier": "^3.5.3" + }, + "scripts": { + "prepare": "husky install" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{js,jsx,ts,tsx,json,css,scss,md}": [ + "prettier --write", + "eslint --fix" + ] + } } diff --git a/packages/admin-ui/eslint.config.js b/packages/admin-ui/eslint.config.js deleted file mode 100644 index 48fe2205..00000000 --- a/packages/admin-ui/eslint.config.js +++ /dev/null @@ -1,39 +0,0 @@ -import globals from 'globals' -import pluginJs from '@eslint/js' -import pluginReact from 'eslint-plugin-react' -import reactCompiler from 'eslint-plugin-react-compiler' -import eslintConfigPrettier from 'eslint-config-prettier' - -/** @type {import('eslint').Linter.Config[]} */ -export default [ - { - files: ['**/*.{js,mjs,cjs,jsx}'], - languageOptions: { - ...pluginReact.configs.flat.recommended.languageOptions, - globals: { - ...globals.browser, - process: 'readonly' - } - }, - settings: { - react: { - version: '16' - } - }, - plugins: { - 'react-compiler': reactCompiler - } - }, - pluginJs.configs.recommended, - pluginReact.configs.flat.recommended, - { - rules: { - 'no-unused-vars': 'off', - 'react/prop-types': 'off', - 'react/display-name': 'off', - 'react/no-unescaped-entities': 'off', - 'react-compiler/react-compiler': 'warn' - } - }, - eslintConfigPrettier -] diff --git a/packages/admin-ui/jsconfig.json b/packages/admin-ui/jsconfig.json deleted file mode 100644 index 30b25afe..00000000 --- a/packages/admin-ui/jsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "." - }, - "include": ["src"] -} diff --git a/packages/admin-ui/package.json b/packages/admin-ui/package.json index 64bd97e5..fedb9c9f 100644 --- a/packages/admin-ui/package.json +++ b/packages/admin-ui/package.json @@ -41,16 +41,10 @@ "yup": "1.6.1" }, "devDependencies": { - "@eslint/js": "^9.16.0", "@tailwindcss/vite": "^4.1.4", "@vitejs/plugin-react-swc": "^3.7.2", "esbuild-plugin-react-virtualized": "^1.0.4", - "eslint": "^9.16.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124", "globals": "^15.13.0", - "lint-staged": "^15.2.10", "prettier": "3.4.1", "tailwindcss": "^4.1.4", "vite": "^6.0.1", From aedabcbdeefdb56f4ec96fb85ac22db16a0752af Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Mon, 12 May 2025 14:49:39 +0100 Subject: [PATCH 2/3] chore: reformat code --- .prettierrc | 2 +- eslint.config.mjs | 16 +- packages/admin-ui/src/Main.jsx | 2 +- .../src/components/Carousel.module.css | 2 +- .../src/components/CollapsibleCard.jsx | 4 +- .../admin-ui/src/components/ConfirmDialog.jsx | 2 +- .../src/components/CopyToClipboard.jsx | 4 +- .../admin-ui/src/components/DeleteDialog.jsx | 2 +- .../admin-ui/src/components/ImagePopper.jsx | 2 +- .../src/components/InformativeDialog.jsx | 8 +- .../src/components/LogsDownloaderPopper.jsx | 33 +- packages/admin-ui/src/components/Modal.jsx | 8 +- .../NotificationCenter/NotificationCenter.jsx | 14 +- .../NotificationCenter.module.css | 16 +- .../NotificationCenter/NotificationRow.jsx | 21 +- .../components/NotificationCenter/index.js | 1 + packages/admin-ui/src/components/Popper.jsx | 24 +- .../admin-ui/src/components/Popper.module.css | 2 +- .../admin-ui/src/components/SearchBox.jsx | 14 +- .../admin-ui/src/components/SearchFilter.jsx | 4 +- packages/admin-ui/src/components/Stepper.jsx | 4 +- .../src/components/Stepper.module.css | 2 +- packages/admin-ui/src/components/Subtitle.jsx | 2 +- packages/admin-ui/src/components/Tooltip.jsx | 26 +- .../BooleanPropertiesTable.jsx | 14 +- .../src/components/buttons/ActionButton.jsx | 6 +- .../components/buttons/BaseButton.styles.js | 36 +- .../src/components/buttons/Button.jsx | 6 +- .../src/components/buttons/Button.module.css | 6 +- .../src/components/buttons/FeatureButton.jsx | 6 +- .../src/components/buttons/IDButton.jsx | 7 +- .../admin-ui/src/components/buttons/Link.jsx | 4 +- .../src/components/buttons/Link.module.css | 2 +- .../src/components/buttons/SubpageButton.jsx | 10 +- .../buttons/SubpageButton.module.css | 2 +- .../buttons/SubpageButton.styles.js | 20 +- .../admin-ui/src/components/buttons/index.js | 2 +- .../components/date-range-picker/Calendar.jsx | 18 +- .../date-range-picker/Calendar.module.css | 4 +- .../date-range-picker/DateRangePicker.jsx | 6 +- .../src/components/date-range-picker/Tile.jsx | 8 +- .../src/components/editableTable/Header.jsx | 19 +- .../editableTable/NamespacedTable.jsx | 2 +- .../src/components/editableTable/Row.jsx | 20 +- .../components/editableTable/Row.module.css | 2 +- .../src/components/editableTable/Table.jsx | 7 +- .../src/components/fake-table/Table.jsx | 16 +- .../components/inputs/base/Autocomplete.jsx | 15 +- .../src/components/inputs/base/CodeInput.jsx | 4 +- .../src/components/inputs/base/Dropdown.jsx | 2 +- .../components/inputs/base/NumberInput.jsx | 7 +- .../src/components/inputs/base/RadioGroup.jsx | 2 +- .../components/inputs/base/SecretInput.jsx | 2 +- .../src/components/inputs/base/Select.jsx | 7 +- .../components/inputs/base/Select.module.css | 2 +- .../src/components/inputs/base/TextInput.jsx | 18 +- .../inputs/base/TextInput.module.css | 2 +- .../src/components/inputs/base/index.js | 3 +- .../src/components/inputs/cashbox/Cashbox.jsx | 14 +- .../inputs/cashbox/Cashbox.module.css | 2 +- .../inputs/formik/CashCassetteInput.jsx | 2 +- .../src/components/inputs/formik/Checkbox.jsx | 13 +- .../src/components/inputs/formik/index.js | 2 +- .../admin-ui/src/components/inputs/index.js | 2 +- .../admin-ui/src/components/layout/Header.jsx | 10 +- .../src/components/layout/Header.module.css | 2 +- .../src/components/layout/Sidebar.jsx | 8 +- .../src/components/layout/Sidebar.module.css | 4 +- .../src/components/layout/TitleSection.jsx | 6 +- .../machineActions/DiagnosticsModal.jsx | 12 +- .../machineActions/MachineActions.jsx | 30 +- .../single-row-table/SingleRowTable.jsx | 4 +- .../src/components/table/EmptyTable.jsx | 2 +- .../admin-ui/src/components/table/Table.jsx | 2 +- .../src/components/table/TableCell.jsx | 4 +- .../src/components/table/TableHeader.jsx | 4 +- .../src/components/table/TableRow.jsx | 4 +- .../admin-ui/src/components/table/index.js | 2 +- .../src/components/tables/DataTable.jsx | 14 +- .../src/components/typography/index.jsx | 14 +- .../src/components/typography/styles.js | 50 +- packages/admin-ui/src/index.jsx | 2 +- .../src/pages/AddMachine/AddMachine.jsx | 24 +- .../src/pages/Analytics/Analytics.jsx | 40 +- .../components/tooltips/GraphTooltip.jsx | 9 +- .../components/wrappers/HourOfDayWrapper.jsx | 10 +- .../components/wrappers/OverTimeWrapper.jsx | 4 +- .../wrappers/TopMachinesWrapper.jsx | 6 +- .../wrappers/VolumeOverTimeWrapper.jsx | 4 +- .../components/wrappers/wrappers.module.css | 18 +- .../src/pages/Analytics/graphs/Graph.jsx | 2 +- .../Analytics/graphs/HourOfDayBarGraph.jsx | 71 +- .../Analytics/graphs/OverTimeDotGraph.jsx | 94 +- .../Analytics/graphs/OverTimeLineGraph.jsx | 98 +- .../Analytics/graphs/TopMachinesBarGraph.jsx | 70 +- .../Authentication/Authentication.module.css | 6 +- .../pages/Authentication/Input2FAState.jsx | 12 +- .../pages/Authentication/InputFIDOState.jsx | 24 +- .../src/pages/Authentication/LoginCard.jsx | 3 +- .../src/pages/Authentication/LoginState.jsx | 42 +- .../src/pages/Authentication/Register.jsx | 32 +- .../src/pages/Authentication/Reset2FA.jsx | 18 +- .../pages/Authentication/ResetPassword.jsx | 16 +- .../pages/Authentication/Setup2FAState.jsx | 12 +- .../src/pages/Authentication/states.js | 2 +- .../src/pages/Blacklist/Blacklist.jsx | 25 +- .../src/pages/Blacklist/BlacklistAdvanced.jsx | 16 +- .../src/pages/Blacklist/BlacklistModal.jsx | 4 +- .../src/pages/Blacklist/BlacklistTable.jsx | 8 +- .../admin-ui/src/pages/Cashout/Cashout.jsx | 2 +- .../admin-ui/src/pages/Cashout/Wizard.jsx | 24 +- .../admin-ui/src/pages/Cashout/WizardStep.jsx | 16 +- packages/admin-ui/src/pages/Cashout/helper.js | 30 +- .../src/pages/Commissions/Commissions.jsx | 16 +- .../components/CommissionsDetails.jsx | 8 +- .../components/CommissionsList.jsx | 39 +- .../admin-ui/src/pages/Commissions/helper.jsx | 127 +-- .../src/pages/Customers/CustomerData.jsx | 129 +-- .../src/pages/Customers/CustomerNotes.jsx | 6 +- .../src/pages/Customers/CustomerPhotos.jsx | 2 +- .../src/pages/Customers/CustomerProfile.jsx | 94 +- .../src/pages/Customers/Customers.jsx | 24 +- .../src/pages/Customers/CustomersList.jsx | 25 +- .../admin-ui/src/pages/Customers/Wizard.jsx | 10 +- .../components/CreateCustomerModal.jsx | 15 +- .../Customers/components/CustomerDetails.jsx | 10 +- .../Customers/components/CustomerSidebar.jsx | 14 +- .../Customers/components/EditableCard.jsx | 5 +- .../pages/Customers/components/PhotosCard.jsx | 2 +- .../Customers/components/PhotosCarousel.jsx | 2 +- .../Customers/components/TransactionsList.jsx | 36 +- .../src/pages/Customers/components/Upload.jsx | 4 +- .../src/pages/Customers/components/index.js | 2 +- .../components/notes/NewNoteModal.jsx | 4 +- .../Customers/components/notes/NoteEdit.jsx | 10 +- .../admin-ui/src/pages/Customers/helper.jsx | 117 +-- .../src/pages/Dashboard/Alerts/Alerts.jsx | 2 +- .../pages/Dashboard/Alerts/AlertsTable.jsx | 4 +- .../src/pages/Dashboard/Alerts/index.js | 1 + .../src/pages/Dashboard/Footer/Footer.jsx | 14 +- .../pages/Dashboard/Footer/Footer.module.css | 6 +- .../src/pages/Dashboard/Footer/index.js | 1 + .../src/pages/Dashboard/RightSide.jsx | 4 +- .../Graphs/PercentageChart.jsx | 2 +- .../SystemPerformance/Graphs/RefLineChart.jsx | 18 +- .../Graphs/RefScatterplot.jsx | 60 +- .../SystemPerformance/InfoWithLabel.jsx | 1 + .../pages/Dashboard/SystemPerformance/Nav.jsx | 2 +- .../SystemPerformance/SystemPerformance.jsx | 24 +- .../Dashboard/SystemPerformance/index.js | 1 + .../Dashboard/SystemStatus/MachinesTable.jsx | 18 +- .../SystemStatus/MachinesTable.styles.js | 38 +- .../Dashboard/SystemStatus/SystemStatus.jsx | 2 +- .../src/pages/Dashboard/SystemStatus/index.js | 1 + .../admin-ui/src/pages/Dashboard/index.js | 1 + .../admin-ui/src/pages/Funding/Funding.jsx | 26 +- .../src/pages/Funding/Funding.module.css | 2 +- .../admin-ui/src/pages/Locales/Locales.jsx | 6 +- packages/admin-ui/src/pages/Locales/helper.js | 44 +- .../admin-ui/src/pages/Logs/Logs.module.css | 2 +- .../admin-ui/src/pages/Logs/MachineLogs.jsx | 6 +- .../admin-ui/src/pages/Logs/ServerLogs.jsx | 16 +- .../src/pages/Logs/ServerLogs.module.css | 2 +- .../LoyaltyPanel/IndividualDiscountModal.jsx | 12 +- .../LoyaltyPanel/IndividualDiscounts.jsx | 16 +- .../src/pages/LoyaltyPanel/PromoCodes.jsx | 16 +- .../pages/LoyaltyPanel/PromoCodesModal.jsx | 4 +- .../MachineComponents/Cassettes/Cassettes.jsx | 10 +- .../MachineComponents/Cassettes/index.js | 1 + .../Commissions/Commissions.jsx | 18 +- .../MachineComponents/Commissions/helper.jsx | 16 +- .../Machines/MachineComponents/Overview.jsx | 2 +- .../Transactions/Transactions.jsx | 22 +- .../MachineComponents/Transactions/index.js | 1 + .../admin-ui/src/pages/Machines/Machines.jsx | 6 +- .../src/pages/Maintenance/CashUnitDetails.jsx | 2 +- .../src/pages/Maintenance/CashUnits.jsx | 28 +- .../src/pages/Maintenance/CashUnitsFooter.jsx | 26 +- .../src/pages/Maintenance/CashboxHistory.jsx | 28 +- .../src/pages/Maintenance/MachineStatus.jsx | 16 +- .../src/pages/Maintenance/Wizard/Wizard.jsx | 48 +- .../pages/Maintenance/Wizard/WizardStep.jsx | 27 +- .../admin-ui/src/pages/Maintenance/helper.jsx | 14 +- .../src/pages/Notifications/Notifications.jsx | 16 +- .../components/EditableNumber.jsx | 4 +- .../components/SingleFieldEditableNumber.jsx | 4 +- .../sections/CryptoBalanceOverrides.jsx | 34 +- .../sections/FiatBalanceAlerts.jsx | 18 +- .../sections/FiatBalanceOverrides.jsx | 38 +- .../pages/Notifications/sections/Setup.jsx | 16 +- .../sections/ThirdPartyProvider.jsx | 18 +- .../src/pages/OperatorInfo/CoinATMRadar.jsx | 10 +- .../src/pages/OperatorInfo/ContactInfo.jsx | 22 +- .../src/pages/OperatorInfo/MachineScreens.jsx | 12 +- .../pages/OperatorInfo/ReceiptPrinting.jsx | 28 +- .../OperatorInfo/SMSNotices/SMSNotices.jsx | 22 +- .../SMSNotices/SMSNoticesModal.jsx | 38 +- .../pages/OperatorInfo/TermsConditions.jsx | 22 +- .../src/pages/Services/FormRenderer.jsx | 11 +- .../admin-ui/src/pages/Services/Services.jsx | 14 +- .../src/pages/Services/schemas/binance.js | 18 +- .../src/pages/Services/schemas/binanceus.js | 18 +- .../src/pages/Services/schemas/bitfinex.js | 18 +- .../src/pages/Services/schemas/bitgo.js | 60 +- .../src/pages/Services/schemas/bitstamp.js | 20 +- .../src/pages/Services/schemas/blockcypher.js | 14 +- .../src/pages/Services/schemas/cex.js | 20 +- .../src/pages/Services/schemas/elliptic.js | 18 +- .../src/pages/Services/schemas/galoy.js | 22 +- .../src/pages/Services/schemas/helper.js | 8 +- .../src/pages/Services/schemas/index.js | 2 +- .../src/pages/Services/schemas/inforu.js | 14 +- .../src/pages/Services/schemas/infura.js | 8 +- .../src/pages/Services/schemas/itbit.js | 22 +- .../src/pages/Services/schemas/kraken.js | 18 +- .../src/pages/Services/schemas/mailgun.js | 14 +- .../src/pages/Services/schemas/scorechain.js | 16 +- .../src/pages/Services/schemas/singlebitgo.js | 22 +- .../src/pages/Services/schemas/sumsub.js | 12 +- .../src/pages/Services/schemas/telnyx.js | 12 +- .../src/pages/Services/schemas/trongrid.js | 10 +- .../src/pages/Services/schemas/twilio.js | 14 +- .../src/pages/Services/schemas/vonage.js | 14 +- .../SessionManagement/SessionManagement.jsx | 16 +- .../src/pages/Transactions/DetailsCard.jsx | 28 +- .../src/pages/Transactions/Transactions.jsx | 32 +- .../CustomInfoRequests/CustomInfoRequests.jsx | 44 +- .../CustomInfoRequests/Forms/ChooseType.jsx | 14 +- .../Forms/NameOfRequirement.jsx | 10 +- .../Forms/Screen1Information.jsx | 4 +- .../Forms/Screen2Information.jsx | 4 +- .../Forms/TypeFields/ChoiceList.jsx | 4 +- .../Forms/TypeFields/NumericalEntry.jsx | 4 +- .../Forms/TypeFields/TextEntry.jsx | 6 +- .../Forms/TypeFields/index.jsx | 16 +- .../Triggers/CustomInfoRequests/Wizard.jsx | 37 +- .../Triggers/CustomInfoRequests/index.js | 1 + .../src/pages/Triggers/TriggerView.jsx | 10 +- .../admin-ui/src/pages/Triggers/Triggers.jsx | 18 +- .../admin-ui/src/pages/Triggers/Wizard.jsx | 18 +- .../Triggers/components/AdvancedTriggers.jsx | 12 +- .../src/pages/Triggers/components/helper.js | 60 +- .../admin-ui/src/pages/Triggers/helper.jsx | 136 +-- .../pages/UserManagement/UserManagement.jsx | 40 +- .../UserManagement/modals/ChangeRoleModal.jsx | 8 +- .../UserManagement/modals/CreateUserModal.jsx | 20 +- .../UserManagement/modals/EnableUserModal.jsx | 14 +- .../pages/UserManagement/modals/FIDOModal.jsx | 2 +- .../UserManagement/modals/Input2FAModal.jsx | 2 +- .../UserManagement/modals/Reset2FAModal.jsx | 12 +- .../modals/ResetPasswordModal.jsx | 14 +- .../src/pages/Wallet/AdvancedWallet.jsx | 18 +- packages/admin-ui/src/pages/Wallet/Wallet.jsx | 16 +- packages/admin-ui/src/pages/Wallet/Wizard.jsx | 16 +- .../admin-ui/src/pages/Wallet/WizardStep.jsx | 20 +- packages/admin-ui/src/pages/Wallet/helper.jsx | 84 +- packages/admin-ui/src/pages/Wizard/Wizard.jsx | 4 +- .../pages/Wizard/components/Commissions.jsx | 4 +- .../src/pages/Wizard/components/Locales.jsx | 8 +- .../src/pages/Wizard/components/Mailgun.jsx | 12 +- .../pages/Wizard/components/Notifications.jsx | 2 +- .../pages/Wizard/components/OperatorInfo.jsx | 1 + .../src/pages/Wizard/components/Twilio.jsx | 10 +- .../pages/Wizard/components/Twilio.module.css | 2 +- .../pages/Wizard/components/Wallet/AllSet.jsx | 13 +- .../Wizard/components/Wallet/Blockcypher.jsx | 10 +- .../Wizard/components/Wallet/ChooseCoin.jsx | 4 +- .../components/Wallet/ChooseExchange.jsx | 10 +- .../Wizard/components/Wallet/ChooseTicker.jsx | 2 +- .../Wizard/components/Wallet/ChooseWallet.jsx | 10 +- .../components/Wallet/Shared.module.css | 2 +- .../pages/Wizard/components/Wallet/Wallet.jsx | 14 +- packages/admin-ui/src/pages/Wizard/helper.jsx | 16 +- .../admin-ui/src/routing/lamassu.routes.jsx | 84 +- packages/admin-ui/src/routing/routes.jsx | 18 +- packages/admin-ui/src/routing/utils.js | 2 +- .../admin-ui/src/styling/global/fonts.css | 20 +- .../admin-ui/src/styling/global/global.css | 17 +- packages/admin-ui/src/styling/helpers.js | 4 +- .../src/styling/icons/ID/card/comet.svg | 12 +- .../src/styling/icons/ID/card/tomato.svg | 12 +- .../src/styling/icons/ID/card/white.svg | 12 +- .../src/styling/icons/ID/card/zodiac.svg | 6 +- .../src/styling/icons/ID/phone/comet.svg | 9 +- .../src/styling/icons/ID/phone/tomato.svg | 9 +- .../src/styling/icons/ID/phone/white.svg | 9 +- .../src/styling/icons/ID/phone/zodiac.svg | 9 +- .../src/styling/icons/ID/photo/comet.svg | 9 +- .../styling/icons/ID/photo/crossed-camera.svg | 9 +- .../src/styling/icons/ID/photo/tomato.svg | 9 +- .../src/styling/icons/ID/photo/white.svg | 9 +- .../src/styling/icons/ID/photo/zodiac.svg | 9 +- .../src/styling/icons/action/arrow/comet.svg | 6 +- .../styling/icons/action/arrow/regular.svg | 6 +- .../src/styling/icons/action/arrow/white.svg | 6 +- .../src/styling/icons/action/arrow/zodiac.svg | 27 +- .../src/styling/icons/action/close/comet.svg | 6 +- .../src/styling/icons/action/close/white.svg | 6 +- .../src/styling/icons/action/close/zodiac.svg | 9 +- .../src/styling/icons/action/copy/comet.svg | 12 +- .../src/styling/icons/action/copy/copy.svg | 9 +- .../src/styling/icons/action/copy/white.svg | 12 +- .../src/styling/icons/action/copy/zodiac.svg | 6 +- .../styling/icons/action/delete/disabled.svg | 52 +- .../styling/icons/action/delete/enabled.svg | 12 +- .../src/styling/icons/action/delete/white.svg | 9 +- .../src/styling/icons/action/edit/comet.svg | 9 +- .../styling/icons/action/edit/disabled.svg | 9 +- .../src/styling/icons/action/edit/enabled.svg | 9 +- .../src/styling/icons/action/edit/white.svg | 9 +- .../styling/icons/action/expand/closed.svg | 3 +- .../src/styling/icons/action/expand/open.svg | 6 +- .../icons/action/external link/white.svg | 9 +- .../icons/action/external link/zodiac.svg | 6 +- .../src/styling/icons/action/help/white.svg | 12 +- .../src/styling/icons/action/help/zodiac.svg | 12 +- .../styling/icons/action/wrench/zodiac.svg | 9 +- .../icons/arrow/carousel-left-arrow.svg | 6 +- .../icons/arrow/carousel-right-arrow.svg | 3 +- .../src/styling/icons/arrow/download_logs.svg | 12 +- .../src/styling/icons/arrow/month_change.svg | 12 +- .../icons/arrow/month_change_right.svg | 12 +- .../src/styling/icons/arrow/transaction.svg | 12 +- .../styling/icons/button/add note/white.svg | 9 +- .../styling/icons/button/add note/zodiac.svg | 9 +- .../styling/icons/button/add-note/white.svg | 19 +- .../styling/icons/button/add-note/zodiac.svg | 12 +- .../src/styling/icons/button/add/white.svg | 9 +- .../src/styling/icons/button/add/zodiac.svg | 9 +- .../styling/icons/button/authorize/white.svg | 3 +- .../styling/icons/button/authorize/zodiac.svg | 3 +- .../styling/icons/button/blacklist/white.svg | 9 +- .../styling/icons/button/blacklist/zodiac.svg | 9 +- .../src/styling/icons/button/block/white.svg | 3 +- .../src/styling/icons/button/block/zodiac.svg | 3 +- .../src/styling/icons/button/cancel/white.svg | 6 +- .../styling/icons/button/cancel/zodiac.svg | 6 +- .../styling/icons/button/configure/white.svg | 12 +- .../styling/icons/button/configure/zodiac.svg | 12 +- .../src/styling/icons/button/data/white.svg | 12 +- .../src/styling/icons/button/data/zodiac.svg | 12 +- .../styling/icons/button/discount/comet.svg | 9 +- .../styling/icons/button/discount/white.svg | 9 +- .../styling/icons/button/discount/zodiac.svg | 9 +- .../styling/icons/button/download/white.svg | 9 +- .../styling/icons/button/download/zodiac.svg | 9 +- .../src/styling/icons/button/edit/white.svg | 12 +- .../src/styling/icons/button/edit/zodiac.svg | 12 +- .../icons/button/export to PDF/white.svg | 12 +- .../icons/button/export to PDF/zodiac.svg | 12 +- .../styling/icons/button/export-pdf/white.svg | 21 +- .../src/styling/icons/button/filter/white.svg | 6 +- .../styling/icons/button/filter/zodiac.svg | 6 +- .../src/styling/icons/button/ignore/white.svg | 3 +- .../styling/icons/button/ignore/zodiac.svg | 3 +- .../src/styling/icons/button/key/white.svg | 9 +- .../src/styling/icons/button/key/zodiac.svg | 9 +- .../src/styling/icons/button/link/export.svg | 12 +- .../src/styling/icons/button/link/white.svg | 6 +- .../src/styling/icons/button/link/zodiac.svg | 6 +- .../src/styling/icons/button/lock/white.svg | 9 +- .../src/styling/icons/button/lock/zodiac.svg | 9 +- .../icons/button/photo/white-resized.svg | 24 +- .../icons/button/photo/zodiac-resized.svg | 16 +- .../src/styling/icons/button/reboot/white.svg | 21 +- .../styling/icons/button/reboot/zodiac.svg | 21 +- .../styling/icons/button/replace/white.svg | 18 +- .../styling/icons/button/replace/zodiac.svg | 21 +- .../src/styling/icons/button/retry/white.svg | 9 +- .../src/styling/icons/button/retry/zodiac.svg | 9 +- .../styling/icons/button/schedule/white.svg | 6 +- .../styling/icons/button/schedule/zodiac.svg | 6 +- .../styling/icons/button/shut down/white.svg | 9 +- .../styling/icons/button/shut down/zodiac.svg | 9 +- .../icons/button/stop-ignoring/white.svg | 10 +- .../icons/button/stop-ignoring/zodiac.svg | 10 +- .../src/styling/icons/button/unpair/white.svg | 6 +- .../styling/icons/button/unpair/zodiac.svg | 6 +- .../button/upload-file/white-resized.svg | 15 +- .../icons/button/upload-file/white.svg | 9 +- .../button/upload-file/zodiac-resized.svg | 12 +- .../icons/button/upload-file/zodiac.svg | 9 +- .../src/styling/icons/button/upload/white.svg | 12 +- .../styling/icons/button/upload/zodiac.svg | 12 +- .../styling/icons/button/user-role/white.svg | 9 +- .../styling/icons/button/user-role/zodiac.svg | 9 +- .../styling/icons/button/whitelist/white.svg | 12 +- .../styling/icons/button/whitelist/zodiac.svg | 12 +- .../icons/cassettes/acceptor-left-filled.svg | 64 +- .../styling/icons/cassettes/acceptor-left.svg | 21 +- .../styling/icons/cassettes/both-filled.svg | 90 +- .../styling/icons/cassettes/cashbox-empty.svg | 9 +- .../icons/cassettes/cashout-cassette-1.svg | 26 +- .../icons/cassettes/cashout-cassette-2.svg | 26 +- .../styling/icons/cassettes/dispenser-1.svg | 30 +- .../styling/icons/cassettes/dispenser-2.svg | 30 +- .../3-cassettes/3-cassettes-open-1-left.svg | 147 +-- .../tejo/3-cassettes/3-cassettes-open-1.svg | 147 +-- .../3-cassettes/3-cassettes-open-2-left.svg | 155 +-- .../tejo/3-cassettes/3-cassettes-open-2.svg | 158 ++-- .../3-cassettes/3-cassettes-open-3-left.svg | 159 ++-- .../tejo/3-cassettes/3-cassettes-open-3.svg | 159 ++-- .../4-cassettes/4-cassettes-open-1-left.svg | 168 ++-- .../tejo/4-cassettes/4-cassettes-open-1.svg | 168 ++-- .../4-cassettes/4-cassettes-open-2-left.svg | 172 ++-- .../tejo/4-cassettes/4-cassettes-open-2.svg | 175 ++-- .../4-cassettes/4-cassettes-open-3-left.svg | 172 ++-- .../tejo/4-cassettes/4-cassettes-open-3.svg | 176 ++-- .../4-cassettes/4-cassettes-open-4-left.svg | 181 ++-- .../tejo/4-cassettes/4-cassettes-open-4.svg | 185 ++-- .../customer-list-view/white.svg | 6 +- .../customer-list-view/zodiac.svg | 6 +- .../circle buttons/exception-view/white.svg | 6 +- .../circle buttons/exception-view/zodiac.svg | 6 +- .../icons/circle buttons/filter/white.svg | 21 +- .../icons/circle buttons/filter/zodiac.svg | 21 +- .../icons/circle buttons/history/white.svg | 9 +- .../icons/circle buttons/history/zodiac.svg | 9 +- .../icons/circle buttons/law/white.svg | 6 +- .../icons/circle buttons/law/zodiac.svg | 6 +- .../circle buttons/listing-view/white.svg | 6 +- .../circle buttons/listing-view/zodiac.svg | 6 +- .../icons/circle buttons/overview/comet.svg | 3 +- .../icons/circle buttons/overview/white.svg | 3 +- .../icons/circle buttons/overview/zodiac.svg | 3 +- .../icons/circle buttons/save/white.svg | 9 +- .../icons/circle buttons/save/zodiac.svg | 10 +- .../icons/circle buttons/search/white.svg | 9 +- .../icons/circle buttons/search/zodiac.svg | 9 +- .../icons/circle buttons/settings/white.svg | 9 +- .../icons/circle buttons/settings/zodiac.svg | 9 +- .../icons/circle buttons/share/white.svg | 6 +- .../icons/circle buttons/share/zodiac.svg | 6 +- .../icons/circle buttons/upload/white.svg | 13 +- .../icons/circle buttons/upload/zodiac.svg | 13 +- .../icons/circle buttons/wizard/white.svg | 9 +- .../icons/circle buttons/wizard/zodiac.svg | 9 +- .../icons/compliance/custom-requirement.svg | 890 +++++++++++++++++- .../src/styling/icons/compliance/keyboard.svg | 3 +- .../src/styling/icons/compliance/keypad.svg | 3 +- .../src/styling/icons/compliance/list.svg | 3 +- .../styling/icons/customer-nav/data/comet.svg | 9 +- .../styling/icons/customer-nav/data/white.svg | 9 +- .../icons/customer-nav/data/zodiac.svg | 9 +- .../styling/icons/customer-nav/note/comet.svg | 9 +- .../styling/icons/customer-nav/note/white.svg | 9 +- .../icons/customer-nav/note/zodiac.svg | 9 +- .../icons/customer-nav/overview/comet.svg | 3 +- .../icons/customer-nav/overview/white.svg | 3 +- .../icons/customer-nav/overview/zodiac.svg | 3 +- .../icons/customer-nav/photos/comet.svg | 9 +- .../icons/customer-nav/photos/white.svg | 9 +- .../src/styling/icons/dashboard/down.svg | 10 +- .../src/styling/icons/dashboard/equal.svg | 3 +- .../src/styling/icons/dashboard/up.svg | 10 +- .../src/styling/icons/direction/cash-in.svg | 6 +- .../src/styling/icons/direction/cash-out.svg | 7 +- .../admin-ui/src/styling/icons/file/comet.svg | 6 +- .../src/styling/icons/file/spring.svg | 6 +- .../src/styling/icons/file/tomato.svg | 6 +- .../src/styling/icons/menu/logo-white.svg | 6 +- .../admin-ui/src/styling/icons/menu/logo.svg | 6 +- .../icons/menu/notification-zodiac.svg | 9 +- .../src/styling/icons/menu/notification.svg | 9 +- .../src/styling/icons/menu/search-zodiac.svg | 9 +- .../src/styling/icons/menu/search.svg | 9 +- .../src/styling/icons/month arrows/left.svg | 12 +- .../src/styling/icons/month arrows/right.svg | 12 +- .../icons/month arrows/right_white.svg | 12 +- .../styling/icons/stage/spring/complete.svg | 10 +- .../styling/icons/stage/spring/current.svg | 7 +- .../src/styling/icons/stage/spring/empty.svg | 7 +- .../styling/icons/stage/zodiac/complete.svg | 10 +- .../styling/icons/stage/zodiac/current.svg | 7 +- .../src/styling/icons/stage/zodiac/empty.svg | 7 +- .../src/styling/icons/stage/zodiac/full.svg | 7 +- .../src/styling/icons/status/pumpkin.svg | 3 +- .../src/styling/icons/status/spring2.svg | 3 +- .../src/styling/icons/status/tomato.svg | 3 +- .../admin-ui/src/styling/icons/stripes.svg | 53 +- .../src/styling/icons/table/empty-table.svg | 30 +- .../src/styling/icons/table/false.svg | 10 +- .../admin-ui/src/styling/icons/table/true.svg | 6 +- .../src/styling/icons/warning-icon/comet.svg | 6 +- .../src/styling/icons/warning-icon/tomato.svg | 6 +- .../src/styling/logos/icon-bitcoin-colour.svg | 10 +- .../styling/logos/icon-bitcoincash-colour.svg | 6 +- .../src/styling/logos/icon-dash-colour.svg | 8 +- .../styling/logos/icon-ethereum-colour.svg | 21 +- .../styling/logos/icon-litecoin-colour.svg | 5 +- .../src/styling/logos/icon-monero-colour.svg | 7 +- .../src/styling/logos/icon-tether-colour.svg | 7 +- .../src/styling/logos/icon-tron-colour.svg | 6 +- .../src/styling/logos/icon-usdc-colour.svg | 9 +- .../src/styling/logos/icon-zcash-colour.svg | 13 +- packages/admin-ui/src/styling/theme.js | 186 ++-- packages/admin-ui/src/styling/variables.js | 2 +- packages/admin-ui/src/utils/apollo.jsx | 31 +- .../admin-ui/src/utils/bill-denominations.js | 146 +-- packages/admin-ui/src/utils/bill-options.js | 2 +- packages/admin-ui/src/utils/config.js | 12 +- packages/admin-ui/src/utils/constants.js | 2 +- packages/admin-ui/src/utils/customer.js | 4 +- packages/admin-ui/src/utils/machine.js | 20 +- packages/admin-ui/src/utils/number.js | 2 +- packages/admin-ui/src/utils/string.js | 6 +- packages/admin-ui/src/utils/timezone-list.js | 28 +- packages/admin-ui/src/utils/timezones.js | 2 +- packages/admin-ui/src/utils/urlResolver.js | 9 - 509 files changed, 6030 insertions(+), 4266 deletions(-) delete mode 100644 packages/admin-ui/src/utils/urlResolver.js diff --git a/.prettierrc b/.prettierrc index fe83baaf..bf93fdb8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { - "trailingComma": "none", + "trailingComma": "all", "tabWidth": 2, "semi": false, "singleQuote": true, diff --git a/eslint.config.mjs b/eslint.config.mjs index 15315c76..2447af6b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,7 +2,6 @@ import js from '@eslint/js' import globals from 'globals' import pluginReact from 'eslint-plugin-react' import json from '@eslint/json' -import css from '@eslint/css' import { defineConfig, globalIgnores } from 'eslint/config' import reactCompiler from 'eslint-plugin-react-compiler' import eslintConfigPrettier from 'eslint-config-prettier/flat' @@ -16,7 +15,13 @@ export default defineConfig([ }, { files: ['packages/admin-ui/**/*.{js,mjs,jsx}'], - languageOptions: { sourceType: 'module', globals: globals.browser } + languageOptions: { + sourceType: 'module', + globals: { + ...globals.browser, + process: 'readonly' + } + } }, { files: ['packages/server/**/*.{js,cjs}'], @@ -35,15 +40,8 @@ export default defineConfig([ language: 'json/json', extends: ['json/recommended'] }, - { - files: ['**/*.css'], - plugins: { css }, - language: 'css/css', - extends: ['css/recommended'] - }, { rules: { - // 'no-unused-vars': 'on', 'react/prop-types': 'off', 'react/display-name': 'off', 'react/no-unescaped-entities': 'off', diff --git a/packages/admin-ui/src/Main.jsx b/packages/admin-ui/src/Main.jsx index ac31806f..376a2828 100644 --- a/packages/admin-ui/src/Main.jsx +++ b/packages/admin-ui/src/Main.jsx @@ -34,7 +34,7 @@ const Main = () => { onCompleted: userResponse => { if (!userData && userResponse?.userData) setUserData(userResponse.userData) - } + }, }) const route = location.pathname diff --git a/packages/admin-ui/src/components/Carousel.module.css b/packages/admin-ui/src/components/Carousel.module.css index 92a4c3b0..3239bde3 100644 --- a/packages/admin-ui/src/components/Carousel.module.css +++ b/packages/admin-ui/src/components/Carousel.module.css @@ -42,4 +42,4 @@ .navButton:hover { background-color: rgba(0, 0, 0, 0.04); -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/components/CollapsibleCard.jsx b/packages/admin-ui/src/components/CollapsibleCard.jsx index 987c32e7..5cc60879 100644 --- a/packages/admin-ui/src/components/CollapsibleCard.jsx +++ b/packages/admin-ui/src/components/CollapsibleCard.jsx @@ -7,7 +7,7 @@ import classnames from 'classnames' const cardState = Object.freeze({ DEFAULT: 'default', SHRUNK: 'shrunk', - EXPANDED: 'expanded' + EXPANDED: 'expanded', }) const CollapsibleCard = ({ className, state, shrunkComponent, children }) => { @@ -19,7 +19,7 @@ const CollapsibleCard = ({ className, state, shrunkComponent, children }) => { } CollapsibleCard.propTypes = { - shrunkComponent: PropTypes.node.isRequired + shrunkComponent: PropTypes.node.isRequired, } export default CollapsibleCard diff --git a/packages/admin-ui/src/components/ConfirmDialog.jsx b/packages/admin-ui/src/components/ConfirmDialog.jsx index 607e8477..2fc880ca 100644 --- a/packages/admin-ui/src/components/ConfirmDialog.jsx +++ b/packages/admin-ui/src/components/ConfirmDialog.jsx @@ -101,5 +101,5 @@ export const ConfirmDialog = memo( ) - } + }, ) diff --git a/packages/admin-ui/src/components/CopyToClipboard.jsx b/packages/admin-ui/src/components/CopyToClipboard.jsx index eac6f815..c4035538 100644 --- a/packages/admin-ui/src/components/CopyToClipboard.jsx +++ b/packages/admin-ui/src/components/CopyToClipboard.jsx @@ -14,7 +14,7 @@ const CopyToClipboard = ({ buttonClassname, children, wrapperClassname, - removeSpace = true + removeSpace = true, }) => { const [anchorEl, setAnchorEl] = useState(null) @@ -68,7 +68,7 @@ const CopyToClipboard = ({ )} - ); + ) } export default CopyToClipboard diff --git a/packages/admin-ui/src/components/DeleteDialog.jsx b/packages/admin-ui/src/components/DeleteDialog.jsx index 1907b286..0d97f918 100644 --- a/packages/admin-ui/src/components/DeleteDialog.jsx +++ b/packages/admin-ui/src/components/DeleteDialog.jsx @@ -34,7 +34,7 @@ export const DeleteDialog = ({ item = 'item', confirmationMessage = `Are you sure you want to delete this ${item}?`, extraMessage, - errorMessage = '' + errorMessage = '', }) => { return ( diff --git a/packages/admin-ui/src/components/ImagePopper.jsx b/packages/admin-ui/src/components/ImagePopper.jsx index d732e957..93291e3b 100644 --- a/packages/admin-ui/src/components/ImagePopper.jsx +++ b/packages/admin-ui/src/components/ImagePopper.jsx @@ -50,7 +50,7 @@ const ImagePopper = memo( ) - } + }, ) export default ImagePopper diff --git a/packages/admin-ui/src/components/InformativeDialog.jsx b/packages/admin-ui/src/components/InformativeDialog.jsx index ed39fe4c..2311e9e0 100644 --- a/packages/admin-ui/src/components/InformativeDialog.jsx +++ b/packages/admin-ui/src/components/InformativeDialog.jsx @@ -7,7 +7,7 @@ import { H1 } from 'src/components/typography' import CloseIcon from 'src/styling/icons/action/close/zodiac.svg?react' export const InformativeDialog = memo( - ({ title = '', open, onDissmised, disabled = false, data, ...props }) => { + ({ title = '', open, onDissmised, data, ...props }) => { const innerOnClose = () => { onDissmised() } @@ -16,8 +16,8 @@ export const InformativeDialog = memo( {data} ) - } + }, ) diff --git a/packages/admin-ui/src/components/LogsDownloaderPopper.jsx b/packages/admin-ui/src/components/LogsDownloaderPopper.jsx index 67ec9087..98f4d947 100644 --- a/packages/admin-ui/src/components/LogsDownloaderPopper.jsx +++ b/packages/admin-ui/src/components/LogsDownloaderPopper.jsx @@ -15,7 +15,6 @@ import { formatDate } from 'src/utils/timezones' import Popper from './Popper' import DateRangePicker from './date-range-picker/DateRangePicker' import { RadioGroup } from './inputs' -import typographyStyles from './typography/styles' import { H4, Info1, Label1, Label2 } from './typography/index.jsx' const DateContainer = ({ date, children }) => { @@ -31,7 +30,7 @@ const DateContainer = ({ date, children }) => {
{`${format( 'MMM', - date + date, )} ${format('yyyy', date)}`} {format('EEEE', date)} @@ -57,7 +56,7 @@ const LogsDownloaderPopover = ({ getLogs, timezone, simplified, - className + className, }) => { const [selectedRadio, setSelectedRadio] = useState(ALL) const [selectedAdvancedRadio, setSelectedAdvancedRadio] = useState(ADVANCED) @@ -65,12 +64,12 @@ const LogsDownloaderPopover = ({ const [range, setRange] = useState({ from: null, until: null }) const [anchorEl, setAnchorEl] = useState(null) const [fetchLogs] = useLazyQuery(query, { - onCompleted: data => createLogsFile(getLogs(data), range) + onCompleted: data => createLogsFile(getLogs(data), range), }) const dateRangePickerClasses = { 'block h-full': selectedRadio === RANGE, - hidden: selectedRadio === ALL + hidden: selectedRadio === ALL, } const handleRadioButtons = evt => { @@ -88,7 +87,7 @@ const LogsDownloaderPopover = ({ (from, until) => { setRange({ from, until }) }, - [setRange] + [setRange], ) const downloadLogs = (range, args) => { @@ -97,8 +96,8 @@ const LogsDownloaderPopover = ({ variables: { ...args, simplified: selectedAdvancedRadio === SIMPLIFIED, - excludeTestingCustomers: true - } + excludeTestingCustomers: true, + }, }) } @@ -112,8 +111,8 @@ const LogsDownloaderPopover = ({ from: range.from, until: range.until, simplified: selectedAdvancedRadio === SIMPLIFIED, - excludeTestingCustomers: true - } + excludeTestingCustomers: true, + }, }) } } @@ -124,7 +123,7 @@ const LogsDownloaderPopover = ({ } const blob = new window.Blob([logs], { - type: 'text/plain;charset=utf-8' + type: 'text/plain;charset=utf-8', }) FileSaver.saveAs( @@ -132,8 +131,8 @@ const LogsDownloaderPopover = ({ selectedRadio === ALL ? `${formatDateFile(new Date())}_${name}.csv` : `${formatDateFile(range.from)}_${formatDateFile( - range.until - )}_${name}.csv` + range.until, + )}_${name}.csv`, ) } @@ -147,12 +146,12 @@ const LogsDownloaderPopover = ({ const radioButtonOptions = [ { display: 'All logs', code: ALL }, - { display: 'Date range', code: RANGE } + { display: 'Date range', code: RANGE }, ] const advancedRadioButtonOptions = [ { display: 'Advanced logs', code: ADVANCED }, - { display: 'Simplified logs', code: SIMPLIFIED } + { display: 'Simplified logs', code: SIMPLIFIED }, ] const open = Boolean(anchorEl) @@ -201,9 +200,9 @@ const LogsDownloaderPopover = ({ hours: 23, minutes: 59, seconds: 59, - milliseconds: 999 + milliseconds: 999, }, - new Date() + new Date(), )} onRangeChange={handleRangeChange} /> diff --git a/packages/admin-ui/src/components/Modal.jsx b/packages/admin-ui/src/components/Modal.jsx index 67aa2b9f..e208b320 100644 --- a/packages/admin-ui/src/components/Modal.jsx +++ b/packages/admin-ui/src/components/Modal.jsx @@ -17,14 +17,12 @@ const Modal = ({ infoPanel, handleClose, children, - secondaryModal, className, closeOnEscape, closeOnBackdropClick, ...props }) => { const TitleCase = small ? H4 : H1 - const closeSize = xl ? 28 : small ? 16 : 20 const innerClose = (evt, reason) => { if (!closeOnBackdropClick && reason === 'backdropClick') return @@ -44,7 +42,7 @@ const Modal = ({ style={{ width, height, minHeight: height ?? 400 }} className={classnames( 'flex flex-col max-h-[90vh] rounded-lg outline-0', - className + className, )}>
{title && ( @@ -78,11 +76,11 @@ const Modal = ({ style={{ width, height: infoPanelHeight, - minHeight: infoPanelHeight ?? 200 + minHeight: infoPanelHeight ?? 200, }} className={classnames( 'mt-4 flex flex-col max-h-[90vh] overflow-y-auto rounded-lg outline-0', - className + className, )}>
{infoPanel} diff --git a/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.jsx b/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.jsx index 6e971d55..1bbc1a39 100644 --- a/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.jsx +++ b/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.jsx @@ -52,28 +52,28 @@ const NotificationCenter = ({ hasUnreadProp, buttonCoords, popperRef, - refetchHasUnreadHeader + refetchHasUnreadHeader, }) => { const { data, loading } = useQuery(GET_NOTIFICATIONS, { - pollInterval: 60000 + pollInterval: 60000, }) const [xOffset, setXoffset] = useState(300) const [showingUnread, setShowingUnread] = useState(false) const machines = R.compose( R.map(R.prop('name')), - R.indexBy(R.prop('deviceId')) + R.indexBy(R.prop('deviceId')), )(R.path(['machines'])(data) ?? []) const notifications = R.path(['notifications'])(data) ?? [] const [hasUnread, setHasUnread] = useState(hasUnreadProp) const [toggleClearNotification] = useMutation(TOGGLE_CLEAR_NOTIFICATION, { onError: () => console.error('Error while clearing notification'), - refetchQueries: () => ['getNotifications'] + refetchQueries: () => ['getNotifications'], }) const [clearAllNotifications] = useMutation(CLEAR_ALL_NOTIFICATIONS, { onError: () => console.error('Error while clearing all notifications'), - refetchQueries: () => ['getNotifications'] + refetchQueries: () => ['getNotifications'], }) useEffect(() => { @@ -103,7 +103,7 @@ const NotificationCenter = ({ valid={n.valid} toggleClear={() => toggleClearNotification({ - variables: { id: n.id, read: !n.read } + variables: { id: n.id, read: !n.read }, }) } /> @@ -121,7 +121,7 @@ const NotificationCenter = ({ className={classes.notificationIcon} style={{ top: buttonCoords?.y ?? 0, - left: buttonCoords?.x ? buttonCoords.x - xOffset : 0 + left: buttonCoords?.x ? buttonCoords.x - xOffset : 0, }}> {hasUnread &&
} diff --git a/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.module.css b/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.module.css index dd30252b..b10a914d 100644 --- a/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.module.css +++ b/packages/admin-ui/src/components/NotificationCenter/NotificationCenter.module.css @@ -6,8 +6,10 @@ box-shadow: 0 0 14px 0 rgba(0, 0, 0, 0.24); } -.container @media only screen and (max-width: 1920px) { - width: 30vw; +@media only screen and (max-width: 1920px) { + .container { + width: 30vw; + } } .header { @@ -75,7 +77,7 @@ } .unread { - background-color: var(--spring3) + background-color: var(--spring3); } .notificationRowIcon { @@ -83,11 +85,11 @@ } .notificationRowIcon > * { - margin-left: 24px + margin-left: 24px; } .readIconWrapper { - flex-grow: 1 + flex-grow: 1; } .unreadIcon { @@ -118,7 +120,7 @@ } .notificationBody { - margin: 0 + margin: 0; } .notificationSubtitle { @@ -142,4 +144,4 @@ height: 9px; background-color: var(--spring); border-radius: 50%; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/components/NotificationCenter/NotificationRow.jsx b/packages/admin-ui/src/components/NotificationCenter/NotificationRow.jsx index 37f4bfa1..d26fa425 100644 --- a/packages/admin-ui/src/components/NotificationCenter/NotificationRow.jsx +++ b/packages/admin-ui/src/components/NotificationCenter/NotificationRow.jsx @@ -12,37 +12,36 @@ import classes from './NotificationCenter.module.css' const types = { transaction: { display: 'Transactions', - icon: + icon: , }, highValueTransaction: { display: 'Transactions', - icon: + icon: , }, fiatBalance: { display: 'Maintenance', - icon: + icon: , }, cryptoBalance: { display: 'Maintenance', - icon: + icon: , }, compliance: { display: 'Compliance', - icon: + icon: , }, - error: { display: 'Error', icon: } + error: { display: 'Error', icon: }, } const NotificationRow = ({ id, type, - detail, message, deviceName, created, read, valid, - toggleClear + toggleClear, }) => { const typeDisplay = R.path([type, 'display'])(types) ?? null const icon = R.path([type, 'icon'])(types) ?? ( @@ -50,7 +49,7 @@ const NotificationRow = ({ ) const age = prettyMs(new Date().getTime() - new Date(created).getTime(), { compact: true, - verbose: true + verbose: true, }) const notificationTitle = typeDisplay && deviceName @@ -61,13 +60,13 @@ const NotificationRow = ({ const iconClass = { [classes.readIcon]: read, - [classes.unreadIcon]: !read + [classes.unreadIcon]: !read, } return (
{icon}
diff --git a/packages/admin-ui/src/components/NotificationCenter/index.js b/packages/admin-ui/src/components/NotificationCenter/index.js index 136ffb3b..fd2be253 100644 --- a/packages/admin-ui/src/components/NotificationCenter/index.js +++ b/packages/admin-ui/src/components/NotificationCenter/index.js @@ -1,2 +1,3 @@ import NotificationCenter from './NotificationCenter' + export default NotificationCenter diff --git a/packages/admin-ui/src/components/Popper.jsx b/packages/admin-ui/src/components/Popper.jsx index d440b987..4fe33c02 100644 --- a/packages/admin-ui/src/components/Popper.jsx +++ b/packages/admin-ui/src/components/Popper.jsx @@ -14,7 +14,7 @@ const Popover = ({ children, bgColor = white, className, ...props }) => { top: ['bottom'], bottom: ['top'], left: ['right'], - right: ['left'] + right: ['left'], } const modifiers = [ @@ -22,36 +22,36 @@ const Popover = ({ children, bgColor = white, className, ...props }) => { name: 'flip', enabled: R.defaultTo(false, props.flip), options: { - allowedAutoPlacements: flipPlacements[props.placement] - } + allowedAutoPlacements: flipPlacements[props.placement], + }, }, { name: 'preventOverflow', enabled: true, options: { - rootBoundary: 'scrollParent' - } + rootBoundary: 'scrollParent', + }, }, { name: 'offset', enabled: true, options: { - offset: [0, 10] - } + offset: [0, 10], + }, }, { name: 'arrow', enabled: R.defaultTo(true, props.showArrow), options: { - element: arrowRef - } + element: arrowRef, + }, }, { name: 'computeStyles', options: { - gpuAcceleration: false - } - } + gpuAcceleration: false, + }, + }, ] return ( diff --git a/packages/admin-ui/src/components/Popper.module.css b/packages/admin-ui/src/components/Popper.module.css index 66826b91..1beb7c5c 100644 --- a/packages/admin-ui/src/components/Popper.module.css +++ b/packages/admin-ui/src/components/Popper.module.css @@ -30,4 +30,4 @@ .tooltip[data-popper-placement^='right'] > div > span { left: -4px; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/components/SearchBox.jsx b/packages/admin-ui/src/components/SearchBox.jsx index dea9cb3d..f3f28ccd 100644 --- a/packages/admin-ui/src/components/SearchBox.jsx +++ b/packages/admin-ui/src/components/SearchBox.jsx @@ -12,7 +12,6 @@ const SearchBox = memo( filters = [], options = [], inputPlaceholder = '', - size, onChange, ...props }) => { @@ -20,7 +19,7 @@ const SearchBox = memo( const inputClasses = { 'flex flex-1 h-8 px-2 py-2 font-md items-center rounded-2xl bg-zircon text-comet': true, - 'rounded-b-none': popupOpen + 'rounded-b-none': popupOpen, } const innerOnChange = filters => onChange(filters) @@ -57,7 +56,7 @@ const SearchBox = memo( placeholder={inputPlaceholder} inputProps={{ className: 'font-bold', - ...params.inputProps + ...params.inputProps, }} /> ) @@ -74,10 +73,11 @@ const SearchBox = memo(
{children} - ) - }} /> - ); - } + ), + }} + /> + ) + }, ) export default SearchBox diff --git a/packages/admin-ui/src/components/SearchFilter.jsx b/packages/admin-ui/src/components/SearchFilter.jsx index 62a5d788..d15287a9 100644 --- a/packages/admin-ui/src/components/SearchFilter.jsx +++ b/packages/admin-ui/src/components/SearchFilter.jsx @@ -12,7 +12,7 @@ const SearchFilter = ({ filters, onFilterDelete, deleteAllFilters, - entries = 0 + entries = 0, }) => { return ( <> @@ -33,7 +33,7 @@ const SearchFilter = ({ {`${entries} ${singularOrPlural( entries, `entry`, - `entries` + `entries`, )}`} } { const separatorClasses = { 'w-7 h-[2px] border-2 z-1': true, 'border-spring': color === 'spring', - 'border-zodiac': color === 'zodiac' + 'border-zodiac': color === 'zodiac', } const separatorEmptyClasses = { 'w-7 h-[2px] border-2 z-1': true, 'border-dust': color === 'spring', - 'border-comet': color === 'zodiac' + 'border-comet': color === 'zodiac', } return ( diff --git a/packages/admin-ui/src/components/Stepper.module.css b/packages/admin-ui/src/components/Stepper.module.css index 34c7b5f7..9dccb967 100644 --- a/packages/admin-ui/src/components/Stepper.module.css +++ b/packages/admin-ui/src/components/Stepper.module.css @@ -9,4 +9,4 @@ height: 100%; width: 100%; overflow: visible; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/components/Subtitle.jsx b/packages/admin-ui/src/components/Subtitle.jsx index 1efb6f3a..31f24fbd 100644 --- a/packages/admin-ui/src/components/Subtitle.jsx +++ b/packages/admin-ui/src/components/Subtitle.jsx @@ -6,7 +6,7 @@ import { TL1 } from './typography' const Subtitle = memo(({ children, className, extraMarginTop }) => { const classNames = { 'text-comet my-4': true, - 'mt-18': extraMarginTop + 'mt-18': extraMarginTop, } return {children} diff --git a/packages/admin-ui/src/components/Tooltip.jsx b/packages/admin-ui/src/components/Tooltip.jsx index 1d825f77..ba73055f 100644 --- a/packages/admin-ui/src/components/Tooltip.jsx +++ b/packages/admin-ui/src/components/Tooltip.jsx @@ -4,30 +4,6 @@ import React, { useState, memo } from 'react' import Popper from 'src/components/Popper' import HelpIcon from 'src/styling/icons/action/help/zodiac.svg?react' -const useStyles = { - transparentButton: { - border: 'none', - backgroundColor: 'transparent', - outline: 'none', - cursor: 'pointer', - marginTop: 4 - }, - relativelyPositioned: { - position: 'relative' - }, - safeSpace: { - position: 'absolute', - backgroundColor: '#0000', - height: 40, - left: '-50%', - width: '200%' - }, - popoverContent: ({ width }) => ({ - width, - padding: [[10, 15]] - }) -} - const usePopperHandler = () => { const [helpPopperAnchorEl, setHelpPopperAnchorEl] = useState(null) @@ -50,7 +26,7 @@ const usePopperHandler = () => { helpPopperOpen, handleOpenHelpPopper, openHelpPopper, - handleCloseHelpPopper + handleCloseHelpPopper, } } diff --git a/packages/admin-ui/src/components/booleanPropertiesTable/BooleanPropertiesTable.jsx b/packages/admin-ui/src/components/booleanPropertiesTable/BooleanPropertiesTable.jsx index 10ace709..65f8bb00 100644 --- a/packages/admin-ui/src/components/booleanPropertiesTable/BooleanPropertiesTable.jsx +++ b/packages/admin-ui/src/components/booleanPropertiesTable/BooleanPropertiesTable.jsx @@ -25,27 +25,27 @@ const BooleanPropertiesTable = memo( const [editing, setEditing] = useState(forcedEditing) const initialValues = R.fromPairs( - elements.map(it => [it.name, data[it.name]?.toString() ?? 'false']) + elements.map(it => [it.name, data[it.name]?.toString() ?? 'false']), ) const validationSchema = Yup.object().shape( R.fromPairs( elements.map(it => [ it.name, - Yup.mixed().oneOf(['true', 'false', true, false]).required() - ]) - ) + Yup.mixed().oneOf(['true', 'false', true, false]).required(), + ]), + ), ) const innerSave = async values => { - const toBoolean = (num, _) => R.equals(num, 'true') + const toBoolean = num => R.equals(num, 'true') save(R.mapObjIndexed(toBoolean, R.filter(R.complement(R.isNil))(values))) setEditing(false) } const radioButtonOptions = [ { display: 'Yes', code: 'true' }, - { display: 'No', code: 'false' } + { display: 'No', code: 'false' }, ] return (
@@ -117,7 +117,7 @@ const BooleanPropertiesTable = memo(
) - } + }, ) export default BooleanPropertiesTable diff --git a/packages/admin-ui/src/components/buttons/ActionButton.jsx b/packages/admin-ui/src/components/buttons/ActionButton.jsx index 43666387..6c0ca257 100644 --- a/packages/admin-ui/src/components/buttons/ActionButton.jsx +++ b/packages/admin-ui/src/components/buttons/ActionButton.jsx @@ -21,7 +21,7 @@ const ActionButton = memo( [moduleStyles.secondary]: color === 'secondary', [moduleStyles.spring]: color === 'spring', [moduleStyles.tomato]: color === 'tomato', - [moduleStyles.center]: center + [moduleStyles.center]: center, } return ( @@ -35,7 +35,7 @@ const ActionButton = memo(
@@ -43,7 +43,7 @@ const ActionButton = memo( {children &&
{children}
} ) - } + }, ) export default ActionButton diff --git a/packages/admin-ui/src/components/buttons/BaseButton.styles.js b/packages/admin-ui/src/components/buttons/BaseButton.styles.js index 3d49746e..b0a034c9 100644 --- a/packages/admin-ui/src/components/buttons/BaseButton.styles.js +++ b/packages/admin-ui/src/components/buttons/BaseButton.styles.js @@ -4,18 +4,18 @@ import { subheaderColor, subheaderDarkColor, offColor, - offDarkColor + offDarkColor, } from 'src/styling/variables' const colors = (color1, color2, color3) => { return { backgroundColor: color1, '&:hover': { - backgroundColor: color2 + backgroundColor: color2, }, '&:active': { - backgroundColor: color3 - } + backgroundColor: color3, + }, } } @@ -30,23 +30,23 @@ export default { height: buttonHeight, color: fontColor, '&:active': { - color: white - } + color: white, + }, }, primary: { extend: colors(subheaderColor, subheaderDarkColor, offColor), '&:active': { color: white, '& $buttonIcon': { - display: 'none' + display: 'none', }, '& $buttonIconActive': { - display: 'block' - } + display: 'block', + }, }, '& $buttonIconActive': { - display: 'none' - } + display: 'none', + }, }, secondary: { extend: colors(offColor, offDarkColor, white), @@ -54,17 +54,17 @@ export default { '&:active': { color: fontColor, '& $buttonIcon': { - display: 'flex' + display: 'flex', }, '& $buttonIconActive': { - display: 'none' - } + display: 'none', + }, }, '& $buttonIcon': { - display: 'none' + display: 'none', }, '& $buttonIconActive': { - display: 'flex' - } - } + display: 'flex', + }, + }, } diff --git a/packages/admin-ui/src/components/buttons/Button.jsx b/packages/admin-ui/src/components/buttons/Button.jsx index a50eac4a..95342ac1 100644 --- a/packages/admin-ui/src/components/buttons/Button.jsx +++ b/packages/admin-ui/src/components/buttons/Button.jsx @@ -29,15 +29,15 @@ const ActionButton = memo( 'text-white', { [moduleStyles.buttonSm]: size === 'sm', - [moduleStyles.buttonXl]: size === 'xl' - } + [moduleStyles.buttonXl]: size === 'xl', + }, )} {...props}> {children}
) - } + }, ) export default ActionButton diff --git a/packages/admin-ui/src/components/buttons/Button.module.css b/packages/admin-ui/src/components/buttons/Button.module.css index be15b02e..c5c5c432 100644 --- a/packages/admin-ui/src/components/buttons/Button.module.css +++ b/packages/admin-ui/src/components/buttons/Button.module.css @@ -14,13 +14,13 @@ .buttonXl { composes: h1 from '../typography/typography.module.css'; height: 61px; - border-radius: 15px + border-radius: 15px; } .buttonSm { height: 32px; padding: 0 16px; - border-radius: 8px + border-radius: 8px; } .button:disabled { @@ -46,4 +46,4 @@ margin-top: 2px; background-color: var(--spring2); box-shadow: 0 2px var(--spring4); -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/components/buttons/FeatureButton.jsx b/packages/admin-ui/src/components/buttons/FeatureButton.jsx index 73fb0a63..da65a2d9 100644 --- a/packages/admin-ui/src/components/buttons/FeatureButton.jsx +++ b/packages/admin-ui/src/components/buttons/FeatureButton.jsx @@ -11,7 +11,7 @@ const FeatureButton = memo( classes.baseButton, classes.roundButton, classes.primary, - className + className, )} {...props}> {Icon && ( @@ -23,7 +23,7 @@ const FeatureButton = memo(
@@ -31,7 +31,7 @@ const FeatureButton = memo( {children} ) - } + }, ) export default FeatureButton diff --git a/packages/admin-ui/src/components/buttons/IDButton.jsx b/packages/admin-ui/src/components/buttons/IDButton.jsx index 437821a5..592557de 100644 --- a/packages/admin-ui/src/components/buttons/IDButton.jsx +++ b/packages/admin-ui/src/components/buttons/IDButton.jsx @@ -11,7 +11,6 @@ const IDButton = memo( className, Icon, InverseIcon, - popoverWidth = 152, children, popoverClassname, ...props @@ -25,11 +24,11 @@ const IDButton = memo( [classes.idButton]: true, [classes.primary]: true, [classes.open]: open, - [classes.closed]: !open + [classes.closed]: !open, } const iconClassNames = { - [classes.buttonIcon]: true + [classes.buttonIcon]: true, } const handleClick = event => { @@ -74,7 +73,7 @@ const IDButton = memo( ) - } + }, ) export default IDButton diff --git a/packages/admin-ui/src/components/buttons/Link.jsx b/packages/admin-ui/src/components/buttons/Link.jsx index 1a4fdad2..39a721d1 100644 --- a/packages/admin-ui/src/components/buttons/Link.jsx +++ b/packages/admin-ui/src/components/buttons/Link.jsx @@ -10,7 +10,7 @@ const Link = memo( [classes.primary]: color === 'primary', [classes.secondary]: color === 'secondary', [classes.noColor]: color === 'noColor', - [classes.action]: color === 'action' + [classes.action]: color === 'action', } return ( @@ -21,7 +21,7 @@ const Link = memo( {children} ) - } + }, ) export default Link diff --git a/packages/admin-ui/src/components/buttons/Link.module.css b/packages/admin-ui/src/components/buttons/Link.module.css index b780f5c6..ee8244f5 100644 --- a/packages/admin-ui/src/components/buttons/Link.module.css +++ b/packages/admin-ui/src/components/buttons/Link.module.css @@ -44,4 +44,4 @@ .action:hover { box-shadow: none; background-color: rgba(72, 246, 148, 0.8); -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/components/buttons/SubpageButton.jsx b/packages/admin-ui/src/components/buttons/SubpageButton.jsx index bf1de782..ed689e41 100644 --- a/packages/admin-ui/src/components/buttons/SubpageButton.jsx +++ b/packages/admin-ui/src/components/buttons/SubpageButton.jsx @@ -12,14 +12,14 @@ const SubpageButton = memo( InverseIcon, toggle, forceDisable = false, - children + children, }) => { const [active, setActive] = useState(false) const isActive = forceDisable ? false : active const classNames = { [classes.button]: true, [classes.normal]: !isActive, - [classes.active]: isActive + [classes.active]: isActive, } const normalButton = @@ -29,14 +29,14 @@ const SubpageButton = memo(

{children}

@@ -56,7 +56,7 @@ const SubpageButton = memo( {isActive ? activeButton : normalButton} ) - } + }, ) export default SubpageButton diff --git a/packages/admin-ui/src/components/buttons/SubpageButton.module.css b/packages/admin-ui/src/components/buttons/SubpageButton.module.css index ba97a0f4..84babdbc 100644 --- a/packages/admin-ui/src/components/buttons/SubpageButton.module.css +++ b/packages/admin-ui/src/components/buttons/SubpageButton.module.css @@ -27,7 +27,7 @@ } .active:hover { - background-color: var(--comet); + background-color: var(--comet); } .buttonIcon { diff --git a/packages/admin-ui/src/components/buttons/SubpageButton.styles.js b/packages/admin-ui/src/components/buttons/SubpageButton.styles.js index 5e2a2ade..a5afd0b6 100644 --- a/packages/admin-ui/src/components/buttons/SubpageButton.styles.js +++ b/packages/admin-ui/src/components/buttons/SubpageButton.styles.js @@ -8,10 +8,10 @@ export default { extend: baseButton, padding: 0, color: white, - borderRadius: baseButton.height / 2 + borderRadius: baseButton.height / 2, }, normalButton: { - width: baseButton.height + width: baseButton.height, }, activeButton: { display: 'flex', @@ -21,26 +21,26 @@ export default { fontWeight: 'bold', padding: '0 5px', '&:hover': { - backgroundColor: offColor - } + backgroundColor: offColor, + }, }, buttonIcon: { width: 16, height: 16, overflow: 'visible', '& g': { - strokeWidth: 1.8 - } + strokeWidth: 1.8, + }, }, buttonIconActiveLeft: { marginRight: 12, - marginLeft: 4 + marginLeft: 4, }, buttonIconActiveRight: { marginRight: 5, - marginLeft: 20 + marginLeft: 20, }, white: { - color: white - } + color: white, + }, } diff --git a/packages/admin-ui/src/components/buttons/index.js b/packages/admin-ui/src/components/buttons/index.js index e227aaa5..10cbe942 100644 --- a/packages/admin-ui/src/components/buttons/index.js +++ b/packages/admin-ui/src/components/buttons/index.js @@ -15,5 +15,5 @@ export { IDButton, AddButton, SupportLinkButton, - SubpageButton + SubpageButton, } diff --git a/packages/admin-ui/src/components/date-range-picker/Calendar.jsx b/packages/admin-ui/src/components/date-range-picker/Calendar.jsx index bd3dd81e..a95102ad 100644 --- a/packages/admin-ui/src/components/date-range-picker/Calendar.jsx +++ b/packages/admin-ui/src/components/date-range-picker/Calendar.jsx @@ -10,7 +10,7 @@ import { lastDayOfMonth, startOfMonth, startOfWeek, - sub + sub, } from 'date-fns/fp' import * as R from 'ramda' import React, { useState } from 'react' @@ -24,7 +24,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => { const [currentDisplayedMonth, setCurrentDisplayedMonth] = useState(new Date()) const weekdays = Array.from(Array(7)).map((_, i) => - format('EEEEE', add({ days: i }, startOfWeek(new Date()))) + format('EEEEE', add({ days: i }, startOfWeek(new Date()))), ) const monthLength = month => getDaysInMonth(month) @@ -33,21 +33,21 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => { const lastMonth = sub({ months: 1 }, month) const lastMonthRange = R.range(0, getDay(startOfMonth(month))).reverse() const lastMonthDays = R.map(i => - sub({ days: i }, lastDayOfMonth(lastMonth)) + sub({ days: i }, lastDayOfMonth(lastMonth)), )(lastMonthRange) const thisMonthRange = R.range(0, monthLength(month)) const thisMonthDays = R.map(i => add({ days: i }, startOfMonth(month)))( - thisMonthRange + thisMonthRange, ) const nextMonth = add({ months: 1 }, month) const nextMonthRange = R.range( 0, - 42 - lastMonthDays.length - thisMonthDays.length + 42 - lastMonthDays.length - thisMonthDays.length, ) const nextMonthDays = R.map(i => add({ days: i }, startOfMonth(nextMonth)))( - nextMonthRange + nextMonthRange, ) return R.concat(R.concat(lastMonthDays, thisMonthDays), nextMonthDays) @@ -63,7 +63,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => { isSameMonth(minDate, prevMonth) || differenceInMonths(minDate, prevMonth) > 0 ? prevMonth - : currentDisplayedMonth + : currentDisplayedMonth, ) } } @@ -75,7 +75,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => { isSameMonth(maxDate, nextMonth) || differenceInMonths(nextMonth, maxDate) > 0 ? nextMonth - : currentDisplayedMonth + : currentDisplayedMonth, ) } } @@ -91,7 +91,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => { {`${format('MMMM', currentDisplayedMonth)} ${format( 'yyyy', - currentDisplayedMonth + currentDisplayedMonth, )}`}
diff --git a/packages/admin-ui/src/components/layout/Sidebar.module.css b/packages/admin-ui/src/components/layout/Sidebar.module.css index e1bf6b83..0f0f8df9 100644 --- a/packages/admin-ui/src/components/layout/Sidebar.module.css +++ b/packages/admin-ui/src/components/layout/Sidebar.module.css @@ -39,7 +39,7 @@ } .link::after { - content: ""; + content: ''; display: block; background: var(--zodiac); width: 4px; @@ -103,4 +103,4 @@ .stepperPast { border: 1px solid var(--zodiac); -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/components/layout/TitleSection.jsx b/packages/admin-ui/src/components/layout/TitleSection.jsx index c42022ac..c22b4048 100644 --- a/packages/admin-ui/src/components/layout/TitleSection.jsx +++ b/packages/admin-ui/src/components/layout/TitleSection.jsx @@ -15,13 +15,13 @@ const TitleSection = ({ buttons = [], children, appendix, - appendixRight + appendixRight, }) => { return (
{title} @@ -44,7 +44,7 @@ const TitleSection = ({ {button.text} - ) + ), )} )} diff --git a/packages/admin-ui/src/components/machineActions/DiagnosticsModal.jsx b/packages/admin-ui/src/components/machineActions/DiagnosticsModal.jsx index d9519634..fb002172 100644 --- a/packages/admin-ui/src/components/machineActions/DiagnosticsModal.jsx +++ b/packages/admin-ui/src/components/machineActions/DiagnosticsModal.jsx @@ -12,7 +12,7 @@ const STATES = { EMPTY: 'EMPTY', RUNNING: 'RUNNING', FAILURE: 'FAILURE', - FILLED: 'FILLED' + FILLED: 'FILLED', } const MACHINE = gql` @@ -47,7 +47,7 @@ const MACHINE_LOGS = gql` const createCsv = async ({ machineLogsCsv }) => { const machineLogs = new Blob([machineLogsCsv], { - type: 'text/plain;charset=utf-8' + type: 'text/plain;charset=utf-8', }) FileSaver.saveAs(machineLogs, 'machineLogs.csv') @@ -59,11 +59,11 @@ const DiagnosticsModal = ({ onClose, deviceId, sendAction }) => { let timeout = null const [fetchSummary, { loading }] = useLazyQuery(MACHINE_LOGS, { - onCompleted: data => createCsv(data) + onCompleted: data => createCsv(data), }) const { data, stopPolling, startPolling } = useQuery(MACHINE, { - variables: { deviceId } + variables: { deviceId }, }) useEffect(() => { @@ -168,8 +168,8 @@ const DiagnosticsModal = ({ onClose, deviceId, sendAction }) => { variables: { from: subMinutes(new Date(timestamp), 5), deviceId, - limit: 500 - } + limit: 500, + }, }) }} className="mt-auto ml-auto mr-2 mb-0"> diff --git a/packages/admin-ui/src/components/machineActions/MachineActions.jsx b/packages/admin-ui/src/components/machineActions/MachineActions.jsx index 954b815a..c8ae031e 100644 --- a/packages/admin-ui/src/components/machineActions/MachineActions.jsx +++ b/packages/admin-ui/src/components/machineActions/MachineActions.jsx @@ -49,7 +49,7 @@ const isStaticState = machineState => { 'unpaired', 'maintenance', 'virgin', - 'wifiList' + 'wifiList', ] return staticStates.includes(machineState) } @@ -73,7 +73,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { const [fetchMachineEvents, { loading: loadingEvents }] = useLazyQuery( MACHINE, - preflightOptions + preflightOptions, ) const [simpleMachineAction] = useMutation(MACHINE_ACTION) @@ -86,7 +86,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { onCompleted: () => { onActionSuccess && onActionSuccess() setAction({ display: action.display, command: null }) - } + }, }) const confirmDialogOpen = Boolean(action.command) @@ -100,7 +100,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { ? warningMessage : null setAction({ ...actionToDo, message }) - } + }, }) fetchMachineEvents() } @@ -118,7 +118,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { setAction({ command: 'rename', display: 'Rename', - confirmationMessage: 'Write the new name for this machine' + confirmationMessage: 'Write the new name for this machine', }) }> Rename @@ -131,7 +131,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { onClick={() => setAction({ command: 'unpair', - display: 'Unpair' + display: 'Unpair', }) }> Unpair @@ -144,7 +144,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { onClick={() => setAction({ command: 'reboot', - display: 'Reboot' + display: 'Reboot', }) }> Reboot @@ -159,7 +159,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { command: 'shutdown', display: 'Shutdown', message: - 'In order to bring it back online, the machine will need to be visited and its power reset.' + 'In order to bring it back online, the machine will need to be visited and its power reset.', }) }> Shutdown @@ -172,7 +172,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { onClick={() => { machineStatusPreflight({ command: 'restartServices', - display: 'Restart services for' + display: 'Restart services for', }) }}> Restart services @@ -188,7 +188,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { command: 'emptyUnit', display: 'Empty', message: - "Triggering this action will move all cash inside the machine towards its cashbox (if possible), allowing for the collection of cash from the machine via only its cashbox. Depending on how full the cash units are, it's possible that this action will need to be used more than once to ensure that the unit is left completely empty." + "Triggering this action will move all cash inside the machine towards its cashbox (if possible), allowing for the collection of cash from the machine via only its cashbox. Depending on how full the cash units are, it's possible that this action will need to be used more than once to ensure that the unit is left completely empty.", }) }}> Empty Unit @@ -205,7 +205,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { command: 'refillUnit', display: 'Refill', message: - 'Triggering this action will refill the recyclers in this machine, by using bills present in its cassettes. This action may require manual operation of the cassettes and close attention to make sure that the denominations in the cassettes match the denominations in the recyclers.' + 'Triggering this action will refill the recyclers in this machine, by using bills present in its cassettes. This action may require manual operation of the cassettes and close attention to make sure that the denominations in the cassettes match the denominations in the recyclers.', }) }}> Refill Unit @@ -228,8 +228,8 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { simpleMachineAction({ variables: { deviceId: machine.deviceId, - action: 'diagnostics' - } + action: 'diagnostics', + }, }) } deviceId={machine.deviceId} @@ -253,8 +253,8 @@ const MachineActions = memo(({ machine, onActionSuccess }) => { variables: { deviceId: machine.deviceId, action: `${action?.command}`, - ...(action?.command === 'rename' && { newName: value }) - } + ...(action?.command === 'rename' && { newName: value }), + }, }) }} onDismissed={() => { diff --git a/packages/admin-ui/src/components/single-row-table/SingleRowTable.jsx b/packages/admin-ui/src/components/single-row-table/SingleRowTable.jsx index 8e7cad48..490ac0c6 100644 --- a/packages/admin-ui/src/components/single-row-table/SingleRowTable.jsx +++ b/packages/admin-ui/src/components/single-row-table/SingleRowTable.jsx @@ -7,7 +7,7 @@ import { TBody, Td, Th, - Tr + Tr, } from 'src/components/fake-table/Table' import EditIcon from 'src/styling/icons/action/edit/white.svg?react' @@ -19,7 +19,7 @@ const SingleRowTable = ({ title, items, onEdit, - className + className, }) => { return ( <> diff --git a/packages/admin-ui/src/components/table/EmptyTable.jsx b/packages/admin-ui/src/components/table/EmptyTable.jsx index 5859bdfc..96f653c5 100644 --- a/packages/admin-ui/src/components/table/EmptyTable.jsx +++ b/packages/admin-ui/src/components/table/EmptyTable.jsx @@ -8,7 +8,7 @@ const EmptyTable = memo(({ message, className }) => {

{message}

diff --git a/packages/admin-ui/src/components/table/Table.jsx b/packages/admin-ui/src/components/table/Table.jsx index 980220b9..c54bedd7 100644 --- a/packages/admin-ui/src/components/table/Table.jsx +++ b/packages/admin-ui/src/components/table/Table.jsx @@ -7,7 +7,7 @@ const Table = memo(({ className, children, ...props }) => { {...props} className={classnames( 'table-fixed border-separate border-spacing-0', - className + className, )}> {children} diff --git a/packages/admin-ui/src/components/table/TableCell.jsx b/packages/admin-ui/src/components/table/TableCell.jsx index 204915ff..36e8501a 100644 --- a/packages/admin-ui/src/components/table/TableCell.jsx +++ b/packages/admin-ui/src/components/table/TableCell.jsx @@ -7,7 +7,7 @@ const TableCell = memo( ({ colspan, rightAlign, className, children, ...props }) => { const styles = { [classes.tableCell]: true, - 'text-right': rightAlign + 'text-right': rightAlign, } return ( @@ -18,7 +18,7 @@ const TableCell = memo( {children} ) - } + }, ) export default TableCell diff --git a/packages/admin-ui/src/components/table/TableHeader.jsx b/packages/admin-ui/src/components/table/TableHeader.jsx index cd1b895e..9653d1ba 100644 --- a/packages/admin-ui/src/components/table/TableHeader.jsx +++ b/packages/admin-ui/src/components/table/TableHeader.jsx @@ -5,7 +5,7 @@ const TableHeaderCell = memo( ({ rightAlign, children, className, ...props }) => { const styles = { 'bg-zodiac text-white py-0 px-6 h-8 text-sm text-left': true, - 'text-right': rightAlign + 'text-right': rightAlign, } return ( @@ -13,7 +13,7 @@ const TableHeaderCell = memo( {children} ) - } + }, ) export default TableHeaderCell diff --git a/packages/admin-ui/src/components/table/TableRow.jsx b/packages/admin-ui/src/components/table/TableRow.jsx index 8c026bd7..916416ea 100644 --- a/packages/admin-ui/src/components/table/TableRow.jsx +++ b/packages/admin-ui/src/components/table/TableRow.jsx @@ -8,7 +8,7 @@ const TableRow = memo( 'h-8': !header && size === 'sm', 'h-9 font-bold text-base ': !header && size === 'lg', 'bg-misty-rose': error, - 'bg-spring3': success + 'bg-spring3': success, } return ( @@ -16,7 +16,7 @@ const TableRow = memo( {children} ) - } + }, ) export default TableRow diff --git a/packages/admin-ui/src/components/table/index.js b/packages/admin-ui/src/components/table/index.js index 289d0b01..85ccc33d 100644 --- a/packages/admin-ui/src/components/table/index.js +++ b/packages/admin-ui/src/components/table/index.js @@ -15,5 +15,5 @@ export { TableHead, TableHeader, TableRow, - TableBody + TableBody, } diff --git a/packages/admin-ui/src/components/tables/DataTable.jsx b/packages/admin-ui/src/components/tables/DataTable.jsx index 29bba105..a4e3a97b 100644 --- a/packages/admin-ui/src/components/tables/DataTable.jsx +++ b/packages/admin-ui/src/components/tables/DataTable.jsx @@ -5,7 +5,7 @@ import { AutoSizer, List, CellMeasurer, - CellMeasurerCache + CellMeasurerCache, } from 'react-virtualized' import { Table, @@ -13,7 +13,7 @@ import { THead, Tr, Td, - Th + Th, } from 'src/components/fake-table/Table' import { H4 } from 'src/components/typography' import ExpandClosedIcon from 'src/styling/icons/action/expand/closed.svg?react' @@ -40,7 +40,7 @@ const Row = ({ const trClasses = { 'cursor-pointer': hasPointer, 'border-2 border-transparent': true, - 'border-2 border-zircon shadow-md': expanded + 'border-2 border-zircon shadow-md': expanded, } return ( @@ -79,7 +79,7 @@ const Row = ({
@@ -126,7 +126,7 @@ const DataTable = ({ const cache = new CellMeasurerCache({ defaultHeight: 58, - fixedWidth: true + fixedWidth: true, }) function rowRenderer({ index, key, parent, style }) { @@ -168,12 +168,12 @@ const DataTable = ({
diff --git a/packages/admin-ui/src/components/typography/index.jsx b/packages/admin-ui/src/components/typography/index.jsx index 58a0c026..a0776a6e 100644 --- a/packages/admin-ui/src/components/typography/index.jsx +++ b/packages/admin-ui/src/components/typography/index.jsx @@ -7,7 +7,7 @@ function H1({ children, noMargin, className, ...props }) { const classNames = { [styles.h1]: true, [styles.noMargin]: noMargin, - [className]: !!className + [className]: !!className, } return ( @@ -21,7 +21,7 @@ function H2({ children, noMargin, className, ...props }) { const classNames = { [styles.h2]: true, [styles.noMargin]: noMargin, - [className]: !!className + [className]: !!className, } return ( @@ -35,7 +35,7 @@ function H3({ children, noMargin, className, ...props }) { const classNames = { [styles.h3]: true, [styles.noMargin]: noMargin, - [className]: !!className + [className]: !!className, } return ( @@ -49,7 +49,7 @@ function H4({ children, noMargin, className, ...props }) { const classNames = { [styles.h4]: true, [styles.noMargin]: noMargin, - [className]: !!className + [className]: !!className, } return ( @@ -63,7 +63,7 @@ function H5({ children, noMargin, className, ...props }) { const classNames = { [styles.h5]: true, [styles.noMargin]: noMargin, - [className]: !!className + [className]: !!className, } return ( @@ -90,7 +90,7 @@ function pBuilder(elementClass) { [className]: !!className, [styles[elementClass]]: elementClass, [styles.inline]: inline, - [styles.noMargin]: noMargin + [styles.noMargin]: noMargin, } return (

@@ -115,5 +115,5 @@ export { Mono, Label1, Label2, - Label3 + Label3, } diff --git a/packages/admin-ui/src/components/typography/styles.js b/packages/admin-ui/src/components/typography/styles.js index 1d3f9fc8..d86386ba 100644 --- a/packages/admin-ui/src/components/typography/styles.js +++ b/packages/admin-ui/src/components/typography/styles.js @@ -7,132 +7,132 @@ import { fontSize5, fontPrimary, fontSecondary, - fontMonospaced + fontMonospaced, } from 'src/styling/variables' const base = { lineHeight: '120%', - color: fontColor + color: fontColor, } export default { base: { lineHeight: '120%', - color: fontColor + color: fontColor, }, h1: { extend: base, fontSize: fontSize1, fontFamily: fontPrimary, - fontWeight: 900 + fontWeight: 900, }, h2: { extend: base, fontSize: fontSize2, fontFamily: fontPrimary, - fontWeight: 900 + fontWeight: 900, }, h3: { extend: base, fontSize: fontSize4, fontFamily: fontPrimary, - fontWeight: 900 + fontWeight: 900, }, h4: { extend: base, fontSize: fontSize4, fontFamily: fontPrimary, - fontWeight: 700 + fontWeight: 700, }, h5: { extend: base, fontSize: fontSize3, fontFamily: fontPrimary, - fontWeight: 700 + fontWeight: 700, }, p: { ...base, fontSize: fontSize4, fontFamily: fontSecondary, - fontWeight: 500 + fontWeight: 500, }, tl1: { extend: base, fontSize: fontSize2, fontFamily: fontSecondary, - fontWeight: 700 + fontWeight: 700, }, tl2: { extend: base, fontSize: fontSize4, fontFamily: fontSecondary, - fontWeight: 700 + fontWeight: 700, }, info1: { extend: base, fontSize: fontSize1, fontFamily: fontSecondary, - fontWeight: 700 + fontWeight: 700, }, info2: { extend: base, fontSize: fontSize3, fontFamily: fontSecondary, - fontWeight: 700 + fontWeight: 700, }, info3: { extend: base, fontSize: fontSize3, fontFamily: fontSecondary, - fontWeight: 500 + fontWeight: 500, }, mono: { extend: base, fontSize: fontSize4, fontFamily: fontMonospaced, - fontWeight: 500 + fontWeight: 500, }, monoBold: { - fontWeight: 700 + fontWeight: 700, }, monoSmall: { - fontSize: fontSize5 + fontSize: fontSize5, }, inputFont: { fontSize: fontSize2, fontFamily: fontSecondary, fontWeight: 500, lineHeight: '110%', - color: fontColor + color: fontColor, }, regularLabel: { fontSize: fontSize4, fontFamily: fontSecondary, fontWeight: 500, - lineHeight: '110%' + lineHeight: '110%', }, label1: { fontSize: fontSize5, fontFamily: fontSecondary, fontWeight: 500, - color: fontColor + color: fontColor, }, label2: { fontSize: fontSize5, fontFamily: fontSecondary, fontWeight: 700, - color: fontColor + color: fontColor, }, label3: { fontSize: fontSize4, fontFamily: fontSecondary, fontWeight: 500, - color: fontColor + color: fontColor, }, inline: { - display: 'inline' + display: 'inline', }, noMargin: { - margin: 0 - } + margin: 0, + }, } diff --git a/packages/admin-ui/src/index.jsx b/packages/admin-ui/src/index.jsx index 638af18d..728ca19d 100644 --- a/packages/admin-ui/src/index.jsx +++ b/packages/admin-ui/src/index.jsx @@ -7,5 +7,5 @@ ReactDOM.render( , - document.getElementById('root') + document.getElementById('root'), ) diff --git a/packages/admin-ui/src/pages/AddMachine/AddMachine.jsx b/packages/admin-ui/src/pages/AddMachine/AddMachine.jsx index 55930c41..4c6fdebb 100644 --- a/packages/admin-ui/src/pages/AddMachine/AddMachine.jsx +++ b/packages/admin-ui/src/pages/AddMachine/AddMachine.jsx @@ -58,7 +58,7 @@ const QrCodeComponent = ({ qrCode, name, count, onPaired }) => { if (hasNewMachine) { timeout.current = setTimeout( () => onPaired(addedMachine), - CLOSE_SCREEN_TIMEOUT + CLOSE_SCREEN_TIMEOUT, ) } @@ -111,7 +111,7 @@ const QrCodeComponent = ({ qrCode, name, count, onPaired }) => { } const initialValues = { - name: '' + name: '', } const validationSchema = Yup.object().shape({ @@ -124,9 +124,9 @@ const validationSchema = Yup.object().shape({ (value, context) => !R.includes( R.toLower(value), - R.map(R.toLower, context.options.context.machineNames) - ) - ) + R.map(R.toLower, context.options.context.machineNames), + ), + ), }) const MachineNameComponent = ({ nextStep, setQrCode, setName }) => { @@ -138,7 +138,7 @@ const MachineNameComponent = ({ nextStep, setQrCode, setName }) => { setQrCode(createPairingTotem) nextStep() }, - onError: e => console.log(e) + onError: e => console.log(e), }) const { data } = useQuery(GET_MACHINES) @@ -147,7 +147,7 @@ const MachineNameComponent = ({ nextStep, setQrCode, setName }) => { const uniqueNameValidator = value => { try { validationSchema.validateSync(value, { - context: { machineNames: machineNames } + context: { machineNames: machineNames }, }) } catch (error) { return error @@ -189,12 +189,12 @@ const MachineNameComponent = ({ nextStep, setQrCode, setName }) => { const steps = [ { label: 'Machine name', - component: MachineNameComponent + component: MachineNameComponent, }, { label: 'Scan QR code', - component: QrCodeComponent - } + component: QrCodeComponent, + }, ] const renderStepper = (step, it, idx) => { @@ -208,7 +208,7 @@ const renderStepper = (step, it, idx) => { className={classnames({ 'mr-6 text-comet': true, 'text-zodiac font-bold': active, - 'text-zodiac': past + 'text-zodiac': past, })}> {it.label} @@ -219,7 +219,7 @@ const renderStepper = (step, it, idx) => {

)} diff --git a/packages/admin-ui/src/pages/Analytics/Analytics.jsx b/packages/admin-ui/src/pages/Analytics/Analytics.jsx index 3dbb5f3f..43a4b21d 100644 --- a/packages/admin-ui/src/pages/Analytics/Analytics.jsx +++ b/packages/admin-ui/src/pages/Analytics/Analytics.jsx @@ -26,29 +26,29 @@ const REPRESENTING_OPTIONS = [ { code: 'overTime', display: 'Over time' }, { code: 'volumeOverTime', display: 'Volume' }, { code: 'topMachines', display: 'Top machines' }, - { code: 'hourOfTheDay', display: 'Hour of the day' } + { code: 'hourOfTheDay', display: 'Hour of the day' }, ] const PERIOD_OPTIONS = [ { code: 'day', display: 'Last 24 hours' }, { code: 'threeDays', display: 'Last 3 days' }, { code: 'week', display: 'Last 7 days' }, - { code: 'month', display: 'Last 30 days' } + { code: 'month', display: 'Last 30 days' }, ] const TIME_OPTIONS = { day: DAY, threeDays: 3 * DAY, week: WEEK, - month: MONTH + month: MONTH, } const DAY_OPTIONS = R.map( it => ({ code: R.toLower(it), - display: it + display: it, }), Array.from(Array(7)).map((_, i) => - format('EEEE', add({ days: i }, startOfWeek(new Date()))) - ) + format('EEEE', add({ days: i }, startOfWeek(new Date()))), + ), ) const GET_TRANSACTIONS = gql` @@ -112,7 +112,7 @@ const OverviewEntry = ({ label, value, oldValue, currency }) => { const growthClasses = { 'font-bold': true, 'text-malachite': R.gt(value, oldValue), - 'text-tomato': R.gt(oldValue, value) + 'text-tomato': R.gt(oldValue, value), } return ( @@ -139,8 +139,8 @@ const Analytics = () => { variables: { from: subDays(65, endOfToday()), until: endOfToday(), - excludeTestingCustomers: true - } + excludeTestingCustomers: true, + }, }) const { data: configResponse, loading: configLoading } = useQuery(GET_DATA) @@ -148,7 +148,7 @@ const Analytics = () => { const [period, setPeriod] = useState(PERIOD_OPTIONS[0]) const [machine, setMachine] = useState(MACHINE_OPTIONS[0]) const [selectedDay, setSelectedDay] = useState( - R.equals(representing.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null + R.equals(representing.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null, ) const loading = txLoading || configLoading @@ -175,20 +175,20 @@ const Analytics = () => { tx => (!tx.dispensed || !tx.expired) && (tx.sendConfirmed || tx.dispense) && - !tx.hasError - ) + !tx.hasError, + ), ) ?? [] const machineOptions = R.clone(MACHINE_OPTIONS) R.forEach( m => machineOptions.push({ code: m.deviceId, display: m.name }), - machines + machines, ) const machineTxs = R.filter( tx => (machine.code === 'all' ? true : tx.deviceId === machine.code), - data + data, ) const filteredData = timeInterval => ({ @@ -213,35 +213,35 @@ const Analytics = () => { txDay < Date.now() - TIME_OPTIONS[timeInterval] && txDay >= Date.now() - 2 * TIME_OPTIONS[timeInterval] ) - }) ?? [] + }) ?? [], }) const txs = { current: filteredData(period.code).current.length, - previous: filteredData(period.code).previous.length + previous: filteredData(period.code).previous.length, } const median = values => (values.length === 0 ? 0 : R.median(values)) const medianAmount = { current: median(R.map(d => d.fiat, filteredData(period.code).current)), - previous: median(R.map(d => d.fiat, filteredData(period.code).previous)) + previous: median(R.map(d => d.fiat, filteredData(period.code).previous)), } const txVolume = { current: R.sum(R.map(d => d.fiat, filteredData(period.code).current)), - previous: R.sum(R.map(d => d.fiat, filteredData(period.code).previous)) + previous: R.sum(R.map(d => d.fiat, filteredData(period.code).previous)), } const commissions = { current: R.sum(R.map(d => d.profit, filteredData(period.code).current)), - previous: R.sum(R.map(d => d.profit, filteredData(period.code).previous)) + previous: R.sum(R.map(d => d.profit, filteredData(period.code).previous)), } const handleRepresentationChange = newRepresentation => { setRepresenting(newRepresentation) setSelectedDay( - R.equals(newRepresentation.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null + R.equals(newRepresentation.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null, ) } diff --git a/packages/admin-ui/src/pages/Analytics/components/tooltips/GraphTooltip.jsx b/packages/admin-ui/src/pages/Analytics/components/tooltips/GraphTooltip.jsx index 9c11e46e..3aa8057f 100644 --- a/packages/admin-ui/src/pages/Analytics/components/tooltips/GraphTooltip.jsx +++ b/packages/admin-ui/src/pages/Analytics/components/tooltips/GraphTooltip.jsx @@ -13,20 +13,19 @@ const GraphTooltip = ({ coords, data, dateInterval, - period, currency, - representing + representing, }) => { const formattedDateInterval = !R.includes('hourOfDay', representing.code) ? [ formatDate(dateInterval[1], null, 'MMM d'), formatDate(dateInterval[1], null, 'HH:mm'), - formatDate(dateInterval[0], null, 'HH:mm') + formatDate(dateInterval[0], null, 'HH:mm'), ] : [ formatDate(dateInterval[1], null, 'MMM d'), formatDateNonUtc(dateInterval[1], 'HH:mm'), - formatDateNonUtc(dateInterval[0], 'HH:mm') + formatDateNonUtc(dateInterval[0], 'HH:mm'), ] const transactions = R.reduce( @@ -37,7 +36,7 @@ const GraphTooltip = ({ return acc }, { volume: 0, cashIn: 0, cashOut: 0 }, - data + data, ) return ( diff --git a/packages/admin-ui/src/pages/Analytics/components/wrappers/HourOfDayWrapper.jsx b/packages/admin-ui/src/pages/Analytics/components/wrappers/HourOfDayWrapper.jsx index aaa1bf52..b3ad7e98 100644 --- a/packages/admin-ui/src/pages/Analytics/components/wrappers/HourOfDayWrapper.jsx +++ b/packages/admin-ui/src/pages/Analytics/components/wrappers/HourOfDayWrapper.jsx @@ -12,7 +12,7 @@ import classes from './wrappers.module.css' const options = [ { code: 'hourOfDayTransactions', display: 'Transactions' }, - { code: 'hourOfDayVolume', display: 'Volume' } + { code: 'hourOfDayVolume', display: 'Volume' }, ] const HourOfDayBarGraphHeader = ({ @@ -26,13 +26,13 @@ const HourOfDayBarGraphHeader = ({ dayOptions, handleDayChange, timezone, - currency + currency, }) => { const [graphType /*, setGraphType */] = useState(options[0].code) const legend = { cashIn:
, - cashOut:
+ cashOut:
, } const offset = getTimezoneOffset(timezone) @@ -41,7 +41,7 @@ const HourOfDayBarGraphHeader = ({ (acc, value) => { const created = new Date(value.created) created.setTime( - created.getTime() + created.getTimezoneOffset() * MINUTE + offset + created.getTime() + created.getTimezoneOffset() * MINUTE + offset, ) switch (created.getDay()) { case 0: @@ -71,7 +71,7 @@ const HourOfDayBarGraphHeader = ({ return acc }, R.fromPairs(R.map(it => [it.code, []], dayOptions)), - data + data, ) return ( diff --git a/packages/admin-ui/src/pages/Analytics/components/wrappers/OverTimeWrapper.jsx b/packages/admin-ui/src/pages/Analytics/components/wrappers/OverTimeWrapper.jsx index 1e36ac37..2cf845df 100644 --- a/packages/admin-ui/src/pages/Analytics/components/wrappers/OverTimeWrapper.jsx +++ b/packages/admin-ui/src/pages/Analytics/components/wrappers/OverTimeWrapper.jsx @@ -18,7 +18,7 @@ const OverTimeDotGraphHeader = ({ selectedMachine, handleMachineChange, timezone, - currency + currency, }) => { const [logarithmic, setLogarithmic] = useState() @@ -35,7 +35,7 @@ const OverTimeDotGraphHeader = ({ d="M 5 6 l 20 0" /> - ) + ), } return ( diff --git a/packages/admin-ui/src/pages/Analytics/components/wrappers/TopMachinesWrapper.jsx b/packages/admin-ui/src/pages/Analytics/components/wrappers/TopMachinesWrapper.jsx index ab0f9c52..88f426e8 100644 --- a/packages/admin-ui/src/pages/Analytics/components/wrappers/TopMachinesWrapper.jsx +++ b/packages/admin-ui/src/pages/Analytics/components/wrappers/TopMachinesWrapper.jsx @@ -8,7 +8,7 @@ import classes from './wrappers.module.css' const options = [ { code: 'topMachinesTransactions', display: 'Transactions' }, - { code: 'topMachinesVolume', display: 'Volume' } + { code: 'topMachinesVolume', display: 'Volume' }, ] const TopMachinesBarGraphHeader = ({ @@ -18,13 +18,13 @@ const TopMachinesBarGraphHeader = ({ machines, selectedMachine, timezone, - currency + currency, }) => { const [graphType /*, setGraphType */] = useState(options[0].code) const legend = { cashIn:
, - cashOut:
+ cashOut:
, } return ( diff --git a/packages/admin-ui/src/pages/Analytics/components/wrappers/VolumeOverTimeWrapper.jsx b/packages/admin-ui/src/pages/Analytics/components/wrappers/VolumeOverTimeWrapper.jsx index 60347b55..04540569 100644 --- a/packages/admin-ui/src/pages/Analytics/components/wrappers/VolumeOverTimeWrapper.jsx +++ b/packages/admin-ui/src/pages/Analytics/components/wrappers/VolumeOverTimeWrapper.jsx @@ -18,7 +18,7 @@ const VolumeOverTimeGraphHeader = ({ selectedMachine, handleMachineChange, timezone, - currency + currency, }) => { const [logarithmic, setLogarithmic] = useState() @@ -42,7 +42,7 @@ const VolumeOverTimeGraphHeader = ({ strokeLinecap="round" /> - ) + ), } return ( diff --git a/packages/admin-ui/src/pages/Analytics/components/wrappers/wrappers.module.css b/packages/admin-ui/src/pages/Analytics/components/wrappers/wrappers.module.css index c32bd59c..7cd53193 100644 --- a/packages/admin-ui/src/pages/Analytics/components/wrappers/wrappers.module.css +++ b/packages/admin-ui/src/pages/Analytics/components/wrappers/wrappers.module.css @@ -12,7 +12,7 @@ .graphHeaderRight { margin-top: 15px; display: flex; - gap: 30px + gap: 30px; } .cashInIcon { @@ -45,12 +45,12 @@ .graphHeaderSwitchBox { display: flex; flex-direction: column; -/*'& > *': {*/ -/* margin: 0*/ -/*},*/ -/*'& > :first-child': {*/ -/* marginBottom: 2,*/ -/* extend: label1,*/ -/* color: offColor*/ -/*}*/ + /*'& > *': {*/ + /* margin: 0*/ + /*},*/ + /*'& > :first-child': {*/ + /* marginBottom: 2,*/ + /* extend: label1,*/ + /* color: offColor*/ + /*}*/ } diff --git a/packages/admin-ui/src/pages/Analytics/graphs/Graph.jsx b/packages/admin-ui/src/pages/Analytics/graphs/Graph.jsx index 18ac8fdf..d6c85f3e 100644 --- a/packages/admin-ui/src/pages/Analytics/graphs/Graph.jsx +++ b/packages/admin-ui/src/pages/Analytics/graphs/Graph.jsx @@ -17,7 +17,7 @@ const GraphWrapper = ({ selectedMachine, machines, selectedDay, - log + log, }) => { const [selectionCoords, setSelectionCoords] = useState(null) const [selectionDateInterval, setSelectionDateInterval] = useState(null) diff --git a/packages/admin-ui/src/pages/Analytics/graphs/HourOfDayBarGraph.jsx b/packages/admin-ui/src/pages/Analytics/graphs/HourOfDayBarGraph.jsx index 702bbe66..ae5bac5c 100644 --- a/packages/admin-ui/src/pages/Analytics/graphs/HourOfDayBarGraph.jsx +++ b/packages/admin-ui/src/pages/Analytics/graphs/HourOfDayBarGraph.jsx @@ -11,7 +11,7 @@ import { subheaderDarkColor, fontColor, fontSecondary, - subheaderColor + subheaderColor, } from 'src/styling/variables' import { MINUTE } from 'src/utils/time' import { toUtc } from 'src/utils/timezones' @@ -22,7 +22,6 @@ const Graph = ({ setSelectionCoords, setSelectionData, setSelectionDateInterval, - selectedMachine }) => { const ref = useRef(null) @@ -36,9 +35,9 @@ const Graph = ({ top: 25, right: 0.5, bottom: 27, - left: 36.5 + left: 36.5, }), - [] + [], ) const offset = getTimezoneOffset(timezone) @@ -64,7 +63,7 @@ const Graph = ({ const tzCreated = new Date(it.created).setTime( new Date(it.created).getTime() + new Date(it.created).getTimezoneOffset() * MINUTE + - offset + offset, ) const created = new Date(tzCreated) @@ -77,7 +76,7 @@ const Graph = ({ created.getUTCHours() < new Date(upperBound).getUTCHours()) ) }, data), - [data, offset] + [data, offset], ) const txClassByHourInterval = useCallback( @@ -91,16 +90,16 @@ const Graph = ({ return acc }, { cashIn: 0, cashOut: 0 }, - filterByHourInterval(lowerBound, upperBound) + filterByHourInterval(lowerBound, upperBound), ), - [filterByHourInterval] + [filterByHourInterval], ) const x = d3 .scaleUtc() .domain([ toUtc(startOfDay(new Date())), - toUtc(add({ days: 1 }, startOfDay(new Date()))) + toUtc(add({ days: 1 }, startOfDay(new Date()))), ]) .rangeRound([GRAPH_MARGIN.left, GRAPH_WIDTH - GRAPH_MARGIN.right]) @@ -111,7 +110,7 @@ const Graph = ({ const upperBound = R.clone(it) return [lowerBound, filterByHourInterval(lowerBound, upperBound)] }, - R.init(getTickIntervals(x.domain(), 2)) + R.init(getTickIntervals(x.domain(), 2)), ) const groupedByTxClass = R.map( @@ -121,7 +120,7 @@ const Graph = ({ const upperBound = R.clone(it) return [lowerBound, txClassByHourInterval(lowerBound, upperBound)] }, - R.init(getTickIntervals(x.domain(), 2)) + R.init(getTickIntervals(x.domain(), 2)), ) const y = d3 @@ -130,13 +129,13 @@ const Graph = ({ 0, d3.max( groupedByTxClass.map(it => it[1]), - d => d.cashIn + d.cashOut + d => d.cashIn + d.cashOut, ) !== 0 ? d3.max( groupedByTxClass.map(it => it[1]), - d => d.cashIn + d.cashOut + d => d.cashIn + d.cashOut, ) - : 50 + : 50, ]) .range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top]) @@ -145,15 +144,15 @@ const Graph = ({ g .attr( 'transform', - `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})` + `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`, ) .call( d3 .axisBottom(x) .ticks(d3.timeHour.every(2)) - .tickFormat(d3.timeFormat('%H:%M')) + .tickFormat(d3.timeFormat('%H:%M')), ), - [GRAPH_MARGIN, x] + [GRAPH_MARGIN, x], ) const buildYAxis = useCallback( @@ -165,10 +164,10 @@ const Graph = ({ .axisLeft(y) .ticks(GRAPH_HEIGHT / 100) .tickSize(0) - .tickFormat(``) + .tickFormat(``), ) .call(g => g.select('.domain').remove()), - [GRAPH_MARGIN, y] + [GRAPH_MARGIN, y], ) const buildVerticalLines = useCallback( @@ -195,7 +194,7 @@ const Graph = ({ }) .attr('y1', GRAPH_MARGIN.top) .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom), - [GRAPH_MARGIN, x] + [GRAPH_MARGIN, x], ) const buildHoverableEventRects = useCallback( @@ -227,15 +226,15 @@ const Graph = ({ const endDate = R.clone(date) const filteredData = groupedByDateInterval.find(it => - R.equals(startDate, it[0]) + R.equals(startDate, it[0]), )[1] const rectXCoords = { left: R.clone(d.target.getBoundingClientRect().x), right: R.clone( d.target.getBoundingClientRect().x + - d.target.getBoundingClientRect().width - ) + d.target.getBoundingClientRect().width, + ), } const xCoord = @@ -248,18 +247,18 @@ const Graph = ({ setSelectionData(filteredData) setSelectionCoords({ x: Math.round(xCoord), - y: Math.round(yCoord) + y: Math.round(yCoord), }) d3.select(`#event-rect-${x(d.target.__data__)}`).attr( 'fill', - subheaderColor + subheaderColor, ) }) .on('mouseleave', d => { d3.select(`#event-rect-${x(d.target.__data__)}`).attr( 'fill', - 'transparent' + 'transparent', ) setSelectionDateInterval(null) setSelectionData(null) @@ -271,8 +270,8 @@ const Graph = ({ setSelectionCoords, setSelectionData, setSelectionDateInterval, - x - ] + x, + ], ) const buildEventRects = useCallback( @@ -298,7 +297,7 @@ const Graph = ({ .attr('height', GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top) .attr('stroke', 'transparent') .attr('fill', 'transparent'), - [GRAPH_MARGIN, x] + [GRAPH_MARGIN, x], ) const formatTicksText = useCallback( @@ -309,7 +308,7 @@ const Graph = ({ .style('fill', fontColor) .style('stroke-width', 0.5) .style('font-family', fontSecondary), - [] + [], ) const drawCashIn = useCallback( @@ -334,7 +333,7 @@ const Graph = ({ GRAPH_HEIGHT - y(interval[1].cashIn) - GRAPH_MARGIN.bottom - - BAR_MARGIN / 2 + BAR_MARGIN / 2, ) }) .attr('width', d => { @@ -348,7 +347,7 @@ const Graph = ({ }) .attr('rx', 2.5) }, - [x, y, GRAPH_MARGIN, groupedByTxClass] + [x, y, GRAPH_MARGIN, groupedByTxClass], ) const drawCashOut = useCallback( @@ -377,7 +376,7 @@ const Graph = ({ GRAPH_HEIGHT - y(interval[1].cashOut) - GRAPH_MARGIN.bottom - - BAR_MARGIN / 2 + BAR_MARGIN / 2, ) }) .attr('width', d => { @@ -391,7 +390,7 @@ const Graph = ({ }) .attr('rx', 2.5) }, - [x, y, GRAPH_MARGIN, groupedByTxClass] + [x, y, GRAPH_MARGIN, groupedByTxClass], ) const drawChart = useCallback(() => { @@ -417,7 +416,7 @@ const Graph = ({ buildVerticalLines, drawCashIn, formatTicksText, - drawCashOut + drawCashOut, ]) useEffect(() => { @@ -433,5 +432,5 @@ export default memo( (prev, next) => R.equals(prev.period, next.period) && R.equals(prev.selectedDay, next.selectedDay) && - R.equals(prev.selectedMachine, next.selectedMachine) + R.equals(prev.selectedMachine, next.selectedMachine), ) diff --git a/packages/admin-ui/src/pages/Analytics/graphs/OverTimeDotGraph.jsx b/packages/admin-ui/src/pages/Analytics/graphs/OverTimeDotGraph.jsx index 12971143..7fc2cf85 100644 --- a/packages/admin-ui/src/pages/Analytics/graphs/OverTimeDotGraph.jsx +++ b/packages/admin-ui/src/pages/Analytics/graphs/OverTimeDotGraph.jsx @@ -13,7 +13,7 @@ import { fontColor, primaryColor, fontSecondary, - subheaderColor + subheaderColor, } from 'src/styling/variables' import { numberToFiatAmount } from 'src/utils/number' import { MINUTE, DAY, WEEK, MONTH } from 'src/utils/time' @@ -25,7 +25,7 @@ const Graph = ({ setSelectionCoords, setSelectionData, setSelectionDateInterval, - log = false + log = false, }) => { const ref = useRef(null) @@ -38,9 +38,9 @@ const Graph = ({ top: 25, right: 3.5, bottom: 27, - left: 38 + left: 38, }), - [] + [], ) const offset = getTimezoneOffset(timezone) @@ -50,7 +50,7 @@ const Graph = ({ day: [NOW - DAY, NOW], threeDays: [NOW - 3 * DAY, NOW], week: [NOW - WEEK, NOW], - month: [NOW - MONTH, NOW] + month: [NOW - MONTH, NOW], } const dataPoints = useMemo( @@ -59,28 +59,28 @@ const Graph = ({ freq: 24, step: 60 * 60 * 1000, tick: d3.utcHour.every(1), - labelFormat: '%H:%M' + labelFormat: '%H:%M', }, threeDays: { freq: 12, step: 6 * 60 * 60 * 1000, tick: d3.utcDay.every(1), - labelFormat: '%a %d' + labelFormat: '%a %d', }, week: { freq: 7, step: 24 * 60 * 60 * 1000, tick: d3.utcDay.every(1), - labelFormat: '%a %d' + labelFormat: '%a %d', }, month: { freq: 30, step: 24 * 60 * 60 * 1000, tick: d3.utcDay.every(1), - labelFormat: '%d' - } + labelFormat: '%d', + }, }), - [] + [], ) const getPastAndCurrentDayLabels = useCallback(d => { @@ -97,11 +97,11 @@ const Graph = ({ const previousDateMonth = previousDate.getUTCMonth() const daysOfWeek = Array.from(Array(7)).map((_, i) => - format('EEE', add({ days: i }, startOfWeek(new Date()))) + format('EEE', add({ days: i }, startOfWeek(new Date()))), ) const months = Array.from(Array(12)).map((_, i) => - format('LLL', add({ months: i }, startOfYear(new Date()))) + format('LLL', add({ months: i }, startOfYear(new Date()))), ) return { @@ -112,7 +112,7 @@ const Graph = ({ current: currentDateMonth !== previousDateMonth ? months[currentDateMonth] - : `${daysOfWeek[currentDateWeekday]} ${currentDateDay}` + : `${daysOfWeek[currentDateWeekday]} ${currentDateDay}`, } }, []) @@ -134,7 +134,7 @@ const Graph = ({ return points }, - [NOW, dataPoints, period.code] + [NOW, dataPoints, period.code], ) const buildAreas = useCallback( @@ -159,7 +159,7 @@ const Graph = ({ return points }, - [NOW, dataPoints, period.code] + [NOW, dataPoints, period.code], ) const x = d3 @@ -177,7 +177,7 @@ const Graph = ({ .scaleLinear() .domain([ 0, - (d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.03 + (d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.03, ]) .nice() .range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top]) @@ -186,7 +186,7 @@ const Graph = ({ .scaleLog() .domain([ (d3.min(data, d => new BigNumber(d.fiat).toNumber()) ?? 1) * 0.9, - (d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.1 + (d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.1, ]) .range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top]) @@ -196,7 +196,7 @@ const Graph = ({ const fullBreakpoints = [ graphLimits[1], ...R.filter(it => it > dataLimits[0] && it < dataLimits[1], breakpoints), - dataLimits[0] + dataLimits[0], ] const intervals = [] @@ -227,7 +227,7 @@ const Graph = ({ g .attr( 'transform', - `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})` + `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`, ) .call( d3 @@ -235,18 +235,18 @@ const Graph = ({ .ticks(dataPoints[period.code].tick) .tickFormat(d => { return d3.timeFormat(dataPoints[period.code].labelFormat)( - d.getTime() + d.getTimezoneOffset() * MINUTE + d.getTime() + d.getTimezoneOffset() * MINUTE, ) }) - .tickSizeOuter(0) + .tickSizeOuter(0), ) .call(g => g .select('.domain') .attr('stroke', primaryColor) - .attr('stroke-width', 1) + .attr('stroke-width', 1), ), - [GRAPH_MARGIN, dataPoints, period.code, x] + [GRAPH_MARGIN, dataPoints, period.code, x], ) const buildYAxis = useCallback( @@ -264,12 +264,12 @@ const Graph = ({ if (d >= 1000) return numberToFiatAmount(d / 1000) + 'k' return numberToFiatAmount(d) - }) + }), ) .select('.domain') .attr('stroke', primaryColor) .attr('stroke-width', 1), - [GRAPH_MARGIN, y, log] + [GRAPH_MARGIN, y, log], ) const buildGrid = useCallback( @@ -286,7 +286,7 @@ const Graph = ({ .attr('x1', d => 0.5 + x(d)) .attr('x2', d => 0.5 + x(d)) .attr('y1', GRAPH_MARGIN.top) - .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom) + .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom), ) // Horizontal lines .call(g => @@ -297,13 +297,13 @@ const Graph = ({ d3 .axisLeft(y) .scale() - .ticks(GRAPH_HEIGHT / 100) + .ticks(GRAPH_HEIGHT / 100), ) .join('line') .attr('y1', d => 0.5 + y(d)) .attr('y2', d => 0.5 + y(d)) .attr('x1', GRAPH_MARGIN.left) - .attr('x2', GRAPH_WIDTH) + .attr('x2', GRAPH_WIDTH), ) // Vertical transparent rectangles for events .call(g => @@ -319,14 +319,14 @@ const Graph = ({ const intervals = getAreaInterval( buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100), x.range(), - x2.range() + x2.range(), ) const interval = getAreaIntervalByX(intervals, xValue) return Math.round((interval[0] - interval[1]) * 100) / 100 }) .attr( 'height', - GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top + GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top, ) .attr('stroke', 'transparent') .attr('fill', 'transparent') @@ -336,7 +336,7 @@ const Graph = ({ const intervals = getAreaInterval( buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100), x.range(), - x2.range() + x2.range(), ) const dateInterval = getDateIntervalByX(areas, intervals, xValue) @@ -354,8 +354,8 @@ const Graph = ({ left: R.clone(d.target.getBoundingClientRect().x), right: R.clone( d.target.getBoundingClientRect().x + - d.target.getBoundingClientRect().width - ) + d.target.getBoundingClientRect().width, + ), } const xCoord = @@ -370,7 +370,7 @@ const Graph = ({ setSelectionData(filteredData) setSelectionCoords({ x: Math.round(xCoord), - y: Math.round(yCoord) + y: Math.round(yCoord), }) d3.select(d.target).attr('fill', subheaderColor) @@ -380,7 +380,7 @@ const Graph = ({ setSelectionDateInterval(null) setSelectionData(null) setSelectionCoords(null) - }) + }), ) // Thick vertical lines .call(g => @@ -391,7 +391,7 @@ const Graph = ({ buildTicks(x.domain()).filter(x => { if (period.code === 'day') return x.getUTCHours() === 0 return x.getUTCDate() === 1 - }) + }), ) .join('line') .attr('class', 'dateSeparator') @@ -400,7 +400,7 @@ const Graph = ({ .attr('y1', GRAPH_MARGIN.top - 50) .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom) .attr('stroke-width', 5) - .join('text') + .join('text'), ) // Left side breakpoint label .call(g => { @@ -458,8 +458,8 @@ const Graph = ({ offset, setSelectionCoords, setSelectionData, - setSelectionDateInterval - ] + setSelectionDateInterval, + ], ) const formatTicksText = useCallback( @@ -470,7 +470,7 @@ const Graph = ({ .style('fill', fontColor) .style('stroke-width', 0.5) .style('font-family', fontSecondary), - [] + [], ) const formatText = useCallback( @@ -481,7 +481,7 @@ const Graph = ({ .style('fill', offColor) .style('stroke-width', 0.5) .style('font-family', fontSecondary), - [] + [], ) const formatTicks = useCallback(() => { @@ -505,10 +505,10 @@ const Graph = ({ .attr('y1', 0.5 + y(median)) .attr('y2', 0.5 + y(median)) .attr('x1', GRAPH_MARGIN.left) - .attr('x2', GRAPH_WIDTH) + .attr('x2', GRAPH_WIDTH), ) }, - [GRAPH_MARGIN, y, data, log] + [GRAPH_MARGIN, y, data, log], ) const drawData = useCallback( @@ -524,7 +524,7 @@ const Graph = ({ .attr('fill', d => (d.txClass === 'cashIn' ? java : neon)) .attr('r', 3.5) }, - [data, offset, x, y] + [data, offset, x, y], ) const drawChart = useCallback(() => { @@ -550,7 +550,7 @@ const Graph = ({ drawData, formatText, formatTicks, - formatTicksText + formatTicksText, ]) useEffect(() => { @@ -566,5 +566,5 @@ export default memo( (prev, next) => R.equals(prev.period, next.period) && R.equals(prev.selectedMachine, next.selectedMachine) && - R.equals(prev.log, next.log) + R.equals(prev.log, next.log), ) diff --git a/packages/admin-ui/src/pages/Analytics/graphs/OverTimeLineGraph.jsx b/packages/admin-ui/src/pages/Analytics/graphs/OverTimeLineGraph.jsx index 68074bcb..f945e261 100644 --- a/packages/admin-ui/src/pages/Analytics/graphs/OverTimeLineGraph.jsx +++ b/packages/admin-ui/src/pages/Analytics/graphs/OverTimeLineGraph.jsx @@ -8,7 +8,7 @@ import { differenceInMilliseconds, format, startOfWeek, - startOfYear + startOfYear, } from 'date-fns/fp' import * as R from 'ramda' import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react' @@ -21,7 +21,7 @@ import { fontColor, primaryColor, fontSecondary, - subheaderColor + subheaderColor, } from 'src/styling/variables' import { numberToFiatAmount } from 'src/utils/number' import { MINUTE, DAY, WEEK, MONTH } from 'src/utils/time' @@ -33,7 +33,7 @@ const Graph = ({ setSelectionCoords, setSelectionData, setSelectionDateInterval, - log = false + log = false, }) => { const ref = useRef(null) @@ -46,9 +46,9 @@ const Graph = ({ top: 25, right: 3.5, bottom: 27, - left: 38 + left: 38, }), - [] + [], ) const offset = getTimezoneOffset(timezone) @@ -58,7 +58,7 @@ const Graph = ({ day: [NOW - DAY, NOW], threeDays: [NOW - 3 * DAY, NOW], week: [NOW - WEEK, NOW], - month: [NOW - MONTH, NOW] + month: [NOW - MONTH, NOW], } const dataPoints = useMemo( @@ -67,28 +67,28 @@ const Graph = ({ freq: 24, step: 60 * 60 * 1000, tick: d3.utcHour.every(1), - labelFormat: '%H:%M' + labelFormat: '%H:%M', }, threeDays: { freq: 12, step: 6 * 60 * 60 * 1000, tick: d3.utcDay.every(1), - labelFormat: '%a %d' + labelFormat: '%a %d', }, week: { freq: 7, step: 24 * 60 * 60 * 1000, tick: d3.utcDay.every(1), - labelFormat: '%a %d' + labelFormat: '%a %d', }, month: { freq: 30, step: 24 * 60 * 60 * 1000, tick: d3.utcDay.every(1), - labelFormat: '%d' - } + labelFormat: '%d', + }, }), - [] + [], ) const getPastAndCurrentDayLabels = useCallback(d => { @@ -105,11 +105,11 @@ const Graph = ({ const previousDateMonth = previousDate.getUTCMonth() const daysOfWeek = Array.from(Array(7)).map((_, i) => - format('EEE', add({ days: i }, startOfWeek(new Date()))) + format('EEE', add({ days: i }, startOfWeek(new Date()))), ) const months = Array.from(Array(12)).map((_, i) => - format('LLL', add({ months: i }, startOfYear(new Date()))) + format('LLL', add({ months: i }, startOfYear(new Date()))), ) return { @@ -120,7 +120,7 @@ const Graph = ({ current: currentDateMonth !== previousDateMonth ? months[currentDateMonth] - : `${daysOfWeek[currentDateWeekday]} ${currentDateDay}` + : `${daysOfWeek[currentDateWeekday]} ${currentDateDay}`, } }, []) @@ -142,7 +142,7 @@ const Graph = ({ return points }, - [NOW, dataPoints, period.code] + [NOW, dataPoints, period.code], ) const buildAreas = useCallback( @@ -167,7 +167,7 @@ const Graph = ({ return points }, - [NOW, dataPoints, period.code] + [NOW, dataPoints, period.code], ) const x = d3 @@ -192,14 +192,14 @@ const Graph = ({ else if (i === dates.length - 1) return addMilliseconds( -dataPoints[period.code].step, - dates[dates.length - 2] + dates[dates.length - 2], ) else return date }) .map(date => { const middleOfBin = addMilliseconds( dataPoints[period.code].step / 2, - date + date, ) const txs = data.filter(tx => { @@ -236,7 +236,7 @@ const Graph = ({ .scaleLog() .domain([ min === 0 ? 0.9 : min * 0.9, - (max === min ? min + Math.pow(10, 2 * min + 1) : max) * 2 + (max === min ? min + Math.pow(10, 2 * min + 1) : max) * 2, ]) .clamp(true) .range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top]) @@ -247,7 +247,7 @@ const Graph = ({ const fullBreakpoints = [ graphLimits[1], ...R.filter(it => it > dataLimits[0] && it < dataLimits[1], breakpoints), - dataLimits[0] + dataLimits[0], ] const intervals = [] @@ -278,7 +278,7 @@ const Graph = ({ g .attr( 'transform', - `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})` + `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`, ) .call( d3 @@ -286,18 +286,18 @@ const Graph = ({ .ticks(dataPoints[period.code].tick) .tickFormat(d => { return d3.timeFormat(dataPoints[period.code].labelFormat)( - d.getTime() + d.getTimezoneOffset() * MINUTE + d.getTime() + d.getTimezoneOffset() * MINUTE, ) }) - .tickSizeOuter(0) + .tickSizeOuter(0), ) .call(g => g .select('.domain') .attr('stroke', primaryColor) - .attr('stroke-width', 1) + .attr('stroke-width', 1), ), - [GRAPH_MARGIN, dataPoints, period.code, x] + [GRAPH_MARGIN, dataPoints, period.code, x], ) const buildYAxis = useCallback( @@ -315,12 +315,12 @@ const Graph = ({ if (d >= 1000) return numberToFiatAmount(d / 1000) + 'k' return numberToFiatAmount(d) - }) + }), ) .select('.domain') .attr('stroke', primaryColor) .attr('stroke-width', 1), - [GRAPH_MARGIN, y, log] + [GRAPH_MARGIN, y, log], ) const buildGrid = useCallback( @@ -337,7 +337,7 @@ const Graph = ({ .attr('x1', d => 0.5 + x(d)) .attr('x2', d => 0.5 + x(d)) .attr('y1', GRAPH_MARGIN.top) - .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom) + .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom), ) // Horizontal lines .call(g => @@ -348,13 +348,13 @@ const Graph = ({ d3 .axisLeft(y) .scale() - .ticks(GRAPH_HEIGHT / 100) + .ticks(GRAPH_HEIGHT / 100), ) .join('line') .attr('y1', d => 0.5 + y(d)) .attr('y2', d => 0.5 + y(d)) .attr('x1', GRAPH_MARGIN.left) - .attr('x2', GRAPH_WIDTH) + .attr('x2', GRAPH_WIDTH), ) // Vertical transparent rectangles for events .call(g => @@ -370,14 +370,14 @@ const Graph = ({ const intervals = getAreaInterval( buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100), x.range(), - x2.range() + x2.range(), ) const interval = getAreaIntervalByX(intervals, xValue) return Math.round((interval[0] - interval[1]) * 100) / 100 }) .attr( 'height', - GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top + GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top, ) .attr('stroke', 'transparent') .attr('fill', 'transparent') @@ -387,7 +387,7 @@ const Graph = ({ const intervals = getAreaInterval( buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100), x.range(), - x2.range() + x2.range(), ) const dateInterval = getDateIntervalByX(areas, intervals, xValue) @@ -405,8 +405,8 @@ const Graph = ({ left: R.clone(d.target.getBoundingClientRect().x), right: R.clone( d.target.getBoundingClientRect().x + - d.target.getBoundingClientRect().width - ) + d.target.getBoundingClientRect().width, + ), } const xCoord = @@ -421,7 +421,7 @@ const Graph = ({ setSelectionData(filteredData) setSelectionCoords({ x: Math.round(xCoord), - y: Math.round(yCoord) + y: Math.round(yCoord), }) d3.select(d.target).attr('fill', subheaderColor) @@ -431,7 +431,7 @@ const Graph = ({ setSelectionDateInterval(null) setSelectionData(null) setSelectionCoords(null) - }) + }), ) // Thick vertical lines .call(g => @@ -442,7 +442,7 @@ const Graph = ({ buildTicks(x.domain()).filter(x => { if (period.code === 'day') return x.getUTCHours() === 0 return x.getUTCDate() === 1 - }) + }), ) .join('line') .attr('class', 'dateSeparator') @@ -451,7 +451,7 @@ const Graph = ({ .attr('y1', GRAPH_MARGIN.top - 50) .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom) .attr('stroke-width', 5) - .join('text') + .join('text'), ) // Left side breakpoint label .call(g => { @@ -509,8 +509,8 @@ const Graph = ({ offset, setSelectionCoords, setSelectionData, - setSelectionDateInterval - ] + setSelectionDateInterval, + ], ) const formatTicksText = useCallback( @@ -521,7 +521,7 @@ const Graph = ({ .style('fill', fontColor) .style('stroke-width', 0.5) .style('font-family', fontSecondary), - [] + [], ) const formatText = useCallback( @@ -532,7 +532,7 @@ const Graph = ({ .style('fill', offColor) .style('stroke-width', 0.5) .style('font-family', fontSecondary), - [] + [], ) const formatTicks = useCallback(() => { @@ -574,7 +574,7 @@ const Graph = ({ .line() .curve(d3.curveMonotoneX) .x(d => x(d.date)) - .y(d => y(d.cashIn)) + .y(d => y(d.cashIn)), ) g.append('g') @@ -599,10 +599,10 @@ const Graph = ({ .line() .curve(d3.curveMonotoneX) .x(d => x(d.date)) - .y(d => y(d.cashOut)) + .y(d => y(d.cashOut)), ) }, - [x, y, bins, GRAPH_MARGIN] + [x, y, bins, GRAPH_MARGIN], ) const drawChart = useCallback(() => { @@ -626,7 +626,7 @@ const Graph = ({ drawData, formatText, formatTicks, - formatTicksText + formatTicksText, ]) useEffect(() => { @@ -642,5 +642,5 @@ export default memo( (prev, next) => R.equals(prev.period, next.period) && R.equals(prev.selectedMachine, next.selectedMachine) && - R.equals(prev.log, next.log) + R.equals(prev.log, next.log), ) diff --git a/packages/admin-ui/src/pages/Analytics/graphs/TopMachinesBarGraph.jsx b/packages/admin-ui/src/pages/Analytics/graphs/TopMachinesBarGraph.jsx index 1152a111..ef8ef505 100644 --- a/packages/admin-ui/src/pages/Analytics/graphs/TopMachinesBarGraph.jsx +++ b/packages/admin-ui/src/pages/Analytics/graphs/TopMachinesBarGraph.jsx @@ -8,10 +8,10 @@ import { neon, subheaderDarkColor, fontColor, - fontSecondary + fontSecondary, } from 'src/styling/variables' -const Graph = ({ data, machines, currency, selectedMachine }) => { +const Graph = ({ data, machines, currency }) => { const ref = useRef(null) const AMOUNT_OF_MACHINES = 5 @@ -24,9 +24,9 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { top: 25, right: 0.5, bottom: 27, - left: 36.5 + left: 36.5, }), - [] + [], ) const machinesClone = R.clone(machines) @@ -41,7 +41,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { if (!R.isNil(machinesClone[it])) return machinesClone[it] return { code: `ghostMachine${it}`, display: `` } }, - R.times(R.identity, AMOUNT_OF_MACHINES) + R.times(R.identity, AMOUNT_OF_MACHINES), ) const txByDevice = R.reduce( @@ -50,14 +50,14 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { return acc }, {}, - filledMachines + filledMachines, ) const getDeviceVolume = deviceId => R.reduce( (acc, value) => acc + BigNumber(value.fiat).toNumber(), 0, - txByDevice[deviceId] + txByDevice[deviceId], ) const getDeviceVolumeByTxClass = deviceId => @@ -70,18 +70,18 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { return acc }, { cashIn: 0, cashOut: 0 }, - txByDevice[deviceId] + txByDevice[deviceId], ) const devicesByVolume = R.sort( (a, b) => b[1] - a[1], - R.map(m => [m.code, getDeviceVolume(m.code)], filledMachines) + R.map(m => [m.code, getDeviceVolume(m.code)], filledMachines), ) const topMachines = R.take(AMOUNT_OF_MACHINES, devicesByVolume) const txClassVolumeByDevice = R.fromPairs( - R.map(v => [v[0], getDeviceVolumeByTxClass(v[0])], topMachines) + R.map(v => [v[0], getDeviceVolumeByTxClass(v[0])], topMachines), ) const x = d3 @@ -94,7 +94,9 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { .scaleLinear() .domain([ 0, - d3.max(topMachines, d => d[1]) !== 0 ? d3.max(topMachines, d => d[1]) : 50 + d3.max(topMachines, d => d[1]) !== 0 + ? d3.max(topMachines, d => d[1]) + : 50, ]) .range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top]) @@ -104,7 +106,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { .attr('class', 'x-axis-1') .attr( 'transform', - `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})` + `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`, ) .call( d3 @@ -113,12 +115,12 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { d => `${ R.find(it => it.code === d[0], filledMachines).display ?? '' - }` + }`, ) .tickSize(0) - .tickPadding(10) + .tickPadding(10), ), - [GRAPH_MARGIN, x, filledMachines] + [GRAPH_MARGIN, x, filledMachines], ) const buildXAxis2 = useCallback( @@ -126,7 +128,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { g.attr('class', 'x-axis-2') .attr( 'transform', - `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})` + `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`, ) .call( d3 @@ -134,24 +136,24 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { .tickFormat(d => R.includes(`ghostMachine`, d[0]) ? `` - : `${d[1].toFixed(2)} ${currency}` + : `${d[1].toFixed(2)} ${currency}`, ) .tickSize(0) - .tickPadding(10) + .tickPadding(10), ) }, - [GRAPH_MARGIN, x, currency] + [GRAPH_MARGIN, x, currency], ) const positionXAxisLabels = useCallback(() => { - d3.selectAll('.x-axis-1 .tick text').attr('transform', function (d) { + d3.selectAll('.x-axis-1 .tick text').attr('transform', function () { const widthPerEntry = (x.range()[1] - x.range()[0]) / AMOUNT_OF_MACHINES return `translate(${-widthPerEntry / 2.25 + this.getBBox().width / 2}, 0)` }) }, [x]) const positionXAxis2Labels = useCallback(() => { - d3.selectAll('.x-axis-2 .tick text').attr('transform', function (d) { + d3.selectAll('.x-axis-2 .tick text').attr('transform', function () { const widthPerEntry = (x.range()[1] - x.range()[0]) / AMOUNT_OF_MACHINES return `translate(${widthPerEntry / 2.25 - this.getBBox().width / 2}, 0)` }) @@ -166,10 +168,10 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { .axisLeft(y) .ticks(GRAPH_HEIGHT / 100) .tickSize(0) - .tickFormat(``) + .tickFormat(``), ) .call(g => g.select('.domain').remove()), - [GRAPH_MARGIN, y] + [GRAPH_MARGIN, y], ) const formatTicksText = useCallback( @@ -180,7 +182,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { .style('fill', fontColor) .style('stroke-width', 0.5) .style('font-family', fontSecondary), - [] + [], ) const buildGrid = useCallback( @@ -213,10 +215,10 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { return 0.5 + x(d) - paddedXValue }) .attr('y1', GRAPH_MARGIN.top) - .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom) + .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom), ) }, - [GRAPH_MARGIN, x] + [GRAPH_MARGIN, x], ) const drawCashIn = useCallback( @@ -231,13 +233,13 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { R.clamp( 0, GRAPH_HEIGHT, - GRAPH_HEIGHT - y(d[1].cashIn) - GRAPH_MARGIN.bottom - BAR_MARGIN - ) + GRAPH_HEIGHT - y(d[1].cashIn) - GRAPH_MARGIN.bottom - BAR_MARGIN, + ), ) .attr('width', x.bandwidth()) .attr('rx', 2.5) }, - [txClassVolumeByDevice, x, y, GRAPH_MARGIN] + [txClassVolumeByDevice, x, y, GRAPH_MARGIN], ) const drawCashOut = useCallback( @@ -252,7 +254,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { d => y(d[1].cashIn + d[1].cashOut) - GRAPH_MARGIN.top + - GRAPH_MARGIN.bottom + GRAPH_MARGIN.bottom, ) .attr('height', d => { return R.clamp( @@ -261,13 +263,13 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { GRAPH_HEIGHT - y(d[1].cashOut) - GRAPH_MARGIN.bottom - - BAR_MARGIN / 2 + BAR_MARGIN / 2, ) }) .attr('width', x.bandwidth()) .attr('rx', 2.5) }, - [txClassVolumeByDevice, x, y, GRAPH_MARGIN] + [txClassVolumeByDevice, x, y, GRAPH_MARGIN], ) const drawChart = useCallback(() => { @@ -295,7 +297,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => { formatTicksText, buildGrid, drawCashIn, - drawCashOut + drawCashOut, ]) useEffect(() => { @@ -310,5 +312,5 @@ export default memo( Graph, (prev, next) => R.equals(prev.period, next.period) && - R.equals(prev.selectedMachine, next.selectedMachine) + R.equals(prev.selectedMachine, next.selectedMachine), ) diff --git a/packages/admin-ui/src/pages/Authentication/Authentication.module.css b/packages/admin-ui/src/pages/Authentication/Authentication.module.css index 88648763..57fedb4e 100644 --- a/packages/admin-ui/src/pages/Authentication/Authentication.module.css +++ b/packages/admin-ui/src/pages/Authentication/Authentication.module.css @@ -1,5 +1,6 @@ .welcomeBackground { - background: var(--ghost) url(/wizard-background.svg) no-repeat fixed center center; + background: var(--ghost) url(/wizard-background.svg) no-repeat fixed center + center; background-size: cover; height: 100vh; width: 100vw; @@ -22,7 +23,7 @@ display: flex; flex-direction: row; align-items: center; - margin-bottom: 30px + margin-bottom: 30px; } .icon { @@ -70,4 +71,3 @@ .confirm2FAInput { margin-top: 25px; } - diff --git a/packages/admin-ui/src/pages/Authentication/Input2FAState.jsx b/packages/admin-ui/src/pages/Authentication/Input2FAState.jsx index b1326044..415d38f9 100644 --- a/packages/admin-ui/src/pages/Authentication/Input2FAState.jsx +++ b/packages/admin-ui/src/pages/Authentication/Input2FAState.jsx @@ -46,7 +46,7 @@ const Input2FAState = ({ state, dispatch }) => { onCompleted: ({ userData }) => { setUserData(userData) history.push('/') - } + }, }) const [input2FA, { error: mutationError }] = useMutation(INPUT_2FA, { @@ -55,15 +55,15 @@ const Input2FAState = ({ state, dispatch }) => { return getUserData() } return setInvalidToken(true) - } + }, }) const handle2FAChange = value => { dispatch({ type: STATES.INPUT_2FA, payload: { - twoFAField: value - } + twoFAField: value, + }, }) setInvalidToken(false) } @@ -79,8 +79,8 @@ const Input2FAState = ({ state, dispatch }) => { username: state.clientField, password: state.passwordField, code: state.twoFAField, - rememberMe: state.rememberMeField - } + rememberMe: state.rememberMeField, + }, } input2FA(options) diff --git a/packages/admin-ui/src/pages/Authentication/InputFIDOState.jsx b/packages/admin-ui/src/pages/Authentication/InputFIDOState.jsx index 3c8a1b70..7a3b24f3 100644 --- a/packages/admin-ui/src/pages/Authentication/InputFIDOState.jsx +++ b/packages/admin-ui/src/pages/Authentication/InputFIDOState.jsx @@ -24,12 +24,12 @@ const validationSchema = Yup.object().shape({ localClient: Yup.string() .required('Client field is required!') .email('Username field should be in an email format!'), - localRememberMe: Yup.boolean() + localRememberMe: Yup.boolean(), }) const initialValues = { localClient: '', - localRememberMe: false + localRememberMe: false, } const InputFIDOState = ({ state, strategy }) => { @@ -74,8 +74,8 @@ const InputFIDOState = ({ state, strategy }) => { { onCompleted: ({ validateAssertion: success }) => { success ? getUserData() : setInvalidToken(true) - } - } + }, + }, ) const [assertionOptions, { error: assertionQueryError }] = useLazyQuery( @@ -86,11 +86,11 @@ const InputFIDOState = ({ state, strategy }) => { ? { username: state.clientField, password: state.passwordField, - domain: window.location.hostname + domain: window.location.hostname, } : { username: localClientField, - domain: window.location.hostname + domain: window.location.hostname, }, onCompleted: ({ generateAssertionOptions: options }) => { startAssertion(options) @@ -102,31 +102,31 @@ const InputFIDOState = ({ state, strategy }) => { password: state.passwordField, rememberMe: state.rememberMeField, assertionResponse: res, - domain: window.location.hostname + domain: window.location.hostname, } : { username: localClientField, rememberMe: localRememberMeField, assertionResponse: res, - domain: window.location.hostname + domain: window.location.hostname, } validateAssertion({ - variables + variables, }) }) .catch(err => { console.error(err) setInvalidToken(true) }) - } - } + }, + }, ) const [getUserData, { error: queryError }] = useLazyQuery(GET_USER_DATA, { onCompleted: ({ userData }) => { setUserData(userData) history.push('/') - } + }, }) const getErrorMsg = (formikErrors, formikTouched) => { diff --git a/packages/admin-ui/src/pages/Authentication/LoginCard.jsx b/packages/admin-ui/src/pages/Authentication/LoginCard.jsx index d6617fdc..3661f1b0 100644 --- a/packages/admin-ui/src/pages/Authentication/LoginCard.jsx +++ b/packages/admin-ui/src/pages/Authentication/LoginCard.jsx @@ -1,6 +1,5 @@ import Paper from '@mui/material/Paper' import React, { useReducer } from 'react' -import { H5 } from 'src/components/typography' import Logo from 'src/styling/icons/menu/logo.svg?react' import Input2FAState from './Input2FAState' @@ -18,7 +17,7 @@ const initialState = { clientField: '', passwordField: '', rememberMeField: false, - loginState: STATES.LOGIN + loginState: STATES.LOGIN, } const reducer = (state, action) => { diff --git a/packages/admin-ui/src/pages/Authentication/LoginState.jsx b/packages/admin-ui/src/pages/Authentication/LoginState.jsx index ce8b4c72..af07216f 100644 --- a/packages/admin-ui/src/pages/Authentication/LoginState.jsx +++ b/packages/admin-ui/src/pages/Authentication/LoginState.jsx @@ -44,13 +44,13 @@ const GET_USER_DATA = gql` const validationSchema = Yup.object().shape({ email: Yup.string().label('Email').required().email(), password: Yup.string().required('Password field is required'), - rememberMe: Yup.boolean() + rememberMe: Yup.boolean(), }) const initialValues = { email: '', password: '', - rememberMe: false + rememberMe: false, } const getErrorMsg = (formikErrors, formikTouched, mutationError) => { @@ -62,7 +62,7 @@ const getErrorMsg = (formikErrors, formikTouched, mutationError) => { return null } -const LoginState = ({ state, dispatch, strategy }) => { +const LoginState = ({ dispatch, strategy }) => { const history = useHistory() const { setUserData } = useContext(AppContext) @@ -72,8 +72,8 @@ const LoginState = ({ state, dispatch, strategy }) => { const options = { variables: { username, - password - } + password, + }, } const { data: loginResponse } = await login(options) @@ -84,16 +84,16 @@ const LoginState = ({ state, dispatch, strategy }) => { payload: { clientField: username, passwordField: password, - rememberMeField: rememberMe - } + rememberMeField: rememberMe, + }, }) } const [validateAssertion, { error: FIDOMutationError }] = useMutation( VALIDATE_ASSERTION, { - onCompleted: ({ validateAssertion: success }) => success && getUserData() - } + onCompleted: ({ validateAssertion: success }) => success && getUserData(), + }, ) const [assertionOptions, { error: assertionQueryError }] = useLazyQuery( @@ -105,15 +105,15 @@ const LoginState = ({ state, dispatch, strategy }) => { validateAssertion({ variables: { assertionResponse: res, - domain: window.location.hostname - } + domain: window.location.hostname, + }, }) }) .catch(err => { console.error(err) }) - } - } + }, + }, ) const [getUserData, { error: userDataQueryError }] = useLazyQuery( @@ -122,8 +122,8 @@ const LoginState = ({ state, dispatch, strategy }) => { onCompleted: ({ userData }) => { setUserData(userData) history.push('/') - } - } + }, + }, ) return ( @@ -149,7 +149,7 @@ const LoginState = ({ state, dispatch, strategy }) => { loginMutationError || FIDOMutationError || assertionQueryError || - userDataQueryError + userDataQueryError, )} /> { loginMutationError || FIDOMutationError || assertionQueryError || - userDataQueryError + userDataQueryError, )} />
@@ -183,7 +183,7 @@ const LoginState = ({ state, dispatch, strategy }) => { loginMutationError || FIDOMutationError || assertionQueryError || - userDataQueryError + userDataQueryError, ) && (

{getErrorMsg( @@ -192,7 +192,7 @@ const LoginState = ({ state, dispatch, strategy }) => { loginMutationError || FIDOMutationError || assertionQueryError || - userDataQueryError + userDataQueryError, )}

)} @@ -202,11 +202,11 @@ const LoginState = ({ state, dispatch, strategy }) => { onClick={() => { return strategy === 'FIDOUsernameless' ? assertionOptions({ - variables: { domain: window.location.hostname } + variables: { domain: window.location.hostname }, }) : dispatch({ type: 'FIDO', - payload: {} + payload: {}, }) }} buttonClassName="w-full" diff --git a/packages/admin-ui/src/pages/Authentication/Register.jsx b/packages/admin-ui/src/pages/Authentication/Register.jsx index e7b71398..4a961e38 100644 --- a/packages/admin-ui/src/pages/Authentication/Register.jsx +++ b/packages/admin-ui/src/pages/Authentication/Register.jsx @@ -45,22 +45,22 @@ const validationSchema = Yup.object({ .required('A password is required') .min( PASSWORD_MIN_LENGTH, - `Your password must contain at least ${PASSWORD_MIN_LENGTH} characters` + `Your password must contain at least ${PASSWORD_MIN_LENGTH} characters`, ), confirmPassword: Yup.string() .required('Please confirm the password') - .oneOf([Yup.ref('password')], 'Passwords must match') + .oneOf([Yup.ref('password')], 'Passwords must match'), }) const initialValues = { password: '', - confirmPassword: '' + confirmPassword: '', } const initialState = { username: null, role: null, - result: '' + result: '', } const reducer = (state, action) => { @@ -72,7 +72,7 @@ const getErrorMsg = ( formikErrors, formikTouched, queryError, - mutationError + mutationError, ) => { if (!formikErrors || !formikTouched) return null if (queryError || mutationError) return 'Internal server error' @@ -94,32 +94,32 @@ const Register = () => { onCompleted: ({ validateRegisterLink: info }) => { if (!info) { return dispatch({ - type: 'failure' + type: 'failure', }) } dispatch({ type: 'success', payload: { username: info.username, - role: info.role - } + role: info.role, + }, }) }, onError: () => dispatch({ - type: 'failure' - }) + type: 'failure', + }), } const { error: queryError, loading } = useQuery( VALIDATE_REGISTER_LINK, - queryOptions + queryOptions, ) const [register, { error: mutationError }] = useMutation(REGISTER, { onCompleted: ({ register: success }) => { if (success) history.push('/wizard', { fromAuthRegister: true }) - } + }, }) return ( @@ -148,8 +148,8 @@ const Register = () => { token: token, username: state.username, password: values.password, - role: state.role - } + role: state.role, + }, }) }}> {({ errors, touched }) => ( @@ -175,14 +175,14 @@ const Register = () => { errors, touched, queryError, - mutationError + mutationError, ) && (

{getErrorMsg( errors, touched, queryError, - mutationError + mutationError, )}

)} diff --git a/packages/admin-ui/src/pages/Authentication/Reset2FA.jsx b/packages/admin-ui/src/pages/Authentication/Reset2FA.jsx index 3d93ce88..0a26d2b7 100644 --- a/packages/admin-ui/src/pages/Authentication/Reset2FA.jsx +++ b/packages/admin-ui/src/pages/Authentication/Reset2FA.jsx @@ -34,7 +34,7 @@ const initialState = { userID: null, secret: null, otpauth: null, - result: null + result: null, } const reducer = (state, action) => { @@ -63,7 +63,7 @@ const Reset2FA = () => { onCompleted: ({ validateReset2FALink: info }) => { if (!info) { dispatch({ - type: 'failure' + type: 'failure', }) } else { dispatch({ @@ -71,22 +71,22 @@ const Reset2FA = () => { payload: { userID: info.user_id, secret: info.secret, - otpauth: info.otpauth - } + otpauth: info.otpauth, + }, }) } }, onError: () => { dispatch({ - type: 'failure' + type: 'failure', }) - } + }, }) const [reset2FA, { error: mutationError }] = useMutation(RESET_2FA, { onCompleted: ({ reset2FA: success }) => { success ? history.push('/') : setInvalidToken(true) - } + }, }) const getErrorMsg = () => { @@ -107,8 +107,8 @@ const Reset2FA = () => { variables: { token: token, userID: state.userID, - code: twoFAConfirmation - } + code: twoFAConfirmation, + }, }) } diff --git a/packages/admin-ui/src/pages/Authentication/ResetPassword.jsx b/packages/admin-ui/src/pages/Authentication/ResetPassword.jsx index 5f440f17..31ba0907 100644 --- a/packages/admin-ui/src/pages/Authentication/ResetPassword.jsx +++ b/packages/admin-ui/src/pages/Authentication/ResetPassword.jsx @@ -33,17 +33,17 @@ const validationSchema = Yup.object().shape({ .test( 'len', 'New password must contain more than 8 characters', - val => val.length >= 8 + val => val.length >= 8, ), confirmPassword: Yup.string().oneOf( [Yup.ref('password'), null], - 'Passwords must match' - ) + 'Passwords must match', + ), }) const initialValues = { password: '', - confirmPassword: '' + confirmPassword: '', } const getErrorMsg = (formikErrors, formikTouched, mutationError) => { @@ -78,13 +78,13 @@ const ResetPassword = () => { onError: () => { setLoading(false) setSuccess(false) - } + }, }) const [resetPassword, { error }] = useMutation(RESET_PASSWORD, { onCompleted: ({ resetPassword: success }) => { if (success) history.push('/') - } + }, }) return ( @@ -112,8 +112,8 @@ const ResetPassword = () => { variables: { token: token, userID: userID, - newPassword: values.confirmPassword - } + newPassword: values.confirmPassword, + }, }) }}> {({ errors, touched }) => ( diff --git a/packages/admin-ui/src/pages/Authentication/Setup2FAState.jsx b/packages/admin-ui/src/pages/Authentication/Setup2FAState.jsx index 24c8507b..5b1e78bd 100644 --- a/packages/admin-ui/src/pages/Authentication/Setup2FAState.jsx +++ b/packages/admin-ui/src/pages/Authentication/Setup2FAState.jsx @@ -47,7 +47,7 @@ const GET_USER_DATA = gql` } ` -const Setup2FAState = ({ state, dispatch }) => { +const Setup2FAState = ({ state }) => { const history = useHistory() const { setUserData } = useContext(AppContext) @@ -68,7 +68,7 @@ const Setup2FAState = ({ state, dispatch }) => { onCompleted: ({ get2FASecret }) => { setSecret(get2FASecret.secret) setOtpauth(get2FASecret.otpauth) - } + }, } const mutationOptions = { @@ -76,8 +76,8 @@ const Setup2FAState = ({ state, dispatch }) => { username: state.clientField, password: state.passwordField, rememberMe: state.rememberMeField, - codeConfirmation: twoFAConfirmation - } + codeConfirmation: twoFAConfirmation, + }, } const { error: queryError } = useQuery(GET_2FA_SECRET, queryOptions) @@ -86,13 +86,13 @@ const Setup2FAState = ({ state, dispatch }) => { onCompleted: ({ userData }) => { setUserData(userData) history.push('/') - } + }, }) const [setup2FA, { error: mutationError }] = useMutation(SETUP_2FA, { onCompleted: ({ setup2FA: success }) => { success ? getUserData() : setInvalidToken(true) - } + }, }) const getErrorMsg = () => { diff --git a/packages/admin-ui/src/pages/Authentication/states.js b/packages/admin-ui/src/pages/Authentication/states.js index b2463c85..180ccf27 100644 --- a/packages/admin-ui/src/pages/Authentication/states.js +++ b/packages/admin-ui/src/pages/Authentication/states.js @@ -2,7 +2,7 @@ const STATES = { LOGIN: 'LOGIN', SETUP_2FA: 'SETUP2FA', INPUT_2FA: 'INPUT2FA', - FIDO: 'FIDO' + FIDO: 'FIDO', } export { STATES } diff --git a/packages/admin-ui/src/pages/Blacklist/Blacklist.jsx b/packages/admin-ui/src/pages/Blacklist/Blacklist.jsx index 8bee117a..a6a6b915 100644 --- a/packages/admin-ui/src/pages/Blacklist/Blacklist.jsx +++ b/packages/admin-ui/src/pages/Blacklist/Blacklist.jsx @@ -90,8 +90,8 @@ const PaperWalletDialog = ({ onConfirmed, onDissmised, open, props }) => { borderRadius: 8, minWidth: 656, bottom: 125, - right: 7 - } + right: 7, + }, }} {...props}>
@@ -136,20 +136,20 @@ const Blacklist = () => { setErrorMsg(errorMessage) }, onCompleted: () => setDeleteDialog(false), - refetchQueries: () => ['getBlacklistData'] + refetchQueries: () => ['getBlacklistData'], }) const [addEntry] = useMutation(ADD_ROW, { - refetchQueries: () => ['getBlacklistData'] + refetchQueries: () => ['getBlacklistData'], }) const [saveConfig] = useMutation(SAVE_CONFIG, { - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const [editMessage] = useMutation(EDIT_BLACKLIST_MESSAGE, { onError: e => setEditMessageError(e), - refetchQueries: () => ['getBlacklistData'] + refetchQueries: () => ['getBlacklistData'], }) const blacklistData = R.path(['blacklist'])(blacklistResponse) ?? [] @@ -172,7 +172,7 @@ const Blacklist = () => { const handleConfirmDialog = confirm => { addressReuseSave({ - enablePaperWalletOnly: confirm + enablePaperWalletOnly: confirm, }) setConfirmDialog(false) } @@ -193,6 +193,7 @@ const Blacklist = () => { setErrorMsg(`Server error${': ' + res?.errors[0]?.message}`) } } catch (e) { + console.error(e) setErrorMsg('Server error') } } @@ -201,8 +202,8 @@ const Blacklist = () => { editMessage({ variables: { id: r.id, - content: r.content - } + content: r.content, + }, }) } @@ -222,8 +223,8 @@ const Blacklist = () => { text: 'Advanced settings', icon: SettingsIcon, inverseIcon: ReverseSettingsIcon, - toggle: setAdvancedSettings - } + toggle: setAdvancedSettings, + }, ]}> {!advancedSettings && (
@@ -234,7 +235,7 @@ const Blacklist = () => { onChange={e => enablePaperWalletOnly ? addressReuseSave({ - enablePaperWalletOnly: e.target.checked + enablePaperWalletOnly: e.target.checked, }) : setConfirmDialog(true) } diff --git a/packages/admin-ui/src/pages/Blacklist/BlacklistAdvanced.jsx b/packages/admin-ui/src/pages/Blacklist/BlacklistAdvanced.jsx index 6050af0c..ec08fb4f 100644 --- a/packages/admin-ui/src/pages/Blacklist/BlacklistAdvanced.jsx +++ b/packages/admin-ui/src/pages/Blacklist/BlacklistAdvanced.jsx @@ -30,7 +30,7 @@ const BlacklistAdvanced = ({ data, editBlacklistMessage, onClose, - mutationError + mutationError, }) => { const [selectedMessage, setSelectedMessage] = useState(null) @@ -41,7 +41,7 @@ const BlacklistAdvanced = ({ width: 250, textAlign: 'left', size: 'sm', - view: it => R.path(['label'], it) + view: it => R.path(['label'], it), }, { name: 'content', @@ -49,7 +49,7 @@ const BlacklistAdvanced = ({ width: 690, textAlign: 'left', size: 'sm', - view: it => R.path(['content'], it) + view: it => R.path(['content'], it), }, { name: 'edit', @@ -63,7 +63,7 @@ const BlacklistAdvanced = ({ - ) + ), }, { name: 'deleteButton', @@ -86,8 +86,8 @@ const BlacklistAdvanced = ({ )} - ) - } + ), + }, ] const handleModalClose = () => { @@ -102,12 +102,12 @@ const BlacklistAdvanced = ({ const initialValues = { label: !R.isNil(selectedMessage) ? selectedMessage.label : '', - content: !R.isNil(selectedMessage) ? selectedMessage.content : '' + content: !R.isNil(selectedMessage) ? selectedMessage.content : '', } const validationSchema = Yup.object().shape({ label: Yup.string().required('A label is required!'), - content: Yup.string().required('The message content is required!').trim() + content: Yup.string().required('The message content is required!').trim(), }) return ( diff --git a/packages/admin-ui/src/pages/Blacklist/BlacklistModal.jsx b/packages/admin-ui/src/pages/Blacklist/BlacklistModal.jsx index dbd267f9..5d8fdf80 100644 --- a/packages/admin-ui/src/pages/Blacklist/BlacklistModal.jsx +++ b/packages/admin-ui/src/pages/Blacklist/BlacklistModal.jsx @@ -27,10 +27,10 @@ const BlackListModal = ({ onClose, addToBlacklist, errorMsg }) => { validateOnBlur={false} validateOnChange={false} initialValues={{ - address: '' + address: '', }} validationSchema={Yup.object({ - address: Yup.string().trim().required('An address is required') + address: Yup.string().trim().required('An address is required'), })} onSubmit={({ address }) => { handleAddToBlacklist(address.trim()) diff --git a/packages/admin-ui/src/pages/Blacklist/BlacklistTable.jsx b/packages/admin-ui/src/pages/Blacklist/BlacklistTable.jsx index 4171f1b1..53932a27 100644 --- a/packages/admin-ui/src/pages/Blacklist/BlacklistTable.jsx +++ b/packages/admin-ui/src/pages/Blacklist/BlacklistTable.jsx @@ -13,7 +13,7 @@ const BlacklistTable = ({ errorMessage, setErrorMessage, deleteDialog, - setDeleteDialog + setDeleteDialog, }) => { const [toBeDeleted, setToBeDeleted] = useState() @@ -28,7 +28,7 @@ const BlacklistTable = ({
{R.path(['address'], it)}
- ) + ), }, { name: 'deleteButton', @@ -47,8 +47,8 @@ const BlacklistTable = ({ - ) - } + ), + }, ] return ( diff --git a/packages/admin-ui/src/pages/Cashout/Cashout.jsx b/packages/admin-ui/src/pages/Cashout/Cashout.jsx index 2872b5a8..419f5782 100644 --- a/packages/admin-ui/src/pages/Cashout/Cashout.jsx +++ b/packages/admin-ui/src/pages/Cashout/Cashout.jsx @@ -51,7 +51,7 @@ const CashOut = ({ name: SCREEN_KEY }) => { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, { onCompleted: () => setWizard(false), - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const save = (rawConfig, accounts) => { diff --git a/packages/admin-ui/src/pages/Cashout/Wizard.jsx b/packages/admin-ui/src/pages/Cashout/Wizard.jsx index aba07f1f..39ecf8d6 100644 --- a/packages/admin-ui/src/pages/Cashout/Wizard.jsx +++ b/packages/admin-ui/src/pages/Cashout/Wizard.jsx @@ -21,7 +21,7 @@ const Wizard = ({ machine, locale, onClose, save, error }) => { const LAST_STEP = machine.numberOfCassettes + machine.numberOfRecyclers + 1 const [{ step, config }, setState] = useState({ step: 0, - config: { active: true } + config: { active: true }, }) const options = getBillOptions(locale, denominations) @@ -34,8 +34,8 @@ const Wizard = ({ machine, locale, onClose, save, error }) => { return save( toNamespace( machine.deviceId, - DenominationsSchema.cast(config, { assert: false }) - ) + DenominationsSchema.cast(config, { assert: false }), + ), ) } @@ -43,7 +43,7 @@ const Wizard = ({ machine, locale, onClose, save, error }) => { setState({ step: step + 1, - config: newConfig + config: newConfig, }) } @@ -57,10 +57,10 @@ const Wizard = ({ machine, locale, onClose, save, error }) => { inputProps: { options: options, labelProp: 'display', - valueProp: 'code' - } + valueProp: 'code', + }, }), - R.range(1, machine.numberOfCassettes + 1) + R.range(1, machine.numberOfCassettes + 1), ), R.map( it => ({ @@ -71,11 +71,11 @@ const Wizard = ({ machine, locale, onClose, save, error }) => { inputProps: { options: options, labelProp: 'display', - valueProp: 'code' - } + valueProp: 'code', + }, }), - R.range(1, machine.numberOfRecyclers + 1) - ) + R.range(1, machine.numberOfRecyclers + 1), + ), ) const schema = () => @@ -119,7 +119,7 @@ const Wizard = ({ machine, locale, onClose, save, error }) => { recycler6: machine.numberOfRecyclers >= 6 && step >= machine.numberOfCassettes + 6 ? Yup.number().required() - : Yup.number().transform(transformNumber).nullable() + : Yup.number().transform(transformNumber).nullable(), }) return ( diff --git a/packages/admin-ui/src/pages/Cashout/WizardStep.jsx b/packages/admin-ui/src/pages/Cashout/WizardStep.jsx index 6aa881c9..3f44c24d 100644 --- a/packages/admin-ui/src/pages/Cashout/WizardStep.jsx +++ b/packages/admin-ui/src/pages/Cashout/WizardStep.jsx @@ -19,23 +19,23 @@ import tejo4CassetteFour from 'src/styling/icons/cassettes/tejo/4-cassettes/4-ca const getCassetesArtworks = () => ({ 1: { - 1: cassetteOne + 1: cassetteOne, }, 2: { 1: cassetteOne, - 2: cassetteTwo + 2: cassetteTwo, }, 3: { 1: tejo3CassetteOne, 2: tejo3CassetteTwo, - 3: tejo3CassetteThree + 3: tejo3CassetteThree, }, 4: { 1: tejo4CassetteOne, 2: tejo4CassetteTwo, 3: tejo4CassetteThree, - 4: tejo4CassetteFour - } + 4: tejo4CassetteFour, + }, }) const WizardStep = ({ @@ -48,7 +48,7 @@ const WizardStep = ({ steps, fiatCurrency, options, - numberOfCassettes + numberOfCassettes, }) => { const label = isLastStep ? 'Finish' : 'Next' const cassetteIcon = getCassetesArtworks()[numberOfCassettes] @@ -68,7 +68,7 @@ const WizardStep = ({ cassette1: '', cassette2: '', cassette3: '', - cassette4: '' + cassette4: '', }} enableReinitialize validationSchema={schema}> @@ -101,7 +101,7 @@ const WizardStep = ({
- ) + ), )} R.any(key => !R.isNil(values[key]), denominationKeys) @@ -98,19 +98,19 @@ const DenominationsSchema = Yup.object() : context.createError({ path: '', message: - 'The recyclers or at least one of the cassettes must have a value' - }) + 'The recyclers or at least one of the cassettes must have a value', + }), ) const getElements = (machines, locale = {}) => { const fiatCurrency = R.prop('fiatCurrency')(locale) const maxNumberOfCassettes = Math.max( ...R.map(it => it.numberOfCassettes, machines), - 0 + 0, ) const maxNumberOfRecyclers = Math.max( ...R.map(it => it.numberOfRecyclers, machines), - 0 + 0, ) const numberOfCashUnits = maxNumberOfCassettes + Math.ceil(maxNumberOfRecyclers / 2) @@ -122,7 +122,7 @@ const getElements = (machines, locale = {}) => { options: options, labelProp: 'display', valueProp: 'code', - className: 'w-full' + className: 'w-full', } : { decimalPlaces: 0 } @@ -133,8 +133,8 @@ const getElements = (machines, locale = {}) => { width: widthsByNumberOfUnits[numberOfCashUnits]?.machine, view: it => machines.find(({ deviceId }) => deviceId === it).name, size: 'sm', - editable: false - } + editable: false, + }, ] R.until( @@ -156,11 +156,11 @@ const getElements = (machines, locale = {}) => { isHidden: machine => it > machines.find(({ deviceId }) => deviceId === machine.id) - .numberOfCassettes + .numberOfCassettes, }) return R.add(1, it) }, - 1 + 1, ) R.until( @@ -182,12 +182,12 @@ const getElements = (machines, locale = {}) => { it > Math.ceil( machines.find(({ deviceId }) => deviceId === machine.id) - .numberOfRecyclers / 2 - ) + .numberOfRecyclers / 2, + ), }) return R.add(1, it) }, - 1 + 1, ) return elements diff --git a/packages/admin-ui/src/pages/Commissions/Commissions.jsx b/packages/admin-ui/src/pages/Commissions/Commissions.jsx index bfe4a9d2..73bdcb61 100644 --- a/packages/admin-ui/src/pages/Commissions/Commissions.jsx +++ b/packages/admin-ui/src/pages/Commissions/Commissions.jsx @@ -36,7 +36,7 @@ const SAVE_CONFIG = gql` ` const removeCoinFromOverride = crypto => override => R.mergeRight(override, { - cryptoCurrencies: R.without([crypto], override.cryptoCurrencies) + cryptoCurrencies: R.without([crypto], override.cryptoCurrencies), }) const Commissions = ({ name: SCREEN_KEY }) => { @@ -45,7 +45,7 @@ const Commissions = ({ name: SCREEN_KEY }) => { const { data, loading } = useQuery(GET_DATA) const [saveConfig] = useMutation(SAVE_CONFIG, { refetchQueries: () => ['getData'], - onError: error => setError(error) + onError: error => setError(error), }) const config = data?.config && fromNamespace(SCREEN_KEY)(data.config) @@ -78,11 +78,11 @@ const Commissions = ({ name: SCREEN_KEY }) => { const machineOverrides = R.map(removeCoin)(filterMachine(it)) const overrides = machineOverrides.concat( - R.filter(it => !sameMachine(it), it) + R.filter(it => !sameMachine(it), it), ) const config = { - commissions_overrides: R.prepend(override, overrides) + commissions_overrides: R.prepend(override, overrides), } return saveConfig({ variables: { config } }) @@ -92,8 +92,8 @@ const Commissions = ({ name: SCREEN_KEY }) => { ? [ { label: 'Override value', - icon: - } + icon: , + }, ] : [] @@ -107,8 +107,8 @@ const Commissions = ({ name: SCREEN_KEY }) => { text: 'List view', icon: ListingViewIcon, inverseIcon: ReverseListingViewIcon, - toggle: setShowMachines - } + toggle: setShowMachines, + }, ]} iconClassName="ml-1" appendix={ diff --git a/packages/admin-ui/src/pages/Commissions/components/CommissionsDetails.jsx b/packages/admin-ui/src/pages/Commissions/components/CommissionsDetails.jsx index e96c03c3..521a153d 100644 --- a/packages/admin-ui/src/pages/Commissions/components/CommissionsDetails.jsx +++ b/packages/admin-ui/src/pages/Commissions/components/CommissionsDetails.jsx @@ -8,7 +8,7 @@ import { getOverridesSchema, defaults, overridesDefaults, - getOrder + getOrder, } from 'src/pages/Commissions/helper' import { Table as EditableTable } from 'src/components/editableTable' @@ -23,7 +23,7 @@ const CommissionsDetails = memo( const orderedCommissionsOverrides = R.sortWith([ R.ascend(getOrder), - R.ascend(R.prop('machine')) + R.ascend(R.prop('machine')), ])(commissionOverrides) const onEditingDefault = (it, editing) => setEditingDefault(editing) @@ -63,7 +63,7 @@ const CommissionsDetails = memo( validationSchema={getOverridesSchema( orderedCommissionsOverrides, data, - locale + locale, )} data={orderedCommissionsOverrides} elements={overrides(data, currency, orderedCommissionsOverrides)} @@ -73,7 +73,7 @@ const CommissionsDetails = memo( ) - } + }, ) export default CommissionsDetails diff --git a/packages/admin-ui/src/pages/Commissions/components/CommissionsList.jsx b/packages/admin-ui/src/pages/Commissions/components/CommissionsList.jsx index 927660c8..debfd42b 100644 --- a/packages/admin-ui/src/pages/Commissions/components/CommissionsList.jsx +++ b/packages/admin-ui/src/pages/Commissions/components/CommissionsList.jsx @@ -4,7 +4,7 @@ import { overridesDefaults, getCommissions, getListCommissionsSchema, - commissionsList + commissionsList, } from 'src/pages/Commissions/helper' import { Table as EditableTable } from 'src/components/editableTable' @@ -12,39 +12,39 @@ import { Select } from 'src/components/inputs' const SHOW_ALL = { code: 'SHOW_ALL', - display: 'Show all' + display: 'Show all', } const ORDER_OPTIONS = [ { code: 'machine', - display: 'Machine name' + display: 'Machine name', }, { code: 'cryptoCurrencies', - display: 'Cryptocurrency' + display: 'Cryptocurrency', }, { code: 'cashIn', - display: 'Cash-in' + display: 'Cash-in', }, { code: 'cashOut', - display: 'Cash-out' + display: 'Cash-out', }, { code: 'fixedFee', - display: 'Fixed fee' + display: 'Fixed fee', }, { code: 'minimumTx', - display: 'Minimum Tx' - } + display: 'Minimum Tx', + }, ] const getElement = (code, display) => ({ code: code, - display: display || code + display: display || code, }) const sortCommissionsBy = prop => { @@ -61,12 +61,13 @@ const sortCommissionsBy = prop => { const filterCommissions = (coinFilter, machineFilter) => R.compose( R.filter( - it => (machineFilter === SHOW_ALL) | (machineFilter.code === it.machine) + it => (machineFilter === SHOW_ALL) | (machineFilter.code === it.machine), ), R.filter( it => - (coinFilter === SHOW_ALL) | (coinFilter.code === it.cryptoCurrencies[0]) - ) + (coinFilter === SHOW_ALL) | + (coinFilter.code === it.cryptoCurrencies[0]), + ), ) const CommissionsList = memo( @@ -79,7 +80,7 @@ const CommissionsList = memo( const getMachineCoins = deviceId => { const override = R.prop('overrides', localeConfig)?.find( - R.propEq('machine', deviceId) + R.propEq('machine', deviceId), ) const machineCoins = override @@ -96,20 +97,20 @@ const CommissionsList = memo( const machineData = R.sortBy( R.prop('display'), - R.map(getMachineElement)(R.prop('machines', data)) + R.map(getMachineElement)(R.prop('machines', data)), ) const machinesCoinsTuples = R.unnest( - R.map(getMachineCoins)(machineData.map(R.prop('code'))) + R.map(getMachineCoins)(machineData.map(R.prop('code'))), ) const commissions = R.map(([deviceId, cryptoCode]) => - getCommissions(cryptoCode, deviceId, config) + getCommissions(cryptoCode, deviceId, config), )(machinesCoinsTuples) const tableData = R.compose( sortCommissionsBy(orderProp), - filterCommissions(coinFilter, machineFilter) + filterCommissions(coinFilter, machineFilter), )(commissions) return ( @@ -155,7 +156,7 @@ const CommissionsList = memo(
) - } + }, ) export default CommissionsList diff --git a/packages/admin-ui/src/pages/Commissions/helper.jsx b/packages/admin-ui/src/pages/Commissions/helper.jsx index d15f9184..c1c74472 100644 --- a/packages/admin-ui/src/pages/Commissions/helper.jsx +++ b/packages/admin-ui/src/pages/Commissions/helper.jsx @@ -14,12 +14,12 @@ import { CURRENCY_MAX } from 'src/utils/constants' const ALL_MACHINES = { name: 'All Machines', - deviceId: 'ALL_MACHINES' + deviceId: 'ALL_MACHINES', } const ALL_COINS = { display: 'All Coins', - code: 'ALL_COINS' + code: 'ALL_COINS', } const cashInAndOutHeaderStyle = { marginLeft: 6, whiteSpace: 'nowrap' } @@ -68,11 +68,11 @@ const onCryptoChange = (prev, curr, setValue) => { setValue(curr) } -const getOverridesFields = (getData, currency, auxElements) => { +const getOverridesFields = (getData, currency) => { const machineData = [ALL_MACHINES].concat(getData(['machines'])) const rawCryptos = getData(['cryptoCurrencies']) const cryptoData = [ALL_COINS].concat( - R.map(it => ({ display: it.code, code: it.code }))(rawCryptos ?? []) + R.map(it => ({ display: it.code, code: it.code }))(rawCryptos ?? []), ) return [ @@ -85,8 +85,8 @@ const getOverridesFields = (getData, currency, auxElements) => { inputProps: { options: machineData, valueProp: 'deviceId', - labelProp: 'name' - } + labelProp: 'name', + }, }, { name: 'cryptoCurrencies', @@ -100,8 +100,8 @@ const getOverridesFields = (getData, currency, auxElements) => { labelProp: 'display', multiple: true, onChange: onCryptoChange, - shouldStayOpen: true - } + shouldStayOpen: true, + }, }, { header: cashInHeader, @@ -113,8 +113,8 @@ const getOverridesFields = (getData, currency, auxElements) => { suffix: '%', bold: bold, inputProps: { - decimalPlaces: 3 - } + decimalPlaces: 3, + }, }, { header: cashOutHeader, @@ -126,8 +126,8 @@ const getOverridesFields = (getData, currency, auxElements) => { suffix: '%', bold: bold, inputProps: { - decimalPlaces: 3 - } + decimalPlaces: 3, + }, }, { name: 'fixedFee', @@ -139,8 +139,8 @@ const getOverridesFields = (getData, currency, auxElements) => { suffix: currency, bold: bold, inputProps: { - decimalPlaces: 2 - } + decimalPlaces: 2, + }, }, { name: 'minimumTx', @@ -153,8 +153,8 @@ const getOverridesFields = (getData, currency, auxElements) => { suffix: currency, bold: bold, inputProps: { - decimalPlaces: 2 - } + decimalPlaces: 2, + }, }, { name: 'cashOutFixedFee', @@ -167,9 +167,9 @@ const getOverridesFields = (getData, currency, auxElements) => { suffix: currency, bold: bold, inputProps: { - decimalPlaces: 2 - } - } + decimalPlaces: 2, + }, + }, ] } @@ -185,8 +185,8 @@ const mainFields = currency => [ suffix: '%', bold: bold, inputProps: { - decimalPlaces: 3 - } + decimalPlaces: 3, + }, }, { header: cashOutHeader, @@ -199,8 +199,8 @@ const mainFields = currency => [ suffix: '%', bold: bold, inputProps: { - decimalPlaces: 3 - } + decimalPlaces: 3, + }, }, { name: 'fixedFee', @@ -214,8 +214,8 @@ const mainFields = currency => [ suffix: currency, bold: bold, inputProps: { - decimalPlaces: 2 - } + decimalPlaces: 2, + }, }, { name: 'minimumTx', @@ -229,8 +229,8 @@ const mainFields = currency => [ suffix: currency, bold: bold, inputProps: { - decimalPlaces: 2 - } + decimalPlaces: 2, + }, }, { name: 'cashOutFixedFee', @@ -244,9 +244,9 @@ const mainFields = currency => [ suffix: currency, bold: bold, inputProps: { - decimalPlaces: 2 - } - } + decimalPlaces: 2, + }, + }, ] const overrides = (auxData, currency, auxElements) => { @@ -286,7 +286,7 @@ const getSchema = locale => { .label('Cash-out fixed fee') .min(0) .max(highestBill) - .required() + .required(), }) } @@ -303,7 +303,7 @@ const getAlreadyUsed = (id, machine, values) => { const alreadyUsed = R.compose( R.uniq, R.flatten, - R.map(getCrypto) + R.map(getCrypto), )(filteredOverrides) if (machine !== originalMachineId) return alreadyUsed ?? [] @@ -316,11 +316,11 @@ const getOverridesSchema = (values, rawData, locale) => { const machineData = [ALL_MACHINES].concat(getData(['machines'])) const rawCryptos = getData(['cryptoCurrencies']) const cryptoData = [ALL_COINS].concat( - R.map(it => ({ display: it.code, code: it.code }))(rawCryptos ?? []) + R.map(it => ({ display: it.code, code: it.code }))(rawCryptos ?? []), ) const bills = getBillOptions(locale, denominations).map(it => - parseInt(it.code) + parseInt(it.code), ) const highestBill = R.isEmpty(bills) ? CURRENCY_MAX : Math.max(...bills) @@ -336,7 +336,7 @@ const getOverridesSchema = (values, rawData, locale) => { const isAllCoins = R.includes(ALL_COINS.code, cryptoCurrencies) if (isAllMachines && isAllCoins) { return this.createError({ - message: `All machines and all coins should be configured in the default setup table` + message: `All machines and all coins should be configured in the default setup table`, }) } @@ -346,7 +346,7 @@ const getOverridesSchema = (values, rawData, locale) => { const machineView = getView( machineData, 'name', - 'deviceId' + 'deviceId', )(machine) const message = `${codes} already overridden for machine: ${machineView}` @@ -354,7 +354,7 @@ const getOverridesSchema = (values, rawData, locale) => { return this.createError({ message }) } return true - } + }, }) .label('Crypto currencies') .required() @@ -383,7 +383,7 @@ const getOverridesSchema = (values, rawData, locale) => { .label('Cash-out fixed fee') .min(0) .max(highestBill) - .required() + .required(), }) } @@ -392,7 +392,7 @@ const defaults = { cashOut: '', fixedFee: '', minimumTx: '', - cashOutFixedFee: '' + cashOutFixedFee: '', } const overridesDefaults = { @@ -402,7 +402,7 @@ const overridesDefaults = { cashOut: '', fixedFee: '', minimumTx: '', - cashOutFixedFee: '' + cashOutFixedFee: '', } const getOrder = ({ machine, cryptoCurrencies }) => { @@ -426,7 +426,7 @@ const createCommissions = (cryptoCode, deviceId, isDefault, config) => { machine: deviceId, cryptoCurrencies: [cryptoCode], default: isDefault, - id: uuidv4() + id: uuidv4(), } } @@ -438,7 +438,8 @@ const getCommissions = (cryptoCode, deviceId, config) => { } const specificOverride = R.find( - it => it.machine === deviceId && R.includes(cryptoCode)(it.cryptoCurrencies) + it => + it.machine === deviceId && R.includes(cryptoCode)(it.cryptoCurrencies), )(overrides) if (specificOverride !== undefined) @@ -446,7 +447,7 @@ const getCommissions = (cryptoCode, deviceId, config) => { const machineOverride = R.find( it => - it.machine === deviceId && R.includes('ALL_COINS')(it.cryptoCurrencies) + it.machine === deviceId && R.includes('ALL_COINS')(it.cryptoCurrencies), )(overrides) if (machineOverride !== undefined) @@ -455,7 +456,7 @@ const getCommissions = (cryptoCode, deviceId, config) => { const coinOverride = R.find( it => it.machine === 'ALL_MACHINES' && - R.includes(cryptoCode)(it.cryptoCurrencies) + R.includes(cryptoCode)(it.cryptoCurrencies), )(overrides) if (coinOverride !== undefined) @@ -466,7 +467,7 @@ const getCommissions = (cryptoCode, deviceId, config) => { const getListCommissionsSchema = locale => { const bills = getBillOptions(locale, denominations).map(it => - parseInt(it.code) + parseInt(it.code), ) const highestBill = R.isEmpty(bills) ? CURRENCY_MAX : Math.max(...bills) @@ -497,21 +498,21 @@ const getListCommissionsSchema = locale => { .label('Cash-out fixed fee') .min(0) .max(highestBill) - .required() + .required(), }) } -const getTextStyle = (obj, isEditing) => { +const getTextStyle = obj => { return { color: obj.default ? primaryColor : secondaryColorDark } } -const commissionsList = (auxData, currency, auxElements) => { +const commissionsList = (auxData, currency) => { const getData = R.path(R.__, auxData) return getListCommissionsFields(getData, currency, defaults) } -const getListCommissionsFields = (getData, currency, defaults) => { +const getListCommissionsFields = (getData, currency) => { const machineData = [ALL_MACHINES].concat(getData(['machines'])) return [ @@ -520,7 +521,7 @@ const getListCommissionsFields = (getData, currency, defaults) => { width: 196, size: 'sm', view: getView(machineData, 'name', 'deviceId'), - editable: false + editable: false, }, { name: 'cryptoCurrencies', @@ -528,7 +529,7 @@ const getListCommissionsFields = (getData, currency, defaults) => { width: 150, view: R.prop(0), size: 'sm', - editable: false + editable: false, }, { header: cashInHeader, @@ -540,8 +541,8 @@ const getListCommissionsFields = (getData, currency, defaults) => { suffix: '%', textStyle: obj => getTextStyle(obj), inputProps: { - decimalPlaces: 3 - } + decimalPlaces: 3, + }, }, { header: cashOutHeader, @@ -554,8 +555,8 @@ const getListCommissionsFields = (getData, currency, defaults) => { suffix: '%', textStyle: obj => getTextStyle(obj), inputProps: { - decimalPlaces: 3 - } + decimalPlaces: 3, + }, }, { name: 'fixedFee', @@ -567,8 +568,8 @@ const getListCommissionsFields = (getData, currency, defaults) => { suffix: currency, textStyle: obj => getTextStyle(obj), inputProps: { - decimalPlaces: 2 - } + decimalPlaces: 2, + }, }, { name: 'minimumTx', @@ -580,8 +581,8 @@ const getListCommissionsFields = (getData, currency, defaults) => { suffix: currency, textStyle: obj => getTextStyle(obj), inputProps: { - decimalPlaces: 2 - } + decimalPlaces: 2, + }, }, { name: 'cashOutFixedFee', @@ -594,9 +595,9 @@ const getListCommissionsFields = (getData, currency, defaults) => { suffix: currency, textStyle: obj => getTextStyle(obj), inputProps: { - decimalPlaces: 2 - } - } + decimalPlaces: 2, + }, + }, ] } @@ -610,5 +611,5 @@ export { getOrder, getCommissions, getListCommissionsSchema, - commissionsList + commissionsList, } diff --git a/packages/admin-ui/src/pages/Customers/CustomerData.jsx b/packages/admin-ui/src/pages/Customers/CustomerData.jsx index df572d82..b04e7a1d 100644 --- a/packages/admin-ui/src/pages/Customers/CustomerData.jsx +++ b/packages/admin-ui/src/pages/Customers/CustomerData.jsx @@ -10,7 +10,7 @@ import * as Yup from 'yup' import { TextInput } from 'src/components/inputs/formik' import { OVERRIDE_AUTHORIZED, - OVERRIDE_REJECTED + OVERRIDE_REJECTED, } from 'src/pages/Customers/components/consts' import { onlyFirstToUpper } from 'src/utils/string' @@ -20,7 +20,7 @@ import { customerDataSchemas, formatDates, tryFormatDate, - getFormattedPhone + getFormattedPhone, } from './helper' const IMAGE_WIDTH = 165 @@ -50,7 +50,7 @@ const CustomerData = ({ updateCustomRequest, authorizeCustomRequest, updateCustomEntry, - checkAgainstSanctions + checkAgainstSanctions, }) => { const [previewPhoto, setPreviewPhoto] = useState(null) const [previewCard, setPreviewCard] = useState(null) @@ -68,21 +68,22 @@ const CustomerData = ({ : 'Failed' const sortByName = R.sortBy( - R.compose(R.toLower, R.path(['customInfoRequest', 'customRequest', 'name'])) + R.compose( + R.toLower, + R.path(['customInfoRequest', 'customRequest', 'name']), + ), ) const customFields = [] const customRequirements = [] const customInfoRequests = sortByName( - R.path(['customInfoRequests'])(customer) ?? [] + R.path(['customInfoRequests'])(customer) ?? [], ) const phone = R.path(['phone'])(customer) const email = R.path(['email'])(customer) const smsData = R.path(['subscriberInfo'])(customer) - const isEven = elem => elem % 2 === 0 - const getVisibleCards = R.filter(elem => elem.isAvailable) const initialValues = { @@ -93,23 +94,23 @@ const CustomerData = ({ dateOfBirth: tryFormatDate(rawDob), gender: R.path(['gender'])(idData) ?? '', country: R.path(['country'])(idData) ?? '', - expirationDate: tryFormatDate(rawExpirationDate) + expirationDate: tryFormatDate(rawExpirationDate), }, usSsn: { - usSsn: customer.usSsn ?? '' + usSsn: customer.usSsn ?? '', }, frontCamera: { - frontCamera: null + frontCamera: null, }, idCardPhoto: { - idCardPhoto: null + idCardPhoto: null, }, email: { - email + email, }, smsData: { - phoneNumber: getFormattedPhone(phone, locale.country) - } + phoneNumber: getFormattedPhone(phone, locale.country), + }, } const smsDataElements = [ @@ -117,8 +118,8 @@ const CustomerData = ({ name: 'phoneNumber', label: 'Phone number', component: TextInput, - editable: false - } + editable: false, + }, ] const smsDataSchema = { @@ -128,10 +129,10 @@ const CustomerData = ({ if (R.length(fields) === 2) { return Yup.object().shape({ [R.head(fields)]: Yup.string().required(), - [R.last(fields)]: Yup.string().required() + [R.last(fields)]: Yup.string().required(), }) } - }) + }), } const cards = [ @@ -146,18 +147,18 @@ const CustomerData = ({ deleteEditedData: () => deleteEditedData({ idCardData: null }), save: values => editCustomer({ - idCardData: R.merge(idData, formatDates(values)) + idCardData: R.merge(idData, formatDates(values)), }), validationSchema: customerDataSchemas.idCardData, checkAgainstSanctions: () => checkAgainstSanctions({ variables: { - customerId: R.path(['id'])(customer) - } + customerId: R.path(['id'])(customer), + }, }), initialValues: initialValues.idCardData, isAvailable: !R.isNil(idData), - editable: true + editable: true, }, { fields: smsDataElements, @@ -169,15 +170,15 @@ const CustomerData = ({ save: values => { editCustomer({ subscriberInfo: { - result: R.merge(smsData, R.omit(['phoneNumber'])(values)) - } + result: R.merge(smsData, R.omit(['phoneNumber'])(values)), + }, }) }, validationSchema: smsDataSchema.smsData, initialValues: initialValues.smsData, isAvailable: !R.isNil(phone), hasAdditionalData: !R.isNil(smsData) && !R.isEmpty(smsData), - editable: false + editable: false, }, { title: 'Email', @@ -190,13 +191,13 @@ const CustomerData = ({ deleteEditedData: () => deleteEditedData({ email: null }), initialValues: initialValues.email, isAvailable: !R.isNil(customer.email), - editable: false + editable: false, }, { title: 'Name', titleIcon: , isAvailable: false, - editable: true + editable: true, }, { title: 'Sanctions check', @@ -207,7 +208,7 @@ const CustomerData = ({ reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }), children: () => {sanctionsDisplay}, isAvailable: !R.isNil(sanctions), - editable: true + editable: true, }, { fields: customerDataElements.frontCamera, @@ -221,7 +222,7 @@ const CustomerData = ({ setPreviewPhoto(null) return replacePhoto({ newPhoto: values.frontCamera, - photoType: 'frontCamera' + photoType: 'frontCamera', }) }, cancel: () => setPreviewPhoto(null), @@ -245,7 +246,7 @@ const CustomerData = ({ validationSchema: customerDataSchemas.frontCamera, initialValues: initialValues.frontCamera, isAvailable: !R.isNil(customer.frontCameraPath), - editable: true + editable: true, }, { fields: customerDataElements.idCardPhoto, @@ -259,7 +260,7 @@ const CustomerData = ({ setPreviewCard(null) return replacePhoto({ newPhoto: values.idCardPhoto, - photoType: 'idCardPhoto' + photoType: 'idCardPhoto', }) }, cancel: () => setPreviewCard(null), @@ -283,7 +284,7 @@ const CustomerData = ({ validationSchema: customerDataSchemas.idCardPhoto, initialValues: initialValues.idCardPhoto, isAvailable: !R.isNil(customer.idCardPhotoPath), - editable: true + editable: true, }, { fields: customerDataElements.usSsn, @@ -298,8 +299,8 @@ const CustomerData = ({ validationSchema: customerDataSchemas.usSsn, initialValues: initialValues.usSsn, isAvailable: !R.isNil(customer.usSsn), - editable: true - } + editable: true, + }, ] R.forEach(it => { @@ -310,8 +311,8 @@ const CustomerData = ({ label: it.customInfoRequest.customRequest.name, value: it.customerData.data ?? '', component: TextInput, - editable: true - } + editable: true, + }, ], title: it.customInfoRequest.customRequest.name, titleIcon: , @@ -321,16 +322,16 @@ const CustomerData = ({ variables: { customerId: it.customerId, infoRequestId: it.customInfoRequest.id, - override: OVERRIDE_AUTHORIZED - } + override: OVERRIDE_AUTHORIZED, + }, }), reject: () => authorizeCustomRequest({ variables: { customerId: it.customerId, infoRequestId: it.customInfoRequest.id, - override: OVERRIDE_REJECTED - } + override: OVERRIDE_REJECTED, + }, }), save: values => { updateCustomRequest({ @@ -339,18 +340,18 @@ const CustomerData = ({ infoRequestId: it.customInfoRequest.id, data: { info_request_id: it.customInfoRequest.id, - data: values[it.customInfoRequest.id] - } - } + data: values[it.customInfoRequest.id], + }, + }, }) }, deleteEditedData: () => {}, validationSchema: Yup.object().shape({ - [it.customInfoRequest.id]: Yup.string() + [it.customInfoRequest.id]: Yup.string(), }), initialValues: { - [it.customInfoRequest.id]: it.customerData.data ?? '' - } + [it.customInfoRequest.id]: it.customerData.data ?? '', + }, }) }, customInfoRequests) @@ -363,27 +364,27 @@ const CustomerData = ({ label: it.label, value: it.value ?? '', component: TextInput, - editable: true - } + editable: true, + }, ], title: it.label, titleIcon: , save: values => { updateCustomEntry({ fieldId: it.id, - value: values[it.label] + value: values[it.label], }) }, deleteEditedData: () => {}, validationSchema: Yup.object().shape({ - [it.label]: Yup.string() + [it.label]: Yup.string(), }), initialValues: { - [it.label]: it.value ?? '' - } + [it.label]: it.value ?? '', + }, }) }, - R.path(['customFields'])(customer) ?? [] + R.path(['customFields'])(customer) ?? [], ) R.forEach( @@ -393,10 +394,10 @@ const CustomerData = ({ name: it, label: onlyFirstToUpper(it), component: TextInput, - editable: false + editable: false, }) }, - R.keys(smsData) ?? [] + R.keys(smsData) ?? [], ) const externalCompliance = R.map(it => ({ @@ -404,26 +405,26 @@ const CustomerData = ({ { name: 'externalId', label: 'Third Party ID', - editable: false + editable: false, }, { name: 'lastKnownStatus', label: 'Last Known Status', - editable: false + editable: false, }, { name: 'lastUpdated', label: 'Last Updated', - editable: false - } + editable: false, + }, ], titleIcon: , title: `External Info [${it.service}]`, initialValues: it ?? { externalId: '', lastKnownStatus: '', - lastUpdated: '' - } + lastUpdated: '', + }, }))(customer.externalCompliance ?? []) const editableCard = ( @@ -443,9 +444,9 @@ const CustomerData = ({ hasImage, hasAdditionalData, editable, - checkAgainstSanctions + checkAgainstSanctions, }, - idx + idx, ) => { return (
@@ -474,7 +475,7 @@ const CustomerData = ({ const nonEditableCard = ( { title, state, titleIcon, fields, hasImage, initialValues, children }, - idx + idx, ) => { return (
diff --git a/packages/admin-ui/src/pages/Customers/CustomerNotes.jsx b/packages/admin-ui/src/pages/Customers/CustomerNotes.jsx index ee1ad95e..5f5cf2cb 100644 --- a/packages/admin-ui/src/pages/Customers/CustomerNotes.jsx +++ b/packages/admin-ui/src/pages/Customers/CustomerNotes.jsx @@ -12,14 +12,14 @@ const CustomerNotes = ({ createNote, deleteNote, editNote, - timezone + timezone, }) => { const [openModal, setOpenModal] = useState(false) const [editing, setEditing] = useState(null) const customerNotes = R.sort( (a, b) => new Date(b?.created).getTime() - new Date(a?.created).getTime(), - customer.notes ?? [] + customer.notes ?? [], ) const handleModalClose = () => { @@ -39,7 +39,7 @@ const CustomerNotes = ({ if (!R.equals(it.newContent, it.oldContent)) { editNote({ noteId: it.noteId, - newContent: it.newContent + newContent: it.newContent, }) } setEditing(null) diff --git a/packages/admin-ui/src/pages/Customers/CustomerPhotos.jsx b/packages/admin-ui/src/pages/Customers/CustomerPhotos.jsx index 75d81372..761899f5 100644 --- a/packages/admin-ui/src/pages/Customers/CustomerPhotos.jsx +++ b/packages/admin-ui/src/pages/Customers/CustomerPhotos.jsx @@ -49,7 +49,7 @@ export const PhotoCard = ({ date, src, setPhotosDialog, - setPhotoClickIndex + setPhotoClickIndex, }) => { return ( { const { data: customerResponse, refetch: getCustomer, - loading: customerLoading + loading: customerLoading, } = useQuery(GET_CUSTOMER, { - variables: { customerId } + variables: { customerId }, }) const { data: configResponse, loading: configLoading } = useQuery(GET_DATA) const { data: activeCustomRequests } = useQuery(GET_ACTIVE_CUSTOM_REQUESTS, { variables: { - onlyEnabled: true - } + onlyEnabled: true, + }, }) const [setCustomEntry] = useMutation(SET_CUSTOM_ENTRY, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [editCustomEntry] = useMutation(EDIT_CUSTOM_ENTRY, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [replaceCustomerPhoto] = useMutation(REPLACE_CUSTOMER_PHOTO, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [editCustomerData] = useMutation(EDIT_CUSTOMER, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [deleteCustomerEditedData] = useMutation(DELETE_EDITED_CUSTOMER, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [setCustomer] = useMutation(SET_CUSTOMER, { onCompleted: () => { getCustomer() }, - onError: error => setError(error) + onError: error => setError(error), }) const [authorizeCustomRequest] = useMutation(SET_AUTHORIZED_REQUEST, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [setCustomerCustomInfoRequest] = useMutation( SET_CUSTOMER_CUSTOM_INFO_REQUEST, { - onCompleted: () => getCustomer() - } + onCompleted: () => getCustomer(), + }, ) const [createNote] = useMutation(CREATE_NOTE, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [deleteNote] = useMutation(DELETE_NOTE, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [editNote] = useMutation(EDIT_NOTE, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const saveCustomEntry = it => { @@ -366,8 +366,8 @@ const CustomerProfile = memo(() => { variables: { customerId, label: it.title, - value: it.data - } + value: it.data, + }, }) setWizard(null) } @@ -377,31 +377,31 @@ const CustomerProfile = memo(() => { variables: { customerId, fieldId: it.fieldId, - value: it.value - } + value: it.value, + }, }) } const [enableTestCustomer] = useMutation(ENABLE_TEST_CUSTOMER, { variables: { customerId }, - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [disableTestCustomer] = useMutation(DISABLE_TEST_CUSTOMER, { variables: { customerId }, - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const [checkAgainstSanctions] = useLazyQuery(CHECK_AGAINST_SANCTIONS, { - onCompleted: () => getCustomer() + onCompleted: () => getCustomer(), }) const updateCustomer = it => setCustomer({ variables: { customerId, - customerInput: it - } + customerInput: it, + }, }) const replacePhoto = it => { @@ -409,8 +409,8 @@ const CustomerProfile = memo(() => { variables: { customerId, newPhoto: it.newPhoto, - photoType: it.photoType - } + photoType: it.photoType, + }, }) setWizard(null) } @@ -419,8 +419,8 @@ const CustomerProfile = memo(() => { editCustomerData({ variables: { customerId, - customerEdit: it - } + customerEdit: it, + }, }) setWizard(null) } @@ -429,8 +429,8 @@ const CustomerProfile = memo(() => { deleteCustomerEditedData({ variables: { customerId, - customerEdit: it - } + customerEdit: it, + }, }) const createCustomerNote = it => @@ -438,23 +438,23 @@ const CustomerProfile = memo(() => { variables: { customerId, title: it.title, - content: it.content - } + content: it.content, + }, }) const deleteCustomerNote = it => deleteNote({ variables: { - noteId: it.noteId - } + noteId: it.noteId, + }, }) const editCustomerNote = it => editNote({ variables: { noteId: it.noteId, - newContent: it.newContent - } + newContent: it.newContent, + }, }) const onClickSidebarItem = code => setClickedItem(code) @@ -464,7 +464,7 @@ const CustomerProfile = memo(() => { const customerData = R.path(['customer'])(customerResponse) ?? [] const rawTransactions = R.path(['transactions'])(customerData) ?? [] const sortedTransactions = R.sort(R.descend(R.prop('cryptoAtoms')))( - rawTransactions + rawTransactions, ) const name = getName(customerData) const blocked = @@ -477,12 +477,12 @@ const CustomerProfile = memo(() => { const isPhotos = clickedItem === 'photos' const frontCameraData = R.pick(['frontCameraPath', 'frontCameraAt'])( - customerData + customerData, ) const txPhotosData = sortedTransactions && R.map(R.pick(['id', 'txCustomerPhotoPath', 'txCustomerPhotoAt']))( - sortedTransactions + sortedTransactions, ) const photosData = formatPhotosData(R.append(frontCameraData, txPhotosData)) @@ -491,8 +491,8 @@ const CustomerProfile = memo(() => { { photoDir: 'id-card-photo', path: customerData.idCardPhotoPath, - date: customerData.idCardPhotoAt - } + date: customerData.idCardPhotoAt, + }, ] : [] @@ -503,7 +503,7 @@ const CustomerProfile = memo(() => { const customInfoRequirementOptions = activeCustomRequests?.customInfoRequests?.map(it => ({ value: it.id, - display: it.customRequest.name + display: it.customRequest.name, })) ?? [] const email = R.path(['email'])(customerData) @@ -565,7 +565,7 @@ const CustomerProfile = memo(() => { InverseIcon={AuthorizeReversedIcon} onClick={() => updateCustomer({ - suspendedUntil: null + suspendedUntil: null, }) }> {`Unsuspend customer`} @@ -582,7 +582,7 @@ const CustomerProfile = memo(() => { updateCustomer({ authorizedOverride: blocked ? OVERRIDE_AUTHORIZED - : OVERRIDE_REJECTED + : OVERRIDE_REJECTED, }) }> {`${blocked ? 'Authorize' : 'Block'} customer`} diff --git a/packages/admin-ui/src/pages/Customers/Customers.jsx b/packages/admin-ui/src/pages/Customers/Customers.jsx index 284363e6..892fe634 100644 --- a/packages/admin-ui/src/pages/Customers/Customers.jsx +++ b/packages/admin-ui/src/pages/Customers/Customers.jsx @@ -108,10 +108,10 @@ const Customers = () => { const { data: customersResponse, loading: customerLoading, - refetch + refetch, } = useQuery(GET_CUSTOMERS, { variables, - onCompleted: data => setFilteredCustomers(R.path(['customers'])(data)) + onCompleted: data => setFilteredCustomers(R.path(['customers'])(data)), }) const { data: filtersResponse, loading: loadingFilters } = @@ -122,9 +122,9 @@ const Customers = () => { refetchQueries: () => [ { query: GET_CUSTOMERS, - variables - } - ] + variables, + }, + ], }) const configData = R.path(['config'])(customersResponse) ?? [] @@ -137,14 +137,14 @@ const Customers = () => { R.assoc( 'authorizedStatus', getAuthorizedStatus(c, triggers, customRequirementsData), - c + c, ) const byAuthorized = c => (c.authorizedStatus.label === 'Pending' ? 0 : 1) const byLastActive = c => new Date(R.prop('lastActive', c) ?? '0') const customersData = R.pipe( R.map(setAuthorizedStatus), - R.sortWith([R.ascend(byAuthorized), R.descend(byLastActive)]) + R.sortWith([R.ascend(byAuthorized), R.descend(byLastActive)]), )(filteredCustomers ?? []) const onFilterChange = filters => { @@ -157,7 +157,7 @@ const Customers = () => { name: filtersObject.name, email: filtersObject.email, address: filtersObject.address, - id: filtersObject.id + id: filtersObject.id, }) refetch && refetch() @@ -165,7 +165,7 @@ const Customers = () => { const onFilterDelete = filter => { const newFilters = R.filter( - f => !R.whereEq(R.pick(['type', 'value'], f), filter) + f => !R.whereEq(R.pick(['type', 'value'], f), filter), )(filters) setFilters(newFilters) @@ -177,7 +177,7 @@ const Customers = () => { name: filtersObject.name, email: filtersObject.email, address: filtersObject.address, - id: filtersObject.id + id: filtersObject.id, }) refetch && refetch() @@ -192,7 +192,7 @@ const Customers = () => { name: filtersObject.name, email: filtersObject.email, address: filtersObject.address, - id: filtersObject.id + id: filtersObject.id, }) refetch && refetch() @@ -224,7 +224,7 @@ const Customers = () => { } labels={[ { label: 'Cash-in', icon: }, - { label: 'Cash-out', icon: } + { label: 'Cash-out', icon: }, ]} /> {filters.length > 0 && ( diff --git a/packages/admin-ui/src/pages/Customers/CustomersList.jsx b/packages/admin-ui/src/pages/Customers/CustomersList.jsx index 66b67833..10f01cfa 100644 --- a/packages/admin-ui/src/pages/Customers/CustomersList.jsx +++ b/packages/admin-ui/src/pages/Customers/CustomersList.jsx @@ -8,44 +8,37 @@ import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react' import { getFormattedPhone, getName } from './helper' -const CustomersList = ({ - data, - locale, - onClick, - loading, - triggers, - customRequests -}) => { +const CustomersList = ({ data, locale, onClick, loading }) => { const elements = [ { header: 'Phone/email', width: 199, view: it => `${getFormattedPhone(it.phone, locale.country) || ''} - ${it.email || ''}` + ${it.email || ''}`, }, { header: 'Name', width: 241, - view: getName + view: getName, }, { header: 'Total Txs', width: 126, textAlign: 'right', - view: it => `${Number.parseInt(it.totalTxs)}` + view: it => `${Number.parseInt(it.totalTxs)}`, }, { header: 'Total spent', width: 152, textAlign: 'right', view: it => - `${Number.parseFloat(it.totalSpent)} ${it.lastTxFiatCode ?? ''}` + `${Number.parseFloat(it.totalSpent)} ${it.lastTxFiatCode ?? ''}`, }, { header: 'Last active', width: 133, view: it => - (it.lastActive && format('yyyy-MM-dd', new Date(it.lastActive))) ?? '' + (it.lastActive && format('yyyy-MM-dd', new Date(it.lastActive))) ?? '', }, { header: 'Last transaction', @@ -62,13 +55,13 @@ const CustomersList = ({ {hasLastTx && lastIcon} ) - } + }, }, { header: 'Status', width: 191, - view: it => - } + view: it => , + }, ] return ( diff --git a/packages/admin-ui/src/pages/Customers/Wizard.jsx b/packages/admin-ui/src/pages/Customers/Wizard.jsx index cb94bbab..f11c845b 100644 --- a/packages/admin-ui/src/pages/Customers/Wizard.jsx +++ b/packages/admin-ui/src/pages/Customers/Wizard.jsx @@ -13,7 +13,7 @@ import { requirementElements, formatDates, REQUIREMENT, - ID_CARD_DATA + ID_CARD_DATA, } from './helper' const LAST_STEP = 2 @@ -41,12 +41,12 @@ const Wizard = ({ error, customInfoRequirementOptions, addCustomerData, - addPhoto + addPhoto, }) => { const [selectedValues, setSelectedValues] = useState(null) const [{ step, config }, setState] = useState({ - step: 1 + step: 1, }) const isIdCardData = values => values?.requirement === ID_CARD_DATA @@ -67,7 +67,7 @@ const Wizard = ({ case 'customerDataUpload': return addPhoto({ newPhoto: R.head(R.values(it)), - photoType: R.head(R.keys(it)) + photoType: R.head(R.keys(it)), }) case 'customEntry': return save(newConfig) @@ -82,7 +82,7 @@ const Wizard = ({ setState({ step: step + 1, - config: newConfig + config: newConfig, }) } diff --git a/packages/admin-ui/src/pages/Customers/components/CreateCustomerModal.jsx b/packages/admin-ui/src/pages/Customers/components/CreateCustomerModal.jsx index c71f5289..4ca2737f 100644 --- a/packages/admin-ui/src/pages/Customers/components/CreateCustomerModal.jsx +++ b/packages/admin-ui/src/pages/Customers/components/CreateCustomerModal.jsx @@ -17,13 +17,14 @@ const getValidationSchema = countryCodes => .test('is-valid-number', 'That is not a valid phone number', value => { try { return countryCodes.some(countryCode => - parsePhoneNumberWithError(value, countryCode).isValid() + parsePhoneNumberWithError(value, countryCode).isValid(), ) } catch (e) { + console.error(e) return false } }) - .trim() + .trim(), }) const formatPhoneNumber = (countryCodes, numberStr) => { @@ -36,7 +37,7 @@ const formatPhoneNumber = (countryCodes, numberStr) => { } const initialValues = { - phoneNumber: '' + phoneNumber: '', } const getErrorMsg = (formikErrors, formikTouched) => { @@ -49,7 +50,7 @@ const getErrorMsg = (formikErrors, formikTouched) => { const CreateCustomerModal = ({ showModal, handleClose, onSubmit, locale }) => { const possibleCountries = R.append( locale?.country, - R.map(it => it.country, locale?.overrides ?? []) + R.map(it => it.country, locale?.overrides ?? []), ) return ( @@ -68,9 +69,9 @@ const CreateCustomerModal = ({ showModal, handleClose, onSubmit, locale }) => { variables: { phoneNumber: formatPhoneNumber( possibleCountries, - values.phoneNumber - ) - } + values.phoneNumber, + ), + }, }) }}> {({ errors, touched }) => ( diff --git a/packages/admin-ui/src/pages/Customers/components/CustomerDetails.jsx b/packages/admin-ui/src/pages/Customers/components/CustomerDetails.jsx index fb09c760..e720e332 100644 --- a/packages/admin-ui/src/pages/Customers/components/CustomerDetails.jsx +++ b/packages/admin-ui/src/pages/Customers/components/CustomerDetails.jsx @@ -18,29 +18,29 @@ const CustomerDetails = memo(({ customer, photosData, locale, timezone }) => { { header: 'Phone number', size: 172, - value: getFormattedPhone(customer.phone, locale.country) - } + value: getFormattedPhone(customer.phone, locale.country), + }, ] if (idNumber) elements.push({ header: 'ID number', size: 172, - value: idNumber + value: idNumber, }) if (usSsn) elements.push({ header: 'US SSN', size: 127, - value: usSsn + value: usSsn, }) if (email) elements.push({ header: 'Email', size: 190, - value: email + value: email, }) return ( diff --git a/packages/admin-ui/src/pages/Customers/components/CustomerSidebar.jsx b/packages/admin-ui/src/pages/Customers/components/CustomerSidebar.jsx index 3b396d96..b7109e78 100644 --- a/packages/admin-ui/src/pages/Customers/components/CustomerSidebar.jsx +++ b/packages/admin-ui/src/pages/Customers/components/CustomerSidebar.jsx @@ -17,26 +17,26 @@ const CustomerSidebar = ({ isSelected, onClick }) => { code: 'overview', display: 'Overview', Icon: OverviewIcon, - InverseIcon: OverviewReversedIcon + InverseIcon: OverviewReversedIcon, }, { code: 'customerData', display: 'Customer data', Icon: CustomerDataIcon, - InverseIcon: CustomerDataReversedIcon + InverseIcon: CustomerDataReversedIcon, }, { code: 'notes', display: 'Notes', Icon: NoteIcon, - InverseIcon: NoteReversedIcon + InverseIcon: NoteReversedIcon, }, { code: 'photos', display: 'Photos & files', Icon: Photos, - InverseIcon: PhotosReversedIcon - } + InverseIcon: PhotosReversedIcon, + }, ] return ( @@ -46,7 +46,7 @@ const CustomerSidebar = ({ isSelected, onClick }) => { key={idx} className={classnames({ 'gap-4 p-4 cursor-pointer flex items-center': true, - 'bg-comet2': isSelected(code) + 'bg-comet2': isSelected(code), })} onClick={() => onClick(code)}> {isSelected(code) ? : } @@ -54,7 +54,7 @@ const CustomerSidebar = ({ isSelected, onClick }) => { noMargin className={classnames({ 'text-comet2': true, - 'text-white font-bold': isSelected(code) + 'text-white font-bold': isSelected(code), })}> {display}

diff --git a/packages/admin-ui/src/pages/Customers/components/EditableCard.jsx b/packages/admin-ui/src/pages/Customers/components/EditableCard.jsx index f03378f0..8ec57d4a 100644 --- a/packages/admin-ui/src/pages/Customers/components/EditableCard.jsx +++ b/packages/admin-ui/src/pages/Customers/components/EditableCard.jsx @@ -20,7 +20,7 @@ import SaveReversedIcon from 'src/styling/icons/circle buttons/save/white.svg?re import { ActionButton } from 'src/components/buttons' import { OVERRIDE_REJECTED, - OVERRIDE_PENDING + OVERRIDE_PENDING, } from 'src/pages/Customers/components/consts' const ReadOnlyField = ({ field, value }) => { @@ -73,9 +73,8 @@ const EditableCard = ({ children = () => {}, validationSchema, initialValues, - deleteEditedData, editable, - checkAgainstSanctions + checkAgainstSanctions, }) => { const formRef = useRef() diff --git a/packages/admin-ui/src/pages/Customers/components/PhotosCard.jsx b/packages/admin-ui/src/pages/Customers/components/PhotosCard.jsx index dd72a435..8a0fd60c 100644 --- a/packages/admin-ui/src/pages/Customers/components/PhotosCard.jsx +++ b/packages/admin-ui/src/pages/Customers/components/PhotosCard.jsx @@ -13,7 +13,7 @@ const PhotosCard = memo(({ photosData, timezone }) => { const sortedPhotosData = R.sortWith( [(a, b) => R.has('id', a) - R.has('id', b), R.descend(R.prop('date'))], - photosData + photosData, ) const singlePhoto = R.head(sortedPhotosData) diff --git a/packages/admin-ui/src/pages/Customers/components/PhotosCarousel.jsx b/packages/admin-ui/src/pages/Customers/components/PhotosCarousel.jsx index 006addca..91e6b567 100644 --- a/packages/admin-ui/src/pages/Customers/components/PhotosCarousel.jsx +++ b/packages/admin-ui/src/pages/Customers/components/PhotosCarousel.jsx @@ -43,7 +43,7 @@ const PhotosCarousel = memo(({ photosData, timezone }) => { formatDate( photosData[currentIndex]?.date, timezone, - 'yyyy-MM-dd HH:mm' + 'yyyy-MM-dd HH:mm', )}
diff --git a/packages/admin-ui/src/pages/Customers/components/TransactionsList.jsx b/packages/admin-ui/src/pages/Customers/components/TransactionsList.jsx index f426a20e..60594450 100644 --- a/packages/admin-ui/src/pages/Customers/components/TransactionsList.jsx +++ b/packages/admin-ui/src/pages/Customers/components/TransactionsList.jsx @@ -25,16 +25,16 @@ const TransactionsList = ({ customer, data, loading }) => { size: 127, value: ifNotNull( customer.totalTxs, - `${Number.parseInt(customer.totalTxs)}` - ) + `${Number.parseInt(customer.totalTxs)}`, + ), }, { header: 'Transaction volume', size: 167, value: ifNotNull( customer.totalSpent, - `${Number.parseFloat(customer.totalSpent)} ${customer.lastTxFiatCode}` - ) + `${Number.parseFloat(customer.totalSpent)} ${customer.lastTxFiatCode}`, + ), }, { header: 'Last active', @@ -43,7 +43,7 @@ const TransactionsList = ({ customer, data, loading }) => { !R.isNil(timezone) && ((customer.lastActive && formatDate(customer.lastActive, timezone, 'yyyy-MM-dd')) ?? - '') + ''), }, { header: 'Last transaction', @@ -54,14 +54,14 @@ const TransactionsList = ({ customer, data, loading }) => { {`${Number.parseFloat(customer.lastTxFiat)} ${customer.lastTxFiatCode}`} - - ) + , + ), }, { header: 'Last used machine', size: 198, - value: ifNotNull(lastUsedMachineName, <>{lastUsedMachineName}) - } + value: ifNotNull(lastUsedMachineName, <>{lastUsedMachineName}), + }, ] const tableElements = [ @@ -75,12 +75,12 @@ const TransactionsList = ({ customer, data, loading }) => { )} - ) + ), }, { header: 'Machine', width: 160, - view: R.path(['machineName']) + view: R.path(['machineName']), }, { header: 'Transaction ID', @@ -89,7 +89,7 @@ const TransactionsList = ({ customer, data, loading }) => { {it.id} - ) + ), }, { header: 'Cash', @@ -100,7 +100,7 @@ const TransactionsList = ({ customer, data, loading }) => { {`${Number.parseFloat(it.fiat)} `} {it.fiatCode} - ) + ), }, { header: 'Crypto', @@ -109,22 +109,22 @@ const TransactionsList = ({ customer, data, loading }) => { view: it => ( <> {`${toUnit(new BigNumber(it.cryptoAtoms), it.cryptoCode).toFormat( - 5 + 5, )} `} {it.cryptoCode} - ) + ), }, { header: 'Date', width: 100, - view: it => formatDate(it.created, timezone, 'yyyy‑MM‑dd') + view: it => formatDate(it.created, timezone, 'yyyy‑MM‑dd'), }, { header: 'Time (h:m:s)', width: 130, - view: it => formatDate(it.created, timezone, 'HH:mm:ss') - } + view: it => formatDate(it.created, timezone, 'HH:mm:ss'), + }, ] return ( diff --git a/packages/admin-ui/src/pages/Customers/components/Upload.jsx b/packages/admin-ui/src/pages/Customers/components/Upload.jsx index 1e81c289..94b48ae3 100644 --- a/packages/admin-ui/src/pages/Customers/components/Upload.jsx +++ b/packages/admin-ui/src/pages/Customers/components/Upload.jsx @@ -27,10 +27,10 @@ const Upload = ({ type }) => { setData({ preview: isImage ? URL.createObjectURL(R.head(acceptedData)) - : R.head(acceptedData).name + : R.head(acceptedData).name, }) }, - [isImage, type, setFieldValue] + [isImage, type, setFieldValue], ) const { getRootProps, getInputProps } = useDropzone({ onDrop }) diff --git a/packages/admin-ui/src/pages/Customers/components/index.js b/packages/admin-ui/src/pages/Customers/components/index.js index 448efe76..41020d36 100644 --- a/packages/admin-ui/src/pages/Customers/components/index.js +++ b/packages/admin-ui/src/pages/Customers/components/index.js @@ -12,5 +12,5 @@ export { CustomerSidebar, EditableCard, Wizard, - Upload + Upload, } diff --git a/packages/admin-ui/src/pages/Customers/components/notes/NewNoteModal.jsx b/packages/admin-ui/src/pages/Customers/components/notes/NewNoteModal.jsx index 5725472a..99d09f5c 100644 --- a/packages/admin-ui/src/pages/Customers/components/notes/NewNoteModal.jsx +++ b/packages/admin-ui/src/pages/Customers/components/notes/NewNoteModal.jsx @@ -9,12 +9,12 @@ import { TextInput } from 'src/components/inputs/formik' const initialValues = { title: '', - content: '' + content: '', } const validationSchema = Yup.object().shape({ title: Yup.string().required().trim().max(25), - content: Yup.string().required() + content: Yup.string().required(), }) const NewNoteModal = ({ showModal, onClose, onSubmit, errorMsg }) => { diff --git a/packages/admin-ui/src/pages/Customers/components/notes/NoteEdit.jsx b/packages/admin-ui/src/pages/Customers/components/notes/NoteEdit.jsx index ea9f8ca3..d53bc2d2 100644 --- a/packages/admin-ui/src/pages/Customers/components/notes/NoteEdit.jsx +++ b/packages/admin-ui/src/pages/Customers/components/notes/NoteEdit.jsx @@ -17,11 +17,11 @@ const NoteEdit = ({ note, cancel, edit, timezone }) => { const formRef = useRef() const validationSchema = Yup.object().shape({ - content: Yup.string() + content: Yup.string(), }) const initialValues = { - content: note.content + content: note.content, } return ( @@ -33,8 +33,8 @@ const NoteEdit = ({ note, cancel, edit, timezone }) => { { delimited: ', ' }, intervalToDuration({ start: toTimezone(new Date(note.lastEditedAt), timezone), - end: toTimezone(new Date(), timezone) - }) + end: toTimezone(new Date(), timezone), + }), )} {` ago`}

@@ -74,7 +74,7 @@ const NoteEdit = ({ note, cancel, edit, timezone }) => { edit({ noteId: note.id, newContent: content, - oldContent: note.content + oldContent: note.content, }) } innerRef={formRef}> diff --git a/packages/admin-ui/src/pages/Customers/helper.jsx b/packages/admin-ui/src/pages/Customers/helper.jsx index ca672fc7..8e894c04 100644 --- a/packages/admin-ui/src/pages/Customers/helper.jsx +++ b/packages/admin-ui/src/pages/Customers/helper.jsx @@ -10,7 +10,7 @@ import * as Yup from 'yup' import { RadioGroup, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { MANUAL } from 'src/utils/constants' @@ -24,7 +24,7 @@ const ID_CARD_DATA = 'idCardData' const getAuthorizedStatus = (it, triggers, customRequests) => { const fields = R.concat( ['frontCamera', 'idCardData', 'idCardPhoto', 'email', 'usSsn', 'sanctions'], - R.map(ite => ite.id, customRequests) + R.map(ite => ite.id, customRequests), ) const fieldsWithPathSuffix = ['frontCamera', 'idCardPhoto'] @@ -34,13 +34,13 @@ const getAuthorizedStatus = (it, triggers, customRequests) => { : fieldName const manualOverrides = R.filter( ite => R.equals(R.toLower(ite.automation), MANUAL), - triggers?.overrides ?? [] + triggers?.overrides ?? [], ) return ( !!R.find( ite => R.equals(ite.requirement, triggerName), - manualOverrides + manualOverrides, ) || R.equals(R.toLower(triggers.automation ?? ''), MANUAL) ) } @@ -50,7 +50,7 @@ const getAuthorizedStatus = (it, triggers, customRequests) => { if (uuidValidate(ite)) { const request = R.find( iter => iter.infoRequestId === ite, - it.customInfoRequests + it.customInfoRequests, ) return !R.isNil(request) && R.equals(request.override, 'automatic') } @@ -69,7 +69,7 @@ const getAuthorizedStatus = (it, triggers, customRequests) => { if (uuidValidate(ite)) { const request = R.find( iter => iter.infoRequestId === ite, - it.customInfoRequests + it.customInfoRequests, ) return !R.isNil(request) && R.equals(request.override, 'blocked') } @@ -115,11 +115,11 @@ const getName = it => { const entryOptions = [ { display: 'Custom entry', code: 'custom' }, - { display: 'Populate existing requirement', code: 'requirement' } + { display: 'Populate existing requirement', code: 'requirement' }, ] const dataOptions = [ - { display: 'Text', code: 'text' } + { display: 'Text', code: 'text' }, // TODO: Requires backend modifications to support File and Image // { display: 'File', code: 'file' }, // { display: 'Image', code: 'image' } @@ -130,12 +130,12 @@ const requirementOptions = [ { display: 'ID data', code: 'idCardData' }, { display: 'US SSN', code: 'usSsn' }, { display: 'Email', code: 'email' }, - { display: 'Customer camera', code: 'frontCamera' } + { display: 'Customer camera', code: 'frontCamera' }, ] const customTextOptions = [ { label: 'Data entry title', name: 'title' }, - { label: 'Data entry', name: 'data' } + { label: 'Data entry', name: 'data' }, ] const customUploadOptions = [{ label: 'Data entry title', name: 'title' }] @@ -144,40 +144,40 @@ const entryTypeSchema = Yup.lazy(values => { if (values.entryType === 'custom') { return Yup.object().shape({ entryType: Yup.string().required(), - dataType: Yup.string().required() + dataType: Yup.string().required(), }) } else if (values.entryType === 'requirement') { return Yup.object().shape({ entryType: Yup.string().required(), - requirement: Yup.string().required() + requirement: Yup.string().required(), }) } }) const customFileSchema = Yup.object().shape({ title: Yup.string().required(), - file: Yup.mixed().required() + file: Yup.mixed().required(), }) const customImageSchema = Yup.object().shape({ title: Yup.string().required(), - image: Yup.mixed().required() + image: Yup.mixed().required(), }) const customTextSchema = Yup.object().shape({ title: Yup.string().required(), - data: Yup.string().required() + data: Yup.string().required(), }) const updateRequirementOptions = it => [ { display: 'Custom information requirement', - code: 'custom' + code: 'custom', }, - ...it + ...it, ] -const EntryType = ({ customInfoRequirementOptions }) => { +const EntryType = () => { const { values } = useFormikContext() const displayCustomOptions = values.entryType === CUSTOM @@ -265,7 +265,7 @@ const ManualDataEntry = ({ selectedValues, customInfoRequirementOptions }) => { isOptionEqualToValue={R.eqProps('code')} labelProp={'display'} options={customInfoRequirementOptions} - onChange={(evt, it) => {}} + onChange={() => {}} /> )}
@@ -297,29 +297,29 @@ const customElements = { options: customTextOptions, Component: ManualDataEntry, initialValues: { data: '', title: '' }, - saveType: 'customEntry' + saveType: 'customEntry', }, file: { schema: customFileSchema, options: customUploadOptions, Component: ManualDataEntry, initialValues: { file: null, title: '' }, - saveType: 'customEntryUpload' + saveType: 'customEntryUpload', }, image: { schema: customImageSchema, options: customUploadOptions, Component: ManualDataEntry, initialValues: { image: null, title: '' }, - saveType: 'customEntryUpload' - } + saveType: 'customEntryUpload', + }, } const entryType = { schema: entryTypeSchema, options: entryOptions, Component: EntryType, - initialValues: { entryType: '' } + initialValues: { entryType: '' }, } // Customer data @@ -330,44 +330,44 @@ const customerDataElements = { name: 'firstName', label: 'First name', component: TextInput, - editable: true + editable: true, }, { name: 'documentNumber', label: 'ID number', component: TextInput, - editable: true + editable: true, }, { name: 'dateOfBirth', label: 'Birthdate', component: TextInput, - editable: true + editable: true, }, { name: 'gender', label: 'Gender', component: TextInput, - editable: true + editable: true, }, { name: 'lastName', label: 'Last name', component: TextInput, - editable: true + editable: true, }, { name: 'expirationDate', label: 'Expiration date', component: TextInput, - editable: true + editable: true, }, { name: 'country', label: 'Country', component: TextInput, - editable: true - } + editable: true, + }, ], usSsn: [ { @@ -375,8 +375,8 @@ const customerDataElements = { label: 'US SSN', component: TextInput, size: 190, - editable: true - } + editable: true, + }, ], email: [ { @@ -384,11 +384,11 @@ const customerDataElements = { label: 'Email', component: TextInput, size: 190, - editable: false - } + editable: false, + }, ], idCardPhoto: [{ name: 'idCardPhoto' }], - frontCamera: [{ name: 'frontCamera' }] + frontCamera: [{ name: 'frontCamera' }], } const customerDataSchemas = { @@ -399,7 +399,7 @@ const customerDataSchemas = { dateOfBirth: Yup.string() .test({ test: val => isValid(parse(new Date(), 'yyyy-MM-dd', val)), - message: 'Date must be in format YYYY-MM-DD' + message: 'Date must be in format YYYY-MM-DD', }) .required(), gender: Yup.string().required(), @@ -407,22 +407,22 @@ const customerDataSchemas = { expirationDate: Yup.string() .test({ test: val => isValid(parse(new Date(), 'yyyy-MM-dd', val)), - message: 'Date must be in format YYYY-MM-DD' + message: 'Date must be in format YYYY-MM-DD', }) - .required() + .required(), }), usSsn: Yup.object().shape({ - usSsn: Yup.string().required() + usSsn: Yup.string().required(), }), idCardPhoto: Yup.object().shape({ - idCardPhoto: Yup.mixed().required() + idCardPhoto: Yup.mixed().required(), }), frontCamera: Yup.object().shape({ - frontCamera: Yup.mixed().required() + frontCamera: Yup.mixed().required(), }), email: Yup.object().shape({ - email: Yup.string().required() - }) + email: Yup.string().required(), + }), } const requirementElements = { @@ -437,44 +437,44 @@ const requirementElements = { dateOfBirth: '', gender: '', country: '', - expirationDate: '' + expirationDate: '', }, - saveType: 'customerData' + saveType: 'customerData', }, usSsn: { schema: customerDataSchemas.usSsn, options: customerDataElements.usSsn, Component: ManualDataEntry, initialValues: { usSsn: '' }, - saveType: 'customerData' + saveType: 'customerData', }, email: { schema: customerDataSchemas.email, options: customerDataElements.email, Component: ManualDataEntry, initialValues: { email: '' }, - saveType: 'customerData' + saveType: 'customerData', }, idCardPhoto: { schema: customerDataSchemas.idCardPhoto, options: customerDataElements.idCardPhoto, Component: ManualDataEntry, initialValues: { idCardPhoto: null }, - saveType: 'customerDataUpload' + saveType: 'customerDataUpload', }, frontCamera: { schema: customerDataSchemas.frontCamera, options: customerDataElements.frontCamera, Component: ManualDataEntry, initialValues: { frontCamera: null }, - saveType: 'customerDataUpload' + saveType: 'customerDataUpload', }, custom: { // schema: customerDataSchemas.customInfoRequirement, Component: ManualDataEntry, initialValues: { customInfoRequirement: null }, - saveType: 'customInfoRequirement' - } + saveType: 'customInfoRequirement', + }, } const tryFormatDate = rawDate => { @@ -485,6 +485,7 @@ const tryFormatDate = rawDate => { '' ) } catch (err) { + console.error(err) return '' } } @@ -493,8 +494,8 @@ const formatDates = values => { R.map( elem => (values[elem] = format('yyyyMMdd')( - parse(new Date(), 'yyyy-MM-dd', values[elem]) - )) + parse(new Date(), 'yyyy-MM-dd', values[elem]), + )), )(['dateOfBirth', 'expirationDate']) return values } @@ -520,13 +521,13 @@ const addPhotoDir = R.map(it => { const standardizeKeys = R.map(R.compose(R.fromPairs, R.map(mapKeys), R.toPairs)) const filterByPhotoAvailable = R.filter( - tx => !R.isNil(tx.date) && !R.isNil(tx.path) + tx => !R.isNil(tx.date) && !R.isNil(tx.path), ) const formatPhotosData = R.compose( filterByPhotoAvailable, addPhotoDir, - standardizeKeys + standardizeKeys, ) export { @@ -543,5 +544,5 @@ export { tryFormatDate, REQUIREMENT, CUSTOM, - ID_CARD_DATA + ID_CARD_DATA, } diff --git a/packages/admin-ui/src/pages/Dashboard/Alerts/Alerts.jsx b/packages/admin-ui/src/pages/Dashboard/Alerts/Alerts.jsx index bb88d73f..e0f66499 100644 --- a/packages/admin-ui/src/pages/Dashboard/Alerts/Alerts.jsx +++ b/packages/admin-ui/src/pages/Dashboard/Alerts/Alerts.jsx @@ -35,7 +35,7 @@ const Alerts = ({ onReset, onExpand, size }) => { const alerts = R.path(['alerts'])(data) ?? [] const machines = R.compose( R.map(R.prop('name')), - R.indexBy(R.prop('deviceId')) + R.indexBy(R.prop('deviceId')), )(data?.machines ?? []) const alertsLength = alerts.length diff --git a/packages/admin-ui/src/pages/Dashboard/Alerts/AlertsTable.jsx b/packages/admin-ui/src/pages/Dashboard/Alerts/AlertsTable.jsx index 604a2992..35345401 100644 --- a/packages/admin-ui/src/pages/Dashboard/Alerts/AlertsTable.jsx +++ b/packages/admin-ui/src/pages/Dashboard/Alerts/AlertsTable.jsx @@ -13,13 +13,13 @@ const icons = { error: , fiatBalance: ( - ) + ), } const links = { error: '/maintenance/machine-status', fiatBalance: '/maintenance/cash-cassettes', - cryptoBalance: '/maintenance/funding' + cryptoBalance: '/maintenance/funding', } const AlertsTable = ({ numToRender, alerts, machines }) => { diff --git a/packages/admin-ui/src/pages/Dashboard/Alerts/index.js b/packages/admin-ui/src/pages/Dashboard/Alerts/index.js index 2b2f3f45..62844083 100644 --- a/packages/admin-ui/src/pages/Dashboard/Alerts/index.js +++ b/packages/admin-ui/src/pages/Dashboard/Alerts/index.js @@ -1,2 +1,3 @@ import Alerts from './Alerts' + export default Alerts diff --git a/packages/admin-ui/src/pages/Dashboard/Footer/Footer.jsx b/packages/admin-ui/src/pages/Dashboard/Footer/Footer.jsx index 6b751407..cd50cf23 100644 --- a/packages/admin-ui/src/pages/Dashboard/Footer/Footer.jsx +++ b/packages/admin-ui/src/pages/Dashboard/Footer/Footer.jsx @@ -47,24 +47,24 @@ const Footer = () => { const tickerName = tickerIdx > -1 ? accountsConfig[tickerIdx].display : '' const cashInNoCommission = parseFloat( - R.path(['cryptoRates', 'withoutCommissions', key, 'cashIn'])(data) + R.path(['cryptoRates', 'withoutCommissions', key, 'cashIn'])(data), ) const cashOutNoCommission = parseFloat( - R.path(['cryptoRates', 'withoutCommissions', key, 'cashOut'])(data) + R.path(['cryptoRates', 'withoutCommissions', key, 'cashOut'])(data), ) const avgOfAskBid = new BigNumber( - (cashInNoCommission + cashOutNoCommission) / 2 + (cashInNoCommission + cashOutNoCommission) / 2, ).toFormat(2) const cashIn = new BigNumber( parseFloat( - R.path(['cryptoRates', 'withCommissions', key, 'cashIn'])(data) - ) + R.path(['cryptoRates', 'withCommissions', key, 'cashIn'])(data), + ), ).toFormat(2) const cashOut = new BigNumber( parseFloat( - R.path(['cryptoRates', 'withCommissions', key, 'cashOut'])(data) - ) + R.path(['cryptoRates', 'withCommissions', key, 'cashOut'])(data), + ), ).toFormat(2) return ( diff --git a/packages/admin-ui/src/pages/Dashboard/Footer/Footer.module.css b/packages/admin-ui/src/pages/Dashboard/Footer/Footer.module.css index 304ffbb5..c3c89bf6 100644 --- a/packages/admin-ui/src/pages/Dashboard/Footer/Footer.module.css +++ b/packages/admin-ui/src/pages/Dashboard/Footer/Footer.module.css @@ -12,9 +12,9 @@ } .footer1:hover { - transition: min-height 0.5s ease-in; - min-height: 200px; - } + transition: min-height 0.5s ease-in; + min-height: 200px; +} .content1 { width: 1200px; diff --git a/packages/admin-ui/src/pages/Dashboard/Footer/index.js b/packages/admin-ui/src/pages/Dashboard/Footer/index.js index 426e806d..9c7e8318 100644 --- a/packages/admin-ui/src/pages/Dashboard/Footer/index.js +++ b/packages/admin-ui/src/pages/Dashboard/Footer/index.js @@ -1,2 +1,3 @@ import Footer from './Footer' + export default Footer diff --git a/packages/admin-ui/src/pages/Dashboard/RightSide.jsx b/packages/admin-ui/src/pages/Dashboard/RightSide.jsx index e7a438f0..acffe2dc 100644 --- a/packages/admin-ui/src/pages/Dashboard/RightSide.jsx +++ b/packages/admin-ui/src/pages/Dashboard/RightSide.jsx @@ -39,7 +39,7 @@ const RightSide = () => { { className={classnames({ 'flex-[0.1]': systemStatusSize === cardState.SHRUNK, 'flex-1': systemStatusSize === cardState.DEFAULT, - 'flex-[0.9]': systemStatusSize === cardState.EXPANDED + 'flex-[0.9]': systemStatusSize === cardState.EXPANDED, })} state={systemStatusSize} shrunkComponent={ diff --git a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/PercentageChart.jsx b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/PercentageChart.jsx index 775ed780..d678140e 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/PercentageChart.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/PercentageChart.jsx @@ -12,7 +12,7 @@ const PercentageChart = ({ cashIn, cashOut }) => { const percentageClasses = { 'h-35 rounded-sm flex items-center justify-center': true, - 'min-w-2 rounded-xs': value < 5 && value > 0 + 'min-w-2 rounded-xs': value < 5 && value > 0, } return ( diff --git a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefLineChart.jsx b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefLineChart.jsx index 2dc3787e..1d5808e7 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefLineChart.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefLineChart.jsx @@ -23,14 +23,14 @@ const reducer = (acc, tx) => { const timeFrameMS = { Day: 24 * 3600 * 1000, Week: 7 * 24 * 3600 * 1000, - Month: 30 * 24 * 3600 * 1000 + Month: 30 * 24 * 3600 * 1000, } const RefLineChart = ({ data: realData, previousTimeData, previousProfit, - timeFrame + timeFrame, }) => { const svgRef = useRef() @@ -55,7 +55,7 @@ const RefLineChart = ({ if (!aggregatedTX.length && previousTimeData.length) { const mockPoint1 = { created: new Date().toISOString(), - profit: 0 + profit: 0, } const mockPoint2 = mockPoint(mockPoint1, -timeFrameMS[timeFrame], 1) return [[mockPoint1, mockPoint2], false] @@ -64,7 +64,7 @@ const RefLineChart = ({ if (aggregatedTX.length && !previousTimeData.length) { const mockPoint1 = { created: new Date().toISOString(), - profit: 1 + profit: 1, } const mockPoint2 = mockPoint(mockPoint1, -timeFrameMS[timeFrame], 0) return [[mockPoint1, mockPoint2], false] @@ -75,13 +75,13 @@ const RefLineChart = ({ R.append( { created: new Date( - Date.now() - timeFrameMS[timeFrame] + Date.now() - timeFrameMS[timeFrame], ).toISOString(), - profit: previousProfit + profit: previousProfit, }, - aggregatedTX + aggregatedTX, ), - false + false, ] } // the boolean value is for zeroProfit. It makes the line render at y = 0 instead of y = 50% of container height @@ -120,7 +120,7 @@ const RefLineChart = ({ .data([ { offset: '0%', color: 'var(--zircon)' }, { offset: '25%', color: 'var(--zircon)' }, - { offset: '100%', color: 'var(--ghost)' } + { offset: '100%', color: 'var(--ghost)' }, ]) .enter() .append('stop') diff --git a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.jsx b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.jsx index e1ef4388..02a8d53e 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Graphs/RefScatterplot.jsx @@ -17,9 +17,9 @@ const Graph = ({ data, timeFrame, timezone }) => { top: 20, right: 3.5, bottom: 27, - left: 33.5 + left: 33.5, }), - [] + [], ) const offset = getTimezoneOffset(timezone) @@ -28,7 +28,7 @@ const Graph = ({ data, timeFrame, timezone }) => { const periodDomains = { Day: [NOW - DAY, NOW], Week: [NOW - WEEK, NOW], - Month: [NOW - MONTH, NOW] + Month: [NOW - MONTH, NOW], } const dataPoints = useMemo( @@ -37,27 +37,27 @@ const Graph = ({ data, timeFrame, timezone }) => { freq: 24, step: 60 * 60 * 1000, tick: d3.utcHour.every(4), - labelFormat: '%H:%M' + labelFormat: '%H:%M', }, Week: { freq: 7, step: 24 * 60 * 60 * 1000, tick: d3.utcDay.every(1), - labelFormat: '%a %d' + labelFormat: '%a %d', }, Month: { freq: 30, step: 24 * 60 * 60 * 1000, tick: d3.utcDay.every(2), - labelFormat: '%d' - } + labelFormat: '%d', + }, }), - [] + [], ) const filterDay = useCallback( x => (timeFrame === 'Day' ? x.getUTCHours() === 0 : x.getUTCDate() === 1), - [timeFrame] + [timeFrame], ) const getPastAndCurrentDayLabels = useCallback(d => { @@ -74,11 +74,11 @@ const Graph = ({ data, timeFrame, timezone }) => { const previousDateMonth = previousDate.getUTCMonth() const daysOfWeek = Array.from(Array(7)).map((_, i) => - format('EEE', add({ days: i }, startOfWeek(new Date()))) + format('EEE', add({ days: i }, startOfWeek(new Date()))), ) const months = Array.from(Array(12)).map((_, i) => - format('LLL', add({ months: i }, startOfYear(new Date()))) + format('LLL', add({ months: i }, startOfYear(new Date()))), ) return { @@ -89,7 +89,7 @@ const Graph = ({ data, timeFrame, timezone }) => { current: currentDateMonth !== previousDateMonth ? months[currentDateMonth] - : `${daysOfWeek[currentDateWeekday]} ${currentDateDay}` + : `${daysOfWeek[currentDateWeekday]} ${currentDateDay}`, } }, []) @@ -111,7 +111,7 @@ const Graph = ({ data, timeFrame, timezone }) => { return points }, - [NOW, dataPoints, timeFrame] + [NOW, dataPoints, timeFrame], ) const x = d3 @@ -123,7 +123,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .scaleLinear() .domain([ 0, - (d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.05 + (d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.05, ]) .nice() .range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top]) @@ -137,7 +137,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .attr('height', GRAPH_HEIGHT - GRAPH_MARGIN.top - GRAPH_MARGIN.bottom) .attr('fill', 'var(--ghost)') }, - [GRAPH_MARGIN] + [GRAPH_MARGIN], ) const buildXAxis = useCallback( @@ -145,7 +145,7 @@ const Graph = ({ data, timeFrame, timezone }) => { g .attr( 'transform', - `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})` + `translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`, ) .call( d3 @@ -153,12 +153,12 @@ const Graph = ({ data, timeFrame, timezone }) => { .ticks(dataPoints[timeFrame].tick) .tickFormat(d => { return d3.timeFormat(dataPoints[timeFrame].labelFormat)( - d.getTime() + d.getTimezoneOffset() * MINUTE + d.getTime() + d.getTimezoneOffset() * MINUTE, ) - }) + }), ) .call(g => g.select('.domain').remove()), - [GRAPH_MARGIN, dataPoints, timeFrame, x] + [GRAPH_MARGIN, dataPoints, timeFrame, x], ) const buildYAxis = useCallback( @@ -173,12 +173,12 @@ const Graph = ({ data, timeFrame, timezone }) => { if (d >= 1000) return numberToFiatAmount(d / 1000) + 'k' return numberToFiatAmount(d) - }) + }), ) .call(g => g.select('.domain').remove()) .selectAll('text') .attr('dy', '-0.25rem'), - [GRAPH_MARGIN, y] + [GRAPH_MARGIN, y], ) const buildGrid = useCallback( @@ -196,7 +196,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .attr('x2', d => 0.5 + x(d)) .attr('y1', GRAPH_MARGIN.top) .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom) - .attr('stroke-width', 1) + .attr('stroke-width', 1), ) // Horizontal lines .call(g => @@ -208,7 +208,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .attr('y1', d => 0.5 + y(d)) .attr('y2', d => 0.5 + y(d)) .attr('x1', GRAPH_MARGIN.left) - .attr('x2', GRAPH_WIDTH) + .attr('x2', GRAPH_WIDTH), ) // Thick vertical lines .call(g => @@ -223,7 +223,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .attr('y1', GRAPH_MARGIN.top - 10) .attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom) .attr('stroke-width', 2) - .join('text') + .join('text'), ) // Left side breakpoint label .call(g => { @@ -262,7 +262,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .text(labels.current) }) }, - [GRAPH_MARGIN, buildTicks, getPastAndCurrentDayLabels, x, y, filterDay] + [GRAPH_MARGIN, buildTicks, getPastAndCurrentDayLabels, x, y, filterDay], ) const formatTicksText = useCallback( @@ -273,7 +273,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .style('fill', 'var(--comet)') .style('stroke-width', 0) .style('font-family', 'var(--museo)'), - [] + [], ) const formatText = useCallback( @@ -284,7 +284,7 @@ const Graph = ({ data, timeFrame, timezone }) => { .style('fill', 'var(--comet)') .style('stroke-width', 0) .style('font-family', 'var(--museo)'), - [] + [], ) const formatTicks = useCallback(() => { @@ -304,11 +304,11 @@ const Graph = ({ data, timeFrame, timezone }) => { }) .attr('cy', d => y(new BigNumber(d.fiat).toNumber())) .attr('fill', d => - d.txClass === 'cashIn' ? 'var(--java)' : 'var(--neon)' + d.txClass === 'cashIn' ? 'var(--java)' : 'var(--neon)', ) .attr('r', 3.5) }, - [data, offset, x, y] + [data, offset, x, y], ) const drawChart = useCallback(() => { @@ -334,7 +334,7 @@ const Graph = ({ data, timeFrame, timezone }) => { drawData, formatText, formatTicks, - formatTicksText + formatTicksText, ]) useEffect(() => { diff --git a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/InfoWithLabel.jsx b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/InfoWithLabel.jsx index 367f9c7c..54cc3582 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/InfoWithLabel.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/InfoWithLabel.jsx @@ -1,5 +1,6 @@ import React from 'react' import { Info1, Label1 } from 'src/components/typography/index' + const InfoWithLabel = ({ info, label }) => { return (
diff --git a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Nav.jsx b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Nav.jsx index e81c28c3..4dc251b2 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Nav.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/Nav.jsx @@ -27,7 +27,7 @@ const Nav = ({ handleSetRange, showPicker }) => { className={classnames({ 'cursor-pointer text-comet': true, 'font-bold text-zodiac border-b-zodiac border-b-2': - isSelected(it) + isSelected(it), })}> {it}
diff --git a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/SystemPerformance.jsx b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/SystemPerformance.jsx index 838ded7e..31c68f50 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/SystemPerformance.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/SystemPerformance.jsx @@ -52,7 +52,7 @@ const GET_DATA = gql` const SystemPerformance = () => { const [selectedRange, setSelectedRange] = useState('Day') const { data, loading } = useQuery(GET_DATA, { - variables: { excludeTestingCustomers: true } + variables: { excludeTestingCustomers: true }, }) const fiatLocale = fromNamespace('locale')(data?.config).fiatCurrency const timezone = fromNamespace('locale')(data?.config).timezone @@ -62,7 +62,7 @@ const SystemPerformance = () => { const periodDomains = { Day: [NOW - DAY, NOW], Week: [NOW - WEEK, NOW], - Month: [NOW - MONTH, NOW] + Month: [NOW - MONTH, NOW], } const isInRangeAndNoError = getLastTimePeriod => t => { @@ -74,11 +74,11 @@ const SystemPerformance = () => { t.error === null && isAfter( toTimezone(t.created, timezone), - toTimezone(periodDomains[selectedRange][1], timezone) + toTimezone(periodDomains[selectedRange][1], timezone), ) && isAfter( toTimezone(periodDomains[selectedRange][0], timezone), - toTimezone(t.created, timezone) + toTimezone(t.created, timezone), ) ) } @@ -86,11 +86,11 @@ const SystemPerformance = () => { t.error === null && isAfter( toTimezone(periodDomains[selectedRange][1], timezone), - toTimezone(t.created, timezone) + toTimezone(t.created, timezone), ) && isAfter( toTimezone(t.created, timezone), - toTimezone(periodDomains[selectedRange][0], timezone) + toTimezone(periodDomains[selectedRange][0], timezone), ) ) } @@ -104,10 +104,10 @@ const SystemPerformance = () => { } const transactionsToShow = R.map(convertFiatToLocale)( - R.filter(isInRangeAndNoError(false), data?.transactions ?? []) + R.filter(isInRangeAndNoError(false), data?.transactions ?? []), ) const transactionsLastTimePeriod = R.map(convertFiatToLocale)( - R.filter(isInRangeAndNoError(true), data?.transactions ?? []) + R.filter(isInRangeAndNoError(true), data?.transactions ?? []), ) const getNumTransactions = () => { @@ -121,7 +121,7 @@ const SystemPerformance = () => { return R.reduce( (acc, value) => acc.plus(value.profit), new BigNumber(0), - transactions + transactions, ) } @@ -141,7 +141,7 @@ const SystemPerformance = () => { const getDirectionPercent = () => { const [cashIn, cashOut] = R.partition(R.propEq('txClass', 'cashIn'))( - transactionsToShow + transactionsToShow, ) const totalLength = cashIn.length + cashOut.length if (totalLength === 0) { @@ -150,7 +150,7 @@ const SystemPerformance = () => { return { cashIn: Math.round((cashIn.length / totalLength) * 100), - cashOut: Math.round((cashOut.length / totalLength) * 100) + cashOut: Math.round((cashOut.length / totalLength) * 100), } } @@ -160,7 +160,7 @@ const SystemPerformance = () => { 'text-tomato': percentChange < 0, 'text-spring4': percentChange > 0, 'text-comet': percentChange === 0, - 'flex items-center justify-center gap-1': true + 'flex items-center justify-center gap-1': true, } const getPercentageIcon = () => { diff --git a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/index.js b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/index.js index bd06fb98..8bea8c7f 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemPerformance/index.js +++ b/packages/admin-ui/src/pages/Dashboard/SystemPerformance/index.js @@ -1,2 +1,3 @@ import SystemPerformance from './SystemPerformance' + export default SystemPerformance diff --git a/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.jsx b/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.jsx index 37362174..e0fea8f7 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.jsx @@ -28,14 +28,14 @@ const GET_CONFIG = gql` const StyledCell = styled(TableCell)({ borderBottom: '4px solid white', padding: 0, - paddingLeft: '15px' + paddingLeft: '15px', }) const HeaderCell = styled(TableCell)({ borderBottom: '4px solid white', padding: 0, paddingLeft: '15px', - backgroundColor: 'white' + backgroundColor: 'white', }) const MachinesTable = ({ machines = [], numToRender }) => { @@ -44,7 +44,7 @@ const MachinesTable = ({ machines = [], numToRender }) => { const { data } = useQuery(GET_CONFIG) const fillingPercentageSettings = fromNamespace( 'notifications', - R.path(['config'], data) ?? {} + R.path(['config'], data) ?? {}, ) const getPercent = (notes, capacity = 500) => { @@ -55,7 +55,7 @@ const MachinesTable = ({ machines = [], numToRender }) => { const percent = getPercent(notes, capacity) const percentageThreshold = R.pipe( R.path([`fillingPercentageCassette${cassetteIdx}`]), - R.defaultTo(PERCENTAGE_THRESHOLD) + R.defaultTo(PERCENTAGE_THRESHOLD), )(fillingPercentageSettings) return percent < percentageThreshold ? ( {`${percent}%`} @@ -66,13 +66,13 @@ const MachinesTable = ({ machines = [], numToRender }) => { const redirect = ({ name, deviceId }) => { return history.push(`/machines/${deviceId}`, { - selectedMachine: name + selectedMachine: name, }) } const maxNumberOfCassettes = Math.max( ...R.map(it => it.numberOfCassettes, machines), - 0 + 0, ) return ( @@ -119,7 +119,7 @@ const MachinesTable = ({ machines = [], numToRender }) => { sx={{ borderBottom: '4px solid white', padding: 0, - paddingLeft: '15px' + paddingLeft: '15px', }} align="left">
@@ -138,14 +138,14 @@ const MachinesTable = ({ machines = [], numToRender }) => { {makePercentageText( it, - machine.cashUnits[`cassette${it}`] + machine.cashUnits[`cassette${it}`], )} ) : ( {`— %`} - ) + ), )} ) diff --git a/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.styles.js b/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.styles.js index b8774833..844a9455 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.styles.js +++ b/packages/admin-ui/src/pages/Dashboard/SystemStatus/MachinesTable.styles.js @@ -2,28 +2,28 @@ import { backgroundColor, offColor, errorColor, - primaryColor + primaryColor, } from 'src/styling/variables' const styles = { label: { margin: 0, - color: offColor + color: offColor, }, row: { backgroundColor: backgroundColor, - borderBottom: 'none' + borderBottom: 'none', }, clickableRow: { - cursor: 'pointer' + cursor: 'pointer', }, header: { display: 'flex', alignItems: 'center', - whiteSpace: 'pre' + whiteSpace: 'pre', }, error: { - color: errorColor + color: errorColor, }, button: { color: primaryColor, @@ -32,46 +32,46 @@ const styles = { padding: 0, textTransform: 'none', '&:hover': { - backgroundColor: 'transparent' + backgroundColor: 'transparent', }, - marginBottom: -40 + marginBottom: -40, }, buttonLabel: { position: 'absolute', bottom: 160, - marginBottom: 0 + marginBottom: 0, }, statusHeader: { - marginLeft: 2 + marginLeft: 2, }, tableBody: { - overflow: 'auto' + overflow: 'auto', }, tl2: { - display: 'inline' + display: 'inline', }, label1: { - display: 'inline' + display: 'inline', }, machinesTableContainer: { - height: 220 + height: 220, }, expandedMachinesTableContainer: { - height: 414 + height: 414, }, centerLabel: { marginBottom: 0, padding: 0, - textAlign: 'center' + textAlign: 'center', }, machineNameWrapper: { display: 'flex', flexDirection: 'row', - alignItems: 'center' + alignItems: 'center', }, machineRedirectIcon: { - marginLeft: 10 - } + marginLeft: 10, + }, } export default styles diff --git a/packages/admin-ui/src/pages/Dashboard/SystemStatus/SystemStatus.jsx b/packages/admin-ui/src/pages/Dashboard/SystemStatus/SystemStatus.jsx index 7b043d1a..1bd249b6 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemStatus/SystemStatus.jsx +++ b/packages/admin-ui/src/pages/Dashboard/SystemStatus/SystemStatus.jsx @@ -60,7 +60,7 @@ const SystemStatus = ({ onReset, onExpand, size }) => { const machinesTableContainerClasses = { 'h-55': !showAllItems, - 'h-103': showAllItems + 'h-103': showAllItems, } // const uptime = data?.uptime ?? [{}] return ( diff --git a/packages/admin-ui/src/pages/Dashboard/SystemStatus/index.js b/packages/admin-ui/src/pages/Dashboard/SystemStatus/index.js index 74b705c6..41973d9b 100644 --- a/packages/admin-ui/src/pages/Dashboard/SystemStatus/index.js +++ b/packages/admin-ui/src/pages/Dashboard/SystemStatus/index.js @@ -1,2 +1,3 @@ import SystemStatus from './SystemStatus' + export default SystemStatus diff --git a/packages/admin-ui/src/pages/Dashboard/index.js b/packages/admin-ui/src/pages/Dashboard/index.js index d08092b2..f952036f 100644 --- a/packages/admin-ui/src/pages/Dashboard/index.js +++ b/packages/admin-ui/src/pages/Dashboard/index.js @@ -1,2 +1,3 @@ import Dashboard from './Dashboard' + export default Dashboard diff --git a/packages/admin-ui/src/pages/Funding/Funding.jsx b/packages/admin-ui/src/pages/Funding/Funding.jsx index 9c0c589d..86d83735 100644 --- a/packages/admin-ui/src/pages/Funding/Funding.jsx +++ b/packages/admin-ui/src/pages/Funding/Funding.jsx @@ -13,7 +13,7 @@ import { Td, THead, TBody, - Table + Table, } from 'src/components/fake-table/Table.jsx' import Sidebar from 'src/components/layout/Sidebar.jsx' import { @@ -22,7 +22,7 @@ import { Info2, Info3, Label1, - Label3 + Label3, } from 'src/components/typography/index.jsx' import CopyToClipboard from 'src/components/CopyToClipboard.jsx' @@ -36,7 +36,7 @@ const NODE_NOT_CONNECTED_ERR = const sizes = { big: 165, time: 140, - date: 130 + date: 130, } const GET_FUNDING = gql` @@ -67,7 +67,7 @@ const getConfirmedTotal = list => { list .filter(it => !it.errorMsg) .map(it => new BigNumber(it.fiatConfirmedBalance)) - .reduce(sumReducer, new BigNumber(0)) + .reduce(sumReducer, new BigNumber(0)), ) } @@ -76,7 +76,7 @@ const getPendingTotal = list => { list .filter(it => !it.errorMsg) .map(it => new BigNumber(it.fiatPending)) - .reduce(sumReducer, new BigNumber(0)) + .reduce(sumReducer, new BigNumber(0)), ) } @@ -90,22 +90,22 @@ const Funding = () => { fiatValue: 1000.0, date: new Date(), performedBy: null, - pending: true + pending: true, }, { cryptoAmount: 10.0, balance: 12.23, fiatValue: 12000.0, date: new Date(), - performedBy: null + performedBy: null, }, { cryptoAmount: 5.0, balance: 5.0, fiatValue: 50000.0, date: new Date(), - performedBy: null - } + performedBy: null, + }, ] const isSelected = it => { @@ -122,11 +122,11 @@ const Funding = () => { const itemRender = (it, active) => { const itemClass = { [classes.item]: true, - [classes.inactiveItem]: !active + [classes.inactiveItem]: !active, } const wrapperClass = { [classes.itemWrapper]: true, - [classes.error]: it.errorMsg + [classes.error]: it.errorMsg, } return ( @@ -212,7 +212,7 @@ const Funding = () => { {`(${signIfPositive(selected.fiatPending)} ${formatNumber( - selected.fiatPending + selected.fiatPending, )} pending)`}
@@ -226,7 +226,7 @@ const Funding = () => { key={selected.cryptoCode}> {formatAddress( selected.cryptoCode, - selected.fundingAddress + selected.fundingAddress, )} diff --git a/packages/admin-ui/src/pages/Funding/Funding.module.css b/packages/admin-ui/src/pages/Funding/Funding.module.css index 85eff346..d6a0baac 100644 --- a/packages/admin-ui/src/pages/Funding/Funding.module.css +++ b/packages/admin-ui/src/pages/Funding/Funding.module.css @@ -104,4 +104,4 @@ font-weight: 400; width: 375px; margin: 12px 24px; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/pages/Locales/Locales.jsx b/packages/admin-ui/src/pages/Locales/Locales.jsx index 06a526da..78388b83 100644 --- a/packages/admin-ui/src/pages/Locales/Locales.jsx +++ b/packages/admin-ui/src/pages/Locales/Locales.jsx @@ -20,7 +20,7 @@ import { LocaleSchema, OverridesSchema, localeDefaults, - overridesDefaults + overridesDefaults, } from './helper' const GET_DATA = gql` @@ -113,7 +113,7 @@ const Locales = ({ name: SCREEN_KEY }) => { const [saveConfig] = useMutation(SAVE_CONFIG, { onCompleted: () => setWizard(false), refetchQueries: () => ['getData'], - onError: error => setError(error) + onError: error => setError(error), }) const [dataToSave, setDataToSave] = useState(null) @@ -229,7 +229,7 @@ const Locales = ({ name: SCREEN_KEY }) => { elements={overrides(data, localeOverrides, onChangeCoin)} disableAdd={R.compose(R.isEmpty, R.difference)( data?.machines.map(m => m.deviceId) ?? [], - localeOverrides?.map(o => o.machine) ?? [] + localeOverrides?.map(o => o.machine) ?? [], )} setEditing={onEditingOverrides} forceDisable={isEditingDefault} diff --git a/packages/admin-ui/src/pages/Locales/helper.js b/packages/admin-ui/src/pages/Locales/helper.js index b8193c56..2b82f11e 100644 --- a/packages/admin-ui/src/pages/Locales/helper.js +++ b/packages/admin-ui/src/pages/Locales/helper.js @@ -7,7 +7,7 @@ import { labels as timezoneList } from 'src/utils/timezone-list' const getFields = (getData, names, onChange, auxElements = []) => { return R.filter( it => R.includes(it.name, names), - allFields(getData, onChange, auxElements) + allFields(getData, onChange, auxElements), ) } @@ -17,7 +17,7 @@ const allFields = (getData, onChange, auxElements = []) => { return R.compose( it => `${R.prop(code)(it)} ${it?.isBeta ? '(Beta)' : ''}`, - R.find(R.propEq(compare ?? 'code', it)) + R.find(R.propEq(compare ?? 'code', it)), )(data) } @@ -60,8 +60,8 @@ const allFields = (getData, onChange, auxElements = []) => { options: it => R.concat(findSuggestion(it))(suggestionFilter(machineData)), valueProp: 'deviceId', - labelProp: 'name' - } + labelProp: 'name', + }, }, { name: 'country', @@ -72,8 +72,8 @@ const allFields = (getData, onChange, auxElements = []) => { inputProps: { options: countryData, valueProp: 'code', - labelProp: 'display' - } + labelProp: 'display', + }, }, { name: 'fiatCurrency', @@ -84,8 +84,8 @@ const allFields = (getData, onChange, auxElements = []) => { inputProps: { options: currencyData, valueProp: 'code', - labelProp: 'code' - } + labelProp: 'code', + }, }, { name: 'languages', @@ -97,8 +97,8 @@ const allFields = (getData, onChange, auxElements = []) => { options: languageData, valueProp: 'code', labelProp: 'display', - multiple: true - } + multiple: true, + }, }, { name: 'cryptoCurrencies', @@ -112,8 +112,8 @@ const allFields = (getData, onChange, auxElements = []) => { labelProp: 'codeLabel', multiple: true, optionsLimit: null, - onChange - } + onChange, + }, }, { name: 'timezone', @@ -124,9 +124,9 @@ const allFields = (getData, onChange, auxElements = []) => { inputProps: { options: timezonesData, valueProp: 'code', - labelProp: 'label' - } - } + labelProp: 'label', + }, + }, ] } @@ -137,7 +137,7 @@ const mainFields = (auxData, configureCoin) => { getData, ['country', 'fiatCurrency', 'languages', 'cryptoCurrencies', 'timezone'], configureCoin, - undefined + undefined, ) } @@ -148,7 +148,7 @@ const overrides = (auxData, auxElements, configureCoin) => { getData, ['machine', 'country', 'languages', 'cryptoCurrencies'], configureCoin, - auxElements + auxElements, ) } @@ -157,14 +157,14 @@ const LocaleSchema = Yup.object().shape({ fiatCurrency: Yup.string().label('Fiat currency').required(), languages: Yup.array().label('Languages').required().min(1).max(4), cryptoCurrencies: Yup.array().label('Crypto currencies').required().min(1), - timezone: Yup.string().label('Timezone').required() + timezone: Yup.string().label('Timezone').required(), }) const OverridesSchema = Yup.object().shape({ machine: Yup.string().label('Machine').required(), country: Yup.string().label('Country').required(), languages: Yup.array().label('Languages').required().min(1).max(4), - cryptoCurrencies: Yup.array().label('Crypto currencies').required().min(1) + cryptoCurrencies: Yup.array().label('Crypto currencies').required().min(1), }) const localeDefaults = { @@ -172,14 +172,14 @@ const localeDefaults = { fiatCurrency: '', languages: [], cryptoCurrencies: [], - timezone: '' + timezone: '', } const overridesDefaults = { machine: '', country: '', languages: [], - cryptoCurrencies: [] + cryptoCurrencies: [], } export { @@ -188,5 +188,5 @@ export { LocaleSchema, OverridesSchema, localeDefaults, - overridesDefaults + overridesDefaults, } diff --git a/packages/admin-ui/src/pages/Logs/Logs.module.css b/packages/admin-ui/src/pages/Logs/Logs.module.css index cf3dde45..869f58dd 100644 --- a/packages/admin-ui/src/pages/Logs/Logs.module.css +++ b/packages/admin-ui/src/pages/Logs/Logs.module.css @@ -71,4 +71,4 @@ .buttonsWrapper > * { margin: auto 6px; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/pages/Logs/MachineLogs.jsx b/packages/admin-ui/src/pages/Logs/MachineLogs.jsx index 0046ae3c..d6fccf0c 100644 --- a/packages/admin-ui/src/pages/Logs/MachineLogs.jsx +++ b/packages/admin-ui/src/pages/Logs/MachineLogs.jsx @@ -12,7 +12,7 @@ import { TableRow, TableHeader, TableBody, - TableCell + TableCell, } from 'src/components/table/index.js' import { formatDate } from 'src/utils/timezones.js' @@ -91,8 +91,8 @@ const Logs = () => { { variables: { deviceId, limit: NUM_LOG_RESULTS }, skip: !selected, - onCompleted: () => setSaveMessage('') - } + onCompleted: () => setSaveMessage(''), + }, ) if (machineResponse?.machines?.length && !selected) { diff --git a/packages/admin-ui/src/pages/Logs/ServerLogs.jsx b/packages/admin-ui/src/pages/Logs/ServerLogs.jsx index ac2d1926..ceded0db 100644 --- a/packages/admin-ui/src/pages/Logs/ServerLogs.jsx +++ b/packages/admin-ui/src/pages/Logs/ServerLogs.jsx @@ -13,7 +13,7 @@ import { TableRow, TableHeader, TableBody, - TableCell + TableCell, } from 'src/components/table/index.js' import { startCase } from 'src/utils/string.js' import { formatDate } from 'src/utils/timezones.js' @@ -73,8 +73,8 @@ const Logs = () => { const { data, loading: dataLoading } = useQuery(GET_SERVER_DATA, { onCompleted: () => setSaveMessage(''), variables: { - limit: NUM_LOG_RESULTS - } + limit: NUM_LOG_RESULTS, + }, }) const { data: configResponse, loading: configLoading } = useQuery(GET_DATA) const timezone = R.path(['config', 'locale_timezone'], configResponse) @@ -82,7 +82,7 @@ const Logs = () => { const defaultLogLevels = [ { code: 'error', display: 'Error' }, { code: 'info', display: 'Info' }, - { code: 'debug', display: 'Debug' } + { code: 'debug', display: 'Debug' }, ] const serverVersion = data?.serverVersion const processStates = data?.uptime ?? [] @@ -93,9 +93,9 @@ const Logs = () => { R.concat(defaultLogLevels), R.map(it => ({ code: R.path(['logLevel'])(it), - display: startCase(R.path(['logLevel'])(it)) + display: startCase(R.path(['logLevel'])(it)), })), - R.path(['serverLogs']) + R.path(['serverLogs']), ) const handleLogLevelChange = logLevel => { @@ -166,7 +166,7 @@ const Logs = () => { data.serverLogs .filter( log => - logLevel === SHOW_ALL || log.logLevel === logLevel.code + logLevel === SHOW_ALL || log.logLevel === logLevel.code, ) .map((log, idx) => ( @@ -175,7 +175,7 @@ const Logs = () => { formatDate( log.timestamp, timezone, - 'yyyy-MM-dd HH:mm' + 'yyyy-MM-dd HH:mm', )} {log.logLevel} diff --git a/packages/admin-ui/src/pages/Logs/ServerLogs.module.css b/packages/admin-ui/src/pages/Logs/ServerLogs.module.css index ddea352c..b6cea140 100644 --- a/packages/admin-ui/src/pages/Logs/ServerLogs.module.css +++ b/packages/admin-ui/src/pages/Logs/ServerLogs.module.css @@ -17,4 +17,4 @@ .uptimeContainer { margin: auto 0 auto 0; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscountModal.jsx b/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscountModal.jsx index 840964ab..7fdefc2f 100644 --- a/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscountModal.jsx +++ b/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscountModal.jsx @@ -12,7 +12,7 @@ import { NumberInput, Autocomplete } from 'src/components/inputs/formik' const initialValues = { customer: '', - discount: '' + discount: '', } const validationSchema = Yup.object().shape({ @@ -20,7 +20,7 @@ const validationSchema = Yup.object().shape({ discount: Yup.number() .required('A discount rate is required!') .min(0, 'Discount rate should be a positive number!') - .max(100, 'Discount rate should have a maximum value of 100%!') + .max(100, 'Discount rate should have a maximum value of 100%!'), }) const getErrorMsg = (formikErrors, formikTouched, mutationError) => { @@ -39,14 +39,14 @@ const IndividualDiscountModal = ({ onClose, creationError, addDiscount, - customers + customers, }) => { const handleAddDiscount = (customer, discount) => { addDiscount({ variables: { customerId: customer, - discount: parseInt(discount) - } + discount: parseInt(discount), + }, }) setShowModal(false) } @@ -85,7 +85,7 @@ const IndividualDiscountModal = ({ it?.idCardData?.firstName && it?.idCardData?.lastName ? ` ` : `` - }${it?.idCardData?.lastName ?? ``} (${it.phone})` + }${it?.idCardData?.lastName ?? ``} (${it.phone})`, }))(customers)} labelProp="display" valueProp="code" diff --git a/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscounts.jsx b/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscounts.jsx index 936cedff..fb6b08d5 100644 --- a/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscounts.jsx +++ b/packages/admin-ui/src/pages/LoyaltyPanel/IndividualDiscounts.jsx @@ -69,8 +69,8 @@ const IndividualDiscounts = () => { const [createDiscount, { error: creationError }] = useMutation( CREATE_DISCOUNT, { - refetchQueries: () => ['individualDiscounts'] - } + refetchQueries: () => ['individualDiscounts'], + }, ) const [deleteDiscount] = useMutation(DELETE_DISCOUNT, { @@ -79,7 +79,7 @@ const IndividualDiscounts = () => { setErrorMsg(errorMessage) }, onCompleted: () => setDeleteDialog(false), - refetchQueries: () => ['individualDiscounts'] + refetchQueries: () => ['individualDiscounts'], }) const elements = [ @@ -95,7 +95,7 @@ const IndividualDiscounts = () => { {t.customer.phone}
) - } + }, }, { header: 'Name', @@ -115,7 +115,7 @@ const IndividualDiscounts = () => { : `` }${customer.idCardData.lastName ?? ``}`} ) - } + }, }, { header: 'Discount rate', @@ -126,7 +126,7 @@ const IndividualDiscounts = () => { <> {t.discount} % - ) + ), }, { header: 'Revoke', @@ -143,8 +143,8 @@ const IndividualDiscounts = () => { - ) - } + ), + }, ] return ( diff --git a/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodes.jsx b/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodes.jsx index d53a727a..73b3ddca 100644 --- a/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodes.jsx +++ b/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodes.jsx @@ -59,23 +59,23 @@ const PromoCodes = () => { setErrorMsg(errorMessage) }, onCompleted: () => setDeleteDialog(false), - refetchQueries: () => ['promoCodes'] + refetchQueries: () => ['promoCodes'], }) const [createCode] = useMutation(CREATE_CODE, { - refetchQueries: () => ['promoCodes'] + refetchQueries: () => ['promoCodes'], }) const addCode = (code, discount) => { setErrorMsg(null) createCode({ - variables: { code: code, discount: discount } + variables: { code: code, discount: discount }, }) .then(res => { if (!res.errors) return setShowModal(false) const duplicateCodeError = R.any(it => - R.includes('duplicate', it?.message) + R.includes('duplicate', it?.message), )(res.errors) const msg = duplicateCodeError ? DUPLICATE_ERROR_MSG : DEFAULT_ERROR_MSG @@ -93,7 +93,7 @@ const PromoCodes = () => { width: 300, textAlign: 'left', size: 'sm', - view: t => t.code + view: t => t.code, }, { header: 'Discount', @@ -104,7 +104,7 @@ const PromoCodes = () => { <> {t.discount} % in commissions - ) + ), }, { header: 'Delete', @@ -121,8 +121,8 @@ const PromoCodes = () => { - ) - } + ), + }, ] return ( diff --git a/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodesModal.jsx b/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodesModal.jsx index 15d064e5..d44ebf71 100644 --- a/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodesModal.jsx +++ b/packages/admin-ui/src/pages/LoyaltyPanel/PromoCodesModal.jsx @@ -12,7 +12,7 @@ import { TextInput, NumberInput } from 'src/components/inputs/formik' const initialValues = { code: '', - discount: '' + discount: '', } const validationSchema = Yup.object().shape({ @@ -21,7 +21,7 @@ const validationSchema = Yup.object().shape({ .trim() .max(25) .matches(/^\S*$/, 'No whitespace allowed'), - discount: Yup.number().required().min(0).max(100) + discount: Yup.number().required().min(0).max(100), }) const PromoCodesModal = ({ showModal, onClose, errorMsg, addCode }) => { diff --git a/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/Cassettes.jsx b/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/Cassettes.jsx index 0dd0b5a4..f055537b 100644 --- a/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/Cassettes.jsx +++ b/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/Cassettes.jsx @@ -37,7 +37,7 @@ const widths = { name: 0, cashbox: 175, cassettes: 585, - edit: 90 + edit: 90, } const CashCassettes = ({ machine, config, refetchData, bills }) => { @@ -50,11 +50,11 @@ const CashCassettes = ({ machine, config, refetchData, bills }) => { const getCashoutSettings = deviceId => fromNamespace(deviceId)(cashout) const elements = R.filter(it => it.name !== 'name')( - helper.getElements(config, bills, setWizard, widths) + helper.getElements(config, bills, setWizard, widths), ) const [setCassetteBills, { error }] = useMutation(SET_CASSETTE_BILLS, { - refetchQueries: () => refetchData() + refetchQueries: () => refetchData(), }) const onSave = (_, cashUnits) => @@ -62,8 +62,8 @@ const CashCassettes = ({ machine, config, refetchData, bills }) => { variables: { action: 'setCassetteBills', deviceId: machine.deviceId, - cashUnits - } + cashUnits, + }, }) const InnerCashUnitDetails = ({ it }) => ( diff --git a/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/index.js b/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/index.js index e70fe4c3..5d089de2 100644 --- a/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/index.js +++ b/packages/admin-ui/src/pages/Machines/MachineComponents/Cassettes/index.js @@ -1,2 +1,3 @@ import Cassettes from './Cassettes' + export default Cassettes diff --git a/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/Commissions.jsx b/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/Commissions.jsx index 29dae910..4e5bf03e 100644 --- a/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/Commissions.jsx +++ b/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/Commissions.jsx @@ -1,4 +1,4 @@ -import { useQuery, useMutation, gql } from "@apollo/client"; +import { useQuery, useMutation, gql } from '@apollo/client' import * as R from 'ramda' import React from 'react' @@ -30,12 +30,12 @@ const SAVE_CONFIG = gql` const Commissions = ({ name: SCREEN_KEY, id: deviceId }) => { const { data, loading } = useQuery(GET_DATA) const [saveConfig] = useMutation(SAVE_CONFIG, { - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const config = data?.config && fromNamespace(SCREEN_KEY)(data.config) const currency = R.path(['fiatCurrency'])( - fromNamespace(namespaces.LOCALE)(data?.config) + fromNamespace(namespaces.LOCALE)(data?.config), ) const saveOverrides = it => { @@ -49,7 +49,7 @@ const Commissions = ({ name: SCREEN_KEY, id: deviceId }) => { const overrides = config.overrides ? R.concat( R.filter(R.propEq('machine', 'ALL_MACHINES'), config.overrides), - R.filter(R.propEq('machine', deviceId), config.overrides) + R.filter(R.propEq('machine', deviceId), config.overrides), ) : [] @@ -64,7 +64,7 @@ const Commissions = ({ name: SCREEN_KEY, id: deviceId }) => { cashOut: config.cashOut, fixedFee: config.fixedFee, minimumTx: config.minimumTx, - cashOutFixedFee: config.cashOutFixedFee + cashOutFixedFee: config.cashOutFixedFee, }, R.project( ['cashIn', 'cashOut', 'fixedFee', 'minimumTx', 'cashOutFixedFee'], @@ -72,11 +72,11 @@ const Commissions = ({ name: SCREEN_KEY, id: deviceId }) => { o => R.includes(coin.code, o.cryptoCurrencies) || R.includes('ALL_COINS', o.cryptoCurrencies), - overrides - ) - ) + overrides, + ), + ), ), - data.cryptoCurrencies + data.cryptoCurrencies, ) } diff --git a/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/helper.jsx b/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/helper.jsx index 70065128..aa3bf049 100644 --- a/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/helper.jsx +++ b/packages/admin-ui/src/pages/Machines/MachineComponents/Commissions/helper.jsx @@ -24,7 +24,7 @@ const getOverridesFields = currency => { name: 'name', width: 280, size: 'sm', - view: it => `${it}` + view: it => `${it}`, }, { header: cashInHeader, @@ -32,7 +32,7 @@ const getOverridesFields = currency => { display: 'Cash-in', width: 130, textAlign: 'right', - suffix: '%' + suffix: '%', }, { header: cashOutHeader, @@ -42,8 +42,8 @@ const getOverridesFields = currency => { textAlign: 'right', suffix: '%', inputProps: { - decimalPlaces: 3 - } + decimalPlaces: 3, + }, }, { name: 'fixedFee', @@ -51,7 +51,7 @@ const getOverridesFields = currency => { width: 155, doubleHeader: 'Cash-in only', textAlign: 'right', - suffix: currency + suffix: currency, }, { name: 'minimumTx', @@ -59,7 +59,7 @@ const getOverridesFields = currency => { width: 155, doubleHeader: 'Cash-in only', textAlign: 'right', - suffix: currency + suffix: currency, }, { name: 'cashOutFixedFee', @@ -67,8 +67,8 @@ const getOverridesFields = currency => { width: 155, doubleHeader: 'Cash-out only', textAlign: 'right', - suffix: currency - } + suffix: currency, + }, ] } diff --git a/packages/admin-ui/src/pages/Machines/MachineComponents/Overview.jsx b/packages/admin-ui/src/pages/Machines/MachineComponents/Overview.jsx index da3f939a..8fc4c6e9 100644 --- a/packages/admin-ui/src/pages/Machines/MachineComponents/Overview.jsx +++ b/packages/admin-ui/src/pages/Machines/MachineComponents/Overview.jsx @@ -28,7 +28,7 @@ const Overview = ({ data, onActionSuccess }) => {

{data.lastPing ? formatDistance(new Date(data.lastPing), new Date(), { - addSuffix: true + addSuffix: true, }) : 'unknown'}

diff --git a/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/Transactions.jsx b/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/Transactions.jsx index aac44707..4eb9bdc6 100644 --- a/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/Transactions.jsx +++ b/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/Transactions.jsx @@ -77,9 +77,9 @@ const Transactions = ({ id }) => { { variables: { limit: NUM_LOG_RESULTS, - deviceId: id - } - } + deviceId: id, + }, + }, ) const { data: configData, loading: configLoading } = useQuery(GET_DATA) @@ -102,20 +102,20 @@ const Transactions = ({ id }) => { header: '', width: 0, size: 'sm', - view: it => (it.txClass === 'cashOut' ? : ) + view: it => (it.txClass === 'cashOut' ? : ), }, { header: 'Customer', width: 122, size: 'sm', - view: Customer.displayName + view: Customer.displayName, }, { header: 'Cash', width: 144, textAlign: 'right', size: 'sm', - view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}` + view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}`, }, { header: 'Crypto', @@ -125,7 +125,7 @@ const Transactions = ({ id }) => { view: it => `${toUnit(new BigNumber(it.cryptoAtoms), it.cryptoCode).toFormat(5)} ${ it.cryptoCode - }` + }`, }, { header: 'Address', @@ -133,21 +133,21 @@ const Transactions = ({ id }) => { className: 'overflow-hidden whitespace-nowrap text-ellipsis', size: 'sm', textAlign: 'left', - width: 140 + width: 140, }, { header: 'Date', view: it => formatDate(it.created, timezone, 'yyyy‑MM‑dd'), textAlign: 'left', size: 'sm', - width: 140 + width: 140, }, { header: 'Status', view: it => getStatus(it), size: 'sm', - width: 20 - } + width: 20, + }, ] const handleClick = e => { diff --git a/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/index.js b/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/index.js index 1d3254c5..57bc006e 100644 --- a/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/index.js +++ b/packages/admin-ui/src/pages/Machines/MachineComponents/Transactions/index.js @@ -1,2 +1,3 @@ import Transactions from './Transactions' + export default Transactions diff --git a/packages/admin-ui/src/pages/Machines/Machines.jsx b/packages/admin-ui/src/pages/Machines/Machines.jsx index 51975340..e26bce22 100644 --- a/packages/admin-ui/src/pages/Machines/Machines.jsx +++ b/packages/admin-ui/src/pages/Machines/Machines.jsx @@ -79,9 +79,9 @@ const MachineRoute = () => { deviceId: id, billFilters: { deviceId: id, - batch: 'none' - } - } + batch: 'none', + }, + }, }) const reload = () => { diff --git a/packages/admin-ui/src/pages/Maintenance/CashUnitDetails.jsx b/packages/admin-ui/src/pages/Maintenance/CashUnitDetails.jsx index 00b196ac..60128171 100644 --- a/packages/admin-ui/src/pages/Maintenance/CashUnitDetails.jsx +++ b/packages/admin-ui/src/pages/Maintenance/CashUnitDetails.jsx @@ -12,7 +12,7 @@ const CashUnitDetails = ({ bills, currency, config, - hideMachineData = false + hideMachineData = false, }) => { const billCount = R.countBy(it => it.fiat)(bills) const fillingPercentageSettings = fromNamespace('notifications', config) diff --git a/packages/admin-ui/src/pages/Maintenance/CashUnits.jsx b/packages/admin-ui/src/pages/Maintenance/CashUnits.jsx index 8d045bf5..4dc49033 100644 --- a/packages/admin-ui/src/pages/Maintenance/CashUnits.jsx +++ b/packages/admin-ui/src/pages/Maintenance/CashUnits.jsx @@ -108,7 +108,7 @@ const widths = { name: 250, cashbox: 200, cassettes: 575, - edit: 90 + edit: 90, } const CashCassettes = () => { @@ -119,9 +119,9 @@ const CashCassettes = () => { const { data, loading: dataLoading } = useQuery(GET_MACHINES_AND_CONFIG, { variables: { billFilters: { - batch: 'none' - } - } + batch: 'none', + }, + }, }) const [wizard, setWizard] = useState(false) const [machineId, setMachineId] = useState('') @@ -130,18 +130,18 @@ const CashCassettes = () => { const unpairedMachines = R.path(['unpairedMachines'])(data) ?? [] const config = R.path(['config'])(data) ?? {} const [setCassetteBills, { error }] = useMutation(SET_CASSETTE_BILLS, { - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const [saveConfig] = useMutation(SAVE_CONFIG, { onCompleted: () => setEditingSchema(false), - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const timezone = R.path(['config', 'locale_timezone'], data) const bills = R.groupBy(bill => bill.deviceId)(R.path(['bills'])(data) ?? []) const deviceIds = R.uniq( - R.map(R.prop('deviceId'))(R.path(['bills'])(data) ?? []) + R.map(R.prop('deviceId'))(R.path(['bills'])(data) ?? []), ) const cashout = data?.config && fromNamespace('cashOut')(data.config) const locale = data?.config && fromNamespace('locale')(data.config) @@ -154,8 +154,8 @@ const CashCassettes = () => { variables: { action: 'setCassetteBills', deviceId: id, - cashUnits - } + cashUnits, + }, }) } @@ -176,7 +176,7 @@ const CashCassettes = () => { const radioButtonOptions = [ { display: 'Automatic', code: AUTOMATIC }, - { display: 'Manual', code: MANUAL } + { display: 'Manual', code: MANUAL }, ] const handleRadioButtons = evt => { @@ -189,7 +189,7 @@ const CashCassettes = () => { bills, setWizard, widths, - setMachineId + setMachineId, ) const InnerCashUnitDetails = ({ it }) => ( @@ -211,7 +211,7 @@ const CashCassettes = () => { text: 'Cash box history', icon: HistoryIcon, inverseIcon: ReverseHistoryIcon, - toggle: setShowHistory + toggle: setShowHistory, }, { component: showHistory ? ( @@ -226,8 +226,8 @@ const CashCassettes = () => { /> ) : ( <> - ) - } + ), + }, ]} className="flex items-center mr-[1px]" appendix={ diff --git a/packages/admin-ui/src/pages/Maintenance/CashUnitsFooter.jsx b/packages/admin-ui/src/pages/Maintenance/CashUnitsFooter.jsx index 856828af..dde73e25 100644 --- a/packages/admin-ui/src/pages/Maintenance/CashUnitsFooter.jsx +++ b/packages/admin-ui/src/pages/Maintenance/CashUnitsFooter.jsx @@ -8,18 +8,12 @@ import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react' import { fromNamespace } from 'src/utils/config' import { numberToFiatAmount } from 'src/utils/number' -const CashCassettesFooter = ({ - machines, - config, - currencyCode, - bills, - deviceIds -}) => { +const CashCassettesFooter = ({ machines, config, currencyCode, bills }) => { const cashout = config && fromNamespace('cashOut')(config) const getCashoutSettings = id => fromNamespace(id)(cashout) const cashoutReducerFn = ( acc, - { cashUnits: { cassette1, cassette2, cassette3, cassette4 }, id } + { cashUnits: { cassette1, cassette2, cassette3, cassette4 }, id }, ) => { const cassette1Denomination = getCashoutSettings(id).cassette1 ?? 0 const cassette2Denomination = getCashoutSettings(id).cassette2 ?? 0 @@ -29,7 +23,7 @@ const CashCassettesFooter = ({ (acc[0] += cassette1 * cassette1Denomination), (acc[1] += cassette2 * cassette2Denomination), (acc[2] += cassette3 * cassette3Denomination), - (acc[3] += cassette4 * cassette4Denomination) + (acc[3] += cassette4 * cassette4Denomination), ] } @@ -42,10 +36,10 @@ const CashCassettesFooter = ({ recycler3, recycler4, recycler5, - recycler6 + recycler6, }, - id - } + id, + }, ) => { const recycler1Denomination = getCashoutSettings(id).recycler1 ?? 0 const recycler2Denomination = getCashoutSettings(id).recycler2 ?? 0 @@ -59,22 +53,22 @@ const CashCassettesFooter = ({ (acc[0] += recycler3 * recycler3Denomination), (acc[1] += recycler4 * recycler4Denomination), (acc[0] += recycler5 * recycler5Denomination), - (acc[1] += recycler6 * recycler6Denomination) + (acc[1] += recycler6 * recycler6Denomination), ] } const totalInRecyclers = R.sum( - R.reduce(recyclerReducerFn, [0, 0, 0, 0, 0, 0], machines) + R.reduce(recyclerReducerFn, [0, 0, 0, 0, 0, 0], machines), ) const totalInCassettes = R.sum( - R.reduce(cashoutReducerFn, [0, 0, 0, 0], machines) + R.reduce(cashoutReducerFn, [0, 0, 0, 0], machines), ) const totalInCashBox = R.sum(R.map(it => it.fiat)(bills)) const total = new BigNumber( - totalInCassettes + totalInCashBox + totalInRecyclers + totalInCassettes + totalInCashBox + totalInRecyclers, ).toFormat(0) return ( diff --git a/packages/admin-ui/src/pages/Maintenance/CashboxHistory.jsx b/packages/admin-ui/src/pages/Maintenance/CashboxHistory.jsx index 70b8f41e..44b8f0af 100644 --- a/packages/admin-ui/src/pages/Maintenance/CashboxHistory.jsx +++ b/packages/admin-ui/src/pages/Maintenance/CashboxHistory.jsx @@ -38,15 +38,15 @@ const CashboxHistory = ({ machines, currency, timezone }) => { <> Cash cassette {i} refill - + , ), R.assoc( `cash-cassette-${i}-empty`, <> Cash cassette {i} emptied - - ) + , + ), )(ret), { 'cash-box-empty': ( @@ -54,9 +54,9 @@ const CashboxHistory = ({ machines, currency, timezone }) => { Cash box emptied - ) + ), }, - R.range(1, 5) + R.range(1, 5), ) const elements = [ @@ -69,7 +69,7 @@ const CashboxHistory = ({ machines, currency, timezone }) => {
{getOperationRender[it.operationType]}
- ) + ), }, { name: 'machine', @@ -80,8 +80,8 @@ const CashboxHistory = ({ machines, currency, timezone }) => { R.prop('deviceId'), id => R.find(R.propEq('id', id), machines), R.defaultTo({ name: Unpaired device }), - R.prop('name') - ) + R.prop('name'), + ), }, { name: 'billCount', @@ -90,10 +90,10 @@ const CashboxHistory = ({ machines, currency, timezone }) => { textAlign: 'left', input: NumberInput, inputProps: { - decimalPlaces: 0 + decimalPlaces: 0, }, view: it => - R.isNil(it.customBillCount) ? it.billCount : it.customBillCount + R.isNil(it.customBillCount) ? it.billCount : it.customBillCount, }, { name: 'total', @@ -104,22 +104,22 @@ const CashboxHistory = ({ machines, currency, timezone }) => { {it.fiatTotal} {currency} - ) + ), }, { name: 'date', header: 'Date', width: 135, textAlign: 'right', - view: it => formatDate(it.created, timezone, 'yyyy-MM-dd') + view: it => formatDate(it.created, timezone, 'yyyy-MM-dd'), }, { name: 'time', header: 'Time (h:m)', width: 125, textAlign: 'right', - view: it => formatDate(it.created, timezone, 'HH:mm') - } + view: it => formatDate(it.created, timezone, 'HH:mm'), + }, ] return ( diff --git a/packages/admin-ui/src/pages/Maintenance/MachineStatus.jsx b/packages/admin-ui/src/pages/Maintenance/MachineStatus.jsx index 34eea6d3..b105a2b1 100644 --- a/packages/admin-ui/src/pages/Maintenance/MachineStatus.jsx +++ b/packages/admin-ui/src/pages/Maintenance/MachineStatus.jsx @@ -61,7 +61,7 @@ const MachineStatus = () => { const { data: machinesResponse, refetch, - loading: machinesLoading + loading: machinesLoading, } = useQuery(GET_MACHINES) const { data: configResponse, configLoading } = useQuery(GET_DATA) const timezone = R.path(['config', 'locale_timezone'], configResponse) @@ -82,14 +82,14 @@ const MachineStatus = () => {
- ) + ), }, { header: 'Status', width: 350, size: 'sm', textAlign: 'left', - view: m => + view: m => , }, { header: 'Last ping', @@ -99,22 +99,22 @@ const MachineStatus = () => { view: m => m.lastPing ? formatDistance(new Date(m.lastPing), new Date(), { - addSuffix: true + addSuffix: true, }) - : 'unknown' + : 'unknown', }, { header: 'Software version', width: 200, size: 'sm', textAlign: 'left', - view: m => m.version || 'unknown' - } + view: m => m.version || 'unknown', + }, ] const machines = R.path(['machines'])(machinesResponse) ?? [] const expandedIndex = R.findIndex(R.propEq('deviceId', addedMachineId))( - machines + machines, ) const InnerMachineDetailsRow = ({ it }) => ( diff --git a/packages/admin-ui/src/pages/Maintenance/Wizard/Wizard.jsx b/packages/admin-ui/src/pages/Maintenance/Wizard/Wizard.jsx index a42680e4..b261e394 100644 --- a/packages/admin-ui/src/pages/Maintenance/Wizard/Wizard.jsx +++ b/packages/admin-ui/src/pages/Maintenance/Wizard/Wizard.jsx @@ -7,7 +7,7 @@ import { MAX_NUMBER_OF_CASSETTES } from 'src/utils/constants' import { cashUnitCapacity, getCashUnitCapacity, - modelPrettifier + modelPrettifier, } from 'src/utils/machine' import { defaultToZero } from 'src/utils/number' @@ -19,7 +19,7 @@ const MODAL_HEIGHT = 535 const CASSETTE_FIELDS = R.map( it => `cassette${it}`, - R.range(1, MAX_NUMBER_OF_CASSETTES + 1) + R.range(1, MAX_NUMBER_OF_CASSETTES + 1), ) const RECYCLER_FIELDS = [ @@ -28,7 +28,7 @@ const RECYCLER_FIELDS = [ 'recycler3', 'recycler4', 'recycler5', - 'recycler6' + 'recycler6', ] const canManuallyLoadRecyclers = ({ model }) => ['grandola'].includes(model) @@ -36,7 +36,7 @@ const canManuallyLoadRecyclers = ({ model }) => ['grandola'].includes(model) const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { const [{ step, config }, setState] = useState({ step: 0, - config: { active: true } + config: { active: true }, }) const isCashOutDisabled = @@ -60,7 +60,7 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { if (isLastStep) { const wasCashboxEmptied = [ config?.wasCashboxEmptied, - it?.wasCashboxEmptied + it?.wasCashboxEmptied, ].includes('YES') const cassettes = buildCashUnitObj(CASSETTE_FIELDS, it) @@ -71,7 +71,7 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { const cashUnits = { cashbox: wasCashboxEmptied ? 0 : machine?.cashUnits.cashbox, ...cassettes, - ...recyclers + ...recyclers, } save(machine.id, cashUnits) @@ -80,7 +80,7 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { setState({ step: step + 1, - config: newConfig + config: newConfig, }) } @@ -102,11 +102,11 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { modelPrettifier[machine.model] } maximum cassette capacity is ${getCashUnitCapacity( machine.model, - 'cassette' - )} bills` - ) - }) - })) + 'cassette', + )} bills`, + ), + }), + })), ) const makeRecyclerSteps = R.pipe( @@ -126,10 +126,10 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { `${modelPrettifier[machine.model]} maximum recycler capacity is ${ cashUnitCapacity[machine.model].recycler - } bills` - ) - }) - })) + } bills`, + ), + }), + })), ) const makeCassettesInitialValues = () => @@ -140,7 +140,7 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { return acc }, {}, - R.range(1, numberOfCassettes + 1) + R.range(1, numberOfCassettes + 1), ) : {} @@ -153,7 +153,7 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { return acc }, {}, - R.range(1, numberOfRecyclers + 1) + R.range(1, numberOfRecyclers + 1), ) : {} @@ -163,19 +163,19 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { const steps = R.pipe( R.concat( makeRecyclerSteps( - canManuallyLoadRecyclers(machine) ? numberOfRecyclers : 0 - ) + canManuallyLoadRecyclers(machine) ? numberOfRecyclers : 0, + ), ), R.concat(makeCassetteSteps(isCashOutDisabled ? 0 : numberOfCassettes)), R.concat([ { type: 'cashbox', schema: Yup.object().shape({ - wasCashboxEmptied: Yup.string().required('Select one option.') + wasCashboxEmptied: Yup.string().required('Select one option.'), }), - cashoutRequired: false - } - ]) + cashoutRequired: false, + }, + ]), )([]) return ( diff --git a/packages/admin-ui/src/pages/Maintenance/Wizard/WizardStep.jsx b/packages/admin-ui/src/pages/Maintenance/Wizard/WizardStep.jsx index 51007534..87056254 100644 --- a/packages/admin-ui/src/pages/Maintenance/Wizard/WizardStep.jsx +++ b/packages/admin-ui/src/pages/Maintenance/Wizard/WizardStep.jsx @@ -46,8 +46,8 @@ const cassetesArtworks = (step, numberOfCassettes, numberOfRecyclers) => { tejo4CassetteOne, tejo4CassetteTwo, tejo4CassetteThree, - tejo4CassetteFour - ] + tejo4CassetteFour, + ], ][numberOfCassettes - 1][step - cassetteStepsStart] : [ /* TODO: Recycler artwork */ @@ -58,8 +58,8 @@ const cassetesArtworks = (step, numberOfCassettes, numberOfRecyclers) => { tejo4CassetteOne, tejo4CassetteTwo, tejo4CassetteThree, - tejo4CassetteFour - ] + tejo4CassetteFour, + ], ][numberOfRecyclers - 1][step - cassetteStepsStart] } @@ -69,13 +69,13 @@ const getCashUnitFieldName = (step, numberOfCassettes, numberOfRecyclers) => { if (isCassetteStep(step, numberOfCassettes)) return { name: `cassette${step - cassetteStepsStart + 1}`, - category: 'cassette' + category: 'cassette', } const recyclerStepsStart = CASHBOX_STEP + numberOfCassettes + 1 if (isRecyclerStep(step, numberOfCassettes, numberOfRecyclers)) return { name: `recycler${Math.ceil(step - recyclerStepsStart + 1)}`, - category: 'recycler' + category: 'recycler', } } @@ -84,18 +84,17 @@ const WizardStep = ({ name, machine, cashoutSettings, - error, lastStep, steps, fiatCurrency, onContinue, - initialValues + initialValues, }) => { const label = lastStep ? 'Finish' : 'Confirm' const stepOneRadioOptions = [ { display: 'Yes', code: 'YES' }, - { display: 'No', code: 'NO' } + { display: 'No', code: 'NO' }, ] const numberOfCassettes = machine.numberOfCassettes @@ -111,7 +110,7 @@ const WizardStep = ({ cassetteCount, count => 100 * (count / getCashUnitCapacity(machine.model, cashUnitCategory)), - R.clamp(0, 100) + R.clamp(0, 100), ) return ( @@ -155,7 +154,7 @@ const WizardStep = ({

Since previous update

@@ -168,7 +167,7 @@ const WizardStep = ({
{machine?.cashUnits.cashbox} @@ -203,7 +202,7 @@ const WizardStep = ({ src={cassetesArtworks( step, numberOfCassettes, - numberOfRecyclers + numberOfRecyclers, )}>
@@ -232,7 +231,7 @@ const WizardStep = ({
{ header: 'Machine', width: widths.name, view: m => <>{m.name}, - input: ({ field: { value: name } }) => <>{name} + input: ({ field: { value: name } }) => <>{name}, }, { name: 'cashbox', @@ -39,8 +39,8 @@ const getElements = (config, bills, setWizard, widths, setMachineId) => { /> ), inputProps: { - decimalPlaces: 0 - } + decimalPlaces: 0, + }, }, { name: 'cassettes', @@ -111,8 +111,8 @@ const getElements = (config, bills, setWizard, widths, setMachineId) => { ) }, inputProps: { - decimalPlaces: 0 - } + decimalPlaces: 0, + }, }, { name: 'edit', @@ -131,8 +131,8 @@ const getElements = (config, bills, setWizard, widths, setMachineId) => { ) - } - } + }, + }, ] return elements diff --git a/packages/admin-ui/src/pages/Notifications/Notifications.jsx b/packages/admin-ui/src/pages/Notifications/Notifications.jsx index ee09c0cf..a6a28a5f 100644 --- a/packages/admin-ui/src/pages/Notifications/Notifications.jsx +++ b/packages/admin-ui/src/pages/Notifications/Notifications.jsx @@ -1,4 +1,4 @@ -import { useQuery, useMutation, gql } from "@apollo/client"; +import { useQuery, useMutation, gql } from '@apollo/client' import * as R from 'ramda' import React, { useState } from 'react' import Modal from 'src/components/Modal' @@ -70,7 +70,7 @@ const Notifications = ({ displayOverrides = true, displayTitle = true, displayThirdPartyProvider = true, - wizard = false + wizard = false, }) => { const [section, setSection] = useState(null) const [error, setError] = useState(null) @@ -83,7 +83,7 @@ const Notifications = ({ const [saveConfig] = useMutation(SAVE_CONFIG, { refetchQueries: ['getData'], onCompleted: () => setEditingKey(null), - onError: error => setError(error) + onError: error => setError(error), }) const [saveAccount] = useMutation(SAVE_ACCOUNT, { @@ -92,7 +92,7 @@ const Notifications = ({ setEmailSetupPopup(false) }, refetchQueries: ['getData'], - onError: error => setError(error) + onError: error => setError(error), }) const config = fromNamespace(SCREEN_KEY)(data?.config) @@ -103,7 +103,7 @@ const Notifications = ({ const mailgunAvailable = R.has('mailgun', data?.accounts || {}) const currency = R.path(['fiatCurrency'])( - fromNamespace(namespaces.LOCALE)(data?.config) + fromNamespace(namespaces.LOCALE)(data?.config), ) const save = R.curry((section, rawConfig) => { @@ -123,14 +123,14 @@ const Notifications = ({ const twilioSave = it => { setError(null) return saveAccount({ - variables: { accounts: { twilio: it } } + variables: { accounts: { twilio: it } }, }).then(() => R.compose(save(null), toNamespace('sms'))({ active: true })) } const mailgunSave = it => { setError(null) return saveAccount({ - variables: { accounts: { mailgun: it } } + variables: { accounts: { mailgun: it } }, }).then(() => R.compose(save(null), toNamespace('email'))({ active: true })) } @@ -153,7 +153,7 @@ const Notifications = ({ twilioAvailable, setSmsSetupPopup, mailgunAvailable, - setEmailSetupPopup + setEmailSetupPopup, } return ( diff --git a/packages/admin-ui/src/pages/Notifications/components/EditableNumber.jsx b/packages/admin-ui/src/pages/Notifications/components/EditableNumber.jsx index 6abbcf4b..116ae291 100644 --- a/packages/admin-ui/src/pages/Notifications/components/EditableNumber.jsx +++ b/packages/admin-ui/src/pages/Notifications/components/EditableNumber.jsx @@ -13,13 +13,13 @@ const EditableNumber = ({ decoration, className, decimalPlaces = 0, - width = 80 + width = 80, }) => { const { values } = useFormikContext() const classNames = { 'h-13': true, - className + className, } return ( diff --git a/packages/admin-ui/src/pages/Notifications/components/SingleFieldEditableNumber.jsx b/packages/admin-ui/src/pages/Notifications/components/SingleFieldEditableNumber.jsx index 78d486d1..39a30588 100644 --- a/packages/admin-ui/src/pages/Notifications/components/SingleFieldEditableNumber.jsx +++ b/packages/admin-ui/src/pages/Notifications/components/SingleFieldEditableNumber.jsx @@ -18,7 +18,7 @@ const SingleFieldEditableNumber = ({ max = 9999999, name, section, - className + className, }) => { const [saving, setSaving] = useState(false) @@ -42,7 +42,7 @@ const SingleFieldEditableNumber = ({ .integer() .min(min) .max(max) - .nullable() + .nullable(), }) return ( diff --git a/packages/admin-ui/src/pages/Notifications/sections/CryptoBalanceOverrides.jsx b/packages/admin-ui/src/pages/Notifications/sections/CryptoBalanceOverrides.jsx index dd083ead..a92d0b6c 100644 --- a/packages/admin-ui/src/pages/Notifications/sections/CryptoBalanceOverrides.jsx +++ b/packages/admin-ui/src/pages/Notifications/sections/CryptoBalanceOverrides.jsx @@ -23,27 +23,27 @@ const CryptoBalanceOverrides = ({ section }) => { error, currency, isDisabled, - setEditing + setEditing, } = useContext(NotificationsCtx) const setupValues = data?.cryptoBalanceOverrides ?? [] const innerSetEditing = it => setEditing(NAME, it) const onDelete = id => { const newOverrides = { - cryptoBalanceOverrides: R.reject(it => it.id === id, setupValues) + cryptoBalanceOverrides: R.reject(it => it.id === id, setupValues), } return save(newOverrides) } const overriddenCryptos = R.map(R.prop(CRYPTOCURRENCY_KEY))(setupValues) const suggestionFilter = R.filter( - it => !R.contains(it.code, overriddenCryptos) + it => !R.contains(it.code, overriddenCryptos), ) const suggestions = suggestionFilter(cryptoCurrencies) const findSuggestion = it => { const coin = R.compose(R.find(R.propEq('code', it?.cryptoCurrency)))( - cryptoCurrencies + cryptoCurrencies, ) return coin ? [coin] : [] } @@ -51,7 +51,7 @@ const CryptoBalanceOverrides = ({ section }) => { const initialValues = { [CRYPTOCURRENCY_KEY]: null, [LOW_BALANCE_KEY]: '', - [HIGH_BALANCE_KEY]: '' + [HIGH_BALANCE_KEY]: '', } const notesMin = 0 @@ -65,7 +65,7 @@ const CryptoBalanceOverrides = ({ section }) => { .label('Low balance') .when(HIGH_BALANCE_KEY, { is: HIGH_BALANCE_KEY => !HIGH_BALANCE_KEY, - then: schema => schema.required() + then: schema => schema.required(), }) .transform(transformNumber) .integer() @@ -76,21 +76,21 @@ const CryptoBalanceOverrides = ({ section }) => { .label('High balance') .when(LOW_BALANCE_KEY, { is: LOW_BALANCE_KEY => !LOW_BALANCE_KEY, - then: schema => schema.required() + then: schema => schema.required(), }) .transform(transformNumber) .integer() .min(notesMin) .max(CURRENCY_MAX) - .nullable() + .nullable(), }, - [LOW_BALANCE_KEY, HIGH_BALANCE_KEY] + [LOW_BALANCE_KEY, HIGH_BALANCE_KEY], ) const viewCrypto = it => R.compose( R.path(['display']), - R.find(R.propEq('code', it)) + R.find(R.propEq('code', it)), )(cryptoCurrencies) const elements = [ @@ -105,8 +105,8 @@ const CryptoBalanceOverrides = ({ section }) => { options: it => R.concat(suggestions, findSuggestion(it)), optionsLimit: null, valueProp: 'code', - labelProp: 'display' - } + labelProp: 'display', + }, }, { name: LOW_BALANCE_KEY, @@ -116,8 +116,8 @@ const CryptoBalanceOverrides = ({ section }) => { input: NumberInput, suffix: currency, inputProps: { - decimalPlaces: 2 - } + decimalPlaces: 2, + }, }, { name: HIGH_BALANCE_KEY, @@ -127,9 +127,9 @@ const CryptoBalanceOverrides = ({ section }) => { input: NumberInput, suffix: currency, inputProps: { - decimalPlaces: 2 - } - } + decimalPlaces: 2, + }, + }, ] return ( diff --git a/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceAlerts.jsx b/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceAlerts.jsx index 7c136cc1..0bead6e3 100644 --- a/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceAlerts.jsx +++ b/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceAlerts.jsx @@ -20,23 +20,23 @@ const DEFAULT_NUMBER_OF_RECYCLERS = 0 const notesMin = 0 const notesMax = 9999999 -const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => { +const FiatBalance = ({ section, fieldWidth = 80 }) => { const { isEditing, isDisabled, setEditing, data, save, - machines = [] + machines = [], } = useContext(NotificationsCtx) const maxNumberOfCassettes = Math.max( ...R.map(it => it.numberOfCassettes, machines), - DEFAULT_NUMBER_OF_CASSETTES + DEFAULT_NUMBER_OF_CASSETTES, ) const maxNumberOfRecyclers = Math.max( ...R.map(it => it.numberOfRecyclers, machines), - DEFAULT_NUMBER_OF_RECYCLERS + DEFAULT_NUMBER_OF_RECYCLERS, ) const percentValidation = Yup.number() @@ -62,7 +62,7 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => { fillingPercentageRecycler3: percentValidation, fillingPercentageRecycler4: percentValidation, fillingPercentageRecycler5: percentValidation, - fillingPercentageRecycler6: percentValidation + fillingPercentageRecycler6: percentValidation, }) return ( @@ -81,7 +81,7 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => { fillingPercentageRecycler3: data?.fillingPercentageRecycler3 ?? '', fillingPercentageRecycler4: data?.fillingPercentageRecycler4 ?? '', fillingPercentageRecycler5: data?.fillingPercentageRecycler5 ?? '', - fillingPercentageRecycler6: data?.fillingPercentageRecycler6 ?? '' + fillingPercentageRecycler6: data?.fillingPercentageRecycler6 ?? '', }} validationSchema={schema} onSubmit={it => save(section, schema.cast(it))} @@ -145,7 +145,7 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
), - R.times(R.identity, maxNumberOfCassettes) + R.times(R.identity, maxNumberOfCassettes), )}
@@ -210,9 +210,9 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => { />
- + , ], - R.times(R.identity, maxNumberOfRecyclers / 2) + R.times(R.identity, maxNumberOfRecyclers / 2), )}
diff --git a/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceOverrides.jsx b/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceOverrides.jsx index 32e37865..06b69515 100644 --- a/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceOverrides.jsx +++ b/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceOverrides.jsx @@ -23,13 +23,13 @@ const CASSETTE_LIST = [ CASSETTE_1_KEY, CASSETTE_2_KEY, CASSETTE_3_KEY, - CASSETTE_4_KEY + CASSETTE_4_KEY, ] const widthsByNumberOfCassettes = { 2: { machine: 230, cashbox: 150, cassette: 250 }, 3: { machine: 216, cashbox: 150, cassette: 270 }, - 4: { machine: 210, cashbox: 150, cassette: 204 } + 4: { machine: 210, cashbox: 150, cassette: 204 }, } const FiatBalanceOverrides = ({ config, section }) => { @@ -39,7 +39,7 @@ const FiatBalanceOverrides = ({ config, section }) => { save, isDisabled, setEditing, - error + error, } = useContext(NotificationsCtx) const setupValues = data?.fiatBalanceOverrides ?? [] @@ -50,7 +50,7 @@ const FiatBalanceOverrides = ({ config, section }) => { const suggestions = R.differenceWith( (it, m) => it.deviceId === m, machines, - overriddenMachines + overriddenMachines, ) const findSuggestion = it => { @@ -64,7 +64,7 @@ const FiatBalanceOverrides = ({ config, section }) => { [CASSETTE_1_KEY]: '', [CASSETTE_2_KEY]: '', [CASSETTE_3_KEY]: '', - [CASSETTE_4_KEY]: '' + [CASSETTE_4_KEY]: '', } const notesMin = 0 @@ -72,7 +72,7 @@ const FiatBalanceOverrides = ({ config, section }) => { const maxNumberOfCassettes = Math.max( ...R.map(it => it.numberOfCassettes, machines), - DEFAULT_NUMBER_OF_CASSETTES + DEFAULT_NUMBER_OF_CASSETTES, ) const percentMin = 0 @@ -114,7 +114,7 @@ const FiatBalanceOverrides = ({ config, section }) => { .integer() .min(percentMin) .max(percentMax) - .nullable() + .nullable(), }) .test((values, context) => R.any(key => !R.isNil(values[key]), R.prepend(CASHBOX_KEY, CASSETTE_LIST)) @@ -122,8 +122,8 @@ const FiatBalanceOverrides = ({ config, section }) => { : context.createError({ path: CASHBOX_KEY, message: - 'The cash box or at least one of the cassettes must have a value' - }) + 'The cash box or at least one of the cassettes must have a value', + }), ) const viewMachine = it => @@ -141,8 +141,8 @@ const FiatBalanceOverrides = ({ config, section }) => { inputProps: { options: it => R.concat(suggestions, findSuggestion(it)), valueProp: 'deviceId', - labelProp: 'name' - } + labelProp: 'name', + }, }, { name: CASHBOX_KEY, @@ -153,9 +153,9 @@ const FiatBalanceOverrides = ({ config, section }) => { input: NumberInput, suffix: 'notes', inputProps: { - decimalPlaces: 0 - } - } + decimalPlaces: 0, + }, + }, ], R.map( it => ({ @@ -168,7 +168,7 @@ const FiatBalanceOverrides = ({ config, section }) => { input: NumberInput, suffix: '%', inputProps: { - decimalPlaces: 0 + decimalPlaces: 0, }, view: el => el?.toString() ?? '—', isHidden: value => @@ -177,11 +177,11 @@ const FiatBalanceOverrides = ({ config, section }) => { R.defaultTo( 0, machines.find(({ deviceId }) => deviceId === value.machine) - ?.numberOfCassettes - ) + ?.numberOfCassettes, + ), }), - R.range(1, maxNumberOfCassettes + 1) - ) + R.range(1, maxNumberOfCassettes + 1), + ), ) return ( diff --git a/packages/admin-ui/src/pages/Notifications/sections/Setup.jsx b/packages/admin-ui/src/pages/Notifications/sections/Setup.jsx index 82fecb6e..50733fca 100644 --- a/packages/admin-ui/src/pages/Notifications/sections/Setup.jsx +++ b/packages/admin-ui/src/pages/Notifications/sections/Setup.jsx @@ -7,7 +7,7 @@ import { TBody, Tr, Td, - Th + Th, } from 'src/components/fake-table/Table' import { fromNamespace, toNamespace } from 'src/utils/config' @@ -22,7 +22,7 @@ const sizes = { compliance: 178, errors: 142, security: 152, - active: 263 + active: 263, } const Row = ({ @@ -31,7 +31,7 @@ const Row = ({ forceDisable, save, shouldUpperCase, - onActivation + onActivation, }) => { const disabled = forceDisable || !data || !data.active @@ -80,7 +80,7 @@ const Setup = ({ wizard, forceDisable }) => { twilioAvailable, setSmsSetupPopup, mailgunAvailable, - setEmailSetupPopup + setEmailSetupPopup, } = useContext(NotificationsCtx) const namespaces = [ @@ -92,7 +92,7 @@ const Setup = ({ wizard, forceDisable }) => { if (mailgunAvailable) return true setEmailSetupPopup(true) return false - } + }, }, { name: 'sms', @@ -102,14 +102,14 @@ const Setup = ({ wizard, forceDisable }) => { if (twilioAvailable) return true setSmsSetupPopup(true) return false - } + }, }, { name: 'notificationCenter', forceDisable: forceDisable, shouldUpperCase: false, - onActivation: () => true - } + onActivation: () => true, + }, ] const widthAdjust = wizard ? 20 : 0 diff --git a/packages/admin-ui/src/pages/Notifications/sections/ThirdPartyProvider.jsx b/packages/admin-ui/src/pages/Notifications/sections/ThirdPartyProvider.jsx index 46f990f5..60a21e92 100644 --- a/packages/admin-ui/src/pages/Notifications/sections/ThirdPartyProvider.jsx +++ b/packages/admin-ui/src/pages/Notifications/sections/ThirdPartyProvider.jsx @@ -15,7 +15,7 @@ const ThirdPartyProvider = () => { save, data: _data, error, - accountsConfig + accountsConfig, } = useContext(NotificationsCtx) const data = fromNamespace('thirdParty')(_data) @@ -25,7 +25,7 @@ const ThirdPartyProvider = () => { const getDisplayName = type => it => R.compose( R.prop('display'), - R.find(R.propEq('code', it)) + R.find(R.propEq('code', it)), )(filterOptions(type)) const innerSave = async value => { @@ -35,7 +35,7 @@ const ThirdPartyProvider = () => { const ThirdPartySchema = Yup.object().shape({ sms: Yup.string('SMS must be a string').required('SMS is required'), - email: Yup.string('Email must be a string').required('Email is required') + email: Yup.string('Email must be a string').required('Email is required'), }) const elements = [ @@ -48,8 +48,8 @@ const ThirdPartyProvider = () => { inputProps: { options: filterOptions('sms'), valueProp: 'code', - labelProp: 'display' - } + labelProp: 'display', + }, }, { name: 'email', @@ -60,13 +60,13 @@ const ThirdPartyProvider = () => { inputProps: { options: filterOptions('email'), valueProp: 'code', - labelProp: 'display' - } - } + labelProp: 'display', + }, + }, ] const values = { sms: data.sms ?? 'twilio', - email: data.email ?? 'mailgun' + email: data.email ?? 'mailgun', } return ( diff --git a/packages/admin-ui/src/pages/OperatorInfo/CoinATMRadar.jsx b/packages/admin-ui/src/pages/OperatorInfo/CoinATMRadar.jsx index f8064b34..ea5c2b33 100644 --- a/packages/admin-ui/src/pages/OperatorInfo/CoinATMRadar.jsx +++ b/packages/admin-ui/src/pages/OperatorInfo/CoinATMRadar.jsx @@ -23,12 +23,12 @@ const CoinATMRadar = memo(({ wizard }) => { const { data } = useQuery(GET_CONFIG) const [saveConfig] = useMutation(SAVE_CONFIG, { - refetchQueries: ['getData'] + refetchQueries: ['getData'], }) const save = it => saveConfig({ - variables: { config: toNamespace(namespaces.COIN_ATM_RADAR, it) } + variables: { config: toNamespace(namespaces.COIN_ATM_RADAR, it) }, }) const coinAtmRadarConfig = @@ -54,12 +54,12 @@ const CoinATMRadar = memo(({ wizard }) => { elements={[ { name: 'commissions', - display: 'Commissions' + display: 'Commissions', }, { name: 'limitsAndVerification', - display: 'Limits and verification' - } + display: 'Limits and verification', + }, ]} save={save} /> diff --git a/packages/admin-ui/src/pages/OperatorInfo/ContactInfo.jsx b/packages/admin-ui/src/pages/OperatorInfo/ContactInfo.jsx index 1d0d83df..53cd7a90 100644 --- a/packages/admin-ui/src/pages/OperatorInfo/ContactInfo.jsx +++ b/packages/admin-ui/src/pages/OperatorInfo/ContactInfo.jsx @@ -71,14 +71,14 @@ const ContactInfo = ({ wizard }) => { const [saveConfig] = useMutation(SAVE_CONFIG, { onCompleted: () => setEditing(false), refetchQueries: () => ['getData'], - onError: e => setError(e) + onError: e => setError(e), }) const { data } = useQuery(GET_CONFIG) const save = it => { return saveConfig({ - variables: { config: toNamespace(namespaces.OPERATOR_INFO, it) } + variables: { config: toNamespace(namespaces.OPERATOR_INFO, it) }, }) } @@ -95,7 +95,7 @@ const ContactInfo = ({ wizard }) => { .email('Please enter a valid email address') .required('An email is required'), website: Yup.string(), - companyNumber: Yup.string() + companyNumber: Yup.string(), }) const fields = [ @@ -103,32 +103,32 @@ const ContactInfo = ({ wizard }) => { name: 'name', label: 'Company name', value: info.name ?? '', - component: TextInput + component: TextInput, }, { name: 'phone', label: 'Phone number', value: info.phone, - component: TextInput + component: TextInput, }, { name: 'email', label: 'Email', value: info.email ?? '', - component: TextInput + component: TextInput, }, { name: 'website', label: 'Website', value: info.website ?? '', - component: TextInput + component: TextInput, }, { name: 'companyNumber', label: 'Company registration number', value: info.companyNumber ?? '', - component: TextInput - } + component: TextInput, + }, ] const findField = name => R.find(R.propEq('name', name))(fields) @@ -143,8 +143,8 @@ const ContactInfo = ({ wizard }) => { phone: findValue('phone'), email: findValue('email'), website: findValue('website'), - companyNumber: findValue('companyNumber') - } + companyNumber: findValue('companyNumber'), + }, } const getErrorMsg = formikErrors => diff --git a/packages/admin-ui/src/pages/OperatorInfo/MachineScreens.jsx b/packages/admin-ui/src/pages/OperatorInfo/MachineScreens.jsx index 6a66f783..d53fdf9f 100644 --- a/packages/admin-ui/src/pages/OperatorInfo/MachineScreens.jsx +++ b/packages/admin-ui/src/pages/OperatorInfo/MachineScreens.jsx @@ -19,24 +19,24 @@ const SAVE_CONFIG = gql` } ` -const MachineScreens = memo(({ wizard }) => { +const MachineScreens = memo(() => { const { data } = useQuery(GET_CONFIG) const [saveConfig] = useMutation(SAVE_CONFIG, { - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const save = it => { const formatConfig = R.compose( toNamespace(namespaces.MACHINE_SCREENS), toNamespace('rates'), - R.mergeRight(ratesScreenConfig) + R.mergeRight(ratesScreenConfig), ) return saveConfig({ variables: { - config: formatConfig({ active: it }) - } + config: formatConfig({ active: it }), + }, }) } @@ -47,7 +47,7 @@ const MachineScreens = memo(({ wizard }) => { data?.config && R.compose( fromNamespace('rates'), - fromNamespace(namespaces.MACHINE_SCREENS) + fromNamespace(namespaces.MACHINE_SCREENS), )(data.config) if (!machineScreensConfig) return null diff --git a/packages/admin-ui/src/pages/OperatorInfo/ReceiptPrinting.jsx b/packages/admin-ui/src/pages/OperatorInfo/ReceiptPrinting.jsx index 4f0209d9..88ad61da 100644 --- a/packages/admin-ui/src/pages/OperatorInfo/ReceiptPrinting.jsx +++ b/packages/admin-ui/src/pages/OperatorInfo/ReceiptPrinting.jsx @@ -24,7 +24,7 @@ const ReceiptPrinting = memo(({ wizard }) => { const { data } = useQuery(GET_CONFIG) const [saveConfig] = useMutation(SAVE_CONFIG, { - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const saveSwitch = object => { @@ -32,15 +32,15 @@ const ReceiptPrinting = memo(({ wizard }) => { variables: { config: toNamespace( namespaces.RECEIPT, - R.mergeRight(receiptPrintingConfig, object) - ) - } + R.mergeRight(receiptPrintingConfig, object), + ), + }, }) } const save = it => saveConfig({ - variables: { config: toNamespace(namespaces.RECEIPT, it) } + variables: { config: toNamespace(namespaces.RECEIPT, it) }, }) const receiptPrintingConfig = @@ -76,36 +76,36 @@ const ReceiptPrinting = memo(({ wizard }) => { elements={[ { name: 'operatorWebsite', - display: 'Operator website' + display: 'Operator website', }, { name: 'operatorEmail', - display: 'Operator email' + display: 'Operator email', }, { name: 'operatorPhone', - display: 'Operator phone' + display: 'Operator phone', }, { name: 'companyNumber', - display: 'Company registration number' + display: 'Company registration number', }, { name: 'machineLocation', - display: 'Machine location' + display: 'Machine location', }, { name: 'customerNameOrPhoneNumber', - display: 'Customer name or phone number (if known)' + display: 'Customer name or phone number (if known)', }, { name: 'exchangeRate', - display: 'Exchange rate' + display: 'Exchange rate', }, { name: 'addressQRCode', - display: 'Address QR code' - } + display: 'Address QR code', + }, ]} save={save} /> diff --git a/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNotices.jsx b/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNotices.jsx index bae80f2d..287938c8 100644 --- a/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNotices.jsx +++ b/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNotices.jsx @@ -82,13 +82,13 @@ const TOOLTIPS = { cashOutDispenseReady: ``, smsReceipt: formatContent(`The contents of this notice will be appended to the end of the SMS receipt sent, and not replace it.\n - To edit the contents of the SMS receipt, please go to the 'Receipt' tab`) + To edit the contents of the SMS receipt, please go to the 'Receipt' tab`), } const SMSPreview = ({ sms, coords, timezone }) => { const matches = { '#code': 123, - '#timestamp': formatDate(new Date(), timezone, 'HH:mm') + '#timestamp': formatDate(new Date(), timezone, 'HH:mm'), } return ( @@ -128,17 +128,17 @@ const SMSNotices = () => { const [editMessage] = useMutation(EDIT_SMS_NOTICE, { onError: ({ msg }) => setErrorMsg(msg), - refetchQueries: () => ['SMSNotices'] + refetchQueries: () => ['SMSNotices'], }) const [enableMessage] = useMutation(ENABLE_SMS_NOTICE, { onError: ({ msg }) => setErrorMsg(msg), - refetchQueries: () => ['SMSNotices'] + refetchQueries: () => ['SMSNotices'], }) const [disableMessage] = useMutation(DISABLE_SMS_NOTICE, { onError: ({ msg }) => setErrorMsg(msg), - refetchQueries: () => ['SMSNotices'] + refetchQueries: () => ['SMSNotices'], }) const loading = messagesLoading @@ -164,7 +164,7 @@ const SMSNotices = () => { ) : ( R.prop('messageName', it) - ) + ), }, { header: 'Edit', @@ -182,7 +182,7 @@ const SMSNotices = () => { - ) + ), }, { header: 'Enable', @@ -199,7 +199,7 @@ const SMSNotices = () => { }} checked={it.enabled} /> - ) + ), }, { header: '', @@ -215,7 +215,7 @@ const SMSNotices = () => { y: window.innerHeight - 5 - - e.currentTarget.getBoundingClientRect().bottom + e.currentTarget.getBoundingClientRect().bottom, }) R.equals(selectedSMS, it) ? setPreviewOpen(!previewOpen) @@ -229,8 +229,8 @@ const SMSNotices = () => { )} - ) - } + ), + }, ] return ( diff --git a/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNoticesModal.jsx b/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNoticesModal.jsx index 4939df23..29dcd876 100644 --- a/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNoticesModal.jsx +++ b/packages/admin-ui/src/pages/OperatorInfo/SMSNotices/SMSNoticesModal.jsx @@ -30,39 +30,39 @@ const PREFILL = { name: 'has-code', message: 'The confirmation code is missing from the message!', exclusive: false, - test: value => value?.match(/#code/g)?.length > 0 + test: value => value?.match(/#code/g)?.length > 0, }) .test({ name: 'has-single-code', message: 'There should be a single confirmation code!', exclusive: false, - test: value => value?.match(/#code/g)?.length === 1 - }) + test: value => value?.match(/#code/g)?.length === 1, + }), }, cashOutDispenseReady: { - validator: Yup.string().required('The message content is required!').trim() + validator: Yup.string().required('The message content is required!').trim(), }, smsReceipt: { - validator: Yup.string().trim() - } + validator: Yup.string().trim(), + }, } const CHIPS = { smsCode: [ { code: '#code', display: 'Confirmation code', obligatory: true }, - { code: '#timestamp', display: 'Timestamp', obligatory: false } + { code: '#timestamp', display: 'Timestamp', obligatory: false }, ], cashOutDispenseReady: [ - { code: '#timestamp', display: 'Timestamp', obligatory: false } + { code: '#timestamp', display: 'Timestamp', obligatory: false }, ], - smsReceipt: [{ code: '#timestamp', display: 'Timestamp', obligatory: false }] + smsReceipt: [{ code: '#timestamp', display: 'Timestamp', obligatory: false }], } const DEFAULT_MESSAGES = { smsCode: 'Your cryptomat code: #code', cashOutDispenseReady: 'Your cash is waiting! Go to the Cryptomat and press Redeem within 24 hours. [#timestamp]', - smsReceipt: '' + smsReceipt: '', } const SMSNoticesModal = ({ @@ -70,18 +70,18 @@ const SMSNoticesModal = ({ onClose, sms, creationError, - submit + submit, }) => { const initialValues = { event: !R.isNil(sms) ? sms.event : '', - message: !R.isNil(sms) ? sms.message : '' + message: !R.isNil(sms) ? sms.message : '', } const validationSchema = Yup.object().shape({ event: Yup.string().required('An event is required!'), message: PREFILL[sms?.event]?.validator ?? - Yup.string().required('The message content is required!').trim() + Yup.string().required('The message content is required!').trim(), }) const handleSubmit = values => { @@ -90,14 +90,14 @@ const SMSNoticesModal = ({ variables: { id: sms.id, event: values.event, - message: values.message - } + message: values.message, + }, }) : submit({ variables: { event: values.event, - message: values.message - } + message: values.message, + }, }) onClose() } @@ -160,8 +160,8 @@ const SMSNoticesModal = ({ 'message', values.message.concat( R.last(values.message) === ' ' ? '' : ' ', - ite.code - ) + ite.code, + ), ) }} /> diff --git a/packages/admin-ui/src/pages/OperatorInfo/TermsConditions.jsx b/packages/admin-ui/src/pages/OperatorInfo/TermsConditions.jsx index 95dfee1b..0065d74e 100644 --- a/packages/admin-ui/src/pages/OperatorInfo/TermsConditions.jsx +++ b/packages/admin-ui/src/pages/OperatorInfo/TermsConditions.jsx @@ -32,7 +32,7 @@ const Field = ({ }) => { const info3ClassNames = { 'overflow-hidden whitespace-nowrap text-ellipsis h-6': !multiline, - 'wrap-anywhere overflow-y-auto h-32 mt-4 leading-[23px]': multiline + 'wrap-anywhere overflow-y-auto h-32 mt-4 leading-[23px]': multiline, } return ( @@ -88,7 +88,7 @@ const TermsConditions = () => { setEditing(false) }, refetchQueries: () => ['getData'], - onError: e => setError(e) + onError: e => setError(e), }) const { data } = useQuery(GET_CONFIG) @@ -102,7 +102,7 @@ const TermsConditions = () => { const save = it => saveConfig({ - variables: { config: toNamespace(namespaces.TERMS_CONDITIONS, it) } + variables: { config: toNamespace(namespaces.TERMS_CONDITIONS, it) }, }) const fields = [ @@ -110,7 +110,7 @@ const TermsConditions = () => { name: 'title', label: 'Screen title', value: formData.title ?? '', - width: 282 + width: 282, }, { name: 'text', @@ -118,22 +118,22 @@ const TermsConditions = () => { value: formData.text ?? '', width: 502, multiline: true, - rows: 6 + rows: 6, }, { name: 'acceptButtonText', label: 'Accept button text', value: formData.acceptButtonText ?? '', placeholder: 'I accept', - width: 282 + width: 282, }, { name: 'cancelButtonText', label: 'Cancel button text', value: formData.cancelButtonText ?? '', placeholder: 'Cancel', - width: 282 - } + width: 282, + }, ] const findField = name => R.find(R.propEq('name', name))(fields) @@ -143,7 +143,7 @@ const TermsConditions = () => { title: findValue('title'), text: findValue('text'), acceptButtonText: findValue('acceptButtonText'), - cancelButtonText: findValue('cancelButtonText') + cancelButtonText: findValue('cancelButtonText'), } const validationSchema = Yup.object().shape({ @@ -151,14 +151,14 @@ const TermsConditions = () => { .required('The screen title is required') .max(50, 'Too long'), text: Yup.string('The text content must be a string').required( - 'The text content is required' + 'The text content is required', ), acceptButtonText: Yup.string('The accept button text must be a string') .required('The accept button text is required') .max(50, 'The accept button text is too long'), cancelButtonText: Yup.string('The cancel button text must be a string') .required('The cancel button text is required') - .max(50, 'The cancel button text is too long') + .max(50, 'The cancel button text is too long'), }) return ( diff --git a/packages/admin-ui/src/pages/Services/FormRenderer.jsx b/packages/admin-ui/src/pages/Services/FormRenderer.jsx index ca4cbf3b..220faaab 100644 --- a/packages/admin-ui/src/pages/Services/FormRenderer.jsx +++ b/packages/admin-ui/src/pages/Services/FormRenderer.jsx @@ -14,11 +14,10 @@ const FormRenderer = ({ save, buttonLabel = 'Save changes', buttonClass, - xs = 12 }) => { const initialValues = R.compose( R.mergeAll, - R.map(({ code }) => ({ [code]: (value && value[code]) ?? '' })) + R.map(({ code }) => ({ [code]: (value && value[code]) ?? '' })), )(elements) const values = R.merge(initialValues, value) @@ -31,10 +30,10 @@ const FormRenderer = ({ R.filter( elem => R.prop('component', elem) === SecretInput && - R.isEmpty(it[R.prop('code', elem)]) - ) + R.isEmpty(it[R.prop('code', elem)]), + ), )(elements) - return save(R.omit(emptySecretFields, it)).catch(s => { + return save(R.omit(emptySecretFields, it)).catch(() => { setSaveError({ save: 'Failed to save changes' }) }) } @@ -62,7 +61,7 @@ const FormRenderer = ({ fullWidth={true} /> - ) + ), )}
diff --git a/packages/admin-ui/src/pages/Services/Services.jsx b/packages/admin-ui/src/pages/Services/Services.jsx index c5e728f0..aac6085c 100644 --- a/packages/admin-ui/src/pages/Services/Services.jsx +++ b/packages/admin-ui/src/pages/Services/Services.jsx @@ -38,7 +38,7 @@ const Services = () => { const { data: marketsData, loading: marketsLoading } = useQuery(GET_MARKETS) const [saveAccount] = useMutation(SAVE_ACCOUNT, { onCompleted: () => setEditingSchema(null), - refetchQueries: ['getData'] + refetchQueries: ['getData'], }) const markets = marketsData?.getMarkets @@ -52,7 +52,7 @@ const Services = () => { const values = accounts[code] || {} return R.map(({ display, code, long }) => ({ label: display, - value: long ? formatLong(values[code]) : values[code] + value: long ? formatLong(values[code]) : values[code], }))(faceElements) } @@ -75,8 +75,8 @@ const Services = () => { inputProps: { isPasswordFilled: !R.isNil(accounts[code]) && - !R.isNil(R.path([elem.code], accounts[code])) - } + !R.isNil(R.path([elem.code], accounts[code])), + }, } }, elements) } @@ -87,11 +87,11 @@ const Services = () => { const mapToCode = R.map(R.prop(['code'])) const passwordFields = R.compose( mapToCode, - filterBySecretComponent + filterBySecretComponent, )(elements) return R.mapObjIndexed( (value, key) => (R.includes(key, passwordFields) ? '' : value), - account + account, ) } @@ -124,7 +124,7 @@ const Services = () => { saveAccount({ - variables: { accounts: { [editingSchema.code]: it } } + variables: { accounts: { [editingSchema.code]: it } }, }) } elements={getElements(editingSchema)} diff --git a/packages/admin-ui/src/pages/Services/schemas/binance.js b/packages/admin-ui/src/pages/Services/schemas/binance.js index faec0e35..4182a6e4 100644 --- a/packages/admin-ui/src/pages/Services/schemas/binance.js +++ b/packages/admin-ui/src/pages/Services/schemas/binance.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest, buildCurrencyOptions } from './helper' @@ -19,12 +19,12 @@ const schema = markets => { display: 'API key', component: TextInput, face: true, - long: true + long: true, }, { code: 'privateKey', display: 'Private key', - component: SecretInput + component: SecretInput, }, { code: 'currencyMarket', @@ -33,10 +33,10 @@ const schema = markets => { inputProps: { options: buildCurrencyOptions(markets), labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -47,10 +47,10 @@ const schema = markets => { .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') + 'The currency market must be a string', + ).required('The currency market is required'), }) - } + }, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/binanceus.js b/packages/admin-ui/src/pages/Services/schemas/binanceus.js index 74795e24..708fc593 100644 --- a/packages/admin-ui/src/pages/Services/schemas/binanceus.js +++ b/packages/admin-ui/src/pages/Services/schemas/binanceus.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest, buildCurrencyOptions } from './helper' @@ -19,12 +19,12 @@ const schema = markets => { display: 'API key', component: TextInput, face: true, - long: true + long: true, }, { code: 'privateKey', display: 'Private key', - component: SecretInput + component: SecretInput, }, { code: 'currencyMarket', @@ -33,10 +33,10 @@ const schema = markets => { inputProps: { options: buildCurrencyOptions(markets), labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -47,10 +47,10 @@ const schema = markets => { .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') + 'The currency market must be a string', + ).required('The currency market is required'), }) - } + }, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/bitfinex.js b/packages/admin-ui/src/pages/Services/schemas/bitfinex.js index c0485af1..67563406 100644 --- a/packages/admin-ui/src/pages/Services/schemas/bitfinex.js +++ b/packages/admin-ui/src/pages/Services/schemas/bitfinex.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest, buildCurrencyOptions } from './helper' @@ -19,12 +19,12 @@ const schema = markets => { display: 'API key', component: TextInput, face: true, - long: true + long: true, }, { code: 'secret', display: 'API secret', - component: SecretInput + component: SecretInput, }, { code: 'currencyMarket', @@ -33,10 +33,10 @@ const schema = markets => { inputProps: { options: buildCurrencyOptions(markets), labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -47,10 +47,10 @@ const schema = markets => { .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') + 'The currency market must be a string', + ).required('The currency market is required'), }) - } + }, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/bitgo.js b/packages/admin-ui/src/pages/Services/schemas/bitgo.js index 07c2705b..8185701d 100644 --- a/packages/admin-ui/src/pages/Services/schemas/bitgo.js +++ b/packages/admin-ui/src/pages/Services/schemas/bitgo.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { TextInput, SecretInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest } from './helper' @@ -15,7 +15,7 @@ const buildTestValidation = (id, passphrase) => { .max(100, 'Too long') .when(id, { is: isDefined, - then: schema => schema.test(secretTest(passphrase)) + then: schema => schema.test(secretTest(passphrase)), }) } @@ -29,7 +29,7 @@ export default { display: 'API token', component: TextInput, face: true, - long: true + long: true, }, { code: 'environment', @@ -38,63 +38,63 @@ export default { inputProps: { options: [ { code: 'prod', display: 'prod' }, - { code: 'test', display: 'test' } + { code: 'test', display: 'test' }, ], labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true + face: true, }, { code: 'BTCWalletId', display: 'BTC wallet ID', - component: TextInput + component: TextInput, }, { code: 'BTCWalletPassphrase', display: 'BTC wallet passphrase', - component: SecretInput + component: SecretInput, }, { code: 'LTCWalletId', display: 'LTC wallet ID', - component: TextInput + component: TextInput, }, { code: 'LTCWalletPassphrase', display: 'LTC wallet passphrase', - component: SecretInput + component: SecretInput, }, { code: 'ZECWalletId', display: 'ZEC wallet ID', - component: TextInput + component: TextInput, }, { code: 'ZECWalletPassphrase', display: 'ZEC wallet passphrase', - component: SecretInput + component: SecretInput, }, { code: 'BCHWalletId', display: 'BCH wallet ID', - component: TextInput + component: TextInput, }, { code: 'BCHWalletPassphrase', display: 'BCH wallet passphrase', - component: SecretInput + component: SecretInput, }, { code: 'DASHWalletId', display: 'DASH wallet ID', - component: TextInput + component: TextInput, }, { code: 'DASHWalletPassphrase', display: 'DASH wallet passphrase', - component: SecretInput - } + component: SecretInput, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -103,47 +103,47 @@ export default { .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' + 'The BTC wallet ID is too long', ), BTCWalletPassphrase: buildTestValidation( 'BTCWalletId', - account?.BTCWalletPassphrase + account?.BTCWalletPassphrase, ), LTCWalletId: Yup.string('The LTC wallet ID must be a string').max( 100, - 'The LTC wallet ID is too long' + 'The LTC wallet ID is too long', ), LTCWalletPassphrase: buildTestValidation( 'LTCWalletId', - account?.LTCWalletPassphrase + account?.LTCWalletPassphrase, ), ZECWalletId: Yup.string('The ZEC wallet ID must be a string').max( 100, - 'The ZEC wallet ID is too long' + 'The ZEC wallet ID is too long', ), ZECWalletPassphrase: buildTestValidation( 'ZECWalletId', - account?.ZECWalletPassphrase + account?.ZECWalletPassphrase, ), BCHWalletId: Yup.string('The BCH wallet ID must be a string').max( 100, - 'The BCH wallet ID is too long' + 'The BCH wallet ID is too long', ), BCHWalletPassphrase: buildTestValidation( 'BCHWalletId', - account?.BCHWalletPassphrase + account?.BCHWalletPassphrase, ), DASHWalletId: Yup.string('The DASH wallet ID must be a string').max( 100, - 'The DASH wallet ID is too long' + 'The DASH wallet ID is too long', ), DASHWalletPassphrase: buildTestValidation( 'DASHWalletId', - account?.DASHWalletPassphrase + account?.DASHWalletPassphrase, ), environment: Yup.string('The environment must be a string') .matches(/(prod|test)/) - .required('The environment is required') - }); - } + .required('The environment is required'), + }) + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/bitstamp.js b/packages/admin-ui/src/pages/Services/schemas/bitstamp.js index e9061e9e..d2664809 100644 --- a/packages/admin-ui/src/pages/Services/schemas/bitstamp.js +++ b/packages/admin-ui/src/pages/Services/schemas/bitstamp.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest, buildCurrencyOptions } from './helper' @@ -19,19 +19,19 @@ const schema = markets => { display: 'Client ID', component: TextInput, face: true, - long: true + long: true, }, { code: 'key', display: 'API key', component: TextInput, face: true, - long: true + long: true, }, { code: 'secret', display: 'API secret', - component: SecretInput + component: SecretInput, }, { code: 'currencyMarket', @@ -40,10 +40,10 @@ const schema = markets => { inputProps: { options: buildCurrencyOptions(markets), labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -57,10 +57,10 @@ const schema = markets => { .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') + 'The currency market must be a string', + ).required('The currency market is required'), }) - } + }, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/blockcypher.js b/packages/admin-ui/src/pages/Services/schemas/blockcypher.js index d0875577..564127bb 100644 --- a/packages/admin-ui/src/pages/Services/schemas/blockcypher.js +++ b/packages/admin-ui/src/pages/Services/schemas/blockcypher.js @@ -12,13 +12,13 @@ export default { display: 'API token', component: TextInput, face: true, - long: true + long: true, }, { code: 'confidenceFactor', display: 'Confidence factor', component: NumberInput, - face: true + face: true, }, { code: 'rbf', @@ -30,10 +30,10 @@ export default { 'Lower the confidence of RBF transactions (Available when using bitcoind.)', label: 'Lower the confidence of RBF transactions', requirement: 'bitcoind', - rightSideLabel: true + rightSideLabel: true, }, - face: true - } + face: true, + }, ], getValidationSchema: () => { return Yup.object().shape({ @@ -44,7 +44,7 @@ export default { .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') + .required('The confidence factor is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/cex.js b/packages/admin-ui/src/pages/Services/schemas/cex.js index b887db93..25488088 100644 --- a/packages/admin-ui/src/pages/Services/schemas/cex.js +++ b/packages/admin-ui/src/pages/Services/schemas/cex.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest, buildCurrencyOptions } from './helper' @@ -19,19 +19,19 @@ const schema = markets => { display: 'API key', component: TextInput, face: true, - long: true + long: true, }, { code: 'uid', display: 'User ID', component: TextInput, face: true, - long: true + long: true, }, { code: 'privateKey', display: 'Private key', - component: SecretInput + component: SecretInput, }, { code: 'currencyMarket', @@ -40,10 +40,10 @@ const schema = markets => { inputProps: { options: buildCurrencyOptions(markets), labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -57,10 +57,10 @@ const schema = markets => { .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') + 'The currency market must be a string', + ).required('The currency market is required'), }) - } + }, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/elliptic.js b/packages/admin-ui/src/pages/Services/schemas/elliptic.js index 41d0d810..acb943f8 100644 --- a/packages/admin-ui/src/pages/Services/schemas/elliptic.js +++ b/packages/admin-ui/src/pages/Services/schemas/elliptic.js @@ -13,19 +13,19 @@ export default { { code: 'apiKey', display: 'API Key', - component: SecretInputFormik + component: SecretInputFormik, }, { code: 'apiSecret', display: 'API Secret', - component: SecretInputFormik + component: SecretInputFormik, }, { code: 'scoreThreshold', display: 'Score threshold', component: NumberInputFormik, face: true, - long: false + long: false, }, { code: 'enabled', @@ -35,10 +35,10 @@ export default { disabledMessage: 'This plugin is disabled', label: 'Enabled', requirement: null, - rightSideLabel: true + rightSideLabel: true, }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -56,8 +56,8 @@ export default { .test( 'no-leading-zeros', 'The score threshold must not have leading zeros', - leadingZerosTest - ) + leadingZerosTest, + ), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/galoy.js b/packages/admin-ui/src/pages/Services/schemas/galoy.js index d98a07f4..a0fc2727 100644 --- a/packages/admin-ui/src/pages/Services/schemas/galoy.js +++ b/packages/admin-ui/src/pages/Services/schemas/galoy.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest } from './helper' @@ -16,7 +16,7 @@ export default { { code: 'apiSecret', display: 'API Secret', - component: SecretInput + component: SecretInput, }, { code: 'environment', @@ -25,23 +25,23 @@ export default { inputProps: { options: [ { code: 'main', display: 'prod' }, - { code: 'test', display: 'test' } + { code: 'test', display: 'test' }, ], labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true + face: true, }, { code: 'endpoint', display: 'Endpoint', - component: TextInput + component: TextInput, }, { code: 'walletId', display: 'Wallet ID', - component: SecretInput - } + component: SecretInput, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -56,7 +56,7 @@ export default { .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') - }); - } + .required('The endpoint is required'), + }) + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/helper.js b/packages/admin-ui/src/pages/Services/schemas/helper.js index f4be746c..65347f36 100644 --- a/packages/admin-ui/src/pages/Services/schemas/helper.js +++ b/packages/admin-ui/src/pages/Services/schemas/helper.js @@ -5,7 +5,7 @@ import * as R from 'ramda' const WARNING_LEVELS = { CLEAN: 'clean', PARTIAL: 'partial', - IMPORTANT: 'important' + IMPORTANT: 'important', } const secretTest = (secret, message) => ({ @@ -16,7 +16,7 @@ const secretTest = (secret, message) => ({ return this.createError() } return true - } + }, }) const leadingZerosTest = (value, context) => { @@ -39,7 +39,7 @@ const buildCurrencyOptions = markets => { R.length(unavailableCryptosFiltered) > 1 ? `${R.join( ', ', - R.slice(0, -1, unavailableCryptosFiltered) + R.slice(0, -1, unavailableCryptosFiltered), )} and ${R.last(unavailableCryptosFiltered)}` : unavailableCryptosFiltered[0] @@ -56,7 +56,7 @@ const buildCurrencyOptions = markets => { warning: warningLevel, warningMessage: !R.isEmpty(unavailableCryptosFiltered) ? `No market pairs available for ${unavailableMarketsStr}` - : `All market pairs are available` + : `All market pairs are available`, } }, R.keys(markets)) } diff --git a/packages/admin-ui/src/pages/Services/schemas/index.js b/packages/admin-ui/src/pages/Services/schemas/index.js index e952771b..695aa598 100644 --- a/packages/admin-ui/src/pages/Services/schemas/index.js +++ b/packages/admin-ui/src/pages/Services/schemas/index.js @@ -48,7 +48,7 @@ const schemas = (markets = {}) => { [trongrid.code]: trongrid, [binance.code]: binance, [bitfinex.code]: bitfinex, - [sumsub.code]: sumsub + [sumsub.code]: sumsub, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/inforu.js b/packages/admin-ui/src/pages/Services/schemas/inforu.js index a334afe6..3dbe292f 100644 --- a/packages/admin-ui/src/pages/Services/schemas/inforu.js +++ b/packages/admin-ui/src/pages/Services/schemas/inforu.js @@ -13,25 +13,25 @@ export default { code: 'username', display: 'InforU username', component: TextInputFormik, - face: true + face: true, }, { code: 'apiKey', display: 'API Key', - component: SecretInputFormik + component: SecretInputFormik, }, { code: 'fromNumber', display: 'InforU sender', component: TextInputFormik, - face: true + face: true, }, { code: 'toNumber', display: 'Notifications Number (international format)', component: TextInputFormik, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -46,7 +46,7 @@ export default { .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') + .required('The notifications number is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/infura.js b/packages/admin-ui/src/pages/Services/schemas/infura.js index 8a18752b..87d3895d 100644 --- a/packages/admin-ui/src/pages/Services/schemas/infura.js +++ b/packages/admin-ui/src/pages/Services/schemas/infura.js @@ -10,14 +10,14 @@ export default { code: 'endpoint', display: 'Endpoint', component: TextInputFormik, - face: true - } + 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') + .required('The endpoint is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/itbit.js b/packages/admin-ui/src/pages/Services/schemas/itbit.js index d6607461..d679af1b 100644 --- a/packages/admin-ui/src/pages/Services/schemas/itbit.js +++ b/packages/admin-ui/src/pages/Services/schemas/itbit.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { buildCurrencyOptions, secretTest } from './helper' @@ -19,24 +19,24 @@ const schema = markets => { display: 'User ID', component: TextInput, face: true, - long: true + long: true, }, { code: 'walletId', display: 'Wallet ID', component: TextInput, face: true, - long: true + long: true, }, { code: 'clientKey', display: 'Client key', - component: TextInput + component: TextInput, }, { code: 'clientSecret', display: 'Client secret', - component: SecretInput + component: SecretInput, }, { code: 'currencyMarket', @@ -45,10 +45,10 @@ const schema = markets => { inputProps: { options: buildCurrencyOptions(markets), labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -65,10 +65,10 @@ const schema = markets => { .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') + 'The currency market must be a string', + ).required('The currency market is required'), }) - } + }, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/kraken.js b/packages/admin-ui/src/pages/Services/schemas/kraken.js index 2c0ee271..0ad9f12f 100644 --- a/packages/admin-ui/src/pages/Services/schemas/kraken.js +++ b/packages/admin-ui/src/pages/Services/schemas/kraken.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { SecretInput, TextInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' import { secretTest, buildCurrencyOptions } from './helper' @@ -19,12 +19,12 @@ const schema = markets => { display: 'API key', component: TextInput, face: true, - long: true + long: true, }, { code: 'privateKey', display: 'Private key', - component: SecretInput + component: SecretInput, }, { code: 'currencyMarket', @@ -33,10 +33,10 @@ const schema = markets => { inputProps: { options: buildCurrencyOptions(markets), labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -47,10 +47,10 @@ const schema = markets => { .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') + 'The currency market must be a string', + ).required('The currency market is required'), }) - } + }, } } diff --git a/packages/admin-ui/src/pages/Services/schemas/mailgun.js b/packages/admin-ui/src/pages/Services/schemas/mailgun.js index b01b84b7..c00f69a0 100644 --- a/packages/admin-ui/src/pages/Services/schemas/mailgun.js +++ b/packages/admin-ui/src/pages/Services/schemas/mailgun.js @@ -9,25 +9,25 @@ export default { { code: 'apiKey', display: 'API key', - component: TextInputFormik + component: TextInputFormik, }, { code: 'domain', display: 'Domain', - component: TextInputFormik + component: TextInputFormik, }, { code: 'fromEmail', display: 'From email', component: TextInputFormik, - face: true + face: true, }, { code: 'toEmail', display: 'To email', component: TextInputFormik, - face: true - } + face: true, + }, ], getValidationSchema: () => { return Yup.object().shape({ @@ -44,7 +44,7 @@ export default { 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') + .required('The to email is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/scorechain.js b/packages/admin-ui/src/pages/Services/schemas/scorechain.js index 2ee4eb97..ac1c011c 100644 --- a/packages/admin-ui/src/pages/Services/schemas/scorechain.js +++ b/packages/admin-ui/src/pages/Services/schemas/scorechain.js @@ -13,14 +13,14 @@ export default { { code: 'apiKey', display: 'API Key', - component: SecretInputFormik + component: SecretInputFormik, }, { code: 'scoreThreshold', display: 'Score threshold', component: NumberInputFormik, face: true, - long: false + long: false, }, { code: 'enabled', @@ -30,10 +30,10 @@ export default { disabledMessage: 'This plugin is disabled', label: 'Enabled', requirement: null, - rightSideLabel: true + rightSideLabel: true, }, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -48,8 +48,8 @@ export default { .test( 'no-leading-zeros', 'The score threshold must not have leading zeros', - leadingZerosTest - ) + leadingZerosTest, + ), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/singlebitgo.js b/packages/admin-ui/src/pages/Services/schemas/singlebitgo.js index 9e206632..de47811b 100644 --- a/packages/admin-ui/src/pages/Services/schemas/singlebitgo.js +++ b/packages/admin-ui/src/pages/Services/schemas/singlebitgo.js @@ -3,7 +3,7 @@ import * as Yup from 'yup' import { TextInput, SecretInput, - Autocomplete + Autocomplete, } from 'src/components/inputs/formik' const singleBitgo = code => ({ @@ -16,7 +16,7 @@ const singleBitgo = code => ({ display: 'API token', component: TextInput, face: true, - long: true + long: true, }, { code: 'environment', @@ -25,23 +25,23 @@ const singleBitgo = code => ({ inputProps: { options: [ { code: 'prod', display: 'prod' }, - { code: 'test', display: 'test' } + { code: 'test', display: 'test' }, ], labelProp: 'display', - valueProp: 'code' + valueProp: 'code', }, - face: true + face: true, }, { code: `${code}WalletId`, display: `${code} wallet ID`, - component: TextInput + component: TextInput, }, { code: `${code}WalletPassphrase`, display: `${code} wallet passphrase`, - component: SecretInput - } + component: SecretInput, + }, ], validationSchema: Yup.object().shape({ token: Yup.string('The token must be a string') @@ -54,11 +54,11 @@ const singleBitgo = code => ({ .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` + `The ${code} passphrase must be a string`, ) .max(100, `The ${code} wallet passphrase is too long`) - .required(`The ${code} wallet passphrase is required`) - }) + .required(`The ${code} wallet passphrase is required`), + }), }) export default singleBitgo diff --git a/packages/admin-ui/src/pages/Services/schemas/sumsub.js b/packages/admin-ui/src/pages/Services/schemas/sumsub.js index ee156a9e..b4f13c5d 100644 --- a/packages/admin-ui/src/pages/Services/schemas/sumsub.js +++ b/packages/admin-ui/src/pages/Services/schemas/sumsub.js @@ -12,19 +12,19 @@ const schema = { { code: 'apiToken', display: 'API Token', - component: SecretInput + component: SecretInput, }, { code: 'secretKey', display: 'Secret Key', - component: SecretInput + component: SecretInput, }, { code: 'applicantLevel', display: 'Applicant Level', component: TextInput, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -36,9 +36,9 @@ const schema = { .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') + .required('The applicant level is required'), }) - } + }, } export default schema diff --git a/packages/admin-ui/src/pages/Services/schemas/telnyx.js b/packages/admin-ui/src/pages/Services/schemas/telnyx.js index ce11ed48..b33b8c7e 100644 --- a/packages/admin-ui/src/pages/Services/schemas/telnyx.js +++ b/packages/admin-ui/src/pages/Services/schemas/telnyx.js @@ -12,20 +12,20 @@ export default { { code: 'apiKey', display: 'API Key', - component: SecretInputFormik + component: SecretInputFormik, }, { code: 'fromNumber', display: 'Telnyx Number (international format)', component: TextInputFormik, - face: true + face: true, }, { code: 'toNumber', display: 'Notifications Number (international format)', component: TextInputFormik, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -37,7 +37,7 @@ export default { .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') + .required('The notifications number is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/trongrid.js b/packages/admin-ui/src/pages/Services/schemas/trongrid.js index affc3989..4d6bb9ea 100644 --- a/packages/admin-ui/src/pages/Services/schemas/trongrid.js +++ b/packages/admin-ui/src/pages/Services/schemas/trongrid.js @@ -11,14 +11,14 @@ export default { display: 'API Key', component: TextInputFormik, face: true, - long: true - } + long: true, + }, ], - getValidationSchema: account => { + getValidationSchema: () => { 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') + .required('The project ID is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/twilio.js b/packages/admin-ui/src/pages/Services/schemas/twilio.js index 756287a3..d22d51be 100644 --- a/packages/admin-ui/src/pages/Services/schemas/twilio.js +++ b/packages/admin-ui/src/pages/Services/schemas/twilio.js @@ -12,25 +12,25 @@ export default { { code: 'accountSid', display: 'Account SID', - component: TextInputFormik + component: TextInputFormik, }, { code: 'authToken', display: 'Auth token', - component: SecretInputFormik + component: SecretInputFormik, }, { code: 'fromNumber', display: 'Twilio number (international format)', component: TextInputFormik, - face: true + face: true, }, { code: 'toNumber', display: 'Notifications number (international format)', component: TextInputFormik, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -45,7 +45,7 @@ export default { .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') + .required('The notifications number is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/Services/schemas/vonage.js b/packages/admin-ui/src/pages/Services/schemas/vonage.js index fb95023a..6aaeeee1 100644 --- a/packages/admin-ui/src/pages/Services/schemas/vonage.js +++ b/packages/admin-ui/src/pages/Services/schemas/vonage.js @@ -12,25 +12,25 @@ export default { { code: 'apiKey', display: 'API Key', - component: TextInputFormik + component: TextInputFormik, }, { code: 'apiSecret', display: 'API Secret', - component: SecretInputFormik + component: SecretInputFormik, }, { code: 'fromNumber', display: 'Vonage Number (international format)', component: TextInputFormik, - face: true + face: true, }, { code: 'toNumber', display: 'Notifications Number (international format)', component: TextInputFormik, - face: true - } + face: true, + }, ], getValidationSchema: account => { return Yup.object().shape({ @@ -45,7 +45,7 @@ export default { .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') + .required('The notifications number is required'), }) - } + }, } diff --git a/packages/admin-ui/src/pages/SessionManagement/SessionManagement.jsx b/packages/admin-ui/src/pages/SessionManagement/SessionManagement.jsx index ce108590..ec64df8e 100644 --- a/packages/admin-ui/src/pages/SessionManagement/SessionManagement.jsx +++ b/packages/admin-ui/src/pages/SessionManagement/SessionManagement.jsx @@ -42,7 +42,7 @@ const SessionManagement = () => { const { data: tknResponse, loading: sessionsLoading } = useQuery(GET_SESSIONS) const [deleteSession] = useMutation(DELETE_SESSION, { - refetchQueries: () => ['sessions'] + refetchQueries: () => ['sessions'], }) const { data: configResponse, loading: configLoading } = useQuery(GET_DATA) @@ -56,7 +56,7 @@ const SessionManagement = () => { width: 207, textAlign: 'left', size: 'sm', - view: s => s.sess.user.username + view: s => s.sess.user.username, }, { header: 'Last known use', @@ -67,7 +67,7 @@ const SessionManagement = () => { if (R.isNil(s.sess.ua)) return 'No Record' const ua = parser(s.sess.ua) return `${ua.browser.name} ${ua.browser.version} on ${ua.os.name} ${ua.os.version}` - } + }, }, { header: 'Last known location', @@ -76,7 +76,7 @@ const SessionManagement = () => { size: 'sm', view: s => { return isLocalhost(s.sess.ipAddress) ? 'This device' : s.sess.ipAddress - } + }, }, { header: 'Expiration date', @@ -87,8 +87,8 @@ const SessionManagement = () => { `${formatDate(s.expire, timezone, 'yyyy-MM-dd')} ${formatDate( s.expire, timezone, - 'HH:mm:ss' - )}` + 'HH:mm:ss', + )}`, }, { header: '', @@ -104,8 +104,8 @@ const SessionManagement = () => { - ) - } + ), + }, ] return ( diff --git a/packages/admin-ui/src/pages/Transactions/DetailsCard.jsx b/packages/admin-ui/src/pages/Transactions/DetailsCard.jsx index e07ec7ab..cf3799aa 100644 --- a/packages/admin-ui/src/pages/Transactions/DetailsCard.jsx +++ b/packages/admin-ui/src/pages/Transactions/DetailsCard.jsx @@ -28,7 +28,7 @@ import { primaryColor, subheaderColor, errorColor, - offErrorColor + offErrorColor, } from 'src/styling/variables' import { SWEEPABLE_CRYPTOS } from 'src/utils/constants' import * as Customer from 'src/utils/customer' @@ -113,7 +113,7 @@ const DetailsRow = ({ it: tx, timezone }) => { const zip = new JSZip() const [fetchSummary] = useLazyQuery(TX_SUMMARY, { - onCompleted: data => createCsv(R.filter(it => !R.isEmpty(it), data)) + onCompleted: data => createCsv(R.filter(it => !R.isEmpty(it), data)), }) const [cancelTransaction] = useMutation( @@ -121,13 +121,13 @@ const DetailsRow = ({ it: tx, timezone }) => { { onError: ({ message }) => setErrorMessage(message ?? 'An error occurred.'), - refetchQueries: () => ['transactions'] - } + refetchQueries: () => ['transactions'], + }, ) const commission = BigNumber(tx.profit).toFixed(2, 1) // ROUND_DOWN const commissionPercentage = BigNumber( - Number.parseFloat(tx.commissionPercentage, 2) * 100 + Number.parseFloat(tx.commissionPercentage, 2) * 100, ).toFixed(2, 1) // ROUND_DOWN const fixedFee = Number.parseFloat(tx.fixedFee) || 0 const fiat = BigNumber(tx.fiat).minus(fixedFee).toFixed(2, 1) // ROUND_DOWN @@ -145,7 +145,7 @@ const DetailsRow = ({ it: tx, timezone }) => { (tx.customerIdCardData.dateOfBirth && differenceInYears( parseDateString(tx.customerIdCardData.dateOfBirth), - new Date() + new Date(), )) ?? '', country: tx.customerIdCardData.country, @@ -153,9 +153,9 @@ const DetailsRow = ({ it: tx, timezone }) => { idCardExpirationDate: (tx.customerIdCardData.expirationDate && format('yyyy-MM-dd')( - parseDateString(tx.customerIdCardData.expirationDate) + parseDateString(tx.customerIdCardData.expirationDate), )) ?? - '' + '', } const from = sub({ minutes: MINUTES_OFFSET }, new Date(tx.created)) @@ -163,7 +163,7 @@ const DetailsRow = ({ it: tx, timezone }) => { const downloadRawLogs = ({ id: txId, deviceId, txClass }, timezone) => { fetchSummary({ - variables: { txId, from, until, deviceId, txClass, timezone } + variables: { txId, from, until, deviceId, txClass, timezone }, }) } @@ -220,7 +220,7 @@ const DetailsRow = ({ it: tx, timezone }) => { noMargin className={classNames({ 'font-bold ml-1': true, - 'text-tomato': hasChainAnalysisError(tx) + 'text-tomato': hasChainAnalysisError(tx), })}> {tx.walletScore}

@@ -396,7 +396,7 @@ const DetailsRow = ({ it: tx, timezone }) => { className="w-40" onClick={() => setAction({ - command: 'cancelTx' + command: 'cancelTx', }) }> Cancel transaction @@ -434,8 +434,8 @@ const DetailsRow = ({ it: tx, timezone }) => { setAction({ command: null }) cancelTransaction({ variables: { - id: tx.id - } + id: tx.id, + }, }) }} onDismissed={() => { @@ -453,5 +453,5 @@ export default memo( prev.it.id === next.it.id && prev.it.hasError === next.it.hasError && prev.it.batchError === next.it.batchError && - getStatus(prev.it) === getStatus(next.it) + getStatus(prev.it) === getStatus(next.it), ) diff --git a/packages/admin-ui/src/pages/Transactions/Transactions.jsx b/packages/admin-ui/src/pages/Transactions/Transactions.jsx index daaad78b..f385231a 100644 --- a/packages/admin-ui/src/pages/Transactions/Transactions.jsx +++ b/packages/admin-ui/src/pages/Transactions/Transactions.jsx @@ -138,7 +138,7 @@ const Transactions = () => { const [filters, setFilters] = useState([]) const { data: filtersResponse, loading: filtersLoading } = useQuery( - GET_TRANSACTION_FILTERS + GET_TRANSACTION_FILTERS, ) const [variables, setVariables] = useState({ limit: NUM_LOG_RESULTS }) const { @@ -146,7 +146,7 @@ const Transactions = () => { loading: transactionsLoading, refetch, startPolling, - stopPolling + stopPolling, } = useQuery(GET_TRANSACTIONS, { variables }) useEffect(() => { @@ -168,14 +168,14 @@ const Transactions = () => { header: '', width: 32, size: 'sm', - view: it => (it.txClass === 'cashOut' ? : ) + view: it => (it.txClass === 'cashOut' ? : ), }, { header: 'Machine', name: 'machineName', width: 160, size: 'sm', - view: R.path(['machineName']) + view: R.path(['machineName']), }, { header: 'Customer', @@ -198,14 +198,14 @@ const Transactions = () => {
)} - ) + ), }, { header: 'Cash', width: 144, textAlign: 'right', size: 'sm', - view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}` + view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}`, }, { header: 'Crypto', @@ -215,14 +215,14 @@ const Transactions = () => { view: it => `${toUnit(new BigNumber(it.cryptoAtoms), it.cryptoCode)} ${ it.cryptoCode - }` + }`, }, { header: 'Address', view: it => formatCryptoAddress(it.cryptoCode, it.toAddress), className: 'overflow-hidden whitespace-nowrap text-ellipsis', size: 'sm', - width: 140 + width: 140, }, { header: 'Date', @@ -230,7 +230,7 @@ const Transactions = () => { timezone && formatDate(it.created, timezone, 'yyyy-MM-dd HH:mm'), textAlign: 'right', size: 'sm', - width: 195 + width: 195, }, { header: 'Status', @@ -252,8 +252,8 @@ const Transactions = () => { }, textAlign: 'left', size: 'sm', - width: 80 - } + width: 80, + }, ] const onFilterChange = filters => { @@ -270,7 +270,7 @@ const Transactions = () => { cryptoCode: filtersObject.crypto, toAddress: filtersObject.address, status: filtersObject.status, - swept: filtersObject.swept && filtersObject.swept === 'Swept' + swept: filtersObject.swept && filtersObject.swept === 'Swept', }) refetch && refetch() @@ -278,7 +278,7 @@ const Transactions = () => { const onFilterDelete = filter => { const newFilters = R.filter( - f => !R.whereEq(R.pick(['type', 'value'], f), filter) + f => !R.whereEq(R.pick(['type', 'value'], f), filter), )(filters) setFilters(newFilters) @@ -294,7 +294,7 @@ const Transactions = () => { cryptoCode: filtersObject.crypto, toAddress: filtersObject.address, status: filtersObject.status, - swept: filtersObject.swept && filtersObject.swept === 'Swept' + swept: filtersObject.swept && filtersObject.swept === 'Swept', }) refetch && refetch() @@ -313,7 +313,7 @@ const Transactions = () => { cryptoCode: filtersObject.crypto, toAddress: filtersObject.address, status: filtersObject.status, - swept: filtersObject.swept && filtersObject.swept === 'Swept' + swept: filtersObject.swept && filtersObject.swept === 'Swept', }) refetch && refetch() @@ -336,7 +336,7 @@ const Transactions = () => { labels={[ { icon: , label: 'Cash-in' }, { icon: , label: 'Cash-out' }, - { icon: errorLabel, label: 'Transaction error' } + { icon: errorLabel, label: 'Transaction error' }, ]} appendix={
diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/CustomInfoRequests.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/CustomInfoRequests.jsx index 0eb40c6a..260ef1e9 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/CustomInfoRequests.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/CustomInfoRequests.jsx @@ -18,7 +18,7 @@ import SvgIcon from '@mui/material/SvgIcon' const inputTypeDisplay = { numerical: 'Numerical', text: 'Text', - choiceList: 'Choice list' + choiceList: 'Choice list', } const constraintTypeDisplay = { @@ -28,7 +28,7 @@ const constraintTypeDisplay = { length: 'Length', selectOne: 'Select one', selectMultiple: 'Select multiple', - spaceSeparation: 'Space separation' + spaceSeparation: 'Space separation', } const GET_DATA = gql` @@ -72,7 +72,7 @@ const REMOVE_ROW = gql` const CustomInfoRequests = ({ showWizard, toggleWizard, - data: customRequests + data: customRequests, }) => { const [toBeDeleted, setToBeDeleted] = useState() const [toBeEdited, setToBeEdited] = useState() @@ -83,7 +83,7 @@ const CustomInfoRequests = ({ const [saveConfig] = useMutation(SAVE_CONFIG, { refetchQueries: () => ['getData'], - onError: () => setHasError(true) + onError: () => setHasError(true), }) const [addEntry] = useMutation(ADD_ROW, { @@ -95,7 +95,7 @@ const CustomInfoRequests = ({ setHasError(false) toggleWizard() }, - refetchQueries: () => ['customInfoRequests'] + refetchQueries: () => ['customInfoRequests'], }) const [editEntry] = useMutation(EDIT_ROW, { @@ -108,7 +108,7 @@ const CustomInfoRequests = ({ setToBeEdited(null) toggleWizard() }, - refetchQueries: () => ['getData', 'customInfoRequests'] + refetchQueries: () => ['getData', 'customInfoRequests'], }) const [removeEntry] = useMutation(REMOVE_ROW, { @@ -120,7 +120,7 @@ const CustomInfoRequests = ({ setDeleteDialog(false) setHasError(false) }, - refetchQueries: () => ['getData', 'customInfoRequests'] + refetchQueries: () => ['getData', 'customInfoRequests'], }) const config = R.path(['config'])(configData) ?? [] @@ -128,16 +128,16 @@ const CustomInfoRequests = ({ const handleDelete = id => { removeEntry({ variables: { - id - } + id, + }, }).then(() => { const triggersConfig = (config && fromNamespace(namespaces.TRIGGERS)(config)) ?? [] const cleanConfig = { overrides: R.reject( it => it.requirement === id, - triggersConfig.overrides - ) + triggersConfig.overrides, + ), } const newConfig = toNamespace(namespaces.TRIGGERS)(cleanConfig) saveConfig({ variables: { config: newConfig } }) @@ -149,16 +149,16 @@ const CustomInfoRequests = ({ return editEntry({ variables: { id: values.id, - customRequest: R.omit(['id'])(values) - } + customRequest: R.omit(['id'])(values), + }, }) } return addEntry({ variables: { customRequest: { - ...values - } - } + ...values, + }, + }, }) } @@ -186,14 +186,14 @@ const CustomInfoRequests = ({ width: 300, textAlign: 'left', size: 'sm', - view: it => it.customRequest.name + view: it => it.customRequest.name, }, { header: 'Data entry type', width: 300, textAlign: 'left', size: 'sm', - view: it => inputTypeDisplay[it.customRequest.input.type] + view: it => inputTypeDisplay[it.customRequest.input.type], }, { header: 'Constraints', @@ -201,7 +201,7 @@ const CustomInfoRequests = ({ textAlign: 'left', size: 'sm', view: it => - constraintTypeDisplay[it.customRequest.input.constraintType] + constraintTypeDisplay[it.customRequest.input.constraintType], }, { header: 'Edit', @@ -220,7 +220,7 @@ const CustomInfoRequests = ({ ) - } + }, }, { header: 'Delete', @@ -239,8 +239,8 @@ const CustomInfoRequests = ({ ) - } - } + }, + }, ]} data={customRequests} Details={DetailsRow} diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/ChooseType.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/ChooseType.jsx index c70b732c..8a52f818 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/ChooseType.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/ChooseType.jsx @@ -20,7 +20,7 @@ const MakeIcon = IconSvg => ( maxWidth: 104, maxHeight: 64, minWidth: 104, - minHeight: 64 + minHeight: 64, }}>
@@ -33,21 +33,21 @@ const ChooseType = () => { title: 'Numerical entry', description: 'User will enter information with a keypad. Good for dates, ID numbers, etc.', - icon: () => MakeIcon(Keypad) + icon: () => MakeIcon(Keypad), }, { value: 'text', title: 'Text entry', description: 'User will entry information with a keyboard. Good for names, email, address, etc.', - icon: () => MakeIcon(Keyboard) + icon: () => MakeIcon(Keyboard), }, { value: 'choiceList', title: 'Choice list', description: 'Gives user multiple options to choose from.', - icon: () => MakeIcon(List) - } + icon: () => MakeIcon(List), + }, ] return ( @@ -65,11 +65,11 @@ const ChooseType = () => { } const validationSchema = Yup.object().shape({ - inputType: Yup.string().label('Input type').required() + inputType: Yup.string().label('Input type').required(), }) const defaultValues = { - inputType: '' + inputType: '', } export default ChooseType diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/NameOfRequirement.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/NameOfRequirement.jsx index 1f0b9b14..70f95c64 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/NameOfRequirement.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/NameOfRequirement.jsx @@ -32,16 +32,16 @@ const validationSchema = existingRequirements => .test( 'unique-name', 'A custom information requirement with that name already exists', - (value, _context) => + value => !R.includes( R.toLower(R.defaultTo('', value)), - R.map(it => R.toLower(it.customRequest.name), existingRequirements) - ) - ) + R.map(it => R.toLower(it.customRequest.name), existingRequirements), + ), + ), }) const defaultValues = { - requirementName: '' + requirementName: '', } export default NameOfRequirement diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen1Information.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen1Information.jsx index 4e338d5f..7433ce63 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen1Information.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen1Information.jsx @@ -32,12 +32,12 @@ const Screen1Information = () => { const validationSchema = Yup.object().shape({ screen1Title: Yup.string().label('Screen title').required(), - screen1Text: Yup.string().label('Screen text').required() + screen1Text: Yup.string().label('Screen text').required(), }) const defaultValues = { screen1Title: '', - screen1Text: '' + screen1Text: '', } export default Screen1Information diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen2Information.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen2Information.jsx index 0f455cb5..b531c31d 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen2Information.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/Screen2Information.jsx @@ -30,12 +30,12 @@ const ScreenInformation = () => { const validationSchema = Yup.object().shape({ screen2Title: Yup.string().label('Screen title').required(), - screen2Text: Yup.string().label('Screen text').required() + screen2Text: Yup.string().label('Screen text').required(), }) const defaultValues = { screen2Title: '', - screen2Text: '' + screen2Text: '', } export default ScreenInformation diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/ChoiceList.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/ChoiceList.jsx index e514d747..2ce9a71c 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/ChoiceList.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/ChoiceList.jsx @@ -13,7 +13,7 @@ const nonEmptyStr = obj => obj.text && obj.text.length const options = [ { display: 'Select just one', code: 'selectOne' }, - { display: 'Select multiple', code: 'selectMultiple' } + { display: 'Select multiple', code: 'selectMultiple' }, ] const ChoiceList = () => { @@ -26,7 +26,7 @@ const ChoiceList = () => { 'mb-0': true, 'text-tomato': !R.path(['values', 'constraintType'])(context) && - R.path(['errors', 'constraintType'])(context) + R.path(['errors', 'constraintType'])(context), } const hasError = choice => { diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/NumericalEntry.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/NumericalEntry.jsx index fff64499..b6a6da64 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/NumericalEntry.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/NumericalEntry.jsx @@ -9,7 +9,7 @@ import { TL1, H4 } from 'src/components/typography' const options = [ { display: 'None', code: 'none' }, { display: 'Date', code: 'date' }, - { display: 'Length', code: 'length' } + { display: 'Length', code: 'length' }, ] const NumericalEntry = () => { @@ -23,7 +23,7 @@ const NumericalEntry = () => { 'mb-0': true, 'text-tomat': !R.path(['values', 'constraintType'])(context) && - R.path(['errors', 'constraintType'])(context) + R.path(['errors', 'constraintType'])(context), } return ( diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/TextEntry.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/TextEntry.jsx index 6b9ed614..dc07e766 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/TextEntry.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/TextEntry.jsx @@ -12,8 +12,8 @@ const options = [ { display: 'Space separation', subtitle: '(e.g. first and last name)', - code: 'spaceSeparation' - } + code: 'spaceSeparation', + }, ] const TextEntry = () => { @@ -22,7 +22,7 @@ const TextEntry = () => { 'mt-0': true, 'text-tomato': !R.path(['values', 'constraintType'])(context) && - R.path(['errors', 'constraintType'])(context) + R.path(['errors', 'constraintType'])(context), } const getLabelInputs = () => { diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/index.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/index.jsx index 3c608e8c..2ecc3233 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/index.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Forms/TypeFields/index.jsx @@ -33,7 +33,7 @@ const defaultValues = { inputLength: '', inputLabel1: '', inputLabel2: '', - listChoices: [{ text: '' }, { text: '' }] + listChoices: [{ text: '' }, { text: '' }], } const validationSchema = Yup.lazy(values => { @@ -45,8 +45,8 @@ const validationSchema = Yup.lazy(values => { is: 'length', then: schema => schema.min(0).required('The number of digits is required'), - otherwise: schema => schema.notRequired() - }) + otherwise: schema => schema.notRequired(), + }), }) case 'text': return Yup.object({ @@ -55,8 +55,8 @@ const validationSchema = Yup.lazy(values => { inputLabel2: Yup.string().when('constraintType', { is: 'spaceSeparation', then: schema => schema.label('Second word label').required(), - otherwise: schema => schema.notRequired() - }) + otherwise: schema => schema.notRequired(), + }), }) case 'choiceList': return Yup.object({ @@ -64,10 +64,10 @@ const validationSchema = Yup.lazy(values => { listChoices: Yup.array().test( 'has-2-or-more', 'Choice list needs to have two or more non empty fields', - (values, ctx) => { + values => { return R.filter(nonEmptyStr)(values).length > 1 - } - ) + }, + ), }) default: return Yup.mixed().notRequired() diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Wizard.jsx b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Wizard.jsx index 0bcebe78..a0c2c669 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Wizard.jsx +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/Wizard.jsx @@ -9,23 +9,23 @@ import { Button } from 'src/components/buttons' import ChooseType, { validationSchema as chooseTypeSchema, - defaultValues as chooseTypeDefaults + defaultValues as chooseTypeDefaults, } from './Forms/ChooseType' import NameOfRequirement, { validationSchema as nameOfReqSchema, - defaultValues as nameOfReqDefaults + defaultValues as nameOfReqDefaults, } from './Forms/NameOfRequirement' import Screen1Information, { validationSchema as screen1InfoSchema, - defaultValues as screen1InfoDefaults + defaultValues as screen1InfoDefaults, } from './Forms/Screen1Information' import Screen2Information, { validationSchema as screen2InfoSchema, - defaultValues as screen2InfoDefaults + defaultValues as screen2InfoDefaults, } from './Forms/Screen2Information' import TypeFields, { defaultValues as typeFieldsDefaults, - validationSchema as typeFieldsValidationSchema + validationSchema as typeFieldsValidationSchema, } from './Forms/TypeFields' import WizardSplash from './WizardSplash' @@ -35,21 +35,21 @@ const getStep = (step, existingRequirements) => [ { validationSchema: nameOfReqSchema(existingRequirements), - Component: NameOfRequirement + Component: NameOfRequirement, }, { validationSchema: screen1InfoSchema, - Component: Screen1Information + Component: Screen1Information, }, { validationSchema: chooseTypeSchema, Component: ChooseType }, { validationSchema: screen2InfoSchema, - Component: Screen2Information + Component: Screen2Information, }, { validationSchema: typeFieldsValidationSchema, - Component: TypeFields - } + Component: TypeFields, + }, ][step - 1] const nonEmptyStr = obj => obj.text && obj.text.length @@ -69,16 +69,16 @@ const formatValues = (values, isEditing) => { name: values.requirementName, screen1: { text: values.screen1Text, - title: values.screen1Title + title: values.screen1Title, }, screen2: { title: values.screen2Title, - text: values.screen2Text + text: values.screen2Text, }, input: { type: values.inputType, - constraintType: values.constraintType - } + constraintType: values.constraintType, + }, } if (isChoiceList) { @@ -117,7 +117,7 @@ const makeEditingValues = ({ customRequest, id }) => { inputLabel2: customRequest.input.label2, listChoices: customRequest.input.choiceList, constraintType: customRequest.input.constraintType, - inputLength: customRequest.input.numDigits + inputLength: customRequest.input.numDigits, } } @@ -125,11 +125,10 @@ const chooseNotNull = (a, b) => (R.isNil(b) ? a : b) const Wizard = ({ onClose, - error = false, toBeEdited, onSave, hasError, - existingRequirements + existingRequirements, }) => { const isEditing = !R.isNil(toBeEdited) const [step, setStep] = useState(isEditing ? 1 : 0) @@ -139,7 +138,7 @@ const Wizard = ({ ...screen1InfoDefaults, ...screen2InfoDefaults, ...chooseTypeDefaults, - ...typeFieldsDefaults + ...typeFieldsDefaults, } // If we're editing, filter out the requirement being edited so that validation schemas don't enter in circular conflicts @@ -149,7 +148,7 @@ const Wizard = ({ const stepOptions = getStep(step, existingRequirements) const isLastStep = step === LAST_STEP - const onContinue = (values, actions) => { + const onContinue = values => { const showScreen2 = values.inputType === 'numerical' || values.inputType === 'choiceList' if (isEditing && step === 2) { diff --git a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/index.js b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/index.js index 5d99663f..0b85a771 100644 --- a/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/index.js +++ b/packages/admin-ui/src/pages/Triggers/CustomInfoRequests/index.js @@ -1,2 +1,3 @@ import CustomInfoRequests from './CustomInfoRequests' + export default CustomInfoRequests diff --git a/packages/admin-ui/src/pages/Triggers/TriggerView.jsx b/packages/admin-ui/src/pages/Triggers/TriggerView.jsx index 6bd1f76f..871e1602 100644 --- a/packages/admin-ui/src/pages/Triggers/TriggerView.jsx +++ b/packages/admin-ui/src/pages/Triggers/TriggerView.jsx @@ -25,29 +25,29 @@ const TriggerView = ({ addNewTriger, emailAuth, complianceServices, - customInfoRequests + customInfoRequests, }) => { const currency = R.path(['fiatCurrency'])( - fromNamespace(namespaces.LOCALE)(config) + fromNamespace(namespaces.LOCALE)(config), ) const [error, setError] = useState(null) const [saveConfig] = useMutation(SAVE_CONFIG, { onCompleted: () => toggleWizard(true), refetchQueries: () => ['getData'], - onError: error => setError(error) + onError: error => setError(error), }) const save = config => { setError(null) return saveConfig({ - variables: { config: { triggers: toServer(config.triggers) } } + variables: { config: { triggers: toServer(config.triggers) } }, }) } const add = rawConfig => { const toSave = R.concat([ - { id: uuidv4(), direction: 'both', ...rawConfig } + { id: uuidv4(), direction: 'both', ...rawConfig }, ])(triggers) return saveConfig({ variables: { config: { triggers: toServer(toSave) } } }) } diff --git a/packages/admin-ui/src/pages/Triggers/Triggers.jsx b/packages/admin-ui/src/pages/Triggers/Triggers.jsx index 781a74f2..7212950f 100644 --- a/packages/admin-ui/src/pages/Triggers/Triggers.jsx +++ b/packages/admin-ui/src/pages/Triggers/Triggers.jsx @@ -70,14 +70,14 @@ const Triggers = () => { const enabledCustomInfoRequests = R.pipe( R.path(['customInfoRequests']), R.defaultTo([]), - R.filter(R.propEq('enabled', true)) + R.filter(R.propEq('enabled', true)), )(customInfoReqData) const emailAuth = data?.config?.triggersConfig_customerAuthentication === 'EMAIL' const complianceServices = R.filter(R.propEq('class', 'compliance'))( - data?.accountsConfig || [] + data?.accountsConfig || [], ) const triggers = fromServer(data?.config?.triggers ?? []) const complianceConfig = @@ -87,7 +87,7 @@ const Triggers = () => { const [saveConfig] = useMutation(SAVE_CONFIG, { onCompleted: () => setWizard(false), refetchQueries: () => ['getData'], - onError: error => setError(error) + onError: error => setError(error), }) const [saveAccount] = useMutation(SAVE_ACCOUNT, { @@ -96,7 +96,7 @@ const Triggers = () => { toggleWizard('newTrigger')() }, refetchQueries: () => ['getData'], - onError: error => setError(error) + onError: error => setError(error), }) const addressReuseSave = rawConfig => { @@ -105,7 +105,7 @@ const Triggers = () => { } const titleSectionWidth = { - 'w-230': !subMenu === 'customInfoRequests' + 'w-230': !subMenu === 'customInfoRequests', } const setBlur = shouldBlur => { @@ -128,7 +128,7 @@ const Triggers = () => { const twilioSave = it => { setError(null) return saveAccount({ - variables: { accounts: { twilio: it } } + variables: { accounts: { twilio: it } }, }) } const addNewTriger = () => { @@ -149,7 +149,7 @@ const Triggers = () => { toggle: show => { refetch() setSubMenu(show ? 'advancedSettings' : false) - } + }, }, { text: 'Custom info requests', @@ -159,8 +159,8 @@ const Triggers = () => { toggle: show => { refetch() setSubMenu(show ? 'customInfoRequests' : false) - } - } + }, + }, ]} className={classnames(titleSectionWidth)}> {!subMenu && ( diff --git a/packages/admin-ui/src/pages/Triggers/Wizard.jsx b/packages/admin-ui/src/pages/Triggers/Wizard.jsx index 11f5f6c7..492e1f6a 100644 --- a/packages/admin-ui/src/pages/Triggers/Wizard.jsx +++ b/packages/admin-ui/src/pages/Triggers/Wizard.jsx @@ -19,7 +19,7 @@ const getStep = ( customInfoRequests, complianceServices, emailAuth, - triggers + triggers, ) => { switch (step) { // case 1: @@ -32,7 +32,7 @@ const getStep = ( triggers, customInfoRequests, complianceServices, - emailAuth + emailAuth, ) default: return Fragment @@ -97,7 +97,7 @@ const getTypeText = (config, currency) => { {singularOrPlural( config.threshold.threshold, 'transaction', - 'transactions' + 'transactions', )}{' '} in {orUnderline(config.threshold.thresholdDays)}{' '} {singularOrPlural(config.threshold.thresholdDays, 'day', 'days')} @@ -189,11 +189,11 @@ const Wizard = ({ customInfoRequests, complianceServices, emailAuth, - triggers + triggers, }) => { const [liveValues, setLiveValues] = useState({}) const [{ step, config }, setState] = useState({ - step: 1 + step: 1, }) const isLastStep = step === LAST_STEP @@ -203,7 +203,7 @@ const Wizard = ({ customInfoRequests, complianceServices, emailAuth, - triggers + triggers, ) const onContinue = async it => { @@ -215,7 +215,7 @@ const Wizard = ({ setState({ step: step + 1, - config: newConfig + config: newConfig, }) } @@ -228,12 +228,12 @@ const Wizard = ({ const hasRequirementError = requirements().hasRequirementError( errors, touched, - values + values, ) const hasCustomRequirementError = requirements().hasCustomRequirementError( errors, touched, - values + values, ) const hasAmountError = diff --git a/packages/admin-ui/src/pages/Triggers/components/AdvancedTriggers.jsx b/packages/admin-ui/src/pages/Triggers/components/AdvancedTriggers.jsx index 3186ff1f..b51849da 100644 --- a/packages/admin-ui/src/pages/Triggers/components/AdvancedTriggers.jsx +++ b/packages/admin-ui/src/pages/Triggers/components/AdvancedTriggers.jsx @@ -1,4 +1,4 @@ -import { useMutation, useQuery, gql } from "@apollo/client"; +import { useMutation, useQuery, gql } from '@apollo/client' import * as R from 'ramda' import React, { memo, useState } from 'react' import Section from 'src/components/layout/Section' @@ -12,7 +12,7 @@ import { defaults, overridesDefaults, getDefaultSettings, - getOverrides + getOverrides, } from './helper' const SAVE_CONFIG = gql` @@ -50,21 +50,21 @@ const AdvancedTriggersSettings = memo(() => { const customInfoRequests = R.path(['customInfoRequests'])(customInfoReqData) ?? [] const enabledCustomInfoRequests = R.filter(R.propEq('enabled', true))( - customInfoRequests + customInfoRequests, ) const loading = configLoading || customInfoLoading const [saveConfig] = useMutation(SAVE_CONFIG, { refetchQueries: () => ['getData'], - onError: error => setError(error) + onError: error => setError(error), }) const saveDefaults = it => { const newConfig = toNamespace(SCREEN_KEY)(it.triggersConfig[0]) setError(null) return saveConfig({ - variables: { config: newConfig } + variables: { config: newConfig }, }) } @@ -117,7 +117,7 @@ const AdvancedTriggersSettings = memo(() => { save={saveOverrides} validationSchema={getOverridesSchema( requirementsOverrides, - enabledCustomInfoRequests + enabledCustomInfoRequests, )} data={requirementsOverrides} elements={getOverrides(enabledCustomInfoRequests)} diff --git a/packages/admin-ui/src/pages/Triggers/components/helper.js b/packages/admin-ui/src/pages/Triggers/components/helper.js index 70ab864f..26690843 100644 --- a/packages/admin-ui/src/pages/Triggers/components/helper.js +++ b/packages/admin-ui/src/pages/Triggers/components/helper.js @@ -9,12 +9,12 @@ const buildAdvancedRequirementOptions = customInfoRequests => { { display: 'ID card image', code: 'idCardPhoto' }, { display: 'ID data', code: 'idCardData' }, { display: 'Customer camera', code: 'facephoto' }, - { display: 'US SSN', code: 'usSsn' } + { display: 'US SSN', code: 'usSsn' }, ] const custom = R.map(it => ({ display: it.customRequest.name, - code: it.id + code: it.id, }))(customInfoRequests) return R.concat(base, custom) @@ -24,8 +24,8 @@ const displayRequirement = (code, customInfoRequests) => { return R.prop( 'display', R.find(R.propEq('code', code))( - buildAdvancedRequirementOptions(customInfoRequests) - ) + buildAdvancedRequirementOptions(customInfoRequests), + ), ) } @@ -34,7 +34,7 @@ const defaultSchema = Yup.object().shape({ automation: Yup.string() .label('Automation') .matches(/(Manual|Automatic)/) - .required() + .required(), }) const getOverridesSchema = (values, customInfoRequests) => { @@ -51,19 +51,19 @@ const getOverridesSchema = (values, customInfoRequests) => { return this.createError({ message: `Requirement '${displayRequirement( requirement, - customInfoRequests - )}' already overridden` + customInfoRequests, + )}' already overridden`, }) } return true - } + }, }), expirationTime: Yup.string().label('Expiration time').required(), automation: Yup.string() .label('Automation') .matches(/(Manual|Automatic)/) - .required() - }); + .required(), + }) } const getDefaultSettings = () => { @@ -73,7 +73,7 @@ const getDefaultSettings = () => { header: 'Expiration time', width: 196, size: 'sm', - editable: false + editable: false, }, { name: 'automation', @@ -84,11 +84,11 @@ const getDefaultSettings = () => { inputProps: { options: [ { code: 'Automatic', display: 'Automatic' }, - { code: 'Manual', display: 'Manual' } + { code: 'Manual', display: 'Manual' }, ], labelProp: 'display', - valueProp: 'code' - } + valueProp: 'code', + }, }, { name: 'customerAuthentication', @@ -99,12 +99,12 @@ const getDefaultSettings = () => { inputProps: { options: [ { code: 'SMS', display: 'SMS' }, - { code: 'EMAIL', display: 'EMAIL' } + { code: 'EMAIL', display: 'EMAIL' }, ], labelProp: 'display', - valueProp: 'code' - } - } + valueProp: 'code', + }, + }, ] } @@ -117,21 +117,21 @@ const getOverrides = customInfoRequests => { size: 'sm', view: getView( buildAdvancedRequirementOptions(customInfoRequests), - 'display' + 'display', ), input: Autocomplete, inputProps: { options: buildAdvancedRequirementOptions(customInfoRequests), labelProp: 'display', - valueProp: 'code' - } + valueProp: 'code', + }, }, { name: 'expirationTime', header: 'Expiration time', width: 196, size: 'sm', - editable: false + editable: false, }, { name: 'automation', @@ -142,12 +142,12 @@ const getOverrides = customInfoRequests => { inputProps: { options: [ { code: 'Automatic', display: 'Automatic' }, - { code: 'Manual', display: 'Manual' } + { code: 'Manual', display: 'Manual' }, ], labelProp: 'display', - valueProp: 'code' - } - } + valueProp: 'code', + }, + }, ] } @@ -155,14 +155,14 @@ const defaults = [ { expirationTime: 'Forever', automation: 'Automatic', - customerAuth: 'SMS' - } + customerAuth: 'SMS', + }, ] const overridesDefaults = { requirement: '', expirationTime: 'Forever', - automation: 'Automatic' + automation: 'Automatic', } export { @@ -171,5 +171,5 @@ export { defaults, overridesDefaults, getDefaultSettings, - getOverrides + getOverrides, } diff --git a/packages/admin-ui/src/pages/Triggers/helper.jsx b/packages/admin-ui/src/pages/Triggers/helper.jsx index b6d82200..55506b6d 100644 --- a/packages/admin-ui/src/pages/Triggers/helper.jsx +++ b/packages/admin-ui/src/pages/Triggers/helper.jsx @@ -18,24 +18,24 @@ const threshold = Yup.object().shape({ thresholdDays: Yup.number() .transform(transformNumber) .nullable() - .label('Invalid threshold days') + .label('Invalid threshold days'), }) const requirement = Yup.object().shape({ requirement: Yup.string().required(), - suspensionDays: Yup.number().transform(transformNumber).nullable() + suspensionDays: Yup.number().transform(transformNumber).nullable(), }) const Schema = Yup.object() .shape({ triggerType, requirement, - threshold + threshold, // direction }) .test(({ threshold, triggerType }, context) => { const errorMessages = { - txAmount: threshold => 'Amount must be greater than or equal to 0', + txAmount: () => 'Amount must be greater than or equal to 0', txVolume: threshold => { const thresholdMessage = 'Volume must be greater than or equal to 0' const thresholdDaysMessage = 'Days must be greater than 0' @@ -52,7 +52,7 @@ const Schema = Yup.object() if (threshold.thresholdDays <= 0) message.push(thresholdDaysMessage) return message.join(', ') }, - consecutiveDays: threshold => 'Days must be greater than 0' + consecutiveDays: () => 'Days must be greater than 0', } const thresholdValidator = { txAmount: threshold => threshold.threshold >= 0, @@ -60,14 +60,14 @@ const Schema = Yup.object() threshold.threshold >= 0 && threshold.thresholdDays > 0, txVelocity: threshold => threshold.threshold > 0 && threshold.thresholdDays > 0, - consecutiveDays: threshold => threshold.thresholdDays > 0 + consecutiveDays: threshold => threshold.thresholdDays > 0, } if (triggerType && thresholdValidator[triggerType](threshold)) return return context.createError({ path: 'threshold', - message: errorMessages[triggerType](threshold) + message: errorMessages[triggerType](threshold), }) }) .test(({ requirement }, context) => { @@ -80,7 +80,7 @@ const Schema = Yup.object() return context.createError({ path: 'requirement', - message: 'Suspension days must be greater than 0' + message: 'Suspension days must be greater than 0', }) }) @@ -88,16 +88,16 @@ const Schema = Yup.object() const typeSchema = Yup.object() .shape({ triggerType: Yup.string('The trigger type must be a string').required( - 'The trigger type is required' + 'The trigger type is required', ), threshold: Yup.object({ threshold: Yup.number().transform(transformNumber).nullable(), - thresholdDays: Yup.number().transform(transformNumber).nullable() - }) + thresholdDays: Yup.number().transform(transformNumber).nullable(), + }), }) .test(({ threshold, triggerType }, context) => { const errorMessages = { - txAmount: threshold => 'Amount must be greater than or equal to 0', + txAmount: () => 'Amount must be greater than or equal to 0', txVolume: threshold => { const thresholdMessage = 'Volume must be greater than or equal to 0' const thresholdDaysMessage = 'Days must be greater than 0' @@ -118,7 +118,7 @@ const typeSchema = Yup.object() message.push(thresholdDaysMessage) return message.join(', ') }, - consecutiveDays: threshold => 'Days must be greater than 0' + consecutiveDays: () => 'Days must be greater than 0', } const thresholdValidator = { txAmount: threshold => threshold.threshold >= 0, @@ -126,7 +126,7 @@ const typeSchema = Yup.object() threshold.threshold >= 0 && threshold.thresholdDays > 0, txVelocity: threshold => threshold.threshold > 0 && threshold.thresholdDays > 0, - consecutiveDays: threshold => threshold.thresholdDays > 0 + consecutiveDays: threshold => threshold.thresholdDays > 0, } if (!triggerType) return @@ -135,7 +135,7 @@ const typeSchema = Yup.object() return context.createError({ path: 'threshold', - message: errorMessages[triggerType](threshold) + message: errorMessages[triggerType](threshold), }) }) @@ -143,10 +143,10 @@ const typeOptions = [ { display: 'Transaction amount', code: 'txAmount' }, { display: 'Transaction volume', - code: 'txVolume' + code: 'txVolume', }, { display: 'Transaction velocity', code: 'txVelocity' }, - { display: 'Consecutive days', code: 'consecutiveDays' } + { display: 'Consecutive days', code: 'consecutiveDays' }, ] const Type = ({ ...props }) => { @@ -154,7 +154,7 @@ const Type = ({ ...props }) => { useFormikContext() const typeClass = { - 'text-tomato': errors.triggerType && touched.triggerType + 'text-tomato': errors.triggerType && touched.triggerType, } const containsType = R.contains(values?.triggerType) @@ -177,7 +177,7 @@ const Type = ({ ...props }) => { const triggerTypeError = !!(hasDaysError || hasAmountError) const thresholdClass = { - 'text-tomato': triggerTypeError + 'text-tomato': triggerTypeError, } const isRadioGroupActive = () => { @@ -205,7 +205,7 @@ const Type = ({ ...props }) => { handleChange(e) setTouched({ threshold: false, - thresholdDays: false + thresholdDays: false, }) }} /> @@ -277,8 +277,8 @@ const type = currency => ({ props: { currency }, initialValues: { triggerType: '', - threshold: { threshold: '', thresholdDays: '' } - } + threshold: { threshold: '', thresholdDays: '' }, + }, }) const requirementSchema = Yup.object() @@ -288,17 +288,17 @@ const requirementSchema = Yup.object() suspensionDays: Yup.number().when('requirement', { is: value => value === 'suspend', then: schema => schema.nullable().transform(transformNumber), - otherwise: schema => schema.nullable().transform(() => null) + otherwise: schema => schema.nullable().transform(() => null), }), customInfoRequestId: Yup.string().when('requirement', { is: value => value !== 'custom', - then: schema => schema.nullable().transform(() => '') + then: schema => schema.nullable().transform(() => ''), }), externalService: Yup.string().when('requirement', { is: value => value !== 'external', - then: schema => schema.nullable().transform(() => '') - }) - }).required() + then: schema => schema.nullable().transform(() => ''), + }), + }).required(), }) .test(({ requirement }, context) => { const requirementValidator = (requirement, type) => { @@ -323,19 +323,19 @@ const requirementSchema = Yup.object() if (requirement && !requirementValidator(requirement, 'suspend')) return context.createError({ path: 'requirement', - message: 'Suspension days must be greater than 0' + message: 'Suspension days must be greater than 0', }) if (requirement && !requirementValidator(requirement, 'custom')) return context.createError({ path: 'requirement', - message: 'You must select an item' + message: 'You must select an item', }) if (requirement && !requirementValidator(requirement, 'external')) return context.createError({ path: 'requirement', - message: 'You must select an item' + message: 'You must select an item', }) }) @@ -343,25 +343,25 @@ const requirementOptions = [ { display: 'SMS verification', code: 'sms' }, { display: 'Email verification', - code: 'email' + code: 'email', }, { display: 'ID card image', code: 'idCardPhoto' }, { display: 'ID data', - code: 'idCardData' + code: 'idCardData', }, { display: 'Customer camera', code: 'facephoto' }, { display: 'Sanctions', code: 'sanctions' }, { display: 'US SSN', - code: 'usSsn' + code: 'usSsn', }, // { display: 'Super user', code: 'superuser' }, { display: 'Suspend', code: 'suspend' }, { display: 'Block', code: 'block' }, { display: 'External verification', - code: 'external' - } + code: 'external', + }, ] const hasRequirementError = (errors, touched, values) => @@ -386,7 +386,7 @@ const Requirement = ({ triggers, emailAuth, complianceServices, - customInfoRequests = [] + customInfoRequests = [], }) => { const { touched, errors, values, handleChange, setTouched } = useFormikContext() @@ -400,12 +400,12 @@ const Requirement = ({ if (value.requirement.requirement === 'custom') acc.push({ triggerType: value.triggerType, - id: value.requirement.customInfoRequestId + id: value.requirement.customInfoRequestId, }) return acc }, [], - triggers + triggers, ) const availableCustomRequirements = R.filter( @@ -413,24 +413,24 @@ const Requirement = ({ !R.includes( { triggerType: config.triggerType, - id: it.id + id: it.id, }, - customRequirementsInUse + customRequirementsInUse, ), - customInfoRequests + customInfoRequests, ) const makeCustomReqOptions = () => availableCustomRequirements.map(it => ({ value: it.id, - display: it.customRequest.name + display: it.customRequest.name, })) const enableCustomRequirement = !R.isEmpty(availableCustomRequirements) const customInfoOption = { display: 'Custom information requirement', - code: 'custom' + code: 'custom', } const itemToRemove = emailAuth ? 'sms' : 'email' @@ -444,7 +444,7 @@ const Requirement = ({ (!!errors.requirement && !isSuspend && !isCustom && !isExternal) || (isSuspend && hasRequirementError(errors, touched, values)) || (isCustom && hasCustomRequirementError(errors, touched, values)) || - (isExternal && hasExternalRequirementError(errors, touched, values)) + (isExternal && hasExternalRequirementError(errors, touched, values)), } return ( @@ -462,7 +462,7 @@ const Requirement = ({ onChange={e => { handleChange(e) setTouched({ - suspensionDays: false + suspensionDays: false, }) }} /> @@ -496,7 +496,7 @@ const Requirement = ({ name="requirement.externalService" options={complianceServices.map(it => ({ value: it.code, - display: it.display + display: it.display, }))} /> @@ -510,7 +510,7 @@ const requirements = ( triggers, customInfoRequests, complianceServices, - emailAuth + emailAuth, ) => ({ schema: requirementSchema, options: requirementOptions, @@ -520,7 +520,7 @@ const requirements = ( triggers, customInfoRequests, emailAuth, - complianceServices + complianceServices, }, hasRequirementError: hasRequirementError, hasCustomRequirementError: hasCustomRequirementError, @@ -530,9 +530,9 @@ const requirements = ( requirement: '', suspensionDays: '', customInfoRequestId: '', - externalService: '' - } - } + externalService: '', + }, + }, }) const getView = (data, code, compare) => it => { @@ -553,7 +553,7 @@ const RequirementInput = ({ customInfoRequests = [] }) => { const isSuspend = requirement === 'suspend' const display = customRequestId ? (R.path(['customRequest', 'name'])( - R.find(customReqIdMatches(customRequestId))(customInfoRequests) + R.find(customReqIdMatches(customRequestId))(customInfoRequests), ) ?? '') : getView(requirementOptions, 'display')(requirement) @@ -579,12 +579,12 @@ const RequirementView = ({ suspensionDays, customInfoRequestId, externalService, - customInfoRequests = [] + customInfoRequests = [], }) => { const display = requirement === 'custom' ? (R.path(['customRequest', 'name'])( - R.find(customReqIdMatches(customInfoRequestId))(customInfoRequests) + R.find(customReqIdMatches(customInfoRequestId))(customInfoRequests), ) ?? '') : requirement === 'external' ? `External verification (${onlyFirstToUpper(externalService)})` @@ -607,7 +607,7 @@ const DisplayThreshold = ({ config, currency, isEdit }) => { const inputClasses = { '-mt-1': true, 'w-13': config?.triggerType === 'txVelocity', - 'w-15': config?.triggerType === 'consecutiveDays' + 'w-15': config?.triggerType === 'consecutiveDays', } const threshold = config?.threshold?.threshold @@ -712,8 +712,8 @@ const getElements = (currency, customInfoRequests) => [ options: typeOptions, valueProp: 'code', labelProp: 'display', - optionsLimit: null - } + optionsLimit: null, + }, }, { name: 'requirement', @@ -723,7 +723,7 @@ const getElements = (currency, customInfoRequests) => [ input: () => , view: it => ( - ) + ), }, { name: 'threshold', @@ -731,16 +731,16 @@ const getElements = (currency, customInfoRequests) => [ width: 254, textAlign: 'right', input: () => , - view: (it, config) => - } + view: (it, config) => , + }, ] const triggerOrder = R.map(R.prop('code'))(typeOptions) const sortBy = [ R.comparator( (a, b) => - triggerOrder.indexOf(a.triggerType) < triggerOrder.indexOf(b.triggerType) - ) + triggerOrder.indexOf(a.triggerType) < triggerOrder.indexOf(b.triggerType), + ), ] const fromServer = triggers => { @@ -758,14 +758,14 @@ const fromServer = triggers => { requirement, suspensionDays, customInfoRequestId, - externalService + externalService, }, threshold: { threshold, - thresholdDays + thresholdDays, }, - ...rest - }) + ...rest, + }), )(triggers) } @@ -777,7 +777,7 @@ const toServer = triggers => thresholdDays: threshold.thresholdDays, customInfoRequestId: requirement.customInfoRequestId, externalService: requirement.externalService, - ...rest + ...rest, }))(triggers) export { @@ -789,5 +789,5 @@ export { fromServer, toServer, getView, - requirementOptions + requirementOptions, } diff --git a/packages/admin-ui/src/pages/UserManagement/UserManagement.jsx b/packages/admin-ui/src/pages/UserManagement/UserManagement.jsx index d7850b37..85b25245 100644 --- a/packages/admin-ui/src/pages/UserManagement/UserManagement.jsx +++ b/packages/admin-ui/src/pages/UserManagement/UserManagement.jsx @@ -65,7 +65,7 @@ const initialState = { showResetPasswordModal: false, showReset2FAModal: false, showRoleModal: false, - showEnableUserModal: false + showEnableUserModal: false, } const reducer = (_, action) => { @@ -82,7 +82,7 @@ const reducer = (_, action) => { const roleMapper = { user: 'Regular', - superuser: 'Superuser' + superuser: 'Superuser', } const Users = () => { @@ -94,9 +94,9 @@ const Users = () => { const [userInfo, setUserInfo] = useState(null) const [validateAttestation] = useMutation(VALIDATE_ATTESTATION, { - onCompleted: res => { + onCompleted: () => { // TODO: show a brief popup to have UX feedback? - } + }, }) const [generateAttestationOptions] = useLazyQuery(GENERATE_ATTESTATION, { @@ -106,11 +106,11 @@ const Users = () => { variables: { userID: userInfo.id, attestationResponse: res, - domain: window.location.hostname - } + domain: window.location.hostname, + }, }) }) - } + }, }) const elements = [ @@ -128,7 +128,7 @@ const Users = () => { ) return {u.username} - } + }, }, { header: 'Role', @@ -146,13 +146,13 @@ const Users = () => { setUserInfo(u) dispatch({ type: 'open', - payload: 'showRoleModal' + payload: 'showRoleModal', }) }} value={u.role === 'superuser'} /> - ) + ), }, { header: 'Actions', @@ -170,7 +170,7 @@ const Users = () => { setUserInfo(u) dispatch({ type: 'open', - payload: 'showResetPasswordModal' + payload: 'showResetPasswordModal', }) }}> Reset password @@ -183,7 +183,7 @@ const Users = () => { setUserInfo(u) dispatch({ type: 'open', - payload: 'showReset2FAModal' + payload: 'showReset2FAModal', }) }}> Reset 2FA @@ -196,15 +196,15 @@ const Users = () => { if (IP_CHECK_REGEX.test(window.location.hostname)) { dispatch({ type: 'open', - payload: 'showFIDOModal' + payload: 'showFIDOModal', }) } else { setUserInfo(u) generateAttestationOptions({ variables: { userID: u.id, - domain: window.location.hostname - } + domain: window.location.hostname, + }, }) } }}> @@ -212,7 +212,7 @@ const Users = () => { ) - } + }, }, { header: 'Enabled', @@ -227,13 +227,13 @@ const Users = () => { setUserInfo(u) dispatch({ type: 'open', - payload: 'showEnableUserModal' + payload: 'showEnableUserModal', }) }} value={u.enabled} /> - ) - } + ), + }, ] return ( @@ -246,7 +246,7 @@ const Users = () => { onClick={() => { dispatch({ type: 'open', - payload: 'showCreateUserModal' + payload: 'showCreateUserModal', }) }}> Add new user diff --git a/packages/admin-ui/src/pages/UserManagement/modals/ChangeRoleModal.jsx b/packages/admin-ui/src/pages/UserManagement/modals/ChangeRoleModal.jsx index e048d02c..a5a0f6cb 100644 --- a/packages/admin-ui/src/pages/UserManagement/modals/ChangeRoleModal.jsx +++ b/packages/admin-ui/src/pages/UserManagement/modals/ChangeRoleModal.jsx @@ -28,7 +28,7 @@ const CHANGE_USER_ROLE = gql` const ChangeRoleModal = ({ state, dispatch, user, requiresConfirmation }) => { const [changeUserRole, { error }] = useMutation(CHANGE_USER_ROLE, { onCompleted: () => handleClose(), - refetchQueries: () => ['users'] + refetchQueries: () => ['users'], }) const [confirmation, setConfirmation] = useState(null) @@ -38,8 +38,8 @@ const ChangeRoleModal = ({ state, dispatch, user, requiresConfirmation }) => { variables: { confirmationCode: confirmation, id: user.id, - newRole: user.role === 'superuser' ? 'user' : 'superuser' - } + newRole: user.role === 'superuser' ? 'user' : 'superuser', + }, }) } @@ -47,7 +47,7 @@ const ChangeRoleModal = ({ state, dispatch, user, requiresConfirmation }) => { setConfirmation(null) dispatch({ type: 'close', - payload: 'showRoleModal' + payload: 'showRoleModal', }) } diff --git a/packages/admin-ui/src/pages/UserManagement/modals/CreateUserModal.jsx b/packages/admin-ui/src/pages/UserManagement/modals/CreateUserModal.jsx index a962c8ed..c801ecd0 100644 --- a/packages/admin-ui/src/pages/UserManagement/modals/CreateUserModal.jsx +++ b/packages/admin-ui/src/pages/UserManagement/modals/CreateUserModal.jsx @@ -27,23 +27,23 @@ const validationSchema = Yup.object().shape({ username: Yup.string() .email('Username field should be in an email format!') .required('Username field is required!'), - role: Yup.string().required('Role field is required!') + role: Yup.string().required('Role field is required!'), }) const initialValues = { username: '', - role: '' + role: '', } const radioOptions = [ { code: 'user', - display: 'Regular user' + display: 'Regular user', }, { code: 'superuser', - display: 'Superuser' - } + display: 'Superuser', + }, ] const getErrorMsg = (formikErrors, formikTouched, mutationError) => { @@ -62,18 +62,18 @@ const CreateUserModal = ({ state, dispatch }) => { setCreateUserURL(null) dispatch({ type: 'close', - payload: 'showCreateUserModal' + payload: 'showCreateUserModal', }) } const [createUser, { error }] = useMutation(CREATE_USER, { onCompleted: ({ createRegisterToken: token }) => { setCreateUserURL(urlResolver(`/register?t=${token.token}`)) - } + }, }) const roleClass = (formikErrors, formikTouched) => ({ - [classes.error]: formikErrors.role && formikTouched.role + [classes.error]: formikErrors.role && formikTouched.role, }) return ( @@ -91,7 +91,7 @@ const CreateUserModal = ({ state, dispatch }) => { onSubmit={values => { setUsernameField(values.username) createUser({ - variables: { username: values.username, role: values.role } + variables: { username: values.username, role: values.role }, }) }}> {({ errors, touched }) => ( @@ -107,7 +107,7 @@ const CreateUserModal = ({ state, dispatch }) => {

Role

diff --git a/packages/admin-ui/src/pages/UserManagement/modals/EnableUserModal.jsx b/packages/admin-ui/src/pages/UserManagement/modals/EnableUserModal.jsx index dcb23287..c1c4bd7a 100644 --- a/packages/admin-ui/src/pages/UserManagement/modals/EnableUserModal.jsx +++ b/packages/admin-ui/src/pages/UserManagement/modals/EnableUserModal.jsx @@ -28,12 +28,12 @@ const DISABLE_USER = gql` const EnableUserModal = ({ state, dispatch, user, requiresConfirmation }) => { const [enableUser, { error: enableError }] = useMutation(ENABLE_USER, { onCompleted: () => handleClose(), - refetchQueries: () => ['users'] + refetchQueries: () => ['users'], }) const [disableUser, { error: disableError }] = useMutation(DISABLE_USER, { onCompleted: () => handleClose(), - refetchQueries: () => ['users'] + refetchQueries: () => ['users'], }) const [confirmation, setConfirmation] = useState(null) @@ -42,8 +42,8 @@ const EnableUserModal = ({ state, dispatch, user, requiresConfirmation }) => { disableUser({ variables: { confirmationCode: confirmation, - id: user.id - } + id: user.id, + }, }) } @@ -51,8 +51,8 @@ const EnableUserModal = ({ state, dispatch, user, requiresConfirmation }) => { enableUser({ variables: { confirmationCode: confirmation, - id: user.id - } + id: user.id, + }, }) } @@ -64,7 +64,7 @@ const EnableUserModal = ({ state, dispatch, user, requiresConfirmation }) => { setConfirmation(null) dispatch({ type: 'close', - payload: 'showEnableUserModal' + payload: 'showEnableUserModal', }) } diff --git a/packages/admin-ui/src/pages/UserManagement/modals/FIDOModal.jsx b/packages/admin-ui/src/pages/UserManagement/modals/FIDOModal.jsx index 172e93a0..d1304d19 100644 --- a/packages/admin-ui/src/pages/UserManagement/modals/FIDOModal.jsx +++ b/packages/admin-ui/src/pages/UserManagement/modals/FIDOModal.jsx @@ -10,7 +10,7 @@ const ChangeRoleModal = ({ state, dispatch }) => { const handleClose = () => { dispatch({ type: 'close', - payload: 'showFIDOModal' + payload: 'showFIDOModal', }) } diff --git a/packages/admin-ui/src/pages/UserManagement/modals/Input2FAModal.jsx b/packages/admin-ui/src/pages/UserManagement/modals/Input2FAModal.jsx index 450d3d5f..2730aab1 100644 --- a/packages/admin-ui/src/pages/UserManagement/modals/Input2FAModal.jsx +++ b/packages/admin-ui/src/pages/UserManagement/modals/Input2FAModal.jsx @@ -32,7 +32,7 @@ const Input2FAModal = ({ showModal, handleClose, setConfirmation }) => { const [confirm2FA, { error: queryError }] = useLazyQuery(CONFIRM_2FA, { onCompleted: ({ confirm2FA: success }) => - !success ? setInvalidCode(true) : onContinue() + !success ? setInvalidCode(true) : onContinue(), }) const getErrorMsg = () => { diff --git a/packages/admin-ui/src/pages/UserManagement/modals/Reset2FAModal.jsx b/packages/admin-ui/src/pages/UserManagement/modals/Reset2FAModal.jsx index ccf0e043..1cf5ae78 100644 --- a/packages/admin-ui/src/pages/UserManagement/modals/Reset2FAModal.jsx +++ b/packages/admin-ui/src/pages/UserManagement/modals/Reset2FAModal.jsx @@ -28,8 +28,8 @@ const Reset2FAModal = ({ state, dispatch, user, requiresConfirmation }) => { { onCompleted: ({ createReset2FAToken: token }) => { setReset2FAUrl(urlResolver(`/reset2fa?t=${token.token}`)) - } - } + }, + }, ) const [confirmation, setConfirmation] = useState(null) @@ -40,22 +40,22 @@ const Reset2FAModal = ({ state, dispatch, user, requiresConfirmation }) => { createReset2FAToken({ variables: { confirmationCode: confirmation, - userID: user?.id - } + userID: user?.id, + }, }) }, [ confirmation, createReset2FAToken, requiresConfirmation, state.showReset2FAModal, - user?.id + user?.id, ]) const handleClose = () => { setConfirmation(null) dispatch({ type: 'close', - payload: 'showReset2FAModal' + payload: 'showReset2FAModal', }) } diff --git a/packages/admin-ui/src/pages/UserManagement/modals/ResetPasswordModal.jsx b/packages/admin-ui/src/pages/UserManagement/modals/ResetPasswordModal.jsx index 1ebdfa70..1f8da1bc 100644 --- a/packages/admin-ui/src/pages/UserManagement/modals/ResetPasswordModal.jsx +++ b/packages/admin-ui/src/pages/UserManagement/modals/ResetPasswordModal.jsx @@ -27,7 +27,7 @@ const ResetPasswordModal = ({ state, dispatch, user, - requiresConfirmation + requiresConfirmation, }) => { const [resetPasswordUrl, setResetPasswordUrl] = useState('') @@ -36,8 +36,8 @@ const ResetPasswordModal = ({ { onCompleted: ({ createResetPasswordToken: token }) => { setResetPasswordUrl(urlResolver(`/resetpassword?t=${token.token}`)) - } - } + }, + }, ) const [confirmation, setConfirmation] = useState(null) @@ -48,22 +48,22 @@ const ResetPasswordModal = ({ createResetPasswordToken({ variables: { confirmationCode: confirmation, - userID: user?.id - } + userID: user?.id, + }, }) }, [ confirmation, createResetPasswordToken, requiresConfirmation, state.showResetPasswordModal, - user?.id + user?.id, ]) const handleClose = () => { setConfirmation(null) dispatch({ type: 'close', - payload: 'showResetPasswordModal' + payload: 'showResetPasswordModal', }) } diff --git a/packages/admin-ui/src/pages/Wallet/AdvancedWallet.jsx b/packages/admin-ui/src/pages/Wallet/AdvancedWallet.jsx index 9a56cdaf..d54600e7 100644 --- a/packages/admin-ui/src/pages/Wallet/AdvancedWallet.jsx +++ b/packages/admin-ui/src/pages/Wallet/AdvancedWallet.jsx @@ -1,4 +1,4 @@ -import { useQuery, useMutation, gql } from "@apollo/client"; +import { useQuery, useMutation, gql } from '@apollo/client' import * as R from 'ramda' import React, { useState } from 'react' import Section from 'src/components/layout/Section' @@ -11,7 +11,7 @@ import { getAdvancedWalletElements, getAdvancedWalletElementsOverrides, OverridesDefaults, - OverridesSchema + OverridesSchema, } from './helper' const SAVE_CONFIG = gql` @@ -39,12 +39,12 @@ const AdvancedWallet = () => { const [isEditingOverrides, setEditingOverrides] = useState(false) const [saveConfig, { error }] = useMutation(SAVE_CONFIG, { - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const save = rawConfig => { const config = toNamespace(SCREEN_KEY)( - toNamespace(ADVANCED)(rawConfig.wallets[0]) + toNamespace(ADVANCED)(rawConfig.wallets[0]), ) return saveConfig({ variables: { config } }) } @@ -60,22 +60,22 @@ const AdvancedWallet = () => { const cryptoCurrencies = data?.cryptoCurrencies ?? [] const AdvancedWalletSettings = fromNamespace(ADVANCED)( - fromNamespace(SCREEN_KEY)(data?.config) + fromNamespace(SCREEN_KEY)(data?.config), ) const AdvancedWalletSettingsOverrides = AdvancedWalletSettings.overrides ?? [] const overriddenCryptos = R.map(R.prop(CRYPTOCURRENCY_KEY))( - AdvancedWalletSettingsOverrides + AdvancedWalletSettingsOverrides, ) const suggestionFilter = R.filter( - it => !R.contains(it.code, overriddenCryptos) + it => !R.contains(it.code, overriddenCryptos), ) const coinSuggestions = suggestionFilter(cryptoCurrencies) const findSuggestion = it => { const coin = R.compose(R.find(R.propEq('code', it?.cryptoCurrency)))( - cryptoCurrencies + cryptoCurrencies, ) return coin ? [coin] : [] } @@ -113,7 +113,7 @@ const AdvancedWallet = () => { data={AdvancedWalletSettingsOverrides ?? []} elements={getAdvancedWalletElementsOverrides( coinSuggestions, - findSuggestion + findSuggestion, )} disableAdd={!coinSuggestions?.length} setEditing={onEditingOverrides} diff --git a/packages/admin-ui/src/pages/Wallet/Wallet.jsx b/packages/admin-ui/src/pages/Wallet/Wallet.jsx index 8e61444e..67704801 100644 --- a/packages/admin-ui/src/pages/Wallet/Wallet.jsx +++ b/packages/admin-ui/src/pages/Wallet/Wallet.jsx @@ -1,4 +1,4 @@ -import { useQuery, useMutation, gql } from "@apollo/client"; +import { useQuery, useMutation, gql } from '@apollo/client' import * as R from 'ramda' import React, { useState } from 'react' import Modal from 'src/components/Modal' @@ -68,14 +68,14 @@ const Wallet = ({ name: SCREEN_KEY }) => { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, { onCompleted: () => setWizard(false), - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const { data: marketsData } = useQuery(GET_MARKETS) const [saveAccount] = useMutation(SAVE_ACCOUNT, { onCompleted: () => setEditingSchema(null), - refetchQueries: () => ['getData'] + refetchQueries: () => ['getData'], }) const save = (rawConfig, accounts) => { @@ -114,7 +114,7 @@ const Wallet = ({ name: SCREEN_KEY }) => { const wizardSave = it => saveAccount({ - variables: { accounts: { [editingSchema.code]: it } } + variables: { accounts: { [editingSchema.code]: it } }, }).then(it => { onChangeFunction() setOnChangeFunction(null) @@ -131,8 +131,8 @@ const Wallet = ({ name: SCREEN_KEY }) => { text: 'Advanced settings', icon: SettingsIcon, inverseIcon: ReverseSettingsIcon, - toggle: setAdvancedSettings - } + toggle: setAdvancedSettings, + }, ]} appendix={ @@ -188,7 +188,9 @@ const Wallet = ({ name: SCREEN_KEY }) => { diff --git a/packages/admin-ui/src/pages/Wallet/Wizard.jsx b/packages/admin-ui/src/pages/Wallet/Wizard.jsx index ebb91549..2321ab47 100644 --- a/packages/admin-ui/src/pages/Wallet/Wizard.jsx +++ b/packages/admin-ui/src/pages/Wallet/Wizard.jsx @@ -44,12 +44,12 @@ const Wizard = ({ accounts, fiatCurrency, save, - error + error, }) => { const [{ step, config, accountsToSave }, setState] = useState({ step: 0, config: { active: true }, - accountsToSave: {} + accountsToSave: {}, }) const title = `Enable ${coin.display}` @@ -64,7 +64,7 @@ const Wizard = ({ const commonWizardSteps = [ { type: 'ticker', ...tickers }, { type: 'wallet', ...wallets }, - { type: 'exchange', ...exchanges } + { type: 'exchange', ...exchanges }, ] const hasZeroConfs = @@ -75,14 +75,14 @@ const Wizard = ({ type: 'zeroConf', name: 'confidence checking', schema: Yup.object().shape({ - zeroConfLimit: Yup.number().required() + zeroConfLimit: Yup.number().required(), }), - ...zeroConfs + ...zeroConfs, } const zeroConfLimitStep = { type: 'zeroConfLimit', - name: '0-conf limit' + name: '0-conf limit', } const wizardSteps = hasZeroConfs @@ -90,7 +90,7 @@ const Wizard = ({ commonWizardSteps, has0Conf(coin) ? [confidenceCheckingStep] - : [confidenceCheckingStep, zeroConfLimitStep] + : [confidenceCheckingStep, zeroConfLimitStep], ) : commonWizardSteps @@ -111,7 +111,7 @@ const Wizard = ({ setState({ step: step + 1, config: newConfig, - accountsToSave: newAccounts + accountsToSave: newAccounts, }) } diff --git a/packages/admin-ui/src/pages/Wallet/WizardStep.jsx b/packages/admin-ui/src/pages/Wallet/WizardStep.jsx index 0b1fcb30..7e4746a6 100644 --- a/packages/admin-ui/src/pages/Wallet/WizardStep.jsx +++ b/packages/admin-ui/src/pages/Wallet/WizardStep.jsx @@ -16,7 +16,7 @@ const initialState = { form: null, selected: null, isNew: false, - iError: false + iError: false, } const reducer = (state, action) => { @@ -26,7 +26,7 @@ const reducer = (state, action) => { form: null, selected: action.selected, isNew: null, - iError: false + iError: false, } case 'new': return { form: state.form, selected: null, isNew: true, iError: false } @@ -35,7 +35,7 @@ const reducer = (state, action) => { form: action.form, selected: action.form.code, isNew: true, - iError: false + iError: false, } case 'error': return R.merge(state, { innerError: true }) @@ -62,11 +62,11 @@ const WizardStep = ({ fiatCurrency, filled, unfilled, - getValue + getValue, }) => { const [{ innerError, selected, form, isNew }, dispatch] = useReducer( reducer, - initialState + initialState, ) useEffect(() => { @@ -83,7 +83,7 @@ const WizardStep = ({ const label = isLastStep ? 'Finish' : 'Next' const displayName = name ?? type const subtitleClass = classnames('mt-8 mb-5 mx-0', { - 'text-tomato': innerError + 'text-tomato': innerError, }) return ( <> @@ -115,7 +115,7 @@ const WizardStep = ({ initialValues={{ zeroConfLimit: '' }} enableReinitialize validationSchema={stepSchema}> - {({ values, setFieldValue }) => ( + {({ setFieldValue }) => (
{ dispatch({ type: 'select', - selected: event.target.value + selected: event.target.value, }) setFieldValue(event.target.id, event.target.value) }} @@ -143,7 +143,7 @@ const WizardStep = ({ {!R.isEmpty(unfilled) && !R.isNil(unfilled) && ( { + onChange={() => { dispatch({ type: 'new' }) }} labelClassName="w-[150px] h-12" @@ -170,7 +170,7 @@ const WizardStep = ({ save={it => innerContinue({ [type]: form.code }, { [form.code]: it })} elements={schemas[form.code].elements} validationSchema={schemas[form.code].getValidationSchema( - accounts[form.code] + accounts[form.code], )} value={getValue(form.code)} buttonLabel={label} diff --git a/packages/admin-ui/src/pages/Wallet/helper.jsx b/packages/admin-ui/src/pages/Wallet/helper.jsx index 482c7d1c..fc6128fc 100644 --- a/packages/admin-ui/src/pages/Wallet/helper.jsx +++ b/packages/admin-ui/src/pages/Wallet/helper.jsx @@ -6,7 +6,7 @@ import * as Yup from 'yup' import { Autocomplete, Checkbox, - NumberInput + NumberInput, } from 'src/components/inputs/formik' import { CURRENCY_MAX } from 'src/utils/constants' import { defaultToZero } from 'src/utils/number' @@ -16,26 +16,26 @@ const filterCoins = ({ id }) => R.filter(it => R.contains(id)(it.cryptos)) const WalletSchema = Yup.object().shape({ ticker: Yup.string('The ticker must be a string').required( - 'The ticker is required' + 'The ticker is required', ), wallet: Yup.string('The wallet must be a string').required( - 'The wallet is required' + 'The wallet is required', ), exchange: Yup.string('The exchange must be a string').required( - 'The exchange is required' + 'The exchange is required', ), zeroConf: Yup.string('The confidence checking must be a string'), zeroConfLimit: Yup.number('The 0-conf limit must be an integer') .integer('The 0-conf limit must be an integer') .min(0, 'The 0-conf limit must be a positive integer') .max(CURRENCY_MAX) - .transform(defaultToZero) + .transform(defaultToZero), }) const AdvancedWalletSchema = Yup.object().shape({ cryptoUnits: Yup.string().required(), feeMultiplier: Yup.string().required(), - allowTransactionBatching: Yup.boolean() + allowTransactionBatching: Yup.boolean(), }) const OverridesSchema = Yup.object().shape({ @@ -46,14 +46,14 @@ const OverridesSchema = Yup.object().shape({ cryptoCurrency: Yup.string().required(), allowTransactionBatching: Yup.boolean() .default(() => false) - .required() + .required(), }) const OverridesDefaults = { cryptoUnits: '', feeMultiplier: '', cryptoCurrency: '', - allowTransactionBatching: null + allowTransactionBatching: null, } const viewFeeMultiplier = it => @@ -72,12 +72,12 @@ const feeOptions = [ { display: '-30%', code: '0.7' }, { display: '-40%', code: '0.6' }, { display: '-50%', code: '0.5' }, - { display: '-60%', code: '0.4' } + { display: '-60%', code: '0.4' }, ] const cryptoUnitsDefaultOptions = [ { display: 'mili', code: 'mili' }, - { display: 'full', code: 'full' } + { display: 'full', code: 'full' }, ] const getCryptoUnitsOptions = it => { @@ -99,8 +99,8 @@ const getAdvancedWalletElements = () => { inputProps: { options: cryptoUnitsDefaultOptions, valueProp: 'code', - labelProp: 'display' - } + labelProp: 'display', + }, }, { name: 'allowTransactionBatching', @@ -111,7 +111,7 @@ const getAdvancedWalletElements = () => { view: (_, ite) => { return ite.allowTransactionBatching ? 'Yes' : `No` }, - input: Checkbox + input: Checkbox, }, { name: 'feeMultiplier', @@ -124,15 +124,15 @@ const getAdvancedWalletElements = () => { inputProps: { options: feeOptions, valueProp: 'code', - labelProp: 'display' - } - } + labelProp: 'display', + }, + }, ] } const getAdvancedWalletElementsOverrides = ( coinSuggestions, - findSuggestion + findSuggestion, ) => { return [ { @@ -143,9 +143,9 @@ const getAdvancedWalletElementsOverrides = ( options: it => R.concat(coinSuggestions, findSuggestion(it)), optionsLimit: null, valueProp: 'code', - labelProp: 'display' + labelProp: 'display', }, - size: 'sm' + size: 'sm', }, { name: 'cryptoUnits', @@ -156,8 +156,8 @@ const getAdvancedWalletElementsOverrides = ( inputProps: { options: getCryptoUnitsOptions, valueProp: 'code', - labelProp: 'display' - } + labelProp: 'display', + }, }, { name: 'allowTransactionBatching', @@ -170,7 +170,7 @@ const getAdvancedWalletElementsOverrides = ( return ite.allowTransactionBatching ? 'Yes' : 'No' }, input: Checkbox, - editable: it => it.cryptoCurrency === 'BTC' + editable: it => it.cryptoCurrency === 'BTC', }, { name: 'feeMultiplier', @@ -187,16 +187,16 @@ const getAdvancedWalletElementsOverrides = ( inputProps: { options: feeOptions, valueProp: 'code', - labelProp: 'display' + labelProp: 'display', }, - editable: it => it.cryptoCurrency === 'BTC' - } + editable: it => it.cryptoCurrency === 'BTC', + }, ] } const has0Conf = R.complement( /* NOTE: List of coins without 0conf settings. */ - R.pipe(R.prop('id'), R.flip(R.includes)(['ETH', 'USDT', 'USDC'])) + R.pipe(R.prop('id'), R.flip(R.includes)(['ETH', 'USDT', 'USDC'])), ) const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { @@ -204,7 +204,7 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { const viewCryptoCurrency = it => { const currencyDisplay = R.compose( it => `${R.prop(['display'])(it)} ${it?.isBeta ? '(Beta)' : ''}`, - R.find(R.propEq('code', it)) + R.find(R.propEq('code', it)), )(cryptoCurrencies) return currencyDisplay } @@ -213,11 +213,11 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { const getDisplayName = type => it => R.compose( R.prop('display'), - R.find(R.propEq('code', it)) + R.find(R.propEq('code', it)), )(filterOptions(type)) const getOptions = R.curry((option, it) => - filterCoins(it)(filterOptions(option)) + filterCoins(it)(filterOptions(option)), ) return [ @@ -227,7 +227,7 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { width: 150 - widthAdjust, view: viewCryptoCurrency, size: 'sm', - editable: false + editable: false, }, { name: 'ticker', @@ -240,8 +240,8 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { options: getOptions('ticker'), valueProp: 'code', labelProp: 'display', - optionsLimit: null - } + optionsLimit: null, + }, }, { name: 'wallet', @@ -255,8 +255,8 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { valueProp: 'code', labelProp: 'display', optionsLimit: null, - onChange - } + onChange, + }, }, { name: 'exchange', @@ -270,8 +270,8 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { valueProp: 'code', labelProp: 'display', optionsLimit: null, - onChange - } + onChange, + }, }, { name: 'zeroConf', @@ -293,9 +293,9 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { valueProp: 'code', labelProp: 'display', optionsLimit: null, - onChange + onChange, }, - editable: has0Conf + editable: has0Conf, }, { name: 'zeroConfLimit', @@ -307,10 +307,10 @@ const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => { input: NumberInput, width: 145 - widthAdjust, inputProps: { - decimalPlaces: 0 + decimalPlaces: 0, }, - editable: has0Conf - } + editable: has0Conf, + }, ] } @@ -323,5 +323,5 @@ export { getAdvancedWalletElementsOverrides, OverridesDefaults, OverridesSchema, - has0Conf + has0Conf, } diff --git a/packages/admin-ui/src/pages/Wizard/Wizard.jsx b/packages/admin-ui/src/pages/Wizard/Wizard.jsx index 729ccbbf..82123298 100644 --- a/packages/admin-ui/src/pages/Wizard/Wizard.jsx +++ b/packages/admin-ui/src/pages/Wizard/Wizard.jsx @@ -21,7 +21,7 @@ const GET_DATA = gql` } ` -const Wizard = ({ fromAuthRegister }) => { +const Wizard = () => { const { data, loading } = useQuery(GET_DATA) const history = useHistory() const { setWizardTested } = useContext(AppContext) @@ -68,7 +68,7 @@ const Wizard = ({ fromAuthRegister }) => { 'flex flex-col justify-between py-4 px-0 bg-white': true, 'bg-[url(/wizard-background.svg)] bg-no-repeat bg-center bg-fixed bg-cover': isWelcome, - 'blur-sm pointer-events-none': footerExp + 'blur-sm pointer-events-none': footerExp, } return ( diff --git a/packages/admin-ui/src/pages/Wizard/components/Commissions.jsx b/packages/admin-ui/src/pages/Wizard/components/Commissions.jsx index b4f6f95b..50f2e32e 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Commissions.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Commissions.jsx @@ -23,7 +23,7 @@ function Commissions({ isActive, doContinue }) { const { data } = useQuery(GET_DATA) const [saveConfig] = useMutation(SAVE_CONFIG, { - onCompleted: doContinue + onCompleted: doContinue, }) const save = it => { @@ -32,7 +32,7 @@ function Commissions({ isActive, doContinue }) { } const currency = R.path(['fiatCurrency'])( - fromNamespace(namespaces.LOCALE)(data?.config) + fromNamespace(namespaces.LOCALE)(data?.config), ) const locale = fromNamespace(namespaces.LOCALE)(data?.config) diff --git a/packages/admin-ui/src/pages/Wizard/components/Locales.jsx b/packages/admin-ui/src/pages/Wizard/components/Locales.jsx index 8ed67674..da95bb48 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Locales.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Locales.jsx @@ -8,7 +8,7 @@ import { Table as EditableTable } from 'src/components/editableTable' import { mainFields, localeDefaults as defaults, - LocaleSchema as schema + LocaleSchema as schema, } from 'src/pages/Locales/helper' import { toNamespace } from 'src/utils/config' @@ -51,7 +51,7 @@ function Locales({ isActive, doContinue }) { const { data } = useQuery(GET_DATA) const [saveConfig] = useMutation(SAVE_CONFIG, { - onCompleted: doContinue + onCompleted: doContinue, }) const save = it => { @@ -61,7 +61,7 @@ function Locales({ isActive, doContinue }) { const cryptoCurrencies = getConfiguredCoins( data?.config || {}, - data?.cryptoCurrencies || [] + data?.cryptoCurrencies || [], ) const onChangeCoin = (prev, curr, setValue) => setValue(curr) @@ -83,7 +83,7 @@ function Locales({ isActive, doContinue }) { data={[]} elements={mainFields( R.mergeRight(data, { cryptoCurrencies }), - onChangeCoin + onChangeCoin, )} /> diff --git a/packages/admin-ui/src/pages/Wizard/components/Mailgun.jsx b/packages/admin-ui/src/pages/Wizard/components/Mailgun.jsx index b467a12b..6d9b8cc2 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Mailgun.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Mailgun.jsx @@ -1,4 +1,4 @@ -import { useMutation, useQuery, gql } from "@apollo/client"; +import { useMutation, useQuery, gql } from '@apollo/client' import React, { useState, useEffect } from 'react' import { H4, Info3 } from 'src/components/typography' import FormRenderer from 'src/pages/Services/FormRenderer' @@ -33,12 +33,12 @@ const SAVE_ACCOUNTS = gql` const options = [ { code: 'enabled', - display: 'Yes, send notifications to my email' + display: 'Yes, send notifications to my email', }, { code: 'disabled', - display: "No, don't send email notifications" - } + display: "No, don't send email notifications", + }, ] const Mailgun = () => { @@ -111,7 +111,9 @@ const Mailgun = () => { value={accounts.mailgun} save={saveAccount} elements={mailgunSchema.elements} - validationSchema={mailgunSchema.getValidationSchema(accounts.mailgun)} + validationSchema={mailgunSchema.getValidationSchema( + accounts.mailgun, + )} buttonLabel={'Save'} /> diff --git a/packages/admin-ui/src/pages/Wizard/components/Notifications.jsx b/packages/admin-ui/src/pages/Wizard/components/Notifications.jsx index b547f0ca..1c459693 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Notifications.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Notifications.jsx @@ -19,7 +19,7 @@ const pages = [ SETUP_CHANNELS, TRANSACTION_ALERTS, FIAT_BALANCE_ALERTS, - CRYPTO_BALANCE_ALERTS + CRYPTO_BALANCE_ALERTS, ] const N = () => { diff --git a/packages/admin-ui/src/pages/Wizard/components/OperatorInfo.jsx b/packages/admin-ui/src/pages/Wizard/components/OperatorInfo.jsx index 127a27fd..0f09aeba 100644 --- a/packages/admin-ui/src/pages/Wizard/components/OperatorInfo.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/OperatorInfo.jsx @@ -1,4 +1,5 @@ import React from 'react' + // import OperatorInfo from 'src/pages/OperatorInfo' function WizardOperatorInfo() { diff --git a/packages/admin-ui/src/pages/Wizard/components/Twilio.jsx b/packages/admin-ui/src/pages/Wizard/components/Twilio.jsx index 0b00f2b4..fa4bb382 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Twilio.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Twilio.jsx @@ -29,12 +29,12 @@ const SAVE_ACCOUNTS = gql` const options = [ { code: 'enable', - display: 'Yes, I will' + display: 'Yes, I will', }, { code: 'disable', - display: 'No, not for now' - } + display: 'No, not for now', + }, ] function Twilio({ doContinue }) { @@ -43,7 +43,7 @@ function Twilio({ doContinue }) { const { data, refetch } = useQuery(GET_CONFIG) const [saveAccounts] = useMutation(SAVE_ACCOUNTS, { - onCompleted: doContinue + onCompleted: doContinue, }) const accounts = data?.accounts ?? [] @@ -65,7 +65,7 @@ function Twilio({ doContinue }) { const titleClasses = { 'ml-2 mb-2': true, - [sharedClasses.error]: error + [sharedClasses.error]: error, } return ( diff --git a/packages/admin-ui/src/pages/Wizard/components/Twilio.module.css b/packages/admin-ui/src/pages/Wizard/components/Twilio.module.css index 30bd9e37..3654ca3b 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Twilio.module.css +++ b/packages/admin-ui/src/pages/Wizard/components/Twilio.module.css @@ -21,4 +21,4 @@ .info { margin-top: 20px; margin-bottom: 20px; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/AllSet.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/AllSet.jsx index f2c59217..8f7a02ac 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/AllSet.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/AllSet.jsx @@ -1,4 +1,4 @@ -import { useQuery, useMutation, gql } from "@apollo/client"; +import { useQuery, useMutation, gql } from '@apollo/client' import * as R from 'ramda' import React, { useState } from 'react' import { P, H4 } from 'src/components/typography' @@ -9,6 +9,7 @@ import { NamespacedTable as EditableTable } from 'src/components/editableTable' import { toNamespace, namespaces } from 'src/utils/config' import classes from './Shared.module.css' + const GET_INFO = gql` query getData { config @@ -36,7 +37,7 @@ const SAVE_CONFIG = gql` const AllSet = ({ data: currentData, doContinue }) => { const { data } = useQuery(GET_INFO) const [saveConfig] = useMutation(SAVE_CONFIG, { - onCompleted: doContinue + onCompleted: doContinue, }) const [error, setError] = useState(false) @@ -49,7 +50,7 @@ const AllSet = ({ data: currentData, doContinue }) => { const save = () => { const adjustedData = { zeroConfLimit: 0, - ...currentData + ...currentData, } if (!WalletSchema.isValidSync(adjustedData)) return setError(true) @@ -61,16 +62,16 @@ const AllSet = ({ data: currentData, doContinue }) => { const presentableData = R.pipe( R.omit(['coin', 'zeroConf', 'zeroConfLimit']), - toNamespace(coin) + toNamespace(coin), )(currentData) const presentableElements = R.filter( R.pipe( R.prop('name'), R.flip(R.includes)(['zeroConf', 'zeroConfLimit']), - R.not() + R.not(), ), - getElements(cryptoCurrencies, accountsConfig, null, true) + getElements(cryptoCurrencies, accountsConfig, null, true), ) return ( diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/Blockcypher.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/Blockcypher.jsx index 3cf99128..72e9238f 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/Blockcypher.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/Blockcypher.jsx @@ -1,4 +1,4 @@ -import { useMutation, useQuery, gql } from "@apollo/client"; +import { useMutation, useQuery, gql } from '@apollo/client' import React, { useState } from 'react' import { P, H4 } from 'src/components/typography' import FormRenderer from 'src/pages/Services/FormRenderer' @@ -23,18 +23,18 @@ const SAVE_ACCOUNTS = gql` const options = [ { code: 'enable', - display: 'I will enable cash-out' + display: 'I will enable cash-out', }, { code: 'disable', - display: "I won't enable cash-out" - } + display: "I won't enable cash-out", + }, ] const Blockcypher = ({ addData }) => { const { data } = useQuery(GET_CONFIG) const [saveConfig] = useMutation(SAVE_ACCOUNTS, { - onCompleted: () => addData({ zeroConf: 'blockcypher' }) + onCompleted: () => addData({ zeroConf: 'blockcypher' }), }) const [selected, setSelected] = useState(null) diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseCoin.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseCoin.jsx index adeb32b4..3e4b4e1b 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseCoin.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseCoin.jsx @@ -1,4 +1,4 @@ -import { useQuery, gql } from "@apollo/client"; +import { useQuery, gql } from '@apollo/client' import { Formik, Form, Field } from 'formik' import React, { useState } from 'react' import PromptWhenDirty from 'src/components/PromptWhenDirty' @@ -20,7 +20,7 @@ const GET_CONFIG = gql` ` const schema = Yup.object().shape({ - coin: Yup.string().required() + coin: Yup.string().required(), }) const ChooseCoin = ({ addData }) => { diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseExchange.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseExchange.jsx index 4a5df8d9..ce1d845c 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseExchange.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseExchange.jsx @@ -1,4 +1,4 @@ -import { useQuery, useMutation, gql } from "@apollo/client"; +import { useQuery, useMutation, gql } from '@apollo/client' import { getEquivalentCode } from '@lamassu/coins/lightUtils' import * as R from 'ramda' import React, { useState } from 'react' @@ -41,7 +41,7 @@ const isConfigurable = it => const ChooseExchange = ({ data: currentData, addData }) => { const { data } = useQuery(GET_CONFIG) const [saveAccounts] = useMutation(SAVE_ACCOUNTS, { - onCompleted: () => submit() + onCompleted: () => submit(), }) const [selected, setSelected] = useState(null) @@ -75,7 +75,7 @@ const ChooseExchange = ({ data: currentData, addData }) => { itbit: 'https://support.lamassu.is/hc/en-us/articles/360026195032-itBit-trading', bitstamp: - 'https://support.lamassu.is/hc/en-us/articles/115001206911-Bitstamp-trading' + 'https://support.lamassu.is/hc/en-us/articles/115001206911-Bitstamp-trading', } return ( @@ -113,7 +113,9 @@ const ChooseExchange = ({ data: currentData, addData }) => { value={accounts[selected]} save={saveExchange(selected)} elements={schema[selected].elements} - validationSchema={schema[selected].getValidationSchema(accounts[selected])} + validationSchema={schema[selected].getValidationSchema( + accounts[selected], + )} buttonLabel={'Continue'} buttonClass={classes.formButton} /> diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseTicker.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseTicker.jsx index 23c73a23..76fc301d 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseTicker.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseTicker.jsx @@ -1,4 +1,4 @@ -import { useQuery, gql } from "@apollo/client"; +import { useQuery, gql } from '@apollo/client' import { getEquivalentCode } from '@lamassu/coins/lightUtils' import * as R from 'ramda' import React, { useState } from 'react' diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx index e898d4d4..691ede32 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx @@ -1,4 +1,4 @@ -import { useQuery, useMutation, gql } from "@apollo/client"; +import { useQuery, useMutation, gql } from '@apollo/client' import * as R from 'ramda' import React, { useState } from 'react' import { H4, Info3 } from 'src/components/typography' @@ -45,7 +45,7 @@ const isLocalHosted = it => 'litecoind', 'dashd', 'zcashd', - 'bitcoincashd' + 'bitcoincashd', ]) const ChooseWallet = ({ data: currentData, addData }) => { @@ -53,7 +53,7 @@ const ChooseWallet = ({ data: currentData, addData }) => { const schema = _schema() const { data } = useQuery(GET_CONFIG) const [saveAccounts] = useMutation(SAVE_ACCOUNTS, { - onCompleted: () => submit() + onCompleted: () => submit(), }) const [selected, setSelected] = useState(null) @@ -143,7 +143,7 @@ const ChooseWallet = ({ data: currentData, addData }) => { save={saveWallet(selected)} elements={schema.infura.elements} validationSchema={schema.infura.getValidationSchema( - accounts.infura + accounts.infura, )} buttonLabel={'Continue'} buttonClass={classes.formButton} @@ -158,7 +158,7 @@ const ChooseWallet = ({ data: currentData, addData }) => { save={saveWallet(selected)} elements={schema.trongrid.elements} validationSchema={schema.trongrid.getValidationSchema( - accounts.trongrid + accounts.trongrid, )} buttonLabel={'Continue'} buttonClass={classes.formButton} diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/Shared.module.css b/packages/admin-ui/src/pages/Wizard/components/Wallet/Shared.module.css index c52d200d..7e03a9df 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/Shared.module.css +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/Shared.module.css @@ -42,4 +42,4 @@ .formButton { margin: 24px 0 0; -} \ No newline at end of file +} diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/Wallet.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/Wallet.jsx index 0580b673..616cfbf7 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/Wallet.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/Wallet.jsx @@ -13,28 +13,28 @@ import ChooseWallet from './ChooseWallet' const steps = [ { label: 'Choose cryptocurrency', - component: ChooseCoin + component: ChooseCoin, }, { label: 'Choose wallet', - component: ChooseWallet + component: ChooseWallet, }, { label: 'Choose ticker', - component: ChooseTicker + component: ChooseTicker, }, { label: 'Exchange', - component: ChooseExchange + component: ChooseExchange, }, { label: 'Blockcypher', - component: Blockcypher + component: Blockcypher, }, { label: 'All set', - component: AllSet - } + component: AllSet, + }, ] const Wallet = ({ doContinue }) => { diff --git a/packages/admin-ui/src/pages/Wizard/helper.jsx b/packages/admin-ui/src/pages/Wizard/helper.jsx index 47035521..6e1590b9 100644 --- a/packages/admin-ui/src/pages/Wizard/helper.jsx +++ b/packages/admin-ui/src/pages/Wizard/helper.jsx @@ -15,7 +15,7 @@ import Welcome from './components/Welcome' const getConfiguredCoins = (config, crypto) => { const wallet = fromNamespace(namespaces.WALLETS, config) return R.filter(it => - WalletSchema.isValidSync(fromNamespace(it.code, wallet)) + WalletSchema.isValidSync(fromNamespace(it.code, wallet)), )(crypto) } @@ -25,7 +25,7 @@ const hasValidWallet = (config, crypto) => { const hasValidConfig = R.compose( R.any(R.identity), - R.map(it => WalletSchema.isValidSync(it)) + R.map(it => WalletSchema.isValidSync(it)), )(coins) return hasValidConfig @@ -60,7 +60,7 @@ const getWizardStep = (config, crypto) => { const STEPS = [ { id: 'welcome', - Component: Welcome + Component: Welcome, }, { id: 'wallet', @@ -69,7 +69,7 @@ const STEPS = [ subtitle: 'Wallet settings', text: `Your wallet settings are the first step for this wizard. We'll start by setting up one of cryptocurrencies to get you up and running, - but you can later set up as many as you want.` + but you can later set up as many as you want.`, }, { id: 'locale', @@ -78,7 +78,7 @@ const STEPS = [ subtitle: 'Locales', text: `From the Locales panel, you can define default settings that will be applied to all machines you add to your network later on. - These settings may be overridden for specific machines in the Overrides section.` + These settings may be overridden for specific machines in the Overrides section.`, }, { id: 'twilio', @@ -93,7 +93,7 @@ const STEPS = [ You'll need to configure Twilio if you're offering cash-out or any compliance options - ) + ), }, { id: 'commissions', @@ -105,8 +105,8 @@ const STEPS = [ you'll later add to your network. Default settings keep you from having to enter the same values everytime you add a new machine. Once a machine is added, you may override these values per machine and per - cryptocurrency in the overrides section.` - } + cryptocurrency in the overrides section.`, + }, // { // id: 'notifications', // Component: Notifications, diff --git a/packages/admin-ui/src/routing/lamassu.routes.jsx b/packages/admin-ui/src/routing/lamassu.routes.jsx index 81781d0b..70b5586e 100644 --- a/packages/admin-ui/src/routing/lamassu.routes.jsx +++ b/packages/admin-ui/src/routing/lamassu.routes.jsx @@ -37,7 +37,7 @@ const getLamassuRoutes = () => [ label: 'Transactions', route: '/transactions', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Transactions + component: Transactions, }, { key: 'maintenance', @@ -53,44 +53,44 @@ const getLamassuRoutes = () => [ label: 'Cash units', route: '/maintenance/cash-units', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: CashUnits + component: CashUnits, }, { key: 'funding', label: 'Funding', route: '/maintenance/funding', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Funding + component: Funding, }, { key: 'logs', label: 'Machine logs', route: '/maintenance/logs', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: MachineLogs + component: MachineLogs, }, { key: 'machine-status', label: 'Machine status', route: '/maintenance/machine-status', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: MachineStatus + component: MachineStatus, }, { key: 'server-logs', label: 'Server', route: '/maintenance/server-logs', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: ServerLogs - } - ] + component: ServerLogs, + }, + ], }, { key: 'analytics', label: 'Analytics', route: '/analytics', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Analytics + component: Analytics, }, { key: 'settings', @@ -106,42 +106,42 @@ const getLamassuRoutes = () => [ label: 'Commissions', route: '/settings/commissions', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Commissions + component: Commissions, }, { key: namespaces.LOCALE, label: 'Locales', route: '/settings/locale', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Locales + component: Locales, }, { key: namespaces.CASH_OUT, label: 'Cash-out', route: '/settings/cash-out', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Cashout + component: Cashout, }, { key: namespaces.NOTIFICATIONS, label: 'Notifications', route: '/settings/notifications', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Notifications + component: Notifications, }, { key: 'services', label: 'Third-party services', route: '/settings/3rd-party-services', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Services + component: Services, }, { key: namespaces.WALLETS, label: 'Wallet', route: '/settings/wallet-settings', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: WalletSettings + component: WalletSettings, }, { key: namespaces.OPERATOR_INFO, @@ -154,7 +154,7 @@ const getLamassuRoutes = () => [ ) @@ -165,46 +165,46 @@ const getLamassuRoutes = () => [ label: 'Contact information', route: '/settings/operator-info/contact-info', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: ContactInfo + component: ContactInfo, }, { key: 'receipt-printing', label: 'Receipt', route: '/settings/operator-info/receipt-printing', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: ReceiptPrinting + component: ReceiptPrinting, }, { key: 'sms-notices', label: 'SMS notices', route: '/settings/operator-info/sms-notices', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: SMSNotices + component: SMSNotices, }, { key: 'coin-atm-radar', label: 'Coin ATM Radar', route: '/settings/operator-info/coin-atm-radar', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: CoinAtmRadar + component: CoinAtmRadar, }, { key: 'terms-conditions', label: 'Terms & Conditions', route: '/settings/operator-info/terms-conditions', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: TermsConditions + component: TermsConditions, }, { key: 'machine-screens', label: 'Machine screens', route: '/settings/operator-info/machine-screens', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: MachineScreens - } - ] - } - ] + component: MachineScreens, + }, + ], + }, + ], }, { key: 'compliance', @@ -220,21 +220,21 @@ const getLamassuRoutes = () => [ label: 'Triggers', route: '/compliance/triggers', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Triggers + component: Triggers, }, { key: 'customers', label: 'Customers', route: '/compliance/customers', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Customers + component: Customers, }, { key: 'blacklist', label: 'Blacklist', route: '/compliance/blacklist', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: Blacklist + component: Blacklist, }, { key: 'loyalty', @@ -247,7 +247,7 @@ const getLamassuRoutes = () => [ ) @@ -258,24 +258,24 @@ const getLamassuRoutes = () => [ label: 'Individual discounts', route: '/compliance/loyalty/individual-discounts', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: IndividualDiscounts + component: IndividualDiscounts, }, { key: 'promo-codes', label: 'Promo codes', route: '/compliance/loyalty/codes', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: PromoCodes - } - ] + component: PromoCodes, + }, + ], }, { key: 'customer', route: '/compliance/customer/:id', allowedRoles: [ROLES.USER, ROLES.SUPERUSER], - component: CustomerProfile - } - ] + component: CustomerProfile, + }, + ], }, { key: 'system', @@ -291,17 +291,17 @@ const getLamassuRoutes = () => [ label: 'User management', route: '/system/user-management', allowedRoles: [ROLES.SUPERUSER], - component: UserManagement + component: UserManagement, }, { key: 'session-management', label: 'Session management', route: '/system/session-management', allowedRoles: [ROLES.SUPERUSER], - component: SessionManagement - } - ] - } + component: SessionManagement, + }, + ], + }, ] export default getLamassuRoutes diff --git a/packages/admin-ui/src/routing/routes.jsx b/packages/admin-ui/src/routing/routes.jsx index 8fd3c366..baa349e2 100644 --- a/packages/admin-ui/src/routing/routes.jsx +++ b/packages/admin-ui/src/routing/routes.jsx @@ -7,7 +7,7 @@ import { Redirect, Switch, useHistory, - useLocation + useLocation, } from 'react-router-dom' import Login from 'src/pages/Authentication/Login' import Register from 'src/pages/Authentication/Register' @@ -30,7 +30,7 @@ const tree = getLamassuRoutes() const map = R.map(R.when(R.has('children'), R.prop('children'))) const mappedRoutes = R.compose(R.flatten, map)(tree) const parentRoutes = R.filter(R.has('children'))(mappedRoutes).concat( - R.filter(R.has('children'))(tree) + R.filter(R.has('children'))(tree), ) const leafRoutes = R.compose(R.flatten, map)(mappedRoutes) @@ -41,8 +41,8 @@ const hasSidebar = route => R.compose( R.flatten, R.map(R.prop('children')), - R.filter(R.has('children')) - )(mappedRoutes) + R.filter(R.has('children')), + )(mappedRoutes), ) const getParent = route => @@ -51,9 +51,9 @@ const getParent = route => 'route', R.dropLast( 1, - R.dropLastWhile(x => x !== '/', route) - ) - ) + R.dropLastWhile(x => x !== '/', route), + ), + ), )(flattened) const Routes = () => { @@ -68,7 +68,7 @@ const Routes = () => { '/login', '/register', '/resetpassword', - '/reset2fa' + '/reset2fa', ] if (!wizardTested && !R.contains(location.pathname)(dontTriggerPages)) { @@ -94,7 +94,7 @@ const Routes = () => { R.findIndex(R.propEq('route', location.state.prev))(leafRoutes) > R.findIndex(R.propEq('route', location.pathname))(leafRoutes) ? 'right' - : 'left' + : 'left', } : { timeout: 400 } diff --git a/packages/admin-ui/src/routing/utils.js b/packages/admin-ui/src/routing/utils.js index 6222bfe4..39cbfbf0 100644 --- a/packages/admin-ui/src/routing/utils.js +++ b/packages/admin-ui/src/routing/utils.js @@ -7,5 +7,5 @@ export const isLoggedIn = userData => export const ROLES = { USER: 'user', - SUPERUSER: 'superuser' + SUPERUSER: 'superuser', } diff --git a/packages/admin-ui/src/styling/global/fonts.css b/packages/admin-ui/src/styling/global/fonts.css index e5623b10..cb63e217 100644 --- a/packages/admin-ui/src/styling/global/fonts.css +++ b/packages/admin-ui/src/styling/global/fonts.css @@ -26,14 +26,18 @@ font-family: 'Mont'; font-weight: 900; font-style: normal; - src: url('/fonts/MontHeavy/mont-heavy-webfont.woff2') format('woff2'), url('/fonts/MontHeavy/mont-heavy-webfont.woff') format('woff'); + src: + url('/fonts/MontHeavy/mont-heavy-webfont.woff2') format('woff2'), + url('/fonts/MontHeavy/mont-heavy-webfont.woff') format('woff'); } @font-face { font-family: 'Mont'; font-weight: 700; font-style: normal; - src: url('/fonts/MontHeavy/mont-bold-webfont.woff2') format('woff2'), url('/fonts/MontHeavy/mont-bold-webfont.woff') format('woff'); + src: + url('/fonts/MontHeavy/mont-bold-webfont.woff2') format('woff2'), + url('/fonts/MontHeavy/mont-bold-webfont.woff') format('woff'); } /*! @@ -63,14 +67,18 @@ font-family: 'MuseoSans'; font-weight: 500; font-style: normal; - src: url("/fonts/MuseoSans/MuseoSans_500-webfont.woff2") format("woff2"), url("/fonts/MuseoSans/MuseoSans_500-webfont.woff") format("woff"); + src: + url('/fonts/MuseoSans/MuseoSans_500-webfont.woff2') format('woff2'), + url('/fonts/MuseoSans/MuseoSans_500-webfont.woff') format('woff'); } @font-face { font-family: 'MuseoSans'; font-weight: 700; font-style: normal; - src: url("/fonts/MuseoSans/MuseoSans_700-webfont.woff2") format("woff2"), url("/fonts/MuseoSans/MuseoSans_700-webfont.woff") format("woff"); + src: + url('/fonts/MuseoSans/MuseoSans_700-webfont.woff2') format('woff2'), + url('/fonts/MuseoSans/MuseoSans_700-webfont.woff') format('woff'); } /* BP-mono Freely distributed at http://backpacker.gr/fonts/5 */ @@ -78,5 +86,5 @@ font-family: 'BPmono'; font-weight: 500; font-style: normal; - src: url("/fonts/BPmono/BPmono.ttf") format("truetype"), -} \ No newline at end of file + src: url('/fonts/BPmono/BPmono.ttf') format('truetype'); +} diff --git a/packages/admin-ui/src/styling/global/global.css b/packages/admin-ui/src/styling/global/global.css index bd8f92da..3b65166d 100644 --- a/packages/admin-ui/src/styling/global/global.css +++ b/packages/admin-ui/src/styling/global/global.css @@ -1,9 +1,9 @@ -@import "./fonts.css"; +@import './fonts.css'; /* TODO remove important */ @layer theme, base, mui, components, utilities; -@import "tailwindcss/theme.css" layer(theme); -@import "tailwindcss/utilities.css" layer(utilities) important; +@import 'tailwindcss/theme.css' layer(theme); +@import 'tailwindcss/utilities.css' layer(utilities) important; :root { --zodiac: #1b2559; @@ -12,7 +12,6 @@ --spring3: #ecfbef; --spring4: #3fd07e; - --comet: #5f668a; --comet2: #72799d; --comet3: #525772; @@ -27,7 +26,7 @@ --java: #16d6d3; --neon: #5a67ff; - --malachite: #00CD5A; + --malachite: #00cd5a; --orange-yellow: #ffcc00; --mont: 'Mont'; @@ -110,14 +109,14 @@ html { } a::-moz-focus-inner, -input[type="submit"]::-moz-focus-inner, -input[type="button"]::-moz-focus-inner { +input[type='submit']::-moz-focus-inner, +input[type='button']::-moz-focus-inner { border: 0; } a::-moz-focus-inner, -input[type="submit"]::-moz-focus-inner, -input[type="button"]::-moz-focus-inner { +input[type='submit']::-moz-focus-inner, +input[type='button']::-moz-focus-inner { border: 0; } diff --git a/packages/admin-ui/src/styling/helpers.js b/packages/admin-ui/src/styling/helpers.js index 0854a253..8934e1d5 100644 --- a/packages/admin-ui/src/styling/helpers.js +++ b/packages/admin-ui/src/styling/helpers.js @@ -2,7 +2,7 @@ import { inputFontSize, inputFontSizeLg, inputFontSizeSm, - inputFontWeightBold + inputFontWeightBold, } from './variables' const respondTo = breakpoint => @@ -20,7 +20,7 @@ const bySize = size => { } const bold = { - fontWeight: inputFontWeightBold + fontWeight: inputFontWeightBold, } export { respondTo, bySize, bold } diff --git a/packages/admin-ui/src/styling/icons/ID/card/comet.svg b/packages/admin-ui/src/styling/icons/ID/card/comet.svg index 0163f2cb..dc06e354 100644 --- a/packages/admin-ui/src/styling/icons/ID/card/comet.svg +++ b/packages/admin-ui/src/styling/icons/ID/card/comet.svg @@ -1,10 +1,14 @@ - + icon/ID/card/new/comet - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/card/tomato.svg b/packages/admin-ui/src/styling/icons/ID/card/tomato.svg index a3effa2d..af75dc22 100644 --- a/packages/admin-ui/src/styling/icons/ID/card/tomato.svg +++ b/packages/admin-ui/src/styling/icons/ID/card/tomato.svg @@ -1,10 +1,14 @@ - + icon/ID/card/new/tomato - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/card/white.svg b/packages/admin-ui/src/styling/icons/ID/card/white.svg index 3448f195..dca48378 100644 --- a/packages/admin-ui/src/styling/icons/ID/card/white.svg +++ b/packages/admin-ui/src/styling/icons/ID/card/white.svg @@ -1,10 +1,14 @@ - + icon/ID/card/new/white - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/card/zodiac.svg b/packages/admin-ui/src/styling/icons/ID/card/zodiac.svg index 14a0fc39..b22569b3 100644 --- a/packages/admin-ui/src/styling/icons/ID/card/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/ID/card/zodiac.svg @@ -1,7 +1,9 @@ - + icon/ID/card/new/zodiac - + diff --git a/packages/admin-ui/src/styling/icons/ID/phone/comet.svg b/packages/admin-ui/src/styling/icons/ID/phone/comet.svg index ea786af7..1748dc14 100644 --- a/packages/admin-ui/src/styling/icons/ID/phone/comet.svg +++ b/packages/admin-ui/src/styling/icons/ID/phone/comet.svg @@ -1,7 +1,10 @@ - + icon/ID/phone/new/comet - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/phone/tomato.svg b/packages/admin-ui/src/styling/icons/ID/phone/tomato.svg index ac4320cc..ad479f62 100644 --- a/packages/admin-ui/src/styling/icons/ID/phone/tomato.svg +++ b/packages/admin-ui/src/styling/icons/ID/phone/tomato.svg @@ -1,7 +1,10 @@ - + icon/ID/phone/new/tomato - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/phone/white.svg b/packages/admin-ui/src/styling/icons/ID/phone/white.svg index 6485d8b9..8a8a9e16 100644 --- a/packages/admin-ui/src/styling/icons/ID/phone/white.svg +++ b/packages/admin-ui/src/styling/icons/ID/phone/white.svg @@ -1,7 +1,10 @@ - + icon/ID/phone/new/white - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/phone/zodiac.svg b/packages/admin-ui/src/styling/icons/ID/phone/zodiac.svg index 6454e804..f0586e26 100644 --- a/packages/admin-ui/src/styling/icons/ID/phone/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/ID/phone/zodiac.svg @@ -1,7 +1,10 @@ - + icon/ID/phone/new/zodiac - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/photo/comet.svg b/packages/admin-ui/src/styling/icons/ID/photo/comet.svg index ddeb1e5a..8d120e99 100644 --- a/packages/admin-ui/src/styling/icons/ID/photo/comet.svg +++ b/packages/admin-ui/src/styling/icons/ID/photo/comet.svg @@ -1,7 +1,10 @@ - + icon/ID/photo/new/comet - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/photo/crossed-camera.svg b/packages/admin-ui/src/styling/icons/ID/photo/crossed-camera.svg index 38a5e2e4..625733ea 100644 --- a/packages/admin-ui/src/styling/icons/ID/photo/crossed-camera.svg +++ b/packages/admin-ui/src/styling/icons/ID/photo/crossed-camera.svg @@ -1,14 +1,17 @@ - + Created with Sketch. - + - + diff --git a/packages/admin-ui/src/styling/icons/ID/photo/tomato.svg b/packages/admin-ui/src/styling/icons/ID/photo/tomato.svg index 77b2e366..fce78c6e 100644 --- a/packages/admin-ui/src/styling/icons/ID/photo/tomato.svg +++ b/packages/admin-ui/src/styling/icons/ID/photo/tomato.svg @@ -1,7 +1,10 @@ - + icon/ID/photo/new/tomato - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/photo/white.svg b/packages/admin-ui/src/styling/icons/ID/photo/white.svg index 1c541cc7..29e4b507 100644 --- a/packages/admin-ui/src/styling/icons/ID/photo/white.svg +++ b/packages/admin-ui/src/styling/icons/ID/photo/white.svg @@ -1,7 +1,10 @@ - + icon/ID/photo/new/white - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/ID/photo/zodiac.svg b/packages/admin-ui/src/styling/icons/ID/photo/zodiac.svg index b02095e1..7ab1193c 100644 --- a/packages/admin-ui/src/styling/icons/ID/photo/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/ID/photo/zodiac.svg @@ -1,7 +1,10 @@ - + icon/ID/photo/new/zodiac - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/arrow/comet.svg b/packages/admin-ui/src/styling/icons/action/arrow/comet.svg index 1f46001f..500eff33 100644 --- a/packages/admin-ui/src/styling/icons/action/arrow/comet.svg +++ b/packages/admin-ui/src/styling/icons/action/arrow/comet.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/arrow/regular.svg b/packages/admin-ui/src/styling/icons/action/arrow/regular.svg index 66bcf2f4..f3919756 100644 --- a/packages/admin-ui/src/styling/icons/action/arrow/regular.svg +++ b/packages/admin-ui/src/styling/icons/action/arrow/regular.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/arrow/white.svg b/packages/admin-ui/src/styling/icons/action/arrow/white.svg index 430756e6..5f29c3cb 100644 --- a/packages/admin-ui/src/styling/icons/action/arrow/white.svg +++ b/packages/admin-ui/src/styling/icons/action/arrow/white.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/arrow/zodiac.svg b/packages/admin-ui/src/styling/icons/action/arrow/zodiac.svg index 164bb129..e37328fe 100644 --- a/packages/admin-ui/src/styling/icons/action/arrow/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/action/arrow/zodiac.svg @@ -1,15 +1,16 @@ - - - - - - - - - + width="13" + height="8" + viewBox="0 0 13 8" + xmlns="http://www.w3.org/2000/svg"> + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/close/comet.svg b/packages/admin-ui/src/styling/icons/action/close/comet.svg index 17e28139..e3590b84 100644 --- a/packages/admin-ui/src/styling/icons/action/close/comet.svg +++ b/packages/admin-ui/src/styling/icons/action/close/comet.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/close/white.svg b/packages/admin-ui/src/styling/icons/action/close/white.svg index c0ef73c9..1443838f 100644 --- a/packages/admin-ui/src/styling/icons/action/close/white.svg +++ b/packages/admin-ui/src/styling/icons/action/close/white.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/close/zodiac.svg b/packages/admin-ui/src/styling/icons/action/close/zodiac.svg index 9f3de3ae..30535870 100644 --- a/packages/admin-ui/src/styling/icons/action/close/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/action/close/zodiac.svg @@ -1,13 +1,14 @@ - + -Created with Sketch. - + Created with Sketch. + - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/copy/copy.svg b/packages/admin-ui/src/styling/icons/action/copy/copy.svg index 4f7cb694..382ae6b8 100644 --- a/packages/admin-ui/src/styling/icons/action/copy/copy.svg +++ b/packages/admin-ui/src/styling/icons/action/copy/copy.svg @@ -1,12 +1,15 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/action/copy/white.svg b/packages/admin-ui/src/styling/icons/action/copy/white.svg index 21ad5401..541e0d80 100644 --- a/packages/admin-ui/src/styling/icons/action/copy/white.svg +++ b/packages/admin-ui/src/styling/icons/action/copy/white.svg @@ -1,9 +1,13 @@ - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/copy/zodiac.svg b/packages/admin-ui/src/styling/icons/action/copy/zodiac.svg index 1e54058c..ca973f97 100644 --- a/packages/admin-ui/src/styling/icons/action/copy/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/action/copy/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/delete/disabled.svg b/packages/admin-ui/src/styling/icons/action/delete/disabled.svg index 4aabb607..170a1ca6 100644 --- a/packages/admin-ui/src/styling/icons/action/delete/disabled.svg +++ b/packages/admin-ui/src/styling/icons/action/delete/disabled.svg @@ -1,29 +1,29 @@ - - - - - - - - - - - + width="22" + height="22" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + + + + + + + + + + + - \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/delete/enabled.svg b/packages/admin-ui/src/styling/icons/action/delete/enabled.svg index a3960659..46844490 100644 --- a/packages/admin-ui/src/styling/icons/action/delete/enabled.svg +++ b/packages/admin-ui/src/styling/icons/action/delete/enabled.svg @@ -1,12 +1,16 @@ - + Created with Sketch. - + - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/delete/white.svg b/packages/admin-ui/src/styling/icons/action/delete/white.svg index 087327c2..108e9347 100644 --- a/packages/admin-ui/src/styling/icons/action/delete/white.svg +++ b/packages/admin-ui/src/styling/icons/action/delete/white.svg @@ -1,5 +1,6 @@ - + Created with Sketch. @@ -10,8 +11,10 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/action/edit/comet.svg b/packages/admin-ui/src/styling/icons/action/edit/comet.svg index 10c785d1..e1bc5672 100644 --- a/packages/admin-ui/src/styling/icons/action/edit/comet.svg +++ b/packages/admin-ui/src/styling/icons/action/edit/comet.svg @@ -1,8 +1,11 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/edit/disabled.svg b/packages/admin-ui/src/styling/icons/action/edit/disabled.svg index 76b31834..a17fba71 100644 --- a/packages/admin-ui/src/styling/icons/action/edit/disabled.svg +++ b/packages/admin-ui/src/styling/icons/action/edit/disabled.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/edit/enabled.svg b/packages/admin-ui/src/styling/icons/action/edit/enabled.svg index deea0765..19152575 100644 --- a/packages/admin-ui/src/styling/icons/action/edit/enabled.svg +++ b/packages/admin-ui/src/styling/icons/action/edit/enabled.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/edit/white.svg b/packages/admin-ui/src/styling/icons/action/edit/white.svg index 1464e3dc..bb82ec6f 100644 --- a/packages/admin-ui/src/styling/icons/action/edit/white.svg +++ b/packages/admin-ui/src/styling/icons/action/edit/white.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/expand/closed.svg b/packages/admin-ui/src/styling/icons/action/expand/closed.svg index 4b515464..26e2cfe3 100644 --- a/packages/admin-ui/src/styling/icons/action/expand/closed.svg +++ b/packages/admin-ui/src/styling/icons/action/expand/closed.svg @@ -1,5 +1,6 @@ - + Created with Sketch. diff --git a/packages/admin-ui/src/styling/icons/action/expand/open.svg b/packages/admin-ui/src/styling/icons/action/expand/open.svg index 2c324b69..053a7b07 100644 --- a/packages/admin-ui/src/styling/icons/action/expand/open.svg +++ b/packages/admin-ui/src/styling/icons/action/expand/open.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/external link/white.svg b/packages/admin-ui/src/styling/icons/action/external link/white.svg index 4f03b0dc..348b54f0 100644 --- a/packages/admin-ui/src/styling/icons/action/external link/white.svg +++ b/packages/admin-ui/src/styling/icons/action/external link/white.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/action/external link/zodiac.svg b/packages/admin-ui/src/styling/icons/action/external link/zodiac.svg index abc9beb5..7a9ff1cb 100644 --- a/packages/admin-ui/src/styling/icons/action/external link/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/action/external link/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/action/help/white.svg b/packages/admin-ui/src/styling/icons/action/help/white.svg index 80960209..58ee0a08 100644 --- a/packages/admin-ui/src/styling/icons/action/help/white.svg +++ b/packages/admin-ui/src/styling/icons/action/help/white.svg @@ -1,10 +1,14 @@ - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/help/zodiac.svg b/packages/admin-ui/src/styling/icons/action/help/zodiac.svg index 4ad70c8d..3a776167 100644 --- a/packages/admin-ui/src/styling/icons/action/help/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/action/help/zodiac.svg @@ -1,10 +1,14 @@ - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/action/wrench/zodiac.svg b/packages/admin-ui/src/styling/icons/action/wrench/zodiac.svg index 0cf3417a..57be21d9 100644 --- a/packages/admin-ui/src/styling/icons/action/wrench/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/action/wrench/zodiac.svg @@ -1,10 +1,13 @@ - - + + - + diff --git a/packages/admin-ui/src/styling/icons/arrow/carousel-left-arrow.svg b/packages/admin-ui/src/styling/icons/arrow/carousel-left-arrow.svg index e6518d3f..9a6eb387 100644 --- a/packages/admin-ui/src/styling/icons/arrow/carousel-left-arrow.svg +++ b/packages/admin-ui/src/styling/icons/arrow/carousel-left-arrow.svg @@ -1,6 +1,8 @@ - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/arrow/carousel-right-arrow.svg b/packages/admin-ui/src/styling/icons/arrow/carousel-right-arrow.svg index 3f812933..42e09bf2 100644 --- a/packages/admin-ui/src/styling/icons/arrow/carousel-right-arrow.svg +++ b/packages/admin-ui/src/styling/icons/arrow/carousel-right-arrow.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/arrow/download_logs.svg b/packages/admin-ui/src/styling/icons/arrow/download_logs.svg index 19eaf13d..f4a8ed4d 100644 --- a/packages/admin-ui/src/styling/icons/arrow/download_logs.svg +++ b/packages/admin-ui/src/styling/icons/arrow/download_logs.svg @@ -1,11 +1,15 @@ - + arrow download logs Created with Sketch. - - - + + + diff --git a/packages/admin-ui/src/styling/icons/arrow/month_change.svg b/packages/admin-ui/src/styling/icons/arrow/month_change.svg index 92a7e526..29b02986 100644 --- a/packages/admin-ui/src/styling/icons/arrow/month_change.svg +++ b/packages/admin-ui/src/styling/icons/arrow/month_change.svg @@ -1,15 +1,19 @@ - + Created with Sketch. - + - - + + diff --git a/packages/admin-ui/src/styling/icons/arrow/month_change_right.svg b/packages/admin-ui/src/styling/icons/arrow/month_change_right.svg index 465955d9..7217a0e3 100644 --- a/packages/admin-ui/src/styling/icons/arrow/month_change_right.svg +++ b/packages/admin-ui/src/styling/icons/arrow/month_change_right.svg @@ -1,15 +1,19 @@ - + Created with Sketch. - + - - + + diff --git a/packages/admin-ui/src/styling/icons/arrow/transaction.svg b/packages/admin-ui/src/styling/icons/arrow/transaction.svg index 84501d20..0348a1b8 100644 --- a/packages/admin-ui/src/styling/icons/arrow/transaction.svg +++ b/packages/admin-ui/src/styling/icons/arrow/transaction.svg @@ -1,14 +1,18 @@ - - - + + + - + diff --git a/packages/admin-ui/src/styling/icons/button/add note/white.svg b/packages/admin-ui/src/styling/icons/button/add note/white.svg index 6c1c9ab2..05183ced 100644 --- a/packages/admin-ui/src/styling/icons/button/add note/white.svg +++ b/packages/admin-ui/src/styling/icons/button/add note/white.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + @@ -12,7 +14,8 @@ - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/add note/zodiac.svg b/packages/admin-ui/src/styling/icons/button/add note/zodiac.svg index 5bf3b738..ac7d3a07 100644 --- a/packages/admin-ui/src/styling/icons/button/add note/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/add note/zodiac.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + @@ -13,7 +15,8 @@ - + diff --git a/packages/admin-ui/src/styling/icons/button/add-note/white.svg b/packages/admin-ui/src/styling/icons/button/add-note/white.svg index 42862d8d..d180298a 100644 --- a/packages/admin-ui/src/styling/icons/button/add-note/white.svg +++ b/packages/admin-ui/src/styling/icons/button/add-note/white.svg @@ -1,17 +1,18 @@ - + - - - - - - - + + + + + + + diff --git a/packages/admin-ui/src/styling/icons/button/add-note/zodiac.svg b/packages/admin-ui/src/styling/icons/button/add-note/zodiac.svg index 92be9843..0eff2aad 100644 --- a/packages/admin-ui/src/styling/icons/button/add-note/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/add-note/zodiac.svg @@ -1,11 +1,14 @@ - + Created with Sketch. - - + + @@ -14,7 +17,8 @@ - + diff --git a/packages/admin-ui/src/styling/icons/button/add/white.svg b/packages/admin-ui/src/styling/icons/button/add/white.svg index efc8ebe5..64f33699 100644 --- a/packages/admin-ui/src/styling/icons/button/add/white.svg +++ b/packages/admin-ui/src/styling/icons/button/add/white.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/button/add/zodiac.svg b/packages/admin-ui/src/styling/icons/button/add/zodiac.svg index 02cd42ad..eee13c17 100644 --- a/packages/admin-ui/src/styling/icons/button/add/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/add/zodiac.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/button/authorize/white.svg b/packages/admin-ui/src/styling/icons/button/authorize/white.svg index 54a67625..13243cc4 100644 --- a/packages/admin-ui/src/styling/icons/button/authorize/white.svg +++ b/packages/admin-ui/src/styling/icons/button/authorize/white.svg @@ -4,6 +4,7 @@ Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/authorize/zodiac.svg b/packages/admin-ui/src/styling/icons/button/authorize/zodiac.svg index 805dd29d..c0f236a4 100644 --- a/packages/admin-ui/src/styling/icons/button/authorize/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/authorize/zodiac.svg @@ -6,6 +6,7 @@ - + diff --git a/packages/admin-ui/src/styling/icons/button/blacklist/white.svg b/packages/admin-ui/src/styling/icons/button/blacklist/white.svg index 2723fad2..2b060ce8 100644 --- a/packages/admin-ui/src/styling/icons/button/blacklist/white.svg +++ b/packages/admin-ui/src/styling/icons/button/blacklist/white.svg @@ -1,10 +1,12 @@ - + Created with Sketch. - + @@ -12,7 +14,8 @@ - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/blacklist/zodiac.svg b/packages/admin-ui/src/styling/icons/button/blacklist/zodiac.svg index 83e5d63b..5f12bfa7 100644 --- a/packages/admin-ui/src/styling/icons/button/blacklist/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/blacklist/zodiac.svg @@ -1,10 +1,12 @@ - + Created with Sketch. - + @@ -13,7 +15,8 @@ - + diff --git a/packages/admin-ui/src/styling/icons/button/block/white.svg b/packages/admin-ui/src/styling/icons/button/block/white.svg index 71606bc4..d959fb3f 100644 --- a/packages/admin-ui/src/styling/icons/button/block/white.svg +++ b/packages/admin-ui/src/styling/icons/button/block/white.svg @@ -2,7 +2,8 @@ Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/block/zodiac.svg b/packages/admin-ui/src/styling/icons/button/block/zodiac.svg index 0d2abcb7..0c746ac2 100644 --- a/packages/admin-ui/src/styling/icons/button/block/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/block/zodiac.svg @@ -2,7 +2,8 @@ Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/cancel/white.svg b/packages/admin-ui/src/styling/icons/button/cancel/white.svg index 632b1027..2969bbd8 100644 --- a/packages/admin-ui/src/styling/icons/button/cancel/white.svg +++ b/packages/admin-ui/src/styling/icons/button/cancel/white.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/cancel/zodiac.svg b/packages/admin-ui/src/styling/icons/button/cancel/zodiac.svg index 852f9dc3..4e03443a 100644 --- a/packages/admin-ui/src/styling/icons/button/cancel/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/cancel/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/configure/white.svg b/packages/admin-ui/src/styling/icons/button/configure/white.svg index dee9aff8..d3b1aa63 100644 --- a/packages/admin-ui/src/styling/icons/button/configure/white.svg +++ b/packages/admin-ui/src/styling/icons/button/configure/white.svg @@ -1,9 +1,13 @@ - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/configure/zodiac.svg b/packages/admin-ui/src/styling/icons/button/configure/zodiac.svg index 8e7462c7..617cad3d 100644 --- a/packages/admin-ui/src/styling/icons/button/configure/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/configure/zodiac.svg @@ -1,9 +1,13 @@ - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/data/white.svg b/packages/admin-ui/src/styling/icons/button/data/white.svg index 4e50be4f..3b2fb541 100644 --- a/packages/admin-ui/src/styling/icons/button/data/white.svg +++ b/packages/admin-ui/src/styling/icons/button/data/white.svg @@ -1,14 +1,18 @@ - + - - + + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/data/zodiac.svg b/packages/admin-ui/src/styling/icons/button/data/zodiac.svg index 66e0049c..e7c76b3c 100644 --- a/packages/admin-ui/src/styling/icons/button/data/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/data/zodiac.svg @@ -1,14 +1,18 @@ - + - - + + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/discount/comet.svg b/packages/admin-ui/src/styling/icons/button/discount/comet.svg index 4ad0de56..31e4114a 100644 --- a/packages/admin-ui/src/styling/icons/button/discount/comet.svg +++ b/packages/admin-ui/src/styling/icons/button/discount/comet.svg @@ -1,7 +1,10 @@ - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/discount/white.svg b/packages/admin-ui/src/styling/icons/button/discount/white.svg index 56d5d8ad..5e58402a 100644 --- a/packages/admin-ui/src/styling/icons/button/discount/white.svg +++ b/packages/admin-ui/src/styling/icons/button/discount/white.svg @@ -1,7 +1,10 @@ - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/discount/zodiac.svg b/packages/admin-ui/src/styling/icons/button/discount/zodiac.svg index 15d095d6..1295f4a9 100644 --- a/packages/admin-ui/src/styling/icons/button/discount/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/discount/zodiac.svg @@ -1,7 +1,10 @@ - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/download/white.svg b/packages/admin-ui/src/styling/icons/button/download/white.svg index 8ceed415..07ec7552 100644 --- a/packages/admin-ui/src/styling/icons/button/download/white.svg +++ b/packages/admin-ui/src/styling/icons/button/download/white.svg @@ -1,12 +1,15 @@ - + Created with Sketch. - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/download/zodiac.svg b/packages/admin-ui/src/styling/icons/button/download/zodiac.svg index 40db4acd..02d68d43 100644 --- a/packages/admin-ui/src/styling/icons/button/download/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/download/zodiac.svg @@ -1,12 +1,15 @@ - + Created with Sketch. - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/edit/white.svg b/packages/admin-ui/src/styling/icons/button/edit/white.svg index 717b213d..443c83de 100644 --- a/packages/admin-ui/src/styling/icons/button/edit/white.svg +++ b/packages/admin-ui/src/styling/icons/button/edit/white.svg @@ -1,9 +1,13 @@ - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/edit/zodiac.svg b/packages/admin-ui/src/styling/icons/button/edit/zodiac.svg index e112bb5a..378791ca 100644 --- a/packages/admin-ui/src/styling/icons/button/edit/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/edit/zodiac.svg @@ -1,9 +1,13 @@ - + Created with Sketch. - - - + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/export to PDF/white.svg b/packages/admin-ui/src/styling/icons/button/export to PDF/white.svg index e32a1e35..c48bcb63 100644 --- a/packages/admin-ui/src/styling/icons/button/export to PDF/white.svg +++ b/packages/admin-ui/src/styling/icons/button/export to PDF/white.svg @@ -1,5 +1,6 @@ - + Created with Sketch. @@ -8,10 +9,13 @@ - - + + - + diff --git a/packages/admin-ui/src/styling/icons/button/export to PDF/zodiac.svg b/packages/admin-ui/src/styling/icons/button/export to PDF/zodiac.svg index bebece76..b6fe94af 100644 --- a/packages/admin-ui/src/styling/icons/button/export to PDF/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/export to PDF/zodiac.svg @@ -1,5 +1,6 @@ - + Created with Sketch. @@ -8,10 +9,13 @@ - - + + - + diff --git a/packages/admin-ui/src/styling/icons/button/export-pdf/white.svg b/packages/admin-ui/src/styling/icons/button/export-pdf/white.svg index 90fa4f2e..fa8d163e 100644 --- a/packages/admin-ui/src/styling/icons/button/export-pdf/white.svg +++ b/packages/admin-ui/src/styling/icons/button/export-pdf/white.svg @@ -1,10 +1,15 @@ - - - - - - - - + + + + + + + + diff --git a/packages/admin-ui/src/styling/icons/button/filter/white.svg b/packages/admin-ui/src/styling/icons/button/filter/white.svg index a3542fe9..960997a1 100644 --- a/packages/admin-ui/src/styling/icons/button/filter/white.svg +++ b/packages/admin-ui/src/styling/icons/button/filter/white.svg @@ -1,7 +1,9 @@ - + icon/button/filter/white - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/filter/zodiac.svg b/packages/admin-ui/src/styling/icons/button/filter/zodiac.svg index 51cdefd6..af292f68 100644 --- a/packages/admin-ui/src/styling/icons/button/filter/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/filter/zodiac.svg @@ -1,7 +1,9 @@ - + icon/button/filter/zodiac - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/ignore/white.svg b/packages/admin-ui/src/styling/icons/button/ignore/white.svg index 6e64b340..08c44091 100644 --- a/packages/admin-ui/src/styling/icons/button/ignore/white.svg +++ b/packages/admin-ui/src/styling/icons/button/ignore/white.svg @@ -1,5 +1,6 @@ - + Created with Sketch. diff --git a/packages/admin-ui/src/styling/icons/button/ignore/zodiac.svg b/packages/admin-ui/src/styling/icons/button/ignore/zodiac.svg index 7fb57a3e..74fb3a30 100644 --- a/packages/admin-ui/src/styling/icons/button/ignore/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/ignore/zodiac.svg @@ -1,5 +1,6 @@ - + Created with Sketch. diff --git a/packages/admin-ui/src/styling/icons/button/key/white.svg b/packages/admin-ui/src/styling/icons/button/key/white.svg index ec28f490..53114386 100644 --- a/packages/admin-ui/src/styling/icons/button/key/white.svg +++ b/packages/admin-ui/src/styling/icons/button/key/white.svg @@ -1,11 +1,14 @@ - + icon/button/key/white - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/key/zodiac.svg b/packages/admin-ui/src/styling/icons/button/key/zodiac.svg index cd9d1645..73b2c34f 100644 --- a/packages/admin-ui/src/styling/icons/button/key/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/key/zodiac.svg @@ -1,11 +1,14 @@ - + icon/button/key/zodiac - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/link/export.svg b/packages/admin-ui/src/styling/icons/button/link/export.svg index 2a46ca2f..6ac38d04 100644 --- a/packages/admin-ui/src/styling/icons/button/link/export.svg +++ b/packages/admin-ui/src/styling/icons/button/link/export.svg @@ -1,5 +1,6 @@ - + Created with Sketch. @@ -8,10 +9,13 @@ - - + + - + diff --git a/packages/admin-ui/src/styling/icons/button/link/white.svg b/packages/admin-ui/src/styling/icons/button/link/white.svg index decd7fc4..6e65cddf 100644 --- a/packages/admin-ui/src/styling/icons/button/link/white.svg +++ b/packages/admin-ui/src/styling/icons/button/link/white.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/link/zodiac.svg b/packages/admin-ui/src/styling/icons/button/link/zodiac.svg index 20e48189..bf3bd27a 100644 --- a/packages/admin-ui/src/styling/icons/button/link/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/link/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/lock/white.svg b/packages/admin-ui/src/styling/icons/button/lock/white.svg index 53b5e362..db4395ec 100644 --- a/packages/admin-ui/src/styling/icons/button/lock/white.svg +++ b/packages/admin-ui/src/styling/icons/button/lock/white.svg @@ -1,10 +1,13 @@ - + icon/button/lock/white - - + + diff --git a/packages/admin-ui/src/styling/icons/button/lock/zodiac.svg b/packages/admin-ui/src/styling/icons/button/lock/zodiac.svg index fd064efc..a637c03b 100644 --- a/packages/admin-ui/src/styling/icons/button/lock/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/lock/zodiac.svg @@ -1,10 +1,13 @@ - + icon/button/lock/zodiac - - + + diff --git a/packages/admin-ui/src/styling/icons/button/photo/white-resized.svg b/packages/admin-ui/src/styling/icons/button/photo/white-resized.svg index fc199166..f25831cf 100644 --- a/packages/admin-ui/src/styling/icons/button/photo/white-resized.svg +++ b/packages/admin-ui/src/styling/icons/button/photo/white-resized.svg @@ -1,19 +1,29 @@ - + - - + + - + - + - - + + diff --git a/packages/admin-ui/src/styling/icons/button/photo/zodiac-resized.svg b/packages/admin-ui/src/styling/icons/button/photo/zodiac-resized.svg index 1065bc1f..a6137d47 100644 --- a/packages/admin-ui/src/styling/icons/button/photo/zodiac-resized.svg +++ b/packages/admin-ui/src/styling/icons/button/photo/zodiac-resized.svg @@ -1,18 +1,24 @@ - + - + - + - - + + diff --git a/packages/admin-ui/src/styling/icons/button/reboot/white.svg b/packages/admin-ui/src/styling/icons/button/reboot/white.svg index 0531c087..6158eba1 100644 --- a/packages/admin-ui/src/styling/icons/button/reboot/white.svg +++ b/packages/admin-ui/src/styling/icons/button/reboot/white.svg @@ -1,17 +1,24 @@ - + Created with Sketch. - + - - + + - - - + + + diff --git a/packages/admin-ui/src/styling/icons/button/reboot/zodiac.svg b/packages/admin-ui/src/styling/icons/button/reboot/zodiac.svg index 4e117986..13eec9cc 100644 --- a/packages/admin-ui/src/styling/icons/button/reboot/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/reboot/zodiac.svg @@ -1,17 +1,24 @@ - + Created with Sketch. - + - - + + - - - + + + diff --git a/packages/admin-ui/src/styling/icons/button/replace/white.svg b/packages/admin-ui/src/styling/icons/button/replace/white.svg index dd3d3f3c..961863ea 100644 --- a/packages/admin-ui/src/styling/icons/button/replace/white.svg +++ b/packages/admin-ui/src/styling/icons/button/replace/white.svg @@ -1,14 +1,20 @@ - - + + - + - - - + + + diff --git a/packages/admin-ui/src/styling/icons/button/replace/zodiac.svg b/packages/admin-ui/src/styling/icons/button/replace/zodiac.svg index 65d17fb1..f4cb3787 100644 --- a/packages/admin-ui/src/styling/icons/button/replace/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/replace/zodiac.svg @@ -1,14 +1,21 @@ - - + + - - + + - - - + + + diff --git a/packages/admin-ui/src/styling/icons/button/retry/white.svg b/packages/admin-ui/src/styling/icons/button/retry/white.svg index 9de65c2e..6d70c719 100644 --- a/packages/admin-ui/src/styling/icons/button/retry/white.svg +++ b/packages/admin-ui/src/styling/icons/button/retry/white.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - + - + diff --git a/packages/admin-ui/src/styling/icons/button/retry/zodiac.svg b/packages/admin-ui/src/styling/icons/button/retry/zodiac.svg index 1aa858cc..0b87802e 100644 --- a/packages/admin-ui/src/styling/icons/button/retry/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/retry/zodiac.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - + - + diff --git a/packages/admin-ui/src/styling/icons/button/schedule/white.svg b/packages/admin-ui/src/styling/icons/button/schedule/white.svg index de4cbc71..6abbf19e 100644 --- a/packages/admin-ui/src/styling/icons/button/schedule/white.svg +++ b/packages/admin-ui/src/styling/icons/button/schedule/white.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/schedule/zodiac.svg b/packages/admin-ui/src/styling/icons/button/schedule/zodiac.svg index ffb90c44..5f60117e 100644 --- a/packages/admin-ui/src/styling/icons/button/schedule/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/schedule/zodiac.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/shut down/white.svg b/packages/admin-ui/src/styling/icons/button/shut down/white.svg index d5b995de..3e5f74be 100644 --- a/packages/admin-ui/src/styling/icons/button/shut down/white.svg +++ b/packages/admin-ui/src/styling/icons/button/shut down/white.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - + - + diff --git a/packages/admin-ui/src/styling/icons/button/shut down/zodiac.svg b/packages/admin-ui/src/styling/icons/button/shut down/zodiac.svg index b892ccdd..d049be65 100644 --- a/packages/admin-ui/src/styling/icons/button/shut down/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/shut down/zodiac.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - + - + diff --git a/packages/admin-ui/src/styling/icons/button/stop-ignoring/white.svg b/packages/admin-ui/src/styling/icons/button/stop-ignoring/white.svg index 561392e8..8a37b9b3 100644 --- a/packages/admin-ui/src/styling/icons/button/stop-ignoring/white.svg +++ b/packages/admin-ui/src/styling/icons/button/stop-ignoring/white.svg @@ -1,8 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/stop-ignoring/zodiac.svg b/packages/admin-ui/src/styling/icons/button/stop-ignoring/zodiac.svg index 11e609bc..0885ebeb 100644 --- a/packages/admin-ui/src/styling/icons/button/stop-ignoring/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/stop-ignoring/zodiac.svg @@ -1,8 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/unpair/white.svg b/packages/admin-ui/src/styling/icons/button/unpair/white.svg index be65f2c8..4f886625 100644 --- a/packages/admin-ui/src/styling/icons/button/unpair/white.svg +++ b/packages/admin-ui/src/styling/icons/button/unpair/white.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/unpair/zodiac.svg b/packages/admin-ui/src/styling/icons/button/unpair/zodiac.svg index 099464f0..63a48a3b 100644 --- a/packages/admin-ui/src/styling/icons/button/unpair/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/unpair/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/button/upload-file/white-resized.svg b/packages/admin-ui/src/styling/icons/button/upload-file/white-resized.svg index 63e43664..ff74e5b4 100644 --- a/packages/admin-ui/src/styling/icons/button/upload-file/white-resized.svg +++ b/packages/admin-ui/src/styling/icons/button/upload-file/white-resized.svg @@ -1,5 +1,6 @@ - + @@ -8,12 +9,16 @@ - - + + - - + + diff --git a/packages/admin-ui/src/styling/icons/button/upload-file/white.svg b/packages/admin-ui/src/styling/icons/button/upload-file/white.svg index c59dca65..500c8759 100644 --- a/packages/admin-ui/src/styling/icons/button/upload-file/white.svg +++ b/packages/admin-ui/src/styling/icons/button/upload-file/white.svg @@ -1,5 +1,6 @@ - + @@ -8,12 +9,14 @@ - + - + diff --git a/packages/admin-ui/src/styling/icons/button/upload-file/zodiac-resized.svg b/packages/admin-ui/src/styling/icons/button/upload-file/zodiac-resized.svg index 5d7e61f1..3460c2b6 100644 --- a/packages/admin-ui/src/styling/icons/button/upload-file/zodiac-resized.svg +++ b/packages/admin-ui/src/styling/icons/button/upload-file/zodiac-resized.svg @@ -1,5 +1,6 @@ - + @@ -8,12 +9,15 @@ - - + + - + diff --git a/packages/admin-ui/src/styling/icons/button/upload-file/zodiac.svg b/packages/admin-ui/src/styling/icons/button/upload-file/zodiac.svg index 26bab67b..16a0df2c 100644 --- a/packages/admin-ui/src/styling/icons/button/upload-file/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/upload-file/zodiac.svg @@ -1,5 +1,6 @@ - + @@ -8,12 +9,14 @@ - + - + diff --git a/packages/admin-ui/src/styling/icons/button/upload/white.svg b/packages/admin-ui/src/styling/icons/button/upload/white.svg index 9d24f44b..a3fda130 100644 --- a/packages/admin-ui/src/styling/icons/button/upload/white.svg +++ b/packages/admin-ui/src/styling/icons/button/upload/white.svg @@ -1,14 +1,18 @@ - + Created with Sketch. - + - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/upload/zodiac.svg b/packages/admin-ui/src/styling/icons/button/upload/zodiac.svg index a4f0d8db..fe24d97e 100644 --- a/packages/admin-ui/src/styling/icons/button/upload/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/upload/zodiac.svg @@ -1,14 +1,18 @@ - + Created with Sketch. - + - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/user-role/white.svg b/packages/admin-ui/src/styling/icons/button/user-role/white.svg index 13e5a9a5..59cf2a14 100644 --- a/packages/admin-ui/src/styling/icons/button/user-role/white.svg +++ b/packages/admin-ui/src/styling/icons/button/user-role/white.svg @@ -1,9 +1,12 @@ - + icon/button/user-role/white - + - + diff --git a/packages/admin-ui/src/styling/icons/button/user-role/zodiac.svg b/packages/admin-ui/src/styling/icons/button/user-role/zodiac.svg index 5a05f3a5..726f6d31 100644 --- a/packages/admin-ui/src/styling/icons/button/user-role/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/user-role/zodiac.svg @@ -1,9 +1,12 @@ - + icon/button/user-role/zodiac - + - + diff --git a/packages/admin-ui/src/styling/icons/button/whitelist/white.svg b/packages/admin-ui/src/styling/icons/button/whitelist/white.svg index d200d15c..fd494dfe 100644 --- a/packages/admin-ui/src/styling/icons/button/whitelist/white.svg +++ b/packages/admin-ui/src/styling/icons/button/whitelist/white.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - - + + @@ -13,6 +16,7 @@ - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/button/whitelist/zodiac.svg b/packages/admin-ui/src/styling/icons/button/whitelist/zodiac.svg index a0cdb412..1095e80d 100644 --- a/packages/admin-ui/src/styling/icons/button/whitelist/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/button/whitelist/zodiac.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - - + + @@ -13,6 +16,7 @@ - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/acceptor-left-filled.svg b/packages/admin-ui/src/styling/icons/cassettes/acceptor-left-filled.svg index c2a9b20c..3ee73c04 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/acceptor-left-filled.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/acceptor-left-filled.svg @@ -1 +1,63 @@ -acceptor-left-filled \ No newline at end of file + + + + + acceptor-left-filled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/acceptor-left.svg b/packages/admin-ui/src/styling/icons/cassettes/acceptor-left.svg index 06519d18..e2cee8dc 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/acceptor-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/acceptor-left.svg @@ -1 +1,20 @@ -acceptor-left \ No newline at end of file + + + + + acceptor-left + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/both-filled.svg b/packages/admin-ui/src/styling/icons/cassettes/both-filled.svg index 6104c6dd..f3dc501d 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/both-filled.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/both-filled.svg @@ -1 +1,89 @@ -both-filled \ No newline at end of file + + + + + both-filled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/cashbox-empty.svg b/packages/admin-ui/src/styling/icons/cassettes/cashbox-empty.svg index bbcec59a..a16fb500 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/cashbox-empty.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/cashbox-empty.svg @@ -1,12 +1,15 @@ - + 07E3DD15-D5E4-46A8-BF7B-064F598230CE - - + + diff --git a/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-1.svg b/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-1.svg index e0067b7c..2f750684 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-1.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-1.svg @@ -1 +1,25 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-2.svg b/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-2.svg index d58ba026..2471d673 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-2.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/cashout-cassette-2.svg @@ -1 +1,25 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/dispenser-1.svg b/packages/admin-ui/src/styling/icons/cassettes/dispenser-1.svg index a255b0b3..f4a54381 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/dispenser-1.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/dispenser-1.svg @@ -1 +1,29 @@ -v2-1 \ No newline at end of file + + + + + v2-1 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/dispenser-2.svg b/packages/admin-ui/src/styling/icons/cassettes/dispenser-2.svg index 68c9ea1b..c2806db8 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/dispenser-2.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/dispenser-2.svg @@ -1 +1,29 @@ -v2-2 \ No newline at end of file + + + + + v2-2 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1-left.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1-left.svg index 4053f270..ca88beff 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1-left.svg @@ -1,85 +1,88 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1.svg index f1f34689..036c40b3 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-1.svg @@ -1,85 +1,88 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2-left.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2-left.svg index d5afa864..df52d50e 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2-left.svg @@ -1,89 +1,92 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2.svg index 3f30e76f..f8e29e28 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-2.svg @@ -1,92 +1,96 @@ - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3-left.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3-left.svg index 239b19dd..32d6f7d3 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3-left.svg @@ -1,91 +1,94 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3.svg index f5af1913..d367db4c 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/3-cassettes/3-cassettes-open-3.svg @@ -1,91 +1,94 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1-left.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1-left.svg index af490251..e8d58ae2 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1-left.svg @@ -1,95 +1,99 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1.svg index 68ad3092..c18f5252 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-1.svg @@ -1,95 +1,99 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2-left.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2-left.svg index 94f1ad71..4e004674 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2-left.svg @@ -1,97 +1,101 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2.svg index b431117a..c5a4c712 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-2.svg @@ -1,100 +1,105 @@ - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3-left.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3-left.svg index bc22c04c..8a8f1b3e 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3-left.svg @@ -1,97 +1,101 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3.svg index 3cf139ca..39be85db 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-3.svg @@ -1,99 +1,103 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4-left.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4-left.svg index 89c7c360..1ad69163 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4-left.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4-left.svg @@ -1,101 +1,106 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4.svg b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4.svg index 3dd819f7..866c9d75 100644 --- a/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4.svg +++ b/packages/admin-ui/src/styling/icons/cassettes/tejo/4-cassettes/4-cassettes-open-4.svg @@ -1,103 +1,108 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/white.svg index c610ebf5..13ddbcba 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/white.svg @@ -1,6 +1,8 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/zodiac.svg index 6e07620e..910e0225 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/customer-list-view/zodiac.svg @@ -1,6 +1,8 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/exception-view/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/exception-view/white.svg index 6302951e..e3f7add0 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/exception-view/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/exception-view/white.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/exception-view/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/exception-view/zodiac.svg index ffb4661c..a35dfdcd 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/exception-view/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/exception-view/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/filter/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/filter/white.svg index c85b1138..a88db1c3 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/filter/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/filter/white.svg @@ -1,13 +1,20 @@ - + Created with Sketch. - - - - - - + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/filter/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/filter/zodiac.svg index 367fa7e3..c338dc08 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/filter/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/filter/zodiac.svg @@ -1,13 +1,20 @@ - + Created with Sketch. - - - - - - + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/history/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/history/white.svg index 21bc2206..093db36a 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/history/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/history/white.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - + - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/history/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/history/zodiac.svg index 41486cfb..9a3ce056 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/history/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/history/zodiac.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - + - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/law/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/law/white.svg index e82cd95e..a92bdc15 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/law/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/law/white.svg @@ -1,6 +1,8 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/law/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/law/zodiac.svg index 8c13bc6c..3ab6c4f9 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/law/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/law/zodiac.svg @@ -1,6 +1,8 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/listing-view/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/listing-view/white.svg index 9a10d18b..cd4b1c7a 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/listing-view/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/listing-view/white.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/listing-view/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/listing-view/zodiac.svg index 45e3122d..2dc959af 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/listing-view/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/listing-view/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/overview/comet.svg b/packages/admin-ui/src/styling/icons/circle buttons/overview/comet.svg index 2ab2ffd7..b450c0d5 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/overview/comet.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/overview/comet.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/overview/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/overview/white.svg index aa7c2af6..2e193c68 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/overview/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/overview/white.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/overview/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/overview/zodiac.svg index db0c70ec..2d5a5461 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/overview/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/overview/zodiac.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/save/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/save/white.svg index 870b1188..6d3f4ac7 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/save/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/save/white.svg @@ -1,5 +1,6 @@ - + Created with Sketch. @@ -7,12 +8,14 @@ - + - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/save/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/save/zodiac.svg index 7428d35d..cc68d335 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/save/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/save/zodiac.svg @@ -1,5 +1,6 @@ - + Created with Sketch. @@ -9,12 +10,15 @@ - + - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/search/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/search/white.svg index 6ca60dd1..13eeedf8 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/search/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/search/white.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/search/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/search/zodiac.svg index 89515ba2..56e57c69 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/search/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/search/zodiac.svg @@ -1,10 +1,13 @@ - + icon/search/dark02 - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/settings/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/settings/white.svg index c2f13552..6349fd2c 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/settings/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/settings/white.svg @@ -1,8 +1,11 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/settings/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/settings/zodiac.svg index 1f60c4c0..a122c9b4 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/settings/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/settings/zodiac.svg @@ -1,8 +1,11 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/share/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/share/white.svg index 058f9a51..fb5fe6e1 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/share/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/share/white.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/share/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/share/zodiac.svg index d77c5160..65cd828f 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/share/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/share/zodiac.svg @@ -1,8 +1,10 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/circle buttons/upload/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/upload/white.svg index 912372ba..28a22c06 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/upload/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/upload/white.svg @@ -1,12 +1,17 @@ - + Created with Sketch. - - + + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/upload/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/upload/zodiac.svg index 08c4c550..c9f950c0 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/upload/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/upload/zodiac.svg @@ -1,12 +1,17 @@ - + Created with Sketch. - - + + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/wizard/white.svg b/packages/admin-ui/src/styling/icons/circle buttons/wizard/white.svg index e07c6d13..4e19de15 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/wizard/white.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/wizard/white.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/circle buttons/wizard/zodiac.svg b/packages/admin-ui/src/styling/icons/circle buttons/wizard/zodiac.svg index 485e5aeb..aabb52bd 100644 --- a/packages/admin-ui/src/styling/icons/circle buttons/wizard/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/circle buttons/wizard/zodiac.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/compliance/custom-requirement.svg b/packages/admin-ui/src/styling/icons/compliance/custom-requirement.svg index 4fdf906d..33f74e7c 100644 --- a/packages/admin-ui/src/styling/icons/compliance/custom-requirement.svg +++ b/packages/admin-ui/src/styling/icons/compliance/custom-requirement.svg @@ -1 +1,889 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/compliance/keyboard.svg b/packages/admin-ui/src/styling/icons/compliance/keyboard.svg index f7d68740..bee03752 100644 --- a/packages/admin-ui/src/styling/icons/compliance/keyboard.svg +++ b/packages/admin-ui/src/styling/icons/compliance/keyboard.svg @@ -1,5 +1,6 @@ - + entry-icon/keyboard diff --git a/packages/admin-ui/src/styling/icons/compliance/keypad.svg b/packages/admin-ui/src/styling/icons/compliance/keypad.svg index 02b01b3f..495c98f0 100644 --- a/packages/admin-ui/src/styling/icons/compliance/keypad.svg +++ b/packages/admin-ui/src/styling/icons/compliance/keypad.svg @@ -1,5 +1,6 @@ - + entry-icon/keypad diff --git a/packages/admin-ui/src/styling/icons/compliance/list.svg b/packages/admin-ui/src/styling/icons/compliance/list.svg index 459fde15..e94cbb67 100644 --- a/packages/admin-ui/src/styling/icons/compliance/list.svg +++ b/packages/admin-ui/src/styling/icons/compliance/list.svg @@ -1,5 +1,6 @@ - + entry-icon/list diff --git a/packages/admin-ui/src/styling/icons/customer-nav/data/comet.svg b/packages/admin-ui/src/styling/icons/customer-nav/data/comet.svg index 3504ab61..6ee63c8b 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/data/comet.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/data/comet.svg @@ -1,8 +1,11 @@ - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/customer-nav/data/white.svg b/packages/admin-ui/src/styling/icons/customer-nav/data/white.svg index b517414e..37890014 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/data/white.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/data/white.svg @@ -1,8 +1,11 @@ - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/customer-nav/data/zodiac.svg b/packages/admin-ui/src/styling/icons/customer-nav/data/zodiac.svg index 735c8741..5c9e2b38 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/data/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/data/zodiac.svg @@ -1,8 +1,11 @@ - + - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/customer-nav/note/comet.svg b/packages/admin-ui/src/styling/icons/customer-nav/note/comet.svg index 951e3108..51f39880 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/note/comet.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/note/comet.svg @@ -1,9 +1,12 @@ - + - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/customer-nav/note/white.svg b/packages/admin-ui/src/styling/icons/customer-nav/note/white.svg index def24ed8..764fe4ad 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/note/white.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/note/white.svg @@ -1,9 +1,12 @@ - + - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/customer-nav/note/zodiac.svg b/packages/admin-ui/src/styling/icons/customer-nav/note/zodiac.svg index 6388cc81..afd33ca2 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/note/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/note/zodiac.svg @@ -1,9 +1,12 @@ - + - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/customer-nav/overview/comet.svg b/packages/admin-ui/src/styling/icons/customer-nav/overview/comet.svg index 8bb25dc1..70b3d6af 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/overview/comet.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/overview/comet.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/customer-nav/overview/white.svg b/packages/admin-ui/src/styling/icons/customer-nav/overview/white.svg index 77d347eb..f5dd4a28 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/overview/white.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/overview/white.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/customer-nav/overview/zodiac.svg b/packages/admin-ui/src/styling/icons/customer-nav/overview/zodiac.svg index b0d4319a..c4720367 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/overview/zodiac.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/overview/zodiac.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/customer-nav/photos/comet.svg b/packages/admin-ui/src/styling/icons/customer-nav/photos/comet.svg index 95ee5f2c..af7211c2 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/photos/comet.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/photos/comet.svg @@ -1,10 +1,13 @@ - + icon/customer-nav/photos/comet - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/customer-nav/photos/white.svg b/packages/admin-ui/src/styling/icons/customer-nav/photos/white.svg index e27fef8f..3ef7bcf8 100644 --- a/packages/admin-ui/src/styling/icons/customer-nav/photos/white.svg +++ b/packages/admin-ui/src/styling/icons/customer-nav/photos/white.svg @@ -1,10 +1,13 @@ - + icon/customer-nav/photos/white - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/dashboard/down.svg b/packages/admin-ui/src/styling/icons/dashboard/down.svg index 497eb245..ea095515 100644 --- a/packages/admin-ui/src/styling/icons/dashboard/down.svg +++ b/packages/admin-ui/src/styling/icons/dashboard/down.svg @@ -1,10 +1,14 @@ - + - - + + diff --git a/packages/admin-ui/src/styling/icons/dashboard/equal.svg b/packages/admin-ui/src/styling/icons/dashboard/equal.svg index a37df157..e4aad0ed 100644 --- a/packages/admin-ui/src/styling/icons/dashboard/equal.svg +++ b/packages/admin-ui/src/styling/icons/dashboard/equal.svg @@ -1,5 +1,6 @@ - + diff --git a/packages/admin-ui/src/styling/icons/dashboard/up.svg b/packages/admin-ui/src/styling/icons/dashboard/up.svg index 08357739..d4f9ae3e 100644 --- a/packages/admin-ui/src/styling/icons/dashboard/up.svg +++ b/packages/admin-ui/src/styling/icons/dashboard/up.svg @@ -1,10 +1,14 @@ - + - - + + diff --git a/packages/admin-ui/src/styling/icons/direction/cash-in.svg b/packages/admin-ui/src/styling/icons/direction/cash-in.svg index ae91335b..474ba3b1 100644 --- a/packages/admin-ui/src/styling/icons/direction/cash-in.svg +++ b/packages/admin-ui/src/styling/icons/direction/cash-in.svg @@ -1,10 +1,12 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/direction/cash-out.svg b/packages/admin-ui/src/styling/icons/direction/cash-out.svg index 123fdd02..b30796cf 100644 --- a/packages/admin-ui/src/styling/icons/direction/cash-out.svg +++ b/packages/admin-ui/src/styling/icons/direction/cash-out.svg @@ -1,10 +1,13 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/file/comet.svg b/packages/admin-ui/src/styling/icons/file/comet.svg index 86dc1d0d..f031f4fc 100644 --- a/packages/admin-ui/src/styling/icons/file/comet.svg +++ b/packages/admin-ui/src/styling/icons/file/comet.svg @@ -1,8 +1,10 @@ - + - + diff --git a/packages/admin-ui/src/styling/icons/file/spring.svg b/packages/admin-ui/src/styling/icons/file/spring.svg index 26d73281..041f0699 100644 --- a/packages/admin-ui/src/styling/icons/file/spring.svg +++ b/packages/admin-ui/src/styling/icons/file/spring.svg @@ -1,8 +1,10 @@ - + - + diff --git a/packages/admin-ui/src/styling/icons/file/tomato.svg b/packages/admin-ui/src/styling/icons/file/tomato.svg index f7cae5ad..c6840ff4 100644 --- a/packages/admin-ui/src/styling/icons/file/tomato.svg +++ b/packages/admin-ui/src/styling/icons/file/tomato.svg @@ -1,8 +1,10 @@ - + - + diff --git a/packages/admin-ui/src/styling/icons/menu/logo-white.svg b/packages/admin-ui/src/styling/icons/menu/logo-white.svg index 7de68c79..90f8c08c 100644 --- a/packages/admin-ui/src/styling/icons/menu/logo-white.svg +++ b/packages/admin-ui/src/styling/icons/menu/logo-white.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/menu/logo.svg b/packages/admin-ui/src/styling/icons/menu/logo.svg index bbdc846a..7e7d2e44 100644 --- a/packages/admin-ui/src/styling/icons/menu/logo.svg +++ b/packages/admin-ui/src/styling/icons/menu/logo.svg @@ -1,9 +1,11 @@ - + Created with Sketch. - + diff --git a/packages/admin-ui/src/styling/icons/menu/notification-zodiac.svg b/packages/admin-ui/src/styling/icons/menu/notification-zodiac.svg index d35d2028..5c1a019d 100644 --- a/packages/admin-ui/src/styling/icons/menu/notification-zodiac.svg +++ b/packages/admin-ui/src/styling/icons/menu/notification-zodiac.svg @@ -1,12 +1,15 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/menu/notification.svg b/packages/admin-ui/src/styling/icons/menu/notification.svg index cc917d32..4385728d 100644 --- a/packages/admin-ui/src/styling/icons/menu/notification.svg +++ b/packages/admin-ui/src/styling/icons/menu/notification.svg @@ -1,12 +1,15 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/menu/search-zodiac.svg b/packages/admin-ui/src/styling/icons/menu/search-zodiac.svg index 99fb94be..efb34a8e 100644 --- a/packages/admin-ui/src/styling/icons/menu/search-zodiac.svg +++ b/packages/admin-ui/src/styling/icons/menu/search-zodiac.svg @@ -1,12 +1,15 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/menu/search.svg b/packages/admin-ui/src/styling/icons/menu/search.svg index 9a7ea06f..66d37b18 100644 --- a/packages/admin-ui/src/styling/icons/menu/search.svg +++ b/packages/admin-ui/src/styling/icons/menu/search.svg @@ -1,12 +1,15 @@ - + Created with Sketch. - - + + diff --git a/packages/admin-ui/src/styling/icons/month arrows/left.svg b/packages/admin-ui/src/styling/icons/month arrows/left.svg index e5ce3c47..a3d438fd 100644 --- a/packages/admin-ui/src/styling/icons/month arrows/left.svg +++ b/packages/admin-ui/src/styling/icons/month arrows/left.svg @@ -1,5 +1,6 @@ - + Created with Sketch. @@ -7,7 +8,8 @@ - + @@ -15,8 +17,10 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/month arrows/right.svg b/packages/admin-ui/src/styling/icons/month arrows/right.svg index 500673b1..2692ac8b 100644 --- a/packages/admin-ui/src/styling/icons/month arrows/right.svg +++ b/packages/admin-ui/src/styling/icons/month arrows/right.svg @@ -1,11 +1,13 @@ - + - + @@ -13,8 +15,10 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/month arrows/right_white.svg b/packages/admin-ui/src/styling/icons/month arrows/right_white.svg index 9d8cae10..0213c036 100644 --- a/packages/admin-ui/src/styling/icons/month arrows/right_white.svg +++ b/packages/admin-ui/src/styling/icons/month arrows/right_white.svg @@ -1,11 +1,13 @@ - + - + @@ -13,8 +15,10 @@ - - + + diff --git a/packages/admin-ui/src/styling/icons/stage/spring/complete.svg b/packages/admin-ui/src/styling/icons/stage/spring/complete.svg index c5bca93a..34885ee0 100644 --- a/packages/admin-ui/src/styling/icons/stage/spring/complete.svg +++ b/packages/admin-ui/src/styling/icons/stage/spring/complete.svg @@ -1,9 +1,13 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/stage/spring/current.svg b/packages/admin-ui/src/styling/icons/stage/spring/current.svg index c84bb8c1..f38ccab4 100644 --- a/packages/admin-ui/src/styling/icons/stage/spring/current.svg +++ b/packages/admin-ui/src/styling/icons/stage/spring/current.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/stage/spring/empty.svg b/packages/admin-ui/src/styling/icons/stage/spring/empty.svg index ef66b65c..10485bac 100644 --- a/packages/admin-ui/src/styling/icons/stage/spring/empty.svg +++ b/packages/admin-ui/src/styling/icons/stage/spring/empty.svg @@ -1,8 +1,11 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/stage/zodiac/complete.svg b/packages/admin-ui/src/styling/icons/stage/zodiac/complete.svg index 0fae49dd..21c1c973 100644 --- a/packages/admin-ui/src/styling/icons/stage/zodiac/complete.svg +++ b/packages/admin-ui/src/styling/icons/stage/zodiac/complete.svg @@ -1,9 +1,13 @@ - + Created with Sketch. - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/stage/zodiac/current.svg b/packages/admin-ui/src/styling/icons/stage/zodiac/current.svg index 044d18b2..e2eff64f 100644 --- a/packages/admin-ui/src/styling/icons/stage/zodiac/current.svg +++ b/packages/admin-ui/src/styling/icons/stage/zodiac/current.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/stage/zodiac/empty.svg b/packages/admin-ui/src/styling/icons/stage/zodiac/empty.svg index 4529dbf6..77411f82 100644 --- a/packages/admin-ui/src/styling/icons/stage/zodiac/empty.svg +++ b/packages/admin-ui/src/styling/icons/stage/zodiac/empty.svg @@ -1,8 +1,11 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/stage/zodiac/full.svg b/packages/admin-ui/src/styling/icons/stage/zodiac/full.svg index 8e905e5e..3062b257 100644 --- a/packages/admin-ui/src/styling/icons/stage/zodiac/full.svg +++ b/packages/admin-ui/src/styling/icons/stage/zodiac/full.svg @@ -1,9 +1,12 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/status/pumpkin.svg b/packages/admin-ui/src/styling/icons/status/pumpkin.svg index fc11e181..f081464c 100644 --- a/packages/admin-ui/src/styling/icons/status/pumpkin.svg +++ b/packages/admin-ui/src/styling/icons/status/pumpkin.svg @@ -1,4 +1,5 @@ - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/status/spring2.svg b/packages/admin-ui/src/styling/icons/status/spring2.svg index 0ffc9aa2..0edd8bd3 100644 --- a/packages/admin-ui/src/styling/icons/status/spring2.svg +++ b/packages/admin-ui/src/styling/icons/status/spring2.svg @@ -1,4 +1,5 @@ - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/status/tomato.svg b/packages/admin-ui/src/styling/icons/status/tomato.svg index 7e5eb10a..58748c43 100644 --- a/packages/admin-ui/src/styling/icons/status/tomato.svg +++ b/packages/admin-ui/src/styling/icons/status/tomato.svg @@ -1,4 +1,5 @@ - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/stripes.svg b/packages/admin-ui/src/styling/icons/stripes.svg index f1c87ebd..929c5598 100644 --- a/packages/admin-ui/src/styling/icons/stripes.svg +++ b/packages/admin-ui/src/styling/icons/stripes.svg @@ -1,28 +1,29 @@ - - - - - {' '} - + xmlns="http://www.w3.org/2000/svg" + width="100%" + height="36px"> + + + + + + {' '} + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/table/empty-table.svg b/packages/admin-ui/src/styling/icons/table/empty-table.svg index 76464a0c..fb0284b4 100644 --- a/packages/admin-ui/src/styling/icons/table/empty-table.svg +++ b/packages/admin-ui/src/styling/icons/table/empty-table.svg @@ -1,27 +1,33 @@ - + - + - + - + - + - + @@ -38,10 +44,14 @@ - - - - + + + + diff --git a/packages/admin-ui/src/styling/icons/table/false.svg b/packages/admin-ui/src/styling/icons/table/false.svg index 4d44d93f..466d068d 100644 --- a/packages/admin-ui/src/styling/icons/table/false.svg +++ b/packages/admin-ui/src/styling/icons/table/false.svg @@ -1,11 +1,15 @@ - + Group - + - + diff --git a/packages/admin-ui/src/styling/icons/table/true.svg b/packages/admin-ui/src/styling/icons/table/true.svg index d805af9d..288843f3 100644 --- a/packages/admin-ui/src/styling/icons/table/true.svg +++ b/packages/admin-ui/src/styling/icons/table/true.svg @@ -1,9 +1,11 @@ - + icon/table/true Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/warning-icon/comet.svg b/packages/admin-ui/src/styling/icons/warning-icon/comet.svg index 6d59876c..0548ac6e 100644 --- a/packages/admin-ui/src/styling/icons/warning-icon/comet.svg +++ b/packages/admin-ui/src/styling/icons/warning-icon/comet.svg @@ -1,11 +1,13 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/icons/warning-icon/tomato.svg b/packages/admin-ui/src/styling/icons/warning-icon/tomato.svg index eeb7b713..4922e485 100644 --- a/packages/admin-ui/src/styling/icons/warning-icon/tomato.svg +++ b/packages/admin-ui/src/styling/icons/warning-icon/tomato.svg @@ -1,11 +1,13 @@ - + Created with Sketch. - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/logos/icon-bitcoin-colour.svg b/packages/admin-ui/src/styling/logos/icon-bitcoin-colour.svg index 94b562ad..57336810 100644 --- a/packages/admin-ui/src/styling/logos/icon-bitcoin-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-bitcoin-colour.svg @@ -1,7 +1,7 @@ - - + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/logos/icon-bitcoincash-colour.svg b/packages/admin-ui/src/styling/logos/icon-bitcoincash-colour.svg index e10506eb..096583ef 100644 --- a/packages/admin-ui/src/styling/logos/icon-bitcoincash-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-bitcoincash-colour.svg @@ -1,4 +1,6 @@ - - + + diff --git a/packages/admin-ui/src/styling/logos/icon-dash-colour.svg b/packages/admin-ui/src/styling/logos/icon-dash-colour.svg index 73da05d9..137b1737 100644 --- a/packages/admin-ui/src/styling/logos/icon-dash-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-dash-colour.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/logos/icon-ethereum-colour.svg b/packages/admin-ui/src/styling/logos/icon-ethereum-colour.svg index f095d8c0..dd16d053 100644 --- a/packages/admin-ui/src/styling/logos/icon-ethereum-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-ethereum-colour.svg @@ -1,11 +1,14 @@ - - - - - - - - - + + + + + + + + + diff --git a/packages/admin-ui/src/styling/logos/icon-litecoin-colour.svg b/packages/admin-ui/src/styling/logos/icon-litecoin-colour.svg index 0e25829b..ba607d19 100644 --- a/packages/admin-ui/src/styling/logos/icon-litecoin-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-litecoin-colour.svg @@ -1,4 +1,5 @@ - - + + diff --git a/packages/admin-ui/src/styling/logos/icon-monero-colour.svg b/packages/admin-ui/src/styling/logos/icon-monero-colour.svg index af777a4d..d0e1290a 100644 --- a/packages/admin-ui/src/styling/logos/icon-monero-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-monero-colour.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/logos/icon-tether-colour.svg b/packages/admin-ui/src/styling/logos/icon-tether-colour.svg index 3efaa649..f4547484 100644 --- a/packages/admin-ui/src/styling/logos/icon-tether-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-tether-colour.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/logos/icon-tron-colour.svg b/packages/admin-ui/src/styling/logos/icon-tron-colour.svg index 7f0efe2f..42d65be8 100644 --- a/packages/admin-ui/src/styling/logos/icon-tron-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-tron-colour.svg @@ -1,7 +1,9 @@ - + - + \ No newline at end of file diff --git a/packages/admin-ui/src/styling/logos/icon-usdc-colour.svg b/packages/admin-ui/src/styling/logos/icon-usdc-colour.svg index 5dfea926..16b6b806 100644 --- a/packages/admin-ui/src/styling/logos/icon-usdc-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-usdc-colour.svg @@ -1,5 +1,8 @@ - - - + + + diff --git a/packages/admin-ui/src/styling/logos/icon-zcash-colour.svg b/packages/admin-ui/src/styling/logos/icon-zcash-colour.svg index 7e7655dc..7eb63db6 100644 --- a/packages/admin-ui/src/styling/logos/icon-zcash-colour.svg +++ b/packages/admin-ui/src/styling/logos/icon-zcash-colour.svg @@ -1,15 +1,16 @@ - + -headerArtboard 7 - - - + diff --git a/packages/admin-ui/src/styling/theme.js b/packages/admin-ui/src/styling/theme.js index 96748084..5dbee8be 100644 --- a/packages/admin-ui/src/styling/theme.js +++ b/packages/admin-ui/src/styling/theme.js @@ -22,30 +22,30 @@ import { tomato, mistyRose, linen, - pumpkin + pumpkin, } from './variables' const { p } = typographyStyles let theme = createTheme({ typography: { - fontFamily: inputFontFamily + fontFamily: inputFontFamily, }, palette: { primary: { light: secondaryColor, dark: secondaryColor, - main: secondaryColor + main: secondaryColor, }, secondary: { light: secondaryColor, dark: secondaryColor, - main: secondaryColor + main: secondaryColor, }, background: { - default: backgroundColor - } - } + default: backgroundColor, + }, + }, }) theme = createTheme(theme, { @@ -53,18 +53,18 @@ theme = createTheme(theme, { MuiTypography: { styleOverrides: { root: { ...p }, - body1: { ...p } - } + body1: { ...p }, + }, }, MuiIconButtonBase: { defaultProps: { - disableRipple: true - } + disableRipple: true, + }, }, MuiButtonBase: { defaultProps: { - disableRipple: true - } + disableRipple: true, + }, }, MuiSwitch: { styleOverrides: { @@ -72,18 +72,18 @@ theme = createTheme(theme, { width: 32, height: 20, padding: 0, - margin: theme.spacing(1) + margin: theme.spacing(1), }, thumb: { width: 16, - height: 16 + height: 16, }, track: { borderRadius: 17, border: 'none', backgroundColor: offColor, opacity: 1, - transition: theme.transitions.create(['background-color', 'border']) + transition: theme.transitions.create(['background-color', 'border']), }, switchBase: { padding: 2, @@ -91,96 +91,96 @@ theme = createTheme(theme, { color: disabledColor2, '& + .MuiSwitch-track': { backgroundColor: disabledColor, - opacity: 1 - } + opacity: 1, + }, }, '&.Mui-checked': { transform: 'translateX(58%)', color: theme.palette.common.white, '&.Mui-disabled': { - color: disabledColor2 + color: disabledColor2, }, '& + .MuiSwitch-track': { backgroundColor: secondaryColor, opacity: 1, - border: 'none' - } + border: 'none', + }, }, '&.Mui-focusVisible .MuiSwitch-thumb': { border: '6px solid #fff', - boxShadow: '0 0 4px 0 rgba(0,0,0,0.24)' - } - } - } + boxShadow: '0 0 4px 0 rgba(0,0,0,0.24)', + }, + }, + }, }, MuiMenuItem: { styleOverrides: { root: { '&:hover': { - backgroundColor: subheaderColor + backgroundColor: subheaderColor, }, '&.Mui-selected': { '&:hover': { - backgroundColor: subheaderColor + backgroundColor: subheaderColor, }, - backgroundColor: subheaderColor - } - } - } + backgroundColor: subheaderColor, + }, + }, + }, }, MuiAutocomplete: { styleOverrides: { root: { - color: fontColor + color: fontColor, }, noOptions: { - padding: `6px 16px` + padding: `6px 16px`, }, option: { '&.Mui-focused': { - backgroundColor: subheaderColor + backgroundColor: subheaderColor, }, '&[aria-selected="true"]': { - backgroundColor: `${subheaderColor} !important` - } + backgroundColor: `${subheaderColor} !important`, + }, }, paper: { color: fontColor, - margin: 0 + margin: 0, }, listbox: { - padding: 0 + padding: 0, }, tag: { '&[data-tag-index="0"]': { - marginLeft: 0 + marginLeft: 0, }, margin: 2, backgroundColor: subheaderColor, borderRadius: 4, - height: 18 - } - } + height: 18, + }, + }, }, MuiPaper: { styleOverrides: { root: { - color: primaryColor + color: primaryColor, }, elevation1: { - boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)' - } - } + boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)', + }, + }, }, MuiCheckbox: { styleOverrides: { root: { color: secondaryColor, '&.Mui-checked': { - color: secondaryColor - } - } - } + color: secondaryColor, + }, + }, + }, }, MuiChip: { styleOverrides: { @@ -188,7 +188,7 @@ theme = createTheme(theme, { backgroundColor: subheaderColor, borderRadius: 4, margin: theme.spacing(0.5, 0.25), - height: 18 + height: 18, }, label: { fontSize: smallestFontSize, @@ -196,76 +196,76 @@ theme = createTheme(theme, { fontWeight: inputFontWeight, fontFamily: inputFontFamily, paddingRight: 4, - paddingLeft: 4 + paddingLeft: 4, }, colorDefault: { backgroundColor: zircon, '& .MuiChip-label': { - color: primaryColor - } + color: primaryColor, + }, }, colorWarning: { backgroundColor: linen, '& .MuiChip-label': { - color: pumpkin - } + color: pumpkin, + }, }, colorError: { backgroundColor: mistyRose, '& .MuiChip-label': { - color: tomato - } + color: tomato, + }, }, colorSuccess: { backgroundColor: spring3, '& .MuiChip-label': { - color: spring4 - } - } - } + color: spring4, + }, + }, + }, }, MuiInput: { styleOverrides: { root: { - color: fontColor + color: fontColor, }, underline: { '&:before': { - borderBottom: `2px solid ${fontColor}` - } - } - } + borderBottom: `2px solid ${fontColor}`, + }, + }, + }, }, MuiInputLabel: { styleOverrides: { root: { font: 'inherit', fontSize: fontSize3, - color: offColor + color: offColor, }, shrink: { color: fontColor, - transform: 'translate(0, 1.7px) scale(0.83)' - } - } + transform: 'translate(0, 1.7px) scale(0.83)', + }, + }, }, MuiFormLabel: { styleOverrides: { root: { '&.Mui-focused': { - color: fontColor - } - } - } + color: fontColor, + }, + }, + }, }, MuiListItem: { styleOverrides: { root: { '&:nth-of-type(odd)': { - backgroundColor: backgroundColor - } - } - } + backgroundColor: backgroundColor, + }, + }, + }, }, MuiToggleButton: { styleOverrides: { @@ -275,21 +275,21 @@ theme = createTheme(theme, { borderColor: primaryColor, borderTopColor: `${primaryColor} !important`, '&:hover': { - backgroundColor: zircon2 - } + backgroundColor: zircon2, + }, }, '&:hover': { - backgroundColor: zircon2 - } - } - } + backgroundColor: zircon2, + }, + }, + }, }, MuiToggleButtonGroup: { styleOverrides: { vertical: { borderRadius: 8, border: 'none', - borderColor: zircon + borderColor: zircon, }, firstButton: { borderTop: '1px solid', @@ -297,7 +297,7 @@ theme = createTheme(theme, { borderTopRightRadius: 8, borderTopLeftRadius: 8, borderBottomRightRadius: 8, - borderBottomLeftRadius: 8 + borderBottomLeftRadius: 8, }, lastButton: { borderTop: '1px solid', @@ -305,7 +305,7 @@ theme = createTheme(theme, { borderTopRightRadius: 8, borderTopLeftRadius: 8, borderBottomRightRadius: 8, - borderBottomLeftRadius: 8 + borderBottomLeftRadius: 8, }, middleButton: { borderTop: '1px solid', @@ -313,11 +313,11 @@ theme = createTheme(theme, { borderTopRightRadius: 8, borderTopLeftRadius: 8, borderBottomRightRadius: 8, - borderBottomLeftRadius: 8 - } - } - } - } + borderBottomLeftRadius: 8, + }, + }, + }, + }, }) export default theme diff --git a/packages/admin-ui/src/styling/variables.js b/packages/admin-ui/src/styling/variables.js index d3542141..0ca762df 100644 --- a/packages/admin-ui/src/styling/variables.js +++ b/packages/admin-ui/src/styling/variables.js @@ -168,5 +168,5 @@ export { tableCellHeight, tableHeaderColor, tableErrorColor, - tableSuccessColor + tableSuccessColor, } diff --git a/packages/admin-ui/src/utils/apollo.jsx b/packages/admin-ui/src/utils/apollo.jsx index d141ca15..a9ef9881 100644 --- a/packages/admin-ui/src/utils/apollo.jsx +++ b/packages/admin-ui/src/utils/apollo.jsx @@ -1,6 +1,11 @@ -import { ApolloClient, ApolloProvider, InMemoryCache, ApolloLink } from "@apollo/client"; -import { onError } from "@apollo/client/link/error" -import createUploadLink from "apollo-upload-client/createUploadLink.mjs"; +import { + ApolloClient, + ApolloProvider, + InMemoryCache, + ApolloLink, +} from '@apollo/client' +import { onError } from '@apollo/client/link/error' +import createUploadLink from 'apollo-upload-client/createUploadLink.mjs' import React, { useContext } from 'react' import { useHistory, useLocation } from 'react-router-dom' @@ -8,7 +13,7 @@ import AppContext from 'src/AppContext' const uploadLink = createUploadLink({ credentials: 'include', - uri: `/graphql` + uri: `/graphql`, }) const getClient = (history, location, getUserData, setUserData, setRole) => @@ -22,7 +27,7 @@ const getClient = (history, location, getUserData, setUserData, setRole) => if (location.pathname !== '/login') history.push('/login') } console.log( - `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` + `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, ) }) if (networkError) console.log(`[Network error]: ${networkError}`) @@ -31,7 +36,7 @@ const getClient = (history, location, getUserData, setUserData, setRole) => return forward(operation).map(response => { const context = operation.getContext() const { - response: { headers } + response: { headers }, } = context if (headers) { @@ -42,22 +47,22 @@ const getClient = (history, location, getUserData, setUserData, setRole) => return response }) }), - uploadLink + uploadLink, ]), cache: new InMemoryCache(), defaultOptions: { watchQuery: { fetchPolicy: 'no-cache', - errorPolicy: 'ignore' + errorPolicy: 'ignore', }, query: { fetchPolicy: 'no-cache', - errorPolicy: 'all' + errorPolicy: 'all', }, mutate: { - errorPolicy: 'all' - } - } + errorPolicy: 'all', + }, + }, }) const Provider = ({ children }) => { @@ -69,7 +74,7 @@ const Provider = ({ children }) => { location, () => userData, setUserData, - setRole + setRole, ) return {children} diff --git a/packages/admin-ui/src/utils/bill-denominations.js b/packages/admin-ui/src/utils/bill-denominations.js index 508a26fd..99f51ebe 100644 --- a/packages/admin-ui/src/utils/bill-denominations.js +++ b/packages/admin-ui/src/utils/bill-denominations.js @@ -9,9 +9,9 @@ export default { 100: [0xa5, 0x91], 200: [0xa7, 0x93], 500: [0xa9, 0x95], - 1000: [0xad, 0x99] + 1000: [0xad, 0x99], }, - polymer: false + polymer: false, }, ANG: { thickness: 0x0c, @@ -19,9 +19,9 @@ export default { 10: [0x98, 0x8e], 25: [0x98, 0x8e], 50: [0x98, 0x8e], - 100: [0x98, 0x8e] + 100: [0x98, 0x8e], }, - polymer: false + polymer: false, }, AUD: { thickness: 0x0d, @@ -30,9 +30,9 @@ export default { 10: [0x93, 0x7f], 20: [0x9a, 0x86], 50: [0xa1, 0x8d], - 100: [0xa8, 0x94] + 100: [0xa8, 0x94], }, - polymer: true + polymer: true, }, BGN: { thickness: 0x0d, @@ -43,9 +43,9 @@ export default { 10: [0x83, 0x79], 20: [0x88, 0x7e], 50: [0x8d, 0x83], - 100: [0x92, 0x88] + 100: [0x92, 0x88], }, - polymer: false + polymer: false, }, CAD: { thickness: 0x0d, @@ -54,9 +54,9 @@ export default { 10: [0xa2, 0x8e], 20: [0xa2, 0x8e], 50: [0xa2, 0x8e], - 100: [0xa2, 0x8e] + 100: [0xa2, 0x8e], }, - polymer: true + polymer: true, }, CHF: { thickness: 0x0d, @@ -66,9 +66,9 @@ export default { 50: [0x93, 0x7f], 100: [0x9a, 0x86], 200: [0xa1, 0x8d], - 1000: [0xa8, 0x94] + 1000: [0xa8, 0x94], }, - polymer: false + polymer: false, }, CNY: { thickness: 0x0d, @@ -78,9 +78,9 @@ export default { 10: [0x96, 0x82], 20: [0x9b, 0x87], 50: [0xa0, 0x8c], - 100: [0x91, 0xa5] + 100: [0x91, 0xa5], }, - polymer: false + polymer: false, }, COP: { thickness: 0x0d, @@ -90,9 +90,9 @@ export default { 10000: [0x94, 0x80], 20000: [0x99, 0x85], 50000: [0x9e, 0x8a], - 100000: [0xa3, 0x8f] + 100000: [0xa3, 0x8f], }, - polymer: false + polymer: false, }, CRC: { thickness: 0x0d, @@ -102,9 +102,9 @@ export default { 5000: [0x90, 0x86], 10000: [0x97, 0x8d], 20000: [0x9e, 0x94], - 50000: [0xaa, 0x96] + 50000: [0xaa, 0x96], }, - polymer: true + polymer: true, }, CZK: { thickness: 0x0c, @@ -114,9 +114,9 @@ export default { 500: [0xa2, 0x8e], 1000: [0xa8, 0x94], 2000: [0xae, 0x9a], - 5000: [0xb4, 0xa0] + 5000: [0xb4, 0xa0], }, - polymer: false + polymer: false, }, EUR: { thickness: 0x0c, @@ -127,9 +127,9 @@ export default { 50: [0x96, 0x82], 100: [0x9d, 0x89], 200: [0xa3, 0x8f], - 500: [0xaa, 0x96] + 500: [0xaa, 0x96], }, - polymer: false + polymer: false, }, GBP: { thickness: 0x0d, @@ -137,9 +137,9 @@ export default { 5: [0x91, 0x7d], 10: [0x98, 0x84], 20: [0x95, 0x81], - 50: [0xa6, 0x92] + 50: [0xa6, 0x92], }, - polymer: true + polymer: true, }, GHS: { thickness: 0x0c, @@ -149,9 +149,9 @@ export default { 5: [0x97, 0x83], 10: [0x9b, 0x87], 20: [0x9f, 0x8b], - 50: [0xa3, 0x8f] + 50: [0xa3, 0x8f], }, - polymer: false + polymer: false, }, GIP: { thickness: 0x0c, @@ -160,9 +160,9 @@ export default { 10: [0x92, 0x88], 20: [0x9b, 0x91], 50: [0xa2, 0x98], - 100: [0xa9, 0x9f] + 100: [0xa9, 0x9f], }, - polymer: false + polymer: false, }, GTQ: { thickness: 0x0c, @@ -172,9 +172,9 @@ export default { 20: [0xa3, 0x99], 50: [0xa1, 0x97], 100: [0xa1, 0x97], - 200: [0xa1, 0x97] + 200: [0xa1, 0x97], }, - polymer: false + polymer: false, }, HKD: { thickness: 0x0d, @@ -184,9 +184,9 @@ export default { 50: [0x9e, 0x8a], 100: [0xa3, 0x8f], 500: [0xa8, 0x94], - 1000: [0xae, 0x9a] + 1000: [0xae, 0x9a], }, - polymer: false + polymer: false, }, HNL: { thickness: 0x0c, @@ -199,9 +199,9 @@ export default { 50: [0xa6, 0x92], 100: [0xa6, 0x92], 200: [0xa6, 0x92], - 500: [0xa6, 0x92] + 500: [0xa6, 0x92], }, - polymer: false + polymer: false, }, HRK: { thickness: 0x0c, @@ -213,9 +213,9 @@ export default { 100: [0x8f, 0x85], 200: [0x93, 0x89], 500: [0x97, 0x8d], - 1000: [0x9b, 0x91] + 1000: [0x9b, 0x91], }, - polymer: false + polymer: false, }, ILS: { thickness: 0x0d, @@ -223,9 +223,9 @@ export default { 20: [0x8b, 0x77], 50: [0x92, 0x7e], 100: [0x99, 0x85], - 200: [0xa0, 0x8c] + 200: [0xa0, 0x8c], }, - polymer: false + polymer: false, }, JPY: { thickness: 0x0d, @@ -233,9 +233,9 @@ export default { 1000: [0x99, 0x93], 2000: [0x9c, 0x98], 5000: [0x9d, 0x9a], - 10000: [0xa3, 0x9e] + 10000: [0xa3, 0x9e], }, - polymer: false + polymer: false, }, KRW: { thickness: 0x0d, @@ -244,9 +244,9 @@ export default { 2000: [0x96, 0x82], 5000: [0x98, 0x84], 10000: [0x9e, 0x8a], - 50000: [0xa4, 0x90] + 50000: [0xa4, 0x90], }, - polymer: false + polymer: false, }, MDL: { thickness: 0x0c, @@ -259,9 +259,9 @@ export default { 100: [0x83, 0x6f], 200: [0x8f, 0x7b], 500: [0x8f, 0x7b], - 1000: [0x8f, 0x7b] + 1000: [0x8f, 0x7b], }, - polymer: false + polymer: false, }, MKD: { thickness: 0x0c, @@ -273,9 +273,9 @@ export default { 500: [0x9a, 0x90], 1000: [0x9d, 0x93], 2000: [0x9d, 0x93], - 5000: [0xa0, 0x96] + 5000: [0xa0, 0x96], }, - polymer: true + polymer: true, }, MXN: { thickness: 0x0c, @@ -285,9 +285,9 @@ export default { 100: [0x89, 0x7f], 200: [0x90, 0x86], 500: [0x97, 0x8d], - 1000: [0x9e, 0x94] + 1000: [0x9e, 0x94], }, - polymer: true + polymer: true, }, MYR: { thickness: 0x0c, @@ -297,9 +297,9 @@ export default { 10: [0x96, 0x82], 20: [0x9b, 0x87], 50: [0x9b, 0x87], - 100: [0xa0, 0x8c] + 100: [0xa0, 0x8c], }, - polymer: false + polymer: false, }, NAD: { thickness: 0x0c, @@ -308,9 +308,9 @@ export default { 20: [0x8b, 0x81], 50: [0x91, 0x87], 100: [0x97, 0x8d], - 200: [0x9d, 0x93] + 200: [0x9d, 0x93], }, - polymer: false + polymer: false, }, NZD: { thickness: 0x0c, @@ -319,9 +319,9 @@ export default { 10: [0x91, 0x87], 20: [0x96, 0x8c], 50: [0x9b, 0x91], - 100: [0xa0, 0x96] + 100: [0xa0, 0x96], }, - polymer: true + polymer: true, }, PHP: { thickness: 0x0c, @@ -330,9 +330,9 @@ export default { 100: [0xaa, 0x96], 200: [0xaa, 0x96], 500: [0xaa, 0x96], - 1000: [0xaa, 0x96] + 1000: [0xaa, 0x96], }, - polymer: false + polymer: false, }, PLN: { thickness: 0x0c, @@ -342,9 +342,9 @@ export default { 50: [0x89, 0x7f], 100: [0x8f, 0x85], 200: [0x95, 0x8b], - 500: [0x9b, 0x91] + 500: [0x9b, 0x91], }, - polymer: false + polymer: false, }, RON: { thickness: 0x0c, @@ -355,9 +355,9 @@ export default { 50: [0x96, 0x82], 100: [0x9d, 0x89], 200: [0xa0, 0x8c], - 500: [0xa3, 0x8f] + 500: [0xa3, 0x8f], }, - polymer: true + polymer: true, }, SGD: { thickness: 0x0c, @@ -367,9 +367,9 @@ export default { 10: [0x97, 0x83], 50: [0xa6, 0x92], 100: [0xac, 0x98], - 1000: [0xb4, 0xa0] + 1000: [0xb4, 0xa0], }, - polymer: false + polymer: false, }, TWD: { thickness: 0x0d, @@ -378,9 +378,9 @@ export default { 200: [0xa0, 0x8c], 500: [0xa5, 0x91], 1000: [0xaa, 0x96], - 2000: [0xaf, 0x9b] + 2000: [0xaf, 0x9b], }, - polymer: false + polymer: false, }, USD: { thickness: 0x0d, @@ -391,9 +391,9 @@ export default { 10: [0xa6, 0x92], 20: [0xa6, 0x92], 50: [0xa6, 0x92], - 100: [0xa6, 0x92] + 100: [0xa6, 0x92], }, - polymer: false + polymer: false, }, UYU: { thickness: 0x0d, @@ -404,9 +404,9 @@ export default { 200: [0xa4, 0x9a], 500: [0xa4, 0x9a], 1000: [0xa4, 0x9a], - 2000: [0xa4, 0x9a] + 2000: [0xa4, 0x9a], }, - polymer: false + polymer: false, }, XCD: { thickness: 0x0c, @@ -415,9 +415,9 @@ export default { 10: [0x9b, 0x87], 20: [0x9b, 0x87], 50: [0x9b, 0x87], - 100: [0x9b, 0x87] + 100: [0x9b, 0x87], }, - polymer: true + polymer: true, }, ZAR: { thickness: 0x0c, @@ -426,8 +426,8 @@ export default { 20: [0x90, 0x7c], 50: [0x96, 0x82], 100: [0x9c, 0x88], - 200: [0xa2, 0x8e] + 200: [0xa2, 0x8e], }, - polymer: false - } + polymer: false, + }, } diff --git a/packages/admin-ui/src/utils/bill-options.js b/packages/admin-ui/src/utils/bill-options.js index b95cb009..6481375a 100644 --- a/packages/admin-ui/src/utils/bill-options.js +++ b/packages/admin-ui/src/utils/bill-options.js @@ -5,7 +5,7 @@ const getBillOptions = R.curry((locale, denomiations) => { return R.compose( R.map(code => ({ code: parseInt(code), display: code })), R.keys, - R.path([currency, 'lengths']) + R.path([currency, 'lengths']), )(denomiations) }) diff --git a/packages/admin-ui/src/utils/config.js b/packages/admin-ui/src/utils/config.js index 0c282719..b521c471 100644 --- a/packages/admin-ui/src/utils/config.js +++ b/packages/admin-ui/src/utils/config.js @@ -13,30 +13,30 @@ const namespaces = { COIN_ATM_RADAR: 'coinAtmRadar', TERMS_CONDITIONS: 'termsConditions', TRIGGERS: 'triggersConfig', - MACHINE_SCREENS: 'machineScreens' + MACHINE_SCREENS: 'machineScreens', } const mapKeys = R.curry((fn, obj) => - R.fromPairs(R.map(R.adjust(0, fn), R.toPairs(obj))) + R.fromPairs(R.map(R.adjust(0, fn), R.toPairs(obj))), ) const filterByKey = R.curry((fn, obj) => - R.fromPairs(R.filter(it => fn(it[0]), R.toPairs(obj))) + R.fromPairs(R.filter(it => fn(it[0]), R.toPairs(obj))), ) const stripl = R.curry((q, str) => - R.startsWith(q, str) ? str.slice(q.length) : str + R.startsWith(q, str) ? str.slice(q.length) : str, ) const filtered = key => filterByKey(R.startsWith(`${key}_`)) const stripped = key => mapKeys(stripl(`${key}_`)) const fromNamespace = R.curry((key, config) => - R.compose(stripped(key), filtered(key))(config) + R.compose(stripped(key), filtered(key))(config), ) const toNamespace = R.curry((key, config) => - mapKeys(it => `${key}_${it}`)(config) + mapKeys(it => `${key}_${it}`)(config), ) export { fromNamespace, toNamespace, namespaces } diff --git a/packages/admin-ui/src/utils/constants.js b/packages/admin-ui/src/utils/constants.js index ed26830c..959edfc1 100644 --- a/packages/admin-ui/src/utils/constants.js +++ b/packages/admin-ui/src/utils/constants.js @@ -19,5 +19,5 @@ export { MANUAL, WALLET_SCORING_DEFAULT_THRESHOLD, IP_CHECK_REGEX, - SWEEPABLE_CRYPTOS + SWEEPABLE_CRYPTOS, } diff --git a/packages/admin-ui/src/utils/customer.js b/packages/admin-ui/src/utils/customer.js index ac7da296..8a7af097 100644 --- a/packages/admin-ui/src/utils/customer.js +++ b/packages/admin-ui/src/utils/customer.js @@ -8,7 +8,7 @@ const formatFullName = R.pipe( R.values, R.reject(R.allPass([R.isNil, R.isEmpty])), R.map(onlyFirstToUpper), - R.join(' ') + R.join(' '), ) const formatName = idCardData => { @@ -29,7 +29,7 @@ const displayName = ({ customerName, customerIdCardData, customerPhone, - customerEmail + customerEmail, }) => isAnonymous ? 'Anonymous' diff --git a/packages/admin-ui/src/utils/machine.js b/packages/admin-ui/src/utils/machine.js index 362e5e35..991a34fe 100644 --- a/packages/admin-ui/src/utils/machine.js +++ b/packages/admin-ui/src/utils/machine.js @@ -4,44 +4,44 @@ const modelPrettifier = { gaia: 'Gaia', tejo: 'Tejo', aveiro: 'Aveiro', - grandola: 'Grândola' + grandola: 'Grândola', } const cashUnitCapacity = { default: { cashbox: 600, - cassette: 500 + cassette: 500, }, douro: { cashbox: 600, - cassette: 500 + cassette: 500, }, grandola: { cashbox: 2500, - recycler: 2800 + recycler: 2800, }, aveiro: { cashbox: 1500, recycler: 60, escrow: 20, - cassette: 500 + cassette: 500, }, tejo: { // TODO: add support for the different cashbox configuration in Tejo cashbox: 1000, - cassette: 500 + cassette: 500, }, gaia: { - cashbox: 600 + cashbox: 600, }, sintra: { cashbox: 1000, - cassette: 500 + cassette: 500, }, gmuk1: { cashbox: 2200, - cassette: 2000 - } + cassette: 2000, + }, } const getCashUnitCapacity = (model, device) => { diff --git a/packages/admin-ui/src/utils/number.js b/packages/admin-ui/src/utils/number.js index a1221231..ab1aae76 100644 --- a/packages/admin-ui/src/utils/number.js +++ b/packages/admin-ui/src/utils/number.js @@ -17,5 +17,5 @@ export { defaultToZero, transformNumber, numberToFiatAmount, - numberToCryptoAmount + numberToCryptoAmount, } diff --git a/packages/admin-ui/src/utils/string.js b/packages/admin-ui/src/utils/string.js index 63ea1f54..2fc36494 100644 --- a/packages/admin-ui/src/utils/string.js +++ b/packages/admin-ui/src/utils/string.js @@ -5,7 +5,7 @@ const formatLong = value => { return `${value.slice(0, 8)}(...)${value.slice( value.length - 8, - value.length + value.length, )}` } @@ -18,7 +18,7 @@ const onlyFirstToUpper = R.compose(toFirstUpper, R.toLower) const splitOnUpper = R.compose( R.split(' '), R.replace(/([A-Z])/g, ' $1'), - toFirstLower + toFirstLower, ) const startCase = R.compose(R.join(' '), R.map(onlyFirstToUpper), splitOnUpper) @@ -32,5 +32,5 @@ export { onlyFirstToUpper, formatLong, singularOrPlural, - sentenceCase + sentenceCase, } diff --git a/packages/admin-ui/src/utils/timezone-list.js b/packages/admin-ui/src/utils/timezone-list.js index f7984f7d..f0bb37c7 100644 --- a/packages/admin-ui/src/utils/timezone-list.js +++ b/packages/admin-ui/src/utils/timezone-list.js @@ -14,7 +14,7 @@ const timezones = { 'America/Regina': { short: 'CST', long: 'Saskatchewan' }, 'America/Mexico_City': { short: 'CST', - long: 'Guadalajara, Mexico City, Monterrey' + long: 'Guadalajara, Mexico City, Monterrey', }, 'America/Belize': { short: 'CST', long: 'Central America' }, 'America/Detroit': { short: 'EST', long: 'Eastern Time' }, @@ -27,7 +27,7 @@ const timezones = { 'America/Montevideo': { short: 'UYT', long: 'Montevideo' }, 'America/Argentina/Buenos_Aires': { short: null, - long: 'Buenos Aires, Georgetown' + long: 'Buenos Aires, Georgetown', }, 'America/Godthab': { short: null, long: 'Greenland' }, 'America/Los_Angeles': { short: 'PST', long: 'Pacific Time' }, @@ -41,23 +41,23 @@ const timezones = { 'Atlantic/Canary': { short: 'WET', long: 'Canary Islands' }, 'Europe/Belgrade': { short: 'CET', - long: 'Belgrade, Bratislava, Budapest, Ljubljana, Prague' + long: 'Belgrade, Bratislava, Budapest, Ljubljana, Prague', }, 'Europe/Sarajevo': { short: 'CET', long: 'Sarajevo, Skopje, Warsaw, Zagreb' }, 'Europe/Brussels': { short: 'CET', - long: 'Brussels, Copenhagen, Madrid, Paris' + long: 'Brussels, Copenhagen, Madrid, Paris', }, 'Europe/Amsterdam': { short: 'CET', - long: 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' + long: 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna', }, 'Africa/Algiers': { short: 'CET', long: 'West Central Africa' }, 'Europe/Bucharest': { short: 'EET', long: 'Bucharest' }, 'Africa/Cairo': { short: 'EET', long: 'Cairo' }, 'Europe/Helsinki': { short: 'EET', - long: 'Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius' + long: 'Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius', }, 'Europe/Athens': { short: 'EET', long: 'Athens, Istanbul, Minsk' }, 'Asia/Jerusalem': { short: 'IST', long: 'Jerusalem' }, @@ -82,7 +82,7 @@ const timezones = { 'Asia/Krasnoyarsk': { short: 'KRAT', long: 'Krasnoyarsk' }, 'Asia/Shanghai': { short: 'CST', - long: 'Beijing, Chongqing, Hong Kong SAR, Urumqi' + long: 'Beijing, Chongqing, Hong Kong SAR, Urumqi', }, 'Asia/Kuala_Lumpur': { short: 'MYT', long: 'Kuala Lumpur, Singapore' }, 'Asia/Taipei': { short: 'CST', long: 'Taipei' }, @@ -100,12 +100,12 @@ const timezones = { 'Pacific/Guam': { short: 'ChST', long: 'Guam, Port Moresby' }, 'Asia/Magadan': { short: 'MAGT', - long: 'Magadan, Solomon Islands, New Caledonia' + long: 'Magadan, Solomon Islands, New Caledonia', }, 'Asia/Kamchatka': { short: 'PETT', long: 'Kamchatka, Marshall Islands' }, 'Pacific/Fiji': { short: 'FJT', long: 'Fiji Islands' }, 'Pacific/Auckland': { short: 'NZDT', long: 'Auckland, Wellington' }, - 'Pacific/Tongatapu': { short: null, long: "Nuku'alofa" } + 'Pacific/Tongatapu': { short: null, long: "Nuku'alofa" }, } const buildTzLabels = timezoneList => { @@ -115,29 +115,29 @@ const buildTzLabels = timezoneList => { const isNegative = getTimezoneOffset(value[0]) < 0 const duration = intervalToDuration({ start: 0, - end: Math.abs(getTimezoneOffset(value[0])) + end: Math.abs(getTimezoneOffset(value[0])), }) const hours = duration.hours.toLocaleString('en-US', { minimumIntegerDigits: 2, - useGrouping: false + useGrouping: false, }) const minutes = duration.minutes.toLocaleString('en-US', { minimumIntegerDigits: 2, - useGrouping: false + useGrouping: false, }) const prefix = `(GMT${isNegative ? `-` : `+`}${hours}:${minutes})` acc.push({ label: `${prefix} - ${value[1].long}`, - code: value[0] + code: value[0], }) return acc }, [], - pairs + pairs, ) } diff --git a/packages/admin-ui/src/utils/timezones.js b/packages/admin-ui/src/utils/timezones.js index 88d111ae..f4c1ec14 100644 --- a/packages/admin-ui/src/utils/timezones.js +++ b/packages/admin-ui/src/utils/timezones.js @@ -15,7 +15,7 @@ const formatDate = (date, timezone, pattern) => { const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone const newDate = utcToZonedTime( timezone, - zonedTimeToUtc(browserTimezone, date) + zonedTimeToUtc(browserTimezone, date), ) return format(pattern, newDate) } diff --git a/packages/admin-ui/src/utils/urlResolver.js b/packages/admin-ui/src/utils/urlResolver.js deleted file mode 100644 index fc198043..00000000 --- a/packages/admin-ui/src/utils/urlResolver.js +++ /dev/null @@ -1,9 +0,0 @@ -const url = `https://${ - process.env.NODE_ENV === 'development' - ? window.location.host - : window.location.hostname -}` - -const urlResolver = content => `${url}${content}` - -export { urlResolver } From 68517170e2c01c3791987cd4d1e4f63405eb46cb Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Mon, 12 May 2025 15:35:00 +0100 Subject: [PATCH 3/3] chore: server code formatting --- eslint.config.mjs | 45 +- package-lock.json | 316 ++++++- package.json | 1 + packages/server/lib/app.js | 43 +- packages/server/lib/auth-tokens.js | 4 +- packages/server/lib/bill-math.js | 137 +-- packages/server/lib/blacklist.js | 20 +- packages/server/lib/blockchain/bitcoin.js | 84 +- packages/server/lib/blockchain/bitcoincash.js | 25 +- packages/server/lib/blockchain/common.js | 88 +- packages/server/lib/blockchain/dash.js | 49 +- packages/server/lib/blockchain/do-volume.js | 18 +- packages/server/lib/blockchain/ethereum.js | 13 +- packages/server/lib/blockchain/install.js | 199 ++-- packages/server/lib/blockchain/litecoin.js | 45 +- packages/server/lib/blockchain/monero.js | 19 +- packages/server/lib/blockchain/zcash.js | 25 +- .../lib/blockexplorers/mempool.space.js | 12 +- packages/server/lib/cash-in/cash-in-atomic.js | 66 +- packages/server/lib/cash-in/cash-in-low.js | 127 ++- packages/server/lib/cash-in/cash-in-tx.js | 154 ++-- .../server/lib/cash-out/cash-out-actions.js | 56 +- .../server/lib/cash-out/cash-out-atomic.js | 186 ++-- .../server/lib/cash-out/cash-out-helper.js | 108 ++- packages/server/lib/cash-out/cash-out-low.js | 64 +- packages/server/lib/cash-out/cash-out-tx.js | 149 +-- packages/server/lib/cashbox-batches.js | 155 ++-- packages/server/lib/coin-change.js | 34 +- .../server/lib/coinatmradar/coinatmradar.js | 81 +- .../coinatmradar/test/coinatmradar.test.js | 80 +- packages/server/lib/commission-math.js | 26 +- packages/server/lib/compliance-external.js | 39 +- packages/server/lib/compliance-triggers.js | 40 +- packages/server/lib/compliance.js | 100 +- packages/server/lib/compliance_overrides.js | 4 +- packages/server/lib/constants.js | 24 +- packages/server/lib/customer-notes.js | 6 +- packages/server/lib/customers.js | 742 ++++++++------- packages/server/lib/db-migrate-store.js | 8 +- packages/server/lib/db.js | 5 +- packages/server/lib/email.js | 34 +- packages/server/lib/environment-helper.js | 6 +- packages/server/lib/error.js | 2 +- packages/server/lib/event-bus.js | 13 +- packages/server/lib/exchange.js | 89 +- packages/server/lib/forex.js | 73 +- packages/server/lib/graphql/resolvers.js | 339 ++++--- packages/server/lib/graphql/server.js | 12 +- packages/server/lib/graphql/types.js | 388 ++++---- packages/server/lib/hardware-credentials.js | 12 +- packages/server/lib/layer2.js | 49 +- packages/server/lib/logger.js | 22 +- packages/server/lib/logs.js | 105 ++- packages/server/lib/loyalty.js | 38 +- packages/server/lib/machine-loader.js | 560 +++++++----- packages/server/lib/middlewares/authorize.js | 3 +- packages/server/lib/middlewares/ca.js | 5 +- .../server/lib/middlewares/errorHandler.js | 6 +- .../lib/middlewares/filterOldRequests.js | 12 +- packages/server/lib/middlewares/operatorId.js | 2 +- .../lib/middlewares/populateDeviceId.js | 11 +- .../lib/middlewares/populateSettings.js | 80 +- .../middlewares/rejectIncompatbleMachines.js | 14 +- packages/server/lib/middlewares/state.js | 6 +- packages/server/lib/migrate.js | 4 +- packages/server/lib/mnemonic-helpers.js | 4 +- packages/server/lib/new-admin/admin-server.js | 52 +- .../server/lib/new-admin/config/accounts.js | 197 +++- .../lib/new-admin/config/data/countries.json | 251 ++++- .../lib/new-admin/config/data/languages.json | 517 ++++++----- packages/server/lib/new-admin/config/index.js | 6 +- packages/server/lib/new-admin/filters.js | 4 +- .../lib/new-admin/graphql/directives/auth.js | 26 +- .../server/lib/new-admin/graphql/errors.js | 28 +- .../modules/authentication/FIDO2FAStrategy.js | 246 ++--- .../FIDOPasswordlessStrategy.js | 161 ++-- .../FIDOUsernamelessStrategy.js | 148 +-- .../graphql/modules/authentication/index.js | 4 +- .../graphql/modules/userManagement.js | 151 +-- .../graphql/resolvers/bill.resolver.js | 4 +- .../graphql/resolvers/blacklist.resolver.js | 6 +- .../graphql/resolvers/cashbox.resolver.js | 18 +- .../graphql/resolvers/config.resolver.js | 10 +- .../graphql/resolvers/currency.resolver.js | 4 +- .../resolvers/customInfoRequests.resolver.js | 51 +- .../graphql/resolvers/customer.resolver.js | 50 +- .../graphql/resolvers/funding.resolver.js | 4 +- .../lib/new-admin/graphql/resolvers/index.js | 2 +- .../graphql/resolvers/log.resolver.js | 21 +- .../graphql/resolvers/loyalty.resolver.js | 24 +- .../graphql/resolvers/machine.resolver.js | 20 +- .../graphql/resolvers/market.resolver.js | 4 +- .../resolvers/notification.resolver.js | 9 +- .../graphql/resolvers/pairing.resolver.js | 4 +- .../graphql/resolvers/rates.resolver.js | 6 +- .../graphql/resolvers/sanctions.resolver.js | 4 +- .../graphql/resolvers/scalar.resolver.js | 8 +- .../graphql/resolvers/settings.resolver.js | 7 +- .../graphql/resolvers/sms.resolver.js | 9 +- .../graphql/resolvers/status.resolver.js | 4 +- .../graphql/resolvers/transaction.resolver.js | 124 ++- .../graphql/resolvers/users.resolver.js | 144 ++- .../graphql/resolvers/version.resolver.js | 4 +- .../server/lib/new-admin/graphql/schema.js | 2 +- .../new-admin/graphql/types/cashbox.type.js | 6 +- .../graphql/types/customInfoRequests.type.js | 31 +- .../new-admin/graphql/types/customer.type.js | 29 +- .../lib/new-admin/graphql/types/index.js | 2 +- .../lib/new-admin/graphql/types/log.type.js | 32 +- .../new-admin/graphql/types/loyalty.type.js | 7 +- .../new-admin/graphql/types/machine.type.js | 9 +- .../lib/new-admin/graphql/types/sms.type.js | 3 +- .../graphql/types/transaction.type.js | 34 +- .../middlewares/cleanUserSessions.js | 8 +- .../lib/new-admin/middlewares/context.js | 5 +- .../server/lib/new-admin/middlewares/index.js | 2 +- .../lib/new-admin/middlewares/session.js | 33 +- .../server/lib/new-admin/services/bills.js | 16 +- .../new-admin/services/customInfoRequests.js | 105 ++- .../server/lib/new-admin/services/funding.js | 77 +- .../server/lib/new-admin/services/login.js | 17 +- .../server/lib/new-admin/services/machines.js | 17 +- .../server/lib/new-admin/services/pairing.js | 29 +- .../lib/new-admin/services/server-logs.js | 11 +- .../lib/new-admin/services/supervisor.js | 36 +- .../lib/new-admin/services/transactions.js | 298 ++++-- packages/server/lib/new-config-manager.js | 188 ++-- packages/server/lib/new-settings-loader.js | 197 ++-- packages/server/lib/notifier/codes.js | 6 +- packages/server/lib/notifier/email.js | 34 +- packages/server/lib/notifier/index.js | 304 ++++--- .../server/lib/notifier/notificationCenter.js | 211 +++-- packages/server/lib/notifier/queries.js | 23 +- packages/server/lib/notifier/sms.js | 16 +- .../server/lib/notifier/test/email.test.js | 12 +- .../server/lib/notifier/test/notifier.test.js | 108 ++- packages/server/lib/notifier/test/sms.test.js | 12 +- .../server/lib/notifier/test/utils.test.js | 52 +- packages/server/lib/notifier/utils.js | 79 +- packages/server/lib/notifier/webhook.js | 4 +- packages/server/lib/ofac/index.js | 41 +- packages/server/lib/ofac/loading.js | 51 +- packages/server/lib/ofac/matching.js | 53 +- packages/server/lib/ofac/name-utils.js | 15 +- packages/server/lib/ofac/parsing.js | 32 +- packages/server/lib/ofac/update.js | 52 +- packages/server/lib/operator.js | 5 +- packages/server/lib/pairing.js | 67 +- packages/server/lib/pg-transport.js | 6 +- packages/server/lib/plugin-helper.js | 9 +- packages/server/lib/plugins.js | 861 ++++++++++-------- packages/server/lib/plugins/common/ccxt.js | 104 +-- .../server/lib/plugins/common/json-rpc.js | 75 +- .../server/lib/plugins/compliance/consts.js | 4 +- .../mock-compliance/mock-compliance.js | 13 +- .../lib/plugins/compliance/sumsub/request.js | 11 +- .../plugins/compliance/sumsub/sumsub.api.js | 38 +- .../lib/plugins/compliance/sumsub/sumsub.js | 51 +- .../lib/plugins/email/mailgun/mailgun.js | 14 +- .../plugins/email/mock-email/mock-email.js | 4 +- .../server/lib/plugins/exchange/binance.js | 15 +- .../server/lib/plugins/exchange/binanceus.js | 15 +- .../server/lib/plugins/exchange/bitfinex.js | 16 +- .../server/lib/plugins/exchange/bitstamp.js | 54 +- packages/server/lib/plugins/exchange/ccxt.js | 221 +++-- packages/server/lib/plugins/exchange/cex.js | 15 +- .../server/lib/plugins/exchange/consts.js | 13 +- packages/server/lib/plugins/exchange/itbit.js | 67 +- .../server/lib/plugins/exchange/kraken.js | 86 +- .../lib/plugins/exchange/mock-exchange.js | 20 +- .../server/lib/plugins/sms/inforu/inforu.js | 93 +- .../lib/plugins/sms/mock-sms/mock-sms.js | 4 +- .../server/lib/plugins/sms/telnyx/telnyx.js | 11 +- .../server/lib/plugins/sms/twilio/twilio.js | 20 +- .../server/lib/plugins/sms/vonage/vonage.js | 11 +- .../lib/plugins/sms/whatsapp/whatsapp.js | 83 +- packages/server/lib/plugins/ticker/bitpay.js | 11 +- packages/server/lib/plugins/ticker/ccxt.js | 51 +- .../server/lib/plugins/ticker/mock-ticker.js | 8 +- .../ticker/pazuz-ticker/pazuz-ticker.js | 29 +- .../server/lib/plugins/tokens/erc20.abi.json | 316 ++++--- .../wallet-scoring/elliptic/elliptic.js | 56 +- .../mock-scoring/mock-scoring.js | 22 +- .../wallet-scoring/scorechain/scorechain.js | 31 +- .../wallet/bitcoincashd/bitcoincashd.js | 113 ++- .../lib/plugins/wallet/bitcoind/bitcoind.js | 180 ++-- .../server/lib/plugins/wallet/bitgo/bitgo.js | 117 +-- .../server/lib/plugins/wallet/dashd/dashd.js | 106 ++- .../server/lib/plugins/wallet/galoy/galoy.js | 323 ++++--- .../server/lib/plugins/wallet/geth/base.js | 216 +++-- .../server/lib/plugins/wallet/geth/geth.js | 2 +- .../lib/plugins/wallet/infura/infura.js | 49 +- .../lib/plugins/wallet/litecoind/litecoind.js | 97 +- .../plugins/wallet/mock-wallet/mock-wallet.js | 116 ++- .../lib/plugins/wallet/monerod/monerod.js | 247 +++-- .../wallet/pazuz-wallet/pazuz-wallet.js | 98 -- .../server/lib/plugins/wallet/tron/base.js | 102 ++- .../lib/plugins/wallet/trongrid/trongrid.js | 2 +- .../lib/plugins/wallet/zcashd/zcashd.js | 146 +-- .../zero-conf/blockcypher/blockcypher.js | 78 +- .../mock-zero-conf/mock-zero-conf.js | 16 +- packages/server/lib/poller.js | 207 +++-- packages/server/lib/postgresql_interface.js | 84 +- packages/server/lib/pp.js | 2 +- packages/server/lib/respond.js | 9 +- packages/server/lib/route-helpers.js | 34 +- packages/server/lib/routes.js | 17 +- packages/server/lib/routes/cashboxRoutes.js | 28 +- packages/server/lib/routes/customerRoutes.js | 269 ++++-- .../server/lib/routes/diagnosticsRoutes.js | 2 +- packages/server/lib/routes/failedQRScans.js | 2 +- packages/server/lib/routes/logsRoutes.js | 13 +- packages/server/lib/routes/pairingRoutes.js | 5 +- .../server/lib/routes/performanceRoutes.js | 9 +- packages/server/lib/routes/probeLnRoutes.js | 18 +- packages/server/lib/routes/stateRoutes.js | 5 +- .../lib/routes/termsAndConditionsRoutes.js | 27 +- packages/server/lib/routes/txRoutes.js | 23 +- .../lib/routes/verifyPromoCodeRoutes.js | 27 +- packages/server/lib/routes/verifyTxRoutes.js | 2 +- .../server/lib/routes/verifyUserRoutes.js | 2 +- packages/server/lib/sanctions.js | 42 +- packages/server/lib/session-manager.js | 21 +- packages/server/lib/sms-notices.js | 33 +- packages/server/lib/sms.js | 66 +- packages/server/lib/ticker.js | 44 +- packages/server/lib/time.js | 2 +- packages/server/lib/tx-batching-processing.js | 38 +- packages/server/lib/tx-batching.js | 58 +- packages/server/lib/tx.js | 71 +- packages/server/lib/users.js | 103 ++- packages/server/lib/utils.js | 3 +- packages/server/lib/wallet-scoring.js | 39 +- packages/server/lib/wallet.js | 259 ++++-- 234 files changed, 9824 insertions(+), 6195 deletions(-) delete mode 100644 packages/server/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js diff --git a/eslint.config.mjs b/eslint.config.mjs index 2447af6b..deb07af4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,13 +5,21 @@ import json from '@eslint/json' import { defineConfig, globalIgnores } from 'eslint/config' import reactCompiler from 'eslint-plugin-react-compiler' import eslintConfigPrettier from 'eslint-config-prettier/flat' +import pluginJest from 'eslint-plugin-jest' export default defineConfig([ - globalIgnores(['**/build', '**/package.json', '**/package-lock.json']), + globalIgnores([ + '**/build', + '**/package.json', + '**/package-lock.json', + '**/currencies.json', + '**/countries.json', + '**/languages.json', + ]), { files: ['**/*.{js,mjs,cjs,jsx}'], plugins: { js }, - extends: ['js/recommended'] + extends: ['js/recommended'], }, { files: ['packages/admin-ui/**/*.{js,mjs,jsx}'], @@ -19,18 +27,18 @@ export default defineConfig([ sourceType: 'module', globals: { ...globals.browser, - process: 'readonly' - } - } + process: 'readonly', + }, + }, }, { files: ['packages/server/**/*.{js,cjs}'], - languageOptions: { sourceType: 'commonjs', globals: globals.node } + languageOptions: { sourceType: 'commonjs', globals: globals.node }, }, { ...pluginReact.configs.flat.recommended, settings: { react: { version: 'detect' } }, - files: ['packages/admin-ui/**/*.{jsx,js}'] + files: ['packages/admin-ui/**/*.{jsx,js}'], }, { ...reactCompiler.configs.recommended }, eslintConfigPrettier, @@ -38,14 +46,29 @@ export default defineConfig([ files: ['**/*.json'], plugins: { json }, language: 'json/json', - extends: ['json/recommended'] + extends: ['json/recommended'], }, { rules: { 'react/prop-types': 'off', 'react/display-name': 'off', 'react/no-unescaped-entities': 'off', - 'react-compiler/react-compiler': 'warn' - } - } + 'react-compiler/react-compiler': 'warn', + }, + }, + { + // update this to match your test files + files: ['**/*.spec.js', '**/*.test.js'], + plugins: { jest: pluginJest }, + languageOptions: { + globals: pluginJest.environments.globals.globals, + }, + rules: { + 'jest/no-disabled-tests': 'warn', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'warn', + 'jest/valid-expect': 'error', + }, + }, ]) diff --git a/package-lock.json b/package-lock.json index a14199a4..04a689a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@eslint/json": "^0.12.0", "eslint": "^9.26.0", "eslint-config-prettier": "^10.1.5", + "eslint-plugin-jest": "^28.11.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-compiler": "^19.1.0-rc.1", "globals": "^16.1.0", @@ -5773,6 +5774,44 @@ } ] }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@otplib/core": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz", @@ -7460,6 +7499,158 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.0.tgz", + "integrity": "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/visitor-keys": "8.32.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.0.tgz", + "integrity": "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.0.tgz", + "integrity": "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/visitor-keys": "8.32.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.0.tgz", + "integrity": "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.0", + "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/typescript-estree": "8.32.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.0.tgz", + "integrity": "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitejs/plugin-react-swc": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.9.0.tgz", @@ -12177,6 +12368,32 @@ "eslint": ">=4.19.1" } }, + "node_modules/eslint-plugin-jest": { + "version": "28.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.11.0.tgz", + "integrity": "sha512-QAfipLcNCWLVocVbZW8GimKn5p5iiMcgGbRzz8z/P5q7xw+cNEpYqyzFMtIF/ZgF2HLOyy+dYBut+DoYolvqig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "engines": { + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, "node_modules/eslint-plugin-node": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", @@ -13355,6 +13572,36 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -13375,6 +13622,16 @@ "resolved": "https://registry.npmjs.org/fastpriorityqueue/-/fastpriorityqueue-0.7.5.tgz", "integrity": "sha512-3Pa0n9gwy8yIbEsT3m2j/E9DXgWvvjfiZjjqcJ+AdNKTAlVMIuFYrYG5Y3RHEM8O6cwv9hOpOWY/NaMfywoQVA==" }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -14261,7 +14518,8 @@ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "node_modules/har-schema": { "version": "2.0.0", @@ -15331,6 +15589,7 @@ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "optional": true, + "peer": true, "bin": { "is-docker": "cli.js" }, @@ -15722,6 +15981,7 @@ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "optional": true, + "peer": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -19561,6 +19821,16 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/merkle-lib": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", @@ -20233,6 +20503,7 @@ "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", "dev": true, "optional": true, + "peer": true, "dependencies": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -20248,6 +20519,7 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "optional": true, + "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -22993,6 +23265,17 @@ "node": ">= 4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -24036,7 +24319,8 @@ "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "node_modules/side-channel": { "version": "1.1.0", @@ -26364,6 +26648,19 @@ "semver": "bin/semver" } }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-invariant": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", @@ -26615,6 +26912,21 @@ "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/ua-parser-js": { "version": "1.0.40", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", diff --git a/package.json b/package.json index a05fb6b9..78242888 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@eslint/json": "^0.12.0", "eslint": "^9.26.0", "eslint-config-prettier": "^10.1.5", + "eslint-plugin-jest": "^28.11.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-compiler": "^19.1.0-rc.1", "globals": "^16.1.0", diff --git a/packages/server/lib/app.js b/packages/server/lib/app.js index 875146b8..c3848f88 100644 --- a/packages/server/lib/app.js +++ b/packages/server/lib/app.js @@ -19,8 +19,8 @@ const CA_PATH = process.env.CA_PATH const version = require('../package.json').version logger.info('Version: %s', version) -function run () { - return new Promise((resolve, reject) => { +function run() { + return new Promise(resolve => { let count = 0 let handler @@ -31,12 +31,11 @@ function run () { } const runner = () => { - settingsLoader.loadLatest() + settingsLoader + .loadLatest() .then(settings => { clearInterval(handler) - return loadSanctions(settings) - .then(startServer) - .then(resolve) + return loadSanctions(settings).then(startServer).then(resolve) }) .catch(errorHandler) } @@ -46,23 +45,23 @@ function run () { }) } -function loadSanctions (settings) { - return Promise.resolve() - .then(() => { - const triggers = configManager.getTriggers(settings.config) - const hasSanctions = complianceTriggers.hasSanctions(triggers) +function loadSanctions(settings) { + return Promise.resolve().then(() => { + const triggers = configManager.getTriggers(settings.config) + const hasSanctions = complianceTriggers.hasSanctions(triggers) - if (!hasSanctions) return + if (!hasSanctions) return - logger.info('Loading sanctions DB...') - return ofacUpdate.update() - .then(() => logger.info('Sanctions DB updated')) - .then(ofac.load) - .then(() => logger.info('Sanctions DB loaded')) - }) + logger.info('Loading sanctions DB...') + return ofacUpdate + .update() + .then(() => logger.info('Sanctions DB updated')) + .then(ofac.load) + .then(() => logger.info('Sanctions DB loaded')) + }) } -async function startServer () { +async function startServer() { const app = await loadRoutes() poller.setup() @@ -72,16 +71,14 @@ async function startServer () { cert: fs.readFileSync(CERT_PATH), ca: fs.readFileSync(CA_PATH), requestCert: true, - rejectUnauthorized: false + rejectUnauthorized: false, } const server = https.createServer(httpsServerOptions, app) const port = argv.port || 3000 - await new Promise((resolve) => - server.listen({ port }, resolve), - ) + await new Promise(resolve => server.listen({ port }, resolve)) logger.info(`lamassu-server listening on port ${port}`) } diff --git a/packages/server/lib/auth-tokens.js b/packages/server/lib/auth-tokens.js index d42fa1b1..6fd8920a 100644 --- a/packages/server/lib/auth-tokens.js +++ b/packages/server/lib/auth-tokens.js @@ -3,7 +3,7 @@ const crypto = require('crypto') const constants = require('./constants') const db = require('./db') -function createAuthToken (userID, type) { +function createAuthToken(userID, type) { const token = crypto.randomBytes(32).toString('hex') const sql = `INSERT INTO auth_tokens (token, type, user_id) VALUES ($1, $2, $3) ON CONFLICT (user_id, type) DO UPDATE SET token=$1, expire=now() + interval '${constants.AUTH_TOKEN_EXPIRATION_TIME}' RETURNING *` @@ -11,5 +11,5 @@ function createAuthToken (userID, type) { } module.exports = { - createAuthToken + createAuthToken, } diff --git a/packages/server/lib/bill-math.js b/packages/server/lib/bill-math.js index f83ff49c..570c998d 100644 --- a/packages/server/lib/bill-math.js +++ b/packages/server/lib/bill-math.js @@ -1,7 +1,6 @@ const _ = require('lodash/fp') const sumService = require('@haensl/subset-sum') -const BN = require('./bn') const logger = require('./logger') const cc = require('./coin-change') @@ -11,7 +10,7 @@ const BILL_LIST_MODES = { LOWEST_VALUE_FIRST: 2, HIGHEST_VALUE_FIRST: 3, UNIT_ROUND_ROBIN: 4, - VALUE_ROUND_ROBIN: 5 + VALUE_ROUND_ROBIN: 5, } const buildBillList = (units, mode) => { @@ -23,7 +22,7 @@ const buildBillList = (units, mode) => { return acc }, [], - _.reverse(units) + _.reverse(units), ) case BILL_LIST_MODES.FIRST_UNIT_FIRST: return _.reduce( @@ -32,7 +31,7 @@ const buildBillList = (units, mode) => { return acc }, [], - units + units, ) case BILL_LIST_MODES.LOWEST_VALUE_FIRST: return _.reduce( @@ -41,7 +40,7 @@ const buildBillList = (units, mode) => { return acc }, [], - _.orderBy(['denomination'], ['asc'])(units) + _.orderBy(['denomination'], ['asc'])(units), ) case BILL_LIST_MODES.HIGHEST_VALUE_FIRST: return _.reduce( @@ -50,58 +49,59 @@ const buildBillList = (units, mode) => { return acc }, [], - _.orderBy(['denomination'], ['desc'])(units) + _.orderBy(['denomination'], ['desc'])(units), ) - case BILL_LIST_MODES.UNIT_ROUND_ROBIN: - { - const amountOfBills = _.reduce( - (acc, value) => acc + value.count, - 0, - units - ) - - const _units = _.filter(it => it.count > 0)(_.cloneDeep(units)) - const bills = [] - - for(let i = 0; i < amountOfBills; i++) { - const idx = i % _.size(_units) - if (_units[idx].count > 0) { - bills.push(_units[idx].denomination) - _units[idx].count-- - } - - if (_units[idx].count === 0) { - _units.splice(idx, 1) - } + case BILL_LIST_MODES.UNIT_ROUND_ROBIN: { + const amountOfBills = _.reduce( + (acc, value) => acc + value.count, + 0, + units, + ) + + const _units = _.filter(it => it.count > 0)(_.cloneDeep(units)) + const bills = [] + + for (let i = 0; i < amountOfBills; i++) { + const idx = i % _.size(_units) + if (_units[idx].count > 0) { + bills.push(_units[idx].denomination) + _units[idx].count-- } - return bills + if (_units[idx].count === 0) { + _units.splice(idx, 1) + } } - case BILL_LIST_MODES.VALUE_ROUND_ROBIN: - { - const amountOfBills = _.reduce( - (acc, value) => acc + value.count, - 0, - units - ) - - const _units = _.flow([_.filter(it => it.count > 0), _.orderBy(['denomination'], ['asc'])])(_.cloneDeep(units)) - const bills = [] - - for(let i = 0; i < amountOfBills; i++) { - const idx = i % _.size(_units) - if (_units[idx].count > 0) { - bills.push(_units[idx].denomination) - _units[idx].count-- - } - - if (_units[idx].count === 0) { - _units.splice(idx, 1) - } + + return bills + } + case BILL_LIST_MODES.VALUE_ROUND_ROBIN: { + const amountOfBills = _.reduce( + (acc, value) => acc + value.count, + 0, + units, + ) + + const _units = _.flow([ + _.filter(it => it.count > 0), + _.orderBy(['denomination'], ['asc']), + ])(_.cloneDeep(units)) + const bills = [] + + for (let i = 0; i < amountOfBills; i++) { + const idx = i % _.size(_units) + if (_units[idx].count > 0) { + bills.push(_units[idx].denomination) + _units[idx].count-- } - return bills + if (_units[idx].count === 0) { + _units.splice(idx, 1) + } } + + return bills + } default: throw new Error(`Invalid mode: ${mode}`) } @@ -113,11 +113,13 @@ const getSolution_old = (units, amount, mode) => { if (_.sum(billList) < amount.toNumber()) { return [] } - + const solver = sumService.subsetSum(billList, amount.toNumber()) const solution = _.countBy(Math.floor, solver.next().value) - return Object.entries(solution) - .map(([denomination, provisioned]) => [_.toNumber(denomination), provisioned]) + return Object.entries(solution).map(([denomination, provisioned]) => [ + _.toNumber(denomination), + provisioned, + ]) } const getSolution = (units, amount) => { @@ -128,28 +130,35 @@ const getSolution = (units, amount) => { } const solutionToOriginalUnits = (solution, units) => { - const billsToAssign = (count, left) => _.clamp(0, count)(_.isNaN(left) || _.isNil(left) ? 0 : left) + const billsToAssign = (count, left) => + _.clamp(0, count)(_.isNaN(left) || _.isNil(left) ? 0 : left) const billsLeft = Object.fromEntries(solution) - return units.map( - ({ count, name, denomination }) => { - const provisioned = billsToAssign(count, billsLeft[denomination]) - billsLeft[denomination] -= provisioned - return { name, denomination, provisioned } - } - ) + return units.map(({ count, name, denomination }) => { + const provisioned = billsToAssign(count, billsLeft[denomination]) + billsLeft[denomination] -= provisioned + return { name, denomination, provisioned } + }) } function makeChange(outCassettes, amount) { - const ss_solution = getSolution_old(outCassettes, amount, BILL_LIST_MODES.VALUE_ROUND_ROBIN) + const ss_solution = getSolution_old( + outCassettes, + amount, + BILL_LIST_MODES.VALUE_ROUND_ROBIN, + ) const cc_solution = getSolution(outCassettes, amount) if (!cc.check(cc_solution, amount.toNumber())) { - logger.error(new Error("coin-change provided a bad solution")) + logger.error(new Error('coin-change provided a bad solution')) return solutionToOriginalUnits(ss_solution, outCassettes) } if (!!ss_solution !== !!cc_solution) { - logger.error(new Error(`subset-sum and coin-change don't agree on solvability -- subset-sum:${!!ss_solution} coin-change:${!!cc_solution}`)) + logger.error( + new Error( + `subset-sum and coin-change don't agree on solvability -- subset-sum:${!!ss_solution} coin-change:${!!cc_solution}`, + ), + ) return solutionToOriginalUnits(ss_solution, outCassettes) } diff --git a/packages/server/lib/blacklist.js b/packages/server/lib/blacklist.js index 7665916c..b4dbc12f 100644 --- a/packages/server/lib/blacklist.js +++ b/packages/server/lib/blacklist.js @@ -8,7 +8,7 @@ const getBlacklist = () => db.any( `SELECT blacklist.address AS address, blacklist_messages.content AS blacklistMessage FROM blacklist JOIN blacklist_messages - ON blacklist.blacklist_message_id = blacklist_messages.id` + ON blacklist.blacklist_message_id = blacklist_messages.id`, ) const deleteFromBlacklist = address => { @@ -19,7 +19,9 @@ const deleteFromBlacklist = address => { const isValidAddress = address => { try { - return !_.isEmpty(addressDetector.getSupportedCoinsForAddress(address).matches) + return !_.isEmpty( + addressDetector.getSupportedCoinsForAddress(address).matches, + ) } catch { return false } @@ -29,24 +31,20 @@ const insertIntoBlacklist = address => { if (!isValidAddress(address)) { return Promise.reject(new Error('Invalid address')) } - return db - .none( - 'INSERT INTO blacklist (address) VALUES ($1);', - [address] - ) + return db.none('INSERT INTO blacklist (address) VALUES ($1);', [address]) } -function blocked (address) { +function blocked(address) { const sql = `SELECT address, content FROM blacklist b LEFT OUTER JOIN blacklist_messages bm ON bm.id = b.blacklist_message_id WHERE address = $1` return db.oneOrNone(sql, [address]) } -function getMessages () { +function getMessages() { const sql = `SELECT * FROM blacklist_messages` return db.any(sql) } -function editBlacklistMessage (id, content) { +function editBlacklistMessage(id, content) { const sql = `UPDATE blacklist_messages SET content = $1 WHERE id = $2 RETURNING id` return db.oneOrNone(sql, [content, id]) } @@ -57,5 +55,5 @@ module.exports = { deleteFromBlacklist, insertIntoBlacklist, getMessages, - editBlacklistMessage + editBlacklistMessage, } diff --git a/packages/server/lib/blockchain/bitcoin.js b/packages/server/lib/blockchain/bitcoin.js index 9d91ecb9..d066c633 100644 --- a/packages/server/lib/blockchain/bitcoin.js +++ b/packages/server/lib/blockchain/bitcoin.js @@ -1,5 +1,4 @@ const path = require('path') -const _ = require('lodash/fp') const { utils: coinUtils } = require('@lamassu/coins') @@ -13,9 +12,11 @@ const coinRec = coinUtils.getCryptoCurrency('BTC') const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR const tmpDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'tmp') : '/tmp' -const usrBinDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'bin') : '/usr/local/bin' +const usrBinDir = isDevMode() + ? path.resolve(BLOCKCHAIN_DIR, 'bin') + : '/usr/local/bin' -function setup (dataDir) { +function setup(dataDir) { !isDevMode() && common.firewall([coinRec.defaultPort]) const config = buildConfig() common.writeFile(path.resolve(dataDir, coinRec.configFile), config) @@ -23,12 +24,17 @@ function setup (dataDir) { !isDevMode() && common.writeSupervisorConfig(coinRec, cmd) } -function updateCore (coinRec, isCurrentlyRunning) { +function updateCore(coinRec, isCurrentlyRunning) { common.logger.info('Updating Bitcoin Core. This may take a minute...') !isDevMode() && common.es(`sudo supervisorctl stop bitcoin`) common.es(`curl -#o /tmp/bitcoin.tar.gz ${coinRec.url}`) - if (common.es(`sha256sum /tmp/bitcoin.tar.gz | awk '{print $1}'`).trim() !== coinRec.urlHash) { - common.logger.info('Failed to update Bitcoin Core: Package signature do not match!') + if ( + common.es(`sha256sum /tmp/bitcoin.tar.gz | awk '{print $1}'`).trim() !== + coinRec.urlHash + ) { + common.logger.info( + 'Failed to update Bitcoin Core: Package signature do not match!', + ) return } common.es(`tar -xzf /tmp/bitcoin.tar.gz -C /tmp/`) @@ -38,39 +44,71 @@ function updateCore (coinRec, isCurrentlyRunning) { common.es(`rm -r ${tmpDir}/${coinRec.dir.replace('/bin', '')}`) common.es(`rm ${tmpDir}/bitcoin.tar.gz`) - if (common.es(`grep "addresstype=p2sh-segwit" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { + if ( + common.es( + `grep "addresstype=p2sh-segwit" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`, + ) + ) { common.logger.info(`Enabling bech32 receiving addresses in config file..`) - common.es(`sed -i 's/addresstype=p2sh-segwit/addresstype=bech32/g' ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) + common.es( + `sed -i 's/addresstype=p2sh-segwit/addresstype=bech32/g' ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`, + ) } else { - common.logger.info(`bech32 receiving addresses already defined, skipping...`) + common.logger.info( + `bech32 receiving addresses already defined, skipping...`, + ) } - if (common.es(`grep "changetype=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { + if ( + common.es( + `grep "changetype=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`, + ) + ) { common.logger.info(`changetype already defined, skipping...`) } else { common.logger.info(`Enabling bech32 change addresses in config file..`) - common.es(`echo "\nchangetype=bech32" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) + common.es( + `echo "\nchangetype=bech32" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`, + ) } - if (common.es(`grep "listenonion=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { + if ( + common.es( + `grep "listenonion=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`, + ) + ) { common.logger.info(`listenonion already defined, skipping...`) } else { common.logger.info(`Setting 'listenonion=0' in config file...`) - common.es(`echo "\nlistenonion=0" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) + common.es( + `echo "\nlistenonion=0" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`, + ) } - if (common.es(`grep "fallbackfee=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { + if ( + common.es( + `grep "fallbackfee=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`, + ) + ) { common.logger.info(`fallbackfee already defined, skipping...`) } else { common.logger.info(`Setting 'fallbackfee=0.00005' in config file...`) - common.es(`echo "\nfallbackfee=0.00005" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) + common.es( + `echo "\nfallbackfee=0.00005" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`, + ) } - if (common.es(`grep "rpcworkqueue=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`)) { + if ( + common.es( + `grep "rpcworkqueue=" ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf || true`, + ) + ) { common.logger.info(`rpcworkqueue already defined, skipping...`) } else { common.logger.info(`Setting 'rpcworkqueue=2000' in config file...`) - common.es(`echo "\nrpcworkqueue=2000" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`) + common.es( + `echo "\nrpcworkqueue=2000" >> ${BLOCKCHAIN_DIR}/bitcoin/bitcoin.conf`, + ) } if (isCurrentlyRunning && !isDevMode()) { @@ -81,7 +119,7 @@ function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Bitcoin Core is updated!') } -function buildConfig () { +function buildConfig() { return `rpcuser=lamassuserver rpcpassword=${common.randomPass()} ${isDevMode() ? `regtest=1` : ``} @@ -97,13 +135,15 @@ walletrbf=1 listenonion=0 fallbackfee=0.00005 rpcworkqueue=2000 -${isDevMode() - ? `[regtest] +${ + isDevMode() + ? `[regtest] rpcport=18333 bind=0.0.0.0:18332 ${isRemoteNode(coinRec) ? `connect=${process.env.BTC_NODE_HOST}:${process.env.BTC_NODE_PORT}` : ``}` - : `rpcport=8333 + : `rpcport=8333 bind=0.0.0.0:8332 -${isRemoteNode(coinRec) ? `connect=${process.env.BTC_NODE_HOST}:${process.env.BTC_NODE_PORT}` : ``}`} +${isRemoteNode(coinRec) ? `connect=${process.env.BTC_NODE_HOST}:${process.env.BTC_NODE_PORT}` : ``}` +} ` } diff --git a/packages/server/lib/blockchain/bitcoincash.js b/packages/server/lib/blockchain/bitcoincash.js index 53e4ab8e..4a4d891b 100644 --- a/packages/server/lib/blockchain/bitcoincash.js +++ b/packages/server/lib/blockchain/bitcoincash.js @@ -8,7 +8,7 @@ module.exports = { setup, updateCore } const coinRec = coinUtils.getCryptoCurrency('BCH') -function setup (dataDir) { +function setup(dataDir) { common.firewall([coinRec.defaultPort]) const config = buildConfig() common.writeFile(path.resolve(dataDir, coinRec.configFile), config) @@ -16,12 +16,17 @@ function setup (dataDir) { common.writeSupervisorConfig(coinRec, cmd) } -function updateCore (coinRec, isCurrentlyRunning) { +function updateCore(coinRec, isCurrentlyRunning) { common.logger.info('Updating Bitcoin Cash. This may take a minute...') common.es(`sudo supervisorctl stop bitcoincash`) common.es(`curl -#Lo /tmp/bitcoincash.tar.gz ${coinRec.url}`) - if (common.es(`sha256sum /tmp/bitcoincash.tar.gz | awk '{print $1}'`).trim() !== coinRec.urlHash) { - common.logger.info('Failed to update Bitcoin Cash: Package signature do not match!') + if ( + common.es(`sha256sum /tmp/bitcoincash.tar.gz | awk '{print $1}'`).trim() !== + coinRec.urlHash + ) { + common.logger.info( + 'Failed to update Bitcoin Cash: Package signature do not match!', + ) return } common.es(`tar -xzf /tmp/bitcoincash.tar.gz -C /tmp/`) @@ -32,11 +37,17 @@ function updateCore (coinRec, isCurrentlyRunning) { common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`) common.es(`rm /tmp/bitcoincash.tar.gz`) - if (common.es(`grep "listenonion=" /mnt/blockchains/bitcoincash/bitcoincash.conf || true`)) { + if ( + common.es( + `grep "listenonion=" /mnt/blockchains/bitcoincash/bitcoincash.conf || true`, + ) + ) { common.logger.info(`listenonion already defined, skipping...`) } else { common.logger.info(`Setting 'listenonion=0' in config file...`) - common.es(`echo "\nlistenonion=0" >> /mnt/blockchains/bitcoincash/bitcoincash.conf`) + common.es( + `echo "\nlistenonion=0" >> /mnt/blockchains/bitcoincash/bitcoincash.conf`, + ) } if (isCurrentlyRunning) { @@ -47,7 +58,7 @@ function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Bitcoin Cash is updated!') } -function buildConfig () { +function buildConfig() { return `rpcuser=lamassuserver rpcpassword=${common.randomPass()} dbcache=500 diff --git a/packages/server/lib/blockchain/common.js b/packages/server/lib/blockchain/common.js index 169deb45..e9b105bb 100644 --- a/packages/server/lib/blockchain/common.js +++ b/packages/server/lib/blockchain/common.js @@ -7,7 +7,7 @@ const makeDir = require('make-dir') const _ = require('lodash/fp') -const logger = require('console-log-level')({level: 'info'}) +const logger = require('console-log-level')({ level: 'info' }) const { isDevMode } = require('../environment-helper') @@ -23,13 +23,15 @@ module.exports = { isInstalledSoftware, writeFile, getBinaries, - isUpdateDependent + isUpdateDependent, } const BINARIES = { BTC: { - defaultUrl: 'https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz', - defaultUrlHash: '376194f06596ecfa40331167c39bc70c355f960280bd2a645fdbf18f66527397', + defaultUrl: + 'https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz', + defaultUrlHash: + '376194f06596ecfa40331167c39bc70c355f960280bd2a645fdbf18f66527397', defaultDir: 'bitcoin-0.20.1/bin', url: 'https://bitcoincore.org/bin/bitcoin-core-28.0/bitcoin-28.0-x86_64-linux-gnu.tar.gz', dir: 'bitcoin-28.0/bin', @@ -46,16 +48,20 @@ const BINARIES = { urlHash: '3cb82f490e9c8e88007a0216b5261b33ef0fda962b9258441b2def59cb272a4d', }, DASH: { - defaultUrl: 'https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz', - defaultUrlHash: 'd89c2afd78183f3ee815adcccdff02098be0c982633889e7b1e9c9656fbef219', + defaultUrl: + 'https://github.com/dashpay/dash/releases/download/v18.1.0/dashcore-18.1.0-x86_64-linux-gnu.tar.gz', + defaultUrlHash: + 'd89c2afd78183f3ee815adcccdff02098be0c982633889e7b1e9c9656fbef219', defaultDir: 'dashcore-18.1.0/bin', url: 'https://github.com/dashpay/dash/releases/download/v21.1.1/dashcore-21.1.1-x86_64-linux-gnu.tar.gz', dir: 'dashcore-21.1.1/bin', urlHash: 'c3157d4a82a3cb7c904a68e827bd1e629854fefcc0dcaf1de4343a810a190bf5', }, LTC: { - defaultUrl: 'https://download.litecoin.org/litecoin-0.18.1/linux/litecoin-0.18.1-x86_64-linux-gnu.tar.gz', - defaultUrlHash: 'ca50936299e2c5a66b954c266dcaaeef9e91b2f5307069b9894048acf3eb5751', + defaultUrl: + 'https://download.litecoin.org/litecoin-0.18.1/linux/litecoin-0.18.1-x86_64-linux-gnu.tar.gz', + defaultUrlHash: + 'ca50936299e2c5a66b954c266dcaaeef9e91b2f5307069b9894048acf3eb5751', defaultDir: 'litecoin-0.18.1/bin', url: 'https://download.litecoin.org/litecoin-0.21.4/linux/litecoin-0.21.4-x86_64-linux-gnu.tar.gz', dir: 'litecoin-0.21.4/bin', @@ -64,38 +70,44 @@ const BINARIES = { BCH: { url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v28.0.0/bitcoin-cash-node-28.0.0-x86_64-linux-gnu.tar.gz', dir: 'bitcoin-cash-node-28.0.0/bin', - files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']], + files: [ + ['bitcoind', 'bitcoincashd'], + ['bitcoin-cli', 'bitcoincash-cli'], + ], urlHash: 'ba735cd3b70fab35ac1496e38596cec1f8d34989924376de001d4a86198f7158', }, XMR: { url: 'https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.4.tar.bz2', dir: 'monero-x86_64-linux-gnu-v0.18.3.4', - files: [['monerod', 'monerod'], ['monero-wallet-rpc', 'monero-wallet-rpc']], + files: [ + ['monerod', 'monerod'], + ['monero-wallet-rpc', 'monero-wallet-rpc'], + ], urlHash: '51ba03928d189c1c11b5379cab17dd9ae8d2230056dc05c872d0f8dba4a87f1d', - } + }, } const coinsUpdateDependent = ['BTC', 'LTC', 'DASH'] -function firewall (ports) { +function firewall(ports) { if (!ports || ports.length === 0) throw new Error('No ports supplied') const portsString = ports.join(',') es(`sudo ufw allow ${portsString}`) } -function randomPass () { +function randomPass() { return crypto.randomBytes(32).toString('hex') } -function es (cmd) { - const env = {HOME: os.userInfo().homedir} - const options = {encoding: 'utf8', env} +function es(cmd) { + const env = { HOME: os.userInfo().homedir } + const options = { encoding: 'utf8', env } const res = cp.execSync(cmd, options) logger.debug(res) return res.toString() } -function generateSupervisorConfig (cryptoCode, command, isWallet = false) { +function generateSupervisorConfig(cryptoCode, command, isWallet = false) { return `[program:${cryptoCode}${isWallet ? `-wallet` : ``}] command=nice ${command} autostart=true @@ -108,34 +120,46 @@ environment=HOME="/root" ` } -function writeSupervisorConfig (coinRec, cmd, walletCmd = '') { +function writeSupervisorConfig(coinRec, cmd, walletCmd = '') { if (isInstalledSoftware(coinRec)) return const blockchain = coinRec.code if (!_.isNil(coinRec.wallet)) { - const supervisorConfigWallet = generateSupervisorConfig(blockchain, walletCmd, true) - writeFile(`/etc/supervisor/conf.d/${coinRec.code}-wallet.conf`, supervisorConfigWallet) + const supervisorConfigWallet = generateSupervisorConfig( + blockchain, + walletCmd, + true, + ) + writeFile( + `/etc/supervisor/conf.d/${coinRec.code}-wallet.conf`, + supervisorConfigWallet, + ) } const supervisorConfig = generateSupervisorConfig(blockchain, cmd) writeFile(`/etc/supervisor/conf.d/${coinRec.code}.conf`, supervisorConfig) } -function isInstalledSoftware (coinRec) { +function isInstalledSoftware(coinRec) { if (isDevMode()) { - return fs.existsSync(`${BLOCKCHAIN_DIR}/${coinRec.code}/${coinRec.configFile}`) - && fs.existsSync(`${BLOCKCHAIN_DIR}/bin/${coinRec.daemon}`) + return ( + fs.existsSync( + `${BLOCKCHAIN_DIR}/${coinRec.code}/${coinRec.configFile}`, + ) && fs.existsSync(`${BLOCKCHAIN_DIR}/bin/${coinRec.daemon}`) + ) } - const nodeInstalled = fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`) + const nodeInstalled = fs.existsSync( + `/etc/supervisor/conf.d/${coinRec.code}.conf`, + ) const walletInstalled = _.isNil(coinRec.wallet) ? true : fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.wallet.conf`) return nodeInstalled && walletInstalled } -function fetchAndInstall (coinRec) { +function fetchAndInstall(coinRec) { const requiresUpdate = isUpdateDependent(coinRec.cryptoCode) if (isInstalledSoftware(coinRec)) return @@ -149,12 +173,16 @@ function fetchAndInstall (coinRec) { es(`wget -q ${url}`) if (es(`sha256sum ${downloadFile} | awk '{print $1}'`).trim() !== hash) { - logger.info(`Failed to install ${coinRec.code}: Package signature do not match!`) + logger.info( + `Failed to install ${coinRec.code}: Package signature do not match!`, + ) return } es(`tar -xf ${downloadFile}`) - const usrBinDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'bin') : '/usr/local/bin' + const usrBinDir = isDevMode() + ? path.resolve(BLOCKCHAIN_DIR, 'bin') + : '/usr/local/bin' if (isDevMode()) { makeDir.sync(usrBinDir) @@ -170,7 +198,7 @@ function fetchAndInstall (coinRec) { }, binaries.files) } -function writeFile (path, content) { +function writeFile(path, content) { try { fs.writeFileSync(path, content) } catch (err) { @@ -183,12 +211,12 @@ function writeFile (path, content) { } } -function getBinaries (coinCode) { +function getBinaries(coinCode) { const binaries = BINARIES[coinCode] if (!binaries) throw new Error(`No such coin: ${coinCode}`) return binaries } -function isUpdateDependent (coinCode) { +function isUpdateDependent(coinCode) { return _.includes(coinCode, coinsUpdateDependent) } diff --git a/packages/server/lib/blockchain/dash.js b/packages/server/lib/blockchain/dash.js index 51ed159f..29a74088 100644 --- a/packages/server/lib/blockchain/dash.js +++ b/packages/server/lib/blockchain/dash.js @@ -8,7 +8,7 @@ module.exports = { setup, updateCore } const coinRec = coinUtils.getCryptoCurrency('DASH') -function setup (dataDir) { +function setup(dataDir) { common.firewall([coinRec.defaultPort]) const config = buildConfig() common.writeFile(path.resolve(dataDir, coinRec.configFile), config) @@ -16,12 +16,17 @@ function setup (dataDir) { common.writeSupervisorConfig(coinRec, cmd) } -function updateCore (coinRec, isCurrentlyRunning) { +function updateCore(coinRec, isCurrentlyRunning) { common.logger.info('Updating Dash Core. This may take a minute...') common.es(`sudo supervisorctl stop dash`) common.es(`curl -#Lo /tmp/dash.tar.gz ${coinRec.url}`) - if (common.es(`sha256sum /tmp/dash.tar.gz | awk '{print $1}'`).trim() !== coinRec.urlHash) { - common.logger.info('Failed to update Dash Core: Package signature do not match!') + if ( + common.es(`sha256sum /tmp/dash.tar.gz | awk '{print $1}'`).trim() !== + coinRec.urlHash + ) { + common.logger.info( + 'Failed to update Dash Core: Package signature do not match!', + ) return } common.es(`tar -xzf /tmp/dash.tar.gz -C /tmp/`) @@ -31,20 +36,38 @@ function updateCore (coinRec, isCurrentlyRunning) { common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`) common.es(`rm /tmp/dash.tar.gz`) - if (common.es(`grep "enableprivatesend=" /mnt/blockchains/dash/dash.conf || true`)) { + if ( + common.es( + `grep "enableprivatesend=" /mnt/blockchains/dash/dash.conf || true`, + ) + ) { common.logger.info(`Switching from 'PrivateSend' to 'CoinJoin'...`) - common.es(`sed -i 's/enableprivatesend/enablecoinjoin/g' /mnt/blockchains/dash/dash.conf`) - } else if (common.es(`grep "enablecoinjoin=" /mnt/blockchains/dash/dash.conf || true`)) { + common.es( + `sed -i 's/enableprivatesend/enablecoinjoin/g' /mnt/blockchains/dash/dash.conf`, + ) + } else if ( + common.es(`grep "enablecoinjoin=" /mnt/blockchains/dash/dash.conf || true`) + ) { common.logger.info(`enablecoinjoin already defined, skipping...`) } else { common.logger.info(`Enabling CoinJoin in config file...`) common.es(`echo "\nenablecoinjoin=1" >> /mnt/blockchains/dash/dash.conf`) } - if (common.es(`grep "privatesendautostart=" /mnt/blockchains/dash/dash.conf || true`)) { + if ( + common.es( + `grep "privatesendautostart=" /mnt/blockchains/dash/dash.conf || true`, + ) + ) { common.logger.info(`Switching from 'PrivateSend' to 'CoinJoin'...`) - common.es(`sed -i 's/privatesendautostart/coinjoinautostart/g' /mnt/blockchains/dash/dash.conf`) - } else if (common.es(`grep "coinjoinautostart=" /mnt/blockchains/dash/dash.conf || true`)) { + common.es( + `sed -i 's/privatesendautostart/coinjoinautostart/g' /mnt/blockchains/dash/dash.conf`, + ) + } else if ( + common.es( + `grep "coinjoinautostart=" /mnt/blockchains/dash/dash.conf || true`, + ) + ) { common.logger.info(`coinjoinautostart already defined, skipping...`) } else { common.logger.info(`Enabling CoinJoin AutoStart in config file...`) @@ -53,7 +76,9 @@ function updateCore (coinRec, isCurrentlyRunning) { if (common.es(`grep "litemode=" /mnt/blockchains/dash/dash.conf || true`)) { common.logger.info(`Switching from 'LiteMode' to 'DisableGovernance'...`) - common.es(`sed -i 's/litemode/disablegovernance/g' /mnt/blockchains/dash/dash.conf`) + common.es( + `sed -i 's/litemode/disablegovernance/g' /mnt/blockchains/dash/dash.conf`, + ) } else { common.es(`echo "\ndisablegovernance already defined, skipping..."`) } @@ -66,7 +91,7 @@ function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Dash Core is updated!') } -function buildConfig () { +function buildConfig() { return `rpcuser=lamassuserver rpcpassword=${common.randomPass()} dbcache=500 diff --git a/packages/server/lib/blockchain/do-volume.js b/packages/server/lib/blockchain/do-volume.js index e3f5b825..671d8d68 100644 --- a/packages/server/lib/blockchain/do-volume.js +++ b/packages/server/lib/blockchain/do-volume.js @@ -6,20 +6,20 @@ const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR const MOUNT_POINT = BLOCKCHAIN_DIR -module.exports = {prepareVolume} +module.exports = { prepareVolume } const logger = common.logger -function isMounted () { +function isMounted() { return fs.existsSync(MOUNT_POINT) } -function isFormatted (volumePath) { +function isFormatted(volumePath) { const res = common.es(`file --dereference -s ${volumePath}`).trim() return res !== `${volumePath}: data` } -function formatVolume (volumePath) { +function formatVolume(volumePath) { if (isFormatted(volumePath)) { logger.info('Volume is already formatted.') return @@ -29,7 +29,7 @@ function formatVolume (volumePath) { common.es(`sudo mkfs.ext4 ${volumePath}`) } -function mountVolume (volumePath) { +function mountVolume(volumePath) { if (isMounted()) { logger.info('Volume is already mounted.') return @@ -38,10 +38,12 @@ function mountVolume (volumePath) { logger.info('Mounting...') common.es(`sudo mkdir -p ${MOUNT_POINT}`) common.es(`sudo mount -o discard,defaults ${volumePath} ${MOUNT_POINT}`) - common.es(`echo ${volumePath} ${MOUNT_POINT} ext4 defaults,nofail,discard 0 0 | sudo tee -a /etc/fstab`) + common.es( + `echo ${volumePath} ${MOUNT_POINT} ext4 defaults,nofail,discard 0 0 | sudo tee -a /etc/fstab`, + ) } -function locateVolume () { +function locateVolume() { const res = common.es('ls /dev/disk/by-id/*') const lines = res.trim().split('\n') @@ -58,7 +60,7 @@ function locateVolume () { return lines[0].trim() } -function prepareVolume () { +function prepareVolume() { if (isMounted()) { logger.info('Volume is already mounted.') return true diff --git a/packages/server/lib/blockchain/ethereum.js b/packages/server/lib/blockchain/ethereum.js index 9434ebdc..42eab589 100644 --- a/packages/server/lib/blockchain/ethereum.js +++ b/packages/server/lib/blockchain/ethereum.js @@ -4,11 +4,16 @@ const common = require('./common') module.exports = { setup, updateCore } -function updateCore (coinRec, isCurrentlyRunning) { - common.logger.info('Updating the Geth Ethereum wallet. This may take a minute...') +function updateCore(coinRec, isCurrentlyRunning) { + common.logger.info( + 'Updating the Geth Ethereum wallet. This may take a minute...', + ) common.es(`sudo supervisorctl stop ethereum`) common.es(`curl -#o /tmp/ethereum.tar.gz ${coinRec.url}`) - if (common.es(`sha256sum /tmp/ethereum.tar.gz | awk '{print $1}'`).trim() !== coinRec.urlHash) { + if ( + common.es(`sha256sum /tmp/ethereum.tar.gz | awk '{print $1}'`).trim() !== + coinRec.urlHash + ) { common.logger.info('Failed to update Geth: Package signature do not match!') return } @@ -27,7 +32,7 @@ function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Geth is updated!') } -function setup (dataDir) { +function setup(dataDir) { const coinRec = coinUtils.getCryptoCurrency('ETH') common.firewall([coinRec.defaultPort]) const cmd = `/usr/local/bin/${coinRec.daemon} --datadir "${dataDir}" --syncmode="light" --cache 2048 --maxpeers 40 --http` diff --git a/packages/server/lib/blockchain/install.js b/packages/server/lib/blockchain/install.js index 821591d6..5aa2f744 100644 --- a/packages/server/lib/blockchain/install.js +++ b/packages/server/lib/blockchain/install.js @@ -10,7 +10,11 @@ const _ = require('lodash/fp') const { utils: coinUtils } = require('@lamassu/coins') const settingsLoader = require('../new-settings-loader') const wallet = require('../wallet') -const { isDevMode, isRemoteNode, isRemoteWallet } = require('../environment-helper') +const { + isDevMode, + isRemoteNode, + isRemoteWallet, +} = require('../environment-helper') const common = require('./common') const doVolume = require('./do-volume') @@ -24,35 +28,38 @@ const PLUGINS = { BCH: require('./bitcoincash.js'), DASH: require('./dash.js'), LTC: require('./litecoin.js'), - XMR: require('./monero.js') + XMR: require('./monero.js'), } const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR module.exports = { isEnvironmentValid, - run + run, } -function installedVolumeFilePath (crypto) { +function installedVolumeFilePath(crypto) { return path.resolve(coinUtils.cryptoDir(crypto, BLOCKCHAIN_DIR), '.installed') } -function isInstalledVolume (crypto) { +function isInstalledVolume(crypto) { return fs.existsSync(installedVolumeFilePath(crypto)) } -function isInstalledSoftware (crypto) { +function isInstalledSoftware(crypto) { return common.isInstalledSoftware(crypto) } -function processCryptos (codes) { +function processCryptos(codes) { if (_.isEmpty(codes)) { logger.info('No cryptos selected. Exiting.') process.exit(0) } - logger.info('Thanks! Installing: %s. Will take a while...', _.join(', ', codes)) + logger.info( + 'Thanks! Installing: %s. Will take a while...', + _.join(', ', codes), + ) const selectedCryptos = _.map(code => _.find(['code', code], cryptos), codes) @@ -89,22 +96,32 @@ function processCryptos (codes) { logger.info('Installation complete.') } -function isEnvironmentValid (crypto) { +function isEnvironmentValid(crypto) { if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_LOCATION`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_LOCATION is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_NODE_LOCATION is not set!`, + ) if (_.isEmpty(process.env[`${crypto.cryptoCode}_WALLET_LOCATION`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_WALLET_LOCATION is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_WALLET_LOCATION is not set!`, + ) if (isRemoteWallet(crypto) && !isRemoteNode(crypto)) - throw new Error(`Invalid environment setup for ${crypto.display}: It's not possible to use a remote wallet without using a remote node!`) + throw new Error( + `Invalid environment setup for ${crypto.display}: It's not possible to use a remote wallet without using a remote node!`, + ) if (isRemoteNode(crypto) && !isRemoteWallet(crypto)) { if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_HOST`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_HOST is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_NODE_HOST is not set!`, + ) if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_PORT`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_PORT is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_NODE_PORT is not set!`, + ) if (_.isEmpty(process.env.BLOCKCHAIN_DIR)) throw new Error(`The environment variable for BLOCKCHAIN_DIR is not set!`) @@ -112,28 +129,39 @@ function isEnvironmentValid (crypto) { if (isRemoteWallet(crypto)) { if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_RPC_HOST`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_RPC_HOST is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_NODE_RPC_HOST is not set!`, + ) if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_RPC_PORT`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_RPC_PORT is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_NODE_RPC_PORT is not set!`, + ) if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_USER`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_USER is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_NODE_USER is not set!`, + ) if (_.isEmpty(process.env[`${crypto.cryptoCode}_NODE_PASSWORD`])) - throw new Error(`The environment variable for ${crypto.cryptoCode}_NODE_PASSWORD is not set!`) + throw new Error( + `The environment variable for ${crypto.cryptoCode}_NODE_PASSWORD is not set!`, + ) } return true } -function setupCrypto (crypto) { +function setupCrypto(crypto) { logger.info(`Installing ${crypto.display}...`) - if (!isEnvironmentValid(crypto)) throw new Error(`Environment error for ${crypto.display}`) + if (!isEnvironmentValid(crypto)) + throw new Error(`Environment error for ${crypto.display}`) if (isRemoteWallet(crypto)) { - logger.info(`Environment variable ${crypto.cryptoCode}_WALLET_LOCATION is set as 'remote', so there's no need to install a node in the system. Exiting...`) + logger.info( + `Environment variable ${crypto.cryptoCode}_WALLET_LOCATION is set as 'remote', so there's no need to install a node in the system. Exiting...`, + ) return } @@ -141,7 +169,9 @@ function setupCrypto (crypto) { makeDir.sync(cryptoDir) const cryptoPlugin = plugin(crypto) const oldDir = process.cwd() - const tmpDir = isDevMode() ? path.resolve(BLOCKCHAIN_DIR, 'tmp', 'blockchain-install') : '/tmp/blockchain-install' + const tmpDir = isDevMode() + ? path.resolve(BLOCKCHAIN_DIR, 'tmp', 'blockchain-install') + : '/tmp/blockchain-install' makeDir.sync(tmpDir) process.chdir(tmpDir) @@ -157,62 +187,74 @@ function setupCrypto (crypto) { process.chdir(oldDir) } -function updateCrypto (crypto) { +function updateCrypto(crypto) { if (!common.isUpdateDependent(crypto.cryptoCode)) return const cryptoPlugin = plugin(crypto) // TODO: we need to refactor the way we retrieve this status, p.e Monero uses two // services with specific names, so each coin should have its implementation. // Currently, it's not a breaking change because only BTC is update dependent - const status = common.es(`sudo supervisorctl status ${crypto.code} | awk '{ print $2 }'`).trim() + const status = common + .es(`sudo supervisorctl status ${crypto.code} | awk '{ print $2 }'`) + .trim() const isCurrentlyRunning = _.includes(status, ['RUNNING', 'STARTING']) - cryptoPlugin.updateCore(common.getBinaries(crypto.cryptoCode), isCurrentlyRunning) + cryptoPlugin.updateCore( + common.getBinaries(crypto.cryptoCode), + isCurrentlyRunning, + ) } -function plugin (crypto) { +function plugin(crypto) { const plugin = PLUGINS[crypto.cryptoCode] if (!plugin) throw new Error(`No such plugin: ${crypto.cryptoCode}`) return plugin } -function getBlockchainSyncStatus (cryptoList) { - return settingsLoader.loadLatest() - .then(settings => { - if (isDevMode()) return new Array(_.size(cryptoList)).fill('ready') +function getBlockchainSyncStatus(cryptoList) { + return settingsLoader.loadLatest().then(settings => { + if (isDevMode()) return new Array(_.size(cryptoList)).fill('ready') - const blockchainStatuses = _.reduce((acc, value) => { - const processStatus = common.es(`sudo supervisorctl status ${value.code} | awk '{ print $2 }'`).trim() - return acc.then(a => { - if (processStatus === 'RUNNING') { - return wallet.checkBlockchainStatus(settings, value.cryptoCode) - .then(res => Promise.resolve({ ...a, [value.cryptoCode]: res })) - } - return Promise.resolve({ ...a }) - }) - }, - Promise.resolve({}), - cryptoList - ) + const blockchainStatuses = _.reduce( + (acc, value) => { + const processStatus = common + .es(`sudo supervisorctl status ${value.code} | awk '{ print $2 }'`) + .trim() + return acc.then(a => { + if (processStatus === 'RUNNING') { + return wallet + .checkBlockchainStatus(settings, value.cryptoCode) + .then(res => Promise.resolve({ ...a, [value.cryptoCode]: res })) + } + return Promise.resolve({ ...a }) + }) + }, + Promise.resolve({}), + cryptoList, + ) - return blockchainStatuses - }) + return blockchainStatuses + }) } -function isInstalled (crypto) { +function isInstalled(crypto) { return isDevMode() ? isInstalledSoftware(crypto) : isInstalledSoftware(crypto) && isInstalledVolume(crypto) } -function isDisabled (crypto) { +function isDisabled(crypto) { switch (crypto.cryptoCode) { case 'XMR': - return isInstalled(crypto) && 'Installed' || isInstalled(_.find(it => it.code === 'zcash', cryptos)) && 'Insufficient resources. Contact support.' + return ( + (isInstalled(crypto) && 'Installed') || + (isInstalled(_.find(it => it.code === 'zcash', cryptos)) && + 'Insufficient resources. Contact support.') + ) default: return isInstalled(crypto) && 'Installed' } } -function run () { +function run() { const choices = _.flow([ _.filter(c => !c.hideFromInstall), _.map(c => { @@ -220,47 +262,70 @@ function run () { name: c.display, value: c.code, checked: isInstalled(c), - disabled: isDisabled(c) + disabled: isDisabled(c), } }), ])(cryptos) const questions = [] - const validateAnswers = async (answers) => { - if (_.size(answers) > 2) return { message: `Please insert a maximum of two coins to install.`, isValid: false } + const validateAnswers = async answers => { + if (_.size(answers) > 2) + return { + message: `Please insert a maximum of two coins to install.`, + isValid: false, + } if ( _.isEmpty(_.difference(['monero', 'zcash'], answers)) || - (_.includes('monero', answers) && isInstalled(_.find(it => it.code === 'zcash', cryptos))) || - (_.includes('zcash', answers) && isInstalled(_.find(it => it.code === 'monero', cryptos))) + (_.includes('monero', answers) && + isInstalled(_.find(it => it.code === 'zcash', cryptos))) || + (_.includes('zcash', answers) && + isInstalled(_.find(it => it.code === 'monero', cryptos))) ) { - return { message: `Zcash and Monero installations are temporarily mutually exclusive, given the space needed for their blockchains. Contact support for more information.`, isValid: false } + return { + message: `Zcash and Monero installations are temporarily mutually exclusive, given the space needed for their blockchains. Contact support for more information.`, + isValid: false, + } } - return getBlockchainSyncStatus(cryptos) - .then(blockchainStatuses => { - const result = _.reduce((acc, value) => ({ ...acc, [value]: _.isNil(acc[value]) ? 1 : acc[value] + 1 }), {}, _.values(blockchainStatuses)) - if (_.size(answers) + result.syncing > 2) { - return { message: `Installing these coins would pass the 2 parallel blockchain synchronization limit. Please try again with fewer coins or try again later.`, isValid: false } + return getBlockchainSyncStatus(cryptos).then(blockchainStatuses => { + const result = _.reduce( + (acc, value) => ({ + ...acc, + [value]: _.isNil(acc[value]) ? 1 : acc[value] + 1, + }), + {}, + _.values(blockchainStatuses), + ) + if (_.size(answers) + result.syncing > 2) { + return { + message: `Installing these coins would pass the 2 parallel blockchain synchronization limit. Please try again with fewer coins or try again later.`, + isValid: false, } + } - if (result.syncing > 2) { - return { message: `There are currently more than 2 blockchains in their initial synchronization. Please try again later.`, isValid: false } + if (result.syncing > 2) { + return { + message: `There are currently more than 2 blockchains in their initial synchronization. Please try again later.`, + isValid: false, } + } - return { message: null, isValid: true } - }) + return { message: null, isValid: true } + }) } questions.push({ type: 'checkbox', name: 'crypto', - message: 'Which cryptocurrencies would you like to install?\nTo prevent server resource overloading, only TWO coins should be syncing simultaneously.\nMore coins can be installed after this process is over.', - choices + message: + 'Which cryptocurrencies would you like to install?\nTo prevent server resource overloading, only TWO coins should be syncing simultaneously.\nMore coins can be installed after this process is over.', + choices, }) - inquirer.prompt(questions) + inquirer + .prompt(questions) .then(answers => Promise.all([validateAnswers(answers.crypto), answers])) .then(([res, answers]) => { if (res.isValid) { diff --git a/packages/server/lib/blockchain/litecoin.js b/packages/server/lib/blockchain/litecoin.js index ce128dd0..7522dbfb 100644 --- a/packages/server/lib/blockchain/litecoin.js +++ b/packages/server/lib/blockchain/litecoin.js @@ -8,7 +8,7 @@ module.exports = { setup, updateCore } const coinRec = coinUtils.getCryptoCurrency('LTC') -function setup (dataDir) { +function setup(dataDir) { common.firewall([coinRec.defaultPort]) const config = buildConfig() common.writeFile(path.resolve(dataDir, coinRec.configFile), config) @@ -16,12 +16,17 @@ function setup (dataDir) { common.writeSupervisorConfig(coinRec, cmd) } -function updateCore (coinRec, isCurrentlyRunning) { +function updateCore(coinRec, isCurrentlyRunning) { common.logger.info('Updating Litecoin Core. This may take a minute...') common.es(`sudo supervisorctl stop litecoin`) common.es(`curl -#o /tmp/litecoin.tar.gz ${coinRec.url}`) - if (common.es(`sha256sum /tmp/litecoin.tar.gz | awk '{print $1}'`).trim() !== coinRec.urlHash) { - common.logger.info('Failed to update Litecoin Core: Package signature do not match!') + if ( + common.es(`sha256sum /tmp/litecoin.tar.gz | awk '{print $1}'`).trim() !== + coinRec.urlHash + ) { + common.logger.info( + 'Failed to update Litecoin Core: Package signature do not match!', + ) return } common.es(`tar -xzf /tmp/litecoin.tar.gz -C /tmp/`) @@ -31,25 +36,43 @@ function updateCore (coinRec, isCurrentlyRunning) { common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`) common.es(`rm /tmp/litecoin.tar.gz`) - if (common.es(`grep "changetype=" /mnt/blockchains/litecoin/litecoin.conf || true`)) { + if ( + common.es( + `grep "changetype=" /mnt/blockchains/litecoin/litecoin.conf || true`, + ) + ) { common.logger.info(`changetype already defined, skipping...`) } else { common.logger.info(`Enabling bech32 change addresses in config file..`) - common.es(`echo "\nchangetype=bech32" >> /mnt/blockchains/litecoin/litecoin.conf`) + common.es( + `echo "\nchangetype=bech32" >> /mnt/blockchains/litecoin/litecoin.conf`, + ) } - if (common.es(`grep "blockfilterindex=" /mnt/blockchains/litecoin/litecoin.conf || true`)) { + if ( + common.es( + `grep "blockfilterindex=" /mnt/blockchains/litecoin/litecoin.conf || true`, + ) + ) { common.logger.info(`blockfilterindex already defined, skipping...`) } else { common.logger.info(`Disabling blockfilterindex in config file..`) - common.es(`echo "\nblockfilterindex=0" >> /mnt/blockchains/litecoin/litecoin.conf`) + common.es( + `echo "\nblockfilterindex=0" >> /mnt/blockchains/litecoin/litecoin.conf`, + ) } - if (common.es(`grep "peerblockfilters=" /mnt/blockchains/litecoin/litecoin.conf || true`)) { + if ( + common.es( + `grep "peerblockfilters=" /mnt/blockchains/litecoin/litecoin.conf || true`, + ) + ) { common.logger.info(`peerblockfilters already defined, skipping...`) } else { common.logger.info(`Disabling peerblockfilters in config file..`) - common.es(`echo "\npeerblockfilters=0" >> /mnt/blockchains/litecoin/litecoin.conf`) + common.es( + `echo "\npeerblockfilters=0" >> /mnt/blockchains/litecoin/litecoin.conf`, + ) } if (isCurrentlyRunning) { @@ -60,7 +83,7 @@ function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Litecoin Core is updated!') } -function buildConfig () { +function buildConfig() { return `rpcuser=lamassuserver rpcpassword=${common.randomPass()} dbcache=500 diff --git a/packages/server/lib/blockchain/monero.js b/packages/server/lib/blockchain/monero.js index 870f3920..679f150e 100644 --- a/packages/server/lib/blockchain/monero.js +++ b/packages/server/lib/blockchain/monero.js @@ -8,7 +8,7 @@ module.exports = { setup, updateCore } const coinRec = utils.getCryptoCurrency('XMR') -function setup (dataDir) { +function setup(dataDir) { common.firewall([coinRec.defaultPort]) const auth = `lamassuserver:${common.randomPass()}` const config = buildConfig(auth) @@ -18,19 +18,26 @@ function setup (dataDir) { common.writeSupervisorConfig(coinRec, cmd, walletCmd) } -function updateCore (coinRec, isCurrentlyRunning) { +function updateCore(coinRec, isCurrentlyRunning) { common.logger.info('Updating Monero. This may take a minute...') common.es(`sudo supervisorctl stop monero monero-wallet`) common.es(`curl -#o /tmp/monero.tar.gz ${coinRec.url}`) - if (common.es(`sha256sum /tmp/monero.tar.gz | awk '{print $1}'`).trim() !== coinRec.urlHash) { - common.logger.info('Failed to update Monero: Package signature do not match!') + if ( + common.es(`sha256sum /tmp/monero.tar.gz | awk '{print $1}'`).trim() !== + coinRec.urlHash + ) { + common.logger.info( + 'Failed to update Monero: Package signature do not match!', + ) return } common.es(`tar -xf /tmp/monero.tar.gz -C /tmp/`) common.logger.info('Updating wallet...') common.es(`cp /tmp/${coinRec.dir}/monerod /usr/local/bin/monerod`) - common.es(`cp /tmp/${coinRec.dir}/monero-wallet-rpc /usr/local/bin/monero-wallet-rpc`) + common.es( + `cp /tmp/${coinRec.dir}/monero-wallet-rpc /usr/local/bin/monero-wallet-rpc`, + ) common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`) common.es(`rm /tmp/monero.tar.gz`) @@ -42,7 +49,7 @@ function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Monero is updated!') } -function buildConfig (auth) { +function buildConfig(auth) { return `rpc-login=${auth} stagenet=0 restricted-rpc=1 diff --git a/packages/server/lib/blockchain/zcash.js b/packages/server/lib/blockchain/zcash.js index a6baed51..1434cf06 100644 --- a/packages/server/lib/blockchain/zcash.js +++ b/packages/server/lib/blockchain/zcash.js @@ -9,12 +9,17 @@ module.exports = { setup, updateCore } const es = common.es const logger = common.logger -function updateCore (coinRec, isCurrentlyRunning) { +function updateCore(coinRec, isCurrentlyRunning) { common.logger.info('Updating your Zcash wallet. This may take a minute...') common.es(`sudo supervisorctl stop zcash`) common.es(`curl -#Lo /tmp/zcash.tar.gz ${coinRec.url}`) - if (common.es(`sha256sum /tmp/zcash.tar.gz | awk '{print $1}'`).trim() !== coinRec.urlHash) { - common.logger.info('Failed to update Zcash: Package signature do not match!') + if ( + common.es(`sha256sum /tmp/zcash.tar.gz | awk '{print $1}'`).trim() !== + coinRec.urlHash + ) { + common.logger.info( + 'Failed to update Zcash: Package signature do not match!', + ) return } common.es(`tar -xzf /tmp/zcash.tar.gz -C /tmp/`) @@ -24,11 +29,17 @@ function updateCore (coinRec, isCurrentlyRunning) { common.es(`rm -r /tmp/${coinRec.dir.replace('/bin', '')}`) common.es(`rm /tmp/zcash.tar.gz`) - if (common.es(`grep "walletrequirebackup=" /mnt/blockchains/zcash/zcash.conf || true`)) { + if ( + common.es( + `grep "walletrequirebackup=" /mnt/blockchains/zcash/zcash.conf || true`, + ) + ) { common.logger.info(`walletrequirebackup already defined, skipping...`) } else { common.logger.info(`Setting 'walletrequirebackup=false' in config file...`) - common.es(`echo "\nwalletrequirebackup=false" >> /mnt/blockchains/zcash/zcash.conf`) + common.es( + `echo "\nwalletrequirebackup=false" >> /mnt/blockchains/zcash/zcash.conf`, + ) } if (isCurrentlyRunning) { @@ -39,7 +50,7 @@ function updateCore (coinRec, isCurrentlyRunning) { common.logger.info('Zcash is updated!') } -function setup (dataDir) { +function setup(dataDir) { es('sudo apt-get update') es('sudo apt-get install libgomp1 -y') const coinRec = coinUtils.getCryptoCurrency('ZEC') @@ -54,7 +65,7 @@ function setup (dataDir) { common.writeSupervisorConfig(coinRec, cmd) } -function buildConfig () { +function buildConfig() { return `mainnet=1 addnode=mainnet.z.cash rpcuser=lamassuserver diff --git a/packages/server/lib/blockexplorers/mempool.space.js b/packages/server/lib/blockexplorers/mempool.space.js index 383c1c72..07a6cf66 100644 --- a/packages/server/lib/blockexplorers/mempool.space.js +++ b/packages/server/lib/blockexplorers/mempool.space.js @@ -1,16 +1,18 @@ -const axios = require("axios"); +const axios = require('axios') const getSatBEstimateFee = () => { - return axios.get('https://mempool.space/api/v1/fees/recommended') + return axios + .get('https://mempool.space/api/v1/fees/recommended') .then(r => r.data.hourFee) } const getSatBEstimateFees = () => { - return axios.get('https://mempool.space/api/v1/fees/recommended') + return axios + .get('https://mempool.space/api/v1/fees/recommended') .then(r => r.data) } module.exports = { getSatBEstimateFees, - getSatBEstimateFee -} \ No newline at end of file + getSatBEstimateFee, +} diff --git a/packages/server/lib/cash-in/cash-in-atomic.js b/packages/server/lib/cash-in/cash-in-atomic.js index 4995520e..bd452912 100644 --- a/packages/server/lib/cash-in/cash-in-atomic.js +++ b/packages/server/lib/cash-in/cash-in-atomic.js @@ -8,63 +8,73 @@ const cashInLow = require('./cash-in-low') module.exports = { atomic } -function atomic (machineTx, pi) { +function atomic(machineTx) { const TransactionMode = pgp.txMode.TransactionMode const isolationLevel = pgp.txMode.isolationLevel const mode = new TransactionMode({ tiLevel: isolationLevel.serializable }) - function transaction (t) { + function transaction(t) { const sql = 'select * from cash_in_txs where id=$1' const sql2 = 'select * from bills where cash_in_txs_id=$1' - return t.oneOrNone(sql, [machineTx.id]) - .then(row => { - if (row && row.tx_version >= machineTx.txVersion) throw new E.StaleTxError({ txId: machineTx.id }) + return t.oneOrNone(sql, [machineTx.id]).then(row => { + if (row && row.tx_version >= machineTx.txVersion) + throw new E.StaleTxError({ txId: machineTx.id }) - return t.any(sql2, [machineTx.id]) - .then(billRows => { - const dbTx = cashInLow.toObj(row) + return t.any(sql2, [machineTx.id]).then(billRows => { + const dbTx = cashInLow.toObj(row) - return preProcess(dbTx, machineTx, pi) - .then(preProcessedTx => cashInLow.upsert(t, dbTx, preProcessedTx)) - .then(r => { - return insertNewBills(t, billRows, machineTx) - .then(newBills => _.set('newBills', newBills, r)) - }) + return preProcess(dbTx, machineTx) + .then(preProcessedTx => cashInLow.upsert(t, dbTx, preProcessedTx)) + .then(r => { + return insertNewBills(t, billRows, machineTx).then(newBills => + _.set('newBills', newBills, r), + ) }) }) + }) } return db.tx({ mode }, transaction) } -function insertNewBills (t, billRows, machineTx) { +function insertNewBills(t, billRows, machineTx) { const bills = pullNewBills(billRows, machineTx) if (_.isEmpty(bills)) return Promise.resolve([]) const dbBills = _.map(cashInLow.massage, bills) - const billsByDestination = _.countBy(_.get(['destination_unit']) ,dbBills) + const billsByDestination = _.countBy(_.get(['destination_unit']), dbBills) - const columns = ['id', 'fiat', 'fiat_code', 'crypto_code', 'cash_in_fee', 'cash_in_txs_id', 'device_time', 'destination_unit'] + const columns = [ + 'id', + 'fiat', + 'fiat_code', + 'crypto_code', + 'cash_in_fee', + 'cash_in_txs_id', + 'device_time', + 'destination_unit', + ] const sql = pgp.helpers.insert(dbBills, columns, 'bills') const deviceID = machineTx.deviceId const sql2 = `update devices set recycler1 = recycler1 + $2, recycler2 = recycler2 + $3, recycler3 = recycler3 + $4, recycler4 = recycler4 + $5, recycler5 = recycler5 + $6, recycler6 = recycler6 + $7 where device_id = $1` - return t.none(sql2, [ - deviceID, - _.defaultTo(0, billsByDestination.recycler1), - _.defaultTo(0, billsByDestination.recycler2), - _.defaultTo(0, billsByDestination.recycler3), - _.defaultTo(0, billsByDestination.recycler4), - _.defaultTo(0, billsByDestination.recycler5), - _.defaultTo(0, billsByDestination.recycler6) - ]) + return t + .none(sql2, [ + deviceID, + _.defaultTo(0, billsByDestination.recycler1), + _.defaultTo(0, billsByDestination.recycler2), + _.defaultTo(0, billsByDestination.recycler3), + _.defaultTo(0, billsByDestination.recycler4), + _.defaultTo(0, billsByDestination.recycler5), + _.defaultTo(0, billsByDestination.recycler6), + ]) .then(() => { return t.none(sql) }) .then(() => bills) } -function pullNewBills (billRows, machineTx) { +function pullNewBills(billRows, machineTx) { if (_.isEmpty(machineTx.bills)) return [] const toBill = _.mapKeys(_.camelCase) @@ -73,7 +83,7 @@ function pullNewBills (billRows, machineTx) { return _.differenceBy(_.get('id'), machineTx.bills, bills) } -function preProcess (dbTx, machineTx, pi) { +function preProcess(dbTx, machineTx) { // Note: The way this works is if we're clear to send, // we mark the transaction as sendPending. // diff --git a/packages/server/lib/cash-in/cash-in-low.js b/packages/server/lib/cash-in/cash-in-low.js index d6331f0b..54428065 100644 --- a/packages/server/lib/cash-in/cash-in-low.js +++ b/packages/server/lib/cash-in/cash-in-low.js @@ -8,26 +8,40 @@ const E = require('../error') const PENDING_INTERVAL_MS = 60 * T.minutes -const massageFields = ['direction', 'cryptoNetwork', 'bills', 'blacklisted', 'blacklistMessage', 'addressReuse', 'promoCodeApplied', 'validWalletScore', 'cashInFeeCrypto'] +const massageFields = [ + 'direction', + 'cryptoNetwork', + 'bills', + 'blacklisted', + 'blacklistMessage', + 'addressReuse', + 'promoCodeApplied', + 'validWalletScore', + 'cashInFeeCrypto', +] const massageUpdateFields = _.concat(massageFields, 'cryptoAtoms') -const massage = _.flow(_.omit(massageFields), - convertBigNumFields, _.mapKeys(_.snakeCase)) +const massage = _.flow( + _.omit(massageFields), + convertBigNumFields, + _.mapKeys(_.snakeCase), +) -const massageUpdates = _.flow(_.omit(massageUpdateFields), - convertBigNumFields, _.mapKeys(_.snakeCase)) +const massageUpdates = _.flow( + _.omit(massageUpdateFields), + convertBigNumFields, + _.mapKeys(_.snakeCase), +) -module.exports = {toObj, upsert, insert, update, massage, isClearToSend} +module.exports = { toObj, upsert, insert, update, massage, isClearToSend } -function convertBigNumFields (obj) { +function convertBigNumFields(obj) { const convert = value => - value && BN.isBigNumber(value) - ? value.toString() - : value + value && BN.isBigNumber(value) ? value.toString() : value return _.mapValues(convert, obj) } -function toObj (row) { +function toObj(row) { if (!row) return null const keys = _.keys(row) @@ -35,7 +49,15 @@ function toObj (row) { keys.forEach(key => { const objKey = _.camelCase(key) - if (_.includes(key, ['crypto_atoms', 'fiat', 'cash_in_fee', 'commission_percentage', 'raw_ticker_price'])) { + if ( + _.includes(key, [ + 'crypto_atoms', + 'fiat', + 'cash_in_fee', + 'commission_percentage', + 'raw_ticker_price', + ]) + ) { newObj[objKey] = new BN(row[key]) return } @@ -48,35 +70,35 @@ function toObj (row) { return newObj } -function upsert (t, dbTx, preProcessedTx) { +function upsert(t, dbTx, preProcessedTx) { if (!dbTx) { - return insert(t, preProcessedTx) - .then(tx => ({dbTx, tx})) + return insert(t, preProcessedTx).then(tx => ({ dbTx, tx })) } - return update(t, dbTx, diff(dbTx, preProcessedTx)) - .then(tx => ({dbTx, tx})) + return update(t, dbTx, diff(dbTx, preProcessedTx)).then(tx => ({ dbTx, tx })) } -function insert (t, tx) { +function insert(t, tx) { const dbTx = massage(tx) const sql = pgp.helpers.insert(dbTx, null, 'cash_in_txs') + ' returning *' - return t.one(sql) - .then(toObj) + return t.one(sql).then(toObj) } -function update (t, tx, changes) { +function update(t, tx, changes) { if (_.isEmpty(changes)) return Promise.resolve(tx) - const dbChanges = isFinalTxStage(changes) ? massage(changes) : massageUpdates(changes) - const sql = pgp.helpers.update(dbChanges, null, 'cash_in_txs') + - pgp.as.format(' where id=$1', [tx.id]) + ' returning *' + const dbChanges = isFinalTxStage(changes) + ? massage(changes) + : massageUpdates(changes) + const sql = + pgp.helpers.update(dbChanges, null, 'cash_in_txs') + + pgp.as.format(' where id=$1', [tx.id]) + + ' returning *' - return t.one(sql) - .then(toObj) + return t.one(sql).then(toObj) } -function diff (oldTx, newTx) { +function diff(oldTx, newTx) { let updatedTx = {} if (!oldTx) throw new Error('oldTx must not be null') @@ -89,10 +111,15 @@ function diff (oldTx, newTx) { if (_.isEqualWith(nilEqual, oldField, newField)) return if (!ensureRatchet(oldField, newField, fieldKey)) { - logger.warn('Value from lamassu-machine would violate ratchet [%s]', fieldKey) + logger.warn( + 'Value from lamassu-machine would violate ratchet [%s]', + fieldKey, + ) logger.warn('Old tx: %j', oldTx) logger.warn('New tx: %j', newTx) - throw new E.RatchetError('Value from lamassu-machine would violate ratchet') + throw new E.RatchetError( + 'Value from lamassu-machine would violate ratchet', + ) } updatedTx[fieldKey] = newField @@ -101,12 +128,29 @@ function diff (oldTx, newTx) { return updatedTx } -function ensureRatchet (oldField, newField, fieldKey) { - const monotonic = ['cryptoAtoms', 'fiat', 'send', 'sendConfirmed', 'operatorCompleted', 'timedout', 'txVersion', 'batched', 'discount'] - const free = ['sendPending', 'error', 'errorCode', 'customerId', 'discountSource'] +function ensureRatchet(oldField, newField, fieldKey) { + const monotonic = [ + 'cryptoAtoms', + 'fiat', + 'send', + 'sendConfirmed', + 'operatorCompleted', + 'timedout', + 'txVersion', + 'batched', + 'discount', + ] + const free = [ + 'sendPending', + 'error', + 'errorCode', + 'customerId', + 'discountSource', + ] if (_.isNil(oldField)) return true - if (_.includes(fieldKey, monotonic)) return isMonotonic(oldField, newField, fieldKey) + if (_.includes(fieldKey, monotonic)) + return isMonotonic(oldField, newField, fieldKey) if (_.includes(fieldKey, free)) { if (_.isNil(newField)) return false @@ -114,13 +158,14 @@ function ensureRatchet (oldField, newField, fieldKey) { } if (_.isNil(newField)) return false - if (BN.isBigNumber(oldField) && BN.isBigNumber(newField)) return new BN(oldField).eq(newField) + if (BN.isBigNumber(oldField) && BN.isBigNumber(newField)) + return new BN(oldField).eq(newField) if (oldField.toString() === newField.toString()) return true return false } -function isMonotonic (oldField, newField, fieldKey) { +function isMonotonic(oldField, newField, fieldKey) { if (_.isNil(newField)) return false if (_.isBoolean(oldField)) return oldField === newField || !oldField if (BN.isBigNumber(oldField)) return oldField.lte(newField) @@ -129,20 +174,22 @@ function isMonotonic (oldField, newField, fieldKey) { throw new Error(`Unexpected value [${fieldKey}]: ${oldField}, ${newField}`) } -function nilEqual (a, b) { +function nilEqual(a, b) { if (_.isNil(a) && _.isNil(b)) return true return undefined } -function isClearToSend (oldTx, newTx) { +function isClearToSend(oldTx, newTx) { const now = Date.now() - return (newTx.send || newTx.batched) && + return ( + (newTx.send || newTx.batched) && (!oldTx || (!oldTx.sendPending && !oldTx.sendConfirmed)) && - (newTx.created > now - PENDING_INTERVAL_MS) + newTx.created > now - PENDING_INTERVAL_MS + ) } -function isFinalTxStage (txChanges) { +function isFinalTxStage(txChanges) { return txChanges.send || txChanges.batched } diff --git a/packages/server/lib/cash-in/cash-in-tx.js b/packages/server/lib/cash-in/cash-in-tx.js index a4e8c228..8ca54625 100644 --- a/packages/server/lib/cash-in/cash-in-tx.js +++ b/packages/server/lib/cash-in/cash-in-tx.js @@ -25,93 +25,114 @@ case else 'Pending' end` -module.exports = { post, monitorPending, cancel, PENDING_INTERVAL, TRANSACTION_STATES } +module.exports = { + post, + monitorPending, + cancel, + PENDING_INTERVAL, + TRANSACTION_STATES, +} -function post (machineTx, pi) { +function post(machineTx, pi) { logger.silly('Updating cashin tx:', machineTx) - return cashInAtomic.atomic(machineTx, pi) - .then(r => { - const updatedTx = r.tx - let addressReuse = false + return cashInAtomic.atomic(machineTx).then(r => { + const updatedTx = r.tx + let addressReuse = false - const promises = [settingsLoader.loadLatestConfig()] + const promises = [settingsLoader.loadLatestConfig()] - const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero() - if (isFirstPost) { - promises.push( - checkForBlacklisted(updatedTx), - doesTxReuseAddress(updatedTx), - getWalletScore(updatedTx, pi) - ) - } + const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero() + if (isFirstPost) { + promises.push( + checkForBlacklisted(updatedTx), + doesTxReuseAddress(updatedTx), + getWalletScore(updatedTx, pi), + ) + } - return Promise.all(promises) - .then(([config, blacklisted = false, isReusedAddress = false, walletScore = null]) => { - const { rejectAddressReuse } = configManager.getCompliance(config) - const isBlacklisted = !!blacklisted + return Promise.all(promises).then( + ([ + config, + blacklisted = false, + isReusedAddress = false, + walletScore = null, + ]) => { + const { rejectAddressReuse } = configManager.getCompliance(config) + const isBlacklisted = !!blacklisted - if (isBlacklisted) { - notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, false) - } else if (isReusedAddress && rejectAddressReuse) { - notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, true) - addressReuse = true - } - return postProcess(r, pi, isBlacklisted, addressReuse, walletScore) - .then(changes => _.set('walletScore', _.isNil(walletScore) ? null : walletScore.score, changes)) - .then(changes => cashInLow.update(db, updatedTx, changes)) - .then(_.flow( + if (isBlacklisted) { + notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, false) + } else if (isReusedAddress && rejectAddressReuse) { + notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, true) + addressReuse = true + } + return postProcess(r, pi, isBlacklisted, addressReuse, walletScore) + .then(changes => + _.set( + 'walletScore', + _.isNil(walletScore) ? null : walletScore.score, + changes, + ), + ) + .then(changes => cashInLow.update(db, updatedTx, changes)) + .then( + _.flow( _.set('bills', machineTx.bills), _.set('blacklisted', isBlacklisted), _.set('blacklistMessage', blacklisted?.content), _.set('addressReuse', addressReuse), - _.set('validWalletScore', _.isNil(walletScore) || walletScore.isValid), - )) - }) - }) + _.set( + 'validWalletScore', + _.isNil(walletScore) || walletScore.isValid, + ), + ), + ) + }, + ) + }) } -function registerTrades (pi, r) { +function registerTrades(pi, r) { _.forEach(bill => pi.buy(bill, r.tx), r.newBills) } -function logAction (rec, tx) { +function logAction(rec, tx) { const action = { tx_id: tx.id, action: rec.action || (rec.sendConfirmed ? 'sendCoins' : 'sendCoinsError'), error: rec.error, error_code: rec.errorCode, - tx_hash: rec.txHash + tx_hash: rec.txHash, } const sql = pgp.helpers.insert(action, null, 'cash_in_actions') - return db.none(sql) - .then(_.constant(rec)) + return db.none(sql).then(_.constant(rec)) } -function logActionById (action, _rec, txId) { +function logActionById(action, _rec, txId) { const rec = _.assign(_rec, { action, tx_id: txId }) const sql = pgp.helpers.insert(rec, null, 'cash_in_actions') return db.none(sql) } -function checkForBlacklisted (tx) { +function checkForBlacklisted(tx) { return blacklist.blocked(tx.toAddress) } -function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { +function postProcess(r, pi, isBlacklisted, addressReuse, walletScore) { if (addressReuse) { return Promise.resolve({ operatorCompleted: true, - error: 'Address Reused' + error: 'Address Reused', }) } if (isBlacklisted) { return Promise.resolve({ operatorCompleted: true, - error: 'Blacklisted Address' + error: 'Blacklisted Address', }) } @@ -120,7 +141,7 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { walletScore: walletScore.score, operatorCompleted: true, error: 'Chain analysis score is above defined threshold', - errorCode: 'scoreThresholdReached' + errorCode: 'scoreThresholdReached', }) } @@ -128,7 +149,8 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { if (!cashInLow.isClearToSend(r.dbTx, r.tx)) return Promise.resolve({}) - return pi.sendCoins(r.tx) + return pi + .sendCoins(r.tx) .then(txObj => { if (txObj.batched) { return { @@ -136,7 +158,7 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { batchTime: 'now()^', sendPending: true, error: null, - errorCode: null + errorCode: null, } } @@ -147,7 +169,7 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { sendTime: 'now()^', sendPending: false, error: null, - errorCode: null + errorCode: null, } }) .catch(err => { @@ -161,17 +183,18 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) { sendTime: 'now()^', error: err.message, errorCode: err.name, - sendPending: true + sendPending: true, } }) .then(sendRec => { - pi.notifyOperator(r.tx, sendRec) - .catch((err) => logger.error('Failure sending transaction notification', err)) + pi.notifyOperator(r.tx, sendRec).catch(err => + logger.error('Failure sending transaction notification', err), + ) return logAction(sendRec, r.tx) }) } -function doesTxReuseAddress (tx) { +function doesTxReuseAddress(tx) { const sql = ` SELECT EXISTS ( SELECT DISTINCT to_address FROM ( @@ -181,15 +204,14 @@ function doesTxReuseAddress (tx) { return db.one(sql, [tx.id, tx.toAddress]).then(({ exists }) => exists) } -function getWalletScore (tx, pi) { - return pi.isWalletScoringEnabled(tx) - .then(isEnabled => { - if (!isEnabled) return null - return pi.rateAddress(tx.cryptoCode, tx.toAddress) - }) +function getWalletScore(tx, pi) { + return pi.isWalletScoringEnabled(tx).then(isEnabled => { + if (!isEnabled) return null + return pi.rateAddress(tx.cryptoCode, tx.toAddress) + }) } -function monitorPending (settings) { +function monitorPending(settings) { const sql = `select * from cash_in_txs where created > now() - interval $1 and send @@ -203,27 +225,29 @@ function monitorPending (settings) { const tx = cashInLow.toObj(row) const pi = plugins(settings, tx.deviceId) - return post(tx, pi) - .catch(logger.error) + return post(tx, pi).catch(logger.error) } - return db.any(sql, [PENDING_INTERVAL, MAX_PENDING]) + return db + .any(sql, [PENDING_INTERVAL, MAX_PENDING]) .then(rows => pEachSeries(rows, row => processPending(row))) .catch(logger.error) } -function cancel (txId) { +function cancel(txId) { const updateRec = { error: 'Operator cancel', error_code: 'operatorCancel', operator_completed: true, - batch_id: null + batch_id: null, } return Promise.resolve() .then(() => { - return pgp.helpers.update(updateRec, null, 'cash_in_txs') + - pgp.as.format(' where id=$1', [txId]) + return ( + pgp.helpers.update(updateRec, null, 'cash_in_txs') + + pgp.as.format(' where id=$1', [txId]) + ) }) .then(sql => db.result(sql, false)) .then(res => { diff --git a/packages/server/lib/cash-out/cash-out-actions.js b/packages/server/lib/cash-out/cash-out-actions.js index 9c43e21a..07f7b8bd 100644 --- a/packages/server/lib/cash-out/cash-out-actions.js +++ b/packages/server/lib/cash-out/cash-out-actions.js @@ -1,51 +1,63 @@ const _ = require('lodash/fp') const pgp = require('pg-promise')() -module.exports = {logDispense, logActionById, logAction, logError} +module.exports = { logDispense, logActionById, logAction, logError } -function logDispense (t, tx) { - const baseRec = {error: tx.error, error_code: tx.errorCode} +function logDispense(t, tx) { + const baseRec = { error: tx.error, error_code: tx.errorCode } const rec = _.merge(mapDispense(tx), baseRec) const action = _.isEmpty(tx.error) ? 'dispense' : 'dispenseError' return logAction(t, action, rec, tx) } -function logActionById (t, action, _rec, txId) { - const rec = _.assign(_rec, {action, tx_id: txId, redeem: false}) +function logActionById(t, action, _rec, txId) { + const rec = _.assign(_rec, { action, tx_id: txId, redeem: false }) const sql = pgp.helpers.insert(rec, null, 'cash_out_actions') return t.none(sql) } -function logAction (t, action, _rec, tx) { - const rec = _.assign(_rec, {action, tx_id: tx.id, redeem: !!tx.redeem, device_id: tx.deviceId}) +function logAction(t, action, _rec, tx) { + const rec = _.assign(_rec, { + action, + tx_id: tx.id, + redeem: !!tx.redeem, + device_id: tx.deviceId, + }) const sql = pgp.helpers.insert(rec, null, 'cash_out_actions') - return t.none(sql) - .then(_.constant(tx)) + return t.none(sql).then(_.constant(tx)) } -function logError (t, action, err, tx) { - return logAction(t, action, { - error: err.message, - error_code: err.name - }, tx) +function logError(t, action, err, tx) { + return logAction( + t, + action, + { + error: err.message, + error_code: err.name, + }, + tx, + ) } -function mapDispense (tx) { +function mapDispense(tx) { const bills = tx.bills if (_.isEmpty(bills)) return {} const res = {} - _.forEach(it => { - const suffix = _.snakeCase(bills[it].name.replace(/cassette/gi, '')) - res[`provisioned_${suffix}`] = bills[it].provisioned - res[`denomination_${suffix}`] = bills[it].denomination - res[`dispensed_${suffix}`] = bills[it].dispensed - res[`rejected_${suffix}`] = bills[it].rejected - }, _.times(_.identity(), _.size(bills))) + _.forEach( + it => { + const suffix = _.snakeCase(bills[it].name.replace(/cassette/gi, '')) + res[`provisioned_${suffix}`] = bills[it].provisioned + res[`denomination_${suffix}`] = bills[it].denomination + res[`dispensed_${suffix}`] = bills[it].dispensed + res[`rejected_${suffix}`] = bills[it].rejected + }, + _.times(_.identity(), _.size(bills)), + ) return res } diff --git a/packages/server/lib/cash-out/cash-out-atomic.js b/packages/server/lib/cash-out/cash-out-atomic.js index ca5d2464..87d56a18 100644 --- a/packages/server/lib/cash-out/cash-out-atomic.js +++ b/packages/server/lib/cash-out/cash-out-atomic.js @@ -13,170 +13,208 @@ const toObj = helper.toObj module.exports = { atomic } -function atomic (tx, pi, fromClient) { +function atomic(tx, pi, fromClient) { const TransactionMode = pgp.txMode.TransactionMode const isolationLevel = pgp.txMode.isolationLevel const mode = new TransactionMode({ tiLevel: isolationLevel.serializable }) - function transaction (t) { + function transaction(t) { const sql = 'SELECT * FROM cash_out_txs WHERE id=$1 FOR UPDATE' - return t.oneOrNone(sql, [tx.id]) + return t + .oneOrNone(sql, [tx.id]) .then(toObj) .then(oldTx => { - const isStale = fromClient && oldTx && (oldTx.txVersion >= tx.txVersion) + const isStale = fromClient && oldTx && oldTx.txVersion >= tx.txVersion if (isStale) throw new E.StaleTxError({ txId: tx.id }) // Server doesn't bump version, so we just prevent from version being older. - const isStaleFromServer = !fromClient && oldTx && (oldTx.txVersion > tx.txVersion) - if (isStaleFromServer) throw new Error('Stale Error: server triggered', tx.id) + const isStaleFromServer = + !fromClient && oldTx && oldTx.txVersion > tx.txVersion + if (isStaleFromServer) + throw new Error('Stale Error: server triggered', tx.id) - return preProcess(t, oldTx, tx, pi) - .then(preProcessedTx => cashOutLow.upsert(t, oldTx, preProcessedTx)) + return preProcess(t, oldTx, tx, pi).then(preProcessedTx => + cashOutLow.upsert(t, oldTx, preProcessedTx), + ) }) } return db.tx({ mode }, transaction) } -function preProcess (t, oldTx, newTx, pi) { +function preProcess(t, oldTx, newTx, pi) { if (!oldTx) { - return pi.isHd(newTx) + return pi + .isHd(newTx) .then(isHd => nextHd(t, isHd, newTx)) .then(newTxHd => { - return pi.newAddress(newTxHd) - .then(_.merge(newTxHd)) + return pi.newAddress(newTxHd).then(_.merge(newTxHd)) }) .then(addressedTx => { const rec = { to_address: addressedTx.toAddress, - layer_2_address: addressedTx.layer2Address + layer_2_address: addressedTx.layer2Address, } return cashOutActions.logAction(t, 'provisionAddress', rec, addressedTx) }) .catch(err => { - pi.notifyOperator(newTx, { isRedemption: false, error: 'Error while provisioning address' }) - .catch((err) => logger.error('Failure sending transaction notification', err)) - return cashOutActions.logError(t, 'provisionAddress', err, newTx) - .then(() => { throw err }) + pi.notifyOperator(newTx, { + isRedemption: false, + error: 'Error while provisioning address', + }).catch(err => + logger.error('Failure sending transaction notification', err), + ) + return cashOutActions + .logError(t, 'provisionAddress', err, newTx) + .then(() => { + throw err + }) }) } - return Promise.resolve(updateStatus(oldTx, newTx)) - .then(updatedTx => { - if (updatedTx.status !== oldTx.status) { - const isZeroConf = pi.isZeroConf(updatedTx) - updatedTx.justAuthorized = wasJustAuthorized(oldTx, updatedTx, isZeroConf) + return Promise.resolve(updateStatus(oldTx, newTx)).then(updatedTx => { + if (updatedTx.status !== oldTx.status) { + const isZeroConf = pi.isZeroConf(updatedTx) + updatedTx.justAuthorized = wasJustAuthorized(oldTx, updatedTx, isZeroConf) - const rec = { - to_address: updatedTx.toAddress, - tx_hash: updatedTx.txHash - } - - return cashOutActions.logAction(t, updatedTx.status, rec, updatedTx) + const rec = { + to_address: updatedTx.toAddress, + tx_hash: updatedTx.txHash, } - const hasError = !oldTx.error && newTx.error - const hasDispenseOccurred = !oldTx.dispenseConfirmed && dispenseOccurred(newTx.bills) + return cashOutActions.logAction(t, updatedTx.status, rec, updatedTx) + } - if (hasError || hasDispenseOccurred) { - return cashOutActions.logDispense(t, updatedTx) - .then(it => updateCassettes(t, updatedTx).then(() => it) ) - .then((t) => { - pi.notifyOperator(updatedTx, { isRedemption: true }) - .catch((err) => logger.error('Failure sending transaction notification', err)) - return t - }) - } + const hasError = !oldTx.error && newTx.error + const hasDispenseOccurred = + !oldTx.dispenseConfirmed && dispenseOccurred(newTx.bills) - if (!oldTx.phone && newTx.phone) { - return cashOutActions.logAction(t, 'addPhone', {}, updatedTx) - } + if (hasError || hasDispenseOccurred) { + return cashOutActions + .logDispense(t, updatedTx) + .then(it => updateCassettes(t, updatedTx).then(() => it)) + .then(t => { + pi.notifyOperator(updatedTx, { isRedemption: true }).catch(err => + logger.error('Failure sending transaction notification', err), + ) + return t + }) + } - if (!oldTx.redeem && newTx.redeem) { - return cashOutActions.logAction(t, 'redeemLater', {}, updatedTx) - } + if (!oldTx.phone && newTx.phone) { + return cashOutActions.logAction(t, 'addPhone', {}, updatedTx) + } - return updatedTx - }) + if (!oldTx.redeem && newTx.redeem) { + return cashOutActions.logAction(t, 'redeemLater', {}, updatedTx) + } + + return updatedTx + }) } -function nextHd (t, isHd, tx) { +function nextHd(t, isHd, tx) { if (!isHd) return Promise.resolve(tx) - return t.one("select nextval('hd_indices_seq') as hd_index") + return t + .one("select nextval('hd_indices_seq') as hd_index") .then(row => _.set('hdIndex', row.hd_index, tx)) } -function updateCassettes (t, tx) { +function updateCassettes(t, tx) { if (!dispenseOccurred(tx.bills)) return Promise.resolve() - const billsStmt = _.join(', ')(_.map(it => `${tx.bills[it].name} = ${tx.bills[it].name} - $${it + 1}`)(_.range(0, _.size(tx.bills)))) + const billsStmt = _.join(', ')( + _.map(it => `${tx.bills[it].name} = ${tx.bills[it].name} - $${it + 1}`)( + _.range(0, _.size(tx.bills)), + ), + ) const returnStmt = _.join(', ')(_.map(bill => `${bill.name}`)(tx.bills)) const sql = `UPDATE devices SET ${billsStmt} WHERE device_id = $${_.size(tx.bills) + 1} RETURNING ${returnStmt}` const values = [] - _.forEach(it => values.push( - tx.bills[it].dispensed + tx.bills[it].rejected - ), _.times(_.identity(), _.size(tx.bills))) + _.forEach( + it => values.push(tx.bills[it].dispensed + tx.bills[it].rejected), + _.times(_.identity(), _.size(tx.bills)), + ) values.push(tx.deviceId) return t.one(sql, values) } -function wasJustAuthorized (oldTx, newTx, isZeroConf) { - const isAuthorized = () => _.includes(oldTx.status, ['notSeen', 'published', 'rejected']) && +function wasJustAuthorized(oldTx, newTx, isZeroConf) { + const isAuthorized = () => + _.includes(oldTx.status, ['notSeen', 'published', 'rejected']) && _.includes(newTx.status, ['authorized', 'instant', 'confirmed']) - const isConfirmed = () => _.includes(oldTx.status, ['notSeen', 'published', 'authorized', 'rejected']) && - _.includes(newTx.status, ['instant', 'confirmed']) + const isConfirmed = () => + _.includes(oldTx.status, [ + 'notSeen', + 'published', + 'authorized', + 'rejected', + ]) && _.includes(newTx.status, ['instant', 'confirmed']) return isZeroConf ? isAuthorized() : isConfirmed() } -function isPublished (status) { - return _.includes(status, ['published', 'rejected', 'authorized', 'instant', 'confirmed']) +function isPublished(status) { + return _.includes(status, [ + 'published', + 'rejected', + 'authorized', + 'instant', + 'confirmed', + ]) } -function isConfirmed (status) { +function isConfirmed(status) { return status === 'confirmed' } -function updateStatus (oldTx, newTx) { +function updateStatus(oldTx, newTx) { const oldStatus = oldTx.status const newStatus = ratchetStatus(oldStatus, newTx.status) - const publishedAt = !oldTx.publishedAt && isPublished(newStatus) - ? 'now()^' - : undefined + const publishedAt = + !oldTx.publishedAt && isPublished(newStatus) ? 'now()^' : undefined - const confirmedAt = !oldTx.confirmedAt && isConfirmed(newStatus) - ? 'now()^' - : undefined + const confirmedAt = + !oldTx.confirmedAt && isConfirmed(newStatus) ? 'now()^' : undefined const updateRec = { publishedAt, confirmedAt, - status: newStatus + status: newStatus, } return _.merge(newTx, updateRec) } -function ratchetStatus (oldStatus, newStatus) { - const statusOrder = ['notSeen', 'published', 'rejected', - 'authorized', 'instant', 'confirmed'] +function ratchetStatus(oldStatus, newStatus) { + const statusOrder = [ + 'notSeen', + 'published', + 'rejected', + 'authorized', + 'instant', + 'confirmed', + ] if (oldStatus === newStatus) return oldStatus if (newStatus === 'insufficientFunds') return newStatus - const idx = Math.max(statusOrder.indexOf(oldStatus), statusOrder.indexOf(newStatus)) + const idx = Math.max( + statusOrder.indexOf(oldStatus), + statusOrder.indexOf(newStatus), + ) return statusOrder[idx] } -function dispenseOccurred (bills) { +function dispenseOccurred(bills) { if (_.isEmpty(bills)) return false return _.every(_.overEvery([_.has('dispensed'), _.has('rejected')]), bills) } diff --git a/packages/server/lib/cash-out/cash-out-helper.js b/packages/server/lib/cash-out/cash-out-helper.js index 1b34b155..92919069 100644 --- a/packages/server/lib/cash-out/cash-out-helper.js +++ b/packages/server/lib/cash-out/cash-out-helper.js @@ -40,18 +40,31 @@ const SNAKE_CASE_BILL_FIELDS = [ 'provisioned_recycler_3', 'provisioned_recycler_4', 'provisioned_recycler_5', - 'provisioned_recycler_6' + 'provisioned_recycler_6', ] const BILL_FIELDS = _.map(_.camelCase, SNAKE_CASE_BILL_FIELDS) -module.exports = { redeemableTxs, toObj, toDb, REDEEMABLE_AGE, CASH_OUT_TRANSACTION_STATES } +module.exports = { + redeemableTxs, + toObj, + toDb, + REDEEMABLE_AGE, + CASH_OUT_TRANSACTION_STATES, +} -const mapValuesWithKey = _.mapValues.convert({cap: false}) +const mapValuesWithKey = _.mapValues.convert({ cap: false }) -function convertBigNumFields (obj) { +function convertBigNumFields(obj) { const convert = (value, key) => { - if (_.includes(key, [ 'cryptoAtoms', 'receivedCryptoAtoms', 'fiat', 'fixedFee' ])) { + if ( + _.includes(key, [ + 'cryptoAtoms', + 'receivedCryptoAtoms', + 'fiat', + 'fixedFee', + ]) + ) { // BACKWARDS_COMPATIBILITY 10.1 // bills before 10.2 don't have fixedFee if (key === 'fixedFee' && !value) return new BN(0).toString() @@ -59,62 +72,62 @@ function convertBigNumFields (obj) { } // Only test isNil for these fields since the others should not be empty. - if (_.includes(key, [ 'commissionPercentage', 'rawTickerPrice' ]) && !_.isNil(value)) { + if ( + _.includes(key, ['commissionPercentage', 'rawTickerPrice']) && + !_.isNil(value) + ) { return value.toString() } return value } - const convertKey = key => _.includes(key, ['cryptoAtoms', 'fiat']) - ? key + '#' - : key + const convertKey = key => + _.includes(key, ['cryptoAtoms', 'fiat']) ? key + '#' : key return _.mapKeys(convertKey, mapValuesWithKey(convert, obj)) } -function convertField (key) { +function convertField(key) { return _.snakeCase(key) } -function addDbBills (tx) { +function addDbBills(tx) { const bills = tx.bills if (_.isEmpty(bills)) return tx const billsObj = _.flow( - _.reduce( - (acc, value) => { - const suffix = _.snakeCase(value.name.replace(/cassette/gi, '')) - return { - ...acc, - [`provisioned_${suffix}`]: value.provisioned, - [`denomination_${suffix}`]: value.denomination - } - }, - {} - ), + _.reduce((acc, value) => { + const suffix = _.snakeCase(value.name.replace(/cassette/gi, '')) + return { + ...acc, + [`provisioned_${suffix}`]: value.provisioned, + [`denomination_${suffix}`]: value.denomination, + } + }, {}), it => { - const missingKeys = _.reduce( - (acc, value) => { - return _.assign({ [value]: 0 })(acc) - }, - {} - )(_.difference(SNAKE_CASE_BILL_FIELDS, _.keys(it))) + const missingKeys = _.reduce((acc, value) => { + return _.assign({ [value]: 0 })(acc) + }, {})(_.difference(SNAKE_CASE_BILL_FIELDS, _.keys(it))) return _.assign(missingKeys, it) - } + }, )(bills) return _.assign(tx, billsObj) } -function toDb (tx) { - const massager = _.flow(convertBigNumFields, addDbBills, - _.omit(['direction', 'bills', 'promoCodeApplied']), _.mapKeys(convertField)) +function toDb(tx) { + const massager = _.flow( + convertBigNumFields, + addDbBills, + _.omit(['direction', 'bills', 'promoCodeApplied']), + _.mapKeys(convertField), + ) return massager(tx) } -function toObj (row) { +function toObj(row) { if (!row) return null const keys = _.keys(row) @@ -126,7 +139,14 @@ function toObj (row) { newObj[objKey] = new BN(row[key]) return } - if (_.includes(key, ['crypto_atoms', 'fiat', 'commission_percentage', 'raw_ticker_price'])) { + if ( + _.includes(key, [ + 'crypto_atoms', + 'fiat', + 'commission_percentage', + 'raw_ticker_price', + ]) + ) { newObj[objKey] = new BN(row[key]) return } @@ -137,11 +157,20 @@ function toObj (row) { newObj.direction = 'cashOut' if (_.every(_.isNil, _.at(BILL_FIELDS, newObj))) return newObj - if (_.some(_.isNil, _.at(BILL_FIELDS, newObj))) throw new Error('Missing cassette values') + if (_.some(_.isNil, _.at(BILL_FIELDS, newObj))) + throw new Error('Missing cassette values') const billFieldsArr = _.concat( - _.map(it => ({ name: `cassette${it + 1}`, denomination: newObj[`denomination${it + 1}`], provisioned: newObj[`provisioned${it + 1}`] }))(_.range(0, MAX_CASSETTES)), - _.map(it => ({ name: `recycler${it + 1}`, denomination: newObj[`denominationRecycler${it + 1}`], provisioned: newObj[`provisionedRecycler${it + 1}`] }))(_.range(0, MAX_RECYCLERS)), + _.map(it => ({ + name: `cassette${it + 1}`, + denomination: newObj[`denomination${it + 1}`], + provisioned: newObj[`provisioned${it + 1}`], + }))(_.range(0, MAX_CASSETTES)), + _.map(it => ({ + name: `recycler${it + 1}`, + denomination: newObj[`denominationRecycler${it + 1}`], + provisioned: newObj[`provisionedRecycler${it + 1}`], + }))(_.range(0, MAX_RECYCLERS)), ) // There can't be bills with denomination === 0. @@ -151,7 +180,7 @@ function toObj (row) { return _.set('bills', bills, _.omit(BILL_FIELDS, newObj)) } -function redeemableTxs (deviceId) { +function redeemableTxs(deviceId) { const sql = `select * from cash_out_txs where device_id=$1 and redeem=$2 @@ -164,6 +193,5 @@ function redeemableTxs (deviceId) { ) and extract(epoch from (now() - greatest(created, confirmed_at))) < $4` - return db.any(sql, [deviceId, true, false, REDEEMABLE_AGE]) - .then(_.map(toObj)) + return db.any(sql, [deviceId, true, false, REDEEMABLE_AGE]).then(_.map(toObj)) } diff --git a/packages/server/lib/cash-out/cash-out-low.js b/packages/server/lib/cash-out/cash-out-low.js index 24aea40c..29d4bad8 100644 --- a/packages/server/lib/cash-out/cash-out-low.js +++ b/packages/server/lib/cash-out/cash-out-low.js @@ -7,73 +7,91 @@ const { anonymousCustomer } = require('../constants') const toDb = helper.toDb const toObj = helper.toObj -const UPDATEABLE_FIELDS = ['txHash', 'txVersion', 'status', 'dispense', 'dispenseConfirmed', - 'notified', 'redeem', 'phone', 'error', 'swept', 'publishedAt', 'confirmedAt', 'errorCode', - 'receivedCryptoAtoms', 'walletScore', 'customerId' ] +const UPDATEABLE_FIELDS = [ + 'txHash', + 'txVersion', + 'status', + 'dispense', + 'dispenseConfirmed', + 'notified', + 'redeem', + 'phone', + 'error', + 'swept', + 'publishedAt', + 'confirmedAt', + 'errorCode', + 'receivedCryptoAtoms', + 'walletScore', + 'customerId', +] -module.exports = {upsert, update, insert} +module.exports = { upsert, update, insert } -function upsert (t, oldTx, tx) { +function upsert(t, oldTx, tx) { if (!oldTx) { - return insert(t, tx) - .then(newTx => [oldTx, newTx]) + return insert(t, tx).then(newTx => [oldTx, newTx]) } - return update(t, tx, diff(oldTx, tx)) - .then(newTx => [oldTx, newTx, tx.justAuthorized]) + return update(t, tx, diff(oldTx, tx)).then(newTx => [ + oldTx, + newTx, + tx.justAuthorized, + ]) } -function insert (t, tx) { +function insert(t, tx) { const dbTx = toDb(tx) const sql = pgp.helpers.insert(dbTx, null, 'cash_out_txs') + ' returning *' - return t.one(sql) - .then(toObj) + return t.one(sql).then(toObj) } -function update (t, tx, changes) { +function update(t, tx, changes) { if (_.isEmpty(changes)) return Promise.resolve(tx) const dbChanges = toDb(changes) - const sql = pgp.helpers.update(dbChanges, null, 'cash_out_txs') + + const sql = + pgp.helpers.update(dbChanges, null, 'cash_out_txs') + pgp.as.format(' where id=$1', [tx.id]) const newTx = _.merge(tx, changes) - return t.none(sql) - .then(() => newTx) + return t.none(sql).then(() => newTx) } -function diff (oldTx, newTx) { +function diff(oldTx, newTx) { let updatedTx = {} UPDATEABLE_FIELDS.forEach(fieldKey => { - if (oldTx && _.isEqualWith(nilEqual, oldTx[fieldKey], newTx[fieldKey])) return + if (oldTx && _.isEqualWith(nilEqual, oldTx[fieldKey], newTx[fieldKey])) + return // We never null out an existing field - if (oldTx && _.isNil(newTx[fieldKey])) return updatedTx[fieldKey] = oldTx[fieldKey] + if (oldTx && _.isNil(newTx[fieldKey])) + return (updatedTx[fieldKey] = oldTx[fieldKey]) switch (fieldKey) { case 'customerId': if (oldTx.customerId === anonymousCustomer.uuid) { - return updatedTx['customerId'] = newTx['customerId'] + return (updatedTx['customerId'] = newTx['customerId']) } return // prevent dispense changing from 'true' to 'false' case 'dispense': if (!oldTx.dispense) { - return updatedTx[fieldKey] = newTx[fieldKey] + return (updatedTx[fieldKey] = newTx[fieldKey]) } return default: - return updatedTx[fieldKey] = newTx[fieldKey] + return (updatedTx[fieldKey] = newTx[fieldKey]) } }) return updatedTx } -function nilEqual (a, b) { +function nilEqual(a, b) { if (_.isNil(a) && _.isNil(b)) return true return undefined diff --git a/packages/server/lib/cash-out/cash-out-tx.js b/packages/server/lib/cash-out/cash-out-tx.js index 93274e4d..2944f450 100644 --- a/packages/server/lib/cash-out/cash-out-tx.js +++ b/packages/server/lib/cash-out/cash-out-tx.js @@ -20,7 +20,7 @@ module.exports = { monitorLiveIncoming, monitorStaleIncoming, monitorUnnotified, - cancel + cancel, } const STALE_INCOMING_TX_AGE = T.day @@ -31,38 +31,40 @@ const INSUFFICIENT_FUNDS_CODE = 570 const toObj = helper.toObj -function selfPost (tx, pi) { +function selfPost(tx, pi) { return post(tx, pi, false) } -function post (tx, pi, fromClient = true) { +function post(tx, pi, fromClient = true) { logger.silly('Updating cashout -- tx:', JSON.stringify(tx)) logger.silly('Updating cashout -- fromClient:', JSON.stringify(fromClient)) - return cashOutAtomic.atomic(tx, pi, fromClient) - .then(txVector => { - const [, newTx, justAuthorized] = txVector - return postProcess(txVector, justAuthorized, pi) - .then(changes => cashOutLow.update(db, newTx, changes)) - }) + return cashOutAtomic.atomic(tx, pi, fromClient).then(txVector => { + const [, newTx, justAuthorized] = txVector + return postProcess(txVector, justAuthorized, pi).then(changes => + cashOutLow.update(db, newTx, changes), + ) + }) } -function postProcess (txVector, justAuthorized, pi) { +function postProcess(txVector, justAuthorized, pi) { const [oldTx, newTx] = txVector if (justAuthorized) { pi.sell(newTx) - pi.notifyOperator(newTx, { isRedemption: false }) - .catch((err) => logger.error('Failure sending transaction notification', err)) + pi.notifyOperator(newTx, { isRedemption: false }).catch(err => + logger.error('Failure sending transaction notification', err), + ) } if ((newTx.dispense && !oldTx.dispense) || (newTx.redeem && !oldTx.redeem)) { - return pi.buildAvailableUnits(newTx.id) + return pi + .buildAvailableUnits(newTx.id) .then(units => { units = _.concat(units.cassettes, units.recyclers) logger.silly('Computing bills to dispense:', { txId: newTx.id, units: units, - fiat: newTx.fiat + fiat: newTx.fiat, }) const bills = billMath.makeChange(units, newTx.fiat) logger.silly('Bills to dispense:', JSON.stringify(bills)) @@ -73,27 +75,38 @@ function postProcess (txVector, justAuthorized, pi) { .then(bills => { const rec = {} - _.forEach(it => { - const suffix = _.snakeCase(bills[it].name.replace(/cassette/gi, '')) - rec[`provisioned_${suffix}`] = bills[it].provisioned - rec[`denomination_${suffix}`] = bills[it].denomination - }, _.times(_.identity(), _.size(bills))) + _.forEach( + it => { + const suffix = _.snakeCase(bills[it].name.replace(/cassette/gi, '')) + rec[`provisioned_${suffix}`] = bills[it].provisioned + rec[`denomination_${suffix}`] = bills[it].denomination + }, + _.times(_.identity(), _.size(bills)), + ) - return cashOutActions.logAction(db, 'provisionNotes', rec, newTx) + return cashOutActions + .logAction(db, 'provisionNotes', rec, newTx) .then(_.constant({ bills })) }) .catch(err => { - pi.notifyOperator(newTx, { error: err.message, isRedemption: true }) - .catch((err) => logger.error('Failure sending transaction notification', err)) - return cashOutActions.logError(db, 'provisionNotesError', err, newTx) - .then(() => { throw err }) + pi.notifyOperator(newTx, { + error: err.message, + isRedemption: true, + }).catch(err => + logger.error('Failure sending transaction notification', err), + ) + return cashOutActions + .logError(db, 'provisionNotesError', err, newTx) + .then(() => { + throw err + }) }) } return Promise.resolve({}) } -function fetchOpenTxs (statuses, fromAge, toAge) { +function fetchOpenTxs(statuses, fromAge, toAge) { const sql = `select * from cash_out_txs where ((extract(epoch from (now() - created))) * 1000)>$1 @@ -103,20 +116,27 @@ function fetchOpenTxs (statuses, fromAge, toAge) { const statusClause = _.map(pgp.as.text, statuses).join(',') - return db.any(sql, [fromAge, toAge, statusClause]) + return db + .any(sql, [fromAge, toAge, statusClause]) .then(rows => rows.map(toObj)) } -function processTxStatus (tx, settings) { +function processTxStatus(tx, settings) { const pi = plugins(settings, tx.deviceId) - return pi.getStatus(tx) - .then(res => _.assign(tx, { receivedCryptoAtoms: res.receivedCryptoAtoms, status: res.status })) + return pi + .getStatus(tx) + .then(res => + _.assign(tx, { + receivedCryptoAtoms: res.receivedCryptoAtoms, + status: res.status, + }), + ) .then(_tx => getWalletScore(_tx, pi)) .then(_tx => selfPost(_tx, pi)) } -function getWalletScore (tx, pi) { +function getWalletScore(tx, pi) { const statuses = ['published', 'authorized', 'confirmed', 'insufficientFunds'] if (!_.includes(tx.status, statuses) || !_.isNil(tx.walletScore)) { @@ -124,40 +144,54 @@ function getWalletScore (tx, pi) { } // Transaction shows up on the blockchain, we can request the sender address - return pi.isWalletScoringEnabled(tx) - .then(isEnabled => { - if (!isEnabled) return tx - return pi.rateTransaction(tx) - .then(res => - res.isValid - ? _.assign(tx, { walletScore: res.score }) - : _.assign(tx, { + return pi.isWalletScoringEnabled(tx).then(isEnabled => { + if (!isEnabled) return tx + return pi + .rateTransaction(tx) + .then(res => + res.isValid + ? _.assign(tx, { walletScore: res.score }) + : _.assign(tx, { walletScore: res.score, error: 'Chain analysis score is above defined threshold', errorCode: 'scoreThresholdReached', - dispense: true - }) - ) - .catch(error => _.assign(tx, { + dispense: true, + }), + ) + .catch(error => + _.assign(tx, { walletScore: 10, error: `Failure getting address score: ${error.message}`, errorCode: 'walletScoringError', - dispense: true - })) - }) + dispense: true, + }), + ) + }) } -function monitorLiveIncoming (settings) { +function monitorLiveIncoming(settings) { const statuses = ['notSeen', 'published', 'insufficientFunds'] return monitorIncoming(settings, statuses, 0, STALE_LIVE_INCOMING_TX_AGE) } -function monitorStaleIncoming (settings) { - const statuses = ['notSeen', 'published', 'authorized', 'instant', 'rejected', 'insufficientFunds'] - return monitorIncoming(settings, statuses, STALE_LIVE_INCOMING_TX_AGE, STALE_INCOMING_TX_AGE) +function monitorStaleIncoming(settings) { + const statuses = [ + 'notSeen', + 'published', + 'authorized', + 'instant', + 'rejected', + 'insufficientFunds', + ] + return monitorIncoming( + settings, + statuses, + STALE_LIVE_INCOMING_TX_AGE, + STALE_INCOMING_TX_AGE, + ) } -function monitorIncoming (settings, statuses, fromAge, toAge) { +function monitorIncoming(settings, statuses, fromAge, toAge) { return fetchOpenTxs(statuses, fromAge, toAge) .then(txs => pEachSeries(txs, tx => processTxStatus(tx, settings))) .catch(err => { @@ -169,7 +203,7 @@ function monitorIncoming (settings, statuses, fromAge, toAge) { }) } -function monitorUnnotified (settings) { +function monitorUnnotified(settings) { const sql = `select * from cash_out_txs where ((extract(epoch from (now() - created))) * 1000)<$1 @@ -179,23 +213,26 @@ function monitorUnnotified (settings) { and (redeem=$4 or ((extract(epoch from (now() - created))) * 1000)>$5)` const notify = tx => plugins(settings, tx.deviceId).notifyConfirmation(tx) - return db.any(sql, [MAX_NOTIFY_AGE, false, false, true, MIN_NOTIFY_AGE]) + return db + .any(sql, [MAX_NOTIFY_AGE, false, false, true, MIN_NOTIFY_AGE]) .then(rows => _.map(toObj, rows)) .then(txs => Promise.all(txs.map(notify))) .catch(logger.error) } -function cancel (txId) { +function cancel(txId) { const updateRec = { error: 'Operator cancel', error_code: 'operatorCancel', - dispense: true + dispense: true, } return Promise.resolve() .then(() => { - return pgp.helpers.update(updateRec, null, 'cash_out_txs') + - pgp.as.format(' where id=$1', [txId]) + return ( + pgp.helpers.update(updateRec, null, 'cash_out_txs') + + pgp.as.format(' where id=$1', [txId]) + ) }) .then(sql => db.result(sql, false)) .then(res => { diff --git a/packages/server/lib/cashbox-batches.js b/packages/server/lib/cashbox-batches.js index 43205308..f3ad34d3 100644 --- a/packages/server/lib/cashbox-batches.js +++ b/packages/server/lib/cashbox-batches.js @@ -4,8 +4,9 @@ const _ = require('lodash/fp') const uuid = require('uuid') const camelize = require('./utils') -function createCashboxBatch (deviceId, cashboxCount) { - if (_.isEqual(0, cashboxCount)) throw new Error('Cash box is empty. Cash box batch could not be created.') +function createCashboxBatch(deviceId, cashboxCount) { + if (_.isEqual(0, cashboxCount)) + throw new Error('Cash box is empty. Cash box batch could not be created.') const sql = `INSERT INTO cash_unit_operation (id, device_id, created, operation_type) VALUES ($1, $2, now(), 'cash-box-empty') RETURNING *` const sql2 = ` UPDATE bills SET cashbox_batch_id=$1 @@ -24,55 +25,93 @@ function createCashboxBatch (deviceId, cashboxCount) { const q1 = t.one(sql, [batchId, deviceId]) const q2 = t.none(sql2, [batchId, deviceId]) const q3 = t.none(sql3, [batchId, deviceId]) - return t.batch([q1, q2, q3]) - .then(([it]) => it) + return t.batch([q1, q2, q3]).then(([it]) => it) }) } -function updateMachineWithBatch (machineContext, oldCashboxCount) { +function updateMachineWithBatch(machineContext, oldCashboxCount) { const cashUnits = machineContext.cashUnits - const cashUnitNames = ['cashbox', 'cassette1', 'cassette2', 'cassette3', 'cassette4', 'recycler1', 'recycler2', 'recycler3', 'recycler4', 'recycler5', 'recycler6'] - const isValidContext = _.has(['deviceId', 'cashUnits'], machineContext) && _.has(cashUnitNames, cashUnits) - const cassettes = _.filter(it => !_.isNil(it))([cashUnits.cassette1, cashUnits.cassette2, cashUnits.cassette3, cashUnits.cassette4]) - const isCassetteAmountWithinRange = _.inRange(constants.CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES, constants.CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES + 1, _.size(cassettes)) + const cashUnitNames = [ + 'cashbox', + 'cassette1', + 'cassette2', + 'cassette3', + 'cassette4', + 'recycler1', + 'recycler2', + 'recycler3', + 'recycler4', + 'recycler5', + 'recycler6', + ] + const isValidContext = + _.has(['deviceId', 'cashUnits'], machineContext) && + _.has(cashUnitNames, cashUnits) + const cassettes = _.filter(it => !_.isNil(it))([ + cashUnits.cassette1, + cashUnits.cassette2, + cashUnits.cassette3, + cashUnits.cassette4, + ]) + const isCassetteAmountWithinRange = _.inRange( + constants.CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES, + constants.CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES + 1, + _.size(cassettes), + ) if (!isValidContext && !isCassetteAmountWithinRange) throw new Error('Insufficient info to create a new cashbox batch') - if (_.isEqual(0, oldCashboxCount)) throw new Error('Cash box is empty. Cash box batch could not be created.') + if (_.isEqual(0, oldCashboxCount)) + throw new Error('Cash box is empty. Cash box batch could not be created.') return db.tx(t => { const deviceId = machineContext.deviceId const batchId = uuid.v4() - const q1 = t.none(`INSERT INTO cash_unit_operation (id, device_id, created, operation_type) VALUES ($1, $2, now(), 'cash-box-empty')`, [batchId, deviceId]) - const q2 = t.none(`UPDATE bills SET cashbox_batch_id=$1 FROM cash_in_txs + const q1 = t.none( + `INSERT INTO cash_unit_operation (id, device_id, created, operation_type) VALUES ($1, $2, now(), 'cash-box-empty')`, + [batchId, deviceId], + ) + const q2 = t.none( + `UPDATE bills SET cashbox_batch_id=$1 FROM cash_in_txs WHERE bills.cash_in_txs_id = cash_in_txs.id AND cash_in_txs.device_id = $2 AND bills.destination_unit = 'cashbox' AND - bills.cashbox_batch_id IS NULL`, [batchId, deviceId]) - const q3 = t.none(`UPDATE empty_unit_bills SET cashbox_batch_id=$1 - WHERE empty_unit_bills.device_id = $2 AND empty_unit_bills.cashbox_batch_id IS NULL`, [batchId, deviceId]) - const q4 = t.none(` + bills.cashbox_batch_id IS NULL`, + [batchId, deviceId], + ) + const q3 = t.none( + `UPDATE empty_unit_bills SET cashbox_batch_id=$1 + WHERE empty_unit_bills.device_id = $2 AND empty_unit_bills.cashbox_batch_id IS NULL`, + [batchId, deviceId], + ) + const q4 = t.none( + ` UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=coalesce($5, recycler1), recycler2=coalesce($6, recycler2), recycler3=coalesce($7, recycler3), recycler4=coalesce($8, recycler4), recycler5=coalesce($9, recycler5), recycler6=coalesce($10, recycler6) WHERE device_id=$11 - `, [ - cashUnits.cassette1, - cashUnits.cassette2, - cashUnits.cassette3, - cashUnits.cassette4, - cashUnits.recycler1, - cashUnits.recycler2, - cashUnits.recycler3, - cashUnits.recycler4, - cashUnits.recycler5, - cashUnits.recycler6, - machineContext.deviceId - ]) + `, + [ + cashUnits.cassette1, + cashUnits.cassette2, + cashUnits.cassette3, + cashUnits.cassette4, + cashUnits.recycler1, + cashUnits.recycler2, + cashUnits.recycler3, + cashUnits.recycler4, + cashUnits.recycler5, + cashUnits.recycler6, + machineContext.deviceId, + ], + ) return t.batch([q1, q2, q3, q4]) }) } -function getBatches (from = new Date(0).toISOString(), until = new Date().toISOString()) { +function getBatches( + from = new Date(0).toISOString(), + until = new Date().toISOString(), +) { const sql = ` SELECT cuo.id, @@ -96,28 +135,25 @@ function getBatches (from = new Date(0).toISOString(), until = new Date().toISOS return db.any(sql, [from, until]).then(camelize) } -function editBatchById (id, performedBy) { +function editBatchById(id, performedBy) { const sql = `UPDATE cash_unit_operation SET performed_by=$1 WHERE id=$2 AND cuo.operation_type = 'cash-box-empty'` return db.none(sql, [performedBy, id]) } -function logFormatter (data) { - return _.map( - it => { - return { - id: it.id, - deviceId: it.deviceId, - created: it.created, - operationType: it.operationType, - billCount: it.billCount, - fiatTotal: it.fiatTotal - } - }, - data - ) +function logFormatter(data) { + return _.map(it => { + return { + id: it.id, + deviceId: it.deviceId, + created: it.created, + operationType: it.operationType, + billCount: it.billCount, + fiatTotal: it.fiatTotal, + } + }, data) } -function getMachineUnbatchedBills (deviceId) { +function getMachineUnbatchedBills(deviceId) { const sql = ` SELECT now() AS created, cash_in_txs.device_id, json_agg(b.*) AS bills FROM bills b LEFT OUTER JOIN cash_in_txs ON b.cash_in_txs_id = cash_in_txs.id @@ -125,12 +161,13 @@ function getMachineUnbatchedBills (deviceId) { GROUP BY cash_in_txs.device_id ` - return db.oneOrNone(sql, [deviceId]) + return db + .oneOrNone(sql, [deviceId]) .then(res => _.mapKeys(it => _.camelCase(it), res)) .then(logFormatterSingle) } -function getBatchById (id) { +function getBatchById(id) { const sql = ` SELECT cb.id, cb.device_id, cb.created, cb.operation_type, cb.bill_count_override, cb.performed_by, json_agg(b.*) AS bills FROM cash_unit_operation AS cb @@ -139,14 +176,22 @@ function getBatchById (id) { GROUP BY cb.id ` - return db.oneOrNone(sql, [id]).then(res => _.mapKeys(it => _.camelCase(it), res)) + return db + .oneOrNone(sql, [id]) + .then(res => _.mapKeys(it => _.camelCase(it), res)) .then(logFormatterSingle) } -function logFormatterSingle (data) { +function logFormatterSingle(data) { const bills = _.filter( - it => !(_.isNil(it) || _.isNil(it.fiat_code) || _.isNil(it.fiat) || _.isNaN(it.fiat)), - data.bills + it => + !( + _.isNil(it) || + _.isNil(it.fiat_code) || + _.isNil(it.fiat) || + _.isNaN(it.fiat) + ), + data.bills, ) return { @@ -161,9 +206,9 @@ function logFormatterSingle (data) { return acc }, {}, - bills + bills, ), - billsByDenomination: _.countBy(it => `${it.fiat} ${it.fiat_code}`, bills) + billsByDenomination: _.countBy(it => `${it.fiat} ${it.fiat_code}`, bills), } } @@ -174,5 +219,5 @@ module.exports = { editBatchById, getBatchById, getMachineUnbatchedBills, - logFormatter + logFormatter, } diff --git a/packages/server/lib/coin-change.js b/packages/server/lib/coin-change.js index 4a1c964a..38e42e92 100644 --- a/packages/server/lib/coin-change.js +++ b/packages/server/lib/coin-change.js @@ -9,16 +9,13 @@ */ const prepare_denominations = denominations => JSON.parse(JSON.stringify(denominations)) - .sort(([d1, c1], [d2, c2]) => d1 < d2) + .sort(([d1], [d2]) => d1 < d2) .reduce( ([csum, denoms], [denom, count]) => { - csum += denom*count - return [ - csum, - [{ denom, count, csum }].concat(denoms) - ] + csum += denom * count + return [csum, [{ denom, count, csum }].concat(denoms)] }, - [0, []] + [0, []], )[1] /* ([csum, denoms]) => denoms */ const max_denomination_multiplicity = (denom, count, target) => @@ -38,18 +35,18 @@ const memo_get = (memo, target, denom) => { const memo_set = (memo, target, denom, solution) => { let denom_solutions = memo[target] - if (denom_solutions === undefined) - memo[target] = denom_solutions = {} - return denom_solutions[denom] = solution + if (denom_solutions === undefined) memo[target] = denom_solutions = {} + return (denom_solutions[denom] = solution) } const check = (solution, target) => - !solution - || target === solution.reduce((sum, [denom, provisioned]) => sum + denom*provisioned, 0) + !solution || + target === + solution.reduce((sum, [denom, provisioned]) => sum + denom * provisioned, 0) const model = denominations => ({ denominations: prepare_denominations(denominations), - memo: {} + memo: {}, }) /* @@ -73,16 +70,19 @@ const solve = (model, target) => { * of the denominations, or if the target is not divisible by any of the * denominations */ - if (target > csum) - return memo_set(memo, target, denom, false) + if (target > csum) return memo_set(memo, target, denom, false) let solution = memo_get(memo, target, denom) if (solution === false) continue /* not here, keep looking */ if (solution) return solution /* we've previously computed a solution */ /* solution === null */ - for (let nd = max_denomination_multiplicity(denom, count, target); nd >= 0; nd--) { - solution = coin_change(didx+1, target - denom*nd) + for ( + let nd = max_denomination_multiplicity(denom, count, target); + nd >= 0; + nd-- + ) { + solution = coin_change(didx + 1, target - denom * nd) if (solution) return memo_set(memo, target, denom, [[denom, nd]].concat(solution)) } diff --git a/packages/server/lib/coinatmradar/coinatmradar.js b/packages/server/lib/coinatmradar/coinatmradar.js index 88b9eaf4..0c9eddab 100644 --- a/packages/server/lib/coinatmradar/coinatmradar.js +++ b/packages/server/lib/coinatmradar/coinatmradar.js @@ -18,9 +18,11 @@ const STALE_INTERVAL = '2 minutes' module.exports = { update } -function mapCoin (rates, deviceId, settings, cryptoCode) { +function mapCoin(rates, deviceId, settings, cryptoCode) { const config = settings.config - const buildedRates = plugins(settings, deviceId).buildRates(rates)[cryptoCode] || { cashIn: null, cashOut: null } + const buildedRates = plugins(settings, deviceId).buildRates(rates)[ + cryptoCode + ] || { cashIn: null, cashOut: null } const commissions = configManager.getCommissions(cryptoCode, deviceId, config) const coinAtmRadar = configManager.getCoinAtmRadar(config) @@ -30,8 +32,12 @@ function mapCoin (rates, deviceId, settings, cryptoCode) { const cashOutFee = showCommissions ? commissions.cashOut / 100 : null const cashInFixedFee = showCommissions ? commissions.fixedFee : null const cashOutFixedFee = showCommissions ? commissions.cashOutFixedFee : null - const cashInRate = showCommissions ? _.invoke('cashIn.toNumber', buildedRates) : null - const cashOutRate = showCommissions ? _.invoke('cashOut.toNumber', buildedRates) : null + const cashInRate = showCommissions + ? _.invoke('cashIn.toNumber', buildedRates) + : null + const cashOutRate = showCommissions + ? _.invoke('cashOut.toNumber', buildedRates) + : null return { cryptoCode, @@ -40,11 +46,11 @@ function mapCoin (rates, deviceId, settings, cryptoCode) { cashInFixedFee, cashOutFixedFee, cashInRate, - cashOutRate + cashOutRate, } } -function mapIdentification (config) { +function mapIdentification(config) { const triggers = configManager.getTriggers(config) return { @@ -52,11 +58,11 @@ function mapIdentification (config) { isPalmVein: false, isPhoto: complianceTriggers.hasFacephoto(triggers), isIdDocScan: complianceTriggers.hasIdScan(triggers), - isFingerprint: false + isFingerprint: false, } } -function mapMachine (rates, settings, machineRow) { +function mapMachine(rates, settings, machineRow) { const deviceId = machineRow.device_id const config = settings.config @@ -69,10 +75,15 @@ function mapMachine (rates, settings, machineRow) { const lastOnline = machineRow.last_online.toISOString() const status = machineRow.stale ? 'online' : 'offline' const showLimitsAndVerification = coinAtmRadar.limitsAndVerification - const cashLimit = showLimitsAndVerification ? (_.get('threshold', complianceTriggers.getCashLimit(triggers)) || Infinity) : null + const cashLimit = showLimitsAndVerification + ? _.get('threshold', complianceTriggers.getCashLimit(triggers)) || Infinity + : null const cryptoCurrencies = locale.cryptoCurrencies const identification = mapIdentification(config) - const coins = _.map(_.partial(mapCoin, [rates, deviceId, settings]), cryptoCurrencies) + const coins = _.map( + _.partial(mapCoin, [rates, deviceId, settings]), + cryptoCurrencies, + ) return { machineId: deviceId, address: { @@ -80,12 +91,12 @@ function mapMachine (rates, settings, machineRow) { city: null, region: null, postalCode: null, - country: null + country: null, }, location: { name: null, url: null, - phone: null + phone: null, }, status, lastOnline, @@ -98,20 +109,21 @@ function mapMachine (rates, settings, machineRow) { cashOutDailyLimit: cashLimit, fiatCurrency: locale.fiatCurrency, identification, - coins + coins, } } -function getMachines (rates, settings) { +function getMachines(rates, settings) { const sql = `select device_id, last_online, now() - last_online < $1 as stale from devices where display=TRUE and paired=TRUE order by created` - return db.any(sql, [STALE_INTERVAL]) + return db + .any(sql, [STALE_INTERVAL]) .then(_.map(_.partial(mapMachine, [rates, settings]))) } -function sendRadar (data) { +function sendRadar(data) { const url = COIN_ATM_RADAR_URL if (_.isEmpty(url)) { @@ -123,31 +135,32 @@ function sendRadar (data) { method: 'post', data, timeout: TIMEOUT, - maxContentLength: MAX_CONTENT_LENGTH + maxContentLength: MAX_CONTENT_LENGTH, } - return axios.default(config) - .then(r => logger.info(r.status)) + return axios.default(config).then(r => logger.info(r.status)) } -function mapRecord (rates, settings) { +function mapRecord(rates, settings) { const timestamp = new Date().toISOString() - return Promise.all([getMachines(rates, settings), getOperatorId('coinatmradar')]) - .then(([machines, operatorId]) => { - return { - operatorId: operatorId, - operator: { - name: null, - phone: null, - email: null - }, - timestamp, - machines - } - }) + return Promise.all([ + getMachines(rates, settings), + getOperatorId('coinatmradar'), + ]).then(([machines, operatorId]) => { + return { + operatorId: operatorId, + operator: { + name: null, + phone: null, + email: null, + }, + timestamp, + machines, + } + }) } -function update (rates, settings) { +function update(rates, settings) { const coinAtmRadar = configManager.getCoinAtmRadar(settings.config) if (!coinAtmRadar.active) return Promise.resolve() diff --git a/packages/server/lib/coinatmradar/test/coinatmradar.test.js b/packages/server/lib/coinatmradar/test/coinatmradar.test.js index 4e96a0db..512de274 100644 --- a/packages/server/lib/coinatmradar/test/coinatmradar.test.js +++ b/packages/server/lib/coinatmradar/test/coinatmradar.test.js @@ -80,7 +80,7 @@ const settings = { threshold: 123, id: '9c3b5af8-b1d1-4125-b169-0e913b33894c', direction: 'both', - triggerType: 'txAmount' + triggerType: 'txAmount', }, { requirement: 'sms', @@ -88,7 +88,7 @@ const settings = { thresholdDays: 1, id: 'b0e1e6a8-be1b-4e43-ac5f-3e4951e86f8b', direction: 'both', - triggerType: 'txVelocity' + triggerType: 'txVelocity', }, { requirement: 'sms', @@ -96,58 +96,58 @@ const settings = { thresholdDays: 1, id: '6ac38fe6-172c-48a4-8a7f-605213cbd600', direction: 'both', - triggerType: 'txVolume' - } + triggerType: 'txVolume', + }, ], notifications_sms_transactions: true, - notifications_highValueTransaction: 50 + notifications_highValueTransaction: 50, }, - accounts: {} + accounts: {}, } const rates = [ { rates: { ask: new BN(19164.3), - bid: new BN(19164.2) + bid: new BN(19164.2), }, - timestamp: +new Date() + timestamp: +new Date(), }, { rates: { ask: new BN(594.54), - bid: new BN(594.09) + bid: new BN(594.09), }, - timestamp: +new Date() + timestamp: +new Date(), }, { rates: { ask: new BN(84.38), - bid: new BN(84.37) + bid: new BN(84.37), }, - timestamp: +new Date() + timestamp: +new Date(), }, { rates: { ask: new BN(102.8), - bid: new BN(101.64) + bid: new BN(101.64), }, - timestamp: +new Date() + timestamp: +new Date(), }, { rates: { ask: new BN(74.91), - bid: new BN(74.12) + bid: new BN(74.12), }, - timestamp: +new Date() + timestamp: +new Date(), }, { rates: { ask: new BN(284.4), - bid: new BN(284.4) + bid: new BN(284.4), }, - timestamp: +new Date() - } + timestamp: +new Date(), + }, ] const dbResponse = [ @@ -155,32 +155,32 @@ const dbResponse = [ device_id: 'mock7e531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4', last_online: new Date('2020-11-16T13:11:03.169Z'), - stale: false + stale: false, }, { device_id: '9871e58aa2643ff9445cbc299b50397430ada75157d6c29b4c93548fff0f48f7', last_online: new Date('2020-11-16T16:21:35.948Z'), - stale: false + stale: false, }, { device_id: '5ae0d02dedeb77b6521bd5eb7c9159bdc025873fa0bcb6f87aaddfbda0c50913', last_online: new Date('2020-11-19T15:07:57.089Z'), - stale: false + stale: false, }, { device_id: 'f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05', last_online: new Date('2020-11-26T20:05:57.792Z'), - stale: false + stale: false, }, { device_id: '490ab16ee0c124512dc769be1f3e7ee3894ce1e5b4b8b975e134fb326e551e88', last_online: new Date('2020-12-04T16:48:05.129Z'), - stale: false - } + stale: false, + }, ] function validateData(data) { @@ -189,7 +189,7 @@ function validateData(data) { operator: yup.object().shape({ name: yup.string().nullable(), phone: yup.string().nullable(), - email: yup.string().email().nullable() + email: yup.string().email().nullable(), }), timestamp: yup.string().required('timestamp not provided'), machines: yup.array().of( @@ -200,12 +200,12 @@ function validateData(data) { city: yup.string().nullable(), region: yup.string().nullable(), postalCode: yup.string().nullable(), - country: yup.string().nullable() + country: yup.string().nullable(), }), location: yup.object().required('location object not provided').shape({ name: yup.string().nullable(), url: yup.string().nullable(), - phone: yup.string().nullable() + phone: yup.string().nullable(), }), status: yup .string() @@ -231,7 +231,7 @@ function validateData(data) { .required('isIdDocScan boolean not defined'), isFingerprint: yup .boolean() - .required('isFingerprint boolean not defined') + .required('isFingerprint boolean not defined'), }), coins: yup.array().of( yup.object().shape({ @@ -240,11 +240,11 @@ function validateData(data) { cashOutFee: yup.number().nullable(), cashInFixedFee: yup.number().nullable(), cashInRate: yup.number().nullable(), - cashOutRate: yup.number().nullable() - }) - ) - }) - ) + cashOutRate: yup.number().nullable(), + }), + ), + }), + ), }) return schema.validate(data) } @@ -252,13 +252,13 @@ function validateData(data) { test('Verify axios request schema', async () => { const axios = require('axios') - jest.spyOn(axios, 'default').mockImplementation( - jest.fn(req => - validateData(req.data) - .then(() => ({ status: 'mock status 200' })) - .catch(e => fail(e)) + jest + .spyOn(axios, 'default') + .mockImplementation( + jest.fn(req => + validateData(req.data).then(() => ({ status: 'mock status 200' })), + ), ) - ) db.any.mockResolvedValue(dbResponse) await car.update(rates, settings) diff --git a/packages/server/lib/commission-math.js b/packages/server/lib/commission-math.js index 61103f2d..aa9079b3 100644 --- a/packages/server/lib/commission-math.js +++ b/packages/server/lib/commission-math.js @@ -2,31 +2,41 @@ const BN = require('./bn') const configManager = require('./new-config-manager') const { utils: coinUtils } = require('@lamassu/coins') -function truncateCrypto (cryptoAtoms, cryptoCode) { +function truncateCrypto(cryptoAtoms, cryptoCode) { const DECIMAL_PLACES = 6 if (cryptoAtoms.eq(0)) return cryptoAtoms const scale = coinUtils.getCryptoCurrency(cryptoCode).unitScale const scaleFactor = BN(10).pow(scale) - return new BN(cryptoAtoms).integerValue(BN.ROUND_DOWN).div(scaleFactor) - .decimalPlaces(DECIMAL_PLACES).times(scaleFactor) + return new BN(cryptoAtoms) + .integerValue(BN.ROUND_DOWN) + .div(scaleFactor) + .decimalPlaces(DECIMAL_PLACES) + .times(scaleFactor) } -function fiatToCrypto (tx, rec, deviceId, config) { +function fiatToCrypto(tx, rec, deviceId, config) { const usableFiat = rec.fiat - rec.cashInFee - const commissions = configManager.getCommissions(tx.cryptoCode, deviceId, config) + const commissions = configManager.getCommissions( + tx.cryptoCode, + deviceId, + config, + ) const tickerRate = new BN(tx.rawTickerPrice) const discount = getDiscountRate(tx.discount, commissions[tx.direction]) const rate = tickerRate.times(discount).decimalPlaces(5) const unitScale = coinUtils.getCryptoCurrency(tx.cryptoCode).unitScale const unitScaleFactor = new BN(10).pow(unitScale) - return truncateCrypto(new BN(usableFiat).div(rate.div(unitScaleFactor)), tx.cryptoCode) + return truncateCrypto( + new BN(usableFiat).div(rate.div(unitScaleFactor)), + tx.cryptoCode, + ) } -function getDiscountRate (discount, commission) { +function getDiscountRate(discount, commission) { const bnDiscount = discount ? new BN(discount) : new BN(0) const bnCommission = new BN(commission) const percentageDiscount = new BN(1).minus(bnDiscount.div(100)) @@ -36,5 +46,5 @@ function getDiscountRate (discount, commission) { module.exports = { fiatToCrypto, - getDiscountRate + getDiscountRate, } diff --git a/packages/server/lib/compliance-external.js b/packages/server/lib/compliance-external.js index 466b77e9..422a4081 100644 --- a/packages/server/lib/compliance-external.js +++ b/packages/server/lib/compliance-external.js @@ -8,20 +8,25 @@ const getPlugin = (settings, pluginCode) => { const account = settings.accounts[pluginCode] const plugin = ph.load(ph.COMPLIANCE, pluginCode) - return ({ plugin, account }) + return { plugin, account } } const getStatus = (settings, service, customerId) => { try { const { plugin, account } = getPlugin(settings, service) - return plugin.getApplicantStatus(account, customerId) - .then((status) => ({ + return plugin + .getApplicantStatus(account, customerId) + .then(status => ({ service, - status + status, })) - .catch((error) => { - if (error.response.status !== 404) logger.error(`Error getting applicant for service ${service}:`, error.message) + .catch(error => { + if (error.response.status !== 404) + logger.error( + `Error getting applicant for service ${service}:`, + error.message, + ) return { service: service, status: null, @@ -34,28 +39,22 @@ const getStatus = (settings, service, customerId) => { status: null, }) } - } const getStatusMap = (settings, customerExternalCompliance) => { const triggers = configManager.getTriggers(settings.config) - const services = _.flow( - _.map('externalService'), - _.compact, - _.uniq - )(triggers) + const services = _.flow(_.map('externalService'), _.compact, _.uniq)(triggers) const applicantPromises = _.map(service => { return getStatus(settings, service, customerExternalCompliance) })(services) - return Promise.all(applicantPromises) - .then((applicantResults) => { - return _.reduce((map, result) => { - if (result.status) map[result.service] = result.status - return map - }, {})(applicantResults) - }) + return Promise.all(applicantPromises).then(applicantResults => { + return _.reduce((map, result) => { + if (result.status) map[result.service] = result.status + return map + }, {})(applicantResults) + }) } const createApplicant = (settings, externalService, customerId) => { @@ -76,5 +75,5 @@ module.exports = { getStatusMap, getStatus, createApplicant, - createLink + createLink, } diff --git a/packages/server/lib/compliance-triggers.js b/packages/server/lib/compliance-triggers.js index dcbe6ab2..7bb75b0c 100644 --- a/packages/server/lib/compliance-triggers.js +++ b/packages/server/lib/compliance-triggers.js @@ -1,26 +1,35 @@ const _ = require('lodash/fp') -function getBackwardsCompatibleTriggers (triggers) { - const filtered = _.filter(_.matches({ triggerType: 'txVolume', direction: 'both', thresholdDays: 1 }))(triggers) +function getBackwardsCompatibleTriggers(triggers) { + const filtered = _.filter( + _.matches({ triggerType: 'txVolume', direction: 'both', thresholdDays: 1 }), + )(triggers) const grouped = _.groupBy(_.prop('requirement'))(filtered) - return _.mapValues(_.compose(_.get('threshold'), _.minBy('threshold')))(grouped) + return _.mapValues(_.compose(_.get('threshold'), _.minBy('threshold')))( + grouped, + ) } -function hasSanctions (triggers) { +function hasSanctions(triggers) { return _.some(_.matches({ requirement: 'sanctions' }))(triggers) } -function maxDaysThreshold (triggers) { +function maxDaysThreshold(triggers) { return _.max(_.map('thresholdDays')(triggers)) } -function getCashLimit (triggers) { - const withFiat = _.filter(({ triggerType }) => _.includes(triggerType, ['txVolume', 'txAmount'])) - const blocking = _.filter(({ requirement }) => _.includes(requirement, ['block', 'suspend'])) +function getCashLimit(triggers) { + const withFiat = _.filter(({ triggerType }) => + _.includes(triggerType, ['txVolume', 'txAmount']), + ) + const blocking = _.filter(({ requirement }) => + _.includes(requirement, ['block', 'suspend']), + ) return _.compose(_.minBy('threshold'), blocking, withFiat)(triggers) } -const hasRequirement = requirement => _.compose(_.negate(_.isEmpty), _.find(_.matches({ requirement }))) +const hasRequirement = requirement => + _.compose(_.negate(_.isEmpty), _.find(_.matches({ requirement }))) const hasPhone = hasRequirement('sms') const hasFacephoto = hasRequirement('facephoto') @@ -28,7 +37,16 @@ const hasIdScan = hasRequirement('idCardData') const AUTH_METHODS = { SMS: 'SMS', - EMAIL: 'EMAIL' + EMAIL: 'EMAIL', } -module.exports = { getBackwardsCompatibleTriggers, hasSanctions, maxDaysThreshold, getCashLimit, hasPhone, hasFacephoto, hasIdScan, AUTH_METHODS } \ No newline at end of file +module.exports = { + getBackwardsCompatibleTriggers, + hasSanctions, + maxDaysThreshold, + getCashLimit, + hasPhone, + hasFacephoto, + hasIdScan, + AUTH_METHODS, +} diff --git a/packages/server/lib/compliance.js b/packages/server/lib/compliance.js index ddb27065..ab3a6343 100644 --- a/packages/server/lib/compliance.js +++ b/packages/server/lib/compliance.js @@ -5,75 +5,87 @@ const logger = require('./logger') const db = require('./db') const ofac = require('./ofac/index') -function logSanctionsMatch (deviceId, customer, sanctionsId, alias) { +function logSanctionsMatch(deviceId, customer, sanctionsId, alias) { const sql = `insert into sanctions_logs (id, device_id, sanctioned_id, sanctioned_alias_id, sanctioned_alias_full_name, customer_id) values ($1, $2, $3, $4, $5, $6)` - return db.none(sql, [uuid.v4(), deviceId, sanctionsId, alias.id, alias.fullName, customer.id]) + return db.none(sql, [ + uuid.v4(), + deviceId, + sanctionsId, + alias.id, + alias.fullName, + customer.id, + ]) } -function logSanctionsMatches (deviceId, customer, results) { - const logAlias = resultId => alias => logSanctionsMatch(deviceId, customer, resultId, alias) +function logSanctionsMatches(deviceId, customer, results) { + const logAlias = resultId => alias => + logSanctionsMatch(deviceId, customer, resultId, alias) const logResult = result => _.map(logAlias(result.id), result.aliases) return Promise.all(_.flatMap(logResult, results)) } -function matchOfac (deviceId, customer) { - return Promise.resolve() - .then(() => { - // Probably because we haven't asked for ID yet - if (!_.isPlainObject(customer.idCardData)) { - return true - } +function matchOfac(deviceId, customer) { + return Promise.resolve().then(() => { + // Probably because we haven't asked for ID yet + if (!_.isPlainObject(customer.idCardData)) { + return true + } - const nameParts = { - firstName: customer.idCardData.firstName, - lastName: customer.idCardData.lastName - } + const nameParts = { + firstName: customer.idCardData.firstName, + lastName: customer.idCardData.lastName, + } - if (_.some(_.isNil, _.values(nameParts))) { - logger.error(new Error(`Insufficient idCardData while matching OFAC for: ${customer.id}`)) - return true - } + if (_.some(_.isNil, _.values(nameParts))) { + logger.error( + new Error( + `Insufficient idCardData while matching OFAC for: ${customer.id}`, + ), + ) + return true + } - const birthDate = customer.idCardData.dateOfBirth + const birthDate = customer.idCardData.dateOfBirth - if (_.isNil(birthDate)) { - logger.error(new Error(`No birth date while matching OFAC for: ${customer.id}`)) - return true - } + if (_.isNil(birthDate)) { + logger.error( + new Error(`No birth date while matching OFAC for: ${customer.id}`), + ) + return true + } - const options = { - threshold: 0.85, - fullNameThreshold: 0.95, - debug: false - } + const options = { + threshold: 0.85, + fullNameThreshold: 0.95, + debug: false, + } - const results = ofac.match(nameParts, birthDate, options) + const results = ofac.match(nameParts, birthDate, options) - return logSanctionsMatches(deviceId, customer, results) - .then(() => !_.isEmpty(results)) - }) + return logSanctionsMatches(deviceId, customer, results).then( + () => !_.isEmpty(results), + ) + }) } -function validateOfac (deviceId, customer) { +function validateOfac(deviceId, customer) { if (customer.sanctionsOverride === 'blocked') return Promise.resolve(false) if (customer.sanctionsOverride === 'verified') return Promise.resolve(true) - return matchOfac(deviceId, customer) - .then(didMatch => !didMatch) + return matchOfac(deviceId, customer).then(didMatch => !didMatch) } -function validationPatch (deviceId, customer) { - return validateOfac(deviceId, customer) - .then(sanctions => - _.isNil(customer.sanctions) || customer.sanctions !== sanctions ? - { sanctions } : - {} - ) +function validationPatch(deviceId, customer) { + return validateOfac(deviceId, customer).then(sanctions => + _.isNil(customer.sanctions) || customer.sanctions !== sanctions + ? { sanctions } + : {}, + ) } -module.exports = {validationPatch} +module.exports = { validationPatch } diff --git a/packages/server/lib/compliance_overrides.js b/packages/server/lib/compliance_overrides.js index f84fe0d1..a70c1948 100644 --- a/packages/server/lib/compliance_overrides.js +++ b/packages/server/lib/compliance_overrides.js @@ -11,7 +11,7 @@ const uuid = require('uuid') * * @returns {object} Newly created compliance override */ -function add (complianceOverride) { +function add(complianceOverride) { const sql = `insert into compliance_overrides (id, customer_id, @@ -25,7 +25,7 @@ function add (complianceOverride) { complianceOverride.customerId, complianceOverride.complianceType, complianceOverride.overrideBy, - complianceOverride.verification + complianceOverride.verification, ]) } diff --git a/packages/server/lib/constants.js b/packages/server/lib/constants.js index 07c2ad84..64f98687 100644 --- a/packages/server/lib/constants.js +++ b/packages/server/lib/constants.js @@ -10,43 +10,43 @@ const PSQL_URL = `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HO const anonymousCustomer = { uuid: '47ac1184-8102-11e7-9079-8f13a7117867', - name: 'anonymous' + name: 'anonymous', } const CASH_UNIT_CAPACITY = { default: { cashbox: 600, - cassette: 500 + cassette: 500, }, douro: { cashbox: 600, - cassette: 500 + cassette: 500, }, grandola: { cashbox: 2000, - recycler: 2800 + recycler: 2800, }, aveiro: { cashbox: 1500, recycler: 60, - cassette: 500 + cassette: 500, }, tejo: { // TODO: add support for the different cashbox configuration in Tejo cashbox: 1000, - cassette: 500 + cassette: 500, }, gaia: { - cashbox: 600 + cashbox: 600, }, sintra: { cashbox: 1000, - cassette: 500 + cassette: 500, }, gmuk1: { cashbox: 2200, - cassette: 2000 - } + cassette: 2000, + }, } const CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES = 2 @@ -69,7 +69,7 @@ const WALLET_SCORE_THRESHOLD = 9 const BALANCE_FETCH_SPEED_MULTIPLIER = { NORMAL: 1, - SLOW: 3 + SLOW: 3, } module.exports = { @@ -90,5 +90,5 @@ module.exports = { WALLET_SCORE_THRESHOLD, RECEIPT, PSQL_URL, - BALANCE_FETCH_SPEED_MULTIPLIER + BALANCE_FETCH_SPEED_MULTIPLIER, } diff --git a/packages/server/lib/customer-notes.js b/packages/server/lib/customer-notes.js index b9e4ae81..cfe92da5 100644 --- a/packages/server/lib/customer-notes.js +++ b/packages/server/lib/customer-notes.js @@ -5,7 +5,9 @@ const db = require('./db') const getCustomerNotes = customerId => { const sql = `SELECT * FROM customer_notes WHERE customer_id=$1` - return db.oneOrNone(sql, [customerId]).then(res => _.mapKeys((_, key) => _.camelize(key), res)) + return db + .oneOrNone(sql, [customerId]) + .then(res => _.mapKeys((_, key) => _.camelize(key), res)) } const createCustomerNote = (customerId, userId, title, content) => { @@ -27,5 +29,5 @@ module.exports = { getCustomerNotes, createCustomerNote, deleteCustomerNote, - updateCustomerNote + updateCustomerNote, } diff --git a/packages/server/lib/customers.js b/packages/server/lib/customers.js index d53534a3..706ad860 100644 --- a/packages/server/lib/customers.js +++ b/packages/server/lib/customers.js @@ -21,7 +21,11 @@ const externalCompliance = require('./compliance-external') const { APPROVED, RETRY } = require('./plugins/compliance/consts') -const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel', 'scoreThresholdReached', 'walletScoringError'] +const TX_PASSTHROUGH_ERROR_CODES = [ + 'operatorCancel', + 'scoreThresholdReached', + 'walletScoringError', +] const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR @@ -37,16 +41,16 @@ const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR * * @returns {object} Newly created customer */ -function add (customer) { - const sql = 'insert into customers (id, phone, phone_at) values ($1, $2, now()) returning *' - return db.one(sql, [uuid.v4(), customer.phone]) - .then(camelize) +function add(customer) { + const sql = + 'insert into customers (id, phone, phone_at) values ($1, $2, now()) returning *' + return db.one(sql, [uuid.v4(), customer.phone]).then(camelize) } -function addWithEmail (customer) { - const sql = 'insert into customers (id, email, email_at) values ($1, $2, now()) returning *' - return db.one(sql, [uuid.v4(), customer.email]) - .then(camelize) +function addWithEmail(customer) { + const sql = + 'insert into customers (id, email, email_at) values ($1, $2, now()) returning *' + return db.one(sql, [uuid.v4(), customer.email]).then(camelize) } /** @@ -60,16 +64,14 @@ function addWithEmail (customer) { * * @returns {object} Customer */ -function get (phone) { +function get(phone) { const sql = 'select * from customers where phone=$1' - return db.oneOrNone(sql, [phone]) - .then(camelize) + return db.oneOrNone(sql, [phone]).then(camelize) } -function getWithEmail (email) { +function getWithEmail(email) { const sql = 'select * from customers where email=$1' - return db.oneOrNone(sql, [email]) - .then(camelize) + return db.oneOrNone(sql, [email]).then(camelize) } /** @@ -84,16 +86,20 @@ function getWithEmail (email) { * * @returns {Promise} Newly updated Customer */ -function update (id, data, userToken) { +function update(id, data, userToken) { const formattedData = _.omit(['id'], _.mapKeys(_.snakeCase, data)) - const enhancedUpdateData = enhanceAtFields(enhanceOverrideFields(formattedData, userToken)) + const enhancedUpdateData = enhanceAtFields( + enhanceOverrideFields(formattedData, userToken), + ) const updateData = updateOverride(enhancedUpdateData) - const sql = Pgp.helpers.update(updateData, _.keys(updateData), 'customers') + + const sql = + Pgp.helpers.update(updateData, _.keys(updateData), 'customers') + ' where id=$1 returning *' - return db.one(sql, [id]) + return db + .one(sql, [id]) .then(assignCustomerData) .then(addComplianceOverrides(id, updateData, userToken)) .then(getCustomInfoRequestsData) @@ -111,7 +117,7 @@ function update (id, data, userToken) { * * @returns {Promise} Newly updated Customer */ -async function updateCustomer (id, data, userToken) { +async function updateCustomer(id, data, userToken) { const formattedData = _.pick( [ 'sanctions', @@ -123,16 +129,20 @@ async function updateCustomer (id, data, userToken) { 'sanctions_override', 'front_camera_override', 'suspended_until', - 'phone_override' + 'phone_override', ], - _.mapKeys(_.snakeCase, data)) + _.mapKeys(_.snakeCase, data), + ) - const enhancedUpdateData = enhanceAtFields(enhanceOverrideFields(formattedData, userToken)) + const enhancedUpdateData = enhanceAtFields( + enhanceOverrideFields(formattedData, userToken), + ) const updateData = updateOverride(enhancedUpdateData) - + if (!_.isEmpty(updateData)) { - const sql = Pgp.helpers.update(updateData, _.keys(updateData), 'customers') + - ' where id=$1' + const sql = + Pgp.helpers.update(updateData, _.keys(updateData), 'customers') + + ' where id=$1' await db.none(sql, [id]) } @@ -153,32 +163,38 @@ async function updateCustomer (id, data, userToken) { * @returns {Promise} Newly updated Customer */ -function edit (id, data, userToken) { +function edit(id, data, userToken) { const defaults = [ 'front_camera', 'id_card_data', 'id_card_photo', 'us_ssn', 'subscriber_info', - 'name' + 'name', ] - const filteredData = _.pick(defaults, _.mapKeys(_.snakeCase, _.omitBy(_.isNil, data))) + const filteredData = _.pick( + defaults, + _.mapKeys(_.snakeCase, _.omitBy(_.isNil, data)), + ) if (_.isEmpty(filteredData)) return getCustomerById(id) - const formattedData = enhanceEditedPhotos(enhanceEditedFields(filteredData, userToken)) + const formattedData = enhanceEditedPhotos( + enhanceEditedFields(filteredData, userToken), + ) const defaultDbData = { customer_id: id, created: new Date(), - ...formattedData + ...formattedData, } - const cs = new Pgp.helpers.ColumnSet(_.keys(defaultDbData), - { table: 'edited_customer_data' }) - const onConflict = ' ON CONFLICT (customer_id) DO UPDATE SET ' + + const cs = new Pgp.helpers.ColumnSet(_.keys(defaultDbData), { + table: 'edited_customer_data', + }) + const onConflict = + ' ON CONFLICT (customer_id) DO UPDATE SET ' + cs.assignColumns({ from: 'EXCLUDED', skip: ['customer_id', 'created'] }) const upsert = Pgp.helpers.insert(defaultDbData, cs) + onConflict - return db.none(upsert) - .then(getCustomerById(id)) + return db.none(upsert).then(getCustomerById(id)) } /** @@ -193,9 +209,9 @@ function edit (id, data, userToken) { * @returns {object} fields enhanced with *_by and *_at fields */ -function enhanceEditedFields (fields, userToken) { +function enhanceEditedFields(fields, userToken) { if (!userToken) return fields - _.mapKeys((field) => { + _.mapKeys(field => { fields[field + '_by'] = userToken fields[field + '_at'] = 'now()^' }, fields) @@ -212,8 +228,8 @@ function enhanceEditedFields (fields, userToken) { * @returns {object} fields enhanced with *_path */ -function enhanceEditedPhotos (fields) { - return _.mapKeys((field) => { +function enhanceEditedPhotos(fields) { + return _.mapKeys(field => { if (_.includes(field, ['front_camera', 'id_card_photo'])) { return field + '_path' } @@ -234,7 +250,7 @@ function enhanceEditedPhotos (fields) { * */ -function deleteEditedData (id, data) { +function deleteEditedData(id, data) { // TODO: NOT IMPLEMENTING THIS FEATURE FOR THE CURRENT VERSION const defaults = [ 'front_camera', @@ -242,13 +258,14 @@ function deleteEditedData (id, data) { 'id_card_photo', 'us_ssn', 'subscriber_info', - 'name' + 'name', ] const filteredData = _.pick(defaults, _.mapKeys(_.snakeCase, data)) if (_.isEmpty(filteredData)) return getCustomerById(id) - const cs = new Pgp.helpers.ColumnSet(_.keys(filteredData), - { table: 'edited_customer_data' }) + const cs = new Pgp.helpers.ColumnSet(_.keys(filteredData), { + table: 'edited_customer_data', + }) const update = Pgp.helpers.update(filteredData, cs) db.none(update) return getCustomerById(id) @@ -267,9 +284,10 @@ function deleteEditedData (id, data) { * @returns {object} path New photo path * */ -async function updateEditedPhoto (id, photo, photoType) { +async function updateEditedPhoto(id, photo, photoType) { const newPatch = {} - const baseDir = photoType === 'frontCamera' ? FRONT_CAMERA_DIR : ID_PHOTO_CARD_DIR + const baseDir = + photoType === 'frontCamera' ? FRONT_CAMERA_DIR : ID_PHOTO_CARD_DIR const { createReadStream, filename } = photo const stream = createReadStream() @@ -297,11 +315,6 @@ const invalidateCustomerNotifications = (id, data) => { return notifierQueries.invalidateNotification(detailB, 'compliance') } -const updateSubscriberData = (customerId, data, userToken) => { - const sql = `UPDATE customers SET subscriber_info=$1, subscriber_info_at=now(), subscriber_info_by=$2 WHERE id=$3` - return db.none(sql, [data, userToken, customerId]) -} - /** * Get customer by id * @@ -315,9 +328,10 @@ const updateSubscriberData = (customerId, data, userToken) => { * * Used for the machine. */ -function getById (id) { +function getById(id) { const sql = 'select * from customers where id=$1' - return db.oneOrNone(sql, [id]) + return db + .oneOrNone(sql, [id]) .then(assignCustomerData) .then(getCustomInfoRequestsData) .then(getExternalComplianceMachine) @@ -334,19 +348,16 @@ function getById (id) { * @param {object} customer Customer with snake_case fields * @returns {object} Camelized Customer object */ -function camelize (customer) { +function camelize(customer) { return customer ? _.mapKeys(_.camelCase, customer) : null } -function camelizeDeep (customer) { - return _.flow( - camelize, - it => ({ - ...it, - notes: (it.notes ?? []).map(camelize), - externalCompliance: (it.externalCompliance ?? []).map(camelize) - }) - )(customer) +function camelizeDeep(customer) { + return _.flow(camelize, it => ({ + ...it, + notes: (it.notes ?? []).map(camelize), + externalCompliance: (it.externalCompliance ?? []).map(camelize), + }))(customer) } /** @@ -358,7 +369,7 @@ function camelizeDeep (customer) { * * @returns {array} Array of compliance types' names */ -function getComplianceTypes () { +function getComplianceTypes() { return [ 'sms', 'email', @@ -367,35 +378,40 @@ function getComplianceTypes () { 'front_camera', 'sanctions', 'authorized', - 'us_ssn' ] + 'us_ssn', + ] } -function updateOverride (fields) { +function updateOverride(fields) { const updateableFields = [ 'id_card_data', 'id_card_photo_path', 'front_camera_path', 'authorized', - 'us_ssn' + 'us_ssn', ] const removePathSuffix = _.map(_.replace('_path', '')) const getPairs = _.map(f => [`${f}_override`, 'automatic']) const updatedFields = _.intersection(updateableFields, _.keys(fields)) - const overrideFields = _.compose(_.fromPairs, getPairs, removePathSuffix)(updatedFields) + const overrideFields = _.compose( + _.fromPairs, + getPairs, + removePathSuffix, + )(updatedFields) return _.merge(fields, overrideFields) } -function enhanceAtFields (fields) { +function enhanceAtFields(fields) { const updateableFields = [ 'id_card_data', 'id_card_photo', 'front_camera', 'sanctions', 'authorized', - 'us_ssn' + 'us_ssn', ] const updatedFields = _.intersection(updateableFields, _.keys(fields)) @@ -415,17 +431,21 @@ function enhanceAtFields (fields) { * @param {string} userToken Acting user's token * @returns {object} fields enhanced with *_by and *_at fields */ -function enhanceOverrideFields (fields, userToken) { +function enhanceOverrideFields(fields, userToken) { if (!userToken) return fields // Populate with computedFields (user who overrode and overridden timestamps date) - return _.reduce(_.assign, fields, _.map((type) => { - return (fields[type + '_override']) - ? { - [type + '_override_by']: userToken, - [type + '_override_at']: 'now()^' - } - : {} - }, getComplianceTypes())) + return _.reduce( + _.assign, + fields, + _.map(type => { + return fields[type + '_override'] + ? { + [type + '_override_by']: userToken, + [type + '_override_at']: 'now()^', + } + : {} + }, getComplianceTypes()), + ) } /** @@ -443,24 +463,26 @@ function enhanceOverrideFields (fields, userToken) { * * @returns {promise} Result from compliance_overrides creation */ -function addComplianceOverrides (id, customer, userToken) { +function addComplianceOverrides(id, customer, userToken) { // Prepare compliance overrides to save const overrides = _.map(field => { const complianceName = field + '_override' - return (customer[complianceName]) ? { - customerId: id, - complianceType: field, - overrideBy: userToken, - verification: customer[complianceName] - } : null + return customer[complianceName] + ? { + customerId: id, + complianceType: field, + overrideBy: userToken, + verification: customer[complianceName], + } + : null }, getComplianceTypes()) // Save all the updated override fields - return Promise.all(_.map(complianceOverrides.add, _.compact(overrides))) - .then(() => customer) + return Promise.all(_.map(complianceOverrides.add, _.compact(overrides))).then( + () => customer, + ) } - /** * Query all customers * @@ -470,23 +492,24 @@ function addComplianceOverrides (id, customer, userToken) { * * @returns {array} Array of customers populated with status field */ -function batch () { +function batch() { const sql = `select * from customers where id != $1 order by created desc limit $2` - return db.any(sql, [ anonymous.uuid, NUM_RESULTS ]) - .then(customers => Promise.all(_.map(customer => { - return getCustomInfoRequestsData(customer) - .then(camelize) - }, customers))) + return db.any(sql, [anonymous.uuid, NUM_RESULTS]).then(customers => + Promise.all( + _.map(customer => { + return getCustomInfoRequestsData(customer).then(camelize) + }, customers), + ), + ) } -function getSlimCustomerByIdBatch (ids) { +function getSlimCustomerByIdBatch(ids) { const sql = `SELECT id, phone, id_card_data FROM customers WHERE id = ANY($1::uuid[])` - return db.any(sql, [ids]) - .then(customers => _.map(camelize, customers)) + return db.any(sql, [ids]).then(customers => _.map(camelize, customers)) } // TODO: getCustomersList and getCustomerById are very similar, so this should be refactored @@ -499,8 +522,17 @@ function getSlimCustomerByIdBatch (ids) { * @returns {array} Array of customers with it's transactions aggregations */ -function getCustomersList (phone = null, name = null, address = null, id = null, email = null) { - const passableErrorCodes = _.map(Pgp.as.text, TX_PASSTHROUGH_ERROR_CODES).join(',') +function getCustomersList( + phone = null, + name = null, + address = null, + id = null, + email = null, +) { + const passableErrorCodes = _.map( + Pgp.as.text, + TX_PASSTHROUGH_ERROR_CODES, + ).join(',') const sql = `SELECT id, authorized_override, days_suspended, is_suspended, front_camera_path, front_camera_override, phone, email, sms_override, id_card_data, id_card_data_override, id_card_data_expiration, @@ -543,11 +575,24 @@ function getCustomersList (phone = null, name = null, address = null, id = null, AND ($8 IS NULL OR email = $8) ORDER BY last_active DESC limit $3` - return db.any(sql, [ passableErrorCodes, anonymous.uuid, NUM_RESULTS, phone, name, address, id, email ]) - .then(customers => Promise.all(_.map(customer => - getCustomInfoRequestsData(customer) - .then(camelizeDeep), customers) - ) + return db + .any(sql, [ + passableErrorCodes, + anonymous.uuid, + NUM_RESULTS, + phone, + name, + address, + id, + email, + ]) + .then(customers => + Promise.all( + _.map( + customer => getCustomInfoRequestsData(customer).then(camelizeDeep), + customers, + ), + ), ) } @@ -560,8 +605,11 @@ function getCustomersList (phone = null, name = null, address = null, id = null, * * Used for the server. */ -function getCustomerById (id) { - const passableErrorCodes = _.map(Pgp.as.text, TX_PASSTHROUGH_ERROR_CODES).join(',') +function getCustomerById(id) { + const passableErrorCodes = _.map( + Pgp.as.text, + TX_PASSTHROUGH_ERROR_CODES, + ).join(',') const sql = `SELECT id, authorized_override, days_suspended, is_suspended, front_camera_path, front_camera_at, front_camera_override, phone, phone_at, email, email_at, phone_override, sms_override, id_card_data_at, id_card_data, id_card_data_override, id_card_data_expiration, id_card_photo_path, id_card_photo_at, id_card_photo_override, us_ssn_at, us_ssn, us_ssn_override, sanctions, sanctions_at, @@ -596,7 +644,8 @@ function getCustomerById (id) { ) cn ON c.id = cn.customer_id WHERE c.id = $2 ) AS cl WHERE rn = 1` - return db.oneOrNone(sql, [passableErrorCodes, id]) + return db + .oneOrNone(sql, [passableErrorCodes, id]) .then(assignCustomerData) .then(getCustomInfoRequestsData) .then(getExternalCompliance) @@ -604,16 +653,17 @@ function getCustomerById (id) { .then(formatSubscriberInfo) } -function assignCustomerData (customer) { - return getEditedData(customer.id) - .then(customerEditedData => selectLatestData(customer, customerEditedData)) +function assignCustomerData(customer) { + return getEditedData(customer.id).then(customerEditedData => + selectLatestData(customer, customerEditedData), + ) } function formatSubscriberInfo(customer) { const subscriberInfo = customer.subscriberInfo - if(!subscriberInfo) return customer + if (!subscriberInfo) return customer const result = subscriberInfo.result - if(_.isEmpty(result)) return _.omit(['subscriberInfo'], customer) + if (_.isEmpty(result)) return _.omit(['subscriberInfo'], customer) const name = _.get('belongs_to.name')(result) const street = _.get('current_addresses[0].street_line_1')(result) @@ -623,7 +673,7 @@ function formatSubscriberInfo(customer) { customer.subscriberInfo = { name, - address: `${street ?? ''} ${city ?? ''}${street || city ? ',' : ''} ${stateCode ?? ''} ${postalCode ?? ''}` + address: `${street ?? ''} ${city ?? ''}${street || city ? ',' : ''} ${stateCode ?? ''} ${postalCode ?? ''}`, } return customer @@ -636,33 +686,32 @@ function formatSubscriberInfo(customer) { * * @returns {array} A single customer instance with the most recent edited data */ -function getEditedData (id) { +function getEditedData(id) { const sql = `SELECT * FROM edited_customer_data WHERE customer_id = $1` - return db.oneOrNone(sql, [id]) - .then(_.omitBy(_.isNil)) + return db.oneOrNone(sql, [id]).then(_.omitBy(_.isNil)) } -function selectLatestData (customerData, customerEditedData) { +function selectLatestData(customerData, customerEditedData) { const defaults = [ 'front_camera', 'id_card_data', 'id_card_photo', 'us_ssn', 'subscriber_info', - 'name' + 'name', ] _.map(field => { const atField = field + '_at' const byField = field + '_by' - if (_.includes(field, ['front_camera', 'id_card_photo'])) field = field + '_path' + if (_.includes(field, ['front_camera', 'id_card_photo'])) + field = field + '_path' if (!_.has(field, customerData) || !_.has(field, customerEditedData)) return if (customerData[atField] < customerEditedData[atField]) { customerData[field] = customerEditedData[field] customerData[atField] = customerEditedData[atField] customerData[byField] = customerEditedData[byField] } - } - , defaults) + }, defaults) return customerData } @@ -671,54 +720,52 @@ function selectLatestData (customerData, customerEditedData) { * @param {Object} patch customer update record * @returns {Promise} new patch to be applied */ -function updatePhotoCard (id, patch) { - return Promise.resolve(patch) - .then(patch => { - // Base64 encoded image /9j/4AAQSkZJRgABAQAAAQ.. - const imageData = _.get('idCardPhotoData', patch) +function updatePhotoCard(id, patch) { + return Promise.resolve(patch).then(patch => { + // Base64 encoded image /9j/4AAQSkZJRgABAQAAAQ.. + const imageData = _.get('idCardPhotoData', patch) - if (_.isEmpty(imageData)) { - return patch - } + if (_.isEmpty(imageData)) { + return patch + } - // remove idCardPhotoData from the update record - const newPatch = _.omit('idCardPhotoData', patch) + // remove idCardPhotoData from the update record + const newPatch = _.omit('idCardPhotoData', patch) - // decode the base64 string to binary data - const decodedImageData = Buffer.from(imageData, 'base64') + // decode the base64 string to binary data + const decodedImageData = Buffer.from(imageData, 'base64') - // workout the image hash - // i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50 - const hash = crypto - .createHash('sha256') - .update(imageData) - .digest('hex') + // workout the image hash + // i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50 + const hash = crypto.createHash('sha256').update(imageData).digest('hex') - // workout the image folder - // i.e. 24/0e/85 - const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash))))) + // workout the image folder + // i.e. 24/0e/85 + const rpath = _.join( + path.sep, + _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash)))), + ) - // i.e. ..//idphotocard/24/0e/85 - const dirname = path.join(ID_PHOTO_CARD_DIR, rpath) + // i.e. ..//idphotocard/24/0e/85 + const dirname = path.join(ID_PHOTO_CARD_DIR, rpath) - // create the directory tree if needed - _.attempt(() => makeDir.sync(dirname)) + // create the directory tree if needed + _.attempt(() => makeDir.sync(dirname)) - // i.e. ..//idphotocard/24/0e/85/240e85ff2e4bb931f235985dd01....jpg - const filename = path.join(dirname, hash + '.jpg') + // i.e. ..//idphotocard/24/0e/85/240e85ff2e4bb931f235985dd01....jpg + const filename = path.join(dirname, hash + '.jpg') - // update db record patch - // i.e. { - // "idCardPhotoPath": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg", - // "idCardPhotoAt": "now()" - // } - newPatch.idCardPhotoPath = path.join(rpath, hash + '.jpg') - newPatch.idCardPhotoAt = 'now()' + // update db record patch + // i.e. { + // "idCardPhotoPath": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg", + // "idCardPhotoAt": "now()" + // } + newPatch.idCardPhotoPath = path.join(rpath, hash + '.jpg') + newPatch.idCardPhotoAt = 'now()' - // write image file - return writeFile(filename, decodedImageData) - .then(() => newPatch) - }) + // write image file + return writeFile(filename, decodedImageData).then(() => newPatch) + }) } /** @@ -726,32 +773,30 @@ function updatePhotoCard (id, patch) { * @param {String} directory directory path of id card data for a certain user */ -function updatePhotos (imagesData, id, dir) { - return Promise.resolve(imagesData) - .then(imagesData => { - const newPatch = {} - if (_.isEmpty(imagesData)) { - return newPatch - } - // i.e. ..////idcarddata - const dirname = path.join(dir) - // create the directory tree if needed - _.attempt(() => makeDir.sync(dirname)) - const promises = imagesData.map((imageData, index) => { - // decode the base64 string to binary data - const decodedImageData = Buffer.from(imageData, 'base64') - // i.e. ..////idcarddata/1.jpg - const filename = path.join(dirname, index + '.jpg') - return writeFile(filename, decodedImageData) - }) - - return Promise.all(promises) - .then(arr => { - newPatch.idCardData = path.join(dirname) - newPatch.idCardDataAt = 'now()' - return newPatch - }) +function updatePhotos(imagesData, id, dir) { + return Promise.resolve(imagesData).then(imagesData => { + const newPatch = {} + if (_.isEmpty(imagesData)) { + return newPatch + } + // i.e. ..////idcarddata + const dirname = path.join(dir) + // create the directory tree if needed + _.attempt(() => makeDir.sync(dirname)) + const promises = imagesData.map((imageData, index) => { + // decode the base64 string to binary data + const decodedImageData = Buffer.from(imageData, 'base64') + // i.e. ..////idcarddata/1.jpg + const filename = path.join(dirname, index + '.jpg') + return writeFile(filename, decodedImageData) }) + + return Promise.all(promises).then(() => { + newPatch.idCardData = path.join(dirname) + newPatch.idCardDataAt = 'now()' + return newPatch + }) + }) } /** @@ -759,192 +804,218 @@ function updatePhotos (imagesData, id, dir) { * @param {Object} patch customer latest id card photos * @returns {Promise} new patch to be applied */ -function updateIdCardData (patch, id) { +function updateIdCardData(patch, id) { /* TODO: fetch operator id */ const operatorId = 'id-operator' const directory = `${OPERATOR_DATA_DIR}/${operatorId}/${id}/` - return Promise.resolve(patch) - .then(patch => { - const imagesData = _.get('photos', patch) - return updatePhotos(imagesData, id, directory) - .catch(err => logger.error('while saving the image: ', err)) - }) + return Promise.resolve(patch).then(patch => { + const imagesData = _.get('photos', patch) + return updatePhotos(imagesData, id, directory).catch(err => + logger.error('while saving the image: ', err), + ) + }) } /** * @param {String} imageData customer t&c photo data * @returns {Promise} new patch to be applied */ -function updateTxCustomerPhoto (imageData) { - return Promise.resolve(imageData) - .then(imageData => { - const newPatch = {} - const directory = `${OPERATOR_DATA_DIR}/customersphotos` +function updateTxCustomerPhoto(imageData) { + return Promise.resolve(imageData).then(imageData => { + const newPatch = {} + const directory = `${OPERATOR_DATA_DIR}/customersphotos` - if (_.isEmpty(imageData)) { - return - } + if (_.isEmpty(imageData)) { + return + } - // decode the base64 string to binary data - const decodedImageData = Buffer.from(imageData, 'base64') + // decode the base64 string to binary data + const decodedImageData = Buffer.from(imageData, 'base64') - // workout the image hash - // i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50 - const hash = crypto - .createHash('sha256') - .update(imageData) - .digest('hex') + // workout the image hash + // i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50 + const hash = crypto.createHash('sha256').update(imageData).digest('hex') - // workout the image folder - // i.e. 24/0e/85 - const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash))))) + // workout the image folder + // i.e. 24/0e/85 + const rpath = _.join( + path.sep, + _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash)))), + ) - // i.e. ..///customersphotos/24/0e/85 - const dirname = path.join(directory, rpath) + // i.e. ..///customersphotos/24/0e/85 + const dirname = path.join(directory, rpath) - // create the directory tree if needed - _.attempt(() => makeDir.sync(dirname)) + // create the directory tree if needed + _.attempt(() => makeDir.sync(dirname)) - // i.e. ..///customersphotos/24/0e/85/240e85ff2e4bb931f235985dd01....jpg - const filename = path.join(dirname, hash + '.jpg') + // i.e. ..///customersphotos/24/0e/85/240e85ff2e4bb931f235985dd01....jpg + const filename = path.join(dirname, hash + '.jpg') - // update db record patch - // i.e. { - // "idCustomerTxPhoto": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg", - // "idCustomerTxPhotoAt": "now()" - // } - newPatch.txCustomerPhotoPath = path.join(rpath, hash + '.jpg') - newPatch.txCustomerPhotoAt = 'now()' + // update db record patch + // i.e. { + // "idCustomerTxPhoto": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg", + // "idCustomerTxPhotoAt": "now()" + // } + newPatch.txCustomerPhotoPath = path.join(rpath, hash + '.jpg') + newPatch.txCustomerPhotoAt = 'now()' - // write image file - return writeFile(filename, decodedImageData) - .then(() => newPatch) - }) + // write image file + return writeFile(filename, decodedImageData).then(() => newPatch) + }) } -function updateFrontCamera (id, patch) { - return Promise.resolve(patch) - .then(patch => { - // Base64 encoded image /9j/4AAQSkZJRgABAQAAAQ.. - const imageData = _.get('frontCameraData', patch) +function updateFrontCamera(id, patch) { + return Promise.resolve(patch).then(patch => { + // Base64 encoded image /9j/4AAQSkZJRgABAQAAAQ.. + const imageData = _.get('frontCameraData', patch) - if (_.isEmpty(imageData)) { - return patch - } + if (_.isEmpty(imageData)) { + return patch + } - // remove idCardPhotoData from the update record - const newPatch = _.omit('frontCameraData', patch) + // remove idCardPhotoData from the update record + const newPatch = _.omit('frontCameraData', patch) - // decode the base64 string to binary data - const decodedImageData = Buffer.from(imageData, 'base64') + // decode the base64 string to binary data + const decodedImageData = Buffer.from(imageData, 'base64') - // workout the image hash - // i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50 - const hash = crypto - .createHash('sha256') - .update(imageData) - .digest('hex') + // workout the image hash + // i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50 + const hash = crypto.createHash('sha256').update(imageData).digest('hex') - // workout the image folder - // i.e. 24/0e/85 - const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash))))) + // workout the image folder + // i.e. 24/0e/85 + const rpath = _.join( + path.sep, + _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash)))), + ) - // i.e. ..//idphotocard/24/0e/85 - const dirname = path.join(FRONT_CAMERA_DIR, rpath) + // i.e. ..//idphotocard/24/0e/85 + const dirname = path.join(FRONT_CAMERA_DIR, rpath) - // create the directory tree if needed - _.attempt(() => makeDir.sync(dirname)) + // create the directory tree if needed + _.attempt(() => makeDir.sync(dirname)) - // i.e. ..//idphotocard/24/0e/85/240e85ff2e4bb931f235985dd01....jpg - const filename = path.join(dirname, hash + '.jpg') + // i.e. ..//idphotocard/24/0e/85/240e85ff2e4bb931f235985dd01....jpg + const filename = path.join(dirname, hash + '.jpg') - // update db record patch - // i.e. { - // "idCardPhotoPath": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg", - // "idCardPhotoAt": "now()" - // } - newPatch.frontCameraPath = path.join(rpath, hash + '.jpg') - newPatch.frontCameraAt = 'now()' + // update db record patch + // i.e. { + // "idCardPhotoPath": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg", + // "idCardPhotoAt": "now()" + // } + newPatch.frontCameraPath = path.join(rpath, hash + '.jpg') + newPatch.frontCameraAt = 'now()' - // write image file - return writeFile(filename, decodedImageData) - .then(() => newPatch) - }) + // write image file + return writeFile(filename, decodedImageData).then(() => newPatch) + }) } -function addCustomField (customerId, label, value) { +function addCustomField(customerId, label, value) { const sql = `SELECT * FROM custom_field_definitions WHERE label=$1 LIMIT 1` - return db.oneOrNone(sql, [label]) - .then(res => db.tx(t => { - if (_.isNil(res)) { - const fieldId = uuid.v4() - const q1 = t.none(`INSERT INTO custom_field_definitions (id, label) VALUES ($1, $2)`, [fieldId, label]) - const q2 = t.none(`INSERT INTO customer_custom_field_pairs (customer_id, custom_field_id, value) VALUES ($1, $2, $3)`, [customerId, fieldId, value]) - return t.batch([q1, q2]) - } + return db + .oneOrNone(sql, [label]) + .then(res => + db.tx(t => { + if (_.isNil(res)) { + const fieldId = uuid.v4() + const q1 = t.none( + `INSERT INTO custom_field_definitions (id, label) VALUES ($1, $2)`, + [fieldId, label], + ) + const q2 = t.none( + `INSERT INTO customer_custom_field_pairs (customer_id, custom_field_id, value) VALUES ($1, $2, $3)`, + [customerId, fieldId, value], + ) + return t.batch([q1, q2]) + } - if (!_.isNil(res) && !res.active) { - const q1 = t.none(`UPDATE custom_field_definitions SET active = true WHERE id=$1`, [res.id]) - const q2 = t.none(`INSERT INTO customer_custom_field_pairs (customer_id, custom_field_id, value) VALUES ($1, $2, $3)`, [customerId, res.id, value]) - return t.batch([q1, q2]) - } else if (!_.isNil(res) && res.active) { - const q1 = t.none(`INSERT INTO customer_custom_field_pairs (customer_id, custom_field_id, value) VALUES ($1, $2, $3)`, [customerId, res.id, value]) - return t.batch([q1]) - } - }) + if (!_.isNil(res) && !res.active) { + const q1 = t.none( + `UPDATE custom_field_definitions SET active = true WHERE id=$1`, + [res.id], + ) + const q2 = t.none( + `INSERT INTO customer_custom_field_pairs (customer_id, custom_field_id, value) VALUES ($1, $2, $3)`, + [customerId, res.id, value], + ) + return t.batch([q1, q2]) + } else if (!_.isNil(res) && res.active) { + const q1 = t.none( + `INSERT INTO customer_custom_field_pairs (customer_id, custom_field_id, value) VALUES ($1, $2, $3)`, + [customerId, res.id, value], + ) + return t.batch([q1]) + } + }), ) .then(res => !_.isNil(res)) } -function saveCustomField (customerId, fieldId, newValue) { +function saveCustomField(customerId, fieldId, newValue) { const sql = `UPDATE customer_custom_field_pairs SET value=$1 WHERE customer_id=$2 AND custom_field_id=$3` return db.none(sql, [newValue, customerId, fieldId]) } -function removeCustomField (customerId, fieldId) { +function removeCustomField(customerId, fieldId) { const sql = `SELECT * FROM customer_custom_field_pairs WHERE custom_field_id=$1` - return db.any(sql, [fieldId]) - .then(res => db.tx(t => { + return db.any(sql, [fieldId]).then(res => + db.tx(t => { // Is the field to be removed the only one of its kind in the pairs table? if (_.size(res) === 1) { - const q1 = t.none(`DELETE FROM customer_custom_field_pairs WHERE customer_id=$1 AND custom_field_id=$2`, [customerId, fieldId]) - const q2 = t.none(`UPDATE custom_field_definitions SET active = false WHERE id=$1`, [fieldId]) + const q1 = t.none( + `DELETE FROM customer_custom_field_pairs WHERE customer_id=$1 AND custom_field_id=$2`, + [customerId, fieldId], + ) + const q2 = t.none( + `UPDATE custom_field_definitions SET active = false WHERE id=$1`, + [fieldId], + ) return t.batch([q1, q2]) } else { - const q1 = t.none(`DELETE FROM customer_custom_field_pairs WHERE customer_id=$1 AND custom_field_id=$2`, [customerId, fieldId]) + const q1 = t.none( + `DELETE FROM customer_custom_field_pairs WHERE customer_id=$1 AND custom_field_id=$2`, + [customerId, fieldId], + ) return t.batch([q1]) } - })) + }), + ) } -function getCustomInfoRequestsData (customer) { +function getCustomInfoRequestsData(customer) { if (!customer) return const sql = `SELECT * FROM customers_custom_info_requests WHERE customer_id = $1` - return db.any(sql, [customer.id]).then(res => _.set('custom_info_request_data', res, customer)) + return db + .any(sql, [customer.id]) + .then(res => _.set('custom_info_request_data', res, customer)) } -function enableTestCustomer (customerId) { +function enableTestCustomer(customerId) { const sql = `UPDATE customers SET is_test_customer=true WHERE id=$1` return db.none(sql, [customerId]) } -function disableTestCustomer (customerId) { +function disableTestCustomer(customerId) { const sql = `UPDATE customers SET is_test_customer=false WHERE id=$1` return db.none(sql, [customerId]) } -function updateLastAuthAttempt (customerId, deviceId) { +function updateLastAuthAttempt(customerId, deviceId) { const sql = `UPDATE customers SET last_auth_attempt=NOW(), last_used_machine=$2 WHERE id=$1` return db.none(sql, [customerId, deviceId]) } -function getExternalComplianceMachine (customer) { - return settingsLoader.loadLatest() +function getExternalComplianceMachine(customer) { + return settingsLoader + .loadLatest() .then(settings => externalCompliance.getStatusMap(settings, customer.id)) .then(statusMap => { return updateExternalComplianceByMap(customer.id, statusMap) - .then(() => customer.externalCompliance = statusMap) + .then(() => (customer.externalCompliance = statusMap)) .then(() => customer) }) } @@ -963,14 +1034,17 @@ function updateExternalComplianceByMap(customerId, serviceMap) { WHERE customer_id=$2 AND service=$3 ` const pairs = _.toPairs(serviceMap) - const promises = _.map(([service, status]) => db.none(sql, [status.answer, customerId, service]))(pairs) + const promises = _.map(([service, status]) => + db.none(sql, [status.answer, customerId, service]), + )(pairs) return Promise.all(promises) } function getExternalCompliance(customer) { const sql = `SELECT external_id, service, last_known_status, last_updated FROM customer_external_compliance where customer_id=$1` - return db.manyOrNone(sql, [customer.id]) + return db + .manyOrNone(sql, [customer.id]) .then(compliance => { customer.externalCompliance = compliance }) @@ -984,42 +1058,53 @@ function getOpenExternalCompliance() { function notifyRetryExternalCompliance(settings, customerId, service) { const sql = 'SELECT phone FROM customers WHERE id=$1' - const promises = [db.one(sql, [customerId]), externalCompliance.createLink(settings, service, customerId)] + const promises = [ + db.one(sql, [customerId]), + externalCompliance.createLink(settings, service, customerId), + ] - return Promise.all(promises) - .then(([toNumber, link]) => { - const body = `Your external compliance verification has failed. Please try again. Link for retry: ${link}` + return Promise.all(promises).then(([toNumber, link]) => { + const body = `Your external compliance verification has failed. Please try again. Link for retry: ${link}` - return sms.sendMessage(settings, { toNumber, body }) - }) + return sms.sendMessage(settings, { toNumber, body }) + }) } function notifyApprovedExternalCompliance(settings, customerId) { const sql = 'SELECT phone FROM customers WHERE id=$1' - return db.one(sql, [customerId]) - .then((toNumber) => { - const body = 'Your external compliance verification has been approved.' + return db.one(sql, [customerId]).then(toNumber => { + const body = 'Your external compliance verification has been approved.' - return sms.sendMessage(settings, { toNumber, body }) - }) + return sms.sendMessage(settings, { toNumber, body }) + }) } function checkExternalCompliance(settings) { - return getOpenExternalCompliance() - .then(externals => { - console.log(externals) - const promises = _.map(external => { - return externalCompliance.getStatus(settings, external.service, external.customer_id) - .then(status => { - console.log('status', status, external.customer_id, external.service) - if (status.status.answer === RETRY) notifyRetryExternalCompliance(settings, external.customer_id, status.service) - if (status.status.answer === APPROVED) notifyApprovedExternalCompliance(settings, external.customer_id) + return getOpenExternalCompliance().then(externals => { + console.log(externals) + const promises = _.map(external => { + return externalCompliance + .getStatus(settings, external.service, external.customer_id) + .then(status => { + console.log('status', status, external.customer_id, external.service) + if (status.status.answer === RETRY) + notifyRetryExternalCompliance( + settings, + external.customer_id, + status.service, + ) + if (status.status.answer === APPROVED) + notifyApprovedExternalCompliance(settings, external.customer_id) - return updateExternalCompliance(external.customer_id, external.service, status.status.answer) - }) - }, externals) - return Promise.all(promises) - }) + return updateExternalCompliance( + external.customer_id, + external.service, + status.status.answer, + ) + }) + }, externals) + return Promise.all(promises) + }) } function addExternalCompliance(customerId, service, id) { @@ -1027,7 +1112,6 @@ function addExternalCompliance(customerId, service, id) { return db.none(sql, [customerId, id, service]) } - module.exports = { add, addWithEmail, @@ -1054,5 +1138,5 @@ module.exports = { disableTestCustomer, updateLastAuthAttempt, addExternalCompliance, - checkExternalCompliance + checkExternalCompliance, } diff --git a/packages/server/lib/db-migrate-store.js b/packages/server/lib/db-migrate-store.js index 6a0ca9c2..94f09624 100644 --- a/packages/server/lib/db-migrate-store.js +++ b/packages/server/lib/db-migrate-store.js @@ -1,15 +1,15 @@ const db = require('../lib/db') const logger = require('./logger') -const upsert = 'insert into migrations (id, data) values (1, $1) on conflict (id) do update set data = $1' +const upsert = + 'insert into migrations (id, data) values (1, $1) on conflict (id) do update set data = $1' -function DbMigrateStore () { -} +function DbMigrateStore() {} DbMigrateStore.prototype.save = function (set, fn) { let insertData = JSON.stringify({ lastRun: set.lastRun, - migrations: set.migrations + migrations: set.migrations, }) db.none(upsert, [insertData]).then(fn).catch(logger.error) } diff --git a/packages/server/lib/db.js b/packages/server/lib/db.js index e55b4964..fc942f2d 100644 --- a/packages/server/lib/db.js +++ b/packages/server/lib/db.js @@ -16,9 +16,8 @@ const pgp = Pgp({ else if (e.query) { logger.error(e.query) e.params && logger.error(e.params) - } - else logger.error(err) - } + } else logger.error(err) + }, }) const db = pgp(PSQL_URL) diff --git a/packages/server/lib/email.js b/packages/server/lib/email.js index 53367ca4..b0abb024 100644 --- a/packages/server/lib/email.js +++ b/packages/server/lib/email.js @@ -1,25 +1,25 @@ const ph = require('./plugin-helper') -function sendMessage (settings, rec) { - return Promise.resolve() - .then(() => { - const pluginCode = settings.config.notifications_thirdParty_email || 'mailgun' - const plugin = ph.load(ph.EMAIL, pluginCode) - const account = settings.accounts[pluginCode] +function sendMessage(settings, rec) { + return Promise.resolve().then(() => { + const pluginCode = + settings.config.notifications_thirdParty_email || 'mailgun' + const plugin = ph.load(ph.EMAIL, pluginCode) + const account = settings.accounts[pluginCode] - return plugin.sendMessage(account, rec) - }) + return plugin.sendMessage(account, rec) + }) } -function sendCustomerMessage (settings, rec) { - return Promise.resolve() - .then(() => { - const pluginCode = settings.config.notifications_thirdParty_email || 'mailgun' - const plugin = ph.load(ph.EMAIL, pluginCode) - const account = settings.accounts[pluginCode] +function sendCustomerMessage(settings, rec) { + return Promise.resolve().then(() => { + const pluginCode = + settings.config.notifications_thirdParty_email || 'mailgun' + const plugin = ph.load(ph.EMAIL, pluginCode) + const account = settings.accounts[pluginCode] - return plugin.sendMessage(account, rec) - }) + return plugin.sendMessage(account, rec) + }) } -module.exports = {sendMessage, sendCustomerMessage} +module.exports = { sendMessage, sendCustomerMessage } diff --git a/packages/server/lib/environment-helper.js b/packages/server/lib/environment-helper.js index 777dcef4..04d900a0 100644 --- a/packages/server/lib/environment-helper.js +++ b/packages/server/lib/environment-helper.js @@ -5,11 +5,11 @@ const isProdMode = () => process.env.NODE_ENV === 'production' require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) -function isRemoteNode (crypto) { +function isRemoteNode(crypto) { return process.env[`${crypto.cryptoCode}_NODE_LOCATION`] === 'remote' } -function isRemoteWallet (crypto) { +function isRemoteWallet(crypto) { return process.env[`${crypto.cryptoCode}_WALLET_LOCATION`] === 'remote' } @@ -17,5 +17,5 @@ module.exports = { isDevMode, isProdMode, isRemoteNode, - isRemoteWallet + isRemoteWallet, } diff --git a/packages/server/lib/error.js b/packages/server/lib/error.js index 87be70cc..d9a5188c 100644 --- a/packages/server/lib/error.js +++ b/packages/server/lib/error.js @@ -15,7 +15,7 @@ const E = function (name) { module.exports = E -function register (errorName) { +function register(errorName) { E[errorName] = E(errorName) } diff --git a/packages/server/lib/event-bus.js b/packages/server/lib/event-bus.js index 0c481327..74dc7367 100644 --- a/packages/server/lib/event-bus.js +++ b/packages/server/lib/event-bus.js @@ -5,7 +5,7 @@ const _ = require('lodash/fp') const subscriptions = {} -function subscribe (eventType, callback) { +function subscribe(eventType, callback) { const id = uuid.v1() if (!subscriptions[eventType]) subscriptions[eventType] = {} @@ -15,15 +15,18 @@ function subscribe (eventType, callback) { return { unsubscribe: () => { delete subscriptions[eventType][id] - if (_.keys(subscriptions[eventType]).length === 0) delete subscriptions[eventType] - } + if (_.keys(subscriptions[eventType]).length === 0) + delete subscriptions[eventType] + }, } } -function publish (eventType, arg) { +function publish(eventType, arg) { if (!subscriptions[eventType]) return - _.keys(subscriptions[eventType]).forEach(key => subscriptions[eventType][key](arg)) + _.keys(subscriptions[eventType]).forEach(key => + subscriptions[eventType][key](arg), + ) } module.exports = { subscribe, publish } diff --git a/packages/server/lib/exchange.js b/packages/server/lib/exchange.js index 00316eae..2c5b1c24 100644 --- a/packages/server/lib/exchange.js +++ b/packages/server/lib/exchange.js @@ -6,71 +6,80 @@ const ccxt = require('./plugins/exchange/ccxt') const mockExchange = require('./plugins/exchange/mock-exchange') const accounts = require('./new-admin/config/accounts') -function lookupExchange (settings, cryptoCode) { - const exchange = configManager.getWalletSettings(cryptoCode, settings.config).exchange +function lookupExchange(settings, cryptoCode) { + const exchange = configManager.getWalletSettings( + cryptoCode, + settings.config, + ).exchange if (exchange === 'no-exchange') return null return exchange } -function fetchExchange (settings, cryptoCode) { - return Promise.resolve() - .then(() => { - const exchangeName = lookupExchange(settings, cryptoCode) - if (exchangeName === 'mock-exchange') return { exchangeName, account: { currencyMarket: 'EUR' } } - if (!exchangeName) throw new Error('No exchange set') - const account = settings.accounts[exchangeName] +function fetchExchange(settings, cryptoCode) { + return Promise.resolve().then(() => { + const exchangeName = lookupExchange(settings, cryptoCode) + if (exchangeName === 'mock-exchange') + return { exchangeName, account: { currencyMarket: 'EUR' } } + if (!exchangeName) throw new Error('No exchange set') + const account = settings.accounts[exchangeName] - return { exchangeName, account } - }) + return { exchangeName, account } + }) } -function buy (settings, tradeEntry) { +function buy(settings, tradeEntry) { const { cryptoAtoms, fiatCode, cryptoCode } = tradeEntry - return fetchExchange(settings, cryptoCode) - .then(r => { - if (r.exchangeName === 'mock-exchange') { - return mockExchange.buy(cryptoAtoms, fiatCode, cryptoCode) - } - return ccxt.trade('buy', r.account, tradeEntry, r.exchangeName) - }) + return fetchExchange(settings, cryptoCode).then(r => { + if (r.exchangeName === 'mock-exchange') { + return mockExchange.buy(cryptoAtoms, fiatCode, cryptoCode) + } + return ccxt.trade('buy', r.account, tradeEntry, r.exchangeName) + }) } -function sell (settings, tradeEntry) { +function sell(settings, tradeEntry) { const { cryptoAtoms, fiatCode, cryptoCode } = tradeEntry - return fetchExchange(settings, cryptoCode) - .then(r => { - if (r.exchangeName === 'mock-exchange') { - return mockExchange.sell(cryptoAtoms, fiatCode, cryptoCode) - } - return ccxt.trade('sell', r.account, tradeEntry, r.exchangeName) - }) + return fetchExchange(settings, cryptoCode).then(r => { + if (r.exchangeName === 'mock-exchange') { + return mockExchange.sell(cryptoAtoms, fiatCode, cryptoCode) + } + return ccxt.trade('sell', r.account, tradeEntry, r.exchangeName) + }) } -function active (settings, cryptoCode) { +function active(settings, cryptoCode) { return !!lookupExchange(settings, cryptoCode) } -function getMarkets () { - const filterExchanges = _.filter(it => it.class === 'exchange' && !it.dev && it.code !== 'no-exchange') - const availableExchanges = _.map(it => it.code, filterExchanges(accounts.ACCOUNT_LIST)) +function getMarkets() { + const filterExchanges = _.filter( + it => it.class === 'exchange' && !it.dev && it.code !== 'no-exchange', + ) + const availableExchanges = _.map( + it => it.code, + filterExchanges(accounts.ACCOUNT_LIST), + ) const fetchMarketForExchange = exchange => - ccxt.getMarkets(exchange, ALL_CRYPTOS) + ccxt + .getMarkets(exchange, ALL_CRYPTOS) .then(markets => ({ exchange, markets })) .catch(error => ({ exchange, markets: [], - error: error.message + error: error.message, })) - const transformToObject = _.reduce((acc, { exchange, markets }) => ({ - ...acc, - [exchange]: markets - }), {}) + const transformToObject = _.reduce( + (acc, { exchange, markets }) => ({ + ...acc, + [exchange]: markets, + }), + {}, + ) const promises = _.map(fetchMarketForExchange, availableExchanges) - return Promise.all(promises) - .then(transformToObject) + return Promise.all(promises).then(transformToObject) } module.exports = { @@ -78,5 +87,5 @@ module.exports = { buy, sell, active, - getMarkets + getMarkets, } diff --git a/packages/server/lib/forex.js b/packages/server/lib/forex.js index 6c5ea781..dc71bef7 100644 --- a/packages/server/lib/forex.js +++ b/packages/server/lib/forex.js @@ -7,34 +7,52 @@ const T = require('./time') const MAX_ROTATIONS = 5 -const _getFiatRates = () => ( - axios.get('https://bitpay.com/api/rates') - .then(response => response.data) -) +const _getFiatRates = () => + axios.get('https://bitpay.com/api/rates').then(response => response.data) const getFiatRates = mem(_getFiatRates, { maxAge: 6 * T.hours, - cacheKey: () => '' + cacheKey: () => '', }) const API_QUEUE = [ - { api: getBitPayFxRate, name: 'bitpay', fiatCodeProperty: 'code', rateProperty: 'rate' } + { + api: getBitPayFxRate, + name: 'bitpay', + fiatCodeProperty: 'code', + rateProperty: 'rate', + }, ] -function getBitPayFxRate (fiatCode, defaultFiatMarket, fiatCodeProperty, rateProperty) { - return getFiatRates() - .then(({ data: fxRates }) => { - const defaultFiatRate = findCurrencyRates(fxRates, defaultFiatMarket, fiatCodeProperty, rateProperty) - const fxRate = findCurrencyRates(fxRates, fiatCode, fiatCodeProperty, rateProperty).div(defaultFiatRate) - return { - fxRate - } - }) +function getBitPayFxRate( + fiatCode, + defaultFiatMarket, + fiatCodeProperty, + rateProperty, +) { + return getFiatRates().then(({ data: fxRates }) => { + const defaultFiatRate = findCurrencyRates( + fxRates, + defaultFiatMarket, + fiatCodeProperty, + rateProperty, + ) + const fxRate = findCurrencyRates( + fxRates, + fiatCode, + fiatCodeProperty, + rateProperty, + ).div(defaultFiatRate) + return { + fxRate, + } + }) } -function findCurrencyRates (fxRates, fiatCode, fiatCodeProperty, rateProperty) { +function findCurrencyRates(fxRates, fiatCode, fiatCodeProperty, rateProperty) { const rates = _.find(_.matchesProperty(fiatCodeProperty, fiatCode), fxRates) - if (!rates || !rates[rateProperty]) throw new Error(`Unsupported currency: ${fiatCode}`) + if (!rates || !rates[rateProperty]) + throw new Error(`Unsupported currency: ${fiatCode}`) return new BN(rates[rateProperty].toString()) } @@ -46,15 +64,20 @@ const getRate = (retries = 1, fiatCode, defaultFiatMarket) => { if (!activeAPI) throw new Error(`FOREX api ${selected} does not exist.`) - return activeAPI(fiatCode, defaultFiatMarket, fiatCodeProperty, rateProperty) - .catch(() => { - // Switch service - const erroredService = API_QUEUE.shift() - API_QUEUE.push(erroredService) - if (retries >= MAX_ROTATIONS) throw new Error(`FOREX API error from ${erroredService.name}`) + return activeAPI( + fiatCode, + defaultFiatMarket, + fiatCodeProperty, + rateProperty, + ).catch(() => { + // Switch service + const erroredService = API_QUEUE.shift() + API_QUEUE.push(erroredService) + if (retries >= MAX_ROTATIONS) + throw new Error(`FOREX API error from ${erroredService.name}`) - return getRate(++retries, fiatCode) - }) + return getRate(++retries, fiatCode) + }) } module.exports = { getFiatRates, getRate } diff --git a/packages/server/lib/graphql/resolvers.js b/packages/server/lib/graphql/resolvers.js index 2654c058..433273b0 100644 --- a/packages/server/lib/graphql/resolvers.js +++ b/packages/server/lib/graphql/resolvers.js @@ -4,7 +4,10 @@ const nmd = require('nano-markdown') const plugins = require('../plugins') const configManager = require('../new-config-manager') const settingsLoader = require('../new-settings-loader') -const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests') +const { + batchGetCustomInfoRequest, + getCustomInfoRequests, +} = require('../new-admin/services/customInfoRequests') const state = require('../middlewares/state') const { getMachine } = require('../machine-loader') @@ -14,23 +17,26 @@ const urlsToPing = [ `us.archive.ubuntu.com`, `uk.archive.ubuntu.com`, `za.archive.ubuntu.com`, - `cn.archive.ubuntu.com` + `cn.archive.ubuntu.com`, ] const speedtestFiles = [ { url: 'https://github.com/lamassu/speed-test-assets/raw/main/python-defaults_2.7.18-3.tar.gz', - size: 44668 - } + size: 44668, + }, ] const addSmthInfo = (dstField, srcFields) => smth => - (smth && smth.active) ? _.set(dstField, _.pick(srcFields, smth)) : _.identity + smth && smth.active ? _.set(dstField, _.pick(srcFields, smth)) : _.identity -const addOperatorInfo = addSmthInfo( - 'operatorInfo', - ['name', 'phone', 'email', 'website', 'companyNumber'] -) +const addOperatorInfo = addSmthInfo('operatorInfo', [ + 'name', + 'phone', + 'email', + 'website', + 'companyNumber', +]) const addReceiptInfo = receiptInfo => ret => { if (!receiptInfo) return ret @@ -56,87 +62,90 @@ const addReceiptInfo = receiptInfo => ret => { _.pick(fields), )(receiptInfo) - return (receiptInfo.paper || receiptInfo.sms) ? - _.set('receiptInfo', receiptInfo, ret) : - ret + return receiptInfo.paper || receiptInfo.sms + ? _.set('receiptInfo', receiptInfo, ret) + : ret } - -const addMachineScreenOpts = smth => _.update( - 'screenOptions', - _.flow( - addSmthInfo( - 'rates', - [ - 'active' - ] - )(smth.rates) +const addMachineScreenOpts = smth => + _.update( + 'screenOptions', + _.flow(addSmthInfo('rates', ['active'])(smth.rates)), ) -) /* TODO: Simplify this. */ const buildTriggers = allTriggers => { const normalTriggers = [] const customTriggers = _.filter(o => { - if (_.isEmpty(o.customInfoRequestId) || _.isNil(o.customInfoRequestId)) normalTriggers.push(o) + if (_.isEmpty(o.customInfoRequestId) || _.isNil(o.customInfoRequestId)) + normalTriggers.push(o) return !_.isNil(o.customInfoRequestId) && !_.isEmpty(o.customInfoRequestId) }, allTriggers) return _.flow( _.map(_.get('customInfoRequestId')), - batchGetCustomInfoRequest - )(customTriggers) - .then(res => { - res.forEach((details, index) => { - // make sure we aren't attaching the details to the wrong trigger - if (customTriggers[index].customInfoRequestId !== details.id) return - customTriggers[index] = { ...customTriggers[index], customInfoRequest: details } - }) - return [...normalTriggers, ...customTriggers] + batchGetCustomInfoRequest, + )(customTriggers).then(res => { + res.forEach((details, index) => { + // make sure we aren't attaching the details to the wrong trigger + if (customTriggers[index].customInfoRequestId !== details.id) return + customTriggers[index] = { + ...customTriggers[index], + customInfoRequest: details, + } }) + return [...normalTriggers, ...customTriggers] + }) } -const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings, }) => { - const massageCoins = _.map(_.pick([ - 'batchable', - 'cashInCommission', - 'cashInFee', - 'cashOutCommission', - 'cashOutFee', - 'cryptoCode', - 'cryptoCodeDisplay', - 'cryptoNetwork', - 'cryptoUnits', - 'display', - 'minimumTx', - 'isCashInOnly' - ])) +const staticConfig = ({ + currentConfigVersion, + deviceId, + deviceName, + pq, + settings, +}) => { + const massageCoins = _.map( + _.pick([ + 'batchable', + 'cashInCommission', + 'cashInFee', + 'cashOutCommission', + 'cashOutFee', + 'cryptoCode', + 'cryptoCodeDisplay', + 'cryptoNetwork', + 'cryptoUnits', + 'display', + 'minimumTx', + 'isCashInOnly', + ]), + ) const staticConf = _.flow( - _.pick([ - 'coins', - 'configVersion', - 'timezone', - 'screenOptions' - ]), + _.pick(['coins', 'configVersion', 'timezone', 'screenOptions']), _.update('coins', massageCoins), _.set('serverVersion', VERSION), )(pq) return Promise.all([ !!configManager.getCompliance(settings.config).enablePaperWalletOnly, - configManager.getTriggersAutomation(getCustomInfoRequests(true), settings.config), + configManager.getTriggersAutomation( + getCustomInfoRequests(true), + settings.config, + ), buildTriggers(configManager.getTriggers(settings.config)), - configManager.getWalletSettings('BTC', settings.config).layer2 !== 'no-layer2', + configManager.getWalletSettings('BTC', settings.config).layer2 !== + 'no-layer2', configManager.getLocale(deviceId, settings.config), configManager.getOperatorInfo(settings.config), configManager.getReceipt(settings.config), configManager.getAllMachineScreenOpts(settings.config), !!configManager.getCashOut(deviceId, settings.config).active, getMachine(deviceId, currentConfigVersion), - configManager.getCustomerAuthenticationMethod(settings.config) - ]) - .then(([ + configManager.getCustomerAuthenticationMethod(settings.config), + ]).then( + ([ enablePaperWalletOnly, triggersAutomation, triggers, @@ -149,88 +158,121 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings { numberOfCassettes, numberOfRecyclers }, customerAuthentication, ]) => - (currentConfigVersion && currentConfigVersion >= staticConf.configVersion) ? - null : - _.flow( - _.assign({ - enablePaperWalletOnly, - triggersAutomation, - triggers, - hasLightning, - localeInfo: { - country: localeInfo.country, - languages: localeInfo.languages, - fiatCode: localeInfo.fiatCurrency - }, - machineInfo: { deviceId, deviceName, numberOfCassettes, numberOfRecyclers }, - twoWayMode, - customerAuthentication, - speedtestFiles, - urlsToPing, - }), - addOperatorInfo(operatorInfo), - addReceiptInfo(receiptInfo), - addMachineScreenOpts(machineScreenOpts) - )(staticConf)) + currentConfigVersion && currentConfigVersion >= staticConf.configVersion + ? null + : _.flow( + _.assign({ + enablePaperWalletOnly, + triggersAutomation, + triggers, + hasLightning, + localeInfo: { + country: localeInfo.country, + languages: localeInfo.languages, + fiatCode: localeInfo.fiatCurrency, + }, + machineInfo: { + deviceId, + deviceName, + numberOfCassettes, + numberOfRecyclers, + }, + twoWayMode, + customerAuthentication, + speedtestFiles, + urlsToPing, + }), + addOperatorInfo(operatorInfo), + addReceiptInfo(receiptInfo), + addMachineScreenOpts(machineScreenOpts), + )(staticConf), + ) } - const setZeroConfLimit = config => coin => _.set( 'zeroConfLimit', configManager.getWalletSettings(coin.cryptoCode, config).zeroConfLimit ?? 0, - coin + coin, ) -const dynamicConfig = ({ deviceId, operatorId, pid, pq, settings, }) => { +const dynamicConfig = ({ deviceId, operatorId, pid, pq, settings }) => { const massageCassettes = cassettes => - cassettes ? - _.flow( - cassettes => _.set('physical', _.get('cassettes', cassettes), cassettes), - cassettes => _.set('virtual', _.get('virtualCassettes', cassettes), cassettes), - _.unset('cassettes'), - _.unset('virtualCassettes') - )(cassettes) : - null + cassettes + ? _.flow( + cassettes => + _.set('physical', _.get('cassettes', cassettes), cassettes), + cassettes => + _.set('virtual', _.get('virtualCassettes', cassettes), cassettes), + _.unset('cassettes'), + _.unset('virtualCassettes'), + )(cassettes) + : null const massageRecyclers = recyclers => - recyclers ? - _.flow( - recyclers => _.set('physical', _.get('recyclers', recyclers), recyclers), - recyclers => _.set('virtual', _.get('virtualRecyclers', recyclers), recyclers), - _.unset('recyclers'), - _.unset('virtualRecyclers') - )(recyclers) : - null + recyclers + ? _.flow( + recyclers => + _.set('physical', _.get('recyclers', recyclers), recyclers), + recyclers => + _.set('virtual', _.get('virtualRecyclers', recyclers), recyclers), + _.unset('recyclers'), + _.unset('virtualRecyclers'), + )(recyclers) + : null - state.pids = _.update(operatorId, _.set(deviceId, { pid, ts: Date.now() }), state.pids) + state.pids = _.update( + operatorId, + _.set(deviceId, { pid, ts: Date.now() }), + state.pids, + ) const res = _.flow( - _.pick(['areThereAvailablePromoCodes', 'balances', 'cassettes', 'recyclers', 'coins', 'rates']), + _.pick([ + 'areThereAvailablePromoCodes', + 'balances', + 'cassettes', + 'recyclers', + 'coins', + 'rates', + ]), _.update('cassettes', massageCassettes), _.update('recyclers', massageRecyclers), /* [{ cryptoCode, rates }, ...] => [[cryptoCode, rates], ...] */ - _.update('coins', _.map(({ cryptoCode, rates }) => [cryptoCode, rates])), + _.update( + 'coins', + _.map(({ cryptoCode, rates }) => [cryptoCode, rates]), + ), /* [{ cryptoCode: balance }, ...] => [[cryptoCode, { balance }], ...] */ - _.update('balances', _.flow( - _.toPairs, - _.map(([cryptoCode, balance]) => [cryptoCode, { balance }]) - )), + _.update( + 'balances', + _.flow( + _.toPairs, + _.map(([cryptoCode, balance]) => [cryptoCode, { balance }]), + ), + ), /* Group the separate objects by cryptoCode */ /* { balances, coins, rates } => { cryptoCode: { balance, ask, bid, cashIn, cashOut }, ... } */ - ({ areThereAvailablePromoCodes, balances, cassettes, recyclers, coins, rates }) => ({ + ({ + areThereAvailablePromoCodes, + balances, + cassettes, + recyclers, + coins, + rates, + }) => ({ areThereAvailablePromoCodes, cassettes, recyclers, coins: _.flow( _.reduce( (ret, [cryptoCode, obj]) => _.update(cryptoCode, _.assign(obj), ret), - rates + rates, ), /* { cryptoCode: { balance, ask, bid, cashIn, cashOut }, ... } => [[cryptoCode, { balance, ask, bid, cashIn, cashOut }], ...] */ @@ -240,17 +282,36 @@ const dynamicConfig = ({ deviceId, operatorId, pid, pq, settings, }) => { _.map(([cryptoCode, obj]) => _.set('cryptoCode', cryptoCode, obj)), /* Only send coins which have all information needed by the machine. This prevents the machine going down if there's an issue with the coin node */ - _.filter(coin => ['ask', 'bid', 'balance', 'cashIn', 'cashOut', 'cryptoCode'].every(it => it in coin)) - )(_.concat(balances, coins)) + _.filter(coin => + ['ask', 'bid', 'balance', 'cashIn', 'cashOut', 'cryptoCode'].every( + it => it in coin, + ), + ), + )(_.concat(balances, coins)), }), _.update('coins', _.map(setZeroConfLimit(settings.config))), _.set('reboot', !!pid && state.reboots?.[operatorId]?.[deviceId] === pid), - _.set('shutdown', !!pid && state.shutdowns?.[operatorId]?.[deviceId] === pid), - _.set('restartServices', !!pid && state.restartServicesMap?.[operatorId]?.[deviceId] === pid), - _.set('emptyUnit', !!pid && state.emptyUnit?.[operatorId]?.[deviceId] === pid), - _.set('refillUnit', !!pid && state.refillUnit?.[operatorId]?.[deviceId] === pid), - _.set('diagnostics', !!pid && state.diagnostics?.[operatorId]?.[deviceId] === pid), + _.set( + 'shutdown', + !!pid && state.shutdowns?.[operatorId]?.[deviceId] === pid, + ), + _.set( + 'restartServices', + !!pid && state.restartServicesMap?.[operatorId]?.[deviceId] === pid, + ), + _.set( + 'emptyUnit', + !!pid && state.emptyUnit?.[operatorId]?.[deviceId] === pid, + ), + _.set( + 'refillUnit', + !!pid && state.refillUnit?.[operatorId]?.[deviceId] === pid, + ), + _.set( + 'diagnostics', + !!pid && state.diagnostics?.[operatorId]?.[deviceId] === pid, + ), )(pq) // Clean up the state middleware and prevent commands from being issued more than once @@ -269,8 +330,11 @@ const dynamicConfig = ({ deviceId, operatorId, pid, pq, settings, }) => { return res } - -const configs = (parent, { currentConfigVersion }, { deviceId, deviceName, operatorId, pid, settings }, info) => +const configs = ( + parent, + { currentConfigVersion }, + { deviceId, deviceName, operatorId, pid, settings }, +) => plugins(settings, deviceId) .pollQueries() .then(pq => ({ @@ -290,15 +354,17 @@ const configs = (parent, { currentConfigVersion }, { deviceId, deviceName, opera }), })) - -const massageTerms = terms => (terms.active && terms.text) ? ({ - tcPhoto: Boolean(terms.tcPhoto), - delay: Boolean(terms.delay), - title: terms.title, - text: nmd(terms.text), - accept: terms.acceptButtonText, - cancel: terms.cancelButtonText, -}) : null +const massageTerms = terms => + terms.active && terms.text + ? { + tcPhoto: Boolean(terms.tcPhoto), + delay: Boolean(terms.delay), + title: terms.title, + text: nmd(terms.text), + accept: terms.acceptButtonText, + cancel: terms.cancelButtonText, + } + : null /* * The type of the result of `configManager.getTermsConditions()` is more or @@ -327,7 +393,7 @@ const massageTerms = terms => (terms.active && terms.text) ? ({ * If the `hash` differs from `currentHash` then everything is resent (to * simplify machine implementation). */ -const terms = (parent, { currentConfigVersion, currentHash }, { deviceId, settings }, info) => { +const terms = (parent, { currentConfigVersion, currentHash }, { settings }) => { const isNone = x => _.isNil(x) || _.isEmpty(x) let latestTerms = configManager.getTermsConditions(settings.config) @@ -342,17 +408,22 @@ const terms = (parent, { currentConfigVersion, currentHash }, { deviceId, settin const isHashNew = hash !== currentHash const text = isHashNew ? latestTerms.text : null - return settingsLoader.fetchCurrentConfigVersion() + return settingsLoader + .fetchCurrentConfigVersion() .catch(() => null) - .then(configVersion => isHashNew || _.isNil(currentConfigVersion) || currentConfigVersion < configVersion) - .then(isVersionNew => isVersionNew ? _.omit(['text'], latestTerms) : null) + .then( + configVersion => + isHashNew || + _.isNil(currentConfigVersion) || + currentConfigVersion < configVersion, + ) + .then(isVersionNew => (isVersionNew ? _.omit(['text'], latestTerms) : null)) .then(details => ({ hash, details, text })) } - module.exports = { Query: { configs, terms, - } + }, } diff --git a/packages/server/lib/graphql/server.js b/packages/server/lib/graphql/server.js index 7d6c8be8..2d8cd5ac 100644 --- a/packages/server/lib/graphql/server.js +++ b/packages/server/lib/graphql/server.js @@ -5,11 +5,11 @@ const { ApolloServer } = require('@apollo/server') const devMode = !!require('minimist')(process.argv.slice(2)).dev const context = ({ req, res }) => ({ - deviceId: req.deviceId, /* lib/middlewares/populateDeviceId.js */ - deviceName: req.deviceName, /* lib/middlewares/authorize.js */ - operatorId: res.locals.operatorId, /* lib/middlewares/operatorId.js */ + deviceId: req.deviceId /* lib/middlewares/populateDeviceId.js */, + deviceName: req.deviceName /* lib/middlewares/authorize.js */, + operatorId: res.locals.operatorId /* lib/middlewares/operatorId.js */, pid: req.query.pid, - settings: req.settings, /* lib/middlewares/populateSettings.js */ + settings: req.settings /* lib/middlewares/populateSettings.js */, }) const graphQLServer = new ApolloServer({ @@ -21,7 +21,7 @@ const graphQLServer = new ApolloServer({ return error }, includeStacktraceInErrorResponses: devMode, - logger + logger, }) -module.exports = { graphQLServer, context } \ No newline at end of file +module.exports = { graphQLServer, context } diff --git a/packages/server/lib/graphql/types.js b/packages/server/lib/graphql/types.js index a4dfa4c9..cca000b4 100644 --- a/packages/server/lib/graphql/types.js +++ b/packages/server/lib/graphql/types.js @@ -1,234 +1,234 @@ const gql = require('graphql-tag') module.exports = gql` -type Coin { - cryptoCode: String! - cryptoCodeDisplay: String! - display: String! - minimumTx: String! - cashInFee: String! - cashOutFee: String! - cashInCommission: String! - cashOutCommission: String! - cryptoNetwork: String! - cryptoUnits: String! - batchable: Boolean! - isCashInOnly: Boolean! -} + type Coin { + cryptoCode: String! + cryptoCodeDisplay: String! + display: String! + minimumTx: String! + cashInFee: String! + cashOutFee: String! + cashInCommission: String! + cashOutCommission: String! + cryptoNetwork: String! + cryptoUnits: String! + batchable: Boolean! + isCashInOnly: Boolean! + } -type LocaleInfo { - country: String! - fiatCode: String! - languages: [String!]! -} + type LocaleInfo { + country: String! + fiatCode: String! + languages: [String!]! + } -type OperatorInfo { - name: String! - phone: String! - email: String! - website: String! - companyNumber: String! -} + type OperatorInfo { + name: String! + phone: String! + email: String! + website: String! + companyNumber: String! + } -type MachineInfo { - deviceId: String! @deprecated(reason: "unused by the machine") - deviceName: String - numberOfCassettes: Int - numberOfRecyclers: Int -} + type MachineInfo { + deviceId: String! @deprecated(reason: "unused by the machine") + deviceName: String + numberOfCassettes: Int + numberOfRecyclers: Int + } -type ReceiptInfo { - paper: Boolean! - automaticPrint: Boolean! - sms: Boolean! - operatorWebsite: Boolean! - operatorEmail: Boolean! - operatorPhone: Boolean! - companyNumber: Boolean! - machineLocation: Boolean! - customerNameOrPhoneNumber: Boolean! - exchangeRate: Boolean! - addressQRCode: Boolean! -} + type ReceiptInfo { + paper: Boolean! + automaticPrint: Boolean! + sms: Boolean! + operatorWebsite: Boolean! + operatorEmail: Boolean! + operatorPhone: Boolean! + companyNumber: Boolean! + machineLocation: Boolean! + customerNameOrPhoneNumber: Boolean! + exchangeRate: Boolean! + addressQRCode: Boolean! + } -type MachineScreenOptions { - rates: RateScreenOptions! -} + type MachineScreenOptions { + rates: RateScreenOptions! + } -type RateScreenOptions { - active: Boolean! -} + type RateScreenOptions { + active: Boolean! + } -type SpeedtestFile { - url: String! - size: Int! -} + type SpeedtestFile { + url: String! + size: Int! + } -enum TriggerAutomationType { - Automatic - Manual -} + enum TriggerAutomationType { + Automatic + Manual + } -type CustomTriggersAutomation { - id: ID! - type: TriggerAutomationType! -} + type CustomTriggersAutomation { + id: ID! + type: TriggerAutomationType! + } -type TriggersAutomation { - sanctions: TriggerAutomationType! - idCardPhoto: TriggerAutomationType! - idCardData: TriggerAutomationType! - facephoto: TriggerAutomationType! - usSsn: TriggerAutomationType! - custom: [CustomTriggersAutomation]! -} + type TriggersAutomation { + sanctions: TriggerAutomationType! + idCardPhoto: TriggerAutomationType! + idCardData: TriggerAutomationType! + facephoto: TriggerAutomationType! + usSsn: TriggerAutomationType! + custom: [CustomTriggersAutomation]! + } -type CustomScreen { - text: String! - title: String! -} + type CustomScreen { + text: String! + title: String! + } -type CustomInput { - type: String! - constraintType: String! - label1: String - label2: String - choiceList: [String] -} + type CustomInput { + type: String! + constraintType: String! + label1: String + label2: String + choiceList: [String] + } -type CustomRequest { - name: String! - input: CustomInput! - screen1: CustomScreen! - screen2: CustomScreen! -} + type CustomRequest { + name: String! + input: CustomInput! + screen1: CustomScreen! + screen2: CustomScreen! + } -type CustomInfoRequest { - id: String! - enabled: Boolean! - customRequest: CustomRequest! -} + type CustomInfoRequest { + id: String! + enabled: Boolean! + customRequest: CustomRequest! + } -type Trigger { - id: String! - direction: String! - requirement: String! - triggerType: String! + type Trigger { + id: String! + direction: String! + requirement: String! + triggerType: String! - suspensionDays: Float - threshold: Int - thresholdDays: Int - customInfoRequestId: String @deprecated(reason: "use customInfoRequest.id") - customInfoRequest: CustomInfoRequest - externalService: String -} + suspensionDays: Float + threshold: Int + thresholdDays: Int + customInfoRequestId: String @deprecated(reason: "use customInfoRequest.id") + customInfoRequest: CustomInfoRequest + externalService: String + } -type TermsDetails { - tcPhoto: Boolean! - delay: Boolean! - title: String! - accept: String! - cancel: String! -} + type TermsDetails { + tcPhoto: Boolean! + delay: Boolean! + title: String! + accept: String! + cancel: String! + } -type Terms { - hash: String! - text: String - details: TermsDetails -} + type Terms { + hash: String! + text: String + details: TermsDetails + } -enum CustomerAuthentication { - EMAIL - SMS -} + enum CustomerAuthentication { + EMAIL + SMS + } -type StaticConfig { - configVersion: Int! + type StaticConfig { + configVersion: Int! - coins: [Coin!]! - enablePaperWalletOnly: Boolean! - hasLightning: Boolean! - serverVersion: String! - timezone: Int! - twoWayMode: Boolean! - customerAuthentication: CustomerAuthentication! + coins: [Coin!]! + enablePaperWalletOnly: Boolean! + hasLightning: Boolean! + serverVersion: String! + timezone: Int! + twoWayMode: Boolean! + customerAuthentication: CustomerAuthentication! - localeInfo: LocaleInfo! - operatorInfo: OperatorInfo - machineInfo: MachineInfo! - receiptInfo: ReceiptInfo - screenOptions: MachineScreenOptions + localeInfo: LocaleInfo! + operatorInfo: OperatorInfo + machineInfo: MachineInfo! + receiptInfo: ReceiptInfo + screenOptions: MachineScreenOptions - speedtestFiles: [SpeedtestFile!]! - urlsToPing: [String!]! + speedtestFiles: [SpeedtestFile!]! + urlsToPing: [String!]! - triggersAutomation: TriggersAutomation! - triggers: [Trigger!]! -} + triggersAutomation: TriggersAutomation! + triggers: [Trigger!]! + } -type DynamicCoinValues { - # NOTE: Doesn't seem to be used anywhere outside of lib/plugins.js. - # However, it can be used to generate the cache key, if we ever move to an - # actual caching mechanism. - #timestamp: String! + type DynamicCoinValues { + # NOTE: Doesn't seem to be used anywhere outside of lib/plugins.js. + # However, it can be used to generate the cache key, if we ever move to an + # actual caching mechanism. + #timestamp: String! - cryptoCode: String! - balance: String! + cryptoCode: String! + balance: String! - # Raw rates - ask: String! - bid: String! + # Raw rates + ask: String! + bid: String! - # Rates with commissions applied - cashIn: String! - cashOut: String! + # Rates with commissions applied + cashIn: String! + cashOut: String! - zeroConfLimit: Int! -} + zeroConfLimit: Int! + } -type PhysicalCassette { - name: String! - denomination: Int! - count: Int! -} + type PhysicalCassette { + name: String! + denomination: Int! + count: Int! + } -type PhysicalRecycler { - name: String! - number: Int! - denomination: Int! - count: Int! -} + type PhysicalRecycler { + name: String! + number: Int! + denomination: Int! + count: Int! + } -type Cassettes { - physical: [PhysicalCassette!]! - virtual: [Int!]! -} + type Cassettes { + physical: [PhysicalCassette!]! + virtual: [Int!]! + } -type Recyclers { - physical: [PhysicalRecycler!]! - virtual: [Int!]! -} + type Recyclers { + physical: [PhysicalRecycler!]! + virtual: [Int!]! + } -type DynamicConfig { - areThereAvailablePromoCodes: Boolean! - cassettes: Cassettes - recyclers: Recyclers - coins: [DynamicCoinValues!]! - reboot: Boolean! - shutdown: Boolean! - restartServices: Boolean! - emptyUnit: Boolean! - refillUnit: Boolean! - diagnostics: Boolean! -} + type DynamicConfig { + areThereAvailablePromoCodes: Boolean! + cassettes: Cassettes + recyclers: Recyclers + coins: [DynamicCoinValues!]! + reboot: Boolean! + shutdown: Boolean! + restartServices: Boolean! + emptyUnit: Boolean! + refillUnit: Boolean! + diagnostics: Boolean! + } -type Configs { - static: StaticConfig - dynamic: DynamicConfig! -} + type Configs { + static: StaticConfig + dynamic: DynamicConfig! + } -type Query { - configs(currentConfigVersion: Int): Configs! - terms(currentHash: String, currentConfigVersion: Int): Terms -} + type Query { + configs(currentConfigVersion: Int): Configs! + terms(currentHash: String, currentConfigVersion: Int): Terms + } ` diff --git a/packages/server/lib/hardware-credentials.js b/packages/server/lib/hardware-credentials.js index 761fd045..d83f8751 100644 --- a/packages/server/lib/hardware-credentials.js +++ b/packages/server/lib/hardware-credentials.js @@ -2,27 +2,27 @@ const uuid = require('uuid') const db = require('./db') -function createHardwareCredential (userID, credentialData) { +function createHardwareCredential(userID, credentialData) { const sql = `INSERT INTO hardware_credentials (id, user_id, data) VALUES ($1, $2, $3)` return db.none(sql, [uuid.v4(), userID, credentialData]) } -function getHardwareCredentials () { +function getHardwareCredentials() { const sql = `SELECT * FROM hardware_credentials` return db.any(sql) } -function getHardwareCredentialsByUserId (userID) { +function getHardwareCredentialsByUserId(userID) { const sql = `SELECT * FROM hardware_credentials WHERE user_id=$1` return db.any(sql, [userID]) } -function getUserByUserHandle (userHandle) { +function getUserByUserHandle(userHandle) { const sql = `SELECT users.id, users.username, users.role FROM users INNER JOIN hardware_credentials hc ON users.id=hc.user_id WHERE data->>'userHandle'=$1::jsonb::text` return db.oneOrNone(sql, [userHandle]) } -function updateHardwareCredential (credential) { +function updateHardwareCredential(credential) { const sql = `UPDATE hardware_credentials SET last_used=now(), data=$1 WHERE id=$2` return db.none(sql, [credential.data, credential.id]) } @@ -32,5 +32,5 @@ module.exports = { getHardwareCredentials, getHardwareCredentialsByUserId, getUserByUserHandle, - updateHardwareCredential + updateHardwareCredential, } diff --git a/packages/server/lib/layer2.js b/packages/server/lib/layer2.js index b77403da..e64289e2 100644 --- a/packages/server/lib/layer2.js +++ b/packages/server/lib/layer2.js @@ -2,38 +2,47 @@ const configManager = require('./new-config-manager') const ph = require('./plugin-helper') const _ = require('lodash/fp') -function fetch (settings, cryptoCode) { - const plugin = configManager.getWalletSettings(cryptoCode, settings.config).layer2 +function fetch(settings, cryptoCode) { + const plugin = configManager.getWalletSettings( + cryptoCode, + settings.config, + ).layer2 if (_.isEmpty(plugin) || plugin === 'no-layer2') return Promise.resolve() const layer2 = ph.load(ph.LAYER2, plugin) const account = settings.accounts[plugin] - return Promise.resolve({layer2, account}) + return Promise.resolve({ layer2, account }) } -function newAddress (settings, info) { - return fetch(settings, info.cryptoCode) - .then(r => { - if (!r) return - return r.layer2.newAddress(r.account, info) - }) +function newAddress(settings, info) { + return fetch(settings, info.cryptoCode).then(r => { + if (!r) return + return r.layer2.newAddress(r.account, info) + }) } -function getStatus (settings, tx) { +function getStatus(settings, tx) { const toAddress = tx.layer2Address - if (!toAddress) return Promise.resolve({status: 'notSeen'}) + if (!toAddress) return Promise.resolve({ status: 'notSeen' }) - return fetch(settings, tx.cryptoCode) - .then(r => { - if (!r) return {status: 'notSeen'} - return r.layer2.getStatus(r.account, toAddress, tx.cryptoAtoms, tx.cryptoCode) - }) + return fetch(settings, tx.cryptoCode).then(r => { + if (!r) return { status: 'notSeen' } + return r.layer2.getStatus( + r.account, + toAddress, + tx.cryptoAtoms, + tx.cryptoCode, + ) + }) } -function cryptoNetwork (settings, cryptoCode) { - const plugin = configManager.getWalletSettings(cryptoCode, settings.config).layer2 +function cryptoNetwork(settings, cryptoCode) { + const plugin = configManager.getWalletSettings( + cryptoCode, + settings.config, + ).layer2 const layer2 = ph.load(ph.LAYER2, plugin) const account = settings.accounts[plugin] @@ -41,7 +50,7 @@ function cryptoNetwork (settings, cryptoCode) { return layer2.cryptoNetwork(account, cryptoCode) } -function isLayer2Address (address) { +function isLayer2Address(address) { return address.split(':').length >= 2 } @@ -49,5 +58,5 @@ module.exports = { isLayer2Address, newAddress, getStatus, - cryptoNetwork + cryptoNetwork, } diff --git a/packages/server/lib/logger.js b/packages/server/lib/logger.js index ef2bda41..3507a706 100644 --- a/packages/server/lib/logger.js +++ b/packages/server/lib/logger.js @@ -7,40 +7,42 @@ const LOG_LEVEL = process.env.LOG_LEVEL const logger = new winston.Logger({ level: LOG_LEVEL, transports: [ - new (winston.transports.Console)({ + new winston.transports.Console({ timestamp: true, colorize: true, handleExceptions: true, - humanReadableUnhandledException: true + humanReadableUnhandledException: true, }), new Postgres({ connectionString: PSQL_URL, tableName: 'server_logs', handleExceptions: true, - humanReadableUnhandledException: true - }) + humanReadableUnhandledException: true, + }), ], rewriters: [ - (...[,, meta]) => { + (...[, , meta]) => { if (meta.isAxiosError) { return { message: meta.message, status: meta.response?.status, data: meta.response?.data, url: meta.config?.url, - method: meta.config?.method + method: meta.config?.method, } } - return meta instanceof Error ? { message: meta.message, stack: meta.stack, meta } : meta - } + return meta instanceof Error + ? { message: meta.message, stack: meta.stack, meta } + : meta + }, ], - exitOnError: false + exitOnError: false, }) logger.stream = { write: message => { logger.info(message.trim()) - } + }, } module.exports = logger diff --git a/packages/server/lib/logs.js b/packages/server/lib/logs.js index 8187e23e..ae8970dd 100644 --- a/packages/server/lib/logs.js +++ b/packages/server/lib/logs.js @@ -7,7 +7,6 @@ const logger = require('./logger') const pgp = require('pg-promise')() const getMachineName = require('./machine-loader').getMachineName -const NUM_RESULTS = 500 /** * Get the latest log's timestamp @@ -20,12 +19,15 @@ const NUM_RESULTS = 500 * * @returns {date} Last timestamp */ -function getLastSeen (deviceId) { +function getLastSeen(deviceId) { const sql = `select id, timestamp, serial from logs where device_id=$1 order by timestamp desc, serial desc limit 1` - return db.oneOrNone(sql, [deviceId]) - .then(log => log ? {timestamp: log.timestamp, serial: log.serial, id: log.id} : null) + return db + .oneOrNone(sql, [deviceId]) + .then(log => + log ? { timestamp: log.timestamp, serial: log.serial, id: log.id } : null, + ) } /** @@ -40,10 +42,11 @@ function getLastSeen (deviceId) { * * @returns {null} */ -function update (deviceId, logLines) { - const cs = new pgp.helpers.ColumnSet([ - 'id', 'device_id', 'log_level', 'timestamp', 'serial', 'message'], - {table: 'logs'}) +function update(deviceId, logLines) { + const cs = new pgp.helpers.ColumnSet( + ['id', 'device_id', 'log_level', 'timestamp', 'serial', 'message'], + { table: 'logs' }, + ) const logs = _.map(log => { const formatted = { @@ -52,7 +55,7 @@ function update (deviceId, logLines) { message: log.msg, logLevel: _.contains('error', _.lowerCase(log.msg)) ? 'error' : 'info', timestamp: log.timestamp, - serial: log.serial || 0 + serial: log.serial || 0, } return _.mapKeys(_.snakeCase, formatted) }, logLines) @@ -61,7 +64,7 @@ function update (deviceId, logLines) { return db.none(sql) } -function clearOldLogs () { +function clearOldLogs() { const sqls = `delete from logs where timestamp < now() - interval '3 days'; delete from server_logs @@ -69,7 +72,7 @@ function clearOldLogs () { return db.multi(sqls) } -function getUnlimitedMachineLogs (deviceId, until = new Date().toISOString()) { +function getUnlimitedMachineLogs(deviceId, until = new Date().toISOString()) { // Note: sql is a little confusing here, since timestamp is used both as a column // and a reserved word, but it works. const sql = `select id, log_level, timestamp, message from logs @@ -78,14 +81,21 @@ function getUnlimitedMachineLogs (deviceId, until = new Date().toISOString()) { and timestamp > (timestamp $2 - interval '2 days') order by timestamp desc, serial desc` - return Promise.all([db.any(sql, [ deviceId, until ]), getMachineName(deviceId)]) - .then(([logs, machineName]) => ({ - logs: _.map(_.mapKeys(_.camelCase), logs), - currentMachine: {deviceId, name: machineName} - })) + return Promise.all([ + db.any(sql, [deviceId, until]), + getMachineName(deviceId), + ]).then(([logs, machineName]) => ({ + logs: _.map(_.mapKeys(_.camelCase), logs), + currentMachine: { deviceId, name: machineName }, + })) } -function getMachineLogs (deviceId, until = new Date().toISOString(), limit = null, offset = 0) { +function getMachineLogs( + deviceId, + until = new Date().toISOString(), + limit = null, + offset = 0, +) { const sql = `select id, log_level, timestamp, message from logs where device_id=$1 and timestamp <= $2 @@ -93,14 +103,22 @@ function getMachineLogs (deviceId, until = new Date().toISOString(), limit = nul limit $3 offset $4` - return Promise.all([db.any(sql, [ deviceId, until, limit, offset ]), getMachineName(deviceId)]) - .then(([logs, machineName]) => ({ - logs: _.map(_.mapKeys(_.camelCase), logs), - currentMachine: {deviceId, name: machineName} - })) + return Promise.all([ + db.any(sql, [deviceId, until, limit, offset]), + getMachineName(deviceId), + ]).then(([logs, machineName]) => ({ + logs: _.map(_.mapKeys(_.camelCase), logs), + currentMachine: { deviceId, name: machineName }, + })) } -function simpleGetMachineLogs (deviceId, from = new Date(0).toISOString(), until = new Date().toISOString(), limit = null, offset = 0) { +function simpleGetMachineLogs( + deviceId, + from = new Date(0).toISOString(), + until = new Date().toISOString(), + limit = null, + offset = 0, +) { const sql = `select id, log_level, timestamp, message from logs where device_id=$1 and timestamp >= $2 @@ -109,31 +127,38 @@ function simpleGetMachineLogs (deviceId, from = new Date(0).toISOString(), until limit $4 offset $5` - return db.any(sql, [ deviceId, from, until, limit, offset ]) + return db + .any(sql, [deviceId, from, until, limit, offset]) .then(_.map(_.mapKeys(_.camelCase))) } -function logDateFormat (timezone, logs, fields) { +function logDateFormat(timezone, logs, fields) { return _.map(log => { - const values = _.map( - field => - { - if (_.isNil(log[field])) return null - if (!isValid(log[field])) { - logger.warn(`Tried to convert to ${timezone} timezone the value ${log[field]} and failed. Returning original value...`) - return log[field] - } - const date = utcToZonedTime(timezone, log[field]) - return `${format('yyyy-MM-dd', date)}T${format('HH:mm:ss.SSS', date)}` - }, - fields - ) + const values = _.map(field => { + if (_.isNil(log[field])) return null + if (!isValid(log[field])) { + logger.warn( + `Tried to convert to ${timezone} timezone the value ${log[field]} and failed. Returning original value...`, + ) + return log[field] + } + const date = utcToZonedTime(timezone, log[field]) + return `${format('yyyy-MM-dd', date)}T${format('HH:mm:ss.SSS', date)}` + }, fields) const fieldsToOverride = _.zipObject(fields, values) return { ...log, - ...fieldsToOverride + ...fieldsToOverride, } }, logs) } -module.exports = { getUnlimitedMachineLogs, getMachineLogs, simpleGetMachineLogs, update, getLastSeen, clearOldLogs, logDateFormat } +module.exports = { + getUnlimitedMachineLogs, + getMachineLogs, + simpleGetMachineLogs, + update, + getLastSeen, + clearOldLogs, + logDateFormat, +} diff --git a/packages/server/lib/loyalty.js b/packages/server/lib/loyalty.js index 9ac9e0b6..013e92e8 100644 --- a/packages/server/lib/loyalty.js +++ b/packages/server/lib/loyalty.js @@ -1,62 +1,66 @@ const db = require('./db') const uuid = require('uuid') const _ = require('lodash/fp') -const pgp = require('pg-promise')() -function getAvailablePromoCodes () { +function getAvailablePromoCodes() { const sql = `SELECT * FROM coupons WHERE soft_deleted=false` return db.any(sql) } -function getPromoCode (code) { +function getPromoCode(code) { const sql = `SELECT * FROM coupons WHERE code=$1 AND soft_deleted=false` return db.oneOrNone(sql, [code]) } -function createPromoCode (code, discount) { +function createPromoCode(code, discount) { const sql = `INSERT INTO coupons (id, code, discount) VALUES ($1, $2, $3) RETURNING *` return db.one(sql, [uuid.v4(), code, discount]) } -function deletePromoCode (id) { +function deletePromoCode(id) { const sql = `UPDATE coupons SET soft_deleted=true WHERE id=$1` return db.none(sql, [id]) } -function getNumberOfAvailablePromoCodes () { +function getNumberOfAvailablePromoCodes() { const sql = `SELECT COUNT(id) FROM coupons WHERE soft_deleted=false` return db.one(sql).then(res => res.count) } -function getAvailableIndividualDiscounts () { +function getAvailableIndividualDiscounts() { const sql = `SELECT * FROM individual_discounts WHERE soft_deleted=false` - return db.any(sql).then(res => _.map(it => ({ - id: it.id, - customerId: it.customer_id, - discount: it.discount - }), res)) + return db.any(sql).then(res => + _.map( + it => ({ + id: it.id, + customerId: it.customer_id, + discount: it.discount, + }), + res, + ), + ) } -function getCustomerActiveIndividualDiscount (customerId) { +function getCustomerActiveIndividualDiscount(customerId) { const sql = `SELECT * FROM individual_discounts WHERE customer_id=$1 AND soft_deleted=false LIMIT 1` return db.oneOrNone(sql, [customerId]).then(res => { if (!_.isNil(res)) { return { id: res.id, customerId: res.customer_id, - discount: res.discount + discount: res.discount, } } return res }) } -function createIndividualDiscount (customerId, discount) { +function createIndividualDiscount(customerId, discount) { const sql = `INSERT INTO individual_discounts (id, customer_id, discount) VALUES ($1, $2, $3)` return db.none(sql, [uuid.v4(), customerId, discount]) } -function deleteIndividualDiscount (id) { +function deleteIndividualDiscount(id) { const sql = `UPDATE individual_discounts SET soft_deleted=true WHERE id=$1` return db.none(sql, [id]) } @@ -70,5 +74,5 @@ module.exports = { getAvailableIndividualDiscounts, getCustomerActiveIndividualDiscount, createIndividualDiscount, - deleteIndividualDiscount + deleteIndividualDiscount, } diff --git a/packages/server/lib/machine-loader.js b/packages/server/lib/machine-loader.js index e846b026..2fd8da7c 100644 --- a/packages/server/lib/machine-loader.js +++ b/packages/server/lib/machine-loader.js @@ -13,7 +13,7 @@ const dbm = require('./postgresql_interface') const configManager = require('./new-config-manager') const notifierUtils = require('./notifier/utils') const notifierQueries = require('./notifier/queries') -const { GraphQLError } = require('graphql'); +const { GraphQLError } = require('graphql') const { loadLatestConfig } = require('./new-settings-loader') const logger = require('./logger') @@ -37,7 +37,7 @@ select d.*, COALESCE(emptybills, 0) + COALESCE(regularbills, 0) as cashbox from group by cit.device_id ) as nbills on nbills.device_id = d.device_id` -function toMachineObject (r) { +function toMachineObject(r) { return { deviceId: r.device_id, cashUnits: { @@ -51,48 +51,56 @@ function toMachineObject (r) { recycler3: r.recycler3, recycler4: r.recycler4, recycler5: r.recycler5, - recycler6: r.recycler6 + recycler6: r.recycler6, }, numberOfCassettes: r.number_of_cassettes, numberOfRecyclers: r.number_of_recyclers, version: r.version, model: r.model, diagnostics: { - timestamp: r.diagnostics_timestamp? new Date(r.diagnostics_timestamp) : null, - scanTimestamp: r.diagnostics_scan_timestamp? new Date(r.diagnostics_scan_timestamp) : null, - frontTimestamp: r.diagnostics_front_timestamp? new Date(r.diagnostics_front_timestamp) : null + timestamp: r.diagnostics_timestamp + ? new Date(r.diagnostics_timestamp) + : null, + scanTimestamp: r.diagnostics_scan_timestamp + ? new Date(r.diagnostics_scan_timestamp) + : null, + frontTimestamp: r.diagnostics_front_timestamp + ? new Date(r.diagnostics_front_timestamp) + : null, }, pairedAt: new Date(r.created), lastPing: new Date(r.last_online), name: r.name, - paired: r.paired + paired: r.paired, // TODO: we shall start using this JSON field at some point // location: r.location, } } -function getMachineIds () { +function getMachineIds() { const sql = 'select device_id from devices' return db.any(sql) } -function getMachines () { +function getMachines() { const sql = `${MACHINE_WITH_CALCULATED_FIELD_SQL} where display=TRUE ORDER BY created` - return db.any(sql) - .then(rr => rr.map(toMachineObject)) + return db.any(sql).then(rr => rr.map(toMachineObject)) } -function getUnpairedMachines () { - return db.any('SELECT * FROM unpaired_devices') - .then(_.map(r => - _.flow( - _.set('deviceId', _.get('device_id', r)), - _.unset('device_id') - )(r) - )) +function getUnpairedMachines() { + return db + .any('SELECT * FROM unpaired_devices') + .then( + _.map(r => + _.flow( + _.set('deviceId', _.get('device_id', r)), + _.unset('device_id'), + )(r), + ), + ) } -function getConfig (defaultConfig) { +function getConfig(defaultConfig) { return defaultConfig ? Promise.resolve(defaultConfig) : loadLatestConfig() } @@ -104,7 +112,7 @@ const getStatus = (ping, stuck) => { return fullyFunctionalStatus } -function addName (pings, events, config) { +function addName(pings, events, config) { return machine => { const cashOutConfig = configManager.getCashOut(machine.deviceId, config) @@ -113,22 +121,38 @@ function addName (pings, events, config) { const statuses = [ getStatus( _.first(pings[machine.deviceId]), - _.first(checkStuckScreen(events, machine)) - ) + _.first(checkStuckScreen(events, machine)), + ), ] return _.assign(machine, { cashOut, statuses }) } } -function getMachineNames (config) { - return Promise.all([getMachines(), getConfig(config), getNetworkHeartbeat(), getNetworkPerformance()]) - .then(([rawMachines, config, heartbeat, performance]) => Promise.all( - [rawMachines, checkPings(rawMachines), dbm.machineEvents(), config, heartbeat, performance] - )) +function getMachineNames(config) { + return Promise.all([ + getMachines(), + getConfig(config), + getNetworkHeartbeat(), + getNetworkPerformance(), + ]) + .then(([rawMachines, config, heartbeat, performance]) => + Promise.all([ + rawMachines, + checkPings(rawMachines), + dbm.machineEvents(), + config, + heartbeat, + performance, + ]), + ) .then(([rawMachines, pings, events, config, heartbeat, performance]) => { - const mergeByDeviceId = (x, y) => _.values(_.merge(_.keyBy('deviceId', x), _.keyBy('deviceId', y))) - const machines = mergeByDeviceId(mergeByDeviceId(rawMachines, heartbeat), performance) + const mergeByDeviceId = (x, y) => + _.values(_.merge(_.keyBy('deviceId', x), _.keyBy('deviceId', y))) + const machines = mergeByDeviceId( + mergeByDeviceId(rawMachines, heartbeat), + performance, + ) return machines.map(addName(pings, events, config)) }) @@ -144,67 +168,133 @@ function getMachineNames (config) { * @param {string} machineId machine id * @returns {string} machine name */ -function getMachineName (machineId) { +function getMachineName(machineId) { const sql = 'SELECT name FROM devices WHERE device_id=$1' - return db.oneOrNone(sql, [machineId]) - .then(it => it?.name) + return db.oneOrNone(sql, [machineId]).then(it => it?.name) } -function getMachine (machineId, config) { +function getMachine(machineId, config) { const sql = `${MACHINE_WITH_CALCULATED_FIELD_SQL} WHERE d.device_id = $1` const queryMachine = db.oneOrNone(sql, [machineId]).then(r => { - if (r === null) throw new GraphQLError('Resource doesn\'t exist', { extensions: { code: 'NOT_FOUND' } }) + if (r === null) + throw new GraphQLError("Resource doesn't exist", { + extensions: { code: 'NOT_FOUND' }, + }) else return toMachineObject(r) }) - return Promise.all([queryMachine, dbm.machineEvents(), config, getNetworkHeartbeatByDevice(machineId), getNetworkPerformanceByDevice(machineId)]) - .then(([machine, events, config, heartbeat, performance]) => { - const pings = checkPings([machine]) - const mergedMachine = { - ...machine, - responseTime: _.get('responseTime', heartbeat), - packetLoss: _.get('packetLoss', heartbeat), - downloadSpeed: _.get('downloadSpeed', performance), - } + return Promise.all([ + queryMachine, + dbm.machineEvents(), + config, + getNetworkHeartbeatByDevice(machineId), + getNetworkPerformanceByDevice(machineId), + ]).then(([machine, events, config, heartbeat, performance]) => { + const pings = checkPings([machine]) + const mergedMachine = { + ...machine, + responseTime: _.get('responseTime', heartbeat), + packetLoss: _.get('packetLoss', heartbeat), + downloadSpeed: _.get('downloadSpeed', performance), + } - return addName(pings, events, config)(mergedMachine) - }) + return addName(pings, events, config)(mergedMachine) + }) } -function renameMachine (rec) { +function renameMachine(rec) { const sql = 'UPDATE devices SET name=$1 WHERE device_id=$2' return db.none(sql, [rec.newName, rec.deviceId]) } -function resetCashOutBills (rec) { +function resetCashOutBills(rec) { const detailB = notifierUtils.buildDetail({ deviceId: rec.deviceId }) - const { cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6 } = rec.cashUnits + const { + cassette1, + cassette2, + cassette3, + cassette4, + recycler1, + recycler2, + recycler3, + recycler4, + recycler5, + recycler6, + } = rec.cashUnits const sql = `UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11;` - return db.none(sql, [cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6, rec.deviceId]).then(() => notifierQueries.invalidateNotification(detailB, 'fiatBalance')) + return db + .none(sql, [ + cassette1, + cassette2, + cassette3, + cassette4, + recycler1, + recycler2, + recycler3, + recycler4, + recycler5, + recycler6, + rec.deviceId, + ]) + .then(() => notifierQueries.invalidateNotification(detailB, 'fiatBalance')) } -function setCassetteBills (rec) { - const { cashbox, cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6 } = rec.cashUnits - return getMachine(rec.deviceId) - .then(machine => { - const oldCashboxCount = machine?.cashUnits?.cashbox - if (_.isNil(oldCashboxCount) || cashbox.toString() === oldCashboxCount.toString()) { - const sql = ` +function setCassetteBills(rec) { + const { + cashbox, + cassette1, + cassette2, + cassette3, + cassette4, + recycler1, + recycler2, + recycler3, + recycler4, + recycler5, + recycler6, + } = rec.cashUnits + return getMachine(rec.deviceId).then(machine => { + const oldCashboxCount = machine?.cashUnits?.cashbox + if ( + _.isNil(oldCashboxCount) || + cashbox.toString() === oldCashboxCount.toString() + ) { + const sql = ` UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=coalesce($5, recycler1), recycler2=coalesce($6, recycler2), recycler3=coalesce($7, recycler3), recycler4=coalesce($8, recycler4), recycler5=coalesce($9, recycler5), recycler6=coalesce($10, recycler6) WHERE device_id=$11` - return db.none(sql, [cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6, rec.deviceId]) - } + return db.none(sql, [ + cassette1, + cassette2, + cassette3, + cassette4, + recycler1, + recycler2, + recycler3, + recycler4, + recycler5, + recycler6, + rec.deviceId, + ]) + } - return batching.updateMachineWithBatch({ ...rec, oldCashboxValue: oldCashboxCount }) + return batching.updateMachineWithBatch({ + ...rec, + oldCashboxValue: oldCashboxCount, }) + }) } -function emptyMachineUnits ({ deviceId, newUnits, fiatCode }) { +function emptyMachineUnits({ deviceId, newUnits, fiatCode }) { return loadLatestConfig() - .then(config => Promise.all([getMachine(deviceId), configManager.getCashOut(deviceId, config)])) + .then(config => + Promise.all([ + getMachine(deviceId), + configManager.getCashOut(deviceId, config), + ]), + ) .then(([machine, cashoutSettings]) => { const movedBills = _.reduce( (acc, value) => ({ @@ -212,34 +302,42 @@ function emptyMachineUnits ({ deviceId, newUnits, fiatCode }) { [value]: { operationName: `cash-${_.replace(/(cassette|recycler)/g, '$1-')(value)}-empty`, delta: newUnits[value] - machine.cashUnits[value], - denomination: value !== 'cashbox' ? cashoutSettings[value] : null - } + denomination: value !== 'cashbox' ? cashoutSettings[value] : null, + }, }), {}, - _.keys(newUnits) + _.keys(newUnits), ) - const operationNames = _.mapValues(it => it.operationName)(_.filter(it => Math.abs(it.delta) > 0)(_.omit(['cashbox'], movedBills))) + const operationNames = _.mapValues(it => it.operationName)( + _.filter(it => Math.abs(it.delta) > 0)(_.omit(['cashbox'], movedBills)), + ) const operationsToCreate = _.map(it => ({ id: uuid.v4(), device_id: deviceId, - operation_type: it + operation_type: it, }))(operationNames) const billArr = _.reduce( (acc, value) => { const unit = movedBills[value] - return _.concat(acc, _.times(() => ({ - id: uuid.v4(), - fiat: unit.denomination, - fiat_code: fiatCode, - device_id: deviceId - // TODO: Uncomment this if we decide to keep track of bills across multiple operations. For now, we'll just create the emptying operations for each unit affected, but not relate these events with individual bills and just use the field for the cashbox batch event - // cash_unit_operation_id: _.find(it => it.operation_type === `cash-${_.replace(/(cassette|recycler)/g, '$1-')(value)}-empty`, operationsToCreate).id - }), Math.abs(unit.delta))) + return _.concat( + acc, + _.times( + () => ({ + id: uuid.v4(), + fiat: unit.denomination, + fiat_code: fiatCode, + device_id: deviceId, + // TODO: Uncomment this if we decide to keep track of bills across multiple operations. For now, we'll just create the emptying operations for each unit affected, but not relate these events with individual bills and just use the field for the cashbox batch event + // cash_unit_operation_id: _.find(it => it.operation_type === `cash-${_.replace(/(cassette|recycler)/g, '$1-')(value)}-empty`, operationsToCreate).id + }), + Math.abs(unit.delta), + ), + ) }, [], - _.keys(_.omit(['cashbox'], movedBills)) + _.keys(_.omit(['cashbox'], movedBills)), ) // This occurs when an empty unit is called when the units are already empty, hence, no bills moved around @@ -249,59 +347,76 @@ function emptyMachineUnits ({ deviceId, newUnits, fiatCode }) { return db.tx(t => { const q1Cols = ['id', 'device_id', 'operation_type'] - const q1= t.none(pgp.helpers.insert(operationsToCreate, q1Cols, 'cash_unit_operation')) + const q1 = t.none( + pgp.helpers.insert(operationsToCreate, q1Cols, 'cash_unit_operation'), + ) const q2Cols = ['id', 'fiat', 'fiat_code', 'device_id'] - const q2 = t.none(pgp.helpers.insert(billArr, q2Cols, 'empty_unit_bills')) - const q3 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11`, [ - _.defaultTo(machine.cashUnits.cassette1, newUnits.cassette1), - _.defaultTo(machine.cashUnits.cassette2, newUnits.cassette2), - _.defaultTo(machine.cashUnits.cassette3, newUnits.cassette3), - _.defaultTo(machine.cashUnits.cassette4, newUnits.cassette4), - _.defaultTo(machine.cashUnits.recycler1, newUnits.recycler1), - _.defaultTo(machine.cashUnits.recycler2, newUnits.recycler2), - _.defaultTo(machine.cashUnits.recycler3, newUnits.recycler3), - _.defaultTo(machine.cashUnits.recycler4, newUnits.recycler4), - _.defaultTo(machine.cashUnits.recycler5, newUnits.recycler5), - _.defaultTo(machine.cashUnits.recycler6, newUnits.recycler6), - deviceId - ]) + const q2 = t.none( + pgp.helpers.insert(billArr, q2Cols, 'empty_unit_bills'), + ) + const q3 = t.none( + `UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11`, + [ + _.defaultTo(machine.cashUnits.cassette1, newUnits.cassette1), + _.defaultTo(machine.cashUnits.cassette2, newUnits.cassette2), + _.defaultTo(machine.cashUnits.cassette3, newUnits.cassette3), + _.defaultTo(machine.cashUnits.cassette4, newUnits.cassette4), + _.defaultTo(machine.cashUnits.recycler1, newUnits.recycler1), + _.defaultTo(machine.cashUnits.recycler2, newUnits.recycler2), + _.defaultTo(machine.cashUnits.recycler3, newUnits.recycler3), + _.defaultTo(machine.cashUnits.recycler4, newUnits.recycler4), + _.defaultTo(machine.cashUnits.recycler5, newUnits.recycler5), + _.defaultTo(machine.cashUnits.recycler6, newUnits.recycler6), + deviceId, + ], + ) return t.batch([q1, q2, q3]) }) }) } -function refillMachineUnits ({ deviceId, newUnits }) { - return getMachine(deviceId) - .then(machine => { - const movedBills = _.reduce( - (acc, value) => ({ - ...acc, - [value]: { - operationName: `cash-${_.replace(/(recycler)/g, '$1-')(value)}-refill`, - delta: newUnits[value] - machine.cashUnits[value] - } - }), - {}, - _.keys(newUnits) +function refillMachineUnits({ deviceId, newUnits }) { + return getMachine(deviceId).then(machine => { + const movedBills = _.reduce( + (acc, value) => ({ + ...acc, + [value]: { + operationName: `cash-${_.replace(/(recycler)/g, '$1-')(value)}-refill`, + delta: newUnits[value] - machine.cashUnits[value], + }, + }), + {}, + _.keys(newUnits), + ) + + const operationNames = _.mapValues(it => it.operationName)( + _.filter(it => Math.abs(it.delta) > 0)( + _.omit( + ['cassette1', 'cassette2', 'cassette3', 'cassette4'], + movedBills, + ), + ), + ) + const operationsToCreate = _.map(it => ({ + id: uuid.v4(), + device_id: deviceId, + operation_type: it, + }))(operationNames) + + // This occurs when a refill unit is called when the loading boxes are empty, hence, no bills moved around + if (_.isEmpty(operationsToCreate)) { + return Promise.resolve() + } + + return db.tx(t => { + const q1Cols = ['id', 'device_id', 'operation_type'] + const q1 = t.none( + pgp.helpers.insert(operationsToCreate, q1Cols, 'cash_unit_operation'), ) - - const operationNames = _.mapValues(it => it.operationName)(_.filter(it => Math.abs(it.delta) > 0)(_.omit(['cassette1', 'cassette2', 'cassette3', 'cassette4'], movedBills))) - const operationsToCreate = _.map(it => ({ - id: uuid.v4(), - device_id: deviceId, - operation_type: it - }))(operationNames) - - // This occurs when a refill unit is called when the loading boxes are empty, hence, no bills moved around - if (_.isEmpty(operationsToCreate)) { - return Promise.resolve() - } - - return db.tx(t => { - const q1Cols = ['id', 'device_id', 'operation_type'] - const q1= t.none(pgp.helpers.insert(operationsToCreate, q1Cols, 'cash_unit_operation')) - const q2 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11`, [ + const q2 = t.none( + `UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11`, + [ _.defaultTo(machine.cashUnits.cassette1, newUnits.cassette1), _.defaultTo(machine.cashUnits.cassette2, newUnits.cassette2), _.defaultTo(machine.cashUnits.cassette3, newUnits.cassette3), @@ -312,64 +427,70 @@ function refillMachineUnits ({ deviceId, newUnits }) { _.defaultTo(machine.cashUnits.recycler4, newUnits.recycler4), _.defaultTo(machine.cashUnits.recycler5, newUnits.recycler5), _.defaultTo(machine.cashUnits.recycler6, newUnits.recycler6), - deviceId - ]) - - return t.batch([q1, q2]) - }) + deviceId, + ], + ) + + return t.batch([q1, q2]) }) + }) } -function unpair (rec) { +function unpair(rec) { return pairing.unpair(rec.deviceId) } -function reboot (rec) { - return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify( - { +function reboot(rec) { + return db.none('NOTIFY $1:name, $2', [ + 'machineAction', + JSON.stringify({ action: 'reboot', - value: _.pick(['deviceId', 'operatorId', 'action'], rec) - } - )]) + value: _.pick(['deviceId', 'operatorId', 'action'], rec), + }), + ]) } -function shutdown (rec) { - return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify( - { +function shutdown(rec) { + return db.none('NOTIFY $1:name, $2', [ + 'machineAction', + JSON.stringify({ action: 'shutdown', - value: _.pick(['deviceId', 'operatorId', 'action'], rec) - } - )]) + value: _.pick(['deviceId', 'operatorId', 'action'], rec), + }), + ]) } -function restartServices (rec) { - return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify( - { +function restartServices(rec) { + return db.none('NOTIFY $1:name, $2', [ + 'machineAction', + JSON.stringify({ action: 'restartServices', - value: _.pick(['deviceId', 'operatorId', 'action'], rec) - } - )]) + value: _.pick(['deviceId', 'operatorId', 'action'], rec), + }), + ]) } -function emptyUnit (rec) { - return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify( - { +function emptyUnit(rec) { + return db.none('NOTIFY $1:name, $2', [ + 'machineAction', + JSON.stringify({ action: 'emptyUnit', - value: _.pick(['deviceId', 'operatorId', 'action'], rec) - } - )]) + value: _.pick(['deviceId', 'operatorId', 'action'], rec), + }), + ]) } -function refillUnit (rec) { - return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify( - { +function refillUnit(rec) { + return db.none('NOTIFY $1:name, $2', [ + 'machineAction', + JSON.stringify({ action: 'refillUnit', - value: _.pick(['deviceId', 'operatorId', 'action'], rec) - } - )]) + value: _.pick(['deviceId', 'operatorId', 'action'], rec), + }), + ]) } -function diagnostics (rec) { +function diagnostics(rec) { const directory = `${OPERATOR_DATA_DIR}/diagnostics/${rec.deviceId}/` const sql = `UPDATE devices SET diagnostics_timestamp = NULL, @@ -391,48 +512,65 @@ function diagnostics (rec) { return Promise.all(removeFiles) .then(() => db.none(sql, [rec.deviceId])) - .then(() => db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify( - { - action: 'diagnostics', - value: _.pick(['deviceId', 'operatorId', 'action'], rec) - } - )])) + .then(() => + db.none('NOTIFY $1:name, $2', [ + 'machineAction', + JSON.stringify({ + action: 'diagnostics', + value: _.pick(['deviceId', 'operatorId', 'action'], rec), + }), + ]), + ) } -function setMachine (rec, operatorId) { +function setMachine(rec, operatorId) { rec.operatorId = operatorId switch (rec.action) { - case 'rename': return renameMachine(rec) - case 'resetCashOutBills': return resetCashOutBills(rec) - case 'setCassetteBills': return setCassetteBills(rec) - case 'unpair': return unpair(rec) - case 'reboot': return reboot(rec) - case 'shutdown': return shutdown(rec) - case 'restartServices': return restartServices(rec) - case 'emptyUnit': return emptyUnit(rec) - case 'refillUnit': return refillUnit(rec) - case 'diagnostics': return diagnostics(rec) - default: throw new Error('No such action: ' + rec.action) + case 'rename': + return renameMachine(rec) + case 'resetCashOutBills': + return resetCashOutBills(rec) + case 'setCassetteBills': + return setCassetteBills(rec) + case 'unpair': + return unpair(rec) + case 'reboot': + return reboot(rec) + case 'shutdown': + return shutdown(rec) + case 'restartServices': + return restartServices(rec) + case 'emptyUnit': + return emptyUnit(rec) + case 'refillUnit': + return refillUnit(rec) + case 'diagnostics': + return diagnostics(rec) + default: + throw new Error('No such action: ' + rec.action) } } -function updateNetworkPerformance (deviceId, data) { +function updateNetworkPerformance(deviceId, data) { if (_.isEmpty(data)) return Promise.resolve(true) const downloadSpeed = _.head(data) const dbData = { device_id: deviceId, download_speed: downloadSpeed.speed, - created: new Date() + created: new Date(), } - const cs = new pgp.helpers.ColumnSet(['device_id', 'download_speed', 'created'], - { table: 'machine_network_performance' }) - const onConflict = ' ON CONFLICT (device_id) DO UPDATE SET ' + + const cs = new pgp.helpers.ColumnSet( + ['device_id', 'download_speed', 'created'], + { table: 'machine_network_performance' }, + ) + const onConflict = + ' ON CONFLICT (device_id) DO UPDATE SET ' + cs.assignColumns({ from: 'EXCLUDED', skip: ['device_id'] }) const upsert = pgp.helpers.insert(dbData, cs) + onConflict return db.none(upsert) } -function updateNetworkHeartbeat (deviceId, data) { +function updateNetworkHeartbeat(deviceId, data) { if (_.isEmpty(data)) return Promise.resolve(true) const avgResponseTime = _.meanBy(e => _.toNumber(e.averageResponseTime), data) const avgPacketLoss = _.meanBy(e => _.toNumber(e.packetLoss), data) @@ -440,41 +578,47 @@ function updateNetworkHeartbeat (deviceId, data) { id: uuid.v4(), device_id: deviceId, average_response_time: avgResponseTime, - average_packet_loss: avgPacketLoss + average_packet_loss: avgPacketLoss, } const sql = pgp.helpers.insert(dbData, null, 'machine_network_heartbeat') return db.none(sql) } -function getNetworkPerformance () { +function getNetworkPerformance() { const sql = `SELECT device_id, download_speed FROM machine_network_performance` - return db.manyOrNone(sql) - .then(res => _.map(_.mapKeys(_.camelCase))(res)) + return db.manyOrNone(sql).then(res => _.map(_.mapKeys(_.camelCase))(res)) } -function getNetworkHeartbeat () { +function getNetworkHeartbeat() { const sql = `SELECT AVG(average_response_time) AS response_time, AVG(average_packet_loss) AS packet_loss, device_id FROM machine_network_heartbeat GROUP BY device_id` - return db.manyOrNone(sql) - .then(res => _.map(_.mapKeys(_.camelCase))(res)) + return db.manyOrNone(sql).then(res => _.map(_.mapKeys(_.camelCase))(res)) } -function getNetworkPerformanceByDevice (deviceId) { +function getNetworkPerformanceByDevice(deviceId) { const sql = `SELECT device_id, download_speed FROM machine_network_performance WHERE device_id = $1` - return db.manyOrNone(sql, [deviceId]) - .then(res => _.mapKeys(_.camelCase, _.find(it => it.device_id === deviceId, res))) + return db.manyOrNone(sql, [deviceId]).then(res => + _.mapKeys( + _.camelCase, + _.find(it => it.device_id === deviceId, res), + ), + ) } -function getNetworkHeartbeatByDevice (deviceId) { +function getNetworkHeartbeatByDevice(deviceId) { const sql = `SELECT AVG(average_response_time) AS response_time, AVG(average_packet_loss) AS packet_loss, device_id FROM machine_network_heartbeat WHERE device_id = $1 GROUP BY device_id` - return db.manyOrNone(sql, [deviceId]) - .then(res => _.mapKeys(_.camelCase, _.find(it => it.device_id === deviceId, res))) + return db.manyOrNone(sql, [deviceId]).then(res => + _.mapKeys( + _.camelCase, + _.find(it => it.device_id === deviceId, res), + ), + ) } -function updateDiagnostics (deviceId, images) { +function updateDiagnostics(deviceId, images) { const sql = `UPDATE devices SET diagnostics_timestamp = NOW(), diagnostics_scan_updated_at = CASE WHEN $2 THEN NOW() ELSE diagnostics_scan_updated_at END, @@ -484,19 +628,25 @@ function updateDiagnostics (deviceId, images) { const directory = `${OPERATOR_DATA_DIR}/diagnostics/${deviceId}/` const { scan, front } = images - return updatePhotos(directory, [['scan.jpg', scan], ['front.jpg', front]]) + return updatePhotos(directory, [ + ['scan.jpg', scan], + ['front.jpg', front], + ]) .then(() => db.none(sql, [deviceId, !!scan, !!front])) .catch(err => logger.error('while running machine diagnostics: ', err)) } const updateFailedQRScans = (deviceId, frames) => { - const timestamp = (new Date()).toISOString() + const timestamp = new Date().toISOString() const directory = `${OPERATOR_DATA_DIR}/failedQRScans/${deviceId}/` - const filenames = _.map(no => `${timestamp}-${no}.jpg`, _.range(0, _.size(frames))) + const filenames = _.map( + no => `${timestamp}-${no}.jpg`, + _.range(0, _.size(frames)), + ) return updatePhotos(directory, _.zip(filenames, frames)) } -function createPhoto (name, data, dir) { +function createPhoto(name, data, dir) { if (!data) { logger.error(`Diagnostics error: No data to save for ${name} photo`) return Promise.resolve() @@ -507,12 +657,12 @@ function createPhoto (name, data, dir) { return fsPromises.writeFile(filename, decodedImageData) } -function updatePhotos (dir, photoPairs) { +function updatePhotos(dir, photoPairs) { const dirname = path.join(dir) _.attempt(() => makeDir.sync(dirname)) - return Promise.all(photoPairs.map( - ([filename, data]) => createPhoto(filename, data, dirname) - )) + return Promise.all( + photoPairs.map(([filename, data]) => createPhoto(filename, data, dirname)), + ) } module.exports = { @@ -530,5 +680,5 @@ module.exports = { emptyMachineUnits, refillMachineUnits, updateDiagnostics, - updateFailedQRScans + updateFailedQRScans, } diff --git a/packages/server/lib/middlewares/authorize.js b/packages/server/lib/middlewares/authorize.js index a76a7438..1a4d24df 100644 --- a/packages/server/lib/middlewares/authorize.js +++ b/packages/server/lib/middlewares/authorize.js @@ -2,7 +2,8 @@ const pairing = require('../pairing') const logger = require('../logger') const authorize = function (req, res, next) { - return pairing.isPaired(req.deviceId) + return pairing + .isPaired(req.deviceId) .then(deviceName => { if (deviceName) { req.deviceName = deviceName diff --git a/packages/server/lib/middlewares/ca.js b/packages/server/lib/middlewares/ca.js index 04b3dbc0..a0f65768 100644 --- a/packages/server/lib/middlewares/ca.js +++ b/packages/server/lib/middlewares/ca.js @@ -1,10 +1,11 @@ const pairing = require('../pairing') const logger = require('../logger') -function ca (req, res) { +function ca(req, res) { const token = req.query.token - return pairing.authorizeCaDownload(token) + return pairing + .authorizeCaDownload(token) .then(ca => res.json({ ca })) .catch(error => { logger.error(error.message) diff --git a/packages/server/lib/middlewares/errorHandler.js b/packages/server/lib/middlewares/errorHandler.js index ac695263..1c5b742c 100644 --- a/packages/server/lib/middlewares/errorHandler.js +++ b/packages/server/lib/middlewares/errorHandler.js @@ -1,9 +1,7 @@ const logger = require('../logger') -function errorHandler (err, req, res, next) { - const statusCode = err.name === 'HTTPError' - ? err.code || 500 - : 500 +function errorHandler(err, req, res) { + const statusCode = err.name === 'HTTPError' ? err.code || 500 : 500 const json = { error: err.message } diff --git a/packages/server/lib/middlewares/filterOldRequests.js b/packages/server/lib/middlewares/filterOldRequests.js index 601207b6..b4f041ce 100644 --- a/packages/server/lib/middlewares/filterOldRequests.js +++ b/packages/server/lib/middlewares/filterOldRequests.js @@ -5,19 +5,23 @@ const CLOCK_SKEW = 60 * 1000 const REQUEST_TTL = 3 * 60 * 1000 const THROTTLE_CLOCK_SKEW = 60 * 1000 -function filterOldRequests (req, res, next) { +function filterOldRequests(req, res, next) { const deviceTime = req.deviceTime const deviceId = req.deviceId const timestamp = Date.now() const delta = timestamp - Date.parse(deviceTime) - const shouldTrigger = !state.canLogClockSkewMap[deviceId] || + const shouldTrigger = + !state.canLogClockSkewMap[deviceId] || timestamp - state.canLogClockSkewMap[deviceId] >= THROTTLE_CLOCK_SKEW if (delta > CLOCK_SKEW && shouldTrigger) { state.canLogClockSkewMap[deviceId] = timestamp - logger.error('Clock skew with lamassu-machine[%s] too high [%ss], adjust lamassu-machine clock', - req.deviceName, (delta / 1000).toFixed(2)) + logger.error( + 'Clock skew with lamassu-machine[%s] too high [%ss], adjust lamassu-machine clock', + req.deviceName, + (delta / 1000).toFixed(2), + ) } if (delta > REQUEST_TTL) return res.status(408).json({ error: 'stale' }) diff --git a/packages/server/lib/middlewares/operatorId.js b/packages/server/lib/middlewares/operatorId.js index d11d22f6..057046ea 100644 --- a/packages/server/lib/middlewares/operatorId.js +++ b/packages/server/lib/middlewares/operatorId.js @@ -1,6 +1,6 @@ const { getOperatorId } = require('../operator') -function findOperatorId (req, res, next) { +function findOperatorId(req, res, next) { return getOperatorId('middleware') .then(operatorId => { res.locals.operatorId = operatorId diff --git a/packages/server/lib/middlewares/populateDeviceId.js b/packages/server/lib/middlewares/populateDeviceId.js index 884cc436..9c139e4e 100644 --- a/packages/server/lib/middlewares/populateDeviceId.js +++ b/packages/server/lib/middlewares/populateDeviceId.js @@ -1,6 +1,6 @@ const crypto = require('crypto') -function sha256 (buf) { +function sha256(buf) { if (!buf) return null const hash = crypto.createHash('sha256') @@ -9,10 +9,13 @@ function sha256 (buf) { } const populateDeviceId = function (req, res, next) { - const peerCert = req.socket.getPeerCertificate ? req.socket.getPeerCertificate() : null + const peerCert = req.socket.getPeerCertificate + ? req.socket.getPeerCertificate() + : null const deviceId = peerCert?.raw ? sha256(peerCert.raw) : null - - if (!deviceId) return res.status(500).json({ error: 'Unable to find certificate' }) + + if (!deviceId) + return res.status(500).json({ error: 'Unable to find certificate' }) req.deviceId = deviceId req.deviceTime = req.get('date') diff --git a/packages/server/lib/middlewares/populateSettings.js b/packages/server/lib/middlewares/populateSettings.js index 18add0d1..6a34ca3d 100644 --- a/packages/server/lib/middlewares/populateSettings.js +++ b/packages/server/lib/middlewares/populateSettings.js @@ -3,57 +3,74 @@ const state = require('./state') const newSettingsLoader = require('../new-settings-loader') const logger = require('../logger') -db.connect({ direct: true }).then(sco => { - sco.client.on('notification', data => { - const parsedData = JSON.parse(data.payload) - return reload(parsedData.operatorId) +db.connect({ direct: true }) + .then(sco => { + sco.client.on('notification', data => { + const parsedData = JSON.parse(data.payload) + return reload(parsedData.operatorId) + }) + return sco.none('LISTEN $1:name', 'reload') }) - return sco.none('LISTEN $1:name', 'reload') -}).catch(console.error) + .catch(console.error) -db.connect({ direct: true }).then(sco => { - sco.client.on('notification', data => { - const parsedData = JSON.parse(data.payload) - return machineAction(parsedData.action, parsedData.value) +db.connect({ direct: true }) + .then(sco => { + sco.client.on('notification', data => { + const parsedData = JSON.parse(data.payload) + return machineAction(parsedData.action, parsedData.value) + }) + return sco.none('LISTEN $1:name', 'machineAction') }) - return sco.none('LISTEN $1:name', 'machineAction') -}).catch(console.error) + .catch(console.error) -function machineAction (type, value) { +function machineAction(type, value) { const deviceId = value.deviceId const operatorId = value.operatorId const pid = state.pids?.[operatorId]?.[deviceId]?.pid switch (type) { case 'reboot': - logger.debug(`Rebooting machine '${deviceId}' from operator ${operatorId}`) + logger.debug( + `Rebooting machine '${deviceId}' from operator ${operatorId}`, + ) state.reboots[operatorId] = { [deviceId]: pid } break case 'shutdown': - logger.debug(`Shutting down machine '${deviceId}' from operator ${operatorId}`) + logger.debug( + `Shutting down machine '${deviceId}' from operator ${operatorId}`, + ) state.shutdowns[operatorId] = { [deviceId]: pid } break case 'restartServices': - logger.debug(`Restarting services of machine '${deviceId}' from operator ${operatorId}`) + logger.debug( + `Restarting services of machine '${deviceId}' from operator ${operatorId}`, + ) state.restartServicesMap[operatorId] = { [deviceId]: pid } break case 'emptyUnit': - logger.debug(`Emptying units from machine '${deviceId}' from operator ${operatorId}`) + logger.debug( + `Emptying units from machine '${deviceId}' from operator ${operatorId}`, + ) state.emptyUnit[operatorId] = { [deviceId]: pid } break case 'refillUnit': - logger.debug(`Refilling recyclers from machine '${deviceId}' from operator ${operatorId}`) + logger.debug( + `Refilling recyclers from machine '${deviceId}' from operator ${operatorId}`, + ) state.refillUnit[operatorId] = { [deviceId]: pid } break case 'diagnostics': - logger.debug(`Running diagnostics on machine '${deviceId}' from operator ${operatorId}`) + logger.debug( + `Running diagnostics on machine '${deviceId}' from operator ${operatorId}`, + ) state.diagnostics[operatorId] = { [deviceId]: pid } + break default: break } } -function reload (operatorId) { +function reload(operatorId) { state.needsSettingsReload[operatorId] = true } @@ -73,11 +90,14 @@ const populateSettings = function (req, res, next) { // 4. There's no cached config, cache and send the latest config if (versionId) { - const cachedVersionedSettings = settingsCache.get(`${operatorId}-v${versionId}`) + const cachedVersionedSettings = settingsCache.get( + `${operatorId}-v${versionId}`, + ) if (!cachedVersionedSettings) { logger.debug('Fetching a specific config version cached value') - return newSettingsLoader.load(versionId) + return newSettingsLoader + .load(versionId) .then(settings => { settingsCache.set(`${operatorId}-v${versionId}`, settings) req.settings = settings @@ -94,16 +114,22 @@ const populateSettings = function (req, res, next) { const operatorSettings = settingsCache.get(`${operatorId}-latest`) if (!!needsSettingsReload[operatorId] || !operatorSettings) { - !!needsSettingsReload[operatorId] - ? logger.debug('Fetching and caching a new latest config value, as a reload was requested') - : logger.debug('Fetching the latest config version because there\'s no cached value') + needsSettingsReload[operatorId] + ? logger.debug( + 'Fetching and caching a new latest config value, as a reload was requested', + ) + : logger.debug( + "Fetching the latest config version because there's no cached value", + ) - return newSettingsLoader.loadLatest() + return newSettingsLoader + .loadLatest() .then(settings => { const versionId = settings.version settingsCache.set(`${operatorId}-latest`, settings) settingsCache.set(`${operatorId}-v${versionId}`, settings) - if (!!needsSettingsReload[operatorId]) delete needsSettingsReload[operatorId] + if (needsSettingsReload[operatorId]) + delete needsSettingsReload[operatorId] req.settings = settings }) .then(() => next()) diff --git a/packages/server/lib/middlewares/rejectIncompatbleMachines.js b/packages/server/lib/middlewares/rejectIncompatbleMachines.js index 48ecef6f..0086e464 100644 --- a/packages/server/lib/middlewares/rejectIncompatbleMachines.js +++ b/packages/server/lib/middlewares/rejectIncompatbleMachines.js @@ -12,20 +12,24 @@ const rejectIncompatibleMachines = function (req, res, next) { const machineMajor = semver.major(machineVersion) if (serverMajor - machineMajor > 1) { - logger.error(`Machine version too old: ${machineVersion} deviceId: ${deviceId}`) + logger.error( + `Machine version too old: ${machineVersion} deviceId: ${deviceId}`, + ) return res.status(400).json({ - error: 'Machine version too old' + error: 'Machine version too old', }) } if (serverMajor < machineMajor) { - logger.error(`Machine version too new: ${machineVersion} deviceId: ${deviceId}`) + logger.error( + `Machine version too new: ${machineVersion} deviceId: ${deviceId}`, + ) return res.status(400).json({ - error: 'Machine version too new' + error: 'Machine version too new', }) } next() } -module.exports = rejectIncompatibleMachines \ No newline at end of file +module.exports = rejectIncompatibleMachines diff --git a/packages/server/lib/middlewares/state.js b/packages/server/lib/middlewares/state.js index ee5da8e6..b6419d0f 100644 --- a/packages/server/lib/middlewares/state.js +++ b/packages/server/lib/middlewares/state.js @@ -7,7 +7,7 @@ module.exports = (function () { needsSettingsReload: {}, settingsCache: new NodeCache({ stdTTL: SETTINGS_CACHE_REFRESH, - checkperiod: SETTINGS_CACHE_REFRESH // Clear cache every hour + checkperiod: SETTINGS_CACHE_REFRESH, // Clear cache every hour }), canLogClockSkewMap: {}, canGetLastSeenMap: {}, @@ -18,6 +18,6 @@ module.exports = (function () { emptyUnit: {}, refillUnit: {}, diagnostics: {}, - mnemonic: null + mnemonic: null, } -}()) +})() diff --git a/packages/server/lib/migrate.js b/packages/server/lib/migrate.js index 610f926e..0313307d 100644 --- a/packages/server/lib/migrate.js +++ b/packages/server/lib/migrate.js @@ -7,11 +7,11 @@ const migrateDir = path.resolve(__dirname, '..', 'migrations') const migrateOpts = { migrationsDirectory: migrateDir, stateStore: new DbMigrateStore(), - filterFunction: it => it.match(/^\d+.*\.js$/) + filterFunction: it => it.match(/^\d+.*\.js$/), } module.exports = { run } -function run () { +function run() { return new Promise((resolve, reject) => { migrate.load(migrateOpts, (err, set) => { if (err) return reject(err) diff --git a/packages/server/lib/mnemonic-helpers.js b/packages/server/lib/mnemonic-helpers.js index ad4e3675..1a308235 100644 --- a/packages/server/lib/mnemonic-helpers.js +++ b/packages/server/lib/mnemonic-helpers.js @@ -1,7 +1,7 @@ const bip39 = require('bip39') const os = require('os') -function fromSeed (seed) { +function fromSeed(seed) { const words = bip39.entropyToMnemonic(seed).split(' ') let mnemonic = '' @@ -11,7 +11,7 @@ function fromSeed (seed) { return mnemonic } -function toEntropyBuffer (mnemonic) { +function toEntropyBuffer(mnemonic) { const hex = bip39.mnemonicToEntropy(mnemonic.split('\n').join(' ').trim()) return Buffer.from(hex.trim(), 'hex') } diff --git a/packages/server/lib/new-admin/admin-server.js b/packages/server/lib/new-admin/admin-server.js index 3b759bcf..03153dc4 100644 --- a/packages/server/lib/new-admin/admin-server.js +++ b/packages/server/lib/new-admin/admin-server.js @@ -9,8 +9,12 @@ const nocache = require('nocache') const cookieParser = require('cookie-parser') const { ApolloServer } = require('@apollo/server') const { expressMiddleware } = require('@apollo/server/express4') -const { ApolloServerPluginLandingPageDisabled } = require('@apollo/server/plugin/disabled') -const { ApolloServerPluginLandingPageLocalDefault } = require('@apollo/server/plugin/landingPage/default') +const { + ApolloServerPluginLandingPageDisabled, +} = require('@apollo/server/plugin/disabled') +const { + ApolloServerPluginLandingPageLocalDefault, +} = require('@apollo/server/plugin/landingPage/default') const { mergeResolvers } = require('@graphql-tools/merge') const { makeExecutableSchema } = require('@graphql-tools/schema') @@ -23,7 +27,11 @@ const { authDirectiveTransformer } = require('./graphql/directives') const { typeDefs, resolvers } = require('./graphql/schema') const findOperatorId = require('../middlewares/operatorId') const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants') -const { session, cleanUserSessions, buildApolloContext } = require('./middlewares') +const { + session, + cleanUserSessions, + buildApolloContext, +} = require('./middlewares') const devMode = require('minimist')(process.argv.slice(2)).dev @@ -55,8 +63,12 @@ const loadRoutes = async () => { app.use(session) // Dynamic import for graphql-upload since it's not a CommonJS module - const { default: graphqlUploadExpress } = await import('graphql-upload/graphqlUploadExpress.mjs') - const { default: GraphQLUpload } = await import('graphql-upload/GraphQLUpload.mjs') + const { default: graphqlUploadExpress } = await import( + 'graphql-upload/graphqlUploadExpress.mjs' + ) + const { default: GraphQLUpload } = await import( + 'graphql-upload/GraphQLUpload.mjs' + ) app.use(graphqlUploadExpress()) @@ -75,29 +87,33 @@ const loadRoutes = async () => { return formattedError }, plugins: [ - devMode - ? ApolloServerPluginLandingPageLocalDefault() - : ApolloServerPluginLandingPageDisabled() - ] + devMode + ? ApolloServerPluginLandingPageLocalDefault() + : ApolloServerPluginLandingPageDisabled(), + ], }) - await apolloServer.start(); + await apolloServer.start() app.use( '/graphql', express.json(), expressMiddleware(apolloServer, { - context: async ({ req, res }) => buildApolloContext({ req, res }) - }) - ); - + context: async ({ req, res }) => buildApolloContext({ req, res }), + }), + ) app.use('/id-card-photo', serveStatic(ID_PHOTO_CARD_DIR, { index: false })) - app.use('/front-camera-photo', serveStatic(FRONT_CAMERA_DIR, { index: false })) + app.use( + '/front-camera-photo', + serveStatic(FRONT_CAMERA_DIR, { index: false }), + ) app.use('/operator-data', serveStatic(OPERATOR_DATA_DIR, { index: false })) // Everything not on graphql or api/register is redirected to the front-end - app.get('*', (req, res) => res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html'))) + app.get('*', (req, res) => + res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html')), + ) return app } @@ -105,10 +121,10 @@ const loadRoutes = async () => { const certOptions = { key: fs.readFileSync(KEY_PATH), cert: fs.readFileSync(CERT_PATH), - ca: fs.readFileSync(CA_PATH) + ca: fs.readFileSync(CA_PATH), } -async function run () { +async function run() { const app = await loadRoutes() const serverPort = devMode ? 8070 : 443 diff --git a/packages/server/lib/new-admin/config/accounts.js b/packages/server/lib/new-admin/config/accounts.js index aed637d9..87a3e35a 100644 --- a/packages/server/lib/new-admin/config/accounts.js +++ b/packages/server/lib/new-admin/config/accounts.js @@ -3,8 +3,10 @@ const _ = require('lodash/fp') const { ALL } = require('../../plugins/common/ccxt') -const { BTC, BCH, DASH, ETH, LTC, USDT, ZEC, XMR, LN, TRX, USDT_TRON, USDC } = COINS -const { bitpay, itbit, bitstamp, kraken, binanceus, cex, binance, bitfinex } = ALL +const { BTC, BCH, DASH, ETH, LTC, USDT, ZEC, XMR, LN, TRX, USDT_TRON, USDC } = + COINS +const { bitpay, itbit, bitstamp, kraken, binanceus, cex, binance, bitfinex } = + ALL const TICKER = 'ticker' const WALLET = 'wallet' @@ -18,39 +20,142 @@ const WALLET_SCORING = 'wallet_scoring' const COMPLIANCE = 'compliance' const ALL_ACCOUNTS = [ - { code: 'bitfinex', display: 'Bitfinex', class: TICKER, cryptos: bitfinex.CRYPTO }, - { code: 'bitfinex', display: 'Bitfinex', class: EXCHANGE, cryptos: bitfinex.CRYPTO }, - { code: 'binance', display: 'Binance', class: TICKER, cryptos: binance.CRYPTO }, - { code: 'binanceus', display: 'Binance.us', class: TICKER, cryptos: binanceus.CRYPTO }, + { + code: 'bitfinex', + display: 'Bitfinex', + class: TICKER, + cryptos: bitfinex.CRYPTO, + }, + { + code: 'bitfinex', + display: 'Bitfinex', + class: EXCHANGE, + cryptos: bitfinex.CRYPTO, + }, + { + code: 'binance', + display: 'Binance', + class: TICKER, + cryptos: binance.CRYPTO, + }, + { + code: 'binanceus', + display: 'Binance.us', + class: TICKER, + cryptos: binanceus.CRYPTO, + }, { code: 'cex', display: 'CEX.IO', class: TICKER, cryptos: cex.CRYPTO }, { code: 'bitpay', display: 'Bitpay', class: TICKER, cryptos: bitpay.CRYPTO }, { code: 'kraken', display: 'Kraken', class: TICKER, cryptos: kraken.CRYPTO }, - { code: 'bitstamp', display: 'Bitstamp', class: TICKER, cryptos: bitstamp.CRYPTO }, + { + code: 'bitstamp', + display: 'Bitstamp', + class: TICKER, + cryptos: bitstamp.CRYPTO, + }, { code: 'itbit', display: 'itBit', class: TICKER, cryptos: itbit.CRYPTO }, - { code: 'mock-ticker', display: 'Mock (Caution!)', class: TICKER, cryptos: ALL_CRYPTOS, dev: true }, + { + code: 'mock-ticker', + display: 'Mock (Caution!)', + class: TICKER, + cryptos: ALL_CRYPTOS, + dev: true, + }, { code: 'bitcoind', display: 'bitcoind', class: WALLET, cryptos: [BTC] }, - { code: 'no-layer2', display: 'No Layer 2', class: LAYER_2, cryptos: ALL_CRYPTOS }, - { code: 'infura', display: 'Infura/Alchemy', class: WALLET, cryptos: [ETH, USDT, USDC] }, - { code: 'trongrid', display: 'Trongrid', class: WALLET, cryptos: [TRX, USDT_TRON] }, - { code: 'geth', display: 'geth (deprecated)', class: WALLET, cryptos: [ETH, USDT, USDC] }, + { + code: 'no-layer2', + display: 'No Layer 2', + class: LAYER_2, + cryptos: ALL_CRYPTOS, + }, + { + code: 'infura', + display: 'Infura/Alchemy', + class: WALLET, + cryptos: [ETH, USDT, USDC], + }, + { + code: 'trongrid', + display: 'Trongrid', + class: WALLET, + cryptos: [TRX, USDT_TRON], + }, + { + code: 'geth', + display: 'geth (deprecated)', + class: WALLET, + cryptos: [ETH, USDT, USDC], + }, { code: 'zcashd', display: 'zcashd', class: WALLET, cryptos: [ZEC] }, { code: 'litecoind', display: 'litecoind', class: WALLET, cryptos: [LTC] }, { code: 'dashd', display: 'dashd', class: WALLET, cryptos: [DASH] }, { code: 'monerod', display: 'monerod', class: WALLET, cryptos: [XMR] }, - { code: 'bitcoincashd', display: 'bitcoincashd', class: WALLET, cryptos: [BCH] }, - { code: 'bitgo', display: 'BitGo', class: WALLET, cryptos: [BTC, ZEC, LTC, BCH, DASH] }, + { + code: 'bitcoincashd', + display: 'bitcoincashd', + class: WALLET, + cryptos: [BCH], + }, + { + code: 'bitgo', + display: 'BitGo', + class: WALLET, + cryptos: [BTC, ZEC, LTC, BCH, DASH], + }, { code: 'galoy', display: 'Galoy', class: WALLET, cryptos: [LN] }, - { code: 'bitstamp', display: 'Bitstamp', class: EXCHANGE, cryptos: bitstamp.CRYPTO }, + { + code: 'bitstamp', + display: 'Bitstamp', + class: EXCHANGE, + cryptos: bitstamp.CRYPTO, + }, { code: 'itbit', display: 'itBit', class: EXCHANGE, cryptos: itbit.CRYPTO }, - { code: 'kraken', display: 'Kraken', class: EXCHANGE, cryptos: kraken.CRYPTO }, - { code: 'binance', display: 'Binance', class: EXCHANGE, cryptos: binance.CRYPTO }, - { code: 'binanceus', display: 'Binance.us', class: EXCHANGE, cryptos: binanceus.CRYPTO }, + { + code: 'kraken', + display: 'Kraken', + class: EXCHANGE, + cryptos: kraken.CRYPTO, + }, + { + code: 'binance', + display: 'Binance', + class: EXCHANGE, + cryptos: binance.CRYPTO, + }, + { + code: 'binanceus', + display: 'Binance.us', + class: EXCHANGE, + cryptos: binanceus.CRYPTO, + }, { code: 'cex', display: 'CEX.IO', class: EXCHANGE, cryptos: cex.CRYPTO }, - { code: 'mock-wallet', display: 'Mock (Caution!)', class: WALLET, cryptos: ALL_CRYPTOS, dev: true }, - { code: 'no-exchange', display: 'No exchange', class: EXCHANGE, cryptos: ALL_CRYPTOS }, - { code: 'mock-exchange', display: 'Mock exchange', class: EXCHANGE, cryptos: ALL_CRYPTOS, dev: true }, + { + code: 'mock-wallet', + display: 'Mock (Caution!)', + class: WALLET, + cryptos: ALL_CRYPTOS, + dev: true, + }, + { + code: 'no-exchange', + display: 'No exchange', + class: EXCHANGE, + cryptos: ALL_CRYPTOS, + }, + { + code: 'mock-exchange', + display: 'Mock exchange', + class: EXCHANGE, + cryptos: ALL_CRYPTOS, + dev: true, + }, { code: 'mock-sms', display: 'Mock SMS', class: SMS, dev: true }, - { code: 'mock-id-verify', display: 'Mock ID verifier', class: ID_VERIFIER, dev: true }, + { + code: 'mock-id-verify', + display: 'Mock ID verifier', + class: ID_VERIFIER, + dev: true, + }, { code: 'twilio', display: 'Twilio', class: SMS }, { code: 'telnyx', display: 'Telnyx', class: SMS }, { code: 'vonage', display: 'Vonage', class: SMS }, @@ -58,17 +163,51 @@ const ALL_ACCOUNTS = [ { code: 'mailgun', display: 'Mailgun', class: EMAIL }, { code: 'mock-email', display: 'Mock Email', class: EMAIL, dev: true }, { code: 'none', display: 'None', class: ZERO_CONF, cryptos: ALL_CRYPTOS }, - { code: 'blockcypher', display: 'Blockcypher', class: ZERO_CONF, cryptos: [BTC] }, - { code: 'mock-zero-conf', display: 'Mock 0-conf', class: ZERO_CONF, cryptos: ALL_CRYPTOS, dev: true }, - { code: 'scorechain', display: 'Scorechain', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, DASH, USDT, USDC, USDT_TRON, TRX] }, - { code: 'elliptic', display: 'Elliptic', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, USDT, USDC, USDT_TRON, TRX, ZEC] }, - { code: 'mock-scoring', display: 'Mock scoring', class: WALLET_SCORING, cryptos: ALL_CRYPTOS, dev: true }, + { + code: 'blockcypher', + display: 'Blockcypher', + class: ZERO_CONF, + cryptos: [BTC], + }, + { + code: 'mock-zero-conf', + display: 'Mock 0-conf', + class: ZERO_CONF, + cryptos: ALL_CRYPTOS, + dev: true, + }, + { + code: 'scorechain', + display: 'Scorechain', + class: WALLET_SCORING, + cryptos: [BTC, ETH, LTC, BCH, DASH, USDT, USDC, USDT_TRON, TRX], + }, + { + code: 'elliptic', + display: 'Elliptic', + class: WALLET_SCORING, + cryptos: [BTC, ETH, LTC, BCH, USDT, USDC, USDT_TRON, TRX, ZEC], + }, + { + code: 'mock-scoring', + display: 'Mock scoring', + class: WALLET_SCORING, + cryptos: ALL_CRYPTOS, + dev: true, + }, { code: 'sumsub', display: 'Sumsub', class: COMPLIANCE }, - { code: 'mock-compliance', display: 'Mock Compliance', class: COMPLIANCE, dev: true }, + { + code: 'mock-compliance', + display: 'Mock Compliance', + class: COMPLIANCE, + dev: true, + }, ] const flags = require('minimist')(process.argv.slice(2)) const devMode = flags.dev || flags.lamassuDev -const ACCOUNT_LIST = devMode ? ALL_ACCOUNTS : _.filter(it => !it.dev)(ALL_ACCOUNTS) +const ACCOUNT_LIST = devMode + ? ALL_ACCOUNTS + : _.filter(it => !it.dev)(ALL_ACCOUNTS) module.exports = { ACCOUNT_LIST } diff --git a/packages/server/lib/new-admin/config/data/countries.json b/packages/server/lib/new-admin/config/data/countries.json index e4fa0c3d..3f51f18d 100644 --- a/packages/server/lib/new-admin/config/data/countries.json +++ b/packages/server/lib/new-admin/config/data/countries.json @@ -1 +1,250 @@ -[{"code":"US","display":"United States"},{"code":"GB","display":"United Kingdom"},{"code":"CA","display":"Canada"},{"code":"AU","display":"Australia"},{"code":"AW","display":"Aruba"},{"code":"AF","display":"Afghanistan"},{"code":"AO","display":"Angola"},{"code":"AI","display":"Anguilla"},{"code":"AX","display":"Åland Islands"},{"code":"AL","display":"Albania"},{"code":"AD","display":"Andorra"},{"code":"AE","display":"United Arab Emirates"},{"code":"AR","display":"Argentina"},{"code":"AM","display":"Armenia"},{"code":"AS","display":"American Samoa"},{"code":"AQ","display":"Antarctica"},{"code":"TF","display":"French Southern and Antarctic Lands"},{"code":"AG","display":"Antigua and Barbuda"},{"code":"AT","display":"Austria"},{"code":"AZ","display":"Azerbaijan"},{"code":"BI","display":"Burundi"},{"code":"BE","display":"Belgium"},{"code":"BJ","display":"Benin"},{"code":"BF","display":"Burkina Faso"},{"code":"BD","display":"Bangladesh"},{"code":"BG","display":"Bulgaria"},{"code":"BH","display":"Bahrain"},{"code":"BS","display":"Bahamas"},{"code":"BA","display":"Bosnia and Herzegovina"},{"code":"BL","display":"Saint Barthélemy"},{"code":"BY","display":"Belarus"},{"code":"BZ","display":"Belize"},{"code":"BM","display":"Bermuda"},{"code":"BO","display":"Bolivia"},{"code":"BR","display":"Brazil"},{"code":"BB","display":"Barbados"},{"code":"BN","display":"Brunei"},{"code":"BT","display":"Bhutan"},{"code":"BV","display":"Bouvet Island"},{"code":"BW","display":"Botswana"},{"code":"CF","display":"Central African Republic"},{"code":"CC","display":"Cocos (Keeling) Islands"},{"code":"CH","display":"Switzerland"},{"code":"CL","display":"Chile"},{"code":"CN","display":"China"},{"code":"CI","display":"Ivory Coast"},{"code":"CM","display":"Cameroon"},{"code":"CD","display":"DR Congo"},{"code":"CG","display":"Republic of the Congo"},{"code":"CK","display":"Cook Islands"},{"code":"CO","display":"Colombia"},{"code":"KM","display":"Comoros"},{"code":"CV","display":"Cape Verde"},{"code":"CR","display":"Costa Rica"},{"code":"CU","display":"Cuba"},{"code":"CW","display":"Curaçao"},{"code":"CX","display":"Christmas Island"},{"code":"KY","display":"Cayman Islands"},{"code":"CY","display":"Cyprus"},{"code":"CZ","display":"Czech Republic"},{"code":"DE","display":"Germany"},{"code":"DJ","display":"Djibouti"},{"code":"DM","display":"Dominica"},{"code":"DK","display":"Denmark"},{"code":"DO","display":"Dominican Republic"},{"code":"DZ","display":"Algeria"},{"code":"EC","display":"Ecuador"},{"code":"EG","display":"Egypt"},{"code":"ER","display":"Eritrea"},{"code":"EH","display":"Western Sahara"},{"code":"ES","display":"Spain"},{"code":"EE","display":"Estonia"},{"code":"ET","display":"Ethiopia"},{"code":"FI","display":"Finland"},{"code":"FJ","display":"Fiji"},{"code":"FK","display":"Falkland Islands"},{"code":"FR","display":"France"},{"code":"FO","display":"Faroe Islands"},{"code":"FM","display":"Micronesia"},{"code":"GA","display":"Gabon"},{"code":"GE","display":"Georgia"},{"code":"GG","display":"Guernsey"},{"code":"GH","display":"Ghana"},{"code":"GI","display":"Gibraltar"},{"code":"GN","display":"Guinea"},{"code":"GP","display":"Guadeloupe"},{"code":"GM","display":"Gambia"},{"code":"GW","display":"Guinea-Bissau"},{"code":"GQ","display":"Equatorial Guinea"},{"code":"GR","display":"Greece"},{"code":"GD","display":"Grenada"},{"code":"GL","display":"Greenland"},{"code":"GT","display":"Guatemala"},{"code":"GF","display":"French Guiana"},{"code":"GU","display":"Guam"},{"code":"GY","display":"Guyana"},{"code":"HK","display":"Hong Kong"},{"code":"HM","display":"Heard Island and McDonald Islands"},{"code":"HN","display":"Honduras"},{"code":"HR","display":"Croatia"},{"code":"HT","display":"Haiti"},{"code":"HU","display":"Hungary"},{"code":"ID","display":"Indonesia"},{"code":"IM","display":"Isle of Man"},{"code":"IN","display":"India"},{"code":"IO","display":"British Indian Ocean Territory"},{"code":"IE","display":"Ireland"},{"code":"IR","display":"Iran"},{"code":"IQ","display":"Iraq"},{"code":"IS","display":"Iceland"},{"code":"IL","display":"Israel"},{"code":"IT","display":"Italy"},{"code":"JM","display":"Jamaica"},{"code":"JE","display":"Jersey"},{"code":"JO","display":"Jordan"},{"code":"JP","display":"Japan"},{"code":"KZ","display":"Kazakhstan"},{"code":"KE","display":"Kenya"},{"code":"KG","display":"Kyrgyzstan"},{"code":"KH","display":"Cambodia"},{"code":"KI","display":"Kiribati"},{"code":"KN","display":"Saint Kitts and Nevis"},{"code":"KR","display":"South Korea"},{"code":"XK","display":"Kosovo"},{"code":"KW","display":"Kuwait"},{"code":"LA","display":"Laos"},{"code":"LB","display":"Lebanon"},{"code":"LR","display":"Liberia"},{"code":"LY","display":"Libya"},{"code":"LC","display":"Saint Lucia"},{"code":"LI","display":"Liechtenstein"},{"code":"LK","display":"Sri Lanka"},{"code":"LS","display":"Lesotho"},{"code":"LT","display":"Lithuania"},{"code":"LU","display":"Luxembourg"},{"code":"LV","display":"Latvia"},{"code":"MO","display":"Macau"},{"code":"MF","display":"Saint Martin"},{"code":"MA","display":"Morocco"},{"code":"MC","display":"Monaco"},{"code":"MD","display":"Moldova"},{"code":"MG","display":"Madagascar"},{"code":"MV","display":"Maldives"},{"code":"MX","display":"Mexico"},{"code":"MH","display":"Marshall Islands"},{"code":"MK","display":"Macedonia"},{"code":"ML","display":"Mali"},{"code":"MT","display":"Malta"},{"code":"MM","display":"Myanmar"},{"code":"ME","display":"Montenegro"},{"code":"MN","display":"Mongolia"},{"code":"MP","display":"Northern Mariana Islands"},{"code":"MZ","display":"Mozambique"},{"code":"MR","display":"Mauritania"},{"code":"MS","display":"Montserrat"},{"code":"MQ","display":"Martinique"},{"code":"MU","display":"Mauritius"},{"code":"MW","display":"Malawi"},{"code":"MY","display":"Malaysia"},{"code":"YT","display":"Mayotte"},{"code":"NA","display":"Namibia"},{"code":"NC","display":"New Caledonia"},{"code":"NE","display":"Niger"},{"code":"NF","display":"Norfolk Island"},{"code":"NG","display":"Nigeria"},{"code":"NI","display":"Nicaragua"},{"code":"NU","display":"Niue"},{"code":"NL","display":"Netherlands"},{"code":"NO","display":"Norway"},{"code":"NP","display":"Nepal"},{"code":"NR","display":"Nauru"},{"code":"NZ","display":"New Zealand"},{"code":"OM","display":"Oman"},{"code":"PK","display":"Pakistan"},{"code":"PA","display":"Panama"},{"code":"PN","display":"Pitcairn Islands"},{"code":"PE","display":"Peru"},{"code":"PH","display":"Philippines"},{"code":"PW","display":"Palau"},{"code":"PG","display":"Papua New Guinea"},{"code":"PL","display":"Poland"},{"code":"PR","display":"Puerto Rico"},{"code":"KP","display":"North Korea"},{"code":"PT","display":"Portugal"},{"code":"PY","display":"Paraguay"},{"code":"PS","display":"Palestine"},{"code":"PF","display":"French Polynesia"},{"code":"QA","display":"Qatar"},{"code":"RE","display":"Réunion"},{"code":"RO","display":"Romania"},{"code":"RU","display":"Russia"},{"code":"RW","display":"Rwanda"},{"code":"SA","display":"Saudi Arabia"},{"code":"SD","display":"Sudan"},{"code":"SN","display":"Senegal"},{"code":"SG","display":"Singapore"},{"code":"GS","display":"South Georgia"},{"code":"SJ","display":"Svalbard and Jan Mayen"},{"code":"SB","display":"Solomon Islands"},{"code":"SL","display":"Sierra Leone"},{"code":"SV","display":"El Salvador"},{"code":"SM","display":"San Marino"},{"code":"SO","display":"Somalia"},{"code":"PM","display":"Saint Pierre and Miquelon"},{"code":"RS","display":"Serbia"},{"code":"SS","display":"South Sudan"},{"code":"ST","display":"São Tomé and Príncipe"},{"code":"SR","display":"Suriname"},{"code":"SK","display":"Slovakia"},{"code":"SI","display":"Slovenia"},{"code":"SE","display":"Sweden"},{"code":"SZ","display":"Swaziland"},{"code":"SX","display":"Sint Maarten"},{"code":"SC","display":"Seychelles"},{"code":"SY","display":"Syria"},{"code":"TC","display":"Turks and Caicos Islands"},{"code":"TD","display":"Chad"},{"code":"TG","display":"Togo"},{"code":"TH","display":"Thailand"},{"code":"TJ","display":"Tajikistan"},{"code":"TK","display":"Tokelau"},{"code":"TM","display":"Turkmenistan"},{"code":"TL","display":"Timor-Leste"},{"code":"TO","display":"Tonga"},{"code":"TT","display":"Trinidad and Tobago"},{"code":"TN","display":"Tunisia"},{"code":"TR","display":"Turkey"},{"code":"TV","display":"Tuvalu"},{"code":"TW","display":"Taiwan"},{"code":"TZ","display":"Tanzania"},{"code":"UG","display":"Uganda"},{"code":"UA","display":"Ukraine"},{"code":"UM","display":"United States Minor Outlying Islands"},{"code":"UY","display":"Uruguay"},{"code":"UZ","display":"Uzbekistan"},{"code":"VA","display":"Vatican City"},{"code":"VC","display":"Saint Vincent and the Grenadines"},{"code":"VE","display":"Venezuela"},{"code":"VG","display":"British Virgin Islands"},{"code":"VI","display":"United States Virgin Islands"},{"code":"VN","display":"Vietnam"},{"code":"VU","display":"Vanuatu"},{"code":"WF","display":"Wallis and Futuna"},{"code":"WS","display":"Samoa"},{"code":"YE","display":"Yemen"},{"code":"ZA","display":"South Africa"},{"code":"ZM","display":"Zambia"},{"code":"ZW","display":"Zimbabwe"}] \ No newline at end of file +[ + { "code": "US", "display": "United States" }, + { "code": "GB", "display": "United Kingdom" }, + { "code": "CA", "display": "Canada" }, + { "code": "AU", "display": "Australia" }, + { "code": "AW", "display": "Aruba" }, + { "code": "AF", "display": "Afghanistan" }, + { "code": "AO", "display": "Angola" }, + { "code": "AI", "display": "Anguilla" }, + { "code": "AX", "display": "Åland Islands" }, + { "code": "AL", "display": "Albania" }, + { "code": "AD", "display": "Andorra" }, + { "code": "AE", "display": "United Arab Emirates" }, + { "code": "AR", "display": "Argentina" }, + { "code": "AM", "display": "Armenia" }, + { "code": "AS", "display": "American Samoa" }, + { "code": "AQ", "display": "Antarctica" }, + { "code": "TF", "display": "French Southern and Antarctic Lands" }, + { "code": "AG", "display": "Antigua and Barbuda" }, + { "code": "AT", "display": "Austria" }, + { "code": "AZ", "display": "Azerbaijan" }, + { "code": "BI", "display": "Burundi" }, + { "code": "BE", "display": "Belgium" }, + { "code": "BJ", "display": "Benin" }, + { "code": "BF", "display": "Burkina Faso" }, + { "code": "BD", "display": "Bangladesh" }, + { "code": "BG", "display": "Bulgaria" }, + { "code": "BH", "display": "Bahrain" }, + { "code": "BS", "display": "Bahamas" }, + { "code": "BA", "display": "Bosnia and Herzegovina" }, + { "code": "BL", "display": "Saint Barthélemy" }, + { "code": "BY", "display": "Belarus" }, + { "code": "BZ", "display": "Belize" }, + { "code": "BM", "display": "Bermuda" }, + { "code": "BO", "display": "Bolivia" }, + { "code": "BR", "display": "Brazil" }, + { "code": "BB", "display": "Barbados" }, + { "code": "BN", "display": "Brunei" }, + { "code": "BT", "display": "Bhutan" }, + { "code": "BV", "display": "Bouvet Island" }, + { "code": "BW", "display": "Botswana" }, + { "code": "CF", "display": "Central African Republic" }, + { "code": "CC", "display": "Cocos (Keeling) Islands" }, + { "code": "CH", "display": "Switzerland" }, + { "code": "CL", "display": "Chile" }, + { "code": "CN", "display": "China" }, + { "code": "CI", "display": "Ivory Coast" }, + { "code": "CM", "display": "Cameroon" }, + { "code": "CD", "display": "DR Congo" }, + { "code": "CG", "display": "Republic of the Congo" }, + { "code": "CK", "display": "Cook Islands" }, + { "code": "CO", "display": "Colombia" }, + { "code": "KM", "display": "Comoros" }, + { "code": "CV", "display": "Cape Verde" }, + { "code": "CR", "display": "Costa Rica" }, + { "code": "CU", "display": "Cuba" }, + { "code": "CW", "display": "Curaçao" }, + { "code": "CX", "display": "Christmas Island" }, + { "code": "KY", "display": "Cayman Islands" }, + { "code": "CY", "display": "Cyprus" }, + { "code": "CZ", "display": "Czech Republic" }, + { "code": "DE", "display": "Germany" }, + { "code": "DJ", "display": "Djibouti" }, + { "code": "DM", "display": "Dominica" }, + { "code": "DK", "display": "Denmark" }, + { "code": "DO", "display": "Dominican Republic" }, + { "code": "DZ", "display": "Algeria" }, + { "code": "EC", "display": "Ecuador" }, + { "code": "EG", "display": "Egypt" }, + { "code": "ER", "display": "Eritrea" }, + { "code": "EH", "display": "Western Sahara" }, + { "code": "ES", "display": "Spain" }, + { "code": "EE", "display": "Estonia" }, + { "code": "ET", "display": "Ethiopia" }, + { "code": "FI", "display": "Finland" }, + { "code": "FJ", "display": "Fiji" }, + { "code": "FK", "display": "Falkland Islands" }, + { "code": "FR", "display": "France" }, + { "code": "FO", "display": "Faroe Islands" }, + { "code": "FM", "display": "Micronesia" }, + { "code": "GA", "display": "Gabon" }, + { "code": "GE", "display": "Georgia" }, + { "code": "GG", "display": "Guernsey" }, + { "code": "GH", "display": "Ghana" }, + { "code": "GI", "display": "Gibraltar" }, + { "code": "GN", "display": "Guinea" }, + { "code": "GP", "display": "Guadeloupe" }, + { "code": "GM", "display": "Gambia" }, + { "code": "GW", "display": "Guinea-Bissau" }, + { "code": "GQ", "display": "Equatorial Guinea" }, + { "code": "GR", "display": "Greece" }, + { "code": "GD", "display": "Grenada" }, + { "code": "GL", "display": "Greenland" }, + { "code": "GT", "display": "Guatemala" }, + { "code": "GF", "display": "French Guiana" }, + { "code": "GU", "display": "Guam" }, + { "code": "GY", "display": "Guyana" }, + { "code": "HK", "display": "Hong Kong" }, + { "code": "HM", "display": "Heard Island and McDonald Islands" }, + { "code": "HN", "display": "Honduras" }, + { "code": "HR", "display": "Croatia" }, + { "code": "HT", "display": "Haiti" }, + { "code": "HU", "display": "Hungary" }, + { "code": "ID", "display": "Indonesia" }, + { "code": "IM", "display": "Isle of Man" }, + { "code": "IN", "display": "India" }, + { "code": "IO", "display": "British Indian Ocean Territory" }, + { "code": "IE", "display": "Ireland" }, + { "code": "IR", "display": "Iran" }, + { "code": "IQ", "display": "Iraq" }, + { "code": "IS", "display": "Iceland" }, + { "code": "IL", "display": "Israel" }, + { "code": "IT", "display": "Italy" }, + { "code": "JM", "display": "Jamaica" }, + { "code": "JE", "display": "Jersey" }, + { "code": "JO", "display": "Jordan" }, + { "code": "JP", "display": "Japan" }, + { "code": "KZ", "display": "Kazakhstan" }, + { "code": "KE", "display": "Kenya" }, + { "code": "KG", "display": "Kyrgyzstan" }, + { "code": "KH", "display": "Cambodia" }, + { "code": "KI", "display": "Kiribati" }, + { "code": "KN", "display": "Saint Kitts and Nevis" }, + { "code": "KR", "display": "South Korea" }, + { "code": "XK", "display": "Kosovo" }, + { "code": "KW", "display": "Kuwait" }, + { "code": "LA", "display": "Laos" }, + { "code": "LB", "display": "Lebanon" }, + { "code": "LR", "display": "Liberia" }, + { "code": "LY", "display": "Libya" }, + { "code": "LC", "display": "Saint Lucia" }, + { "code": "LI", "display": "Liechtenstein" }, + { "code": "LK", "display": "Sri Lanka" }, + { "code": "LS", "display": "Lesotho" }, + { "code": "LT", "display": "Lithuania" }, + { "code": "LU", "display": "Luxembourg" }, + { "code": "LV", "display": "Latvia" }, + { "code": "MO", "display": "Macau" }, + { "code": "MF", "display": "Saint Martin" }, + { "code": "MA", "display": "Morocco" }, + { "code": "MC", "display": "Monaco" }, + { "code": "MD", "display": "Moldova" }, + { "code": "MG", "display": "Madagascar" }, + { "code": "MV", "display": "Maldives" }, + { "code": "MX", "display": "Mexico" }, + { "code": "MH", "display": "Marshall Islands" }, + { "code": "MK", "display": "Macedonia" }, + { "code": "ML", "display": "Mali" }, + { "code": "MT", "display": "Malta" }, + { "code": "MM", "display": "Myanmar" }, + { "code": "ME", "display": "Montenegro" }, + { "code": "MN", "display": "Mongolia" }, + { "code": "MP", "display": "Northern Mariana Islands" }, + { "code": "MZ", "display": "Mozambique" }, + { "code": "MR", "display": "Mauritania" }, + { "code": "MS", "display": "Montserrat" }, + { "code": "MQ", "display": "Martinique" }, + { "code": "MU", "display": "Mauritius" }, + { "code": "MW", "display": "Malawi" }, + { "code": "MY", "display": "Malaysia" }, + { "code": "YT", "display": "Mayotte" }, + { "code": "NA", "display": "Namibia" }, + { "code": "NC", "display": "New Caledonia" }, + { "code": "NE", "display": "Niger" }, + { "code": "NF", "display": "Norfolk Island" }, + { "code": "NG", "display": "Nigeria" }, + { "code": "NI", "display": "Nicaragua" }, + { "code": "NU", "display": "Niue" }, + { "code": "NL", "display": "Netherlands" }, + { "code": "NO", "display": "Norway" }, + { "code": "NP", "display": "Nepal" }, + { "code": "NR", "display": "Nauru" }, + { "code": "NZ", "display": "New Zealand" }, + { "code": "OM", "display": "Oman" }, + { "code": "PK", "display": "Pakistan" }, + { "code": "PA", "display": "Panama" }, + { "code": "PN", "display": "Pitcairn Islands" }, + { "code": "PE", "display": "Peru" }, + { "code": "PH", "display": "Philippines" }, + { "code": "PW", "display": "Palau" }, + { "code": "PG", "display": "Papua New Guinea" }, + { "code": "PL", "display": "Poland" }, + { "code": "PR", "display": "Puerto Rico" }, + { "code": "KP", "display": "North Korea" }, + { "code": "PT", "display": "Portugal" }, + { "code": "PY", "display": "Paraguay" }, + { "code": "PS", "display": "Palestine" }, + { "code": "PF", "display": "French Polynesia" }, + { "code": "QA", "display": "Qatar" }, + { "code": "RE", "display": "Réunion" }, + { "code": "RO", "display": "Romania" }, + { "code": "RU", "display": "Russia" }, + { "code": "RW", "display": "Rwanda" }, + { "code": "SA", "display": "Saudi Arabia" }, + { "code": "SD", "display": "Sudan" }, + { "code": "SN", "display": "Senegal" }, + { "code": "SG", "display": "Singapore" }, + { "code": "GS", "display": "South Georgia" }, + { "code": "SJ", "display": "Svalbard and Jan Mayen" }, + { "code": "SB", "display": "Solomon Islands" }, + { "code": "SL", "display": "Sierra Leone" }, + { "code": "SV", "display": "El Salvador" }, + { "code": "SM", "display": "San Marino" }, + { "code": "SO", "display": "Somalia" }, + { "code": "PM", "display": "Saint Pierre and Miquelon" }, + { "code": "RS", "display": "Serbia" }, + { "code": "SS", "display": "South Sudan" }, + { "code": "ST", "display": "São Tomé and Príncipe" }, + { "code": "SR", "display": "Suriname" }, + { "code": "SK", "display": "Slovakia" }, + { "code": "SI", "display": "Slovenia" }, + { "code": "SE", "display": "Sweden" }, + { "code": "SZ", "display": "Swaziland" }, + { "code": "SX", "display": "Sint Maarten" }, + { "code": "SC", "display": "Seychelles" }, + { "code": "SY", "display": "Syria" }, + { "code": "TC", "display": "Turks and Caicos Islands" }, + { "code": "TD", "display": "Chad" }, + { "code": "TG", "display": "Togo" }, + { "code": "TH", "display": "Thailand" }, + { "code": "TJ", "display": "Tajikistan" }, + { "code": "TK", "display": "Tokelau" }, + { "code": "TM", "display": "Turkmenistan" }, + { "code": "TL", "display": "Timor-Leste" }, + { "code": "TO", "display": "Tonga" }, + { "code": "TT", "display": "Trinidad and Tobago" }, + { "code": "TN", "display": "Tunisia" }, + { "code": "TR", "display": "Turkey" }, + { "code": "TV", "display": "Tuvalu" }, + { "code": "TW", "display": "Taiwan" }, + { "code": "TZ", "display": "Tanzania" }, + { "code": "UG", "display": "Uganda" }, + { "code": "UA", "display": "Ukraine" }, + { "code": "UM", "display": "United States Minor Outlying Islands" }, + { "code": "UY", "display": "Uruguay" }, + { "code": "UZ", "display": "Uzbekistan" }, + { "code": "VA", "display": "Vatican City" }, + { "code": "VC", "display": "Saint Vincent and the Grenadines" }, + { "code": "VE", "display": "Venezuela" }, + { "code": "VG", "display": "British Virgin Islands" }, + { "code": "VI", "display": "United States Virgin Islands" }, + { "code": "VN", "display": "Vietnam" }, + { "code": "VU", "display": "Vanuatu" }, + { "code": "WF", "display": "Wallis and Futuna" }, + { "code": "WS", "display": "Samoa" }, + { "code": "YE", "display": "Yemen" }, + { "code": "ZA", "display": "South Africa" }, + { "code": "ZM", "display": "Zambia" }, + { "code": "ZW", "display": "Zimbabwe" } +] diff --git a/packages/server/lib/new-admin/config/data/languages.json b/packages/server/lib/new-admin/config/data/languages.json index b61a3758..d055c3b2 100644 --- a/packages/server/lib/new-admin/config/data/languages.json +++ b/packages/server/lib/new-admin/config/data/languages.json @@ -1,255 +1,266 @@ { -"attribute": {"name":0, "nativeName":1}, -"rtl": {"ar":1,"dv":1,"fa":1,"ha":1,"he":1,"ks":1,"ku":1,"ps":1,"ur":1,"yi":1}, -"lang": { -"aa":["Afar","Afar"], -"ab":["Abkhazian","Аҧсуа"], -"af":["Afrikaans","Afrikaans"], -"ak":["Akan","Akana"], -"am":["Amharic","አማርኛ"], -"an":["Aragonese","Aragonés"], -"ar":["Arabic","العربية"], -"as":["Assamese","অসমীয়া"], -"av":["Avar","Авар"], -"ay":["Aymara","Aymar"], -"az":["Azerbaijani","Azərbaycanca / آذربايجان"], -"ba":["Bashkir","Башҡорт"], -"be":["Belarusian","Беларуская"], -"bg":["Bulgarian","Български"], -"bh":["Bihari","भोजपुरी"], -"bi":["Bislama","Bislama"], -"bm":["Bambara","Bamanankan"], -"bn":["Bengali","বাংলা"], -"bo":["Tibetan","བོད་ཡིག / Bod skad"], -"br":["Breton","Brezhoneg"], -"bs":["Bosnian","Bosanski"], -"ca":["Catalan","Català"], -"ce":["Chechen","Нохчийн"], -"ch":["Chamorro","Chamoru"], -"co":["Corsican","Corsu"], -"cr":["Cree","Nehiyaw"], -"cs":["Czech","Česky"], -"cu":["Old Church Slavonic / Old Bulgarian","словѣньскъ / slověnĭskŭ"], -"cv":["Chuvash","Чăваш"], -"cy":["Welsh","Cymraeg"], -"da":["Danish","Dansk"], -"de":["German","Deutsch"], -"dv":["Divehi","ދިވެހިބަސް"], -"dz":["Dzongkha","ཇོང་ཁ"], -"ee":["Ewe","Ɛʋɛ"], -"el":["Greek","Ελληνικά"], -"en":["English","English"], -"eo":["Esperanto","Esperanto"], -"es":["Spanish","Español"], -"et":["Estonian","Eesti"], -"eu":["Basque","Euskara"], -"fa":["Persian","فارسی"], -"ff":["Peul","Fulfulde"], -"fi":["Finnish","Suomi"], -"fj":["Fijian","Na Vosa Vakaviti"], -"fo":["Faroese","Føroyskt"], -"fr":["French","Français"], -"fy":["West Frisian","Frysk"], -"ga":["Irish","Gaeilge"], -"gd":["Scottish Gaelic","Gàidhlig"], -"gl":["Galician","Galego"], -"gn":["Guarani","Avañe'ẽ"], -"gu":["Gujarati","ગુજરાતી"], -"gv":["Manx","Gaelg"], -"ha":["Hausa","هَوُسَ"], -"he":["Hebrew","עברית"], -"hi":["Hindi","हिन्दी"], -"ho":["Hiri Motu","Hiri Motu"], -"hr":["Croatian","Hrvatski"], -"ht":["Haitian","Krèyol ayisyen"], -"hu":["Hungarian","Magyar"], -"hy":["Armenian","Հայերեն"], -"hz":["Herero","Otsiherero"], -"ia":["Interlingua","Interlingua"], -"id":["Indonesian","Bahasa Indonesia"], -"ie":["Interlingue","Interlingue"], -"ig":["Igbo","Igbo"], -"ii":["Sichuan Yi","ꆇꉙ / 四川彝语"], -"ik":["Inupiak","Iñupiak"], -"io":["Ido","Ido"], -"is":["Icelandic","Íslenska"], -"it":["Italian","Italiano"], -"iu":["Inuktitut","ᐃᓄᒃᑎᑐᑦ"], -"ja":["Japanese","日本語"], -"jv":["Javanese","Basa Jawa"], -"ka":["Georgian","ქართული"], -"kg":["Kongo","KiKongo"], -"ki":["Kikuyu","Gĩkũyũ"], -"kj":["Kuanyama","Kuanyama"], -"kk":["Kazakh","Қазақша"], -"kl":["Greenlandic","Kalaallisut"], -"km":["Cambodian","ភាសាខ្មែរ"], -"kn":["Kannada","ಕನ್ನಡ"], -"ko":["Korean","한국어"], -"kr":["Kanuri","Kanuri"], -"ks":["Kashmiri","कश्मीरी / كشميري"], -"ku":["Kurdish","Kurdî / كوردی"], -"kv":["Komi","Коми"], -"kw":["Cornish","Kernewek"], -"ky":["Kirghiz","Kırgızca / Кыргызча"], -"la":["Latin","Latina"], -"lb":["Luxembourgish","Lëtzebuergesch"], -"lg":["Ganda","Luganda"], -"li":["Limburgian","Limburgs"], -"ln":["Lingala","Lingála"], -"lo":["Laotian","ລາວ / Pha xa lao"], -"lt":["Lithuanian","Lietuvių"], -"lv":["Latvian","Latviešu"], -"mg":["Malagasy","Malagasy"], -"mh":["Marshallese","Kajin Majel / Ebon"], -"mi":["Maori","Māori"], -"mk":["Macedonian","Македонски"], -"ml":["Malayalam","മലയാളം"], -"mn":["Mongolian","Монгол"], -"mo":["Moldovan","Moldovenească"], -"mr":["Marathi","मराठी"], -"ms":["Malay","Bahasa Melayu"], -"mt":["Maltese","bil-Malti"], -"my":["Burmese","Myanmasa"], -"na":["Nauruan","Dorerin Naoero"], -"nd":["North Ndebele","Sindebele"], -"ne":["Nepali","नेपाली"], -"ng":["Ndonga","Oshiwambo"], -"nl":["Dutch","Nederlands"], -"nn":["Norwegian Nynorsk","Norsk (nynorsk)"], -"no":["Norwegian","Norsk (bokmål / riksmål)"], -"nr":["South Ndebele","isiNdebele"], -"nv":["Navajo","Diné bizaad"], -"ny":["Chichewa","Chi-Chewa"], -"oc":["Occitan","Occitan"], -"oj":["Ojibwa","ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin"], -"om":["Oromo","Oromoo"], -"or":["Oriya","ଓଡ଼ିଆ"], -"os":["Ossetian / Ossetic","Иронау"], -"pa":["Panjabi / Punjabi","ਪੰਜਾਬੀ / पंजाबी / پنجابي"], -"pi":["Pali","Pāli / पाऴि"], -"pl":["Polish","Polski"], -"ps":["Pashto","پښتو"], -"pt":["Portuguese","Português"], -"qu":["Quechua","Runa Simi"], -"rm":["Raeto Romance","Rumantsch"], -"rn":["Kirundi","Kirundi"], -"ro":["Romanian","Română"], -"ru":["Russian","Русский"], -"rw":["Rwandi","Kinyarwandi"], -"sa":["Sanskrit","संस्कृतम्"], -"sc":["Sardinian","Sardu"], -"sd":["Sindhi","सिनधि"], -"se":["Northern Sami","Sámegiella"], -"sg":["Sango","Sängö"], -"sh":["Serbo-Croatian","Srpskohrvatski / Српскохрватски"], -"si":["Sinhalese","සිංහල"], -"sk":["Slovak","Slovenčina"], -"sl":["Slovenian","Slovenščina"], -"sm":["Samoan","Gagana Samoa"], -"sn":["Shona","chiShona"], -"so":["Somalia","Soomaaliga"], -"sq":["Albanian","Shqip"], -"sr":["Serbian","Српски"], -"ss":["Swati","SiSwati"], -"st":["Southern Sotho","Sesotho"], -"su":["Sundanese","Basa Sunda"], -"sv":["Swedish","Svenska"], -"sw":["Swahili","Kiswahili"], -"ta":["Tamil","தமிழ்"], -"te":["Telugu","తెలుగు"], -"tg":["Tajik","Тоҷикӣ"], -"th":["Thai","ไทย / Phasa Thai"], -"ti":["Tigrinya","ትግርኛ"], -"tk":["Turkmen","Туркмен / تركمن"], -"tl":["Tagalog / Filipino","Tagalog"], -"tn":["Tswana","Setswana"], -"to":["Tonga","Lea Faka-Tonga"], -"tr":["Turkish","Türkçe"], -"ts":["Tsonga","Xitsonga"], -"tt":["Tatar","Tatarça"], -"tw":["Twi","Twi"], -"ty":["Tahitian","Reo Mā`ohi"], -"ug":["Uyghur","Uyƣurqə / ئۇيغۇرچە"], -"uk":["Ukrainian","Українська"], -"ur":["Urdu","اردو"], -"uz":["Uzbek","Ўзбек"], -"ve":["Venda","Tshivenḓa"], -"vi":["Vietnamese","Tiếng Việt"], -"vo":["Volapük","Volapük"], -"wa":["Walloon","Walon"], -"wo":["Wolof","Wollof"], -"xh":["Xhosa","isiXhosa"], -"yi":["Yiddish","ייִדיש"], -"yo":["Yoruba","Yorùbá"], -"za":["Zhuang","Cuengh / Tôô / 壮语"], -"zh":["Chinese","中文"], -"zu":["Zulu","isiZulu"] -}, -"supported": [ - "en-US", - "en-CA", - "fr-QC", - "ach-UG", - "af-ZA", - "ar-SA", - "bg-BG", - "ca-ES", - "cs-CZ", - "cy-GB", - "de-DE", - "de-AT", - "de-CH", - "da-DK", - "el-GR", - "en-GB", - "en-AU", - "en-HK", - "en-IE", - "en-NZ", - "en-PR", - "es-ES", - "es-MX", - "et-EE", - "fi-FI", - "fr-FR", - "fr-CH", - "fur-IT", - "ga-IE", - "gd-GB", - "he-IL", - "hr-HR", - "hu-HU", - "hy-AM", - "id-ID", - "it-CH", - "it-IT", - "ja-JP", - "ka-GE", - "ko-KR", - "ky-KG", - "lt-LT", - "nb-NO", - "nl-BE", - "nl-NL", - "pt-PT", - "pt-BR", - "pl-PL", - "ro-RO", - "ru-RU", - "sco-GB", - "sh-HR", - "sk-SK", - "sl-SI", - "sr-SP", - "sv-SE", - "th-TH", - "tr-TR", - "uk-UA", - "vi-VN", - "zh-CN", - "zh-HK", - "zh-SG", - "zh-TW" -] + "attribute": { "name": 0, "nativeName": 1 }, + "rtl": { + "ar": 1, + "dv": 1, + "fa": 1, + "ha": 1, + "he": 1, + "ks": 1, + "ku": 1, + "ps": 1, + "ur": 1, + "yi": 1 + }, + "lang": { + "aa": ["Afar", "Afar"], + "ab": ["Abkhazian", "Аҧсуа"], + "af": ["Afrikaans", "Afrikaans"], + "ak": ["Akan", "Akana"], + "am": ["Amharic", "አማርኛ"], + "an": ["Aragonese", "Aragonés"], + "ar": ["Arabic", "العربية"], + "as": ["Assamese", "অসমীয়া"], + "av": ["Avar", "Авар"], + "ay": ["Aymara", "Aymar"], + "az": ["Azerbaijani", "Azərbaycanca / آذربايجان"], + "ba": ["Bashkir", "Башҡорт"], + "be": ["Belarusian", "Беларуская"], + "bg": ["Bulgarian", "Български"], + "bh": ["Bihari", "भोजपुरी"], + "bi": ["Bislama", "Bislama"], + "bm": ["Bambara", "Bamanankan"], + "bn": ["Bengali", "বাংলা"], + "bo": ["Tibetan", "བོད་ཡིག / Bod skad"], + "br": ["Breton", "Brezhoneg"], + "bs": ["Bosnian", "Bosanski"], + "ca": ["Catalan", "Català"], + "ce": ["Chechen", "Нохчийн"], + "ch": ["Chamorro", "Chamoru"], + "co": ["Corsican", "Corsu"], + "cr": ["Cree", "Nehiyaw"], + "cs": ["Czech", "Česky"], + "cu": ["Old Church Slavonic / Old Bulgarian", "словѣньскъ / slověnĭskŭ"], + "cv": ["Chuvash", "Чăваш"], + "cy": ["Welsh", "Cymraeg"], + "da": ["Danish", "Dansk"], + "de": ["German", "Deutsch"], + "dv": ["Divehi", "ދިވެހިބަސް"], + "dz": ["Dzongkha", "ཇོང་ཁ"], + "ee": ["Ewe", "Ɛʋɛ"], + "el": ["Greek", "Ελληνικά"], + "en": ["English", "English"], + "eo": ["Esperanto", "Esperanto"], + "es": ["Spanish", "Español"], + "et": ["Estonian", "Eesti"], + "eu": ["Basque", "Euskara"], + "fa": ["Persian", "فارسی"], + "ff": ["Peul", "Fulfulde"], + "fi": ["Finnish", "Suomi"], + "fj": ["Fijian", "Na Vosa Vakaviti"], + "fo": ["Faroese", "Føroyskt"], + "fr": ["French", "Français"], + "fy": ["West Frisian", "Frysk"], + "ga": ["Irish", "Gaeilge"], + "gd": ["Scottish Gaelic", "Gàidhlig"], + "gl": ["Galician", "Galego"], + "gn": ["Guarani", "Avañe'ẽ"], + "gu": ["Gujarati", "ગુજરાતી"], + "gv": ["Manx", "Gaelg"], + "ha": ["Hausa", "هَوُسَ"], + "he": ["Hebrew", "עברית"], + "hi": ["Hindi", "हिन्दी"], + "ho": ["Hiri Motu", "Hiri Motu"], + "hr": ["Croatian", "Hrvatski"], + "ht": ["Haitian", "Krèyol ayisyen"], + "hu": ["Hungarian", "Magyar"], + "hy": ["Armenian", "Հայերեն"], + "hz": ["Herero", "Otsiherero"], + "ia": ["Interlingua", "Interlingua"], + "id": ["Indonesian", "Bahasa Indonesia"], + "ie": ["Interlingue", "Interlingue"], + "ig": ["Igbo", "Igbo"], + "ii": ["Sichuan Yi", "ꆇꉙ / 四川彝语"], + "ik": ["Inupiak", "Iñupiak"], + "io": ["Ido", "Ido"], + "is": ["Icelandic", "Íslenska"], + "it": ["Italian", "Italiano"], + "iu": ["Inuktitut", "ᐃᓄᒃᑎᑐᑦ"], + "ja": ["Japanese", "日本語"], + "jv": ["Javanese", "Basa Jawa"], + "ka": ["Georgian", "ქართული"], + "kg": ["Kongo", "KiKongo"], + "ki": ["Kikuyu", "Gĩkũyũ"], + "kj": ["Kuanyama", "Kuanyama"], + "kk": ["Kazakh", "Қазақша"], + "kl": ["Greenlandic", "Kalaallisut"], + "km": ["Cambodian", "ភាសាខ្មែរ"], + "kn": ["Kannada", "ಕನ್ನಡ"], + "ko": ["Korean", "한국어"], + "kr": ["Kanuri", "Kanuri"], + "ks": ["Kashmiri", "कश्मीरी / كشميري"], + "ku": ["Kurdish", "Kurdî / كوردی"], + "kv": ["Komi", "Коми"], + "kw": ["Cornish", "Kernewek"], + "ky": ["Kirghiz", "Kırgızca / Кыргызча"], + "la": ["Latin", "Latina"], + "lb": ["Luxembourgish", "Lëtzebuergesch"], + "lg": ["Ganda", "Luganda"], + "li": ["Limburgian", "Limburgs"], + "ln": ["Lingala", "Lingála"], + "lo": ["Laotian", "ລາວ / Pha xa lao"], + "lt": ["Lithuanian", "Lietuvių"], + "lv": ["Latvian", "Latviešu"], + "mg": ["Malagasy", "Malagasy"], + "mh": ["Marshallese", "Kajin Majel / Ebon"], + "mi": ["Maori", "Māori"], + "mk": ["Macedonian", "Македонски"], + "ml": ["Malayalam", "മലയാളം"], + "mn": ["Mongolian", "Монгол"], + "mo": ["Moldovan", "Moldovenească"], + "mr": ["Marathi", "मराठी"], + "ms": ["Malay", "Bahasa Melayu"], + "mt": ["Maltese", "bil-Malti"], + "my": ["Burmese", "Myanmasa"], + "na": ["Nauruan", "Dorerin Naoero"], + "nd": ["North Ndebele", "Sindebele"], + "ne": ["Nepali", "नेपाली"], + "ng": ["Ndonga", "Oshiwambo"], + "nl": ["Dutch", "Nederlands"], + "nn": ["Norwegian Nynorsk", "Norsk (nynorsk)"], + "no": ["Norwegian", "Norsk (bokmål / riksmål)"], + "nr": ["South Ndebele", "isiNdebele"], + "nv": ["Navajo", "Diné bizaad"], + "ny": ["Chichewa", "Chi-Chewa"], + "oc": ["Occitan", "Occitan"], + "oj": ["Ojibwa", "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin"], + "om": ["Oromo", "Oromoo"], + "or": ["Oriya", "ଓଡ଼ିଆ"], + "os": ["Ossetian / Ossetic", "Иронау"], + "pa": ["Panjabi / Punjabi", "ਪੰਜਾਬੀ / पंजाबी / پنجابي"], + "pi": ["Pali", "Pāli / पाऴि"], + "pl": ["Polish", "Polski"], + "ps": ["Pashto", "پښتو"], + "pt": ["Portuguese", "Português"], + "qu": ["Quechua", "Runa Simi"], + "rm": ["Raeto Romance", "Rumantsch"], + "rn": ["Kirundi", "Kirundi"], + "ro": ["Romanian", "Română"], + "ru": ["Russian", "Русский"], + "rw": ["Rwandi", "Kinyarwandi"], + "sa": ["Sanskrit", "संस्कृतम्"], + "sc": ["Sardinian", "Sardu"], + "sd": ["Sindhi", "सिनधि"], + "se": ["Northern Sami", "Sámegiella"], + "sg": ["Sango", "Sängö"], + "sh": ["Serbo-Croatian", "Srpskohrvatski / Српскохрватски"], + "si": ["Sinhalese", "සිංහල"], + "sk": ["Slovak", "Slovenčina"], + "sl": ["Slovenian", "Slovenščina"], + "sm": ["Samoan", "Gagana Samoa"], + "sn": ["Shona", "chiShona"], + "so": ["Somalia", "Soomaaliga"], + "sq": ["Albanian", "Shqip"], + "sr": ["Serbian", "Српски"], + "ss": ["Swati", "SiSwati"], + "st": ["Southern Sotho", "Sesotho"], + "su": ["Sundanese", "Basa Sunda"], + "sv": ["Swedish", "Svenska"], + "sw": ["Swahili", "Kiswahili"], + "ta": ["Tamil", "தமிழ்"], + "te": ["Telugu", "తెలుగు"], + "tg": ["Tajik", "Тоҷикӣ"], + "th": ["Thai", "ไทย / Phasa Thai"], + "ti": ["Tigrinya", "ትግርኛ"], + "tk": ["Turkmen", "Туркмен / تركمن"], + "tl": ["Tagalog / Filipino", "Tagalog"], + "tn": ["Tswana", "Setswana"], + "to": ["Tonga", "Lea Faka-Tonga"], + "tr": ["Turkish", "Türkçe"], + "ts": ["Tsonga", "Xitsonga"], + "tt": ["Tatar", "Tatarça"], + "tw": ["Twi", "Twi"], + "ty": ["Tahitian", "Reo Mā`ohi"], + "ug": ["Uyghur", "Uyƣurqə / ئۇيغۇرچە"], + "uk": ["Ukrainian", "Українська"], + "ur": ["Urdu", "اردو"], + "uz": ["Uzbek", "Ўзбек"], + "ve": ["Venda", "Tshivenḓa"], + "vi": ["Vietnamese", "Tiếng Việt"], + "vo": ["Volapük", "Volapük"], + "wa": ["Walloon", "Walon"], + "wo": ["Wolof", "Wollof"], + "xh": ["Xhosa", "isiXhosa"], + "yi": ["Yiddish", "ייִדיש"], + "yo": ["Yoruba", "Yorùbá"], + "za": ["Zhuang", "Cuengh / Tôô / 壮语"], + "zh": ["Chinese", "中文"], + "zu": ["Zulu", "isiZulu"] + }, + "supported": [ + "en-US", + "en-CA", + "fr-QC", + "ach-UG", + "af-ZA", + "ar-SA", + "bg-BG", + "ca-ES", + "cs-CZ", + "cy-GB", + "de-DE", + "de-AT", + "de-CH", + "da-DK", + "el-GR", + "en-GB", + "en-AU", + "en-HK", + "en-IE", + "en-NZ", + "en-PR", + "es-ES", + "es-MX", + "et-EE", + "fi-FI", + "fr-FR", + "fr-CH", + "fur-IT", + "ga-IE", + "gd-GB", + "he-IL", + "hr-HR", + "hu-HU", + "hy-AM", + "id-ID", + "it-CH", + "it-IT", + "ja-JP", + "ka-GE", + "ko-KR", + "ky-KG", + "lt-LT", + "nb-NO", + "nl-BE", + "nl-NL", + "pt-PT", + "pt-BR", + "pl-PL", + "ro-RO", + "ru-RU", + "sco-GB", + "sh-HR", + "sk-SK", + "sl-SI", + "sr-SP", + "sv-SE", + "th-TH", + "tr-TR", + "uk-UA", + "vi-VN", + "zh-CN", + "zh-HK", + "zh-SG", + "zh-TW" + ] } diff --git a/packages/server/lib/new-admin/config/index.js b/packages/server/lib/new-admin/config/index.js index 0cabd55f..87fc75cf 100644 --- a/packages/server/lib/new-admin/config/index.js +++ b/packages/server/lib/new-admin/config/index.js @@ -7,10 +7,10 @@ const countries = require('./data/countries.json') const currenciesRec = require('./data/currencies.json') const languageRec = require('./data/languages.json') -function massageCurrencies (currencies) { +function massageCurrencies(currencies) { const convert = r => ({ code: r['Alphabetic Code'], - display: r['Currency'] + display: r['Currency'], }) const top5Codes = ['USD', 'EUR', 'GBP', 'CAD', 'AUD'] const mapped = _.map(convert, currencies) @@ -37,7 +37,7 @@ const massageCryptos = cryptos => { code: crypto['cryptoCode'], display: crypto['display'], codeDisplay: crypto['cryptoCodeDisplay'] ?? crypto['cryptoCode'], - isBeta: betaList.includes(crypto.cryptoCode) + isBeta: betaList.includes(crypto.cryptoCode), }) return _.map(convert, cryptos) diff --git a/packages/server/lib/new-admin/filters.js b/packages/server/lib/new-admin/filters.js index 4eb5dda7..8e152fe9 100644 --- a/packages/server/lib/new-admin/filters.js +++ b/packages/server/lib/new-admin/filters.js @@ -2,7 +2,7 @@ const db = require('../db') const cashInTx = require('../cash-in/cash-in-tx') const { CASH_OUT_TRANSACTION_STATES } = require('../cash-out/cash-out-helper') -function transaction () { +function transaction() { const sql = `SELECT DISTINCT * FROM ( SELECT 'type' AS type, NULL AS label, 'Cash In' AS value UNION SELECT 'type' AS type, NULL AS label, 'Cash Out' AS value UNION @@ -27,7 +27,7 @@ function transaction () { return db.any(sql) } -function customer () { +function customer() { const sql = `SELECT DISTINCT * FROM ( SELECT 'phone' AS type, phone AS value FROM customers WHERE phone IS NOT NULL UNION SELECT 'email' AS type, email AS value FROM customers WHERE email IS NOT NULL UNION diff --git a/packages/server/lib/new-admin/graphql/directives/auth.js b/packages/server/lib/new-admin/graphql/directives/auth.js index 637da16b..24725613 100644 --- a/packages/server/lib/new-admin/graphql/directives/auth.js +++ b/packages/server/lib/new-admin/graphql/directives/auth.js @@ -7,7 +7,7 @@ const { AuthenticationError } = require('../errors') function authDirectiveTransformer(schema, directiveName = 'auth') { return mapSchema(schema, { // For object types - [MapperKind.OBJECT_TYPE]: (objectType) => { + [MapperKind.OBJECT_TYPE]: objectType => { const directive = getDirective(schema, objectType, directiveName)?.[0] if (directive) { const requiredAuthRole = directive.requires @@ -15,7 +15,7 @@ function authDirectiveTransformer(schema, directiveName = 'auth') { } return objectType }, - + // For field definitions [MapperKind.OBJECT_FIELD]: (fieldConfig, _fieldName, typeName) => { const directive = getDirective(schema, fieldConfig, directiveName)?.[0] @@ -23,26 +23,30 @@ function authDirectiveTransformer(schema, directiveName = 'auth') { const requiredAuthRole = directive.requires fieldConfig._requiredAuthRole = requiredAuthRole } - + // Get the parent object type const objectType = schema.getType(typeName) - + // Apply auth check to the field's resolver const { resolve = defaultFieldResolver } = fieldConfig fieldConfig.resolve = function (root, args, context, info) { - const requiredRoles = fieldConfig._requiredAuthRole || objectType._requiredAuthRole - if (!requiredRoles) return resolve.apply(this, [root, args, context, info]) - + const requiredRoles = + fieldConfig._requiredAuthRole || objectType._requiredAuthRole + if (!requiredRoles) + return resolve.apply(this, [root, args, context, info]) + const user = context.req.session.user if (!user || !_.includes(_.upperCase(user.role), requiredRoles)) { - throw new AuthenticationError('You do not have permission to access this resource!') + throw new AuthenticationError( + 'You do not have permission to access this resource!', + ) } - + return resolve.apply(this, [root, args, context, info]) } - + return fieldConfig - } + }, }) } diff --git a/packages/server/lib/new-admin/graphql/errors.js b/packages/server/lib/new-admin/graphql/errors.js index 43d349c8..74206853 100644 --- a/packages/server/lib/new-admin/graphql/errors.js +++ b/packages/server/lib/new-admin/graphql/errors.js @@ -5,8 +5,8 @@ class AuthenticationError extends GraphQLError { constructor() { super('Authentication failed', { extensions: { - code: 'UNAUTHENTICATED' - } + code: 'UNAUTHENTICATED', + }, }) } } @@ -15,8 +15,8 @@ class InvalidCredentialsError extends GraphQLError { constructor() { super('Invalid credentials', { extensions: { - code: 'INVALID_CREDENTIALS' - } + code: 'INVALID_CREDENTIALS', + }, }) } } @@ -25,8 +25,8 @@ class UserAlreadyExistsError extends GraphQLError { constructor() { super('User already exists', { extensions: { - code: 'USER_ALREADY_EXISTS' - } + code: 'USER_ALREADY_EXISTS', + }, }) } } @@ -35,8 +35,8 @@ class InvalidTwoFactorError extends GraphQLError { constructor() { super('Invalid two-factor code', { extensions: { - code: 'INVALID_TWO_FACTOR_CODE' - } + code: 'INVALID_TWO_FACTOR_CODE', + }, }) } } @@ -45,8 +45,8 @@ class InvalidUrlError extends GraphQLError { constructor() { super('Invalid URL token', { extensions: { - code: 'INVALID_URL_TOKEN' - } + code: 'INVALID_URL_TOKEN', + }, }) } } @@ -55,8 +55,8 @@ class UserInputError extends GraphQLError { constructor() { super('User input error', { extensions: { - code: ApolloServerErrorCode.BAD_USER_INPUT - } + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, }) } } @@ -67,5 +67,5 @@ module.exports = { UserAlreadyExistsError, InvalidTwoFactorError, InvalidUrlError, - UserInputError -} \ No newline at end of file + UserInputError, +} diff --git a/packages/server/lib/new-admin/graphql/modules/authentication/FIDO2FAStrategy.js b/packages/server/lib/new-admin/graphql/modules/authentication/FIDO2FAStrategy.js index b8ab9d74..4d3a087c 100644 --- a/packages/server/lib/new-admin/graphql/modules/authentication/FIDO2FAStrategy.js +++ b/packages/server/lib/new-admin/graphql/modules/authentication/FIDO2FAStrategy.js @@ -12,60 +12,70 @@ const devMode = require('minimist')(process.argv.slice(2)).dev const REMEMBER_ME_AGE = 90 * T.day const generateAttestationOptions = (session, options) => { - return users.getUserById(options.userId).then(user => { - return Promise.all([credentials.getHardwareCredentialsByUserId(user.id), user]) - }).then(([userDevices, user]) => { - const opts = simpleWebauthn.generateAttestationOptions({ - rpName: 'Lamassu', - rpID: options.domain, - userName: user.username, - userID: user.id, - timeout: 60000, - attestationType: 'indirect', - excludeCredentials: userDevices.map(dev => ({ - id: dev.data.credentialID, - type: 'public-key', - transports: ['usb', 'ble', 'nfc', 'internal'] - })), - authenticatorSelection: { - userVerification: 'discouraged', - requireResidentKey: false - } + return users + .getUserById(options.userId) + .then(user => { + return Promise.all([ + credentials.getHardwareCredentialsByUserId(user.id), + user, + ]) }) - - session.webauthn = { - attestation: { - challenge: opts.challenge - } - } - - return opts - }) -} - -const generateAssertionOptions = (session, options) => { - return userManagement.authenticateUser(options.username, options.password).then(user => { - return credentials.getHardwareCredentialsByUserId(user.id).then(devices => { - const opts = simpleWebauthn.generateAssertionOptions({ + .then(([userDevices, user]) => { + const opts = simpleWebauthn.generateAttestationOptions({ + rpName: 'Lamassu', + rpID: options.domain, + userName: user.username, + userID: user.id, timeout: 60000, - allowCredentials: devices.map(dev => ({ + attestationType: 'indirect', + excludeCredentials: userDevices.map(dev => ({ id: dev.data.credentialID, type: 'public-key', - transports: ['usb', 'ble', 'nfc', 'internal'] + transports: ['usb', 'ble', 'nfc', 'internal'], })), - userVerification: 'discouraged', - rpID: options.domain + authenticatorSelection: { + userVerification: 'discouraged', + requireResidentKey: false, + }, }) session.webauthn = { - assertion: { - challenge: opts.challenge - } + attestation: { + challenge: opts.challenge, + }, } return opts }) - }) +} + +const generateAssertionOptions = (session, options) => { + return userManagement + .authenticateUser(options.username, options.password) + .then(user => { + return credentials + .getHardwareCredentialsByUserId(user.id) + .then(devices => { + const opts = simpleWebauthn.generateAssertionOptions({ + timeout: 60000, + allowCredentials: devices.map(dev => ({ + id: dev.data.credentialID, + type: 'public-key', + transports: ['usb', 'ble', 'nfc', 'internal'], + })), + userVerification: 'discouraged', + rpID: options.domain, + }) + + session.webauthn = { + assertion: { + challenge: opts.challenge, + }, + } + + return opts + }) + }) } const validateAttestation = (session, options) => { @@ -78,98 +88,112 @@ const validateAttestation = (session, options) => { credential: options.attestationResponse, expectedChallenge: `${expectedChallenge}`, expectedOrigin: `https://${options.domain}${devMode ? `:3001` : ``}`, - expectedRPID: options.domain - }) - ]) - .then(([user, verification]) => { - const { verified, attestationInfo } = verification + expectedRPID: options.domain, + }), + ]).then(([user, verification]) => { + const { verified, attestationInfo } = verification - if (!(verified || attestationInfo)) { - session.webauthn = null - return false - } + if (!(verified || attestationInfo)) { + session.webauthn = null + return false + } - const { - counter, - credentialPublicKey, - credentialID - } = attestationInfo + const { counter, credentialPublicKey, credentialID } = attestationInfo - return credentials.getHardwareCredentialsByUserId(user.id) - .then(userDevices => { - const existingDevice = userDevices.find(device => device.data.credentialID === credentialID) + return credentials + .getHardwareCredentialsByUserId(user.id) + .then(userDevices => { + const existingDevice = userDevices.find( + device => device.data.credentialID === credentialID, + ) - if (!existingDevice) { - const newDevice = { - counter, - credentialPublicKey, - credentialID - } - credentials.createHardwareCredential(user.id, newDevice) + if (!existingDevice) { + const newDevice = { + counter, + credentialPublicKey, + credentialID, } + credentials.createHardwareCredential(user.id, newDevice) + } - session.webauthn = null - return verified - }) - }) + session.webauthn = null + return verified + }) + }) } const validateAssertion = (session, options) => { - return userManagement.authenticateUser(options.username, options.password).then(user => { - const expectedChallenge = session.webauthn.assertion.challenge + return userManagement + .authenticateUser(options.username, options.password) + .then(user => { + const expectedChallenge = session.webauthn.assertion.challenge - return credentials.getHardwareCredentialsByUserId(user.id).then(devices => { - const dbAuthenticator = _.find(dev => { - return Buffer.from(dev.data.credentialID).compare(base64url.toBuffer(options.assertionResponse.rawId)) === 0 - }, devices) + return credentials + .getHardwareCredentialsByUserId(user.id) + .then(devices => { + const dbAuthenticator = _.find(dev => { + return ( + Buffer.from(dev.data.credentialID).compare( + base64url.toBuffer(options.assertionResponse.rawId), + ) === 0 + ) + }, devices) - if (!dbAuthenticator.data) { - throw new Error(`Could not find authenticator matching ${options.assertionResponse.id}`) - } + if (!dbAuthenticator.data) { + throw new Error( + `Could not find authenticator matching ${options.assertionResponse.id}`, + ) + } - const convertedAuthenticator = _.merge( - dbAuthenticator.data, - { credentialPublicKey: Buffer.from(dbAuthenticator.data.credentialPublicKey) } - ) + const convertedAuthenticator = _.merge(dbAuthenticator.data, { + credentialPublicKey: Buffer.from( + dbAuthenticator.data.credentialPublicKey, + ), + }) - let verification - try { - verification = simpleWebauthn.verifyAssertionResponse({ - credential: options.assertionResponse, - expectedChallenge: `${expectedChallenge}`, - expectedOrigin: `https://${options.domain}${devMode ? `:3001` : ``}`, - expectedRPID: options.domain, - authenticator: convertedAuthenticator - }) - } catch (err) { - console.error(err) - return false - } + let verification + try { + verification = simpleWebauthn.verifyAssertionResponse({ + credential: options.assertionResponse, + expectedChallenge: `${expectedChallenge}`, + expectedOrigin: `https://${options.domain}${devMode ? `:3001` : ``}`, + expectedRPID: options.domain, + authenticator: convertedAuthenticator, + }) + } catch (err) { + console.error(err) + return false + } - const { verified, assertionInfo } = verification + const { verified, assertionInfo } = verification - if (!verified) { - session.webauthn = null - return false - } + if (!verified) { + session.webauthn = null + return false + } - dbAuthenticator.data.counter = assertionInfo.newCounter - return credentials.updateHardwareCredential(dbAuthenticator) - .then(() => { - const finalUser = { id: user.id, username: user.username, role: user.role } - session.user = finalUser - if (options.rememberMe) session.cookie.maxAge = REMEMBER_ME_AGE + dbAuthenticator.data.counter = assertionInfo.newCounter + return credentials + .updateHardwareCredential(dbAuthenticator) + .then(() => { + const finalUser = { + id: user.id, + username: user.username, + role: user.role, + } + session.user = finalUser + if (options.rememberMe) session.cookie.maxAge = REMEMBER_ME_AGE - session.webauthn = null - return verified + session.webauthn = null + return verified + }) }) }) - }) } module.exports = { generateAttestationOptions, generateAssertionOptions, validateAttestation, - validateAssertion + validateAssertion, } diff --git a/packages/server/lib/new-admin/graphql/modules/authentication/FIDOPasswordlessStrategy.js b/packages/server/lib/new-admin/graphql/modules/authentication/FIDOPasswordlessStrategy.js index 26091bde..1581df3b 100644 --- a/packages/server/lib/new-admin/graphql/modules/authentication/FIDOPasswordlessStrategy.js +++ b/packages/server/lib/new-admin/graphql/modules/authentication/FIDOPasswordlessStrategy.js @@ -11,35 +11,41 @@ const devMode = require('minimist')(process.argv.slice(2)).dev const REMEMBER_ME_AGE = 90 * T.day const generateAttestationOptions = (session, options) => { - return users.getUserById(options.userId).then(user => { - return Promise.all([credentials.getHardwareCredentialsByUserId(user.id), user]) - }).then(([userDevices, user]) => { - const opts = simpleWebauthn.generateAttestationOptions({ - rpName: 'Lamassu', - rpID: options.domain, - userName: user.username, - userID: user.id, - timeout: 60000, - attestationType: 'indirect', - excludeCredentials: userDevices.map(dev => ({ - id: dev.data.credentialID, - type: 'public-key', - transports: ['usb', 'ble', 'nfc', 'internal'] - })), - authenticatorSelection: { - userVerification: 'discouraged', - requireResidentKey: false - } + return users + .getUserById(options.userId) + .then(user => { + return Promise.all([ + credentials.getHardwareCredentialsByUserId(user.id), + user, + ]) }) + .then(([userDevices, user]) => { + const opts = simpleWebauthn.generateAttestationOptions({ + rpName: 'Lamassu', + rpID: options.domain, + userName: user.username, + userID: user.id, + timeout: 60000, + attestationType: 'indirect', + excludeCredentials: userDevices.map(dev => ({ + id: dev.data.credentialID, + type: 'public-key', + transports: ['usb', 'ble', 'nfc', 'internal'], + })), + authenticatorSelection: { + userVerification: 'discouraged', + requireResidentKey: false, + }, + }) - session.webauthn = { - attestation: { - challenge: opts.challenge + session.webauthn = { + attestation: { + challenge: opts.challenge, + }, } - } - return opts - }) + return opts + }) } const generateAssertionOptions = (session, options) => { @@ -50,16 +56,16 @@ const generateAssertionOptions = (session, options) => { allowCredentials: devices.map(dev => ({ id: dev.data.credentialID, type: 'public-key', - transports: ['usb', 'ble', 'nfc', 'internal'] + transports: ['usb', 'ble', 'nfc', 'internal'], })), userVerification: 'discouraged', - rpID: options.domain + rpID: options.domain, }) session.webauthn = { assertion: { - challenge: opts.challenge - } + challenge: opts.challenge, + }, } return opts @@ -77,40 +83,38 @@ const validateAttestation = (session, options) => { credential: options.attestationResponse, expectedChallenge: `${expectedChallenge}`, expectedOrigin: `https://${options.domain}${devMode ? `:3001` : ``}`, - expectedRPID: options.domain - }) - ]) - .then(([user, verification]) => { - const { verified, attestationInfo } = verification + expectedRPID: options.domain, + }), + ]).then(([user, verification]) => { + const { verified, attestationInfo } = verification - if (!(verified || attestationInfo)) { - session.webauthn = null - return false - } + if (!(verified || attestationInfo)) { + session.webauthn = null + return false + } - const { - counter, - credentialPublicKey, - credentialID - } = attestationInfo + const { counter, credentialPublicKey, credentialID } = attestationInfo - return credentials.getHardwareCredentialsByUserId(user.id) - .then(userDevices => { - const existingDevice = userDevices.find(device => device.data.credentialID === credentialID) + return credentials + .getHardwareCredentialsByUserId(user.id) + .then(userDevices => { + const existingDevice = userDevices.find( + device => device.data.credentialID === credentialID, + ) - if (!existingDevice) { - const newDevice = { - counter, - credentialPublicKey, - credentialID - } - credentials.createHardwareCredential(user.id, newDevice) + if (!existingDevice) { + const newDevice = { + counter, + credentialPublicKey, + credentialID, } + credentials.createHardwareCredential(user.id, newDevice) + } - session.webauthn = null - return verified - }) - }) + session.webauthn = null + return verified + }) + }) } const validateAssertion = (session, options) => { @@ -119,17 +123,24 @@ const validateAssertion = (session, options) => { return credentials.getHardwareCredentialsByUserId(user.id).then(devices => { const dbAuthenticator = _.find(dev => { - return Buffer.from(dev.data.credentialID).compare(base64url.toBuffer(options.assertionResponse.rawId)) === 0 + return ( + Buffer.from(dev.data.credentialID).compare( + base64url.toBuffer(options.assertionResponse.rawId), + ) === 0 + ) }, devices) if (!dbAuthenticator.data) { - throw new Error(`Could not find authenticator matching ${options.assertionResponse.id}`) + throw new Error( + `Could not find authenticator matching ${options.assertionResponse.id}`, + ) } - const convertedAuthenticator = _.merge( - dbAuthenticator.data, - { credentialPublicKey: Buffer.from(dbAuthenticator.data.credentialPublicKey) } - ) + const convertedAuthenticator = _.merge(dbAuthenticator.data, { + credentialPublicKey: Buffer.from( + dbAuthenticator.data.credentialPublicKey, + ), + }) let verification try { @@ -138,7 +149,7 @@ const validateAssertion = (session, options) => { expectedChallenge: `${expectedChallenge}`, expectedOrigin: `https://${options.domain}${devMode ? `:3001` : ``}`, expectedRPID: options.domain, - authenticator: convertedAuthenticator + authenticator: convertedAuthenticator, }) } catch (err) { console.error(err) @@ -148,20 +159,22 @@ const validateAssertion = (session, options) => { const { verified, assertionInfo } = verification if (!verified) { - context.req.session.webauthn = null return false } dbAuthenticator.data.counter = assertionInfo.newCounter - return credentials.updateHardwareCredential(dbAuthenticator) - .then(() => { - const finalUser = { id: user.id, username: user.username, role: user.role } - session.user = finalUser - if (options.rememberMe) session.cookie.maxAge = REMEMBER_ME_AGE + return credentials.updateHardwareCredential(dbAuthenticator).then(() => { + const finalUser = { + id: user.id, + username: user.username, + role: user.role, + } + session.user = finalUser + if (options.rememberMe) session.cookie.maxAge = REMEMBER_ME_AGE - session.webauthn = null - return verified - }) + session.webauthn = null + return verified + }) }) }) } @@ -170,5 +183,5 @@ module.exports = { generateAttestationOptions, generateAssertionOptions, validateAttestation, - validateAssertion + validateAssertion, } diff --git a/packages/server/lib/new-admin/graphql/modules/authentication/FIDOUsernamelessStrategy.js b/packages/server/lib/new-admin/graphql/modules/authentication/FIDOUsernamelessStrategy.js index b547d4f5..0d41f240 100644 --- a/packages/server/lib/new-admin/graphql/modules/authentication/FIDOUsernamelessStrategy.js +++ b/packages/server/lib/new-admin/graphql/modules/authentication/FIDOUsernamelessStrategy.js @@ -22,19 +22,19 @@ const generateAttestationOptions = (session, options) => { excludeCredentials: devices.map(dev => ({ id: dev.data.credentialID, type: 'public-key', - transports: ['usb', 'ble', 'nfc', 'internal'] + transports: ['usb', 'ble', 'nfc', 'internal'], })), authenticatorSelection: { authenticatorAttachment: 'cross-platform', userVerification: 'discouraged', - requireResidentKey: false - } + requireResidentKey: false, + }, }) session.webauthn = { attestation: { - challenge: opts.challenge - } + challenge: opts.challenge, + }, } return opts @@ -48,16 +48,16 @@ const generateAssertionOptions = (session, options) => { allowCredentials: devices.map(dev => ({ id: dev.data.credentialID, type: 'public-key', - transports: ['usb', 'ble', 'nfc', 'internal'] + transports: ['usb', 'ble', 'nfc', 'internal'], })), userVerification: 'discouraged', - rpID: options.domain + rpID: options.domain, }) session.webauthn = { assertion: { - challenge: opts.challenge - } + challenge: opts.challenge, + }, } return opts }) @@ -73,50 +73,52 @@ const validateAttestation = (session, options) => { credential: options.attestationResponse, expectedChallenge: `${expectedChallenge}`, expectedOrigin: `https://${options.domain}${devMode ? `:3001` : ``}`, - expectedRPID: options.domain - }) - ]) - .then(([user, verification]) => { - const { verified, attestationInfo } = verification + expectedRPID: options.domain, + }), + ]).then(([user, verification]) => { + const { verified, attestationInfo } = verification + + if (!(verified || attestationInfo)) { + session.webauthn = null + return verified + } + + const { + fmt, + counter, + aaguid, + credentialPublicKey, + credentialID, + credentialType, + userVerified, + attestationObject, + } = attestationInfo + + return credentials + .getHardwareCredentialsByUserId(user.id) + .then(userDevices => { + const existingDevice = userDevices.find( + device => device.data.credentialID === credentialID, + ) + + if (!existingDevice) { + const newDevice = { + fmt, + counter, + aaguid, + credentialPublicKey, + credentialID, + credentialType, + userVerified, + attestationObject, + } + credentials.createHardwareCredential(user.id, newDevice) + } - if (!(verified || attestationInfo)) { session.webauthn = null return verified - } - - const { - fmt, - counter, - aaguid, - credentialPublicKey, - credentialID, - credentialType, - userVerified, - attestationObject - } = attestationInfo - - return credentials.getHardwareCredentialsByUserId(user.id) - .then(userDevices => { - const existingDevice = userDevices.find(device => device.data.credentialID === credentialID) - - if (!existingDevice) { - const newDevice = { - fmt, - counter, - aaguid, - credentialPublicKey, - credentialID, - credentialType, - userVerified, - attestationObject - } - credentials.createHardwareCredential(user.id, newDevice) - } - - session.webauthn = null - return verified - }) - }) + }) + }) } const validateAssertion = (session, options) => { @@ -124,17 +126,24 @@ const validateAssertion = (session, options) => { return credentials.getHardwareCredentials().then(devices => { const dbAuthenticator = _.find(dev => { - return Buffer.from(dev.data.credentialID).compare(base64url.toBuffer(options.assertionResponse.rawId)) === 0 + return ( + Buffer.from(dev.data.credentialID).compare( + base64url.toBuffer(options.assertionResponse.rawId), + ) === 0 + ) }, devices) if (!dbAuthenticator.data) { - throw new Error(`Could not find authenticator matching ${options.assertionResponse.id}`) + throw new Error( + `Could not find authenticator matching ${options.assertionResponse.id}`, + ) } - const convertedAuthenticator = _.merge( - dbAuthenticator.data, - { credentialPublicKey: Buffer.from(dbAuthenticator.data.credentialPublicKey) } - ) + const convertedAuthenticator = _.merge(dbAuthenticator.data, { + credentialPublicKey: Buffer.from( + dbAuthenticator.data.credentialPublicKey, + ), + }) let verification try { @@ -143,7 +152,7 @@ const validateAssertion = (session, options) => { expectedChallenge: `${expectedChallenge}`, expectedOrigin: `https://${options.domain}${devMode ? `:3001` : ``}`, expectedRPID: options.domain, - authenticator: convertedAuthenticator + authenticator: convertedAuthenticator, }) } catch (err) { console.error(err) @@ -160,16 +169,19 @@ const validateAssertion = (session, options) => { dbAuthenticator.data.counter = assertionInfo.newCounter return Promise.all([ credentials.updateHardwareCredential(dbAuthenticator), - users.getUserById(dbAuthenticator.user_id) - ]) - .then(([_, user]) => { - const finalUser = { id: user.id, username: user.username, role: user.role } - session.user = finalUser - session.cookie.maxAge = REMEMBER_ME_AGE - - session.webauthn = null - return verified - }) + users.getUserById(dbAuthenticator.user_id), + ]).then(([, user]) => { + const finalUser = { + id: user.id, + username: user.username, + role: user.role, + } + session.user = finalUser + session.cookie.maxAge = REMEMBER_ME_AGE + + session.webauthn = null + return verified + }) }) } @@ -177,5 +189,5 @@ module.exports = { generateAttestationOptions, generateAssertionOptions, validateAttestation, - validateAssertion + validateAssertion, } diff --git a/packages/server/lib/new-admin/graphql/modules/authentication/index.js b/packages/server/lib/new-admin/graphql/modules/authentication/index.js index 1b4591cc..924cf427 100644 --- a/packages/server/lib/new-admin/graphql/modules/authentication/index.js +++ b/packages/server/lib/new-admin/graphql/modules/authentication/index.js @@ -5,7 +5,7 @@ const FIDOUsernameless = require('./FIDOUsernamelessStrategy') const STRATEGIES = { FIDO2FA, FIDOPasswordless, - FIDOUsernameless + FIDOUsernameless, } // FIDO2FA, FIDOPasswordless or FIDOUsernameless @@ -13,5 +13,5 @@ const CHOSEN_STRATEGY = 'FIDO2FA' module.exports = { CHOSEN_STRATEGY, - strategy: STRATEGIES[CHOSEN_STRATEGY] + strategy: STRATEGIES[CHOSEN_STRATEGY], } diff --git a/packages/server/lib/new-admin/graphql/modules/userManagement.js b/packages/server/lib/new-admin/graphql/modules/userManagement.js index 320c8f4c..492afa97 100644 --- a/packages/server/lib/new-admin/graphql/modules/userManagement.js +++ b/packages/server/lib/new-admin/graphql/modules/userManagement.js @@ -14,11 +14,16 @@ const credentials = require('../../../hardware-credentials') const REMEMBER_ME_AGE = 90 * T.day const authenticateUser = (username, password) => { - return users.getUserByUsername(username) + return users + .getUserByUsername(username) .then(user => { const hashedPassword = user.password - if (!hashedPassword || !user.enabled) throw new authErrors.InvalidCredentialsError() - return Promise.all([argon2.verify(hashedPassword, password), hashedPassword]) + if (!hashedPassword || !user.enabled) + throw new authErrors.InvalidCredentialsError() + return Promise.all([ + argon2.verify(hashedPassword, password), + hashedPassword, + ]) }) .then(([isMatch, hashedPassword]) => { if (!isMatch) throw new authErrors.InvalidCredentialsError() @@ -32,7 +37,9 @@ const authenticateUser = (username, password) => { const destroySessionIfSameUser = (context, user) => { const sessionUser = getUserFromCookie(context) - if (sessionUser && user.id === sessionUser.id) { context.req.session.destroy() } + if (sessionUser && user.id === sessionUser.id) { + context.req.session.destroy() + } } const destroySessionIfBeingUsed = (sessID, context) => { @@ -56,15 +63,13 @@ const initializeSession = (context, user, rememberMe) => { } const executeProtectedAction = (code, id, context, action) => { - return users.getUserById(id) - .then(user => { - if (user.role !== 'superuser') { - return action() - } + return users.getUserById(id).then(user => { + if (user.role !== 'superuser') { + return action() + } - return confirm2FA(code, context) - .then(() => action()) - }) + return confirm2FA(code, context).then(() => action()) + }) } const getUserData = context => { @@ -79,10 +84,18 @@ const get2FASecret = (username, password) => { return authenticateUser(username, password) .then(user => { const secret = otplib.authenticator.generateSecret() - const otpauth = otplib.authenticator.keyuri(user.username, constants.AUTHENTICATOR_ISSUER_ENTITY, secret) - return Promise.all([users.saveTemp2FASecret(user.id, secret), secret, otpauth]) + const otpauth = otplib.authenticator.keyuri( + user.username, + constants.AUTHENTICATOR_ISSUER_ENTITY, + secret, + ) + return Promise.all([ + users.saveTemp2FASecret(user.id, secret), + secret, + otpauth, + ]) }) - .then(([_, secret, otpauth]) => { + .then(([, secret, otpauth]) => { return { secret, otpauth } }) } @@ -103,35 +116,43 @@ const confirm2FA = (token, context) => { const validateRegisterLink = token => { if (!token) throw new authErrors.InvalidUrlError() - return users.validateUserRegistrationToken(token) - .then(r => { - if (!r.success) throw new authErrors.InvalidUrlError() - return { username: r.username, role: r.role } - }) + return users.validateUserRegistrationToken(token).then(r => { + if (!r.success) throw new authErrors.InvalidUrlError() + return { username: r.username, role: r.role } + }) } const validateResetPasswordLink = token => { if (!token) throw new authErrors.InvalidUrlError() - return users.validateAuthToken(token, 'reset_password') - .then(r => { - if (!r.success) throw new authErrors.InvalidUrlError() - return { id: r.userID } - }) + return users.validateAuthToken(token, 'reset_password').then(r => { + if (!r.success) throw new authErrors.InvalidUrlError() + return { id: r.userID } + }) } const validateReset2FALink = token => { if (!token) throw new authErrors.InvalidUrlError() - return users.validateAuthToken(token, 'reset_twofa') + return users + .validateAuthToken(token, 'reset_twofa') .then(r => { if (!r.success) throw new authErrors.InvalidUrlError() return users.getUserById(r.userID) }) .then(user => { const secret = otplib.authenticator.generateSecret() - const otpauth = otplib.authenticator.keyuri(user.username, constants.AUTHENTICATOR_ISSUER_ENTITY, secret) - return Promise.all([users.saveTemp2FASecret(user.id, secret), user, secret, otpauth]) + const otpauth = otplib.authenticator.keyuri( + user.username, + constants.AUTHENTICATOR_ISSUER_ENTITY, + secret, + ) + return Promise.all([ + users.saveTemp2FASecret(user.id, secret), + user, + secret, + otpauth, + ]) }) - .then(([_, user, secret, otpauth]) => { + .then(([, user, secret, otpauth]) => { return { user_id: user.id, secret, otpauth } }) } @@ -144,7 +165,10 @@ const deleteSession = (sessionID, context) => { const login = (username, password) => { return authenticateUser(username, password) .then(user => { - return Promise.all([credentials.getHardwareCredentialsByUserId(user.id), user.twofa_code]) + return Promise.all([ + credentials.getHardwareCredentialsByUserId(user.id), + user.twofa_code, + ]) }) .then(([devices, twoFASecret]) => { if (!_.isEmpty(devices)) return 'FIDO' @@ -153,21 +177,32 @@ const login = (username, password) => { } const input2FA = (username, password, rememberMe, code, context) => { - return authenticateUser(username, password) - .then(user => { - const secret = user.twofa_code - const isCodeValid = otplib.authenticator.verify({ token: code, secret: secret }) - if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() - - initializeSession(context, user, rememberMe) - return true + return authenticateUser(username, password).then(user => { + const secret = user.twofa_code + const isCodeValid = otplib.authenticator.verify({ + token: code, + secret: secret, }) + if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() + + initializeSession(context, user, rememberMe) + return true + }) } -const setup2FA = (username, password, rememberMe, codeConfirmation, context) => { +const setup2FA = ( + username, + password, + rememberMe, + codeConfirmation, + context, +) => { return authenticateUser(username, password) .then(user => { - const isCodeValid = otplib.authenticator.verify({ token: codeConfirmation, secret: user.temp_twofa_code }) + const isCodeValid = otplib.authenticator.verify({ + token: codeConfirmation, + secret: user.temp_twofa_code, + }) if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() initializeSession(context, user, rememberMe) @@ -202,24 +237,23 @@ const createReset2FAToken = (code, userID, context) => { } const createRegisterToken = (username, role) => { - return users.getUserByUsername(username) - .then(user => { - if (user) throw new authErrors.UserAlreadyExistsError() + return users.getUserByUsername(username).then(user => { + if (user) throw new authErrors.UserAlreadyExistsError() - return users.createUserRegistrationToken(username, role) - }) + return users.createUserRegistrationToken(username, role) + }) } const register = (token, username, password, role) => { - return users.getUserByUsername(username) - .then(user => { - if (user) throw new authErrors.UserAlreadyExistsError() - return users.register(token, username, password, role).then(() => true) - }) + return users.getUserByUsername(username).then(user => { + if (user) throw new authErrors.UserAlreadyExistsError() + return users.register(token, username, password, role).then(() => true) + }) } const resetPassword = (token, userID, newPassword, context) => { - return users.getUserById(userID) + return users + .getUserById(userID) .then(user => { destroySessionIfSameUser(context, user) return users.updatePassword(token, user.id, newPassword) @@ -228,9 +262,13 @@ const resetPassword = (token, userID, newPassword, context) => { } const reset2FA = (token, userID, code, context) => { - return users.getUserById(userID) + return users + .getUserById(userID) .then(user => { - const isCodeValid = otplib.authenticator.verify({ token: code, secret: user.temp_twofa_code }) + const isCodeValid = otplib.authenticator.verify({ + token: code, + secret: user.temp_twofa_code, + }) if (!isCodeValid) throw new authErrors.InvalidTwoFactorError() destroySessionIfSameUser(context, user) @@ -240,7 +278,10 @@ const reset2FA = (token, userID, code, context) => { } const getToken = context => { - if (_.isNil(context.req.cookies['lamassu_sid']) || _.isNil(context.req.session.user.id)) + if ( + _.isNil(context.req.cookies['lamassu_sid']) || + _.isNil(context.req.session.user.id) + ) throw new authErrors.AuthenticationError('Authentication failed') return context.req.session.user.id @@ -267,5 +308,5 @@ module.exports = { register, resetPassword, reset2FA, - getToken + getToken, } diff --git a/packages/server/lib/new-admin/graphql/resolvers/bill.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/bill.resolver.js index 7128332b..b899d517 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/bill.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/bill.resolver.js @@ -2,8 +2,8 @@ const bills = require('../../services/bills') const resolvers = { Query: { - bills: (...[, { filters }]) => bills.getBills(filters) - } + bills: (...[, { filters }]) => bills.getBills(filters), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/blacklist.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/blacklist.resolver.js index e0b63d53..6effa47e 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/blacklist.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/blacklist.resolver.js @@ -3,7 +3,7 @@ const blacklist = require('../../../blacklist') const resolvers = { Query: { blacklist: () => blacklist.getBlacklist(), - blacklistMessages: () => blacklist.getMessages() + blacklistMessages: () => blacklist.getMessages(), }, Mutation: { deleteBlacklistRow: (...[, { address }]) => @@ -11,8 +11,8 @@ const resolvers = { insertBlacklistRow: (...[, { address }]) => blacklist.insertIntoBlacklist(address), editBlacklistMessage: (...[, { id, content }]) => - blacklist.editBlacklistMessage(id, content) - } + blacklist.editBlacklistMessage(id, content), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/cashbox.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/cashbox.resolver.js index 193666c5..31c3005d 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/cashbox.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/cashbox.resolver.js @@ -5,13 +5,21 @@ const logDateFormat = require('../../../logs').logDateFormat const resolvers = { Query: { cashboxBatches: () => cashbox.getBatches(), - cashboxBatchesCsv: (...[, { from, until, timezone }]) => cashbox.getBatches(from, until) - .then(data => parseAsync(logDateFormat(timezone, cashbox.logFormatter(data), ['created']))) + cashboxBatchesCsv: (...[, { from, until, timezone }]) => + cashbox + .getBatches(from, until) + .then(data => + parseAsync( + logDateFormat(timezone, cashbox.logFormatter(data), ['created']), + ), + ), }, Mutation: { - createBatch: (...[, { deviceId, cashboxCount }]) => cashbox.createCashboxBatch(deviceId, cashboxCount), - editBatch: (...[, { id, performedBy }]) => cashbox.editBatchById(id, performedBy) - } + createBatch: (...[, { deviceId, cashboxCount }]) => + cashbox.createCashboxBatch(deviceId, cashboxCount), + editBatch: (...[, { id, performedBy }]) => + cashbox.editBatchById(id, performedBy), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/config.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/config.resolver.js index 6bb38a22..e793541c 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/config.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/config.resolver.js @@ -1,11 +1,15 @@ -const { accounts: accountsConfig, countries, languages } = require('../../config') +const { + accounts: accountsConfig, + countries, + languages, +} = require('../../config') const resolver = { Query: { countries: () => countries, languages: () => languages, - accountsConfig: () => accountsConfig - } + accountsConfig: () => accountsConfig, + }, } module.exports = resolver diff --git a/packages/server/lib/new-admin/graphql/resolvers/currency.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/currency.resolver.js index dba21069..2574298b 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/currency.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/currency.resolver.js @@ -3,8 +3,8 @@ const { coins, currencies } = require('../../config') const resolver = { Query: { currencies: () => currencies, - cryptoCurrencies: () => coins - } + cryptoCurrencies: () => coins, + }, } module.exports = resolver diff --git a/packages/server/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js index fefdcf6b..0b0f99d3 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js @@ -2,32 +2,55 @@ const authentication = require('../modules/userManagement') const queries = require('../../services/customInfoRequests') const DataLoader = require('dataloader') -const customerCustomInfoRequestsLoader = new DataLoader(ids => queries.batchGetAllCustomInfoRequestsForCustomer(ids), { cache: false }) +const customerCustomInfoRequestsLoader = new DataLoader( + ids => queries.batchGetAllCustomInfoRequestsForCustomer(ids), + { cache: false }, +) -const customInfoRequestLoader = new DataLoader(ids => queries.batchGetCustomInfoRequest(ids), { cache: false }) +const customInfoRequestLoader = new DataLoader( + ids => queries.batchGetCustomInfoRequest(ids), + { cache: false }, +) const resolvers = { Customer: { - customInfoRequests: parent => customerCustomInfoRequestsLoader.load(parent.id) + customInfoRequests: parent => + customerCustomInfoRequestsLoader.load(parent.id), }, CustomRequestData: { - customInfoRequest: parent => customInfoRequestLoader.load(parent.infoRequestId) + customInfoRequest: parent => + customInfoRequestLoader.load(parent.infoRequestId), }, Query: { - customInfoRequests: (...[, { onlyEnabled }]) => queries.getCustomInfoRequests(onlyEnabled), - customerCustomInfoRequests: (...[, { customerId }]) => queries.getAllCustomInfoRequestsForCustomer(customerId), - customerCustomInfoRequest: (...[, { customerId, infoRequestId }]) => queries.getCustomInfoRequestForCustomer(customerId, infoRequestId) + customInfoRequests: (...[, { onlyEnabled }]) => + queries.getCustomInfoRequests(onlyEnabled), + customerCustomInfoRequests: (...[, { customerId }]) => + queries.getAllCustomInfoRequestsForCustomer(customerId), + customerCustomInfoRequest: (...[, { customerId, infoRequestId }]) => + queries.getCustomInfoRequestForCustomer(customerId, infoRequestId), }, Mutation: { - insertCustomInfoRequest: (...[, { customRequest }]) => queries.addCustomInfoRequest(customRequest), - removeCustomInfoRequest: (...[, { id }]) => queries.removeCustomInfoRequest(id), - editCustomInfoRequest: (...[, { id, customRequest }]) => queries.editCustomInfoRequest(id, customRequest), - setAuthorizedCustomRequest: (...[, { customerId, infoRequestId, override }, context]) => { + insertCustomInfoRequest: (...[, { customRequest }]) => + queries.addCustomInfoRequest(customRequest), + removeCustomInfoRequest: (...[, { id }]) => + queries.removeCustomInfoRequest(id), + editCustomInfoRequest: (...[, { id, customRequest }]) => + queries.editCustomInfoRequest(id, customRequest), + setAuthorizedCustomRequest: ( + ...[, { customerId, infoRequestId, override }, context] + ) => { const token = authentication.getToken(context) - return queries.setAuthorizedCustomRequest(customerId, infoRequestId, override, token) + return queries.setAuthorizedCustomRequest( + customerId, + infoRequestId, + override, + token, + ) }, - setCustomerCustomInfoRequest: (...[, { customerId, infoRequestId, data }]) => queries.setCustomerData(customerId, infoRequestId, data) - } + setCustomerCustomInfoRequest: ( + ...[, { customerId, infoRequestId, data }] + ) => queries.setCustomerData(customerId, infoRequestId, data), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/customer.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/customer.resolver.js index 30f5159c..bdd7c640 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/customer.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/customer.resolver.js @@ -6,41 +6,56 @@ const customerNotes = require('../../../customer-notes') const machineLoader = require('../../../machine-loader') const addLastUsedMachineName = customer => - (customer.lastUsedMachine ? machineLoader.getMachineName(customer.lastUsedMachine) : Promise.resolve(null)) - .then(lastUsedMachineName => Object.assign(customer, { lastUsedMachineName })) + (customer.lastUsedMachine + ? machineLoader.getMachineName(customer.lastUsedMachine) + : Promise.resolve(null) + ).then(lastUsedMachineName => + Object.assign(customer, { lastUsedMachineName }), + ) const resolvers = { Customer: { - isAnonymous: parent => (parent.customerId === anonymous.uuid) + isAnonymous: parent => parent.customerId === anonymous.uuid, }, Query: { - customers: (...[, { phone, email, name, address, id }]) => customers.getCustomersList(phone, name, address, id, email), - customer: (...[, { customerId }]) => customers.getCustomerById(customerId).then(addLastUsedMachineName), - customerFilters: () => filters.customer() + customers: (...[, { phone, email, name, address, id }]) => + customers.getCustomersList(phone, name, address, id, email), + customer: (...[, { customerId }]) => + customers.getCustomerById(customerId).then(addLastUsedMachineName), + customerFilters: () => filters.customer(), }, Mutation: { - setCustomer: (root, { customerId, customerInput }, context, info) => { + setCustomer: (root, { customerId, customerInput }, context) => { const token = authentication.getToken(context) - if (customerId === anonymous.uuid) return customers.getCustomerById(customerId) + if (customerId === anonymous.uuid) + return customers.getCustomerById(customerId) return customers.updateCustomer(customerId, customerInput, token) }, - addCustomField: (...[, { customerId, label, value }]) => customers.addCustomField(customerId, label, value), - saveCustomField: (...[, { customerId, fieldId, value }]) => customers.saveCustomField(customerId, fieldId, value), - removeCustomField: (...[, [ { customerId, fieldId } ]]) => customers.removeCustomField(customerId, fieldId), + addCustomField: (...[, { customerId, label, value }]) => + customers.addCustomField(customerId, label, value), + saveCustomField: (...[, { customerId, fieldId, value }]) => + customers.saveCustomField(customerId, fieldId, value), + removeCustomField: (...[, [{ customerId, fieldId }]]) => + customers.removeCustomField(customerId, fieldId), editCustomer: async (root, { customerId, customerEdit }, context) => { const token = authentication.getToken(context) const editedData = await customerEdit return customers.edit(customerId, editedData, token) }, - replacePhoto: async (root, { customerId, photoType, newPhoto }, context) => { + replacePhoto: async ( + root, + { customerId, photoType, newPhoto }, + context, + ) => { const token = authentication.getToken(context) const { file } = newPhoto const photo = await file if (!photo) return customers.getCustomerById(customerId) - return customers.updateEditedPhoto(customerId, photo, photoType) + return customers + .updateEditedPhoto(customerId, photo, photoType) .then(newPatch => customers.edit(customerId, newPatch, token)) }, - deleteEditedData: (root, { customerId, customerEdit }) => { + deleteEditedData: (root, { customerId }) => { // TODO: NOT IMPLEMENTING THIS FEATURE FOR THE CURRENT VERSION return customers.getCustomerById(customerId) }, @@ -55,12 +70,13 @@ const resolvers = { deleteCustomerNote: (...[, { noteId }]) => { return customerNotes.deleteCustomerNote(noteId) }, - createCustomer: (...[, { phoneNumber }]) => customers.add({ phone: phoneNumber }), + createCustomer: (...[, { phoneNumber }]) => + customers.add({ phone: phoneNumber }), enableTestCustomer: (...[, { customerId }]) => customers.enableTestCustomer(customerId), disableTestCustomer: (...[, { customerId }]) => - customers.disableTestCustomer(customerId) - } + customers.disableTestCustomer(customerId), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/funding.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/funding.resolver.js index b9b1c16c..abe217ec 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/funding.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/funding.resolver.js @@ -2,8 +2,8 @@ const funding = require('../../services/funding') const resolvers = { Query: { - funding: () => funding.getFunding() - } + funding: () => funding.getFunding(), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/index.js b/packages/server/lib/new-admin/graphql/resolvers/index.js index ea3cb3fa..d7656198 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/index.js +++ b/packages/server/lib/new-admin/graphql/resolvers/index.js @@ -47,7 +47,7 @@ const resolvers = [ status, transaction, user, - version + version, ] module.exports = mergeResolvers(resolvers) diff --git a/packages/server/lib/new-admin/graphql/resolvers/log.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/log.resolver.js index 3ed9276a..543b83ab 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/log.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/log.resolver.js @@ -1,5 +1,4 @@ const { parseAsync } = require('json2csv') -const _ = require('lodash/fp') const logs = require('../../../logs') const serverLogs = require('../../services/server-logs') @@ -8,15 +7,23 @@ const resolvers = { Query: { machineLogs: (...[, { deviceId, from, until, limit, offset }]) => logs.simpleGetMachineLogs(deviceId, from, until, limit, offset), - machineLogsCsv: (...[, { deviceId, from, until, limit, offset, timezone }]) => - logs.simpleGetMachineLogs(deviceId, from, until, limit, offset) - .then(res => parseAsync(logs.logDateFormat(timezone, res, ['timestamp']))), + machineLogsCsv: ( + ...[, { deviceId, from, until, limit, offset, timezone }] + ) => + logs + .simpleGetMachineLogs(deviceId, from, until, limit, offset) + .then(res => + parseAsync(logs.logDateFormat(timezone, res, ['timestamp'])), + ), serverLogs: (...[, { from, until, limit, offset }]) => serverLogs.getServerLogs(from, until, limit, offset), serverLogsCsv: (...[, { from, until, limit, offset, timezone }]) => - serverLogs.getServerLogs(from, until, limit, offset) - .then(res => parseAsync(logs.logDateFormat(timezone, res, ['timestamp']))) - } + serverLogs + .getServerLogs(from, until, limit, offset) + .then(res => + parseAsync(logs.logDateFormat(timezone, res, ['timestamp'])), + ), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/loyalty.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/loyalty.resolver.js index dd63da4b..532d458a 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/loyalty.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/loyalty.resolver.js @@ -3,24 +3,30 @@ const DataLoader = require('dataloader') const loyalty = require('../../../loyalty') const { getSlimCustomerByIdBatch } = require('../../../customers') -const customerLoader = new DataLoader(ids => { - return getSlimCustomerByIdBatch(ids) -}, { cache: false }) +const customerLoader = new DataLoader( + ids => { + return getSlimCustomerByIdBatch(ids) + }, + { cache: false }, +) const resolvers = { IndividualDiscount: { - customer: parent => customerLoader.load(parent.customerId) + customer: parent => customerLoader.load(parent.customerId), }, Query: { promoCodes: () => loyalty.getAvailablePromoCodes(), - individualDiscounts: () => loyalty.getAvailableIndividualDiscounts() + individualDiscounts: () => loyalty.getAvailableIndividualDiscounts(), }, Mutation: { - createPromoCode: (...[, { code, discount }]) => loyalty.createPromoCode(code, discount), + createPromoCode: (...[, { code, discount }]) => + loyalty.createPromoCode(code, discount), deletePromoCode: (...[, { codeId }]) => loyalty.deletePromoCode(codeId), - createIndividualDiscount: (...[, { customerId, discount }]) => loyalty.createIndividualDiscount(customerId, discount), - deleteIndividualDiscount: (...[, { discountId }]) => loyalty.deleteIndividualDiscount(discountId) - } + createIndividualDiscount: (...[, { customerId, discount }]) => + loyalty.createIndividualDiscount(customerId, discount), + deleteIndividualDiscount: (...[, { discountId }]) => + loyalty.deleteIndividualDiscount(discountId), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/machine.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/machine.resolver.js index ebfbeaa2..1a404014 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/machine.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/machine.resolver.js @@ -3,25 +3,29 @@ const DataLoader = require('dataloader') const { machineAction } = require('../../services/machines') const machineLoader = require('../../../machine-loader') -const machineEventsByIdBatch = require('../../../postgresql_interface').machineEventsByIdBatch +const machineEventsByIdBatch = + require('../../../postgresql_interface').machineEventsByIdBatch -const machineEventsLoader = new DataLoader(ids => { - return machineEventsByIdBatch(ids) -}, { cache: false }) +const machineEventsLoader = new DataLoader( + ids => { + return machineEventsByIdBatch(ids) + }, + { cache: false }, +) const resolvers = { Machine: { - latestEvent: parent => machineEventsLoader.load(parent.deviceId) + latestEvent: parent => machineEventsLoader.load(parent.deviceId), }, Query: { machines: () => machineLoader.getMachineNames(), machine: (...[, { deviceId }]) => machineLoader.getMachine(deviceId), - unpairedMachines: () => machineLoader.getUnpairedMachines() + unpairedMachines: () => machineLoader.getUnpairedMachines(), }, Mutation: { machineAction: (...[, { deviceId, action, cashUnits, newName }, context]) => - machineAction({ deviceId, action, cashUnits, newName }, context) - } + machineAction({ deviceId, action, cashUnits, newName }, context), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/market.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/market.resolver.js index 49864417..56f178b7 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/market.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/market.resolver.js @@ -2,8 +2,8 @@ const exchange = require('../../../exchange') const resolvers = { Query: { - getMarkets: () => exchange.getMarkets() - } + getMarkets: () => exchange.getMarkets(), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/notification.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/notification.resolver.js index 4a2db72a..d7dcd705 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/notification.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/notification.resolver.js @@ -4,12 +4,13 @@ const resolvers = { Query: { notifications: () => notifierQueries.getNotifications(), hasUnreadNotifications: () => notifierQueries.hasUnreadNotifications(), - alerts: () => notifierQueries.getAlerts() + alerts: () => notifierQueries.getAlerts(), }, Mutation: { - toggleClearNotification: (...[, { id, read }]) => notifierQueries.setRead(id, read), - clearAllNotifications: () => notifierQueries.markAllAsRead() - } + toggleClearNotification: (...[, { id, read }]) => + notifierQueries.setRead(id, read), + clearAllNotifications: () => notifierQueries.markAllAsRead(), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/pairing.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/pairing.resolver.js index 510a6ef1..c3d35565 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/pairing.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/pairing.resolver.js @@ -2,8 +2,8 @@ const pairing = require('../../services/pairing') const resolvers = { Mutation: { - createPairingTotem: (...[, { name }]) => pairing.totem(name) - } + createPairingTotem: (...[, { name }]) => pairing.totem(name), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/rates.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/rates.resolver.js index dffa74e1..80751a7a 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/rates.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/rates.resolver.js @@ -10,12 +10,12 @@ const resolvers = { return pi.getRawRates().then(r => { return { withCommissions: pi.buildRates(r), - withoutCommissions: pi.buildRatesNoCommission(r) + withoutCommissions: pi.buildRatesNoCommission(r), } }) }), - fiatRates: () => forex.getFiatRates() - } + fiatRates: () => forex.getFiatRates(), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/sanctions.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/sanctions.resolver.js index bb1a8862..236cc63f 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/sanctions.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/sanctions.resolver.js @@ -6,8 +6,8 @@ const resolvers = { checkAgainstSanctions: (...[, { customerId }, context]) => { const token = authentication.getToken(context) return sanctions.checkByUser(customerId, token) - } - } + }, + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/scalar.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/scalar.resolver.js index ddfc12ac..2c6cbdba 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/scalar.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/scalar.resolver.js @@ -1,9 +1,13 @@ -const { DateTimeISOResolver, JSONResolver, JSONObjectResolver } = require('graphql-scalars') +const { + DateTimeISOResolver, + JSONResolver, + JSONObjectResolver, +} = require('graphql-scalars') const resolvers = { JSON: JSONResolver, JSONObject: JSONObjectResolver, - DateTimeISO: DateTimeISOResolver + DateTimeISO: DateTimeISOResolver, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/settings.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/settings.resolver.js index e0453511..551cfaee 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/settings.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/settings.resolver.js @@ -3,12 +3,13 @@ const settingsLoader = require('../../../new-settings-loader') const resolvers = { Query: { accounts: () => settingsLoader.showAccounts(), - config: () => settingsLoader.loadLatestConfigOrNone() + config: () => settingsLoader.loadLatestConfigOrNone(), }, Mutation: { - saveAccounts: (...[, { accounts }]) => settingsLoader.saveAccounts(accounts), + saveAccounts: (...[, { accounts }]) => + settingsLoader.saveAccounts(accounts), saveConfig: (...[, { config }]) => settingsLoader.saveConfig(config), - } + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/sms.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/sms.resolver.js index 8098837b..df445e4a 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/sms.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/sms.resolver.js @@ -2,13 +2,14 @@ const smsNotices = require('../../../sms-notices') const resolvers = { Query: { - SMSNotices: () => smsNotices.getSMSNotices() + SMSNotices: () => smsNotices.getSMSNotices(), }, Mutation: { - editSMSNotice: (...[, { id, event, message }]) => smsNotices.editSMSNotice(id, event, message), + editSMSNotice: (...[, { id, event, message }]) => + smsNotices.editSMSNotice(id, event, message), enableSMSNotice: (...[, { id }]) => smsNotices.enableSMSNotice(id), - disableSMSNotice: (...[, { id }]) => smsNotices.disableSMSNotice(id) - } + disableSMSNotice: (...[, { id }]) => smsNotices.disableSMSNotice(id), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/status.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/status.resolver.js index f357b78b..5a4930a4 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/status.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/status.resolver.js @@ -2,8 +2,8 @@ const supervisor = require('../../services/supervisor') const resolvers = { Query: { - uptime: () => supervisor.getAllProcessInfo() - } + uptime: () => supervisor.getAllProcessInfo(), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/transaction.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/transaction.resolver.js index 12e7e2b6..d5c3b9df 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/transaction.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/transaction.resolver.js @@ -1,6 +1,5 @@ const DataLoader = require('dataloader') const { parseAsync } = require('json2csv') -const _ = require('lodash/fp') const filters = require('../../filters') const cashOutTx = require('../../../cash-out/cash-out-tx') @@ -9,35 +8,124 @@ const transactions = require('../../services/transactions') const anonymous = require('../../../constants').anonymousCustomer const logDateFormat = require('../../../logs').logDateFormat -const transactionsLoader = new DataLoader(ids => transactions.getCustomerTransactionsBatch(ids), { cache: false }) +const transactionsLoader = new DataLoader( + ids => transactions.getCustomerTransactionsBatch(ids), + { cache: false }, +) const resolvers = { Customer: { - transactions: parent => transactionsLoader.load(parent.id) + transactions: parent => transactionsLoader.load(parent.id), }, Transaction: { - isAnonymous: parent => (parent.customerId === anonymous.uuid) + isAnonymous: parent => parent.customerId === anonymous.uuid, }, Query: { - transactions: (...[, { from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status, swept, excludeTestingCustomers }]) => - transactions.batch(from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status, swept, excludeTestingCustomers), - transactionsCsv: (...[, { from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status, swept, timezone, excludeTestingCustomers, simplified }]) => - transactions.batch(from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status, swept, excludeTestingCustomers, simplified) - .then(data => parseAsync(logDateFormat(timezone, data, ['created', 'sendTime', 'publishedAt']))), + transactions: ( + ...[ + , + { + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + swept, + excludeTestingCustomers, + }, + ] + ) => + transactions.batch( + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + swept, + excludeTestingCustomers, + ), + transactionsCsv: ( + ...[ + , + { + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + swept, + timezone, + excludeTestingCustomers, + simplified, + }, + ] + ) => + transactions + .batch( + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + swept, + excludeTestingCustomers, + simplified, + ) + .then(data => + parseAsync( + logDateFormat(timezone, data, [ + 'created', + 'sendTime', + 'publishedAt', + ]), + ), + ), transactionCsv: (...[, { id, txClass, timezone }]) => - transactions.getTx(id, txClass).then(data => - parseAsync(logDateFormat(timezone, [data], ['created', 'sendTime', 'publishedAt'])) - ), + transactions + .getTx(id, txClass) + .then(data => + parseAsync( + logDateFormat( + timezone, + [data], + ['created', 'sendTime', 'publishedAt'], + ), + ), + ), txAssociatedDataCsv: (...[, { id, txClass, timezone }]) => - transactions.getTxAssociatedData(id, txClass).then(data => - parseAsync(logDateFormat(timezone, data, ['created'])) - ), - transactionFilters: () => filters.transaction() + transactions + .getTxAssociatedData(id, txClass) + .then(data => parseAsync(logDateFormat(timezone, data, ['created']))), + transactionFilters: () => filters.transaction(), }, Mutation: { cancelCashOutTransaction: (...[, { id }]) => cashOutTx.cancel(id), - cancelCashInTransaction: (...[, { id }]) => cashInTx.cancel(id) - } + cancelCashInTransaction: (...[, { id }]) => cashInTx.cancel(id), + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js index c62bc8e0..513a341c 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js @@ -19,7 +19,11 @@ const getAttestationQueryOptions = variables => { const getAssertionQueryOptions = variables => { switch (authentication.CHOSEN_STRATEGY) { case 'FIDO2FA': - return { username: variables.username, password: variables.password, domain: variables.domain } + return { + username: variables.username, + password: variables.password, + domain: variables.domain, + } case 'FIDOPasswordless': return { username: variables.username, domain: variables.domain } case 'FIDOUsernameless': @@ -32,11 +36,23 @@ const getAssertionQueryOptions = variables => { const getAttestationMutationOptions = variables => { switch (authentication.CHOSEN_STRATEGY) { case 'FIDO2FA': - return { userId: variables.userID, attestationResponse: variables.attestationResponse, domain: variables.domain } + return { + userId: variables.userID, + attestationResponse: variables.attestationResponse, + domain: variables.domain, + } case 'FIDOPasswordless': - return { userId: variables.userID, attestationResponse: variables.attestationResponse, domain: variables.domain } + return { + userId: variables.userID, + attestationResponse: variables.attestationResponse, + domain: variables.domain, + } case 'FIDOUsernameless': - return { userId: variables.userID, attestationResponse: variables.attestationResponse, domain: variables.domain } + return { + userId: variables.userID, + attestationResponse: variables.attestationResponse, + domain: variables.domain, + } default: return {} } @@ -45,11 +61,25 @@ const getAttestationMutationOptions = variables => { const getAssertionMutationOptions = variables => { switch (authentication.CHOSEN_STRATEGY) { case 'FIDO2FA': - return { username: variables.username, password: variables.password, rememberMe: variables.rememberMe, assertionResponse: variables.assertionResponse, domain: variables.domain } + return { + username: variables.username, + password: variables.password, + rememberMe: variables.rememberMe, + assertionResponse: variables.assertionResponse, + domain: variables.domain, + } case 'FIDOPasswordless': - return { username: variables.username, rememberMe: variables.rememberMe, assertionResponse: variables.assertionResponse, domain: variables.domain } + return { + username: variables.username, + rememberMe: variables.rememberMe, + assertionResponse: variables.assertionResponse, + domain: variables.domain, + } case 'FIDOUsernameless': - return { assertionResponse: variables.assertionResponse, domain: variables.domain } + return { + assertionResponse: variables.assertionResponse, + domain: variables.domain, + } default: return {} } @@ -59,34 +89,82 @@ const resolver = { Query: { users: () => users.getUsers(), sessions: () => sessionManager.getSessions(), - userSessions: (...[, { username }]) => sessionManager.getSessionsByUsername(username), - userData: (...[, {}, context]) => userManagement.getUserData(context), - get2FASecret: (...[, { username, password }]) => userManagement.get2FASecret(username, password), - confirm2FA: (...[, { code }, context]) => userManagement.confirm2FA(code, context), - validateRegisterLink: (...[, { token }]) => userManagement.validateRegisterLink(token), - validateResetPasswordLink: (...[, { token }]) => userManagement.validateResetPasswordLink(token), - validateReset2FALink: (...[, { token }]) => userManagement.validateReset2FALink(token), - generateAttestationOptions: (...[, variables, context]) => authentication.strategy.generateAttestationOptions(context.req.session, getAttestationQueryOptions(variables)), - generateAssertionOptions: (...[, variables, context]) => authentication.strategy.generateAssertionOptions(context.req.session, getAssertionQueryOptions(variables)) + userSessions: (...[, { username }]) => + sessionManager.getSessionsByUsername(username), + userData: (...[, , context]) => userManagement.getUserData(context), + get2FASecret: (...[, { username, password }]) => + userManagement.get2FASecret(username, password), + confirm2FA: (...[, { code }, context]) => + userManagement.confirm2FA(code, context), + validateRegisterLink: (...[, { token }]) => + userManagement.validateRegisterLink(token), + validateResetPasswordLink: (...[, { token }]) => + userManagement.validateResetPasswordLink(token), + validateReset2FALink: (...[, { token }]) => + userManagement.validateReset2FALink(token), + generateAttestationOptions: (...[, variables, context]) => + authentication.strategy.generateAttestationOptions( + context.req.session, + getAttestationQueryOptions(variables), + ), + generateAssertionOptions: (...[, variables, context]) => + authentication.strategy.generateAssertionOptions( + context.req.session, + getAssertionQueryOptions(variables), + ), }, Mutation: { - enableUser: (...[, { confirmationCode, id }, context]) => userManagement.enableUser(confirmationCode, id, context), - disableUser: (...[, { confirmationCode, id }, context]) => userManagement.disableUser(confirmationCode, id, context), - deleteSession: (...[, { sid }, context]) => userManagement.deleteSession(sid, context), - deleteUserSessions: (...[, { username }]) => sessionManager.deleteSessionsByUsername(username), - changeUserRole: (...[, { confirmationCode, id, newRole }, context]) => userManagement.changeUserRole(confirmationCode, id, newRole, context), - login: (...[, { username, password }]) => userManagement.login(username, password), - input2FA: (...[, { username, password, rememberMe, code }, context]) => userManagement.input2FA(username, password, rememberMe, code, context), - setup2FA: (...[, { username, password, rememberMe, codeConfirmation }, context]) => userManagement.setup2FA(username, password, rememberMe, codeConfirmation, context), - createResetPasswordToken: (...[, { confirmationCode, userID }, context]) => userManagement.createResetPasswordToken(confirmationCode, userID, context), - createReset2FAToken: (...[, { confirmationCode, userID }, context]) => userManagement.createReset2FAToken(confirmationCode, userID, context), - createRegisterToken: (...[, { username, role }]) => userManagement.createRegisterToken(username, role), - register: (...[, { token, username, password, role }]) => userManagement.register(token, username, password, role), - resetPassword: (...[, { token, userID, newPassword }, context]) => userManagement.resetPassword(token, userID, newPassword, context), - reset2FA: (...[, { token, userID, code }, context]) => userManagement.reset2FA(token, userID, code, context), - validateAttestation: (...[, variables, context]) => authentication.strategy.validateAttestation(context.req.session, getAttestationMutationOptions(variables)), - validateAssertion: (...[, variables, context]) => authentication.strategy.validateAssertion(context.req.session, getAssertionMutationOptions(variables)) - } + enableUser: (...[, { confirmationCode, id }, context]) => + userManagement.enableUser(confirmationCode, id, context), + disableUser: (...[, { confirmationCode, id }, context]) => + userManagement.disableUser(confirmationCode, id, context), + deleteSession: (...[, { sid }, context]) => + userManagement.deleteSession(sid, context), + deleteUserSessions: (...[, { username }]) => + sessionManager.deleteSessionsByUsername(username), + changeUserRole: (...[, { confirmationCode, id, newRole }, context]) => + userManagement.changeUserRole(confirmationCode, id, newRole, context), + login: (...[, { username, password }]) => + userManagement.login(username, password), + input2FA: (...[, { username, password, rememberMe, code }, context]) => + userManagement.input2FA(username, password, rememberMe, code, context), + setup2FA: ( + ...[, { username, password, rememberMe, codeConfirmation }, context] + ) => + userManagement.setup2FA( + username, + password, + rememberMe, + codeConfirmation, + context, + ), + createResetPasswordToken: (...[, { confirmationCode, userID }, context]) => + userManagement.createResetPasswordToken( + confirmationCode, + userID, + context, + ), + createReset2FAToken: (...[, { confirmationCode, userID }, context]) => + userManagement.createReset2FAToken(confirmationCode, userID, context), + createRegisterToken: (...[, { username, role }]) => + userManagement.createRegisterToken(username, role), + register: (...[, { token, username, password, role }]) => + userManagement.register(token, username, password, role), + resetPassword: (...[, { token, userID, newPassword }, context]) => + userManagement.resetPassword(token, userID, newPassword, context), + reset2FA: (...[, { token, userID, code }, context]) => + userManagement.reset2FA(token, userID, code, context), + validateAttestation: (...[, variables, context]) => + authentication.strategy.validateAttestation( + context.req.session, + getAttestationMutationOptions(variables), + ), + validateAssertion: (...[, variables, context]) => + authentication.strategy.validateAssertion( + context.req.session, + getAssertionMutationOptions(variables), + ), + }, } module.exports = resolver diff --git a/packages/server/lib/new-admin/graphql/resolvers/version.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/version.resolver.js index d6f5b1f1..aa3e564c 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/version.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/version.resolver.js @@ -2,8 +2,8 @@ const serverVersion = require('../../../../package.json').version const resolvers = { Query: { - serverVersion: () => serverVersion - } + serverVersion: () => serverVersion, + }, } module.exports = resolvers diff --git a/packages/server/lib/new-admin/graphql/schema.js b/packages/server/lib/new-admin/graphql/schema.js index 68bd4257..218c3e91 100644 --- a/packages/server/lib/new-admin/graphql/schema.js +++ b/packages/server/lib/new-admin/graphql/schema.js @@ -3,5 +3,5 @@ const resolvers = require('./resolvers') module.exports = { resolvers: resolvers, - typeDefs: types + typeDefs: types, } diff --git a/packages/server/lib/new-admin/graphql/types/cashbox.type.js b/packages/server/lib/new-admin/graphql/types/cashbox.type.js index ada0441d..35b6d933 100644 --- a/packages/server/lib/new-admin/graphql/types/cashbox.type.js +++ b/packages/server/lib/new-admin/graphql/types/cashbox.type.js @@ -14,7 +14,11 @@ const typeDef = gql` type Query { cashboxBatches: [CashboxBatch] @auth - cashboxBatchesCsv(from: DateTimeISO, until: DateTimeISO, timezone: String): String @auth + cashboxBatchesCsv( + from: DateTimeISO + until: DateTimeISO + timezone: String + ): String @auth } type Mutation { diff --git a/packages/server/lib/new-admin/graphql/types/customInfoRequests.type.js b/packages/server/lib/new-admin/graphql/types/customInfoRequests.type.js index 88d2def3..cbd33d6a 100644 --- a/packages/server/lib/new-admin/graphql/types/customInfoRequests.type.js +++ b/packages/server/lib/new-admin/graphql/types/customInfoRequests.type.js @@ -1,10 +1,9 @@ const gql = require('graphql-tag') const typeDef = gql` - type CustomInfoRequest { - id: ID!, - enabled: Boolean, + id: ID! + enabled: Boolean customRequest: JSON } @@ -42,15 +41,31 @@ const typeDef = gql` type Query { customInfoRequests(onlyEnabled: Boolean): [CustomInfoRequest] @auth customerCustomInfoRequests(customerId: ID!): [CustomRequestData] @auth - customerCustomInfoRequest(customerId: ID!, infoRequestId: ID!): CustomRequestData @auth + customerCustomInfoRequest( + customerId: ID! + infoRequestId: ID! + ): CustomRequestData @auth } type Mutation { - insertCustomInfoRequest(customRequest: CustomRequestInput!): CustomInfoRequest @auth + insertCustomInfoRequest( + customRequest: CustomRequestInput! + ): CustomInfoRequest @auth removeCustomInfoRequest(id: ID!): CustomInfoRequest @auth - editCustomInfoRequest(id: ID!, customRequest: CustomRequestInput!): CustomInfoRequest @auth - setAuthorizedCustomRequest(customerId: ID!, infoRequestId: ID!, override: String!): Boolean @auth - setCustomerCustomInfoRequest(customerId: ID!, infoRequestId: ID!, data: JSON!): Boolean @auth + editCustomInfoRequest( + id: ID! + customRequest: CustomRequestInput! + ): CustomInfoRequest @auth + setAuthorizedCustomRequest( + customerId: ID! + infoRequestId: ID! + override: String! + ): Boolean @auth + setCustomerCustomInfoRequest( + customerId: ID! + infoRequestId: ID! + data: JSON! + ): Boolean @auth } ` diff --git a/packages/server/lib/new-admin/graphql/types/customer.type.js b/packages/server/lib/new-admin/graphql/types/customer.type.js index 3c6b00fd..f6d16bf2 100644 --- a/packages/server/lib/new-admin/graphql/types/customer.type.js +++ b/packages/server/lib/new-admin/graphql/types/customer.type.js @@ -95,20 +95,37 @@ const typeDef = gql` } type Query { - customers(phone: String, name: String, email: String, address: String, id: String): [Customer] @auth + customers( + phone: String + name: String + email: String + address: String + id: String + ): [Customer] @auth customer(customerId: ID!): Customer @auth customerFilters: [Filter] @auth } type Mutation { setCustomer(customerId: ID!, customerInput: CustomerInput): Customer @auth - addCustomField(customerId: ID!, label: String!, value: String!): Boolean @auth - saveCustomField(customerId: ID!, fieldId: ID!, value: String!): Boolean @auth + addCustomField(customerId: ID!, label: String!, value: String!): Boolean + @auth + saveCustomField(customerId: ID!, fieldId: ID!, value: String!): Boolean + @auth removeCustomField(customerId: ID!, fieldId: ID!): Boolean @auth editCustomer(customerId: ID!, customerEdit: CustomerEdit): Customer @auth - deleteEditedData(customerId: ID!, customerEdit: CustomerEdit): Customer @auth - replacePhoto(customerId: ID!, photoType: String, newPhoto: Upload): Customer @auth - createCustomerNote(customerId: ID!, title: String!, content: String!): Boolean @auth + deleteEditedData(customerId: ID!, customerEdit: CustomerEdit): Customer + @auth + replacePhoto( + customerId: ID! + photoType: String + newPhoto: Upload + ): Customer @auth + createCustomerNote( + customerId: ID! + title: String! + content: String! + ): Boolean @auth editCustomerNote(noteId: ID!, newContent: String!): Boolean @auth deleteCustomerNote(noteId: ID!): Boolean @auth createCustomer(phoneNumber: String): Customer @auth diff --git a/packages/server/lib/new-admin/graphql/types/index.js b/packages/server/lib/new-admin/graphql/types/index.js index e33c50b5..6460a8bb 100644 --- a/packages/server/lib/new-admin/graphql/types/index.js +++ b/packages/server/lib/new-admin/graphql/types/index.js @@ -47,7 +47,7 @@ const types = [ status, transaction, user, - version + version, ] module.exports = mergeTypeDefs(types) diff --git a/packages/server/lib/new-admin/graphql/types/log.type.js b/packages/server/lib/new-admin/graphql/types/log.type.js index de7aee38..5d10fdfb 100644 --- a/packages/server/lib/new-admin/graphql/types/log.type.js +++ b/packages/server/lib/new-admin/graphql/types/log.type.js @@ -16,10 +16,34 @@ const typeDef = gql` } type Query { - machineLogs(deviceId: ID!, from: DateTimeISO, until: DateTimeISO, limit: Int, offset: Int): [MachineLog] @auth - machineLogsCsv(deviceId: ID!, from: DateTimeISO, until: DateTimeISO, limit: Int, offset: Int, timezone: String): String @auth - serverLogs(from: DateTimeISO, until: DateTimeISO, limit: Int, offset: Int): [ServerLog] @auth - serverLogsCsv(from: DateTimeISO, until: DateTimeISO, limit: Int, offset: Int, timezone: String): String @auth + machineLogs( + deviceId: ID! + from: DateTimeISO + until: DateTimeISO + limit: Int + offset: Int + ): [MachineLog] @auth + machineLogsCsv( + deviceId: ID! + from: DateTimeISO + until: DateTimeISO + limit: Int + offset: Int + timezone: String + ): String @auth + serverLogs( + from: DateTimeISO + until: DateTimeISO + limit: Int + offset: Int + ): [ServerLog] @auth + serverLogsCsv( + from: DateTimeISO + until: DateTimeISO + limit: Int + offset: Int + timezone: String + ): String @auth } ` diff --git a/packages/server/lib/new-admin/graphql/types/loyalty.type.js b/packages/server/lib/new-admin/graphql/types/loyalty.type.js index 0227a503..862f5545 100644 --- a/packages/server/lib/new-admin/graphql/types/loyalty.type.js +++ b/packages/server/lib/new-admin/graphql/types/loyalty.type.js @@ -6,7 +6,7 @@ const typeDef = gql` customer: DiscountCustomer! discount: Int } - + type DiscountCustomer { id: ID! phone: String @@ -27,7 +27,10 @@ const typeDef = gql` type Mutation { createPromoCode(code: String!, discount: Int!): PromoCode @auth deletePromoCode(codeId: ID!): PromoCode @auth - createIndividualDiscount(customerId: ID!, discount: Int!): IndividualDiscount @auth + createIndividualDiscount( + customerId: ID! + discount: Int! + ): IndividualDiscount @auth deleteIndividualDiscount(discountId: ID!): IndividualDiscount @auth } ` diff --git a/packages/server/lib/new-admin/graphql/types/machine.type.js b/packages/server/lib/new-admin/graphql/types/machine.type.js index d157913c..387e2f2d 100644 --- a/packages/server/lib/new-admin/graphql/types/machine.type.js +++ b/packages/server/lib/new-admin/graphql/types/machine.type.js @@ -30,7 +30,7 @@ const typeDef = gql` frontTimestamp: DateTimeISO scanTimestamp: DateTimeISO } - + type CashUnits { cashbox: Int cassette1: Int @@ -98,7 +98,12 @@ const typeDef = gql` } type Mutation { - machineAction(deviceId:ID!, action: MachineAction!, cashUnits: CashUnitsInput, newName: String): Machine @auth + machineAction( + deviceId: ID! + action: MachineAction! + cashUnits: CashUnitsInput + newName: String + ): Machine @auth } ` diff --git a/packages/server/lib/new-admin/graphql/types/sms.type.js b/packages/server/lib/new-admin/graphql/types/sms.type.js index e134a20d..36375204 100644 --- a/packages/server/lib/new-admin/graphql/types/sms.type.js +++ b/packages/server/lib/new-admin/graphql/types/sms.type.js @@ -21,7 +21,8 @@ const typeDef = gql` } type Mutation { - editSMSNotice(id: ID!, event: SMSNoticeEvent!, message: String!): SMSNotice @auth + editSMSNotice(id: ID!, event: SMSNoticeEvent!, message: String!): SMSNotice + @auth enableSMSNotice(id: ID!): SMSNotice @auth disableSMSNotice(id: ID!): SMSNotice @auth } diff --git a/packages/server/lib/new-admin/graphql/types/transaction.type.js b/packages/server/lib/new-admin/graphql/types/transaction.type.js index 9edde74b..40193c75 100644 --- a/packages/server/lib/new-admin/graphql/types/transaction.type.js +++ b/packages/server/lib/new-admin/graphql/types/transaction.type.js @@ -60,8 +60,38 @@ const typeDef = gql` } type Query { - transactions(from: DateTimeISO, until: DateTimeISO, limit: Int, offset: Int, txClass: String, deviceId: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String, swept: Boolean, excludeTestingCustomers: Boolean): [Transaction] @auth - transactionsCsv(from: DateTimeISO, until: DateTimeISO, limit: Int, offset: Int, txClass: String, deviceId: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String, swept: Boolean, timezone: String, excludeTestingCustomers: Boolean, simplified: Boolean): String @auth + transactions( + from: DateTimeISO + until: DateTimeISO + limit: Int + offset: Int + txClass: String + deviceId: String + customerName: String + fiatCode: String + cryptoCode: String + toAddress: String + status: String + swept: Boolean + excludeTestingCustomers: Boolean + ): [Transaction] @auth + transactionsCsv( + from: DateTimeISO + until: DateTimeISO + limit: Int + offset: Int + txClass: String + deviceId: String + customerName: String + fiatCode: String + cryptoCode: String + toAddress: String + status: String + swept: Boolean + timezone: String + excludeTestingCustomers: Boolean + simplified: Boolean + ): String @auth transactionCsv(id: ID, txClass: String, timezone: String): String @auth txAssociatedDataCsv(id: ID, txClass: String, timezone: String): String @auth transactionFilters: [Filter] @auth diff --git a/packages/server/lib/new-admin/middlewares/cleanUserSessions.js b/packages/server/lib/new-admin/middlewares/cleanUserSessions.js index 5372b412..8fa45d41 100644 --- a/packages/server/lib/new-admin/middlewares/cleanUserSessions.js +++ b/packages/server/lib/new-admin/middlewares/cleanUserSessions.js @@ -4,13 +4,17 @@ const logger = require('../../logger') let schemaCache = Date.now() -const cleanUserSessions = (cleanInterval) => (req, res, next) => { +const cleanUserSessions = cleanInterval => (req, res, next) => { const now = Date.now() if (schemaCache + cleanInterval > now) return next() logger.debug(`Clearing expired sessions for schema 'public'`) - return db.none('DELETE FROM $1^ WHERE expire < to_timestamp($2 / 1000.0)', [USER_SESSIONS_TABLE_NAME, now]) + return db + .none('DELETE FROM $1^ WHERE expire < to_timestamp($2 / 1000.0)', [ + USER_SESSIONS_TABLE_NAME, + now, + ]) .then(() => { schemaCache = now return next() diff --git a/packages/server/lib/new-admin/middlewares/context.js b/packages/server/lib/new-admin/middlewares/context.js index 6db47245..60fa5d5f 100644 --- a/packages/server/lib/new-admin/middlewares/context.js +++ b/packages/server/lib/new-admin/middlewares/context.js @@ -8,9 +8,10 @@ const buildApolloContext = async ({ req, res }) => { const user = await users.verifyAndUpdateUser( req.session.user.id, req.headers['user-agent'] || 'Unknown', - req.ip + req.ip, ) - if (!user || !user.enabled) throw new AuthenticationError('Authentication failed') + if (!user || !user.enabled) + throw new AuthenticationError('Authentication failed') req.session.ua = req.headers['user-agent'] || 'Unknown' req.session.ipAddress = req.ip diff --git a/packages/server/lib/new-admin/middlewares/index.js b/packages/server/lib/new-admin/middlewares/index.js index cedba31c..3eea6f7b 100644 --- a/packages/server/lib/new-admin/middlewares/index.js +++ b/packages/server/lib/new-admin/middlewares/index.js @@ -5,5 +5,5 @@ const session = require('./session') module.exports = { cleanUserSessions, buildApolloContext, - session + session, } diff --git a/packages/server/lib/new-admin/middlewares/session.js b/packages/server/lib/new-admin/middlewares/session.js index 2c0400af..fb8991e7 100644 --- a/packages/server/lib/new-admin/middlewares/session.js +++ b/packages/server/lib/new-admin/middlewares/session.js @@ -6,21 +6,24 @@ const db = require('../../db') const { USER_SESSIONS_TABLE_NAME } = require('../../constants') const { getOperatorId } = require('../../operator') -router.use('*', async (req, res, next) => getOperatorId('authentication').then(operatorId => session({ - store: new PgSession({ - pgPromise: db, - tableName: USER_SESSIONS_TABLE_NAME - }), - name: 'lamassu_sid', - secret: operatorId, - resave: false, - saveUninitialized: false, - cookie: { - httpOnly: true, - secure: true, - sameSite: true - } -})(req, res, next)) +router.use('*', async (req, res, next) => + getOperatorId('authentication').then(operatorId => + session({ + store: new PgSession({ + pgPromise: db, + tableName: USER_SESSIONS_TABLE_NAME, + }), + name: 'lamassu_sid', + secret: operatorId, + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + secure: true, + sameSite: true, + }, + })(req, res, next), + ), ) module.exports = router diff --git a/packages/server/lib/new-admin/services/bills.js b/packages/server/lib/new-admin/services/bills.js index d79d7002..78092ab8 100644 --- a/packages/server/lib/new-admin/services/bills.js +++ b/packages/server/lib/new-admin/services/bills.js @@ -4,7 +4,9 @@ const pgp = require('pg-promise')() const db = require('../../db') const getBills = filters => { - const deviceStatement = !_.isNil(filters.deviceId) ? `WHERE device_id = ${pgp.as.text(filters.deviceId)}` : `` + const deviceStatement = !_.isNil(filters.deviceId) + ? `WHERE device_id = ${pgp.as.text(filters.deviceId)}` + : `` const batchStatement = filter => { switch (filter) { case 'none': @@ -12,7 +14,9 @@ const getBills = filters => { case 'any': return `WHERE b.cashbox_batch_id IS NOT NULL` default: - return _.isNil(filter) ? `` : `WHERE b.cashbox_batch_id = ${pgp.as.text(filter)}` + return _.isNil(filter) + ? `` + : `WHERE b.cashbox_batch_id = ${pgp.as.text(filter)}` } } @@ -22,10 +26,12 @@ const getBills = filters => { const sql2 = `SELECT b.id, b.fiat, b.fiat_code, b.created, b.cashbox_batch_id, b.device_id FROM empty_unit_bills b ${deviceStatement} ${!_.isNil(filters.deviceId) && !_.isNil(filters.batch) ? `AND ${_.replace('WHERE', '', batchStatement(filters.batch))}` : `${batchStatement(filters.batch)}`}` - return Promise.all([db.any(sql), db.any(sql2)]) - .then(([bills, operationalBills]) => _.map(_.mapKeys(_.camelCase), _.concat(bills, operationalBills))) + return Promise.all([db.any(sql), db.any(sql2)]).then( + ([bills, operationalBills]) => + _.map(_.mapKeys(_.camelCase), _.concat(bills, operationalBills)), + ) } module.exports = { - getBills + getBills, } diff --git a/packages/server/lib/new-admin/services/customInfoRequests.js b/packages/server/lib/new-admin/services/customInfoRequests.js index fff8dc39..daec3480 100644 --- a/packages/server/lib/new-admin/services/customInfoRequests.js +++ b/packages/server/lib/new-admin/services/customInfoRequests.js @@ -2,7 +2,10 @@ const db = require('../../db') const uuid = require('uuid') const _ = require('lodash/fp') const pgp = require('pg-promise')() -const { loadLatestConfigOrNone, saveConfig } = require('../../../lib/new-settings-loader') +const { + loadLatestConfigOrNone, + saveConfig, +} = require('../../../lib/new-settings-loader') const getCustomInfoRequests = (onlyEnabled = false) => { const sql = onlyEnabled @@ -12,38 +15,57 @@ const getCustomInfoRequests = (onlyEnabled = false) => { return res.map(item => ({ id: item.id, enabled: item.enabled, - customRequest: item.custom_request + customRequest: item.custom_request, })) }) } -const addCustomInfoRequest = (customRequest) => { - const sql = 'INSERT INTO custom_info_requests (id, custom_request) VALUES ($1, $2)' +const addCustomInfoRequest = customRequest => { + const sql = + 'INSERT INTO custom_info_requests (id, custom_request) VALUES ($1, $2)' const id = uuid.v4() return db.none(sql, [id, customRequest]).then(() => ({ id })) } -const removeCustomInfoRequest = (id) => { +const removeCustomInfoRequest = id => { return loadLatestConfigOrNone() - .then(cfg => saveConfig({triggers: _.remove(x => x.customInfoRequestId === id, cfg.triggers ?? [])})) - .then(() => db.none('UPDATE custom_info_requests SET enabled = false WHERE id = $1', [id])) - .then(() => ({ id })); + .then(cfg => + saveConfig({ + triggers: _.remove( + x => x.customInfoRequestId === id, + cfg.triggers ?? [], + ), + }), + ) + .then(() => + db.none('UPDATE custom_info_requests SET enabled = false WHERE id = $1', [ + id, + ]), + ) + .then(() => ({ id })) } const editCustomInfoRequest = (id, customRequest) => { - return db.none('UPDATE custom_info_requests SET custom_request = $1 WHERE id=$2', [customRequest, id]).then(() => ({ id, customRequest })) + return db + .none('UPDATE custom_info_requests SET custom_request = $1 WHERE id=$2', [ + customRequest, + id, + ]) + .then(() => ({ id, customRequest })) } -const getAllCustomInfoRequestsForCustomer = (customerId) => { +const getAllCustomInfoRequestsForCustomer = customerId => { const sql = `SELECT * FROM customers_custom_info_requests WHERE customer_id = $1` - return db.any(sql, [customerId]).then(res => res.map(item => ({ - customerId: item.customer_id, - infoRequestId: item.info_request_id, - customerData: item.customer_data, - override: item.override, - overrideAt: item.override_at, - overrideBy: item.override_by - }))) + return db.any(sql, [customerId]).then(res => + res.map(item => ({ + customerId: item.customer_id, + infoRequestId: item.info_request_id, + customerData: item.customer_data, + override: item.override, + overrideAt: item.override_at, + overrideBy: item.override_by, + })), + ) } const getCustomInfoRequestForCustomer = (customerId, infoRequestId) => { @@ -55,12 +77,12 @@ const getCustomInfoRequestForCustomer = (customerId, infoRequestId) => { customerData: item.customer_data, override: item.override, overrideAt: item.override_at, - overrideBy: item.override_by + overrideBy: item.override_by, } }) } -const batchGetAllCustomInfoRequestsForCustomer = (customerIds) => { +const batchGetAllCustomInfoRequestsForCustomer = customerIds => { const sql = `SELECT * FROM customers_custom_info_requests WHERE customer_id IN ($1^)` return db.any(sql, [_.map(pgp.as.text, customerIds).join(',')]).then(res => { const map = _.groupBy('customer_id', res) @@ -72,40 +94,49 @@ const batchGetAllCustomInfoRequestsForCustomer = (customerIds) => { customerData: item.customer_data, override: item.override, overrideAt: item.override_at, - overrideBy: item.override_by + overrideBy: item.override_by, })) }) }) } -const getCustomInfoRequest = (infoRequestId) => { +const getCustomInfoRequest = infoRequestId => { const sql = `SELECT * FROM custom_info_requests WHERE id = $1` return db.one(sql, [infoRequestId]).then(item => ({ id: item.id, enabled: item.enabled, - customRequest: item.custom_request + customRequest: item.custom_request, })) } -const batchGetCustomInfoRequest = (infoRequestIds) => { +const batchGetCustomInfoRequest = infoRequestIds => { if (infoRequestIds.length === 0) return Promise.resolve([]) const sql = `SELECT * FROM custom_info_requests WHERE id IN ($1^)` - return db.any(sql, [_.map(pgp.as.text, infoRequestIds).join(',')]).then(res => { - const map = _.groupBy('id', res) - return infoRequestIds.map(id => { - const item = map[id][0] // since id is primary key the array always has 1 element - return { - id: item.id, - enabled: item.enabled, - customRequest: item.custom_request - } + return db + .any(sql, [_.map(pgp.as.text, infoRequestIds).join(',')]) + .then(res => { + const map = _.groupBy('id', res) + return infoRequestIds.map(id => { + const item = map[id][0] // since id is primary key the array always has 1 element + return { + id: item.id, + enabled: item.enabled, + customRequest: item.custom_request, + } + }) }) - }) } -const setAuthorizedCustomRequest = (customerId, infoRequestId, override, token) => { +const setAuthorizedCustomRequest = ( + customerId, + infoRequestId, + override, + token, +) => { const sql = `UPDATE customers_custom_info_requests SET override = $1, override_by = $2, override_at = now() WHERE customer_id = $3 AND info_request_id = $4` - return db.none(sql, [override, token, customerId, infoRequestId]).then(() => true) + return db + .none(sql, [override, token, customerId, infoRequestId]) + .then(() => true) } const setCustomerData = (customerId, infoRequestId, data) => { @@ -138,5 +169,5 @@ module.exports = { batchGetCustomInfoRequest, setAuthorizedCustomRequest, setCustomerData, - setCustomerDataViaMachine + setCustomerDataViaMachine, } diff --git a/packages/server/lib/new-admin/services/funding.js b/packages/server/lib/new-admin/services/funding.js index 5b425d82..9d74d0d6 100644 --- a/packages/server/lib/new-admin/services/funding.js +++ b/packages/server/lib/new-admin/services/funding.js @@ -7,55 +7,65 @@ const ticker = require('../../ticker') const txBatching = require('../../tx-batching') const { utils: coinUtils } = require('@lamassu/coins') -function computeCrypto (cryptoCode, _balance) { +function computeCrypto(cryptoCode, _balance) { const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode) const unitScale = cryptoRec.unitScale return new BN(_balance).shiftedBy(-unitScale).decimalPlaces(5) } -function computeFiat (rate, cryptoCode, _balance) { +function computeFiat(rate, cryptoCode, _balance) { const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode) const unitScale = cryptoRec.unitScale return new BN(_balance).shiftedBy(-unitScale).times(rate).decimalPlaces(5) } -function getSingleCoinFunding (settings, fiatCode, cryptoCode) { +function getSingleCoinFunding(settings, fiatCode, cryptoCode) { const promises = [ wallet.newFunding(settings, cryptoCode), ticker.getRates(settings, fiatCode, cryptoCode), - txBatching.getOpenBatchCryptoValue(cryptoCode) + txBatching.getOpenBatchCryptoValue(cryptoCode), ] - return Promise.all(promises) - .then(([fundingRec, ratesRec, batchRec]) => { - const rates = ratesRec.rates - const rate = (rates.ask.plus(rates.bid)).div(2) - const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance - const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance) - const pending = fundingRec.fundingPendingBalance.minus(batchRec) - const fiatPending = computeFiat(rate, cryptoCode, pending) - const fundingAddress = fundingRec.fundingAddress - const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress) + return Promise.all(promises).then(([fundingRec, ratesRec, batchRec]) => { + const rates = ratesRec.rates + const rate = rates.ask.plus(rates.bid).div(2) + const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance + const fiatConfirmedBalance = computeFiat( + rate, + cryptoCode, + fundingConfirmedBalance, + ) + const pending = fundingRec.fundingPendingBalance.minus(batchRec) + const fiatPending = computeFiat(rate, cryptoCode, pending) + const fundingAddress = fundingRec.fundingAddress + const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress) - return { + return { + cryptoCode, + fundingAddress, + fundingAddressUrl, + confirmedBalance: computeCrypto( cryptoCode, - fundingAddress, - fundingAddressUrl, - confirmedBalance: computeCrypto(cryptoCode, fundingConfirmedBalance).toFormat(5), - pending: computeCrypto(cryptoCode, pending).toFormat(5), - fiatConfirmedBalance: fiatConfirmedBalance, - fiatPending: fiatPending, - fiatCode - } - }) + fundingConfirmedBalance, + ).toFormat(5), + pending: computeCrypto(cryptoCode, pending).toFormat(5), + fiatConfirmedBalance: fiatConfirmedBalance, + fiatPending: fiatPending, + fiatCode, + } + }) } // Promise.allSettled not running on current version of node -const reflect = p => p.then(value => ({ value, status: 'fulfilled' }), error => ({ error: error.toString(), status: 'rejected' })) +const reflect = p => + p.then( + value => ({ value, status: 'fulfilled' }), + error => ({ error: error.toString(), status: 'rejected' }), + ) -function getFunding () { +function getFunding() { return settingsLoader.loadLatest().then(settings => { const cryptoCodes = configManager.getAllCryptoCurrencies(settings.config) const fiatCode = configManager.getGlobalLocale(settings.config).fiatCurrency @@ -63,12 +73,15 @@ function getFunding () { const cryptoCurrencies = coinUtils.cryptoCurrencies() const cryptoDisplays = _.filter(pareCoins, cryptoCurrencies) - const promises = cryptoDisplays.map(it => getSingleCoinFunding(settings, fiatCode, it.cryptoCode)) - return Promise.all(promises.map(reflect)) - .then((response) => { - const mapped = response.map(it => _.merge({ errorMsg: it.error }, it.value)) - return _.toArray(_.merge(mapped, cryptoDisplays)) - }) + const promises = cryptoDisplays.map(it => + getSingleCoinFunding(settings, fiatCode, it.cryptoCode), + ) + return Promise.all(promises.map(reflect)).then(response => { + const mapped = response.map(it => + _.merge({ errorMsg: it.error }, it.value), + ) + return _.toArray(_.merge(mapped, cryptoDisplays)) + }) }) } diff --git a/packages/server/lib/new-admin/services/login.js b/packages/server/lib/new-admin/services/login.js index 8eb19c53..67fda521 100644 --- a/packages/server/lib/new-admin/services/login.js +++ b/packages/server/lib/new-admin/services/login.js @@ -1,16 +1,23 @@ const db = require('../../db') -function validateUser (username, password) { +function validateUser(username, password) { return db.tx(t => { - const q1 = t.one('SELECT * FROM users WHERE username=$1 AND password=$2', [username, password]) - const q2 = t.none('UPDATE users SET last_accessed = now() WHERE username=$1', [username]) + const q1 = t.one('SELECT * FROM users WHERE username=$1 AND password=$2', [ + username, + password, + ]) + const q2 = t.none( + 'UPDATE users SET last_accessed = now() WHERE username=$1', + [username], + ) - return t.batch([q1, q2]) + return t + .batch([q1, q2]) .then(([user]) => user) .catch(() => false) }) } module.exports = { - validateUser + validateUser, } diff --git a/packages/server/lib/new-admin/services/machines.js b/packages/server/lib/new-admin/services/machines.js index b280c9b9..386da1fd 100644 --- a/packages/server/lib/new-admin/services/machines.js +++ b/packages/server/lib/new-admin/services/machines.js @@ -1,19 +1,26 @@ const machineLoader = require('../../machine-loader') const { UserInputError } = require('../graphql/errors') -function getMachine (machineId) { - return machineLoader.getMachines() +function getMachine(machineId) { + return machineLoader + .getMachines() .then(machines => machines.find(({ deviceId }) => deviceId === machineId)) } -function machineAction ({ deviceId, action, cashUnits, newName }, context) { +function machineAction({ deviceId, action, cashUnits, newName }, context) { const operatorId = context.res.locals.operatorId return getMachine(deviceId) .then(machine => { - if (!machine) throw new UserInputError(`machine:${deviceId} not found`, { deviceId }) + if (!machine) + throw new UserInputError(`machine:${deviceId} not found`, { deviceId }) return machine }) - .then(machineLoader.setMachine({ deviceId, action, cashUnits, newName }, operatorId)) + .then( + machineLoader.setMachine( + { deviceId, action, cashUnits, newName }, + operatorId, + ), + ) .then(getMachine(deviceId)) } diff --git a/packages/server/lib/new-admin/services/pairing.js b/packages/server/lib/new-admin/services/pairing.js index d267c1a9..f3446447 100644 --- a/packages/server/lib/new-admin/services/pairing.js +++ b/packages/server/lib/new-admin/services/pairing.js @@ -3,7 +3,6 @@ const pify = require('pify') const readFile = pify(fs.readFile) const crypto = require('crypto') const baseX = require('base-x') -const { parse, NIL } = require('uuid') const db = require('../../db') const pairing = require('../../pairing') @@ -16,19 +15,23 @@ const HOSTNAME = process.env.HOSTNAME const unpair = pairing.unpair -function totem (name) { - return readFile(CA_PATH) - .then(data => { - const caHash = crypto.createHash('sha256').update(data).digest() - const token = crypto.randomBytes(32) - const hexToken = token.toString('hex') - const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex') - const buf = Buffer.concat([caHash, token, Buffer.from(HOSTNAME)]) - const sql = 'insert into pairing_tokens (token, name) values ($1, $3), ($2, $3)' +function totem(name) { + return readFile(CA_PATH).then(data => { + const caHash = crypto.createHash('sha256').update(data).digest() + const token = crypto.randomBytes(32) + const hexToken = token.toString('hex') + const caHexToken = crypto + .createHash('sha256') + .update(hexToken) + .digest('hex') + const buf = Buffer.concat([caHash, token, Buffer.from(HOSTNAME)]) + const sql = + 'insert into pairing_tokens (token, name) values ($1, $3), ($2, $3)' - return db.none(sql, [hexToken, caHexToken, name]) - .then(() => bsAlpha.encode(buf)) - }) + return db + .none(sql, [hexToken, caHexToken, name]) + .then(() => bsAlpha.encode(buf)) + }) } module.exports = { totem, unpair } diff --git a/packages/server/lib/new-admin/services/server-logs.js b/packages/server/lib/new-admin/services/server-logs.js index 80d93c7f..1b4d4641 100644 --- a/packages/server/lib/new-admin/services/server-logs.js +++ b/packages/server/lib/new-admin/services/server-logs.js @@ -1,16 +1,21 @@ const _ = require('lodash/fp') -const uuid = require('uuid') const db = require('../../db') -function getServerLogs (from = new Date(0).toISOString(), until = new Date().toISOString(), limit = null, offset = 0) { +function getServerLogs( + from = new Date(0).toISOString(), + until = new Date().toISOString(), + limit = null, + offset = 0, +) { const sql = `select id, log_level, timestamp, message from server_logs where timestamp >= $1 and timestamp <= $2 order by timestamp desc limit $3 offset $4` - return db.any(sql, [ from, until, limit, offset ]) + return db + .any(sql, [from, until, limit, offset]) .then(_.map(_.mapKeys(_.camelCase))) } diff --git a/packages/server/lib/new-admin/services/supervisor.js b/packages/server/lib/new-admin/services/supervisor.js index 30e6b3c0..a6d6f4c0 100644 --- a/packages/server/lib/new-admin/services/supervisor.js +++ b/packages/server/lib/new-admin/services/supervisor.js @@ -6,8 +6,8 @@ const { promisify } = require('util') // [inet_http_server] // port = 127.0.0.1:9001 -function getAllProcessInfo () { - const convertStates = (state) => { +function getAllProcessInfo() { + const convertStates = state => { // From http://supervisord.org/subprocess.html#process-states switch (state) { case 'STOPPED': @@ -33,28 +33,30 @@ function getAllProcessInfo () { const client = xmlrpc.createClient({ host: 'localhost', port: '9001', - path: '/RPC2' + path: '/RPC2', }) client.methodCall[promisify.custom] = (method, params) => { - return new Promise((resolve, reject) => client.methodCall(method, params, (err, value) => { - if (err) reject(err) - else resolve(value) - })) + return new Promise((resolve, reject) => + client.methodCall(method, params, (err, value) => { + if (err) reject(err) + else resolve(value) + }), + ) } return promisify(client.methodCall)('supervisor.getAllProcessInfo', []) - .then((value) => { - return value.map(process => ( - { - name: process.name, - state: convertStates(process.statename), - uptime: (process.statename === 'RUNNING') ? process.now - process.start : 0 - } - )) + .then(value => { + return value.map(process => ({ + name: process.name, + state: convertStates(process.statename), + uptime: + process.statename === 'RUNNING' ? process.now - process.start : 0, + })) }) - .catch((error) => { - if (error.code === 'ECONNREFUSED') logger.error('Failed to connect to supervisord HTTP server.') + .catch(error => { + if (error.code === 'ECONNREFUSED') + logger.error('Failed to connect to supervisord HTTP server.') else logger.error(error) }) } diff --git a/packages/server/lib/new-admin/services/transactions.js b/packages/server/lib/new-admin/services/transactions.js index 8806d954..4afbd6bc 100644 --- a/packages/server/lib/new-admin/services/transactions.js +++ b/packages/server/lib/new-admin/services/transactions.js @@ -4,14 +4,16 @@ const pgp = require('pg-promise')() const db = require('../../db') const BN = require('../../bn') const { utils: coinUtils } = require('@lamassu/coins') -const machineLoader = require('../../machine-loader') const tx = require('../../tx') const cashInTx = require('../../cash-in/cash-in-tx') -const { REDEEMABLE_AGE, CASH_OUT_TRANSACTION_STATES } = require('../../cash-out/cash-out-helper') +const { + REDEEMABLE_AGE, + CASH_OUT_TRANSACTION_STATES, +} = require('../../cash-out/cash-out-helper') const NUM_RESULTS = 1000 -function addProfits (txs) { +function addProfits(txs) { return _.map(it => { const profit = getProfit(it).toString() return _.set('profit', profit, it) @@ -28,7 +30,7 @@ const DEVICE_NAME_QUERY = ` END AS machine_name ` - const DEVICE_NAME_JOINS = ` +const DEVICE_NAME_JOINS = ` LEFT JOIN devices d ON txs.device_id = d.device_id LEFT JOIN ( SELECT device_id, name, unpaired, paired @@ -38,7 +40,7 @@ const DEVICE_NAME_QUERY = ` AND (txs.created >= ud.paired) ` -function batch ( +function batch( from = new Date(0).toISOString(), until = new Date().toISOString(), limit = null, @@ -52,20 +54,19 @@ function batch ( status = null, swept = null, excludeTestingCustomers = false, - simplified + simplified, ) { const isCsvExport = _.isBoolean(simplified) const packager = _.flow( _.flatten, _.orderBy(_.property('created'), ['desc']), - _.map(_.flow( - camelize, - _.mapKeys(k => - k == 'cashInFee' ? 'fixedFee' : - k - ) - )), - addProfits + _.map( + _.flow( + camelize, + _.mapKeys(k => (k == 'cashInFee' ? 'fixedFee' : k)), + ), + ), + addProfits, ) const cashInSql = `SELECT 'cashIn' AS tx_class, txs.*, @@ -138,46 +139,164 @@ function batch ( let promises if (hasCashInOnlyFilters && hasCashOutOnlyFilters) { - throw new Error('Trying to filter transactions with mutually exclusive filters') + throw new Error( + 'Trying to filter transactions with mutually exclusive filters', + ) } if (hasCashInOnlyFilters) { - promises = [db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status])] + promises = [ + db.any(cashInSql, [ + cashInTx.PENDING_INTERVAL, + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + ]), + ] } else if (hasCashOutOnlyFilters) { - promises = [db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status, swept])] + promises = [ + db.any(cashOutSql, [ + REDEEMABLE_AGE, + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + swept, + ]), + ] } else { promises = [ - db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status]), - db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset, txClass, deviceId, customerName, fiatCode, cryptoCode, toAddress, status, swept]) + db.any(cashInSql, [ + cashInTx.PENDING_INTERVAL, + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + ]), + db.any(cashOutSql, [ + REDEEMABLE_AGE, + from, + until, + limit, + offset, + txClass, + deviceId, + customerName, + fiatCode, + cryptoCode, + toAddress, + status, + swept, + ]), ] } return Promise.all(promises) .then(packager) .then(res => - !isCsvExport ? res : - // GQL transactions and transactionsCsv both use this function and - // if we don't check for the correct simplified value, the Transactions page polling - // will continuously build a csv in the background - simplified ? simplifiedBatch(res) : - advancedBatch(res) + !isCsvExport + ? res + : // GQL transactions and transactionsCsv both use this function and + // if we don't check for the correct simplified value, the Transactions page polling + // will continuously build a csv in the background + simplified + ? simplifiedBatch(res) + : advancedBatch(res), ) } -function advancedBatch (data) { - const fields = ['txClass', 'id', 'deviceId', 'toAddress', 'cryptoAtoms', - 'cryptoCode', 'fiat', 'fiatCode', 'fee', 'status', 'fiatProfit', 'cryptoAmount', - 'dispense', 'notified', 'redeem', 'phone', 'error', 'fixedFee', - 'created', 'confirmedAt', 'hdIndex', 'swept', 'timedout', - 'dispenseConfirmed', 'provisioned1', 'provisioned2', 'provisioned3', 'provisioned4', - 'provisionedRecycler1', 'provisionedRecycler2', 'provisionedRecycler3', 'provisionedRecycler4', 'provisionedRecycler5', 'provisionedRecycler6', - 'denomination1', 'denomination2', 'denomination3', 'denomination4', - 'denominationRecycler1', 'denominationRecycler2', 'denominationRecycler3', 'denominationRecycler4', 'denominationRecycler5', 'denominationRecycler6', - 'errorCode', 'customerId', 'txVersion', 'publishedAt', 'termsAccepted', 'layer2Address', - 'commissionPercentage', 'rawTickerPrice', 'receivedCryptoAtoms', - 'discount', 'txHash', 'customerPhone', 'customerEmail', 'customerIdCardDataNumber', - 'customerIdCardDataExpiration', 'customerIdCardData', 'customerName', 'sendTime', - 'customerFrontCameraPath', 'customerIdCardPhotoPath', 'expired', 'machineName', 'walletScore'] +function advancedBatch(data) { + const fields = [ + 'txClass', + 'id', + 'deviceId', + 'toAddress', + 'cryptoAtoms', + 'cryptoCode', + 'fiat', + 'fiatCode', + 'fee', + 'status', + 'fiatProfit', + 'cryptoAmount', + 'dispense', + 'notified', + 'redeem', + 'phone', + 'error', + 'fixedFee', + 'created', + 'confirmedAt', + 'hdIndex', + 'swept', + 'timedout', + 'dispenseConfirmed', + 'provisioned1', + 'provisioned2', + 'provisioned3', + 'provisioned4', + 'provisionedRecycler1', + 'provisionedRecycler2', + 'provisionedRecycler3', + 'provisionedRecycler4', + 'provisionedRecycler5', + 'provisionedRecycler6', + 'denomination1', + 'denomination2', + 'denomination3', + 'denomination4', + 'denominationRecycler1', + 'denominationRecycler2', + 'denominationRecycler3', + 'denominationRecycler4', + 'denominationRecycler5', + 'denominationRecycler6', + 'errorCode', + 'customerId', + 'txVersion', + 'publishedAt', + 'termsAccepted', + 'layer2Address', + 'commissionPercentage', + 'rawTickerPrice', + 'receivedCryptoAtoms', + 'discount', + 'txHash', + 'customerPhone', + 'customerEmail', + 'customerIdCardDataNumber', + 'customerIdCardDataExpiration', + 'customerIdCardData', + 'customerName', + 'sendTime', + 'customerFrontCameraPath', + 'customerIdCardPhotoPath', + 'expired', + 'machineName', + 'walletScore', + ] const addAdvancedFields = _.map(it => ({ ...it, @@ -191,28 +310,48 @@ function advancedBatch (data) { return _.compose(_.map(_.pick(fields)), addAdvancedFields)(data) } -function simplifiedBatch (data) { - const fields = ['txClass', 'id', 'created', 'machineName', 'fee', - 'cryptoCode', 'cryptoAtoms', 'fiat', 'fiatCode', 'phone', 'email', 'toAddress', - 'txHash', 'dispense', 'error', 'status', 'fiatProfit', 'cryptoAmount'] +function simplifiedBatch(data) { + const fields = [ + 'txClass', + 'id', + 'created', + 'machineName', + 'fee', + 'cryptoCode', + 'cryptoAtoms', + 'fiat', + 'fiatCode', + 'phone', + 'email', + 'toAddress', + 'txHash', + 'dispense', + 'error', + 'status', + 'fiatProfit', + 'cryptoAmount', + ] const addSimplifiedFields = _.map(it => ({ ...it, status: getStatus(it), fiatProfit: getProfit(it).toString(), - cryptoAmount: getCryptoAmount(it).toString() + cryptoAmount: getCryptoAmount(it).toString(), })) return _.compose(_.map(_.pick(fields)), addSimplifiedFields)(data) } -const getCryptoAmount = it => coinUtils.toUnit(BN(it.cryptoAtoms), it.cryptoCode) +const getCryptoAmount = it => + coinUtils.toUnit(BN(it.cryptoAtoms), it.cryptoCode) const getProfit = it => { /* fiat - crypto*tickerPrice */ - const calcCashInProfit = (fiat, crypto, tickerPrice) => fiat.minus(crypto.times(tickerPrice)) + const calcCashInProfit = (fiat, crypto, tickerPrice) => + fiat.minus(crypto.times(tickerPrice)) /* crypto*tickerPrice - fiat */ - const calcCashOutProfit = (fiat, crypto, tickerPrice) => crypto.times(tickerPrice).minus(fiat) + const calcCashOutProfit = (fiat, crypto, tickerPrice) => + crypto.times(tickerPrice).minus(fiat) const fiat = BN(it.fiat) const crypto = getCryptoAmount(it) @@ -247,10 +386,15 @@ const getStatus = it => { return getCashInStatus(it) } -function getCustomerTransactionsBatch (ids) { - const packager = _.flow(it => { - return it - }, _.flatten, _.orderBy(_.property('created'), ['desc']), _.map(camelize)) +function getCustomerTransactionsBatch(ids) { + const packager = _.flow( + it => { + return it + }, + _.flatten, + _.orderBy(_.property('created'), ['desc']), + _.map(camelize), + ) const cashInSql = `SELECT 'cashIn' AS tx_class, txs.*, c.phone AS customer_phone, @@ -292,16 +436,25 @@ function getCustomerTransactionsBatch (ids) { WHERE c.id IN ($1^) ORDER BY created DESC limit $2` return Promise.all([ - db.any(cashInSql, [_.map(pgp.as.text, ids).join(','), cashInTx.PENDING_INTERVAL, NUM_RESULTS]), - db.any(cashOutSql, [_.map(pgp.as.text, ids).join(','), NUM_RESULTS, REDEEMABLE_AGE]) + db.any(cashInSql, [ + _.map(pgp.as.text, ids).join(','), + cashInTx.PENDING_INTERVAL, + NUM_RESULTS, + ]), + db.any(cashOutSql, [ + _.map(pgp.as.text, ids).join(','), + NUM_RESULTS, + REDEEMABLE_AGE, + ]), ]) - .then(packager).then(transactions => { + .then(packager) + .then(transactions => { const transactionMap = _.groupBy('customerId', transactions) return ids.map(id => transactionMap[id]) }) } -function single (txId) { +function single(txId) { const packager = _.flow(_.compact, _.map(camelize)) const cashInSql = `SELECT 'cashIn' AS tx_class, txs.*, @@ -344,18 +497,17 @@ function single (txId) { return Promise.all([ db.oneOrNone(cashInSql, [cashInTx.PENDING_INTERVAL, txId]), - db.oneOrNone(cashOutSql, [txId, REDEEMABLE_AGE]) + db.oneOrNone(cashOutSql, [txId, REDEEMABLE_AGE]), ]) .then(packager) .then(_.head) } -function cancel (txId) { - return tx.cancel(txId) - .then(() => single(txId)) +function cancel(txId) { + return tx.cancel(txId).then(() => single(txId)) } -function getTx (txId, txClass) { +function getTx(txId, txClass) { const cashInSql = `select 'cashIn' as tx_class, txs.*, ((not txs.send_confirmed) and (txs.created <= now() - interval $1)) as expired from cash_in_txs as txs @@ -372,7 +524,7 @@ function getTx (txId, txClass) { : db.oneOrNone(cashOutSql, [txId, REDEEMABLE_AGE]) } -function getTxAssociatedData (txId, txClass) { +function getTxAssociatedData(txId, txClass) { const billsSql = `select 'bills' as bills, b.* from bills b where cash_in_txs_id = $1` const actionsSql = `select 'cash_out_actions' as cash_out_actions, actions.* from cash_out_actions actions where tx_id = $1` @@ -381,15 +533,27 @@ function getTxAssociatedData (txId, txClass) { : db.manyOrNone(actionsSql, [txId]) } -function updateTxCustomerPhoto (customerId, txId, direction, data) { +function updateTxCustomerPhoto(customerId, txId, direction, data) { const formattedData = _.mapKeys(_.snakeCase, data) - const cashInSql = 'UPDATE cash_in_txs SET tx_customer_photo_at = $1, tx_customer_photo_path = $2 WHERE customer_id=$3 AND id=$4' + const cashInSql = + 'UPDATE cash_in_txs SET tx_customer_photo_at = $1, tx_customer_photo_path = $2 WHERE customer_id=$3 AND id=$4' - const cashOutSql = 'UPDATE cash_out_txs SET tx_customer_photo_at = $1, tx_customer_photo_path = $2 WHERE customer_id=$3 AND id=$4' + const cashOutSql = + 'UPDATE cash_out_txs SET tx_customer_photo_at = $1, tx_customer_photo_path = $2 WHERE customer_id=$3 AND id=$4' return direction === 'cashIn' - ? db.oneOrNone(cashInSql, [formattedData.tx_customer_photo_at, formattedData.tx_customer_photo_path, customerId, txId]) - : db.oneOrNone(cashOutSql, [formattedData.tx_customer_photo_at, formattedData.tx_customer_photo_path, customerId, txId]) + ? db.oneOrNone(cashInSql, [ + formattedData.tx_customer_photo_at, + formattedData.tx_customer_photo_path, + customerId, + txId, + ]) + : db.oneOrNone(cashOutSql, [ + formattedData.tx_customer_photo_at, + formattedData.tx_customer_photo_path, + customerId, + txId, + ]) } module.exports = { @@ -399,5 +563,5 @@ module.exports = { getCustomerTransactionsBatch, getTx, getTxAssociatedData, - updateTxCustomerPhoto + updateTxCustomerPhoto, } diff --git a/packages/server/lib/new-config-manager.js b/packages/server/lib/new-config-manager.js index 239bfa20..ef4d2070 100644 --- a/packages/server/lib/new-config-manager.js +++ b/packages/server/lib/new-config-manager.js @@ -14,19 +14,26 @@ const namespaces = { CASH_OUT: 'cashOut', CASH_IN: 'cashIn', COMPLIANCE: 'compliance', - MACHINE_SCREENS: 'machineScreens' + MACHINE_SCREENS: 'machineScreens', } const machineScreens = { - RATES: 'rates' + RATES: 'rates', } -const stripl = _.curry((q, str) => _.startsWith(q, str) ? str.slice(q.length) : str) -const filter = namespace => _.pickBy((value, key) => _.startsWith(`${namespace}_`)(key)) +const stripl = _.curry((q, str) => + _.startsWith(q, str) ? str.slice(q.length) : str, +) +const filter = namespace => + _.pickBy((value, key) => _.startsWith(`${namespace}_`)(key)) const strip = key => _.mapKeys(stripl(`${key}_`)) -const fromNamespace = _.curry((key, config) => _.compose(strip(key), filter(key))(config)) -const toNamespace = _.curry((ns, config) => _.mapKeys(key => `${ns}_${key}`, config)) +const fromNamespace = _.curry((key, config) => + _.compose(strip(key), filter(key))(config), +) +const toNamespace = _.curry((ns, config) => + _.mapKeys(key => `${ns}_${key}`, config), +) const getCommissions = (cryptoCode, deviceId, config) => { const commissions = fromNamespace(namespaces.COMMISSIONS)(config) @@ -34,9 +41,12 @@ const getCommissions = (cryptoCode, deviceId, config) => { if (_.isEmpty(overrides)) return _.omit('overrides', commissions) - const specificFilter = it => it.machine === deviceId && _.includes(cryptoCode)(it.cryptoCurrencies) - const specificAllCoinsFilter = it => it.machine === deviceId && _.includes('ALL_COINS')(it.cryptoCurrencies) - const allMachinesFilter = it => it.machine === 'ALL_MACHINES' && _.includes(cryptoCode)(it.cryptoCurrencies) + const specificFilter = it => + it.machine === deviceId && _.includes(cryptoCode)(it.cryptoCurrencies) + const specificAllCoinsFilter = it => + it.machine === deviceId && _.includes('ALL_COINS')(it.cryptoCurrencies) + const allMachinesFilter = it => + it.machine === 'ALL_MACHINES' && _.includes(cryptoCode)(it.cryptoCurrencies) const specificOverrides = _.filter(specificFilter)(overrides) const specificAllCoinsOverrides = _.filter(specificAllCoinsFilter)(overrides) @@ -46,7 +56,7 @@ const getCommissions = (cryptoCode, deviceId, config) => { commissions, ...allMachinesOverrides, ...specificAllCoinsOverrides, - ...specificOverrides + ...specificOverrides, ] return _.omit('overrides', _.assignAll(priorityOrderOverrides)) @@ -55,7 +65,10 @@ const getCommissions = (cryptoCode, deviceId, config) => { const getLocale = (deviceId, it) => { const locale = fromNamespace(namespaces.LOCALE)(it) const filter = _.matches({ machine: deviceId }) - return _.omit('overrides', _.assignAll([locale, ..._.filter(filter)(locale.overrides)])) + return _.omit( + 'overrides', + _.assignAll([locale, ..._.filter(filter)(locale.overrides)]), + ) } const getGlobalLocale = it => getLocale(null, it) @@ -65,22 +78,41 @@ const getWalletSettings = (key, it) => { const getAdvancedSettings = it => { const advancedSettings = fromNamespace(namespaces.ADVANCED)(it) - return _.omit(['overrides', 'cryptoCurrency', 'id'], _.assignAll([advancedSettings, ..._.filter(filter)(advancedSettings.overrides)])) + return _.omit( + ['overrides', 'cryptoCurrency', 'id'], + _.assignAll([ + advancedSettings, + ..._.filter(filter)(advancedSettings.overrides), + ]), + ) } const walletsSettings = fromNamespace(namespaces.WALLETS)(it) - return _.assign(fromNamespace(key)(walletsSettings), getAdvancedSettings(walletsSettings)) + return _.assign( + fromNamespace(key)(walletsSettings), + getAdvancedSettings(walletsSettings), + ) } -const getCashOut = (key, it) => _.compose(fromNamespace(key), fromNamespace(namespaces.CASH_OUT))(it) +const getCashOut = (key, it) => + _.compose(fromNamespace(key), fromNamespace(namespaces.CASH_OUT))(it) const getGlobalCashOut = fromNamespace(namespaces.CASH_OUT) const getOperatorInfo = fromNamespace(namespaces.OPERATOR_INFO) const getCoinAtmRadar = fromNamespace(namespaces.COIN_ATM_RADAR) const getTermsConditions = fromNamespace(namespaces.TERMS_CONDITIONS) const getReceipt = fromNamespace(namespaces.RECEIPT) const getCompliance = fromNamespace(namespaces.COMPLIANCE) -const getMachineScreenOpts = (screenName, config) => _.compose(fromNamespace(screenName), fromNamespace(namespaces.MACHINE_SCREENS))(config) -const getAllMachineScreenOpts = config => _.reduce((acc, value) => ({ ...acc, [value]: getMachineScreenOpts(value, config) }), {}, _.values(machineScreens)) +const getMachineScreenOpts = (screenName, config) => + _.compose( + fromNamespace(screenName), + fromNamespace(namespaces.MACHINE_SCREENS), + )(config) +const getAllMachineScreenOpts = config => + _.reduce( + (acc, value) => ({ ...acc, [value]: getMachineScreenOpts(value, config) }), + {}, + _.values(machineScreens), + ) -const getAllCryptoCurrencies = (config) => { +const getAllCryptoCurrencies = config => { const locale = fromNamespace(namespaces.LOCALE)(config) const cryptos = locale.cryptoCurrencies const overridesCryptos = _.map(_.get('cryptoCurrencies'))(locale.overrides) @@ -92,17 +124,38 @@ const getNotifications = (cryptoCurrency, machine, config) => { const smsSettings = fromNamespace('sms', notifications) const emailSettings = fromNamespace('email', notifications) - const notificationCenterSettings = fromNamespace('notificationCenter', notifications) + const notificationCenterSettings = fromNamespace( + 'notificationCenter', + notifications, + ) - const notifNoOverrides = _.omit(['cryptoBalanceOverrides', 'fiatBalanceOverrides'], notifications) + const notifNoOverrides = _.omit( + ['cryptoBalanceOverrides', 'fiatBalanceOverrides'], + notifications, + ) const findByCryptoCurrency = _.find(_.matches({ cryptoCurrency })) const findByMachine = _.find(_.matches({ machine })) - const cryptoFields = ['cryptoHighBalance', 'cryptoLowBalance', 'highBalance', 'lowBalance'] - const fiatFields = ['cashInAlertThreshold', 'fillingPercentageCassette1', 'fillingPercentageCassette2', 'fillingPercentageCassette3', 'fillingPercentageCassette4'] + const cryptoFields = [ + 'cryptoHighBalance', + 'cryptoLowBalance', + 'highBalance', + 'lowBalance', + ] + const fiatFields = [ + 'cashInAlertThreshold', + 'fillingPercentageCassette1', + 'fillingPercentageCassette2', + 'fillingPercentageCassette3', + 'fillingPercentageCassette4', + ] - const getCryptoSettings = _.compose(_.pick(cryptoFields), _.defaultTo(notifications), findByCryptoCurrency) + const getCryptoSettings = _.compose( + _.pick(cryptoFields), + _.defaultTo(notifications), + findByCryptoCurrency, + ) const cryptoSettings = getCryptoSettings(notifications.cryptoBalanceOverrides) if (cryptoSettings.highBalance) { @@ -120,7 +173,14 @@ const getNotifications = (cryptoCurrency, machine, config) => { _.assignWith(_.defaultTo, notifications), _.pick(fiatFields), )(notifications.fiatBalanceOverrides) - return { ...notifNoOverrides, sms: smsSettings, email: emailSettings, ...cryptoSettings, ...fiatSettings, notificationCenter: notificationCenterSettings } + return { + ...notifNoOverrides, + sms: smsSettings, + email: emailSettings, + ...cryptoSettings, + ...fiatSettings, + notificationCenter: notificationCenterSettings, + } } const getGlobalNotifications = config => getNotifications(null, null, config) @@ -132,53 +192,77 @@ function getCustomerAuthenticationMethod(config) { } /* `customInfoRequests` is the result of a call to `getCustomInfoRequests` */ -const getTriggersAutomation = (customInfoRequests, config, oldFormat = false) => { - return customInfoRequests - .then(infoRequests => { - const defaultAutomation = _.get('triggersConfig_automation')(config) - const overrides = _.get('triggersConfig_overrides')(config) +const getTriggersAutomation = ( + customInfoRequests, + config, + oldFormat = false, +) => { + return customInfoRequests.then(infoRequests => { + const defaultAutomation = _.get('triggersConfig_automation')(config) + const overrides = _.get('triggersConfig_overrides')(config) - const requirements = { - sanctions: defaultAutomation, - idCardPhoto: defaultAutomation, - idCardData: defaultAutomation, - facephoto: defaultAutomation, - usSsn: defaultAutomation - } - - if (oldFormat) { - _.forEach(it => { requirements[it.id] = defaultAutomation }, infoRequests) - const oldRequirementsOverrides = _.reduce((acc, override) => _.assign(acc, { [override.requirement]: override.automation }), {}, overrides) - return _.assign(requirements, oldRequirementsOverrides) - } - - requirements.custom = [] + const requirements = { + sanctions: defaultAutomation, + idCardPhoto: defaultAutomation, + idCardData: defaultAutomation, + facephoto: defaultAutomation, + usSsn: defaultAutomation, + } + if (oldFormat) { _.forEach(it => { - requirements.custom.push({ id: it.id, type: defaultAutomation }) + requirements[it.id] = defaultAutomation }, infoRequests) + const oldRequirementsOverrides = _.reduce( + (acc, override) => + _.assign(acc, { [override.requirement]: override.automation }), + {}, + overrides, + ) + return _.assign(requirements, oldRequirementsOverrides) + } - const requirementsOverrides = _.reduce((acc, override) => { + requirements.custom = [] + + _.forEach(it => { + requirements.custom.push({ id: it.id, type: defaultAutomation }) + }, infoRequests) + + const requirementsOverrides = _.reduce( + (acc, override) => { return _.assign( acc, !validate(override.requirement) ? { [override.requirement]: override.automation } - : { custom: [...acc.custom ?? [], { id: override.requirement, type: override.automation }] } + : { + custom: [ + ...(acc.custom ?? []), + { id: override.requirement, type: override.automation }, + ], + }, ) - }, {}, overrides) + }, + {}, + overrides, + ) - return _.assign(requirements, requirementsOverrides) - }) + return _.assign(requirements, requirementsOverrides) + }) } const splitGetFirst = _.compose(_.head, _.split('_')) -const getCryptosFromWalletNamespace = - _.compose(_.without(['advanced']), _.uniq, _.map(splitGetFirst), _.keys, fromNamespace('wallets')) +const getCryptosFromWalletNamespace = _.compose( + _.without(['advanced']), + _.uniq, + _.map(splitGetFirst), + _.keys, + fromNamespace('wallets'), +) const getCashInSettings = config => fromNamespace(namespaces.CASH_IN)(config) -const getCryptoUnits = (crypto, config) => +const getCryptoUnits = (crypto, config) => getWalletSettings(crypto, config).cryptoUnits ?? 'full' const setTermsConditions = toNamespace(namespaces.TERMS_CONDITIONS) diff --git a/packages/server/lib/new-settings-loader.js b/packages/server/lib/new-settings-loader.js index 5b4dc0a7..090d80ba 100644 --- a/packages/server/lib/new-settings-loader.js +++ b/packages/server/lib/new-settings-loader.js @@ -3,7 +3,10 @@ const crypto = require('crypto') const _ = require('lodash/fp') const db = require('./db') const { getOperatorId } = require('./operator') -const { getTermsConditions, setTermsConditions } = require('./new-config-manager') +const { + getTermsConditions, + setTermsConditions, +} = require('./new-config-manager') const NEW_SETTINGS_LOADER_SCHEMA_VERSION = 2 const PASSWORD_FILLED = 'PASSWORD_FILLED' @@ -27,39 +30,32 @@ const SECRET_FIELDS = [ 'galoy.apiSecret', 'bitfinex.secret', 'sumsub.apiToken', - 'sumsub.privateKey' + 'sumsub.privateKey', ] /* * JSON.stringify isn't necessarily deterministic so this function may compute * different hashes for the same object. */ -const md5hash = text => - crypto - .createHash('MD5') - .update(text) - .digest('hex') +const md5hash = text => crypto.createHash('MD5').update(text).digest('hex') const addTermsHash = configs => { const terms = _.omit(['hash'], getTermsConditions(configs)) - return !terms?.text ? - configs : - _.flow( - _.get('text'), - md5hash, - hash => _.set('hash', hash, terms), - setTermsConditions, - _.assign(configs), - )(terms) + return !terms?.text + ? configs + : _.flow( + _.get('text'), + md5hash, + hash => _.set('hash', hash, terms), + setTermsConditions, + _.assign(configs), + )(terms) } const notifyReload = (dbOrTx, operatorId) => - dbOrTx.none( - 'NOTIFY $1:name, $2', - ['reload', JSON.stringify({ operatorId })] - ) + dbOrTx.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ operatorId })]) -function saveAccounts (accounts) { +function saveAccounts(accounts) { if (!accounts) { return Promise.resolve() } @@ -68,8 +64,8 @@ function saveAccounts (accounts) { INSERT INTO user_config (type, data, valid, schema_version) SELECT 'accounts', $1, TRUE, $2 WHERE 'accounts' NOT IN (SELECT type FROM user_config)` - return Promise.all([loadAccounts(), getOperatorId('middleware')]) - .then(([currentAccounts, operatorId]) => { + return Promise.all([loadAccounts(), getOperatorId('middleware')]).then( + ([currentAccounts, operatorId]) => { const newAccounts = _.merge(currentAccounts, accounts) // Only allow one wallet scoring active at a time @@ -81,14 +77,21 @@ function saveAccounts (accounts) { newAccounts.elliptic.enabled = false } - return db.tx(t => - t.none(accountsSql, [{ accounts: newAccounts }, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) - .then(() => notifyReload(t, operatorId)) - ).catch(console.error) - }) + return db + .tx(t => + t + .none(accountsSql, [ + { accounts: newAccounts }, + NEW_SETTINGS_LOADER_SCHEMA_VERSION, + ]) + .then(() => notifyReload(t, operatorId)), + ) + .catch(console.error) + }, + ) } -function loadAccounts (schemaVersion) { +function loadAccounts(schemaVersion) { const sql = `SELECT data FROM user_config WHERE type = $1 @@ -97,72 +100,85 @@ function loadAccounts (schemaVersion) { ORDER BY id DESC LIMIT 1` - return db.oneOrNone(sql, ['accounts', schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db + .oneOrNone(sql, [ + 'accounts', + schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION, + ]) .then(_.compose(_.defaultTo({}), _.get('data.accounts'))) } -function hideSecretFields (accounts) { +function hideSecretFields(accounts) { return _.flow( _.filter(path => !_.isEmpty(_.get(path, accounts))), _.reduce( (accounts, path) => _.assoc(path, PASSWORD_FILLED, accounts), - accounts - ) + accounts, + ), )(SECRET_FIELDS) } -function showAccounts (schemaVersion) { - return loadAccounts(schemaVersion) - .then(hideSecretFields) +function showAccounts(schemaVersion) { + return loadAccounts(schemaVersion).then(hideSecretFields) } const insertConfigRow = (dbOrTx, data) => dbOrTx.none( "INSERT INTO user_config (type, data, valid, schema_version) VALUES ('config', $1, TRUE, $2)", - [data, NEW_SETTINGS_LOADER_SCHEMA_VERSION] + [data, NEW_SETTINGS_LOADER_SCHEMA_VERSION], ) -function saveConfig (config) { - return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')]) - .then(([currentConfig, operatorId]) => { - const newConfig = addTermsHash(_.assign(currentConfig, config)) - return db.tx(t => - insertConfigRow(t, { config: newConfig }) - .then(() => notifyReload(t, operatorId)) - ).catch(console.error) - }) +function saveConfig(config) { + return Promise.all([ + loadLatestConfigOrNone(), + getOperatorId('middleware'), + ]).then(([currentConfig, operatorId]) => { + const newConfig = addTermsHash(_.assign(currentConfig, config)) + return db + .tx(t => + insertConfigRow(t, { config: newConfig }).then(() => + notifyReload(t, operatorId), + ), + ) + .catch(console.error) + }) } -function removeFromConfig (fields) { - return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')]) - .then(([currentConfig, operatorId]) => { - const newConfig = _.omit(fields, currentConfig) - return db.tx(t => - insertConfigRow(t, { config: newConfig }) - .then(() => notifyReload(t, operatorId)) - ).catch(console.error) - }) +function removeFromConfig(fields) { + return Promise.all([ + loadLatestConfigOrNone(), + getOperatorId('middleware'), + ]).then(([currentConfig, operatorId]) => { + const newConfig = _.omit(fields, currentConfig) + return db + .tx(t => + insertConfigRow(t, { config: newConfig }).then(() => + notifyReload(t, operatorId), + ), + ) + .catch(console.error) + }) } -function migrationSaveConfig (config) { - return loadLatestConfigOrNone() - .then(currentConfig => { - const newConfig = _.assign(currentConfig, config) - return insertConfigRow(db, { config: newConfig }) - .catch(console.error) - }) +function migrationSaveConfig(config) { + return loadLatestConfigOrNone().then(currentConfig => { + const newConfig = _.assign(currentConfig, config) + return insertConfigRow(db, { config: newConfig }).catch(console.error) + }) } -function loadLatest (schemaVersion) { - return Promise.all([loadLatestConfigOrNoneReturningVersion(schemaVersion), loadAccounts(schemaVersion)]) - .then(([configObj, accounts]) => ({ - config: configObj.config, - accounts, - version: configObj.version - })) +function loadLatest(schemaVersion) { + return Promise.all([ + loadLatestConfigOrNoneReturningVersion(schemaVersion), + loadAccounts(schemaVersion), + ]).then(([configObj, accounts]) => ({ + config: configObj.config, + accounts, + version: configObj.version, + })) } -function loadLatestConfig () { +function loadLatestConfig() { const sql = `SELECT data FROM user_config WHERE type = 'config' @@ -170,15 +186,16 @@ function loadLatestConfig () { AND valid ORDER BY id DESC LIMIT 1` - - return db.oneOrNone(sql, [NEW_SETTINGS_LOADER_SCHEMA_VERSION]) - .then(row => row ? row.data.config : {}) + + return db + .oneOrNone(sql, [NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + .then(row => (row ? row.data.config : {})) .catch(err => { throw err }) } -function loadLatestConfigOrNoneReturningVersion (schemaVersion) { +function loadLatestConfigOrNoneReturningVersion(schemaVersion) { const sql = `SELECT data, id FROM user_config WHERE type = 'config' @@ -187,23 +204,25 @@ function loadLatestConfigOrNoneReturningVersion (schemaVersion) { ORDER BY id DESC LIMIT 1` - return db.oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) - .then(row => row ? { config: row.data.config, version: row.id } : {}) + return db + .oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + .then(row => (row ? { config: row.data.config, version: row.id } : {})) } -function loadLatestConfigOrNone (schemaVersion) { +function loadLatestConfigOrNone(schemaVersion) { const sql = `SELECT data FROM user_config WHERE type = 'config' AND schema_version = $1 ORDER BY id DESC LIMIT 1` - - return db.oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) - .then(row => row ? row.data.config : {}) + + return db + .oneOrNone(sql, [schemaVersion || NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + .then(row => (row ? row.data.config : {})) } -function loadConfig (versionId) { +function loadConfig(versionId) { const sql = `SELECT data FROM user_config WHERE id = $1 @@ -211,7 +230,8 @@ function loadConfig (versionId) { AND schema_version = $2 AND valid` - return db.one(sql, [versionId, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db + .one(sql, [versionId, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) .then(row => row.data.config) .catch(err => { if (err.name === 'QueryResultError') { @@ -222,14 +242,15 @@ function loadConfig (versionId) { }) } -function load (versionId) { +function load(versionId) { if (!versionId) Promise.reject('versionId is required') - - return Promise.all([loadConfig(versionId), loadAccounts()]) - .then(([config, accounts]) => ({ + + return Promise.all([loadConfig(versionId), loadAccounts()]).then( + ([config, accounts]) => ({ config, - accounts - })) + accounts, + }), + ) } const fetchCurrentConfigVersion = () => { diff --git a/packages/server/lib/notifier/codes.js b/packages/server/lib/notifier/codes.js index b0e02895..3ff31b2e 100644 --- a/packages/server/lib/notifier/codes.js +++ b/packages/server/lib/notifier/codes.js @@ -18,7 +18,7 @@ const CODES_DISPLAY = { LOW_CASH_OUT: 'Low Cash-out', LOW_RECYCLER_STACKER: 'Low Recycler Stacker', HIGH_RECYCLER_STACKER: 'High Recycler Stacker', - CASHBOX_REMOVED: 'Cashbox removed' + CASHBOX_REMOVED: 'Cashbox removed', } const NETWORK_DOWN_TIME = 3 * T.minute @@ -32,7 +32,7 @@ const NOTIFICATION_TYPES = { CRYPTO_BALANCE: 'cryptoBalance', COMPLIANCE: 'compliance', ERROR: 'error', - SECURITY: 'security' + SECURITY: 'security', } module.exports = { @@ -48,5 +48,5 @@ module.exports = { NETWORK_DOWN_TIME, STALE_STATE, ALERT_SEND_INTERVAL, - NOTIFICATION_TYPES + NOTIFICATION_TYPES, } diff --git a/packages/server/lib/notifier/email.js b/packages/server/lib/notifier/email.js index c53df248..e886aa8c 100644 --- a/packages/server/lib/notifier/email.js +++ b/packages/server/lib/notifier/email.js @@ -11,10 +11,10 @@ const { CASH_BOX_FULL, LOW_CASH_OUT, LOW_RECYCLER_STACKER, - SECURITY + SECURITY, } = require('./codes') -function alertSubject (alertRec, config) { +function alertSubject(alertRec, config) { let alerts = [] if (config.balance) { @@ -27,11 +27,16 @@ function alertSubject (alertRec, config) { if (alerts.length === 0) return null - const alertTypes = _.flow(_.map('code'), _.uniq, _.map(utils.codeDisplay), _.sortBy(o => o))(alerts) + const alertTypes = _.flow( + _.map('code'), + _.uniq, + _.map(utils.codeDisplay), + _.sortBy(o => o), + )(alerts) return '[Lamassu] Errors reported: ' + alertTypes.join(', ') } -function printEmailAlerts (alertRec, config) { +function printEmailAlerts(alertRec, config) { let body = 'Errors were reported by your Lamassu Machines.\n' if (config.balance && alertRec.general.length !== 0) { @@ -50,30 +55,39 @@ function printEmailAlerts (alertRec, config) { return body } -function emailAlerts (alerts) { +function emailAlerts(alerts) { return _.join('\n', _.map(emailAlert, alerts)) + '\n' } -function emailAlert (alert) { +function emailAlert(alert) { switch (alert.code) { case PING: if (alert.age) { - const pingAge = utils.formatAge(alert.age, { compact: true, verbose: true }) + const pingAge = utils.formatAge(alert.age, { + compact: true, + verbose: true, + }) return `Machine down for ${pingAge}` } return 'Machine down for a while.' case STALE: { - const stuckAge = utils.formatAge(alert.age, { compact: true, verbose: true }) + const stuckAge = utils.formatAge(alert.age, { + compact: true, + verbose: true, + }) return `Machine is stuck on ${alert.state} screen for ${stuckAge}` } case LOW_CRYPTO_BALANCE: { - const balance = utils.formatCurrency(alert.fiatBalance.balance, alert.fiatCode) + const balance = utils.formatCurrency( + alert.fiatBalance.balance, + alert.fiatCode, + ) return `Low balance in ${alert.cryptoCode} [${balance}]` } case HIGH_CRYPTO_BALANCE: { const highBalance = utils.formatCurrency( alert.fiatBalance.balance, - alert.fiatCode + alert.fiatCode, ) return `High balance in ${alert.cryptoCode} [${highBalance}]` } diff --git a/packages/server/lib/notifier/index.js b/packages/server/lib/notifier/index.js index 8e61f162..a45ac7d4 100644 --- a/packages/server/lib/notifier/index.js +++ b/packages/server/lib/notifier/index.js @@ -13,48 +13,52 @@ const smsFuncs = require('./sms') const webhookFuncs = require('./webhook') const { STALE, STALE_STATE } = require('./codes') -function buildMessage (alerts, notifications) { +function buildMessage(alerts, notifications) { const smsEnabled = utils.isActive(notifications.sms) const emailEnabled = utils.isActive(notifications.email) let rec = {} if (smsEnabled) { rec = _.set(['sms', 'body'])( - smsFuncs.printSmsAlerts(alerts, notifications.sms) + smsFuncs.printSmsAlerts(alerts, notifications.sms), )(rec) } if (emailEnabled) { rec = _.set(['email', 'subject'])( - emailFuncs.alertSubject(alerts, notifications.email) + emailFuncs.alertSubject(alerts, notifications.email), )(rec) rec = _.set(['email', 'body'])( - emailFuncs.printEmailAlerts(alerts, notifications.email) + emailFuncs.printEmailAlerts(alerts, notifications.email), )(rec) } return rec } -function checkNotification (plugins) { +function checkNotification(plugins) { const notifications = plugins.getNotificationConfig() const smsEnabled = utils.isActive(notifications.sms) const emailEnabled = utils.isActive(notifications.email) - const notificationCenterEnabled = utils.isActive(notifications.notificationCenter) + const notificationCenterEnabled = utils.isActive( + notifications.notificationCenter, + ) - if (!(notificationCenterEnabled || smsEnabled || emailEnabled)) return Promise.resolve() + if (!(notificationCenterEnabled || smsEnabled || emailEnabled)) + return Promise.resolve() return getAlerts(plugins) .then(alerts => { notifyIfActive('errors', 'errorAlertsNotify', alerts) const currentAlertFingerprint = utils.buildAlertFingerprint( alerts, - notifications + notifications, ) if (!currentAlertFingerprint) { const inAlert = !!utils.getAlertFingerprint() // variables for setAlertFingerprint: (fingerprint = null, lastAlertTime = null) utils.setAlertFingerprint(null, null) - if (inAlert) return utils.sendNoAlerts(plugins, smsEnabled, emailEnabled) + if (inAlert) + return utils.sendNoAlerts(plugins, smsEnabled, emailEnabled) } if (utils.shouldNotAlert(currentAlertFingerprint)) return @@ -70,18 +74,18 @@ function checkNotification (plugins) { .catch(logger.error) } -function getAlerts (plugins) { +function getAlerts(plugins) { return Promise.all([ plugins.checkBalances(), queries.machineEvents(), - plugins.getMachineNames() + plugins.getMachineNames(), ]).then(([balances, events, devices]) => { notifyIfActive('balance', 'balancesNotify', balances) return buildAlerts(checkPings(devices), balances, events, devices) }) } -function buildAlerts (pings, balances, events, devices) { +function buildAlerts(pings, balances, events, devices) { const alerts = { devices: {}, deviceNames: {} } alerts.general = _.filter(r => !r.deviceId, balances) _.forEach(device => { @@ -89,10 +93,11 @@ function buildAlerts (pings, balances, events, devices) { const ping = pings[deviceId] || [] const stuckScreen = checkStuckScreen(events, device) - alerts.devices = _.set([deviceId, 'balanceAlerts'], _.filter( - ['deviceId', deviceId], - balances - ), alerts.devices) + alerts.devices = _.set( + [deviceId, 'balanceAlerts'], + _.filter(['deviceId', deviceId], balances), + alerts.devices, + ) alerts.devices[deviceId].deviceAlerts = _.isEmpty(ping) ? stuckScreen : ping alerts.deviceNames[deviceId] = device.name @@ -101,18 +106,18 @@ function buildAlerts (pings, balances, events, devices) { return alerts } -function checkPings (devices) { +function checkPings(devices) { const deviceIds = _.map('deviceId', devices) const pings = _.map(utils.checkPing, devices) return _.zipObject(deviceIds)(pings) } -function checkStuckScreen (deviceEvents, machine) { +function checkStuckScreen(deviceEvents, machine) { const lastEvent = _.pipe( _.filter(e => e.device_id === machine.deviceId), _.sortBy(utils.getDeviceTime), _.map(utils.parseEventNote), - _.last + _.last, )(deviceEvents) if (!lastEvent) return [] @@ -129,115 +134,152 @@ function checkStuckScreen (deviceEvents, machine) { return [] } -function transactionNotify (tx, rec) { +function transactionNotify(tx, rec) { return settingsLoader.loadLatestConfig().then(config => { const notifSettings = configManager.getGlobalNotifications(config) - const highValueTx = tx.fiat.gt(notifSettings.highValueTransaction || Infinity) + const highValueTx = tx.fiat.gt( + notifSettings.highValueTransaction || Infinity, + ) const isCashOut = tx.direction === 'cashOut' // for notification center const directionDisplay = isCashOut ? 'cash-out' : 'cash-in' - const readyToNotify = !isCashOut || (tx.direction === 'cashOut' && rec.isRedemption) + const readyToNotify = + !isCashOut || (tx.direction === 'cashOut' && rec.isRedemption) // awaiting for redesign. notification should not be sent if toggle in the settings table is disabled, // but currently we're sending notifications of high value tx even with the toggle disabled if (readyToNotify && !highValueTx) { - notifyIfActive('transactions', 'notifCenterTransactionNotify', highValueTx, directionDisplay, tx.fiat, tx.fiatCode, tx.deviceId, tx.toAddress) + notifyIfActive( + 'transactions', + 'notifCenterTransactionNotify', + highValueTx, + directionDisplay, + tx.fiat, + tx.fiatCode, + tx.deviceId, + tx.toAddress, + ) } else if (readyToNotify && highValueTx) { - notificationCenter.notifCenterTransactionNotify(highValueTx, directionDisplay, tx.fiat, tx.fiatCode, tx.deviceId, tx.toAddress) + notificationCenter.notifCenterTransactionNotify( + highValueTx, + directionDisplay, + tx.fiat, + tx.fiatCode, + tx.deviceId, + tx.toAddress, + ) } // alert through sms or email any transaction or high value transaction, if SMS || email alerts are enabled - const walletSettings = configManager.getWalletSettings(tx.cryptoCode, config) + const walletSettings = configManager.getWalletSettings( + tx.cryptoCode, + config, + ) const zeroConfLimit = walletSettings.zeroConfLimit || 0 const zeroConf = isCashOut && tx.fiat.lte(zeroConfLimit) - const notificationsEnabled = notifSettings.sms.transactions || notifSettings.email.transactions - const customerPromise = tx.customerId ? customers.getById(tx.customerId) : Promise.resolve({}) + const notificationsEnabled = + notifSettings.sms.transactions || notifSettings.email.transactions + const customerPromise = tx.customerId + ? customers.getById(tx.customerId) + : Promise.resolve({}) if (!notificationsEnabled && !highValueTx) return Promise.resolve() - if (zeroConf && isCashOut && !rec.isRedemption && !rec.error) return Promise.resolve() - if (!zeroConf && rec.isRedemption) return sendRedemptionMessage(tx.id, rec.error) + if (zeroConf && isCashOut && !rec.isRedemption && !rec.error) + return Promise.resolve() + if (!zeroConf && rec.isRedemption) + return sendRedemptionMessage(tx.id, rec.error) - return Promise.all([ - queries.getMachineName(tx.deviceId), - customerPromise - ]).then(([machineName, customer]) => { - return utils.buildTransactionMessage(tx, rec, highValueTx, machineName, customer) - }).then(([msg, highValueTx]) => sendTransactionMessage(msg, highValueTx)) + return Promise.all([queries.getMachineName(tx.deviceId), customerPromise]) + .then(([machineName, customer]) => { + return utils.buildTransactionMessage( + tx, + rec, + highValueTx, + machineName, + customer, + ) + }) + .then(([msg, highValueTx]) => sendTransactionMessage(msg, highValueTx)) }) } -function complianceNotify (settings, customer, deviceId, action, period) { - const timestamp = (new Date()).toLocaleString() - return queries.getMachineName(deviceId) - .then(machineName => { - const notifications = configManager.getGlobalNotifications(settings.config) +function complianceNotify(settings, customer, deviceId, action, period) { + const timestamp = new Date().toLocaleString() + return queries.getMachineName(deviceId).then(machineName => { + const notifications = configManager.getGlobalNotifications(settings.config) - const msgCore = { - BLOCKED: `was blocked`, - SUSPENDED: `was suspended for ${!!period && period} days`, - PENDING_COMPLIANCE: `is waiting for your manual approval`, - } + const msgCore = { + BLOCKED: `was blocked`, + SUSPENDED: `was suspended for ${!!period && period} days`, + PENDING_COMPLIANCE: `is waiting for your manual approval`, + } - const rec = { - sms: { - body: `Customer ${customer.phone} ${msgCore[action]} - ${machineName}. ${timestamp}` - }, - email: { - subject: `Customer compliance`, - body: `Customer ${customer.phone} ${msgCore[action]} in machine ${machineName}. ${timestamp}` - }, - webhook: { - topic: `Customer compliance`, - content: `Customer ${customer.phone} ${msgCore[action]} in machine ${machineName}. ${timestamp}` - } - } + const rec = { + sms: { + body: `Customer ${customer.phone} ${msgCore[action]} - ${machineName}. ${timestamp}`, + }, + email: { + subject: `Customer compliance`, + body: `Customer ${customer.phone} ${msgCore[action]} in machine ${machineName}. ${timestamp}`, + }, + webhook: { + topic: `Customer compliance`, + content: `Customer ${customer.phone} ${msgCore[action]} in machine ${machineName}. ${timestamp}`, + }, + } - const promises = [] + const promises = [] - const emailActive = - notifications.email.active && - notifications.email.compliance + const emailActive = + notifications.email.active && notifications.email.compliance - const smsActive = - notifications.sms.active && - notifications.sms.compliance + const smsActive = notifications.sms.active && notifications.sms.compliance - const webhookActive = true + const webhookActive = true - if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec)) - if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec)) - if (webhookActive) promises.push(webhookFuncs.sendMessage(settings, rec)) + if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec)) + if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec)) + if (webhookActive) promises.push(webhookFuncs.sendMessage(settings, rec)) - notifyIfActive('compliance', 'customerComplianceNotify', customer, deviceId, action, machineName, period) + notifyIfActive( + 'compliance', + 'customerComplianceNotify', + customer, + deviceId, + action, + machineName, + period, + ) - return Promise.all(promises) - .catch(err => console.error(`An error occurred when sending a notification. Please check your notification preferences and 3rd party account configuration: ${err.stack}`)) - }) + return Promise.all(promises).catch(err => + console.error( + `An error occurred when sending a notification. Please check your notification preferences and 3rd party account configuration: ${err.stack}`, + ), + ) + }) } -function sendRedemptionMessage (txId, error) { +function sendRedemptionMessage(txId, error) { const subject = `Here's an update on transaction ${txId}` - const body = error - ? `Error: ${error}` - : 'It was just dispensed successfully' + const body = error ? `Error: ${error}` : 'It was just dispensed successfully' const rec = { sms: { - body: `${subject} - ${body}` + body: `${subject} - ${body}`, }, email: { subject, - body + body, }, webhook: { topic: `Transaction update`, - content: body - } + content: body, + }, } return sendTransactionMessage(rec) } -function sendTransactionMessage (rec, isHighValueTx) { +function sendTransactionMessage(rec, isHighValueTx) { return settingsLoader.loadLatest().then(settings => { const notifications = configManager.getGlobalNotifications(settings.config) @@ -258,62 +300,74 @@ function sendTransactionMessage (rec, isHighValueTx) { const webhookActive = true if (webhookActive) promises.push(webhookFuncs.sendMessage(settings, rec)) - return Promise.all(promises) - .catch(err => console.error(`An error occurred when sending a notification. Please check your notification preferences and 3rd party account configuration: ${err.stack}`)) + return Promise.all(promises).catch(err => + console.error( + `An error occurred when sending a notification. Please check your notification preferences and 3rd party account configuration: ${err.stack}`, + ), + ) }) } -function cashboxNotify (deviceId) { +function cashboxNotify(deviceId) { return Promise.all([ settingsLoader.loadLatest(), - queries.getMachineName(deviceId) - ]) - .then(([settings, machineName]) => { - const notifications = configManager.getGlobalNotifications(settings.config) - const rec = { - sms: { - body: `Cashbox removed - ${machineName}` - }, - email: { - subject: `Cashbox removal`, - body: `Cashbox removed in machine ${machineName}` - }, - webhook: { - topic: `Cashbox removal`, - content: `Cashbox removed in machine ${machineName}` - } - } + queries.getMachineName(deviceId), + ]).then(([settings, machineName]) => { + const notifications = configManager.getGlobalNotifications(settings.config) + const rec = { + sms: { + body: `Cashbox removed - ${machineName}`, + }, + email: { + subject: `Cashbox removal`, + body: `Cashbox removed in machine ${machineName}`, + }, + webhook: { + topic: `Cashbox removal`, + content: `Cashbox removed in machine ${machineName}`, + }, + } - const promises = [] + const promises = [] - const emailActive = - notifications.email.active && - notifications.email.security + const emailActive = + notifications.email.active && notifications.email.security - const smsActive = - notifications.sms.active && - notifications.sms.security + const smsActive = notifications.sms.active && notifications.sms.security - const webhookActive = true - - if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec)) - if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec)) - if (webhookActive) promises.push(webhookFuncs.sendMessage(settings, rec)) - notifyIfActive('security', 'cashboxNotify', deviceId) + const webhookActive = true - return Promise.all(promises) - .catch(err => console.error(`An error occurred when sending a notification. Please check your notification preferences and 3rd party account configuration: ${err.stack}`)) - }) + if (emailActive) promises.push(emailFuncs.sendMessage(settings, rec)) + if (smsActive) promises.push(smsFuncs.sendMessage(settings, rec)) + if (webhookActive) promises.push(webhookFuncs.sendMessage(settings, rec)) + notifyIfActive('security', 'cashboxNotify', deviceId) + + return Promise.all(promises).catch(err => + console.error( + `An error occurred when sending a notification. Please check your notification preferences and 3rd party account configuration: ${err.stack}`, + ), + ) + }) } // for notification center, check if type of notification is active before calling the respective notify function const notifyIfActive = (type, fnName, ...args) => { - return settingsLoader.loadLatestConfig().then(config => { - const notificationSettings = configManager.getGlobalNotifications(config).notificationCenter - if (!notificationCenter[fnName]) return Promise.reject(new Error(`Notification function ${fnName} for type ${type} does not exist`)) - if (!(notificationSettings.active && notificationSettings[type])) return Promise.resolve() - return notificationCenter[fnName](...args) - }).catch(logger.error) + return settingsLoader + .loadLatestConfig() + .then(config => { + const notificationSettings = + configManager.getGlobalNotifications(config).notificationCenter + if (!notificationCenter[fnName]) + return Promise.reject( + new Error( + `Notification function ${fnName} for type ${type} does not exist`, + ), + ) + if (!(notificationSettings.active && notificationSettings[type])) + return Promise.resolve() + return notificationCenter[fnName](...args) + }) + .catch(logger.error) } module.exports = { @@ -324,5 +378,5 @@ module.exports = { checkStuckScreen, sendRedemptionMessage, cashboxNotify, - notifyIfActive + notifyIfActive, } diff --git a/packages/server/lib/notifier/notificationCenter.js b/packages/server/lib/notifier/notificationCenter.js index 7f3480b0..f77817fa 100644 --- a/packages/server/lib/notifier/notificationCenter.js +++ b/packages/server/lib/notifier/notificationCenter.js @@ -11,7 +11,7 @@ const { FIAT_BALANCE, ERROR, HIGH_VALUE_TX, - NORMAL_VALUE_TX + NORMAL_VALUE_TX, }, STALE, @@ -28,9 +28,15 @@ const sanctionsNotify = (customer, phone) => { const code = 'SANCTIONS' const detailB = utils.buildDetail({ customerId: customer.id, code }) const addNotif = phone => - queries.addNotification(COMPLIANCE, `Blocked customer with phone ${phone} for being on the OFAC sanctions list`, detailB) + queries.addNotification( + COMPLIANCE, + `Blocked customer with phone ${phone} for being on the OFAC sanctions list`, + detailB, + ) // if it's a new customer then phone comes as undefined - return phone ? addNotif(phone) : customers.getById(customer.id).then(c => addNotif(c.phone)) + return phone + ? addNotif(phone) + : customers.getById(customer.id).then(c => addNotif(c.phone)) } const clearOldCustomerSuspendedNotifications = (customerId, deviceId) => { @@ -38,16 +44,25 @@ const clearOldCustomerSuspendedNotifications = (customerId, deviceId) => { return queries.invalidateNotification(detailB, 'compliance') } -const customerComplianceNotify = (customer, deviceId, code, machineName, days = null) => { +const customerComplianceNotify = ( + customer, + deviceId, + code, + machineName, + days = null, +) => { // code for now can be "BLOCKED", "SUSPENDED" const detailB = utils.buildDetail({ customerId: customer.id, code, deviceId }) const date = new Date() if (days) { date.setDate(date.getDate() + days) } - const message = code === 'SUSPENDED' ? `Customer ${customer.phone} suspended until ${date.toLocaleString()}` : - code === 'BLOCKED' ? `Customer ${customer.phone} blocked` : - `Customer ${customer.phone} has pending compliance in machine ${machineName}` + const message = + code === 'SUSPENDED' + ? `Customer ${customer.phone} suspended until ${date.toLocaleString()}` + : code === 'BLOCKED' + ? `Customer ${customer.phone} blocked` + : `Customer ${customer.phone} has pending compliance in machine ${machineName}` return clearOldCustomerSuspendedNotifications(customer.id, deviceId) .then(() => queries.getValidNotifications(COMPLIANCE, detailB)) @@ -57,35 +72,52 @@ const customerComplianceNotify = (customer, deviceId, code, machineName, days = }) } -const clearOldFiatNotifications = (balances) => { +const clearOldFiatNotifications = balances => { return queries.getAllValidNotifications(FIAT_BALANCE).then(notifications => { const filterByBalance = _.filter(notification => { const { cassette, deviceId } = notification.detail - return !_.find(balance => balance.cassette === cassette && balance.deviceId === deviceId)(balances) + return !_.find( + balance => + balance.cassette === cassette && balance.deviceId === deviceId, + )(balances) }) - const indexesToInvalidate = _.compose(_.map('id'), filterByBalance)(notifications) + const indexesToInvalidate = _.compose( + _.map('id'), + filterByBalance, + )(notifications) const notInvalidated = _.filter(notification => { return !_.find(id => notification.id === id)(indexesToInvalidate) }, notifications) - return (indexesToInvalidate.length ? queries.batchInvalidate(indexesToInvalidate) : Promise.resolve()).then(() => notInvalidated) + return ( + indexesToInvalidate.length + ? queries.batchInvalidate(indexesToInvalidate) + : Promise.resolve() + ).then(() => notInvalidated) }) } -const fiatBalancesNotify = (fiatWarnings) => { +const fiatBalancesNotify = fiatWarnings => { return clearOldFiatNotifications(fiatWarnings).then(notInvalidated => { return fiatWarnings.forEach(balance => { - if (_.find(o => { - const { cassette, deviceId } = o.detail - return cassette === balance.cassette && deviceId === balance.deviceId - }, notInvalidated)) return - const message = balance.code === LOW_CASH_OUT ? - `Cash-out cassette ${balance.cassette} low or empty!` : - balance.code === LOW_RECYCLER_STACKER ? - `Recycler ${balance.cassette} low or empty!` : - balance.code === CASH_BOX_FULL ? - `Cash box full or almost full!` : - `Cash box full or almost full!` /* Shouldn't happen */ - const detailB = utils.buildDetail({ deviceId: balance.deviceId, cassette: balance.cassette }) + if ( + _.find(o => { + const { cassette, deviceId } = o.detail + return cassette === balance.cassette && deviceId === balance.deviceId + }, notInvalidated) + ) + return + const message = + balance.code === LOW_CASH_OUT + ? `Cash-out cassette ${balance.cassette} low or empty!` + : balance.code === LOW_RECYCLER_STACKER + ? `Recycler ${balance.cassette} low or empty!` + : balance.code === CASH_BOX_FULL + ? `Cash box full or almost full!` + : `Cash box full or almost full!` /* Shouldn't happen */ + const detailB = utils.buildDetail({ + deviceId: balance.deviceId, + cassette: balance.cassette, + }) return queries.addNotification(FIAT_BALANCE, message, detailB) }) }) @@ -95,82 +127,112 @@ const clearOldCryptoNotifications = balances => { return queries.getAllValidNotifications(CRYPTO_BALANCE).then(res => { const filterByBalance = _.filter(notification => { const { cryptoCode, code } = notification.detail - return !_.find(balance => balance.cryptoCode === cryptoCode && balance.code === code)(balances) + return !_.find( + balance => balance.cryptoCode === cryptoCode && balance.code === code, + )(balances) }) const indexesToInvalidate = _.compose(_.map('id'), filterByBalance)(res) const notInvalidated = _.filter(notification => { return !_.find(id => notification.id === id)(indexesToInvalidate) }, res) - return (indexesToInvalidate.length ? queries.batchInvalidate(indexesToInvalidate) : Promise.resolve()).then(() => notInvalidated) + return ( + indexesToInvalidate.length + ? queries.batchInvalidate(indexesToInvalidate) + : Promise.resolve() + ).then(() => notInvalidated) }) } -const cryptoBalancesNotify = (cryptoWarnings) => { +const cryptoBalancesNotify = cryptoWarnings => { return clearOldCryptoNotifications(cryptoWarnings).then(notInvalidated => { return cryptoWarnings.forEach(balance => { // if notification exists in DB and wasnt invalidated then don't add a duplicate - if (_.find(o => { - const { code, cryptoCode } = o.detail - return code === balance.code && cryptoCode === balance.cryptoCode - }, notInvalidated)) return + if ( + _.find(o => { + const { code, cryptoCode } = o.detail + return code === balance.code && cryptoCode === balance.cryptoCode + }, notInvalidated) + ) + return - const fiat = utils.formatCurrency(balance.fiatBalance.balance, balance.fiatCode) + const fiat = utils.formatCurrency( + balance.fiatBalance.balance, + balance.fiatCode, + ) const message = `${balance.code === HIGH_CRYPTO_BALANCE ? 'High' : 'Low'} balance in ${balance.cryptoCode} [${fiat}]` - const detailB = utils.buildDetail({ cryptoCode: balance.cryptoCode, code: balance.code }) + const detailB = utils.buildDetail({ + cryptoCode: balance.cryptoCode, + code: balance.code, + }) return queries.addNotification(CRYPTO_BALANCE, message, detailB) }) }) } -const balancesNotify = (balances) => { - const isCryptoCode = c => _.includes(c, [HIGH_CRYPTO_BALANCE, LOW_CRYPTO_BALANCE]) - const isFiatCode = c => _.includes(c, [LOW_CASH_OUT, CASH_BOX_FULL, LOW_RECYCLER_STACKER]) +const balancesNotify = balances => { + const isCryptoCode = c => + _.includes(c, [HIGH_CRYPTO_BALANCE, LOW_CRYPTO_BALANCE]) + const isFiatCode = c => + _.includes(c, [LOW_CASH_OUT, CASH_BOX_FULL, LOW_RECYCLER_STACKER]) const by = o => - isCryptoCode(o) ? 'crypto' : - isFiatCode(o) ? 'fiat' : - undefined + isCryptoCode(o) ? 'crypto' : isFiatCode(o) ? 'fiat' : undefined const warnings = _.flow( _.groupBy(_.flow(_.get(['code']), by)), _.update('crypto', _.defaultTo([])), _.update('fiat', _.defaultTo([])), )(balances) - return Promise.all([cryptoBalancesNotify(warnings.crypto), fiatBalancesNotify(warnings.fiat)]) + return Promise.all([ + cryptoBalancesNotify(warnings.crypto), + fiatBalancesNotify(warnings.fiat), + ]) } const clearOldErrorNotifications = alerts => { - return queries.getAllValidNotifications(ERROR) - .then(res => { - // for each valid notification in DB see if it exists in alerts - // if the notification doesn't exist in alerts, it is not valid anymore - const filterByAlert = _.filter(notification => { - const { code, deviceId } = notification.detail - return !_.find(alert => alert.code === code && alert.deviceId === deviceId)(alerts) - }) - const indexesToInvalidate = _.compose(_.map('id'), filterByAlert)(res) - if (!indexesToInvalidate.length) return Promise.resolve() - return queries.batchInvalidate(indexesToInvalidate) + return queries.getAllValidNotifications(ERROR).then(res => { + // for each valid notification in DB see if it exists in alerts + // if the notification doesn't exist in alerts, it is not valid anymore + const filterByAlert = _.filter(notification => { + const { code, deviceId } = notification.detail + return !_.find( + alert => alert.code === code && alert.deviceId === deviceId, + )(alerts) }) + const indexesToInvalidate = _.compose(_.map('id'), filterByAlert)(res) + if (!indexesToInvalidate.length) return Promise.resolve() + return queries.batchInvalidate(indexesToInvalidate) + }) } -const errorAlertsNotify = (alertRec) => { +const errorAlertsNotify = alertRec => { const embedDeviceId = deviceId => _.assign({ deviceId }) - const mapToAlerts = _.map(it => _.map(embedDeviceId(it), alertRec.devices[it].deviceAlerts)) + const mapToAlerts = _.map(it => + _.map(embedDeviceId(it), alertRec.devices[it].deviceAlerts), + ) const alerts = _.compose(_.flatten, mapToAlerts, _.keys)(alertRec.devices) return clearOldErrorNotifications(alerts).then(() => { _.forEach(alert => { switch (alert.code) { case PING: { - const detailB = utils.buildDetail({ code: PING, age: alert.age ? alert.age : -1, deviceId: alert.deviceId }) - return queries.getValidNotifications(ERROR, _.omit(['age'], detailB)).then(res => { - if (res.length > 0) return Promise.resolve() - const message = `Machine down` - return queries.addNotification(ERROR, message, detailB) + const detailB = utils.buildDetail({ + code: PING, + age: alert.age ? alert.age : -1, + deviceId: alert.deviceId, }) + return queries + .getValidNotifications(ERROR, _.omit(['age'], detailB)) + .then(res => { + if (res.length > 0) return Promise.resolve() + const message = `Machine down` + return queries.addNotification(ERROR, message, detailB) + }) } case STALE: { - const detailB = utils.buildDetail({ code: STALE, deviceId: alert.deviceId }) + const detailB = utils.buildDetail({ + code: STALE, + deviceId: alert.deviceId, + }) return queries.getValidNotifications(ERROR, detailB).then(res => { if (res.length > 0) return Promise.resolve() const message = `Machine is stuck on ${alert.state} screen` @@ -182,18 +244,39 @@ const errorAlertsNotify = (alertRec) => { }) } -function notifCenterTransactionNotify (isHighValue, direction, fiat, fiatCode, deviceId, cryptoAddress) { +function notifCenterTransactionNotify( + isHighValue, + direction, + fiat, + fiatCode, + deviceId, + cryptoAddress, +) { const messageSuffix = isHighValue ? 'High value' : '' const message = `${messageSuffix} ${fiat} ${fiatCode} ${direction} transaction` - const detailB = utils.buildDetail({ deviceId: deviceId, direction, fiat, fiatCode, cryptoAddress }) - return queries.addNotification(isHighValue ? HIGH_VALUE_TX : NORMAL_VALUE_TX, message, detailB) + const detailB = utils.buildDetail({ + deviceId: deviceId, + direction, + fiat, + fiatCode, + cryptoAddress, + }) + return queries.addNotification( + isHighValue ? HIGH_VALUE_TX : NORMAL_VALUE_TX, + message, + detailB, + ) } const blacklistNotify = (tx, isAddressReuse) => { const code = isAddressReuse ? 'REUSED' : 'BLOCKED' const name = isAddressReuse ? 'reused' : 'blacklisted' - const detailB = utils.buildDetail({ cryptoCode: tx.cryptoCode, code, cryptoAddress: tx.toAddress }) + const detailB = utils.buildDetail({ + cryptoCode: tx.cryptoCode, + code, + cryptoAddress: tx.toAddress, + }) const message = `Blocked ${name} address: ${tx.cryptoCode} ${tx.toAddress.substr(0, 10)}...` return queries.addNotification(COMPLIANCE, message, detailB) } @@ -211,5 +294,5 @@ module.exports = { errorAlertsNotify, notifCenterTransactionNotify, blacklistNotify, - cashboxNotify + cashboxNotify, } diff --git a/packages/server/lib/notifier/queries.js b/packages/server/lib/notifier/queries.js index 7c33aecd..c3fcd721 100644 --- a/packages/server/lib/notifier/queries.js +++ b/packages/server/lib/notifier/queries.js @@ -15,18 +15,22 @@ compliance - notifications related to warnings triggered by compliance settings error - notifications related to errors */ -function getMachineName (machineId) { +function getMachineName(machineId) { const sql = 'SELECT * FROM devices WHERE device_id=$1' - return db.oneOrNone(sql, [machineId]) - .then(it => it.name).catch(logger.error) + return db + .oneOrNone(sql, [machineId]) + .then(it => it.name) + .catch(logger.error) } const addNotification = (type, message, detail) => { const sql = `INSERT INTO notifications (id, type, message, detail) VALUES ($1, $2, $3, $4)` - return db.oneOrNone(sql, [uuidv4(), type, message, detail]).catch(logger.error) + return db + .oneOrNone(sql, [uuidv4(), type, message, detail]) + .catch(logger.error) } -const getAllValidNotifications = (type) => { +const getAllValidNotifications = type => { const sql = `SELECT * FROM notifications WHERE type = $1 AND valid = 't'` return db.any(sql, [type]).catch(logger.error) } @@ -37,7 +41,7 @@ const invalidateNotification = (detail, type) => { return db.none(sql, [type, detail]).catch(logger.error) } -const batchInvalidate = (ids) => { +const batchInvalidate = ids => { const formattedIds = _.map(pgp.as.text, ids).join(',') const sql = `UPDATE notifications SET valid = 'f', read = 't' WHERE id IN ($1^)` return db.none(sql, [formattedIds]).catch(logger.error) @@ -79,7 +83,10 @@ const hasUnreadNotifications = () => { (SELECT * FROM notifications WHERE NOT read AND ${WITHIN_PAST_WEEK}) ` - return db.oneOrNone(sql).then(res => res.exists).catch(logger.error) + return db + .oneOrNone(sql) + .then(res => res.exists) + .catch(logger.error) } const getAlerts = () => { @@ -105,5 +112,5 @@ module.exports = { markAllAsRead, hasUnreadNotifications, getAlerts, - getMachineName + getMachineName, } diff --git a/packages/server/lib/notifier/sms.js b/packages/server/lib/notifier/sms.js index 65a64251..ce89da80 100644 --- a/packages/server/lib/notifier/sms.js +++ b/packages/server/lib/notifier/sms.js @@ -2,7 +2,7 @@ const _ = require('lodash/fp') const utils = require('./utils') const sms = require('../sms') -function printSmsAlerts (alertRec, config) { +function printSmsAlerts(alertRec, config) { let alerts = [] if (config.balance) { @@ -21,31 +21,33 @@ function printSmsAlerts (alertRec, config) { const code = entry[0] const machineNames = _.filter( _.negate(_.isEmpty), - _.map('machineName', entry[1]) + _.map('machineName', entry[1]), ) const cryptoCodes = _.filter( _.negate(_.isEmpty), - _.map('cryptoCode', entry[1]) + _.map('cryptoCode', entry[1]), ) return { codeDisplay: utils.codeDisplay(code), machineNames, - cryptoCodes + cryptoCodes, } }, _.toPairs(alertsMap)) const mapByCodeDisplay = _.map(it => { - if (_.isEmpty(it.machineNames) && _.isEmpty(it.cryptoCodes)) return it.codeDisplay - if (_.isEmpty(it.machineNames)) return `${it.codeDisplay} (${it.cryptoCodes.join(', ')})` + if (_.isEmpty(it.machineNames) && _.isEmpty(it.cryptoCodes)) + return it.codeDisplay + if (_.isEmpty(it.machineNames)) + return `${it.codeDisplay} (${it.cryptoCodes.join(', ')})` return `${it.codeDisplay} (${it.machineNames.join(', ')})` }) const displayAlertTypes = _.compose( _.uniq, mapByCodeDisplay, - _.sortBy('codeDisplay') + _.sortBy('codeDisplay'), )(alertTypes) return '[Lamassu] Errors reported: ' + displayAlertTypes.join(', ') diff --git a/packages/server/lib/notifier/test/email.test.js b/packages/server/lib/notifier/test/email.test.js index 8d27b789..f5eae921 100644 --- a/packages/server/lib/notifier/test/email.test.js +++ b/packages/server/lib/notifier/test/email.test.js @@ -5,14 +5,14 @@ const alertRec = { f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: { balanceAlerts: [], deviceAlerts: [ - { code: 'PING', age: 602784301.446, machineName: 'Abc123' } - ] - } + { code: 'PING', age: 602784301.446, machineName: 'Abc123' }, + ], + }, }, deviceNames: { - f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123' + f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123', }, - general: [] + general: [], } const printEmailMsg = `Errors were reported by your Lamassu Machines. @@ -23,6 +23,6 @@ Machine down for ~6 days test('Print Email Alers', () => { expect(email.printEmailAlerts(alertRec, { active: true, errors: true })).toBe( - printEmailMsg + printEmailMsg, ) }) diff --git a/packages/server/lib/notifier/test/notifier.test.js b/packages/server/lib/notifier/test/notifier.test.js index f49c1098..211c9d0c 100644 --- a/packages/server/lib/notifier/test/notifier.test.js +++ b/packages/server/lib/notifier/test/notifier.test.js @@ -20,7 +20,7 @@ const plugins = { email_errors: false, sms_errors: true, sms: { active: true, errors: true }, - email: { active: false, errors: false } + email: { active: false, errors: false }, }), getMachineNames: () => [ { @@ -36,16 +36,15 @@ const plugins = { name: 'Abc123', paired: true, cashOut: true, - statuses: [{ label: 'Unresponsive', type: 'error' }] - } + statuses: [{ label: 'Unresponsive', type: 'error' }], + }, ], - checkBalances: () => [] + checkBalances: () => [], } const tx = { id: 'bec8d452-9ea2-4846-841b-55a9df8bbd00', - deviceId: - '490ab16ee0c124512dc769be1f3e7ee3894ce1e5b4b8b975e134fb326e551e88', + deviceId: '490ab16ee0c124512dc769be1f3e7ee3894ce1e5b4b8b975e134fb326e551e88', toAddress: 'bc1q7s4yy5n9vp6zhlf6mrw3cttdgx5l3ysr2mhc4v', cryptoAtoms: new BN(252100), cryptoCode: 'BTC', @@ -71,7 +70,7 @@ const tx = { commissionPercentage: new BN(0.11), rawTickerPrice: new BN(18937.4), isPaperWallet: false, - direction: 'cashIn' + direction: 'cashIn', } const notifSettings = { @@ -84,13 +83,13 @@ const notifSettings = { sms: { active: true, errors: true, - transactions: false // force early return + transactions: false, // force early return }, email: { active: false, errors: false, - transactions: false // force early return - } + transactions: false, // force early return + }, } test('Exits checkNotifications with Promise.resolve() if SMS and Email are disabled', async () => { @@ -99,9 +98,9 @@ test('Exits checkNotifications with Promise.resolve() if SMS and Email are disab notifier.checkNotification({ getNotificationConfig: () => ({ sms: { active: false, errors: false }, - email: { active: false, errors: false } - }) - }) + email: { active: false, errors: false }, + }), + }), ).resolves.toBe(undefined) }) @@ -111,9 +110,9 @@ test('Exits checkNotifications with Promise.resolve() if SMS and Email are disab notifier.checkNotification({ getNotificationConfig: () => ({ sms: { active: false, errors: true, balance: true }, - email: { active: false, errors: true, balance: true } - }) - }) + email: { active: false, errors: true, balance: true }, + }), + }), ).resolves.toBe(undefined) }) @@ -124,13 +123,13 @@ test("Check Pings should return code PING for devices that haven't been pinged r deviceId: '7e531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4', lastPing: '2020-11-16T13:11:03.169Z', - name: 'Abc123' - } - ]) + name: 'Abc123', + }, + ]), ).toMatchObject({ '7e531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4': [ - { code: 'PING', machineName: 'Abc123' } - ] + { code: 'PING', machineName: 'Abc123' }, + ], }) }) @@ -141,11 +140,11 @@ test('Checkpings returns empty array as the value for the id prop, if the lastPi deviceId: '7a531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4', lastPing: new Date(), - name: 'Abc123' - } - ]) + name: 'Abc123', + }, + ]), ).toMatchObject({ - '7a531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4': [] + '7a531a2666987aa27b9917ca17df7998f72771c57fdb21c90bc033999edd17e4': [], }) }) @@ -203,7 +202,7 @@ test('checkStuckScreen returns [] if most recent event is idle', () => { note: '{"state":"chooseCoin","isIdle":false}', created: '2020-11-23T19:30:29.209Z', device_time: '1999-11-23T19:30:29.177Z', - age: 157352628.123 + age: 157352628.123, }, { id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2', @@ -213,9 +212,9 @@ test('checkStuckScreen returns [] if most recent event is idle', () => { note: '{"state":"chooseCoin","isIdle":true}', created: '2020-11-23T19:30:29.209Z', device_time: '2020-11-23T19:30:29.177Z', - age: 157352628.123 - } - ]) + age: 157352628.123, + }, + ]), ).toEqual([]) }) @@ -230,7 +229,7 @@ test('checkStuckScreen returns object array of length 1 with prop code: "STALE" note: '{"state":"chooseCoin","isIdle":true}', created: '2020-11-23T19:30:29.209Z', device_time: '1999-11-23T19:30:29.177Z', - age: 0 + age: 0, }, { id: '48ae51c6-c5b4-485e-b81d-aa337fc025e2', @@ -240,8 +239,8 @@ test('checkStuckScreen returns object array of length 1 with prop code: "STALE" note: '{"state":"chooseCoin","isIdle":false}', created: '2020-11-23T19:30:29.209Z', device_time: '2020-11-23T19:30:29.177Z', - age: 157352628.123 - } + age: 157352628.123, + }, ]) expect(result[0]).toMatchObject({ code: 'STALE' }) }) @@ -257,8 +256,8 @@ test('checkStuckScreen returns empty array if age < STALE_STATE', () => { note: '{"state":"chooseCoin","isIdle":false}', created: '2020-11-23T19:30:29.209Z', device_time: '2020-11-23T19:30:29.177Z', - age: 0 - } + age: 0, + }, ]) const result2 = notifier.checkStuckScreen([ { @@ -269,8 +268,8 @@ test('checkStuckScreen returns empty array if age < STALE_STATE', () => { note: '{"state":"chooseCoin","isIdle":false}', created: '2020-11-23T19:30:29.209Z', device_time: '2020-11-23T19:30:29.177Z', - age: STALE_STATE - } + age: STALE_STATE, + }, ]) expect(result1).toEqual([]) expect(result2).toEqual([]) @@ -281,7 +280,10 @@ test('calls sendRedemptionMessage if !zeroConf and rec.isRedemption', async () = const settingsLoader = require('../../new-settings-loader') const loadLatest = jest.spyOn(settingsLoader, 'loadLatest') - const getGlobalNotifications = jest.spyOn(configManager, 'getGlobalNotifications') + const getGlobalNotifications = jest.spyOn( + configManager, + 'getGlobalNotifications', + ) const getWalletSettings = jest.spyOn(configManager, 'getWalletSettings') // sendRedemptionMessage will cause this func to be called @@ -289,19 +291,24 @@ test('calls sendRedemptionMessage if !zeroConf and rec.isRedemption', async () = getWalletSettings.mockReturnValue({ zeroConfLimit: -Infinity }) loadLatest.mockReturnValue(Promise.resolve({})) - getGlobalNotifications.mockReturnValue({ ...notifSettings, sms: { active: true, errors: true, transactions: true }, notificationCenter: { active: true } }) + getGlobalNotifications.mockReturnValue({ + ...notifSettings, + sms: { active: true, errors: true, transactions: true }, + notificationCenter: { active: true }, + }) const response = await notifier.transactionNotify(tx, { isRedemption: true }) // this type of response implies sendRedemptionMessage was called expect(response[0]).toMatchObject({ sms: { - body: "Here's an update on transaction bec8d452-9ea2-4846-841b-55a9df8bbd00 - It was just dispensed successfully" + body: "Here's an update on transaction bec8d452-9ea2-4846-841b-55a9df8bbd00 - It was just dispensed successfully", }, email: { - subject: "Here's an update on transaction bec8d452-9ea2-4846-841b-55a9df8bbd00", - body: 'It was just dispensed successfully' - } + subject: + "Here's an update on transaction bec8d452-9ea2-4846-841b-55a9df8bbd00", + body: 'It was just dispensed successfully', + }, }) }) @@ -310,23 +317,32 @@ test('calls sendTransactionMessage if !zeroConf and !rec.isRedemption', async () const settingsLoader = require('../../new-settings-loader') const queries = require('../queries') const loadLatest = jest.spyOn(settingsLoader, 'loadLatest') - const getGlobalNotifications = jest.spyOn(configManager, 'getGlobalNotifications') + const getGlobalNotifications = jest.spyOn( + configManager, + 'getGlobalNotifications', + ) const getWalletSettings = jest.spyOn(configManager, 'getWalletSettings') const getMachineName = jest.spyOn(queries, 'getMachineName') const buildTransactionMessage = jest.spyOn(utils, 'buildTransactionMessage') // sendMessage on emailFuncs isn't called because it is disabled in getGlobalNotifications.mockReturnValue - jest.spyOn(smsFuncs, 'sendMessage').mockImplementation((_, rec) => ({ prop: rec })) + jest + .spyOn(smsFuncs, 'sendMessage') + .mockImplementation((_, rec) => ({ prop: rec })) buildTransactionMessage.mockImplementation(() => ['mock message', false]) getMachineName.mockResolvedValue('mockMachineName') getWalletSettings.mockReturnValue({ zeroConfLimit: -Infinity }) loadLatest.mockReturnValue(Promise.resolve({})) - getGlobalNotifications.mockReturnValue({ ...notifSettings, sms: { active: true, errors: true, transactions: true }, notificationCenter: { active: true } }) + getGlobalNotifications.mockReturnValue({ + ...notifSettings, + sms: { active: true, errors: true, transactions: true }, + notificationCenter: { active: true }, + }) const response = await notifier.transactionNotify(tx, { isRedemption: false }) // If the return object is this, it means the code went through all the functions expected to go through if // getMachineName, buildTransactionMessage and sendTransactionMessage were called, in this order - expect(response).toEqual([{prop: 'mock message'}]) + expect(response).toEqual([{ prop: 'mock message' }]) }) diff --git a/packages/server/lib/notifier/test/sms.test.js b/packages/server/lib/notifier/test/sms.test.js index d42ac7ac..f4124c41 100644 --- a/packages/server/lib/notifier/test/sms.test.js +++ b/packages/server/lib/notifier/test/sms.test.js @@ -5,18 +5,18 @@ const alertRec = { f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: { balanceAlerts: [], deviceAlerts: [ - { code: 'PING', age: 602784301.446, machineName: 'Abc123' } - ] - } + { code: 'PING', age: 602784301.446, machineName: 'Abc123' }, + ], + }, }, deviceNames: { - f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123' + f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123', }, - general: [] + general: [], } test('Print SMS alerts', () => { expect(sms.printSmsAlerts(alertRec, { active: true, errors: true })).toBe( - '[Lamassu] Errors reported: Machine Down (Abc123)' + '[Lamassu] Errors reported: Machine Down (Abc123)', ) }) diff --git a/packages/server/lib/notifier/test/utils.test.js b/packages/server/lib/notifier/test/utils.test.js index aee67776..d6371455 100644 --- a/packages/server/lib/notifier/test/utils.test.js +++ b/packages/server/lib/notifier/test/utils.test.js @@ -3,7 +3,7 @@ const utils = require('../utils') const plugins = { sendMessage: rec => { return rec - } + }, } const alertRec = { @@ -11,19 +11,19 @@ const alertRec = { f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: { balanceAlerts: [], deviceAlerts: [ - { code: 'PING', age: 1605532263169, machineName: 'Abc123' } - ] - } + { code: 'PING', age: 1605532263169, machineName: 'Abc123' }, + ], + }, }, deviceNames: { - f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123' + f02af604ca9010bd9ae04c427a24da90130da10d355f0a9b235886a89008fc05: 'Abc123', }, - general: [] + general: [], } const notifications = { sms: { active: true, errors: true }, - email: { active: false, errors: false } + email: { active: false, errors: false }, } describe('buildAlertFingerprint', () => { @@ -33,37 +33,37 @@ describe('buildAlertFingerprint', () => { { devices: {}, deviceNames: {}, - general: [] + general: [], }, - notifications - ) + notifications, + ), ).toBe(null) }) - + test('Build alert fingerprint returns null if sms and email are disabled', () => { expect( utils.buildAlertFingerprint(alertRec, { sms: { active: false, errors: true }, - email: { active: false, errors: false } - }) + email: { active: false, errors: false }, + }), ).toBe(null) }) - + test('Build alert fingerprint returns hash if email or [sms] are enabled and there are alerts in alertrec', () => { expect( typeof utils.buildAlertFingerprint(alertRec, { sms: { active: true, errors: true }, - email: { active: false, errors: false } - }) + email: { active: false, errors: false }, + }), ).toBe('string') }) - + test('Build alert fingerprint returns hash if [email] or sms are enabled and there are alerts in alertrec', () => { expect( typeof utils.buildAlertFingerprint(alertRec, { sms: { active: false, errors: false }, - email: { active: true, errors: true } - }) + email: { active: true, errors: true }, + }), ).toBe('string') }) }) @@ -76,8 +76,8 @@ describe('sendNoAlerts', () => { test('Send no alerts returns object with sms prop with sms only enabled', () => { expect(utils.sendNoAlerts(plugins, true, false)).toEqual({ sms: { - body: '[Lamassu] All clear' - } + body: '[Lamassu] All clear', + }, }) }) @@ -85,11 +85,11 @@ describe('sendNoAlerts', () => { expect(utils.sendNoAlerts(plugins, true, true)).toEqual({ email: { body: 'No errors are reported for your machines.', - subject: '[Lamassu] All clear' + subject: '[Lamassu] All clear', }, sms: { - body: '[Lamassu] All clear' - } + body: '[Lamassu] All clear', + }, }) }) @@ -97,8 +97,8 @@ describe('sendNoAlerts', () => { expect(utils.sendNoAlerts(plugins, false, true)).toEqual({ email: { body: 'No errors are reported for your machines.', - subject: '[Lamassu] All clear' - } + subject: '[Lamassu] All clear', + }, }) }) }) diff --git a/packages/server/lib/notifier/utils.js b/packages/server/lib/notifier/utils.js index a7880667..f02c0e8a 100644 --- a/packages/server/lib/notifier/utils.js +++ b/packages/server/lib/notifier/utils.js @@ -7,7 +7,7 @@ const { CODES_DISPLAY, NETWORK_DOWN_TIME, PING, - ALERT_SEND_INTERVAL + ALERT_SEND_INTERVAL, } = require('./codes') const DETAIL_TEMPLATE = { @@ -20,16 +20,17 @@ const DETAIL_TEMPLATE = { cryptoAddress: '', direction: '', fiat: '', - fiatCode: '' + fiatCode: '', } -function parseEventNote (event) { +function parseEventNote(event) { return _.set('note', JSON.parse(event.note), event) } -function checkPing (device) { - const age = Date.now() - (new Date(device.lastPing).getTime()) - if (age > NETWORK_DOWN_TIME) return [{ code: PING, age, machineName: device.name }] +function checkPing(device) { + const age = Date.now() - new Date(device.lastPing).getTime() + if (age > NETWORK_DOWN_TIME) + return [{ code: PING, age, machineName: device.name }] return [] } @@ -41,7 +42,7 @@ const codeDisplay = code => CODES_DISPLAY[code] const alertFingerprint = { fingerprint: null, - lastAlertTime: null + lastAlertTime: null, } const getAlertFingerprint = () => alertFingerprint.fingerprint @@ -60,7 +61,7 @@ const shouldNotAlert = currentAlertFingerprint => { ) } -function buildAlertFingerprint (alertRec, notifications) { +function buildAlertFingerprint(alertRec, notifications) { const sms = getAlertTypes(alertRec, notifications.sms) const email = getAlertTypes(alertRec, notifications.email) if (sms.length === 0 && email.length === 0) return null @@ -72,7 +73,7 @@ function buildAlertFingerprint (alertRec, notifications) { return crypto.createHash('sha256').update(subject).digest('hex') } -function sendNoAlerts (plugins, smsEnabled, emailEnabled) { +function sendNoAlerts(plugins, smsEnabled, emailEnabled) { const subject = '[Lamassu] All clear' let rec = {} @@ -83,14 +84,20 @@ function sendNoAlerts (plugins, smsEnabled, emailEnabled) { if (emailEnabled) { rec = _.set(['email', 'subject'])(subject)(rec) rec = _.set(['email', 'body'])('No errors are reported for your machines.')( - rec + rec, ) } return plugins.sendMessage(rec) } -const buildTransactionMessage = (tx, rec, highValueTx, machineName, customer) => { +const buildTransactionMessage = ( + tx, + rec, + highValueTx, + machineName, + customer, +) => { const isCashOut = tx.direction === 'cashOut' const direction = isCashOut ? 'Cash Out' : 'Cash In' const crypto = `${coinUtils.toUnit(tx.cryptoAtoms, tx.cryptoCode)} ${ @@ -124,41 +131,49 @@ const buildTransactionMessage = (tx, rec, highValueTx, machineName, customer) => const smsSubject = `A ${highValueTx ? 'high value ' : ''}${direction.toLowerCase()} transaction just happened at ${machineName} for ${fiat}` const emailSubject = `A ${highValueTx ? 'high value ' : ''}transaction just happened` - return [{ - sms: { - body: `${smsSubject} – ${status}` + return [ + { + sms: { + body: `${smsSubject} – ${status}`, + }, + email: { + emailSubject, + body, + }, + webhook: { + topic: `New transaction`, + content: body, + }, }, - email: { - emailSubject, - body - }, - webhook: { - topic: `New transaction`, - content: body - } - }, highValueTx] + highValueTx, + ] } -function formatCurrency (num = 0, code) { - const formattedNumber = Number(num).toLocaleString(undefined, {maximumFractionDigits:2, minimumFractionDigits:2}) +function formatCurrency(num = 0, code) { + const formattedNumber = Number(num).toLocaleString(undefined, { + maximumFractionDigits: 2, + minimumFractionDigits: 2, + }) return `${formattedNumber} ${code}` } -function formatAge (age, settings) { +function formatAge(age, settings) { return prettyMs(age, settings) } -function buildDetail (obj) { +function buildDetail(obj) { // obj validation const objKeys = _.keys(obj) const detailKeys = _.keys(DETAIL_TEMPLATE) - if ((_.difference(objKeys, detailKeys)).length > 0) { - return Promise.reject(new Error('Error when building detail object: invalid properties')) + if (_.difference(objKeys, detailKeys).length > 0) { + return Promise.reject( + new Error('Error when building detail object: invalid properties'), + ) } return { ...DETAIL_TEMPLATE, ...obj } } -function deviceAlerts (config, alertRec, device) { +function deviceAlerts(config, alertRec, device) { let alerts = [] if (config.balance) { alerts = _.concat(alerts, alertRec.devices[device].balanceAlerts) @@ -170,7 +185,7 @@ function deviceAlerts (config, alertRec, device) { return alerts } -function getAlertTypes (alertRec, config) { +function getAlertTypes(alertRec, config) { let alerts = [] if (!isActive(config)) return alerts @@ -200,5 +215,5 @@ module.exports = { formatCurrency, formatAge, buildDetail, - deviceAlerts + deviceAlerts, } diff --git a/packages/server/lib/notifier/webhook.js b/packages/server/lib/notifier/webhook.js index fcddf1ed..015d151e 100644 --- a/packages/server/lib/notifier/webhook.js +++ b/packages/server/lib/notifier/webhook.js @@ -12,10 +12,10 @@ const sendMessage = (settings, rec) => { return axios({ method: 'POST', url: WEBHOOK_URL, - data: body + data: body, }) } module.exports = { - sendMessage + sendMessage, } diff --git a/packages/server/lib/ofac/index.js b/packages/server/lib/ofac/index.js index 8f34027b..3847d2d4 100644 --- a/packages/server/lib/ofac/index.js +++ b/packages/server/lib/ofac/index.js @@ -10,7 +10,7 @@ const OFAC_DATA_DIR = process.env.OFAC_DATA_DIR let structs = null -function load () { +function load() { if (!OFAC_DATA_DIR) { const message = 'The ofacDataDir option has not been set in the environment' return Promise.reject(new Error(message)) @@ -19,10 +19,12 @@ function load () { const ofacSourcesDir = path.join(OFAC_DATA_DIR, 'sources') return readdir(ofacSourcesDir) - .then(_.flow( - _.map(file => path.join(ofacSourcesDir, file)), - loader.load - )) + .then( + _.flow( + _.map(file => path.join(ofacSourcesDir, file)), + loader.load, + ), + ) .then(result => { return (structs = result) }) @@ -30,14 +32,14 @@ function load () { // nameParts should be an object like {firstName: "John", lastName: "Doe", ...} -function makeCompatible (nameParts) { +function makeCompatible(nameParts) { const partNames = _.keys(nameParts) const values = _.map(_.lowerCase, _.values(nameParts)) const props = _.zipAll([partNames, values]) return _.map(_.zipObject(['partName', 'value']), props) } -function match (nameParts, birthDateString, options) { +function match(nameParts, birthDateString, options) { if (!structs) { logger.error(new Error('The OFAC data sources have not been loaded yet.')) return false @@ -50,24 +52,27 @@ function match (nameParts, birthDateString, options) { // birthDateString is in YYYYMMDD format const birthDate = _.cond([ - [_.identity, () => { - const year = parseInt(birthDateString.slice(0, 4)) - const month = parseInt(birthDateString.slice(4, 6)) - const day = parseInt(birthDateString.slice(6, 8)) - const date = new Date(year, month - 1, day) + [ + _.identity, + () => { + const year = parseInt(birthDateString.slice(0, 4)) + const month = parseInt(birthDateString.slice(4, 6)) + const day = parseInt(birthDateString.slice(6, 8)) + const date = new Date(year, month - 1, day) - return {year, month, day, date} - }], - [_.stubTrue, () => null] + return { year, month, day, date } + }, + ], + [_.stubTrue, () => null], ])(birthDateString) - const candidate = {parts, fullName, words, birthDate} + const candidate = { parts, fullName, words, birthDate } const result = matcher.match(structs, candidate, options) return result } -function getStructs () { +function getStructs() { return structs } -module.exports = {load, match, getStructs} +module.exports = { load, match, getStructs } diff --git a/packages/server/lib/ofac/loading.js b/packages/server/lib/ofac/loading.js index 8c422977..0b1c59c4 100644 --- a/packages/server/lib/ofac/loading.js +++ b/packages/server/lib/ofac/loading.js @@ -2,20 +2,18 @@ const fs = require('fs') const ndjson = require('ndjson') const _ = require('lodash/fp') - const mapAliases = _.curry((iteratee, individuals) => { const mapIndividual = individual => { - const {id, aliases} = individual + const { id, aliases } = individual return _.map(alias => iteratee(id, alias), aliases) } return _.flatMap(mapIndividual, individuals) }) - const getPhoneticEntries = (individualId, alias) => { const pairPhoneticsWithValues = word => { - const {value, phonetics} = word - const makeEntry = phonetic => ({value, phonetic, aliasId: alias.id}) + const { value, phonetics } = word + const makeEntry = phonetic => ({ value, phonetic, aliasId: alias.id }) return _.map(makeEntry, phonetics) } return _.flatMap(pairPhoneticsWithValues, alias.words) @@ -25,17 +23,13 @@ const producePhoneticMap = _.flow( mapAliases(getPhoneticEntries), _.flatten, _.groupBy(_.get('phonetic')), - _.mapValues(_.flow( - _.map(_.get('aliasId')), - _.uniq - )), + _.mapValues(_.flow(_.map(_.get('aliasId')), _.uniq)), _.toPairs, - entries => new Map(entries) + entries => new Map(entries), ) - const getWords = (individualId, alias) => { - const pairWordsWithIds = word => ({value: word.value, aliasId: alias.id}) + const pairWordsWithIds = word => ({ value: word.value, aliasId: alias.id }) return _.map(pairWordsWithIds, alias.words) } @@ -45,7 +39,7 @@ const produceWordList = _.flow( _.groupBy(_.get('value')), _.mapValues(_.map(_.get('aliasId'))), _.toPairs, - _.map(_.zipObject(['value', 'aliasIds'])) + _.map(_.zipObject(['value', 'aliasIds'])), ) const parseSource = source => { @@ -55,10 +49,13 @@ const parseSource = source => { const jsonStream = readStream.pipe(ndjson.parse()) jsonStream.on('data', individual => { _.forEach(period => { - _.forEach(date => { - const {year, month, day} = date - date.date = new Date(year, month - 1, day) - }, [period.start, period.end]) + _.forEach( + date => { + const { year, month, day } = date + date.date = new Date(year, month - 1, day) + }, + [period.start, period.end], + ) }, individual.birthDatePeriods) individuals.push(individual) }) @@ -71,18 +68,14 @@ const parseSource = source => { }) } -const load = sources => Promise.all(_.map(parseSource, sources)) - .then(_.flow( - _.flatten, - _.compact, - _.uniqBy(_.get('id')), - individuals => { - +const load = sources => + Promise.all(_.map(parseSource, sources)).then( + _.flow(_.flatten, _.compact, _.uniqBy(_.get('id')), individuals => { const individualsMap = _.flow( _.groupBy(_.get('id')), _.mapValues(_.first), _.toPairs, - entries => new Map(entries) + entries => new Map(entries), )(individuals) const makeEntries = (individualId, alias) => [alias.id, alias] @@ -101,9 +94,9 @@ const load = sources => Promise.all(_.map(parseSource, sources)) aliasesMap, aliasToIndividual, phoneticMap, - wordList + wordList, } - } - )) + }), + ) -module.exports = {load} +module.exports = { load } diff --git a/packages/server/lib/ofac/matching.js b/packages/server/lib/ofac/matching.js index f8c089e8..460a333a 100644 --- a/packages/server/lib/ofac/matching.js +++ b/packages/server/lib/ofac/matching.js @@ -7,7 +7,7 @@ const stringSimilarity = _.curry(jaro) // birth date -function isDateWithinSomeDaysOfPeriod (period, date, days) { +function isDateWithinSomeDaysOfPeriod(period, date, days) { const inMillisecs = 24 * 60 * 60 * 1000 const startTime = period.start.date.getTime() - days * inMillisecs @@ -16,32 +16,35 @@ function isDateWithinSomeDaysOfPeriod (period, date, days) { const endTime = period.end.date.getTime() + days * inMillisecs const endDate = new Date(endTime) - return (startDate < date && date < endDate) + return startDate < date && date < endDate } const isBornTooLongSince = _.curry((days, dateObject, individual) => { if (!dateObject) return false if (_.isEmpty(individual.birthDatePeriods)) return false - const isWithinSomeYears = _.partialRight(isDateWithinSomeDaysOfPeriod, [dateObject.date, days]) + const isWithinSomeYears = _.partialRight(isDateWithinSomeDaysOfPeriod, [ + dateObject.date, + days, + ]) return !_.some(isWithinSomeYears, individual.birthDatePeriods) }) // algorithm -function match (structs, candidate, options) { - const {threshold, fullNameThreshold, ratio = 0.5, verboseFor} = options - const {fullName, words, birthDate} = candidate +function match(structs, candidate, options) { + const { threshold, fullNameThreshold, ratio = 0.5, verboseFor } = options + const { fullName, words, birthDate } = candidate // Accept aliases who's full name matches. const doesNameMatch = _.flow( _.get('fullName'), stringSimilarity(fullName), - _.lte(fullNameThreshold) + _.lte(fullNameThreshold), ) const aliases = _.flatMap(_.get('aliases'), structs.individuals) const aliasIdsFromFullName = _.flow( _.filter(doesNameMatch), - _.map(_.get('id')) + _.map(_.get('id')), )(aliases) const phoneticWeight = ratio @@ -60,12 +63,25 @@ function match (structs, candidate, options) { for (const aliasId of wordEntry.aliasIds) { const phoneticScore = phoneticMatches.has(aliasId) ? 1 : -1 - const finalScore = stringWeight * stringScore + phoneticWeight * phoneticScore + const finalScore = + stringWeight * stringScore + phoneticWeight * phoneticScore - verbose && logger.debug(finalScore.toFixed(2), stringScore.toFixed(2), phoneticScore.toFixed(2), word.value, wordEntry.value) + verbose && + logger.debug( + finalScore.toFixed(2), + stringScore.toFixed(2), + phoneticScore.toFixed(2), + word.value, + wordEntry.value, + ) if (finalScore >= threshold) { - const entry = {aliasId, score: finalScore, word: word.value, value: wordEntry.value} + const entry = { + aliasId, + score: finalScore, + word: word.value, + value: wordEntry.value, + } const index = _.sortedIndexBy(x => -x.score, entry, matches) matches.splice(index, 0, entry) } @@ -83,10 +99,10 @@ function match (structs, candidate, options) { _.countBy(_.identity), _.toPairs, _.filter(([aliasId, count]) => { - const {length} = structs.aliasesMap.get(aliasId).words - return (count >= _.min([2, words.length, length])) + const { length } = structs.aliasesMap.get(aliasId).words + return count >= _.min([2, words.length, length]) }), - _.map(_.first) + _.map(_.first), )(matches) // Get the full record for each matched id @@ -94,10 +110,9 @@ function match (structs, candidate, options) { const individualId = structs.aliasToIndividual.get(aliasId) return structs.individualsMap.get(individualId) } - const suspects = _.uniq(_.map(getIndividual, [ - ...aliasIdsFromFullName, - ...aliasIdsFromNamePart - ])) + const suspects = _.uniq( + _.map(getIndividual, [...aliasIdsFromFullName, ...aliasIdsFromNamePart]), + ) // Reject everyone who is born two years away. const twoYears = 365 * 2 @@ -105,4 +120,4 @@ function match (structs, candidate, options) { return _.reject(unqualified, suspects) } -module.exports = {match} +module.exports = { match } diff --git a/packages/server/lib/ofac/name-utils.js b/packages/server/lib/ofac/name-utils.js index 947befa9..de185267 100644 --- a/packages/server/lib/ofac/name-utils.js +++ b/packages/server/lib/ofac/name-utils.js @@ -5,17 +5,24 @@ const makePhonetic = _.flow(doubleMetaphone, _.uniq) // Combine name-parts in a standard order. -const partOrdering = ['firstName', 'middleName', 'maidenName', 'patronymic', 'matronymic', 'lastName'] +const partOrdering = [ + 'firstName', + 'middleName', + 'maidenName', + 'patronymic', + 'matronymic', + 'lastName', +] const usingPartOrder = _.flow( _.get('partName'), - _.partialRight(_.indexOf, [partOrdering]) + _.partialRight(_.indexOf, [partOrdering]), ) const makeFullName = _.flow( _.sortBy(usingPartOrder), _.map(_.get('value')), - _.join(' ') + _.join(' '), ) const makeWords = value => { @@ -27,5 +34,5 @@ const makeWords = value => { module.exports = { makeFullName, - makeWords + makeWords, } diff --git a/packages/server/lib/ofac/parsing.js b/packages/server/lib/ofac/parsing.js index 36b7dcd8..0c0b42d6 100644 --- a/packages/server/lib/ofac/parsing.js +++ b/packages/server/lib/ofac/parsing.js @@ -25,7 +25,7 @@ const partNames = new Map([ [MAIDEN_NAME, 'maidenName'], [PATRONYMIC, 'patronymic'], [MATRONYMIC, 'matronymic'], - [NICKNAME, 'nickname'] + [NICKNAME, 'nickname'], ]) const filteredWords = [ @@ -34,7 +34,7 @@ const filteredWords = [ // group-id to type-id -function processMasterNamePartGroup (groupNode) { +function processMasterNamePartGroup(groupNode) { const namePartGroupNode = groupNode.NamePartGroup const groupId = namePartGroupNode.$.ID const typeId = namePartGroupNode.$.NamePartTypeID @@ -47,7 +47,7 @@ const processDocumentedNamePart = _.curry((groupTypes, namePartNode) => { const typeId = groupTypes.get(groupId) const partName = partNames.get(typeId) const value = _.lowerCase(valueNode.$text) - return {partName, value} + return { partName, value } }) const isLatin = _.matchesProperty(['$', 'DocNameStatusID'], PRIMARY_LATIN) @@ -72,29 +72,26 @@ const processAlias = _.curry((groupTypes, aliasNode) => { const fullName = nameUtils.makeFullName(parts) const words = _.flow( nameUtils.makeWords, - _.reject(_.flow( - _.get('value'), - word => filteredWords.includes(word) - )) + _.reject(_.flow(_.get('value'), word => filteredWords.includes(word))), )(fullName) // if (words.length < 2) { // console.log(JSON.stringify(words)) // } - return {id, parts, fullName, words} + return { id, parts, fullName, words } }) // birth date -function processDate (dateNode) { +function processDate(dateNode) { const year = parseInt(dateNode.Year) const month = parseInt(dateNode.Month) const day = parseInt(dateNode.Day) - return {year, month, day} + return { year, month, day } } -function processFeature (featureNode) { +function processFeature(featureNode) { if (featureNode.$.FeatureTypeID !== BIRTH_DATE) return const datePeriodNode = featureNode.FeatureVersion.DatePeriod @@ -103,7 +100,7 @@ function processFeature (featureNode) { // By using Start.From and End.To we use the extremes of the date-period. const period = { start: datePeriodNode.Start.From, - end: datePeriodNode.End.To + end: datePeriodNode.End.To, } return _.mapValues(processDate, period) @@ -111,13 +108,16 @@ function processFeature (featureNode) { // profile -function processProfile (profileNode) { +function processProfile(profileNode) { if (profileNode.$.PartySubTypeID !== INDIVIDUAL) return const id = profileNode.$.ID const identityNode = profileNode.Identity - const groupTypesEntries = _.map(processMasterNamePartGroup, identityNode.NamePartGroups.MasterNamePartGroup) + const groupTypesEntries = _.map( + processMasterNamePartGroup, + identityNode.NamePartGroups.MasterNamePartGroup, + ) const groupTypes = new Map(groupTypesEntries) const mapCompact = _.flow(_.map, _.compact) @@ -128,7 +128,7 @@ function processProfile (profileNode) { if (_.isEmpty(aliases)) return const birthDatePeriods = mapCompact(processFeature, profileNode.Feature) - const individual = {id, aliases, birthDatePeriods} + const individual = { id, aliases, birthDatePeriods } return individual } @@ -158,4 +158,4 @@ const parse = (source, callback) => { }) } -module.exports = {parse} +module.exports = { parse } diff --git a/packages/server/lib/ofac/update.js b/packages/server/lib/ofac/update.js index 8c520a69..eda039ed 100644 --- a/packages/server/lib/ofac/update.js +++ b/packages/server/lib/ofac/update.js @@ -1,7 +1,14 @@ const parser = require('./parsing') const axios = require('axios') const { createWriteStream } = require('fs') -const { rename, writeFile, readFile, mkdir, copyFile, unlink } = require('fs/promises') +const { + rename, + writeFile, + readFile, + mkdir, + copyFile, + unlink, +} = require('fs/promises') const path = require('path') const _ = require('lodash/fp') @@ -10,17 +17,21 @@ const OFAC_DATA_DIR = process.env.OFAC_DATA_DIR const OFAC_SOURCES_DIR = path.join(OFAC_DATA_DIR, 'sources') const LAST_UPDATED_FILE = path.resolve(OFAC_DATA_DIR, 'last_updated.dat') -const OFAC_SOURCES = [{ - name: 'sdn_advanced', - url: 'https://sanctionslistservice.ofac.treas.gov/api/download/sdn_advanced.xml' -}, { - name: 'cons_advanced', - url: 'https://sanctionslistservice.ofac.treas.gov/api/download/cons_advanced.xml' -}] +const OFAC_SOURCES = [ + { + name: 'sdn_advanced', + url: 'https://sanctionslistservice.ofac.treas.gov/api/download/sdn_advanced.xml', + }, + { + name: 'cons_advanced', + url: 'https://sanctionslistservice.ofac.treas.gov/api/download/cons_advanced.xml', + }, +] const _mkdir = path => - mkdir(path) - .catch(err => err.code === 'EEXIST' ? Promise.resolve() : Promise.reject(err)) + mkdir(path).catch(err => + err.code === 'EEXIST' ? Promise.resolve() : Promise.reject(err), + ) const download = (dstDir, { name, url }) => { const dstFile = path.join(dstDir, name + '.xml') @@ -90,7 +101,7 @@ const moveToSourcesDir = async (srcFile, ofacSourcesDir) => { return dstFile } -function update () { +function update() { if (!OFAC_DATA_DIR) { throw new Error('ofacDataDir must be defined in the environment') } @@ -118,17 +129,20 @@ function update () { if (skipUpdate) return Promise.resolve() const downloads = _.flow( - _.map(file => download(DOWNLOAD_DIR, file).then(parseToJson)) + _.map(file => download(DOWNLOAD_DIR, file).then(parseToJson)), )(OFAC_SOURCES) - return Promise.all(downloads) - .then(parsed => { - const moves = _.map(src => moveToSourcesDir(src, OFAC_SOURCES_DIR), parsed) - const timestamp = new Date().toISOString() + return Promise.all(downloads).then(parsed => { + const moves = _.map( + src => moveToSourcesDir(src, OFAC_SOURCES_DIR), + parsed, + ) + const timestamp = new Date().toISOString() - return Promise.all([...moves]) - .then(() => writeFile(LAST_UPDATED_FILE, timestamp)) - }) + return Promise.all([...moves]).then(() => + writeFile(LAST_UPDATED_FILE, timestamp), + ) + }) }) } diff --git a/packages/server/lib/operator.js b/packages/server/lib/operator.js index ccd9ad5e..b6243f16 100644 --- a/packages/server/lib/operator.js +++ b/packages/server/lib/operator.js @@ -1,10 +1,9 @@ const db = require('./db') const _ = require('lodash/fp') -function getOperatorId (service) { +function getOperatorId(service) { const sql = `SELECT operator_id FROM operator_ids WHERE service = '${service}'` - return db.oneOrNone(sql) - .then(_.get('operator_id')) + return db.oneOrNone(sql).then(_.get('operator_id')) } module.exports = { getOperatorId } diff --git a/packages/server/lib/pairing.js b/packages/server/lib/pairing.js index 72e88f4c..38eecc9b 100644 --- a/packages/server/lib/pairing.js +++ b/packages/server/lib/pairing.js @@ -12,7 +12,7 @@ const CA_PATH = process.env.CA_PATH const DEFAULT_NUMBER_OF_CASSETTES = 2 const DEFAULT_NUMBER_OF_RECYCLERS = 0 -function pullToken (token) { +function pullToken(token) { const sql = `delete from pairing_tokens where token=$1 returning name, created < now() - interval '1 hour' as expired` @@ -20,24 +20,41 @@ function pullToken (token) { } // TODO new-admin: We should remove all configs related to that device. This can get tricky. -function unpair (deviceId) { - return db.tx(t => - t.none(`INSERT INTO unpaired_devices(id, device_id, name, model, paired, unpaired) +function unpair(deviceId) { + return db.tx(t => + t + .none( + `INSERT INTO unpaired_devices(id, device_id, name, model, paired, unpaired) SELECT $1, $2, d.name, d.model, d.created, now() FROM devices d - WHERE device_id=$2` - , [uuid.v4(), deviceId]) - .then(() => { + WHERE device_id=$2`, + [uuid.v4(), deviceId], + ) + .then(() => { const q1 = t.none(`DELETE FROM devices WHERE device_id=$1`, [deviceId]) - const q2 = t.none(`DELETE FROM machine_pings WHERE device_id=$1`, [deviceId]) - const q3 = t.none(`DELETE FROM machine_network_heartbeat WHERE device_id=$1`, [deviceId]) - const q4 = t.none(`DELETE FROM machine_network_performance WHERE device_id=$1`, [deviceId]) + const q2 = t.none(`DELETE FROM machine_pings WHERE device_id=$1`, [ + deviceId, + ]) + const q3 = t.none( + `DELETE FROM machine_network_heartbeat WHERE device_id=$1`, + [deviceId], + ) + const q4 = t.none( + `DELETE FROM machine_network_performance WHERE device_id=$1`, + [deviceId], + ) return t.batch([q1, q2, q3, q4]) - }) + }), ) } -function pair (token, deviceId, machineModel, numOfCassettes = DEFAULT_NUMBER_OF_CASSETTES, numOfRecyclers = DEFAULT_NUMBER_OF_RECYCLERS) { +function pair( + token, + deviceId, + machineModel, + numOfCassettes = DEFAULT_NUMBER_OF_CASSETTES, + numOfRecyclers = DEFAULT_NUMBER_OF_RECYCLERS, +) { return pullToken(token) .then(r => { if (r.expired) return false @@ -46,7 +63,8 @@ function pair (token, deviceId, machineModel, numOfCassettes = DEFAULT_NUMBER_OF on conflict (device_id) do update set paired=TRUE, display=TRUE` - return db.none(insertSql, [deviceId, r.name, numOfCassettes, numOfRecyclers]) + return db + .none(insertSql, [deviceId, r.name, numOfCassettes, numOfRecyclers]) .then(() => true) }) .catch(err => { @@ -55,20 +73,21 @@ function pair (token, deviceId, machineModel, numOfCassettes = DEFAULT_NUMBER_OF }) } -function authorizeCaDownload (caToken) { - return pullToken(caToken) - .then(r => { - if (r.expired) throw new Error('Expired') +function authorizeCaDownload(caToken) { + return pullToken(caToken).then(r => { + if (r.expired) throw new Error('Expired') - return readFile(CA_PATH, {encoding: 'utf8'}) - }) + return readFile(CA_PATH, { encoding: 'utf8' }) + }) } -function isPaired (deviceId) { - const sql = 'select device_id, name from devices where device_id=$1 and paired=TRUE' +function isPaired(deviceId) { + const sql = + 'select device_id, name from devices where device_id=$1 and paired=TRUE' - return db.oneOrNone(sql, [deviceId]) - .then(row => row && row.device_id === deviceId ? row.name : false) + return db + .oneOrNone(sql, [deviceId]) + .then(row => (row && row.device_id === deviceId ? row.name : false)) } -module.exports = {pair, unpair, authorizeCaDownload, isPaired} +module.exports = { pair, unpair, authorizeCaDownload, isPaired } diff --git a/packages/server/lib/pg-transport.js b/packages/server/lib/pg-transport.js index 3d757a46..4ce823de 100644 --- a/packages/server/lib/pg-transport.js +++ b/packages/server/lib/pg-transport.js @@ -6,7 +6,7 @@ const eventBus = require('./event-bus') // of the base functionality and `.exceptions.handle()`. // module.exports = class CustomTransport extends Transport { - constructor (opts) { + constructor(opts) { super(opts) // @@ -24,7 +24,7 @@ module.exports = class CustomTransport extends Transport { this.connectionString = opts.connectionString } - log (level, message, meta, callback) { + log(level, message, meta, callback) { if (!callback) callback = () => {} setImmediate(() => { @@ -37,7 +37,7 @@ module.exports = class CustomTransport extends Transport { callback() } - logException (msg, meta, callback) { + logException(msg, meta, callback) { this.log('error', msg, meta, callback) } } diff --git a/packages/server/lib/plugin-helper.js b/packages/server/lib/plugin-helper.js index de189979..04e43dfe 100644 --- a/packages/server/lib/plugin-helper.js +++ b/packages/server/lib/plugin-helper.js @@ -1,6 +1,3 @@ -const path = require('path') -const fs = require('fs') - const _ = require('lodash/fp') const pluginCodes = { @@ -12,12 +9,12 @@ const pluginCodes = { SMS: 'sms', EMAIL: 'email', ZERO_CONF: 'zero-conf', - COMPLIANCE: 'compliance' + COMPLIANCE: 'compliance', } -module.exports = _.assign({load}, pluginCodes) +module.exports = _.assign({ load }, pluginCodes) -function load (type, pluginCode) { +function load(type, pluginCode) { if (!_.includes(type, _.values(pluginCodes))) { throw new Error(`Unallowed plugin type: ${type}`) } diff --git a/packages/server/lib/plugins.js b/packages/server/lib/plugins.js index 477809a5..c180f740 100644 --- a/packages/server/lib/plugins.js +++ b/packages/server/lib/plugins.js @@ -37,7 +37,7 @@ const notifier = require('./notifier') const { utils: coinUtils } = require('@lamassu/coins') const mapValuesWithKey = _.mapValues.convert({ - cap: false + cap: false, }) const TRADE_TTL = 2 * T.minutes @@ -45,9 +45,8 @@ const STALE_TICKER = 3 * T.minutes const STALE_BALANCE = 3 * T.minutes const tradesQueues = {} -function plugins (settings, deviceId) { - - function internalBuildRates (tickers, withCommission = true) { +function plugins(settings, deviceId) { + function internalBuildRates(tickers, withCommission = true) { const localeConfig = configManager.getLocale(deviceId, settings.config) const cryptoCodes = localeConfig.cryptoCurrencies @@ -55,43 +54,54 @@ function plugins (settings, deviceId) { cryptoCodes.forEach((cryptoCode, i) => { const rateRec = tickers[i] - const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config) + const commissions = configManager.getCommissions( + cryptoCode, + deviceId, + settings.config, + ) if (!rateRec) return - const cashInCommission = new BN(1).plus(new BN(commissions.cashIn).div(100)) + const cashInCommission = new BN(1).plus( + new BN(commissions.cashIn).div(100), + ) const cashOutCommission = _.isNil(commissions.cashOut) ? undefined : new BN(1).plus(new BN(commissions.cashOut).div(100)) - if (Date.now() - rateRec.timestamp > STALE_TICKER) return logger.warn('Stale rate for ' + cryptoCode) + if (Date.now() - rateRec.timestamp > STALE_TICKER) + return logger.warn('Stale rate for ' + cryptoCode) const rate = rateRec.rates - withCommission ? rates[cryptoCode] = { - cashIn: rate.ask.times(cashInCommission).decimalPlaces(5), - cashOut: cashOutCommission && rate.bid.div(cashOutCommission).decimalPlaces(5) - } : rates[cryptoCode] = { - cashIn: rate.ask.decimalPlaces(5), - cashOut: rate.bid.decimalPlaces(5) - } + withCommission + ? (rates[cryptoCode] = { + cashIn: rate.ask.times(cashInCommission).decimalPlaces(5), + cashOut: + cashOutCommission && + rate.bid.div(cashOutCommission).decimalPlaces(5), + }) + : (rates[cryptoCode] = { + cashIn: rate.ask.decimalPlaces(5), + cashOut: rate.bid.decimalPlaces(5), + }) }) return rates } - function buildRatesNoCommission (tickers) { + function buildRatesNoCommission(tickers) { return internalBuildRates(tickers, false) } - function buildRates (tickers) { + function buildRates(tickers) { return internalBuildRates(tickers, true) } - function getNotificationConfig () { + function getNotificationConfig() { return configManager.getGlobalNotifications(settings.config) } - function buildBalances (balanceRecs) { + function buildBalances(balanceRecs) { const localeConfig = configManager.getLocale(deviceId, settings.config) const cryptoCodes = localeConfig.cryptoCurrencies @@ -99,8 +109,10 @@ function plugins (settings, deviceId) { cryptoCodes.forEach((cryptoCode, i) => { const balanceRec = balanceRecs[i] - if (!balanceRec) return logger.warn('No balance for ' + cryptoCode + ' yet') - if (Date.now() - balanceRec.timestamp > STALE_BALANCE) return logger.warn('Stale balance for ' + cryptoCode) + if (!balanceRec) + return logger.warn('No balance for ' + cryptoCode + ' yet') + if (Date.now() - balanceRec.timestamp > STALE_BALANCE) + return logger.warn('Stale balance for ' + cryptoCode) balances[cryptoCode] = balanceRec.balance }) @@ -108,8 +120,11 @@ function plugins (settings, deviceId) { return balances } - function isZeroConf (tx) { - const walletSettings = configManager.getWalletSettings(tx.cryptoCode, settings.config) + function isZeroConf(tx) { + const walletSettings = configManager.getWalletSettings( + tx.cryptoCode, + settings.config, + ) const zeroConfLimit = walletSettings.zeroConfLimit || 0 return tx.fiat.lte(zeroConfLimit) } @@ -119,141 +134,179 @@ function plugins (settings, deviceId) { // cash-out-helper sends 0 as fallback value, need to filter it out as there are no '0' denominations const cashUnitsBills = _.flow( _.get(['bills']), - _.filter(it => _.includes(cashUnitType, it.name) && it.denomination > 0), + _.filter( + it => _.includes(cashUnitType, it.name) && it.denomination > 0, + ), _.zip(cashUnits), )(tx) - const sameDenominations = ([cashUnit, bill]) => cashUnit?.denomination === bill?.denomination + const sameDenominations = ([cashUnit, bill]) => + cashUnit?.denomination === bill?.denomination if (!_.every(sameDenominations, cashUnitsBills)) - throw new Error(`Denominations don't add up, ${cashUnitType}s were changed.`) + throw new Error( + `Denominations don't add up, ${cashUnitType}s were changed.`, + ) return _.map( - ([cashUnit, { provisioned }]) => _.set('count', cashUnit.count - provisioned, cashUnit), - cashUnitsBills + ([cashUnit, { provisioned }]) => + _.set('count', cashUnit.count - provisioned, cashUnit), + cashUnitsBills, ) } return _.reduce(kons, cashUnits, redeemableTxs) } - function computeAvailableCassettes (cassettes, redeemableTxs) { + function computeAvailableCassettes(cassettes, redeemableTxs) { if (_.isEmpty(redeemableTxs)) return cassettes cassettes = accountProvisioned('cassette', cassettes, redeemableTxs) if (_.some(({ count }) => count < 0, cassettes)) - throw new Error('Negative note count: %j', counts) + throw new Error( + `Negative note count detected: ${JSON.stringify(cassettes)}`, + ) return cassettes } - function computeAvailableRecyclers (recyclers, redeemableTxs) { + function computeAvailableRecyclers(recyclers, redeemableTxs) { if (_.isEmpty(redeemableTxs)) return recyclers recyclers = accountProvisioned('recycler', recyclers, redeemableTxs) if (_.some(({ count }) => count < 0, recyclers)) - throw new Error('Negative note count: %j', counts) + throw new Error( + `Negative note count detected: ${JSON.stringify(recyclers)}`, + ) return recyclers } - function buildAvailableCassettes (excludeTxId) { + function buildAvailableCassettes(excludeTxId) { const cashOutConfig = configManager.getCashOut(deviceId, settings.config) if (!cashOutConfig.active) return Promise.resolve() - return Promise.all([dbm.cassetteCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)]) - .then(([{ counts, numberOfCassettes }, redeemableTxs]) => { - redeemableTxs = _.reject(_.matchesProperty('id', excludeTxId), redeemableTxs) + return Promise.all([ + dbm.cassetteCounts(deviceId), + cashOutHelper.redeemableTxs(deviceId, excludeTxId), + ]).then(([{ counts, numberOfCassettes }, redeemableTxs]) => { + redeemableTxs = _.reject( + _.matchesProperty('id', excludeTxId), + redeemableTxs, + ) - const denominations = _.map( - it => cashOutConfig[`cassette${it}`], - _.range(1, numberOfCassettes+1) - ) - - if (counts.length !== denominations.length) - throw new Error('Denominations and respective counts do not match!') + const denominations = _.map( + it => cashOutConfig[`cassette${it}`], + _.range(1, numberOfCassettes + 1), + ) - const cassettes = _.map( - it => ({ - name: `cassette${it + 1}`, - denomination: parseInt(denominations[it], 10), - count: parseInt(counts[it], 10) - }), - _.range(0, numberOfCassettes) - ) + if (counts.length !== denominations.length) + throw new Error('Denominations and respective counts do not match!') - const virtualCassettes = denominations.length ? [Math.max(...denominations) * 2] : [] + const cassettes = _.map( + it => ({ + name: `cassette${it + 1}`, + denomination: parseInt(denominations[it], 10), + count: parseInt(counts[it], 10), + }), + _.range(0, numberOfCassettes), + ) - try { - return { - cassettes: computeAvailableCassettes(cassettes, redeemableTxs), - virtualCassettes - } - } catch (err) { - logger.error(err) - return { - cassettes, - virtualCassettes - } + const virtualCassettes = denominations.length + ? [Math.max(...denominations) * 2] + : [] + + try { + return { + cassettes: computeAvailableCassettes(cassettes, redeemableTxs), + virtualCassettes, } - }) + } catch (err) { + logger.error(err) + return { + cassettes, + virtualCassettes, + } + } + }) } - function buildAvailableRecyclers (excludeTxId) { + function buildAvailableRecyclers(excludeTxId) { const cashOutConfig = configManager.getCashOut(deviceId, settings.config) if (!cashOutConfig.active) return Promise.resolve() - return Promise.all([dbm.recyclerCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)]) - .then(([{ counts, numberOfRecyclers }, redeemableTxs]) => { - redeemableTxs = _.reject(_.matchesProperty('id', excludeTxId), redeemableTxs) + return Promise.all([ + dbm.recyclerCounts(deviceId), + cashOutHelper.redeemableTxs(deviceId, excludeTxId), + ]).then(([{ counts, numberOfRecyclers }, redeemableTxs]) => { + redeemableTxs = _.reject( + _.matchesProperty('id', excludeTxId), + redeemableTxs, + ) - const denominations = _.map( - it => cashOutConfig[`recycler${it}`], - _.range(1, numberOfRecyclers+1) - ) + const denominations = _.map( + it => cashOutConfig[`recycler${it}`], + _.range(1, numberOfRecyclers + 1), + ) - if (counts.length !== denominations.length) - throw new Error('Denominations and respective counts do not match!') + if (counts.length !== denominations.length) + throw new Error('Denominations and respective counts do not match!') - const recyclers = _.map( - it => ({ - number: it + 1, - name: `recycler${it + 1}`, - denomination: parseInt(denominations[it], 10), - count: parseInt(counts[it], 10) - }), - _.range(0, numberOfRecyclers) - ) + const recyclers = _.map( + it => ({ + number: it + 1, + name: `recycler${it + 1}`, + denomination: parseInt(denominations[it], 10), + count: parseInt(counts[it], 10), + }), + _.range(0, numberOfRecyclers), + ) - const virtualRecyclers = denominations.length ? [Math.max(..._.flatten(denominations)) * 2] : [] + const virtualRecyclers = denominations.length + ? [Math.max(..._.flatten(denominations)) * 2] + : [] - try { - return { - recyclers: computeAvailableRecyclers(recyclers, redeemableTxs), - virtualRecyclers - } - } catch (err) { - logger.error(err) - return { - recyclers, - virtualRecyclers - } + try { + return { + recyclers: computeAvailableRecyclers(recyclers, redeemableTxs), + virtualRecyclers, } - }) + } catch (err) { + logger.error(err) + return { + recyclers, + virtualRecyclers, + } + } + }) } - function buildAvailableUnits (excludeTxId) { - return Promise.all([buildAvailableCassettes(excludeTxId), buildAvailableRecyclers(excludeTxId)]) - .then(([cassettes, recyclers]) => ({ cassettes: cassettes.cassettes, recyclers: recyclers.recyclers })) + function buildAvailableUnits(excludeTxId) { + return Promise.all([ + buildAvailableCassettes(excludeTxId), + buildAvailableRecyclers(excludeTxId), + ]).then(([cassettes, recyclers]) => ({ + cassettes: cassettes.cassettes, + recyclers: recyclers.recyclers, + })) } - function mapCoinSettings (coinParams) { - const [ cryptoCode, cryptoNetwork ] = coinParams - const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config) + function mapCoinSettings(coinParams) { + const [cryptoCode, cryptoNetwork] = coinParams + const commissions = configManager.getCommissions( + cryptoCode, + deviceId, + settings.config, + ) const minimumTx = new BN(commissions.minimumTx) const cashInFee = new BN(commissions.fixedFee) const cashOutFee = new BN(commissions.cashOutFixedFee) const cashInCommission = new BN(commissions.cashIn) - const cashOutCommission = _.isNumber(commissions.cashOut) ? new BN(commissions.cashOut) : null + const cashOutCommission = _.isNumber(commissions.cashOut) + ? new BN(commissions.cashOut) + : null const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode) - const cryptoUnits = configManager.getCryptoUnits(cryptoCode, settings.config) + const cryptoUnits = configManager.getCryptoUnits( + cryptoCode, + settings.config, + ) return { cryptoCode, @@ -266,24 +319,30 @@ function plugins (settings, deviceId) { cashInCommission, cashOutCommission, cryptoNetwork, - cryptoUnits + cryptoUnits, } } - function getTickerRates (fiatCode, cryptoCode) { + function getTickerRates(fiatCode, cryptoCode) { return ticker.getRates(settings, fiatCode, cryptoCode) } - function pollQueries () { + function pollQueries() { const localeConfig = configManager.getLocale(deviceId, settings.config) const fiatCode = localeConfig.fiatCurrency const cryptoCodes = localeConfig.cryptoCurrencies - const machineScreenOpts = configManager.getAllMachineScreenOpts(settings.config) + const machineScreenOpts = configManager.getAllMachineScreenOpts( + settings.config, + ) const tickerPromises = cryptoCodes.map(c => getTickerRates(fiatCode, c)) const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c)) - const networkPromises = cryptoCodes.map(c => wallet.cryptoNetwork(settings, c)) - const supportsBatchingPromise = cryptoCodes.map(c => wallet.supportsBatching(settings, c)) + const networkPromises = cryptoCodes.map(c => + wallet.cryptoNetwork(settings, c), + ) + const supportsBatchingPromise = cryptoCodes.map(c => + wallet.supportsBatching(settings, c), + ) return Promise.all([ buildAvailableCassettes(), @@ -294,9 +353,9 @@ function plugins (settings, deviceId) { Promise.all(supportsBatchingPromise), Promise.all(tickerPromises), Promise.all(balancePromises), - Promise.all(networkPromises) - ]) - .then(([ + Promise.all(networkPromises), + ]).then( + ([ cassettes, recyclers, configVersion, @@ -305,19 +364,16 @@ function plugins (settings, deviceId) { batchableCoins, tickers, balances, - networks + networks, ]) => { const coinsWithoutRate = _.flow( _.zip(cryptoCodes), - _.map(mapCoinSettings) + _.map(mapCoinSettings), )(networks) const coins = _.flow( _.map(it => ({ batchable: it })), - _.zipWith( - _.assign, - _.zipWith(_.assign, coinsWithoutRate, tickers) - ) + _.zipWith(_.assign, _.zipWith(_.assign, coinsWithoutRate, tickers)), )(batchableCoins) return { @@ -329,195 +385,219 @@ function plugins (settings, deviceId) { configVersion, areThereAvailablePromoCodes: numberOfAvailablePromoCodes > 0, timezone, - screenOptions: machineScreenOpts + screenOptions: machineScreenOpts, } - }) + }, + ) } - function sendCoins (tx) { - return wallet.supportsBatching(settings, tx.cryptoCode) + function sendCoins(tx) { + return wallet + .supportsBatching(settings, tx.cryptoCode) .then(supportsBatching => { if (supportsBatching) { - return transactionBatching.addTransactionToBatch(tx) - .then(() => ({ - batched: true, - sendPending: false, - error: null, - errorCode: null - })) + return transactionBatching.addTransactionToBatch(tx).then(() => ({ + batched: true, + sendPending: false, + error: null, + errorCode: null, + })) } return wallet.sendCoins(settings, tx) }) } - function recordPing (deviceTime, version, model) { + function recordPing(deviceTime, version, model) { const devices = { version, model, - last_online: deviceTime + last_online: deviceTime, } return Promise.all([ - db.none(`insert into machine_pings(device_id, device_time) values($1, $2) - ON CONFLICT (device_id) DO UPDATE SET device_time = $2, updated = now()`, [deviceId, deviceTime]), - db.none(pgp.helpers.update(devices, null, 'devices') + 'WHERE device_id = ${deviceId}', { - deviceId - }) + db.none( + `insert into machine_pings(device_id, device_time) + values ($1, $2) + ON CONFLICT (device_id) DO UPDATE SET device_time = $2, + updated = now()`, + [deviceId, deviceTime], + ), + db.none( + pgp.helpers.update(devices, null, 'devices') + + 'WHERE device_id = ${deviceId}', + { + deviceId, + }, + ), ]) } - function pruneMachinesHeartbeat () { - const sql = `DELETE FROM machine_network_heartbeat h - USING (SELECT device_id, max(created) as lastEntry FROM machine_network_heartbeat GROUP BY device_id) d - WHERE d.device_id = h.device_id AND h.created < d.lastEntry` + function pruneMachinesHeartbeat() { + const sql = `DELETE + FROM machine_network_heartbeat h + USING (SELECT device_id, max(created) as lastEntry + FROM machine_network_heartbeat + GROUP BY device_id) d + WHERE d.device_id = h.device_id + AND h.created < d.lastEntry` return db.none(sql) } - function isHd (tx) { + function isHd(tx) { return wallet.isHd(settings, tx) } - function getStatus (tx) { - return wallet.getStatus(settings, tx, deviceId) + function getStatus(tx) { + return wallet.getStatus(settings, tx) } - function newAddress (tx) { + function newAddress(tx) { const info = { cryptoCode: tx.cryptoCode, label: 'TX ' + Date.now(), account: 'deposit', hdIndex: tx.hdIndex, cryptoAtoms: tx.cryptoAtoms, - isLightning: tx.isLightning + isLightning: tx.isLightning, } return wallet.newAddress(settings, info, tx) } - function fiatBalance (fiatCode, cryptoCode) { - const commissions = configManager.getCommissions(cryptoCode, deviceId, settings.config) + function fiatBalance(fiatCode, cryptoCode) { + const commissions = configManager.getCommissions( + cryptoCode, + deviceId, + settings.config, + ) return Promise.all([ getTickerRates(fiatCode, cryptoCode), - wallet.balance(settings, cryptoCode) - ]) - .then(([rates, balanceRec]) => { - if (!rates || !balanceRec) return null + wallet.balance(settings, cryptoCode), + ]).then(([rates, balanceRec]) => { + if (!rates || !balanceRec) return null - const rawRate = rates.rates.ask - const cashInCommission = new BN(1).minus(new BN(commissions.cashIn).div(100)) - const balance = balanceRec.balance + const rawRate = rates.rates.ask + const cashInCommission = new BN(1).minus( + new BN(commissions.cashIn).div(100), + ) + const balance = balanceRec.balance - if (!rawRate || !balance) return null + if (!rawRate || !balance) return null - const rate = rawRate.div(cashInCommission) + const rate = rawRate.div(cashInCommission) - const lowBalanceMargin = new BN(0.95) + const lowBalanceMargin = new BN(0.95) - const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode) - const unitScale = cryptoRec.unitScale - const shiftedRate = rate.shiftedBy(-unitScale) - const fiatTransferBalance = balance.times(shiftedRate).times(lowBalanceMargin) + const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode) + const unitScale = cryptoRec.unitScale + const shiftedRate = rate.shiftedBy(-unitScale) + const fiatTransferBalance = balance + .times(shiftedRate) + .times(lowBalanceMargin) - return { - timestamp: balanceRec.timestamp, - balance: fiatTransferBalance.integerValue(BN.ROUND_DOWN).toString() - } - }) + return { + timestamp: balanceRec.timestamp, + balance: fiatTransferBalance.integerValue(BN.ROUND_DOWN).toString(), + } + }) } - function notifyConfirmation (tx) { + function notifyConfirmation(tx) { logger.debug('notifyConfirmation') const phone = tx.phone - const timestamp = `${(new Date()).toISOString().substring(11, 19)} UTC` - return sms.getSms(CASH_OUT_DISPENSE_READY, phone, { timestamp }) + const timestamp = `${new Date().toISOString().substring(11, 19)} UTC` + return sms + .getSms(CASH_OUT_DISPENSE_READY, phone, { timestamp }) .then(smsObj => { const rec = { - sms: smsObj + sms: smsObj, } - - return sms.sendMessage(settings, rec) - .then(() => { - const sql = 'UPDATE cash_out_txs SET notified=$1 WHERE id=$2' - const values = [true, tx.id] - return db.none(sql, values) - }) + return sms.sendMessage(settings, rec).then(() => { + const sql = 'UPDATE cash_out_txs SET notified=$1 WHERE id=$2' + const values = [true, tx.id] + + return db.none(sql, values) + }) }) } - function notifyOperator (tx, rec) { + function notifyOperator(tx, rec) { // notify operator about new transaction and add high volume txs to database return notifier.transactionNotify(tx, rec) } - function clearOldLogs () { - return logs.clearOldLogs() - .catch(logger.error) + function clearOldLogs() { + return logs.clearOldLogs().catch(logger.error) } /* * Trader functions */ - function toMarketString (fiatCode, cryptoCode) { + function toMarketString(fiatCode, cryptoCode) { return [fiatCode, cryptoCode].join('-') } - - function fromMarketString (market) { + + function fromMarketString(market) { const [fiatCode, cryptoCode] = market.split('-') return { fiatCode, cryptoCode } } - function buy (rec, tx) { + function buy(rec, tx) { return buyAndSell(rec, true, tx) } - function sell (rec) { + function sell(rec) { return buyAndSell(rec, false) } - function buyAndSell (rec, doBuy, tx) { + function buyAndSell(rec, doBuy, tx) { const cryptoCode = rec.cryptoCode if (!exchange.active(settings, cryptoCode)) return - return exchange.fetchExchange(settings, cryptoCode) - .then(_exchange => { - const fiatCode = _exchange.account.currencyMarket - const cryptoAtoms = doBuy ? commissionMath.fiatToCrypto(tx, rec, deviceId, settings.config) : rec.cryptoAtoms.negated() + return exchange.fetchExchange(settings, cryptoCode).then(_exchange => { + const fiatCode = _exchange.account.currencyMarket + const cryptoAtoms = doBuy + ? commissionMath.fiatToCrypto(tx, rec, deviceId, settings.config) + : rec.cryptoAtoms.negated() - const market = toMarketString(fiatCode, cryptoCode) + const market = toMarketString(fiatCode, cryptoCode) - const direction = doBuy ? 'cashIn' : 'cashOut' - const internalTxId = tx ? tx.id : rec.id - logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms) - if (!tradesQueues[market]) tradesQueues[market] = [] - tradesQueues[market].push({ - direction, - internalTxId, - fiatCode, - cryptoAtoms, - cryptoCode, - timestamp: Date.now() - }) + const direction = doBuy ? 'cashIn' : 'cashOut' + const internalTxId = tx ? tx.id : rec.id + logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms) + if (!tradesQueues[market]) tradesQueues[market] = [] + tradesQueues[market].push({ + direction, + internalTxId, + fiatCode, + cryptoAtoms, + cryptoCode, + timestamp: Date.now(), }) + }) } - function consolidateTrades (cryptoCode, fiatCode) { + function consolidateTrades(cryptoCode, fiatCode) { const market = toMarketString(fiatCode, cryptoCode) const marketTradesQueues = tradesQueues[market] if (!marketTradesQueues || marketTradesQueues.length === 0) return null - logger.debug('[%s] tradesQueues size: %d', market, marketTradesQueues.length) + logger.debug( + '[%s] tradesQueues size: %d', + market, + marketTradesQueues.length, + ) logger.debug('[%s] tradesQueues head: %j', market, marketTradesQueues[0]) const t1 = Date.now() - const filtered = marketTradesQueues - .filter(tradeEntry => { - return t1 - tradeEntry.timestamp < TRADE_TTL - }) + const filtered = marketTradesQueues.filter(tradeEntry => { + return t1 - tradeEntry.timestamp < TRADE_TTL + }) const filteredCount = marketTradesQueues.length - filtered.length @@ -528,13 +608,22 @@ function plugins (settings, deviceId) { if (filtered.length === 0) return null - const partitionByDirection = _.partition(({ direction }) => direction === 'cashIn') - const [cashInTxs, cashOutTxs] = _.compose(partitionByDirection, _.uniqBy('internalTxId'))(filtered) + const partitionByDirection = _.partition( + ({ direction }) => direction === 'cashIn', + ) + const [cashInTxs, cashOutTxs] = _.compose( + partitionByDirection, + _.uniqBy('internalTxId'), + )(filtered) - const cryptoAtoms = filtered - .reduce((prev, current) => prev.plus(current.cryptoAtoms), new BN(0)) + const cryptoAtoms = filtered.reduce( + (prev, current) => prev.plus(current.cryptoAtoms), + new BN(0), + ) - const timestamp = filtered.map(r => r.timestamp).reduce((acc, r) => Math.max(acc, r), 0) + const timestamp = filtered + .map(r => r.timestamp) + .reduce((acc, r) => Math.max(acc, r), 0) const consolidatedTrade = { cashInTxs, @@ -542,7 +631,7 @@ function plugins (settings, deviceId) { fiatCode, cryptoAtoms, cryptoCode, - timestamp + timestamp, } tradesQueues[market] = [] @@ -551,7 +640,7 @@ function plugins (settings, deviceId) { return consolidatedTrade } - function executeTrades () { + function executeTrades() { const pairs = _.map(fromMarketString)(_.keys(tradesQueues)) pairs.forEach(({ fiatCode, cryptoCode }) => { try { @@ -565,7 +654,7 @@ function plugins (settings, deviceId) { return Promise.resolve() } - function executeTradesForMarket (settings, fiatCode, cryptoCode) { + function executeTradesForMarket(settings, fiatCode, cryptoCode) { if (!exchange.active(settings, cryptoCode)) return const market = toMarketString(fiatCode, cryptoCode) @@ -573,47 +662,53 @@ function plugins (settings, deviceId) { if (tradeEntry === null || tradeEntry.cryptoAtoms.eq(0)) return - return executeTradeForType(tradeEntry) - .catch(err => { - tradesQueues[market].push(tradeEntry) - if (err.name === 'orderTooSmall') return logger.debug(err.message) - logger.error(err) - }) + return executeTradeForType(tradeEntry).catch(err => { + tradesQueues[market].push(tradeEntry) + if (err.name === 'orderTooSmall') return logger.debug(err.message) + logger.error(err) + }) } - function executeTradeForType (_tradeEntry) { - const expand = te => _.assign(te, { - cryptoAtoms: te.cryptoAtoms.abs(), - type: te.cryptoAtoms.gte(0) ? 'buy' : 'sell' - }) + function executeTradeForType(_tradeEntry) { + const expand = te => + _.assign(te, { + cryptoAtoms: te.cryptoAtoms.abs(), + type: te.cryptoAtoms.gte(0) ? 'buy' : 'sell', + }) const tradeEntry = expand(_tradeEntry) const execute = tradeEntry.type === 'buy' ? exchange.buy : exchange.sell - return recordTrade(tradeEntry) - .then(newEntry => { - tradeEntry.tradeId = newEntry.id - return execute(settings, tradeEntry) - .catch(err => { - updateTradeEntry(tradeEntry, newEntry, err) - .then(() => { - logger.error(err) - throw err - }) - }) + return recordTrade(tradeEntry).then(newEntry => { + tradeEntry.tradeId = newEntry.id + return execute(settings, tradeEntry).catch(err => { + updateTradeEntry(tradeEntry, newEntry, err).then(() => { + logger.error(err) + throw err + }) }) + }) } - function updateTradeEntry (tradeEntry, newEntry, err) { + function updateTradeEntry(tradeEntry, newEntry, err) { const data = mergeTradeEntryAndError(tradeEntry, err) - const sql = pgp.helpers.update(data, ['error'], 'trades') + ` WHERE id = ${newEntry.id}` + const sql = + pgp.helpers.update(data, ['error'], 'trades') + + ` WHERE id = ${newEntry.id}` return db.none(sql) } - function recordTradeAndTx (tradeId, { cashInTxs, cashOutTxs }, dbTx) { - const columnSetCashIn = new pgp.helpers.ColumnSet(['tx_id', 'trade_id'], { table: 'cashin_tx_trades' }) - const columnSetCashOut = new pgp.helpers.ColumnSet(['tx_id', 'trade_id'], { table: 'cashout_tx_trades' }) - const mapToEntry = _.map(tx => ({ tx_id: tx.internalTxId, trade_id: tradeId })) + function recordTradeAndTx(tradeId, { cashInTxs, cashOutTxs }, dbTx) { + const columnSetCashIn = new pgp.helpers.ColumnSet(['tx_id', 'trade_id'], { + table: 'cashin_tx_trades', + }) + const columnSetCashOut = new pgp.helpers.ColumnSet(['tx_id', 'trade_id'], { + table: 'cashout_tx_trades', + }) + const mapToEntry = _.map(tx => ({ + tx_id: tx.internalTxId, + trade_id: tradeId, + })) const queries = [] if (!_.isEmpty(cashInTxs)) { @@ -627,89 +722,100 @@ function plugins (settings, deviceId) { return Promise.all(queries) } - function convertBigNumFields (obj) { - const convert = (value, key) => _.includes(key, ['cryptoAtoms', 'fiat']) - ? value.toString() - : value + function convertBigNumFields(obj) { + const convert = (value, key) => + _.includes(key, ['cryptoAtoms', 'fiat']) ? value.toString() : value - const convertKey = key => _.includes(key, ['cryptoAtoms', 'fiat']) - ? key + '#' - : key + const convertKey = key => + _.includes(key, ['cryptoAtoms', 'fiat']) ? key + '#' : key return _.mapKeys(convertKey, mapValuesWithKey(convert, obj)) } - function mergeTradeEntryAndError (tradeEntry, error) { + function mergeTradeEntryAndError(tradeEntry, error) { if (error && error.message) { return Object.assign({}, tradeEntry, { - error: error.message.slice(0, 200) + error: error.message.slice(0, 200), }) } return tradeEntry } - function recordTrade (_tradeEntry, error) { + function recordTrade(_tradeEntry, error) { const massage = _.flow( mergeTradeEntryAndError, _.pick(['cryptoCode', 'cryptoAtoms', 'fiatCode', 'type', 'error']), convertBigNumFields, - _.mapKeys(_.snakeCase) + _.mapKeys(_.snakeCase), ) const tradeEntry = massage(_tradeEntry, error) const sql = pgp.helpers.insert(tradeEntry, null, 'trades') + 'RETURNING *' return db.tx(t => { - return t.oneOrNone(sql) - .then(newTrade => { - return recordTradeAndTx(newTrade.id, _tradeEntry, t) - .then(() => newTrade) - }) + return t.oneOrNone(sql).then(newTrade => { + return recordTradeAndTx(newTrade.id, _tradeEntry, t).then( + () => newTrade, + ) + }) }) } - function sendMessage (rec) { + function sendMessage(rec) { const notifications = configManager.getGlobalNotifications(settings.config) let promises = [] - if (notifications.email.active && rec.email) promises.push(email.sendMessage(settings, rec)) - if (notifications.sms.active && rec.sms) promises.push(sms.sendMessage(settings, rec)) + if (notifications.email.active && rec.email) + promises.push(email.sendMessage(settings, rec)) + if (notifications.sms.active && rec.sms) + promises.push(sms.sendMessage(settings, rec)) return Promise.all(promises) } - function checkDevicesCashBalances (fiatCode, devices) { + function checkDevicesCashBalances(fiatCode, devices) { return _.map(device => checkDeviceCashBalances(fiatCode, device), devices) } - function getCashUnitCapacity (model, device) { + function getCashUnitCapacity(model, device) { if (!CASH_UNIT_CAPACITY[model]) { return CASH_UNIT_CAPACITY.default[device] } return CASH_UNIT_CAPACITY[model][device] } - function checkDeviceCashBalances (fiatCode, device) { + function checkDeviceCashBalances(fiatCode, device) { const deviceId = device.deviceId const machineName = device.name - const notifications = configManager.getNotifications(null, deviceId, settings.config) + const notifications = configManager.getNotifications( + null, + deviceId, + settings.config, + ) - const cashInAlerts = device.cashUnits.cashbox > notifications.cashInAlertThreshold - ? [{ - code: 'CASH_BOX_FULL', - machineName, - deviceId, - notes: device.cashUnits.cashbox - }] - : [] + const cashInAlerts = + device.cashUnits.cashbox > notifications.cashInAlertThreshold + ? [ + { + code: 'CASH_BOX_FULL', + machineName, + deviceId, + notes: device.cashUnits.cashbox, + }, + ] + : [] const cashOutConfig = configManager.getCashOut(deviceId, settings.config) const cashOutEnabled = cashOutConfig.active - const isUnitLow = (have, max, limit) => ((have / max) * 100) < limit + const isUnitLow = (have, max, limit) => (have / max) * 100 < limit - if (!cashOutEnabled) - return cashInAlerts + if (!cashOutEnabled) return cashInAlerts const cassetteCapacity = getCashUnitCapacity(device.model, 'cassette') - const cassetteAlerts = Array(Math.min(device.numberOfCassettes ?? 0, CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES)) + const cassetteAlerts = Array( + Math.min( + device.numberOfCassettes ?? 0, + CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES, + ), + ) .fill(null) .flatMap((_elem, idx) => { const nth = idx + 1 @@ -718,21 +824,28 @@ function plugins (settings, deviceId) { const denomination = cashOutConfig[cassetteField] const limit = notifications[`fillingPercentageCassette${nth}`] - return isUnitLow(notes, cassetteCapacity, limit) ? - [{ - code: 'LOW_CASH_OUT', - cassette: nth, - machineName, - deviceId, - notes, - denomination, - fiatCode - }] : - [] + return isUnitLow(notes, cassetteCapacity, limit) + ? [ + { + code: 'LOW_CASH_OUT', + cassette: nth, + machineName, + deviceId, + notes, + denomination, + fiatCode, + }, + ] + : [] }) const recyclerCapacity = getCashUnitCapacity(device.model, 'recycler') - const recyclerAlerts = Array(Math.min(device.numberOfRecyclers ?? 0, CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS)) + const recyclerAlerts = Array( + Math.min( + device.numberOfRecyclers ?? 0, + CASH_OUT_MAXIMUM_AMOUNT_OF_RECYCLERS, + ), + ) .fill(null) .flatMap((_elem, idx) => { const nth = idx + 1 @@ -741,24 +854,27 @@ function plugins (settings, deviceId) { const denomination = cashOutConfig[recyclerField] const limit = notifications[`fillingPercentageRecycler${nth}`] - return isUnitLow(notes, recyclerCapacity, limit) ? - [{ - code: 'LOW_RECYCLER_STACKER', - cassette: nth, // @see DETAIL_TEMPLATE in /lib/notifier/utils.js - machineName, - deviceId, - notes, - denomination, - fiatCode - }] : - [] + return isUnitLow(notes, recyclerCapacity, limit) + ? [ + { + code: 'LOW_RECYCLER_STACKER', + cassette: nth, // @see DETAIL_TEMPLATE in /lib/notifier/utils.js + machineName, + deviceId, + notes, + denomination, + fiatCode, + }, + ] + : [] }) return [].concat(cashInAlerts, cassetteAlerts, recyclerAlerts) } - function checkCryptoBalances (fiatCode, devices) { - const fiatBalancePromises = cryptoCodes => _.map(c => fiatBalance(fiatCode, c), cryptoCodes) + function checkCryptoBalances(fiatCode, devices) { + const fiatBalancePromises = cryptoCodes => + _.map(c => fiatBalance(fiatCode, c), cryptoCodes) const fetchCryptoCodes = _deviceId => { const localeConfig = configManager.getLocale(_deviceId, settings.config) @@ -769,117 +885,149 @@ function plugins (settings, deviceId) { const cryptoCodes = union(devices) const checkCryptoBalanceWithFiat = _.partial(checkCryptoBalance, [fiatCode]) - return Promise.all(fiatBalancePromises(cryptoCodes)) - .then(balances => _.map(checkCryptoBalanceWithFiat, _.zip(cryptoCodes, balances))) + return Promise.all(fiatBalancePromises(cryptoCodes)).then(balances => + _.map(checkCryptoBalanceWithFiat, _.zip(cryptoCodes, balances)), + ) } - function checkCryptoBalance (fiatCode, rec) { + function checkCryptoBalance(fiatCode, rec) { const [cryptoCode, fiatBalance] = rec if (!fiatBalance) return null - const notifications = configManager.getNotifications(cryptoCode, null, settings.config) + const notifications = configManager.getNotifications( + cryptoCode, + null, + settings.config, + ) const lowAlertThreshold = notifications.cryptoLowBalance const highAlertThreshold = notifications.cryptoHighBalance const req = { cryptoCode, fiatBalance, - fiatCode + fiatCode, } - if (_.isFinite(lowAlertThreshold) && new BN(fiatBalance.balance).lt(lowAlertThreshold)) { + if ( + _.isFinite(lowAlertThreshold) && + new BN(fiatBalance.balance).lt(lowAlertThreshold) + ) { return _.set('code')('LOW_CRYPTO_BALANCE')(req) } - if (_.isFinite(highAlertThreshold) && new BN(fiatBalance.balance).gt(highAlertThreshold)) { + if ( + _.isFinite(highAlertThreshold) && + new BN(fiatBalance.balance).gt(highAlertThreshold) + ) { return _.set('code')('HIGH_CRYPTO_BALANCE')(req) } return null } - function checkBalances () { + function checkBalances() { const localeConfig = configManager.getGlobalLocale(settings.config) const fiatCode = localeConfig.fiatCurrency - return machineLoader.getMachines() - .then(devices => Promise.all([ - checkCryptoBalances(fiatCode, devices), - checkDevicesCashBalances(fiatCode, devices) - ])) + return machineLoader + .getMachines() + .then(devices => + Promise.all([ + checkCryptoBalances(fiatCode, devices), + checkDevicesCashBalances(fiatCode, devices), + ]), + ) .then(_.flow(_.flattenDeep, _.compact)) } - function randomCode () { - return new BN(crypto.randomBytes(3).toString('hex'), 16).shiftedBy(-6).toFixed(6).slice(-6) + function randomCode() { + return new BN(crypto.randomBytes(3).toString('hex'), 16) + .shiftedBy(-6) + .toFixed(6) + .slice(-6) } - function getPhoneCode (phone) { - const code = settings.config.notifications_thirdParty_sms === 'mock-sms' - ? '123' - : randomCode() + function getPhoneCode(phone) { + const code = + settings.config.notifications_thirdParty_sms === 'mock-sms' + ? '123' + : randomCode() - const timestamp = `${(new Date()).toISOString().substring(11, 19)} UTC` - return sms.getSms(CONFIRMATION_CODE, phone, { code, timestamp }) + const timestamp = `${new Date().toISOString().substring(11, 19)} UTC` + return sms + .getSms(CONFIRMATION_CODE, phone, { code, timestamp }) .then(smsObj => { const rec = { - sms: smsObj + sms: smsObj, } - return sms.sendMessage(settings, rec) - .then(() => code) + return sms.sendMessage(settings, rec).then(() => code) }) } - function getEmailCode (toEmail) { - const code = settings.config.notifications_thirdParty_email === 'mock-email' - ? '123' - : randomCode() + function getEmailCode(toEmail) { + const code = + settings.config.notifications_thirdParty_email === 'mock-email' + ? '123' + : randomCode() const rec = { email: { toEmail, subject: 'Your cryptomat code', - body: `Your cryptomat code: ${code}` - } + body: `Your cryptomat code: ${code}`, + }, } - return email.sendCustomerMessage(settings, rec) - .then(() => code) + return email.sendCustomerMessage(settings, rec).then(() => code) } - function sweepHdRow (row) { + function sweepHdRow(row) { const txId = row.id const cryptoCode = row.crypto_code - return wallet.sweep(settings, txId, cryptoCode, row.hd_index) + return wallet + .sweep(settings, txId, cryptoCode, row.hd_index) .then(txHash => { if (txHash) { logger.debug('[%s] Swept address with tx: %s', cryptoCode, txHash) - const sql = `update cash_out_txs set swept='t' - where id=$1` + const sql = `update cash_out_txs + set swept='t' + where id = $1` return db.none(sql, row.id) } }) - .catch(err => logger.error('[%s] [Session ID: %s] Sweep error: %s', cryptoCode, row.id, err.message)) + .catch(err => + logger.error( + '[%s] [Session ID: %s] Sweep error: %s', + cryptoCode, + row.id, + err.message, + ), + ) } - function sweepHd () { - const sql = `SELECT id, crypto_code, hd_index FROM cash_out_txs - WHERE hd_index IS NOT NULL AND NOT swept AND status IN ('confirmed', 'instant') AND created > now() - interval '1 week'` + function sweepHd() { + const sql = `SELECT id, crypto_code, hd_index + FROM cash_out_txs + WHERE hd_index IS NOT NULL + AND NOT swept + AND status IN ('confirmed', 'instant') + AND created > now() - interval '1 week'` - return db.any(sql) + return db + .any(sql) .then(rows => Promise.all(rows.map(sweepHdRow))) .catch(logger.error) } - function getMachineNames () { + function getMachineNames() { return machineLoader.getMachineNames(settings.config) } - function getRawRates () { + function getRawRates() { const localeConfig = configManager.getGlobalLocale(settings.config) const fiatCode = localeConfig.fiatCurrency @@ -889,24 +1037,23 @@ function plugins (settings, deviceId) { return Promise.all(tickerPromises) } - function getRates () { - return getRawRates() - .then(buildRates) + function getRates() { + return getRawRates().then(buildRates) } - function rateAddress (cryptoCode, address) { + function rateAddress(cryptoCode, address) { return walletScoring.rateAddress(settings, cryptoCode, address) } - function rateTransaction (cryptoCode, address) { + function rateTransaction(cryptoCode, address) { return walletScoring.rateTransaction(settings, cryptoCode, address) } - function isWalletScoringEnabled (tx) { + function isWalletScoringEnabled(tx) { return walletScoring.isWalletScoringEnabled(settings, tx.cryptoCode) } - function probeLN (cryptoCode, address) { + function probeLN(cryptoCode, address) { return wallet.probeLN(settings, cryptoCode, address) } @@ -940,7 +1087,7 @@ function plugins (settings, deviceId) { rateTransaction, isWalletScoringEnabled, probeLN, - buildAvailableUnits + buildAvailableUnits, } } diff --git a/packages/server/lib/plugins/common/ccxt.js b/packages/server/lib/plugins/common/ccxt.js index 247ea7b1..a60eb63a 100644 --- a/packages/server/lib/plugins/common/ccxt.js +++ b/packages/server/lib/plugins/common/ccxt.js @@ -1,51 +1,53 @@ -const { COINS } = require('@lamassu/coins') -const _ = require('lodash/fp') -const { utils: coinUtils } = require('@lamassu/coins') - -const kraken = require('../exchange/kraken') -const bitstamp = require('../exchange/bitstamp') -const itbit = require('../exchange/itbit') -const binanceus = require('../exchange/binanceus') -const cex = require('../exchange/cex') -const bitpay = require('../ticker/bitpay') -const binance = require('../exchange/binance') -const bitfinex = require('../exchange/bitfinex') -const logger = require('../../logger') - -const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT, TRX, USDT_TRON, LN, USDC } = COINS - -const ALL = { - cex: cex, - binanceus: binanceus, - kraken: kraken, - bitstamp: bitstamp, - itbit: itbit, - bitpay: bitpay, - binance: binance, - bitfinex: bitfinex -} - -function buildMarket (fiatCode, cryptoCode, serviceName) { - if (!_.includes(cryptoCode, ALL[serviceName].CRYPTO)) { - throw new Error('Unsupported crypto: ' + cryptoCode) - } - - if (_.isNil(fiatCode)) throw new Error('Market pair building failed: Missing fiat code') - return cryptoCode + '/' + fiatCode -} - -function verifyFiatSupport (fiatCode, serviceName) { - const fiat = ALL[serviceName].FIAT - return fiat === 'ALL_CURRENCIES' ? true : _.includes(fiatCode, fiat) -} - -function isConfigValid (config, fields) { - const values = _.map(it => _.get(it)(config))(fields) - return _.every(it => it || it === 0)(values) -} - -function defaultFiatMarket (serviceName) { - return ALL[serviceName].DEFAULT_FIAT_MARKET -} - -module.exports = { buildMarket, ALL, verifyFiatSupport, isConfigValid, defaultFiatMarket } +const _ = require('lodash/fp') + +const kraken = require('../exchange/kraken') +const bitstamp = require('../exchange/bitstamp') +const itbit = require('../exchange/itbit') +const binanceus = require('../exchange/binanceus') +const cex = require('../exchange/cex') +const bitpay = require('../ticker/bitpay') +const binance = require('../exchange/binance') +const bitfinex = require('../exchange/bitfinex') + +const ALL = { + cex: cex, + binanceus: binanceus, + kraken: kraken, + bitstamp: bitstamp, + itbit: itbit, + bitpay: bitpay, + binance: binance, + bitfinex: bitfinex, +} + +function buildMarket(fiatCode, cryptoCode, serviceName) { + if (!_.includes(cryptoCode, ALL[serviceName].CRYPTO)) { + throw new Error('Unsupported crypto: ' + cryptoCode) + } + + if (_.isNil(fiatCode)) + throw new Error('Market pair building failed: Missing fiat code') + return cryptoCode + '/' + fiatCode +} + +function verifyFiatSupport(fiatCode, serviceName) { + const fiat = ALL[serviceName].FIAT + return fiat === 'ALL_CURRENCIES' ? true : _.includes(fiatCode, fiat) +} + +function isConfigValid(config, fields) { + const values = _.map(it => _.get(it)(config))(fields) + return _.every(it => it || it === 0)(values) +} + +function defaultFiatMarket(serviceName) { + return ALL[serviceName].DEFAULT_FIAT_MARKET +} + +module.exports = { + buildMarket, + ALL, + verifyFiatSupport, + isConfigValid, + defaultFiatMarket, +} diff --git a/packages/server/lib/plugins/common/json-rpc.js b/packages/server/lib/plugins/common/json-rpc.js index 41a5ef37..b0e22361 100644 --- a/packages/server/lib/plugins/common/json-rpc.js +++ b/packages/server/lib/plugins/common/json-rpc.js @@ -7,17 +7,19 @@ const request = require('request-promise') const { utils: coinUtils } = require('@lamassu/coins') const logger = require('../../logger') -const { isRemoteNode, isRemoteWallet } = require('../../environment-helper') +const { isRemoteWallet } = require('../../environment-helper') const { isEnvironmentValid } = require('../../blockchain/install') const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR - module.exports = { - fetch, fetchDigest, parseConf, rpcConfig + fetch, + fetchDigest, + parseConf, + rpcConfig, } -function fetch (account = {}, method, params) { +function fetch(account = {}, method, params) { params = _.defaultTo([], params) return Promise.resolve(true) @@ -25,18 +27,22 @@ function fetch (account = {}, method, params) { const data = { method, params, - id: uuid.v4() + id: uuid.v4(), } - if (_.isNil(account.port)) throw new Error('port attribute required for jsonRpc') + if (_.isNil(account.port)) + throw new Error('port attribute required for jsonRpc') - const url = _.defaultTo(`http://${account.host}:${account.port}`, account.url) + const url = _.defaultTo( + `http://${account.host}:${account.port}`, + account.url, + ) return axios({ method: 'post', - auth: {username: account.username, password: account.password}, + auth: { username: account.username, password: account.password }, url, - data + data, }) }) .then(r => { @@ -44,17 +50,19 @@ function fetch (account = {}, method, params) { return r.data.result }) .catch(err => { - throw new Error(JSON.stringify({ - responseMessage: _.get('message', err), - message: _.get('response.data.error.message', err), - code: _.get('response.data.error.code', err) - })) + throw new Error( + JSON.stringify({ + responseMessage: _.get('message', err), + message: _.get('response.data.error.message', err), + code: _.get('response.data.error.code', err), + }), + ) }) } -function generateDigestOptions (account = {}, method, params) { +function generateDigestOptions(account = {}, method, params) { const headers = { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', } const dataString = `{"jsonrpc":"2.0","id":"${uuid.v4()}","method":"${method}","params":${JSON.stringify(params)}}` @@ -68,31 +76,30 @@ function generateDigestOptions (account = {}, method, params) { auth: { user: account.username, pass: account.password, - sendImmediately: false - } + sendImmediately: false, + }, } return options } function fetchDigest(account = {}, method, params = []) { - return Promise.resolve(true) - .then(() => { - if (_.isNil(account.port)) - throw new Error('port attribute required for jsonRpc') + return Promise.resolve(true).then(() => { + if (_.isNil(account.port)) + throw new Error('port attribute required for jsonRpc') - const options = generateDigestOptions(account, method, params) - return request(options) - }) + const options = generateDigestOptions(account, method, params) + return request(options) + }) } -function split (str) { +function split(str) { const i = str.indexOf('=') if (i === -1) return [] return [str.slice(0, i), str.slice(i + 1)] } -function parseConf (confPath) { +function parseConf(confPath) { const conf = fs.readFileSync(confPath) const lines = conf.toString().split('\n') @@ -109,34 +116,36 @@ function parseConf (confPath) { return res } -function rpcConfig (cryptoRec) { +function rpcConfig(cryptoRec) { try { if (isRemoteWallet(cryptoRec) && isEnvironmentValid(cryptoRec)) { return { username: process.env[`${cryptoRec.cryptoCode}_NODE_USER`], password: process.env[`${cryptoRec.cryptoCode}_NODE_PASSWORD`], host: process.env[`${cryptoRec.cryptoCode}_NODE_RPC_HOST`], - port: process.env[`${cryptoRec.cryptoCode}_NODE_RPC_PORT`] + port: process.env[`${cryptoRec.cryptoCode}_NODE_RPC_PORT`], } } const configPath = coinUtils.configPath(cryptoRec, BLOCKCHAIN_DIR) const config = parseConf(configPath) - + return { username: config.rpcuser, password: config.rpcpassword, host: 'localhost', - port: config.rpcport || cryptoRec.defaultPort + port: config.rpcport || cryptoRec.defaultPort, } } catch (err) { if (!isEnvironmentValid(cryptoRec)) { - logger.error('Environment is not correctly setup for remote wallet usage!') + logger.error( + `Environment is not correctly setup for remote wallet usage!, ${err}`, + ) } else { logger.error('Wallet is currently not installed!') } return { - port: cryptoRec.defaultPort + port: cryptoRec.defaultPort, } } } diff --git a/packages/server/lib/plugins/compliance/consts.js b/packages/server/lib/plugins/compliance/consts.js index c6291189..3e0cc98d 100644 --- a/packages/server/lib/plugins/compliance/consts.js +++ b/packages/server/lib/plugins/compliance/consts.js @@ -2,5 +2,5 @@ module.exports = { PENDING: 'PENDING', RETRY: 'RETRY', APPROVED: 'APPROVED', - REJECTED: 'REJECTED' -} \ No newline at end of file + REJECTED: 'REJECTED', +} diff --git a/packages/server/lib/plugins/compliance/mock-compliance/mock-compliance.js b/packages/server/lib/plugins/compliance/mock-compliance/mock-compliance.js index 954b8072..c6b4e84a 100644 --- a/packages/server/lib/plugins/compliance/mock-compliance/mock-compliance.js +++ b/packages/server/lib/plugins/compliance/mock-compliance/mock-compliance.js @@ -1,6 +1,6 @@ const uuid = require('uuid') -const {APPROVED} = require('../consts') +const { APPROVED } = require('../consts') const CODE = 'mock-compliance' @@ -8,18 +8,19 @@ const createLink = (settings, userId, level) => { return `this is a mock external link, ${userId}, ${level}` } -const getApplicantStatus = (account, userId) => { +const getApplicantStatus = account => { return Promise.resolve({ service: CODE, status: { - level: account.applicantLevel, answer: APPROVED - } + level: account.applicantLevel, + answer: APPROVED, + }, }) } const createApplicant = () => { return Promise.resolve({ - id: uuid.v4() + id: uuid.v4(), }) } @@ -27,5 +28,5 @@ module.exports = { CODE, createApplicant, getApplicantStatus, - createLink + createLink, } diff --git a/packages/server/lib/plugins/compliance/sumsub/request.js b/packages/server/lib/plugins/compliance/sumsub/request.js index f102f996..518be3b5 100644 --- a/packages/server/lib/plugins/compliance/sumsub/request.js +++ b/packages/server/lib/plugins/compliance/sumsub/request.js @@ -4,7 +4,7 @@ const _ = require('lodash/fp') const FormData = require('form-data') const axiosConfig = { - baseURL: 'https://api.sumsub.com' + baseURL: 'https://api.sumsub.com', } const getSigBuilder = (apiToken, secretKey) => config => { @@ -25,10 +25,13 @@ const getSigBuilder = (apiToken, secretKey) => config => { return config } -const request = ((account, config) => { +const request = (account, config) => { const instance = axios.create(axiosConfig) - instance.interceptors.request.use(getSigBuilder(account.apiToken, account.secretKey), Promise.reject) + instance.interceptors.request.use( + getSigBuilder(account.apiToken, account.secretKey), + Promise.reject, + ) return instance(config) -}) +} module.exports = request diff --git a/packages/server/lib/plugins/compliance/sumsub/sumsub.api.js b/packages/server/lib/plugins/compliance/sumsub/sumsub.api.js index a7d7557a..70ed644e 100644 --- a/packages/server/lib/plugins/compliance/sumsub/sumsub.api.js +++ b/packages/server/lib/plugins/compliance/sumsub/sumsub.api.js @@ -2,7 +2,9 @@ const request = require('./request') const createApplicant = (account, userId, level) => { if (!userId || !level) { - return Promise.reject(`Missing required fields: userId: ${userId}, level: ${level}`) + return Promise.reject( + `Missing required fields: userId: ${userId}, level: ${level}`, + ) } const config = { @@ -10,12 +12,12 @@ const createApplicant = (account, userId, level) => { url: `/resources/applicants?levelName=${level}`, data: { externalUserId: userId, - sourceKey: 'lamassu' + sourceKey: 'lamassu', }, headers: { 'Content-Type': 'application/json', - 'Accept': 'application/json' - } + Accept: 'application/json', + }, } return request(account, config) @@ -23,7 +25,9 @@ const createApplicant = (account, userId, level) => { const createLink = (account, userId, level) => { if (!userId || !level) { - return Promise.reject(`Missing required fields: userId: ${userId}, level: ${level}`) + return Promise.reject( + `Missing required fields: userId: ${userId}, level: ${level}`, + ) } const config = { @@ -31,8 +35,8 @@ const createLink = (account, userId, level) => { url: `/resources/sdkIntegrations/levels/${level}/websdkLink?ttlInSecs=${600}&externalUserId=${userId}`, headers: { 'Content-Type': 'application/json', - 'Accept': 'application/json' - } + Accept: 'application/json', + }, } return request(account, config) @@ -47,9 +51,9 @@ const getApplicantByExternalId = (account, id) => { method: 'GET', url: `/resources/applicants/-;externalUserId=${id}/one`, headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } + Accept: 'application/json', + 'Content-Type': 'application/json', + }, } return request(account, config) @@ -64,9 +68,9 @@ const getApplicantStatus = (account, id) => { method: 'GET', url: `/resources/applicants/${id}/status`, headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } + Accept: 'application/json', + 'Content-Type': 'application/json', + }, } return request(account, config) @@ -81,9 +85,9 @@ const getApplicantById = (account, id) => { method: 'GET', url: `/resources/applicants/${id}/one`, headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - } + Accept: 'application/json', + 'Content-Type': 'application/json', + }, } return request(account, config) @@ -94,5 +98,5 @@ module.exports = { createApplicant, getApplicantByExternalId, getApplicantById, - getApplicantStatus + getApplicantStatus, } diff --git a/packages/server/lib/plugins/compliance/sumsub/sumsub.js b/packages/server/lib/plugins/compliance/sumsub/sumsub.js index b295f7c3..1c459c2e 100644 --- a/packages/server/lib/plugins/compliance/sumsub/sumsub.js +++ b/packages/server/lib/plugins/compliance/sumsub/sumsub.js @@ -6,48 +6,53 @@ const { PENDING, RETRY, APPROVED, REJECTED } = require('../consts') const CODE = 'sumsub' const getApplicantByExternalId = (account, userId) => { - return sumsubApi.getApplicantByExternalId(account, userId) - .then(r => r.data) + return sumsubApi.getApplicantByExternalId(account, userId).then(r => r.data) } const createApplicant = (account, userId, level) => { - return sumsubApi.createApplicant(account, userId, level) + return sumsubApi + .createApplicant(account, userId, level) .then(r => r.data) .catch(err => { - if (err.response.status === 409) return getApplicantByExternalId(account, userId) + if (err.response.status === 409) + return getApplicantByExternalId(account, userId) throw err }) } const createLink = (account, userId, level) => { - return sumsubApi.createLink(account, userId, level) - .then(r => r.data.url) + return sumsubApi.createLink(account, userId, level).then(r => r.data.url) } const getApplicantStatus = (account, userId) => { - return sumsubApi.getApplicantByExternalId(account, userId) - .then(r => { - const levelName = _.get('data.review.levelName', r) - const reviewStatus = _.get('data.review.reviewStatus', r) - const reviewAnswer = _.get('data.review.reviewResult.reviewAnswer', r) - const reviewRejectType = _.get('data.review.reviewResult.reviewRejectType', r) + return sumsubApi.getApplicantByExternalId(account, userId).then(r => { + const levelName = _.get('data.review.levelName', r) + const reviewStatus = _.get('data.review.reviewStatus', r) + const reviewAnswer = _.get('data.review.reviewResult.reviewAnswer', r) + const reviewRejectType = _.get( + 'data.review.reviewResult.reviewRejectType', + r, + ) - // if last review was from a different level, return the current level and RETRY - if (levelName !== account.applicantLevel) return { level: account.applicantLevel, answer: RETRY } + // if last review was from a different level, return the current level and RETRY + if (levelName !== account.applicantLevel) + return { level: account.applicantLevel, answer: RETRY } - let answer = PENDING - if (reviewStatus === 'init') answer = RETRY - if (reviewAnswer === 'GREEN' && reviewStatus === 'completed') answer = APPROVED - if (reviewAnswer === 'RED' && reviewRejectType === 'RETRY') answer = RETRY - if (reviewAnswer === 'RED' && reviewRejectType === 'FINAL') answer = REJECTED + let answer = PENDING + if (reviewStatus === 'init') answer = RETRY + if (reviewAnswer === 'GREEN' && reviewStatus === 'completed') + answer = APPROVED + if (reviewAnswer === 'RED' && reviewRejectType === 'RETRY') answer = RETRY + if (reviewAnswer === 'RED' && reviewRejectType === 'FINAL') + answer = REJECTED - return { level: levelName, answer } - }) + return { level: levelName, answer } + }) } module.exports = { CODE, createApplicant, getApplicantStatus, - createLink -} \ No newline at end of file + createLink, +} diff --git a/packages/server/lib/plugins/email/mailgun/mailgun.js b/packages/server/lib/plugins/email/mailgun/mailgun.js index 2bd32151..98bf2c41 100644 --- a/packages/server/lib/plugins/email/mailgun/mailgun.js +++ b/packages/server/lib/plugins/email/mailgun/mailgun.js @@ -2,29 +2,29 @@ const Mailgun = require('mailgun-js') const NAME = 'Mailgun' -function sendMessage ({apiKey, domain, fromEmail, toEmail}, rec) { - const mailgun = Mailgun({apiKey, domain}) +function sendMessage({ apiKey, domain, fromEmail, toEmail }, rec) { + const mailgun = Mailgun({ apiKey, domain }) const to = rec.email.toEmail ?? toEmail const emailData = { from: `Lamassu Server ${fromEmail}`, to, subject: rec.email.subject, - text: rec.email.body + text: rec.email.body, } return mailgun.messages().send(emailData) } -function sendCustomerMessage ({apiKey, domain, fromEmail}, rec) { - const mailgun = Mailgun({apiKey, domain}) +function sendCustomerMessage({ apiKey, domain, fromEmail }, rec) { + const mailgun = Mailgun({ apiKey, domain }) const to = rec.email.toEmail const emailData = { from: fromEmail, to, subject: rec.email.subject, - text: rec.email.body + text: rec.email.body, } return mailgun.messages().send(emailData) @@ -33,5 +33,5 @@ function sendCustomerMessage ({apiKey, domain, fromEmail}, rec) { module.exports = { NAME, sendMessage, - sendCustomerMessage + sendCustomerMessage, } diff --git a/packages/server/lib/plugins/email/mock-email/mock-email.js b/packages/server/lib/plugins/email/mock-email/mock-email.js index 219e7dea..0f7cdad4 100644 --- a/packages/server/lib/plugins/email/mock-email/mock-email.js +++ b/packages/server/lib/plugins/email/mock-email/mock-email.js @@ -1,6 +1,6 @@ const NAME = 'mock-email' -function sendMessage (settings, rec) { +function sendMessage(settings, rec) { console.log('sending email', rec) } @@ -11,5 +11,5 @@ function sendCustomerMessage(settings, rec) { module.exports = { NAME, sendMessage, - sendCustomerMessage + sendCustomerMessage, } diff --git a/packages/server/lib/plugins/exchange/binance.js b/packages/server/lib/plugins/exchange/binance.js index dc2a4c52..f66f16b3 100644 --- a/packages/server/lib/plugins/exchange/binance.js +++ b/packages/server/lib/plugins/exchange/binance.js @@ -10,12 +10,19 @@ const FIAT = ['EUR'] const DEFAULT_FIAT_MARKET = 'EUR' const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket'] -const loadConfig = (account) => { +const loadConfig = account => { const mapper = { - 'privateKey': 'secret' + privateKey: 'secret', } - const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account) + const mapped = _.mapKeys(key => (mapper[key] ? mapper[key] : key))(account) return { ...mapped, timeout: 3000 } } -module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE } +module.exports = { + loadConfig, + DEFAULT_FIAT_MARKET, + REQUIRED_CONFIG_FIELDS, + CRYPTO, + FIAT, + ORDER_TYPE, +} diff --git a/packages/server/lib/plugins/exchange/binanceus.js b/packages/server/lib/plugins/exchange/binanceus.js index 1c57b239..7699de8e 100644 --- a/packages/server/lib/plugins/exchange/binanceus.js +++ b/packages/server/lib/plugins/exchange/binanceus.js @@ -10,12 +10,19 @@ const FIAT = ['USD'] const DEFAULT_FIAT_MARKET = 'USD' const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket'] -const loadConfig = (account) => { +const loadConfig = account => { const mapper = { - 'privateKey': 'secret' + privateKey: 'secret', } - const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account) + const mapped = _.mapKeys(key => (mapper[key] ? mapper[key] : key))(account) return { ...mapped, timeout: 3000 } } -module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE } +module.exports = { + loadConfig, + DEFAULT_FIAT_MARKET, + REQUIRED_CONFIG_FIELDS, + CRYPTO, + FIAT, + ORDER_TYPE, +} diff --git a/packages/server/lib/plugins/exchange/bitfinex.js b/packages/server/lib/plugins/exchange/bitfinex.js index a8796f7d..2e1865b7 100644 --- a/packages/server/lib/plugins/exchange/bitfinex.js +++ b/packages/server/lib/plugins/exchange/bitfinex.js @@ -11,12 +11,20 @@ const DEFAULT_FIAT_MARKET = 'EUR' const AMOUNT_PRECISION = 8 const REQUIRED_CONFIG_FIELDS = ['key', 'secret'] -const loadConfig = (account) => { +const loadConfig = account => { const mapper = { - 'key': 'apiKey', + key: 'apiKey', } - const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account) + const mapped = _.mapKeys(key => (mapper[key] ? mapper[key] : key))(account) return { ...mapped, timeout: 3000 } } -module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, DEFAULT_FIAT_MARKET, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION } +module.exports = { + loadConfig, + REQUIRED_CONFIG_FIELDS, + DEFAULT_FIAT_MARKET, + CRYPTO, + FIAT, + ORDER_TYPE, + AMOUNT_PRECISION, +} diff --git a/packages/server/lib/plugins/exchange/bitstamp.js b/packages/server/lib/plugins/exchange/bitstamp.js index 959e7619..a0399b71 100644 --- a/packages/server/lib/plugins/exchange/bitstamp.js +++ b/packages/server/lib/plugins/exchange/bitstamp.js @@ -1,23 +1,31 @@ -const { COINS } = require('@lamassu/coins') -const _ = require('lodash/fp') - -const { ORDER_TYPES } = require('./consts') - -const ORDER_TYPE = ORDER_TYPES.MARKET -const { BTC, ETH, LTC, BCH, USDT, LN, USDC } = COINS -const CRYPTO = [BTC, ETH, LTC, BCH, USDT, LN, USDC] -const FIAT = ['USD', 'EUR'] -const DEFAULT_FIAT_MARKET = 'EUR' -const AMOUNT_PRECISION = 8 -const REQUIRED_CONFIG_FIELDS = ['key', 'secret', 'clientId', 'currencyMarket'] - -const loadConfig = (account) => { - const mapper = { - 'key': 'apiKey', - 'clientId': 'uid' - } - const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account) - return { ...mapped, timeout: 3000 } -} - -module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION } +const { COINS } = require('@lamassu/coins') +const _ = require('lodash/fp') + +const { ORDER_TYPES } = require('./consts') + +const ORDER_TYPE = ORDER_TYPES.MARKET +const { BTC, ETH, LTC, BCH, USDT, LN, USDC } = COINS +const CRYPTO = [BTC, ETH, LTC, BCH, USDT, LN, USDC] +const FIAT = ['USD', 'EUR'] +const DEFAULT_FIAT_MARKET = 'EUR' +const AMOUNT_PRECISION = 8 +const REQUIRED_CONFIG_FIELDS = ['key', 'secret', 'clientId', 'currencyMarket'] + +const loadConfig = account => { + const mapper = { + key: 'apiKey', + clientId: 'uid', + } + const mapped = _.mapKeys(key => (mapper[key] ? mapper[key] : key))(account) + return { ...mapped, timeout: 3000 } +} + +module.exports = { + loadConfig, + DEFAULT_FIAT_MARKET, + REQUIRED_CONFIG_FIELDS, + CRYPTO, + FIAT, + ORDER_TYPE, + AMOUNT_PRECISION, +} diff --git a/packages/server/lib/plugins/exchange/ccxt.js b/packages/server/lib/plugins/exchange/ccxt.js index a69021ba..ab98e22f 100644 --- a/packages/server/lib/plugins/exchange/ccxt.js +++ b/packages/server/lib/plugins/exchange/ccxt.js @@ -1,92 +1,129 @@ -const { utils: coinUtils } = require('@lamassu/coins') -const _ = require('lodash/fp') -const ccxt = require('ccxt') -const mem = require('mem') - -const { buildMarket, ALL, isConfigValid } = require('../common/ccxt') -const { ORDER_TYPES } = require('./consts') -const logger = require('../../logger') -const { currencies } = require('../../new-admin/config') -const T = require('../../time') - -const DEFAULT_PRICE_PRECISION = 2 -const DEFAULT_AMOUNT_PRECISION = 8 - -function trade (side, account, tradeEntry, exchangeName) { - const { cryptoAtoms, fiatCode, cryptoCode: _cryptoCode, tradeId } = tradeEntry - try { - const cryptoCode = coinUtils.getEquivalentCode(_cryptoCode) - const exchangeConfig = ALL[exchangeName] - if (!exchangeConfig) throw Error('Exchange configuration not found') - - const { USER_REF, loadOptions, loadConfig = _.noop, REQUIRED_CONFIG_FIELDS, ORDER_TYPE, AMOUNT_PRECISION } = exchangeConfig - if (!isConfigValid(account, REQUIRED_CONFIG_FIELDS)) throw Error('Invalid config') - - const selectedFiatMarket = account.currencyMarket - const symbol = buildMarket(selectedFiatMarket, cryptoCode, exchangeName) - const precision = _.defaultTo(DEFAULT_AMOUNT_PRECISION, AMOUNT_PRECISION) - const amount = coinUtils.toUnit(cryptoAtoms, cryptoCode).toFixed(precision) - const accountOptions = _.isFunction(loadOptions) ? loadOptions(account) : {} - const withCustomKey = USER_REF ? { [USER_REF]: tradeId } : {} - const options = _.assign(accountOptions, withCustomKey) - const exchange = new ccxt[exchangeName](loadConfig(account)) - - if (ORDER_TYPE === ORDER_TYPES.MARKET) { - return exchange.createOrder(symbol, ORDER_TYPES.MARKET, side, amount, null, options) - } - - return exchange.fetchOrderBook(symbol) - .then(orderBook => { - const price = calculatePrice(side, amount, orderBook).toFixed(DEFAULT_PRICE_PRECISION) - return exchange.createOrder(symbol, ORDER_TYPES.LIMIT, side, amount, price, options) - }) - } catch (e) { - return Promise.reject(e) - } -} - -function calculatePrice (side, amount, orderBook) { - const book = side === 'buy' ? 'asks' : 'bids' - let collected = 0.0 - for (const entry of orderBook[book]) { - collected += parseFloat(entry[1]) - if (collected >= amount) return parseFloat(entry[0]) - } - throw new Error('Insufficient market depth') -} - -function _getMarkets (exchangeName, availableCryptos) { - const prunedCryptos = _.compose(_.uniq, _.map(coinUtils.getEquivalentCode))(availableCryptos) - - try { - const exchange = new ccxt[exchangeName]() - const cryptosToQuoteAgainst = ['USDT'] - const currencyCodes = _.concat(_.map(it => it.code, currencies), cryptosToQuoteAgainst) - - return exchange.fetchMarkets() - .then(_.filter(it => (it.type === 'spot' || it.spot))) - .then(res => - _.reduce((acc, value) => { - if (_.includes(value.base, prunedCryptos) && _.includes(value.quote, currencyCodes)) { - if (value.quote === value.base) return acc - - if (_.isNil(acc[value.quote])) { - return { ...acc, [value.quote]: [value.base] } - } - - acc[value.quote].push(value.base) - } - return acc - }, {}, res) - ) - } catch (e) { - logger.debug(`No CCXT exchange found for ${exchangeName}`) - } -} - -const getMarkets = mem(_getMarkets, { - maxAge: T.week, - cacheKey: (exchangeName, availableCryptos) => exchangeName -}) - -module.exports = { trade, getMarkets } +const { utils: coinUtils } = require('@lamassu/coins') +const _ = require('lodash/fp') +const ccxt = require('ccxt') +const mem = require('mem') + +const { buildMarket, ALL, isConfigValid } = require('../common/ccxt') +const { ORDER_TYPES } = require('./consts') +const logger = require('../../logger') +const { currencies } = require('../../new-admin/config') +const T = require('../../time') + +const DEFAULT_PRICE_PRECISION = 2 +const DEFAULT_AMOUNT_PRECISION = 8 + +function trade(side, account, tradeEntry, exchangeName) { + const { cryptoAtoms, cryptoCode: _cryptoCode, tradeId } = tradeEntry + try { + const cryptoCode = coinUtils.getEquivalentCode(_cryptoCode) + const exchangeConfig = ALL[exchangeName] + if (!exchangeConfig) throw Error('Exchange configuration not found') + + const { + USER_REF, + loadOptions, + loadConfig = _.noop, + REQUIRED_CONFIG_FIELDS, + ORDER_TYPE, + AMOUNT_PRECISION, + } = exchangeConfig + if (!isConfigValid(account, REQUIRED_CONFIG_FIELDS)) + throw Error('Invalid config') + + const selectedFiatMarket = account.currencyMarket + const symbol = buildMarket(selectedFiatMarket, cryptoCode, exchangeName) + const precision = _.defaultTo(DEFAULT_AMOUNT_PRECISION, AMOUNT_PRECISION) + const amount = coinUtils.toUnit(cryptoAtoms, cryptoCode).toFixed(precision) + const accountOptions = _.isFunction(loadOptions) ? loadOptions(account) : {} + const withCustomKey = USER_REF ? { [USER_REF]: tradeId } : {} + const options = _.assign(accountOptions, withCustomKey) + const exchange = new ccxt[exchangeName](loadConfig(account)) + + if (ORDER_TYPE === ORDER_TYPES.MARKET) { + return exchange.createOrder( + symbol, + ORDER_TYPES.MARKET, + side, + amount, + null, + options, + ) + } + + return exchange.fetchOrderBook(symbol).then(orderBook => { + const price = calculatePrice(side, amount, orderBook).toFixed( + DEFAULT_PRICE_PRECISION, + ) + return exchange.createOrder( + symbol, + ORDER_TYPES.LIMIT, + side, + amount, + price, + options, + ) + }) + } catch (e) { + return Promise.reject(e) + } +} + +function calculatePrice(side, amount, orderBook) { + const book = side === 'buy' ? 'asks' : 'bids' + let collected = 0.0 + for (const entry of orderBook[book]) { + collected += parseFloat(entry[1]) + if (collected >= amount) return parseFloat(entry[0]) + } + throw new Error('Insufficient market depth') +} + +function _getMarkets(exchangeName, availableCryptos) { + const prunedCryptos = _.compose( + _.uniq, + _.map(coinUtils.getEquivalentCode), + )(availableCryptos) + + try { + const exchange = new ccxt[exchangeName]() + const cryptosToQuoteAgainst = ['USDT'] + const currencyCodes = _.concat( + _.map(it => it.code, currencies), + cryptosToQuoteAgainst, + ) + + return exchange + .fetchMarkets() + .then(_.filter(it => it.type === 'spot' || it.spot)) + .then(res => + _.reduce( + (acc, value) => { + if ( + _.includes(value.base, prunedCryptos) && + _.includes(value.quote, currencyCodes) + ) { + if (value.quote === value.base) return acc + + if (_.isNil(acc[value.quote])) { + return { ...acc, [value.quote]: [value.base] } + } + + acc[value.quote].push(value.base) + } + return acc + }, + {}, + res, + ), + ) + } catch (e) { + logger.debug(`No CCXT exchange found for ${exchangeName}. ${e}`) + } +} + +const getMarkets = mem(_getMarkets, { + maxAge: T.week, + cacheKey: exchangeName => exchangeName, +}) + +module.exports = { trade, getMarkets } diff --git a/packages/server/lib/plugins/exchange/cex.js b/packages/server/lib/plugins/exchange/cex.js index b9687e15..18e0b932 100644 --- a/packages/server/lib/plugins/exchange/cex.js +++ b/packages/server/lib/plugins/exchange/cex.js @@ -10,12 +10,19 @@ const FIAT = ['USD', 'EUR'] const DEFAULT_FIAT_MARKET = 'EUR' const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket'] -const loadConfig = (account) => { +const loadConfig = account => { const mapper = { - 'privateKey': 'secret' + privateKey: 'secret', } - const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account) + const mapped = _.mapKeys(key => (mapper[key] ? mapper[key] : key))(account) return { ...mapped, timeout: 3000 } } -module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE } +module.exports = { + loadConfig, + DEFAULT_FIAT_MARKET, + REQUIRED_CONFIG_FIELDS, + CRYPTO, + FIAT, + ORDER_TYPE, +} diff --git a/packages/server/lib/plugins/exchange/consts.js b/packages/server/lib/plugins/exchange/consts.js index 690b75c9..5d0e7914 100644 --- a/packages/server/lib/plugins/exchange/consts.js +++ b/packages/server/lib/plugins/exchange/consts.js @@ -1,7 +1,6 @@ - -const ORDER_TYPES = { - MARKET: 'market', - LIMIT: 'limit' -} - -module.exports = { ORDER_TYPES } +const ORDER_TYPES = { + MARKET: 'market', + LIMIT: 'limit', +} + +module.exports = { ORDER_TYPES } diff --git a/packages/server/lib/plugins/exchange/itbit.js b/packages/server/lib/plugins/exchange/itbit.js index d80268e1..2dfbc597 100644 --- a/packages/server/lib/plugins/exchange/itbit.js +++ b/packages/server/lib/plugins/exchange/itbit.js @@ -1,25 +1,42 @@ -const _ = require('lodash/fp') - -const { ORDER_TYPES } = require('./consts') -const { COINS } = require('@lamassu/coins') - -const ORDER_TYPE = ORDER_TYPES.LIMIT -const { BTC, ETH, USDT, LN } = COINS -const CRYPTO = [BTC, ETH, USDT, LN] -const FIAT = ['USD'] -const DEFAULT_FIAT_MARKET = 'USD' -const AMOUNT_PRECISION = 4 -const REQUIRED_CONFIG_FIELDS = ['clientKey', 'clientSecret', 'userId', 'walletId', 'currencyMarket'] - -const loadConfig = (account) => { - const mapper = { - 'clientKey': 'apiKey', - 'clientSecret': 'secret', - 'userId': 'uid' - } - const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(_.omit(['walletId'], account)) - return { ...mapped, timeout: 3000 } -} -const loadOptions = ({ walletId }) => ({ walletId }) - -module.exports = { loadOptions, loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION } +const _ = require('lodash/fp') + +const { ORDER_TYPES } = require('./consts') +const { COINS } = require('@lamassu/coins') + +const ORDER_TYPE = ORDER_TYPES.LIMIT +const { BTC, ETH, USDT, LN } = COINS +const CRYPTO = [BTC, ETH, USDT, LN] +const FIAT = ['USD'] +const DEFAULT_FIAT_MARKET = 'USD' +const AMOUNT_PRECISION = 4 +const REQUIRED_CONFIG_FIELDS = [ + 'clientKey', + 'clientSecret', + 'userId', + 'walletId', + 'currencyMarket', +] + +const loadConfig = account => { + const mapper = { + clientKey: 'apiKey', + clientSecret: 'secret', + userId: 'uid', + } + const mapped = _.mapKeys(key => (mapper[key] ? mapper[key] : key))( + _.omit(['walletId'], account), + ) + return { ...mapped, timeout: 3000 } +} +const loadOptions = ({ walletId }) => ({ walletId }) + +module.exports = { + loadOptions, + loadConfig, + DEFAULT_FIAT_MARKET, + REQUIRED_CONFIG_FIELDS, + CRYPTO, + FIAT, + ORDER_TYPE, + AMOUNT_PRECISION, +} diff --git a/packages/server/lib/plugins/exchange/kraken.js b/packages/server/lib/plugins/exchange/kraken.js index 29e80630..30d49c40 100644 --- a/packages/server/lib/plugins/exchange/kraken.js +++ b/packages/server/lib/plugins/exchange/kraken.js @@ -1,30 +1,56 @@ -const _ = require('lodash/fp') - -const { ORDER_TYPES } = require('./consts') -const { COINS } = require('@lamassu/coins') - -const ORDER_TYPE = ORDER_TYPES.MARKET -const { BTC, BCH, DASH, ETH, LTC, ZEC, XMR, USDT, TRX, USDT_TRON, LN, USDC } = COINS -const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, XMR, USDT, TRX, USDT_TRON, LN, USDC] -const FIAT = ['USD', 'EUR'] -const DEFAULT_FIAT_MARKET = 'EUR' -const AMOUNT_PRECISION = 6 -const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket'] -const USER_REF = 'userref' - -const loadConfig = (account) => { - const mapper = { - 'privateKey': 'secret' - } - const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account) - - return { - ...mapped, - timeout: 3000, - nonce: function () { return this.microseconds() } - } -} - -const loadOptions = () => ({ expiretm: '+60' }) - -module.exports = { USER_REF, loadOptions, loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION } +const _ = require('lodash/fp') + +const { ORDER_TYPES } = require('./consts') +const { COINS } = require('@lamassu/coins') + +const ORDER_TYPE = ORDER_TYPES.MARKET +const { BTC, BCH, DASH, ETH, LTC, ZEC, XMR, USDT, TRX, USDT_TRON, LN, USDC } = + COINS +const CRYPTO = [ + BTC, + ETH, + LTC, + DASH, + ZEC, + BCH, + XMR, + USDT, + TRX, + USDT_TRON, + LN, + USDC, +] +const FIAT = ['USD', 'EUR'] +const DEFAULT_FIAT_MARKET = 'EUR' +const AMOUNT_PRECISION = 6 +const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket'] +const USER_REF = 'userref' + +const loadConfig = account => { + const mapper = { + privateKey: 'secret', + } + const mapped = _.mapKeys(key => (mapper[key] ? mapper[key] : key))(account) + + return { + ...mapped, + timeout: 3000, + nonce: function () { + return this.microseconds() + }, + } +} + +const loadOptions = () => ({ expiretm: '+60' }) + +module.exports = { + USER_REF, + loadOptions, + loadConfig, + DEFAULT_FIAT_MARKET, + REQUIRED_CONFIG_FIELDS, + CRYPTO, + FIAT, + ORDER_TYPE, + AMOUNT_PRECISION, +} diff --git a/packages/server/lib/plugins/exchange/mock-exchange.js b/packages/server/lib/plugins/exchange/mock-exchange.js index bdf287da..aaf8ee45 100644 --- a/packages/server/lib/plugins/exchange/mock-exchange.js +++ b/packages/server/lib/plugins/exchange/mock-exchange.js @@ -1,14 +1,24 @@ module.exports = { buy, - sell + sell, } -function buy (cryptoAtoms, fiatCode, cryptoCode) { - console.log('[mock] buying %s %s for %s', cryptoAtoms.toString(), cryptoCode, fiatCode) +function buy(cryptoAtoms, fiatCode, cryptoCode) { + console.log( + '[mock] buying %s %s for %s', + cryptoAtoms.toString(), + cryptoCode, + fiatCode, + ) return Promise.resolve() } -function sell (cryptoAtoms, fiatCode, cryptoCode) { - console.log('[mock] selling %s %s for %s', cryptoAtoms.toString(), cryptoCode, fiatCode) +function sell(cryptoAtoms, fiatCode, cryptoCode) { + console.log( + '[mock] selling %s %s for %s', + cryptoAtoms.toString(), + cryptoCode, + fiatCode, + ) return Promise.resolve() } diff --git a/packages/server/lib/plugins/sms/inforu/inforu.js b/packages/server/lib/plugins/sms/inforu/inforu.js index 1453ca7f..754ada2a 100644 --- a/packages/server/lib/plugins/sms/inforu/inforu.js +++ b/packages/server/lib/plugins/sms/inforu/inforu.js @@ -1,46 +1,47 @@ -const axios = require('axios') - -const NAME = 'InforU' - -function sendMessage (account, rec) { - const username = account.username - const apiKey = account.apiKey - - const to = rec.sms.toNumber || account.toNumber - const text = rec.sms.body - const from = account.fromNumber - - const url = 'https://capi.inforu.co.il/api/v2/SMS/SendSms' - - const config = { - auth: { - username: username, - password: apiKey - }, - maxBodyLength: Infinity, - headers:{ - 'Content-Type': 'application/json' - } - } - - const data = { - Message: text, - Recipients: [{ - Phone: to - }], - Settings: { - Sender: from - } - } - - axios.post(url, data, config) - .catch(err => { - // console.log(err) - throw new Error(`inforu error: ${err.message}`) - }) -} - -module.exports = { - NAME, - sendMessage -} +const axios = require('axios') + +const NAME = 'InforU' + +function sendMessage(account, rec) { + const username = account.username + const apiKey = account.apiKey + + const to = rec.sms.toNumber || account.toNumber + const text = rec.sms.body + const from = account.fromNumber + + const url = 'https://capi.inforu.co.il/api/v2/SMS/SendSms' + + const config = { + auth: { + username: username, + password: apiKey, + }, + maxBodyLength: Infinity, + headers: { + 'Content-Type': 'application/json', + }, + } + + const data = { + Message: text, + Recipients: [ + { + Phone: to, + }, + ], + Settings: { + Sender: from, + }, + } + + axios.post(url, data, config).catch(err => { + // console.log(err) + throw new Error(`inforu error: ${err.message}`) + }) +} + +module.exports = { + NAME, + sendMessage, +} diff --git a/packages/server/lib/plugins/sms/mock-sms/mock-sms.js b/packages/server/lib/plugins/sms/mock-sms/mock-sms.js index 1bd021b3..fd631200 100644 --- a/packages/server/lib/plugins/sms/mock-sms/mock-sms.js +++ b/packages/server/lib/plugins/sms/mock-sms/mock-sms.js @@ -2,7 +2,7 @@ const _ = require('lodash/fp') const NAME = 'MockSMS' -function sendMessage (account, rec) { +function sendMessage(account, rec) { console.log('Sending SMS: %j', rec) return new Promise((resolve, reject) => { if (_.endsWith('666', _.getOr(false, 'sms.toNumber', rec))) { @@ -15,5 +15,5 @@ function sendMessage (account, rec) { module.exports = { NAME, - sendMessage + sendMessage, } diff --git a/packages/server/lib/plugins/sms/telnyx/telnyx.js b/packages/server/lib/plugins/sms/telnyx/telnyx.js index 74c3bcd0..e8d57428 100644 --- a/packages/server/lib/plugins/sms/telnyx/telnyx.js +++ b/packages/server/lib/plugins/sms/telnyx/telnyx.js @@ -2,20 +2,19 @@ const Telnyx = require('telnyx') const NAME = 'Telnyx' -function sendMessage (account, rec) { +function sendMessage(account, rec) { const telnyx = Telnyx(account.apiKey) const from = account.fromNumber const text = rec.sms.body const to = rec.sms.toNumber || account.toNumber - return telnyx.messages.create({ from, to, text }) - .catch(err => { - throw new Error(`Telnyx error: ${err.message}`) - }) + return telnyx.messages.create({ from, to, text }).catch(err => { + throw new Error(`Telnyx error: ${err.message}`) + }) } module.exports = { NAME, - sendMessage + sendMessage, } diff --git a/packages/server/lib/plugins/sms/twilio/twilio.js b/packages/server/lib/plugins/sms/twilio/twilio.js index cedd6952..1ba462a6 100644 --- a/packages/server/lib/plugins/sms/twilio/twilio.js +++ b/packages/server/lib/plugins/sms/twilio/twilio.js @@ -3,10 +3,12 @@ const _ = require('lodash/fp') const NAME = 'Twilio' -const BAD_NUMBER_CODES = [21201, 21202, 21211, 21214, 21216, 21217, 21219, 21408, - 21610, 21612, 21614, 21608] +const BAD_NUMBER_CODES = [ + 21201, 21202, 21211, 21214, 21216, 21217, 21219, 21408, 21610, 21612, 21614, + 21608, +] -function sendMessage (account, rec) { +function sendMessage(account, rec) { return Promise.resolve() .then(() => { // to catch configuration errors like @@ -14,14 +16,16 @@ function sendMessage (account, rec) { const client = twilio(account.accountSid, account.authToken) const body = rec.sms.body const _toNumber = rec.sms.toNumber || account.toNumber - const from = (_.startsWith('+')(account.fromNumber) - || !_.isNumber(String(account.fromNumber).replace(/\s/g,''))) - ? account.fromNumber : `+${account.fromNumber}` + const from = + _.startsWith('+')(account.fromNumber) || + !_.isNumber(String(account.fromNumber).replace(/\s/g, '')) + ? account.fromNumber + : `+${account.fromNumber}` const opts = { body: body, to: _toNumber, - from + from, } return client.messages.create(opts) @@ -39,5 +43,5 @@ function sendMessage (account, rec) { module.exports = { NAME, - sendMessage + sendMessage, } diff --git a/packages/server/lib/plugins/sms/vonage/vonage.js b/packages/server/lib/plugins/sms/vonage/vonage.js index 2582f695..510ae716 100644 --- a/packages/server/lib/plugins/sms/vonage/vonage.js +++ b/packages/server/lib/plugins/sms/vonage/vonage.js @@ -3,10 +3,10 @@ const { SMS } = require('@vonage/sms') const NAME = 'Vonage' -function sendMessage (account, rec) { +function sendMessage(account, rec) { const credentials = new Auth({ apiKey: account.apiKey, - apiSecret: account.apiSecret + apiSecret: account.apiSecret, }) const from = account.fromNumber @@ -14,10 +14,9 @@ function sendMessage (account, rec) { const to = rec.sms.toNumber || account.toNumber const smsClient = new SMS(credentials) - smsClient.send({ from, text, to }) - .catch(err => { - throw new Error(`Vonage error: ${err.message}`) - }) + smsClient.send({ from, text, to }).catch(err => { + throw new Error(`Vonage error: ${err.message}`) + }) } module.exports = { diff --git a/packages/server/lib/plugins/sms/whatsapp/whatsapp.js b/packages/server/lib/plugins/sms/whatsapp/whatsapp.js index c2454665..2ded3d3f 100644 --- a/packages/server/lib/plugins/sms/whatsapp/whatsapp.js +++ b/packages/server/lib/plugins/sms/whatsapp/whatsapp.js @@ -1,42 +1,41 @@ -const axios = require('axios') - -const NAME = 'Whatsapp' - -function sendMessage (account, rec) { - const phoneId = account.phoneId - const token = account.apiKey - - const to = rec.sms.toNumber || account.toNumber - const template = rec.sms.template - - const url = `https://graph.facebook.com/v17.0/${phoneId}/messages` - - const config = { - headers:{ - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json' - } - } - - const data = { - messaging_product: 'whatsapp', - recipient_type: 'individual', - type: 'template', - to, - template: { - name: template, - language: { code: 'en_US' } - } - } - - axios.post(url, data, config) - .catch(err => { - // console.log(err) - throw new Error(`Whatsapp error: ${err.message}`) - }) -} - -module.exports = { - NAME, - sendMessage -} +const axios = require('axios') + +const NAME = 'Whatsapp' + +function sendMessage(account, rec) { + const phoneId = account.phoneId + const token = account.apiKey + + const to = rec.sms.toNumber || account.toNumber + const template = rec.sms.template + + const url = `https://graph.facebook.com/v17.0/${phoneId}/messages` + + const config = { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + } + + const data = { + messaging_product: 'whatsapp', + recipient_type: 'individual', + type: 'template', + to, + template: { + name: template, + language: { code: 'en_US' }, + }, + } + + axios.post(url, data, config).catch(err => { + // console.log(err) + throw new Error(`Whatsapp error: ${err.message}`) + }) +} + +module.exports = { + NAME, + sendMessage, +} diff --git a/packages/server/lib/plugins/ticker/bitpay.js b/packages/server/lib/plugins/ticker/bitpay.js index 0952ab40..dbfe6fd2 100644 --- a/packages/server/lib/plugins/ticker/bitpay.js +++ b/packages/server/lib/plugins/ticker/bitpay.js @@ -7,16 +7,17 @@ const { BTC, BCH, LN } = COINS const CRYPTO = [BTC, BCH, LN] const FIAT = 'ALL_CURRENCIES' -function ticker (fiatCode, cryptoCode) { - return axios.get('https://bitpay.com/rates/' + cryptoCode + '/' + fiatCode) +function ticker(fiatCode, cryptoCode) { + return axios + .get('https://bitpay.com/rates/' + cryptoCode + '/' + fiatCode) .then(r => { const data = r.data.data const price = new BN(data.rate.toString()) return { rates: { ask: price, - bid: price - } + bid: price, + }, } }) } @@ -25,5 +26,5 @@ module.exports = { ticker, name: 'BitPay', CRYPTO, - FIAT + FIAT, } diff --git a/packages/server/lib/plugins/ticker/ccxt.js b/packages/server/lib/plugins/ticker/ccxt.js index 881085d1..7316d61d 100644 --- a/packages/server/lib/plugins/ticker/ccxt.js +++ b/packages/server/lib/plugins/ticker/ccxt.js @@ -1,7 +1,11 @@ const ccxt = require('ccxt') const BN = require('../../bn') -const { buildMarket, verifyFiatSupport, defaultFiatMarket } = require('../common/ccxt') +const { + buildMarket, + verifyFiatSupport, + defaultFiatMarket, +} = require('../common/ccxt') const { getRate } = require('../../../lib/forex') const RETRIES = 2 @@ -16,7 +20,7 @@ const sanityCheckRates = (ask, bid, tickerName) => { } } -function ticker (fiatCode, cryptoCode, tickerName) { +function ticker(fiatCode, cryptoCode, tickerName) { if (!tickerObjects[tickerName]) { tickerObjects[tickerName] = new ccxt[tickerName]({ timeout: 3000, @@ -30,37 +34,40 @@ function ticker (fiatCode, cryptoCode, tickerName) { return getCurrencyRates(ticker, fiatCode, cryptoCode) } - return getRate(RETRIES, tickerName, defaultFiatMarket(tickerName)) - .then(({ fxRate }) => { + return getRate(RETRIES, tickerName, defaultFiatMarket(tickerName)).then( + ({ fxRate }) => { try { - return getCurrencyRates(ticker, defaultFiatMarket(tickerName), cryptoCode) - .then(res => ({ - rates: { - ask: res.rates.ask.times(fxRate), - bid: res.rates.bid.times(fxRate) - } - })) + return getCurrencyRates( + ticker, + defaultFiatMarket(tickerName), + cryptoCode, + ).then(res => ({ + rates: { + ask: res.rates.ask.times(fxRate), + bid: res.rates.bid.times(fxRate), + }, + })) } catch (e) { return Promise.reject(e) } - }) + }, + ) } -function getCurrencyRates (ticker, fiatCode, cryptoCode) { +function getCurrencyRates(ticker, fiatCode, cryptoCode) { try { if (!ticker.has['fetchTicker']) { throw new Error('Ticker not available') } const symbol = buildMarket(fiatCode, cryptoCode, ticker.id) - return ticker.fetchTicker(symbol) - .then(res => { - sanityCheckRates(res.ask, res.bid, cryptoCode) - return { - rates: { - ask: new BN(res.ask), - bid: new BN(res.bid) - } - } + return ticker.fetchTicker(symbol).then(res => { + sanityCheckRates(res.ask, res.bid, cryptoCode) + return { + rates: { + ask: new BN(res.ask), + bid: new BN(res.bid), + }, + } }) } catch (e) { return Promise.reject(e) diff --git a/packages/server/lib/plugins/ticker/mock-ticker.js b/packages/server/lib/plugins/ticker/mock-ticker.js index 6070e0c0..df038519 100644 --- a/packages/server/lib/plugins/ticker/mock-ticker.js +++ b/packages/server/lib/plugins/ticker/mock-ticker.js @@ -1,12 +1,12 @@ const BN = require('../../bn') -function ticker (fiatCode, cryptoCode) { +function ticker() { return Promise.resolve({ rates: { ask: new BN(105), - bid: new BN(100) - } + bid: new BN(100), + }, }) } -module.exports = {ticker} +module.exports = { ticker } diff --git a/packages/server/lib/plugins/ticker/pazuz-ticker/pazuz-ticker.js b/packages/server/lib/plugins/ticker/pazuz-ticker/pazuz-ticker.js index 34daf691..691b19ec 100644 --- a/packages/server/lib/plugins/ticker/pazuz-ticker/pazuz-ticker.js +++ b/packages/server/lib/plugins/ticker/pazuz-ticker/pazuz-ticker.js @@ -3,26 +3,25 @@ const axios = require('axios').create({ // TODO: get rejectUnauthorized true to work baseURL: `${process.env.TICKER_URL}/api/rates/`, httpsAgent: new https.Agent({ - rejectUnauthorized: false - }) + rejectUnauthorized: false, + }), }) const BN = require('../../../bn') -function ticker (account, fiatCode, cryptoCode) { - return axios.get(`${cryptoCode}/${fiatCode}`) - .then(({ data }) => { - if (data.error) throw new Error(JSON.stringify(data.error)) - return { - rates: { - ask: BN(data.ask), - bid: BN(data.bid), - signature: data.signature - } - } - }) +function ticker(account, fiatCode, cryptoCode) { + return axios.get(`${cryptoCode}/${fiatCode}`).then(({ data }) => { + if (data.error) throw new Error(JSON.stringify(data.error)) + return { + rates: { + ask: BN(data.ask), + bid: BN(data.bid), + signature: data.signature, + }, + } + }) } module.exports = { - ticker + ticker, } diff --git a/packages/server/lib/plugins/tokens/erc20.abi.json b/packages/server/lib/plugins/tokens/erc20.abi.json index b66cb33f..db288ccd 100644 --- a/packages/server/lib/plugins/tokens/erc20.abi.json +++ b/packages/server/lib/plugins/tokens/erc20.abi.json @@ -1,282 +1,272 @@ [ { - "constant":true, - "inputs":[ - - ], - "name":"name", - "outputs":[ + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ { - "name":"", - "type":"string" + "name": "", + "type": "string" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":false, - "inputs":[ + "constant": false, + "inputs": [ { - "name":"_spender", - "type":"address" + "name": "_spender", + "type": "address" }, { - "name":"_value", - "type":"uint256" + "name": "_value", + "type": "uint256" } ], - "name":"approve", - "outputs":[ + "name": "approve", + "outputs": [ { - "name":"success", - "type":"bool" + "name": "success", + "type": "bool" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":true, - "inputs":[ - - ], - "name":"totalSupply", - "outputs":[ + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ { - "name":"", - "type":"uint256" + "name": "", + "type": "uint256" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":false, - "inputs":[ + "constant": false, + "inputs": [ { - "name":"_from", - "type":"address" + "name": "_from", + "type": "address" }, { - "name":"_to", - "type":"address" + "name": "_to", + "type": "address" }, { - "name":"_value", - "type":"uint256" + "name": "_value", + "type": "uint256" } ], - "name":"transferFrom", - "outputs":[ + "name": "transferFrom", + "outputs": [ { - "name":"success", - "type":"bool" + "name": "success", + "type": "bool" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":true, - "inputs":[ - - ], - "name":"decimals", - "outputs":[ + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ { - "name":"", - "type":"uint256" + "name": "", + "type": "uint256" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":true, - "inputs":[ - - ], - "name":"version", - "outputs":[ + "constant": true, + "inputs": [], + "name": "version", + "outputs": [ { - "name":"", - "type":"string" + "name": "", + "type": "string" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":true, - "inputs":[ + "constant": true, + "inputs": [ { - "name":"_owner", - "type":"address" + "name": "_owner", + "type": "address" } ], - "name":"balanceOf", - "outputs":[ + "name": "balanceOf", + "outputs": [ { - "name":"balance", - "type":"uint256" + "name": "balance", + "type": "uint256" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":true, - "inputs":[ - - ], - "name":"symbol", - "outputs":[ + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ { - "name":"", - "type":"string" + "name": "", + "type": "string" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":false, - "inputs":[ + "constant": false, + "inputs": [ { - "name":"_to", - "type":"address" + "name": "_to", + "type": "address" }, { - "name":"_value", - "type":"uint256" + "name": "_value", + "type": "uint256" } ], - "name":"transfer", - "outputs":[ + "name": "transfer", + "outputs": [ { - "name":"success", - "type":"bool" + "name": "success", + "type": "bool" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":false, - "inputs":[ + "constant": false, + "inputs": [ { - "name":"_spender", - "type":"address" + "name": "_spender", + "type": "address" }, { - "name":"_value", - "type":"uint256" + "name": "_value", + "type": "uint256" }, { - "name":"_extraData", - "type":"bytes" + "name": "_extraData", + "type": "bytes" } ], - "name":"approveAndCall", - "outputs":[ + "name": "approveAndCall", + "outputs": [ { - "name":"success", - "type":"bool" + "name": "success", + "type": "bool" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "constant":true, - "inputs":[ + "constant": true, + "inputs": [ { - "name":"_owner", - "type":"address" + "name": "_owner", + "type": "address" }, { - "name":"_spender", - "type":"address" + "name": "_spender", + "type": "address" } ], - "name":"allowance", - "outputs":[ + "name": "allowance", + "outputs": [ { - "name":"remaining", - "type":"uint256" + "name": "remaining", + "type": "uint256" } ], - "payable":false, - "type":"function" + "payable": false, + "type": "function" }, { - "inputs":[ + "inputs": [ { - "name":"_initialAmount", - "type":"uint256" + "name": "_initialAmount", + "type": "uint256" }, { - "name":"_tokenName", - "type":"string" + "name": "_tokenName", + "type": "string" }, { - "name":"_decimalUnits", - "type":"uint8" + "name": "_decimalUnits", + "type": "uint8" }, { - "name":"_tokenSymbol", - "type":"string" + "name": "_tokenSymbol", + "type": "string" } ], - "type":"constructor" + "type": "constructor" }, { - "payable":false, - "type":"fallback" + "payable": false, + "type": "fallback" }, { - "anonymous":false, - "inputs":[ + "anonymous": false, + "inputs": [ { - "indexed":true, - "name":"_from", - "type":"address" + "indexed": true, + "name": "_from", + "type": "address" }, { - "indexed":true, - "name":"_to", - "type":"address" + "indexed": true, + "name": "_to", + "type": "address" }, { - "indexed":false, - "name":"_value", - "type":"uint256" + "indexed": false, + "name": "_value", + "type": "uint256" } ], - "name":"Transfer", - "type":"event" + "name": "Transfer", + "type": "event" }, { - "anonymous":false, - "inputs":[ + "anonymous": false, + "inputs": [ { - "indexed":true, - "name":"_owner", - "type":"address" + "indexed": true, + "name": "_owner", + "type": "address" }, { - "indexed":true, - "name":"_spender", - "type":"address" + "indexed": true, + "name": "_spender", + "type": "address" }, { - "indexed":false, - "name":"_value", - "type":"uint256" + "indexed": false, + "name": "_value", + "type": "uint256" } ], - "name":"Approval", - "type":"event" + "name": "Approval", + "type": "event" } -] \ No newline at end of file +] diff --git a/packages/server/lib/plugins/wallet-scoring/elliptic/elliptic.js b/packages/server/lib/plugins/wallet-scoring/elliptic/elliptic.js index ca17c11e..d9942112 100644 --- a/packages/server/lib/plugins/wallet-scoring/elliptic/elliptic.js +++ b/packages/server/lib/plugins/wallet-scoring/elliptic/elliptic.js @@ -9,34 +9,34 @@ const HOLLISTIC_COINS = { USDT: 'USDT', USDT_TRON: 'USDT', LTC: 'LTC', - TRX: 'TRX' + TRX: 'TRX', } const SINGLE_ASSET_COINS = { ZEC: { asset: 'ZEC', - blockchain: 'zcash' + blockchain: 'zcash', }, BCH: { asset: 'BCH', - blockchain: 'bitcoin_cash' - } + blockchain: 'bitcoin_cash', + }, } const TYPE = { - TRANSACTION: 'transaction', - ADDRESS: 'address' + TRANSACTION: 'transaction', + ADDRESS: 'address', } const SUPPORTED_COINS = { ...HOLLISTIC_COINS, ...SINGLE_ASSET_COINS } -function rate (account, objectType, cryptoCode, objectId) { +function rate(account, objectType, cryptoCode, objectId) { return isWalletScoringEnabled(account, cryptoCode).then(isEnabled => { if (!isEnabled) return Promise.resolve(null) const aml = new AML({ key: account.apiKey, - secret: account.apiSecret + secret: account.apiSecret, }) const isHolistic = Object.keys(HOLLISTIC_COINS).includes(cryptoCode) @@ -44,38 +44,44 @@ function rate (account, objectType, cryptoCode, objectId) { const requestBody = { subject: { asset: isHolistic ? 'holistic' : SINGLE_ASSET_COINS[cryptoCode].asset, - blockchain: isHolistic ? 'holistic' : SINGLE_ASSET_COINS[cryptoCode].blockchain, + blockchain: isHolistic + ? 'holistic' + : SINGLE_ASSET_COINS[cryptoCode].blockchain, type: objectType, - hash: objectId + hash: objectId, }, - type: objectType === TYPE.ADDRESS ? 'wallet_exposure' : 'source_of_funds' + type: objectType === TYPE.ADDRESS ? 'wallet_exposure' : 'source_of_funds', } const threshold = account.scoreThreshold - const endpoint = objectType === TYPE.ADDRESS ? '/v2/wallet/synchronous' : '/v2/analysis/synchronous' + const endpoint = + objectType === TYPE.ADDRESS + ? '/v2/wallet/synchronous' + : '/v2/analysis/synchronous' - return aml.client - .post(endpoint, requestBody) - .then((res) => { - const resScore = res.data?.risk_score + return aml.client.post(endpoint, requestBody).then(res => { + const resScore = res.data?.risk_score - // elliptic returns 0-1 score, but we're accepting 0-100 config - // normalize score to 0-10 where 0 is the lowest risk - // elliptic score can be null and contains decimals - return {score: (resScore || 0) * 10, isValid: ((resScore || 0) * 100) < threshold} - }) + // elliptic returns 0-1 score, but we're accepting 0-100 config + // normalize score to 0-10 where 0 is the lowest risk + // elliptic score can be null and contains decimals + return { + score: (resScore || 0) * 10, + isValid: (resScore || 0) * 100 < threshold, + } + }) }) } -function rateTransaction (account, cryptoCode, transactionId) { +function rateTransaction(account, cryptoCode, transactionId) { return rate(account, TYPE.TRANSACTION, cryptoCode, transactionId) } -function rateAddress (account, cryptoCode, address) { +function rateAddress(account, cryptoCode, address) { return rate(account, TYPE.ADDRESS, cryptoCode, address) } -function isWalletScoringEnabled (account, cryptoCode) { +function isWalletScoringEnabled(account, cryptoCode) { const isAccountEnabled = !_.isNil(account) && account.enabled if (!isAccountEnabled) return Promise.resolve(false) @@ -91,5 +97,5 @@ module.exports = { NAME, rateAddress, rateTransaction, - isWalletScoringEnabled + isWalletScoringEnabled, } diff --git a/packages/server/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js b/packages/server/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js index 0f154ffc..44e00d0a 100644 --- a/packages/server/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js +++ b/packages/server/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js @@ -2,18 +2,22 @@ const NAME = 'FakeScoring' const { WALLET_SCORE_THRESHOLD } = require('../../../constants') -function rateAddress (account, cryptoCode, address) { - return new Promise((resolve, _) => { +function rateAddress(account, cryptoCode, address) { + return new Promise(resolve => { setTimeout(() => { - console.log('[WALLET-SCORING] DEBUG: Mock scoring rating wallet address %s', address) - return Promise.resolve(2) - .then(score => resolve({ address, score, isValid: score < WALLET_SCORE_THRESHOLD })) + console.log( + '[WALLET-SCORING] DEBUG: Mock scoring rating wallet address %s', + address, + ) + return Promise.resolve(2).then(score => + resolve({ address, score, isValid: score < WALLET_SCORE_THRESHOLD }), + ) }, 100) }) } -function isWalletScoringEnabled (account, cryptoCode) { - return new Promise((resolve, _) => { +function isWalletScoringEnabled() { + return new Promise(resolve => { setTimeout(() => { return resolve(true) }, 100) @@ -23,6 +27,6 @@ function isWalletScoringEnabled (account, cryptoCode) { module.exports = { NAME, rateAddress, - rateTransaction:rateAddress, - isWalletScoringEnabled + rateTransaction: rateAddress, + isWalletScoringEnabled, } diff --git a/packages/server/lib/plugins/wallet-scoring/scorechain/scorechain.js b/packages/server/lib/plugins/wallet-scoring/scorechain/scorechain.js index 3a64d4b8..6d7028b4 100644 --- a/packages/server/lib/plugins/wallet-scoring/scorechain/scorechain.js +++ b/packages/server/lib/plugins/wallet-scoring/scorechain/scorechain.js @@ -1,7 +1,6 @@ const axios = require('axios') const _ = require('lodash/fp') - const NAME = 'Scorechain' const SUPPORTED_COINS = { BTC: 'BITCOIN', @@ -11,15 +10,15 @@ const SUPPORTED_COINS = { LTC: 'LITECOIN', DASH: 'DASH', TRX: 'TRON', - USDT_TRON: 'TRON' + USDT_TRON: 'TRON', } const TYPE = { TRANSACTION: 'TRANSACTION', - ADDRESS: 'ADDRESS' + ADDRESS: 'ADDRESS', } -function rate (account, objectType, cryptoCode, objectId) { +function rate(account, objectType, cryptoCode, objectId) { return isWalletScoringEnabled(account, cryptoCode).then(isEnabled => { if (!isEnabled) return Promise.resolve(null) @@ -29,21 +28,25 @@ function rate (account, objectType, cryptoCode, objectId) { objectType, objectId, blockchain: SUPPORTED_COINS[cryptoCode], - coin: "ALL" + coin: 'ALL', } const headers = { - 'accept': 'application/json', + accept: 'application/json', 'X-API-KEY': account.apiKey, - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', } - return axios.post(`https://api.scorechain.com/v1/scoringAnalysis`, payload, {headers}) + return axios + .post(`https://api.scorechain.com/v1/scoringAnalysis`, payload, { + headers, + }) .then(res => { const resScore = res.data?.analysis?.assigned?.result?.score - if (!resScore) throw new Error('Failed to get score from Scorechain API') + if (!resScore) + throw new Error('Failed to get score from Scorechain API') // normalize score to 0-10 where 0 is the lowest risk - return {score: (100 - resScore) / 10, isValid: resScore >= threshold} + return { score: (100 - resScore) / 10, isValid: resScore >= threshold } }) .catch(err => { throw err @@ -51,15 +54,15 @@ function rate (account, objectType, cryptoCode, objectId) { }) } -function rateTransaction (account, cryptoCode, transactionId) { +function rateTransaction(account, cryptoCode, transactionId) { return rate(account, TYPE.TRANSACTION, cryptoCode, transactionId) } -function rateAddress (account, cryptoCode, address) { +function rateAddress(account, cryptoCode, address) { return rate(account, TYPE.ADDRESS, cryptoCode, address) } -function isWalletScoringEnabled (account, cryptoCode) { +function isWalletScoringEnabled(account, cryptoCode) { const isAccountEnabled = !_.isNil(account) && account.enabled if (!isAccountEnabled) return Promise.resolve(false) @@ -75,5 +78,5 @@ module.exports = { NAME, rateAddress, rateTransaction, - isWalletScoringEnabled + isWalletScoringEnabled, } diff --git a/packages/server/lib/plugins/wallet/bitcoincashd/bitcoincashd.js b/packages/server/lib/plugins/wallet/bitcoincashd/bitcoincashd.js index 1208a19e..4ddbc161 100644 --- a/packages/server/lib/plugins/wallet/bitcoincashd/bitcoincashd.js +++ b/packages/server/lib/plugins/wallet/bitcoincashd/bitcoincashd.js @@ -10,11 +10,11 @@ const unitScale = cryptoRec.unitScale const rpcConfig = jsonRpc.rpcConfig(cryptoRec) -function fetch (method, params) { +function fetch(method, params) { return jsonRpc.fetch(rpcConfig, method, params) } -function errorHandle (e) { +function errorHandle(e) { const err = JSON.parse(e.message) switch (err.code) { case -6: @@ -24,115 +24,130 @@ function errorHandle (e) { } } -function checkCryptoCode (cryptoCode) { - if (cryptoCode !== 'BCH') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) +function checkCryptoCode(cryptoCode) { + if (cryptoCode !== 'BCH') + return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) return Promise.resolve() } -function accountBalance (cryptoCode) { +function accountBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } -function accountUnconfirmedBalance (cryptoCode) { +function accountUnconfirmedBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ unconfirmed_balance: balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ unconfirmed_balance: balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } // We want a balance that includes all spends (0 conf) but only deposits that // have at least 1 confirmation. getbalance does this for us automatically. -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return accountBalance(cryptoCode) } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx const coins = cryptoAtoms.shiftedBy(-unitScale).toFixed(8) return checkCryptoCode(cryptoCode) .then(() => fetch('sendtoaddress', [toAddress, coins])) - .then((txId) => fetch('gettransaction', [txId])) - .then((res) => _.pick(['fee', 'txid'], res)) - .then((pickedObj) => { + .then(txId => fetch('gettransaction', [txId])) + .then(res => _.pick(['fee', 'txid'], res)) + .then(pickedObj => { return { fee: new BN(pickedObj.fee).abs().shiftedBy(unitScale).decimalPlaces(0), - txid: pickedObj.txid + txid: pickedObj.txid, } }) .catch(errorHandle) } -function newAddress (account, info, tx, settings, operatorId) { - return checkCryptoCode(info.cryptoCode) - .then(() => fetch('getnewaddress')) +function newAddress(account, info) { + return checkCryptoCode(info.cryptoCode).then(() => fetch('getnewaddress')) } -function addressBalance (address, confs) { - return fetch('getreceivedbyaddress', [address, confs]) - .then(r => new BN(r).shiftedBy(unitScale).decimalPlaces(0)) +function addressBalance(address, confs) { + return fetch('getreceivedbyaddress', [address, confs]).then(r => + new BN(r).shiftedBy(unitScale).decimalPlaces(0), + ) } -function confirmedBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 1)) +function confirmedBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 1)) } -function pendingBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 0)) +function pendingBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 0)) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => confirmedBalance(toAddress, cryptoCode)) .then(confirmed => { - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - return pendingBalance(toAddress, cryptoCode) - .then(pending => { - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) + return pendingBalance(toAddress, cryptoCode).then(pending => { + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + return { receivedCryptoAtoms: pending, status: 'notSeen' } + }) }) } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => { const promises = [ accountUnconfirmedBalance(cryptoCode), accountBalance(cryptoCode), - newAddress(account, { cryptoCode }) + newAddress(account, { cryptoCode }), ] return Promise.all(promises) }) - .then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ - fundingPendingBalance, - fundingConfirmedBalance, - fundingAddress - })) + .then( + ([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ + fundingPendingBalance, + fundingConfirmedBalance, + fundingAddress, + }), + ) } -function cryptoNetwork (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => parseInt(rpcConfig.port, 10) === 18332 ? 'test' : 'main') +function cryptoNetwork(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => + parseInt(rpcConfig.port, 10) === 18332 ? 'test' : 'main', + ) } -function checkBlockchainStatus (cryptoCode) { +function checkBlockchainStatus(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getblockchaininfo')) - .then(res => !!res['initialblockdownload'] ? 'syncing' : 'ready') + .then(res => (res['initialblockdownload'] ? 'syncing' : 'ready')) } -function getTxHashesByAddress (cryptoCode, address) { +function getTxHashesByAddress(cryptoCode, address) { checkCryptoCode(cryptoCode) .then(() => fetch('listreceivedbyaddress', [0, true, true, address])) - .then(txsByAddress => Promise.all(_.map(id => fetch('getrawtransaction', [id]), _.flatMap(it => it.txids, txsByAddress)))) + .then(txsByAddress => + Promise.all( + _.map( + id => fetch('getrawtransaction', [id]), + _.flatMap(it => it.txids, txsByAddress), + ), + ), + ) .then(_.map(({ hash }) => hash)) } @@ -144,5 +159,5 @@ module.exports = { newFunding, cryptoNetwork, checkBlockchainStatus, - getTxHashesByAddress + getTxHashesByAddress, } diff --git a/packages/server/lib/plugins/wallet/bitcoind/bitcoind.js b/packages/server/lib/plugins/wallet/bitcoind/bitcoind.js index ba5d17ae..12bd090f 100644 --- a/packages/server/lib/plugins/wallet/bitcoind/bitcoind.js +++ b/packages/server/lib/plugins/wallet/bitcoind/bitcoind.js @@ -6,7 +6,6 @@ const BN = require('../../../bn') const E = require('../../../error') const logger = require('../../../logger') const { utils: coinUtils } = require('@lamassu/coins') -const { isDevMode } = require('../../../environment-helper') const cryptoRec = coinUtils.getCryptoCurrency('BTC') const unitScale = cryptoRec.unitScale @@ -15,11 +14,11 @@ const rpcConfig = jsonRpc.rpcConfig(cryptoRec) const SUPPORTS_BATCHING = true -function fetch (method, params) { +function fetch(method, params) { return jsonRpc.fetch(rpcConfig, method, params) } -function errorHandle (e) { +function errorHandle(e) { const err = JSON.parse(e.message) switch (err.code) { case -5: @@ -31,32 +30,40 @@ function errorHandle (e) { } } -function checkCryptoCode (cryptoCode) { - if (cryptoCode !== 'BTC') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) +function checkCryptoCode(cryptoCode) { + if (cryptoCode !== 'BTC') + return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) return Promise.resolve() } -function accountBalance (cryptoCode) { +function accountBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getbalances')) - .then(({ mine }) => new BN(mine.trusted).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ mine }) => + new BN(mine.trusted).shiftedBy(unitScale).decimalPlaces(0), + ) .catch(errorHandle) } -function accountUnconfirmedBalance (cryptoCode) { +function accountUnconfirmedBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getbalances')) - .then(({ mine }) => new BN(mine.untrusted_pending).plus(mine.immature).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ mine }) => + new BN(mine.untrusted_pending) + .plus(mine.immature) + .shiftedBy(unitScale) + .decimalPlaces(0), + ) .catch(errorHandle) } // We want a balance that includes all spends (0 conf) but only deposits that // have at least 1 confirmation. getbalance does this for us automatically. -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return accountBalance(cryptoCode) } -function estimateFee () { +function estimateFee() { return getSatBEstimateFee() .then(result => BN(result)) .catch(err => { @@ -64,26 +71,29 @@ function estimateFee () { }) } -function calculateFeeDiscount (feeMultiplier = 1, unitScale) { +function calculateFeeDiscount(feeMultiplier = 1, unitScale) { // 0 makes bitcoind do automatic fee selection const AUTOMATIC_FEE = 0 - return estimateFee() - .then(estimatedFee => { - if (!estimatedFee) { - logger.info('failure estimating fee, using bitcoind automatic fee selection') - return AUTOMATIC_FEE - } - // transform from sat/vB to BTC/kvB and apply the multipler - const newFee = estimatedFee.shiftedBy(-unitScale+3).times(feeMultiplier) - if (newFee.lt(0.00001) || newFee.gt(0.1)) { - logger.info('fee outside safety parameters, defaulting to automatic fee selection') - return AUTOMATIC_FEE - } - return newFee.toFixed(8) - }) + return estimateFee().then(estimatedFee => { + if (!estimatedFee) { + logger.info( + 'failure estimating fee, using bitcoind automatic fee selection', + ) + return AUTOMATIC_FEE + } + // transform from sat/vB to BTC/kvB and apply the multipler + const newFee = estimatedFee.shiftedBy(-unitScale + 3).times(feeMultiplier) + if (newFee.lt(0.00001) || newFee.gt(0.1)) { + logger.info( + 'fee outside safety parameters, defaulting to automatic fee selection', + ) + return AUTOMATIC_FEE + } + return newFee.toFixed(8) + }) } -function sendCoins (account, tx, settings, operatorId, feeMultiplier) { +function sendCoins(account, tx, settings, operatorId, feeMultiplier) { const { toAddress, cryptoAtoms, cryptoCode } = tx const coins = cryptoAtoms.shiftedBy(-unitScale).toFixed(8) @@ -91,102 +101,113 @@ function sendCoins (account, tx, settings, operatorId, feeMultiplier) { .then(() => calculateFeeDiscount(feeMultiplier, unitScale)) .then(newFee => fetch('settxfee', [newFee])) .then(() => fetch('sendtoaddress', [toAddress, coins])) - .then((txId) => fetch('gettransaction', [txId])) - .then((res) => _.pick(['fee', 'txid'], res)) - .then((pickedObj) => { + .then(txId => fetch('gettransaction', [txId])) + .then(res => _.pick(['fee', 'txid'], res)) + .then(pickedObj => { return { fee: new BN(pickedObj.fee).abs().shiftedBy(unitScale).decimalPlaces(0), - txid: pickedObj.txid + txid: pickedObj.txid, } }) .catch(errorHandle) } -function sendCoinsBatch (account, txs, cryptoCode, feeMultiplier) { +function sendCoinsBatch(account, txs, cryptoCode, feeMultiplier) { return checkCryptoCode(cryptoCode) .then(() => calculateFeeDiscount(feeMultiplier, unitScale)) .then(newFee => fetch('settxfee', [newFee])) - .then(() => _.reduce((acc, value) => ({ - ...acc, - [value.toAddress]: _.isNil(acc[value.toAddress]) - ? BN(value.cryptoAtoms).shiftedBy(-unitScale).toFixed(8) - : BN(acc[value.toAddress]).plus(BN(value.cryptoAtoms).shiftedBy(-unitScale).toFixed(8)) - }), {}, txs)) - .then((obj) => fetch('sendmany', ['', obj])) - .then((txId) => fetch('gettransaction', [txId])) - .then((res) => _.pick(['fee', 'txid'], res)) - .then((pickedObj) => ({ + .then(() => + _.reduce( + (acc, value) => ({ + ...acc, + [value.toAddress]: _.isNil(acc[value.toAddress]) + ? BN(value.cryptoAtoms).shiftedBy(-unitScale).toFixed(8) + : BN(acc[value.toAddress]).plus( + BN(value.cryptoAtoms).shiftedBy(-unitScale).toFixed(8), + ), + }), + {}, + txs, + ), + ) + .then(obj => fetch('sendmany', ['', obj])) + .then(txId => fetch('gettransaction', [txId])) + .then(res => _.pick(['fee', 'txid'], res)) + .then(pickedObj => ({ fee: new BN(pickedObj.fee).abs().shiftedBy(unitScale).decimalPlaces(0), - txid: pickedObj.txid + txid: pickedObj.txid, })) .catch(errorHandle) } -function newAddress (account, info, tx, settings, operatorId) { +function newAddress(account, info) { return checkCryptoCode(info.cryptoCode) .then(() => fetch('getnewaddress')) .catch(errorHandle) } -function addressBalance (address, confs) { +function addressBalance(address, confs) { return fetch('getreceivedbyaddress', [address, confs]) .then(r => new BN(r).shiftedBy(unitScale).decimalPlaces(0)) .catch(errorHandle) } -function confirmedBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 1)) +function confirmedBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 1)) } -function pendingBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 0)) +function pendingBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 0)) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => confirmedBalance(toAddress, cryptoCode)) .then(confirmed => { - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - return pendingBalance(toAddress, cryptoCode) - .then(pending => { - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) + return pendingBalance(toAddress, cryptoCode).then(pending => { + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + return { receivedCryptoAtoms: pending, status: 'notSeen' } + }) }) } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => { const promises = [ accountUnconfirmedBalance(cryptoCode), accountBalance(cryptoCode), - newAddress(account, { cryptoCode }) + newAddress(account, { cryptoCode }), ] return Promise.all(promises) }) - .then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ - fundingPendingBalance, - fundingConfirmedBalance, - fundingAddress - })) + .then( + ([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ + fundingPendingBalance, + fundingConfirmedBalance, + fundingAddress, + }), + ) .catch(errorHandle) } -function cryptoNetwork (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => parseInt(rpcConfig.port, 10) === 18332 ? 'test' : 'main') +function cryptoNetwork(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => + parseInt(rpcConfig.port, 10) === 18332 ? 'test' : 'main', + ) } -function fetchRBF (txId) { +function fetchRBF(txId) { return fetch('getmempoolentry', [txId]) - .then((res) => { + .then(res => { return [txId, res['bip125-replaceable']] }) .catch(err => { @@ -195,16 +216,23 @@ function fetchRBF (txId) { }) } -function checkBlockchainStatus (cryptoCode) { +function checkBlockchainStatus(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getblockchaininfo')) - .then(res => !!res['initialblockdownload'] ? 'syncing' : 'ready') + .then(res => (res['initialblockdownload'] ? 'syncing' : 'ready')) } -function getTxHashesByAddress (cryptoCode, address) { +function getTxHashesByAddress(cryptoCode, address) { checkCryptoCode(cryptoCode) .then(() => fetch('listreceivedbyaddress', [0, true, true, address])) - .then(txsByAddress => Promise.all(_.map(id => fetch('getrawtransaction', [id]), _.flatMap(it => it.txids, txsByAddress)))) + .then(txsByAddress => + Promise.all( + _.map( + id => fetch('getrawtransaction', [id]), + _.flatMap(it => it.txids, txsByAddress), + ), + ), + ) .then(_.map(({ hash }) => hash)) } @@ -220,5 +248,5 @@ module.exports = { checkBlockchainStatus, getTxHashesByAddress, fetch, - SUPPORTS_BATCHING + SUPPORTS_BATCHING, } diff --git a/packages/server/lib/plugins/wallet/bitgo/bitgo.js b/packages/server/lib/plugins/wallet/bitgo/bitgo.js index 749ee030..e20a5b8e 100644 --- a/packages/server/lib/plugins/wallet/bitgo/bitgo.js +++ b/packages/server/lib/plugins/wallet/bitgo/bitgo.js @@ -36,7 +36,7 @@ const getWallet = (account, cryptoCode) => { return bitgo.coin(coin).wallets().get({ id: walletId }) } -function checkCryptoCode (cryptoCode) { +function checkCryptoCode(cryptoCode) { if (!SUPPORTED_COINS.includes(cryptoCode)) { return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) } @@ -44,26 +44,26 @@ function checkCryptoCode (cryptoCode) { return Promise.resolve() } -function getLegacyAddress (address, cryptoCode) { +function getLegacyAddress(address, cryptoCode) { if (!BCH_CODES.includes(cryptoCode)) return address return toLegacyAddress(address) } -function getCashAddress (address, cryptoCode) { +function getCashAddress(address, cryptoCode) { if (!BCH_CODES.includes(cryptoCode)) return address return toCashAddress(address) } -function formatToGetStatus (address, cryptoCode) { +function formatToGetStatus(address, cryptoCode) { if (!BCH_CODES.includes(cryptoCode)) return address const [part1, part2] = getLegacyAddress(address, cryptoCode).split(':') return part2 || part1 } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => getWallet(account, cryptoCode)) @@ -72,7 +72,7 @@ function sendCoins (account, tx, settings, operatorId) { address: getLegacyAddress(toAddress, cryptoCode), amount: cryptoAtoms.toNumber(), walletPassphrase: account[`${cryptoCode}WalletPassphrase`], - enforceMinConfirmsForChange: false + enforceMinConfirmsForChange: false, } return wallet.send(params) }) @@ -83,51 +83,55 @@ function sendCoins (account, tx, settings, operatorId) { return { txid: txid, fee: new BN(fee).decimalPlaces(0) } }) .catch(err => { - if (err.message === 'insufficient funds') throw new E.InsufficientFundsError() + if (err.message === 'insufficient funds') + throw new E.InsufficientFundsError() throw err }) } -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => getWallet(account, cryptoCode)) .then(wallet => new BN(wallet._wallet.spendableBalanceString)) } -function newAddress (account, info, tx, settings, operatorId) { +function newAddress(account, info) { return checkCryptoCode(info.cryptoCode) .then(() => getWallet(account, info.cryptoCode)) .then(wallet => { - return wallet.createAddress() - .then(result => { - const address = result.address + return wallet.createAddress().then(result => { + const address = result.address - // If a label was provided, set the label - if (info.label) { - return wallet.updateAddress({ address: address, label: info.label }) - .then(() => getCashAddress(address, info.cryptoCode)) - } + // If a label was provided, set the label + if (info.label) { + return wallet + .updateAddress({ address: address, label: info.label }) + .then(() => getCashAddress(address, info.cryptoCode)) + } - return getCashAddress(address, info.cryptoCode) - }) + return getCashAddress(address, info.cryptoCode) + }) }) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => getWallet(account, cryptoCode)) - .then(wallet => wallet.transfers({ - type: 'receive', - address: formatToGetStatus(toAddress, cryptoCode) - })) + .then(wallet => + wallet.transfers({ + type: 'receive', + address: formatToGetStatus(toAddress, cryptoCode), + }), + ) .then(({ transfers }) => { - const filterConfirmed = _.filter(it => - it.state === 'confirmed' && it.type === 'receive' + const filterConfirmed = _.filter( + it => it.state === 'confirmed' && it.type === 'receive', ) - const filterPending = _.filter(it => - (it.state === 'confirmed' || it.state === 'unconfirmed') && - it.type === 'receive' + const filterPending = _.filter( + it => + (it.state === 'confirmed' || it.state === 'unconfirmed') && + it.type === 'receive', ) const sum = _.reduce((acc, val) => val.plus(acc), new BN(0)) @@ -136,40 +140,43 @@ function getStatus (account, tx, requested, settings, operatorId) { const confirmed = _.compose(sum, toBn, filterConfirmed)(transfers) const pending = _.compose(sum, toBn, filterPending)(transfers) - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } return { receivedCryptoAtoms: pending, status: 'notSeen' } }) } -function newFunding (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => { - return getWallet(account, cryptoCode) - .then(wallet => { - return wallet.createAddress() - .then(result => { - const fundingAddress = result.address - return wallet.updateAddress({ address: fundingAddress, label: 'Funding Address' }) - .then(() => ({ - fundingPendingBalance: new BN(wallet._wallet.balance).minus(wallet._wallet.confirmedBalance), - fundingConfirmedBalance: new BN(wallet._wallet.confirmedBalance), - fundingAddress: getCashAddress(fundingAddress, cryptoCode) - })) - }) - }) +function newFunding(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => { + return getWallet(account, cryptoCode).then(wallet => { + return wallet.createAddress().then(result => { + const fundingAddress = result.address + return wallet + .updateAddress({ address: fundingAddress, label: 'Funding Address' }) + .then(() => ({ + fundingPendingBalance: new BN(wallet._wallet.balance).minus( + wallet._wallet.confirmedBalance, + ), + fundingConfirmedBalance: new BN(wallet._wallet.confirmedBalance), + fundingAddress: getCashAddress(fundingAddress, cryptoCode), + })) + }) }) + }) } -function cryptoNetwork (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => account.environment === 'test' ? 'test' : 'main') +function cryptoNetwork(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => + account.environment === 'test' ? 'test' : 'main', + ) } -function checkBlockchainStatus (cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => Promise.resolve('ready')) +function checkBlockchainStatus(cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => Promise.resolve('ready')) } module.exports = { @@ -180,5 +187,5 @@ module.exports = { getStatus, newFunding, cryptoNetwork, - checkBlockchainStatus + checkBlockchainStatus, } diff --git a/packages/server/lib/plugins/wallet/dashd/dashd.js b/packages/server/lib/plugins/wallet/dashd/dashd.js index e5167332..ce4df134 100644 --- a/packages/server/lib/plugins/wallet/dashd/dashd.js +++ b/packages/server/lib/plugins/wallet/dashd/dashd.js @@ -11,11 +11,11 @@ const unitScale = cryptoRec.unitScale const rpcConfig = jsonRpc.rpcConfig(cryptoRec) -function fetch (method, params) { +function fetch(method, params) { return jsonRpc.fetch(rpcConfig, method, params) } -function errorHandle (e) { +function errorHandle(e) { const err = JSON.parse(e.message) switch (err.code) { case -6: @@ -25,110 +25,124 @@ function errorHandle (e) { } } -function checkCryptoCode (cryptoCode) { - if (cryptoCode !== 'DASH') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) +function checkCryptoCode(cryptoCode) { + if (cryptoCode !== 'DASH') + return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) return Promise.resolve() } -function accountBalance (cryptoCode) { +function accountBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } -function accountUnconfirmedBalance (cryptoCode) { +function accountUnconfirmedBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ unconfirmed_balance: balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ unconfirmed_balance: balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } // We want a balance that includes all spends (0 conf) but only deposits that // have at least 1 confirmation. getbalance does this for us automatically. -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return accountBalance(cryptoCode) } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx const coins = cryptoAtoms.shiftedBy(-unitScale).toFixed(8) return checkCryptoCode(cryptoCode) .then(() => fetch('sendtoaddress', [toAddress, coins])) - .then((txId) => fetch('gettransaction', [txId])) - .then((res) => _.pick(['fee', 'txid'], res)) - .then((pickedObj) => { + .then(txId => fetch('gettransaction', [txId])) + .then(res => _.pick(['fee', 'txid'], res)) + .then(pickedObj => { return { fee: new BN(pickedObj.fee).abs().shiftedBy(unitScale).decimalPlaces(0), - txid: pickedObj.txid + txid: pickedObj.txid, } }) .catch(errorHandle) } -function newAddress (account, info, tx, settings, operatorId) { - return checkCryptoCode(info.cryptoCode) - .then(() => fetch('getnewaddress')) +function newAddress(account, info) { + return checkCryptoCode(info.cryptoCode).then(() => fetch('getnewaddress')) } -function addressBalance (address, confs) { - return fetch('getreceivedbyaddress', [address, confs]) - .then(r => new BN(r).shiftedBy(unitScale).decimalPlaces(0)) +function addressBalance(address, confs) { + return fetch('getreceivedbyaddress', [address, confs]).then(r => + new BN(r).shiftedBy(unitScale).decimalPlaces(0), + ) } -function confirmedBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 1)) +function confirmedBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 1)) } -function pendingBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 0)) +function pendingBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 0)) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => confirmedBalance(toAddress, cryptoCode)) .then(confirmed => { - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - return pendingBalance(toAddress, cryptoCode) - .then(pending => { - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) + return pendingBalance(toAddress, cryptoCode).then(pending => { + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + return { receivedCryptoAtoms: pending, status: 'notSeen' } + }) }) } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => { const promises = [ accountUnconfirmedBalance(cryptoCode), accountBalance(cryptoCode), - newAddress(account, { cryptoCode }) + newAddress(account, { cryptoCode }), ] return Promise.all(promises) }) - .then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ - fundingPendingBalance, - fundingConfirmedBalance, - fundingAddress - })) + .then( + ([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ + fundingPendingBalance, + fundingConfirmedBalance, + fundingAddress, + }), + ) } -function checkBlockchainStatus (cryptoCode) { +function checkBlockchainStatus(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getblockchaininfo')) - .then(res => !!res['initialblockdownload'] ? 'syncing' : 'ready') + .then(res => (res['initialblockdownload'] ? 'syncing' : 'ready')) } -function getTxHashesByAddress (cryptoCode, address) { +function getTxHashesByAddress(cryptoCode, address) { checkCryptoCode(cryptoCode) .then(() => fetch('listreceivedbyaddress', [0, true, true, true, address])) - .then(txsByAddress => Promise.all(_.map(id => fetch('getrawtransaction', [id]), _.flatMap(it => it.txids, txsByAddress)))) + .then(txsByAddress => + Promise.all( + _.map( + id => fetch('getrawtransaction', [id]), + _.flatMap(it => it.txids, txsByAddress), + ), + ), + ) .then(_.map(({ hash }) => hash)) } @@ -139,5 +153,5 @@ module.exports = { getStatus, newFunding, checkBlockchainStatus, - getTxHashesByAddress + getTxHashesByAddress, } diff --git a/packages/server/lib/plugins/wallet/galoy/galoy.js b/packages/server/lib/plugins/wallet/galoy/galoy.js index a2e57906..9192d932 100644 --- a/packages/server/lib/plugins/wallet/galoy/galoy.js +++ b/packages/server/lib/plugins/wallet/galoy/galoy.js @@ -7,16 +7,16 @@ const SUPPORTED_COINS = ['LN'] const BN = require('../../../bn') -function request (graphqlQuery, token, endpoint) { +function request(graphqlQuery, token, endpoint) { const headers = { 'content-type': 'application/json', - 'X-API-KEY': token + 'X-API-KEY': token, } return axios({ method: 'post', url: endpoint, headers: headers, - data: graphqlQuery + data: graphqlQuery, }) .then(r => { if (r.error) throw r.error @@ -27,7 +27,7 @@ function request (graphqlQuery, token, endpoint) { }) } -function checkCryptoCode (cryptoCode) { +function checkCryptoCode(cryptoCode) { if (!SUPPORTED_COINS.includes(cryptoCode)) { return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) } @@ -35,10 +35,10 @@ function checkCryptoCode (cryptoCode) { return Promise.resolve() } -function getTransactionsByAddress (token, endpoint, walletId, address) { +function getTransactionsByAddress(token, endpoint, walletId, address) { const accountInfo = { - 'operationName': 'me', - 'query': `query me($walletId: WalletId!, , $address: OnChainAddress!) { + operationName: 'me', + query: `query me($walletId: WalletId!, , $address: OnChainAddress!) { me { defaultAccount { walletById(walletId: $walletId) { @@ -55,7 +55,7 @@ function getTransactionsByAddress (token, endpoint, walletId, address) { } } }`, - 'variables': { walletId, address } + variables: { walletId, address }, } return request(accountInfo, token, endpoint) .then(r => { @@ -66,10 +66,10 @@ function getTransactionsByAddress (token, endpoint, walletId, address) { }) } -function getGaloyWallet (token, endpoint, walletId) { +function getGaloyWallet(token, endpoint, walletId) { const accountInfo = { - 'operationName': 'me', - 'query': `query me($walletId: WalletId!) { + operationName: 'me', + query: `query me($walletId: WalletId!) { me { defaultAccount { walletById(walletId: $walletId) { @@ -80,7 +80,7 @@ function getGaloyWallet (token, endpoint, walletId) { } } }`, - 'variables': { walletId } + variables: { walletId }, } return request(accountInfo, token, endpoint) .then(r => { @@ -91,18 +91,18 @@ function getGaloyWallet (token, endpoint, walletId) { }) } -function isLnInvoice (address) { +function isLnInvoice(address) { return address.toLowerCase().startsWith('lnbc') } -function isLnurl (address) { +function isLnurl(address) { return address.toLowerCase().startsWith('lnurl') } -function sendFundsOnChain (walletId, address, cryptoAtoms, token, endpoint) { +function sendFundsOnChain(walletId, address, cryptoAtoms, token, endpoint) { const sendOnChain = { - 'operationName': 'onChainPaymentSend', - 'query': `mutation onChainPaymentSend($input: OnChainPaymentSendInput!) { + operationName: 'onChainPaymentSend', + query: `mutation onChainPaymentSend($input: OnChainPaymentSendInput!) { onChainPaymentSend(input: $input) { errors { message @@ -111,18 +111,17 @@ function sendFundsOnChain (walletId, address, cryptoAtoms, token, endpoint) { status } }`, - 'variables': { 'input': { address, amount: cryptoAtoms.toString(), walletId } } + variables: { input: { address, amount: cryptoAtoms.toString(), walletId } }, } - return request(sendOnChain, token, endpoint) - .then(result => { - return result.data.onChainPaymentSend - }) + return request(sendOnChain, token, endpoint).then(result => { + return result.data.onChainPaymentSend + }) } -function sendFundsLNURL (walletId, lnurl, cryptoAtoms, token, endpoint) { +function sendFundsLNURL(walletId, lnurl, cryptoAtoms, token, endpoint) { const sendLnNoAmount = { - 'operationName': 'lnurlPaymentSend', - 'query': `mutation lnurlPaymentSend($input: LnurlPaymentSendInput!) { + operationName: 'lnurlPaymentSend', + query: `mutation lnurlPaymentSend($input: LnurlPaymentSendInput!) { lnurlPaymentSend(input: $input) { errors { message @@ -131,15 +130,23 @@ function sendFundsLNURL (walletId, lnurl, cryptoAtoms, token, endpoint) { status } }`, - 'variables': { 'input': { 'lnurl': `${lnurl}`, 'walletId': `${walletId}`, 'amount': `${cryptoAtoms}` } } + variables: { + input: { + lnurl: `${lnurl}`, + walletId: `${walletId}`, + amount: `${cryptoAtoms}`, + }, + }, } - return request(sendLnNoAmount, token, endpoint).then(result => result.data.lnurlPaymentSend) + return request(sendLnNoAmount, token, endpoint).then( + result => result.data.lnurlPaymentSend, + ) } -function sendFundsLN (walletId, invoice, cryptoAtoms, token, endpoint) { +function sendFundsLN(walletId, invoice, cryptoAtoms, token, endpoint) { const sendLnNoAmount = { - 'operationName': 'lnNoAmountInvoicePaymentSend', - 'query': `mutation lnNoAmountInvoicePaymentSend($input: LnNoAmountInvoicePaymentInput!) { + operationName: 'lnNoAmountInvoicePaymentSend', + query: `mutation lnNoAmountInvoicePaymentSend($input: LnNoAmountInvoicePaymentInput!) { lnNoAmountInvoicePaymentSend(input: $input) { errors { message @@ -148,15 +155,23 @@ function sendFundsLN (walletId, invoice, cryptoAtoms, token, endpoint) { status } }`, - 'variables': { 'input': { 'paymentRequest': invoice, walletId, amount: cryptoAtoms.toString() } } + variables: { + input: { + paymentRequest: invoice, + walletId, + amount: cryptoAtoms.toString(), + }, + }, } - return request(sendLnNoAmount, token, endpoint).then(result => result.data.lnNoAmountInvoicePaymentSend) + return request(sendLnNoAmount, token, endpoint).then( + result => result.data.lnNoAmountInvoicePaymentSend, + ) } -function sendProbeRequest (walletId, invoice, cryptoAtoms, token, endpoint) { +function sendProbeRequest(walletId, invoice, cryptoAtoms, token, endpoint) { const sendProbeNoAmount = { - 'operationName': 'lnNoAmountInvoiceFeeProbe', - 'query': `mutation lnNoAmountInvoiceFeeProbe($input: LnNoAmountInvoiceFeeProbeInput!) { + operationName: 'lnNoAmountInvoiceFeeProbe', + query: `mutation lnNoAmountInvoiceFeeProbe($input: LnNoAmountInvoiceFeeProbeInput!) { lnNoAmountInvoiceFeeProbe(input: $input) { amount errors { @@ -165,22 +180,48 @@ function sendProbeRequest (walletId, invoice, cryptoAtoms, token, endpoint) { } } }`, - 'variables': { 'input': { paymentRequest: invoice, walletId, amount: cryptoAtoms.toString() } } + variables: { + input: { + paymentRequest: invoice, + walletId, + amount: cryptoAtoms.toString(), + }, + }, } - return request(sendProbeNoAmount, token, endpoint).then(result => result.data.lnNoAmountInvoiceFeeProbe) + return request(sendProbeNoAmount, token, endpoint).then( + result => result.data.lnNoAmountInvoiceFeeProbe, + ) } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => { if (isLnInvoice(toAddress)) { - return sendFundsLN(account.walletId, toAddress, cryptoAtoms, account.apiSecret, account.endpoint) + return sendFundsLN( + account.walletId, + toAddress, + cryptoAtoms, + account.apiSecret, + account.endpoint, + ) } if (isLnurl(toAddress)) { - return sendFundsLNURL(account.walletId, toAddress, cryptoAtoms, account.apiSecret, account.endpoint) + return sendFundsLNURL( + account.walletId, + toAddress, + cryptoAtoms, + account.apiSecret, + account.endpoint, + ) } - return sendFundsOnChain(account.walletId, toAddress, cryptoAtoms, account.apiSecret, account.endpoint) + return sendFundsOnChain( + account.walletId, + toAddress, + cryptoAtoms, + account.apiSecret, + account.endpoint, + ) }) .then(result => { switch (result.status) { @@ -193,25 +234,33 @@ function sendCoins (account, tx, settings, operatorId) { case 'PENDING': return '' default: - throw new Error(`Transaction failed: ${_.head(result.errors).message}`) + throw new Error( + `Transaction failed: ${_.head(result.errors).message}`, + ) } }) } -function probeLN (account, cryptoCode, invoice) { +function probeLN(account, cryptoCode, invoice) { const probeHardLimits = [200000, 1000000, 2000000] const promises = probeHardLimits.map(limit => { - return sendProbeRequest(account.walletId, invoice, limit, account.apiSecret, account.endpoint) - .then(r => _.isEmpty(r.errors)) + return sendProbeRequest( + account.walletId, + invoice, + limit, + account.apiSecret, + account.endpoint, + ).then(r => _.isEmpty(r.errors)) }) - return Promise.all(promises) - .then(results => _.zipObject(probeHardLimits, results)) + return Promise.all(promises).then(results => + _.zipObject(probeHardLimits, results), + ) } -function newOnChainAddress (walletId, token, endpoint) { +function newOnChainAddress(walletId, token, endpoint) { const createOnChainAddress = { - 'operationName': 'onChainAddressCreate', - 'query': `mutation onChainAddressCreate($input: OnChainAddressCreateInput!) { + operationName: 'onChainAddressCreate', + query: `mutation onChainAddressCreate($input: OnChainAddressCreateInput!) { onChainAddressCreate(input: $input) { address errors { @@ -220,41 +269,17 @@ function newOnChainAddress (walletId, token, endpoint) { } } }`, - 'variables': { 'input': { walletId } } + variables: { input: { walletId } }, } - return request(createOnChainAddress, token, endpoint) - .then(result => { - return result.data.onChainAddressCreate.address - }) + return request(createOnChainAddress, token, endpoint).then(result => { + return result.data.onChainAddressCreate.address + }) } -function newNoAmountInvoice (walletId, token, endpoint) { +function newInvoice(walletId, cryptoAtoms, token, endpoint) { const createInvoice = { - 'operationName': 'lnNoAmountInvoiceCreate', - 'query': `mutation lnNoAmountInvoiceCreate($input: LnNoAmountInvoiceCreateInput!) { - lnNoAmountInvoiceCreate(input: $input) { - errors { - message - path - } - invoice { - paymentRequest - } - } - }`, - 'variables': { 'input': { walletId } } - } - return request(createInvoice, token, endpoint) - .then(result => { - return result.data.lnNoAmountInvoiceCreate.invoice.paymentRequest - }) - -} - -function newInvoice (walletId, cryptoAtoms, token, endpoint) { - const createInvoice = { - 'operationName': 'lnInvoiceCreate', - 'query': `mutation lnInvoiceCreate($input: LnInvoiceCreateInput!) { + operationName: 'lnInvoiceCreate', + query: `mutation lnInvoiceCreate($input: LnInvoiceCreateInput!) { lnInvoiceCreate(input: $input) { errors { message @@ -265,37 +290,44 @@ function newInvoice (walletId, cryptoAtoms, token, endpoint) { } } }`, - 'variables': { 'input': { walletId, amount: cryptoAtoms.toString() } } + variables: { input: { walletId, amount: cryptoAtoms.toString() } }, } - return request(createInvoice, token, endpoint) - .then(result => { - return result.data.lnInvoiceCreate.invoice.paymentRequest - }) + return request(createInvoice, token, endpoint).then(result => { + return result.data.lnInvoiceCreate.invoice.paymentRequest + }) } -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return checkCryptoCode(cryptoCode) - .then(() => getGaloyWallet(account.apiSecret, account.endpoint, account.walletId)) + .then(() => + getGaloyWallet(account.apiSecret, account.endpoint, account.walletId), + ) .then(wallet => { return new BN(wallet.balance || 0) }) } -function newAddress (account, info, tx, settings, operatorId) { +function newAddress(account, info, tx) { const { cryptoAtoms, cryptoCode } = tx - return checkCryptoCode(cryptoCode) - .then(() => newInvoice(account.walletId, cryptoAtoms, account.apiSecret, account.endpoint)) + return checkCryptoCode(cryptoCode).then(() => + newInvoice( + account.walletId, + cryptoAtoms, + account.apiSecret, + account.endpoint, + ), + ) } -function getInvoiceStatus (token, endpoint, address) { +function getInvoiceStatus(token, endpoint, address) { const query = { - 'operationName': 'lnInvoicePaymentStatus', - 'query': `query lnInvoicePaymentStatus($input: LnInvoicePaymentStatusInput!) { + operationName: 'lnInvoicePaymentStatus', + query: `query lnInvoicePaymentStatus($input: LnInvoicePaymentStatusInput!) { lnInvoicePaymentStatus(input: $input) { status } }`, - 'variables': { input: { paymentRequest: address } } + variables: { input: { paymentRequest: address } }, } return request(query, token, endpoint) .then(r => { @@ -306,62 +338,89 @@ function getInvoiceStatus (token, endpoint, address) { }) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoAtoms, cryptoCode } = tx - const getBalance = _.reduce((acc, value) => { - acc[value.node.status] = acc[value.node.status].plus(new BN(value.node.settlementAmount)) - return acc - }, { SUCCESS: new BN(0), PENDING: new BN(0), FAILURE: new BN(0) }) + const getBalance = _.reduce( + (acc, value) => { + acc[value.node.status] = acc[value.node.status].plus( + new BN(value.node.settlementAmount), + ) + return acc + }, + { SUCCESS: new BN(0), PENDING: new BN(0), FAILURE: new BN(0) }, + ) - return checkCryptoCode(cryptoCode) - .then(() => { - const address = coinUtils.parseUrl(cryptoCode, account.environment, toAddress, false) - if (isLnInvoice(address)) { - return getInvoiceStatus(account.apiSecret, account.endpoint, address) - .then(it => { - const isPaid = it === 'PAID' - if (isPaid) return { receivedCryptoAtoms: cryptoAtoms, status: 'confirmed' } - return { receivedCryptoAtoms: BN(0), status: 'notSeen' } - }) - } - // On-chain and intra-ledger transactions - return getTransactionsByAddress(account.apiSecret, account.endpoint, account.walletId, address) - .then(transactions => { - const { SUCCESS: confirmed, PENDING: pending } = getBalance(transactions.edges) - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) + return checkCryptoCode(cryptoCode).then(() => { + const address = coinUtils.parseUrl( + cryptoCode, + account.environment, + toAddress, + false, + ) + if (isLnInvoice(address)) { + return getInvoiceStatus( + account.apiSecret, + account.endpoint, + address, + ).then(it => { + const isPaid = it === 'PAID' + if (isPaid) + return { receivedCryptoAtoms: cryptoAtoms, status: 'confirmed' } + return { receivedCryptoAtoms: BN(0), status: 'notSeen' } + }) + } + // On-chain and intra-ledger transactions + return getTransactionsByAddress( + account.apiSecret, + account.endpoint, + account.walletId, + address, + ).then(transactions => { + const { SUCCESS: confirmed, PENDING: pending } = getBalance( + transactions.edges, + ) + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + return { receivedCryptoAtoms: pending, status: 'notSeen' } }) + }) } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { // Regular BTC address return checkCryptoCode(cryptoCode) - .then(() => getGaloyWallet(account.apiSecret, account.endpoint, account.walletId)) + .then(() => + getGaloyWallet(account.apiSecret, account.endpoint, account.walletId), + ) .then(wallet => { - return newOnChainAddress(account.walletId, account.apiSecret, account.endpoint) - .then(onChainAddress => [onChainAddress, wallet.balance]) + return newOnChainAddress( + account.walletId, + account.apiSecret, + account.endpoint, + ).then(onChainAddress => [onChainAddress, wallet.balance]) }) .then(([onChainAddress, balance]) => { return { // with the old api is not possible to get pending balance fundingPendingBalance: new BN(0), fundingConfirmedBalance: new BN(balance), - fundingAddress: onChainAddress + fundingAddress: onChainAddress, } }) } -function cryptoNetwork (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => account.environment === 'test' ? 'test' : 'main') +function cryptoNetwork(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => + account.environment === 'test' ? 'test' : 'main', + ) } -function checkBlockchainStatus (cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => Promise.resolve('ready')) +function checkBlockchainStatus(cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => Promise.resolve('ready')) } module.exports = { @@ -373,5 +432,5 @@ module.exports = { newFunding, cryptoNetwork, checkBlockchainStatus, - probeLN + probeLN, } diff --git a/packages/server/lib/plugins/wallet/geth/base.js b/packages/server/lib/plugins/wallet/geth/base.js index 1b8d91ce..15f0fe5c 100644 --- a/packages/server/lib/plugins/wallet/geth/base.js +++ b/packages/server/lib/plugins/wallet/geth/base.js @@ -6,7 +6,6 @@ const web3 = new Web3() const hdkey = require('ethereumjs-wallet/hdkey') const { FeeMarketEIP1559Transaction } = require('@ethereumjs/tx') const { default: Common, Chain, Hardfork } = require('@ethereumjs/common') -const Tx = require('ethereumjs-tx') const { default: PQueue } = require('p-queue') const util = require('ethereumjs-util') const coins = require('@lamassu/coins') @@ -34,7 +33,7 @@ module.exports = { connect, checkBlockchainStatus, getTxHashesByAddress, - _balance + _balance, } const SWEEP_QUEUE = new PQueue({ @@ -55,93 +54,122 @@ const pify = _function => { const logInfuraCall = call => { if (!_.includes('infura', web3.currentProvider.host)) return - _.isNil(infuraCalls[call]) ? infuraCalls[call] = 1 : infuraCalls[call]++ - logger.info(`Calling web3 method ${call} via Infura. Current count for this session: ${JSON.stringify(infuraCalls)}`) + _.isNil(infuraCalls[call]) ? (infuraCalls[call] = 1) : infuraCalls[call]++ + logger.info( + `Calling web3 method ${call} via Infura. Current count for this session: ${JSON.stringify(infuraCalls)}`, + ) } -function connect (url) { +function connect(url) { web3.setProvider(new web3.providers.HttpProvider(url)) } const hex = bigNum => '0x' + bigNum.integerValue(BN.ROUND_DOWN).toString(16) -function privateKey (account) { +function privateKey(account) { return defaultWallet(account).getPrivateKey() } -function isStrictAddress (cryptoCode, toAddress, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => util.isValidChecksumAddress(toAddress)) +function isStrictAddress(cryptoCode, toAddress) { + return checkCryptoCode(cryptoCode).then(() => + util.isValidChecksumAddress(toAddress), + ) } -function getTxHashesByAddress (cryptoCode, address) { - throw new Error(`Transactions hash retrieval is not implemented for this coin!`) +function getTxHashesByAddress() { + throw new Error( + `Transactions hash retrieval is not implemented for this coin!`, + ) } -function sendCoins (account, tx, settings, operatorId, feeMultiplier) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx const isErc20Token = coins.utils.isErc20Token(cryptoCode) return SEND_QUEUE.add(() => - (isErc20Token ? generateErc20Tx : generateTx)(toAddress, defaultWallet(account), cryptoAtoms, false, cryptoCode) + (isErc20Token ? generateErc20Tx : generateTx)( + toAddress, + defaultWallet(account), + cryptoAtoms, + false, + cryptoCode, + ) .then(pify(web3.eth.sendSignedTransaction)) .then(txid => { - return pify(web3.eth.getTransaction)(txid) - .then(tx => { - if (!tx) return { txid } + return pify(web3.eth.getTransaction)(txid).then(tx => { + if (!tx) return { txid } - const fee = new BN(tx.gas).times(new BN(tx.gasPrice)).decimalPlaces(0) + const fee = new BN(tx.gas).times(new BN(tx.gasPrice)).decimalPlaces(0) - return { txid, fee } - }) - }) + return { txid, fee } + }) + }), ) } -function checkCryptoCode (cryptoCode) { +function checkCryptoCode(cryptoCode) { if (cryptoCode === 'ETH' || coins.utils.isErc20Token(cryptoCode)) { return Promise.resolve(cryptoCode) } return Promise.reject(new Error('cryptoCode must be ETH')) } -function balance (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(code => confirmedBalance(defaultAddress(account), code)) +function balance(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(code => + confirmedBalance(defaultAddress(account), code), + ) } const pendingBalance = (address, cryptoCode) => { - const promises = [_balance(true, address, cryptoCode), _balance(false, address, cryptoCode)] - return Promise.all(promises).then(([pending, confirmed]) => BN(pending).minus(confirmed)) + const promises = [ + _balance(true, address, cryptoCode), + _balance(false, address, cryptoCode), + ] + return Promise.all(promises).then(([pending, confirmed]) => + BN(pending).minus(confirmed), + ) } -const confirmedBalance = (address, cryptoCode) => _balance(false, address, cryptoCode) +const confirmedBalance = (address, cryptoCode) => + _balance(false, address, cryptoCode) -function _balance (includePending, address, cryptoCode) { +function _balance(includePending, address, cryptoCode) { if (coins.utils.isErc20Token(cryptoCode)) { - const contract = new web3.eth.Contract(ABI.ERC20, coins.utils.getErc20Token(cryptoCode).contractAddress) - return contract.methods.balanceOf(address.toLowerCase()).call((_, balance) => { - return contract.methods.decimals().call((_, decimals) => BN(balance).div(10 ** decimals)) - }) + const contract = new web3.eth.Contract( + ABI.ERC20, + coins.utils.getErc20Token(cryptoCode).contractAddress, + ) + return contract.methods + .balanceOf(address.toLowerCase()) + .call((_, balance) => { + return contract.methods + .decimals() + .call((_, decimals) => BN(balance).div(10 ** decimals)) + }) } const block = includePending ? 'pending' : undefined - return pify(web3.eth.getBalance)(address.toLowerCase(), block) - /* NOTE: Convert bn.js bignum to bignumber.js bignum */ - .then(balance => balance ? BN(balance) : BN(0)) + return ( + pify(web3.eth.getBalance)(address.toLowerCase(), block) + /* NOTE: Convert bn.js bignum to bignumber.js bignum */ + .then(balance => (balance ? BN(balance) : BN(0))) + ) } -function generateErc20Tx (_toAddress, wallet, amount, includesFee, cryptoCode) { +function generateErc20Tx(_toAddress, wallet, amount, includesFee, cryptoCode) { const fromAddress = '0x' + wallet.getAddress().toString('hex') const toAddress = coins.utils.getErc20Token(cryptoCode).contractAddress const contract = new web3.eth.Contract(ABI.ERC20, toAddress) - const contractData = contract.methods.transfer(_toAddress.toLowerCase(), hex(amount)) + const contractData = contract.methods.transfer( + _toAddress.toLowerCase(), + hex(amount), + ) const txTemplate = { from: fromAddress, to: toAddress, value: hex(BN(0)), - data: contractData.encodeABI() + data: contractData.encodeABI(), } const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) @@ -149,24 +177,22 @@ function generateErc20Tx (_toAddress, wallet, amount, includesFee, cryptoCode) { const promises = [ pify(contractData.estimateGas)(txTemplate), pify(web3.eth.getTransactionCount)(fromAddress), - pify(web3.eth.getBlock)('pending') + pify(web3.eth.getBlock)('pending'), ] return Promise.all(promises) .then(([gas, txCount, { baseFeePerGas }]) => [ BN(gas), _.max([0, txCount, lastUsedNonces[fromAddress] + 1]), - BN(baseFeePerGas) + BN(baseFeePerGas), ]) .then(([gas, txCount, baseFeePerGas]) => { lastUsedNonces[fromAddress] = txCount const maxPriorityFeePerGas = new BN(web3.utils.toWei('1.0', 'gwei')) // web3 default value - const maxFeePerGas = new BN(2).times(baseFeePerGas).plus(maxPriorityFeePerGas) - - if (includesFee && (toSend.isNegative() || toSend.isZero())) { - throw new Error(`Trying to send a nil or negative amount (Transaction ID: ${txId} | Value provided: ${toSend.toNumber()}). This is probably caused due to the estimated fee being higher than the address' balance.`) - } + const maxFeePerGas = new BN(2) + .times(baseFeePerGas) + .plus(maxPriorityFeePerGas) const rawTx = { chainId: 1, @@ -177,7 +203,7 @@ function generateErc20Tx (_toAddress, wallet, amount, includesFee, cryptoCode) { to: toAddress, from: fromAddress, value: hex(BN(0)), - data: contractData.encodeABI() + data: contractData.encodeABI(), } const tx = FeeMarketEIP1559Transaction.fromTxData(rawTx, { common }) @@ -189,7 +215,7 @@ function generateErc20Tx (_toAddress, wallet, amount, includesFee, cryptoCode) { }) } -function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode, txId) { +function generateTx(_toAddress, wallet, amount, includesFee) { const fromAddress = '0x' + wallet.getAddress().toString('hex') const toAddress = _toAddress.toLowerCase() @@ -197,7 +223,7 @@ function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode, txId) const txTemplate = { from: fromAddress, to: toAddress, - value: amount.toString() + value: amount.toString(), } const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) @@ -206,7 +232,7 @@ function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode, txId) pify(web3.eth.estimateGas)(txTemplate), pify(web3.eth.getGasPrice)(), pify(web3.eth.getTransactionCount)(fromAddress), - pify(web3.eth.getBlock)('pending') + pify(web3.eth.getBlock)('pending'), ] return Promise.all(promises) @@ -214,9 +240,9 @@ function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode, txId) BN(gas), BN(gasPrice), _.max([0, txCount, lastUsedNonces[fromAddress] + 1]), - BN(baseFeePerGas) + BN(baseFeePerGas), ]) - .then(([gas, gasPrice, txCount, baseFeePerGas]) => { + .then(([gas, , txCount, baseFeePerGas]) => { lastUsedNonces[fromAddress] = txCount const maxPriorityFeePerGas = new BN(web3.utils.toWei('1.0', 'gwei')) // web3 default value @@ -234,7 +260,7 @@ function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode, txId) gasLimit: hex(gas), to: toAddress, from: fromAddress, - value: hex(toSend) + value: hex(toSend), } const tx = FeeMarketEIP1559Transaction.fromTxData(rawTx, { common }) @@ -246,85 +272,97 @@ function generateTx (_toAddress, wallet, amount, includesFee, cryptoCode, txId) }) } -function defaultWallet (account) { +function defaultWallet(account) { return defaultHdNode(account).deriveChild(0).getWallet() } -function defaultAddress (account) { +function defaultAddress(account) { return defaultWallet(account).getChecksumAddressString() } -function sweep (account, txId, cryptoCode, hdIndex, settings, operatorId) { +function sweep(account, txId, cryptoCode, hdIndex) { const wallet = paymentHdNode(account).deriveChild(hdIndex).getWallet() const fromAddress = wallet.getChecksumAddressString() - return SWEEP_QUEUE.add(() => confirmedBalance(fromAddress, cryptoCode) - .then(r => { + return SWEEP_QUEUE.add(() => + confirmedBalance(fromAddress, cryptoCode).then(r => { if (r.eq(0)) return - return generateTx(defaultAddress(account), wallet, r, true, cryptoCode, txId) - .then(signedTx => pify(web3.eth.sendSignedTransaction)(signedTx)) - }) + return generateTx( + defaultAddress(account), + wallet, + r, + true, + cryptoCode, + txId, + ).then(signedTx => pify(web3.eth.sendSignedTransaction)(signedTx)) + }), ) } -function newAddress (account, info, tx, settings, operatorId) { +function newAddress(account, info) { const childNode = paymentHdNode(account).deriveChild(info.hdIndex) return Promise.resolve(childNode.getWallet().getChecksumAddressString()) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(code => Promise.all([confirmedBalance(toAddress, code), code])) .then(([confirmed, code]) => { - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - return pendingBalance(toAddress, code) - .then(pending => { - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'published' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) + return pendingBalance(toAddress, code).then(pending => { + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'published' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + return { receivedCryptoAtoms: pending, status: 'notSeen' } + }) }) } -function paymentHdNode (account) { +function paymentHdNode(account) { const masterSeed = account.seed if (!masterSeed) throw new Error('No master seed!') const key = hdkey.fromMasterSeed(masterSeed) return key.derivePath(paymentPrefixPath) } -function defaultHdNode (account) { +function defaultHdNode(account) { const masterSeed = account.seed if (!masterSeed) throw new Error('No master seed!') const key = hdkey.fromMasterSeed(masterSeed) return key.derivePath(defaultPrefixPath) } -function newFunding (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(code => { - const fundingAddress = defaultAddress(account) +function newFunding(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(code => { + const fundingAddress = defaultAddress(account) - const promises = [ - pendingBalance(fundingAddress, code), - confirmedBalance(fundingAddress, code) - ] + const promises = [ + pendingBalance(fundingAddress, code), + confirmedBalance(fundingAddress, code), + ] - return Promise.all(promises) - .then(([fundingPendingBalance, fundingConfirmedBalance]) => ({ - fundingPendingBalance, - fundingConfirmedBalance, - fundingAddress - })) - }) + return Promise.all(promises).then( + ([fundingPendingBalance, fundingConfirmedBalance]) => ({ + fundingPendingBalance, + fundingConfirmedBalance, + fundingAddress, + }), + ) + }) } -function checkBlockchainStatus (cryptoCode) { +function checkBlockchainStatus(cryptoCode) { return checkCryptoCode(cryptoCode) - .then(() => connect(`http://localhost:${coins.utils.getCryptoCurrency(cryptoCode).defaultPort}`)) + .then(() => + connect( + `http://localhost:${coins.utils.getCryptoCurrency(cryptoCode).defaultPort}`, + ), + ) .then(() => web3.eth.syncing) - .then(res => res === false ? 'ready' : 'syncing') + .then(res => (res === false ? 'ready' : 'syncing')) } diff --git a/packages/server/lib/plugins/wallet/geth/geth.js b/packages/server/lib/plugins/wallet/geth/geth.js index 61b28e73..a093e158 100644 --- a/packages/server/lib/plugins/wallet/geth/geth.js +++ b/packages/server/lib/plugins/wallet/geth/geth.js @@ -8,7 +8,7 @@ const defaultPort = cryptoRec.defaultPort const NAME = 'geth' -function run (account) { +function run() { base.connect(`http://localhost:${defaultPort}`) } diff --git a/packages/server/lib/plugins/wallet/infura/infura.js b/packages/server/lib/plugins/wallet/infura/infura.js index 5732bc2c..30188f74 100644 --- a/packages/server/lib/plugins/wallet/infura/infura.js +++ b/packages/server/lib/plugins/wallet/infura/infura.js @@ -6,11 +6,13 @@ const { BALANCE_FETCH_SPEED_MULTIPLIER } = require('../../../constants') const NAME = 'infura' -function run (account) { - if (!account.endpoint) throw new Error('Need to configure API endpoint for Infura') +function run(account) { + if (!account.endpoint) + throw new Error('Need to configure API endpoint for Infura') const endpoint = _.startsWith('https://')(account.endpoint) - ? account.endpoint : `https://${account.endpoint}` + ? account.endpoint + : `https://${account.endpoint}` base.connect(endpoint) } @@ -18,24 +20,35 @@ function run (account) { const txsCache = new NodeCache({ stdTTL: T.hour / 1000, checkperiod: T.minute / 1000, - deleteOnExpire: true + deleteOnExpire: true, }) -function shouldGetStatus (tx) { +function shouldGetStatus(tx) { const timePassedSinceTx = Date.now() - new Date(tx.created) - const timePassedSinceReq = Date.now() - new Date(txsCache.get(tx.id).lastReqTime) + const timePassedSinceReq = + Date.now() - new Date(txsCache.get(tx.id).lastReqTime) - if (timePassedSinceTx < 3 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 10 * T.seconds - if (timePassedSinceTx < 5 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 20 * T.seconds - if (timePassedSinceTx < 30 * T.minutes) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.minute - if (timePassedSinceTx < 1 * T.hour) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 2 * T.minute - if (timePassedSinceTx < 3 * T.hours) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 5 * T.minute - if (timePassedSinceTx < 1 * T.day) return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.hour + if (timePassedSinceTx < 3 * T.minutes) + return ( + _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 10 * T.seconds + ) + if (timePassedSinceTx < 5 * T.minutes) + return ( + _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 20 * T.seconds + ) + if (timePassedSinceTx < 30 * T.minutes) + return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.minute + if (timePassedSinceTx < 1 * T.hour) + return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 2 * T.minute + if (timePassedSinceTx < 3 * T.hours) + return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > 5 * T.minute + if (timePassedSinceTx < 1 * T.day) + return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.hour return _.isNil(txsCache.get(tx.id).res) || timePassedSinceReq > T.hour } // Override geth's getStatus function to allow for different polling timing -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested, settings, operatorId) { if (_.isNil(txsCache.get(tx.id))) { txsCache.set(tx.id, { lastReqTime: Date.now() }) } @@ -45,7 +58,8 @@ function getStatus (account, tx, requested, settings, operatorId) { return Promise.resolve(txsCache.get(tx.id).res) } - return base.getStatus(account, tx, requested, settings, operatorId) + return base + .getStatus(account, tx, requested, settings, operatorId) .then(res => { if (res.status === 'confirmed') { txsCache.del(tx.id) // Transaction reached final status, can trim it from the caching obj @@ -57,4 +71,9 @@ function getStatus (account, tx, requested, settings, operatorId) { }) } -module.exports = _.merge(base, { NAME, run, getStatus, fetchSpeed: BALANCE_FETCH_SPEED_MULTIPLIER.SLOW }) +module.exports = _.merge(base, { + NAME, + run, + getStatus, + fetchSpeed: BALANCE_FETCH_SPEED_MULTIPLIER.SLOW, +}) diff --git a/packages/server/lib/plugins/wallet/litecoind/litecoind.js b/packages/server/lib/plugins/wallet/litecoind/litecoind.js index 61dc1f9e..8443ebd6 100644 --- a/packages/server/lib/plugins/wallet/litecoind/litecoind.js +++ b/packages/server/lib/plugins/wallet/litecoind/litecoind.js @@ -11,11 +11,11 @@ const unitScale = cryptoRec.unitScale const rpcConfig = jsonRpc.rpcConfig(cryptoRec) -function fetch (method, params) { +function fetch(method, params) { return jsonRpc.fetch(rpcConfig, method, params) } -function errorHandle (e) { +function errorHandle(e) { const err = JSON.parse(e.message) switch (err.code) { case -6: @@ -25,107 +25,114 @@ function errorHandle (e) { } } -function checkCryptoCode (cryptoCode) { - if (cryptoCode !== 'LTC') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) +function checkCryptoCode(cryptoCode) { + if (cryptoCode !== 'LTC') + return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) return Promise.resolve() } -function accountBalance (cryptoCode) { +function accountBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } -function accountUnconfirmedBalance (cryptoCode) { +function accountUnconfirmedBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ unconfirmed_balance: balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ unconfirmed_balance: balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } // We want a balance that includes all spends (0 conf) but only deposits that // have at least 1 confirmation. getbalance does this for us automatically. -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return accountBalance(cryptoCode) } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx const coins = cryptoAtoms.shiftedBy(-unitScale).toFixed(8) return checkCryptoCode(cryptoCode) .then(() => fetch('sendtoaddress', [toAddress, coins])) - .then((txId) => fetch('gettransaction', [txId])) - .then((res) => _.pick(['fee', 'txid'], res)) - .then((pickedObj) => { + .then(txId => fetch('gettransaction', [txId])) + .then(res => _.pick(['fee', 'txid'], res)) + .then(pickedObj => { return { fee: new BN(pickedObj.fee).abs().shiftedBy(unitScale).decimalPlaces(0), - txid: pickedObj.txid + txid: pickedObj.txid, } }) .catch(errorHandle) } -function newAddress (account, info, tx, settings, operatorId) { - return checkCryptoCode(info.cryptoCode) - .then(() => fetch('getnewaddress')) +function newAddress(account, info) { + return checkCryptoCode(info.cryptoCode).then(() => fetch('getnewaddress')) } -function addressBalance (address, confs) { - return fetch('getreceivedbyaddress', [address, confs]) - .then(r => new BN(r).shiftedBy(unitScale).decimalPlaces(0)) +function addressBalance(address, confs) { + return fetch('getreceivedbyaddress', [address, confs]).then(r => + new BN(r).shiftedBy(unitScale).decimalPlaces(0), + ) } -function confirmedBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 1)) +function confirmedBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 1)) } -function pendingBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 0)) +function pendingBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 0)) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => confirmedBalance(toAddress, cryptoCode)) .then(confirmed => { - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - return pendingBalance(toAddress, cryptoCode) - .then(pending => { - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) + return pendingBalance(toAddress, cryptoCode).then(pending => { + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + return { receivedCryptoAtoms: pending, status: 'notSeen' } + }) }) } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => { const promises = [ accountUnconfirmedBalance(cryptoCode), accountBalance(cryptoCode), - newAddress(account, { cryptoCode }) + newAddress(account, { cryptoCode }), ] return Promise.all(promises) }) - .then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ - fundingPendingBalance, - fundingConfirmedBalance, - fundingAddress - })) + .then( + ([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ + fundingPendingBalance, + fundingConfirmedBalance, + fundingAddress, + }), + ) } -function checkBlockchainStatus (cryptoCode) { +function checkBlockchainStatus(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getblockchaininfo')) - .then(res => !!res['initialblockdownload'] ? 'syncing' : 'ready') + .then(res => (res['initialblockdownload'] ? 'syncing' : 'ready')) } -function getTxHashesByAddress (cryptoCode, address) { +function getTxHashesByAddress() { throw new Error(`Transactions hash retrieval not implemented for this coin!`) } @@ -136,5 +143,5 @@ module.exports = { getStatus, newFunding, checkBlockchainStatus, - getTxHashesByAddress + getTxHashesByAddress, } diff --git a/packages/server/lib/plugins/wallet/mock-wallet/mock-wallet.js b/packages/server/lib/plugins/wallet/mock-wallet/mock-wallet.js index 4e359287..5dfac8d5 100644 --- a/packages/server/lib/plugins/wallet/mock-wallet/mock-wallet.js +++ b/packages/server/lib/plugins/wallet/mock-wallet/mock-wallet.js @@ -14,118 +14,150 @@ const SUPPORTED_COINS = coinUtils.cryptoCurrencies() let t0 -const checkCryptoCode = (cryptoCode) => !_.includes(cryptoCode, SUPPORTED_COINS) - ? Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) - : Promise.resolve() +const checkCryptoCode = cryptoCode => + !_.includes(cryptoCode, SUPPORTED_COINS) + ? Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) + : Promise.resolve() -function _balance (cryptoCode) { +function _balance(cryptoCode) { const cryptoRec = coinUtils.getCryptoCurrency(cryptoCode) const unitScale = cryptoRec.unitScale return new BN(10).shiftedBy(unitScale).decimalPlaces(0) } -function balance (account, cryptoCode, settings, operatorId) { - return Promise.resolve() - .then(() => _balance(cryptoCode)) +function balance(account, cryptoCode) { + return Promise.resolve().then(() => _balance(cryptoCode)) } -function pendingBalance (account, cryptoCode) { - return balance(account, cryptoCode) - .then(b => b.times(1.1)) +function pendingBalance(account, cryptoCode) { + return balance(account, cryptoCode).then(b => b.times(1.1)) } -function confirmedBalance (account, cryptoCode) { +function confirmedBalance(account, cryptoCode) { return balance(account, cryptoCode) } // Note: This makes it easier to test insufficient funds errors let sendCount = 100 -function isInsufficient (cryptoAtoms, cryptoCode) { +function isInsufficient(cryptoAtoms, cryptoCode) { const b = _balance(cryptoCode) return cryptoAtoms.gt(b.div(1000).times(sendCount)) } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx sendCount++ return new Promise((resolve, reject) => { setTimeout(() => { if (isInsufficient(cryptoAtoms, cryptoCode)) { - console.log('[%s] DEBUG: Mock wallet insufficient funds: %s', - cryptoCode, cryptoAtoms.toString()) + console.log( + '[%s] DEBUG: Mock wallet insufficient funds: %s', + cryptoCode, + cryptoAtoms.toString(), + ) return reject(new E.InsufficientFundsError()) } - console.log('[%s] DEBUG: Mock wallet sending %s cryptoAtoms to %s', - cryptoCode, cryptoAtoms.toString(), toAddress) + console.log( + '[%s] DEBUG: Mock wallet sending %s cryptoAtoms to %s', + cryptoCode, + cryptoAtoms.toString(), + toAddress, + ) return resolve({ txid: '', fee: new BN(0) }) }, 2000) }) } -function sendCoinsBatch (account, txs, cryptoCode) { +function sendCoinsBatch(account, txs, cryptoCode) { sendCount = sendCount + txs.length return new Promise((resolve, reject) => { setTimeout(() => { - const cryptoSum = _.reduce((acc, value) => acc.plus(value.crypto_atoms), BN(0), txs) + const cryptoSum = _.reduce( + (acc, value) => acc.plus(value.crypto_atoms), + BN(0), + txs, + ) if (isInsufficient(cryptoSum, cryptoCode)) { - console.log('[%s] DEBUG: Mock wallet insufficient funds: %s', - cryptoCode, cryptoSum.toString()) + console.log( + '[%s] DEBUG: Mock wallet insufficient funds: %s', + cryptoCode, + cryptoSum.toString(), + ) return reject(new E.InsufficientFundsError()) } - console.log('[%s] DEBUG: Mock wallet sending %s cryptoAtoms in a batch', - cryptoCode, cryptoSum.toString()) + console.log( + '[%s] DEBUG: Mock wallet sending %s cryptoAtoms in a batch', + cryptoCode, + cryptoSum.toString(), + ) return resolve({ txid: '', fee: BN(0) }) }, 2000) }) } -function newAddress () { +function newAddress() { t0 = Date.now() - return Promise.resolve('') + return Promise.resolve("") } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { const promises = [ pendingBalance(account, cryptoCode), confirmedBalance(account, cryptoCode), - newAddress(account, { cryptoCode }) + newAddress(account, { cryptoCode }), ] - return Promise.all(promises) - .then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ + return Promise.all(promises).then( + ([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ fundingPendingBalance, fundingConfirmedBalance, - fundingAddress - })) + fundingAddress, + }), + ) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx const elapsed = Date.now() - t0 - if (elapsed < PUBLISH_TIME) return Promise.resolve({ receivedCryptoAtoms: new BN(0), status: 'notSeen' }) - if (elapsed < AUTHORIZE_TIME) return Promise.resolve({ receivedCryptoAtoms: requested, status: 'published' }) - if (elapsed < CONFIRM_TIME) return Promise.resolve({ receivedCryptoAtoms: requested, status: 'authorized' }) + if (elapsed < PUBLISH_TIME) + return Promise.resolve({ + receivedCryptoAtoms: new BN(0), + status: 'notSeen', + }) + if (elapsed < AUTHORIZE_TIME) + return Promise.resolve({ + receivedCryptoAtoms: requested, + status: 'published', + }) + if (elapsed < CONFIRM_TIME) + return Promise.resolve({ + receivedCryptoAtoms: requested, + status: 'authorized', + }) - console.log('[%s] DEBUG: Mock wallet has confirmed transaction [%s]', cryptoCode, toAddress.slice(0, 5)) + console.log( + '[%s] DEBUG: Mock wallet has confirmed transaction [%s]', + cryptoCode, + toAddress.slice(0, 5), + ) return Promise.resolve({ status: 'confirmed' }) } -function getTxHashesByAddress (cryptoCode, address) { - return new Promise((resolve, reject) => { +function getTxHashesByAddress() { + return new Promise(resolve => { setTimeout(() => { return resolve([]) // TODO: should return something other than empty list? }, 100) }) } -function checkBlockchainStatus (cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => Promise.resolve('ready')) +function checkBlockchainStatus(cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => Promise.resolve('ready')) } module.exports = { @@ -137,5 +169,5 @@ module.exports = { getStatus, newFunding, checkBlockchainStatus, - getTxHashesByAddress + getTxHashesByAddress, } diff --git a/packages/server/lib/plugins/wallet/monerod/monerod.js b/packages/server/lib/plugins/wallet/monerod/monerod.js index d5aa5098..36274ba0 100644 --- a/packages/server/lib/plugins/wallet/monerod/monerod.js +++ b/packages/server/lib/plugins/wallet/monerod/monerod.js @@ -13,59 +13,61 @@ const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR const cryptoRec = utils.getCryptoCurrency(COINS.XMR) const configPath = utils.configPath(cryptoRec, BLOCKCHAIN_DIR) -const walletDir = path.resolve(utils.cryptoDir(cryptoRec, BLOCKCHAIN_DIR), 'wallets') +const walletDir = path.resolve( + utils.cryptoDir(cryptoRec, BLOCKCHAIN_DIR), + 'wallets', +) const DIGEST_QUEUE = new PQueue({ concurrency: 1, interval: 150, }) -function createDigestRequest (account = {}, method, params = []) { - return DIGEST_QUEUE.add(() => jsonRpc.fetchDigest(account, method, params) - .then(res => { +function createDigestRequest(account = {}, method, params = []) { + return DIGEST_QUEUE.add(() => + jsonRpc.fetchDigest(account, method, params).then(res => { const r = JSON.parse(res) if (r.error) throw r.error return r.result - }) + }), ) } -function rpcConfig () { +function rpcConfig() { try { const config = jsonRpc.parseConf(configPath) return { username: config['rpc-login'].split(':')[0], password: config['rpc-login'].split(':')[1], - port: cryptoRec.walletPort || cryptoRec.defaultPort + port: cryptoRec.walletPort || cryptoRec.defaultPort, } } catch (err) { - logger.error('Wallet is currently not installed!') + logger.error(`Wallet is currently not installed! ${err}`) return { username: '', password: '', - port: cryptoRec.walletPort || cryptoRec.defaultPort + port: cryptoRec.walletPort || cryptoRec.defaultPort, } } } -function fetch (method, params) { +function fetch(method, params) { return createDigestRequest(rpcConfig(), method, params) } -function handleError (error, method) { - switch(error.code) { - case -13: - { - if ( - fs.existsSync(path.resolve(walletDir, 'Wallet')) && - fs.existsSync(path.resolve(walletDir, 'Wallet.keys')) - ) { - logger.debug('Found wallet! Opening wallet...') - return openWallet() - } - logger.debug('Couldn\'t find wallet! Creating...') - return createWallet() +function handleError(error, method) { + switch (error.code) { + case -13: { + if ( + fs.existsSync(path.resolve(walletDir, 'Wallet')) && + fs.existsSync(path.resolve(walletDir, 'Wallet.keys')) + ) { + logger.debug('Found wallet! Opening wallet...') + return openWallet() } + logger.debug("Couldn't find wallet! Creating...") + return createWallet() + } case -21: throw new Error('Wallet already exists!') case -22: @@ -83,56 +85,63 @@ function handleError (error, method) { _.join(' ', [ `json-rpc::${method} error:`, JSON.stringify(_.get('message', error, '')), - JSON.stringify(_.get('response.data.error', error, '')) - ]) + JSON.stringify(_.get('response.data.error', error, '')), + ]), ) } } -function openWallet () { - return fetch('open_wallet', { filename: 'Wallet' }) - .catch(() => openWalletWithPassword()) +function openWallet() { + return fetch('open_wallet', { filename: 'Wallet' }).catch(() => + openWalletWithPassword(), + ) } -function openWalletWithPassword () { - return fetch('open_wallet', { filename: 'Wallet', password: rpcConfig().password }) +function openWalletWithPassword() { + return fetch('open_wallet', { + filename: 'Wallet', + password: rpcConfig().password, + }) } -function createWallet () { +function createWallet() { return fetch('create_wallet', { filename: 'Wallet', language: 'English' }) .then(() => new Promise(() => setTimeout(() => openWallet(), 3000))) .then(() => fetch('auto_refresh')) } -function checkCryptoCode (cryptoCode) { - if (cryptoCode !== 'XMR') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) +function checkCryptoCode(cryptoCode) { + if (cryptoCode !== 'XMR') + return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) return Promise.resolve() } -function refreshWallet () { - return fetch('refresh') - .catch(err => handleError(err, 'refreshWallet')) +function refreshWallet() { + return fetch('refresh').catch(err => handleError(err, 'refreshWallet')) } -function accountBalance (cryptoCode) { +function accountBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => refreshWallet()) - .then(() => fetch('get_balance', { account_index: 0, address_indices: [0] })) + .then(() => + fetch('get_balance', { account_index: 0, address_indices: [0] }), + ) .then(res => { return BN(res.unlocked_balance).decimalPlaces(0) }) .catch(err => handleError(err, 'accountBalance')) } -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return accountBalance(cryptoCode) } -function sendCoins (account, tx, settings, operatorId, feeMultiplier) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => refreshWallet()) - .then(() => fetch('transfer_split', { + .then(() => + fetch('transfer_split', { destinations: [{ amount: cryptoAtoms, address: toAddress }], account_index: 0, subaddr_indices: [], @@ -142,103 +151,143 @@ function sendCoins (account, tx, settings, operatorId, feeMultiplier) { unlock_time: 0, get_tx_hex: false, new_algorithm: false, - get_tx_metadata: false - })) + get_tx_metadata: false, + }), + ) .then(res => ({ fee: BN(res.fee_list[0]).abs(), - txid: res.tx_hash_list[0] + txid: res.tx_hash_list[0], })) .catch(err => handleError(err, 'sendCoins')) } -function newAddress (account, info, tx, settings, operatorId) { +function newAddress(account, info) { return checkCryptoCode(info.cryptoCode) .then(() => fetch('create_address', { account_index: 0 })) .then(res => res.address) .catch(err => handleError(err, 'newAddress')) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => refreshWallet()) .then(() => fetch('get_address_index', { address: toAddress })) - .then(addressRes => fetch('get_transfers', { in: true, pool: true, account_index: addressRes.index.major, subaddr_indices: [addressRes.index.minor] })) + .then(addressRes => + fetch('get_transfers', { + in: true, + pool: true, + account_index: addressRes.index.major, + subaddr_indices: [addressRes.index.minor], + }), + ) .then(transferRes => { - const confirmedToAddress = _.filter(it => it.address === toAddress, transferRes.in ?? []) - const pendingToAddress = _.filter(it => it.address === toAddress, transferRes.pool ?? []) - const confirmed = _.reduce((acc, value) => acc.plus(value.amount), BN(0), confirmedToAddress) - const pending = _.reduce((acc, value) => acc.plus(value.amount), BN(0), pendingToAddress) + const confirmedToAddress = _.filter( + it => it.address === toAddress, + transferRes.in ?? [], + ) + const pendingToAddress = _.filter( + it => it.address === toAddress, + transferRes.pool ?? [], + ) + const confirmed = _.reduce( + (acc, value) => acc.plus(value.amount), + BN(0), + confirmedToAddress, + ) + const pending = _.reduce( + (acc, value) => acc.plus(value.amount), + BN(0), + pendingToAddress, + ) - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } return { receivedCryptoAtoms: pending, status: 'notSeen' } }) .catch(err => handleError(err, 'getStatus')) } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => refreshWallet()) - .then(() => Promise.all([ - fetch('get_balance', { account_index: 0, address_indices: [0] }), - fetch('create_address', { account_index: 0 }), - fetch('get_transfers', { pool: true, account_index: 0 }) - ])) + .then(() => + Promise.all([ + fetch('get_balance', { account_index: 0, address_indices: [0] }), + fetch('create_address', { account_index: 0 }), + fetch('get_transfers', { pool: true, account_index: 0 }), + ]), + ) .then(([balanceRes, addressRes, transferRes]) => { - const memPoolBalance = _.reduce((acc, value) => acc.plus(value.amount), BN(0), transferRes.pool) + const memPoolBalance = _.reduce( + (acc, value) => acc.plus(value.amount), + BN(0), + transferRes.pool, + ) return { - fundingPendingBalance: BN(balanceRes.balance).minus(balanceRes.unlocked_balance).plus(memPoolBalance), + fundingPendingBalance: BN(balanceRes.balance) + .minus(balanceRes.unlocked_balance) + .plus(memPoolBalance), fundingConfirmedBalance: BN(balanceRes.unlocked_balance), - fundingAddress: addressRes.address + fundingAddress: addressRes.address, } }) .catch(err => handleError(err, 'newFunding')) } -function cryptoNetwork (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => { - switch(parseInt(rpcConfig().port, 10)) { - case 18082: - return 'main' - case 28082: - return 'test' - case 38083: - return 'stage' - default: - return '' - } - }) -} - -function checkBlockchainStatus (cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => { - try { - const config = jsonRpc.parseConf(configPath) - - // Daemon uses a different connection of the wallet - const rpcConfig = { - username: config['rpc-login'].split(':')[0], - password: config['rpc-login'].split(':')[1], - port: cryptoRec.defaultPort - } - - return jsonRpc.fetchDigest(rpcConfig, 'get_info') - .then(res => !!res.synchronized ? 'ready' : 'syncing') - } catch (err) { - throw new Error('XMR daemon is currently not installed') +function cryptoNetwork(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => { + switch (parseInt(rpcConfig().port, 10)) { + case 18082: + return 'main' + case 28082: + return 'test' + case 38083: + return 'stage' + default: + return '' } }) } -function getTxHashesByAddress (cryptoCode, address) { +function checkBlockchainStatus(cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => { + try { + const config = jsonRpc.parseConf(configPath) + + // Daemon uses a different connection of the wallet + const rpcConfig = { + username: config['rpc-login'].split(':')[0], + password: config['rpc-login'].split(':')[1], + port: cryptoRec.defaultPort, + } + + return jsonRpc + .fetchDigest(rpcConfig, 'get_info') + .then(res => (res.synchronized ? 'ready' : 'syncing')) + } catch (err) { + throw new Error(`XMR daemon is currently not installed. ${err}`) + } + }) +} + +function getTxHashesByAddress(cryptoCode, address) { checkCryptoCode(cryptoCode) .then(() => refreshWallet()) .then(() => fetch('get_address_index', { address: address })) - .then(addressRes => fetch('get_transfers', { in: true, pool: true, pending: true, account_index: addressRes.index.major, subaddr_indices: [addressRes.index.minor] })) + .then(addressRes => + fetch('get_transfers', { + in: true, + pool: true, + pending: true, + account_index: addressRes.index.major, + subaddr_indices: [addressRes.index.minor], + }), + ) .then(_.map(({ txid }) => txid)) } @@ -250,5 +299,5 @@ module.exports = { newFunding, cryptoNetwork, checkBlockchainStatus, - getTxHashesByAddress + getTxHashesByAddress, } diff --git a/packages/server/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js b/packages/server/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js deleted file mode 100644 index afb0aa74..00000000 --- a/packages/server/lib/plugins/wallet/pazuz-wallet/pazuz-wallet.js +++ /dev/null @@ -1,98 +0,0 @@ -const https = require('https') -const BN = require('../../../bn') -const E = require('../../../error') -const _ = require('lodash/fp') - -const SUPPORTED_COINS = ['BTC'] - -const axios = require('axios').create({ - // TODO: get rejectUnauthorized true to work - baseURL: `${process.env.WALLET_URL}/api`, - httpsAgent: new https.Agent({ - rejectUnauthorized: false - }) -}) - -const checkCryptoCode = (cryptoCode) => !_.includes(cryptoCode, SUPPORTED_COINS) - ? Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) - : Promise.resolve() - -function balance (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(() => { - return axios.post('/balance', { - cryptoCode, - config: settings.config, - operatorId - }) - }) - .then(({ data }) => { - if (data.error) throw new Error(JSON.stringify(data.error)) - return new BN(data.balance) - }) -} - -function sendCoins (account, tx, settings, operatorId) { - const { cryptoCode } = tx - return checkCryptoCode(cryptoCode) - .then(() => { - return axios.post('/sendCoins', { - tx, - config: settings.config, - operatorId - }) - }) - .then(({ data }) => { - if (data.error && data.error.errorCode === 'sc-001') throw new E.InsufficientFundsError() - else if (data.error) throw new Error(JSON.stringify(data.error)) - const fee = new BN(data.fee).decimalPlaces(0) - const txid = data.txid - return { txid, fee } - }) -} - -function newAddress (account, info, tx, settings, operatorId) { - return checkCryptoCode(info.cryptoCode) - .then(() => axios.post('/newAddress', { - info, - tx, - config: settings.config, - operatorId - })) - .then(({ data }) => { - if(data.error) throw new Error(JSON.stringify(data.error)) - return data.newAddress - }) -} - -function getStatus (account, tx, requested, settings, operatorId) { - return checkCryptoCode(tx.cryptoCode) - .then(() => axios.get(`/balance/${tx.toAddress}?cryptoCode=${tx.cryptoCode}`)) - .then(({ data }) => { - if (data.error) throw new Error(JSON.stringify(data.error)) - const confirmed = new BN(data.confirmedBalance) - const pending = new BN(data.pendingBalance) - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) -} - -function newFunding (account, cryptoCode, settings, operatorId) { - throw new E.NotImplementedError() -} - -function sweep (account, txId, cryptoCode, hdIndex, settings, operatorId) { - throw new E.NotImplementedError() -} - -module.exports = { - balance, - sendCoins, - newAddress, - newFunding, - getStatus, - sweep, - supportsHd: true, -} diff --git a/packages/server/lib/plugins/wallet/tron/base.js b/packages/server/lib/plugins/wallet/tron/base.js index c3a854f5..aa30325f 100644 --- a/packages/server/lib/plugins/wallet/tron/base.js +++ b/packages/server/lib/plugins/wallet/tron/base.js @@ -14,46 +14,54 @@ const SWEEP_QUEUE = new PQueue({ interval: 250, }) -function checkCryptoCode (cryptoCode) { +function checkCryptoCode(cryptoCode) { if (cryptoCode === 'TRX' || coins.utils.isTrc20Token(cryptoCode)) { return Promise.resolve(cryptoCode) } return Promise.reject(new Error('cryptoCode must be TRX')) } -function defaultWallet (account) { +function defaultWallet(account) { const mnemonic = account.mnemonic if (!mnemonic) throw new Error('No mnemonic seed!') - return TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), `${DEFAULT_PREFIX_PATH}/0`) + return TronWeb.fromMnemonic( + mnemonic.replace(/[\r\n]/gm, ' ').trim(), + `${DEFAULT_PREFIX_PATH}/0`, + ) } -function paymentWallet (account, index) { +function paymentWallet(account, index) { const mnemonic = account.mnemonic if (!mnemonic) throw new Error('No mnemonic seed!') - return TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), `${PAYMENT_PREFIX_PATH}/${index}`) + return TronWeb.fromMnemonic( + mnemonic.replace(/[\r\n]/gm, ' ').trim(), + `${PAYMENT_PREFIX_PATH}/${index}`, + ) } -function newAddress (account, info, tx, settings, operatorId) { +function newAddress(account, info) { const wallet = paymentWallet(account, info.hdIndex) return Promise.resolve(wallet.address) } -function defaultAddress (account) { +function defaultAddress(account) { return defaultWallet(account).address } -function balance (account, cryptoCode, settings, operatorId) { - return checkCryptoCode(cryptoCode) - .then(code => confirmedBalance(defaultAddress(account), code)) +function balance(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(code => + confirmedBalance(defaultAddress(account), code), + ) } const confirmedBalance = (address, cryptoCode) => _balance(address, cryptoCode) const _balance = async (address, cryptoCode) => { if (coins.utils.isTrc20Token(cryptoCode)) { - const contractAddress = coins.utils.getTrc20Token(cryptoCode).contractAddress + const contractAddress = + coins.utils.getTrc20Token(cryptoCode).contractAddress const { abi } = await tronWeb.trx.getContract(contractAddress) const contract = tronWeb.contract(abi.entrys, contractAddress) @@ -70,7 +78,12 @@ const sendCoins = async (account, tx) => { const isTrc20Token = coins.utils.isTrc20Token(cryptoCode) const txFunction = isTrc20Token ? generateTrc20Tx : generateTx - const rawTx = await txFunction(toAddress, defaultWallet(account), cryptoAtoms.toString(), cryptoCode) + const rawTx = await txFunction( + toAddress, + defaultWallet(account), + cryptoAtoms.toString(), + cryptoCode, + ) let response = null @@ -97,16 +110,26 @@ const generateTrc20Tx = async (toAddress, wallet, amount, cryptoCode) => { const functionSelector = 'transfer(address,uint256)' const parameters = [ { type: 'address', value: tronWeb.address.toHex(toAddress) }, - { type: 'uint256', value: amount } + { type: 'uint256', value: amount }, ] - const tx = await tronWeb.transactionBuilder.triggerSmartContract(contractAddress, functionSelector, {}, parameters, wallet.address) + const tx = await tronWeb.transactionBuilder.triggerSmartContract( + contractAddress, + functionSelector, + {}, + parameters, + wallet.address, + ) return tronWeb.trx.sign(tx.transaction, wallet.privateKey.slice(2)) } const generateTx = async (toAddress, wallet, amount) => { - const transaction = await tronWeb.transactionBuilder.sendTrx(toAddress, amount, wallet.address) + const transaction = await tronWeb.transactionBuilder.sendTrx( + toAddress, + amount, + wallet.address, + ) const privateKey = wallet.privateKey @@ -114,21 +137,19 @@ const generateTx = async (toAddress, wallet, amount) => { return tronWeb.trx.sign(transaction, privateKey.slice(2)) } -function newFunding (account, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(code => { - const fundingAddress = defaultAddress(account) +function newFunding(account, cryptoCode) { + return checkCryptoCode(cryptoCode).then(code => { + const fundingAddress = defaultAddress(account) - return confirmedBalance(fundingAddress, code) - .then((balance) => ({ - fundingPendingBalance: BN(0), - fundingConfirmedBalance: balance, - fundingAddress - })) - }) + return confirmedBalance(fundingAddress, code).then(balance => ({ + fundingPendingBalance: BN(0), + fundingConfirmedBalance: balance, + fundingAddress, + })) + }) } -function sweep (account, txId, cryptoCode, hdIndex) { +function sweep(account, txId, cryptoCode, hdIndex) { const wallet = paymentWallet(account, hdIndex) const fromAddress = wallet.address const isTrc20Token = coins.utils.isTrc20Token(cryptoCode) @@ -138,7 +159,12 @@ function sweep (account, txId, cryptoCode, hdIndex) { return SWEEP_QUEUE.add(async () => { const r = await confirmedBalance(fromAddress, cryptoCode) if (r.eq(0)) return - const signedTx = await txFunction(defaultAddress(account), wallet, r.toString(), cryptoCode) + const signedTx = await txFunction( + defaultAddress(account), + wallet, + r.toString(), + cryptoCode, + ) let response = null try { response = await tronWeb.trx.sendRawTransaction(signedTx) @@ -157,24 +183,28 @@ function connect(account) { const apiKey = account.apiKey tronWeb = new TronWeb({ fullHost: endpoint, - headers: { "TRON-PRO-API-KEY": apiKey }, - privateKey: '01' + headers: { 'TRON-PRO-API-KEY': apiKey }, + privateKey: '01', }) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(code => confirmedBalance(toAddress, code)) - .then((confirmed) => { - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - if (confirmed.gt(0)) return { receivedCryptoAtoms: confirmed, status: 'insufficientFunds' } + .then(confirmed => { + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (confirmed.gt(0)) + return { receivedCryptoAtoms: confirmed, status: 'insufficientFunds' } return { receivedCryptoAtoms: 0, status: 'notSeen' } }) } -function getTxHashesByAddress (cryptoCode, address) { - throw new Error(`Transactions hash retrieval is not implemented for this coin!`) +function getTxHashesByAddress() { + throw new Error( + `Transactions hash retrieval is not implemented for this coin!`, + ) } module.exports = { diff --git a/packages/server/lib/plugins/wallet/trongrid/trongrid.js b/packages/server/lib/plugins/wallet/trongrid/trongrid.js index 4d21a522..9244c0ca 100644 --- a/packages/server/lib/plugins/wallet/trongrid/trongrid.js +++ b/packages/server/lib/plugins/wallet/trongrid/trongrid.js @@ -3,7 +3,7 @@ const base = require('../tron/base') const NAME = 'trongrid' -function run (account) { +function run(account) { const endpoint = 'https://api.trongrid.io' base.connect({ ...account, endpoint }) diff --git a/packages/server/lib/plugins/wallet/zcashd/zcashd.js b/packages/server/lib/plugins/wallet/zcashd/zcashd.js index 1122bf18..6fb95508 100644 --- a/packages/server/lib/plugins/wallet/zcashd/zcashd.js +++ b/packages/server/lib/plugins/wallet/zcashd/zcashd.js @@ -1,5 +1,5 @@ const _ = require('lodash/fp') -const pRetry = require('p-retry') +const pRetry = require('p-retry') const jsonRpc = require('../../common/json-rpc') const { utils: coinUtils } = require('@lamassu/coins') @@ -12,11 +12,11 @@ const unitScale = cryptoRec.unitScale const rpcConfig = jsonRpc.rpcConfig(cryptoRec) -function fetch (method, params) { +function fetch(method, params) { return jsonRpc.fetch(rpcConfig, method, params) } -function errorHandle (e) { +function errorHandle(e) { const err = JSON.parse(e.message) switch (err.code) { case -6: @@ -26,134 +26,152 @@ function errorHandle (e) { } } -function checkCryptoCode (cryptoCode) { - if (cryptoCode !== 'ZEC') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) +function checkCryptoCode(cryptoCode) { + if (cryptoCode !== 'ZEC') + return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) return Promise.resolve() } -function accountBalance (cryptoCode) { +function accountBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } -function accountUnconfirmedBalance (cryptoCode) { +function accountUnconfirmedBalance(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getwalletinfo')) - .then(({ unconfirmed_balance: balance }) => new BN(balance).shiftedBy(unitScale).decimalPlaces(0)) + .then(({ unconfirmed_balance: balance }) => + new BN(balance).shiftedBy(unitScale).decimalPlaces(0), + ) } // We want a balance that includes all spends (0 conf) but only deposits that // have at least 1 confirmation. getbalance does this for us automatically. -function balance (account, cryptoCode, settings, operatorId) { +function balance(account, cryptoCode) { return accountBalance(cryptoCode) } -function sendCoins (account, tx, settings, operatorId) { +function sendCoins(account, tx) { const { toAddress, cryptoAtoms, cryptoCode } = tx const coins = cryptoAtoms.shiftedBy(-unitScale).toFixed(8) const checkSendStatus = function (opid) { return new Promise((resolve, reject) => { - fetch('z_getoperationstatus', [[opid]]) - .then(res => { - const status = _.get('status', res[0]) - switch (status) { - case 'success': - resolve(res[0]) - break - case 'failed': - throw new pRetry.AbortError(res[0].error) - case 'executing': - reject(new Error('operation still executing')) - break - } - }) + fetch('z_getoperationstatus', [[opid]]).then(res => { + const status = _.get('status', res[0]) + switch (status) { + case 'success': + resolve(res[0]) + break + case 'failed': + throw new pRetry.AbortError(res[0].error) + case 'executing': + reject(new Error('operation still executing')) + break + } + }) }) } - const checker = opid => pRetry(() => checkSendStatus(opid), { retries: 20, minTimeout: 300, factor: 1.05 }) + const checker = opid => + pRetry(() => checkSendStatus(opid), { + retries: 20, + minTimeout: 300, + factor: 1.05, + }) return checkCryptoCode(cryptoCode) - .then(() => fetch('z_sendmany', ['ANY_TADDR', [{ address: toAddress, amount: coins }], null, null, 'NoPrivacy'])) + .then(() => + fetch('z_sendmany', [ + 'ANY_TADDR', + [{ address: toAddress, amount: coins }], + null, + null, + 'NoPrivacy', + ]), + ) .then(checker) - .then((res) => { + .then(res => { return { fee: _.get('params.fee', res), - txid: _.get('result.txid', res) + txid: _.get('result.txid', res), } }) - .then((pickedObj) => { + .then(pickedObj => { return { fee: new BN(pickedObj.fee).abs().shiftedBy(unitScale).decimalPlaces(0), - txid: pickedObj.txid + txid: pickedObj.txid, } }) .catch(errorHandle) } -function newAddress (account, info, tx, settings, operatorId) { - return checkCryptoCode(info.cryptoCode) - .then(() => fetch('getnewaddress')) +function newAddress(account, info) { + return checkCryptoCode(info.cryptoCode).then(() => fetch('getnewaddress')) } -function addressBalance (address, confs) { - return fetch('getreceivedbyaddress', [address, confs]) - .then(r => new BN(r).shiftedBy(unitScale).decimalPlaces(0)) +function addressBalance(address, confs) { + return fetch('getreceivedbyaddress', [address, confs]).then(r => + new BN(r).shiftedBy(unitScale).decimalPlaces(0), + ) } -function confirmedBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 1)) +function confirmedBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 1)) } -function pendingBalance (address, cryptoCode) { - return checkCryptoCode(cryptoCode) - .then(() => addressBalance(address, 0)) +function pendingBalance(address, cryptoCode) { + return checkCryptoCode(cryptoCode).then(() => addressBalance(address, 0)) } -function getStatus (account, tx, requested, settings, operatorId) { +function getStatus(account, tx, requested) { const { toAddress, cryptoCode } = tx return checkCryptoCode(cryptoCode) .then(() => confirmedBalance(toAddress, cryptoCode)) .then(confirmed => { - if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' } + if (confirmed.gte(requested)) + return { receivedCryptoAtoms: confirmed, status: 'confirmed' } - return pendingBalance(toAddress, cryptoCode) - .then(pending => { - if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' } - if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } - return { receivedCryptoAtoms: pending, status: 'notSeen' } - }) + return pendingBalance(toAddress, cryptoCode).then(pending => { + if (pending.gte(requested)) + return { receivedCryptoAtoms: pending, status: 'authorized' } + if (pending.gt(0)) + return { receivedCryptoAtoms: pending, status: 'insufficientFunds' } + return { receivedCryptoAtoms: pending, status: 'notSeen' } + }) }) } -function newFunding (account, cryptoCode, settings, operatorId) { +function newFunding(account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => { const promises = [ accountUnconfirmedBalance(cryptoCode), accountBalance(cryptoCode), - newAddress(account, { cryptoCode }) + newAddress(account, { cryptoCode }), ] return Promise.all(promises) }) - .then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ - fundingPendingBalance, - fundingConfirmedBalance, - fundingAddress - })) + .then( + ([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({ + fundingPendingBalance, + fundingConfirmedBalance, + fundingAddress, + }), + ) } -function checkBlockchainStatus (cryptoCode) { +function checkBlockchainStatus(cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => fetch('getblockchaininfo')) - .then(res => !!res['initial_block_download_complete'] ? 'ready' : 'syncing') + .then(res => (res['initial_block_download_complete'] ? 'ready' : 'syncing')) } -function getTxHashesByAddress (cryptoCode, address) { - checkCryptoCode(cryptoCode) - .then(() => fetch('getaddresstxids', [address])) +function getTxHashesByAddress(cryptoCode, address) { + checkCryptoCode(cryptoCode).then(() => fetch('getaddresstxids', [address])) } module.exports = { @@ -163,5 +181,5 @@ module.exports = { getStatus, newFunding, checkBlockchainStatus, - getTxHashesByAddress + getTxHashesByAddress, } diff --git a/packages/server/lib/plugins/zero-conf/blockcypher/blockcypher.js b/packages/server/lib/plugins/zero-conf/blockcypher/blockcypher.js index ef48e61f..7d19dba5 100644 --- a/packages/server/lib/plugins/zero-conf/blockcypher/blockcypher.js +++ b/packages/server/lib/plugins/zero-conf/blockcypher/blockcypher.js @@ -5,44 +5,64 @@ const _ = require('lodash/fp') const { fetchRBF } = require('../../wallet/bitcoind/bitcoind') module.exports = { authorize } -function highConfidence (confidence, txref, txRBF) { +function highConfidence(confidence, txref, txRBF) { if (txref.double_spend) return 0 if (txRBF) return 0 - if (txref.confirmations > 0 || txref.confidence * 100 >= confidence) return txref.value + if (txref.confirmations > 0 || txref.confidence * 100 >= confidence) + return txref.value return 0 } -function authorize (account, toAddress, cryptoAtoms, cryptoCode, isBitcoindAvailable) { - return Promise.resolve() - .then(() => { - if (cryptoCode !== 'BTC') throw new Error('Unsupported crypto: ' + cryptoCode) +function authorize( + account, + toAddress, + cryptoAtoms, + cryptoCode, + isBitcoindAvailable, +) { + return Promise.resolve().then(() => { + if (cryptoCode !== 'BTC') + throw new Error('Unsupported crypto: ' + cryptoCode) - const query = qs.stringify({ - token: account.token, - includeConfidence: true - }) + const query = qs.stringify({ + token: account.token, + includeConfidence: true, + }) - const confidence = account.confidenceFactor - const isRBFEnabled = account.rbf - const url = `https://api.blockcypher.com/v1/btc/main/addrs/${toAddress}?${query}` + const confidence = account.confidenceFactor + const isRBFEnabled = account.rbf + const url = `https://api.blockcypher.com/v1/btc/main/addrs/${toAddress}?${query}` - return axios.get(url) - .then(r => { - const data = r.data - if (isBitcoindAvailable && isRBFEnabled && data.unconfirmed_txrefs) { - const promises = _.map(unconfirmedTxref => fetchRBF(unconfirmedTxref.tx_hash), data.unconfirmed_txrefs) - return Promise.all(promises) - .then(values => { - const unconfirmedTxsRBF = _.fromPairs(values) - const sumTxRefs = txrefs => _.sumBy(txref => highConfidence(confidence, txref, unconfirmedTxsRBF[txref.tx_hash]), txrefs) - const authorizedValue = sumTxRefs(data.txrefs) + sumTxRefs(data.unconfirmed_txrefs) - return cryptoAtoms.lte(authorizedValue) - }) - } - - const sumTxRefs = txrefs => _.sumBy(txref => highConfidence(confidence, txref), txrefs) - const authorizedValue = sumTxRefs(data.txrefs) + sumTxRefs(data.unconfirmed_txrefs) + return axios.get(url).then(r => { + const data = r.data + if (isBitcoindAvailable && isRBFEnabled && data.unconfirmed_txrefs) { + const promises = _.map( + unconfirmedTxref => fetchRBF(unconfirmedTxref.tx_hash), + data.unconfirmed_txrefs, + ) + return Promise.all(promises).then(values => { + const unconfirmedTxsRBF = _.fromPairs(values) + const sumTxRefs = txrefs => + _.sumBy( + txref => + highConfidence( + confidence, + txref, + unconfirmedTxsRBF[txref.tx_hash], + ), + txrefs, + ) + const authorizedValue = + sumTxRefs(data.txrefs) + sumTxRefs(data.unconfirmed_txrefs) return cryptoAtoms.lte(authorizedValue) }) + } + + const sumTxRefs = txrefs => + _.sumBy(txref => highConfidence(confidence, txref), txrefs) + const authorizedValue = + sumTxRefs(data.txrefs) + sumTxRefs(data.unconfirmed_txrefs) + return cryptoAtoms.lte(authorizedValue) }) + }) } diff --git a/packages/server/lib/plugins/zero-conf/mock-zero-conf/mock-zero-conf.js b/packages/server/lib/plugins/zero-conf/mock-zero-conf/mock-zero-conf.js index d2276170..a4c713a3 100644 --- a/packages/server/lib/plugins/zero-conf/mock-zero-conf/mock-zero-conf.js +++ b/packages/server/lib/plugins/zero-conf/mock-zero-conf/mock-zero-conf.js @@ -1,11 +1,11 @@ -module.exports = {authorize} +module.exports = { authorize } -function authorize (account, toAddress, cryptoAtoms, cryptoCode) { - return Promise.resolve() - .then(() => { - if (cryptoCode !== 'BTC') throw new Error('Unsupported crypto: ' + cryptoCode) +function authorize(account, toAddress, cryptoAtoms, cryptoCode) { + return Promise.resolve().then(() => { + if (cryptoCode !== 'BTC') + throw new Error('Unsupported crypto: ' + cryptoCode) - const isAuthorized = false - return isAuthorized - }) + const isAuthorized = false + return isAuthorized + }) } diff --git a/packages/server/lib/poller.js b/packages/server/lib/poller.js index fefb8d16..227707a2 100644 --- a/packages/server/lib/poller.js +++ b/packages/server/lib/poller.js @@ -31,7 +31,6 @@ const PRUNE_MACHINES_HEARTBEAT = 1 * T.day const TRANSACTION_BATCH_LIFECYCLE = 20 * T.minutes const TICKER_RATES_INTERVAL = 59 * T.seconds const FAILED_SCANS_INTERVAL = 1 * T.day -const EXTERNAL_COMPLIANCE_INTERVAL = 1 * T.minutes const CHECK_NOTIFICATION_INTERVAL = 20 * T.seconds const PENDING_INTERVAL = 10 * T.seconds @@ -44,24 +43,24 @@ const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR const FAST_QUEUE = new Queue({ concurrent: 600, - interval: FAST_QUEUE_WAIT + interval: FAST_QUEUE_WAIT, }) const SLOW_QUEUE = new Queue({ concurrent: 10, - interval: SLOW_QUEUE_WAIT + interval: SLOW_QUEUE_WAIT, }) const QUEUE = { FAST: FAST_QUEUE, - SLOW: SLOW_QUEUE + SLOW: SLOW_QUEUE, } const cachedVariables = new NodeCache({ stdTTL: CACHE_ENTRY_TTL, checkperiod: CACHE_ENTRY_TTL, deleteOnExpire: false, - useClones: false // pass values by reference instead of cloning + useClones: false, // pass values by reference instead of cloning }) cachedVariables.on('expired', (key, val) => { @@ -72,61 +71,74 @@ cachedVariables.on('expired', (key, val) => { } }) -db.connect({ direct: true }).then(sco => { - sco.client.on('notification', () => { - return reload() - }) - return sco.none('LISTEN $1:name', 'reload') -}).catch(console.error) - -function reload () { - return settingsLoader.loadLatest() - .then(settings => { - const pi = plugins(settings) - cachedVariables.set('public', { settings, pi, isReloading: false }) - logger.debug(`Settings for schema 'public' reloaded in poller`) - return updateAndLoadSanctions() +db.connect({ direct: true }) + .then(sco => { + sco.client.on('notification', () => { + return reload() }) + return sco.none('LISTEN $1:name', 'reload') + }) + .catch(console.error) + +function reload() { + return settingsLoader.loadLatest().then(settings => { + const pi = plugins(settings) + cachedVariables.set('public', { settings, pi, isReloading: false }) + logger.debug(`Settings for schema 'public' reloaded in poller`) + return updateAndLoadSanctions() + }) } -function pi () { return cachedVariables.get('public').pi } -function settings () { return cachedVariables.get('public').settings } +function pi() { + return cachedVariables.get('public').pi +} +function settings() { + return cachedVariables.get('public').settings +} -function initialSanctionsDownload () { +function initialSanctionsDownload() { const structs = sanctions.getStructs() - const isEmptyStructs = _.isNil(structs) || _.flow(_.values, _.all(_.isEmpty))(structs) + const isEmptyStructs = + _.isNil(structs) || _.flow(_.values, _.all(_.isEmpty))(structs) if (!isEmptyStructs) return Promise.resolve() return updateAndLoadSanctions() } -function updateAndLoadSanctions () { +function updateAndLoadSanctions() { const triggers = configManager.getTriggers(settings().config) const hasSanctions = complianceTriggers.hasSanctions(triggers) if (!hasSanctions) return Promise.resolve() logger.info('Updating sanctions database...') - return sanctionsUpdater.update() + return sanctionsUpdater + .update() .then(sanctions.load) .then(() => logger.info('Sanctions database updated.')) } -function updateCoinAtmRadar () { - return pi().getRawRates() +function updateCoinAtmRadar() { + return pi() + .getRawRates() .then(rates => coinAtmRadar.update(rates, settings())) } const readdir = dirpath => - fs.readdir(dirpath, { withFileTypes: true }) + fs + .readdir(dirpath, { withFileTypes: true }) .then(_.map(entry => _.set('path', path.join(dirpath, entry.name), entry))) const readdirRec = rootPath => readdir(rootPath) - .then(entries => Promise.all( - entries.map(entry => entry.isDirectory() ? readdirRec(entry.path) : [entry]) - )) + .then(entries => + Promise.all( + entries.map(entry => + entry.isDirectory() ? readdirRec(entry.path) : [entry], + ), + ), + ) .then(_.flatten) const stat = path => fs.stat(path).then(_.set('path', path)) @@ -134,15 +146,19 @@ const pathComponents = p => path.normalize(p).split(path.sep) // @see lib/customers.js:updateIdCardData() const cleanOldFailedPDF417Scans = () => { - const matcher = (c, pat) => typeof pat === 'function' ? pat(c) : c === pat - const PDF417ScanPathPattern = _.concat( - pathComponents(OPERATOR_DATA_DIR), - ["id-operator", s => /* customerid*/ true, "idcarddata", fname => path.extname(fname) === 'jpg'] - ) + const matcher = (c, pat) => (typeof pat === 'function' ? pat(c) : c === pat) + const PDF417ScanPathPattern = _.concat(pathComponents(OPERATOR_DATA_DIR), [ + 'id-operator', + () => /* customerid*/ true, + 'idcarddata', + fname => path.extname(fname) === 'jpg', + ]) const isPDF417Scan = entry => { entry = pathComponents(entry.path) - return entry.length === PDF417ScanPathPattern.length - && _.isMatchWith(matcher, PDF417ScanPathPattern, pathComponents(entry.path)) + return ( + entry.length === PDF417ScanPathPattern.length && + _.isMatchWith(matcher, PDF417ScanPathPattern, pathComponents(entry.path)) + ) } let old = new Date() @@ -153,18 +169,20 @@ const cleanOldFailedPDF417Scans = () => { const isOld = filestat => filestat.mtimeMs < old return readdirRec(path.join(OPERATOR_DATA_DIR, 'id-operator')) - .then(entries => Promise.all( - entries - .filter(entry => entry.isFile() && isPDF417Scan(entry)) - .map(entry => stat(entry.path)) - )) - .then(filestats => Promise.all( - filestats - .filter(isOld) - .map(_.flow(_.get(['path']), fs.unlink)) - )) + .then(entries => + Promise.all( + entries + .filter(entry => entry.isFile() && isPDF417Scan(entry)) + .map(entry => stat(entry.path)), + ), + ) + .then(filestats => + Promise.all( + filestats.filter(isOld).map(_.flow(_.get(['path']), fs.unlink)), + ), + ) .catch(err => { - console.log("Error cleaning up failed PDF417 scans:", err) + console.log('Error cleaning up failed PDF417 scans:', err) }) } @@ -179,25 +197,30 @@ const cleanOldFailedQRScans = () => { } return readdirRec(path.join(OPERATOR_DATA_DIR, 'failedQRScans')) - .then(entries => Promise.all( - entries - .filter(entry => entry.isFile() && isOld(entry.path)) - .map(entry => fs.unlink(entry.path)) - )) + .then(entries => + Promise.all( + entries + .filter(entry => entry.isFile() && isOld(entry.path)) + .map(entry => fs.unlink(entry.path)), + ), + ) .catch(err => { - console.log("Error cleaning up failed QR scans:", err) + console.log('Error cleaning up failed QR scans:', err) }) } -function setup () { - return settingsLoader.loadLatest().then(settings => { - const pi = plugins(settings) - cachedVariables.set('public', { settings, pi, isReloading: false }) - return doPolling() - }).catch(console.error) +function setup() { + return settingsLoader + .loadLatest() + .then(settings => { + const pi = plugins(settings) + cachedVariables.set('public', { settings, pi, isReloading: false }) + return doPolling() + }) + .catch(console.error) } -function recursiveTimeout (func, timeout, ...vars) { +function recursiveTimeout(func, timeout, ...vars) { setTimeout(() => { let promise = null @@ -216,11 +239,11 @@ function recursiveTimeout (func, timeout, ...vars) { }, timeout) } -function addToQueue (func, interval, queue, ...vars) { +function addToQueue(func, interval, queue, ...vars) { recursiveTimeout(func, interval, ...vars) } -function doPolling () { +function doPolling() { pi().executeTrades() pi().clearOldLogs() cashOutTx.monitorLiveIncoming(settings()) @@ -232,20 +255,60 @@ function doPolling () { addToQueue(pi().getRawRates, TICKER_RATES_INTERVAL, QUEUE.FAST) addToQueue(pi().executeTrades, TRADE_INTERVAL, QUEUE.FAST) - addToQueue(cashOutTx.monitorLiveIncoming, LIVE_INCOMING_TX_INTERVAL, QUEUE.FAST, settings) - addToQueue(cashOutTx.monitorStaleIncoming, INCOMING_TX_INTERVAL, QUEUE.FAST, settings) - addToQueue(cashOutTx.monitorUnnotified, UNNOTIFIED_INTERVAL, QUEUE.FAST, settings) + addToQueue( + cashOutTx.monitorLiveIncoming, + LIVE_INCOMING_TX_INTERVAL, + QUEUE.FAST, + settings, + ) + addToQueue( + cashOutTx.monitorStaleIncoming, + INCOMING_TX_INTERVAL, + QUEUE.FAST, + settings, + ) + addToQueue( + cashOutTx.monitorUnnotified, + UNNOTIFIED_INTERVAL, + QUEUE.FAST, + settings, + ) addToQueue(cashInTx.monitorPending, PENDING_INTERVAL, QUEUE.FAST, settings) - addToQueue(processBatches, UNNOTIFIED_INTERVAL, QUEUE.FAST, settings, TRANSACTION_BATCH_LIFECYCLE) + addToQueue( + processBatches, + UNNOTIFIED_INTERVAL, + QUEUE.FAST, + settings, + TRANSACTION_BATCH_LIFECYCLE, + ) addToQueue(pi().sweepHd, SWEEP_HD_INTERVAL, QUEUE.FAST, settings) addToQueue(pi().clearOldLogs, LOGS_CLEAR_INTERVAL, QUEUE.SLOW) - addToQueue(notifier.checkNotification, CHECK_NOTIFICATION_INTERVAL, QUEUE.FAST, pi) - addToQueue(initialSanctionsDownload, SANCTIONS_INITIAL_DOWNLOAD_INTERVAL, QUEUE.SLOW) + addToQueue( + notifier.checkNotification, + CHECK_NOTIFICATION_INTERVAL, + QUEUE.FAST, + pi, + ) + addToQueue( + initialSanctionsDownload, + SANCTIONS_INITIAL_DOWNLOAD_INTERVAL, + QUEUE.SLOW, + ) addToQueue(updateAndLoadSanctions, SANCTIONS_UPDATE_INTERVAL, QUEUE.SLOW) addToQueue(updateCoinAtmRadar, RADAR_UPDATE_INTERVAL, QUEUE.SLOW) - addToQueue(pi().pruneMachinesHeartbeat, PRUNE_MACHINES_HEARTBEAT, QUEUE.SLOW, settings) + addToQueue( + pi().pruneMachinesHeartbeat, + PRUNE_MACHINES_HEARTBEAT, + QUEUE.SLOW, + settings, + ) addToQueue(cleanOldFailedQRScans, FAILED_SCANS_INTERVAL, QUEUE.SLOW, settings) - addToQueue(cleanOldFailedPDF417Scans, FAILED_SCANS_INTERVAL, QUEUE.SLOW, settings) + addToQueue( + cleanOldFailedPDF417Scans, + FAILED_SCANS_INTERVAL, + QUEUE.SLOW, + settings, + ) } module.exports = { setup, reload } diff --git a/packages/server/lib/postgresql_interface.js b/packages/server/lib/postgresql_interface.js index 014d52fa..65b5f1be 100644 --- a/packages/server/lib/postgresql_interface.js +++ b/packages/server/lib/postgresql_interface.js @@ -2,62 +2,76 @@ const _ = require('lodash/fp') const db = require('./db') const pgp = require('pg-promise')() -function getInsertQuery (tableName, fields) { +function getInsertQuery(tableName, fields) { // outputs string like: '$1, $2, $3...' with proper No of items - const placeholders = fields.map(function (_, i) { - return '$' + (i + 1) - }).join(', ') + const placeholders = fields + .map(function (_, i) { + return '$' + (i + 1) + }) + .join(', ') - const query = 'INSERT INTO ' + tableName + - ' (' + fields.join(', ') + ')' + + const query = + 'INSERT INTO ' + + tableName + + ' (' + + fields.join(', ') + + ')' + ' VALUES' + - ' (' + placeholders + ')' + ' (' + + placeholders + + ')' return query } -exports.recordDeviceEvent = function recordDeviceEvent (deviceId, event) { - const sql = 'INSERT INTO device_events (device_id, event_type, ' + +exports.recordDeviceEvent = function recordDeviceEvent(deviceId, event) { + const sql = + 'INSERT INTO device_events (device_id, event_type, ' + 'note, device_time) VALUES ($1, $2, $3, $4)' - const values = [deviceId, event.eventType, event.note, - event.deviceTime] + const values = [deviceId, event.eventType, event.note, event.deviceTime] return db.none(sql, values) } -exports.cassetteCounts = function cassetteCounts (deviceId) { - const sql = 'SELECT cassette1, cassette2, cassette3, cassette4, number_of_cassettes FROM devices ' + +exports.cassetteCounts = function cassetteCounts(deviceId) { + const sql = + 'SELECT cassette1, cassette2, cassette3, cassette4, number_of_cassettes FROM devices ' + 'WHERE device_id=$1' - return db.one(sql, [deviceId]) - .then(row => { - const counts = [] - _.forEach(it => { + return db.one(sql, [deviceId]).then(row => { + const counts = [] + _.forEach( + it => { counts.push(row[`cassette${it + 1}`]) - }, _.times(_.identity(), row.number_of_cassettes)) + }, + _.times(_.identity(), row.number_of_cassettes), + ) - return { numberOfCassettes: row.number_of_cassettes, counts } - }) + return { numberOfCassettes: row.number_of_cassettes, counts } + }) } -exports.recyclerCounts = function recyclerCounts (deviceId) { - const sql = 'SELECT recycler1, recycler2, recycler3, recycler4, recycler5, recycler6, number_of_recyclers FROM devices ' + +exports.recyclerCounts = function recyclerCounts(deviceId) { + const sql = + 'SELECT recycler1, recycler2, recycler3, recycler4, recycler5, recycler6, number_of_recyclers FROM devices ' + 'WHERE device_id=$1' - return db.one(sql, [deviceId]) - .then(row => { - const counts = [] - _.forEach(it => { + return db.one(sql, [deviceId]).then(row => { + const counts = [] + _.forEach( + it => { counts.push(row[`recycler${it + 1}`]) - }, _.times(_.identity(), row.number_of_recyclers)) + }, + _.times(_.identity(), row.number_of_recyclers), + ) - return { numberOfRecyclers: row.number_of_recyclers, counts } - }) + return { numberOfRecyclers: row.number_of_recyclers, counts } + }) } // Note: since we only prune on insert, we'll always have // last known state. -exports.machineEvent = function machineEvent (rec) { +exports.machineEvent = function machineEvent(rec) { const fields = ['id', 'device_id', 'event_type', 'note', 'device_time'] const sql = getInsertQuery('machine_events', fields) const values = [rec.id, rec.deviceId, rec.eventType, rec.note, rec.deviceTime] @@ -65,11 +79,10 @@ exports.machineEvent = function machineEvent (rec) { const deleteSql = `delete from machine_events where created < now() - interval '1 days'` - return db.none(sql, values) - .then(() => db.none(deleteSql)) + return db.none(sql, values).then(() => db.none(deleteSql)) } -exports.machineEventsByIdBatch = function machineEventsByIdBatch (machineIds) { +exports.machineEventsByIdBatch = function machineEventsByIdBatch(machineIds) { const formattedIds = _.map(pgp.as.text, machineIds).join(',') const sql = `SELECT *, (EXTRACT(EPOCH FROM (now() - created))) * 1000 AS age FROM machine_events WHERE device_id IN ($1^) ORDER BY age ASC LIMIT 1` return db.any(sql, [formattedIds]).then(res => { @@ -79,8 +92,9 @@ exports.machineEventsByIdBatch = function machineEventsByIdBatch (machineIds) { }) } -exports.machineEvents = function machineEvents () { - const sql = 'SELECT *, (EXTRACT(EPOCH FROM (now() - created))) * 1000 AS age FROM machine_events' +exports.machineEvents = function machineEvents() { + const sql = + 'SELECT *, (EXTRACT(EPOCH FROM (now() - created))) * 1000 AS age FROM machine_events' return db.any(sql, []) } diff --git a/packages/server/lib/pp.js b/packages/server/lib/pp.js index 42ce480b..21407040 100644 --- a/packages/server/lib/pp.js +++ b/packages/server/lib/pp.js @@ -1,7 +1,7 @@ module.exports = function (label) { return function (o) { console.log(label) - console.log(require('util').inspect(o, {depth: null, colors: true})) + console.log(require('util').inspect(o, { depth: null, colors: true })) return o } } diff --git a/packages/server/lib/respond.js b/packages/server/lib/respond.js index 502e4ea3..3aa9a7e1 100644 --- a/packages/server/lib/respond.js +++ b/packages/server/lib/respond.js @@ -1,13 +1,18 @@ const _ = require('lodash/fp') const notifier = require('./notifier') -function respond (req, res, _body, _status) { +function respond(req, res, _body, _status) { const status = _status || 200 const body = _body || {} const customer = _.getOr({ sanctions: true }, ['customer'], body) // sanctions can be null for new customers so we can't use falsy checks if (customer.sanctions === false) { - notifier.notifyIfActive('compliance', 'sanctionsNotify', customer, req.body.phone) + notifier.notifyIfActive( + 'compliance', + 'sanctionsNotify', + customer, + req.body.phone, + ) } return res.status(status).json(body) } diff --git a/packages/server/lib/route-helpers.js b/packages/server/lib/route-helpers.js index ae4c173f..11e77d06 100644 --- a/packages/server/lib/route-helpers.js +++ b/packages/server/lib/route-helpers.js @@ -7,7 +7,7 @@ const BN = require('./bn') const TRANSACTION_EXPIRATION = T.day -function httpError (msg, code) { +function httpError(msg, code) { const err = new Error(msg) err.name = 'HTTPError' err.code = code || 500 @@ -15,18 +15,18 @@ function httpError (msg, code) { return err } -function stateChange (deviceId, deviceTime, rec) { +function stateChange(deviceId, deviceTime, rec) { const event = { id: rec.uuid, deviceId: deviceId, eventType: 'stateChange', note: JSON.stringify({ state: rec.state, isIdle: rec.isIdle }), - deviceTime: deviceTime + deviceTime: deviceTime, } return dbm.machineEvent(event) } -function toCashOutTx (row) { +function toCashOutTx(row) { if (!row) return null const keys = _.keys(row) @@ -45,18 +45,21 @@ function toCashOutTx (row) { return _.set('direction', 'cashOut', newObj) } -function fetchEmailOrPhoneTx (data, type) { +function fetchEmailOrPhoneTx(data, type) { const sql = `select * from cash_out_txs where ${type === 'email' ? 'email' : 'phone'}=$1 and dispense=$2 and (extract(epoch from (now() - created))) * 1000 < $3` const values = [data, false, TRANSACTION_EXPIRATION] - return db.any(sql, values) + return db + .any(sql, values) .then(_.map(toCashOutTx)) .then(txs => { const seenTxs = _.some(it => it.status !== 'notSeen', txs) - const confirmedTxs = txs.filter(tx => _.includes(tx.status, ['instant', 'confirmed'])) + const confirmedTxs = txs.filter(tx => + _.includes(tx.status, ['instant', 'confirmed']), + ) if (confirmedTxs.length > 0) { const reducer = (acc, val) => { return !acc || val.cryptoAtoms.gt(acc.cryptoAtoms) ? val : acc @@ -67,23 +70,26 @@ function fetchEmailOrPhoneTx (data, type) { return maxTx } - if (txs.length > 0 && !seenTxs) throw httpError('Transaction not seen', 411) - if (txs.length > 0 && seenTxs) throw httpError('Pending transactions', 412) + if (txs.length > 0 && !seenTxs) + throw httpError('Transaction not seen', 411) + if (txs.length > 0 && seenTxs) + throw httpError('Pending transactions', 412) throw httpError('No transactions', 404) }) } -function fetchEmailTx (email) { +function fetchEmailTx(email) { return fetchEmailOrPhoneTx(email, 'email') } -function fetchPhoneTx (phone) { +function fetchPhoneTx(phone) { return fetchEmailOrPhoneTx(phone, 'phone') } -function fetchStatusTx (txId, status) { +function fetchStatusTx(txId, status) { const sql = 'select * from cash_out_txs where id=$1' - return db.oneOrNone(sql, [txId]) + return db + .oneOrNone(sql, [txId]) .then(toCashOutTx) .then(tx => { if (!tx) throw httpError('No transaction', 404) @@ -97,5 +103,5 @@ module.exports = { fetchPhoneTx, fetchEmailTx, fetchStatusTx, - httpError + httpError, } diff --git a/packages/server/lib/routes.js b/packages/server/lib/routes.js index 110f9686..4c813c0f 100644 --- a/packages/server/lib/routes.js +++ b/packages/server/lib/routes.js @@ -47,7 +47,7 @@ const loadRoutes = async () => { '/customer', '/tx', '/verify_promo_code', - '/graphql' + '/graphql', ] // middleware setup @@ -59,11 +59,16 @@ const loadRoutes = async () => { morgan.token('bytesRead', (_req, res) => res.bytesRead) morgan.token('bytesWritten', (_req, res) => res.bytesWritten) - app.use(morgan(':method :url :status :response-time ms -- :bytesRead/:bytesWritten B', { stream: logger.stream })) + app.use( + morgan( + ':method :url :status :response-time ms -- :bytesRead/:bytesWritten B', + { stream: logger.stream }, + ), + ) app.use('/robots.txt', (req, res) => { res.type('text/plain') - res.send("User-agent: *\nDisallow: /") + res.send('User-agent: *\nDisallow: /') }) app.get('/', (req, res) => { @@ -106,12 +111,13 @@ const loadRoutes = async () => { // rejecting poll is enough to render the machine "stuck" app.use(rejectIncompatibleMachines) await graphQLServer.start() - app.use('/graphql', + app.use( + '/graphql', express.json(), expressMiddleware(graphQLServer, { context, }), - ); + ) app.use(errorHandler) app.use((req, res) => { @@ -121,5 +127,4 @@ const loadRoutes = async () => { return app } - module.exports = { loadRoutes } diff --git a/packages/server/lib/routes/cashboxRoutes.js b/packages/server/lib/routes/cashboxRoutes.js index 27d29f14..905c76f3 100644 --- a/packages/server/lib/routes/cashboxRoutes.js +++ b/packages/server/lib/routes/cashboxRoutes.js @@ -4,16 +4,13 @@ const router = express.Router() const cashbox = require('../cashbox-batches') const notifier = require('../notifier') -const { getMachine, setMachine, getMachineName } = require('../machine-loader') +const { getMachine, getMachineName } = require('../machine-loader') const { loadLatestConfig } = require('../new-settings-loader') const { getCashInSettings } = require('../new-config-manager') const { AUTOMATIC } = require('../constants') const logger = require('../logger') - -function cashboxRemoval (req, res, next) { - const operatorId = res.locals.operatorId - +function cashboxRemoval(req, res, next) { notifier.cashboxNotify(req.deviceId).catch(logger.error) return Promise.all([getMachine(req.deviceId), loadLatestConfig()]) @@ -22,16 +19,23 @@ function cashboxRemoval (req, res, next) { if (cashInSettings.cashboxReset !== AUTOMATIC) { return Promise.all([ cashbox.getMachineUnbatchedBills(req.deviceId), - getMachineName(req.deviceId) + getMachineName(req.deviceId), ]) } - return cashbox.createCashboxBatch(req.deviceId, machine.cashbox) - .then(batch => Promise.all([ - cashbox.getBatchById(batch.id), - getMachineName(batch.device_id) - ])) + return cashbox + .createCashboxBatch(req.deviceId, machine.cashbox) + .then(batch => + Promise.all([ + cashbox.getBatchById(batch.id), + getMachineName(batch.device_id), + ]), + ) }) - .then(([batch, machineName]) => res.status(200).send({ batch: _.merge(batch, { machineName }), status: 'OK' })) + .then(([batch, machineName]) => + res + .status(200) + .send({ batch: _.merge(batch, { machineName }), status: 'OK' }), + ) .catch(next) } diff --git a/packages/server/lib/routes/customerRoutes.js b/packages/server/lib/routes/customerRoutes.js index 8b4ea330..7e387bf0 100644 --- a/packages/server/lib/routes/customerRoutes.js +++ b/packages/server/lib/routes/customerRoutes.js @@ -26,10 +26,14 @@ const loyalty = require('../loyalty') const logger = require('../logger') const externalCompliance = require('../compliance-external') -function updateCustomerCustomInfoRequest (customerId, patch) { - const promise = _.isNil(patch.data) ? - Promise.resolve(null) : - customInfoRequestQueries.setCustomerDataViaMachine(customerId, patch.infoRequestId, patch) +function updateCustomerCustomInfoRequest(customerId, patch) { + const promise = _.isNil(patch.data) + ? Promise.resolve(null) + : customInfoRequestQueries.setCustomerDataViaMachine( + customerId, + patch.infoRequestId, + patch, + ) return promise.then(() => customers.getById(customerId)) } @@ -37,21 +41,24 @@ const createPendingManualComplianceNotifs = (settings, customer, deviceId) => { const customInfoRequests = _.reduce( (reqs, req) => _.set(req.info_request_id, req, reqs), {}, - _.get(['customInfoRequestData'], customer) + _.get(['customInfoRequestData'], customer), ) const isPending = field => - uuid.validate(field) ? - _.get([field, 'override'], customInfoRequests) === 'automatic' : - customer[`${field}At`] - && (!customer[`${field}OverrideAt`] - || customer[`${field}OverrideAt`].getTime() < customer[`${field}At`].getTime()) + uuid.validate(field) + ? _.get([field, 'override'], customInfoRequests) === 'automatic' + : customer[`${field}At`] && + (!customer[`${field}OverrideAt`] || + customer[`${field}OverrideAt`].getTime() < + customer[`${field}At`].getTime()) const unnestCustomTriggers = triggersAutomation => { - const customTriggers = _.fromPairs(_.map(({ id, type }) => [id, type], triggersAutomation.custom)) + const customTriggers = _.fromPairs( + _.map(({ id, type }) => [id, type], triggersAutomation.custom), + ) return _.flow( _.unset('custom'), - _.mapKeys(k => k === 'facephoto' ? 'frontCamera' : k), + _.mapKeys(k => (k === 'facephoto' ? 'frontCamera' : k)), _.assign(customTriggers), )(triggersAutomation) } @@ -61,22 +68,31 @@ const createPendingManualComplianceNotifs = (settings, customer, deviceId) => { const hasManualAutomation = triggersAutomation => _.any(isManual, _.values(triggersAutomation)) - configManager.getTriggersAutomation(customInfoRequestQueries.getCustomInfoRequests(true), settings.config) + configManager + .getTriggersAutomation( + customInfoRequestQueries.getCustomInfoRequests(true), + settings.config, + ) .then(triggersAutomation => { triggersAutomation = unnestCustomTriggers(triggersAutomation) if (!hasManualAutomation(triggersAutomation)) return const pendingFields = _.filter( field => isManual(triggersAutomation[field]) && isPending(field), - _.keys(triggersAutomation) + _.keys(triggersAutomation), ) if (!_.isEmpty(pendingFields)) - notifier.complianceNotify(settings, customer, deviceId, 'PENDING_COMPLIANCE') + notifier.complianceNotify( + settings, + customer, + deviceId, + 'PENDING_COMPLIANCE', + ) }) } -function updateCustomer (req, res, next) { +function updateCustomer(req, res, next) { const id = req.params.id const patch = req.body const deviceId = req.deviceId @@ -91,8 +107,11 @@ function updateCustomer (req, res, next) { .catch(next) } - customers.getById(id) - .then(customer => !customer ? Promise.reject(httpError('Not Found', 404)) : {}) + customers + .getById(id) + .then(customer => + !customer ? Promise.reject(httpError('Not Found', 404)) : {}, + ) .then(_.merge(patch)) .then(newPatch => customers.updatePhotoCard(id, newPatch)) .then(newPatch => customers.updateFrontCamera(id, newPatch)) @@ -104,37 +123,44 @@ function updateCustomer (req, res, next) { .catch(next) } -function updateIdCardData (req, res, next) { +function updateIdCardData(req, res, next) { const id = req.params.id const patch = req.body - customers.getById(id) + customers + .getById(id) .then(customer => { - if (!customer) { throw httpError('Not Found', 404) } - return customers.updateIdCardData(patch, id) - .then(() => customer) + if (!customer) { + throw httpError('Not Found', 404) + } + return customers.updateIdCardData(patch, id).then(() => customer) }) .then(customer => respond(req, res, { customer })) .catch(next) } -function triggerSanctions (req, res, next) { +function triggerSanctions(req, res, next) { const id = req.params.id - customers.getById(id) + customers + .getById(id) .then(customer => { - if (!customer) { throw httpError('Not Found', 404) } - return compliance.validationPatch(req.deviceId, customer) + if (!customer) { + throw httpError('Not Found', 404) + } + return compliance + .validationPatch(req.deviceId, customer) .then(patch => customers.update(id, patch)) }) .then(customer => respond(req, res, { customer })) .catch(next) } -function triggerBlock (req, res, next) { +function triggerBlock(req, res, next) { const id = req.params.id const settings = req.settings - customers.update(id, { authorizedOverride: 'blocked' }) + customers + .update(id, { authorizedOverride: 'blocked' }) .then(customer => { notifier.complianceNotify(settings, customer, req.deviceId, 'BLOCKED') return respond(req, res, { customer }) @@ -142,27 +168,39 @@ function triggerBlock (req, res, next) { .catch(next) } -function triggerSuspend (req, res, next) { +function triggerSuspend(req, res, next) { const id = req.params.id const triggerId = req.body.triggerId const settings = req.settings const triggers = configManager.getTriggers(req.settings.config) - const getSuspendDays = _.compose(_.get('suspensionDays'), _.find(_.matches({ id: triggerId }))) + const getSuspendDays = _.compose( + _.get('suspensionDays'), + _.find(_.matches({ id: triggerId })), + ) - const days = _.includes(triggerId, ['no-ff-camera', 'id-card-photo-disabled']) ? 1 : getSuspendDays(triggers) + const days = _.includes(triggerId, ['no-ff-camera', 'id-card-photo-disabled']) + ? 1 + : getSuspendDays(triggers) const suspensionDuration = intervalToDuration({ start: 0, end: T.day * days }) - customers.update(id, { suspendedUntil: add(suspensionDuration, new Date()) }) + customers + .update(id, { suspendedUntil: add(suspensionDuration, new Date()) }) .then(customer => { - notifier.complianceNotify(settings, customer, req.deviceId, 'SUSPENDED', days) + notifier.complianceNotify( + settings, + customer, + req.deviceId, + 'SUSPENDED', + days, + ) return respond(req, res, { customer }) }) .catch(next) } -function updateTxCustomerPhoto (req, res, next) { +function updateTxCustomerPhoto(req, res, next) { const customerId = req.params.id const txId = req.params.txId const tcPhotoData = req.body.tcPhotoData @@ -171,63 +209,81 @@ function updateTxCustomerPhoto (req, res, next) { Promise.all([customers.getById(customerId), txs.getTx(txId, direction)]) .then(([customer, tx]) => { if (!customer || !tx) return - return customers.updateTxCustomerPhoto(tcPhotoData) - .then(newPatch => txs.updateTxCustomerPhoto(customerId, txId, direction, newPatch)) + return customers + .updateTxCustomerPhoto(tcPhotoData) + .then(newPatch => + txs.updateTxCustomerPhoto(customerId, txId, direction, newPatch), + ) }) .then(() => respond(req, res, {})) .catch(next) } -function buildSms (data, receiptOptions) { - return Promise.all([getTx(data.session, data.txClass), loadLatestConfig()]) - .then(([tx, config]) => { - return Promise.all([customers.getCustomerById(tx.customer_id), machineLoader.getMachine(tx.device_id, config)]) - .then(([customer, deviceConfig]) => { - const formattedTx = _.mapKeys(_.camelCase)(tx) - const localeConfig = configManager.getLocale(formattedTx.deviceId, config) - const timezone = localeConfig.timezone +function buildSms(data, receiptOptions) { + return Promise.all([ + getTx(data.session, data.txClass), + loadLatestConfig(), + ]).then(([tx, config]) => { + return Promise.all([ + customers.getCustomerById(tx.customer_id), + machineLoader.getMachine(tx.device_id, config), + ]).then(([customer, deviceConfig]) => { + const formattedTx = _.mapKeys(_.camelCase)(tx) + const localeConfig = configManager.getLocale(formattedTx.deviceId, config) + const timezone = localeConfig.timezone - const cashInCommission = new BN(1).plus(new BN(formattedTx.commissionPercentage)) + const cashInCommission = new BN(1).plus( + new BN(formattedTx.commissionPercentage), + ) - const rate = new BN(formattedTx.rawTickerPrice).multipliedBy(cashInCommission).decimalPlaces(2) - const date = utcToZonedTime(timezone, zonedTimeToUtc(process.env.TZ, new Date())) - const dateString = `${date.toISOString().replace('T', ' ').slice(0, 19)}` + const rate = new BN(formattedTx.rawTickerPrice) + .multipliedBy(cashInCommission) + .decimalPlaces(2) + const date = utcToZonedTime( + timezone, + zonedTimeToUtc(process.env.TZ, new Date()), + ) + const dateString = `${date.toISOString().replace('T', ' ').slice(0, 19)}` - const data = { - operatorInfo: configManager.getOperatorInfo(config), - location: deviceConfig.machineLocation, - customerName: customer.name, - customerPhone: customer.phone, - session: formattedTx.id, - time: dateString, - direction: formattedTx.txClass === 'cashIn' ? 'Cash-in' : 'Cash-out', - fiat: `${formattedTx.fiat.toString()} ${formattedTx.fiatCode}`, - crypto: `${sms.toCryptoUnits(BN(formattedTx.cryptoAtoms), formattedTx.cryptoCode)} ${formattedTx.cryptoCode}`, - rate: `1 ${formattedTx.cryptoCode} = ${rate} ${formattedTx.fiatCode}`, - address: formattedTx.toAddress, - txId: formattedTx.txHash - } + const data = { + operatorInfo: configManager.getOperatorInfo(config), + location: deviceConfig.machineLocation, + customerName: customer.name, + customerPhone: customer.phone, + session: formattedTx.id, + time: dateString, + direction: formattedTx.txClass === 'cashIn' ? 'Cash-in' : 'Cash-out', + fiat: `${formattedTx.fiat.toString()} ${formattedTx.fiatCode}`, + crypto: `${sms.toCryptoUnits(BN(formattedTx.cryptoAtoms), formattedTx.cryptoCode)} ${formattedTx.cryptoCode}`, + rate: `1 ${formattedTx.cryptoCode} = ${rate} ${formattedTx.fiatCode}`, + address: formattedTx.toAddress, + txId: formattedTx.txHash, + } - return sms.formatSmsReceipt(data, receiptOptions) - }) + return sms.formatSmsReceipt(data, receiptOptions) }) + }) } -function sendSmsReceipt (req, res, next) { - const receiptOptions = _.omit(['active', 'sms'], configManager.getReceipt(req.settings.config)) - buildSms(req.body.data, receiptOptions) - .then(smsRequest => { - sms.sendMessage(req.settings, smsRequest) - .then(() => respond(req, res, {})) - .catch(next) - }) +function sendSmsReceipt(req, res, next) { + const receiptOptions = _.omit( + ['active', 'sms'], + configManager.getReceipt(req.settings.config), + ) + buildSms(req.body.data, receiptOptions).then(smsRequest => { + sms + .sendMessage(req.settings, smsRequest) + .then(() => respond(req, res, {})) + .catch(next) + }) } -function getExternalComplianceLink (req, res, next) { +function getExternalComplianceLink(req, res, next) { const customerId = req.query.customer const triggerId = req.query.trigger const isRetry = req.query.isRetry - if (_.isNil(customerId) || _.isNil(triggerId)) return next(httpError('Not Found', 404)) + if (_.isNil(customerId) || _.isNil(triggerId)) + return next(httpError('Not Found', 404)) const settings = req.settings const triggers = configManager.getTriggers(settings.config) @@ -235,17 +291,27 @@ function getExternalComplianceLink (req, res, next) { const externalService = trigger.externalService if (isRetry) { - return externalCompliance.createLink(settings, externalService, customerId) + return externalCompliance + .createLink(settings, externalService, customerId) .then(url => respond(req, res, { url })) } - return externalCompliance.createApplicant(settings, externalService, customerId) - .then(applicant => customers.addExternalCompliance(customerId, externalService, applicant.id)) - .then(() => externalCompliance.createLink(settings, externalService, customerId)) + return externalCompliance + .createApplicant(settings, externalService, customerId) + .then(applicant => + customers.addExternalCompliance( + customerId, + externalService, + applicant.id, + ), + ) + .then(() => + externalCompliance.createLink(settings, externalService, customerId), + ) .then(url => respond(req, res, { url })) } -function addOrUpdateCustomer (customerData, deviceId, config, isEmailAuth) { +function addOrUpdateCustomer(customerData, deviceId, config, isEmailAuth) { const triggers = configManager.getTriggers(config) const maxDaysThreshold = complianceTriggers.maxDaysThreshold(triggers) @@ -262,34 +328,42 @@ function addOrUpdateCustomer (customerData, deviceId, config, isEmailAuth) { .then(customer => customers.getById(customer.id)) .then(customer => { customers.updateLastAuthAttempt(customer.id, deviceId).catch(() => { - logger.info('failure updating last auth attempt for customer ', customer.id) + logger.info( + 'failure updating last auth attempt for customer ', + customer.id, + ) }) return customer }) .then(customer => { - return Tx.customerHistory(customer.id, maxDaysThreshold) - .then(result => { - customer.txHistory = result - return customer - }) + return Tx.customerHistory(customer.id, maxDaysThreshold).then(result => { + customer.txHistory = result + return customer + }) }) .then(customer => { - return loyalty.getCustomerActiveIndividualDiscount(customer.id) + return loyalty + .getCustomerActiveIndividualDiscount(customer.id) .then(discount => ({ ...customer, discount })) }) } -function getOrAddCustomerPhone (req, res, next) { +function getOrAddCustomerPhone(req, res, next) { const deviceId = req.deviceId const customerData = req.body const pi = plugins(req.settings, deviceId) const phone = req.body.phone - return pi.getPhoneCode(phone) + return pi + .getPhoneCode(phone) .then(code => { - return addOrUpdateCustomer(customerData, deviceId, req.settings.config, false) - .then(customer => respond(req, res, { code, customer })) + return addOrUpdateCustomer( + customerData, + deviceId, + req.settings.config, + false, + ).then(customer => respond(req, res, { code, customer })) }) .catch(err => { if (err.name === 'BadNumberError') throw httpError('Bad number', 401) @@ -298,17 +372,22 @@ function getOrAddCustomerPhone (req, res, next) { .catch(next) } -function getOrAddCustomerEmail (req, res, next) { +function getOrAddCustomerEmail(req, res, next) { const deviceId = req.deviceId const customerData = req.body const pi = plugins(req.settings, req.deviceId) const email = req.body.email - return pi.getEmailCode(email) + return pi + .getEmailCode(email) .then(code => { - return addOrUpdateCustomer(customerData, deviceId, req.settings.config, true) - .then(customer => respond(req, res, { code, customer })) + return addOrUpdateCustomer( + customerData, + deviceId, + req.settings.config, + true, + ).then(customer => respond(req, res, { code, customer })) }) .catch(err => { if (err.name === 'BadNumberError') throw httpError('Bad number', 401) diff --git a/packages/server/lib/routes/diagnosticsRoutes.js b/packages/server/lib/routes/diagnosticsRoutes.js index 41d9aa56..244b9a2b 100644 --- a/packages/server/lib/routes/diagnosticsRoutes.js +++ b/packages/server/lib/routes/diagnosticsRoutes.js @@ -3,7 +3,7 @@ const router = express.Router() const { updateDiagnostics } = require('../machine-loader') -function diagnostics (req, res, next) { +function diagnostics(req, res, next) { return updateDiagnostics(req.deviceId, req.body) .then(() => res.status(200).send({ status: 'OK' })) .catch(next) diff --git a/packages/server/lib/routes/failedQRScans.js b/packages/server/lib/routes/failedQRScans.js index e484ef36..cbf76da6 100644 --- a/packages/server/lib/routes/failedQRScans.js +++ b/packages/server/lib/routes/failedQRScans.js @@ -3,7 +3,7 @@ const router = express.Router() const { updateFailedQRScans } = require('../machine-loader') -function failedQRScans (req, res, next) { +function failedQRScans(req, res, next) { return updateFailedQRScans(req.deviceId, req.body) .then(() => res.status(200).send({ status: 'OK' })) .catch(next) diff --git a/packages/server/lib/routes/logsRoutes.js b/packages/server/lib/routes/logsRoutes.js index 59bef758..e482d014 100644 --- a/packages/server/lib/routes/logsRoutes.js +++ b/packages/server/lib/routes/logsRoutes.js @@ -6,15 +6,17 @@ const logs = require('../logs') const THROTTLE_LOGS_QUERY = 30 * 1000 -function getLastSeen (req, res, next) { +function getLastSeen(req, res, next) { const deviceId = req.deviceId const timestamp = Date.now() - const shouldTrigger = !state.canGetLastSeenMap[deviceId] || + const shouldTrigger = + !state.canGetLastSeenMap[deviceId] || timestamp - state.canGetLastSeenMap[deviceId] >= THROTTLE_LOGS_QUERY if (shouldTrigger) { state.canGetLastSeenMap[deviceId] = timestamp - return logs.getLastSeen(deviceId) + return logs + .getLastSeen(deviceId) .then(r => res.json(r)) .catch(next) } @@ -22,8 +24,9 @@ function getLastSeen (req, res, next) { return res.status(408).json({}) } -function updateLogs (req, res, next) { - return logs.update(req.deviceId, req.body.logs) +function updateLogs(req, res, next) { + return logs + .update(req.deviceId, req.body.logs) .then(status => res.json({ success: status })) .catch(next) } diff --git a/packages/server/lib/routes/pairingRoutes.js b/packages/server/lib/routes/pairingRoutes.js index f6a4016c..e0fb8ff0 100644 --- a/packages/server/lib/routes/pairingRoutes.js +++ b/packages/server/lib/routes/pairingRoutes.js @@ -6,14 +6,15 @@ const httpError = require('../route-helpers').httpError const pairing = require('../pairing') const populateDeviceId = require('../middlewares/populateDeviceId') -function pair (req, res, next) { +function pair(req, res, next) { const token = req.query.token const deviceId = req.deviceId const model = req.query.model const numOfCassettes = req.query.numOfCassettes const numOfRecyclers = req.query.numOfRecyclers - return pairing.pair(token, deviceId, model, numOfCassettes, numOfRecyclers) + return pairing + .pair(token, deviceId, model, numOfCassettes, numOfRecyclers) .then(isValid => { if (isValid) return res.json({ status: 'paired' }) throw httpError('Pairing failed') diff --git a/packages/server/lib/routes/performanceRoutes.js b/packages/server/lib/routes/performanceRoutes.js index d09330c2..1983a06b 100644 --- a/packages/server/lib/routes/performanceRoutes.js +++ b/packages/server/lib/routes/performanceRoutes.js @@ -1,15 +1,18 @@ const express = require('express') const router = express.Router() -const { updateNetworkHeartbeat, updateNetworkPerformance } = require('../machine-loader') +const { + updateNetworkHeartbeat, + updateNetworkPerformance, +} = require('../machine-loader') -function networkHeartbeat (req, res, next) { +function networkHeartbeat(req, res, next) { return updateNetworkHeartbeat(req.deviceId, req.body) .then(() => res.status(200).send({ status: 'OK' })) .catch(next) } -function networkPerformance (req, res, next) { +function networkPerformance(req, res, next) { return updateNetworkPerformance(req.deviceId, req.body) .then(() => res.status(200).send({ status: 'OK' })) .catch(next) diff --git a/packages/server/lib/routes/probeLnRoutes.js b/packages/server/lib/routes/probeLnRoutes.js index 62cf127e..babbbde3 100644 --- a/packages/server/lib/routes/probeLnRoutes.js +++ b/packages/server/lib/routes/probeLnRoutes.js @@ -4,17 +4,17 @@ const router = express.Router() const plugins = require('../plugins') const settingsLoader = require('../new-settings-loader') -function probe (req, res, next) { +function probe(req, res, next) { // TODO: why req.settings is undefined? - settingsLoader.loadLatest() - .then(settings => { - const pi = plugins(settings, req.deviceId) - return pi.probeLN('LN', req.body.address) - .then(r => res.status(200).send({ hardLimits: r })) - .catch(next) - }) + settingsLoader.loadLatest().then(settings => { + const pi = plugins(settings, req.deviceId) + return pi + .probeLN('LN', req.body.address) + .then(r => res.status(200).send({ hardLimits: r })) + .catch(next) + }) } router.get('/', probe) -module.exports = router \ No newline at end of file +module.exports = router diff --git a/packages/server/lib/routes/stateRoutes.js b/packages/server/lib/routes/stateRoutes.js index b25b62c0..c4f58e04 100644 --- a/packages/server/lib/routes/stateRoutes.js +++ b/packages/server/lib/routes/stateRoutes.js @@ -4,8 +4,9 @@ const router = express.Router() const helpers = require('../route-helpers') const respond = require('../respond') -function stateChange (req, res, next) { - helpers.stateChange(req.deviceId, req.deviceTime, req.body) +function stateChange(req, res, next) { + helpers + .stateChange(req.deviceId, req.deviceTime, req.body) .then(() => respond(req, res)) .catch(next) } diff --git a/packages/server/lib/routes/termsAndConditionsRoutes.js b/packages/server/lib/routes/termsAndConditionsRoutes.js index fef9f52d..c0ee85c5 100644 --- a/packages/server/lib/routes/termsAndConditionsRoutes.js +++ b/packages/server/lib/routes/termsAndConditionsRoutes.js @@ -6,21 +6,24 @@ const router = express.Router() const configManager = require('../new-config-manager') const settingsLoader = require('../new-settings-loader') -const createTerms = terms => (terms.active && terms.text) ? ({ - delay: terms.delay, - active: terms.active, - tcPhoto: terms.tcPhoto, - title: terms.title, - text: nmd(terms.text), - accept: terms.acceptButtonText, - cancel: terms.cancelButtonText -}) : null +const createTerms = terms => + terms.active && terms.text + ? { + delay: terms.delay, + active: terms.active, + tcPhoto: terms.tcPhoto, + title: terms.title, + text: nmd(terms.text), + accept: terms.acceptButtonText, + cancel: terms.cancelButtonText, + } + : null -function getTermsConditions (req, res, next) { - const deviceId = req.deviceId +function getTermsConditions(req, res, next) { const { config } = req.settings const terms = configManager.getTermsConditions(config) - return settingsLoader.fetchCurrentConfigVersion() + return settingsLoader + .fetchCurrentConfigVersion() .then(version => res.json({ terms: createTerms(terms), version })) .catch(next) } diff --git a/packages/server/lib/routes/txRoutes.js b/packages/server/lib/routes/txRoutes.js index f63755a0..7ddcca23 100644 --- a/packages/server/lib/routes/txRoutes.js +++ b/packages/server/lib/routes/txRoutes.js @@ -10,7 +10,7 @@ const logger = require('../logger') const plugins = require('../plugins') const Tx = require('../tx') -function postTx (req, res, next) { +function postTx(req, res, next) { const pi = plugins(req.settings, req.deviceId) return Tx.post(_.set('deviceId', req.deviceId, req.body), pi) @@ -38,17 +38,20 @@ function postTx (req, res, next) { logger.warn('Harmless DB conflict, the query will be retried.') return res.status(204).json({}) } - if (err instanceof E.StaleTxError) return res.status(409).json({ errorType: 'stale' }) - if (err instanceof E.RatchetError) return res.status(409).json({ errorType: 'ratchet' }) + if (err instanceof E.StaleTxError) + return res.status(409).json({ errorType: 'stale' }) + if (err instanceof E.RatchetError) + return res.status(409).json({ errorType: 'ratchet' }) throw err }) .catch(next) } -function getTx (req, res, next) { +function getTx(req, res, next) { if (req.query.status) { - return helpers.fetchStatusTx(req.params.id, req.query.status) + return helpers + .fetchStatusTx(req.params.id, req.query.status) .then(r => res.json(r)) .catch(next) } @@ -56,9 +59,10 @@ function getTx (req, res, next) { return next(httpError('Not Found', 404)) } -function getPhoneTx (req, res, next) { +function getPhoneTx(req, res, next) { if (req.query.phone) { - return helpers.fetchPhoneTx(req.query.phone) + return helpers + .fetchPhoneTx(req.query.phone) .then(r => res.json(r)) .catch(next) } @@ -66,9 +70,10 @@ function getPhoneTx (req, res, next) { return next(httpError('Not Found', 404)) } -function getEmailTx (req, res, next) { +function getEmailTx(req, res, next) { if (req.query.email) { - return helpers.fetchEmailTx(req.query.email) + return helpers + .fetchEmailTx(req.query.email) .then(r => res.json(r)) .catch(next) } diff --git a/packages/server/lib/routes/verifyPromoCodeRoutes.js b/packages/server/lib/routes/verifyPromoCodeRoutes.js index 6789a322..f8134723 100644 --- a/packages/server/lib/routes/verifyPromoCodeRoutes.js +++ b/packages/server/lib/routes/verifyPromoCodeRoutes.js @@ -7,26 +7,35 @@ const configManager = require('../new-config-manager') const loyalty = require('../loyalty') const respond = require('../respond') -function verifyPromoCode (req, res, next) { - loyalty.getPromoCode(req.body.codeInput) +function verifyPromoCode(req, res, next) { + loyalty + .getPromoCode(req.body.codeInput) .then(promoCode => { if (!promoCode) return next() const transaction = req.body.tx - const commissions = configManager.getCommissions(transaction.cryptoCode, req.deviceId, req.settings.config) + const commissions = configManager.getCommissions( + transaction.cryptoCode, + req.deviceId, + req.settings.config, + ) const tickerRate = new BN(transaction.rawTickerPrice) - const discount = commissionMath.getDiscountRate(promoCode.discount, commissions[transaction.direction]) + const discount = commissionMath.getDiscountRate( + promoCode.discount, + commissions[transaction.direction], + ) const rates = { [transaction.cryptoCode]: { - [transaction.direction]: (transaction.direction === 'cashIn') - ? tickerRate.times(discount).decimalPlaces(5) - : tickerRate.div(discount).decimalPlaces(5) - } + [transaction.direction]: + transaction.direction === 'cashIn' + ? tickerRate.times(discount).decimalPlaces(5) + : tickerRate.div(discount).decimalPlaces(5), + }, } respond(req, res, { promoCode: promoCode, - newRates: rates + newRates: rates, }) }) .catch(next) diff --git a/packages/server/lib/routes/verifyTxRoutes.js b/packages/server/lib/routes/verifyTxRoutes.js index 5c5cef3c..bec5a24b 100644 --- a/packages/server/lib/routes/verifyTxRoutes.js +++ b/packages/server/lib/routes/verifyTxRoutes.js @@ -4,7 +4,7 @@ const router = express.Router() const plugins = require('../plugins') const respond = require('../respond') -function verifyTx (req, res, next) { +function verifyTx(req, res, next) { const pi = plugins(req.settings, req.deviceId) pi.verifyTransaction(req.body) .then(idResult => respond(req, res, idResult)) diff --git a/packages/server/lib/routes/verifyUserRoutes.js b/packages/server/lib/routes/verifyUserRoutes.js index abf471b8..33d0db1b 100644 --- a/packages/server/lib/routes/verifyUserRoutes.js +++ b/packages/server/lib/routes/verifyUserRoutes.js @@ -4,7 +4,7 @@ const router = express.Router() const plugins = require('../plugins') const respond = require('../respond') -function verifyUser (req, res, next) { +function verifyUser(req, res, next) { const pi = plugins(req.settings, req.deviceId) pi.verifyUser(req.body) .then(idResult => respond(req, res, idResult)) diff --git a/packages/server/lib/sanctions.js b/packages/server/lib/sanctions.js index 33900835..7bb37d6d 100644 --- a/packages/server/lib/sanctions.js +++ b/packages/server/lib/sanctions.js @@ -6,20 +6,24 @@ const customers = require('./customers') const sanctionStatus = { loaded: false, - timestamp: null + timestamp: null, } const loadOrUpdateSanctions = () => { - if (!sanctionStatus.loaded || (sanctionStatus.timestamp && Date.now() > sanctionStatus.timestamp + T.day)) { + if ( + !sanctionStatus.loaded || + (sanctionStatus.timestamp && Date.now() > sanctionStatus.timestamp + T.day) + ) { logger.info('No sanction lists loaded. Loading sanctions...') - return ofac.load() + return ofac + .load() .then(() => { logger.info('OFAC sanction list loaded!') sanctionStatus.loaded = true sanctionStatus.timestamp = Date.now() }) .catch(e => { - logger.error('Couldn\'t load OFAC sanction list!', e) + logger.error("Couldn't load OFAC sanction list!", e) }) } @@ -27,18 +31,28 @@ const loadOrUpdateSanctions = () => { } const checkByUser = (customerId, userToken) => { - return Promise.all([loadOrUpdateSanctions(), customers.getCustomerById(customerId)]) - .then(([, customer]) => { - const { firstName, lastName, dateOfBirth } = customer?.idCardData - const birthdate = _.replace(/-/g, '')(dateOfBirth) - const ofacMatches = ofac.match({ firstName, lastName }, birthdate, { threshold: 0.85, fullNameThreshold: 0.95, debug: false }) - const isOfacSanctioned = _.size(ofacMatches) > 0 - customers.updateCustomer(customerId, { sanctions: !isOfacSanctioned }, userToken) - - return { ofacSanctioned: isOfacSanctioned } + return Promise.all([ + loadOrUpdateSanctions(), + customers.getCustomerById(customerId), + ]).then(([, customer]) => { + const { firstName, lastName, dateOfBirth } = customer.idCardData + const birthdate = _.replace(/-/g, '')(dateOfBirth) + const ofacMatches = ofac.match({ firstName, lastName }, birthdate, { + threshold: 0.85, + fullNameThreshold: 0.95, + debug: false, }) + const isOfacSanctioned = _.size(ofacMatches) > 0 + customers.updateCustomer( + customerId, + { sanctions: !isOfacSanctioned }, + userToken, + ) + + return { ofacSanctioned: isOfacSanctioned } + }) } module.exports = { - checkByUser + checkByUser, } diff --git a/packages/server/lib/session-manager.js b/packages/server/lib/session-manager.js index 3785c576..f337d96f 100644 --- a/packages/server/lib/session-manager.js +++ b/packages/server/lib/session-manager.js @@ -1,11 +1,11 @@ const db = require('./db') -function getSessions () { +function getSessions() { const sql = `SELECT * FROM user_sessions ORDER BY sess -> 'user' ->> 'username'` return db.any(sql) } -function getLastSessionPerUser () { +function getLastSessionPerUser() { const sql = `SELECT b.username, a.user_agent, a.ip_address, a.last_used, b.role FROM ( SELECT sess -> 'user' ->> 'username' AS username, sess ->> 'ua' AS user_agent, @@ -19,24 +19,31 @@ function getLastSessionPerUser () { return db.any(sql) } -function getSessionsByUsername (username) { +function getSessionsByUsername(username) { const sql = `SELECT * FROM user_sessions WHERE sess -> 'user' ->> 'username'=$1` return db.any(sql, [username]) } -function getSessionById (sessionID) { +function getSessionById(sessionID) { const sql = `SELECT * FROM user_sessions WHERE sid=$1` return db.any(sql, [sessionID]) } -function deleteSessionsByUsername (username) { +function deleteSessionsByUsername(username) { const sql = `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'username'=$1` return db.none(sql, [username]) } -function deleteSessionById (sessionID) { +function deleteSessionById(sessionID) { const sql = `DELETE FROM user_sessions WHERE sid=$1` return db.none(sql, [sessionID]) } -module.exports = { getSessions, getLastSessionPerUser, getSessionsByUsername, getSessionById, deleteSessionsByUsername, deleteSessionById } +module.exports = { + getSessions, + getLastSessionPerUser, + getSessionsByUsername, + getSessionById, + deleteSessionsByUsername, + deleteSessionById, +} diff --git a/packages/server/lib/sms-notices.js b/packages/server/lib/sms-notices.js index 182cf62f..64f67399 100644 --- a/packages/server/lib/sms-notices.js +++ b/packages/server/lib/sms-notices.js @@ -4,20 +4,31 @@ const db = require('./db') const getSMSNotices = () => { const sql = `SELECT * FROM sms_notices ORDER BY created` - return db.any(sql).then(res => _.map( - it => ({ - id: it.id, - event: _.camelCase(it.event), - message: it.message, - messageName: it.message_name, - enabled: it.enabled, - allowToggle: it.allow_toggle - }), res)) + return db.any(sql).then(res => + _.map( + it => ({ + id: it.id, + event: _.camelCase(it.event), + message: it.message, + messageName: it.message_name, + enabled: it.enabled, + allowToggle: it.allow_toggle, + }), + res, + ), + ) } const createSMSNotice = (event, messageName, message, enabled, allowToggle) => { const sql = `INSERT INTO sms_notices (id, event, message_name, message, enabled, allow_toggle) VALUES ($1, $2, $3, $4, $5, $6)` - return db.none(sql, [uuid.v4(), _.snakeCase(event), messageName, message, enabled, allowToggle]) + return db.none(sql, [ + uuid.v4(), + _.snakeCase(event), + messageName, + message, + enabled, + allowToggle, + ]) } const editSMSNotice = (id, event, message) => { @@ -52,5 +63,5 @@ module.exports = { deleteSMSNotice, getSMSNotice, enableSMSNotice, - disableSMSNotice + disableSMSNotice, } diff --git a/packages/server/lib/sms.js b/packages/server/lib/sms.js index 08d14635..62d16d9c 100644 --- a/packages/server/lib/sms.js +++ b/packages/server/lib/sms.js @@ -5,22 +5,25 @@ const _ = require('lodash/fp') const smsNotices = require('./sms-notices') const { RECEIPT } = require('./constants') -function getSms (event, phone, content) { - return smsNotices.getSMSNotice(event) - .then(msg => { - if (!_.isNil(msg)) { - var accMsg = msg.message - const contentKeys = _.keys(content) - const messageContent = _.reduce((acc, it) => _.replace(`#${it}`, content[it], acc), accMsg, contentKeys) - return { - toNumber: phone, - body: messageContent - } +function getSms(event, phone, content) { + return smsNotices.getSMSNotice(event).then(msg => { + if (!_.isNil(msg)) { + var accMsg = msg.message + const contentKeys = _.keys(content) + const messageContent = _.reduce( + (acc, it) => _.replace(`#${it}`, content[it], acc), + accMsg, + contentKeys, + ) + return { + toNumber: phone, + body: messageContent, } - }) + } + }) } -function getPlugin (settings) { +function getPlugin(settings) { const smsProvider = settings.config.notifications_thirdParty_sms const pluginCode = smsProvider ?? 'twilio' const plugin = ph.load(ph.SMS, pluginCode) @@ -29,12 +32,11 @@ function getPlugin (settings) { return { plugin, account } } -function sendMessage (settings, rec) { - return Promise.resolve() - .then(() => { - const { plugin, account } = getPlugin(settings) - return plugin.sendMessage(account, rec) - }) +function sendMessage(settings, rec) { + return Promise.resolve().then(() => { + const { plugin, account } = getPlugin(settings) + return plugin.sendMessage(account, rec) + }) } const toCryptoUnits = (cryptoAtoms, cryptoCode) => { @@ -42,7 +44,7 @@ const toCryptoUnits = (cryptoAtoms, cryptoCode) => { return cryptoAtoms.shiftedBy(-unitScale) } -function formatSmsReceipt (data, options) { +function formatSmsReceipt(data, options) { var message = `RECEIPT\n` if (data.operatorInfo) { message = message.concat(`Operator information:\n`) @@ -94,22 +96,26 @@ function formatSmsReceipt (data, options) { message = message.concat(`Address: ${data.address}\n`) } - const timestamp = `${(new Date()).toISOString().substring(11, 19)} UTC` + const timestamp = `${new Date().toISOString().substring(11, 19)} UTC` - const postReceiptSmsPromise = getSms(RECEIPT, data.customerPhone, { timestamp }) + const postReceiptSmsPromise = getSms(RECEIPT, data.customerPhone, { + timestamp, + }) - return Promise.all([smsNotices.getSMSNotice(RECEIPT), postReceiptSmsPromise]) - .then(([res, postReceiptSms]) => ({ - sms: { - toNumber: data.customerPhone, - body: res.enabled ? message.concat('\n\n', postReceiptSms.body) : message - } - })) + return Promise.all([ + smsNotices.getSMSNotice(RECEIPT), + postReceiptSmsPromise, + ]).then(([res, postReceiptSms]) => ({ + sms: { + toNumber: data.customerPhone, + body: res.enabled ? message.concat('\n\n', postReceiptSms.body) : message, + }, + })) } module.exports = { getSms, sendMessage, formatSmsReceipt, - toCryptoUnits + toCryptoUnits, } diff --git a/packages/server/lib/ticker.js b/packages/server/lib/ticker.js index 221235e3..f06ee3d4 100644 --- a/packages/server/lib/ticker.js +++ b/packages/server/lib/ticker.js @@ -17,47 +17,57 @@ const PEGGED_FIAT_CURRENCIES = { NAD: 'ZAR' } const getFallbackTicker = ticker => _.difference(['bitpay', 'kraken', 'bitstamp'], [ticker])[0] -const hasRatesOrReject = emsg => r => _.get(['rates'], r) ? - r : - Promise.reject(new Error(emsg)) +const hasRatesOrReject = emsg => r => + _.get(['rates'], r) ? r : Promise.reject(new Error(emsg)) const get1 = (market, fiatCode, cryptoCode, ticker, emsg) => buildTicker(fiatCode, cryptoCode, ticker) .then(hasRatesOrReject(emsg)) .then(({ rates }) => { - return lastRate[market] = { rates, timestamp: Date.now() } + return (lastRate[market] = { rates, timestamp: Date.now() }) }) -const _getRates = (settings, fiatCode, cryptoCode) => Promise.resolve() - .then(() => { - const ticker = configManager.getWalletSettings(cryptoCode, settings.config).ticker +const _getRates = (settings, fiatCode, cryptoCode) => + Promise.resolve().then(() => { + const ticker = configManager.getWalletSettings( + cryptoCode, + settings.config, + ).ticker const market = [cryptoCode, fiatCode].join('-') const fallbackTicker = getFallbackTicker(ticker) - const emsg = fallbackTicker ? - "Failed to get rates with configured ticker, trying fallback" : - "Failed to get ticker rates" + const emsg = fallbackTicker + ? 'Failed to get rates with configured ticker, trying fallback' + : 'Failed to get ticker rates' return get1(market, fiatCode, cryptoCode, ticker, emsg) .catch(err => { logger.error(err) - return fallbackTicker ? - get1(market, fiatCode, cryptoCode, fallbackTicker, "Failed to get rates with fallback ticker") : - lastRate[market] + return fallbackTicker + ? get1( + market, + fiatCode, + cryptoCode, + fallbackTicker, + 'Failed to get rates with fallback ticker', + ) + : lastRate[market] }) - .then(hasRatesOrReject("Failed to get ticker rates")) + .then(hasRatesOrReject('Failed to get ticker rates')) }) -function buildTicker (fiatCode, cryptoCode, tickerName) { +function buildTicker(fiatCode, cryptoCode, tickerName) { fiatCode = _.defaultTo(fiatCode, _.get([fiatCode], PEGGED_FIAT_CURRENCIES)) cryptoCode = coinUtils.getEquivalentCode(cryptoCode) if (tickerName === 'bitpay') return bitpay.ticker(fiatCode, cryptoCode) - if (tickerName === 'mock-ticker') return mockTicker.ticker(fiatCode, cryptoCode) + if (tickerName === 'mock-ticker') + return mockTicker.ticker(fiatCode, cryptoCode) return ccxt.ticker(fiatCode, cryptoCode, tickerName) } const getRates = mem(_getRates, { maxAge: FETCH_INTERVAL, - cacheKey: (settings, fiatCode, cryptoCode) => JSON.stringify([fiatCode, cryptoCode]) + cacheKey: (settings, fiatCode, cryptoCode) => + JSON.stringify([fiatCode, cryptoCode]), }) module.exports = { getRates } diff --git a/packages/server/lib/time.js b/packages/server/lib/time.js index 37cf1bdb..0061bca9 100644 --- a/packages/server/lib/time.js +++ b/packages/server/lib/time.js @@ -23,5 +23,5 @@ module.exports = { weeks, week, years, - year + year, } diff --git a/packages/server/lib/tx-batching-processing.js b/packages/server/lib/tx-batching-processing.js index 94f4791d..a230b4ae 100644 --- a/packages/server/lib/tx-batching-processing.js +++ b/packages/server/lib/tx-batching-processing.js @@ -3,28 +3,28 @@ const _ = require('lodash/fp') const txBatching = require('./tx-batching') const wallet = require('./wallet') -function submitBatch (settings, batch) { - txBatching.getBatchTransactions(batch) - .then(txs => { - if (_.isEmpty(txs)) return Promise.resolve() - return wallet.sendCoinsBatch(settings, txs, batch.crypto_code) - .then(res => txBatching.confirmSentBatch(batch, res)) - .catch(err => txBatching.setErroredBatch(batch, err.message)) - }) +function submitBatch(settings, batch) { + txBatching.getBatchTransactions(batch).then(txs => { + if (_.isEmpty(txs)) return Promise.resolve() + return wallet + .sendCoinsBatch(settings, txs, batch.crypto_code) + .then(res => txBatching.confirmSentBatch(batch, res)) + .catch(err => txBatching.setErroredBatch(batch, err.message)) + }) } -function processBatches (settings, lifecycle) { - return txBatching.getBatchesByStatus(['open']) - .then(batches => { - _.each(batch => { - const elapsedMS = batch.time_elapsed * 1000 +function processBatches(settings, lifecycle) { + return txBatching.getBatchesByStatus(['open']).then(batches => { + _.each(batch => { + const elapsedMS = batch.time_elapsed * 1000 - if (elapsedMS >= lifecycle) { - return txBatching.closeTransactionBatch(batch) - .then(() => submitBatch(settings, batch)) - } - }, batches) - }) + if (elapsedMS >= lifecycle) { + return txBatching + .closeTransactionBatch(batch) + .then(() => submitBatch(settings, batch)) + } + }, batches) + }) } module.exports = processBatches diff --git a/packages/server/lib/tx-batching.js b/packages/server/lib/tx-batching.js index 2b7e793e..e6382bcc 100644 --- a/packages/server/lib/tx-batching.js +++ b/packages/server/lib/tx-batching.js @@ -5,62 +5,72 @@ const uuid = require('uuid') const BN = require('./bn') const db = require('./db') -function addTransactionToBatch (tx) { +function addTransactionToBatch(tx) { const sql = `SELECT * FROM transaction_batches WHERE crypto_code=$1 AND status='open' ORDER BY created_at DESC LIMIT 1` const sql2 = `UPDATE cash_in_txs SET batch_id=$1 WHERE id=$2` - return db.oneOrNone(sql, [tx.cryptoCode]) - .then(batch => { - if (_.isNil(batch)) { - return db.tx(t => { - const newBatchId = uuid.v4() - const q1 = t.none(`INSERT INTO transaction_batches (id, crypto_code) VALUES ($1, $2)`, [newBatchId, tx.cryptoCode]) - const q2 = t.none(sql2, [newBatchId, tx.id]) + return db.oneOrNone(sql, [tx.cryptoCode]).then(batch => { + if (_.isNil(batch)) { + return db.tx(t => { + const newBatchId = uuid.v4() + const q1 = t.none( + `INSERT INTO transaction_batches (id, crypto_code) VALUES ($1, $2)`, + [newBatchId, tx.cryptoCode], + ) + const q2 = t.none(sql2, [newBatchId, tx.id]) - return t.batch([q1, q2]) - }) - } - return db.none(sql2, [batch.id, tx.id]) - }) + return t.batch([q1, q2]) + }) + } + return db.none(sql2, [batch.id, tx.id]) + }) } -function closeTransactionBatch (batch) { +function closeTransactionBatch(batch) { const sql = `UPDATE transaction_batches SET status='ready', closed_at=now() WHERE id=$1` return db.none(sql, [batch.id]) } -function confirmSentBatch (batch, tx) { +function confirmSentBatch(batch, tx) { return db.tx(t => { - const q1 = t.none(`UPDATE transaction_batches SET status='sent', error_message=NULL WHERE id=$1`, [batch.id]) - const q2 = t.none(`UPDATE cash_in_txs SET tx_hash=$1, fee=$2, send=true, send_confirmed=true, send_time=now(), send_pending=false, error=NULL, error_code=NULL WHERE batch_id=$3`, [tx.txid, tx.fee.toString(), batch.id]) + const q1 = t.none( + `UPDATE transaction_batches SET status='sent', error_message=NULL WHERE id=$1`, + [batch.id], + ) + const q2 = t.none( + `UPDATE cash_in_txs SET tx_hash=$1, fee=$2, send=true, send_confirmed=true, send_time=now(), send_pending=false, error=NULL, error_code=NULL WHERE batch_id=$3`, + [tx.txid, tx.fee.toString(), batch.id], + ) return t.batch([q1, q2]) }) } -function setErroredBatch (batch, errorMsg) { +function setErroredBatch(batch, errorMsg) { const sql = `UPDATE transaction_batches SET status='failed', error_message=$1 WHERE id=$2` return db.none(sql, [errorMsg, batch.id]) } -function getBatchTransactions (batch) { +function getBatchTransactions(batch) { const sql = `SELECT * FROM cash_in_txs WHERE batch_id=$1` - return db.manyOrNone(sql, [batch.id]) + return db + .manyOrNone(sql, [batch.id]) .then(res => _.map(_.mapKeys(_.camelCase), res)) } -function getBatchesByStatus (statuses) { +function getBatchesByStatus(statuses) { const sql = `SELECT *, EXTRACT(EPOCH FROM (now() - created_at)) as time_elapsed FROM transaction_batches WHERE status in ($1^)` return db.manyOrNone(sql, [_.map(pgp.as.text, statuses).join(',')]) } -function getOpenBatchCryptoValue (cryptoCode) { +function getOpenBatchCryptoValue(cryptoCode) { const sql = `SELECT * FROM transaction_batches WHERE crypto_code=$1 AND status='open' ORDER BY created_at DESC LIMIT 1` - return db.oneOrNone(sql, [cryptoCode]) + return db + .oneOrNone(sql, [cryptoCode]) .then(batch => { if (_.isNil(batch)) return Promise.resolve([]) return db.any(`SELECT * FROM cash_in_txs WHERE batch_id=$1`, [batch.id]) @@ -75,5 +85,5 @@ module.exports = { setErroredBatch, getBatchTransactions, getBatchesByStatus, - getOpenBatchCryptoValue + getOpenBatchCryptoValue, } diff --git a/packages/server/lib/tx.js b/packages/server/lib/tx.js index d43c2818..5b0cf010 100644 --- a/packages/server/lib/tx.js +++ b/packages/server/lib/tx.js @@ -9,41 +9,41 @@ const T = require('./time') // E.g.: 1853.013808 * 1000 = 1866149.494 const REDEEMABLE_AGE = T.day / 1000 -function process (tx, pi) { - const mtx = massage(tx, pi) +function process(tx, pi) { + const mtx = massage(tx) if (mtx.direction === 'cashIn') return CashInTx.post(mtx, pi) if (mtx.direction === 'cashOut') return CashOutTx.post(mtx, pi) return Promise.reject(new Error('No such tx direction: ' + mtx.direction)) } -function post (tx, pi) { - return process(tx, pi) - .then(_.set('dirty', false)) +function post(tx, pi) { + return process(tx, pi).then(_.set('dirty', false)) } -function massage (tx, pi) { +function massage(tx) { const isDateField = r => r === 'created' || _.endsWith('_time', r) - const transformDate = (v, k) => isDateField(k) ? new Date(v) : v - const mapValuesWithKey = _.mapValues.convert({'cap': false}) + const transformDate = (v, k) => (isDateField(k) ? new Date(v) : v) + const mapValuesWithKey = _.mapValues.convert({ cap: false }) const transformDates = r => mapValuesWithKey(transformDate, r) const mapBN = r => { - const update = r.direction === 'cashIn' - ? { - cryptoAtoms: new BN(r.cryptoAtoms), - fiat: new BN(r.fiat), - cashInFee: new BN(r.cashInFee), - commissionPercentage: new BN(r.commissionPercentage), - rawTickerPrice: r.rawTickerPrice ? new BN(r.rawTickerPrice) : null, - minimumTx: new BN(r.minimumTx) - } - : { - cryptoAtoms: new BN(r.cryptoAtoms), - fiat: new BN(r.fiat), - fixedFee: r.cashOutFee ? new BN(r.cashOutFee) : null, - rawTickerPrice: r.rawTickerPrice ? new BN(r.rawTickerPrice) : null, - commissionPercentage: new BN(r.commissionPercentage) - } + const update = + r.direction === 'cashIn' + ? { + cryptoAtoms: new BN(r.cryptoAtoms), + fiat: new BN(r.fiat), + cashInFee: new BN(r.cashInFee), + commissionPercentage: new BN(r.commissionPercentage), + rawTickerPrice: r.rawTickerPrice ? new BN(r.rawTickerPrice) : null, + minimumTx: new BN(r.minimumTx), + } + : { + cryptoAtoms: new BN(r.cryptoAtoms), + fiat: new BN(r.fiat), + fixedFee: r.cashOutFee ? new BN(r.cashOutFee) : null, + rawTickerPrice: r.rawTickerPrice ? new BN(r.rawTickerPrice) : null, + commissionPercentage: new BN(r.commissionPercentage), + } return _.assign(r, update) } @@ -52,26 +52,29 @@ function massage (tx, pi) { transformDates, mapBN, _.unset('dirty'), - _.unset('cashOutFee') + _.unset('cashOutFee'), ) return mapper(tx) } -function cancel (txId) { +function cancel(txId) { const promises = [ - CashInTx.cancel(txId).then(() => true).catch(() => false), - CashOutTx.cancel(txId).then(() => true).catch(() => false) + CashInTx.cancel(txId) + .then(() => true) + .catch(() => false), + CashOutTx.cancel(txId) + .then(() => true) + .catch(() => false), ] - return Promise.all(promises) - .then(r => { - if (_.some(r)) return - throw new Error('No such transaction') - }) + return Promise.all(promises).then(r => { + if (_.some(r)) return + throw new Error('No such transaction') + }) } -function customerHistory (customerId, thresholdDays) { +function customerHistory(customerId, thresholdDays) { const sql = `SELECT ch.id, ch.created, ch.fiat, ch.direction FROM ( SELECT txIn.id, txIn.created, txIn.fiat, 'cashIn' AS direction, ((NOT txIn.send_confirmed) AND (txIn.created <= now() - interval $3)) AS expired diff --git a/packages/server/lib/users.js b/packages/server/lib/users.js index 3eaa1b74..08677719 100644 --- a/packages/server/lib/users.js +++ b/packages/server/lib/users.js @@ -17,7 +17,7 @@ const db = require('./db') * * @returns {user object} User object (containing name) */ -function get (token) { +function get(token) { const sql = 'SELECT * FROM user_tokens WHERE token=$1' return db.oneOrNone(sql, [token]) } @@ -32,30 +32,31 @@ function get (token) { * * @returns {array} Array of users found */ -function getByIds (ids) { +function getByIds(ids) { const sql = `SELECT * FROM users WHERE id IN ($1^)` const idList = _.map(pgp.as.text, ids).join(',') return db.any(sql, [idList]) } -function getUserById (id) { +function getUserById(id) { const sql = `SELECT * FROM users WHERE id=$1` return db.oneOrNone(sql, [id]) } -function getUserByUsername (username) { +function getUserByUsername(username) { const sql = `SELECT * FROM users WHERE username=$1` return db.oneOrNone(sql, [username]) } -function getUsers () { +function getUsers() { const sql = `SELECT id, username, role, enabled, last_accessed, last_accessed_from, last_accessed_address FROM users ORDER BY username` return db.any(sql) } -function verifyAndUpdateUser (id, ua, ip) { +function verifyAndUpdateUser(id, ua, ip) { const sql = `SELECT id, username, role, enabled FROM users WHERE id=$1 limit 1` - return db.oneOrNone(sql, [id]) + return db + .oneOrNone(sql, [id]) .then(user => { if (!user) return null @@ -65,54 +66,79 @@ function verifyAndUpdateUser (id, ua, ip) { .then(user => user) } -function saveTemp2FASecret (id, secret) { +function saveTemp2FASecret(id, secret) { const sql = 'UPDATE users SET temp_twofa_code=$1 WHERE id=$2' return db.none(sql, [secret, id]) } -function save2FASecret (id, secret) { +function save2FASecret(id, secret) { return db.tx(t => { - const q1 = t.none('UPDATE users SET twofa_code=$1, temp_twofa_code=NULL WHERE id=$2', [secret, id]) - const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id]) + const q1 = t.none( + 'UPDATE users SET twofa_code=$1, temp_twofa_code=NULL WHERE id=$2', + [secret, id], + ) + const q2 = t.none( + `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, + [id], + ) return t.batch([q1, q2]) }) } -function validateAuthToken (token, type) { +function validateAuthToken(token, type) { const sql = `SELECT user_id, now() < expire AS success FROM auth_tokens WHERE token=$1 AND type=$2` - return db.one(sql, [token, type]) + return db + .one(sql, [token, type]) .then(res => ({ userID: res.user_id, success: res.success })) } -function reset2FASecret (token, id, secret) { +function reset2FASecret(token, id, secret) { return validateAuthToken(token, 'reset_twofa').then(res => { if (!res.success) throw new Error('Failed to verify 2FA reset token') return db.tx(t => { - const q1 = t.none('UPDATE users SET twofa_code=$1, temp_twofa_code=NULL WHERE id=$2', [secret, id]) - const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id]) - const q3 = t.none(`DELETE FROM auth_tokens WHERE token=$1 and type='reset_twofa'`, [token]) + const q1 = t.none( + 'UPDATE users SET twofa_code=$1, temp_twofa_code=NULL WHERE id=$2', + [secret, id], + ) + const q2 = t.none( + `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, + [id], + ) + const q3 = t.none( + `DELETE FROM auth_tokens WHERE token=$1 and type='reset_twofa'`, + [token], + ) return t.batch([q1, q2, q3]) }) }) } -function updatePassword (token, id, password) { +function updatePassword(token, id, password) { return validateAuthToken(token, 'reset_password').then(res => { if (!res.success) throw new Error('Failed to verify password reset token') return argon2.hash(password).then(function (hash) { return db.tx(t => { - const q1 = t.none(`UPDATE users SET password=$1 WHERE id=$2`, [hash, id]) - const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id]) - const q3 = t.none(`DELETE FROM auth_tokens WHERE token=$1 and type='reset_password'`, [token]) + const q1 = t.none(`UPDATE users SET password=$1 WHERE id=$2`, [ + hash, + id, + ]) + const q2 = t.none( + `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, + [id], + ) + const q3 = t.none( + `DELETE FROM auth_tokens WHERE token=$1 and type='reset_password'`, + [token], + ) return t.batch([q1, q2, q3]) }) }) }) } -function createUserRegistrationToken (username, role) { +function createUserRegistrationToken(username, role) { const token = crypto.randomBytes(32).toString('hex') const sql = `INSERT INTO user_register_tokens (token, username, role) VALUES ($1, $2, $3) ON CONFLICT (username) DO UPDATE SET token=$1, expire=now() + interval '${constants.REGISTRATION_TOKEN_EXPIRATION_TIME}' RETURNING *` @@ -120,40 +146,51 @@ function createUserRegistrationToken (username, role) { return db.one(sql, [token, username, role]) } -function validateUserRegistrationToken (token) { +function validateUserRegistrationToken(token) { const sql = `SELECT username, role, now() < expire AS success FROM user_register_tokens WHERE token=$1` - return db.one(sql, [token]) - .then(res => ({ username: res.username, role: res.role, success: res.success })) + return db.one(sql, [token]).then(res => ({ + username: res.username, + role: res.role, + success: res.success, + })) } -function register (token, username, password, role) { +function register(token, username, password, role) { return validateUserRegistrationToken(token).then(res => { if (!res.success) throw new Error('Failed to verify registration token') return argon2.hash(password).then(hash => { return db.tx(t => { - const q1 = t.none(`INSERT INTO users (id, username, password, role) VALUES ($1, $2, $3, $4)`, [uuid.v4(), username, hash, role]) - const q2 = t.none(`DELETE FROM user_register_tokens WHERE token=$1`, [token]) + const q1 = t.none( + `INSERT INTO users (id, username, password, role) VALUES ($1, $2, $3, $4)`, + [uuid.v4(), username, hash, role], + ) + const q2 = t.none(`DELETE FROM user_register_tokens WHERE token=$1`, [ + token, + ]) return t.batch([q1, q2]) }) }) }) } -function changeUserRole (id, newRole) { +function changeUserRole(id, newRole) { const sql = `UPDATE users SET role=$1 WHERE id=$2` return db.none(sql, [newRole, id]) } -function enableUser (id) { +function enableUser(id) { const sql = `UPDATE users SET enabled=true WHERE id=$1` return db.none(sql, [id]) } -function disableUser (id) { +function disableUser(id) { return db.tx(t => { const q1 = t.none(`UPDATE users SET enabled=false WHERE id=$1`, [id]) - const q2 = t.none(`DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, [id]) + const q2 = t.none( + `DELETE FROM user_sessions WHERE sess -> 'user' ->> 'id'=$1`, + [id], + ) return t.batch([q1, q2]) }) } @@ -175,5 +212,5 @@ module.exports = { register, changeUserRole, enableUser, - disableUser + disableUser, } diff --git a/packages/server/lib/utils.js b/packages/server/lib/utils.js index a6f7dc45..7f3c01ef 100644 --- a/packages/server/lib/utils.js +++ b/packages/server/lib/utils.js @@ -3,7 +3,8 @@ const _ = require('lodash') const camelize = obj => _.transform(obj, (acc, value, key, target) => { const camelKey = _.isArray(target) ? key : _.camelCase(key.toString()) - acc[camelKey] = _.isObject(value) && !(value instanceof Date) ? camelize(value) : value + acc[camelKey] = + _.isObject(value) && !(value instanceof Date) ? camelize(value) : value }) module.exports = camelize diff --git a/packages/server/lib/wallet-scoring.js b/packages/server/lib/wallet-scoring.js index 9b426faf..4a7ebf28 100644 --- a/packages/server/lib/wallet-scoring.js +++ b/packages/server/lib/wallet-scoring.js @@ -2,7 +2,7 @@ const ph = require('./plugin-helper') const argv = require('minimist')(process.argv.slice(2)) // TODO - This function should be rolled back after UI is created for this feature -function loadWalletScoring (settings) { +function loadWalletScoring(settings) { if (argv.mockScoring) { const mock = ph.load(ph.WALLET_SCORING, 'mock-scoring') return { plugin: mock, account: {} } @@ -11,7 +11,7 @@ function loadWalletScoring (settings) { const scorechainAccount = settings.accounts['scorechain'] if (scorechainAccount?.enabled) { const scorechain = ph.load(ph.WALLET_SCORING, 'scorechain') - return { plugin: scorechain, account: scorechainAccount} + return { plugin: scorechain, account: scorechainAccount } } const ellipticAccount = settings.accounts['elliptic'] @@ -20,35 +20,32 @@ function loadWalletScoring (settings) { return { plugin: elliptic, account: ellipticAccount } } -function rateTransaction (settings, cryptoCode, address) { - return Promise.resolve() - .then(() => { - const { plugin, account } = loadWalletScoring(settings) +function rateTransaction(settings, cryptoCode, address) { + return Promise.resolve().then(() => { + const { plugin, account } = loadWalletScoring(settings) - return plugin.rateAddress(account, cryptoCode, address) - }) + return plugin.rateAddress(account, cryptoCode, address) + }) } -function rateAddress (settings, cryptoCode, address) { - return Promise.resolve() - .then(() => { - const { plugin, account } = loadWalletScoring(settings) +function rateAddress(settings, cryptoCode, address) { + return Promise.resolve().then(() => { + const { plugin, account } = loadWalletScoring(settings) - return plugin.rateAddress(account, cryptoCode, address) - }) + return plugin.rateAddress(account, cryptoCode, address) + }) } -function isWalletScoringEnabled (settings, cryptoCode) { - return Promise.resolve() - .then(() => { - const { plugin, account } = loadWalletScoring(settings) +function isWalletScoringEnabled(settings, cryptoCode) { + return Promise.resolve().then(() => { + const { plugin, account } = loadWalletScoring(settings) - return plugin.isWalletScoringEnabled(account, cryptoCode) - }) + return plugin.isWalletScoringEnabled(account, cryptoCode) + }) } module.exports = { rateAddress, rateTransaction, - isWalletScoringEnabled + isWalletScoringEnabled, } diff --git a/packages/server/lib/wallet.js b/packages/server/lib/wallet.js index 0597b3ed..1f113e17 100644 --- a/packages/server/lib/wallet.js +++ b/packages/server/lib/wallet.js @@ -22,36 +22,50 @@ const ZERO_CONF_EXPIRATION = 60000 const MNEMONIC_PATH = process.env.MNEMONIC_PATH -function computeSeed (masterSeed) { - return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' }) +function computeSeed(masterSeed) { + return hkdf(masterSeed, 32, { + salt: 'lamassu-server-salt', + info: 'wallet-seed', + }) } -function computeOperatorId (masterSeed) { - return hkdf(masterSeed, 16, { salt: 'lamassu-server-salt', info: 'operator-id' }).toString('hex') +function computeOperatorId(masterSeed) { + return hkdf(masterSeed, 16, { + salt: 'lamassu-server-salt', + info: 'operator-id', + }).toString('hex') } -function fetchWallet (settings, cryptoCode) { - return fs.readFile(MNEMONIC_PATH, 'utf8') - .then(mnemonic => { - const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic) - const plugin = configManager.getWalletSettings(cryptoCode, settings.config).wallet - const wallet = ph.load(ph.WALLET, plugin) - const rawAccount = settings.accounts[plugin] - const account = _.set('seed', computeSeed(masterSeed), rawAccount) - const accountWithMnemonic = _.set('mnemonic', mnemonic, account) - if (_.isFunction(wallet.run)) wallet.run(accountWithMnemonic) - const operatorId = computeOperatorId(masterSeed) - return { wallet, account: accountWithMnemonic, operatorId } - }) +function fetchWallet(settings, cryptoCode) { + return fs.readFile(MNEMONIC_PATH, 'utf8').then(mnemonic => { + const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic) + const plugin = configManager.getWalletSettings( + cryptoCode, + settings.config, + ).wallet + const wallet = ph.load(ph.WALLET, plugin) + const rawAccount = settings.accounts[plugin] + const account = _.set('seed', computeSeed(masterSeed), rawAccount) + const accountWithMnemonic = _.set('mnemonic', mnemonic, account) + if (_.isFunction(wallet.run)) wallet.run(accountWithMnemonic) + const operatorId = computeOperatorId(masterSeed) + return { wallet, account: accountWithMnemonic, operatorId } + }) } const lastBalance = {} -function _balance (settings, cryptoCode) { +function _balance(settings, cryptoCode) { return fetchWallet(settings, cryptoCode) .then(r => r.wallet.balance(r.account, cryptoCode, settings, r.operatorId)) - .then(balance => Promise.all([balance, getOpenBatchCryptoValue(cryptoCode)])) - .then(([balance, reservedBalance]) => ({ balance: BN(balance).minus(reservedBalance), reservedBalance, timestamp: Date.now() })) + .then(balance => + Promise.all([balance, getOpenBatchCryptoValue(cryptoCode)]), + ) + .then(([balance, reservedBalance]) => ({ + balance: BN(balance).minus(reservedBalance), + reservedBalance, + timestamp: Date.now(), + })) .then(r => { lastBalance[cryptoCode] = r return r @@ -62,18 +76,24 @@ function _balance (settings, cryptoCode) { }) } -function probeLN (settings, cryptoCode, address) { +function probeLN(settings, cryptoCode, address) { return fetchWallet(settings, cryptoCode).then(r => { if (!r.wallet.probeLN) return null return r.wallet.probeLN(r.account, cryptoCode, address) }) } -function sendCoins (settings, tx) { +function sendCoins(settings, tx) { return fetchWallet(settings, tx.cryptoCode) .then(r => { - const feeMultiplier = new BN(configManager.getWalletSettings(tx.cryptoCode, settings.config).feeMultiplier) - return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) + const feeMultiplier = new BN( + configManager.getWalletSettings( + tx.cryptoCode, + settings.config, + ).feeMultiplier, + ) + return r.wallet + .sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) .then(res => { mem.clear(module.exports.balance) return res @@ -87,11 +107,17 @@ function sendCoins (settings, tx) { }) } -function sendCoinsBatch (settings, txs, cryptoCode) { +function sendCoinsBatch(settings, txs, cryptoCode) { return fetchWallet(settings, cryptoCode) .then(r => { - const feeMultiplier = new BN(configManager.getWalletSettings(cryptoCode, settings.config).feeMultiplier) - return r.wallet.sendCoinsBatch(r.account, txs, cryptoCode, feeMultiplier) + const feeMultiplier = new BN( + configManager.getWalletSettings( + cryptoCode, + settings.config, + ).feeMultiplier, + ) + return r.wallet + .sendCoinsBatch(r.account, txs, cryptoCode, feeMultiplier) .then(res => { mem.clear(module.exports.balance) return res @@ -106,38 +132,40 @@ function sendCoinsBatch (settings, txs, cryptoCode) { }) } -function newAddress (settings, info, tx) { - const walletAddressPromise = fetchWallet(settings, info.cryptoCode) - .then(r => r.wallet.newAddress(r.account, info, tx, settings, r.operatorId)) +function newAddress(settings, info, tx) { + const walletAddressPromise = fetchWallet(settings, info.cryptoCode).then(r => + r.wallet.newAddress(r.account, info, tx, settings, r.operatorId), + ) return Promise.all([ walletAddressPromise, - layer2.newAddress(settings, info) - ]) - .then(([toAddress, layer2Address]) => ({ - toAddress, - layer2Address - })) + layer2.newAddress(settings, info), + ]).then(([toAddress, layer2Address]) => ({ + toAddress, + layer2Address, + })) } -function newFunding (settings, cryptoCode, address) { - return fetchWallet(settings, cryptoCode) - .then(r => { - const wallet = r.wallet - const account = r.account +function newFunding(settings, cryptoCode) { + return fetchWallet(settings, cryptoCode).then(r => { + const wallet = r.wallet + const account = r.account - return wallet.newFunding(account, cryptoCode, settings, r.operatorId) - }) + return wallet.newFunding(account, cryptoCode, settings, r.operatorId) + }) } -function mergeStatus (a, b) { +function mergeStatus(a, b) { if (!a) return b if (!b) return a - return { receivedCryptoAtoms: a.receivedCryptoAtoms, status: mergeStatusMode(a.status, b.status) } + return { + receivedCryptoAtoms: a.receivedCryptoAtoms, + status: mergeStatusMode(a.status, b.status), + } } -function mergeStatusMode (a, b) { +function mergeStatusMode(a, b) { const cleared = ['authorized', 'confirmed', 'instant'] if (_.includes(a, cleared)) return a if (_.includes(b, cleared)) return b @@ -151,25 +179,30 @@ function mergeStatusMode (a, b) { return 'notSeen' } -function getWalletStatus (settings, tx) { - const fudgeFactorEnabled = configManager.getGlobalCashOut(settings.config).fudgeFactorActive +function getWalletStatus(settings, tx) { + const fudgeFactorEnabled = configManager.getGlobalCashOut( + settings.config, + ).fudgeFactorActive const fudgeFactor = fudgeFactorEnabled ? 100 : 0 const requested = tx.cryptoAtoms.minus(fudgeFactor) - const walletStatusPromise = fetchWallet(settings, tx.cryptoCode) - .then(r => r.wallet.getStatus(r.account, tx, requested, settings, r.operatorId)) + const walletStatusPromise = fetchWallet(settings, tx.cryptoCode).then(r => + r.wallet.getStatus(r.account, tx, requested, settings, r.operatorId), + ) return Promise.all([ walletStatusPromise, - layer2.getStatus(settings, tx) - ]) - .then(([walletStatus, layer2Status]) => { - return mergeStatus(walletStatus, layer2Status) - }) + layer2.getStatus(settings, tx), + ]).then(([walletStatus, layer2Status]) => { + return mergeStatus(walletStatus, layer2Status) + }) } -function authorizeZeroConf (settings, tx, machineId) { - const walletSettings = configManager.getWalletSettings(tx.cryptoCode, settings.config) +function authorizeZeroConf(settings, tx) { + const walletSettings = configManager.getWalletSettings( + tx.cryptoCode, + settings.config, + ) const isBitcoindAvailable = walletSettings.wallet === 'bitcoind' const plugin = walletSettings.zeroConf const zeroConfLimit = walletSettings.zeroConfLimit || 0 @@ -192,45 +225,60 @@ function authorizeZeroConf (settings, tx, machineId) { const zeroConf = ph.load(ph.ZERO_CONF, plugin) const account = settings.accounts[plugin] - return zeroConf.authorize(account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode, isBitcoindAvailable) + return zeroConf.authorize( + account, + tx.toAddress, + tx.cryptoAtoms, + tx.cryptoCode, + isBitcoindAvailable, + ) } -function getStatus (settings, tx, machineId) { - return getWalletStatus(settings, tx) - .then((statusRec) => { - if (statusRec.status === 'authorized') { - return authorizeZeroConf(settings, tx, machineId) - .then(isAuthorized => { - const publishAge = Date.now() - tx.publishedAt +function getStatus(settings, tx) { + return getWalletStatus(settings, tx).then(statusRec => { + if (statusRec.status === 'authorized') { + return authorizeZeroConf(settings, tx).then(isAuthorized => { + const publishAge = Date.now() - tx.publishedAt - const unauthorizedStatus = publishAge < ZERO_CONF_EXPIRATION - ? 'published' - : 'rejected' + const unauthorizedStatus = + publishAge < ZERO_CONF_EXPIRATION ? 'published' : 'rejected' - // Sanity check to confirm if there's any cryptoatoms for which to dispense bills - const authorizedStatus = isAuthorized ? 'authorized' : unauthorizedStatus - const status = BN(tx.cryptoAtoms).gt(0) ? authorizedStatus : 'rejected' + // Sanity check to confirm if there's any cryptoatoms for which to dispense bills + const authorizedStatus = isAuthorized + ? 'authorized' + : unauthorizedStatus + const status = BN(tx.cryptoAtoms).gt(0) ? authorizedStatus : 'rejected' - return { receivedCryptoAtoms: statusRec.receivedCryptoAtoms, status } - }) - } + return { receivedCryptoAtoms: statusRec.receivedCryptoAtoms, status } + }) + } - return statusRec - }) + return statusRec + }) } -function sweep (settings, txId, cryptoCode, hdIndex) { - return fetchWallet(settings, cryptoCode) - .then(r => r.wallet.sweep(r.account, txId, cryptoCode, hdIndex, settings, r.operatorId)) +function sweep(settings, txId, cryptoCode, hdIndex) { + return fetchWallet(settings, cryptoCode).then(r => + r.wallet.sweep( + r.account, + txId, + cryptoCode, + hdIndex, + settings, + r.operatorId, + ), + ) } -function isHd (settings, tx) { - return fetchWallet(settings, tx.cryptoCode) - .then(r => r.wallet.supportsHd) +function isHd(settings, tx) { + return fetchWallet(settings, tx.cryptoCode).then(r => r.wallet.supportsHd) } -function cryptoNetwork (settings, cryptoCode) { - const plugin = configManager.getWalletSettings(cryptoCode, settings.config).wallet +function cryptoNetwork(settings, cryptoCode) { + const plugin = configManager.getWalletSettings( + cryptoCode, + settings.config, + ).wallet const account = settings.accounts[plugin] return fetchWallet(settings, cryptoCode).then(r => { if (!r.wallet.cryptoNetwork) return Promise.resolve(false) @@ -238,23 +286,31 @@ function cryptoNetwork (settings, cryptoCode) { }) } -function isStrictAddress (settings, cryptoCode, toAddress) { +function isStrictAddress(settings, cryptoCode, toAddress) { // Note: For now, only for wallets that specifically check for this. - return fetchWallet(settings, cryptoCode) - .then(r => { - if (!r.wallet.isStrictAddress) return true - return r.wallet.isStrictAddress(cryptoCode, toAddress, settings, r.operatorId) - }) -} - -function supportsBatching (settings, cryptoCode) { return fetchWallet(settings, cryptoCode).then(r => { - return Promise.resolve(!!r.wallet.SUPPORTS_BATCHING && !!configManager.getWalletSettings(cryptoCode, settings.config).allowTransactionBatching) + if (!r.wallet.isStrictAddress) return true + return r.wallet.isStrictAddress( + cryptoCode, + toAddress, + settings, + r.operatorId, + ) }) } -function checkBlockchainStatus (settings, cryptoCode) { +function supportsBatching(settings, cryptoCode) { + return fetchWallet(settings, cryptoCode).then(r => { + return Promise.resolve( + !!r.wallet.SUPPORTS_BATCHING && + !!configManager.getWalletSettings(cryptoCode, settings.config) + .allowTransactionBatching, + ) + }) +} + +function checkBlockchainStatus(settings, cryptoCode) { const walletDaemons = { BTC: './plugins/wallet/bitcoind/bitcoind.js', BCH: './plugins/wallet/bitcoincashd/bitcoincashd.js', @@ -262,11 +318,12 @@ function checkBlockchainStatus (settings, cryptoCode) { ETH: './plugins/wallet/geth/base.js', LTC: './plugins/wallet/litecoind/litecoind.js', XMR: './plugins/wallet/monerod/monerod.js', - ZEC: './plugins/wallet/zcashd/zcashd.js' + ZEC: './plugins/wallet/zcashd/zcashd.js', } - return Promise.resolve(require(walletDaemons[cryptoCode])) - .then(({ checkBlockchainStatus }) => checkBlockchainStatus(cryptoCode)) + return Promise.resolve(require(walletDaemons[cryptoCode])).then( + ({ checkBlockchainStatus }) => checkBlockchainStatus(cryptoCode), + ) } const balance = (settings, cryptoCode) => { @@ -286,12 +343,12 @@ const balance = (settings, cryptoCode) => { const balanceNormal = mem(_balance, { maxAge: BALANCE_FETCH_SPEED_MULTIPLIER.NORMAL * FETCH_INTERVAL, - cacheKey: (settings, cryptoCode) => cryptoCode + cacheKey: (settings, cryptoCode) => cryptoCode, }) const balanceSlow = mem(_balance, { maxAge: BALANCE_FETCH_SPEED_MULTIPLIER.SLOW * FETCH_INTERVAL, - cacheKey: (settings, cryptoCode) => cryptoCode + cacheKey: (settings, cryptoCode) => cryptoCode, }) module.exports = { @@ -307,5 +364,5 @@ module.exports = { cryptoNetwork, supportsBatching, checkBlockchainStatus, - probeLN + probeLN, }