Add website api login mutation
This commit is contained in:
parent
c106d7607e
commit
9007437bd3
19 changed files with 385 additions and 27 deletions
|
@ -29,6 +29,10 @@
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
"max-len": ["off"],
|
"max-len": ["off"],
|
||||||
"import/first": ["off"],
|
"import/first": ["off"],
|
||||||
"max-classes-per-file": ["off"]
|
"max-classes-per-file": ["off"],
|
||||||
|
"@typescript-eslint/consistent-type-imports": ["error", {
|
||||||
|
"prefer": "type-imports",
|
||||||
|
"disallowTypeAnnotations": true
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
dist
|
dist
|
||||||
|
secrets
|
||||||
|
|
193
backend/package-lock.json
generated
193
backend/package-lock.json
generated
|
@ -578,6 +578,20 @@
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@wulkanowy/sdk": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wulkanowy/sdk/-/sdk-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-ktBYmd4nNeAs5STH9rf9EaAxPxSCN2PWtuP+rqW980RcWIv6IJddYvW1j8u60VFoThxMPWMpRHr7MeRqjKaC4A==",
|
||||||
|
"requires": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"axios-cookiejar-support": "^1.0.1",
|
||||||
|
"cheerio": "github:dominik-korsa/cheerio#export-types",
|
||||||
|
"date-fns": "^2.16.1",
|
||||||
|
"date-fns-tz": "^1.0.12",
|
||||||
|
"querystring": "^0.2.0",
|
||||||
|
"tough-cookie": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"abstract-logging": {
|
"abstract-logging": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||||
|
@ -979,6 +993,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||||
|
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"axios-cookiejar-support": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-IZJxnAJ99XxiLqNeMOqrPbfR7fRyIfaoSLdPUf4AMQEGkH8URs0ghJK/xtqBsD+KsSr3pKl4DEQjCn834pHMig==",
|
||||||
|
"requires": {
|
||||||
|
"is-redirect": "^1.0.0",
|
||||||
|
"pify": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pify": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"backo2": {
|
"backo2": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||||
|
@ -1033,6 +1071,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"boolbase": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
|
||||||
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
@ -1154,6 +1197,37 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cheerio": {
|
||||||
|
"version": "github:dominik-korsa/cheerio#0204881418326ab4ddd4b68938c7de32580c9ae3",
|
||||||
|
"from": "github:dominik-korsa/cheerio#export-types",
|
||||||
|
"requires": {
|
||||||
|
"cheerio-select": "^1.1.0",
|
||||||
|
"dom-serializer": "~1.2.0",
|
||||||
|
"domhandler": "^4.0.0",
|
||||||
|
"htmlparser2": "^6.0.0",
|
||||||
|
"parse5": "^6.0.1",
|
||||||
|
"parse5-htmlparser2-tree-adapter": "^6.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"parse5": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cheerio-select": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-hOcy3Ps4lg6tMWM/q2NL5ItM+BTt1C8TNbtb9ZGscqDbGhKX0TzLRy9QJ2/KVlmmEbMjCNBublWdO4TPz51SdA==",
|
||||||
|
"requires": {
|
||||||
|
"css-select": "^3.1.2",
|
||||||
|
"css-what": "^4.0.0",
|
||||||
|
"domelementtype": "^2.1.0",
|
||||||
|
"domhandler": "^4.0.0",
|
||||||
|
"domutils": "^2.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "3.5.0",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz",
|
||||||
|
@ -1272,6 +1346,23 @@
|
||||||
"which": "^2.0.1"
|
"which": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"css-select": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==",
|
||||||
|
"requires": {
|
||||||
|
"boolbase": "^1.0.0",
|
||||||
|
"css-what": "^4.0.0",
|
||||||
|
"domhandler": "^4.0.0",
|
||||||
|
"domutils": "^2.4.3",
|
||||||
|
"nth-check": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"css-what": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A=="
|
||||||
|
},
|
||||||
"cssfilter": {
|
"cssfilter": {
|
||||||
"version": "0.0.10",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
||||||
|
@ -1286,6 +1377,16 @@
|
||||||
"array-find-index": "^1.0.1"
|
"array-find-index": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"date-fns": {
|
||||||
|
"version": "2.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
|
||||||
|
"integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ=="
|
||||||
|
},
|
||||||
|
"date-fns-tz": {
|
||||||
|
"version": "1.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.0.12.tgz",
|
||||||
|
"integrity": "sha512-Ca+9pjGkU90XDHnclfSjz9o7g/ZqyYyYI0aCYmbf65P75oy8gktuaRslO3UPXl3ADgAnF9/KCykQkpU3/xvtWQ=="
|
||||||
|
},
|
||||||
"dateformat": {
|
"dateformat": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
|
||||||
|
@ -1381,6 +1482,39 @@
|
||||||
"esutils": "^2.0.2"
|
"esutils": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dom-serializer": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.0.1",
|
||||||
|
"domhandler": "^4.0.0",
|
||||||
|
"entities": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domelementtype": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w=="
|
||||||
|
},
|
||||||
|
"domhandler": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domutils": {
|
||||||
|
"version": "2.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz",
|
||||||
|
"integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==",
|
||||||
|
"requires": {
|
||||||
|
"dom-serializer": "^1.0.1",
|
||||||
|
"domelementtype": "^2.0.1",
|
||||||
|
"domhandler": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dotenv": {
|
"dotenv": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||||
|
@ -1417,6 +1551,11 @@
|
||||||
"ansi-colors": "^4.1.1"
|
"ansi-colors": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"entities": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w=="
|
||||||
|
},
|
||||||
"error-ex": {
|
"error-ex": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
|
@ -2041,6 +2180,11 @@
|
||||||
"integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==",
|
"integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
||||||
|
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
|
||||||
|
},
|
||||||
"for-each": {
|
"for-each": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||||
|
@ -2274,6 +2418,17 @@
|
||||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"htmlparser2": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.0.1",
|
||||||
|
"domhandler": "^4.0.0",
|
||||||
|
"domutils": "^2.4.4",
|
||||||
|
"entities": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"http-errors": {
|
"http-errors": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
|
||||||
|
@ -2419,6 +2574,11 @@
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-redirect": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
|
||||||
|
},
|
||||||
"is-regex": {
|
"is-regex": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
|
||||||
|
@ -2844,6 +3004,14 @@
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"nth-check": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
|
||||||
|
"requires": {
|
||||||
|
"boolbase": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
@ -3132,6 +3300,11 @@
|
||||||
"ipaddr.js": "1.9.1"
|
"ipaddr.js": "1.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"psl": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||||
|
},
|
||||||
"pump": {
|
"pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
@ -3146,6 +3319,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
},
|
},
|
||||||
|
"querystring": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||||
|
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||||
|
},
|
||||||
"queue-microtask": {
|
"queue-microtask": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz",
|
||||||
|
@ -3724,6 +3902,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||||
},
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.33",
|
||||||
|
"punycode": "^2.1.1",
|
||||||
|
"universalify": "^0.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tree-kill": {
|
"tree-kill": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||||
|
@ -3930,6 +4118,11 @@
|
||||||
"random-bytes": "~1.0.0"
|
"random-bytes": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"universalify": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||||
|
},
|
||||||
"uri-js": {
|
"uri-js": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"@types/express": "^4.17.10",
|
"@types/express": "^4.17.10",
|
||||||
"@types/lodash": "^4.14.167",
|
"@types/lodash": "^4.14.167",
|
||||||
"@types/node": "^14.14.21",
|
"@types/node": "^14.14.21",
|
||||||
|
"@wulkanowy/sdk": "^0.1.1",
|
||||||
"apollo-server-fastify": "^2.19.2",
|
"apollo-server-fastify": "^2.19.2",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"fastify": "^3.10.1",
|
"fastify": "^3.10.1",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Connection, createConnection, Repository } from 'typeorm';
|
import type { Connection, Repository } from 'typeorm';
|
||||||
|
import { createConnection } from 'typeorm';
|
||||||
import Application from './entities/application';
|
import Application from './entities/application';
|
||||||
|
|
||||||
class Database {
|
class Database {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
import type { ObjectID } from 'typeorm';
|
||||||
import {
|
import {
|
||||||
BaseEntity,
|
BaseEntity,
|
||||||
Column, Entity, ObjectID, ObjectIdColumn,
|
Column, Entity, ObjectIdColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { nanoid } from 'nanoid';
|
||||||
import { scopes } from '../../constants';
|
import { scopes } from '../../constants';
|
||||||
import database from '../../database/database';
|
import database from '../../database/database';
|
||||||
import { ParamError, ScopeError } from '../../errors';
|
import { ParamError, ScopeError } from '../../errors';
|
||||||
import { MyFastifyInstance } from '../../types';
|
import type { MyFastifyInstance } from '../../types';
|
||||||
import {
|
import {
|
||||||
getSessionData, isObject, parseScopeParam, validateOptionalParam, validateParam,
|
getSessionData, isObject, parseScopeParam, validateOptionalParam, validateParam,
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {
|
import type {
|
||||||
MyFastifyInstance,
|
MyFastifyInstance,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import registerAuthorize from './authorize';
|
import registerAuthorize from './authorize';
|
||||||
|
|
15
backend/src/routes/website-api/errors.ts
Normal file
15
backend/src/routes/website-api/errors.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export class UnknownPromptError extends Error {
|
||||||
|
public name = 'UnknownPromptError';
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
super('Unknown prompt');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidCredentialsError extends Error {
|
||||||
|
public name = 'InvalidCredentialsError';
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
super('User with provided credentials not found');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
import { ApolloServer } from 'apollo-server-fastify';
|
import { ApolloServer } from 'apollo-server-fastify';
|
||||||
import { buildSchema } from 'type-graphql';
|
import { buildSchema } from 'type-graphql';
|
||||||
import { ApolloContext, MyFastifyInstance } from '../../types';
|
import type { ApolloContext, MyFastifyInstance } from '../../types';
|
||||||
import { getSessionData } from '../../utils';
|
import { getSessionData } from '../../utils';
|
||||||
|
import LoginResolver from './resolvers/login-resolver';
|
||||||
import PromptInfoResolver from './resolvers/prompt-info-resolver';
|
import PromptInfoResolver from './resolvers/prompt-info-resolver';
|
||||||
import { WebsiteAPIContext } from './types';
|
import type { WebsiteAPIContext } from './types';
|
||||||
|
|
||||||
export default async function registerWebsiteApi(server: MyFastifyInstance): Promise<void> {
|
export default async function registerWebsiteApi(server: MyFastifyInstance): Promise<void> {
|
||||||
const schema = await buildSchema({
|
const schema = await buildSchema({
|
||||||
authMode: 'error',
|
authMode: 'error',
|
||||||
resolvers: [PromptInfoResolver],
|
resolvers: [
|
||||||
|
PromptInfoResolver,
|
||||||
|
LoginResolver,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
const apolloServer = new ApolloServer({
|
const apolloServer = new ApolloServer({
|
||||||
schema,
|
schema,
|
||||||
|
@ -19,7 +23,7 @@ export default async function registerWebsiteApi(server: MyFastifyInstance): Pro
|
||||||
});
|
});
|
||||||
await server.register(apolloServer.createHandler({
|
await server.register(apolloServer.createHandler({
|
||||||
cors: {
|
cors: {
|
||||||
origin: 'https://google.com',
|
origin: false,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
console.log(apolloServer.graphqlPath);
|
console.log(apolloServer.graphqlPath);
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Field, Int, ObjectType } from 'type-graphql';
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export default class LoginResultStudent {
|
||||||
|
@Field(() => Int)
|
||||||
|
public studentId!: number;
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
public name!: string;
|
||||||
|
}
|
11
backend/src/routes/website-api/models/login-result.ts
Normal file
11
backend/src/routes/website-api/models/login-result.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Field, ObjectType } from 'type-graphql';
|
||||||
|
import LoginResultStudent from './login-result-student';
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export default class LoginResult {
|
||||||
|
@Field(() => String)
|
||||||
|
public encryptedPrivateKey!: string;
|
||||||
|
|
||||||
|
@Field(() => [LoginResultStudent])
|
||||||
|
public students!: LoginResultStudent[];
|
||||||
|
}
|
58
backend/src/routes/website-api/resolvers/login-resolver.ts
Normal file
58
backend/src/routes/website-api/resolvers/login-resolver.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/* eslint-disable class-methods-use-this */
|
||||||
|
import { Client } from '@wulkanowy/sdk';
|
||||||
|
import type { DiaryInfo } from '@wulkanowy/sdk/dist/diary/interfaces/diary/diary-info';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import {
|
||||||
|
Arg, Ctx, Mutation, Resolver,
|
||||||
|
} from 'type-graphql';
|
||||||
|
import {
|
||||||
|
encryptSymmetrical, encryptWithPublicKey, generatePrivatePublicPair, requireEnvHex,
|
||||||
|
} from '../../../utils';
|
||||||
|
import { UnknownPromptError } from '../errors';
|
||||||
|
import LoginResult from '../models/login-result';
|
||||||
|
import type LoginResultStudent from '../models/login-result-student';
|
||||||
|
import type { WebsiteAPIContext } from '../types';
|
||||||
|
|
||||||
|
@Resolver()
|
||||||
|
export default class LoginResolver {
|
||||||
|
@Mutation(() => LoginResult)
|
||||||
|
public async login(
|
||||||
|
@Arg('promptId') promptId: string,
|
||||||
|
@Arg('username') username: string,
|
||||||
|
@Arg('password') password: string,
|
||||||
|
@Arg('host') host: string,
|
||||||
|
@Ctx() { sessionData }: WebsiteAPIContext,
|
||||||
|
): Promise<LoginResult> {
|
||||||
|
const prompt = sessionData.prompts.get(promptId);
|
||||||
|
if (!prompt) throw new UnknownPromptError();
|
||||||
|
const client = new Client(host, () => ({
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
}));
|
||||||
|
await client.login();
|
||||||
|
const diaryList = await client.getDiaryList();
|
||||||
|
const { privateKey, publicKey } = await generatePrivatePublicPair();
|
||||||
|
const encryptedPrivateKey = encryptSymmetrical(
|
||||||
|
privateKey,
|
||||||
|
requireEnvHex('CREDENTIALS_PRIVATE_KEY_ENCRYPT_KEY'),
|
||||||
|
);
|
||||||
|
const encryptedPassword = encryptWithPublicKey(password, publicKey);
|
||||||
|
console.log(diaryList.map((e) => e.serialized.info));
|
||||||
|
const students = _.toPairs(_.groupBy(diaryList.map((e) => e.serialized.info), 'studentId'))
|
||||||
|
.map(([, diaryInfoList]: [string, DiaryInfo[]]) => diaryInfoList[0])
|
||||||
|
.map<LoginResultStudent>((diaryInfo) => ({
|
||||||
|
name: `${diaryInfo.studentFirstName} ${diaryInfo.studentSurname}`,
|
||||||
|
studentId: diaryInfo.studentId,
|
||||||
|
}));
|
||||||
|
prompt.loginInfo = {
|
||||||
|
encryptedPassword,
|
||||||
|
host,
|
||||||
|
username,
|
||||||
|
availableStudentIds: students.map(({ studentId }) => studentId),
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
encryptedPrivateKey,
|
||||||
|
students,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +1,25 @@
|
||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
|
import type { ResolverInterface } from 'type-graphql';
|
||||||
import {
|
import {
|
||||||
Arg, Ctx, FieldResolver, Query, Resolver, ResolverInterface, Root,
|
Arg, Ctx, FieldResolver, Query, Resolver, Root,
|
||||||
} from 'type-graphql';
|
} from 'type-graphql';
|
||||||
import database from '../../../database/database';
|
import database from '../../../database/database';
|
||||||
|
import { UnknownPromptError } from '../errors';
|
||||||
import PromptInfo from '../models/prompt-info';
|
import PromptInfo from '../models/prompt-info';
|
||||||
import PromptInfoApplication from '../models/prompt-info-application';
|
import type PromptInfoApplication from '../models/prompt-info-application';
|
||||||
import { WebsiteAPIContext } from '../types';
|
import type { WebsiteAPIContext } from '../types';
|
||||||
|
|
||||||
@Resolver(PromptInfo)
|
@Resolver(PromptInfo)
|
||||||
export default class PromptInfoResolver implements ResolverInterface<PromptInfo> {
|
export default class PromptInfoResolver implements ResolverInterface<PromptInfo> {
|
||||||
@Query(() => PromptInfo)
|
@Query(() => PromptInfo)
|
||||||
public promptInfo(
|
public promptInfo(
|
||||||
@Arg('id') id: string,
|
@Arg('promptId') promptId: string,
|
||||||
@Ctx() { sessionData }: WebsiteAPIContext,
|
@Ctx() { sessionData }: WebsiteAPIContext,
|
||||||
): Partial<PromptInfo> {
|
): Partial<PromptInfo> {
|
||||||
const prompt = sessionData.prompts.get(id);
|
const prompt = sessionData.prompts.get(promptId);
|
||||||
if (!prompt) throw new Error('Prompt data not found');
|
if (!prompt) throw new UnknownPromptError();
|
||||||
return {
|
return {
|
||||||
id,
|
id: promptId,
|
||||||
clientId: prompt.clientId,
|
clientId: prompt.clientId,
|
||||||
scopes: prompt.scopes,
|
scopes: prompt.scopes,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { ApolloContext, SessionData } from '../../types';
|
import type SessionData from '../../session-data';
|
||||||
|
import type { ApolloContext } from '../../types';
|
||||||
|
|
||||||
export interface WebsiteAPIContext extends ApolloContext {
|
export interface WebsiteAPIContext extends ApolloContext {
|
||||||
sessionData: SessionData;
|
sessionData: SessionData;
|
||||||
|
|
5
backend/src/session-data.ts
Normal file
5
backend/src/session-data.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import type { Prompt } from './types';
|
||||||
|
|
||||||
|
export default class SessionData {
|
||||||
|
public prompts = new Map<string, Prompt>();
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import type {
|
||||||
FastifyInstance, FastifyRequest, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault,
|
FastifyInstance, FastifyRequest, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault,
|
||||||
} from 'fastify';
|
} from 'fastify';
|
||||||
|
import type SessionData from './session-data';
|
||||||
|
|
||||||
export interface Prompt {
|
export interface Prompt {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
|
@ -11,10 +12,12 @@ export interface Prompt {
|
||||||
value: string;
|
value: string;
|
||||||
method: 'plain' | 'S256';
|
method: 'plain' | 'S256';
|
||||||
};
|
};
|
||||||
}
|
loginInfo?: {
|
||||||
|
host: string;
|
||||||
export interface SessionData {
|
username: string;
|
||||||
prompts: Map<string, Prompt>
|
encryptedPassword: string;
|
||||||
|
availableStudentIds: number[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Session {
|
export interface Session {
|
||||||
|
|
43
backend/src/utils/crypto.ts
Normal file
43
backend/src/utils/crypto.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import * as util from 'util';
|
||||||
|
|
||||||
|
export function generatePrivatePublicPair(): Promise<{
|
||||||
|
privateKey: string;
|
||||||
|
publicKey: string;
|
||||||
|
}> {
|
||||||
|
return util.promisify(crypto.generateKeyPair)('rsa', {
|
||||||
|
modulusLength: 1024,
|
||||||
|
publicKeyEncoding: {
|
||||||
|
type: 'spki',
|
||||||
|
format: 'pem',
|
||||||
|
},
|
||||||
|
privateKeyEncoding: {
|
||||||
|
type: 'pkcs8',
|
||||||
|
format: 'pem',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encryptSymmetrical(value: string, key: Buffer): string {
|
||||||
|
const ivBuffer = crypto.randomBytes(16);
|
||||||
|
const cipher = crypto.createCipheriv('aes-256-cbc', key, ivBuffer);
|
||||||
|
const encrypted = Buffer.concat([cipher.update(value), cipher.final()]);
|
||||||
|
return `${encrypted.toString('base64')}@${ivBuffer.toString('base64')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decryptSymmetrical(encrypted: string, key: Buffer): string {
|
||||||
|
const [iv, encryptedData] = encrypted.split('@');
|
||||||
|
const ivBuffer = Buffer.from(iv, 'base64');
|
||||||
|
const encryptedBuffer = Buffer.from(encryptedData, 'base64');
|
||||||
|
const decipher = crypto.createDecipheriv('aes-256-cbc', key, ivBuffer);
|
||||||
|
const decrypted = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()]);
|
||||||
|
return decrypted.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encryptWithPublicKey(value: string, publicKey: string): string {
|
||||||
|
return crypto.publicEncrypt(publicKey, Buffer.from(value)).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decryptWithPrivateKey(encrypted: string, privateKey: string): string {
|
||||||
|
return crypto.privateDecrypt(privateKey, Buffer.from(encrypted, 'base64')).toString();
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { ParamError } from './errors';
|
import { ParamError } from '../errors';
|
||||||
import { Prompt, Session, SessionData } from './types';
|
import SessionData from '../session-data';
|
||||||
|
import type { Session } from '../types';
|
||||||
|
|
||||||
|
export * from './crypto';
|
||||||
|
|
||||||
export function requireEnv(name: string): string {
|
export function requireEnv(name: string): string {
|
||||||
const value = process.env[name];
|
const value = process.env[name];
|
||||||
|
@ -8,6 +11,10 @@ export function requireEnv(name: string): string {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function requireEnvHex(name: string): Buffer {
|
||||||
|
return Buffer.from(requireEnv(name), 'hex');
|
||||||
|
}
|
||||||
|
|
||||||
export function parseIntStrict(value: string, radix = 10): number {
|
export function parseIntStrict(value: string, radix = 10): number {
|
||||||
const number = parseInt(value, radix);
|
const number = parseInt(value, radix);
|
||||||
if (_.isNaN(number)) throw new Error(`Cannot parse ${value} to int`);
|
if (_.isNaN(number)) throw new Error(`Cannot parse ${value} to int`);
|
||||||
|
@ -47,9 +54,7 @@ export function parseScopeParam(key: string, value: unknown): string[] {
|
||||||
export function getSessionData(session: Session): SessionData {
|
export function getSessionData(session: Session): SessionData {
|
||||||
if (session.data === undefined) {
|
if (session.data === undefined) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
session.data = {
|
session.data = new SessionData();
|
||||||
prompts: new Map<string, Prompt>(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return session.data;
|
return session.data;
|
||||||
}
|
}
|
Loading…
Reference in a new issue