feat: typesafe query and more UI changes
This commit is contained in:
parent
d73bb88f1d
commit
64e358f61c
13 changed files with 1347 additions and 109 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@
|
|||
packages/server/certs/
|
||||
packages/server/tests/stress/machines
|
||||
packages/server/tests/stress/config.json
|
||||
packages/typesafe-db/lib/
|
||||
|
|
|
|||
305
package-lock.json
generated
305
package-lock.json
generated
|
|
@ -10,7 +10,8 @@
|
|||
"license": "./LICENSE",
|
||||
"workspaces": [
|
||||
"packages/server",
|
||||
"packages/admin-ui"
|
||||
"packages/admin-ui",
|
||||
"packages/typesafe-db"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@eslint/css": "^0.7.0",
|
||||
|
|
@ -10688,6 +10689,16 @@
|
|||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/diff-sequences": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
|
||||
|
|
@ -10844,6 +10855,22 @@
|
|||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-expand": {
|
||||
"version": "12.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.2.tgz",
|
||||
"integrity": "sha512-lXpXz2ZE1cea1gL4sz2Ipj8y4PiVjytYr3Ij0SWoms1PGxIv7m2CRKuRuCRtHdVuvM/hNJPMxt5PbhboNC4dPQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/downshift": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/downshift/-/downshift-9.0.9.tgz",
|
||||
|
|
@ -11054,6 +11081,16 @@
|
|||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/environment": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
|
||||
|
|
@ -13465,6 +13502,101 @@
|
|||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/git-diff": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/git-diff/-/git-diff-2.0.6.tgz",
|
||||
"integrity": "sha512-/Iu4prUrydE3Pb3lCBMbcSNIf81tgGt0W1ZwknnyF62t3tHmtiJTRj0f+1ZIhp3+Rh0ktz1pJVoa7ZXUCskivA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chalk": "^2.3.2",
|
||||
"diff": "^3.5.0",
|
||||
"loglevel": "^1.6.1",
|
||||
"shelljs": "^0.8.1",
|
||||
"shelljs.exec": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/git-diff/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/git-diff/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/git-diff/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/git-diff/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/git-diff/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/git-diff/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/git-diff/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
|
|
@ -14629,6 +14761,16 @@
|
|||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
|
||||
"integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
|
||||
},
|
||||
"node_modules/interpret": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
|
||||
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/io-ts": {
|
||||
"version": "2.2.20",
|
||||
"resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.20.tgz",
|
||||
|
|
@ -16941,6 +17083,120 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/kysely": {
|
||||
"version": "0.28.2",
|
||||
"resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.2.tgz",
|
||||
"integrity": "sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/kysely-codegen": {
|
||||
"version": "0.18.5",
|
||||
"resolved": "https://registry.npmjs.org/kysely-codegen/-/kysely-codegen-0.18.5.tgz",
|
||||
"integrity": "sha512-bj6DMsXcKo0PrrXUk/fdjFgNC6Pwq+HPBCqhNGuD57gwUJZdci2s2OqhNneQeYpAIWGot7/481WdzTyXrClY2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "4.1.2",
|
||||
"cosmiconfig": "^9.0.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"dotenv-expand": "^12.0.2",
|
||||
"git-diff": "^2.0.6",
|
||||
"micromatch": "^4.0.8",
|
||||
"minimist": "^1.2.8",
|
||||
"pluralize": "^8.0.0",
|
||||
"zod": "^3.24.4"
|
||||
},
|
||||
"bin": {
|
||||
"kysely-codegen": "dist/cli/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@libsql/kysely-libsql": ">=0.3.0 <0.5.0",
|
||||
"@tediousjs/connection-string": ">=0.5.0 <0.6.0",
|
||||
"better-sqlite3": ">=7.6.2 <8.0.0",
|
||||
"kysely": ">=0.27.0 <1.0.0",
|
||||
"kysely-bun-sqlite": ">=0.3.2 <1.0.0",
|
||||
"kysely-bun-worker": ">=1.2.0 <2.0.0",
|
||||
"mysql2": ">=2.3.3 <4.0.0",
|
||||
"pg": ">=8.8.0 <9.0.0",
|
||||
"tarn": ">=3.0.0 <4.0.0",
|
||||
"tedious": ">=18.0.0 <20.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@libsql/kysely-libsql": {
|
||||
"optional": true
|
||||
},
|
||||
"@tediousjs/connection-string": {
|
||||
"optional": true
|
||||
},
|
||||
"better-sqlite3": {
|
||||
"optional": true
|
||||
},
|
||||
"kysely": {
|
||||
"optional": false
|
||||
},
|
||||
"kysely-bun-sqlite": {
|
||||
"optional": true
|
||||
},
|
||||
"kysely-bun-worker": {
|
||||
"optional": true
|
||||
},
|
||||
"mysql2": {
|
||||
"optional": true
|
||||
},
|
||||
"pg": {
|
||||
"optional": true
|
||||
},
|
||||
"tarn": {
|
||||
"optional": true
|
||||
},
|
||||
"tedious": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/kysely-codegen/node_modules/cosmiconfig": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
|
||||
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"env-paths": "^2.2.1",
|
||||
"import-fresh": "^3.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"parse-json": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/d-fischer"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.9.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/kysely-codegen/node_modules/pluralize": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
|
||||
"integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/lamassu-admin": {
|
||||
"resolved": "packages/admin-ui",
|
||||
"link": true
|
||||
|
|
@ -21212,6 +21468,18 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rechoir": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
|
||||
"integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"resolve": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/referrer-policy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz",
|
||||
|
|
@ -22888,6 +23156,34 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
|
||||
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"glob": "^7.0.0",
|
||||
"interpret": "^1.0.0",
|
||||
"rechoir": "^0.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"shjs": "bin/shjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/shelljs.exec": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/shelljs.exec/-/shelljs.exec-1.1.8.tgz",
|
||||
"integrity": "sha512-vFILCw+lzUtiwBAHV8/Ex8JsFjelFMdhONIsgKNLgTzeRckp2AOYRQtHJE/9LhNvdMmE27AGtzWx0+DHpwIwSw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/shellwords": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
|
||||
|
|
@ -25966,12 +26262,15 @@
|
|||
"resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
|
||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||
},
|
||||
"node_modules/typesafe-db": {
|
||||
"resolved": "packages/typesafe-db",
|
||||
"link": true
|
||||
},
|
||||
"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,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -28778,9 +29077,7 @@
|
|||
}
|
||||
},
|
||||
"packages/typesafe-db": {
|
||||
"name": "lamassu-typesafe-db",
|
||||
"version": "11.0.0-beta.0",
|
||||
"extraneous": true,
|
||||
"license": "../LICENSE",
|
||||
"dependencies": {
|
||||
"kysely": "^0.28.2",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@
|
|||
},
|
||||
"workspaces": [
|
||||
"packages/server",
|
||||
"packages/admin-ui"
|
||||
"packages/admin-ui",
|
||||
"packages/typesafe-db"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@eslint/css": "^0.7.0",
|
||||
|
|
|
|||
|
|
@ -290,7 +290,6 @@ const CustomerProfile = memo(() => {
|
|||
const [error, setError] = useState(null)
|
||||
const [clickedItem, setClickedItem] = useState('overview')
|
||||
const { id: customerId } = useParams()
|
||||
console.log(customerId)
|
||||
|
||||
const {
|
||||
data: customerResponse,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import IconButton from '@mui/material/IconButton'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Visibility from '@mui/icons-material/Visibility'
|
||||
import { format } from 'date-fns/fp'
|
||||
import * as R from 'ramda'
|
||||
import React, { useMemo } from 'react'
|
||||
|
|
@ -40,7 +43,6 @@ const CustomersList = ({ data, country, onClick, loading }) => {
|
|||
...alignRight,
|
||||
},
|
||||
{
|
||||
id: 'totalSpent',
|
||||
accessorKey: 'totalSpent',
|
||||
size: 152,
|
||||
enableColumnFilter: false,
|
||||
|
|
@ -49,17 +51,6 @@ const CustomersList = ({ data, country, onClick, loading }) => {
|
|||
header: 'Total spent',
|
||||
...alignRight,
|
||||
},
|
||||
{
|
||||
header: 'Last active',
|
||||
// accessorKey: 'lastActive',
|
||||
accessorFn: it => new Date(it.lastActive),
|
||||
size: 133,
|
||||
enableColumnFilter: false,
|
||||
Cell: ({ cell }) =>
|
||||
(cell.getValue() &&
|
||||
format('yyyy-MM-dd', new Date(cell.getValue()))) ??
|
||||
'',
|
||||
},
|
||||
{
|
||||
header: 'Last transaction',
|
||||
...alignRight,
|
||||
|
|
@ -80,12 +71,34 @@ const CustomersList = ({ data, country, onClick, loading }) => {
|
|||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'lastActive',
|
||||
header: 'Last active',
|
||||
size: 133,
|
||||
enableColumnFilter: false,
|
||||
Cell: ({ cell }) =>
|
||||
(cell.getValue() &&
|
||||
format('yyyy-MM-dd', new Date(cell.getValue()))) ??
|
||||
'',
|
||||
},
|
||||
{
|
||||
header: 'Status',
|
||||
id: 'status',
|
||||
size: 100,
|
||||
size: 150,
|
||||
enableColumnFilter: false,
|
||||
accessorKey: 'authorizedStatus',
|
||||
sortingFn: (rowA, rowB) => {
|
||||
const statusOrder = { success: 0, warning: 1, error: 2 }
|
||||
const statusA = rowA.original.authorizedStatus.type
|
||||
const statusB = rowB.original.authorizedStatus.type
|
||||
|
||||
if (statusA === statusB) {
|
||||
return rowA.original.authorizedStatus.label.localeCompare(
|
||||
rowB.original.authorizedStatus.label,
|
||||
)
|
||||
}
|
||||
|
||||
return statusOrder[statusA] - statusOrder[statusB]
|
||||
},
|
||||
Cell: ({ cell }) => <MainStatus statuses={[cell.getValue()]} />,
|
||||
},
|
||||
],
|
||||
|
|
@ -101,13 +114,22 @@ const CustomersList = ({ data, country, onClick, loading }) => {
|
|||
columnVisibility: {
|
||||
id: false,
|
||||
},
|
||||
sorting: [{ id: 'lastActive', desc: true }],
|
||||
columnPinning: { right: ['mrt-row-actions'] },
|
||||
},
|
||||
state: { isLoading: loading },
|
||||
getRowId: it => it.id,
|
||||
muiTableBodyRowProps: ({ row }) => ({
|
||||
onClick: () => onClick(row),
|
||||
sx: { cursor: 'pointer' },
|
||||
}),
|
||||
enableRowActions: true,
|
||||
positionActionsColumn: 'last',
|
||||
renderRowActions: ({ row }) => (
|
||||
<div>
|
||||
<Tooltip title="Customer page">
|
||||
<IconButton aria-label="Go to customer" onClick={() => onClick(row)}>
|
||||
<Visibility />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
),
|
||||
})
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -293,4 +293,31 @@ describe('getAuthorizedStatus', () => {
|
|||
type: 'error',
|
||||
})
|
||||
})
|
||||
|
||||
it('should return rejected status for blocked custom info request', () => {
|
||||
const customer = {
|
||||
authorizedOverride: null,
|
||||
isSuspended: false,
|
||||
customInfoRequests: [
|
||||
{
|
||||
infoRequestId: '550e8400-e29b-41d4-a716-446655440000',
|
||||
override: 'blocked',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const triggers = {
|
||||
automation: 'manual',
|
||||
overrides: [],
|
||||
}
|
||||
|
||||
const customRequests = [{ id: '550e8400-e29b-41d4-a716-446655440000' }]
|
||||
|
||||
const result = getAuthorizedStatus(customer, triggers, customRequests)
|
||||
|
||||
expect(result).toEqual({
|
||||
label: 'Rejected',
|
||||
type: 'error',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ const fs = require('fs')
|
|||
const util = require('util')
|
||||
|
||||
const db = require('./db')
|
||||
const anonymous = require('../lib/constants').anonymousCustomer
|
||||
const complianceOverrides = require('./compliance_overrides')
|
||||
const writeFile = util.promisify(fs.writeFile)
|
||||
const notifierQueries = require('./notifier/queries')
|
||||
|
|
@ -17,6 +16,7 @@ const sms = require('./sms')
|
|||
const settingsLoader = require('./new-settings-loader')
|
||||
const logger = require('./logger')
|
||||
const externalCompliance = require('./compliance-external')
|
||||
const { getCustomerList } = require('typesafe-db/lib/customers')
|
||||
|
||||
const { APPROVED, RETRY } = require('./plugins/compliance/consts')
|
||||
|
||||
|
|
@ -489,88 +489,8 @@ function getSlimCustomerByIdBatch(ids) {
|
|||
return db.any(sql, [ids]).then(customers => _.map(camelize, customers))
|
||||
}
|
||||
|
||||
// TODO: getCustomersList and getCustomerById are very similar, so this should be refactored
|
||||
|
||||
/**
|
||||
* Query all customers, ordered by last activity
|
||||
* and with aggregate columns based on their
|
||||
* transactions
|
||||
*
|
||||
* @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(',')
|
||||
|
||||
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,
|
||||
id_card_photo_path, id_card_photo_override, us_ssn, us_ssn_override, sanctions, sanctions_at,
|
||||
sanctions_override, total_txs, total_spent, GREATEST(created, last_transaction, last_data_provided, last_auth_attempt) AS last_active, fiat AS last_tx_fiat,
|
||||
fiat_code AS last_tx_fiat_code, tx_class AS last_tx_class, custom_fields, notes, is_test_customer
|
||||
FROM (
|
||||
SELECT c.id, c.authorized_override,
|
||||
greatest(0, date_part('day', c.suspended_until - NOW())) AS days_suspended,
|
||||
c.suspended_until > NOW() AS is_suspended,
|
||||
c.front_camera_path, c.front_camera_override,
|
||||
c.phone, c.email, c.sms_override, c.id_card_data, c.id_card_data_override, c.id_card_data_expiration,
|
||||
c.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions, c.last_auth_attempt,
|
||||
GREATEST(c.phone_at, c.email_at, c.id_card_data_at, c.front_camera_at, c.id_card_photo_at, c.us_ssn_at) AS last_data_provided,
|
||||
c.sanctions_at, c.sanctions_override, c.is_test_customer, c.created, t.tx_class, t.fiat, t.fiat_code, t.created as last_transaction, cn.notes,
|
||||
row_number() OVER (partition by c.id order by t.created desc) AS rn,
|
||||
sum(CASE WHEN t.id IS NOT NULL THEN 1 ELSE 0 END) OVER (partition by c.id) AS total_txs,
|
||||
coalesce(sum(CASE WHEN error_code IS NULL OR error_code NOT IN ($1^) THEN t.fiat ELSE 0 END) OVER (partition by c.id), 0) AS total_spent, ccf.custom_fields
|
||||
FROM customers c LEFT OUTER JOIN (
|
||||
SELECT 'cashIn' AS tx_class, id, fiat, fiat_code, created, customer_id, error_code
|
||||
FROM cash_in_txs WHERE send_confirmed = true OR batched = true UNION
|
||||
SELECT 'cashOut' AS tx_class, id, fiat, fiat_code, created, customer_id, error_code
|
||||
FROM cash_out_txs WHERE confirmed_at IS NOT NULL) AS t ON c.id = t.customer_id
|
||||
LEFT OUTER JOIN (
|
||||
SELECT cf.customer_id, json_agg(json_build_object('id', cf.custom_field_id, 'label', cf.label, 'value', cf.value)) AS custom_fields FROM (
|
||||
SELECT ccfp.custom_field_id, ccfp.customer_id, cfd.label, ccfp.value FROM custom_field_definitions cfd
|
||||
LEFT OUTER JOIN customer_custom_field_pairs ccfp ON cfd.id = ccfp.custom_field_id
|
||||
) cf GROUP BY cf.customer_id
|
||||
) ccf ON c.id = ccf.customer_id
|
||||
LEFT OUTER JOIN (
|
||||
SELECT customer_id, coalesce(json_agg(customer_notes.*), '[]'::json) AS notes FROM customer_notes
|
||||
GROUP BY customer_notes.customer_id
|
||||
) cn ON c.id = cn.customer_id
|
||||
WHERE c.id != $2
|
||||
) AS cl WHERE rn = 1
|
||||
AND ($4 IS NULL OR phone = $4)
|
||||
AND ($5 IS NULL OR CONCAT(id_card_data::json->>'firstName', ' ', id_card_data::json->>'lastName') = $5 OR id_card_data::json->>'firstName' = $5 OR id_card_data::json->>'lastName' = $5)
|
||||
AND ($6 IS NULL OR id_card_data::json->>'address' = $6)
|
||||
AND ($7 IS NULL OR id_card_data::json->>'documentNumber' = $7)
|
||||
AND ($8 IS NULL OR email = $8)
|
||||
ORDER BY last_active DESC
|
||||
limit $3`
|
||||
return db
|
||||
.any(sql, [
|
||||
passableErrorCodes,
|
||||
anonymous.uuid,
|
||||
null,
|
||||
phone,
|
||||
name,
|
||||
address,
|
||||
id,
|
||||
email,
|
||||
])
|
||||
.then(customers =>
|
||||
Promise.all(
|
||||
_.map(
|
||||
customer => getCustomInfoRequestsData(customer).then(camelizeDeep),
|
||||
customers,
|
||||
),
|
||||
),
|
||||
)
|
||||
function getCustomersList() {
|
||||
return getCustomerList({ withCustomInfoRequest: true })
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ const resolvers = {
|
|||
isAnonymous: parent => parent.customerId === anonymous.uuid,
|
||||
},
|
||||
Query: {
|
||||
customers: (...[, { phone, email, name, address, id }]) =>
|
||||
customers.getCustomersList(phone, name, address, id, email),
|
||||
customers: () => customers.getCustomersList(),
|
||||
customer: (...[, { customerId }]) =>
|
||||
customers.getCustomerById(customerId).then(addLastUsedMachineName),
|
||||
},
|
||||
|
|
|
|||
20
packages/typesafe-db/package.json
Normal file
20
packages/typesafe-db/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "typesafe-db",
|
||||
"version": "11.0.0-beta.0",
|
||||
"license": "../LICENSE",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"kysely-codegen": "^0.18.5",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "tsc --watch",
|
||||
"clean": "rm -rf lib",
|
||||
"generate-types": "kysely-codegen --camel-case --out-file ./src/types/types.d.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"kysely": "^0.28.2",
|
||||
"pg": "^8.16.0"
|
||||
}
|
||||
}
|
||||
159
packages/typesafe-db/src/customers.ts
Normal file
159
packages/typesafe-db/src/customers.ts
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import db from './db.js'
|
||||
import { ExpressionBuilder, sql } from 'kysely'
|
||||
import { Customers, DB } from './types/types.js'
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres'
|
||||
|
||||
type CustomerEB = ExpressionBuilder<DB & { c: Customers }, 'c'>
|
||||
|
||||
const TX_PASSTHROUGH_ERROR_CODES = [
|
||||
'operatorCancel',
|
||||
'scoreThresholdReached',
|
||||
'walletScoringError',
|
||||
]
|
||||
|
||||
function transactionUnion(eb: CustomerEB) {
|
||||
return eb
|
||||
.selectFrom('cashInTxs')
|
||||
.select([
|
||||
'created',
|
||||
'fiat',
|
||||
'fiatCode',
|
||||
'errorCode',
|
||||
eb.val('cashIn').as('txClass'),
|
||||
])
|
||||
.where(({ eb, and, or, ref }) =>
|
||||
and([
|
||||
eb('customerId', '=', ref('c.id')),
|
||||
or([eb('sendConfirmed', '=', true), eb('batched', '=', true)]),
|
||||
]),
|
||||
)
|
||||
.unionAll(
|
||||
eb
|
||||
.selectFrom('cashOutTxs')
|
||||
.select([
|
||||
'created',
|
||||
'fiat',
|
||||
'fiatCode',
|
||||
'errorCode',
|
||||
eb.val('cashOut').as('txClass'),
|
||||
])
|
||||
.where(({ eb, and, ref }) =>
|
||||
and([
|
||||
eb('customerId', '=', ref('c.id')),
|
||||
eb('confirmedAt', 'is not', null),
|
||||
]),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
function joinLatestTx(eb: CustomerEB) {
|
||||
return eb
|
||||
.selectFrom(eb =>
|
||||
transactionUnion(eb).orderBy('created', 'desc').limit(1).as('lastTx'),
|
||||
)
|
||||
.select(['fiatCode', 'fiat', 'txClass', 'created'])
|
||||
.as('lastTx')
|
||||
}
|
||||
|
||||
function joinTxsTotals(eb: CustomerEB) {
|
||||
return eb
|
||||
.selectFrom(eb => transactionUnion(eb).as('combinedTxs'))
|
||||
.select([
|
||||
eb => eb.fn.coalesce(eb.fn.countAll(), eb.val(0)).as('totalTxs'),
|
||||
eb =>
|
||||
eb.fn
|
||||
.coalesce(
|
||||
eb.fn.sum(
|
||||
eb
|
||||
.case()
|
||||
.when(
|
||||
eb.or([
|
||||
eb('combinedTxs.errorCode', 'is', null),
|
||||
eb(
|
||||
'combinedTxs.errorCode',
|
||||
'not in',
|
||||
TX_PASSTHROUGH_ERROR_CODES,
|
||||
),
|
||||
]),
|
||||
)
|
||||
.then(eb.ref('combinedTxs.fiat'))
|
||||
.else(0)
|
||||
.end(),
|
||||
),
|
||||
eb.val(0),
|
||||
)
|
||||
.as('totalSpent'),
|
||||
])
|
||||
.as('txStats')
|
||||
}
|
||||
|
||||
interface GetCustomerListOptions {
|
||||
withCustomInfoRequest: boolean
|
||||
}
|
||||
|
||||
const defaultOptions: GetCustomerListOptions = {
|
||||
withCustomInfoRequest: false,
|
||||
}
|
||||
|
||||
// TODO left join lateral is having issues deriving type
|
||||
function getCustomerList(
|
||||
options: GetCustomerListOptions = defaultOptions,
|
||||
): Promise<any[]> {
|
||||
return db
|
||||
.selectFrom('customers as c')
|
||||
.leftJoinLateral(joinTxsTotals, join => join.onTrue())
|
||||
.leftJoinLateral(joinLatestTx, join => join.onTrue())
|
||||
.select(({ eb, fn, val, ref }) => [
|
||||
'id',
|
||||
'authorizedOverride',
|
||||
'frontCameraPath',
|
||||
'frontCameraOverride',
|
||||
'idCardPhotoPath',
|
||||
'idCardPhotoOverride',
|
||||
'idCardData',
|
||||
'idCardDataOverride',
|
||||
'email',
|
||||
'usSsn',
|
||||
'usSsnOverride',
|
||||
'sanctions',
|
||||
'sanctionsOverride',
|
||||
'txStats.totalSpent',
|
||||
'txStats.totalTxs',
|
||||
ref('lastTx.fiatCode').as('lastTxFiatCode'),
|
||||
ref('lastTx.fiat').as('lastTxFiat'),
|
||||
ref('lastTx.txClass').as('lastTxClass'),
|
||||
fn<Date>('GREATEST', [
|
||||
'c.created',
|
||||
// 'lastTx.created',
|
||||
'c.phoneAt',
|
||||
'c.emailAt',
|
||||
'c.idCardDataAt',
|
||||
'c.frontCameraAt',
|
||||
'c.idCardPhotoAt',
|
||||
'c.usSsnAt',
|
||||
'c.lastAuthAttempt',
|
||||
]).as('lastActive'),
|
||||
eb('c.suspendedUntil', '>', fn<Date>('NOW', [])).as('isSuspended'),
|
||||
fn<number>('GREATEST', [
|
||||
val(0),
|
||||
fn<number>('date_part', [
|
||||
val('day'),
|
||||
eb('c.suspendedUntil', '-', fn<Date>('NOW', [])),
|
||||
]),
|
||||
]).as('daysSuspended'),
|
||||
])
|
||||
.$if(options.withCustomInfoRequest, qb =>
|
||||
qb.select(({ eb, ref }) =>
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('customersCustomInfoRequests')
|
||||
.selectAll()
|
||||
.where('customerId', '=', ref('c.id')),
|
||||
).as('customInfoRequestData'),
|
||||
),
|
||||
)
|
||||
.orderBy('lastActive', 'desc')
|
||||
.execute()
|
||||
}
|
||||
|
||||
export { getCustomerList }
|
||||
24
packages/typesafe-db/src/db.ts
Normal file
24
packages/typesafe-db/src/db.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { DB } from './types/types.js'
|
||||
import { Pool } from 'pg'
|
||||
import { Kysely, PostgresDialect, CamelCasePlugin } from 'kysely'
|
||||
import { PSQL_URL } from 'lamassu-server/lib/constants.js'
|
||||
|
||||
const dialect = new PostgresDialect({
|
||||
pool: new Pool({
|
||||
connectionString: PSQL_URL,
|
||||
max: 5,
|
||||
}),
|
||||
})
|
||||
|
||||
export default new Kysely<DB>({
|
||||
dialect,
|
||||
plugins: [new CamelCasePlugin()],
|
||||
log(event) {
|
||||
if (event.level === 'query') {
|
||||
console.log('Query:', event.query.sql)
|
||||
console.log('Parameters:', event.query.parameters)
|
||||
console.log('Duration:', event.queryDurationMillis + 'ms')
|
||||
console.log('---')
|
||||
}
|
||||
},
|
||||
})
|
||||
745
packages/typesafe-db/src/types/types.d.ts
vendored
Normal file
745
packages/typesafe-db/src/types/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,745 @@
|
|||
/**
|
||||
* This file was generated by kysely-codegen.
|
||||
* Please do not edit it manually.
|
||||
*/
|
||||
|
||||
import type { ColumnType } from 'kysely'
|
||||
|
||||
export type AuthTokenType = 'reset_password' | 'reset_twofa'
|
||||
|
||||
export type CashUnit =
|
||||
| 'cashbox'
|
||||
| 'cassette1'
|
||||
| 'cassette2'
|
||||
| 'cassette3'
|
||||
| 'cassette4'
|
||||
| 'recycler1'
|
||||
| 'recycler2'
|
||||
| 'recycler3'
|
||||
| 'recycler4'
|
||||
| 'recycler5'
|
||||
| 'recycler6'
|
||||
|
||||
export type CashUnitOperationType =
|
||||
| 'cash-box-empty'
|
||||
| 'cash-box-refill'
|
||||
| 'cash-cassette-1-count-change'
|
||||
| 'cash-cassette-1-empty'
|
||||
| 'cash-cassette-1-refill'
|
||||
| 'cash-cassette-2-count-change'
|
||||
| 'cash-cassette-2-empty'
|
||||
| 'cash-cassette-2-refill'
|
||||
| 'cash-cassette-3-count-change'
|
||||
| 'cash-cassette-3-empty'
|
||||
| 'cash-cassette-3-refill'
|
||||
| 'cash-cassette-4-count-change'
|
||||
| 'cash-cassette-4-empty'
|
||||
| 'cash-cassette-4-refill'
|
||||
| 'cash-recycler-1-count-change'
|
||||
| 'cash-recycler-1-empty'
|
||||
| 'cash-recycler-1-refill'
|
||||
| 'cash-recycler-2-count-change'
|
||||
| 'cash-recycler-2-empty'
|
||||
| 'cash-recycler-2-refill'
|
||||
| 'cash-recycler-3-count-change'
|
||||
| 'cash-recycler-3-empty'
|
||||
| 'cash-recycler-3-refill'
|
||||
| 'cash-recycler-4-count-change'
|
||||
| 'cash-recycler-4-empty'
|
||||
| 'cash-recycler-4-refill'
|
||||
| 'cash-recycler-5-count-change'
|
||||
| 'cash-recycler-5-empty'
|
||||
| 'cash-recycler-5-refill'
|
||||
| 'cash-recycler-6-count-change'
|
||||
| 'cash-recycler-6-empty'
|
||||
| 'cash-recycler-6-refill'
|
||||
|
||||
export type ComplianceType =
|
||||
| 'authorized'
|
||||
| 'front_camera'
|
||||
| 'hard_limit'
|
||||
| 'id_card_data'
|
||||
| 'id_card_photo'
|
||||
| 'sanctions'
|
||||
| 'sms'
|
||||
| 'us_ssn'
|
||||
|
||||
export type DiscountSource = 'individualDiscount' | 'promoCode'
|
||||
|
||||
export type ExternalComplianceStatus =
|
||||
| 'APPROVED'
|
||||
| 'PENDING'
|
||||
| 'REJECTED'
|
||||
| 'RETRY'
|
||||
|
||||
export type Generated<T> =
|
||||
T extends ColumnType<infer S, infer I, infer U>
|
||||
? ColumnType<S, I | undefined, U>
|
||||
: ColumnType<T, T | undefined, T>
|
||||
|
||||
export type Int8 = ColumnType<
|
||||
string,
|
||||
bigint | number | string,
|
||||
bigint | number | string
|
||||
>
|
||||
|
||||
export type Json = JsonValue
|
||||
|
||||
export type JsonArray = JsonValue[]
|
||||
|
||||
export type JsonObject = {
|
||||
[x: string]: JsonValue | undefined
|
||||
}
|
||||
|
||||
export type JsonPrimitive = boolean | number | string | null
|
||||
|
||||
export type JsonValue = JsonArray | JsonObject | JsonPrimitive
|
||||
|
||||
export type NotificationType =
|
||||
| 'compliance'
|
||||
| 'cryptoBalance'
|
||||
| 'error'
|
||||
| 'fiatBalance'
|
||||
| 'highValueTransaction'
|
||||
| 'security'
|
||||
| 'transaction'
|
||||
|
||||
export type Numeric = ColumnType<string, number | string, number | string>
|
||||
|
||||
export type Role = 'superuser' | 'user'
|
||||
|
||||
export type SmsNoticeEvent =
|
||||
| 'cash_out_dispense_ready'
|
||||
| 'sms_code'
|
||||
| 'sms_receipt'
|
||||
|
||||
export type StatusStage =
|
||||
| 'authorized'
|
||||
| 'confirmed'
|
||||
| 'instant'
|
||||
| 'insufficientFunds'
|
||||
| 'notSeen'
|
||||
| 'published'
|
||||
| 'rejected'
|
||||
|
||||
export type Timestamp = ColumnType<Date, Date | string, Date | string>
|
||||
|
||||
export type TradeType = 'buy' | 'sell'
|
||||
|
||||
export type TransactionBatchStatus = 'failed' | 'open' | 'ready' | 'sent'
|
||||
|
||||
export type VerificationType = 'automatic' | 'blocked' | 'verified'
|
||||
|
||||
export interface AuthTokens {
|
||||
expire: Generated<Timestamp>
|
||||
token: string
|
||||
type: AuthTokenType
|
||||
userId: string | null
|
||||
}
|
||||
|
||||
export interface Bills {
|
||||
cashboxBatchId: string | null
|
||||
cashInFee: Numeric
|
||||
cashInTxsId: string
|
||||
created: Generated<Timestamp>
|
||||
cryptoCode: Generated<string | null>
|
||||
destinationUnit: Generated<CashUnit>
|
||||
deviceTime: Int8
|
||||
fiat: number
|
||||
fiatCode: string
|
||||
id: string
|
||||
legacy: Generated<boolean | null>
|
||||
}
|
||||
|
||||
export interface Blacklist {
|
||||
address: string
|
||||
blacklistMessageId: Generated<string>
|
||||
}
|
||||
|
||||
export interface BlacklistMessages {
|
||||
allowToggle: Generated<boolean>
|
||||
content: string
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface CashInActions {
|
||||
action: string
|
||||
created: Generated<Timestamp>
|
||||
error: string | null
|
||||
errorCode: string | null
|
||||
id: Generated<number>
|
||||
txHash: string | null
|
||||
txId: string
|
||||
}
|
||||
|
||||
export interface CashInTxs {
|
||||
batched: Generated<boolean>
|
||||
batchId: string | null
|
||||
batchTime: Timestamp | null
|
||||
cashInFee: Numeric
|
||||
commissionPercentage: Generated<Numeric | null>
|
||||
created: Generated<Timestamp>
|
||||
cryptoAtoms: Numeric
|
||||
cryptoCode: string
|
||||
customerId: Generated<string | null>
|
||||
deviceId: string
|
||||
discount: number | null
|
||||
discountSource: DiscountSource | null
|
||||
email: string | null
|
||||
error: string | null
|
||||
errorCode: string | null
|
||||
fee: Int8 | null
|
||||
fiat: Numeric
|
||||
fiatCode: string
|
||||
id: string
|
||||
isPaperWallet: Generated<boolean | null>
|
||||
minimumTx: number
|
||||
operatorCompleted: Generated<boolean>
|
||||
phone: string | null
|
||||
rawTickerPrice: Generated<Numeric | null>
|
||||
send: Generated<boolean>
|
||||
sendConfirmed: Generated<boolean>
|
||||
sendPending: Generated<boolean>
|
||||
sendTime: Timestamp | null
|
||||
termsAccepted: Generated<boolean>
|
||||
timedout: Generated<boolean>
|
||||
toAddress: string
|
||||
txCustomerPhotoAt: Timestamp | null
|
||||
txCustomerPhotoPath: string | null
|
||||
txHash: string | null
|
||||
txVersion: number
|
||||
walletScore: number | null
|
||||
}
|
||||
|
||||
export interface CashinTxTrades {
|
||||
tradeId: Generated<number>
|
||||
txId: string
|
||||
}
|
||||
|
||||
export interface CashOutActions {
|
||||
action: string
|
||||
created: Generated<Timestamp>
|
||||
denomination1: number | null
|
||||
denomination2: number | null
|
||||
denomination3: number | null
|
||||
denomination4: number | null
|
||||
denominationRecycler1: number | null
|
||||
denominationRecycler2: number | null
|
||||
denominationRecycler3: number | null
|
||||
denominationRecycler4: number | null
|
||||
denominationRecycler5: number | null
|
||||
denominationRecycler6: number | null
|
||||
deviceId: Generated<string>
|
||||
deviceTime: Int8 | null
|
||||
dispensed1: number | null
|
||||
dispensed2: number | null
|
||||
dispensed3: number | null
|
||||
dispensed4: number | null
|
||||
dispensedRecycler1: number | null
|
||||
dispensedRecycler2: number | null
|
||||
dispensedRecycler3: number | null
|
||||
dispensedRecycler4: number | null
|
||||
dispensedRecycler5: number | null
|
||||
dispensedRecycler6: number | null
|
||||
error: string | null
|
||||
errorCode: string | null
|
||||
id: Generated<number>
|
||||
layer2Address: string | null
|
||||
provisioned1: number | null
|
||||
provisioned2: number | null
|
||||
provisioned3: number | null
|
||||
provisioned4: number | null
|
||||
provisionedRecycler1: number | null
|
||||
provisionedRecycler2: number | null
|
||||
provisionedRecycler3: number | null
|
||||
provisionedRecycler4: number | null
|
||||
provisionedRecycler5: number | null
|
||||
provisionedRecycler6: number | null
|
||||
redeem: Generated<boolean>
|
||||
rejected1: number | null
|
||||
rejected2: number | null
|
||||
rejected3: number | null
|
||||
rejected4: number | null
|
||||
rejectedRecycler1: number | null
|
||||
rejectedRecycler2: number | null
|
||||
rejectedRecycler3: number | null
|
||||
rejectedRecycler4: number | null
|
||||
rejectedRecycler5: number | null
|
||||
rejectedRecycler6: number | null
|
||||
toAddress: string | null
|
||||
txHash: string | null
|
||||
txId: string
|
||||
}
|
||||
|
||||
export interface CashOutTxs {
|
||||
commissionPercentage: Generated<Numeric | null>
|
||||
confirmedAt: Timestamp | null
|
||||
created: Generated<Timestamp>
|
||||
cryptoAtoms: Numeric
|
||||
cryptoCode: string
|
||||
customerId: Generated<string | null>
|
||||
denomination1: number | null
|
||||
denomination2: number | null
|
||||
denomination3: number | null
|
||||
denomination4: number | null
|
||||
denominationRecycler1: number | null
|
||||
denominationRecycler2: number | null
|
||||
denominationRecycler3: number | null
|
||||
denominationRecycler4: number | null
|
||||
denominationRecycler5: number | null
|
||||
denominationRecycler6: number | null
|
||||
deviceId: string
|
||||
discount: number | null
|
||||
discountSource: DiscountSource | null
|
||||
dispense: Generated<boolean>
|
||||
dispenseConfirmed: Generated<boolean | null>
|
||||
email: string | null
|
||||
error: string | null
|
||||
errorCode: string | null
|
||||
fiat: Numeric
|
||||
fiatCode: string
|
||||
fixedFee: Generated<Numeric>
|
||||
hdIndex: Generated<number | null>
|
||||
id: string
|
||||
layer2Address: string | null
|
||||
notified: Generated<boolean>
|
||||
phone: string | null
|
||||
provisioned1: number | null
|
||||
provisioned2: number | null
|
||||
provisioned3: number | null
|
||||
provisioned4: number | null
|
||||
provisionedRecycler1: number | null
|
||||
provisionedRecycler2: number | null
|
||||
provisionedRecycler3: number | null
|
||||
provisionedRecycler4: number | null
|
||||
provisionedRecycler5: number | null
|
||||
provisionedRecycler6: number | null
|
||||
publishedAt: Timestamp | null
|
||||
rawTickerPrice: Generated<Numeric | null>
|
||||
receivedCryptoAtoms: Generated<Numeric | null>
|
||||
redeem: Generated<boolean>
|
||||
status: Generated<StatusStage>
|
||||
swept: Generated<boolean>
|
||||
termsAccepted: Generated<boolean>
|
||||
timedout: Generated<boolean>
|
||||
toAddress: string
|
||||
txCustomerPhotoAt: Timestamp | null
|
||||
txCustomerPhotoPath: string | null
|
||||
txVersion: number
|
||||
walletScore: number | null
|
||||
}
|
||||
|
||||
export interface CashoutTxTrades {
|
||||
tradeId: Generated<number>
|
||||
txId: string
|
||||
}
|
||||
|
||||
export interface CashUnitOperation {
|
||||
billCountOverride: number | null
|
||||
created: Generated<Timestamp>
|
||||
deviceId: string | null
|
||||
id: string
|
||||
operationType: CashUnitOperationType
|
||||
performedBy: string | null
|
||||
}
|
||||
|
||||
export interface ComplianceOverrides {
|
||||
complianceType: ComplianceType
|
||||
customerId: string | null
|
||||
id: string
|
||||
overrideAt: Timestamp
|
||||
overrideBy: string | null
|
||||
verification: VerificationType
|
||||
}
|
||||
|
||||
export interface Coupons {
|
||||
code: string
|
||||
discount: number
|
||||
id: string
|
||||
softDeleted: Generated<boolean | null>
|
||||
}
|
||||
|
||||
export interface CustomerCustomFieldPairs {
|
||||
customerId: string
|
||||
customFieldId: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface CustomerExternalCompliance {
|
||||
customerId: string
|
||||
externalId: string
|
||||
lastKnownStatus: ExternalComplianceStatus | null
|
||||
lastUpdated: Generated<Timestamp>
|
||||
service: string
|
||||
}
|
||||
|
||||
export interface CustomerNotes {
|
||||
content: Generated<string>
|
||||
created: Generated<Timestamp>
|
||||
customerId: string
|
||||
id: string
|
||||
lastEditedAt: Timestamp | null
|
||||
lastEditedBy: string | null
|
||||
title: Generated<string>
|
||||
}
|
||||
|
||||
export interface Customers {
|
||||
address: string | null
|
||||
authorizedAt: Timestamp | null
|
||||
authorizedOverride: Generated<VerificationType>
|
||||
authorizedOverrideAt: Timestamp | null
|
||||
authorizedOverrideBy: string | null
|
||||
created: Generated<Timestamp>
|
||||
email: string | null
|
||||
emailAt: Timestamp | null
|
||||
frontCameraAt: Timestamp | null
|
||||
frontCameraOverride: Generated<VerificationType>
|
||||
frontCameraOverrideAt: Timestamp | null
|
||||
frontCameraOverrideBy: string | null
|
||||
frontCameraPath: string | null
|
||||
id: string
|
||||
idCardData: Json | null
|
||||
idCardDataAt: Timestamp | null
|
||||
idCardDataExpiration: Timestamp | null
|
||||
idCardDataNumber: string | null
|
||||
idCardDataOverride: Generated<VerificationType>
|
||||
idCardDataOverrideAt: Timestamp | null
|
||||
idCardDataOverrideBy: string | null
|
||||
idCardDataRaw: string | null
|
||||
idCardPhotoAt: Timestamp | null
|
||||
idCardPhotoOverride: Generated<VerificationType>
|
||||
idCardPhotoOverrideAt: Timestamp | null
|
||||
idCardPhotoOverrideBy: string | null
|
||||
idCardPhotoPath: string | null
|
||||
isTestCustomer: Generated<boolean>
|
||||
lastAuthAttempt: Timestamp | null
|
||||
lastUsedMachine: string | null
|
||||
name: string | null
|
||||
phone: string | null
|
||||
phoneAt: Timestamp | null
|
||||
phoneOverride: Generated<VerificationType>
|
||||
phoneOverrideAt: Timestamp | null
|
||||
phoneOverrideBy: string | null
|
||||
sanctions: boolean | null
|
||||
sanctionsAt: Timestamp | null
|
||||
sanctionsOverride: Generated<VerificationType>
|
||||
sanctionsOverrideAt: Timestamp | null
|
||||
sanctionsOverrideBy: string | null
|
||||
smsOverride: Generated<VerificationType>
|
||||
smsOverrideAt: Timestamp | null
|
||||
smsOverrideBy: string | null
|
||||
subscriberInfo: Json | null
|
||||
subscriberInfoAt: Timestamp | null
|
||||
subscriberInfoBy: string | null
|
||||
suspendedUntil: Timestamp | null
|
||||
usSsn: string | null
|
||||
usSsnAt: Timestamp | null
|
||||
usSsnOverride: Generated<VerificationType>
|
||||
usSsnOverrideAt: Timestamp | null
|
||||
usSsnOverrideBy: string | null
|
||||
}
|
||||
|
||||
export interface CustomersCustomInfoRequests {
|
||||
customerData: Json
|
||||
customerId: string
|
||||
infoRequestId: string
|
||||
override: Generated<VerificationType>
|
||||
overrideAt: Timestamp | null
|
||||
overrideBy: string | null
|
||||
}
|
||||
|
||||
export interface CustomFieldDefinitions {
|
||||
active: Generated<boolean | null>
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface CustomInfoRequests {
|
||||
customRequest: Json | null
|
||||
enabled: Generated<boolean>
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface Devices {
|
||||
cassette1: Generated<number>
|
||||
cassette2: Generated<number>
|
||||
cassette3: Generated<number>
|
||||
cassette4: Generated<number>
|
||||
created: Generated<Timestamp>
|
||||
deviceId: string
|
||||
diagnosticsFrontUpdatedAt: Timestamp | null
|
||||
diagnosticsScanUpdatedAt: Timestamp | null
|
||||
diagnosticsTimestamp: Timestamp | null
|
||||
display: Generated<boolean>
|
||||
lastOnline: Generated<Timestamp>
|
||||
location: Generated<Json>
|
||||
model: string | null
|
||||
name: string
|
||||
numberOfCassettes: Generated<number>
|
||||
numberOfRecyclers: Generated<number>
|
||||
paired: Generated<boolean>
|
||||
recycler1: Generated<number>
|
||||
recycler2: Generated<number>
|
||||
recycler3: Generated<number>
|
||||
recycler4: Generated<number>
|
||||
recycler5: Generated<number>
|
||||
recycler6: Generated<number>
|
||||
userConfigId: number | null
|
||||
version: string | null
|
||||
}
|
||||
|
||||
export interface EditedCustomerData {
|
||||
created: Generated<Timestamp>
|
||||
customerId: string
|
||||
frontCameraAt: Timestamp | null
|
||||
frontCameraBy: string | null
|
||||
frontCameraPath: string | null
|
||||
idCardData: Json | null
|
||||
idCardDataAt: Timestamp | null
|
||||
idCardDataBy: string | null
|
||||
idCardPhotoAt: Timestamp | null
|
||||
idCardPhotoBy: string | null
|
||||
idCardPhotoPath: string | null
|
||||
name: string | null
|
||||
nameAt: Timestamp | null
|
||||
nameBy: string | null
|
||||
subscriberInfo: Json | null
|
||||
subscriberInfoAt: Timestamp | null
|
||||
subscriberInfoBy: string | null
|
||||
usSsn: string | null
|
||||
usSsnAt: Timestamp | null
|
||||
usSsnBy: string | null
|
||||
}
|
||||
|
||||
export interface EmptyUnitBills {
|
||||
cashboxBatchId: string | null
|
||||
created: Generated<Timestamp>
|
||||
deviceId: string
|
||||
fiat: number
|
||||
fiatCode: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface HardwareCredentials {
|
||||
created: Generated<Timestamp | null>
|
||||
data: Json
|
||||
id: string
|
||||
lastUsed: Generated<Timestamp | null>
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface IndividualDiscounts {
|
||||
customerId: string
|
||||
discount: number
|
||||
id: string
|
||||
softDeleted: Generated<boolean | null>
|
||||
}
|
||||
|
||||
export interface Logs {
|
||||
deviceId: string | null
|
||||
id: string
|
||||
logLevel: string | null
|
||||
message: string | null
|
||||
serial: Generated<number>
|
||||
serverTimestamp: Generated<Timestamp>
|
||||
timestamp: Timestamp | null
|
||||
}
|
||||
|
||||
export interface MachineEvents {
|
||||
created: Generated<Timestamp>
|
||||
deviceId: string
|
||||
deviceTime: Timestamp | null
|
||||
eventType: string
|
||||
id: string
|
||||
note: string | null
|
||||
}
|
||||
|
||||
export interface MachineNetworkHeartbeat {
|
||||
averagePacketLoss: Numeric
|
||||
averageResponseTime: Numeric
|
||||
created: Generated<Timestamp>
|
||||
deviceId: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface MachineNetworkPerformance {
|
||||
created: Generated<Timestamp>
|
||||
deviceId: string
|
||||
downloadSpeed: Numeric
|
||||
}
|
||||
|
||||
export interface MachinePings {
|
||||
deviceId: string
|
||||
deviceTime: Timestamp
|
||||
updated: Generated<Timestamp>
|
||||
}
|
||||
|
||||
export interface Migrations {
|
||||
data: Json
|
||||
id: Generated<number>
|
||||
}
|
||||
|
||||
export interface Notifications {
|
||||
created: Generated<Timestamp>
|
||||
detail: Json | null
|
||||
id: string
|
||||
message: string
|
||||
read: Generated<boolean>
|
||||
type: NotificationType
|
||||
valid: Generated<boolean>
|
||||
}
|
||||
|
||||
export interface OperatorIds {
|
||||
id: Generated<number>
|
||||
operatorId: string
|
||||
service: string
|
||||
}
|
||||
|
||||
export interface PairingTokens {
|
||||
created: Generated<Timestamp>
|
||||
id: Generated<number>
|
||||
name: string
|
||||
token: string | null
|
||||
}
|
||||
|
||||
export interface SanctionsLogs {
|
||||
created: Generated<Timestamp>
|
||||
customerId: string
|
||||
deviceId: string
|
||||
id: string
|
||||
sanctionedAliasFullName: string
|
||||
sanctionedAliasId: string | null
|
||||
sanctionedId: string
|
||||
}
|
||||
|
||||
export interface ServerLogs {
|
||||
deviceId: string | null
|
||||
id: string
|
||||
logLevel: string | null
|
||||
message: string | null
|
||||
meta: Json | null
|
||||
timestamp: Generated<Timestamp | null>
|
||||
}
|
||||
|
||||
export interface SmsNotices {
|
||||
allowToggle: Generated<boolean>
|
||||
created: Generated<Timestamp>
|
||||
enabled: Generated<boolean>
|
||||
event: SmsNoticeEvent
|
||||
id: string
|
||||
message: string
|
||||
messageName: string
|
||||
}
|
||||
|
||||
export interface Trades {
|
||||
created: Generated<Timestamp>
|
||||
cryptoAtoms: Numeric
|
||||
cryptoCode: string
|
||||
error: string | null
|
||||
fiatCode: string
|
||||
id: Generated<number>
|
||||
type: TradeType
|
||||
}
|
||||
|
||||
export interface TransactionBatches {
|
||||
closedAt: Timestamp | null
|
||||
createdAt: Generated<Timestamp>
|
||||
cryptoCode: string
|
||||
errorMessage: string | null
|
||||
id: string
|
||||
status: Generated<TransactionBatchStatus>
|
||||
}
|
||||
|
||||
export interface UnpairedDevices {
|
||||
deviceId: string
|
||||
id: string
|
||||
model: string | null
|
||||
name: string | null
|
||||
paired: Timestamp
|
||||
unpaired: Timestamp
|
||||
}
|
||||
|
||||
export interface UserConfig {
|
||||
created: Generated<Timestamp>
|
||||
data: Json
|
||||
id: Generated<number>
|
||||
schemaVersion: Generated<number>
|
||||
type: string
|
||||
valid: boolean
|
||||
}
|
||||
|
||||
export interface UserRegisterTokens {
|
||||
expire: Generated<Timestamp>
|
||||
role: Generated<Role | null>
|
||||
token: string
|
||||
useFido: Generated<boolean | null>
|
||||
username: string
|
||||
}
|
||||
|
||||
export interface Users {
|
||||
created: Generated<Timestamp>
|
||||
enabled: Generated<boolean | null>
|
||||
id: string
|
||||
lastAccessed: Generated<Timestamp>
|
||||
lastAccessedAddress: string | null
|
||||
lastAccessedFrom: string | null
|
||||
password: string | null
|
||||
role: Generated<Role>
|
||||
tempTwofaCode: string | null
|
||||
twofaCode: string | null
|
||||
username: string
|
||||
}
|
||||
|
||||
export interface UserSessions {
|
||||
expire: Timestamp
|
||||
sess: Json
|
||||
sid: string
|
||||
}
|
||||
|
||||
export interface DB {
|
||||
authTokens: AuthTokens
|
||||
bills: Bills
|
||||
blacklist: Blacklist
|
||||
blacklistMessages: BlacklistMessages
|
||||
cashInActions: CashInActions
|
||||
cashInTxs: CashInTxs
|
||||
cashinTxTrades: CashinTxTrades
|
||||
cashOutActions: CashOutActions
|
||||
cashOutTxs: CashOutTxs
|
||||
cashoutTxTrades: CashoutTxTrades
|
||||
cashUnitOperation: CashUnitOperation
|
||||
complianceOverrides: ComplianceOverrides
|
||||
coupons: Coupons
|
||||
customerCustomFieldPairs: CustomerCustomFieldPairs
|
||||
customerExternalCompliance: CustomerExternalCompliance
|
||||
customerNotes: CustomerNotes
|
||||
customers: Customers
|
||||
customersCustomInfoRequests: CustomersCustomInfoRequests
|
||||
customFieldDefinitions: CustomFieldDefinitions
|
||||
customInfoRequests: CustomInfoRequests
|
||||
devices: Devices
|
||||
editedCustomerData: EditedCustomerData
|
||||
emptyUnitBills: EmptyUnitBills
|
||||
hardwareCredentials: HardwareCredentials
|
||||
individualDiscounts: IndividualDiscounts
|
||||
logs: Logs
|
||||
machineEvents: MachineEvents
|
||||
machineNetworkHeartbeat: MachineNetworkHeartbeat
|
||||
machineNetworkPerformance: MachineNetworkPerformance
|
||||
machinePings: MachinePings
|
||||
migrations: Migrations
|
||||
notifications: Notifications
|
||||
operatorIds: OperatorIds
|
||||
pairingTokens: PairingTokens
|
||||
sanctionsLogs: SanctionsLogs
|
||||
serverLogs: ServerLogs
|
||||
smsNotices: SmsNotices
|
||||
trades: Trades
|
||||
transactionBatches: TransactionBatches
|
||||
unpairedDevices: UnpairedDevices
|
||||
userConfig: UserConfig
|
||||
userRegisterTokens: UserRegisterTokens
|
||||
users: Users
|
||||
userSessions: UserSessions
|
||||
}
|
||||
24
packages/typesafe-db/tsconfig.json
Normal file
24
packages/typesafe-db/tsconfig.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Node 22",
|
||||
"_version": "22.0.0",
|
||||
|
||||
"compilerOptions": {
|
||||
"lib": ["es2023"],
|
||||
"module": "nodenext",
|
||||
"target": "es2022",
|
||||
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node16",
|
||||
|
||||
"outDir": "./lib",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["lib", "node_modules"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue