diff --git a/.gitignore b/.gitignore index 7a08aa7..2eca47b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ node_modules *DS_Store .env -cert diplomas -templates +files logs *.log \ No newline at end of file diff --git a/index.js b/index.js index f03b33c..cb81a64 100644 --- a/index.js +++ b/index.js @@ -2,12 +2,10 @@ require("dotenv").config(); require("module-alias/register"); const { log } = require("@logger"); -const mongo = require("@mongo/mongo"); const server = require("@express/express"); async function start() { try { - await mongo.start(); await server.start(); log.info("All systems running. Let's rock!"); } catch (e) { diff --git a/package-lock.json b/package-lock.json index 6b14a4c..bbfdc1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "eduhund_backend", - "version": "0.4.0", + "version": "0.7.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "eduhund_backend", - "version": "0.4.0", + "version": "0.7.3", "license": "ISC", "dependencies": { "@log4js-node/slack": "^1.0.0", @@ -19,8 +19,9 @@ "module-alias": "^2.2.2", "mongodb": "^4.5.0", "node-fetch": "^2.6.9", + "nodemailer": "^6.9.7", "pg": "^8.7.3", - "sharp": "^0.31.3", + "sharp": "^0.33.0", "supertest": "^6.3.3", "svg-content": "^0.1.1" }, @@ -1321,6 +1322,446 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@emnapi/runtime": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.44.0.tgz", + "integrity": "sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.0.tgz", + "integrity": "sha512-070tEheekI1LJWTGPC9WlQEa5UoKTXzzlORBHMX4TbfUxMiL336YHR8vBEUNsjse0RJCX8dZ4ZXwT595aEF1ug==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.0.tgz", + "integrity": "sha512-pu/nvn152F3qbPeUkr+4e9zVvEhD3jhwzF473veQfMPkOYo9aoWXSfdZH/E6F+nYC3qvFjbxbvdDbUtEbghLqw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.0.tgz", + "integrity": "sha512-VzYd6OwnUR81sInf3alj1wiokY50DjsHz5bvfnsFpxs5tqQxESoHtJO6xyksDs3RIkyhMWq2FufXo6GNSU9BMw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.0.tgz", + "integrity": "sha512-dD9OznTlHD6aovRswaPNEy8dKtSAmNo4++tO7uuR4o5VxbVAOoEQ1uSmN4iFAdQneTHws1lkTZeiXPrcCkh6IA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.0.tgz", + "integrity": "sha512-VwgD2eEikDJUk09Mn9Dzi1OW2OJFRQK+XlBTkUNmAWPrtj8Ly0yq05DFgu1VCMx2/DqCGQVi5A1dM9hTmxf3uw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.0.tgz", + "integrity": "sha512-xTYThiqEZEZc0PRU90yVtM3KE7lw1bKdnDQ9kCTHWbqWyHOe4NpPOtMGy27YnN51q0J5dqRrvicfPbALIOeAZA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.0.tgz", + "integrity": "sha512-o9E46WWBC6JsBlwU4QyU9578G77HBDT1NInd+aERfxeOPbk0qBZHgoDsQmA2v9TbqJRWzoBPx1aLOhprBMgPjw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.0.tgz", + "integrity": "sha512-naldaJy4hSVhWBgEjfdBY85CAa4UO+W1nx6a1sWStHZ7EUfNiuBTTN2KUYT5dH1+p/xij1t2QSXfCiFJoC5S/Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.0.tgz", + "integrity": "sha512-OdorplCyvmSAPsoJLldtLh3nLxRrkAAAOHsGWGDYfN0kh730gifK+UZb3dWORRa6EusNqCTjfXV4GxvgJ/nPDQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.0.tgz", + "integrity": "sha512-FW8iK6rJrg+X2jKD0Ajhjv6y74lToIBEvkZhl42nZt563FfxkCYacrXZtd+q/sRQDypQLzY5WdLkVTbJoPyqNg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.0.tgz", + "integrity": "sha512-4horD3wMFd5a0ddbDY8/dXU9CaOgHjEHALAddXgafoR5oWq5s8X61PDgsSeh4Qupsdo6ycfPPSSNBrfVQnwwrg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.0.tgz", + "integrity": "sha512-dcomVSrtgF70SyOr8RCOCQ8XGVThXwe71A1d8MGA+mXEVRJ/J6/TrCbBEJh9ddcEIIsrnrkolaEvYSHqVhswQw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.0.tgz", + "integrity": "sha512-TiVJbx38J2rNVfA309ffSOB+3/7wOsZYQEOlKqOUdWD/nqkjNGrX+YQGz7nzcf5oy2lC+d37+w183iNXRZNngQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.0.tgz", + "integrity": "sha512-PaZM4Zi7/Ek71WgTdvR+KzTZpBqrQOFcPe7/8ZoPRlTYYRe43k6TWsf4GVH6XKRLMYeSp8J89RfAhBrSP4itNA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.0.tgz", + "integrity": "sha512-1QLbbN0zt+32eVrg7bb1lwtvEaZwlhEsY1OrijroMkwAqlHqFj6R33Y47s2XUv7P6Ie1PwCxK/uFnNqMnkd5kg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.0.tgz", + "integrity": "sha512-CecqgB/CnkvCWFhmfN9ZhPGMLXaEBXl4o7WtA6U3Ztrlh/s7FUKX4vNxpMSYLIrWuuzjiaYdfU3+Tdqh1xaHfw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.0.tgz", + "integrity": "sha512-Hn4js32gUX9qkISlemZBUPuMs0k/xNJebUNl/L6djnU07B/HAA2KaxRVb3HvbU5fL242hLOcp0+tR+M8dvJUFw==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^0.44.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.0.tgz", + "integrity": "sha512-5HfcsCZi3l5nPRF2q3bllMVMDXBqEWI3Q8KQONfzl0TferFE5lnsIG0A1YrntMAGqvkzdW6y1Ci1A2uTvxhfzg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.0.tgz", + "integrity": "sha512-i3DtP/2ce1yKFj4OzOnOYltOEL/+dp4dc4dJXJBv6god1AFTcmkaA99H/7SwOmkCOBQkbVvA3lCGm3/5nDtf9Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2946,16 +3387,6 @@ } ] }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/body-parser": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", @@ -3171,11 +3602,6 @@ "node": ">=10" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -3448,33 +3874,11 @@ "integrity": "sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==", "dev": true }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3515,9 +3919,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "engines": { "node": ">=8" } @@ -3612,14 +4016,6 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3739,14 +4135,6 @@ "node": ">= 0.8.0" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, "node_modules/expect": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", @@ -3952,11 +4340,6 @@ "node": ">= 0.6" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -4048,11 +4431,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4306,11 +4684,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, "node_modules/ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", @@ -5440,17 +5813,6 @@ "node": ">=6" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5462,19 +5824,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "node_modules/module-alias": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", @@ -5511,54 +5860,19 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-abi": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", - "integrity": "sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -5607,6 +5921,14 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" }, + "node_modules/nodemailer": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", + "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5945,31 +6267,6 @@ "node": ">=0.10.0" } }, - "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -6035,15 +6332,6 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -6109,47 +6397,12 @@ "node": ">= 0.8" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "devOptional": true }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6320,25 +6573,42 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sharp": { - "version": "0.31.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.3.tgz", - "integrity": "sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.0.tgz", + "integrity": "sha512-99DZKudjm/Rmz+M0/26t4DKpXyywAOJaayGS9boEn7FvgtG0RYBi46uPE2c+obcJRtA3AZa0QwJot63gJQ1F0Q==", "hasInstallScript": true, "dependencies": { "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.8", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" + "detect-libc": "^2.0.2", + "semver": "^7.5.4" }, "engines": { - "node": ">=14.15.0" + "libvips": ">=8.15.0", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.0", + "@img/sharp-darwin-x64": "0.33.0", + "@img/sharp-libvips-darwin-arm64": "1.0.0", + "@img/sharp-libvips-darwin-x64": "1.0.0", + "@img/sharp-libvips-linux-arm": "1.0.0", + "@img/sharp-libvips-linux-arm64": "1.0.0", + "@img/sharp-libvips-linux-s390x": "1.0.0", + "@img/sharp-libvips-linux-x64": "1.0.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.0", + "@img/sharp-libvips-linuxmusl-x64": "1.0.0", + "@img/sharp-linux-arm": "0.33.0", + "@img/sharp-linux-arm64": "0.33.0", + "@img/sharp-linux-s390x": "0.33.0", + "@img/sharp-linux-x64": "0.33.0", + "@img/sharp-linuxmusl-arm64": "0.33.0", + "@img/sharp-linuxmusl-x64": "0.33.0", + "@img/sharp-wasm32": "0.33.0", + "@img/sharp-win32-ia32": "0.33.0", + "@img/sharp-win32-x64": "0.33.0" } }, "node_modules/sharp/node_modules/semver": { @@ -6392,49 +6662,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -6585,14 +6812,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -8029,32 +8248,6 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -8159,17 +8352,6 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "optional": true }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -8274,11 +8456,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -9597,6 +9774,156 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "@emnapi/runtime": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.44.0.tgz", + "integrity": "sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@img/sharp-darwin-arm64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.0.tgz", + "integrity": "sha512-070tEheekI1LJWTGPC9WlQEa5UoKTXzzlORBHMX4TbfUxMiL336YHR8vBEUNsjse0RJCX8dZ4ZXwT595aEF1ug==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.0.0" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.0.tgz", + "integrity": "sha512-pu/nvn152F3qbPeUkr+4e9zVvEhD3jhwzF473veQfMPkOYo9aoWXSfdZH/E6F+nYC3qvFjbxbvdDbUtEbghLqw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.0.0" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.0.tgz", + "integrity": "sha512-VzYd6OwnUR81sInf3alj1wiokY50DjsHz5bvfnsFpxs5tqQxESoHtJO6xyksDs3RIkyhMWq2FufXo6GNSU9BMw==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.0.tgz", + "integrity": "sha512-dD9OznTlHD6aovRswaPNEy8dKtSAmNo4++tO7uuR4o5VxbVAOoEQ1uSmN4iFAdQneTHws1lkTZeiXPrcCkh6IA==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.0.tgz", + "integrity": "sha512-VwgD2eEikDJUk09Mn9Dzi1OW2OJFRQK+XlBTkUNmAWPrtj8Ly0yq05DFgu1VCMx2/DqCGQVi5A1dM9hTmxf3uw==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.0.tgz", + "integrity": "sha512-xTYThiqEZEZc0PRU90yVtM3KE7lw1bKdnDQ9kCTHWbqWyHOe4NpPOtMGy27YnN51q0J5dqRrvicfPbALIOeAZA==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.0.tgz", + "integrity": "sha512-o9E46WWBC6JsBlwU4QyU9578G77HBDT1NInd+aERfxeOPbk0qBZHgoDsQmA2v9TbqJRWzoBPx1aLOhprBMgPjw==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.0.tgz", + "integrity": "sha512-naldaJy4hSVhWBgEjfdBY85CAa4UO+W1nx6a1sWStHZ7EUfNiuBTTN2KUYT5dH1+p/xij1t2QSXfCiFJoC5S/Q==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.0.tgz", + "integrity": "sha512-OdorplCyvmSAPsoJLldtLh3nLxRrkAAAOHsGWGDYfN0kh730gifK+UZb3dWORRa6EusNqCTjfXV4GxvgJ/nPDQ==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.0.tgz", + "integrity": "sha512-FW8iK6rJrg+X2jKD0Ajhjv6y74lToIBEvkZhl42nZt563FfxkCYacrXZtd+q/sRQDypQLzY5WdLkVTbJoPyqNg==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.0.tgz", + "integrity": "sha512-4horD3wMFd5a0ddbDY8/dXU9CaOgHjEHALAddXgafoR5oWq5s8X61PDgsSeh4Qupsdo6ycfPPSSNBrfVQnwwrg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.0.0" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.0.tgz", + "integrity": "sha512-dcomVSrtgF70SyOr8RCOCQ8XGVThXwe71A1d8MGA+mXEVRJ/J6/TrCbBEJh9ddcEIIsrnrkolaEvYSHqVhswQw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.0.0" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.0.tgz", + "integrity": "sha512-TiVJbx38J2rNVfA309ffSOB+3/7wOsZYQEOlKqOUdWD/nqkjNGrX+YQGz7nzcf5oy2lC+d37+w183iNXRZNngQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.0.0" + } + }, + "@img/sharp-linux-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.0.tgz", + "integrity": "sha512-PaZM4Zi7/Ek71WgTdvR+KzTZpBqrQOFcPe7/8ZoPRlTYYRe43k6TWsf4GVH6XKRLMYeSp8J89RfAhBrSP4itNA==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.0.0" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.0.tgz", + "integrity": "sha512-1QLbbN0zt+32eVrg7bb1lwtvEaZwlhEsY1OrijroMkwAqlHqFj6R33Y47s2XUv7P6Ie1PwCxK/uFnNqMnkd5kg==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.0" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.0.tgz", + "integrity": "sha512-CecqgB/CnkvCWFhmfN9ZhPGMLXaEBXl4o7WtA6U3Ztrlh/s7FUKX4vNxpMSYLIrWuuzjiaYdfU3+Tdqh1xaHfw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.0" + } + }, + "@img/sharp-wasm32": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.0.tgz", + "integrity": "sha512-Hn4js32gUX9qkISlemZBUPuMs0k/xNJebUNl/L6djnU07B/HAA2KaxRVb3HvbU5fL242hLOcp0+tR+M8dvJUFw==", + "optional": true, + "requires": { + "@emnapi/runtime": "^0.44.0" + } + }, + "@img/sharp-win32-ia32": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.0.tgz", + "integrity": "sha512-5HfcsCZi3l5nPRF2q3bllMVMDXBqEWI3Q8KQONfzl0TferFE5lnsIG0A1YrntMAGqvkzdW6y1Ci1A2uTvxhfzg==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.0.tgz", + "integrity": "sha512-i3DtP/2ce1yKFj4OzOnOYltOEL/+dp4dc4dJXJBv6god1AFTcmkaA99H/7SwOmkCOBQkbVvA3lCGm3/5nDtf9Q==", + "optional": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -10904,16 +11231,6 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "body-parser": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", @@ -11051,11 +11368,6 @@ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -11277,24 +11589,11 @@ "integrity": "sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==", "dev": true }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - } - }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -11322,9 +11621,9 @@ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" }, "detect-newline": { "version": "3.1.0", @@ -11394,14 +11693,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -11481,11 +11772,6 @@ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" - }, "expect": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", @@ -11647,11 +11933,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -11714,11 +11995,6 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -11892,11 +12168,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, "ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", @@ -12759,11 +13030,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -12772,16 +13038,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "module-alias": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", @@ -12813,11 +13069,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -12828,29 +13079,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, - "node-abi": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", - "integrity": "sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog==", - "requires": { - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, "node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -12890,6 +13118,11 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" }, + "nodemailer": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", + "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -13131,25 +13364,6 @@ "xtend": "^4.0.0" } }, - "prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "requires": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - } - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -13199,15 +13413,6 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -13248,40 +13453,12 @@ "unpipe": "1.0.0" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" - } - } - }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "devOptional": true }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -13404,18 +13581,32 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sharp": { - "version": "0.31.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.3.tgz", - "integrity": "sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==", - "requires": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.0.tgz", + "integrity": "sha512-99DZKudjm/Rmz+M0/26t4DKpXyywAOJaayGS9boEn7FvgtG0RYBi46uPE2c+obcJRtA3AZa0QwJot63gJQ1F0Q==", + "requires": { + "@img/sharp-darwin-arm64": "0.33.0", + "@img/sharp-darwin-x64": "0.33.0", + "@img/sharp-libvips-darwin-arm64": "1.0.0", + "@img/sharp-libvips-darwin-x64": "1.0.0", + "@img/sharp-libvips-linux-arm": "1.0.0", + "@img/sharp-libvips-linux-arm64": "1.0.0", + "@img/sharp-libvips-linux-s390x": "1.0.0", + "@img/sharp-libvips-linux-x64": "1.0.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.0", + "@img/sharp-libvips-linuxmusl-x64": "1.0.0", + "@img/sharp-linux-arm": "0.33.0", + "@img/sharp-linux-arm64": "0.33.0", + "@img/sharp-linux-s390x": "0.33.0", + "@img/sharp-linux-x64": "0.33.0", + "@img/sharp-linuxmusl-arm64": "0.33.0", + "@img/sharp-linuxmusl-x64": "0.33.0", + "@img/sharp-wasm32": "0.33.0", + "@img/sharp-win32-ia32": "0.33.0", + "@img/sharp-win32-x64": "0.33.0", "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.8", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" + "detect-libc": "^2.0.2", + "semver": "^7.5.4" }, "dependencies": { "semver": { @@ -13456,21 +13647,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -13590,14 +13766,6 @@ } } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -14747,29 +14915,6 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -14850,14 +14995,6 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "optional": true }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -14925,11 +15062,6 @@ "requires-port": "^1.0.0" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/package.json b/package.json index a2456da..c0bddcf 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,9 @@ "module-alias": "^2.2.2", "mongodb": "^4.5.0", "node-fetch": "^2.6.9", + "nodemailer": "^6.9.7", "pg": "^8.7.3", - "sharp": "^0.31.3", + "sharp": "^0.33.0", "supertest": "^6.3.3", "svg-content": "^0.1.1" }, diff --git a/src/API/student/addComment/addComment.js b/src/API/student/addComment/addComment.js deleted file mode 100644 index c95295d..0000000 --- a/src/API/student/addComment/addComment.js +++ /dev/null @@ -1,37 +0,0 @@ -const { checkModuleAccess, pushComment, getUserInfo } = require("@processes"); - -/*** - * addComment StudentAPI method. - * https://api.eduhund.com/docs/student#addComment - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Comments list on success; undefined on fail - */ -async function addComment(req, res, next) { - try { - const { data } = req; - - await getUserInfo(data, next); - - if (!checkModuleAccess(data)) { - next({ code: 10202 }); - return; - } - - const content = await pushComment(data, next); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20209, trace: e }; - next(err); - return; - } -} - -module.exports = addComment; diff --git a/src/API/student/auth/auth.js b/src/API/student/auth/auth.js deleted file mode 100644 index bc035d3..0000000 --- a/src/API/student/auth/auth.js +++ /dev/null @@ -1,43 +0,0 @@ -const { - getUserInfo, - checkCredentials, - authUser, - prepareUserData, -} = require("@processes"); - -/*** - * auth StudentAPI method. - * https://api.eduhund.com/docs/student#auth - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} User data on success; undefined on fail - */ -async function auth(req, res, next) { - try { - const { data } = req; - - const userExists = await getUserInfo(data, next); - if (!userExists) return; - - const credentialsValid = checkCredentials(data, next); - if (!credentialsValid) return; - - await authUser(data); - - const content = prepareUserData(data); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20201, trace: e }; - next(err); - return; - } -} - -module.exports = auth; diff --git a/src/API/student/checkPayment/checkPayment.js b/src/API/student/checkPayment/checkPayment.js deleted file mode 100644 index bd361e3..0000000 --- a/src/API/student/checkPayment/checkPayment.js +++ /dev/null @@ -1,36 +0,0 @@ -const { getUserInfo, checkTransaction, authUser } = require("@processes"); - -/*** - * checkPayment StudentAPI method. - * https://api.eduhund.com/docs/student#checkPayment - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} User data on success; undefined on fail - */ -async function checkPayment(req, res, next) { - try { - const { data } = req; - - const transactionIsValid = await checkTransaction(data, next); - if (!transactionIsValid) return; - - const userExists = await getUserInfo(data, next); - if (!userExists) return; - - const content = await authUser(data); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20203, trace: e }; - next(err); - return; - } -} - -module.exports = checkPayment; diff --git a/src/API/student/createPass/createPass.js b/src/API/student/createPass/createPass.js deleted file mode 100644 index 2000d22..0000000 --- a/src/API/student/createPass/createPass.js +++ /dev/null @@ -1,35 +0,0 @@ -const { checkOTK, updatePass, authUser } = require("@processes"); - -/*** - * createPass StudentAPI method. - * https://api.eduhund.com/docs/student#createPass - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} User data on success; undefined on fail - */ -async function createPass(req, res, next) { - try { - const { data } = req; - - const OTKIsValid = await checkOTK(data, next); - if (!OTKIsValid) return; - - await updatePass(data, next); - - const content = await authUser(data); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20202, trace: e }; - next(err); - return; - } -} - -module.exports = createPass; diff --git a/src/API/student/getCertificate/getCertificate.js b/src/API/student/getCertificate/getCertificate.js deleted file mode 100644 index d7aeb42..0000000 --- a/src/API/student/getCertificate/getCertificate.js +++ /dev/null @@ -1,46 +0,0 @@ -const { - checkFinalAccess, - getModuleInfo, - getStateInfo, - prepareCertificateData, - getUserInfo, -} = require("@processes"); - -/*** - * getDiploma StudentAPI method. - * https://api.eduhund.com/docs/student#getDiploma - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Certificate data on success; undefined on fail - */ -async function getCertificate(req, res, next) { - try { - const { data } = req; - - const dataPromises = [getUserInfo(data), getModuleInfo(data)]; - const [userData, moduleData] = await Promise.all(dataPromises); - if (!checkFinalAccess(data)) { - next({ code: 10201 }); - return; - } - if (!(userData && moduleData)) return; - - await getStateInfo(data, next); - - const content = await prepareCertificateData(data, next); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20210, trace: e }; - next(err); - return; - } -} - -module.exports = getCertificate; diff --git a/src/API/student/getCommentsList/getCommentsList.js b/src/API/student/getCommentsList/getCommentsList.js deleted file mode 100644 index efa5bdd..0000000 --- a/src/API/student/getCommentsList/getCommentsList.js +++ /dev/null @@ -1,45 +0,0 @@ -const { - checkModuleAccess, - getUserInfo, - getTaskInfo, - getStateInfo, - prapareTaskData, -} = require("@processes"); - -/*** - * getCommentsList StudentAPI method. - * https://api.eduhund.com/docs/student#getCommentsList - * - * Canary - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Task data on success; undefined on fail - */ -async function getCommentsList(req, res, next) { - try { - const { data } = req; - - await getUserInfo(data, next); - - if (!checkModuleAccess(data)) { - next({ code: 10201 }); - return; - } - - const state = await getStateInfo(data, next); - console.log(data.user); - const content = { comments: state.comments || [] }; - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20212, trace: e }; - next(err); - return; - } -} - -module.exports = getCommentsList; diff --git a/src/API/student/getCounselor/getCounselor.js b/src/API/student/getCounselor/getCounselor.js deleted file mode 100644 index 29b9528..0000000 --- a/src/API/student/getCounselor/getCounselor.js +++ /dev/null @@ -1,29 +0,0 @@ -const { getCounselorInfo } = require("@processes"); - -/*** - * getCounselor StudentAPI method. - * https://api.eduhund.com/docs/student#getCounselor - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Counselor data on success; undefined on fail - */ -async function getCounselor(req, res, next) { - try { - const { data } = req; - - const content = await getCounselorInfo(data, next); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20211, trace: e }; - next(err); - } -} - -module.exports = getCounselor; diff --git a/src/API/student/getLesson/getLesson.js b/src/API/student/getLesson/getLesson.js deleted file mode 100644 index 45f0772..0000000 --- a/src/API/student/getLesson/getLesson.js +++ /dev/null @@ -1,48 +0,0 @@ -const { - checkModuleAccess, - getUserInfo, - getLessonInfo, - getStateInfo, - prepareLessonData, -} = require("@processes"); - -/*** - * getLesson StudentAPI method. - * https://api.eduhund.com/docs/student#getLesson - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Lesson data on success; undefined on fail - */ -async function getLesson(req, res, next) { - try { - const { data } = req; - - const lessonData = await getLessonInfo(data, next); - if (!lessonData) return; - - await getUserInfo(data, next); - - if (!checkModuleAccess(data)) { - next({ code: 10201 }); - return; - } - - await getStateInfo(data, next); - - const content = await prepareLessonData(data, next); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20206, trace: e }; - next(err); - return; - } -} - -module.exports = getLesson; diff --git a/src/API/student/getMe/getMe.js b/src/API/student/getMe/getMe.js deleted file mode 100644 index bbaf9d3..0000000 --- a/src/API/student/getMe/getMe.js +++ /dev/null @@ -1,33 +0,0 @@ -const { getUserInfo, prepareUserData } = require("@processes"); - -/*** - * getMe StudentAPI method. - * https://api.eduhund.com/docs/student#getMe - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} User data on success; undefined on fail - */ -async function getMe(req, res, next) { - try { - const { data } = req; - - const userExists = await getUserInfo(data, next); - if (!userExists) return; - - const content = prepareUserData(data, next); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20204, trace: e }; - next(err); - return; - } -} - -module.exports = getMe; diff --git a/src/API/student/getModule/getModule.js b/src/API/student/getModule/getModule.js deleted file mode 100644 index 8b2c45c..0000000 --- a/src/API/student/getModule/getModule.js +++ /dev/null @@ -1,49 +0,0 @@ -const { - checkFinalAccess, - getUserInfo, - getModuleInfo, - getStateInfo, - prepareModuleData, -} = require("@processes"); - -/*** - * getModule StudentAPI method. - * https://api.eduhund.com/docs/student#getModule - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Module data on success; undefined on fail - */ -async function getModule(req, res, next) { - try { - const { data } = req; - - const moduleData = await getModuleInfo(data, next); - if (!moduleData) return; - - if (data.isAuth) { - await getUserInfo(data, next); - } - - const isUserAccess = checkFinalAccess(data, next); - - if (isUserAccess) { - await getStateInfo(data, next); - } - - const content = await prepareModuleData(data, next); - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20205, trace: e }; - next(err); - return; - } -} - -module.exports = getModule; diff --git a/src/API/student/getModulesList/getModulesList.js b/src/API/student/getModulesList/getModulesList.js deleted file mode 100644 index 3c38903..0000000 --- a/src/API/student/getModulesList/getModulesList.js +++ /dev/null @@ -1,54 +0,0 @@ -const { - checkFinalAccess, - getUserInfo, - getModuleInfo, - getStateInfo, - prepareModuleData, -} = require("@processes"); - -/*** - * getModulesList StudentAPI method. - * https://api.eduhund.com/docs/student#getModulesList - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Module data on success; undefined on fail - */ -async function getModulesList(req, res, next) { - try { - const { data } = req; - - const modulesList = await getModuleInfo(data, next); - - if (data.isAuth) { - await getUserInfo(data, next); - } - - const content = []; - - for (const moduleData of modulesList) { - data.module = moduleData; - - const isUserAccess = checkFinalAccess(data, next); - - if (isUserAccess) { - await getStateInfo(data, next); - } - - content.push(await prepareModuleData(data, next)); - } - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20213, trace: e }; - next(err); - return; - } -} - -module.exports = getModulesList; diff --git a/src/API/student/getTask/getTask.js b/src/API/student/getTask/getTask.js deleted file mode 100644 index 94ad98d..0000000 --- a/src/API/student/getTask/getTask.js +++ /dev/null @@ -1,50 +0,0 @@ -const { - checkModuleAccess, - getUserInfo, - getTaskInfo, - getStateInfo, - prapareTaskData, -} = require("@processes"); - -/*** - * getTask StudentAPI method. - * https://api.eduhund.com/docs/student#getTask - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Task data on success; undefined on fail - */ -async function getTask(req, res, next) { - try { - const { data } = req; - - await getUserInfo(data, next); - - if (!checkModuleAccess(data)) { - next({ code: 10201 }); - return; - } - - const taskData = await getTaskInfo(data, next); - if (!taskData) return; - - let content = taskData; - if (taskData.type === "practice") { - await getStateInfo(data, next); - content = await prapareTaskData(data, next); - } - - next({ code: 0, content }); - return content; - } catch (e) { - const err = { code: 20207, trace: e }; - next(err); - return; - } -} - -module.exports = getTask; diff --git a/src/API/student/setState/setState.js b/src/API/student/setState/setState.js deleted file mode 100644 index 6e8c2bb..0000000 --- a/src/API/student/setState/setState.js +++ /dev/null @@ -1,85 +0,0 @@ -const { - checkModuleAccess, - getUserInfo, - getTaskInfo, - getStateInfo, - setTaskState, - updateDependenciesTasks, -} = require("@processes"); -const DB = require("@mongo/requests"); - -/*** - * setState StudentAPI method. - * https://api.eduhund.com/docs/student#setState - * - * @since 0.6.0 - * - * @param {Object} req Express request object - * @param {Object} res Express response object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} New task's state on success; undefined on fail - */ -async function setState(req, res, next) { - try { - const { data } = req; - - await getUserInfo(data); - - if (!checkModuleAccess(data)) { - next({ code: 10201 }); - return; - } - - for (const [key, value] of Object.entries(data.newState)) { - if (/[A-Za-z]{3}\d{8}/.test(key)) { - const path = "data." + key + ".state"; - data.newState[path] = value; - delete data.newState[key]; - - const tasks = await DB.getMany("tasks", { - query: { - "content.questions.depends.parentId": { $regex: questionId }, - "content.questions.depends.type": "visibility", - }, - returns: ["id", "content"], - }); - - await updateDependenciesTasks(data.userId, key, tasks, value); - } - } - - const taskData = await getTaskInfo(data, next); - if (!taskData) return; - - const taskState = await getStateInfo(data, next); - if (!taskState) { - data.newState.inProcess = true; - } - - if (data.newState.protest) { - data.newState.score = taskData?.maxScore; - data.newState.isChecked = true; - } else { - if (data.newState.isChecked) { - data.newState.score = taskState?.data - ? calculateScore(stateData?.data, taskData) - : calculateDefaultScore(taskData?.content); - } else { - data.newState.score = 0; - } - } - - const newState = await setTaskState(data, next); - delete newState.comments; - - next({ code: 0, content: newState }); - return newState; - } catch (e) { - const err = { code: 20208, trace: e }; - next(err); - return; - } -} - -module.exports = setState; diff --git a/src/API/student/student.js b/src/API/student/student.js deleted file mode 100644 index e7a2a1c..0000000 --- a/src/API/student/student.js +++ /dev/null @@ -1,106 +0,0 @@ -const auth = require("./auth/auth"); -const checkPayment = require("./checkPayment/checkPayment"); -const createPass = require("./createPass/createPass"); -const getMe = require("./getMe/getMe"); -const getModule = require("./getModule/getModule"); -const getLesson = require("./getLesson/getLesson"); -const getTask = require("./getTask/getTask"); -const setState = require("./setState/setState"); -const addComment = require("./addComment/addComment"); -const getCommentsList = require("./getCommentsList/getCommentsList"); -const getCertificate = require("./getCertificate/getCertificate"); -const getCounselor = require("./getCounselor/getCounselor"); -const getModulesList = require("./getModulesList/getModulesList"); - -const STUDENT = [ - { - name: "auth", - type: "post", - requiredParams: ["email", "pass"], - otherParams: ["lang"], - exec: auth, - }, - { - name: "checkPayment", - type: "get", - requiredParams: ["paymentId"], - exec: checkPayment, - }, - { - name: "createPass", - type: "post", - requiredParams: ["email", "pass", "key"], - otherParams: ["lang"], - exec: [createPass], - }, - { - name: "getMe", - type: "get", - wall: true, - exec: getMe, - }, - { - name: "getModule", - type: "get", - wall: true, - requiredParams: ["moduleId"], - exec: [getModule], - }, - { - name: "getLesson", - type: "get", - wall: true, - requiredParams: ["lessonId"], - exec: [getLesson], - }, - { - name: "getTask", - type: "get", - wall: true, - requiredParams: ["taskId"], - exec: [getTask], - }, - { - name: "setState", - type: "post", - wall: true, - requiredParams: ["taskId", "newState"], - exec: [setState], - }, - { - name: "addComment", - type: "post", - wall: true, - requiredParams: ["taskId", "comment"], - exec: [addComment], - }, - { - name: "getCommentsList", - type: "get", - wall: true, - requiredParams: ["taskId"], - exec: [getCommentsList], - }, - { - name: "getCertificate", - type: "get", - wall: true, - requiredParams: ["moduleId"], - otherParams: ["isColor", "isMascot", "isResult", "isPublic"], - exec: [getCertificate], - }, - { - name: "getCounselor", - type: "get", - requiredParams: ["lang"], - exec: [getCounselor], - }, - { - name: "getModulesList", - type: "get", - wall: true, - exec: [getModulesList], - }, -]; - -module.exports = { STUDENT }; diff --git a/src/assets/colors.json b/src/assets/colors.json index 04c0278..fae6283 100644 --- a/src/assets/colors.json +++ b/src/assets/colors.json @@ -5,5 +5,5 @@ "COM": { "primary": "#6B70E2" }, "UCC": { "primary": "#295A12" }, "HSE": { "primary": "#0C356C" }, - "LGR": { "primary": "#0C356C" } + "LGR": { "primary": "#12C1C1" } } diff --git a/src/assets/lang/dicts/en.json b/src/assets/lang/dicts/en.json index 1052ae0..5e83128 100644 --- a/src/assets/lang/dicts/en.json +++ b/src/assets/lang/dicts/en.json @@ -8,6 +8,6 @@ "certCheck2": "write to", "certSignName": "Olga Pavlova", "certSignPos": "head of insanity", - "certSignNameLinor": "Linor Goralik", - "certSignPosLinor": "автор смысла" + "certSignNameLinor": "Based on a lecture by Linor Goralik", + "certSignPosLinor": "recognized foreign agent" } diff --git a/src/assets/lang/dicts/ru.json b/src/assets/lang/dicts/ru.json index b168a26..7d66ebd 100644 --- a/src/assets/lang/dicts/ru.json +++ b/src/assets/lang/dicts/ru.json @@ -8,6 +8,6 @@ "certCheck2": "напишите на", "certSignName": "Olga Pavlova", "certSignPos": "рквдтль безумия", - "certSignNameLinor": "Linor Goralik", - "certSignPosLinor": "автор смысла" + "certSignNameLinor": "По лекции Линор Горалик", + "certSignPosLinor": "признанный иностранный агент" } diff --git a/src/modules/apiRequests/addComment/addComment.js b/src/modules/apiRequests/addComment/addComment.js index e3f2b93..92b1386 100644 --- a/src/modules/apiRequests/addComment/addComment.js +++ b/src/modules/apiRequests/addComment/addComment.js @@ -1,10 +1,11 @@ const { log } = require("@logger"); + const { getDBRequest } = require("../../dbRequests/dbRequests"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); +const { sendMessage } = require("../../../services/assistant/assistant"); -async function addComment({ req, res }) { - const userId = req?.userId; +async function addComment(req, res) { + const { userId } = req; const { taskId, comment, protest } = req.body; const query = { userId, taskId }; @@ -15,31 +16,43 @@ async function addComment({ req, res }) { readedByTeacher: false, }; - try { - await getDBRequest("setComment", { - query, - data: update, - protest, - returns: [], - }); - const data = generateMessage(0, update); - - res.status(200).send(data); - - return data; - } catch (e) { - log.warn(`${taskId}: Error with processing new comment`); - log.warn(e); - const error = generateMessage(20115); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "addComment", - data: { taskId, comment, protest }, - req, - }); + await getDBRequest("setComment", { + query, + data: update, + protest, + returns: [], + }); + + const data = generateMessage(0, update); + res.status(200).send(data); + + const {email, firstName, lastName} = await getDBRequest("getUserInfo", { + query: { id: userId }, + returns: ["email", "firstName", "lastName"], + }) + + const { name } = await getDBRequest("getTaskInfo", { + query: { id: taskId }, + returns: ["name"] + }) + + const messageData = { + type: "taskComment", + data: { + email, + firstName, + lastName, + taskId: taskId, + taskName: name + }, + text: comment } + + sendMessage(messageData) + + log.info(`New comment from user ${userId}: ${comment}`); + + return; } -module.exports.addComment = addComment; +module.exports = addComment; diff --git a/src/modules/apiRequests/apiRequests.js b/src/modules/apiRequests/apiRequests.js index a3fa885..1baf769 100644 --- a/src/modules/apiRequests/apiRequests.js +++ b/src/modules/apiRequests/apiRequests.js @@ -1,23 +1,33 @@ const { log } = require("@logger"); -const { auth } = require("./auth/auth"); -const { checkPayment } = require("./checkPayment/checkPayment"); -const { createPassword } = require("./createPassword/createPassword"); -const { getTask } = require("./getTask/getTask"); -const { checkTask } = require("./checkTask/checkTask"); -const { setState } = require("./setState/setState"); -const { setControls } = require("./setControls/setControls"); -const { getDashboard } = require("./getDashboard/getDashboard"); -const { getModuleStart } = require("./getModuleStart/getModuleStart"); -const { getModuleFinal } = require("./getModuleFinal/getModuleFinal"); -const { getLessonStart } = require("./getLessonStart/getLessonStart"); -const { getLessonFinal } = require("./getLessonFinal/getLessonFinal"); -const { getDiploma } = require("./getDiploma/getDiploma"); -const { getModuleInfo } = require("./getModuleInfo/getModuleInfo"); -const { getLessonsList } = require("./getLessonsList/getLessonsList"); -const { getTasksList } = require("./getTasksList/getTasksList"); -const { addComment } = require("./addComment/addComment"); -const { getCounselor } = require("./getCounselor/getCounselor"); +const auth = require("./auth/auth"); +const checkPayment = require("./checkPayment/checkPayment"); +const createPassword = require("./createPassword/createPassword"); +const getTask = require("./getTask/getTask"); +const checkTask = require("./checkTask/checkTask"); +const setState = require("./setState/setState"); +const setControls = require("./setControls/setControls"); +const getDashboard = require("./getDashboard/getDashboard"); +const getModuleStart = require("./getModuleStart/getModuleStart"); +const getModuleFinal = require("./getModuleFinal/getModuleFinal"); +const getLessonStart = require("./getLessonStart/getLessonStart"); +const getLessonFinal = require("./getLessonFinal/getLessonFinal"); +const getDiploma = require("./getDiploma/getDiploma"); +const getModuleInfo = require("./getModuleInfo/getModuleInfo"); +const getLessonsList = require("./getLessonsList/getLessonsList"); +const getTasksList = require("./getTasksList/getTasksList"); +const addComment = require("./addComment/addComment"); +const getCounselor = require("./getCounselor/getCounselor"); + + +const resetPassword = require("./resetPassword/resetPassword"); +const createUser = require("./createUser/createUser"); +const updateUser = require("./updateUser/updateUser"); +const getStudentsList = require("./getStudentsList/getStudentsList"); +const getModulesList = require("./getModulesList/getModulesList"); +const changeCommentStatus = require("./changeCommentStatus/changeCommentStatus"); +const getCommentsList = require("./getCommentsList/getCommentsList"); +const newPayment = require("./newPayment/newPayment"); const { checkAuth, @@ -44,6 +54,14 @@ const REQUESTS = { getTasksList, addComment, getCounselor, + resetPassword, + createUser, + updateUser, + getStudentsList, + getModulesList, + changeCommentStatus, + getCommentsList, + newPayment, }; const PUBLIC = [ @@ -218,14 +236,67 @@ const PUBLIC = [ }, ]; +const TEACHER = [ + { + name: "createUser", + method: "post", + path: "/createUser", + exec: [(req, res) => getApiRequest("createUser", { req, res })], + }, + { + name: "resetPassword", + method: "post", + path: "/resetPassword", + exec: [(req, res) => getApiRequest("resetPassword", { req, res })], + }, + { + name: "updateUser", + method: "post", + path: "/updateUser", + exec: [(req, res) => getApiRequest("updateUser", { req, res })], + }, + { + name: "getStudentsList", + method: "get", + path: "/getStudentsList", + exec: [(req, res) => getApiRequest("getStudentsList", { req, res })], + }, + { + name: "getModulesList", + method: "get", + path: "/getModulesList", + exec: [(req, res) => getApiRequest("getModulesList", { req, res })], + }, + { + name: "getCommentsList", + method: "get", + path: "/getCommentsList", + exec: [(req, res) => getApiRequest("getCommentsList", { req, res })], + }, + { + name: "changeCommentStatus", + method: "post", + path: "/changeCommentStatus", + exec: [(req, res) => getApiRequest("changeCommentStatus", { req, res })], + }, + { + name: "newPayment", + method: "post", + path: "/newPayment", + exec: [(req, res) => getApiRequest("newPayment", { req, res })], + }, +] + async function getApiRequest(type, { req, res, next }) { try { - return REQUESTS[type]({ req, res, next }); + return REQUESTS[type](req, res, next); } catch (e) { - log.warn(`Error in API method: ${type}.`, e); + log.error(`Error in API method: ${type}.`); + log.debug(req.userId, req.query, req.body); + log.debug(e); res.sendStatus(500); return; } } -module.exports = { PUBLIC, getApiRequest }; +module.exports = { PUBLIC, TEACHER, getApiRequest }; diff --git a/src/modules/apiRequests/auth/auth.js b/src/modules/apiRequests/auth/auth.js index b6d078c..b3d040d 100644 --- a/src/modules/apiRequests/auth/auth.js +++ b/src/modules/apiRequests/auth/auth.js @@ -1,13 +1,12 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); -const accessTokens = require("../../../services/tokenMachine/tokenMachine"); const { checkPass } = require("../../../utils/pass"); const { generateMessage } = require("../../../utils/messageGenerator"); -const tokens = accessTokens; +const tokens = require("../../../services/tokenMachine/tokenMachine"); -async function auth({ req, res }) { +async function auth(req, res) { const { email, pass, lang } = req.body; const user = await getDBRequest("getUserInfo", { @@ -15,24 +14,22 @@ async function auth({ req, res }) { }); if (!user) { - log.info(`${email}: User didn't found!`); + log.warn(`${email}: User didn't found!`); const error = generateMessage(10101); res.status(401).send(error); return error; } if (!checkPass(user, pass)) { - log.info(`${email}: Invalid password!`); + log.warn(`${email}: Invalid password!`); const error = generateMessage(10102); res.status(401).send(error); return error; } const userToken = tokens.setToken(user); - lang && - lang !== user.lang && - getDBRequest("setUserInfo", { email, data: { lang } }); - log.info(`${user.id}: Auth success!`); + lang && lang !== user.lang && getDBRequest("setUserInfo", { email, data: { lang } }); + const userData = { id: user.id, email: user.email, @@ -41,11 +38,13 @@ async function auth({ req, res }) { lang: lang || user.lang, token: userToken, }; - const data = generateMessage(0, userData); + const data = generateMessage(0, userData); res.status(200).send(data); - return data; + log.info(`${user.id}: Auth success!`); + + return } -module.exports.auth = auth; +module.exports = auth; diff --git a/src/modules/apiRequests/changeCommentStatus/changeCommentStatus.js b/src/modules/apiRequests/changeCommentStatus/changeCommentStatus.js new file mode 100644 index 0000000..ee77704 --- /dev/null +++ b/src/modules/apiRequests/changeCommentStatus/changeCommentStatus.js @@ -0,0 +1,25 @@ +const { log } = require("../../../services/logger/logger"); +const { STATE } = require("../../dbRequests/mongo"); +const { generateMessage } = require("../../../utils/messageGenerator"); + +async function changeCommentStatus(req, res) { + + const {userId, taskId, status = false} = req.body + + await STATE.findOneAndUpdate( + { userId, taskId }, + { + $set: { + "comments.0.readedByTeacher": status, + }, + }, + { upsert: true, returnDocument: "after", returnNewDocument: true } + ); + + const data = generateMessage(0); + res.status(200).send(data); + + return +} + +module.exports = changeCommentStatus; diff --git a/src/modules/apiRequests/checkPayment/checkPayment.js b/src/modules/apiRequests/checkPayment/checkPayment.js index 8fe7df3..7a71edd 100644 --- a/src/modules/apiRequests/checkPayment/checkPayment.js +++ b/src/modules/apiRequests/checkPayment/checkPayment.js @@ -4,7 +4,7 @@ const { getDBRequest } = require("../../dbRequests/dbRequests"); const accessTokens = require("../../../services/tokenMachine/tokenMachine"); const { generateMessage } = require("../../../utils/messageGenerator"); -async function checkPayment({ req, res }) { +async function checkPayment(req, res) { const { paymentId } = req.body; const payment = await getDBRequest("getPaymentInfo", { @@ -12,10 +12,10 @@ async function checkPayment({ req, res }) { }); if (!payment) { - log.info(`${paymentId}: Payment didn't found!`); + log.warn(`${paymentId}: Payment didn't found!`); const error = generateMessage(10104); res.status(401).send(error); - return error; + return; } const user = await getDBRequest("getUserInfo", { @@ -24,23 +24,25 @@ async function checkPayment({ req, res }) { }); if (!user) { - log.info(`${payment.email}: User didn't found!`); + log.warn(`${payment.email}: User didn't found!`); const error = generateMessage(10101); res.status(401).send(error); - return error; + return; } const userToken = accessTokens.setToken(user); - log.info(`${user.id}: Auth success!`); + const userData = { ...user, token: userToken, }; + const data = generateMessage(0, userData); - res.status(200).send(data); - return data; + log.info(`${user.id}: Auth success!`); + + return; } -module.exports = { checkPayment }; +module.exports = checkPayment; diff --git a/src/modules/apiRequests/checkTask/checkTask.js b/src/modules/apiRequests/checkTask/checkTask.js index d9be817..119999e 100644 --- a/src/modules/apiRequests/checkTask/checkTask.js +++ b/src/modules/apiRequests/checkTask/checkTask.js @@ -7,9 +7,8 @@ const { calculateDefaultScore, } = require("../../../utils/calculators"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function checkTask({ req, res }) { +async function checkTask(req, res) { const userId = req?.userId; const { taskId, isChecked, protest } = req.body; @@ -23,43 +22,30 @@ async function checkTask({ req, res }) { ]; let score = 0; - try { - const [taskData, stateData] = await Promise.all(requests); - - if (isChecked) { - if (protest) { - score = taskData?.maxScore; - } else if (!stateData?.data) { - score = calculateDefaultScore(taskData?.content); - } else { - score = calculateScore(stateData?.data, taskData); - } + const [taskData, stateData] = await Promise.all(requests); + + if (isChecked) { + if (protest) { + score = taskData?.maxScore; + } else if (!stateData?.data) { + score = calculateDefaultScore(taskData?.content); + } else { + score = calculateScore(stateData?.data, taskData); } + } - const query = { userId, taskId }; - getDBRequest("setState", { - query, - state: { score, isChecked, protest }, - returns: ["score"], - }); - const data = generateMessage(0, { score }); + const query = { userId, taskId }; - res.status(200).send(data); + getDBRequest("setState", { + query, + state: { score, isChecked, protest }, + returns: ["score"], + }); - return data; - } catch (e) { - log.warn(`${taskId}: Error while task was checking`); - log.warn(e); - const error = generateMessage(20105); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "checkTask", - data: { taskId, isChecked, protest, score }, - req, - }); - } + const data = generateMessage(0, { score }); + res.status(200).send(data); + + return; } -module.exports.checkTask = checkTask; +module.exports = checkTask; diff --git a/src/modules/apiRequests/createPassword/createPassword.js b/src/modules/apiRequests/createPassword/createPassword.js index daaf018..8aeff2d 100644 --- a/src/modules/apiRequests/createPassword/createPassword.js +++ b/src/modules/apiRequests/createPassword/createPassword.js @@ -4,14 +4,14 @@ const { getDBRequest } = require("../../dbRequests/dbRequests"); const { checkKey } = require("../../../services/tokenMachine/OTK"); const { generateMessage } = require("../../../utils/messageGenerator"); -async function createPassword({ req, res, next }) { +async function createPassword(req, res, next) { const { email, pass, key, lang } = req.body; const verify = await checkKey(key); if (!verify) { const error = generateMessage(10105); res.status(401).send(error); - return error; + return ; } const data = { pass }; @@ -28,10 +28,13 @@ async function createPassword({ req, res, next }) { if (!user) { const error = generateMessage(20101); res.status(401).send(error); - return error; + return ; } + log.info(`New password was setted for user ${email}!`); + next(); + return } -module.exports = { createPassword }; +module.exports = createPassword; diff --git a/src/modules/apiRequests/createUser/createUser.js b/src/modules/apiRequests/createUser/createUser.js new file mode 100644 index 0000000..2e10976 --- /dev/null +++ b/src/modules/apiRequests/createUser/createUser.js @@ -0,0 +1,79 @@ +const { log } = require("../../../services/logger/logger"); +const { getDBRequest } = require("../../dbRequests/dbRequests"); +const { setKey } = require("../../../services/tokenMachine/OTK") +const { lowerString } = require("../../../utils/stringProcessor") +const { generateMessage } = require("../../../utils/messageGenerator"); + +async function createUser(req, res) { + const { email, pass, firstName, lastName, modules, startDate, deadline, lang } = req.body + + if (!email) { + res.status(401); + res.send({ + OK: false, + error: "missing_parameters", + error_description: "Missing required parameters", + error_code: 10008, + }); + return + } + const userEmail = lowerString(email); + + const isUserExist = await getDBRequest("checkUsername", { + email: userEmail, + }); + + if (isUserExist) { + res.status(401); + res.send({ + OK: false, + error: "user_already_exist", + error_description: "User with this email is already exist", + error_code: 10008, + }); + + return + } + + const userModules = {}; + (modules || []).forEach((userModule) => { + //const date = new Date(Date.now()); + userModules[userModule.id] = { + start: startDate, // date.toISOString().split("T")[0], + deadline, //calculateDeadline(date, 80), + }; + }); + const newUser = { + email: userEmail, + pass: pass ? hashPass(pass) : "", + firstName, + lastName, + modules: userModules, + lang, + }; + const createdUser = await getDBRequest("addUser", newUser); + + const sendData = { + OK: true, + data: { + id: createdUser.id, + email: createdUser.email, + firstName: createdUser.firstName, + lastName: createdUser.lastName, + }, + }; + + if (!createdUser.pass) { + const secureKey = await setKey(createdUser.id, "oneTimeKey"); + sendData.data.key = secureKey; + } + + const data = generateMessage(0, sendData); + res.status(200).send(data); + + log.info(`New user was created:`, createdUser); + + return +}; + +module.exports = createUser; diff --git a/src/modules/apiRequests/getCommentsList/getCommentsList.js b/src/modules/apiRequests/getCommentsList/getCommentsList.js new file mode 100644 index 0000000..3acb9c7 --- /dev/null +++ b/src/modules/apiRequests/getCommentsList/getCommentsList.js @@ -0,0 +1,53 @@ +const { log } = require("../../../services/logger/logger"); +const { USERS, TASKS, STATE } = require("../../dbRequests/mongo"); +const { generateMessage } = require("../../../utils/messageGenerator"); + +async function getCommentsList(req, res) { + const stateArray = await STATE.find( + { "comments.0": { $exists: true } }, + { + projection: { + _id: 0, + data: 0, + }, + } + ).toArray(); + + const commentList = []; + for (const item of stateArray) { + if (typeof item.comments[0] !== "string") { + const userName = await USERS.findOne({ id: item.userId }).then( + (result) => { + return result.firstName + " " + result?.lastName; + } + ); + const taskInfo = await TASKS.findOne({ id: item.taskId }).then( + (result) => { + return { + module: result?.module, + lesson: result?.lesson, + task: result?.name, + }; + } + ); + + commentList.unshift({ + userId: item.userId, + userName, + module: taskInfo.module, + lesson: taskInfo.lesson, + task: taskInfo.task, + taskId: item.taskId, + message: item.comments[0]?.message, + readedByTeacher: item?.comments[0]?.readedByTeacher || false, + }); + } + } + + const data = generateMessage(0, commentList); + res.status(200).send(data); + + return +} + +module.exports = getCommentsList; diff --git a/src/modules/apiRequests/getCounselor/getCounselor.js b/src/modules/apiRequests/getCounselor/getCounselor.js index a10c379..53d9a64 100644 --- a/src/modules/apiRequests/getCounselor/getCounselor.js +++ b/src/modules/apiRequests/getCounselor/getCounselor.js @@ -1,33 +1,23 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function getCounselor({ req, res }) { +async function getCounselor(req, res) { const userId = req?.userId; const { lang = "en" } = req?.query; - try { + const response = await getDBRequest("getCounselor", { query: { lang } }); - if (response) { - const data = generateMessage(0, response?.pages); - res.status(200).send(data); - return data; - } else { + if (!response) { throw new Error("Selected language is not exist"); } - } catch (e) { - log.warn(`${userId}: Error with getting counselor content`); - log.warn(e); - const error = generateMessage(20105); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getCounselor", - data: { userId }, - req, - }); - } + + const data = generateMessage(0, response.pages); + res.status(200).send(data); + + log.info(`User ${userId} is watching the counselor`); + + return; + } -module.exports.getCounselor = getCounselor; +module.exports = getCounselor; diff --git a/src/modules/apiRequests/getDashboard/getDashboard.js b/src/modules/apiRequests/getDashboard/getDashboard.js index 919e3fd..4253118 100644 --- a/src/modules/apiRequests/getDashboard/getDashboard.js +++ b/src/modules/apiRequests/getDashboard/getDashboard.js @@ -3,143 +3,124 @@ const { getDBRequest } = require("../../dbRequests/dbRequests"); const { calculateTotalScore, - calculateDeadline, } = require("../../../utils/calculators"); const { getNextTaskId } = require("../../../utils/getNextTaskId"); const setLessonsState = require("../../../utils/setLessonsState"); const { getNumberOfDoneTasks } = require("./getNumberOfDoneTasks"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -const DEMO = process.env.DEMO; +const { MACHINE } = process.env; -async function getDashboard({ req, res }) { +async function getDashboard(req, res) { const userId = req?.userId; - try { - const userData = await getDBRequest("getUserInfo", { - query: { id: userId }, - }); - - const username = `${userData?.firstName} ${userData?.lastName}`; - const email = userData?.email; - const lang = userData?.lang; - const userModules = userData?.modules; - - let modulesList = await getDBRequest("getModulesList", { - query: { - ...(DEMO ? {} : { active: true }), - lang, - }, - }); - - modulesList.forEach((module) => { - module.status = "available"; - - delete module.intro; - delete module.final; - }); - - const availableModules = []; - - for (const moduleId of Object.keys(userModules)) { - const moduleData = modulesList.find((item) => item.code == moduleId); - if (!moduleData) continue; - const today = Date.now(); - const startDate = Date.parse(userModules[moduleId].start); - const deadline = Date.parse(calculateDeadline(userModules[moduleId])); - const UTCMidnight = new Date(deadline); - UTCMidnight.setUTCHours(23, 59, 59, 0); - const UTCDeadline = Date.parse(UTCMidnight); - - moduleData.startDate = userModules[moduleId].start; - moduleData.deadline = calculateDeadline(userModules[moduleId]); - - if (moduleData.prevModule) { - if (Object.keys(userModules).includes(moduleData.prevModule)) { - module.status = "unavailable"; - } - } + const { email, lang, modules, firstName, lastName } = await getDBRequest("getUserInfo", { + query: { id: userId }, + }); - if (today < startDate) { - moduleData.status = "paid"; - } else if (today > UTCDeadline) { - moduleData.status = "past"; - } else if (today > UTCDeadline - 864000000 && today < UTCDeadline) { - moduleData.status = "deadline"; - } else { - moduleData.status = "active"; - } + const username = `${firstName} ${lastName}`; - if ( - moduleData.status == "active" || - moduleData.status == "deadline" || - moduleData.status == "past" - ) { - const moduleState = await getDBRequest("getUserState", { - query: { - userId, - taskId: { $regex: `^${moduleId}` }, - }, - }); - - const nextTaskId = getNextTaskId(moduleData, moduleState); - - moduleData.doneTasks = getNumberOfDoneTasks(moduleState); - - moduleData.lessons = setLessonsState(moduleData.lessons, nextTaskId); - - moduleData.totalScore = calculateTotalScore(moduleState); - - moduleData.nextTask = await getDBRequest("getTaskInfo", { - query: { id: nextTaskId }, - }).then((result) => { - let nextTask; - if (result) { - nextTask = { - id: nextTaskId, - type: result?.type, - name: result?.name, - lesson: result?.lesson, - }; - } - return nextTask; - }); - - moduleData.moduleId = moduleId; - - availableModules.push(moduleData); - - modulesList = modulesList.filter((module) => module.code !== moduleId); + let modulesList = await getDBRequest("getModulesList", { + query: { + ...(MACHINE === "prod" ? { active: true } : {}), + lang, + }, + }); + + modulesList.forEach((module) => { + module.status = "available"; + + delete module.intro; + delete module.final; + }); + + const availableModules = []; + + for (const moduleId of Object.keys(modules)) { + const moduleData = modulesList.find((item) => item.code == moduleId); + if (!moduleData) continue; + const today = Date.now(); + const startDate = Date.parse(modules[moduleId].start); + const deadline = Date.parse(modules[moduleId].deadline); + const UTCMidnight = new Date(deadline); + UTCMidnight.setUTCHours(23, 59, 59, 0); + const UTCDeadline = Date.parse(UTCMidnight); + + moduleData.startDate = modules[moduleId].start; + moduleData.deadline = modules[moduleId].deadline; + + if (moduleData.prevModule) { + if (Object.keys(modules).includes(moduleData.prevModule)) { + module.status = "unavailable"; } } - availableModules.push(...modulesList); + if (today < startDate) { + moduleData.status = "paid"; + } else if (today > UTCDeadline) { + moduleData.status = "past"; + } else if (today > UTCDeadline - 864000000 && today < UTCDeadline) { + moduleData.status = "deadline"; + } else { + moduleData.status = "active"; + } - const finalData = { - username, - email, - lang, - modules: availableModules, - }; - const data = generateMessage(0, finalData); - - res.status(200).send(data); - - return data; - } catch (e) { - log.warn(`${userId}: Error with processing dashboard page`); - log.warn(e); - const error = generateMessage(20107); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getDashboard", - data: { userId }, - req, - }); + if ( + moduleData.status == "active" || + moduleData.status == "deadline" || + moduleData.status == "past" + ) { + const moduleState = await getDBRequest("getUserState", { + query: { + userId, + taskId: { $regex: `^${moduleId}` }, + }, + }); + + const nextTaskId = getNextTaskId(moduleData, moduleState); + + moduleData.doneTasks = getNumberOfDoneTasks(moduleState); + + moduleData.lessons = setLessonsState(moduleData.lessons, nextTaskId); + + moduleData.totalScore = calculateTotalScore(moduleState); + + moduleData.nextTask = await getDBRequest("getTaskInfo", { + query: { id: nextTaskId }, + }).then((result) => { + let nextTask; + if (result) { + nextTask = { + id: nextTaskId, + type: result?.type, + name: result?.name, + lesson: result?.lesson, + }; + } + return nextTask; + }); + + moduleData.moduleId = moduleId; + + availableModules.push(moduleData); + + modulesList = modulesList.filter((module) => module.code !== moduleId); + } } + + availableModules.push(...modulesList); + + const finalData = { + username, + email, + lang, + modules: availableModules, + }; + + const data = generateMessage(0, finalData); + res.status(200).send(data); + + return; } -module.exports.getDashboard = getDashboard; +module.exports = getDashboard; diff --git a/src/modules/apiRequests/getDiploma/getDiploma.js b/src/modules/apiRequests/getDiploma/getDiploma.js index b422432..da5f264 100644 --- a/src/modules/apiRequests/getDiploma/getDiploma.js +++ b/src/modules/apiRequests/getDiploma/getDiploma.js @@ -1,9 +1,8 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { generateSkills } = require("./generateSkills"); -const generateCertId = require("../../../processes/prepareData/prepareCertificateData/generateCertId"); +const generateCertId = require("../../../utils/generateCertId"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); const provideData = require("./provideData"); const CyrillicToTranslit = require("cyrillic-to-translit-js"); const { fork } = require("child_process"); @@ -22,9 +21,7 @@ async function generateCert(fullInfo) { }); } -async function getDiploma({ req, res }) { - //const certGen = fork(__dirname + "/../../../utils/certGenerator"); - +async function getDiploma(req, res) { const userId = req?.userId; const { moduleId, lang, isColor, isMascot, isProgress, isPublic } = req?.query; @@ -66,127 +63,114 @@ async function getDiploma({ req, res }) { }), ]; - try { - const [userData, stateData, moduleData] = await Promise.all(requests); + const [userData, stateData, moduleData] = await Promise.all(requests); - const start = userData?.modules?.[moduleId]?.deadline; - const deadline = userData?.modules?.[moduleId]?.deadline; - const now = new Date(Date.now()).toISOString().split("T")[0]; - const certDate = Date.parse(deadline) < Date.parse(now) ? deadline : now; + const start = userData?.modules?.[moduleId]?.deadline; + const deadline = userData?.modules?.[moduleId]?.deadline; + const now = new Date(Date.now()).toISOString().split("T")[0]; + const certDate = Date.parse(deadline) < Date.parse(now) ? deadline : now; - const certId = - userData?.modules?.[moduleId]?.certId || - (await generateCertId(userId, moduleId, start)); + const certId = + userData?.modules?.[moduleId]?.certId || + (await generateCertId(userId, moduleId, start)); - const certData = await getDBRequest("getDiploma", { - query: { id: certId }, - returns: ["lang", "isColor", "isMascot", "isProgress", "isPublic"], - }); + const certData = await getDBRequest("getDiploma", { + query: { id: certId }, + returns: ["lang", "isColor", "isMascot", "isProgress", "isPublic"], + }); - if (params.lang === undefined) params.lang = certData?.lang || moduleData.lang; - if (params.isColor === undefined) params.isColor = certData?.isColor || false; - if (params.isMascot === undefined) params.isMascot = certData.isMascot === undefined ? true : certData?.isMascot; - if (params.isProgress === undefined) params.isProgress = certData.isProgress === undefined ? true : certData?.isProgress; - if (params.isPublic === undefined) params.isPublic = certData?.isPublic || false; + if (params.lang === undefined) params.lang = certData?.lang || moduleData.lang; + if (params.isColor === undefined) params.isColor = certData?.isColor || false; + if (params.isMascot === undefined) params.isMascot = certData?.isMascot === undefined ? true : certData?.isMascot; + if (params.isProgress === undefined) params.isProgress = certData?.isProgress === undefined ? true : certData?.isProgress; + if (params.isPublic === undefined) params.isPublic = certData?.isPublic || false; - getDBRequest("setDiploma", { - query: { id: certId }, - data: params, - returns: ["lang", "isColor", "isMascot", "isProgress", "isPublic"], - }); + getDBRequest("setDiploma", { + query: { id: certId }, + data: params, + returns: ["lang", "isColor", "isMascot", "isProgress", "isPublic"], + }); - const firstName = - params.lang === "ru" - ? userData.firstName - : cyrillicToTranslit.transform(userData.firstName); - const lastName = - params.lang === "ru" - ? userData.lastName - : cyrillicToTranslit.transform(userData.lastName); - - const score = stateData.reduce( - (progress, value) => progress + (value?.score || 0), - 0 - ); - - let maxScore = 0; - for (const lesson of Object.values(moduleData?.lessons)) { - for (const task of lesson.tasks) { - const taskData = await getDBRequest("getTaskInfo", { - query: { - id: task, - type: "practice", - }, - returns: ["maxScore"], - }); - maxScore += taskData?.maxScore || 0; - } + const firstName = + params.lang === "ru" + ? userData.firstName + : cyrillicToTranslit.transform(userData.firstName); + const lastName = + params.lang === "ru" + ? userData.lastName + : cyrillicToTranslit.transform(userData.lastName); + + const score = stateData.reduce( + (progress, value) => progress + (value?.score || 0), + 0 + ); + + let maxScore = 0; + for (const lesson of Object.values(moduleData?.lessons)) { + for (const task of lesson.tasks) { + const taskData = await getDBRequest("getTaskInfo", { + query: { + id: task, + type: "practice", + }, + returns: ["maxScore"], + }); + maxScore += taskData?.maxScore || 0; } - - const doneTasks = stateData.reduce((progress, value) => { - if (value.isChecked) { - return progress + 1; - } else return progress; - }, 0); - - const progress = Math.trunc((score / maxScore) * 100); - - const skills = await generateSkills( - moduleId, - userId, - params.lang || moduleData.lang - ); - - const info = { - moduleId, - moduleName: moduleData?.name, - firstName, - lastName, - certId, - certDate, - progress, - skills, - }; - - const fullInfo = provideData(info, params); - - const fileId = await generateCert(fullInfo); - - Object.assign(moduleData, params); - - moduleData.firstName = firstName; - moduleData.lastName = lastName; - moduleData.start = start; - moduleData.deadline = deadline; - moduleData.certDate = certDate; - moduleData.certId = certId; - moduleData.score = score; - moduleData.maxScore = maxScore; - moduleData.skills = skills; - moduleData.fileId = fileId; - - delete moduleData.lessons; - - moduleData.doneTasks = doneTasks; - - const data = generateMessage(0, moduleData); - - res.status(200).send(data); - - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing diploma`); - log.warn(e); - const error = generateMessage(20111); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getDiploma", - data: { moduleId }, - req, - }); } + + const doneTasks = stateData.reduce((progress, value) => { + if (value.isChecked) { + return progress + 1; + } else return progress; + }, 0); + + const progress = Math.trunc((score / maxScore) * 100); + + const skills = await generateSkills( + moduleId, + userId, + params.lang || moduleData.lang + ); + + const info = { + moduleId, + moduleName: moduleData?.name, + firstName, + lastName, + certId, + certDate, + progress, + skills, + }; + + const fullInfo = provideData(info, params); + + const fileId = await generateCert(fullInfo); + + Object.assign(moduleData, params); + + moduleData.firstName = firstName; + moduleData.lastName = lastName; + moduleData.start = start; + moduleData.deadline = deadline; + moduleData.certDate = certDate; + moduleData.certId = certId; + moduleData.score = score; + moduleData.maxScore = maxScore; + moduleData.skills = skills; + moduleData.fileId = fileId; + + delete moduleData.lessons; + + moduleData.doneTasks = doneTasks; + + const data = generateMessage(0, moduleData); + res.status(200).send(data); + + log.info(`${userId} created a certificate for module ${moduleId}:`, fileId, params); + + return; } -module.exports.getDiploma = getDiploma; +module.exports = getDiploma; diff --git a/src/modules/apiRequests/getDiploma/moduleNameHyphenate.js b/src/modules/apiRequests/getDiploma/moduleNameHyphenate.js index f48cfcd..bac8d18 100644 --- a/src/modules/apiRequests/getDiploma/moduleNameHyphenate.js +++ b/src/modules/apiRequests/getDiploma/moduleNameHyphenate.js @@ -6,7 +6,7 @@ const dict = { UCC: "Сценарии\nвзаимодействия", COM: "Композиция\nв дизайне интерфейсов", HSE: "Hard Skills начинающего\nруководителя", - LGR: "Низкобюджетный\nмаркетинг. Рассылки", + LGR: "Рассылки,\nкоторые читают", }, en: { HSB: "Novice Manager\nHard Skills", @@ -15,7 +15,7 @@ const dict = { UCC: "Use cases\nin Interface Design", COM: "Composition\nin Interface Design", HSE: "Hard Skills\nfor Managers", - LGR: "Низкобюджетный\nмаркетинг. Рассылки", + LGR: "Readable maillist", }, }; diff --git a/src/modules/apiRequests/getDiploma/skillsDict.js b/src/modules/apiRequests/getDiploma/skillsDict.js index 574be58..0f4ba91 100644 --- a/src/modules/apiRequests/getDiploma/skillsDict.js +++ b/src/modules/apiRequests/getDiploma/skillsDict.js @@ -2838,7 +2838,7 @@ const en = { ], LGR: [ { - name: "рассылки для людей", + name: "Newsletters for people", tasks: [ "LGR0102", "LGR0207", @@ -2850,7 +2850,7 @@ const en = { ], }, { - name: "воронка в email-маркетинге", + name: "Email marketing funnels", tasks: [ "LGR0104", "LGR0105", @@ -2866,7 +2866,7 @@ const en = { ], }, { - name: "письмо как баннер", + name: "The letter as a banner", tasks: [ "LGR0113", "LGR0309", @@ -2882,7 +2882,7 @@ const en = { ], }, { - name: "техника изменения повода", + name: "Letters’ occasion techniques", tasks: [ "LGR0202", "LGR0203", @@ -2894,11 +2894,11 @@ const en = { ], }, { - name: "проектирование содержания", + name: "Content design", tasks: ["LGR0302", "LGR0303", "LGR0304", "LGR0305", "LGR0306", "LGR0308"], }, { - name: "упрощение чтения письма", + name: "Making letter reading easier", tasks: ["LGR0402", "LGR0403", "LGR0404", "LGR0405", "LGR0406", "LGR0408"], }, ], diff --git a/src/modules/apiRequests/getLessonFinal/getLessonFinal.js b/src/modules/apiRequests/getLessonFinal/getLessonFinal.js index 61eda03..3cb39c5 100644 --- a/src/modules/apiRequests/getLessonFinal/getLessonFinal.js +++ b/src/modules/apiRequests/getLessonFinal/getLessonFinal.js @@ -4,10 +4,8 @@ const { getDBRequest } = require("../../dbRequests/dbRequests"); const { getModuleId, getLessonId } = require("../../../utils/idExtractor"); const { createSummary } = require("./createSummary"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -const { calculateDeadline } = require("../../../utils/calculators"); -async function getLessonFinal({ req, res }) { +async function getLessonFinal(req, res) { const userId = req?.userId; const fullLessonId = req?.query?.lessonId; @@ -31,63 +29,48 @@ async function getLessonFinal({ req, res }) { }), ]; - try { - const [userData, stateData, moduleData] = await Promise.all(requests); - - const lessonsArray = Object.keys(moduleData.lessons || {}); - const currentLessonIndex = lessonsArray.indexOf(lessonId); - - moduleData.lessonNumber = Number(lessonId); - moduleData.nextLesson = lessonsArray.length > currentLessonIndex + 1; - moduleData.maxScore = moduleData.lessons[lessonId]?.maxScore; - moduleData.content = moduleData.lessons[lessonId]?.final; - - let totalPractice = 0; - for (const task of moduleData.lessons[lessonId]?.tasks) { - await getDBRequest("getTaskInfo", { - query: { id: task }, - returns: ["type"], - }).then((result) => { - if (result?.type == "practice") totalPractice++; - }); - } - moduleData.totalTasks = totalPractice; - - moduleData.deadline = calculateDeadline(userData?.modules?.[moduleId]); - - moduleData.score = stateData.reduce( - (progress, value) => progress + (value?.score || 0), - 0 - ); - - moduleData.summary = createSummary(moduleData?.score, moduleData?.maxScore); - - moduleData.doneTasks = stateData.reduce((progress, value) => { - if (value.isChecked) { - return progress + 1; - } else return progress; - }, 0); - - delete moduleData.lessons; - - const data = generateMessage(0, moduleData); - - res.status(200).send(data); - - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing lesson final page`); - log.warn(e); - const error = generateMessage(20106); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getLessonFinal", - data: { moduleId }, - req, + const [userData, stateData, moduleData] = await Promise.all(requests); + + const lessonsArray = Object.keys(moduleData.lessons || {}); + const currentLessonIndex = lessonsArray.indexOf(lessonId); + + moduleData.lessonNumber = Number(lessonId); + moduleData.nextLesson = lessonsArray.length > currentLessonIndex + 1; + moduleData.maxScore = moduleData.lessons[lessonId]?.maxScore; + moduleData.content = moduleData.lessons[lessonId]?.final; + + let totalPractice = 0; + for (const task of moduleData.lessons[lessonId]?.tasks) { + await getDBRequest("getTaskInfo", { + query: { id: task }, + returns: ["type"], + }).then((result) => { + if (result?.type == "practice") totalPractice++; }); } + moduleData.totalTasks = totalPractice; + + moduleData.deadline = userData?.modules?.[moduleId].deadline; + + moduleData.score = stateData.reduce( + (progress, value) => progress + (value?.score || 0), + 0 + ); + + moduleData.summary = createSummary(moduleData?.score, moduleData?.maxScore); + + moduleData.doneTasks = stateData.reduce((progress, value) => { + if (value.isChecked) { + return progress + 1; + } else return progress; + }, 0); + + delete moduleData.lessons; + + const data = generateMessage(0, moduleData); + res.status(200).send(data); + + return; } -module.exports.getLessonFinal = getLessonFinal; +module.exports = getLessonFinal; diff --git a/src/modules/apiRequests/getLessonStart/getLessonStart.js b/src/modules/apiRequests/getLessonStart/getLessonStart.js index be78468..7b938e7 100644 --- a/src/modules/apiRequests/getLessonStart/getLessonStart.js +++ b/src/modules/apiRequests/getLessonStart/getLessonStart.js @@ -2,9 +2,8 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { getModuleId, getLessonId } = require("../../../utils/idExtractor"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function getLessonStart({ req, res }) { +async function getLessonStart(req, res) { const userId = req.userId; const fullLessonId = req.query.lessonId; @@ -22,35 +21,20 @@ async function getLessonStart({ req, res }) { }), ]; - try { - const [userData, moduleData] = await Promise.all(requests); - - moduleData.lessonNumber = Number(lessonId); - moduleData.lessonName = moduleData.lessons[lessonId]?.title; - moduleData.description = moduleData.lessons[lessonId]?.description; - moduleData.intro = moduleData.lessons[lessonId]?.intro; - moduleData.deadline = userData?.modules?.[moduleId]?.deadline; - - delete moduleData.lessons; - - const data = generateMessage(0, moduleData); - - res.status(200).send(data); - - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing lesson start page`); - log.warn(e); - const error = generateMessage(20105); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getLessonStart", - data: { moduleId }, - req, - }); - } + const [userData, moduleData] = await Promise.all(requests); + + moduleData.lessonNumber = Number(lessonId); + moduleData.lessonName = moduleData.lessons[lessonId]?.title; + moduleData.description = moduleData.lessons[lessonId]?.description; + moduleData.intro = moduleData.lessons[lessonId]?.intro; + moduleData.deadline = userData?.modules?.[moduleId]?.deadline; + + delete moduleData.lessons; + + const data = generateMessage(0, moduleData); + res.status(200).send(data); + + return; } -module.exports.getLessonStart = getLessonStart; +module.exports = getLessonStart; diff --git a/src/modules/apiRequests/getLessonsList/getLessonsList.js b/src/modules/apiRequests/getLessonsList/getLessonsList.js index 96d4b1f..46a145f 100644 --- a/src/modules/apiRequests/getLessonsList/getLessonsList.js +++ b/src/modules/apiRequests/getLessonsList/getLessonsList.js @@ -1,9 +1,8 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function getLessonsList({ req, res }) { +async function getLessonsList(req, res) { const userId = req?.userId; const moduleId = req?.query?.moduleId; @@ -12,68 +11,53 @@ async function getLessonsList({ req, res }) { returns: ["lessons"], }); - try { - const lessonList = []; + const lessonList = []; - for (const lesson of Object.entries(moduleData?.lessons || {})) { - const [lessonId, lessonData] = lesson; - const lessonStateData = await getDBRequest("getUserState", { - query: { - userId, - taskId: { $regex: `^${moduleId}${lessonId}` }, - }, - }); - var inProcess = false; - const progress = - lessonStateData.length === 0 - ? 0 - : Math.trunc( - (lessonStateData.reduce((progress, value) => { - if (value.inProcess && !inProcess) inProcess = true; - return progress + (value.score || 0); - }, 0) / - lessonData?.maxScore) * - 100 - ); - - lessonList.push({ - id: lessonId, - title: lessonData.title, - description: lessonData.description, - maxScore: lessonData.maxScore, - inProcess, - progress, - }); - } - - if (lessonList.length > 0) { - let currentLesson = 0; - lessonList.forEach((lesson, index) => { - if (lesson.inProcess) { - currentLesson = index; - } - }); - lessonList[currentLesson].currentLesson = true; - } - - const data = generateMessage(0, lessonList); - - res.status(200).send(data); + for (const lesson of Object.entries(moduleData?.lessons || {})) { + const [lessonId, lessonData] = lesson; + const lessonStateData = await getDBRequest("getUserState", { + query: { + userId, + taskId: { $regex: `^${moduleId}${lessonId}` }, + }, + }); + var inProcess = false; + const progress = + lessonStateData.length === 0 + ? 0 + : Math.trunc( + (lessonStateData.reduce((progress, value) => { + if (value.inProcess && !inProcess) inProcess = true; + return progress + (value.score || 0); + }, 0) / + lessonData?.maxScore) * + 100 + ); + + lessonList.push({ + id: lessonId, + title: lessonData.title, + description: lessonData.description, + maxScore: lessonData.maxScore, + inProcess, + progress, + }); + } - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing lessons list`); - log.warn(e); - const error = generateMessage(20108); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getLessonsList", - data: { moduleId }, - req, + if (lessonList.length > 0) { + let currentLesson = 0; + lessonList.forEach((lesson, index) => { + if (lesson.inProcess) { + currentLesson = index; + } }); + lessonList[currentLesson].currentLesson = true; } + + const data = generateMessage(0, lessonList); + res.status(200).send(data); + + return; } -module.exports.getLessonsList = getLessonsList; +module.exports = getLessonsList; diff --git a/src/modules/apiRequests/getModuleFinal/getModuleFinal.js b/src/modules/apiRequests/getModuleFinal/getModuleFinal.js index 057ee22..bd23886 100644 --- a/src/modules/apiRequests/getModuleFinal/getModuleFinal.js +++ b/src/modules/apiRequests/getModuleFinal/getModuleFinal.js @@ -1,13 +1,11 @@ const { log } = require("@logger"); const { calculateModuleMaxScore, - calculateDeadline, } = require("../../../utils/calculators"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function getModuleFinal({ req, res }) { +async function getModuleFinal(req, res) { const userId = req?.userId; const moduleId = req?.query?.moduleId; @@ -36,50 +34,35 @@ async function getModuleFinal({ req, res }) { }), ]; - try { - const [userData, stateData, moduleData] = await Promise.all(requests); + const [userData, stateData, moduleData] = await Promise.all(requests); - if (!moduleData) { - const error = generateMessage(10303); - res.status(200).send(error); - return error; - } - - moduleData.deadline = calculateDeadline(userData?.modules?.[moduleId]); + if (!moduleData) { + const error = generateMessage(10303); + res.status(200).send(error); + return error; + } - moduleData.maxScore = calculateModuleMaxScore(moduleData.lessons); + moduleData.deadline = userData?.modules?.[moduleId].deadline; - delete moduleData.lessons; + moduleData.maxScore = calculateModuleMaxScore(moduleData.lessons); - moduleData.score = stateData.reduce( - (progress, value) => progress + (value?.score || 0), - 0 - ); + delete moduleData.lessons; - moduleData.doneTasks = stateData.reduce((progress, value) => { - if (value.isChecked) { - return progress + 1; - } else return progress; - }, 0); + moduleData.score = stateData.reduce( + (progress, value) => progress + (value?.score || 0), + 0 + ); - const data = generateMessage(0, moduleData); + moduleData.doneTasks = stateData.reduce((progress, value) => { + if (value.isChecked) { + return progress + 1; + } else return progress; + }, 0); - res.status(200).send(data); + const data = generateMessage(0, moduleData); + res.status(200).send(data); - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing module final page`); - log.warn(e); - const error = generateMessage(20104); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getModuleFinal", - data: { moduleId }, - req, - }); - } + return; } -module.exports.getModuleFinal = getModuleFinal; +module.exports = getModuleFinal; diff --git a/src/modules/apiRequests/getModuleInfo/getModuleInfo.js b/src/modules/apiRequests/getModuleInfo/getModuleInfo.js index 5d3fa16..d9a92c1 100644 --- a/src/modules/apiRequests/getModuleInfo/getModuleInfo.js +++ b/src/modules/apiRequests/getModuleInfo/getModuleInfo.js @@ -1,10 +1,8 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -const { calculateDeadline } = require("../../../utils/calculators"); -async function getModuleInfo({ req, res }) { +async function getModuleInfo(req, res) { const userId = req?.userId; const moduleId = req?.query?.moduleId; @@ -32,59 +30,44 @@ async function getModuleInfo({ req, res }) { }), ]; - try { - const [userData, stateData, moduleData] = await Promise.all(requests); + const [userData, stateData, moduleData] = await Promise.all(requests); - moduleData.deadline = calculateDeadline(userData?.modules?.[moduleId]); - moduleData.startDate = userData?.modules?.[moduleId]?.start; + moduleData.deadline = userData?.modules?.[moduleId]?.deadline; + moduleData.startDate = userData?.modules?.[moduleId]?.start; - let maxScore = 0; - for (const lesson of Object.values(moduleData?.lessons)) { - for (const task of lesson.tasks) { - const taskData = await getDBRequest("getTaskInfo", { - query: { - id: task, - type: "practice", - }, - returns: ["maxScore"], - }); - maxScore += taskData?.maxScore || 0; - } + let maxScore = 0; + for (const lesson of Object.values(moduleData?.lessons)) { + for (const task of lesson.tasks) { + const taskData = await getDBRequest("getTaskInfo", { + query: { + id: task, + type: "practice", + }, + returns: ["maxScore"], + }); + maxScore += taskData?.maxScore || 0; } + } - moduleData.maxScore = maxScore; - - delete moduleData.lessons; + moduleData.maxScore = maxScore; - moduleData.score = stateData.reduce( - (progress, value) => progress + (value?.score || 0), - 0 - ); + delete moduleData.lessons; - moduleData.doneTasks = stateData.reduce((progress, value) => { - if (value.isChecked) { - return progress + 1; - } else return progress; - }, 0); + moduleData.score = stateData.reduce( + (progress, value) => progress + (value?.score || 0), + 0 + ); - const data = generateMessage(0, moduleData); + moduleData.doneTasks = stateData.reduce((progress, value) => { + if (value.isChecked) { + return progress + 1; + } else return progress; + }, 0); - res.status(200).send(data); + const data = generateMessage(0, moduleData); + res.status(200).send(data); - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing module Info`); - log.warn(e); - const error = generateMessage(20110); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getModuleInfo", - data: { userId }, - req, - }); - } + return; } -module.exports.getModuleInfo = getModuleInfo; +module.exports = getModuleInfo; diff --git a/src/modules/apiRequests/getModuleStart/getModuleStart.js b/src/modules/apiRequests/getModuleStart/getModuleStart.js index a7e9f0e..fcaa08d 100644 --- a/src/modules/apiRequests/getModuleStart/getModuleStart.js +++ b/src/modules/apiRequests/getModuleStart/getModuleStart.js @@ -4,7 +4,7 @@ const { getNextTaskId } = require("../../../utils/getNextTaskId"); const { generateMessage } = require("../../../utils/messageGenerator"); const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function getModuleStart({ req, res }) { +async function getModuleStart(req, res) { const userId = req?.userId; const moduleId = req?.query?.moduleId; @@ -22,43 +22,28 @@ async function getModuleStart({ req, res }) { }), ]; - try { - const [userModules, userState, moduleData] = await Promise.all(requests); + const [userModules, userState, moduleData] = await Promise.all(requests); - if (!moduleData) { - const error = generateMessage(10303); - res.status(200).send(error); - return error; - } - - moduleData.deadline = userModules?.modules?.[moduleId]?.deadline; + if (!moduleData) { + const error = generateMessage(10303); + res.status(200).send(error); + return error; + } - moduleData.nextTaskId = getNextTaskId(moduleData, userState); + moduleData.deadline = userModules?.modules?.[moduleId]?.deadline; - moduleData.lessons = Object.entries(moduleData?.lessons).map( - ([id, data]) => { - return { id, title: data?.title, description: data?.description }; - } - ); + moduleData.nextTaskId = getNextTaskId(moduleData, userState); - const data = generateMessage(0, moduleData); + moduleData.lessons = Object.entries(moduleData?.lessons).map( + ([id, data]) => { + return { id, title: data?.title, description: data?.description }; + } + ); - res.status(200).send(data); + const data = generateMessage(0, moduleData); + res.status(200).send(data); - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing module start page`); - log.warn(e); - const error = generateMessage(20103); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getModuleStart", - data: { moduleId }, - req, - }); - } + return; } -module.exports.getModuleStart = getModuleStart; +module.exports = getModuleStart; diff --git a/src/modules/apiRequests/getModulesList/getModulesList.js b/src/modules/apiRequests/getModulesList/getModulesList.js new file mode 100644 index 0000000..a599b82 --- /dev/null +++ b/src/modules/apiRequests/getModulesList/getModulesList.js @@ -0,0 +1,14 @@ +const { log } = require("../../../services/logger/logger"); +const { getDBRequest } = require("../../dbRequests/dbRequests"); +const { generateMessage } = require("../../../utils/messageGenerator"); + +async function getModulesList(req, res) { + const modulesList = await getDBRequest("getModulesList", {}); + + const data = generateMessage(0, modulesList); + res.status(200).send(data); + + return +} + +module.exports = getModulesList; diff --git a/src/modules/apiRequests/getStudentsList/getStudentsList.js b/src/modules/apiRequests/getStudentsList/getStudentsList.js new file mode 100644 index 0000000..824db76 --- /dev/null +++ b/src/modules/apiRequests/getStudentsList/getStudentsList.js @@ -0,0 +1,140 @@ +const { log } = require("../../../services/logger/logger"); + +const { getDBRequest } = require("../../dbRequests/dbRequests"); +const { calculateTotalScore } = require("../../../utils/calculators"); +const { getNextTaskId } = require("../../../utils/getNextTaskId"); +const { getDeadline } = require("../../../utils/access"); +const { generateMessage } = require("../../../utils/messageGenerator"); + +async function getStudentsList(req, res) { + const usersList = await getDBRequest("getUsersList", {}); + + const usersData = []; + + for (const user of usersList) { + const userData = {}; + userData.id = user.id; + userData.email = user.email; + userData.firstName = user.firstName; + userData.lastName = user.lastName; + userData.lang = user.lang; + userData.gender = user.gender; + userData.isActivated = user.pass ? true : false; + + const userModules = []; + + for (const moduleId of Object.keys(user.modules || {})) { + const start = user.modules[moduleId].start; + const deadline = getDeadline(user.modules[moduleId]); + const prolongations = user.modules[moduleId].prolongations || []; + const certId = user.modules[moduleId].certId; + + const requests = [ + getDBRequest("getModuleInfo", { + query: { code: moduleId }, + }), + getDBRequest("getUserState", { + query: { + userId: user.id, + taskId: { $regex: `^${moduleId}` }, + }, + }), + ]; + + const [moduleData, userState] = await Promise.all(requests); + + if (!moduleData || Object.keys(moduleData).length === 0) { + continue; + } + + const moduleName = moduleData?.shortName; + const mascot = moduleData?.mascot; + + const totalTasks = moduleData?.totalTasks; + + const forsakenTasksInfo = userState.filter( + (task) => task?.inProcess && !task?.isChecked + ); + + const forsakenTasks = []; + + for (const task of forsakenTasksInfo) { + const taskData = await getDBRequest("getTaskInfo", { + query: { id: task?.taskId }, + returns: ["id", "type", "name", "lesson", "title"], + }); + + forsakenTasks.push({ + id: task?.id, + type: taskData?.type, + name: + taskData?.type === "practice" + ? `Задача ${taskData?.name}` + : taskData?.title, + }); + } + + const doneTasks = userState.filter((task) => task?.isChecked).length; + + const comments = userState.filter( + (task) => (task.comments || []).length > 0 + ).length; + + const protests = userState.filter((task) => task?.protest).length; + + const nextTaskId = getNextTaskId(moduleData, userState); + + const nextTask = await getDBRequest("getTaskInfo", { + query: { id: nextTaskId }, + returns: ["id", "type", "name", "lesson", "title"], + }); + + if (nextTask) { + nextTask.name = + nextTask?.type === "practice" + ? `Задача ${nextTask?.name}` + : nextTask?.title; + } + const totalScore = calculateTotalScore(userState); + + const maxScore = Object.keys(moduleData.lessons).reduce( + (sum, lessonId) => { + return sum + moduleData.lessons[lessonId].maxScore; + }, + 0 + ); + + const lessons = []; + + userModules.push({ + id: moduleId, + moduleName, + mascot, + start, + deadline, + prolongations, + certId, + totalTasks, + forsakenTasks, + doneTasks, + comments, + protests, + nextTask, + totalScore, + maxScore, + lessons, + }); + } + + userData.modules = userModules; + + usersData.push(userData); + } + + const data = generateMessage(0, usersData); + res.status(200).send(data); + + return +} + +module.exports = getStudentsList; diff --git a/src/modules/apiRequests/getTask/getTask.js b/src/modules/apiRequests/getTask/getTask.js index 16214e9..46e5726 100644 --- a/src/modules/apiRequests/getTask/getTask.js +++ b/src/modules/apiRequests/getTask/getTask.js @@ -5,9 +5,8 @@ const { getModuleId } = require("../../../utils/idExtractor"); const { prepareModuleData } = require("./prepareModuleData"); const { prepareTaskData } = require("./prepareTaskData"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function getTask({ req, res }) { +async function getTask(req, res) { const userId = req.userId; const { taskId } = req.query; @@ -29,60 +28,45 @@ async function getTask({ req, res }) { }), ]; - try { - const [userData, moduleData, taskData, taskState] = await Promise.all( - requests - ); + const [userData, moduleData, taskData, taskState] = await Promise.all( + requests + ); - if (!moduleData) { - const error = generateMessage(10303); - res.status(200).send(error); - return error; - } - - if (!taskData) { - const error = generateMessage(10301); - res.status(200).send(error); - return error; - } + if (!moduleData) { + const error = generateMessage(10303); + res.status(200).send(error); + return; + } - const preparedModuleData = await prepareModuleData({ moduleData, taskId }); + if (!taskData) { + const error = generateMessage(10301); + res.status(200).send(error); + return; + } - const preparedTaskData = await prepareTaskData({ - taskData, - taskState, - userId, - lang: moduleData?.lang, - }); + const preparedModuleData = await prepareModuleData({ moduleData, taskId }); - const preparedUserData = { - deadline: userData?.modules[moduleId]?.deadline, - }; + const preparedTaskData = await prepareTaskData({ + taskData, + taskState, + userId, + lang: moduleData?.lang, + }); - const aggData = { - ...preparedModuleData, - ...preparedTaskData, - ...preparedUserData, - }; + const preparedUserData = { + deadline: userData?.modules[moduleId]?.deadline, + }; - const data = generateMessage(0, aggData); + const aggData = { + ...preparedModuleData, + ...preparedTaskData, + ...preparedUserData, + }; - res.status(200).send(data); + const data = generateMessage(0, aggData); + res.status(200).send(data); - return data; - } catch (e) { - log.warn(`${taskId}: Error with processing task`); - log.warn(e); - const error = generateMessage(20102); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getTask", - data: { taskId }, - req, - }); - } + return; } -module.exports.getTask = getTask; +module.exports = getTask; diff --git a/src/modules/apiRequests/getTask/prepareTaskData.js b/src/modules/apiRequests/getTask/prepareTaskData.js index 56c68ac..9e695b8 100644 --- a/src/modules/apiRequests/getTask/prepareTaskData.js +++ b/src/modules/apiRequests/getTask/prepareTaskData.js @@ -11,7 +11,6 @@ async function updateVisibility(userId, data) { const { type, parentId, isVisible } = depend; if (type == "visibility") { const visibility = await setVisibility(userId, parentId, id); - console.log("V", visibility); data.isVisible = visibility; if (!isVisible) { break; diff --git a/src/modules/apiRequests/getTasksList/getTasksList.js b/src/modules/apiRequests/getTasksList/getTasksList.js index 6b15249..a4d8d04 100644 --- a/src/modules/apiRequests/getTasksList/getTasksList.js +++ b/src/modules/apiRequests/getTasksList/getTasksList.js @@ -3,9 +3,8 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { getModuleId, getLessonId } = require("../../../utils/idExtractor"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function getTasksList({ req, res }) { +async function getTasksList(req, res) { const userId = req?.userId; const fullLessonId = req?.query?.lessonId; @@ -23,60 +22,46 @@ async function getTasksList({ req, res }) { return error; } - try { - const tasksList = []; + const tasksList = []; - const lesson = lessons[lessonId]; + const lesson = lessons[lessonId]; - if (!lesson) { - const error = generateMessage(10302); + if (!lesson) { + const error = generateMessage(10302); + res.status(200).send(error); + return error; + } + + for (const taskId of lesson.tasks || []) { + const taskData = await getDBRequest("getTaskInfo", { + query: { id: taskId }, + returns: ["id", "type", "title", "name", "maxScore"], + }); + + if (!taskData) { + const error = generateMessage(10301); res.status(200).send(error); return error; } - for (const taskId of lesson.tasks || []) { - const taskData = await getDBRequest("getTaskInfo", { - query: { id: taskId }, - returns: ["id", "type", "title", "name", "maxScore"], + if (taskData?.type === "practice") { + const taskState = await getDBRequest("getStateInfo", { + query: { userId, taskId }, }); - if (!taskData) { - const error = generateMessage(10301); - res.status(200).send(error); - return error; - } + taskData.score = taskState?.score >= 0 ? taskState?.score : null; + taskData.isChecked = taskState?.isChecked || false; + taskData.inProcess = taskState?.inProcess; + } - if (taskData?.type === "practice") { - const taskState = await getDBRequest("getStateInfo", { - query: { userId, taskId }, - }); + tasksList.push(taskData); + } - taskData.score = taskState?.score >= 0 ? taskState?.score : null; - taskData.isChecked = taskState?.isChecked || false; - taskData.inProcess = taskState?.inProcess; - } + const data = generateMessage(0, tasksList); - tasksList.push(taskData); - } + res.status(200).send(data); - const data = generateMessage(0, tasksList); - - res.status(200).send(data); - - return data; - } catch (e) { - log.warn(`${moduleId}: Error with processing tasks list`); - log.warn(e); - const error = generateMessage(20109); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "getTasksList", - data: { userId }, - req, - }); - } + return; } -module.exports.getTasksList = getTasksList; +module.exports = getTasksList; diff --git a/src/modules/apiRequests/newPayment/newPayment.js b/src/modules/apiRequests/newPayment/newPayment.js new file mode 100644 index 0000000..37ce144 --- /dev/null +++ b/src/modules/apiRequests/newPayment/newPayment.js @@ -0,0 +1,305 @@ +const { getDBRequest } = require("../../dbRequests/dbRequests"); +const { createUser } = require("../createUser/createUser"); +const { calculateDeadline } = require("../../../utils/calculators"); +const { hashPass } = require("../../../utils/pass"); +const { setKey } = require("../../../services/tokenMachine/OTK"); +const { log } = require("../../../services/logger/logger"); +const { prepareMail } = require("../../../services/mailer/actions"); +const { sendMail } = require("../../../services/mailer/actions"); + +function checkSource(body) { + const keys = Object.keys(body) + + if (keys.includes("gumroad_fee")) { + return "Gumroad"; + } + + if (keys.includes("sign")) { + return "Tilda"; + } + + return undefined +} + +function splitName(name) { + if (name) { + const [firstName, lastName] = name.split(" ", 2); + return { + firstName, + lastName, + }; + } else return {} + +} + +function prepareData(source, data, date) { + const initData = { source, ts: Date.now() }; + const products = { + ["_-jg_yvw1calvFhRDQaqJg=="]: "HSE", + ["DYGiKngRwU-N1dt_WOJ0lg=="]: "HSP", + }; + + switch (source) { + case "Gumroad": + return { + ...initData, + date, + email: data.email, + firstName: data["First Name"], + lastName: data["Last Name"], + paymentId: data.sale_id, + moduleId: products[data.product_id], + value: data.price, + currency: "USD", + }; + case "Tilda": + const moduleId = data.payment.products[0].sku.slice(0, 3); + const isProlongation = data.payment.products[0].sku.includes("+"); + return { + ...initData, + date, + email: data.email, + ...splitName(data.name), + paymentId: data.payment.orderid, + moduleId, + isProlongation, + value: data.payment.products[0].price, + currency: "RUB", + }; + default: + throw new Error("Payment data wasn't prepared") + } +} + +function getDateObject(date) { + const timestamp = date ? Date.parse(date) : Date.mow(); + return new Date(timestamp); +} + +function getISODateOny(date) { + const dateObject = typeof date === "string" ? getDateObject(date) : date; + return dateObject.toISOString().split("T")[0]; +} + +async function newPayment(req, res) { + + const { body } = req; + + if (body.test) { + res.sendStatus(200); + return; + } + + log.debug(body); + const date = new Date(Date.now()); + + const source = checkSource(body); + + if (!source) { + res.status(400); + res.send({ + OK: false, + error: "invalid_params" + }) + return + } + + const payment = prepareData(source, body, date); + + log.debug(payment); + + const moduleInfo = await getDBRequest("getModuleInfo", { + query: { code: payment.moduleId.toUpperCase() }, + }); + + if (!moduleInfo) { + res.status(400); + res.send({ + OK: false, + error: "invalid_params" + }) + return + } + + const user = await getDBRequest("getUserInfo", { + query: { email: payment.email }, + }); + + let start = getISODateOny(date); + + if (body.start) { + const dateArray = body.start.split("."); + const startDateNormilized = `${dateArray[2]}-${dateArray[1]}-${dateArray[0]}`; + start = getISODateOny(startDateNormilized); + } + let deadline = calculateDeadline(start, 62); + + if (!user) { + const userPass = Math.random().toString(36).substring(2); + const lang = source === "Tilda" ? "ru" : "en"; + + const newUser = { + email: payment.email, + pass: userPass ? hashPass(userPass) : "", + firstName: payment.firstName, + lastName: payment.lastName, + modules: { + [payment.moduleId]: { + start, + deadline, + prolongations: [], + }, + }, + lang, + }; + + const createdUser = await getDBRequest("addUser", newUser); + + const secureKey = await setKey(createdUser?.id, "oneTimeKey"); + + const link = `${process.env.FRONTEND_URL}/createPassword?email=${payment.email}&verifyKey=${secureKey}&lang=${lang}`; + + const params = { + lang, + status: "new", + type: "buy", + start: getISODateOny(date) < start ? "date" : "now", + }; + + const data = { + NAME: newUser?.firstName || "", + MODULE: moduleInfo?.name || "", + MODULELINK: moduleInfo?.moduleLink || "", + MASCOTLETTERTOP: moduleInfo?.mascot?.letterTop || "", + MASCOTLETTERBOTTOM: moduleInfo?.mascot?.letterBottom || "", + START_DATE: new Date(Date.parse(start)).toLocaleDateString("ru-RU", { + month: "long", + day: "numeric", + }), + PASSWORD: userPass || "", + PASSWORD_LINK: link, + }; + + const mail = prepareMail({ params, data }); + + sendMail(mail, newUser?.email, "eduHund"); + + } else if (payment?.isProlongation) { + deadline = calculateDeadline( + user?.modules[payment.moduleId].deadline, + 62 + ); + + const prolData = { + type: "renewal", + paymentId: payment.paymentId, + until: deadline, + }; + + user.modules[payment?.moduleId].deadline = deadline + if (!Array.isArray(user.modules[payment?.moduleId].prolongations)) { + user.modules[payment?.moduleId].prolongations = [] + } + user.modules[payment?.moduleId].prolongations.push(prolData) + + await getDBRequest("setUserInfo", { + id: user?.id, + data: { modules: user.modules }, + }); + + const params = { + lang: "ru" || user?.lang, + status: "renew", + }; + + const data = { + NAME: user?.firstName || "", + MODULE: moduleInfo?.name || "", + MODULELINK: moduleInfo?.moduleLink || "", + MASCOTLETTERTOP: moduleInfo?.mascot?.letterTop || "", + MASCOTLETTERBOTTOM: moduleInfo?.mascot?.letterBottom || "", + START_DATE: new Date(Date.parse(start)).toLocaleDateString("ru-RU", { + month: "long", + day: "numeric", + }), + }; + const mail = prepareMail({ params, data }); + + sendMail(mail, user?.email, "eduHund"); + + } else { + + const activeModules = Object.entries(user.modules || {}).filter( + ([, value]) => + Date.now() - Date.parse(value.deadline) <= 7 * 24 * 60 * 60 * 1000 + ); + + for (const [id] of activeModules) { + const isCurrent = id === payment?.moduleId + const type = isCurrent ? "newBuy" : "otherModule"; + const data = { + type, + paymentId: payment.paymentId, + until: deadline, + }; + + user.modules[id].deadline = deadline + if (!Array.isArray(user.modules[id].prolongations)) { + user.modules[id].prolongations = [] + } + user.modules[id].prolongations.push(data) + } + + if (!user.modules[payment.moduleId]) { + user.modules[payment.moduleId] = { + start, + deadline, + prolongations: [{ + type: "newBuy", + paymentId: payment.paymentId, + until: deadline, + }] + } + } + + await getDBRequest("setUserInfo", { + id: user?.id, + data: { modules: user.modules }, + }); + + const params = { + lang: user?.lang || "en", + status: "current", + type: "buy", + start: getISODateOny(date) < start ? "date" : "now", + }; + + const data = { + NAME: user?.firstName || "", + MODULE: moduleInfo?.name || "", + MODULELINK: moduleInfo?.moduleLink || "", + MASCOTLETTERTOP: moduleInfo?.mascot?.letterTop || "", + MASCOTLETTERBOTTOM: moduleInfo?.mascot?.letterBottom || "", + START_DATE: new Date(Date.parse(start)).toLocaleDateString("ru-RU", { + month: "long", + day: "numeric", + }), + }; + + const mail = prepareMail({ params, data }); + + sendMail(mail, user?.email, "eduHund"); + } + + const response = await getDBRequest("setPayment", { payment }); + + if (response?.acknowledged) { + res.sendStatus(200); + } else { + res.sendStatus(400); + } + + return; +} + +module.exports = newPayment; diff --git a/src/modules/apiRequests/resetPassword/resetPassword.js b/src/modules/apiRequests/resetPassword/resetPassword.js new file mode 100644 index 0000000..4d57150 --- /dev/null +++ b/src/modules/apiRequests/resetPassword/resetPassword.js @@ -0,0 +1,35 @@ +const { log } = require("../../../services/logger/logger"); + +const { getDBRequest } = require("../../dbRequests/dbRequests"); +const { lowerString } = require("../../../utils/stringProcessor") +const { setKey } = require("../../../services/tokenMachine/OTK") + +async function resetPassword(req, res) { + const email = lowerString(req.body.email); + const user = await getDBRequest("checkUsername", { email }) + + if (user) { + const { id, email } = user + const secureKey = await setKey(user?.id, "oneTimeKey"); + res.status(200); + res.send({ + OK: true, + data: { + id, + email, + key: secureKey, + }, + }); + } else { + res.status(401); + res.send({ + OK: false, + error: "invalid_credentials", + error_description: "User didn't found", + error_code: 10001, + }); + } + return +}; + +module.exports = resetPassword \ No newline at end of file diff --git a/src/modules/apiRequests/setControls/setControls.js b/src/modules/apiRequests/setControls/setControls.js index c4f953f..f29bccf 100644 --- a/src/modules/apiRequests/setControls/setControls.js +++ b/src/modules/apiRequests/setControls/setControls.js @@ -2,37 +2,22 @@ const { log } = require("@logger"); const { getDBRequest } = require("../../dbRequests/dbRequests"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); -async function setControls({ req, res }) { +async function setControls(req, res) { const userId = req?.userId; const { taskId, controlsState } = req.body; - try { - const query = { userId, taskId }; - await getDBRequest("setControls", { - query, - controlsState, - returns: [], - }); - const data = generateMessage(0); + const query = { userId, taskId }; + await getDBRequest("setControls", { + query, + controlsState, + returns: [], + }); + + const data = generateMessage(0); + res.status(200).send(data); - res.status(200).send(data); - - return data; - } catch (e) { - log.warn(`${taskId}: Error while updating task's controls state`); - log.warn(e); - const error = generateMessage(20114); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "setControls", - data: { taskId, controlsState }, - req, - }); - } + return; } -module.exports.setControls = setControls; +module.exports = setControls; diff --git a/src/modules/apiRequests/setState/setState.js b/src/modules/apiRequests/setState/setState.js index 2c118c6..cab0dc1 100644 --- a/src/modules/apiRequests/setState/setState.js +++ b/src/modules/apiRequests/setState/setState.js @@ -5,7 +5,6 @@ const { getFullTaskId } = require("../../../utils/idExtractor"); const { getVisibilityUpdateList } = require("./getVisibilityUpdateList"); const { updateDependenciesTasks } = require("./updateDependenciesTasks"); const { generateMessage } = require("../../../utils/messageGenerator"); -const { addUserAction } = require("../../../modules/statistics/addUserAction"); const { getParentContent } = require("../../../utils/getParentContent"); const { refAnswerRight } = require("../../../utils/refAnswerRight"); @@ -40,7 +39,7 @@ async function updateContent(taskData, taskState = {}, userId, lang = "en") { return questionsDict; } -async function setState({ req, res }) { +async function setState(req, res) { const userId = req?.userId; const { questionId, state } = req.body; @@ -58,53 +57,38 @@ async function setState({ req, res }) { returns: ["id", "content"], }); - try { - const questions = getVisibilityUpdateList(tasks, questionId); + const questions = getVisibilityUpdateList(tasks, questionId); - await updateDependenciesTasks(userId, questions, state); + await updateDependenciesTasks(userId, questions, state); - const path = "data." + questionId + ".state"; - const query = { userId, taskId }; - const update = { - [path]: state, - inProcess: true, - }; - const newState = await getDBRequest("setState", { - query, - state: update, - returns: [], - }); - - const updatedData = await updateContent( - taskData, - newState?.value?.data, - userId - ); + const path = "data." + questionId + ".state"; + const query = { userId, taskId }; + const update = { + [path]: state, + inProcess: true, + }; + const newState = await getDBRequest("setState", { + query, + state: update, + returns: [], + }); - const finalData = Object.assign( - {}, - newState?.value?.data || {}, - updatedData - ); + const updatedData = await updateContent( + taskData, + newState?.value?.data, + userId + ); - const data = generateMessage(0, finalData); + const finalData = Object.assign( + {}, + newState?.value?.data || {}, + updatedData + ); - res.status(200).send(data); + const data = generateMessage(0, finalData); + res.status(200).send(data); - return data; - } catch (e) { - log.warn(`${questionId}: Error while setting new question state`); - log.warn(e); - const error = generateMessage(20112); - res.status(400).send(error); - } finally { - addUserAction({ - userId, - action: "setState", - data: { questionId }, - req, - }); - } + return; } -module.exports.setState = setState; +module.exports = setState; diff --git a/src/modules/apiRequests/updateUser/updateUser.js b/src/modules/apiRequests/updateUser/updateUser.js new file mode 100644 index 0000000..41eaf93 --- /dev/null +++ b/src/modules/apiRequests/updateUser/updateUser.js @@ -0,0 +1,59 @@ +const { log } = require("../../../services/logger/logger"); +const { getDBRequest } = require("../../dbRequests/dbRequests"); +const { lowerString } = require("../../../utils/stringProcessor") +const { generateMessage } = require("../../../utils/messageGenerator"); + +async function updateUser(req, res) { + const { id, email, firstName, lastName, lang, gender} = req.body + + if (!id) { + res.status(401); + res.send({ + OK: false, + error: "missing_parameters", + error_description: "Missing required parameters", + error_code: 10008, + }); + return + } + + const data = { + email: lowerString(email), + firstName, + lastName, + lang, + gender, + }; + + for (const key of Object.keys(data)) { + if (!data[key]) delete data[key] + } + + const updatedUser = await getDBRequest("setUserInfo", { + id, + data, + }); + + if (updatedUser) { + delete updatedUser.pass + const sendData = { + OK: true, + data: updatedUser, + }; + + const data = generateMessage(0, sendData); + res.status(200).send(data); + } else { + res.status(401); + res.send({ + OK: false, + error: "updating_error", + error_description: "Error while updating user", + error_code: 10009, + }); + } + + return +}; + +module.exports = updateUser; diff --git a/src/modules/dbRequests/addUser/addUser.js b/src/modules/dbRequests/addUser/addUser.js new file mode 100644 index 0000000..30b2907 --- /dev/null +++ b/src/modules/dbRequests/addUser/addUser.js @@ -0,0 +1,15 @@ +const { log } = require("../../../services/logger/logger"); +const { USERS } = require("../../dbRequests/mongo"); + +async function addUser(user) { + log.info("Registering user:", user); + return USERS.countDocuments().then((totalUsers) => { + const userId = `U${(totalUsers + 1).toString().padStart(7, "0")}`; + user.id = userId; + return USERS.insertOne(user).then(() => { + return user; + }); + }); +} + +module.exports = addUser \ No newline at end of file diff --git a/src/modules/dbRequests/addUserAction/addUserAction.js b/src/modules/dbRequests/addUserAction/addUserAction.js index 007fe27..2094ef7 100644 --- a/src/modules/dbRequests/addUserAction/addUserAction.js +++ b/src/modules/dbRequests/addUserAction/addUserAction.js @@ -1,7 +1,7 @@ -const { db } = require("../mongo"); +const { ACTIONS } = require("../mongo"); function addUserAction({ userId, action, data, params }) { - return db.ACTIONS.insertOne({ + return ACTIONS.insertOne({ ts: Date.now(), userId, action, @@ -10,4 +10,4 @@ function addUserAction({ userId, action, data, params }) { }); } -module.exports.addUserAction = addUserAction; +module.exports = addUserAction; diff --git a/src/modules/dbRequests/checkUsername/checkUsername.js b/src/modules/dbRequests/checkUsername/checkUsername.js new file mode 100644 index 0000000..8cfc1f1 --- /dev/null +++ b/src/modules/dbRequests/checkUsername/checkUsername.js @@ -0,0 +1,11 @@ +const { USERS } = require("../mongo"); + +async function checkUsername({ email }) { + return USERS.findOne({ email }).then((user) => { + if (user) { + return user; + } else return false; + }); +} + +module.exports = checkUsername \ No newline at end of file diff --git a/src/modules/dbRequests/dbRequests.js b/src/modules/dbRequests/dbRequests.js index 02483ab..cff124b 100644 --- a/src/modules/dbRequests/dbRequests.js +++ b/src/modules/dbRequests/dbRequests.js @@ -1,20 +1,24 @@ -const { getUserInfo } = require("./getUserInfo/getUserInfo"); -const { getUserState } = require("./getUserState/getUserState"); -const { getUsersList } = require("./getUsersList/getUsersList"); -const { getModuleInfo } = require("./getModuleInfo/getModuleInfo"); -const { getTaskInfo } = require("./getTaskInfo/getTaskInfo"); -const { getStateInfo } = require("./getStateInfo/getStateInfo"); -const { getModulesList } = require("./getModulesList/getModulesList"); -const { getTasksList } = require("./getTasksList/getTasksList"); -const { setComment } = require("./setComment/setComment"); -const { setState } = require("./setState/setState"); -const { setControls } = require("./setControls/setControls"); -const { addUserAction } = require("./addUserAction/addUserAction"); -const { setUserInfo } = require("./setUserInfo/setUserInfo"); -const { getCounselor } = require("./getCounselor/getCounselor"); -const { getPaymentInfo } = require("./getPaymentInfo/getPaymentInfo"); -const { getDiploma } = require("./getDiploma/getDiploma"); -const { setDiploma } = require("./setDiploma/setDiploma"); +const getUserInfo = require("./getUserInfo/getUserInfo"); +const getUserState = require("./getUserState/getUserState"); +const getUsersList = require("./getUsersList/getUsersList"); +const getModuleInfo = require("./getModuleInfo/getModuleInfo"); +const getTaskInfo = require("./getTaskInfo/getTaskInfo"); +const getStateInfo = require("./getStateInfo/getStateInfo"); +const getModulesList = require("./getModulesList/getModulesList"); +const getTasksList = require("./getTasksList/getTasksList"); +const setComment = require("./setComment/setComment"); +const setState = require("./setState/setState"); +const setControls = require("./setControls/setControls"); +const addUserAction = require("./addUserAction/addUserAction"); +const setUserInfo = require("./setUserInfo/setUserInfo"); +const getCounselor = require("./getCounselor/getCounselor"); +const getPaymentInfo = require("./getPaymentInfo/getPaymentInfo"); +const getDiploma = require("./getDiploma/getDiploma"); +const setDiploma = require("./setDiploma/setDiploma"); +const checkUsername = require("./checkUsername/checkUsername") +const setPayment = require("./setPayment/setPayment"); +const addUser = require("./addUser/addUser"); + const REQUESTS = { getUserInfo, @@ -34,6 +38,9 @@ const REQUESTS = { getPaymentInfo, getDiploma, setDiploma, + checkUsername, + setPayment, + addUser }; function getDBRequest(type, params) { diff --git a/src/modules/dbRequests/getCounselor/getCounselor.js b/src/modules/dbRequests/getCounselor/getCounselor.js index df0cf46..f8972aa 100644 --- a/src/modules/dbRequests/getCounselor/getCounselor.js +++ b/src/modules/dbRequests/getCounselor/getCounselor.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { COUNSELOR } = require("../mongo"); function getCounselor({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getCounselor({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.COUNSELOR.findOne(query, { projection }); + return COUNSELOR.findOne(query, { projection }); } -module.exports.getCounselor = getCounselor; +module.exports = getCounselor; diff --git a/src/modules/dbRequests/getDiploma/getDiploma.js b/src/modules/dbRequests/getDiploma/getDiploma.js index 4544128..d8a0967 100644 --- a/src/modules/dbRequests/getDiploma/getDiploma.js +++ b/src/modules/dbRequests/getDiploma/getDiploma.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { CERTS } = require("../mongo"); function getDiploma({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getDiploma({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.CERTS.findOne(query, { projection }); + return CERTS.findOne(query, { projection }); } -module.exports.getDiploma = getDiploma; +module.exports = getDiploma; diff --git a/src/modules/dbRequests/getModuleInfo/getModuleInfo.js b/src/modules/dbRequests/getModuleInfo/getModuleInfo.js index a0a978c..8436741 100644 --- a/src/modules/dbRequests/getModuleInfo/getModuleInfo.js +++ b/src/modules/dbRequests/getModuleInfo/getModuleInfo.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { MODULES } = require("../mongo"); function getModuleInfo({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getModuleInfo({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.MODULES.findOne(query, { projection }); + return MODULES.findOne(query, { projection }); } -module.exports.getModuleInfo = getModuleInfo; +module.exports = getModuleInfo; diff --git a/src/modules/dbRequests/getModulesList/getModulesList.js b/src/modules/dbRequests/getModulesList/getModulesList.js index bc1be4e..99c6764 100644 --- a/src/modules/dbRequests/getModulesList/getModulesList.js +++ b/src/modules/dbRequests/getModulesList/getModulesList.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { MODULES } = require("../mongo"); function getModulesList({ query = {}, returns = [] }) { const projection = { @@ -7,9 +7,9 @@ function getModulesList({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.MODULES.find(query, { + return MODULES.find(query, { projection, }).toArray(); } -module.exports.getModulesList = getModulesList; +module.exports = getModulesList; diff --git a/src/modules/dbRequests/getPaymentInfo/getPaymentInfo.js b/src/modules/dbRequests/getPaymentInfo/getPaymentInfo.js index b3f9d66..bd1e138 100644 --- a/src/modules/dbRequests/getPaymentInfo/getPaymentInfo.js +++ b/src/modules/dbRequests/getPaymentInfo/getPaymentInfo.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { PAYMENTS } = require("../mongo"); function getPaymentInfo({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getPaymentInfo({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.PAYMENTS.findOne(query, { projection }); + return PAYMENTS.findOne(query, { projection }); } -module.exports.getPaymentInfo = getPaymentInfo; +module.exports = getPaymentInfo; diff --git a/src/modules/dbRequests/getStateInfo/getStateInfo.js b/src/modules/dbRequests/getStateInfo/getStateInfo.js index ca89ade..b29b4d6 100644 --- a/src/modules/dbRequests/getStateInfo/getStateInfo.js +++ b/src/modules/dbRequests/getStateInfo/getStateInfo.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { STATE } = require("../mongo"); function getStateInfo({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getStateInfo({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.STATE.findOne(query, { projection }); + return STATE.findOne(query, { projection }); } -module.exports.getStateInfo = getStateInfo; +module.exports = getStateInfo; diff --git a/src/modules/dbRequests/getTaskInfo/getTaskInfo.js b/src/modules/dbRequests/getTaskInfo/getTaskInfo.js index 3eb1be5..36f41ee 100644 --- a/src/modules/dbRequests/getTaskInfo/getTaskInfo.js +++ b/src/modules/dbRequests/getTaskInfo/getTaskInfo.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { TASKS } = require("../mongo"); function getTaskInfo({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getTaskInfo({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.TASKS.findOne(query, { projection }); + return TASKS.findOne(query, { projection }); } -module.exports.getTaskInfo = getTaskInfo; +module.exports = getTaskInfo; diff --git a/src/modules/dbRequests/getTasksList/getTasksList.js b/src/modules/dbRequests/getTasksList/getTasksList.js index 76c1ecf..2ee7f4e 100644 --- a/src/modules/dbRequests/getTasksList/getTasksList.js +++ b/src/modules/dbRequests/getTasksList/getTasksList.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { TASKS } = require("../mongo"); function getTasksList({ query = {}, returns = [] }) { const projection = { @@ -7,9 +7,9 @@ function getTasksList({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.TASKS.find(query, { + return TASKS.find(query, { projection, }).toArray(); } -module.exports.getTasksList = getTasksList; +module.exports = getTasksList; diff --git a/src/modules/dbRequests/getUserInfo/getUserInfo.js b/src/modules/dbRequests/getUserInfo/getUserInfo.js index 79a9357..1a2551b 100644 --- a/src/modules/dbRequests/getUserInfo/getUserInfo.js +++ b/src/modules/dbRequests/getUserInfo/getUserInfo.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { USERS } = require("../mongo"); function getUserInfo({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getUserInfo({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.USERS.findOne(query, { projection }); + return USERS.findOne(query, { projection }); } -module.exports.getUserInfo = getUserInfo; +module.exports = getUserInfo; diff --git a/src/modules/dbRequests/getUserState/getUserState.js b/src/modules/dbRequests/getUserState/getUserState.js index a082d31..1417bd6 100644 --- a/src/modules/dbRequests/getUserState/getUserState.js +++ b/src/modules/dbRequests/getUserState/getUserState.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { STATE } = require("../mongo"); function getUserState({ query = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function getUserState({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.STATE.find(query, { projection }).toArray(); + return STATE.find(query, { projection }).toArray(); } -module.exports.getUserState = getUserState; +module.exports = getUserState; diff --git a/src/modules/dbRequests/getUsersList/getUsersList.js b/src/modules/dbRequests/getUsersList/getUsersList.js index fa26275..2d46363 100644 --- a/src/modules/dbRequests/getUsersList/getUsersList.js +++ b/src/modules/dbRequests/getUsersList/getUsersList.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { USERS } = require("../mongo"); function getUsersList({ query = {}, returns = [] }) { const projection = { @@ -7,9 +7,9 @@ function getUsersList({ query = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.USERS.find(query, { + return USERS.find(query, { projection, }).toArray(); } -module.exports.getUsersList = getUsersList; +module.exports = getUsersList; diff --git a/src/modules/dbRequests/mongo.js b/src/modules/dbRequests/mongo.js index 92bf24b..c22dfb1 100644 --- a/src/modules/dbRequests/mongo.js +++ b/src/modules/dbRequests/mongo.js @@ -14,7 +14,7 @@ const CERTS = mongoClient.db(DB_NAME).collection("certs"); const PAYMENTS = mongoClient.db(DB_NAME).collection("payments"); const COUNSELOR = mongoClient.db(DB_NAME).collection("counselor"); -module.exports.db = { +module.exports = { USERS, TASKS, STATE, diff --git a/src/modules/dbRequests/setComment/setComment.js b/src/modules/dbRequests/setComment/setComment.js index 6000759..c39a6fe 100644 --- a/src/modules/dbRequests/setComment/setComment.js +++ b/src/modules/dbRequests/setComment/setComment.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { STATE } = require("../mongo"); function setComment({ query = {}, data = {}, protest = false, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function setComment({ query = {}, data = {}, protest = false, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.STATE.findOneAndUpdate( + return STATE.findOneAndUpdate( query, { $push: { @@ -29,4 +29,4 @@ function setComment({ query = {}, data = {}, protest = false, returns = [] }) { ); } -module.exports.setComment = setComment; +module.exports = setComment; diff --git a/src/modules/dbRequests/setControls/setControls.js b/src/modules/dbRequests/setControls/setControls.js index af09f45..ce331b3 100644 --- a/src/modules/dbRequests/setControls/setControls.js +++ b/src/modules/dbRequests/setControls/setControls.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { STATE } = require("../mongo"); function setControls({ query = {}, controlsState = {}, returns = [] }) { const isHintActive = controlsState?.isHintActive || false; @@ -10,7 +10,7 @@ function setControls({ query = {}, controlsState = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.STATE.findOneAndUpdate( + return STATE.findOneAndUpdate( query, { $set: { @@ -28,4 +28,4 @@ function setControls({ query = {}, controlsState = {}, returns = [] }) { ); } -module.exports.setControls = setControls; +module.exports = setControls; diff --git a/src/modules/dbRequests/setDiploma/setDiploma.js b/src/modules/dbRequests/setDiploma/setDiploma.js index 8b27cc0..ffc282f 100644 --- a/src/modules/dbRequests/setDiploma/setDiploma.js +++ b/src/modules/dbRequests/setDiploma/setDiploma.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { CERTS } = require("../mongo"); function setDiploma({ query = {}, data = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function setDiploma({ query = {}, data = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.CERTS.findOneAndUpdate( + return CERTS.findOneAndUpdate( query, { $set: data, @@ -21,4 +21,4 @@ function setDiploma({ query = {}, data = {}, returns = [] }) { ); } -module.exports.setDiploma = setDiploma; +module.exports = setDiploma; diff --git a/src/modules/dbRequests/setPayment/setPayment.js b/src/modules/dbRequests/setPayment/setPayment.js new file mode 100644 index 0000000..f564af7 --- /dev/null +++ b/src/modules/dbRequests/setPayment/setPayment.js @@ -0,0 +1,16 @@ +const { log } = require("../../../services/logger/logger"); +const { PAYMENTS } = require("../mongo"); + +function setPayment({ payment = {}, returns = [] }) { + const projection = { + _id: 0, + }; + for (const param of returns) { + projection[param] = 1; + } + return PAYMENTS.insertOne(payment, { + projection, + }); +} + +module.exports = setPayment; diff --git a/src/modules/dbRequests/setState/setState.js b/src/modules/dbRequests/setState/setState.js index cbc723d..4de28d4 100644 --- a/src/modules/dbRequests/setState/setState.js +++ b/src/modules/dbRequests/setState/setState.js @@ -1,4 +1,4 @@ -const { db } = require("../mongo"); +const { STATE } = require("../mongo"); function setState({ query = {}, state = {}, returns = [] }) { const projection = { @@ -7,7 +7,7 @@ function setState({ query = {}, state = {}, returns = [] }) { for (const param of returns) { projection[param] = 1; } - return db.STATE.findOneAndUpdate( + return STATE.findOneAndUpdate( query, { $set: state, @@ -21,4 +21,4 @@ function setState({ query = {}, state = {}, returns = [] }) { ); } -module.exports.setState = setState; +module.exports = setState; diff --git a/src/modules/dbRequests/setUserInfo/setUserInfo.js b/src/modules/dbRequests/setUserInfo/setUserInfo.js index ab7e0d6..6ed09c5 100644 --- a/src/modules/dbRequests/setUserInfo/setUserInfo.js +++ b/src/modules/dbRequests/setUserInfo/setUserInfo.js @@ -1,12 +1,22 @@ -const { db } = require("../mongo"); +const { USERS } = require("../mongo"); -async function setUserInfo({ email, data }) { - const user = await db.USERS.findOneAndUpdate( - { email }, +async function setUserInfo({ email, id, data }) { + const query = {} + + if (email) { + query.email = email + } + + if (id) { + query.id = id + } + + const user = await USERS.findOneAndUpdate( + query, { $set: data }, { upsert: false, returnDocument: "after" } ); return user.value; } -module.exports.setUserInfo = setUserInfo; +module.exports = setUserInfo; diff --git a/src/modules/userTokens/accessTokens.js b/src/modules/userTokens/accessTokens.js deleted file mode 100644 index 49877a5..0000000 --- a/src/modules/userTokens/accessTokens.js +++ /dev/null @@ -1,43 +0,0 @@ -function rand() { - return Math.random().toString(36).substring(2); -} - -function getToken() { - const accessToken = rand() + rand(); - const refreshToken = rand(); - const expiresIn = Math.floor(Date.now() / 1000 + 7200); - return { - accessToken, - expiresIn, - refreshToken, - }; -} - -function tokenMachine() { - const tokens = {}; - function checkToken(token) { - return tokens?.[token]; - } - - function setToken(user) { - const newToken = getToken(); - tokens[newToken.accessToken] = { - id: user?.id, - }; - return newToken; - } - - function checkList() { - return tokens; - } - return { - checkToken, - setToken, - checkList, - }; -} - -const accessTokens = tokenMachine(); - -module.exports.tokenMachine = tokenMachine; -module.exports.accessTokens = accessTokens; diff --git a/src/processes/authUser/authUser.js b/src/processes/authUser/authUser.js deleted file mode 100644 index cdc007a..0000000 --- a/src/processes/authUser/authUser.js +++ /dev/null @@ -1,29 +0,0 @@ -const DB = require("@mongo/requests"); -const { setToken } = require("@tokenMachine/tokenMachine"); - -/*** - * Function provides user's access token. - * If user pushed to request a UI language, change it too. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {Object} User data on success - */ -async function authUser(data) { - const { lang, user } = data; - - user.token = setToken(user); - - if (lang && lang !== user.lang) { - await DB.setOne("users", { - query: { email: user.email }, - set: { lang }, - }); - user.lang = lang; - } - - return user; -} - -module.exports = authUser; diff --git a/src/processes/checkAccess/checkAccess.js b/src/processes/checkAccess/checkAccess.js deleted file mode 100644 index a84d17e..0000000 --- a/src/processes/checkAccess/checkAccess.js +++ /dev/null @@ -1,43 +0,0 @@ -const { getModuleId } = require("@utils/idExtractor"); -const { calculateDeadline } = require("@utils/calculators"); - -function checkDate(start, deadline) { - if (!start && !deadline) return false; - const today = Date.now(); - return today >= Date.parse(start) && today < Date.parse(deadline); -} - -/** - * Check student access to the module data - * - * @param {Object} data Throught API object - * - * @returns {boolean} Check result - */ -function checkModuleAccess(data) { - const { user, moduleId, lessonId, taskId, questionId } = data; - - const cutModuleId = moduleId || getModuleId(lessonId || taskId || questionId); - - const startDate = user?.modules?.[cutModuleId]?.start; - const deadline = calculateDeadline(user?.modules?.[cutModuleId] || {}); - - return checkDate(startDate, deadline); -} - -/** - * Checks student access to the module final page and the certificate - * - * @param {Object} data Throught API object - * - * @returns {boolean} Check result - */ -function checkFinalAccess(data) { - const { user, moduleId, module } = data; - const modules = Object.keys(user?.modules); - const isFinalAccess = modules.includes(moduleId || module.code); - data.isFinalAccess = isFinalAccess; - return isFinalAccess; -} - -module.exports = { checkModuleAccess, checkFinalAccess }; diff --git a/src/processes/checkCredentials/checkCredentials.js b/src/processes/checkCredentials/checkCredentials.js deleted file mode 100644 index 1c3a340..0000000 --- a/src/processes/checkCredentials/checkCredentials.js +++ /dev/null @@ -1,24 +0,0 @@ -const { checkPass } = require("@utils/pass"); - -/*** - * Function checks user's credentials. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {boolean} Check result - */ -function checkCredentials(data, next) { - const { user, pass } = data; - - const isPassValid = checkPass(user, pass); - - if (!isPassValid) { - next({ code: 10102 }); - return false; - } - - return true; -} - -module.exports = checkCredentials; diff --git a/src/processes/checkOTK/checkOTK.js b/src/processes/checkOTK/checkOTK.js deleted file mode 100644 index 751a099..0000000 --- a/src/processes/checkOTK/checkOTK.js +++ /dev/null @@ -1,22 +0,0 @@ -const { checkKey } = require("@tokenMachine/OTK"); - -/*** - * Function checks one-time key for password change. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {boolean} Check result - */ -async function checkOTK(data, next) { - const { key } = data; - - const verify = await checkKey(key); - if (!verify) { - next({ code: 10105 }); - return false; - } - return true; -} - -module.exports = checkOTK; diff --git a/src/processes/checkTransaction/checkTransaction.js b/src/processes/checkTransaction/checkTransaction.js deleted file mode 100644 index 6abec4e..0000000 --- a/src/processes/checkTransaction/checkTransaction.js +++ /dev/null @@ -1,28 +0,0 @@ -const DB = require("../../services/mongo/requests"); - -/*** - * Function checks user transaction. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {boolean} Check result - */ -async function checkTransaction(data, next) { - const { paymentId } = data; - - const payment = await DB.getOne("payments", { - query: { paymentId }, - }); - - if (!payment) { - next({ code: 10106 }); - return false; - } - - data.email = payment.email; - - return true; -} - -module.exports = checkTransaction; diff --git a/src/processes/getCounselorInfo/getCounselorInfo.js b/src/processes/getCounselorInfo/getCounselorInfo.js deleted file mode 100644 index 767401f..0000000 --- a/src/processes/getCounselorInfo/getCounselorInfo.js +++ /dev/null @@ -1,27 +0,0 @@ -const DB = require("../../services/mongo/requests"); - -/*** - * Function provides data for student counselor. - * - * @param {Object} data Throught API object - * - * @returns {Object} Counselor data - */ -async function getCounselorInfo(data) { - const { lang } = data; - const query = { lang }; - - const counselor = await DB.getOne("counselor", { - query, - }); - - if (!counselor) { - throw new Error(`Can't find the counselor for language ${lang}`); - } - - data.counselor = counselor; - - return counselor; -} - -module.exports = getCounselorInfo; diff --git a/src/processes/getLessonInfo/getLessonInfo.js b/src/processes/getLessonInfo/getLessonInfo.js deleted file mode 100644 index ce6a4d4..0000000 --- a/src/processes/getLessonInfo/getLessonInfo.js +++ /dev/null @@ -1,39 +0,0 @@ -const DB = require("@mongo/requests"); -const { getModuleId, getLessonId } = require("@utils/idExtractor"); - -/*** - * Function provides data for module's specific lesson. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Lesson's data on success; undefined on fail - */ -async function getLessonInfo(data, next) { - const fullLessonId = data.lessonId; - - const moduleId = getModuleId(fullLessonId); - const lessonId = getLessonId(fullLessonId); - - const query = { code: moduleId }; - - const moduleData = await DB.getOne("modules", { query }); - - if (!moduleData) { - next({ code: 10302 }); - return; - } - - const lessonData = moduleData.lessons[lessonId]; - - if (!lessonData) { - next({ code: 10302 }); - return; - } - - data.lesson = lessonData; - - return lessonData; -} - -module.exports = getLessonInfo; diff --git a/src/processes/getModuleInfo/getModuleInfo.js b/src/processes/getModuleInfo/getModuleInfo.js deleted file mode 100644 index c2d3ed9..0000000 --- a/src/processes/getModuleInfo/getModuleInfo.js +++ /dev/null @@ -1,34 +0,0 @@ -const DB = require("@mongo/requests"); - -const DEMO = process.env.DEMO; - -/*** - * Function provides module's main data. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Lesson's data on success; undefined on fail - */ -async function getModuleInfo(data, next) { - const { moduleId } = data; - - if (moduleId) { - const query = { code: moduleId }; - const moduleData = await DB.getOne("modules", { query }); - if (!moduleData) { - next({ code: 10301 }); - return false; - } - data.module = moduleData; - return moduleData; - } else { - const modulesList = await DB.getMany("modules", { - query: DEMO ? {} : { active: true }, - }); - data.modulesList = modulesList; - return modulesList; - } -} - -module.exports = getModuleInfo; diff --git a/src/processes/getStateInfo/getStateInfo.js b/src/processes/getStateInfo/getStateInfo.js deleted file mode 100644 index b7fb702..0000000 --- a/src/processes/getStateInfo/getStateInfo.js +++ /dev/null @@ -1,27 +0,0 @@ -const DB = require("../../services/mongo/requests"); - -/*** - * Function provides state data for requested item (full module | lesson | task). - * - * @param {Object} data Throught API object - * - * @returns {Array | Object} State data for task or array of state data objects - */ -async function getStateInfo(data) { - const { user, moduleId, lessonId, taskId } = data; - const id = moduleId || lessonId || taskId; - const query = { - userId: user.id, - taskId: { $regex: `^${id}` }, - }; - - const stateData = taskId - ? await DB.getOne("state", { query }) - : await DB.getMany("state", { query }); - - data.state = stateData; - - return stateData; -} - -module.exports = getStateInfo; diff --git a/src/processes/getTaskInfo/getTaskInfo.js b/src/processes/getTaskInfo/getTaskInfo.js deleted file mode 100644 index 60b5274..0000000 --- a/src/processes/getTaskInfo/getTaskInfo.js +++ /dev/null @@ -1,30 +0,0 @@ -const DB = require("@mongo/requests"); - -/*** - * Function provides task data. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} Task data on success; undefined on fail - */ -async function getTaskInfo(data, next) { - const { taskId, returns } = data; - const query = { id: taskId }; - - const taskData = await DB.getOne("tasks", { - query, - returns, - }); - - if (!taskData) { - next({ code: 10303 }); - return; - } - - data.task = taskData; - - return taskData; -} - -module.exports = getTaskInfo; diff --git a/src/processes/getUserInfo/getUserInfo.js b/src/processes/getUserInfo/getUserInfo.js deleted file mode 100644 index 43485a1..0000000 --- a/src/processes/getUserInfo/getUserInfo.js +++ /dev/null @@ -1,34 +0,0 @@ -const DB = require("@mongo/requests"); - -/*** - * Function provides main user info. - * - * @param {Object} data Throught API object - * @param {Function} next Express middleware next function - * - * @returns {Object | undefined} User data on success; undefined in fail - */ -async function getUserInfo(data, next) { - const { email, userId } = data; - const query = { email, id: userId }; - Object.keys(query).forEach((key) => { - if (!query[key]) { - delete query[key]; - } - }); - - const user = await DB.getOne("users", { - query, - }); - - if (!user) { - next({ code: 10101 }); - return; - } - - data.user = user; - - return user; -} - -module.exports = getUserInfo; diff --git a/src/processes/prepareData/prepareCertificateData/certGenerator.js b/src/processes/prepareData/prepareCertificateData/certGenerator.js deleted file mode 100644 index e6180cf..0000000 --- a/src/processes/prepareData/prepareCertificateData/certGenerator.js +++ /dev/null @@ -1,23 +0,0 @@ -const sharp = require("sharp"); -const crypto = require("crypto"); -const fs = require("fs"); -const path = require("node:path"); -const { buildSvg } = require("svg-content"); - -async function createCert(data) { - const svg = fs.readFileSync(`./templates/diplomas/poster.svg`).toString(); - const processed = buildSvg(svg, data); - const fileId = crypto.createHash("sha256").update(data.certId).digest("hex"); - - const buffer = Buffer.from(processed); - const folderPath = path.resolve(`./diplomas/${fileId}`); - if (!fs.existsSync(folderPath)) { - fs.mkdirSync(`./diplomas/${fileId}`, { recursive: true }); - } - await sharp(buffer, { density: 150 }) - .png() - .toFile(folderPath + `/medium.png`); - return fileId; -} - -module.exports = createCert; diff --git a/src/processes/prepareData/prepareCertificateData/generateCertId.js b/src/processes/prepareData/prepareCertificateData/generateCertId.js deleted file mode 100644 index a140d22..0000000 --- a/src/processes/prepareData/prepareCertificateData/generateCertId.js +++ /dev/null @@ -1,36 +0,0 @@ -const DB = require("@mongo/requests"); - -function padder(number = 0, count = 1) { - return number.toString().padStart(count, "0"); -} - -async function generateCertId(userId, moduleId, startDate) { - const certsCount = - (await DB.count("certs", { query: { moduleId, startDate } })) || 0; - - const postfix = padder(certsCount + 1, 4); - const date = new Date(Date.parse(startDate)); - const datePart = `${padder(date.getFullYear() - 2000, 2)}${padder( - date.getMonth(), - 2 - )}`; - const certId = `${moduleId}${datePart}${postfix}`; - - DB.insertOne("certs", { - query: { - id: certId, - userId, - moduleId, - startDate, - public: false, - }, - }); - - const path = `modules.${moduleId}.certId`; - - DB.setOne("users", { query: { id: userId }, set: { [path]: certId } }); - - return certId; -} - -module.exports = generateCertId; diff --git a/src/processes/prepareData/prepareCertificateData/generateSkills.js b/src/processes/prepareData/prepareCertificateData/generateSkills.js deleted file mode 100644 index acd1d03..0000000 --- a/src/processes/prepareData/prepareCertificateData/generateSkills.js +++ /dev/null @@ -1,93 +0,0 @@ -const { getDBRequest } = require("../../../modules/dbRequests/dbRequests"); -const { SKILLS } = require("./skillsDict"); - -async function getMaxScore(taskId) { - const data = await getDBRequest("getTaskInfo", { - query: { id: taskId }, - returns: ["maxScore"], - }); - - return data?.maxScore || 0; -} - -async function getScore(userId, taskId) { - const data = await getDBRequest("getStateInfo", { - query: { - userId, - taskId, - }, - returns: ["score"], - }); - - return data?.score || 0; -} - -async function scoreIterator(userId, tasks = []) { - let score = 0; - let maxScore = 0; - - for (taskId of tasks) { - const taskScore = await getScore(userId, taskId); - const taskMaxScore = await getMaxScore(taskId); - - score += taskScore; - maxScore += taskMaxScore; - } - - return [score, maxScore]; -} - -async function getOneSkill(userId, skill) { - const skillData = { - name: skill.name, - }; - - let score = 0; - let maxScore = 0; - - if (!skill.subskills) { - [score, maxScore] = await scoreIterator(userId, skill.tasks); - } else { - const subskills = []; - for (subskill of skill.subskills) { - const subskillData = { - name: subskill.name, - code: subskill.code, - }; - - const [subskillScore, subskillMaxScore] = await scoreIterator( - userId, - subskill.tasks - ); - - const progress = Math.trunc((subskillScore / subskillMaxScore) * 100); - - subskillData.progress = progress; - - subskills.push(subskillData); - - score += subskillScore; - maxScore += subskillMaxScore; - } - - skillData.subskills = subskills; - } - - const progress = Math.trunc((score / maxScore) * 100); - - skillData.progress = progress; - - return skillData; -} - -async function generateSkills(moduleId, userId, lang) { - const skills = []; - - for (const skill of SKILLS[lang][moduleId] || []) { - skills.push(await getOneSkill(userId, skill)); - } - - return skills; -} - -module.exports = generateSkills; diff --git a/src/processes/prepareData/prepareCertificateData/moduleNameHyphenate.js b/src/processes/prepareData/prepareCertificateData/moduleNameHyphenate.js deleted file mode 100644 index 8e00642..0000000 --- a/src/processes/prepareData/prepareCertificateData/moduleNameHyphenate.js +++ /dev/null @@ -1,28 +0,0 @@ -const dict = { - ru: { - HSB: "Hard Skills начинающего\nруководителя", - MIO: "Информационные\nожидания", - TXT: "Несекретные\nсекреты текстов", - UCC: "Сценарии\nвзаимодействия", - COM: "Композиция\nв дизайне интерфейсов", - }, - en: { - HSB: "Novice Manager\nHard Skills", - MIO: "Informational\nexpectations", - TXT: "Non-secret\nSecrets of Text Writing", - UCC: "Use cases\nin Interface Design", - COM: "Composition\nin Interface Design", - }, -}; - -/** - * @param {string} lang - * @param {string} code - * @returns {string} - */ -function hyphenate(lang, code) { - if (dict[lang][code]) return dict[lang][code]; - throw new Error("Module name with this code wasn't found!"); -} - -module.exports = hyphenate; diff --git a/src/processes/prepareData/prepareCertificateData/prepareCertificateData.js b/src/processes/prepareData/prepareCertificateData/prepareCertificateData.js deleted file mode 100644 index 209860c..0000000 --- a/src/processes/prepareData/prepareCertificateData/prepareCertificateData.js +++ /dev/null @@ -1,120 +0,0 @@ -const setDiploma = require("../../setDiploma/setDiploma"); -const generateSkills = require("./generateSkills"); -const generateCertId = require("./generateCertId"); -const createCert = require("./certGenerator"); -const provideData = require("./provideData"); -const CyrillicToTranslit = require("cyrillic-to-translit-js"); - -async function prepareCertificateData(data) { - const cyrillicToTranslit = new CyrillicToTranslit(); - const { - userId, - moduleId, - params: { lang, isColor, isMascot, isProgress, isPublic } = {}, - user, - module, - state, - } = data; - - const params = { - lang: lang || undefined, - isColor: isColor ? isColor === "true" : undefined, - isMascot: isMascot ? isMascot === "true" : undefined, - isProgress: isProgress ? isProgress === "true" : undefined, - isPublic: isPublic ? isPublic === "true" : undefined, - }; - - for (const key of Object.keys(params)) { - if (params[key] === undefined) delete params[key]; - } - - const start = user?.modules?.[moduleId]?.deadline; - const deadline = user?.modules?.[moduleId]?.deadline; - const now = new Date(Date.now()).toISOString().split("T")[0]; - const certDate = Date.parse(deadline) < Date.parse(now) ? deadline : now; - - const certId = - user?.modules?.[moduleId]?.certId || - (await generateCertId(userId, moduleId, start)); - - const certData = await setDiploma({ - query: { id: certId }, - params, - returns: ["lang", "isColor", "isMascot", "isProgress", "isPublic"], - }); - - Object.assign(params, certData?.value || {}); - - if (params.lang === undefined) params.lang = module.lang; - if (params.isColor === undefined) params.isColor = false; - if (params.isMascot === undefined) params.isMascot = true; - if (params.isProgress === undefined) params.isProgress = true; - if (params.isPublic === undefined) params.isPublic = false; - - const firstName = - params.lang === "ru" - ? user.firstName - : cyrillicToTranslit.transform(user.firstName); - const lastName = - params.lang === "ru" - ? user.lastName - : cyrillicToTranslit.transform(user.lastName); - - const score = state.reduce( - (progress, value) => progress + (value?.score || 0), - 0 - ); - - const maxScore = Object.values(module?.lessons).reduce((sum, lesson) => { - return (sum += lesson.maxScore || 0); - }, 0); - - const doneTasks = state.reduce((progress, value) => { - if (value.isChecked) { - return ++progress; - } else return progress; - }, 0); - - const progress = Math.trunc((score / maxScore) * 100); - - const skills = await generateSkills( - moduleId, - userId, - params.lang || module.lang - ); - - const info = { - moduleId, - moduleName: module?.name, - firstName, - lastName, - certId, - certDate, - progress, - skills, - }; - - const fullInfo = provideData(info, params); - - const fileId = await createCert(fullInfo); - - return { - moduleId, - moduleName: module.name, - userId, - firstName, - lastName, - start, - deadline, - certDate, - certId, - score, - maxScore, - skills, - fileId, - doneTasks, - ...params, - }; -} - -module.exports = prepareCertificateData; diff --git a/src/processes/prepareData/prepareCertificateData/provideData.js b/src/processes/prepareData/prepareCertificateData/provideData.js deleted file mode 100644 index 85a5e7c..0000000 --- a/src/processes/prepareData/prepareCertificateData/provideData.js +++ /dev/null @@ -1,92 +0,0 @@ -const getPhrase = require("../../../assets/lang/lang"); -const colors = require("../../../assets/colors.json"); -const hyphenate = require("./moduleNameHyphenate"); - -const locales = { - ru: "ru-RU", - en: "en-US", -}; - -function getCertType(progress = 0) { - return progress >= 80 - ? "certType++" - : progress >= 60 - ? "certType+" - : "certType"; -} - -function processTexts(lang, moduleId, certType, certDate) { - const date = new Date(certDate || "2023-01-01") - .toLocaleDateString(locales[lang], { - day: "numeric", - month: "long", - year: "numeric", - }) - .replace(" г.", ""); - return { - moduleName: hyphenate(lang, moduleId), - certDescription: getPhrase(lang, "certDescription"), - certType: getPhrase(lang, certType), - certCheck1: getPhrase(lang, "certCheck1"), - certCheck2: getPhrase(lang, "certCheck2"), - certSignName: getPhrase(lang, "certSignName"), - certSignPos: getPhrase(lang, "certSignPos"), - certDate: date, - }; -} - -function processColors(moduleId, progress, params) { - const { isColor, isMascot, isProgress } = params; - const bgColor = isColor ? colors[moduleId]?.primary : "#FFFFFF"; - - let primaryColor; - if (moduleId === "MIO") { - primaryColor = isColor ? "#000000" : colors[moduleId]?.primary; - } else { - primaryColor = isColor ? "#FFFFFF" : colors[moduleId]?.primary; - } - - const textColor = !isColor || moduleId === "MIO" ? "#101010" : "#FFFFFF"; - const signColor = !isColor || moduleId === "MIO" ? "b" : "w"; - const skillOpacity = isProgress - ? isColor - ? moduleId === "MIO" - ? "0.6" - : "0.77" - : "1" - : "0"; - const mascotOpacity = - !isMascot || isColor ? "0" : progress >= 60 ? "1" : "0.3"; - const progressOpacity = isProgress ? (progress >= 60 ? "1" : "0.3") : "0"; - const headerOpacity = progress < 60 ? "0.1" : "1"; - - return { - bgColor, - primaryColor, - textColor, - signColor, - skillOpacity, - mascotOpacity, - progressOpacity, - headerOpacity, - }; -} - -function provideData(data, params) { - const { moduleId, progress, certDate } = data; - const { lang } = params; - - const certType = getCertType(progress); - - const textData = processTexts(lang, moduleId, certType, certDate); - const colorData = processColors(moduleId, progress, params); - - return { - ...data, - ...params, - ...textData, - ...colorData, - }; -} - -module.exports = provideData; diff --git a/src/processes/prepareData/prepareCertificateData/skillsDict.js b/src/processes/prepareData/prepareCertificateData/skillsDict.js deleted file mode 100644 index 2389e6d..0000000 --- a/src/processes/prepareData/prepareCertificateData/skillsDict.js +++ /dev/null @@ -1,2409 +0,0 @@ -const ru = { - MIO: [ - { - name: "теоретическая часть", - subskills: [ - { - code: "iBase", - name: "разбираться в основах", - tasks: [ - "MIO0104", - "MIO0105", - "MIO0107", - "MIO0108", - "MIO0207", - "MIO0302", - "MIO0303", - "MIO0306", - "MIO0706", - "MIO0710", - "MIO0712", - "MIO0713", - "MIO0714", - "MIO0717", - "MIO0803", - "MIO0804", - "MIO0805", - "MIO0807", - "MIO0809", - "MIO0810", - "MIO0811", - "MIO0812", - "MIO0814", - "MIO0815", - "MIO0816", - ], - }, - { - code: "iLook", - name: "видеть интерфейс глазами пользователя", - tasks: [ - "MIO0104", - "MIO0105", - "MIO0107", - "MIO0108", - "MIO0109", - "MIO0110", - "MIO0112", - "MIO0113", - "MIO0114", - "MIO0115", - "MIO0117", - "MIO0118", - "MIO0202", - "MIO0203", - "MIO0207", - "MIO0208", - "MIO0209", - "MIO0302", - "MIO0303", - "MIO0306", - "MIO0307", - "MIO0404", - "MIO0417", - "MIO0812", - "MIO0902", - "MIO0903", - "MIO0911", - ], - }, - { - code: "iDraw", - name: "рисовать интерфейс", - tasks: [ - "MIO0308", - "MIO00310", - "MIO0311", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0412", - "MIO0413", - "MIO0414", - "MIO0418", - "MIO0419", - "MIO0420", - "MIO0612", - "MIO0804", - "MIO0807", - "MIO0812", - "MIO0905", - ], - }, - { - code: "iTell", - name: "объяснять другим", - tasks: ["MIO0707", "MIO0909", "MIO0910", "MIO0911"], - }, - { - code: "iSelf", - name: "саморазвиваться", - tasks: ["MIO0715"], - }, - ], - }, - { - name: "думайте", - subskills: [ - { - code: "aZero", - name: "создавать требования с нуля", - tasks: [ - "MIO0302", - "MIO0303", - "MIO0509", - "MIO0711", - "MIO0712", - "MIO0713", - "MIO0714", - "MIO0804", - "MIO0807", - ], - }, - { - code: "aLook", - name: "понимать логику чужого интерфейса", - tasks: [ - "MIO0104", - "MIO0105", - "MIO0107", - "MIO0108", - "MIO0109", - "MIO0110", - "MIO0112", - "MIO0113", - "MIO0114", - "MIO0115", - "MIO0117", - "MIO0118", - "MIO0202", - "MIO0203", - "MIO0207", - "MIO0208", - "MIO0209", - "MIO0210", - "MIO0306", - "MIO0307", - "MIO0404", - ], - }, - { - code: "aImp", - name: "находить места для локальных улучшений", - tasks: [ - "MIO0204", - "MIO0205", - "MIO0304", - "MIO0305", - "MIO0312", - "MIO0504", - "MIO0505", - "MIO0507", - "MIO0508", - "MIO0509", - "MIO0603", - "MIO0604", - "MIO0605", - "MIO0606", - "MIO0607", - "MIO0609", - "MIO0610", - "MIO0611", - "MIO0612", - "MIO0613", - "MIO0614", - "MIO0702", - "MIO0703", - "MIO0707", - "MIO0708", - "MIO0709", - "MIO0711", - "MIO0712", - "MIO0713", - "MIO0714", - "MIO0903", - "MIO0906", - ], - }, - { - code: "aDraft", - name: "понимать, стоит ли доводить эскизы до ума", - tasks: [ - "MIO0112", - "MIO0113", - "MIO0208", - "MIO0209", - "MIO0210", - "MIO0307", - ], - }, - { - code: "aWork", - name: "понимать, будет ли интерфейс работать", - tasks: [ - "MIO0114", - "MIO0115", - "MIO0117", - "MIO0118", - "MIO0202", - "MIO0203", - "MIO0204", - "MIO0205", - "MIO0404", - "MIO0409", - ], - }, - { - code: "aFix", - name: "фиксировать приоритеты", - tasks: [ - "MIO0204", - "MIO0205", - "MIO0210", - "MIO0410", - "MIO0415", - "MIO0417", - "MIO0502", - "MIO0705", - "MIO0913", - "MIO0914", - "MIO0915", - ], - }, - { - code: "aLis", - name: "составлять список экранов", - tasks: ["MIO0109", "MIO0110", "MIO0706", "MIO0715", "MIO0904"], - }, - { - code: "aPlus", - name: "понимать, какие еще экраны нужны системе", - tasks: ["MIO0109", "MIO0110", "MIO0706", "MIO0715", "MIO0904"], - }, - ], - }, - { - name: "рисуйте", - subskills: [ - { - code: "pSketch", - name: "делать быстрые наброски", - tasks: [ - "MIO0308", - "MIO0310", - "MIO0311", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0412", - "MIO0413", - "MIO0414", - ], - }, - { - code: "pMain", - name: "рисовать только самое главное, без деталей", - tasks: ["MIO0913", "MIO0914", "MIO0915"], - }, - { - code: "pReq", - name: "рисовать чётко по требованиям", - tasks: [ - "MIO0308", - "MIO0310", - "MIO0311", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0612", - "MIO0709", - ], - }, - { - code: "pImp", - name: "рисовать небольшие улучшения в интерфейсе", - tasks: ["MIO0613", "MIO0614", "MIO0703", "MIO0708", "MIO0709"], - }, - { - code: "pStart", - name: "просто — начать — рисовать", - tasks: [ - "MIO0308", - "MIO0310", - "MIO0311", - "MIO0312", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0410", - "MIO0412", - "MIO0413", - "MIO0414", - "MIO0415", - "MIO0418", - "MIO0419", - "MIO0420", - "MIO0502", - "MIO0705", - "MIO0905", - "MIO0916", - ], - }, - ], - }, - { - name: "тестируйте", - subskills: [ - { - code: "tReq", - name: "уточнять требования", - tasks: [ - "MIO0503", - "MIO0504", - "MIO0505", - "MIO0507", - "MIO0508", - "MIO0707", - ], - }, - { - code: "tPic", - name: "тестировать интерфейс", - tasks: [ - "MIO0503", - "MIO0504", - "MIO0505", - "MIO0507", - "MIO0508", - "MIO0707", - "MIO0708", - "MIO0909", - ], - }, - ], - }, - { - name: "обсуждайте", - subskills: [ - { - code: "dCol", - name: "обсудить с коллегой", - tasks: ["MIO0503", "MIO0909", "MIO0910", "MIO0911"], - }, - { - code: "dTeam", - name: "обсудить в команде", - tasks: ["MIO0503", "MIO0909", "MIO0910", "MIO0911"], - }, - ], - }, - { - name: "организуйте", - subskills: [ - { - code: "mUser", - name: "работать для пользователя", - tasks: ["MIO0907", "MIO0915"], - }, - { - code: "mTask", - name: "поставить задачу дизайнеру, принять результат", - tasks: ["MIO0904", "MIO0909", "MIO0910"], - }, - { - code: "mDev", - name: "внедрить дизайн в IT-производство", - tasks: ["MIO0909", "MIO0910", "MIO0911"], - }, - { - code: "mSale", - name: "презентовать интерфейс", - tasks: ["MIO0715"], - }, - ], - }, - { - name: "бизнес", - subskills: [ - { - code: "bNew", - name: "нарисовать новый интерфейс", - tasks: [ - "MIO0412", - "MIO0413", - "MIO0414", - "MIO0418", - "MIO0419", - "MIO0420", - "MIO0910", - "MIO0916", - ], - }, - { - code: "bLeg", - name: "заботиться о старой системе", - tasks: [ - "MIO0613", - "MIO0604", - "MIO0605", - "MIO0606", - "MIO0607", - "MIO0609", - "MIO0610", - "MIO0611", - "MIO0612", - "MIO0613", - "MIO0614", - "MIO0702", - "MIO0703", - "MIO0906", - ], - }, - { - code: "bOK", - name: "делать нормально", - tasks: ["MIO0715"], - }, - ], - }, - ], - TXT: [ - { - name: "текст как рабочий навык", - tasks: [ - "TXT0103", - "TXT0104", - "TXT0106", - "TXT0107", - "TXT0108", - "TXT0109", - "TXT0111", - "TXT0112", - "TXT0113", - "TXT0114", - "TXT0115", - "TXT0116", - "TXT0119", - "TXT0120", - "TXT0121", - "TXT0122", - ], - }, - { - name: "интерфейсный текст", - tasks: [ - "TXT0202", - "TXT0204", - "TXT0205", - "TXT0207", - "TXT0208", - "TXT0209", - "TXT0210", - "TXT0211", - "TXT0212", - "TXT0214", - "TXT0215", - "TXT0216", - "TXT0218", - "TXT0220", - "TXT0222", - "TXT0223", - "TXT0224", - "TXT0226", - "TXT0227", - ], - }, - { - name: "командная работа", - tasks: [ - "TXT0303", - "TXT0304", - "TXT0305", - "TXT0306", - "TXT0307", - "TXT0308", - "TXT0309", - "TXT0311", - "TXT0312", - "TXT0313", - "TXT0314", - "TXT0316", - "TXT0317", - "TXT0318", - "TXT0319", - "TXT0320", - ], - }, - { - name: "понимание читателей", - tasks: [ - "TXT0402", - "TXT0403", - "TXT0404", - "TXT0405", - "TXT0406", - "TXT0408", - "TXT0409", - "TXT0410", - "TXT0411", - "TXT0412", - "TXT0413", - "TXT0414", - "TXT0416", - "TXT0417", - "TXT0418", - "TXT0420", - "TXT0421", - "TXT0422", - "TXT0423", - "TXT0424", - "TXT0426", - "TXT0427", - "TXT0428", - "TXT0429", - "TXT0431", - "TXT0502", - "TXT0503", - "TXT0505", - "TXT0506", - "TXT0508", - "TXT0509", - "TXT0510", - "TXT0511", - "TXT0513", - "TXT0514", - "TXT0516", - "TXT0517", - "TXT0518", - "TXT0519", - "TXT0521", - ], - }, - { - name: "механики быстрого улучшайзинга", - tasks: [ - "TXT0602", - "TXT0605", - "TXT0606", - "TXT0607", - "TXT0609", - "TXT0610", - "TXT0612", - "TXT0614", - "TXT0616", - "TXT0617", - "TXT0618", - "TXT0619", - "TXT0621", - "TXT0623", - "TXT0625", - "TXT0627", - "TXT0628", - ], - }, - { - name: "работа на уровне текста", - tasks: [ - "TXT0703", - "TXT0704", - "TXT0705", - "TXT0707", - "TXT0709", - "TXT0710", - "TXT0711", - "TXT0713", - "TXT0714", - "TXT0716", - "TXT0717", - "TXT0718", - "TXT0719", - "TXT0720", - "TXT0721", - "TXT0723", - "TXT0724", - ], - }, - { - name: "экстремальная писанина", - tasks: [ - "TXT0803", - "TXT0804", - "TXT0805", - "TXT0807", - "TXT0808", - "TXT0809", - "TXT0811", - "TXT0812", - "TXT0814", - "TXT0815", - "TXT0816", - "TXT0817", - "TXT0819", - "TXT0821", - "TXT0822", - "TXT0823", - "TXT0824", - "TXT0826", - "TXT0827", - "TXT0828", - "TXT0829", - "TXT0830", - "TXT0831", - "TXT0832", - "TXT0833", - ], - }, - { - name: "лайфхаки", - tasks: [ - "TXT0903", - "TXT0904", - "TXT0905", - "TXT0907", - "TXT0908", - "TXT0909", - "TXT0910", - "TXT0912", - "TXT0913", - "TXT0914", - "TXT0916", - "TXT0917", - "TXT0919", - "TXT0920", - "TXT0921", - ], - }, - ], - HSB: [ - { - name: "жонглирование навыками команды", - tasks: [ - "HSB0104", - "HSB0105", - "HSB0107", - "HSB0108", - "HSB0110", - "HSB0112", - "HSB0113", - "HSB0114", - "HSB0116", - "HSB0117", - "HSB0119", - "HSB0120", - "HSB0122", - "HSB0123", - "HSB0125", - "HSB0126", - "HSB0127", - ], - }, - { - name: "озадачивание подчинённых", - tasks: [ - "HSB0203", - "HSB0204", - "HSB0205", - "HSB0206", - "HSB0208", - "HSB0210", - "HSB0211", - "HSB0212", - "HSB0213", - "HSB0215", - "HSB0217", - "HSB0219", - "HSB0220", - "HSB0222", - "HSB0224", - "HSB0225", - ], - }, - { - name: "работа с конкретным сотрудником", - tasks: [ - "HSB0303", - "HSB0305", - "HSB0306", - "HSB0308", - "HSB0310", - "HSB0311", - "HSB0312", - "HSB0314", - "HSB0315", - "HSB0316", - "HSB0318", - "HSB0319", - "HSB0321", - "HSB0323", - "HSB0325", - "HSB0326", - "HSB0328", - "HSB0330", - "HSB0331", - "HSB0333", - ], - }, - { - name: "как стать начальником", - tasks: [ - "HSB0404", - "HSB0405", - "HSB0407", - "HSB0409", - "HSB0411", - "HSB0413", - "HSB0415", - "HSB0417", - "HSB0419", - "HSB0421", - "HSB0423", - "HSB0425", - "HSB0426", - "HSB0427", - ], - }, - { - name: "принуждение к простоте", - tasks: [ - "HSB0503", - "HSB0504", - "HSB0505", - "HSB0506", - "HSB0507", - "HSB0508", - "HSB0510", - "HSB0511", - "HSB0512", - "HSB0513", - "HSB0514", - "HSB0516", - "HSB0517", - "HSB0518", - "HSB0520", - ], - }, - { - name: "внедрение серебряных пуль", - tasks: [ - "HSB0602", - "HSB0603", - "HSB0604", - "HSB0606", - "HSB0607", - "HSB0609", - "HSB0611", - "HSB0613", - "HSB0614", - "HSB0616", - "HSB0618", - "HSB0620", - ], - }, - { - name: "культура переписки. Деньги", - tasks: [ - "HSB0702", - "HSB0703", - "HSB0704", - "HSB0705", - "HSB0706", - "HSB0707", - "HSB0708", - "HSB0709", - "HSB0711", - "HSB0712", - "HSB0714", - "HSB0715", - "HSB0716", - "HSB0717", - "HSB0719", - "HSB0721", - "HSB0722", - ], - }, - { - name: "навыки продалбывания задачи", - tasks: [ - "HSB0803", - "HSB0804", - "HSB0806", - "HSB0807", - "HSB0808", - "HSB0810", - "HSB0811", - "HSB0812", - "HSB0814", - "HSB0815", - "HSB0817", - "HSB0819", - "HSB0820", - ], - }, - { - name: "лайфхаки", - tasks: [ - "HSB0902", - "HSB0903", - "HSB0905", - "HSB0907", - "HSB0909", - "HSB0910", - "HSB0911", - "HSB0913", - "HSB0915", - "HSB0917", - "HSB0918", - "HSB0919", - ], - }, - ], - COM: [ - { - name: "ключевая задача интерфейсной композиции", - tasks: [ - "COM0102", - "COM0103", - "COM0104", - "COM0106", - "COM0107", - "COM0108", - "COM0109", - "COM0110", - "COM0111", - "COM0112", - "COM0114", - "COM0115", - "COM0116", - "COM0118", - "COM0119", - "COM0120", - "COM0121", - "COM0122", - "COM0124", - "COM0125", - "COM0126", - "COM0127", - "COM0128", - "COM0130", - "COM0131", - "COM0132", - ], - }, - { - name: "история в композиции интерфейса", - tasks: [ - "COM0203", - "COM0204", - "COM0205", - "COM0207", - "COM0210", - "COM0211", - "COM0212", - "COM0214", - "COM0215", - "COM0216", - "COM0218", - "COM0219", - "COM0220", - ], - }, - { - name: "ритм в композиции интерфейса", - tasks: [ - "COM0303", - "COM0305", - "COM0306", - "COM0308", - "COM0309", - "COM0311", - "COM0312", - "COM0314", - "COM0315", - "COM0317", - "COM0319", - "COM0321", - "COM0322", - "COM0323", - "COM0324", - "COM0325", - "COM0326", - "COM0328", - "COM0329", - "COM0331", - "COM0332", - "COM0333", - "COM0335", - ], - }, - { - name: "симметрия", - tasks: [ - "COM0402", - "COM0403", - "COM0404", - "COM0405", - "COM0407", - "COM0408", - "COM0409", - "COM0411", - "COM0412", - "COM0413", - "COM0414", - "COM0416", - "COM0418", - "COM0420", - "COM0421", - "COM0423", - "COM0424", - "COM0426", - "COM0427", - "COM0428", - "COM0431", - "COM0432", - "COM0433", - ], - }, - { - name: "контраст", - tasks: [ - "COM0502", - "COM0503", - "COM0504", - "COM0506", - "COM0507", - "COM0509", - "COM0511", - "COM0512", - "COM0514", - "COM0515", - "COM0517", - "COM0519", - "COM0520", - "COM0522", - "COM0524", - "COM0525", - "COM0527", - "COM0528", - "COM0530", - "COM0531", - "COM0532", - "COM0533", - ], - }, - { - name: "когнитивная композиция", - tasks: [ - "COM0603", - "COM0604", - "COM0607", - "COM0608", - "COM0613", - "COM0615", - "COM0617", - "COM0619", - "COM0621", - "COM0622", - "COM0625", - "COM0626", - "COM0627", - "COM0628", - ], - }, - { - name: "композиционная грамотность", - tasks: [ - "COM0704", - "COM0705", - "COM0707", - "COM0708", - "COM0710", - "COM0711", - "COM0713", - "COM0714", - "COM0716", - "COM0717", - "COM0718", - "COM0719", - ], - }, - { - name: "улучшение интерфейса", - tasks: [ - "COM0803", - "COM0804", - "COM0805", - "COM0807", - "COM0809", - "COM0810", - "COM0811", - "COM0814", - "COM0815", - "COM0816", - ], - }, - { - name: "лайфхаки", - tasks: [ - "COM0902", - "COM0903", - "COM0904", - "COM0905", - "COM0906", - "COM0908", - "COM0910", - "COM0911", - "COM0913", - "COM0915", - "COM0917", - ], - }, - ], - UCC: [ - { - name: "сценарная теория", - tasks: [ - "UCC0107", - "UCC0109", - "UCC0111", - "UCC0113", - "UCC0115", - "UCC0117", - "UCC0119", - "UCC0121", - "UCC0123", - "UCC0125", - ], - }, - { - name: "основы работы со сценариями", - tasks: [ - "UCC0203", - "UCC0205", - "UCC0209", - "UCC0211", - "UCC0213", - "UCC0215", - "UCC0217", - "UCC0219", - "UCC0221", - "UCC0223", - "UCC0225", - "UCC0226", - "UCC0228", - "UCC0230", - "UCC0232", - "UCC0234", - ], - }, - { - name: "управление дизайном и разработкой", - tasks: [ - "UCC0303", - "UCC0304", - "UCC0305", - "UCC0307", - "UCC0308", - "UCC0309", - "UCC0311", - "UCC0312", - "UCC0313", - "UCC0314", - "UCC0315", - "UCC0316", - "UCC0317", - "UCC0318", - ], - }, - { - name: "создание интерфейсов по сценариям", - tasks: [ - "UCC0403", - "UCC0404", - "UCC0405", - "UCC0406", - "UCC0407", - "UCC0408", - "UCC0409", - "UCC0410", - "UCC0411", - "UCC0413", - "UCC0415", - "UCC0416", - "UCC0417", - "UCC0418", - "UCC0419", - "UCC0421", - "UCC0422", - "UCC0423", - "UCC0424", - "UCC0426", - "UCC0502", - "UCC0503", - "UCC0504", - "UCC0505", - "UCC0508", - "UCC0509", - "UCC0510", - "UCC0511", - "UCC0513", - "UCC0514", - "UCC0515", - "UCC0602", - "UCC0604", - "UCC0606", - "UCC0608", - "UCC0609", - "UCC0610", - "UCC0611", - "UCC0612", - "UCC0614", - "UCC0615", - "UCC0616", - "UCC0617", - ], - }, - { - name: "метазадачи", - tasks: [ - "UCC0703", - "UCC0704", - "UCC0705", - "UCC0706", - "UCC0707", - "UCC0708", - "UCC0710", - "UCC0711", - "UCC0713", - "UCC0714", - "UCC0715", - "UCC0716", - "UCC0717", - "UCC0718", - "UCC0719", - "UCC0721", - "UCC0722", - "UCC0723", - "UCC0724", - ], - }, - { - name: "логика сценарных интерфейсов", - tasks: [ - "UCC0803", - "UCC0805", - "UCC0806", - "UCC0808", - "UCC0809", - "UCC0810", - "UCC0812", - "UCC0813", - "UCC0814", - "UCC0816", - "UCC0817", - "UCC0818", - "UCC0819", - "UCC0821", - "UCC0822", - "UCC0823", - "UCC0825", - "UCC0826", - ], - }, - { - name: "нюансы и задачи развития", - tasks: [ - "UCC0903", - "UCC0904", - "UCC0905", - "UCC0906", - "UCC0908", - "UCC0909", - "UCC0910", - "UCC0911", - "UCC0913", - "UCC0914", - "UCC0915", - "UCC0916", - "UCC0917", - "UCC0919", - "UCC0920", - "UCC0921", - "UCC0922", - "UCC0924", - "UCC0925", - "UCC0927", - "UCC0928", - "UCC0930", - "UCC0931", - "UCC0932", - "UCC0933", - ], - }, - ], -}; - -const en = { - MIO: [ - { - name: "Theory", - subskills: [ - { - code: "iBase", - name: "understand the basics", - tasks: [ - "MIO0104", - "MIO0105", - "MIO0107", - "MIO0108", - "MIO0207", - "MIO0302", - "MIO0303", - "MIO0306", - "MIO0706", - "MIO0710", - "MIO0712", - "MIO0713", - "MIO0714", - "MIO0717", - "MIO0803", - "MIO0804", - "MIO0805", - "MIO0807", - "MIO0809", - "MIO0810", - "MIO0811", - "MIO0812", - "MIO0814", - "MIO0815", - "MIO0816", - ], - }, - { - code: "iLook", - name: "see the interface through the eyes of the user", - tasks: [ - "MIO0104", - "MIO0105", - "MIO0107", - "MIO0108", - "MIO0109", - "MIO0110", - "MIO0112", - "MIO0113", - "MIO0114", - "MIO0115", - "MIO0117", - "MIO0118", - "MIO0202", - "MIO0203", - "MIO0207", - "MIO0208", - "MIO0209", - "MIO0302", - "MIO0303", - "MIO0306", - "MIO0307", - "MIO0404", - "MIO0417", - "MIO0812", - "MIO0902", - "MIO0903", - "MIO0911", - ], - }, - { - code: "iDraw", - name: "draw interface", - tasks: [ - "MIO0308", - "MIO00310", - "MIO0311", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0412", - "MIO0413", - "MIO0414", - "MIO0418", - "MIO0419", - "MIO0420", - "MIO0612", - "MIO0804", - "MIO0807", - "MIO0812", - "MIO0905", - ], - }, - { - code: "iTell", - name: "explain to others", - tasks: ["MIO0707", "MIO0909", "MIO0910", "MIO0911"], - }, - { - code: "iSelf", - name: "self-develop", - tasks: ["MIO0715"], - }, - ], - }, - { - name: "Think", - subskills: [ - { - code: "aZero", - name: "create requirements from scratch", - tasks: [ - "MIO0302", - "MIO0303", - "MIO0509", - "MIO0711", - "MIO0712", - "MIO0713", - "MIO0714", - "MIO0804", - "MIO0807", - ], - }, - { - code: "aLook", - name: "to understand the logic of another's interface", - tasks: [ - "MIO0104", - "MIO0105", - "MIO0107", - "MIO0108", - "MIO0109", - "MIO0110", - "MIO0112", - "MIO0113", - "MIO0114", - "MIO0115", - "MIO0117", - "MIO0118", - "MIO0202", - "MIO0203", - "MIO0207", - "MIO0208", - "MIO0209", - "MIO0210", - "MIO0306", - "MIO0307", - "MIO0404", - ], - }, - { - code: "aImp", - name: "find places for local improvements", - tasks: [ - "MIO0204", - "MIO0205", - "MIO0304", - "MIO0305", - "MIO0312", - "MIO0504", - "MIO0505", - "MIO0507", - "MIO0508", - "MIO0509", - "MIO0603", - "MIO0604", - "MIO0605", - "MIO0606", - "MIO0607", - "MIO0609", - "MIO0610", - "MIO0611", - "MIO0612", - "MIO0613", - "MIO0614", - "MIO0702", - "MIO0703", - "MIO0707", - "MIO0708", - "MIO0709", - "MIO0711", - "MIO0712", - "MIO0713", - "MIO0714", - "MIO0903", - "MIO0906", - ], - }, - { - code: "aDraft", - name: "understand whether it is worth to polish the sketches", - tasks: [ - "MIO0112", - "MIO0113", - "MIO0208", - "MIO0209", - "MIO0210", - "MIO0307", - ], - }, - { - code: "aWork", - name: "understand if the interface will work", - tasks: [ - "MIO0114", - "MIO0115", - "MIO0117", - "MIO0118", - "MIO0202", - "MIO0203", - "MIO0204", - "MIO0205", - "MIO0404", - "MIO0409", - ], - }, - { - code: "aFix", - name: "fix priorities", - tasks: [ - "MIO0204", - "MIO0205", - "MIO0210", - "MIO0410", - "MIO0415", - "MIO0417", - "MIO0502", - "MIO0705", - "MIO0913", - "MIO0914", - "MIO0915", - ], - }, - { - code: "aLis", - name: "list screens", - tasks: ["MIO0109", "MIO0110", "MIO0706", "MIO0715", "MIO0904"], - }, - { - code: "aPlus", - name: "understand what other screens the system needs", - tasks: ["MIO0109", "MIO0110", "MIO0706", "MIO0715", "MIO0904"], - }, - ], - }, - { - name: "Sketch", - subskills: [ - { - code: "pSketch", - name: "make quick sketches", - tasks: [ - "MIO0308", - "MIO0310", - "MIO0311", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0412", - "MIO0413", - "MIO0414", - ], - }, - { - code: "pMain", - name: "draw the important, without details", - tasks: ["MIO0913", "MIO0914", "MIO0915"], - }, - { - code: "pReq", - name: "draw according to requirements", - tasks: [ - "MIO0308", - "MIO0310", - "MIO0311", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0612", - "MIO0709", - ], - }, - { - code: "pImp", - name: "draw small improvements in the interface", - tasks: ["MIO0613", "MIO0614", "MIO0703", "MIO0708", "MIO0709"], - }, - { - code: "pStart", - name: "just. start. drawing.", - tasks: [ - "MIO0308", - "MIO0310", - "MIO0311", - "MIO0312", - "MIO0405", - "MIO0406", - "MIO0408", - "MIO0410", - "MIO0412", - "MIO0413", - "MIO0414", - "MIO0415", - "MIO0418", - "MIO0419", - "MIO0420", - "MIO0502", - "MIO0705", - "MIO0905", - "MIO0916", - ], - }, - ], - }, - { - name: "Test", - subskills: [ - { - code: "tReq", - name: "clarify requirements", - tasks: [ - "MIO0503", - "MIO0504", - "MIO0505", - "MIO0507", - "MIO0508", - "MIO0707", - ], - }, - { - code: "tPic", - name: "test interface", - tasks: [ - "MIO0503", - "MIO0504", - "MIO0505", - "MIO0507", - "MIO0508", - "MIO0707", - "MIO0708", - "MIO0909", - ], - }, - ], - }, - { - name: "Discuss", - subskills: [ - { - code: "dCol", - name: "discuss with a colleague", - tasks: ["MIO0503", "MIO0909", "MIO0910", "MIO0911"], - }, - { - code: "dTeam", - name: "discuss in a team", - tasks: ["MIO0503", "MIO0909", "MIO0910", "MIO0911"], - }, - ], - }, - { - name: "Organize", - subskills: [ - { - code: "mUser", - name: "work for the user", - tasks: ["MIO0907", "MIO0915"], - }, - { - code: "mTask", - name: "set a task for the designer, accept the result", - tasks: ["MIO0904", "MIO0909", "MIO0910"], - }, - { - code: "mDev", - name: "implement design in IT production", - tasks: ["MIO0909", "MIO0910", "MIO0911"], - }, - { - code: "mSale", - name: "present interface", - tasks: ["MIO0715"], - }, - ], - }, - { - name: "Business", - subskills: [ - { - code: "bNew", - name: "draw a new interface", - tasks: [ - "MIO0412", - "MIO0413", - "MIO0414", - "MIO0418", - "MIO0419", - "MIO0420", - "MIO0910", - "MIO0916", - ], - }, - { - code: "bLeg", - name: "take care of the old system", - tasks: [ - "MIO0613", - "MIO0604", - "MIO0605", - "MIO0606", - "MIO0607", - "MIO0609", - "MIO0610", - "MIO0611", - "MIO0612", - "MIO0613", - "MIO0614", - "MIO0702", - "MIO0703", - "MIO0906", - ], - }, - { - code: "bOK", - name: "do okay", - tasks: ["MIO0715"], - }, - ], - }, - ], - TXT: [ - { - name: "Writing as a work skill", - tasks: [ - "TXT0103", - "TXT0104", - "TXT0106", - "TXT0107", - "TXT0108", - "TXT0109", - "TXT0111", - "TXT0112", - "TXT0113", - "TXT0114", - "TXT0115", - "TXT0116", - "TXT0119", - "TXT0120", - "TXT0121", - "TXT0122", - ], - }, - { - name: "Interface Text", - tasks: [ - "TXT0202", - "TXT0204", - "TXT0205", - "TXT0207", - "TXT0208", - "TXT0209", - "TXT0210", - "TXT0211", - "TXT0212", - "TXT0214", - "TXT0215", - "TXT0216", - "TXT0218", - "TXT0220", - "TXT0222", - "TXT0223", - "TXT0224", - "TXT0226", - "TXT0227", - ], - }, - { - name: "Team work", - tasks: [ - "TXT0303", - "TXT0304", - "TXT0305", - "TXT0306", - "TXT0307", - "TXT0308", - "TXT0309", - "TXT0311", - "TXT0312", - "TXT0313", - "TXT0314", - "TXT0316", - "TXT0317", - "TXT0318", - "TXT0319", - "TXT0320", - ], - }, - { - name: "Understand your reader", - tasks: [ - "TXT0402", - "TXT0403", - "TXT0404", - "TXT0405", - "TXT0406", - "TXT0408", - "TXT0409", - "TXT0410", - "TXT0411", - "TXT0412", - "TXT0413", - "TXT0414", - "TXT0416", - "TXT0417", - "TXT0418", - "TXT0420", - "TXT0421", - "TXT0422", - "TXT0423", - "TXT0424", - "TXT0426", - "TXT0427", - "TXT0428", - "TXT0429", - "TXT0431", - "TXT0502", - "TXT0503", - "TXT0505", - "TXT0506", - "TXT0508", - "TXT0509", - "TXT0510", - "TXT0511", - "TXT0513", - "TXT0514", - "TXT0516", - "TXT0517", - "TXT0518", - "TXT0519", - "TXT0521", - ], - }, - { - name: "Quick Improvement Techniques", - tasks: [ - "TXT0602", - "TXT0605", - "TXT0606", - "TXT0607", - "TXT0609", - "TXT0610", - "TXT0612", - "TXT0614", - "TXT0616", - "TXT0617", - "TXT0618", - "TXT0619", - "TXT0621", - "TXT0623", - "TXT0625", - "TXT0627", - "TXT0628", - ], - }, - { - name: "Working on Text Level", - tasks: [ - "TXT0703", - "TXT0704", - "TXT0705", - "TXT0707", - "TXT0709", - "TXT0710", - "TXT0711", - "TXT0713", - "TXT0714", - "TXT0716", - "TXT0717", - "TXT0718", - "TXT0719", - "TXT0720", - "TXT0721", - "TXT0723", - "TXT0724", - ], - }, - { - name: "Extreme writing", - tasks: [ - "TXT0803", - "TXT0804", - "TXT0805", - "TXT0807", - "TXT0808", - "TXT0809", - "TXT0811", - "TXT0812", - "TXT0814", - "TXT0815", - "TXT0816", - "TXT0817", - "TXT0819", - "TXT0821", - "TXT0822", - "TXT0823", - "TXT0824", - "TXT0826", - "TXT0827", - "TXT0828", - "TXT0829", - "TXT0830", - "TXT0831", - "TXT0832", - "TXT0833", - ], - }, - { - name: "Lifehacks", - tasks: [ - "TXT0903", - "TXT0904", - "TXT0905", - "TXT0907", - "TXT0908", - "TXT0909", - "TXT0910", - "TXT0912", - "TXT0913", - "TXT0914", - "TXT0916", - "TXT0917", - "TXT0919", - "TXT0920", - "TXT0921", - ], - }, - ], - HSB: [ - { - name: "Team’s skills juggling", - tasks: [ - "HSB0104", - "HSB0105", - "HSB0107", - "HSB0108", - "HSB0110", - "HSB0112", - "HSB0113", - "HSB0114", - "HSB0116", - "HSB0117", - "HSB0119", - "HSB0120", - "HSB0122", - "HSB0123", - "HSB0125", - "HSB0126", - "HSB0127", - ], - }, - { - name: "Assigning tasks to the team", - tasks: [ - "HSB0203", - "HSB0204", - "HSB0205", - "HSB0206", - "HSB0208", - "HSB0210", - "HSB0211", - "HSB0212", - "HSB0213", - "HSB0215", - "HSB0217", - "HSB0219", - "HSB0220", - "HSB0222", - "HSB0224", - "HSB0225", - ], - }, - { - name: "Individual work with team members", - tasks: [ - "HSB0303", - "HSB0305", - "HSB0306", - "HSB0308", - "HSB0310", - "HSB0311", - "HSB0312", - "HSB0314", - "HSB0315", - "HSB0316", - "HSB0318", - "HSB0319", - "HSB0321", - "HSB0323", - "HSB0325", - "HSB0326", - "HSB0328", - "HSB0330", - "HSB0331", - "HSB0333", - ], - }, - { - name: "How to become a boss", - tasks: [ - "HSB0404", - "HSB0405", - "HSB0407", - "HSB0409", - "HSB0411", - "HSB0413", - "HSB0415", - "HSB0417", - "HSB0419", - "HSB0421", - "HSB0423", - "HSB0425", - "HSB0426", - "HSB0427", - ], - }, - { - name: "Coercion to simplicity", - tasks: [ - "HSB0503", - "HSB0504", - "HSB0505", - "HSB0506", - "HSB0507", - "HSB0508", - "HSB0510", - "HSB0511", - "HSB0512", - "HSB0513", - "HSB0514", - "HSB0516", - "HSB0517", - "HSB0518", - "HSB0520", - ], - }, - { - name: "Silver bullets implementation", - tasks: [ - "HSB0602", - "HSB0603", - "HSB0604", - "HSB0606", - "HSB0607", - "HSB0609", - "HSB0611", - "HSB0613", - "HSB0614", - "HSB0616", - "HSB0618", - "HSB0620", - ], - }, - { - name: "Culture of written communication. Money", - tasks: [ - "HSB0702", - "HSB0703", - "HSB0704", - "HSB0705", - "HSB0706", - "HSB0707", - "HSB0708", - "HSB0709", - "HSB0711", - "HSB0712", - "HSB0714", - "HSB0715", - "HSB0716", - "HSB0717", - "HSB0719", - "HSB0721", - "HSB0722", - ], - }, - { - name: "Task failing skills", - tasks: [ - "HSB0803", - "HSB0804", - "HSB0806", - "HSB0807", - "HSB0808", - "HSB0810", - "HSB0811", - "HSB0812", - "HSB0814", - "HSB0815", - "HSB0817", - "HSB0819", - "HSB0820", - ], - }, - { - name: "Lifehaсks", - tasks: [ - "HSB0902", - "HSB0903", - "HSB0905", - "HSB0907", - "HSB0909", - "HSB0910", - "HSB0911", - "HSB0913", - "HSB0915", - "HSB0917", - "HSB0918", - "HSB0919", - ], - }, - ], - COM: [ - { - name: "Key goal of interface composition", - tasks: [ - "COM0102", - "COM0103", - "COM0104", - "COM0106", - "COM0107", - "COM0108", - "COM0109", - "COM0110", - "COM0111", - "COM0112", - "COM0114", - "COM0115", - "COM0116", - "COM0118", - "COM0119", - "COM0120", - "COM0121", - "COM0122", - "COM0124", - "COM0125", - "COM0126", - "COM0127", - "COM0128", - "COM0130", - "COM0131", - "COM0132", - ], - }, - { - name: "Story in interface composition", - tasks: [ - "COM0203", - "COM0204", - "COM0205", - "COM0207", - "COM0210", - "COM0211", - "COM0212", - "COM0214", - "COM0215", - "COM0216", - "COM0218", - "COM0219", - "COM0220", - ], - }, - { - name: "Rhythm", - tasks: [ - "COM0303", - "COM0305", - "COM0306", - "COM0308", - "COM0309", - "COM0311", - "COM0312", - "COM0314", - "COM0315", - "COM0317", - "COM0319", - "COM0321", - "COM0322", - "COM0323", - "COM0324", - "COM0325", - "COM0326", - "COM0328", - "COM0329", - "COM0331", - "COM0332", - "COM0333", - "COM0335", - ], - }, - { - name: "Symmetry", - tasks: [ - "COM0402", - "COM0403", - "COM0404", - "COM0405", - "COM0407", - "COM0408", - "COM0409", - "COM0411", - "COM0412", - "COM0413", - "COM0414", - "COM0416", - "COM0418", - "COM0420", - "COM0421", - "COM0423", - "COM0424", - "COM0426", - "COM0427", - "COM0428", - "COM0431", - "COM0432", - "COM0433", - ], - }, - { - name: "Contrast", - tasks: [ - "COM0502", - "COM0503", - "COM0504", - "COM0506", - "COM0507", - "COM0509", - "COM0511", - "COM0512", - "COM0514", - "COM0515", - "COM0517", - "COM0519", - "COM0520", - "COM0522", - "COM0524", - "COM0525", - "COM0527", - "COM0528", - "COM0530", - "COM0531", - "COM0532", - "COM0533", - ], - }, - { - name: "Cognitive composition", - tasks: [ - "COM0603", - "COM0604", - "COM0607", - "COM0608", - "COM0613", - "COM0615", - "COM0617", - "COM0619", - "COM0621", - "COM0622", - "COM0625", - "COM0626", - "COM0627", - "COM0628", - ], - }, - { - name: "Compositional literacy", - tasks: [ - "COM0704", - "COM0705", - "COM0707", - "COM0708", - "COM0710", - "COM0711", - "COM0713", - "COM0714", - "COM0716", - "COM0717", - "COM0718", - "COM0719", - ], - }, - { - name: "Interface Improvement", - tasks: [ - "COM0803", - "COM0804", - "COM0805", - "COM0807", - "COM0809", - "COM0810", - "COM0811", - "COM0814", - "COM0815", - "COM0816", - ], - }, - { - name: "Lifehaсks", - tasks: [ - "COM0902", - "COM0903", - "COM0904", - "COM0905", - "COM0906", - "COM0908", - "COM0910", - "COM0911", - "COM0913", - "COM0915", - "COM0917", - ], - }, - ], - UCC: [ - { - name: "Theory", - tasks: [ - "UCC0107", - "UCC0109", - "UCC0111", - "UCC0113", - "UCC0115", - "UCC0117", - "UCC0119", - "UCC0121", - "UCC0123", - "UCC0125", - ], - }, - { - name: "Basics", - tasks: [ - "UCC0203", - "UCC0205", - "UCC0209", - "UCC0211", - "UCC0213", - "UCC0215", - "UCC0217", - "UCC0219", - "UCC0221", - "UCC0223", - "UCC0225", - "UCC0226", - "UCC0228", - "UCC0230", - "UCC0232", - "UCC0234", - ], - }, - { - name: "Design and development management", - tasks: [ - "UCC0303", - "UCC0304", - "UCC0305", - "UCC0307", - "UCC0308", - "UCC0309", - "UCC0311", - "UCC0312", - "UCC0313", - "UCC0314", - "UCC0315", - "UCC0316", - "UCC0317", - "UCC0318", - ], - }, - { - name: "Interfaces creation based on use cases", - tasks: [ - "UCC0403", - "UCC0404", - "UCC0405", - "UCC0406", - "UCC0407", - "UCC0408", - "UCC0409", - "UCC0410", - "UCC0411", - "UCC0413", - "UCC0415", - "UCC0416", - "UCC0417", - "UCC0418", - "UCC0419", - "UCC0421", - "UCC0422", - "UCC0423", - "UCC0424", - "UCC0426", - "UCC0502", - "UCC0503", - "UCC0504", - "UCC0505", - "UCC0508", - "UCC0509", - "UCC0510", - "UCC0511", - "UCC0513", - "UCC0514", - "UCC0515", - "UCC0602", - "UCC0604", - "UCC0606", - "UCC0608", - "UCC0609", - "UCC0610", - "UCC0611", - "UCC0612", - "UCC0614", - "UCC0615", - "UCC0616", - "UCC0617", - ], - }, - { - name: "Metagoals", - tasks: [ - "UCC0703", - "UCC0704", - "UCC0705", - "UCC0706", - "UCC0707", - "UCC0708", - "UCC0710", - "UCC0711", - "UCC0713", - "UCC0714", - "UCC0715", - "UCC0716", - "UCC0717", - "UCC0718", - "UCC0719", - "UCC0721", - "UCC0722", - "UCC0723", - "UCC0724", - ], - }, - { - name: "Case based scenarios logic", - tasks: [ - "UCC0803", - "UCC0805", - "UCC0806", - "UCC0808", - "UCC0809", - "UCC0810", - "UCC0812", - "UCC0813", - "UCC0814", - "UCC0816", - "UCC0817", - "UCC0818", - "UCC0819", - "UCC0821", - "UCC0822", - "UCC0823", - "UCC0825", - "UCC0826", - ], - }, - { - name: "Nuances and goals of development", - tasks: [ - "UCC0903", - "UCC0904", - "UCC0905", - "UCC0906", - "UCC0908", - "UCC0909", - "UCC0910", - "UCC0911", - "UCC0913", - "UCC0914", - "UCC0915", - "UCC0916", - "UCC0917", - "UCC0919", - "UCC0920", - "UCC0921", - "UCC0922", - "UCC0924", - "UCC0925", - "UCC0927", - "UCC0928", - "UCC0930", - "UCC0931", - "UCC0932", - "UCC0933", - ], - }, - ], -}; - -module.exports.SKILLS = { ru, en }; diff --git a/src/processes/prepareData/prepareData.js b/src/processes/prepareData/prepareData.js deleted file mode 100644 index 7033580..0000000 --- a/src/processes/prepareData/prepareData.js +++ /dev/null @@ -1,15 +0,0 @@ -const prepareUserData = require("./prepareUserData/prepareUserData"); -const prepareModuleData = require("./prepareModuleData/prepareModuleData"); -const prepareLessonData = require("./prepareLessonData/prepareLessonData"); -const prapareTaskData = require("./prepareTaskData/prapareTaskData"); -const prepareCertificateData = require("./prepareCertificateData/prepareCertificateData"); - -const prepareData = { - prepareUserData, - prepareModuleData, - prepareLessonData, - prapareTaskData, - prepareCertificateData, -}; - -module.exports = prepareData; diff --git a/src/processes/prepareData/prepareLessonData/prepareLessonData.js b/src/processes/prepareData/prepareLessonData/prepareLessonData.js deleted file mode 100644 index 669c42d..0000000 --- a/src/processes/prepareData/prepareLessonData/prepareLessonData.js +++ /dev/null @@ -1,52 +0,0 @@ -const { setTasksState } = require("../../setListState/setListState"); -const { - calculateUserScore, - calculateDoneTasks, -} = require("@utils/calculators"); -const { getLessonId } = require("@utils/idExtractor"); - -/*** - * Function prepares lesson data to send to user. - * - * @param {Object} data Throught API object - * - * @returns {Object} Lesson content - */ -async function prepareLessonData(data) { - const fullLessonId = data.lessonId; - const lessonId = getLessonId(fullLessonId); - const { lesson, state, isAuth } = data; - const { intro, final, maxScore } = lesson; - - const content = { - id: lessonId, - title: lesson.title, - description: lesson.description, - }; - - if (!isAuth) { - return content; - } - - const tasks = await setTasksState(lesson.tasks, state); - - const scoped = { intro, final, tasks }; - - const totalTasks = tasks.reduce((count, task) => { - if (task.type === "practice") { - return count + 1; - } - return count; - }, 0); - - const progress = { - score: calculateUserScore(state), - maxScore, - doneTasks: calculateDoneTasks(state), - totalTasks, - }; - - return Object.assign(content, scoped, progress); -} - -module.exports = prepareLessonData; diff --git a/src/processes/prepareData/prepareModuleData/prepareModuleData.js b/src/processes/prepareData/prepareModuleData/prepareModuleData.js deleted file mode 100644 index 187132d..0000000 --- a/src/processes/prepareData/prepareModuleData/prepareModuleData.js +++ /dev/null @@ -1,110 +0,0 @@ -const getTaskInfo = require("../../getTaskInfo/getTaskInfo"); -const { setLessonsState } = require("../../setListState/setListState"); -const { - calculateUserScore, - calculateModuleMaxScore, - calculateDoneTasks, - calculateDeadline, -} = require("@utils/calculators"); -const { getNextTaskId } = require("@utils/getNextTaskId"); - -function checkMuduleStatus({ start, deadline, modules, prevModule }) { - const now = Date.now(); - const startTS = Date.parse(start); - const deadlineTS = Date.parse(deadline); - const caution = 1000 * 60 * 60 * 24 * 10; - - if (Object.keys(modules).includes(prevModule)) { - return "unavailable"; - } - - if (startTS > now) { - return "paid"; - } - if (now > deadlineTS) { - return "past"; - } - if (now >= deadlineTS - caution && now < deadlineTS) { - return "deadline"; - } - if (now < deadlineTS) { - return "active"; - } - - return "available"; -} - -/*** - * Function prepares module data to send to user. - * - * @param {Object} data Throught API object - * - * @returns {Object} Module content - */ -async function prepareModuleData(data, next) { - const { user, module, state, isAuth, isFinalAccess } = data; - const { intro, final } = module; - - const content = { - id: module.code, - name: module.name, - shortName: module.shortName, - description: module.description, - lang: module.lang, - moduleLink: module.moduleLink, - features: module.features, - mascot: module.mascot, - price: module.price, - buyLink: module.buyLink, - prevModule: module.prevModule, - group: module.group, - status: "available", - }; - - if (!isFinalAccess || !isAuth || !user.modules[module.code]) { - return content; - } - - const prolongation = { - prolongation: module.prolongation, - prolongationLink: module.prolongationLink, - }; - - const nextTaskId = getNextTaskId(module, state); - - const lessons = setLessonsState(module.lessons, nextTaskId); - - const scoped = { intro, final, lessons }; - - const { start, deadline, prolongations } = user.modules[module.code]; - - const lastDeadline = calculateDeadline({ deadline, prolongations }); - - const status = checkMuduleStatus({ - start, - deadline: lastDeadline, - modules: user.modules, - prevModule: module.prevModule, - }); - - const progress = { - score: calculateUserScore(state), - maxScore: calculateModuleMaxScore(module.lessons), - doneTasks: calculateDoneTasks(state), - totalTasks: module.totalTasks, - start, - deadline: lastDeadline, - status, - nextTask: await getTaskInfo( - { - taskId: nextTaskId, - returns: ["id", "name", "type"], - }, - next - ), - }; - - return Object.assign(content, scoped, prolongation, progress); -} - -module.exports = prepareModuleData; diff --git a/src/processes/prepareData/prepareTaskData/prapareTaskData.js b/src/processes/prepareData/prepareTaskData/prapareTaskData.js deleted file mode 100644 index 309f9dc..0000000 --- a/src/processes/prepareData/prepareTaskData/prapareTaskData.js +++ /dev/null @@ -1,136 +0,0 @@ -const { - setVisibility, - getParentContent, - refAnswerRight, -} = require("../../updateContent/updateContent"); -const setTaskState = require("../../setTaskState/setTaskState"); - -/*** - * Normilize State object - * - * @param {Object} taskState Task state params - * - * @returns {Object} Normilize state object - */ -function validateTaskState(taskState) { - return ({ - isChecked = false, - score = null, - inProcess = false, - protest = false, - isHintActive = false, - isOurVarActive = false, - isSolutionActive = false, - comments = [], - } = taskState || {}); -} - -/*** - * Function prepares task data for response - * - * @param {Object} data Throught API obhect - * - * @returns {Object} Task object - */ -async function prepareTaskData(data) { - const { userId, taskId, task, state } = data; - const taskState = validateTaskState(state); - for (const content of task.content) { - for (const introItem of content.intro) { - if (introItem.type == "richText") { - for (const richItem of introItem.value) { - if (richItem.parentId) { - const value = await getParentContent( - userId, - richItem.parentId, - lang - ); - - richItem.value = value[0]; - - if (!value[1]) { - introItem.value = value[0]; - introItem.type = "p"; - break; - } - } - } - } - - if (introItem.parentId) { - const value = await getParentContent(userId, introItem.parentId, lang); - introItem.value = value[0]; - } - } - for (const question of content.questions || []) { - if (question?.subtopic) { - for (let i = 0; i < question?.subtopic.length; i++) { - if (question?.subtopic[i].parentId) { - const value = await getParentContent( - userId, - question?.subtopic[i].parentId, - lang - ); - question.subtopic[i] = value[0]; - } - } - } - - if (taskState?.data?.[question.id]?.isVisible != undefined) { - question.isVisible = taskState?.data?.[question.id]?.isVisible; - } else if ( - question.depends && - taskState?.data?.[question.id]?.isVisible == undefined - ) { - for (const depend of question.depends) { - if (depend.type == "visibility") { - const isVisible = await setVisibility( - userId, - depend.parentId, - question.id - ); - question.isVisible = isVisible; - if (!isVisible) { - break; - } - } - } - } else { - const path = "data." + question.id + ".isVisible"; - await setTaskState({ - userId, - taskId, - newState: { - [path]: true, - }, - }); - question.isVisible = true; - } - const questionId = question.id; - if (question.type === "text" || question.type === "link") { - question.answer = taskState?.data?.[questionId]?.state || ""; - } else { - for (const variant of question.variants) { - if (variant.parentId) { - const value = await getParentContent( - userId, - variant.parentId, - lang - ); - variant.label = value[0]; - } - if (variant.refs) { - variant.isRight = await refAnswerRight(userId, variant.refs); - } - variant.isSelected = - (taskState?.data?.[questionId]?.state || []).find( - (item) => item?.id === variant.id - )?.isSelected || false; - } - } - } - } - return task; -} - -module.exports = prepareTaskData; diff --git a/src/processes/prepareData/prepareUserData/prepareUserData.js b/src/processes/prepareData/prepareUserData/prepareUserData.js deleted file mode 100644 index 8e2524b..0000000 --- a/src/processes/prepareData/prepareUserData/prepareUserData.js +++ /dev/null @@ -1,19 +0,0 @@ -/*** - * Function prepares user data to send to user. - * - * @param {Object} data Throught API object - * - * @returns {Object} User content - */ -function prepareUserData(data) { - const { user, isAuth } = data; - user.modules = Object.keys(user.modules || []); - if (isAuth) { - delete user.token; - } - delete user.pass; - - return user; -} - -module.exports = prepareUserData; diff --git a/src/processes/processes.js b/src/processes/processes.js deleted file mode 100644 index 252a4c5..0000000 --- a/src/processes/processes.js +++ /dev/null @@ -1,37 +0,0 @@ -const getUserInfo = require("./getUserInfo/getUserInfo"); -const checkCredentials = require("./checkCredentials/checkCredentials"); -const checkAccess = require("./checkAccess/checkAccess"); -const checkTransaction = require("./checkTransaction/checkTransaction"); -const checkOTK = require("./checkOTK/checkOTK"); -const authUser = require("./authUser/authUser"); -const updatePass = require("./updatePass/updatePass"); -const getModuleInfo = require("./getModuleInfo/getModuleInfo"); -const getLessonInfo = require("./getLessonInfo/getLessonInfo"); -const getTaskInfo = require("./getTaskInfo/getTaskInfo"); -const getStateInfo = require("./getStateInfo/getStateInfo"); -const getCounselorInfo = require("./getCounselorInfo/getCounselorInfo"); -const prepareData = require("./prepareData/prepareData"); -const updateContent = require("./updateContent/updateContent"); -const setTaskState = require("./setTaskState/setTaskState"); -const pushComment = require("./pushComment/pushComment"); -const setDiploma = require("./setDiploma/setDiploma"); - -module.exports = { - getUserInfo, - checkCredentials, - ...checkAccess, - checkTransaction, - checkOTK, - authUser, - updatePass, - getModuleInfo, - getLessonInfo, - getTaskInfo, - getStateInfo, - getCounselorInfo, - ...prepareData, - ...updateContent, - setTaskState, - pushComment, - setDiploma, -}; diff --git a/src/processes/pushComment/pushComment.js b/src/processes/pushComment/pushComment.js deleted file mode 100644 index 84aa366..0000000 --- a/src/processes/pushComment/pushComment.js +++ /dev/null @@ -1,37 +0,0 @@ -const DB = require("@mongo/requests"); - -/*** - * Function added new comment to the task. - * - * @param {Object} data Throught API object - * - * @returns {Array} Array of task's comments with the new one - */ -async function pushComment(data) { - const { taskId, userId, comment } = data; - - const newComment = { - ts: Date.now(), - message: comment, - readedByTeacher: false, - }; - - const update = await DB.setOne("state", { - query: { taskId, userId }, - push: { - comments: { - $each: [newComment], - $position: 0, - }, - }, - returns: ["comments"], - }); - - if (!update) { - throw new Error(`${taskId}: A problem pushing new comment to DB!`); - } - - return update; -} - -module.exports = pushComment; diff --git a/src/processes/setDiploma/setDiploma.js b/src/processes/setDiploma/setDiploma.js deleted file mode 100644 index a093208..0000000 --- a/src/processes/setDiploma/setDiploma.js +++ /dev/null @@ -1,24 +0,0 @@ -const DB = require("@mongo/requests"); - -/*** - * Function set new sertificate params. - * - * @param {Object} data Throught API object - * - * @returns {Object} New sertificate state - */ -async function setDiploma(data) { - const { query, params } = data; - const update = await DB.setOne("certs", { - query, - set: params, - }); - - if (!update) { - throw new Error(`${query?.id}: A problem with setting diploma state!`); - } - - return update; -} - -module.exports = setDiploma; diff --git a/src/processes/setListState/setListState.js b/src/processes/setListState/setListState.js deleted file mode 100644 index 95015b9..0000000 --- a/src/processes/setListState/setListState.js +++ /dev/null @@ -1,61 +0,0 @@ -const getTaskInfo = require("../getTaskInfo/getTaskInfo"); -const { getLessonId } = require("@utils/idExtractor"); - -/*** - * Function prepares lessons list with current state. - * - * @param {Object} data Throught API object - * - * @returns {Array} List of lessons - */ -function setLessonsState(lessons = {}, nextTaskId) { - const lessonsState = Object.entries(lessons).map(([id, value]) => { - return { - id, - title: value.title, - description: value.description, - status: "future", - }; - }); - - const currentLessonId = getLessonId(nextTaskId); - - const currentLessonIndex = Object.keys(lessons).indexOf(currentLessonId); - - lessonsState.forEach((lesson, index) => { - if (index < currentLessonIndex) { - lesson.status = "past"; - } else if (index === currentLessonIndex) { - lesson.status = "current"; - } - }); - - return lessonsState; -} - -/*** - * Function prepares lessons list with current state. - * - * @param {Object} data Throught API object - * - * @returns {Array} List of lessons - */ -async function setTasksState(tasks = [], state = []) { - const tasksPromises = tasks.map(async (taskId) => { - const taskInfo = await getTaskInfo({ - taskId, - returns: ["id", "name", "type"], - }); - if (taskInfo.type === "practice") { - const taskState = state.find((item) => item.taskId === taskId); - taskInfo.score = taskState?.score || 0; - taskInfo.isChecked = taskState?.isChecked || false; - taskInfo.inProcess = taskState?.inProcess || false; - } - return taskInfo; - }); - - return Promise.all(tasksPromises); -} - -module.exports = { setLessonsState, setTasksState }; diff --git a/src/processes/setTaskState/setTaskState.js b/src/processes/setTaskState/setTaskState.js deleted file mode 100644 index dba5b50..0000000 --- a/src/processes/setTaskState/setTaskState.js +++ /dev/null @@ -1,27 +0,0 @@ -const DB = require("@mongo/requests"); - -/*** - * Function set new task state. - * - * @param {Object} data Throught API object - * - * @returns {Object} New task state - */ -async function setTaskState(data) { - const { taskId, userId, newState } = data; - const update = await DB.setOne("state", { - query: { taskId, userId }, - set: newState, - options: { - insertNew: true, - }, - }); - - if (!update) { - throw new Error(`${taskId}: A problem with setting new task state!`); - } - - return update; -} - -module.exports = setTaskState; diff --git a/src/processes/updateContent/updateContent.js b/src/processes/updateContent/updateContent.js deleted file mode 100644 index 372b8fe..0000000 --- a/src/processes/updateContent/updateContent.js +++ /dev/null @@ -1,226 +0,0 @@ -const getPhrase = require("@assets/lang/lang"); -const { getFullTaskId, getFullQuestionId } = require("@utils/idExtractor"); -const DB = require("@mongo/requests"); - -/*** - * Modify string, if parent task isn't solved - * - * @param {String} taskId Task ID - * @param {String} lang User lang - * - * @returns {Array} Modified string and strange bool - */ -async function getTaskName(taskId, lang) { - const taskData = await DB.getOne("task", { query: { id: taskId } }); - if (!taskData) { - throw new Error(`getParentContent: Can't find task woth ID ${taskId}`); - } - const taskName = getPhrase(lang, "prevTaskFirst", taskData.name); - return [taskName, false]; -} - -/*** - * Serve all tasks, that will be updated - * - * @param {Array} tasks Tasks list - * @param {String} questionId Reference question ID - * - * @returns {Array} Task list to modify - */ -function getVisibilityUpdateList(tasks, questionId) { - const list = []; - for (const task of tasks) { - for (const contentItem of task?.content || []) { - for (const question of contentItem?.questions || []) { - for (const item of question?.depends || []) { - if ( - item.type === "visibility" && - item.parentId.includes(questionId) - ) { - switch (item.parentId.length) { - case 13: - list.push({ - type: "variant", - targetTaskId: task.id, - questionId: question.id, - variantId: item.parentId, - }); - break; - case 11: - list.push({ - type: "question", - targetTaskId: task.id, - questionId: question.id, - }); - break; - } - } - } - } - } - } - return list; -} - -/*** - * Update question visibility - * - * @param {String} userId User ID - * @param {String} contentId Question or Variant ID - * @param {String} childId Question ID to modify - * - * @returns {Boolean} New question visibility - */ -async function setVisibility(userId, contentId, childId) { - const idLength = id.length; - const taskId = getFullTaskId(contentId); - const questionId = getFullQuestionId(contentId); - const path = "data." + childId + ".isVisible"; - const state = await DB.getOne("state", { - query: { - userId, - taskId, - }, - }); - - let isVisible; - switch (idLength) { - case 13: - const answer = (state?.data?.[questionId]?.state || []).find( - (answer) => answer.id === contentId - ); - if (answer?.isSelected && state?.data?.[questionId]?.isVisible) - isVisible = true; - else isVisible = false; - break; - case 11: - if (state?.data?.[questionId] && state?.data?.[questionId]?.isVisible) - isVisible = true; - else isVisible = false; - break; - } - await DB.setOne("state", { - query: { userId, taskId }, - set: { - [path]: isVisible, - }, - }); - return isVisible; -} - -/*** - * Replace question content to previous user's answers - * - * @param {String} userId User ID - * @param {String} contentId Question or Variant ID - * @param {String} lang User's lang - * - * @returns {String} New question content - */ -async function getParentContent(userId, contentId, lang) { - const taskId = getFullTaskId(contentId); - const taskState = await DB.getOne("state", { - query: { - userId, - taskId, - }, - }); - - if (taskState?.isChecked) { - const questionId = getFullQuestionId(contentId); - const parentContent = taskState.data?.[questionId]?.state; - if (parentContent?.value) { - return [parentContent?.value, true]; - } else { - const selectedAnswers = parentContent - .filter((item) => item.isSelected) - .map((item) => item.label); - return [selectedAnswers.join(", ") || "", true]; - } - } else return getTaskName(taskId, lang); -} - -/** - * Switch variant right status - * - * @param {String} userId User ID - * @param {Array} refs List of references - * - * @returns {Boolean} Responsive variant status - */ -async function refAnswerRight(userId, refs = []) { - for (const ref of refs) { - for (const id of ref.variantIds) { - const taskId = getFullTaskId(id); - const questionId = getFullQuestionId(id); - - const state = await DB.getOne("state", { - query: { - userId, - taskId, - }, - }); - const answer = (state?.data?.[questionId]?.state || []).find( - (answer) => answer.id == id - ); - const isRight = - (answer?.isSelected && ref.type == "isRight") || - (!answer?.isSelected && ref.type == "isWrong"); - - if (!isRight) { - return false; - } - } - return true; - } -} - -/** - * Update all task depends - * - * @param {String} userId User ID - * @param {String} questionId Question ID - * @param {Array} tasks List of tasks - * @param {Object} refs Task's state object - * - * @returns {undefined} - */ -async function updateDependenciesTasks(userId, questionId, tasks, state) { - const questions = getVisibilityUpdateList(tasks, questionId); - if (questions.length === 0) { - return; - } - for (const question of questions) { - const targetTask = question.targetTaskId; - const targetQuestion = question.questionId; - const path = "data." + targetQuestion + ".isVisible"; - const query = { userId, taskId: targetTask }; - var data = {}; - switch (question.type) { - case "question": - data = { - [path]: state?.data ? true : false, - }; - break; - case "variant": - const targetVariant = question.variantId; - const variant = (state || []).find((item) => item.id == targetVariant); - data = { - [path]: variant?.isSelected ? true : false, - }; - break; - } - await DB.setOne("state", { - query, - set: data, - }); - } - return; -} - -module.exports = { - setVisibility, - getParentContent, - refAnswerRight, - updateDependenciesTasks, -}; diff --git a/src/processes/updatePass/updatePass.js b/src/processes/updatePass/updatePass.js deleted file mode 100644 index 401ee2d..0000000 --- a/src/processes/updatePass/updatePass.js +++ /dev/null @@ -1,26 +0,0 @@ -const DB = require("@mongo/requests"); - -/*** - * Function set new password for user. - * - * @param {Object} data Throught API object - * - * @returns {Object} User data - */ -async function updatePass(data) { - const { email, pass } = data; - const user = await DB.setOne("users", { - query: { email }, - set: { pass }, - }); - - if (!user) { - throw new Error(`${email}: User didn't found when pass updating !`); - } - - data.user = user; - - return user; -} - -module.exports = updatePass; diff --git a/src/services/assistant/assistant.js b/src/services/assistant/assistant.js new file mode 100644 index 0000000..0fd06dd --- /dev/null +++ b/src/services/assistant/assistant.js @@ -0,0 +1,23 @@ +const fetch = require("node-fetch"); +const { log } = require("@logger"); + +const { ASSISTANT_WEBHOOR_URL, ASSISTANT_ADMIN_TOKEN } = process.env; + +async function sendMessage(body) { + try { + const url = ASSISTANT_WEBHOOR_URL + "/sendMessage" + await fetch(url, { + method: "POST", + body: JSON.stringify(body), + headers: { + "Content-Type": "application/json", + "Authorization": ASSISTANT_ADMIN_TOKEN + }, + }) + } catch (e) { + log.error("sendMessage assistant webhook failed") + log.debug(e) + } +} + +module.exports = { sendMessage } \ No newline at end of file diff --git a/src/services/express/express.js b/src/services/express/express.js index b9d9e88..4adaad2 100644 --- a/src/services/express/express.js +++ b/src/services/express/express.js @@ -2,11 +2,8 @@ const express = require("express"); const cors = require("cors"); const { log } = require("@logger"); -const { PUBLIC } = require("../../modules/apiRequests/apiRequests"); -const { STUDENT } = require("../../API/student/student"); -const { responseHandler, pathHandler } = require("./responses"); -const prepareRequestData = require("./prepareRequestData"); -const { checkAuth } = require("./security"); +const { PUBLIC, TEACHER } = require("../../modules/apiRequests/apiRequests"); +const { checkAdmin } = require("./security"); const { paramsProcessor } = require("../../utils/validate"); const { SERVER_PORT = 8888, ORIGIN = "*" } = process.env; @@ -23,44 +20,34 @@ app.use(express.json()); app.use(require("body-parser").urlencoded({ extended: false })); app.use("/diplomas", express.static("diplomas")); -/* -app.use((req, res, next) => { - log.info(req?.query, req?.body, req?.path); - next(); -}); -*/ // API v.2 -const apiRouter = express.Router(); -app.use("/api/v2", apiRouter); -apiRouter.use(paramsProcessor); +const student = express.Router(); +app.use("/student", student); +student.use(paramsProcessor); for (const request of PUBLIC) { const { path, method, exec } = request; switch (method) { case "get": - apiRouter.get(path, exec); + student.get(path, exec); case "post": - apiRouter.post(path, exec); + student.post(path, exec); } } -// API v.3 -const student = express.Router(); -app.use("/v3/student", student); - -for (const method of STUDENT) { - const { name, type, wall, exec } = method; - switch (type) { +const oldTeacher = express.Router(); +app.use("/teacher", oldTeacher); +oldTeacher.use(checkAdmin); +for (const request of TEACHER) { + const { path, method, exec } = request; + switch (method) { case "get": - student.get("/" + name, checkAuth(wall), prepareRequestData, exec); + oldTeacher.get(path, exec); case "post": - student.post("/" + name, checkAuth(wall), prepareRequestData, exec); + oldTeacher.post(path, exec); } } -app.use(responseHandler); -app.use(pathHandler); - function start() { return new Promise((resolve, reject) => { app.listen(SERVER_PORT, (err) => { diff --git a/src/services/express/security.js b/src/services/express/security.js index 0d54b79..e387458 100644 --- a/src/services/express/security.js +++ b/src/services/express/security.js @@ -1,6 +1,7 @@ const { checkToken } = require("../tokenMachine/tokenMachine"); const trustedMachines = process.env.TRUSTED || []; +const adminToken = process.env.ADMIN_TOKEN /*** * Function checks user access token. @@ -40,4 +41,25 @@ function checkAuth(wall) { }; } -module.exports = { checkAuth }; +function checkAdmin(req, res, next) { + if (Object.keys(req.body).includes("gumroad_fee")) { + next(); + return; + } + const token = req?.headers?.accesstoken; + + if (token !== adminToken) { + res.status(401); + res.send({ + OK: false, + error: "invalid_credentials", + error_description: "Invalid access token", + error_code: 10003, + }); + return; + } else { + next(); + } +} + +module.exports = { checkAuth, checkAdmin }; diff --git a/src/services/fs/fs.js b/src/services/fs/fs.js new file mode 100644 index 0000000..8607653 --- /dev/null +++ b/src/services/fs/fs.js @@ -0,0 +1,36 @@ +const { log } = require("../logger/logger"); +const fs = require("fs") +const path = require('path'); + +const projectPath = process.cwd() + +function createPath(filePath) { + fs.mkdirSync(filePath, { recursive: true }, (err) => { + if (err) throw err; + }); +} + +function readFile(filePath, name) { + const fullPath = path.join(projectPath, filePath) + + try { + return JSON.parse(fs.readFileSync(fullPath + name)) + } catch { + return + } +} + +function writeFile(filePath, name, data) { + const fullPath = path.join(projectPath, filePath) + if (!fs.existsSync(fullPath)) { + createPath(fullPath) + } + + try { + fs.writeFileSync(fullPath + name, JSON.stringify(data)) + } catch { + throw new Error("Dump file is not wroted") + } +} + +module.exports = {readFile, writeFile} \ No newline at end of file diff --git a/src/services/mailer/actions.js b/src/services/mailer/actions.js new file mode 100644 index 0000000..651d4cd --- /dev/null +++ b/src/services/mailer/actions.js @@ -0,0 +1,4 @@ +const sendMail = require("./actions/sendMail"); +const prepareMail = require("./actions/prepareMail"); + +module.exports = { sendMail, prepareMail }; diff --git a/src/services/mailer/actions/prepareMail.js b/src/services/mailer/actions/prepareMail.js new file mode 100644 index 0000000..e30d169 --- /dev/null +++ b/src/services/mailer/actions/prepareMail.js @@ -0,0 +1,8 @@ +const { applyTemplate } = require("../templates"); + +function prepareMail(context) { + const { params, data } = context; + return applyTemplate(params, data); +} + +module.exports = prepareMail; \ No newline at end of file diff --git a/src/services/mailer/actions/sendMail.js b/src/services/mailer/actions/sendMail.js new file mode 100644 index 0000000..af334a9 --- /dev/null +++ b/src/services/mailer/actions/sendMail.js @@ -0,0 +1,14 @@ +const { mailer } = require("../mailer"); + +const { MAIL_FROM } = process.env; + +function sendMail(mail, to, subject) { + mailer.sendMail({ + from: MAIL_FROM, + to, + subject, + html: mail, + }); +} + +module.exports = sendMail; diff --git a/src/services/mailer/mailer.js b/src/services/mailer/mailer.js new file mode 100644 index 0000000..9fc53a0 --- /dev/null +++ b/src/services/mailer/mailer.js @@ -0,0 +1,18 @@ +const nodemailer = require("nodemailer"); + +const { MAIL_USER, MAIL_PASS } = process.env; + +const options = { + pool: true, + host: "smtp.gmail.com", + port: 465, + secure: true, + auth: { + user: MAIL_USER, + pass: MAIL_PASS, + }, +}; + +const mailer = nodemailer.createTransport(options); + +module.exports = { mailer }; diff --git a/src/services/mailer/templates.js b/src/services/mailer/templates.js new file mode 100644 index 0000000..24ed75d --- /dev/null +++ b/src/services/mailer/templates.js @@ -0,0 +1,35 @@ +const fs = require("fs"); +const path = require("path"); + +const templatePath = path.join(__dirname, "templates"); + +function getTemplatesList() { + return fs.readdirSync(templatePath); +} + +function getTemplate({ lang, status, type, start }) { + const nameArray = [lang, status]; + if (type) { + nameArray.push(type); + } + if (start) { + nameArray.push(start); + } + const name = nameArray.join("_") + ".html"; + return fs.readFileSync(path.join(templatePath, name)).toString(); +} + +function applyTemplate(params, data = {}) { + let template = getTemplate(params); + + const prefix = "*|"; + const suffix = "|*"; + + for (const key in data) { + template = template.replaceAll(prefix + key + suffix, data[key]); + } + + return template; +} + +module.exports = { getTemplatesList, getTemplate, applyTemplate }; diff --git a/src/services/mailer/templates/en_new_buy_now.html b/src/services/mailer/templates/en_new_buy_now.html new file mode 100644 index 0000000..ae18c55 --- /dev/null +++ b/src/services/mailer/templates/en_new_buy_now.html @@ -0,0 +1,163 @@ +
+ + + + + + +
+ + + + + + +
+
+ + + + + + + +
+
+ eduHund.ru + • digital mentor +
+
+ + Eduhund logo + +
+
+
+ + + + + + +
+
+ + + + + + + +
+

+ Welcome! +

+
+ Now you’ve got your Digital Mentor +
+
+
+  *|MODULE|* +
+
+
+
+ + + +
+

+ Hi, *|NAME|* +

+
+ You received this letter because you bought + Digital Mentor for Team Leaders and Novice Managers + on Gumroad. +
+
+ The password was generated automatically (*|PASSWORD|*), but you can + change it. + Sign in with your email address. +
+ +
+ Follow the link below for an explanation of how to work with the Digital Mentor and answers to frequently asked student questions. +
+ +
+ If you have any questions, please contact us by mail + edu@eduhund.com + or using a + Telegram-assistant. +
+
+ + +
+ + + + + + +
+
+ + + + + + + +
+ + + + + + +
+
+
+
+
\ No newline at end of file diff --git a/src/services/mailer/templates/ru_current_buy_date.html b/src/services/mailer/templates/ru_current_buy_date.html new file mode 100644 index 0000000..1630fb8 --- /dev/null +++ b/src/services/mailer/templates/ru_current_buy_date.html @@ -0,0 +1,164 @@ +
+ + + + + + +
+ + + + + + +
+
+ + + + + + + +
+
+ eduHund.ru + • задачник для самостоятельных +
+
+ + Eduhund logo + +
+
+
+ + + + + + +
+
+ + + + + + + +
+

+ Вы зачислены +

+
+ Спасибо за покупку! +
+
+
+ *|MODULE|* +
+
+
+
+ + + + + + +
+
+

+ Привет, *|NAME|* +

+
+ Вы получили это письмо, потому что купили модуль + *|MODULE|* + задачника + eduHund. + Он появится в + вашем личном кабинете + *|START_DATE|*. +
+ +
+ Если что-то непонятно или что-то не работает, пишите нам, пожалуйста, на + edu@eduhund.com + или + Telegram-ассистенту. +
+
+
+ + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/src/services/mailer/templates/ru_current_buy_now.html b/src/services/mailer/templates/ru_current_buy_now.html new file mode 100644 index 0000000..2f02180 --- /dev/null +++ b/src/services/mailer/templates/ru_current_buy_now.html @@ -0,0 +1,161 @@ +
+ + + + + + +
+ + + + + + +
+
+ + + + + + + +
+
+ eduHund.ru + • задачник для самостоятельных +
+
+ Eduhund logo + +
+
+
+ + + + + + +
+
+ + + + + + + +
+

+ Вы зачислены +

+
+ Спасибо за покупку! +
+
+
+ *|MODULE|* +
+
+
+
+ + + + + + +
+
+

+ Привет, *|NAME|* +

+
+ Вы получили это письмо, потому что купили модуль + *|MODULE|* + задачника + eduHund. + Он уже появился в + вашем личном кабинете. +
+ +
+ Если что-то непонятно или что-то не работает, пишите нам, пожалуйста, на + edu@eduhund.com + или + Telegram-ассистенту. +
+
+
+ + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/src/services/mailer/templates/ru_new_buy_date.html b/src/services/mailer/templates/ru_new_buy_date.html new file mode 100644 index 0000000..0f2ac5e --- /dev/null +++ b/src/services/mailer/templates/ru_new_buy_date.html @@ -0,0 +1,173 @@ +
+ + + + + + +
+ + + + + + +
+
+ + + + + + + +
+
+ eduHund.ru + • задачник для самостоятельных +
+
+ + Eduhund logo + +
+
+
+ + + + + + +
+
+ + + + + + + +
+

+ Вы зачислены +

+
+ Спасибо за покупку! +
+
+
+ *|MODULE|* +
+
+
+
+ + + + + + +
+
+

+ Привет, *|NAME|* +

+
+ Вы получили это письмо, потому что купили модуль + *|MODULE|* + задачника + eduHund. + Он откроется *|START_DATE|* в + вашем личном кабинете. +
+
+ Пароль к задачнику создан автоматически (*|PASSWORD|*), но вы можете + поменять его, + если хотите. Логин — ваш е-мейл. +
+ +
+ По следующей ссылке — объяснение, как работает задачник, и ответы на самые частые студенческие вопросы. +
+ +
+ Если что-то непонятно или что-то не работает, пишите нам, пожалуйста, на + edu@eduhund.com + или + Telegram-ассистенту. +
+
+
+ + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/src/services/mailer/templates/ru_new_buy_now.html b/src/services/mailer/templates/ru_new_buy_now.html new file mode 100644 index 0000000..7266fcb --- /dev/null +++ b/src/services/mailer/templates/ru_new_buy_now.html @@ -0,0 +1,178 @@ +
+ + + + + + +
+ + + + + + +
+
+ + + + + + + +
+
+ eduHund.ru + • задачник для самостоятельных +
+
+ + Eduhund logo + +
+
+
+ + + + + + +
+
+ + + + + + + +
+

+ Вы зачислены +

+
+ Спасибо за покупку! +
+
+
+ *|MODULE|* +
+
+
+
+ + + + + + +
+
+

+ Привет, *|NAME|* +

+
+ Вы получили это письмо, потому что купили модуль + *|MODULE|* + задачника + eduHund. + Он уже доступен в + вашем личном кабинете. +
+
+ Пароль к задачнику создан автоматически (*|PASSWORD|*), но вы можете + поменять его, + если хотите. Логин — ваш е-мейл. +
+ +
+ По следующей ссылке — объяснение, как работает задачник, и ответы на самые частые студенческие вопросы. +
+ +
+ Если что-то непонятно или что-то не работает, пишите нам, пожалуйста, на + edu@eduhund.com + или + Telegram-ассистенту. +
+
+
+ + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/src/services/mailer/templates/ru_otp.html b/src/services/mailer/templates/ru_otp.html new file mode 100644 index 0000000..9fe6701 --- /dev/null +++ b/src/services/mailer/templates/ru_otp.html @@ -0,0 +1,169 @@ +
+ + + + + + +
+ + + + + + +
+
+ + + + + + + +
+
+ eduHund.ru + • задачник для самостоятельных +
+
+ + Eduhund logo + +
+
+
+ + + + + + +
+
+ + + + + + + +
+

+ Явки-пароли +

+
+ для входа в задачник +
+
+
+ *|MODULE|* +
+
+
+
+ + + + + + +
+
+

+ Привет, *|NAME|* +

+
+ Вы получили это письмо, потому что попросили прислать вам одноразовый код для входа в + задачник. +
+
+ Вот он: *|OTP_CODE|* +
+
+ Вы можете скопировать код и вставить его на странице входа в личный кабинет или перейти по ссылке ниже, чтобы сразу попасть в задачник: +
+ +
+ Код и ссылка перестанут работать через час. Не переживайте, вы сможете запросить их повторно. +
+
+ Если это были не вы, просто проигнорируйте это письмо. Отвечать на него тоже не нужно. А если все-таки переживаете — пишите на + + edu@eduhund.com + + или + Telegram-ассистенту. +
+
+
+ + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/src/services/mailer/templates/ru_renew.html b/src/services/mailer/templates/ru_renew.html new file mode 100644 index 0000000..d78180a --- /dev/null +++ b/src/services/mailer/templates/ru_renew.html @@ -0,0 +1,151 @@ +
+ + + + + + +
+ + + + + + +
+
+ + + + + + + +
+
+ eduHund.ru + • задачник для самостоятельных +
+
+ + Eduhund logo + +
+
+
+ + + + + + +
+
+ + + + + + + +
+

+ Спасибо! +

+
Вы купили продление +
+
+
+ Денежный кот +
+
+
+
+ + + + + + +
+
+

*|NAME|*, здравствуйте

+
Спасибо, что купили продление доступа к дизайн-задачнику «Собаки Павловой».
+
Все в порядке, оплата прошла успешно. Мы сосчитали вас и продлили доступ к модулю + *|MODULE|*. +
+
P.S. Если есть вопросы, пишите нам на + edu@eduhund.com или + Telegram-ассистенту. +
+
+
+ + + + + + +
+ +
+
+
\ No newline at end of file diff --git a/src/services/mongo/mongo.js b/src/services/mongo/mongo.js deleted file mode 100644 index bd5a324..0000000 --- a/src/services/mongo/mongo.js +++ /dev/null @@ -1,20 +0,0 @@ -const { MongoClient } = require("mongodb"); -const { log } = require("@logger"); - -const { DB_URL, DB_NAME } = process.env; - -const mongoClient = new MongoClient(DB_URL); - -function getCollection(name) { - return mongoClient.db(DB_NAME).collection(name); -} - -async function start() { - await mongoClient.connect(); - log.info("Connected to database successfully"); -} - -module.exports = { - start, - getCollection, -}; diff --git a/src/services/mongo/requests.js b/src/services/mongo/requests.js deleted file mode 100644 index 5891db5..0000000 --- a/src/services/mongo/requests.js +++ /dev/null @@ -1,68 +0,0 @@ -const { getCollection } = require("./mongo"); - -function getProjection(returns) { - const projection = { - _id: 0, - }; - for (const param of returns) { - projection[param] = 1; - } - return projection; -} - -function getOptions(returns, options) { - return { - projection: getProjection(returns), - upsert: options.insertNew || false, - returnDocument: "after", - returnNewDocument: true, - }; -} - -const DB = { - count: (collection, data) => { - const { query } = data; - - return getCollection(collection).countDocuments(query) || 0; - }, - insertOne: (collection, data) => { - const { query } = data; - - return getCollection(collection).insertOne(query); - }, - getOne: (collection, data) => { - const { query, returns = [] } = data; - - const projection = getProjection(returns); - - return getCollection(collection).findOne(query, { projection }); - }, - setOne: async (collection, data) => { - const { query, set, push, returns = [], options = {} } = data; - - const allOptions = getOptions(returns, options); - - const response = await getCollection(collection).findOneAndUpdate( - query, - (set && { $set: set }) || (push && { $push: push }), - allOptions - ); - - return response?.value || null; - }, - - getMany: async (collection, data) => { - const { query, returns = [] } = data; - - const projection = getProjection(returns); - - const response = await getCollection(collection) - .find(query, { - projection, - }) - .toArray(); - return response || []; - }, -}; - -module.exports = DB; diff --git a/src/services/tokenMachine/OTK.js b/src/services/tokenMachine/OTK.js index 313b4e9..55fdae1 100644 --- a/src/services/tokenMachine/OTK.js +++ b/src/services/tokenMachine/OTK.js @@ -1,18 +1,57 @@ const fetch = require("node-fetch"); -const server = process.env.REG_OTK_SERVER; +const { OTK_SERVER, OTK_TOKEN } = process.env; + +const bearerToken = "Bearer " + OTK_TOKEN + +function getKeyParams(type) { + switch (type) { + case "oneTimePass": + return { + type: "digit", + length: 4, + life: 3600, + isReusable: false + } + case "oneTimeKey": + return { + type: "string", + length: 8, + life: 62 * 24 * 60 * 60, + isReusable: false + } + default: + return { + type: "digit", + length: 4, + life: 3600, + isReusable: false + } + } +} async function checkKey(key) { - const response = await fetch(`${server}/checkKey?key=${key}`); + const response = await fetch(`${OTK_SERVER}/checkKey?key=${key}`, { + headers: { + "Authorization": bearerToken + }, + }); const data = await response.json(); return data?.OK || false; } -async function setKey(user) { - const response = await fetch(`${server}/setKey`, { +async function setKey(userId, type) { + const body = { + userId, + ...getKeyParams(type) + } + const response = await fetch(`${OTK_SERVER}/setKey`, { method: "POST", - body: JSON.stringify(user), - headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + headers: { + "Content-Type": "application/json", + "Authorization": bearerToken + }, }); const data = await response.json(); return data?.data?.key || undefined; diff --git a/src/services/tokenMachine/tokenMachine.js b/src/services/tokenMachine/tokenMachine.js index bd97ae6..0b4d6ba 100644 --- a/src/services/tokenMachine/tokenMachine.js +++ b/src/services/tokenMachine/tokenMachine.js @@ -1,3 +1,5 @@ +const { readFile, writeFile } = require("../../services/fs/fs") + function rand() { return Math.random().toString(36).substring(2); } @@ -5,25 +7,31 @@ function rand() { function getToken() { const accessToken = rand() + rand(); const refreshToken = rand(); - const expiresIn = Math.floor(Date.now() / 1000 + 7200); + const expiresAt = Date.now() + 5 * 24 * 60 * 60 * 1000; return { accessToken, - expiresIn, + expiresAt, refreshToken, }; } function tokenMachine() { - const tokens = {}; + const tokens = readFile("/files/", "tokens.json",) || {}; function checkToken(token) { return tokens?.[token]; } - function setToken(user) { + function setToken(user, data = {}) { const newToken = getToken(); tokens[newToken.accessToken] = { id: user?.id, + expiresAt: newToken.expiresAt, + ip: data.ip, + ts: Date.now(), + userAgent: data.userAgent, + geo: data.geo }; + writeFile("/files/", "tokens.json", tokens) return newToken; } @@ -37,6 +45,4 @@ function tokenMachine() { }; } -const accessTokens = tokenMachine(); - -module.exports = accessTokens; +module.exports = tokenMachine(); \ No newline at end of file diff --git a/src/utils/access.js b/src/utils/access.js new file mode 100644 index 0000000..6e94680 --- /dev/null +++ b/src/utils/access.js @@ -0,0 +1,22 @@ +/** + * Calculate deadline for user's module + * + * @param {String} date Date of start + * @param {Number} duration Days number of access + * @param {Array} prolongations List of users's prolongations + * + * @returns {String} Date of deadline + */ +function getDeadline({ deadline, prolongations = [] }) { + if (prolongations.length === 0) return deadline; + + if (prolongations.length > 1) { + prolongations.sort((a, b) => Date.parse(b.until) - Date.parse(a.until)); + } + + return prolongations[0]?.until > deadline + ? prolongations[0]?.until + : deadline; +} + +module.exports = { getDeadline }; diff --git a/src/utils/calculators.js b/src/utils/calculators.js index 3df336a..a5a507c 100644 --- a/src/utils/calculators.js +++ b/src/utils/calculators.js @@ -144,20 +144,15 @@ function calculateDefaultScore(content = []) { * * @param {String} date Date of start * @param {Number} duration Days number of access - * @param {Array} prolongations List of users's prolongations * * @returns {String} Date of deadline */ -function calculateDeadline({ deadline, prolongations = [] }) { - if (prolongations.length === 0) return deadline; - - if (prolongations.length > 1) { - prolongations.sort((a, b) => Date.parse(b.until) - Date.parse(a.until)); - } - - return prolongations[0]?.until > deadline - ? prolongations[0]?.until - : deadline; +function calculateDeadline(date, duration) { + const dateStart = new Date(date); + const dateFinish = new Date( + dateStart.setDate(dateStart.getDate() + duration) + ); + return dateFinish.toISOString().split("T")[0]; } /** diff --git a/src/utils/checkAuth.js b/src/utils/checkAuth.js index 38ccbdc..535fc41 100644 --- a/src/utils/checkAuth.js +++ b/src/utils/checkAuth.js @@ -1,4 +1,4 @@ -const { db } = require("../modules/dbRequests/mongo"); +const { USERS } = require("../modules/dbRequests/mongo"); const accessTokens = require("../services/tokenMachine/tokenMachine"); const { getModuleId } = require("./idExtractor"); const { generateMessage } = require("./messageGenerator"); @@ -16,7 +16,7 @@ function checkAuth(req, res, next) { const userId = accessTokens.checkList()?.[token]?.id; if (!userId) { - const error = generateMessage(10103); + const error = generateMessage(10105); res.status(401).send(error); return error; } else { @@ -61,7 +61,7 @@ function checkModuleAccess(req, res, next) { req?.body?.moduleId || getModuleId(lessonId || taskId || questionId); - db.USERS.findOne({ id: userId }).then((user) => { + USERS.findOne({ id: userId }).then((user) => { startDate = user?.modules?.[moduleId]?.start; deadline = user?.modules?.[moduleId]?.deadline; if (checkDate(startDate, deadline)) { @@ -88,7 +88,7 @@ function checkCertAccess(req, res, next) { req?.body?.moduleId || getModuleId(lessonId); - db.USERS.findOne({ id: userId }).then((user) => { + USERS.findOne({ id: userId }).then((user) => { const modules = Object.keys(user?.modules || {}); if (modules.includes(moduleId)) { next(); diff --git a/src/utils/generateCertId.js b/src/utils/generateCertId.js index a140d22..1eb2bc4 100644 --- a/src/utils/generateCertId.js +++ b/src/utils/generateCertId.js @@ -1,4 +1,5 @@ -const DB = require("@mongo/requests"); +const { CERTS } = require("../modules/dbRequests/mongo"); +const { getDBRequest } = require("../modules/dbRequests/dbRequests"); function padder(number = 0, count = 1) { return number.toString().padStart(count, "0"); @@ -6,7 +7,7 @@ function padder(number = 0, count = 1) { async function generateCertId(userId, moduleId, startDate) { const certsCount = - (await DB.count("certs", { query: { moduleId, startDate } })) || 0; + (await CERTS.count( { moduleId, startDate })) || 0; const postfix = padder(certsCount + 1, 4); const date = new Date(Date.parse(startDate)); @@ -16,7 +17,7 @@ async function generateCertId(userId, moduleId, startDate) { )}`; const certId = `${moduleId}${datePart}${postfix}`; - DB.insertOne("certs", { + getDBRequest("setUserInfo", { query: { id: certId, userId, @@ -28,7 +29,10 @@ async function generateCertId(userId, moduleId, startDate) { const path = `modules.${moduleId}.certId`; - DB.setOne("users", { query: { id: userId }, set: { [path]: certId } }); + getDBRequest("setUserInfo", { + query: { id: userId }, + data: { [path]: certId } + }); return certId; } diff --git a/src/utils/getParentContent.js b/src/utils/getParentContent.js index 3ca09dd..97587e0 100644 --- a/src/utils/getParentContent.js +++ b/src/utils/getParentContent.js @@ -2,10 +2,10 @@ const getPhrase = require("@assets/lang/lang"); const { getFullTaskId, getFullQuestionId } = require("./idExtractor"); -const DB = require("@mongo/requests"); +const { getDBRequest } = require("../modules/dbRequests/dbRequests"); async function getTaskName(taskId, lang) { - const taskData = await DB.getOne("tasks", { query: { id: taskId } }); + const taskData = await getDBRequest("getTaskInfo", { query: { id: taskId } }); if (!taskData) { throw new Error(`getParentContent: Can't find task with ID ${taskId}`); } @@ -17,12 +17,9 @@ async function getTaskName(taskId, lang) { async function getParentContent(userId, contentId, lang, sameTask = false) { const taskId = getFullTaskId(contentId); - const taskState = await DB.getOne("state", { - query: { - userId, - taskId, - }, - }); + const taskState = await getDBRequest("getStateInfo", { + query: { userId, taskId }, + }) if (taskState?.isChecked || sameTask) { const questionId = getFullQuestionId(contentId); diff --git a/src/utils/messageGenerator.js b/src/utils/messageGenerator.js index 1713730..e8f29fc 100644 --- a/src/utils/messageGenerator.js +++ b/src/utils/messageGenerator.js @@ -33,6 +33,11 @@ const ERRORS = [ type: "invalid_credentials", description: "Payment didn't found", }, + { + code: 10105, + type: "invalid_credentials", + description: "Access token is invalid or expired", + }, { code: 10201, type: "access_denied", @@ -133,9 +138,19 @@ const ERRORS = [ type: "process_failure", description: "Error with getting counselor content", }, + { + code: 20117, + type: "process_failure", + description: "Error with updating comment status", + }, + { + code: 20118, + type: "process_failure", + description: "Error with resetting user password", + }, ]; -function generateMessage(code, data = {}) { +function generateMessage(code, data) { if (code === 0) { return { OK: true, data }; } else { diff --git a/src/utils/validate.js b/src/utils/validate.js index 677c475..19238b9 100644 --- a/src/utils/validate.js +++ b/src/utils/validate.js @@ -6,24 +6,24 @@ const { lang } = require("../../config.json"); const { generateMessage } = require("./messageGenerator"); const requireParams = { - ["/api/v2/auth"]: ["email", "pass"], - ["/api/v2/checkPayment"]: ["paymentId"], - ["/api/v2/createPassword"]: ["email", "pass", "key"], - ["/api/v2/getDashboard"]: ["accessToken"], - ["/api/v2/getModuleInfo"]: ["moduleId", "accessToken"], - ["/api/v2/getTask"]: ["taskId", "accessToken"], - ["/api/v2/getModuleStart"]: ["moduleId", "accessToken"], - ["/api/v2/getModuleFinal"]: ["moduleId", "accessToken"], - ["/api/v2/getLessonStart"]: ["lessonId", "accessToken"], - ["/api/v2/getLessonFinal"]: ["lessonId", "accessToken"], - ["/api/v2/getLessonsList"]: ["moduleId", "accessToken"], - ["/api/v2/getTasksList"]: ["lessonId", "accessToken"], - ["/api/v2/getDiploma"]: ["moduleId", "accessToken"], - ["/api/v2/setState"]: ["questionId", "state", "accessToken"], - ["/api/v2/checkTask"]: ["taskId", "isChecked", "protest", "accessToken"], - ["/api/v2/setControls"]: ["taskId", "controlsState", "accessToken"], - ["/api/v2/addComment"]: ["taskId", "comment", "protest", "accessToken"], - ["/api/v2/getCounselor"]: ["lang", "accessToken"], + ["/v2/student/auth"]: ["email", "pass"], + ["/v2/student/checkPayment"]: ["paymentId"], + ["/v2/student/createPassword"]: ["email", "pass", "key"], + ["/v2/student/getDashboard"]: ["accessToken"], + ["/v2/student/getModuleInfo"]: ["moduleId", "accessToken"], + ["/v2/student/getTask"]: ["taskId", "accessToken"], + ["/v2/student/getModuleStart"]: ["moduleId", "accessToken"], + ["/v2/student/getModuleFinal"]: ["moduleId", "accessToken"], + ["/v2/student/getLessonStart"]: ["lessonId", "accessToken"], + ["/v2/student/getLessonFinal"]: ["lessonId", "accessToken"], + ["/v2/student/getLessonsList"]: ["moduleId", "accessToken"], + ["/v2/student/getTasksList"]: ["lessonId", "accessToken"], + ["/v2/student/getDiploma"]: ["moduleId", "accessToken"], + ["/v2/student/setState"]: ["questionId", "state", "accessToken"], + ["/v2/student/checkTask"]: ["taskId", "isChecked", "protest", "accessToken"], + ["/v2/student/setControls"]: ["taskId", "controlsState", "accessToken"], + ["/v2/student/addComment"]: ["taskId", "comment", "protest", "accessToken"], + ["/v2/student/getCounselor"]: ["lang", "accessToken"], }; function paramsProcessor(req, res, next) { diff --git a/src/utils/visibilityControl.js b/src/utils/visibilityControl.js index 5a1ed2b..0baf34a 100644 --- a/src/utils/visibilityControl.js +++ b/src/utils/visibilityControl.js @@ -1,4 +1,4 @@ -const { db } = require("../modules/dbRequests/mongo"); +const { STATE } = require("../modules/dbRequests/mongo"); const { getFullTaskId, getFullQuestionId } = require("./idExtractor"); function checkVisibility(id, taskState) { @@ -21,11 +21,11 @@ async function setVisibility(userId, dependQuestionId, currentElementId) { const currentTaskId = getFullTaskId(dependQuestionId); const path = "data." + currentElementId + ".isVisible"; - const taskState = await db.STATE.findOne({ userId, taskId: dependTaskId }); + const taskState = await STATE.findOne({ userId, taskId: dependTaskId }); const isVisible = checkVisibility(dependQuestionId, taskState); - db.STATE.findOneAndUpdate( + STATE.findOneAndUpdate( { userId, taskId: currentTaskId }, { $set: {