mirror of
https://github.com/stonith404/pingvin-share.git
synced 2024-11-05 15:30:14 +01:00
Merge remote-tracking branch 'origin/main' into main
This commit is contained in:
commit
af71317ec4
251
backend/package-lock.json
generated
251
backend/package-lock.json
generated
@ -17,6 +17,7 @@
|
|||||||
"@nestjs/platform-express": "^9.2.1",
|
"@nestjs/platform-express": "^9.2.1",
|
||||||
"@nestjs/schedule": "^2.1.0",
|
"@nestjs/schedule": "^2.1.0",
|
||||||
"@nestjs/throttler": "^3.1.0",
|
"@nestjs/throttler": "^3.1.0",
|
||||||
|
"@prisma/client": "^4.7.1",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
"argon2": "^0.30.2",
|
"argon2": "^0.30.2",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
@ -26,18 +27,20 @@
|
|||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
|
"otplib": "^12.0.1",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-jwt": "^4.0.0",
|
"passport-jwt": "^4.0.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"qrcode-svg": "^1.1.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "^7.6.0"
|
"rxjs": "^7.6.0",
|
||||||
|
"ts-node": "^10.9.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^9.1.5",
|
"@nestjs/cli": "^9.1.5",
|
||||||
"@nestjs/schematics": "^9.0.3",
|
"@nestjs/schematics": "^9.0.3",
|
||||||
"@nestjs/testing": "^9.2.1",
|
"@nestjs/testing": "^9.2.1",
|
||||||
"@prisma/client": "^4.7.1",
|
|
||||||
"@types/archiver": "^5.3.1",
|
"@types/archiver": "^5.3.1",
|
||||||
"@types/cron": "^2.0.0",
|
"@types/cron": "^2.0.0",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
@ -46,6 +49,7 @@
|
|||||||
"@types/node": "^18.11.10",
|
"@types/node": "^18.11.10",
|
||||||
"@types/nodemailer": "^6.4.6",
|
"@types/nodemailer": "^6.4.6",
|
||||||
"@types/passport-jwt": "^3.0.7",
|
"@types/passport-jwt": "^3.0.7",
|
||||||
|
"@types/qrcode-svg": "^1.1.1",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
@ -58,7 +62,6 @@
|
|||||||
"prisma": "^4.7.1",
|
"prisma": "^4.7.1",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"ts-loader": "^9.4.2",
|
"ts-loader": "^9.4.2",
|
||||||
"ts-node": "^10.9.1",
|
|
||||||
"tsconfig-paths": "4.1.1",
|
"tsconfig-paths": "4.1.1",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3",
|
||||||
"wait-on": "^6.0.1"
|
"wait-on": "^6.0.1"
|
||||||
@ -328,7 +331,6 @@
|
|||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "0.3.9"
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
},
|
},
|
||||||
@ -340,7 +342,6 @@
|
|||||||
"version": "0.3.9",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.0.3",
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
@ -443,7 +444,6 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
@ -484,8 +484,7 @@
|
|||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.14",
|
"version": "1.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.15",
|
"version": "0.3.15",
|
||||||
@ -975,6 +974,48 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@otplib/core": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA=="
|
||||||
|
},
|
||||||
|
"node_modules/@otplib/plugin-crypto": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@otplib/core": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@otplib/plugin-thirty-two": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"thirty-two": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@otplib/preset-default": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/preset-default/-/preset-default-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"@otplib/plugin-crypto": "^12.0.1",
|
||||||
|
"@otplib/plugin-thirty-two": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@otplib/preset-v11": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/preset-v11/-/preset-v11-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"@otplib/plugin-crypto": "^12.0.1",
|
||||||
|
"@otplib/plugin-thirty-two": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@phc/format": {
|
"node_modules/@phc/format": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
|
||||||
@ -1013,7 +1054,6 @@
|
|||||||
"version": "4.7.1",
|
"version": "4.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz",
|
||||||
"integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==",
|
"integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c"
|
"@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c"
|
||||||
@ -1034,14 +1074,13 @@
|
|||||||
"version": "4.7.1",
|
"version": "4.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz",
|
||||||
"integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==",
|
"integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true
|
"hasInstallScript": true
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/engines-version": {
|
"node_modules/@prisma/engines-version": {
|
||||||
"version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c",
|
"version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz",
|
||||||
"integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==",
|
"integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@sideway/address": {
|
"node_modules/@sideway/address": {
|
||||||
"version": "4.1.4",
|
"version": "4.1.4",
|
||||||
@ -1067,26 +1106,22 @@
|
|||||||
"node_modules/@tsconfig/node10": {
|
"node_modules/@tsconfig/node10": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node12": {
|
"node_modules/@tsconfig/node12": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node14": {
|
"node_modules/@tsconfig/node14": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node16": {
|
"node_modules/@tsconfig/node16": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/archiver": {
|
"node_modules/@types/archiver": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
@ -1288,6 +1323,12 @@
|
|||||||
"@types/passport": "*"
|
"@types/passport": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/qrcode-svg": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/qrcode-svg/-/qrcode-svg-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-uTuEgFXMknpun//Jj6b1R8T8LiMi9fNpH+cnhZr4b7col2HHTMmjYfm/WOZ7nzjuGpk+oTrpHhePe1qlWtHWTA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.7",
|
"version": "6.9.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
|
||||||
@ -1701,7 +1742,6 @@
|
|||||||
"version": "8.8.0",
|
"version": "8.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||||
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@ -1731,7 +1771,6 @@
|
|||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
@ -1939,8 +1978,7 @@
|
|||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/argon2": {
|
"node_modules/argon2": {
|
||||||
"version": "0.30.2",
|
"version": "0.30.2",
|
||||||
@ -2674,8 +2712,7 @@
|
|||||||
"node_modules/create-require": {
|
"node_modules/create-require": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/cron": {
|
"node_modules/cron": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@ -2826,7 +2863,6 @@
|
|||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
@ -4736,8 +4772,7 @@
|
|||||||
"node_modules/make-error": {
|
"node_modules/make-error": {
|
||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/md5": {
|
"node_modules/md5": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
@ -5320,6 +5355,16 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/otplib": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"@otplib/preset-default": "^12.0.1",
|
||||||
|
"@otplib/preset-v11": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p-limit": {
|
"node_modules/p-limit": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||||
@ -5857,7 +5902,7 @@
|
|||||||
"version": "4.7.1",
|
"version": "4.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz",
|
||||||
"integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==",
|
"integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/engines": "4.7.1"
|
"@prisma/engines": "4.7.1"
|
||||||
@ -5912,6 +5957,14 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode-svg": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw==",
|
||||||
|
"bin": {
|
||||||
|
"qrcode-svg": "bin/qrcode-svg.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
@ -6787,6 +6840,14 @@
|
|||||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/thirty-two": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.2.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/through": {
|
"node_modules/through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
@ -6892,7 +6953,6 @@
|
|||||||
"version": "10.9.1",
|
"version": "10.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
@ -7052,7 +7112,6 @@
|
|||||||
"version": "4.9.3",
|
"version": "4.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
||||||
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@ -7174,8 +7233,7 @@
|
|||||||
"node_modules/v8-compile-cache-lib": {
|
"node_modules/v8-compile-cache-lib": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/validator": {
|
"node_modules/validator": {
|
||||||
"version": "13.7.0",
|
"version": "13.7.0",
|
||||||
@ -7499,7 +7557,6 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
@ -7748,7 +7805,6 @@
|
|||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/trace-mapping": "0.3.9"
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
},
|
},
|
||||||
@ -7757,7 +7813,6 @@
|
|||||||
"version": "0.3.9",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/resolve-uri": "^3.0.3",
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
@ -7843,8 +7898,7 @@
|
|||||||
"@jridgewell/resolve-uri": {
|
"@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@jridgewell/set-array": {
|
"@jridgewell/set-array": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@ -7878,8 +7932,7 @@
|
|||||||
"@jridgewell/sourcemap-codec": {
|
"@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.14",
|
"version": "1.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@jridgewell/trace-mapping": {
|
"@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.15",
|
"version": "0.3.15",
|
||||||
@ -8205,6 +8258,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@otplib/core": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA=="
|
||||||
|
},
|
||||||
|
"@otplib/plugin-crypto": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==",
|
||||||
|
"requires": {
|
||||||
|
"@otplib/core": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@otplib/plugin-thirty-two": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==",
|
||||||
|
"requires": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"thirty-two": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@otplib/preset-default": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/preset-default/-/preset-default-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==",
|
||||||
|
"requires": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"@otplib/plugin-crypto": "^12.0.1",
|
||||||
|
"@otplib/plugin-thirty-two": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@otplib/preset-v11": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@otplib/preset-v11/-/preset-v11-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==",
|
||||||
|
"requires": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"@otplib/plugin-crypto": "^12.0.1",
|
||||||
|
"@otplib/plugin-thirty-two": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@phc/format": {
|
"@phc/format": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
|
||||||
@ -8234,7 +8329,6 @@
|
|||||||
"version": "4.7.1",
|
"version": "4.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz",
|
||||||
"integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==",
|
"integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c"
|
"@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c"
|
||||||
}
|
}
|
||||||
@ -8243,13 +8337,12 @@
|
|||||||
"version": "4.7.1",
|
"version": "4.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz",
|
||||||
"integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==",
|
"integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"@prisma/engines-version": {
|
"@prisma/engines-version": {
|
||||||
"version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c",
|
"version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz",
|
||||||
"integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==",
|
"integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@sideway/address": {
|
"@sideway/address": {
|
||||||
"version": "4.1.4",
|
"version": "4.1.4",
|
||||||
@ -8275,26 +8368,22 @@
|
|||||||
"@tsconfig/node10": {
|
"@tsconfig/node10": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@tsconfig/node12": {
|
"@tsconfig/node12": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@tsconfig/node14": {
|
"@tsconfig/node14": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@tsconfig/node16": {
|
"@tsconfig/node16": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@types/archiver": {
|
"@types/archiver": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
@ -8496,6 +8585,12 @@
|
|||||||
"@types/passport": "*"
|
"@types/passport": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/qrcode-svg": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/qrcode-svg/-/qrcode-svg-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-uTuEgFXMknpun//Jj6b1R8T8LiMi9fNpH+cnhZr4b7col2HHTMmjYfm/WOZ7nzjuGpk+oTrpHhePe1qlWtHWTA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/qs": {
|
"@types/qs": {
|
||||||
"version": "6.9.7",
|
"version": "6.9.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
|
||||||
@ -8816,8 +8911,7 @@
|
|||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "8.8.0",
|
"version": "8.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||||
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"acorn-import-assertions": {
|
"acorn-import-assertions": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
@ -8836,8 +8930,7 @@
|
|||||||
"acorn-walk": {
|
"acorn-walk": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"agent-base": {
|
"agent-base": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
@ -8991,8 +9084,7 @@
|
|||||||
"arg": {
|
"arg": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"argon2": {
|
"argon2": {
|
||||||
"version": "0.30.2",
|
"version": "0.30.2",
|
||||||
@ -9539,8 +9631,7 @@
|
|||||||
"create-require": {
|
"create-require": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"cron": {
|
"cron": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@ -9648,8 +9739,7 @@
|
|||||||
"diff": {
|
"diff": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"dir-glob": {
|
"dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
@ -11123,8 +11213,7 @@
|
|||||||
"make-error": {
|
"make-error": {
|
||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"md5": {
|
"md5": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
@ -11563,6 +11652,16 @@
|
|||||||
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"otplib": {
|
||||||
|
"version": "12.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz",
|
||||||
|
"integrity": "sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==",
|
||||||
|
"requires": {
|
||||||
|
"@otplib/core": "^12.0.1",
|
||||||
|
"@otplib/preset-default": "^12.0.1",
|
||||||
|
"@otplib/preset-v11": "^12.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"p-limit": {
|
"p-limit": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||||
@ -11959,7 +12058,7 @@
|
|||||||
"version": "4.7.1",
|
"version": "4.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz",
|
||||||
"integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==",
|
"integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@prisma/engines": "4.7.1"
|
"@prisma/engines": "4.7.1"
|
||||||
}
|
}
|
||||||
@ -12000,6 +12099,11 @@
|
|||||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"qrcode-svg": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw=="
|
||||||
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
@ -12645,6 +12749,11 @@
|
|||||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"thirty-two": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA=="
|
||||||
|
},
|
||||||
"through": {
|
"through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
@ -12724,7 +12833,6 @@
|
|||||||
"version": "10.9.1",
|
"version": "10.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
@ -12835,8 +12943,7 @@
|
|||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.9.3",
|
"version": "4.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
||||||
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.17.3",
|
"version": "3.17.3",
|
||||||
@ -12916,8 +13023,7 @@
|
|||||||
"v8-compile-cache-lib": {
|
"v8-compile-cache-lib": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"version": "13.7.0",
|
"version": "13.7.0",
|
||||||
@ -13157,8 +13263,7 @@
|
|||||||
"yn": {
|
"yn": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"yocto-queue": {
|
"yocto-queue": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
@ -32,9 +32,11 @@
|
|||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
|
"otplib": "^12.0.1",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-jwt": "^4.0.0",
|
"passport-jwt": "^4.0.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"qrcode-svg": "^1.1.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "^7.6.0",
|
"rxjs": "^7.6.0",
|
||||||
@ -52,6 +54,7 @@
|
|||||||
"@types/node": "^18.11.10",
|
"@types/node": "^18.11.10",
|
||||||
"@types/nodemailer": "^6.4.6",
|
"@types/nodemailer": "^6.4.6",
|
||||||
"@types/passport-jwt": "^3.0.7",
|
"@types/passport-jwt": "^3.0.7",
|
||||||
|
"@types/qrcode-svg": "^1.1.1",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "LoginToken" (
|
||||||
|
"token" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"expiresAt" DATETIME NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"used" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
CONSTRAINT "LoginToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_User" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"password" TEXT NOT NULL,
|
||||||
|
"isAdmin" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"totpEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"totpVerified" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"totpSecret" TEXT
|
||||||
|
);
|
||||||
|
INSERT INTO "new_User" ("createdAt", "email", "id", "isAdmin", "password", "updatedAt", "username") SELECT "createdAt", "email", "id", "isAdmin", "password", "updatedAt", "username" FROM "User";
|
||||||
|
DROP TABLE "User";
|
||||||
|
ALTER TABLE "new_User" RENAME TO "User";
|
||||||
|
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||||
|
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
@ -19,6 +19,11 @@ model User {
|
|||||||
|
|
||||||
shares Share[]
|
shares Share[]
|
||||||
refreshTokens RefreshToken[]
|
refreshTokens RefreshToken[]
|
||||||
|
loginTokens LoginToken[]
|
||||||
|
|
||||||
|
totpEnabled Boolean @default(false)
|
||||||
|
totpVerified Boolean @default(false)
|
||||||
|
totpSecret String?
|
||||||
}
|
}
|
||||||
|
|
||||||
model RefreshToken {
|
model RefreshToken {
|
||||||
@ -31,6 +36,17 @@ model RefreshToken {
|
|||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model LoginToken {
|
||||||
|
token String @id @default(uuid())
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
expiresAt DateTime
|
||||||
|
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
used Boolean @default(false)
|
||||||
|
}
|
||||||
|
|
||||||
model Share {
|
model Share {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
@ -52,6 +52,13 @@ const configVariables: Prisma.ConfigCreateInput[] = [
|
|||||||
value: crypto.randomBytes(256).toString("base64"),
|
value: crypto.randomBytes(256).toString("base64"),
|
||||||
locked: true,
|
locked: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "TOTP_SECRET",
|
||||||
|
description: "A 16 byte random string used to generate TOTP secrets",
|
||||||
|
type: "string",
|
||||||
|
value: crypto.randomBytes(16).toString("base64"),
|
||||||
|
locked: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "ENABLE_EMAIL_RECIPIENTS",
|
key: "ENABLE_EMAIL_RECIPIENTS",
|
||||||
description:
|
description:
|
||||||
|
@ -14,8 +14,11 @@ import { AuthService } from "./auth.service";
|
|||||||
import { GetUser } from "./decorator/getUser.decorator";
|
import { GetUser } from "./decorator/getUser.decorator";
|
||||||
import { AuthRegisterDTO } from "./dto/authRegister.dto";
|
import { AuthRegisterDTO } from "./dto/authRegister.dto";
|
||||||
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
||||||
|
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
|
||||||
|
import { EnableTotpDTO } from "./dto/enableTotp.dto";
|
||||||
import { RefreshAccessTokenDTO } from "./dto/refreshAccessToken.dto";
|
import { RefreshAccessTokenDTO } from "./dto/refreshAccessToken.dto";
|
||||||
import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
|
import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
|
||||||
|
import { VerifyTotpDTO } from "./dto/verifyTotp.dto";
|
||||||
import { JwtGuard } from "./guard/jwt.guard";
|
import { JwtGuard } from "./guard/jwt.guard";
|
||||||
|
|
||||||
@Controller("auth")
|
@Controller("auth")
|
||||||
@ -40,6 +43,13 @@ export class AuthController {
|
|||||||
return this.authService.signIn(dto);
|
return this.authService.signIn(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throttle(10, 5 * 60)
|
||||||
|
@Post("signIn/totp")
|
||||||
|
@HttpCode(200)
|
||||||
|
signInTotp(@Body() dto: AuthSignInTotpDTO) {
|
||||||
|
return this.authService.signInTotp(dto);
|
||||||
|
}
|
||||||
|
|
||||||
@Patch("password")
|
@Patch("password")
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async updatePassword(@GetUser() user: User, @Body() dto: UpdatePasswordDTO) {
|
async updatePassword(@GetUser() user: User, @Body() dto: UpdatePasswordDTO) {
|
||||||
@ -54,4 +64,24 @@ export class AuthController {
|
|||||||
);
|
);
|
||||||
return { accessToken };
|
return { accessToken };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Implement recovery codes to disable 2FA just in case someone gets locked out
|
||||||
|
@Post("totp/enable")
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
async enableTotp(@GetUser() user: User, @Body() body: EnableTotpDTO) {
|
||||||
|
return this.authService.enableTotp(user, body.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("totp/verify")
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
async verifyTotp(@GetUser() user: User, @Body() body: VerifyTotpDTO) {
|
||||||
|
return this.authService.verifyTotp(user, body.password, body.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("totp/disable")
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
async disableTotp(@GetUser() user: User, @Body() body: VerifyTotpDTO) {
|
||||||
|
// Note: We use VerifyTotpDTO here because it has both fields we need: password and totp code
|
||||||
|
return this.authService.disableTotp(user, body.password, body.code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,10 @@ import { ConfigService } from "src/config/config.service";
|
|||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { AuthRegisterDTO } from "./dto/authRegister.dto";
|
import { AuthRegisterDTO } from "./dto/authRegister.dto";
|
||||||
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
||||||
|
import { authenticator, totp } from "otplib";
|
||||||
|
import * as qrcode from "qrcode-svg";
|
||||||
|
import * as crypto from "crypto";
|
||||||
|
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
@ -63,6 +67,69 @@ export class AuthService {
|
|||||||
if (!user || !(await argon.verify(user.password, dto.password)))
|
if (!user || !(await argon.verify(user.password, dto.password)))
|
||||||
throw new UnauthorizedException("Wrong email or password");
|
throw new UnauthorizedException("Wrong email or password");
|
||||||
|
|
||||||
|
// TODO: Make all old loginTokens invalid when a new one is created
|
||||||
|
// Check if the user has TOTP enabled
|
||||||
|
if (user.totpVerified) {
|
||||||
|
const loginToken = await this.createLoginToken(user.id);
|
||||||
|
|
||||||
|
return { loginToken };
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = await this.createAccessToken(user);
|
||||||
|
const refreshToken = await this.createRefreshToken(user.id);
|
||||||
|
|
||||||
|
return { accessToken, refreshToken };
|
||||||
|
}
|
||||||
|
|
||||||
|
async signInTotp(dto: AuthSignInTotpDTO) {
|
||||||
|
if (!dto.email && !dto.username)
|
||||||
|
throw new BadRequestException("Email or username is required");
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
OR: [{ email: dto.email }, { username: dto.username }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user || !(await argon.verify(user.password, dto.password)))
|
||||||
|
throw new UnauthorizedException("Wrong email or password");
|
||||||
|
|
||||||
|
const token = await this.prisma.loginToken.findFirst({
|
||||||
|
where: {
|
||||||
|
token: dto.loginToken,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!token || token.userId != user.id || token.used)
|
||||||
|
throw new UnauthorizedException("Invalid login token");
|
||||||
|
|
||||||
|
if (token.expiresAt < new Date())
|
||||||
|
throw new UnauthorizedException("Login token expired");
|
||||||
|
|
||||||
|
// Check the TOTP code
|
||||||
|
const { totpSecret } = await this.prisma.user.findUnique({
|
||||||
|
where: { id: user.id },
|
||||||
|
select: { totpSecret: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!totpSecret) {
|
||||||
|
throw new BadRequestException("TOTP is not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptedSecret = this.decryptTotpSecret(totpSecret, dto.password);
|
||||||
|
|
||||||
|
const expected = authenticator.generate(decryptedSecret);
|
||||||
|
|
||||||
|
if (dto.totp !== expected) {
|
||||||
|
throw new BadRequestException("Invalid code");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the login token to used
|
||||||
|
await this.prisma.loginToken.update({
|
||||||
|
where: { token: token.token },
|
||||||
|
data: { used: true },
|
||||||
|
});
|
||||||
|
|
||||||
const accessToken = await this.createAccessToken(user);
|
const accessToken = await this.createAccessToken(user);
|
||||||
const refreshToken = await this.createRefreshToken(user.id);
|
const refreshToken = await this.createRefreshToken(user.id);
|
||||||
|
|
||||||
@ -70,7 +137,7 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updatePassword(user: User, oldPassword: string, newPassword: string) {
|
async updatePassword(user: User, oldPassword: string, newPassword: string) {
|
||||||
if (argon.verify(user.password, oldPassword))
|
if (!(await argon.verify(user.password, oldPassword)))
|
||||||
throw new ForbiddenException("Invalid password");
|
throw new ForbiddenException("Invalid password");
|
||||||
|
|
||||||
const hash = await argon.hash(newPassword);
|
const hash = await argon.hash(newPassword);
|
||||||
@ -115,4 +182,161 @@ export class AuthService {
|
|||||||
|
|
||||||
return refreshToken;
|
return refreshToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createLoginToken(userId: string) {
|
||||||
|
const loginToken = (
|
||||||
|
await this.prisma.loginToken.create({
|
||||||
|
data: { userId, expiresAt: moment().add(5, "minutes").toDate() },
|
||||||
|
})
|
||||||
|
).token;
|
||||||
|
|
||||||
|
return loginToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptTotpSecret(totpSecret: string, password: string) {
|
||||||
|
let iv = this.config.get("TOTP_SECRET");
|
||||||
|
iv = Buffer.from(iv, "base64");
|
||||||
|
const key = crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(String(password))
|
||||||
|
.digest("base64")
|
||||||
|
.substr(0, 32);
|
||||||
|
|
||||||
|
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
|
||||||
|
|
||||||
|
let encrypted = cipher.update(totpSecret);
|
||||||
|
|
||||||
|
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||||
|
|
||||||
|
return encrypted.toString("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptTotpSecret(encryptedTotpSecret: string, password: string) {
|
||||||
|
let iv = this.config.get("TOTP_SECRET");
|
||||||
|
iv = Buffer.from(iv, "base64");
|
||||||
|
const key = crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(String(password))
|
||||||
|
.digest("base64")
|
||||||
|
.substr(0, 32);
|
||||||
|
|
||||||
|
const encryptedText = Buffer.from(encryptedTotpSecret, "base64");
|
||||||
|
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
|
||||||
|
let decrypted = decipher.update(encryptedText);
|
||||||
|
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||||
|
|
||||||
|
return decrypted.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
async enableTotp(user: User, password: string) {
|
||||||
|
if (!(await argon.verify(user.password, password)))
|
||||||
|
throw new ForbiddenException("Invalid password");
|
||||||
|
|
||||||
|
// Check if we have a secret already
|
||||||
|
const { totpVerified } = await this.prisma.user.findUnique({
|
||||||
|
where: { id: user.id },
|
||||||
|
select: { totpVerified: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (totpVerified) {
|
||||||
|
throw new BadRequestException("TOTP is already enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe make the issuer configurable with env vars?
|
||||||
|
const secret = authenticator.generateSecret();
|
||||||
|
const encryptedSecret = this.encryptTotpSecret(secret, password);
|
||||||
|
|
||||||
|
const otpURL = totp.keyuri(
|
||||||
|
user.username || user.email,
|
||||||
|
"pingvin-share",
|
||||||
|
secret
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.prisma.user.update({
|
||||||
|
where: { id: user.id },
|
||||||
|
data: {
|
||||||
|
totpEnabled: true,
|
||||||
|
totpSecret: encryptedSecret,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Maybe we should generate the QR code on the client rather than the server?
|
||||||
|
const qrCode = new qrcode({
|
||||||
|
content: otpURL,
|
||||||
|
container: "svg-viewbox",
|
||||||
|
join: true,
|
||||||
|
}).svg();
|
||||||
|
|
||||||
|
return {
|
||||||
|
totpAuthUrl: otpURL,
|
||||||
|
totpSecret: secret,
|
||||||
|
qrCode:
|
||||||
|
"data:image/svg+xml;base64," + Buffer.from(qrCode).toString("base64"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe require a token to verify that the user who started enabling totp is the one who is verifying it?
|
||||||
|
async verifyTotp(user: User, password: string, code: string) {
|
||||||
|
if (!(await argon.verify(user.password, password)))
|
||||||
|
throw new ForbiddenException("Invalid password");
|
||||||
|
|
||||||
|
const { totpSecret } = await this.prisma.user.findUnique({
|
||||||
|
where: { id: user.id },
|
||||||
|
select: { totpSecret: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!totpSecret) {
|
||||||
|
throw new BadRequestException("TOTP is not in progress");
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptedSecret = this.decryptTotpSecret(totpSecret, password);
|
||||||
|
|
||||||
|
const expected = authenticator.generate(decryptedSecret);
|
||||||
|
|
||||||
|
if (code !== expected) {
|
||||||
|
throw new BadRequestException("Invalid code");
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.user.update({
|
||||||
|
where: { id: user.id },
|
||||||
|
data: {
|
||||||
|
totpVerified: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async disableTotp(user: User, password: string, code: string) {
|
||||||
|
if (!(await argon.verify(user.password, password)))
|
||||||
|
throw new ForbiddenException("Invalid password");
|
||||||
|
|
||||||
|
const { totpSecret } = await this.prisma.user.findUnique({
|
||||||
|
where: { id: user.id },
|
||||||
|
select: { totpSecret: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!totpSecret) {
|
||||||
|
throw new BadRequestException("TOTP is not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptedSecret = this.decryptTotpSecret(totpSecret, password);
|
||||||
|
|
||||||
|
const expected = authenticator.generate(decryptedSecret);
|
||||||
|
|
||||||
|
if (code !== expected) {
|
||||||
|
throw new BadRequestException("Invalid code");
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.user.update({
|
||||||
|
where: { id: user.id },
|
||||||
|
data: {
|
||||||
|
totpVerified: false,
|
||||||
|
totpEnabled: false,
|
||||||
|
totpSecret: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
21
backend/src/auth/dto/authSignInTotp.dto.ts
Normal file
21
backend/src/auth/dto/authSignInTotp.dto.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { PickType } from "@nestjs/mapped-types";
|
||||||
|
import { IsEmail, IsOptional, IsString } from "class-validator";
|
||||||
|
import { UserDTO } from "src/user/dto/user.dto";
|
||||||
|
|
||||||
|
export class AuthSignInTotpDTO extends PickType(UserDTO, [
|
||||||
|
"password",
|
||||||
|
] as const) {
|
||||||
|
@IsEmail()
|
||||||
|
@IsOptional()
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
totp: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
loginToken: string;
|
||||||
|
}
|
5
backend/src/auth/dto/enableTotp.dto.ts
Normal file
5
backend/src/auth/dto/enableTotp.dto.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { PickType } from "@nestjs/mapped-types";
|
||||||
|
import { IsEmail, IsOptional, IsString } from "class-validator";
|
||||||
|
import { UserDTO } from "src/user/dto/user.dto";
|
||||||
|
|
||||||
|
export class EnableTotpDTO extends PickType(UserDTO, ["password"] as const) {}
|
8
backend/src/auth/dto/verifyTotp.dto.ts
Normal file
8
backend/src/auth/dto/verifyTotp.dto.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { PickType } from "@nestjs/mapped-types";
|
||||||
|
import { IsEmail, IsOptional, IsString } from "class-validator";
|
||||||
|
import { UserDTO } from "src/user/dto/user.dto";
|
||||||
|
|
||||||
|
export class VerifyTotpDTO extends PickType(UserDTO, ["password"] as const) {
|
||||||
|
@IsString()
|
||||||
|
code: string;
|
||||||
|
}
|
@ -22,6 +22,9 @@ export class UserDTO {
|
|||||||
@Expose()
|
@Expose()
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
totpVerified: boolean;
|
||||||
|
|
||||||
from(partial: Partial<UserDTO>) {
|
from(partial: Partial<UserDTO>) {
|
||||||
return plainToClass(UserDTO, partial, { excludeExtraneousValues: true });
|
return plainToClass(UserDTO, partial, { excludeExtraneousValues: true });
|
||||||
}
|
}
|
||||||
|
131
frontend/src/components/account/showEnableTotpModal.tsx
Normal file
131
frontend/src/components/account/showEnableTotpModal.tsx
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Col,
|
||||||
|
Grid,
|
||||||
|
Image,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useForm, yupResolver } from "@mantine/form";
|
||||||
|
import { useModals } from "@mantine/modals";
|
||||||
|
import { ModalsContextProps } from "@mantine/modals/lib/context";
|
||||||
|
import * as yup from "yup";
|
||||||
|
import useUser from "../../hooks/user.hook";
|
||||||
|
import authService from "../../services/auth.service";
|
||||||
|
import toast from "../../utils/toast.util";
|
||||||
|
|
||||||
|
const showEnableTotpModal = (
|
||||||
|
modals: ModalsContextProps,
|
||||||
|
refreshUser: () => {},
|
||||||
|
options: {
|
||||||
|
qrCode: string;
|
||||||
|
secret: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
return modals.openModal({
|
||||||
|
title: <Title order={4}>Enable TOTP</Title>,
|
||||||
|
children: (
|
||||||
|
<CreateEnableTotpModal options={options} refreshUser={refreshUser} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreateEnableTotpModal = ({
|
||||||
|
options,
|
||||||
|
refreshUser,
|
||||||
|
}: {
|
||||||
|
options: {
|
||||||
|
qrCode: string;
|
||||||
|
secret: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
refreshUser: () => {};
|
||||||
|
}) => {
|
||||||
|
const modals = useModals();
|
||||||
|
const user = useUser();
|
||||||
|
|
||||||
|
console.log(user.user);
|
||||||
|
|
||||||
|
const validationSchema = yup.object().shape({
|
||||||
|
code: yup
|
||||||
|
.string()
|
||||||
|
.min(6)
|
||||||
|
.max(6)
|
||||||
|
.required()
|
||||||
|
.matches(/^[0-9]+$/, { message: "Code must be a number" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
initialValues: {
|
||||||
|
code: "",
|
||||||
|
},
|
||||||
|
validate: yupResolver(validationSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Center>
|
||||||
|
<Stack>
|
||||||
|
<Text>Step 1: Add your authenticator</Text>
|
||||||
|
<Image src={options.qrCode} alt="QR Code" />
|
||||||
|
|
||||||
|
<Center>
|
||||||
|
<span>OR</span>
|
||||||
|
</Center>
|
||||||
|
|
||||||
|
<Tooltip label="Click to copy">
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(options.secret);
|
||||||
|
toast.success("Copied to clipboard");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{options.secret}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Center>
|
||||||
|
<Text fz="xs">Enter manually</Text>
|
||||||
|
</Center>
|
||||||
|
|
||||||
|
<Text>Step 2: Validate your code</Text>
|
||||||
|
|
||||||
|
<form
|
||||||
|
onSubmit={form.onSubmit((values) => {
|
||||||
|
authService
|
||||||
|
.verifyTOTP(values.code, options.password)
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Successfully enabled TOTP");
|
||||||
|
modals.closeAll();
|
||||||
|
refreshUser();
|
||||||
|
})
|
||||||
|
.catch(toast.axiosError);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Grid align="flex-end">
|
||||||
|
<Col xs={9}>
|
||||||
|
<TextInput
|
||||||
|
variant="filled"
|
||||||
|
label="Code"
|
||||||
|
placeholder="******"
|
||||||
|
{...form.getInputProps("code")}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col xs={3}>
|
||||||
|
<Button variant="outline" type="submit">
|
||||||
|
Verify
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
</Stack>
|
||||||
|
</Center>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default showEnableTotpModal;
|
@ -9,7 +9,11 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm, yupResolver } from "@mantine/form";
|
import { useForm, yupResolver } from "@mantine/form";
|
||||||
|
import { showNotification } from "@mantine/notifications";
|
||||||
|
import { setCookie } from "cookies-next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import React from "react";
|
||||||
|
import { TbInfoCircle } from "react-icons/tb";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import useConfig from "../../hooks/config.hook";
|
import useConfig from "../../hooks/config.hook";
|
||||||
import authService from "../../services/auth.service";
|
import authService from "../../services/auth.service";
|
||||||
@ -17,16 +21,24 @@ import toast from "../../utils/toast.util";
|
|||||||
|
|
||||||
const SignInForm = () => {
|
const SignInForm = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
|
const [showTotp, setShowTotp] = React.useState(false);
|
||||||
|
const [loginToken, setLoginToken] = React.useState("");
|
||||||
|
|
||||||
const validationSchema = yup.object().shape({
|
const validationSchema = yup.object().shape({
|
||||||
emailOrUsername: yup.string().required(),
|
emailOrUsername: yup.string().required(),
|
||||||
password: yup.string().min(8).required(),
|
password: yup.string().min(8).required(),
|
||||||
|
totp: yup.string().when("totpRequired", {
|
||||||
|
is: true,
|
||||||
|
then: yup.string().min(6).max(6).required(),
|
||||||
|
otherwise: yup.string(),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
emailOrUsername: "",
|
emailOrUsername: "",
|
||||||
password: "",
|
password: "",
|
||||||
|
totp: "",
|
||||||
},
|
},
|
||||||
validate: yupResolver(validationSchema),
|
validate: yupResolver(validationSchema),
|
||||||
});
|
});
|
||||||
@ -34,10 +46,47 @@ const SignInForm = () => {
|
|||||||
const signIn = (email: string, password: string) => {
|
const signIn = (email: string, password: string) => {
|
||||||
authService
|
authService
|
||||||
.signIn(email, password)
|
.signIn(email, password)
|
||||||
.then(() => window.location.replace("/"))
|
.then((response) => {
|
||||||
|
if (response.data["loginToken"]) {
|
||||||
|
// Prompt the user to enter their totp code
|
||||||
|
setShowTotp(true);
|
||||||
|
showNotification({
|
||||||
|
icon: <TbInfoCircle />,
|
||||||
|
color: "blue",
|
||||||
|
radius: "md",
|
||||||
|
title: "Two-factor authentication required",
|
||||||
|
message: "Please enter your two-factor authentication code",
|
||||||
|
});
|
||||||
|
setLoginToken(response.data["loginToken"]);
|
||||||
|
} else {
|
||||||
|
setCookie("access_token", response.data.accessToken);
|
||||||
|
setCookie("refresh_token", response.data.refreshToken);
|
||||||
|
window.location.replace("/");
|
||||||
|
}
|
||||||
|
})
|
||||||
.catch(toast.axiosError);
|
.catch(toast.axiosError);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const signInTotp = (email: string, password: string, totp: string) => {
|
||||||
|
authService
|
||||||
|
.signInTotp(email, password, totp, loginToken)
|
||||||
|
.then((response) => {
|
||||||
|
setCookie("access_token", response.data.accessToken);
|
||||||
|
setCookie("refresh_token", response.data.refreshToken);
|
||||||
|
window.location.replace("/");
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error?.response?.data?.message == "Login token expired") {
|
||||||
|
toast.error("Login token expired");
|
||||||
|
// Refresh the page to start over
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.axiosError(error);
|
||||||
|
form.setValues({ totp: "" });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container size={420} my={40}>
|
<Container size={420} my={40}>
|
||||||
<Title
|
<Title
|
||||||
@ -59,9 +108,11 @@ const SignInForm = () => {
|
|||||||
)}
|
)}
|
||||||
<Paper withBorder shadow="md" p={30} mt={30} radius="md">
|
<Paper withBorder shadow="md" p={30} mt={30} radius="md">
|
||||||
<form
|
<form
|
||||||
onSubmit={form.onSubmit((values) =>
|
onSubmit={form.onSubmit((values) => {
|
||||||
signIn(values.emailOrUsername, values.password)
|
if (showTotp)
|
||||||
)}
|
signInTotp(values.emailOrUsername, values.password, values.totp);
|
||||||
|
else signIn(values.emailOrUsername, values.password);
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Email or username"
|
label="Email or username"
|
||||||
@ -74,6 +125,15 @@ const SignInForm = () => {
|
|||||||
mt="md"
|
mt="md"
|
||||||
{...form.getInputProps("password")}
|
{...form.getInputProps("password")}
|
||||||
/>
|
/>
|
||||||
|
{showTotp && (
|
||||||
|
<TextInput
|
||||||
|
variant="filled"
|
||||||
|
label="Code"
|
||||||
|
placeholder="******"
|
||||||
|
mt="md"
|
||||||
|
{...form.getInputProps("totp")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Button fullWidth mt="xl" type="submit">
|
<Button fullWidth mt="xl" type="submit">
|
||||||
Sign in
|
Sign in
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm, yupResolver } from "@mantine/form";
|
import { useForm, yupResolver } from "@mantine/form";
|
||||||
|
import { setCookie } from "cookies-next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import useConfig from "../../hooks/config.hook";
|
import useConfig from "../../hooks/config.hook";
|
||||||
@ -33,16 +34,14 @@ const SignUpForm = () => {
|
|||||||
validate: yupResolver(validationSchema),
|
validate: yupResolver(validationSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
const signIn = (email: string, password: string) => {
|
|
||||||
authService
|
|
||||||
.signIn(email, password)
|
|
||||||
.then(() => window.location.replace("/"))
|
|
||||||
.catch(toast.axiosError);
|
|
||||||
};
|
|
||||||
const signUp = (email: string, username: string, password: string) => {
|
const signUp = (email: string, username: string, password: string) => {
|
||||||
authService
|
authService
|
||||||
.signUp(email, username, password)
|
.signUp(email, username, password)
|
||||||
.then(() => signIn(email, password))
|
.then((response) => {
|
||||||
|
setCookie("access_token", response.data.accessToken);
|
||||||
|
setCookie("refresh_token", response.data.refreshToken);
|
||||||
|
window.location.replace("/");
|
||||||
|
})
|
||||||
.catch(toast.axiosError);
|
.catch(toast.axiosError);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import useUser from "../../hooks/user.hook";
|
|||||||
import authService from "../../services/auth.service";
|
import authService from "../../services/auth.service";
|
||||||
|
|
||||||
const ActionAvatar = () => {
|
const ActionAvatar = () => {
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu position="bottom-start" withinPortal>
|
<Menu position="bottom-start" withinPortal>
|
||||||
|
@ -107,7 +107,7 @@ const useStyles = createStyles((theme) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const NavBar = () => {
|
const NavBar = () => {
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
|
|
||||||
const [opened, toggleOpened] = useDisclosure(false);
|
const [opened, toggleOpened] = useDisclosure(false);
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { createContext, useContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
import { CurrentUser } from "../types/user.type";
|
import { UserHook } from "../types/user.type";
|
||||||
|
|
||||||
export const UserContext = createContext<CurrentUser | null>(null);
|
export const UserContext = createContext<UserHook>({
|
||||||
|
user: null,
|
||||||
|
setUser: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
const useUser = () => {
|
const useUser = () => {
|
||||||
return useContext(UserContext);
|
return useContext(UserContext);
|
||||||
|
@ -73,7 +73,7 @@ function App({ Component, pageProps }: AppProps) {
|
|||||||
<LoadingOverlay visible overlayOpacity={1} />
|
<LoadingOverlay visible overlayOpacity={1} />
|
||||||
) : (
|
) : (
|
||||||
<ConfigContext.Provider value={configVariables}>
|
<ConfigContext.Provider value={configVariables}>
|
||||||
<UserContext.Provider value={user} >
|
<UserContext.Provider value={{ user, setUser }}>
|
||||||
<LoadingOverlay visible={isLoading} overlayOpacity={1} />
|
<LoadingOverlay visible={isLoading} overlayOpacity={1} />
|
||||||
<Header />
|
<Header />
|
||||||
<Container>
|
<Container>
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
Paper,
|
Paper,
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
Stack,
|
Stack,
|
||||||
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
@ -13,14 +14,16 @@ import {
|
|||||||
import { useForm, yupResolver } from "@mantine/form";
|
import { useForm, yupResolver } from "@mantine/form";
|
||||||
import { useModals } from "@mantine/modals";
|
import { useModals } from "@mantine/modals";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { Tb2Fa } from "react-icons/tb";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
import showEnableTotpModal from "../../components/account/showEnableTotpModal";
|
||||||
import useUser from "../../hooks/user.hook";
|
import useUser from "../../hooks/user.hook";
|
||||||
import authService from "../../services/auth.service";
|
import authService from "../../services/auth.service";
|
||||||
import userService from "../../services/user.service";
|
import userService from "../../services/user.service";
|
||||||
import toast from "../../utils/toast.util";
|
import toast from "../../utils/toast.util";
|
||||||
|
|
||||||
const Account = () => {
|
const Account = () => {
|
||||||
const user = useUser();
|
const { user, setUser } = useUser();
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -50,6 +53,36 @@ const Account = () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const enableTotpForm = useForm({
|
||||||
|
initialValues: {
|
||||||
|
password: "",
|
||||||
|
},
|
||||||
|
validate: yupResolver(
|
||||||
|
yup.object().shape({
|
||||||
|
password: yup.string().min(8),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const disableTotpForm = useForm({
|
||||||
|
initialValues: {
|
||||||
|
password: "",
|
||||||
|
code: "",
|
||||||
|
},
|
||||||
|
validate: yupResolver(
|
||||||
|
yup.object().shape({
|
||||||
|
password: yup.string().min(8),
|
||||||
|
code: yup
|
||||||
|
.string()
|
||||||
|
.min(6)
|
||||||
|
.max(6)
|
||||||
|
.matches(/^[0-9]+$/, { message: "Code must be a number" }),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const refreshUser = async () => setUser(await userService.getCurrentUser());
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
router.push("/");
|
router.push("/");
|
||||||
return;
|
return;
|
||||||
@ -117,7 +150,95 @@ const Account = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</form>
|
</form>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
<Paper withBorder p="xl" mt="lg">
|
||||||
|
<Title order={5} mb="xs">
|
||||||
|
Security
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Tabs defaultValue="totp">
|
||||||
|
<Tabs.List>
|
||||||
|
<Tabs.Tab value="totp" icon={<Tb2Fa size={14} />}>
|
||||||
|
TOTP
|
||||||
|
</Tabs.Tab>
|
||||||
|
</Tabs.List>
|
||||||
|
|
||||||
|
<Tabs.Panel value="totp" pt="xs">
|
||||||
|
{/* TODO: This is ugly, make it prettier */}
|
||||||
|
{/* If we have totp enabled, show different text */}
|
||||||
|
{user.totpVerified ? (
|
||||||
|
<>
|
||||||
|
<form
|
||||||
|
onSubmit={disableTotpForm.onSubmit((values) => {
|
||||||
|
authService
|
||||||
|
.disableTOTP(values.code, values.password)
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Successfully disabled TOTP");
|
||||||
|
values.password = "";
|
||||||
|
values.code = "";
|
||||||
|
refreshUser();
|
||||||
|
})
|
||||||
|
.catch(toast.axiosError);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
<PasswordInput
|
||||||
|
description="Enter your current password to disable TOTP"
|
||||||
|
label="Password"
|
||||||
|
{...disableTotpForm.getInputProps("password")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
variant="filled"
|
||||||
|
label="Code"
|
||||||
|
placeholder="******"
|
||||||
|
{...disableTotpForm.getInputProps("code")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Group position="right">
|
||||||
|
<Button color="red" type="submit">
|
||||||
|
Disable
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<form
|
||||||
|
onSubmit={enableTotpForm.onSubmit((values) => {
|
||||||
|
authService
|
||||||
|
.enableTOTP(values.password)
|
||||||
|
.then((result) => {
|
||||||
|
showEnableTotpModal(modals, refreshUser, {
|
||||||
|
qrCode: result.qrCode,
|
||||||
|
secret: result.totpSecret,
|
||||||
|
password: values.password,
|
||||||
|
});
|
||||||
|
values.password = "";
|
||||||
|
})
|
||||||
|
.catch(toast.axiosError);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
<PasswordInput
|
||||||
|
label="Password"
|
||||||
|
description="Enter your current password to start enabling TOTP"
|
||||||
|
{...enableTotpForm.getInputProps("password")}
|
||||||
|
/>
|
||||||
|
<Group position="right">
|
||||||
|
<Button type="submit">Start</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Tabs.Panel>
|
||||||
|
</Tabs>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
<Center mt={80}>
|
<Center mt={80}>
|
||||||
|
<Stack>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="red"
|
color="red"
|
||||||
@ -142,6 +263,7 @@ const Account = () => {
|
|||||||
>
|
>
|
||||||
Delete Account
|
Delete Account
|
||||||
</Button>
|
</Button>
|
||||||
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
@ -28,7 +28,7 @@ const MyShares = () => {
|
|||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
const [shares, setShares] = useState<MyShare[]>();
|
const [shares, setShares] = useState<MyShare[]>();
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import configService from "../../services/config.service";
|
|||||||
const Setup = () => {
|
const Setup = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import Meta from "../../components/Meta";
|
|||||||
import useUser from "../../hooks/user.hook";
|
import useUser from "../../hooks/user.hook";
|
||||||
|
|
||||||
const SignIn = () => {
|
const SignIn = () => {
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
if (user) {
|
if (user) {
|
||||||
router.replace("/");
|
router.replace("/");
|
||||||
|
@ -6,7 +6,7 @@ import useUser from "../../hooks/user.hook";
|
|||||||
|
|
||||||
const SignUp = () => {
|
const SignUp = () => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
if (user) {
|
if (user) {
|
||||||
router.replace("/");
|
router.replace("/");
|
||||||
|
@ -70,7 +70,7 @@ const useStyles = createStyles((theme) => ({
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -23,7 +23,7 @@ const Upload = () => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
|
|
||||||
const user = useUser();
|
const { user } = useUser();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const [files, setFiles] = useState<FileUpload[]>([]);
|
const [files, setFiles] = useState<FileUpload[]>([]);
|
||||||
const [isUploading, setisUploading] = useState(false);
|
const [isUploading, setisUploading] = useState(false);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getCookie, setCookies } from "cookies-next";
|
import { getCookie, setCookie } from "cookies-next";
|
||||||
import * as jose from "jose";
|
import * as jose from "jose";
|
||||||
import api from "./api.service";
|
import api from "./api.service";
|
||||||
|
|
||||||
@ -11,8 +11,25 @@ const signIn = async (emailOrUsername: string, password: string) => {
|
|||||||
...emailOrUsernameBody,
|
...emailOrUsernameBody,
|
||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
setCookies("access_token", response.data.accessToken);
|
return response;
|
||||||
setCookies("refresh_token", response.data.refreshToken);
|
};
|
||||||
|
|
||||||
|
const signInTotp = async (
|
||||||
|
emailOrUsername: string,
|
||||||
|
password: string,
|
||||||
|
totp: string,
|
||||||
|
loginToken: string
|
||||||
|
) => {
|
||||||
|
const emailOrUsernameBody = emailOrUsername.includes("@")
|
||||||
|
? { email: emailOrUsername }
|
||||||
|
: { username: emailOrUsername };
|
||||||
|
|
||||||
|
const response = await api.post("auth/signIn/totp", {
|
||||||
|
...emailOrUsernameBody,
|
||||||
|
password,
|
||||||
|
totp,
|
||||||
|
loginToken,
|
||||||
|
});
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,8 +38,8 @@ const signUp = async (email: string, username: string, password: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const signOut = () => {
|
const signOut = () => {
|
||||||
setCookies("access_token", null);
|
setCookie("access_token", null);
|
||||||
setCookies("refresh_token", null);
|
setCookie("refresh_token", null);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,7 +54,7 @@ const refreshAccessToken = async () => {
|
|||||||
const refreshToken = getCookie("refresh_token");
|
const refreshToken = getCookie("refresh_token");
|
||||||
|
|
||||||
const response = await api.post("auth/token", { refreshToken });
|
const response = await api.post("auth/token", { refreshToken });
|
||||||
setCookies("access_token", response.data.accessToken);
|
setCookie("access_token", response.data.accessToken);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
console.info("Refresh token invalid or expired");
|
console.info("Refresh token invalid or expired");
|
||||||
@ -48,10 +65,38 @@ const updatePassword = async (oldPassword: string, password: string) => {
|
|||||||
await api.patch("/auth/password", { oldPassword, password });
|
await api.patch("/auth/password", { oldPassword, password });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const enableTOTP = async (password: string) => {
|
||||||
|
const { data } = await api.post("/auth/totp/enable", { password });
|
||||||
|
|
||||||
|
return {
|
||||||
|
totpAuthUrl: data.totpAuthUrl,
|
||||||
|
totpSecret: data.totpSecret,
|
||||||
|
qrCode: data.qrCode,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyTOTP = async (totpCode: string, password: string) => {
|
||||||
|
await api.post("/auth/totp/verify", {
|
||||||
|
code: totpCode,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const disableTOTP = async (totpCode: string, password: string) => {
|
||||||
|
await api.post("/auth/totp/disable", {
|
||||||
|
code: totpCode,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
signIn,
|
signIn,
|
||||||
|
signInTotp,
|
||||||
signUp,
|
signUp,
|
||||||
signOut,
|
signOut,
|
||||||
refreshAccessToken,
|
refreshAccessToken,
|
||||||
updatePassword,
|
updatePassword,
|
||||||
|
enableTOTP,
|
||||||
|
verifyTOTP,
|
||||||
|
disableTOTP,
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@ type User = {
|
|||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
|
totpVerified: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateUser = {
|
export type CreateUser = {
|
||||||
@ -26,4 +27,9 @@ export type UpdateCurrentUser = {
|
|||||||
|
|
||||||
export type CurrentUser = User & {};
|
export type CurrentUser = User & {};
|
||||||
|
|
||||||
|
export type UserHook = {
|
||||||
|
user: CurrentUser | null;
|
||||||
|
setUser: (user: CurrentUser | null) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export default User;
|
export default User;
|
||||||
|
Loading…
Reference in New Issue
Block a user