From 1298e61fe4d76e6f7b86a3a26fd908057e5ffa23 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 13 Sep 2020 19:01:09 -0300 Subject: [PATCH 01/54] before adding eslint --- backend/.editorconfig | 9 + backend/package.json | 22 +- backend/src/app.js | 85 ---- backend/src/server.ts | 86 ++++ backend/tsconfig.json | 69 +++ backend/yarn.lock | 1045 ++++++++++++++++++++++++++++++++++++++--- 6 files changed, 1150 insertions(+), 166 deletions(-) create mode 100644 backend/.editorconfig delete mode 100644 backend/src/app.js create mode 100644 backend/src/server.ts create mode 100644 backend/tsconfig.json diff --git a/backend/.editorconfig b/backend/.editorconfig new file mode 100644 index 0000000..11695db --- /dev/null +++ b/backend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/backend/package.json b/backend/package.json index 9b43f10..0eaae99 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,16 +4,11 @@ "description": "", "main": "index.js", "scripts": { - "start": "nodemon src/app.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "nodemonConfig": { - "ignore": [ - "controllers/session.json" - ] + "build": "tsc", + "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" }, "author": "", - "license": "ISC", + "license": "MIT", "dependencies": { "@sentry/node": "5.22.3", "bcryptjs": "^2.4.3", @@ -26,14 +21,19 @@ "multer": "^1.4.2", "mysql2": "^2.1.0", "qrcode-terminal": "^0.12.0", - "sequelize": "^6.3.5", + "sequelize": "5", + "sequelize-cli": "5", "socket.io": "^2.3.0", "whatsapp-web.js": "^1.8.2", - "youch": "^2.0.10", "yup": "^0.29.3" }, "devDependencies": { + "@types/cors": "^2.8.7", + "@types/express": "^4.17.8", + "@types/multer": "^1.4.4", + "eslint": "^7.9.0", "nodemon": "^2.0.4", - "sequelize-cli": "^6.2.0" + "ts-node-dev": "^1.0.0-pre.62", + "typescript": "^4.0.2" } } diff --git a/backend/src/app.js b/backend/src/app.js deleted file mode 100644 index e1b5df1..0000000 --- a/backend/src/app.js +++ /dev/null @@ -1,85 +0,0 @@ -require("express-async-errors"); -require("./database"); -const express = require("express"); -const path = require("path"); -const Youch = require("youch"); -const cors = require("cors"); -const multer = require("multer"); -const Sentry = require("@sentry/node"); - -const { initWbot } = require("./libs/wbot"); -const wbotMessageListener = require("./services/wbotMessageListener"); -const wbotMonitor = require("./services/wbotMonitor"); -const Whatsapp = require("./models/Whatsapp"); - -const Router = require("./router"); - -const app = express(); - -const server = app.listen(process.env.PORT, () => { - console.log(`Server started on port: ${process.env.PORT}`); -}); - -Sentry.init({ dsn: process.env.SENTRY_DSN }); - -const fileStorage = multer.diskStorage({ - destination: (req, file, cb) => { - cb(null, path.resolve(__dirname, "..", "public")); - }, - filename: (req, file, cb) => { - cb(null, new Date().getTime() + path.extname(file.originalname)); - }, -}); - -app.use(Sentry.Handlers.requestHandler()); -app.use(cors()); -app.use(express.json()); -app.use(multer({ storage: fileStorage }).single("media")); -app.use("/public", express.static(path.join(__dirname, "..", "public"))); -app.use(Router); - -const io = require("./libs/socket").init(server); -io.on("connection", socket => { - console.log("Client Connected"); - socket.on("joinChatBox", ticketId => { - console.log("A client joined a ticket channel"); - socket.join(ticketId); - }); - - socket.on("joinNotification", () => { - console.log("A client joined notification channel"); - socket.join("notification"); - }); - - socket.on("disconnect", () => { - console.log("Client disconnected"); - }); -}); - -const startWhatsAppSessions = async () => { - const whatsapps = await Whatsapp.findAll(); - - if (whatsapps.length > 0) { - whatsapps.forEach(whatsapp => { - initWbot(whatsapp) - .then(() => { - wbotMessageListener(whatsapp); - wbotMonitor(whatsapp); - }) - .catch(err => console.log(err)); - }); - } -}; -startWhatsAppSessions(); - -app.use(Sentry.Handlers.errorHandler()); - -app.use(async (err, req, res, next) => { - if (process.env.NODE_ENV === "DEVELOPMENT") { - const errors = await new Youch(err, req).toJSON(); - console.log(err); - return res.status(500).json(errors); - } - - return res.status(500).json({ error: "Internal server error" }); -}); diff --git a/backend/src/server.ts b/backend/src/server.ts new file mode 100644 index 0000000..79af569 --- /dev/null +++ b/backend/src/server.ts @@ -0,0 +1,86 @@ +import "express-async-errors"; +import express from "express"; +import path from "path"; +import cors from "cors"; +import multer from "multer"; +// import Sentry from "@sentry/node"; +// require("./database"); + +console.log("eae"); + +// const { initWbot } = require("./libs/wbot"); +// const wbotMessageListener = require("./services/wbotMessageListener"); +// const wbotMonitor = require("./services/wbotMonitor"); +// const Whatsapp = require("./models/Whatsapp"); + +// const Router = require("./router"); + +// const app = express(); + +// const server = app.listen(process.env.PORT, () => { +// console.log(`Server started on port: ${process.env.PORT}`); +// }); + +// Sentry.init({ dsn: process.env.SENTRY_DSN }); + +// const fileStorage = multer.diskStorage({ +// destination: (req, file, cb) => { +// cb(null, path.resolve(__dirname, "..", "public")); +// }, +// filename: (req, file, cb) => { +// cb(null, new Date().getTime() + path.extname(file.originalname)); +// }, +// }); + +// app.use(Sentry.Handlers.requestHandler()); +// app.use(cors()); +// app.use(express.json()); +// app.use(multer({ storage: fileStorage }).single("media")); +// app.use("/public", express.static(path.join(__dirname, "..", "public"))); +// app.use(Router); + +// const io = require("./libs/socket").init(server); +// io.on("connection", socket => { +// console.log("Client Connected"); +// socket.on("joinChatBox", ticketId => { +// console.log("A client joined a ticket channel"); +// socket.join(ticketId); +// }); + +// socket.on("joinNotification", () => { +// console.log("A client joined notification channel"); +// socket.join("notification"); +// }); + +// socket.on("disconnect", () => { +// console.log("Client disconnected"); +// }); +// }); + +// const startWhatsAppSessions = async () => { +// const whatsapps = await Whatsapp.findAll(); + +// if (whatsapps.length > 0) { +// whatsapps.forEach(whatsapp => { +// initWbot(whatsapp) +// .then(() => { +// wbotMessageListener(whatsapp); +// wbotMonitor(whatsapp); +// }) +// .catch(err => console.log(err)); +// }); +// } +// }; +// startWhatsAppSessions(); + +// app.use(Sentry.Handlers.errorHandler()); + +// app.use(async (err, req, res, next) => { +// if (process.env.NODE_ENV === "DEVELOPMENT") { +// const errors = await new Youch(err, req).toJSON(); +// console.log(err); +// return res.status(500).json(errors); +// } + +// return res.status(500).json({ error: "Internal server error" }); +// }); diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..e121133 --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,69 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */, + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/backend/yarn.lock b/backend/yarn.lock index 74122d6..10c94e0 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2,6 +2,27 @@ # yarn lockfile v1 +"@babel/code-frame@^7.0.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/runtime@^7.10.5": version "7.11.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" @@ -9,6 +30,22 @@ dependencies: regenerator-runtime "^0.13.4" +"@eslint/eslintrc@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" + integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.19" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@pedroslopez/moduleraid@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@pedroslopez/moduleraid/-/moduleraid-4.1.0.tgz#468f7195fddc9f367e672ace9269f0698cf4c404" @@ -94,16 +131,97 @@ dependencies: defer-to-connect "^1.0.1" +"@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/connect@*": + version "3.4.33" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" + integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + dependencies: + "@types/node" "*" + +"@types/cors@^2.8.7": + version "2.8.7" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.7.tgz#ab2f47f1cba93bce27dfd3639b006cc0e5600889" + integrity sha512-sOdDRU3oRS7LBNTIqwDkPJyq0lpHYcbMTt0TrjzsXbk/e37hcLTH6eZX7CdbDeN0yJJvzw9hFBZkbtCSbk/jAQ== + dependencies: + "@types/express" "*" + +"@types/express-serve-static-core@*": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz#9a487da757425e4f267e7d1c5720226af7f89591" + integrity sha512-EaEdY+Dty1jEU7U6J4CUWwxL+hyEGMkO5jan5gplfegUgCUsIUWqXxqw47uGjimeT4Qgkz/XUfwoau08+fgvKA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.8": + version "4.17.8" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" + integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/mime@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" + integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== + +"@types/multer@^1.4.4": + version "1.4.4" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.4.tgz#bb5d9abc410da82726ceca74008bb81813349a88" + integrity sha512-wdfkiKBBEMTODNbuF3J+qDDSqJxt50yB9pgDiTcFew7f97Gcc7/sM4HR66ofGgpJPOALWOqKAch4gPyqEXSkeQ== + dependencies: + "@types/express" "*" + "@types/node@*": version "14.6.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a" integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ== +"@types/qs@*": + version "6.9.4" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" + integrity sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/serve-static@*": + version "1.13.5" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53" + integrity sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + "@types/yauzl@^2.9.1": version "2.9.1" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" @@ -124,6 +242,16 @@ accepts@~1.3.4, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn-jsx@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" + integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -141,6 +269,16 @@ agent-base@6: dependencies: debug "4" +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: + version "6.12.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" @@ -148,6 +286,11 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-regex@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -163,7 +306,7 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== -ansi-styles@^3.2.0: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -201,6 +344,23 @@ append-field@^1.0.0: resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -211,6 +371,11 @@ arraybuffer.slice@~0.0.7: resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" @@ -272,7 +437,7 @@ blob@0.0.5: resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== -bluebird@^3.7.2: +bluebird@^3.5.0, bluebird@^3.5.3, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -376,6 +541,24 @@ callsite@1.0.0: resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -389,6 +572,15 @@ cardinal@^2.1.1: ansicolors "~0.3.2" redeyed "~2.1.0" +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" @@ -397,7 +589,15 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@^3.2.2: +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@^3.2.2, chokidar@^3.4.0: version "3.4.2" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== @@ -455,6 +655,14 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +cls-bluebird@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cls-bluebird/-/cls-bluebird-2.1.0.tgz#37ef1e080a8ffb55c2f4164f536f1919e7968aee" + integrity sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4= + dependencies: + is-bluebird "^1.0.2" + shimmer "^1.1.0" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -556,7 +764,7 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.3.1, cookie@^0.3.1: +cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= @@ -584,11 +792,27 @@ cors@^2.8.5: object-assign "^4" vary "^1" +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -602,6 +826,14 @@ date-fns@^2.16.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== +dateformat@~1.0.4-1.2.3: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + debug@2.6.9, debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -609,7 +841,7 @@ debug@2.6.9, debug@^2.2.0: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -630,7 +862,7 @@ debug@~3.1.0: dependencies: ms "2.0.0" -decamelize@^1.2.0: +decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -647,6 +879,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-is@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -680,6 +917,18 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -702,6 +951,13 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +dynamic-dedupe@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= + dependencies: + xtend "^4.0.0" + ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -786,6 +1042,20 @@ engine.io@~3.4.0: engine.io-parser "~2.2.0" ws "^7.1.2" +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" @@ -832,11 +1102,117 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -esprima@~4.0.0: +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-scope@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^7.9.0: + version "7.9.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.9.0.tgz#522aeccc5c3a19017cf0cb46ebfd660a79acf337" + integrity sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.1.3" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + eslint-scope "^5.1.0" + eslint-utils "^2.1.0" + eslint-visitor-keys "^1.3.0" + espree "^7.3.0" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.19" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" + integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -909,6 +1285,21 @@ extract-zip@^2.0.0: optionalDependencies: "@types/yauzl" "^2.9.1" +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -916,6 +1307,13 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -936,6 +1334,14 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -951,6 +1357,20 @@ find-up@^4.0.0: locate-path "^5.0.0" path-exists "^4.0.0" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + fn-name@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" @@ -990,6 +1410,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + generate-function@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" @@ -1002,6 +1427,11 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -1016,7 +1446,7 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -glob-parent@~5.1.0: +glob-parent@^5.0.0, glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== @@ -1042,6 +1472,13 @@ global-dirs@^2.0.1: dependencies: ini "^1.3.5" +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -1091,6 +1528,11 @@ has-yarn@^2.1.0: resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -1158,6 +1600,19 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" @@ -1168,6 +1623,13 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" @@ -1206,6 +1668,11 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1213,6 +1680,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-bluebird@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bluebird/-/is-bluebird-1.0.2.tgz#096439060f4aa411abee19143a84d6a55346d6e2" + integrity sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI= + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -1225,6 +1697,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -1235,7 +1712,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -1285,6 +1762,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -1305,6 +1787,11 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + js-beautify@^1.8.8: version "1.13.0" resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.13.0.tgz#a056d5d3acfd4918549aae3ab039f9f3c51eebb2" @@ -1316,11 +1803,34 @@ js-beautify@^1.8.8: mkdirp "^1.0.4" nopt "^5.0.0" +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1380,6 +1890,25 @@ latest-version@^5.0.0: dependencies: package-json "^6.3.0" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -1435,7 +1964,7 @@ lodash.once@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash@^4.17.15, lodash@^4.17.5: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.5: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -1445,6 +1974,14 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -1489,6 +2026,16 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -1508,6 +2055,22 @@ memoizee@^0.4.14: next-tick "1" timers-ext "^0.1.5" +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -1552,7 +2115,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -1574,17 +2137,17 @@ mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -moment-timezone@^0.5.31: +moment-timezone@^0.5.21: version "0.5.31" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05" integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA== dependencies: moment ">= 2.9.0" -"moment@>= 2.9.0", moment@^2.26.0: - version "2.27.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" - integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== +"moment@>= 2.9.0", moment@^2.24.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.28.0.tgz#cdfe73ce01327cee6537b0fafac2e0f21a237d75" + integrity sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw== ms@2.0.0: version "2.0.0" @@ -1615,11 +2178,6 @@ multer@^1.4.2: type-is "^1.6.4" xtend "^4.0.0" -mustache@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/mustache/-/mustache-3.2.1.tgz#89e78a9d207d78f2799b1e95764a25bf71a28322" - integrity sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA== - mysql2@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.1.0.tgz#55ecfd4353114c148cc4c253192dbbfd000e6642" @@ -1642,6 +2200,11 @@ named-placeholders@^1.1.2: dependencies: lru-cache "^4.1.3" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -1687,6 +2250,16 @@ nopt@~1.0.10: dependencies: abbrev "1" +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -1697,7 +2270,7 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== -object-assign@^4, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -1721,6 +2294,18 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -1762,6 +2347,20 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -1781,6 +2380,13 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -1796,6 +2402,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -1806,6 +2417,15 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -1816,6 +2436,23 @@ picomatch@^2.0.4, picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -1823,6 +2460,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" @@ -1833,7 +2475,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.1: +progress@^2.0.0, progress@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -1879,6 +2521,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + pupa@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" @@ -1939,6 +2586,23 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + readable-stream@1.1.x: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -1978,6 +2642,14 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + redeyed@~2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" @@ -1990,6 +2662,11 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== +regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + registry-auth-token@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" @@ -2004,6 +2681,13 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -2014,7 +2698,12 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -resolve@^1.5.0: +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.0.0, resolve@^1.10.0, resolve@^1.5.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -2035,6 +2724,20 @@ retry-as-promised@^3.2.0: dependencies: any-promise "^1.3.0" +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -2064,7 +2767,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -2074,7 +2777,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2: +semver@^7.2.1: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== @@ -2103,42 +2806,45 @@ seq-queue@^0.0.5: resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4= -sequelize-cli@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-6.2.0.tgz#fd02bfeae23b8226872f9947f3f8212cc49a4771" - integrity sha512-6WQ2x91hg30dUn66mXHnzvHATZ4pyI1GHSNbS/TNN/vRR4BLRSLijadeMgC8zqmKDsL0VqzVVopJWfJakuP++Q== +sequelize-cli@5: + version "5.5.1" + resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-5.5.1.tgz#0b9c2fc04d082cc8ae0a8fe270b96bb606152bab" + integrity sha512-ZM4kUZvY3y14y+Rq3cYxGH7YDJz11jWHcN2p2x7rhAIemouu4CEXr5ebw30lzTBtyXV4j2kTO+nUjZOqzG7k+Q== dependencies: + bluebird "^3.5.3" cli-color "^1.4.0" fs-extra "^7.0.1" js-beautify "^1.8.8" lodash "^4.17.5" resolve "^1.5.0" - umzug "^2.3.0" + umzug "^2.1.0" yargs "^13.1.0" -sequelize-pool@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-6.1.0.tgz#caaa0c1e324d3c2c3a399fed2c7998970925d668" - integrity sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg== +sequelize-pool@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-2.3.0.tgz#64f1fe8744228172c474f530604b6133be64993d" + integrity sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA== -sequelize@^6.3.5: - version "6.3.5" - resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-6.3.5.tgz#80e3db7ac8b76d98c45ca93334197eb6e2335158" - integrity sha512-MiwiPkYSA8NWttRKAXdU9h0TxP6HAc1fl7qZmMO/VQqQOND83G4nZLXd0kWILtAoT9cxtZgFqeb/MPYgEeXwsw== +sequelize@5: + version "5.22.3" + resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.22.3.tgz#7e7a92ddd355d883c9eb11cdb106d874d0d2636f" + integrity sha512-+nxf4TzdrB+PRmoWhR05TP9ukLAurK7qtKcIFv5Vhxm5Z9v+d2PcTT6Ea3YAoIQVkZ47QlT9XWAIUevMT/3l8Q== dependencies: + bluebird "^3.5.0" + cls-bluebird "^2.1.0" debug "^4.1.1" dottie "^2.0.0" inflection "1.12.0" lodash "^4.17.15" - moment "^2.26.0" - moment-timezone "^0.5.31" + moment "^2.24.0" + moment-timezone "^0.5.21" retry-as-promised "^3.2.0" - semver "^7.3.2" - sequelize-pool "^6.0.0" + semver "^6.3.0" + sequelize-pool "^2.3.0" toposort-class "^1.0.1" - uuid "^8.1.0" + uuid "^3.3.3" validator "^10.11.0" - wkx "^0.5.0" + wkx "^0.4.8" serve-static@1.14.1: version "1.14.1" @@ -2160,16 +2866,42 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shimmer@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + sigmund@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= -signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + socket.io-adapter@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" @@ -2225,16 +2957,55 @@ socket.io@^2.3.0: socket.io-client "2.3.0" socket.io-parser "~3.4.0" +source-map-support@^0.5.12, source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + sqlstring@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514" integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg== -stack-trace@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= - "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -2296,12 +3067,36 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-json-comments@~2.0.1: +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -supports-color@^5.5.0: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -2320,6 +3115,16 @@ synchronous-promise@^2.0.13: resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.13.tgz#9d8c165ddee69c5a6542862b405bc50095926702" integrity sha512-R9N6uDkVsghHePKh1TEqbnLddO2IY25OcsksyFp/qBe7XYd0PVbKEWxhcdMhpLzE1I6skj5l4aEZ3CRxcbArlA== +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + tar-fs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5" @@ -2346,6 +3151,11 @@ term-size@^2.1.0: resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -2398,11 +3208,66 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +ts-node-dev@^1.0.0-pre.62: + version "1.0.0-pre.62" + resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.62.tgz#835644c43669b659a880379b9d06df86cef665ad" + integrity sha512-hfsEuCqUZOVnZ86l7A3icxD1nFt1HEmLVbx4YOHCkrbSHPBNWcw+IczAPZo3zz7YiOm9vs0xG6OENNrkgm89tQ== + dependencies: + chokidar "^3.4.0" + dateformat "~1.0.4-1.2.3" + dynamic-dedupe "^0.3.0" + minimist "^1.2.5" + mkdirp "^1.0.4" + resolve "^1.0.0" + rimraf "^2.6.1" + source-map-support "^0.5.12" + tree-kill "^1.2.2" + ts-node "^8.10.2" + tsconfig "^7.0.0" + +ts-node@^8.10.2: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== + dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" + strip-bom "^3.0.0" + strip-json-comments "^2.0.0" + tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -2438,7 +3303,12 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -umzug@^2.3.0: +typescript@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== + +umzug@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/umzug/-/umzug-2.3.0.tgz#0ef42b62df54e216b05dcaf627830a6a8b84a184" integrity sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw== @@ -2496,6 +3366,13 @@ update-notifier@^4.0.0: semver-diff "^3.1.1" xdg-basedir "^4.0.0" +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -2513,10 +3390,23 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^8.1.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" - integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== +uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache@^2.0.3: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" validator@^10.11.0: version "10.11.0" @@ -2543,6 +3433,13 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -2550,13 +3447,18 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -wkx@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c" - integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg== +wkx@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.8.tgz#a092cf088d112683fdc7182fd31493b2c5820003" + integrity sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ== dependencies: "@types/node" "*" +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -2581,6 +3483,13 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@^7.1.2, ws@^7.2.3: version "7.3.1" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" @@ -2660,14 +3569,10 @@ yeast@0.1.2: resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= -youch@^2.0.10: - version "2.0.10" - resolved "https://registry.yarnpkg.com/youch/-/youch-2.0.10.tgz#e0f6312b12304fd330a0c4a0e0925b0123f7d495" - integrity sha512-qPLQW2TuwlcK9sm5i1Gbb9ezRZRZyzr6NsY5cqxsbh+2iEyKPxLlz0OSAc+pQ7mv1pYZLri1MXynggP6R2FcNQ== - dependencies: - cookie "^0.3.1" - mustache "^3.0.0" - stack-trace "0.0.10" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yup@^0.29.3: version "0.29.3" From da33e2880de098d5aaf74d85c39dd2504f56d651 Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 14 Sep 2020 10:37:05 -0300 Subject: [PATCH 02/54] added eslint and prrettier config files --- backend/.eslintignore | 3 + backend/.eslintrc.json | 40 + backend/package.json | 8 + backend/prettier.config.js | 5 + backend/src/router/index.js | 21 - backend/src/router/routes/users.js | 18 - backend/src/routes/index.ts | 23 + backend/src/{router => routes}/routes/auth.js | 0 .../src/{router => routes}/routes/contacts.js | 0 .../src/{router => routes}/routes/messages.js | 0 .../src/{router => routes}/routes/settings.js | 0 .../src/{router => routes}/routes/tickets.js | 0 .../src/{router => routes}/routes/whatsapp.js | 0 .../routes/whatsappsessions.js | 0 backend/src/routes/userRoutes.ts | 20 + backend/src/server.ts | 17 +- backend/yarn-error.log | 4199 +++++++++++++++++ backend/yarn.lock | 593 ++- 18 files changed, 4890 insertions(+), 57 deletions(-) create mode 100644 backend/.eslintignore create mode 100644 backend/.eslintrc.json create mode 100644 backend/prettier.config.js delete mode 100644 backend/src/router/index.js delete mode 100644 backend/src/router/routes/users.js create mode 100644 backend/src/routes/index.ts rename backend/src/{router => routes}/routes/auth.js (100%) rename backend/src/{router => routes}/routes/contacts.js (100%) rename backend/src/{router => routes}/routes/messages.js (100%) rename backend/src/{router => routes}/routes/settings.js (100%) rename backend/src/{router => routes}/routes/tickets.js (100%) rename backend/src/{router => routes}/routes/whatsapp.js (100%) rename backend/src/{router => routes}/routes/whatsappsessions.js (100%) create mode 100644 backend/src/routes/userRoutes.ts create mode 100644 backend/yarn-error.log diff --git a/backend/.eslintignore b/backend/.eslintignore new file mode 100644 index 0000000..77b9a34 --- /dev/null +++ b/backend/.eslintignore @@ -0,0 +1,3 @@ +/*.js +node_modules +dist diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json new file mode 100644 index 0000000..00b6080 --- /dev/null +++ b/backend/.eslintrc.json @@ -0,0 +1,40 @@ +{ + "env": { + "es2021": true, + "node": true + }, + "extends": [ + "airbnb-base", + "plugin:@typescript-eslint/recommended", + "prettier/@typescript-eslint", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint", "prettier"], + "rules": { + "prettier/prettier": "error", + "import/extensions": [ + "error", + "ignorePackages", + { + "ts": "never" + } + ], + "quotes": [ + 1, + "double", + { + "avoidEscape": true + } + ] + }, + "settings": { + "import/resolver": { + "typescript": {} + } + } +} diff --git a/backend/package.json b/backend/package.json index 0eaae99..3a192fe 100644 --- a/backend/package.json +++ b/backend/package.json @@ -31,8 +31,16 @@ "@types/cors": "^2.8.7", "@types/express": "^4.17.8", "@types/multer": "^1.4.4", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", "eslint": "^7.9.0", + "eslint-config-airbnb-base": "^14.2.0", + "eslint-config-prettier": "^6.11.0", + "eslint-import-resolver-typescript": "^2.3.0", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-prettier": "^3.1.4", "nodemon": "^2.0.4", + "prettier": "^2.1.1", "ts-node-dev": "^1.0.0-pre.62", "typescript": "^4.0.2" } diff --git a/backend/prettier.config.js b/backend/prettier.config.js new file mode 100644 index 0000000..ad0b92d --- /dev/null +++ b/backend/prettier.config.js @@ -0,0 +1,5 @@ +module.exports = { + singleQuote: false, + trailingComma: "none", + arrowParens: "avoid", +}; diff --git a/backend/src/router/index.js b/backend/src/router/index.js deleted file mode 100644 index 094ceb3..0000000 --- a/backend/src/router/index.js +++ /dev/null @@ -1,21 +0,0 @@ -const express = require("express"); - -const AuthRoutes = require("./routes/auth"); -const TicketsRoutes = require("./routes/tickets"); -const MessagesRoutes = require("./routes/messages"); -const ContactsRoutes = require("./routes/contacts"); -const WhatsRoutes = require("./routes/whatsapp"); -const UsersRoutes = require("./routes/users"); -const SettingsRoutes = require("./routes/settings"); - -const routes = express.Router(); - -routes.use("/auth", AuthRoutes); -routes.use(TicketsRoutes); -routes.use(MessagesRoutes); -routes.use(ContactsRoutes); -routes.use(WhatsRoutes); -routes.use(UsersRoutes); -routes.use(SettingsRoutes); - -module.exports = routes; diff --git a/backend/src/router/routes/users.js b/backend/src/router/routes/users.js deleted file mode 100644 index 8a50e61..0000000 --- a/backend/src/router/routes/users.js +++ /dev/null @@ -1,18 +0,0 @@ -const express = require("express"); - -const isAuth = require("../../middleware/is-auth"); -const UserController = require("../../controllers/UserController"); - -const routes = express.Router(); - -routes.get("/users", isAuth, UserController.index); - -routes.post("/users", isAuth, UserController.store); - -routes.put("/users/:userId", isAuth, UserController.update); - -routes.get("/users/:userId", isAuth, UserController.show); - -routes.delete("/users/:userId", isAuth, UserController.delete); - -module.exports = routes; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts new file mode 100644 index 0000000..1dffd0a --- /dev/null +++ b/backend/src/routes/index.ts @@ -0,0 +1,23 @@ +import { Router } from "express"; + +import userRoutes from "./userRoutes"; + +// const AuthRoutes = require("./routes/auth"); +// const TicketsRoutes = require("./routes/tickets"); +// const MessagesRoutes = require("./routes/messages"); +// const ContactsRoutes = require("./routes/contacts"); +// const WhatsRoutes = require("./routes/whatsapp"); +// const UsersRoutes = require("./routes/users"); +// const SettingsRoutes = require("./routes/settings"); + +const routes = Router(); + +routes.use(userRoutes); +// routes.use("/auth", AuthRoutes); +// routes.use(TicketsRoutes); +// routes.use(MessagesRoutes); +// routes.use(ContactsRoutes); +// routes.use(WhatsRoutes); +// routes.use(SettingsRoutes); + +export default routes; diff --git a/backend/src/router/routes/auth.js b/backend/src/routes/routes/auth.js similarity index 100% rename from backend/src/router/routes/auth.js rename to backend/src/routes/routes/auth.js diff --git a/backend/src/router/routes/contacts.js b/backend/src/routes/routes/contacts.js similarity index 100% rename from backend/src/router/routes/contacts.js rename to backend/src/routes/routes/contacts.js diff --git a/backend/src/router/routes/messages.js b/backend/src/routes/routes/messages.js similarity index 100% rename from backend/src/router/routes/messages.js rename to backend/src/routes/routes/messages.js diff --git a/backend/src/router/routes/settings.js b/backend/src/routes/routes/settings.js similarity index 100% rename from backend/src/router/routes/settings.js rename to backend/src/routes/routes/settings.js diff --git a/backend/src/router/routes/tickets.js b/backend/src/routes/routes/tickets.js similarity index 100% rename from backend/src/router/routes/tickets.js rename to backend/src/routes/routes/tickets.js diff --git a/backend/src/router/routes/whatsapp.js b/backend/src/routes/routes/whatsapp.js similarity index 100% rename from backend/src/router/routes/whatsapp.js rename to backend/src/routes/routes/whatsapp.js diff --git a/backend/src/router/routes/whatsappsessions.js b/backend/src/routes/routes/whatsappsessions.js similarity index 100% rename from backend/src/router/routes/whatsappsessions.js rename to backend/src/routes/routes/whatsappsessions.js diff --git a/backend/src/routes/userRoutes.ts b/backend/src/routes/userRoutes.ts new file mode 100644 index 0000000..cc77043 --- /dev/null +++ b/backend/src/routes/userRoutes.ts @@ -0,0 +1,20 @@ +import { Router } from "express"; + +// const isAuth = require("../../middleware/is-auth"); +// const UserController = require("../../controllers/UserController"); + +const userRoutes = Router(); + +userRoutes.get("/users", (req, res) => + res.json({ meessage: "lets do some prettier shit here" }) +); + +// routes.post("/users", isAuth, UserController.store); + +// routes.put("/users/:userId", isAuth, UserController.update); + +// routes.get("/users/:userId", isAuth, UserController.show); + +// routes.delete("/users/:userId", isAuth, UserController.delete); + +export default userRoutes; diff --git a/backend/src/server.ts b/backend/src/server.ts index 79af569..bf1b2d1 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,13 +1,13 @@ import "express-async-errors"; import express from "express"; -import path from "path"; -import cors from "cors"; -import multer from "multer"; + +import routes from "./routes"; +// import path from "path"; +// import cors from "cors"; +// import multer from "multer"; // import Sentry from "@sentry/node"; // require("./database"); -console.log("eae"); - // const { initWbot } = require("./libs/wbot"); // const wbotMessageListener = require("./services/wbotMessageListener"); // const wbotMonitor = require("./services/wbotMonitor"); @@ -15,7 +15,12 @@ console.log("eae"); // const Router = require("./router"); -// const app = express(); +const app = express(); + +app.use(routes); +app.listen(8080, () => { + console.log("stated"); +}); // const server = app.listen(process.env.PORT, () => { // console.log(`Server started on port: ${process.env.PORT}`); diff --git a/backend/yarn-error.log b/backend/yarn-error.log new file mode 100644 index 0000000..60a9bbf --- /dev/null +++ b/backend/yarn-error.log @@ -0,0 +1,4199 @@ +Arguments: + /usr/bin/node /usr/share/yarn/bin/yarn.js add -D prettier eslint-config-prettier esling-plugin-prettier + +PATH: + /home/canove/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/bin:/home/canove/.zinit/polaris/sbin:/home/canove/.zinit/polaris/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/ProgramData/chocolatey/bin:/mnt/c/Program Files/Microsoft VS Code/bin:/mnt/c/Program Files (x86)/GNU/GnuPG/pub:/mnt/c/Users/cassio/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/cassio/AppData/Local/GitHubDesktop/bin + +Yarn version: + 1.22.4 + +Node version: + 12.18.2 + +Platform: + linux x64 + +Trace: + Error: https://registry.yarnpkg.com/esling-plugin-prettier: Not found + at Request.params.callback [as _callback] (/usr/share/yarn/lib/cli.js:66987:18) + at Request.self.callback (/usr/share/yarn/lib/cli.js:140748:22) + at Request.emit (events.js:315:20) + at Request. (/usr/share/yarn/lib/cli.js:141720:10) + at Request.emit (events.js:315:20) + at IncomingMessage. (/usr/share/yarn/lib/cli.js:141642:12) + at Object.onceWrapper (events.js:421:28) + at IncomingMessage.emit (events.js:327:22) + at endReadableNT (_stream_readable.js:1221:12) + at processTicksAndRejections (internal/process/task_queues.js:84:21) + +npm manifest: + { + "name": "backend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" + }, + "author": "", + "license": "MIT", + "dependencies": { + "@sentry/node": "5.22.3", + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "date-fns": "^2.16.1", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "express-async-errors": "^3.1.1", + "jsonwebtoken": "^8.5.1", + "multer": "^1.4.2", + "mysql2": "^2.1.0", + "qrcode-terminal": "^0.12.0", + "sequelize": "5", + "sequelize-cli": "5", + "socket.io": "^2.3.0", + "whatsapp-web.js": "^1.8.2", + "yup": "^0.29.3" + }, + "devDependencies": { + "@types/cors": "^2.8.7", + "@types/express": "^4.17.8", + "@types/multer": "^1.4.4", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "eslint": "^7.9.0", + "eslint-config-airbnb-base": "^14.2.0", + "eslint-import-resolver-typescript": "^2.3.0", + "eslint-plugin-import": "^2.21.2", + "nodemon": "^2.0.4", + "ts-node-dev": "^1.0.0-pre.62", + "typescript": "^4.0.2" + } + } + +yarn manifest: + No manifest + +Lockfile: + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + # yarn lockfile v1 + + + "@babel/code-frame@^7.0.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + + "@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + + "@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + + "@babel/runtime@^7.10.5": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== + dependencies: + regenerator-runtime "^0.13.4" + + "@eslint/eslintrc@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" + integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.19" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + + "@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + + "@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + + "@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + + "@pedroslopez/moduleraid@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@pedroslopez/moduleraid/-/moduleraid-4.1.0.tgz#468f7195fddc9f367e672ace9269f0698cf4c404" + integrity sha512-+2OYf1IDLhZlOY3QEwGfZJKfM/uPB3lw9zedl3eqEMtod73r1R7TUnmlh8S7hM8uLUcrWXMnJ9TrMbxXeV1pZg== + + "@sentry/core@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.22.3.tgz#030f435f2b518f282ba8bd954dac90cd70888bd7" + integrity sha512-eGL5uUarw3o4i9QUb9JoFHnhriPpWCaqeaIBB06HUpdcvhrjoowcKZj1+WPec5lFg5XusE35vez7z/FPzmJUDw== + dependencies: + "@sentry/hub" "5.22.3" + "@sentry/minimal" "5.22.3" + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + tslib "^1.9.3" + + "@sentry/hub@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.22.3.tgz#08309a70d2ea8d5e313d05840c1711f34f2fffe5" + integrity sha512-INo47m6N5HFEs/7GMP9cqxOIt7rmRxdERunA3H2L37owjcr77MwHVeeJ9yawRS6FMtbWXplgWTyTIWIYOuqVbw== + dependencies: + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + tslib "^1.9.3" + + "@sentry/minimal@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.22.3.tgz#706e4029ae5494123d3875c658ba8911aa5cc440" + integrity sha512-HoINpYnVYCpNjn2XIPIlqH5o4BAITpTljXjtAftOx6Hzj+Opjg8tR8PWliyKDvkXPpc4kXK9D6TpEDw8MO0wZA== + dependencies: + "@sentry/hub" "5.22.3" + "@sentry/types" "5.22.3" + tslib "^1.9.3" + + "@sentry/node@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.22.3.tgz#adea622eae6811e11edc8f34209c912caed91336" + integrity sha512-TCCKO7hJKiQi1nGmJcQfvbbqv98P08LULh7pb/NaO5pV20t1FtICfGx8UMpORRDehbcAiYq/f7rPOF6X/Xl5iw== + dependencies: + "@sentry/core" "5.22.3" + "@sentry/hub" "5.22.3" + "@sentry/tracing" "5.22.3" + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + + "@sentry/tracing@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.22.3.tgz#9b5a376e3164c007a22e8642ec094104468cac0c" + integrity sha512-Zp59kMCk5v56ZAyErqjv/QvGOWOQ5fRltzeVQVp8unIDTk6gEFXfhwPsYHOokJe1mfkmrgPDV6xAkYgtL3KCDQ== + dependencies: + "@sentry/hub" "5.22.3" + "@sentry/minimal" "5.22.3" + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + tslib "^1.9.3" + + "@sentry/types@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.22.3.tgz#d1d547b30ee8bd7771fa893af74c4f3d71f0fd18" + integrity sha512-cv+VWK0YFgCVDvD1/HrrBWOWYG3MLuCUJRBTkV/Opdy7nkdNjhCAJQrEyMM9zX0sac8FKWKOHT0sykNh8KgmYw== + + "@sentry/utils@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.22.3.tgz#e3bda3e789239eb16d436f768daa12829f33d18f" + integrity sha512-AHNryXMBvIkIE+GQxTlmhBXD0Ksh+5w1SwM5qi6AttH+1qjWLvV6WB4+4pvVvEoS8t5F+WaVUZPQLmCCWp6zKw== + dependencies: + "@sentry/types" "5.22.3" + tslib "^1.9.3" + + "@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + + "@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + + "@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + + "@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + + "@types/connect@*": + version "3.4.33" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" + integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + dependencies: + "@types/node" "*" + + "@types/cors@^2.8.7": + version "2.8.7" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.7.tgz#ab2f47f1cba93bce27dfd3639b006cc0e5600889" + integrity sha512-sOdDRU3oRS7LBNTIqwDkPJyq0lpHYcbMTt0TrjzsXbk/e37hcLTH6eZX7CdbDeN0yJJvzw9hFBZkbtCSbk/jAQ== + dependencies: + "@types/express" "*" + + "@types/express-serve-static-core@*": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz#9a487da757425e4f267e7d1c5720226af7f89591" + integrity sha512-EaEdY+Dty1jEU7U6J4CUWwxL+hyEGMkO5jan5gplfegUgCUsIUWqXxqw47uGjimeT4Qgkz/XUfwoau08+fgvKA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + + "@types/express@*", "@types/express@^4.17.8": + version "4.17.8" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" + integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/qs" "*" + "@types/serve-static" "*" + + "@types/json-schema@^7.0.3": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + + "@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + + "@types/mime@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" + integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== + + "@types/multer@^1.4.4": + version "1.4.4" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.4.tgz#bb5d9abc410da82726ceca74008bb81813349a88" + integrity sha512-wdfkiKBBEMTODNbuF3J+qDDSqJxt50yB9pgDiTcFew7f97Gcc7/sM4HR66ofGgpJPOALWOqKAch4gPyqEXSkeQ== + dependencies: + "@types/express" "*" + + "@types/node@*": + version "14.6.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a" + integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ== + + "@types/qs@*": + version "6.9.4" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" + integrity sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ== + + "@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + + "@types/serve-static@*": + version "1.13.5" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53" + integrity sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + + "@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= + + "@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + + "@types/yauzl@^2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" + integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== + dependencies: + "@types/node" "*" + + "@typescript-eslint/eslint-plugin@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.1.0.tgz#7d309f60815ff35e9627ad85e41928d7b7fd443f" + integrity sha512-U+nRJx8XDUqJxYF0FCXbpmD9nWt/xHDDG0zsw1vrVYAmEAuD/r49iowfurjSL2uTA2JsgtpsyG7mjO7PHf2dYw== + dependencies: + "@typescript-eslint/experimental-utils" "4.1.0" + "@typescript-eslint/scope-manager" "4.1.0" + debug "^4.1.1" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + semver "^7.3.2" + tsutils "^3.17.1" + + "@typescript-eslint/experimental-utils@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.1.0.tgz#263d7225645c09a411c8735eeffd417f50f49026" + integrity sha512-paEYLA37iqRIDPeQwAmoYSiZ3PiHsaAc3igFeBTeqRHgPnHjHLJ9OGdmP6nwAkF65p2QzEsEBtpjNUBWByNWzA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.1.0" + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/typescript-estree" "4.1.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + + "@typescript-eslint/parser@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.1.0.tgz#9b0409411725f14cd7faa81a664e5051225961db" + integrity sha512-hM/WNCQTzDHgS0Ke3cR9zPndL3OTKr9OoN9CL3UqulsAjYDrglSwIIgswSmHBcSbOzLmgaMARwrQEbIumIglvQ== + dependencies: + "@typescript-eslint/scope-manager" "4.1.0" + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/typescript-estree" "4.1.0" + debug "^4.1.1" + + "@typescript-eslint/scope-manager@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.1.0.tgz#9e389745ee9cfe12252ed1e9958808abd6b3a683" + integrity sha512-HD1/u8vFNnxwiHqlWKC/Pigdn0Mvxi84Y6GzbZ5f5sbLrFKu0al02573Er+D63Sw67IffVUXR0uR8rpdfdk+vA== + dependencies: + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/visitor-keys" "4.1.0" + + "@typescript-eslint/types@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.1.0.tgz#edbd3fec346f34e13ce7aa176b03b497a32c496a" + integrity sha512-rkBqWsO7m01XckP9R2YHVN8mySOKKY2cophGM8K5uDK89ArCgahItQYdbg/3n8xMxzu2elss+an1TphlUpDuJw== + + "@typescript-eslint/typescript-estree@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.1.0.tgz#394046ead25164494218c0e3d6b960695ea967f6" + integrity sha512-r6et57qqKAWU173nWyw31x7OfgmKfMEcjJl9vlJEzS+kf9uKNRr4AVTRXfTCwebr7bdiVEkfRY5xGnpPaNPe4Q== + dependencies: + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/visitor-keys" "4.1.0" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + + "@typescript-eslint/visitor-keys@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.1.0.tgz#b2d528c9484e7eda1aa4f86ccf0432fb16e4d545" + integrity sha512-+taO0IZGCtCEsuNTTF2Q/5o8+fHrlml8i9YsZt2AiDCdYEJzYlsmRY991l/6f3jNXFyAWepdQj7n8Na6URiDRQ== + dependencies: + "@typescript-eslint/types" "4.1.0" + eslint-visitor-keys "^2.0.0" + + abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + + accepts@~1.3.4, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + + acorn-jsx@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + + acorn@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" + integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== + + after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + + agent-base@5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + + agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== + dependencies: + debug "4" + + ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: + version "6.12.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + + ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== + dependencies: + string-width "^3.0.0" + + ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + + ansi-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + + ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + + ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + + ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + + ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + + ansicolors@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= + + any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + + anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + + append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= + + arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + + argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + + array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + + array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + + array-includes@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + + array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + + array.prototype.flat@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + + arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + + astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + + async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + + backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + + balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + + base64-arraybuffer@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + + base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + + base64id@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + + bcryptjs@^2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= + + better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= + dependencies: + callsite "1.0.0" + + binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + + bl@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" + integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + + blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + + bluebird@^3.5.0, bluebird@^3.5.3, bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + + body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + + boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + + brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + + braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + + buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + + buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + + buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + + buffer@^5.2.1, buffer@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + + busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM= + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + + bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + + cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + + callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= + + callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + + camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + + camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + + camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + + cardinal@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" + integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU= + dependencies: + ansicolors "~0.3.2" + redeyed "~2.1.0" + + chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + + chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + + chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + + chokidar@^3.2.2, chokidar@^3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + + chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + + ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + + cli-boxes@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + + cli-color@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.4.0.tgz#7d10738f48526824f8fe7da51857cb0f572fe01f" + integrity sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w== + dependencies: + ansi-regex "^2.1.1" + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + memoizee "^0.4.14" + timers-ext "^0.1.5" + + cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + + clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + + cls-bluebird@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cls-bluebird/-/cls-bluebird-2.1.0.tgz#37ef1e080a8ffb55c2f4164f536f1919e7968aee" + integrity sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4= + dependencies: + is-bluebird "^1.0.2" + shimmer "^1.1.0" + + color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + + color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + + color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + + color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + + commander@^2.19.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + + component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + + component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + + component-emitter@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + + component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + + concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + + concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + + config-chain@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + + configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + + confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== + + contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + + content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + + content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + + cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + + cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + + cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + + cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + + core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + + cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + + cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + + crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + + currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + + d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + + date-fns@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" + integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== + + dateformat@~1.0.4-1.2.3: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + + debug@2.6.9, debug@^2.2.0, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + + debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + + debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + + debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + + decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + + decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + + deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + + deep-is@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + + defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + + define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + + denque@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" + integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== + + depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + + destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + + devtools-protocol@0.0.781568: + version "0.0.781568" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.781568.tgz#4cdca90a952d2c77831096ff6cd32695d8715a04" + integrity sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg== + + dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8= + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + + diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + + dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + + doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + + doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + + dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + + dotenv@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + + dottie@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.2.tgz#cc91c0726ce3a054ebf11c55fbc92a7f266dd154" + integrity sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg== + + duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + + dynamic-dedupe@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= + dependencies: + xtend "^4.0.0" + + ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + + editorconfig@^0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" + integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g== + dependencies: + commander "^2.19.0" + lru-cache "^4.1.5" + semver "^5.6.0" + sigmund "^1.0.1" + + ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + + emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + + emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + + encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + + end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + + engine.io-client@~3.4.0: + version "3.4.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.3.tgz#192d09865403e3097e3575ebfeb3861c4d01a66c" + integrity sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw== + dependencies: + component-emitter "~1.3.0" + component-inherit "0.0.3" + debug "~4.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.5" + parseuri "0.0.5" + ws "~6.1.0" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + + engine.io-parser@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed" + integrity sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.5" + blob "0.0.5" + has-binary2 "~1.0.2" + + engine.io@~3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.4.2.tgz#8fc84ee00388e3e228645e0a7d3dfaeed5bd122c" + integrity sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg== + dependencies: + accepts "~1.3.4" + base64id "2.0.0" + cookie "0.3.1" + debug "~4.1.0" + engine.io-parser "~2.2.0" + ws "^7.1.2" + + enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + + error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + + es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + + es-abstract@^1.18.0-next.0: + version "1.18.0-next.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc" + integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + + es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + + es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + + es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + + es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + + es6-weak-map@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + + escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + + escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + + escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + + eslint-config-airbnb-base@^14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz#fe89c24b3f9dc8008c9c0d0d88c28f95ed65e9c4" + integrity sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q== + dependencies: + confusing-browser-globals "^1.0.9" + object.assign "^4.1.0" + object.entries "^1.1.2" + + eslint-import-resolver-node@^0.3.3: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + + eslint-import-resolver-typescript@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.3.0.tgz#0870988098bc6c6419c87705e6b42bee89425445" + integrity sha512-MHSXvmj5e0SGOOBhBbt7C+fWj1bJbtSYFAD85Xeg8nvUtuooTod2HQb8bfhE9f5QyyNxEfgzqOYFCvmdDIcCuw== + dependencies: + debug "^4.1.1" + glob "^7.1.6" + is-glob "^4.0.1" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + + eslint-module-utils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + + eslint-plugin-import@^2.21.2: + version "2.22.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" + integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg== + dependencies: + array-includes "^3.1.1" + array.prototype.flat "^1.2.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.3" + eslint-module-utils "^2.6.0" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.1" + read-pkg-up "^2.0.0" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + + eslint-scope@^5.0.0, eslint-scope@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + + eslint-utils@^2.0.0, eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + + eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + + eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + + eslint@^7.9.0: + version "7.9.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.9.0.tgz#522aeccc5c3a19017cf0cb46ebfd660a79acf337" + integrity sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.1.3" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + eslint-scope "^5.1.0" + eslint-utils "^2.1.0" + eslint-visitor-keys "^1.3.0" + espree "^7.3.0" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.19" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + + espree@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" + integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.3.0" + + esprima@^4.0.0, esprima@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + + esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + + esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + + estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + + estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + + esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + + etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + + event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + + express-async-errors@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/express-async-errors/-/express-async-errors-3.1.1.tgz#6053236d61d21ddef4892d6bd1d736889fc9da41" + integrity sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng== + + express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + + ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + + extract-zip@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + + fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + + fast-glob@^3.1.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + + fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + + fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + + fastq@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" + integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== + dependencies: + reusify "^1.0.4" + + fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + + file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + + fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + + finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + + find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + + find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + + find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + + find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + + flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + + flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + + fn-name@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" + integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA== + + forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + + fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + + fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + + fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + + fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + + fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + + function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + + functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + + generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + + get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + + get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + + get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + + get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + + glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + + glob@^7.1.3, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + + global-dirs@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" + integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A== + dependencies: + ini "^1.3.5" + + globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + + globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + + got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + + graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + + has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + dependencies: + isarray "2.0.1" + + has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + + has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + + has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + + has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + + has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + + has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + + hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + + http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + + http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + + http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + + https-proxy-agent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" + integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== + dependencies: + agent-base "5" + debug "4" + + https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + + iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + + iconv-lite@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.2.tgz#af6d628dccfb463b7364d97f715e4b74b8c8c2b8" + integrity sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag== + dependencies: + safer-buffer ">= 2.1.2 < 3" + + ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + + ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + + ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + + ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + + import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + + import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + + imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + + indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + + indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + + inflection@1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.12.0.tgz#a200935656d6f5f6bc4dc7502e1aecb703228416" + integrity sha1-ogCTVlbW9fa8TcdQLhrstwMihBY= + + inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + + inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + + inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + + ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + + ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + + is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + + is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + + is-bluebird@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bluebird/-/is-bluebird-1.0.2.tgz#096439060f4aa411abee19143a84d6a55346d6e2" + integrity sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI= + + is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.1.tgz#4d1e21a4f437509d25ce55f8184350771421c96d" + integrity sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg== + + is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + + is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + + is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + + is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + + is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + + is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + + is-installed-globally@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + + is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + + is-npm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" + integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== + + is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + + is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + + is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + + is-promise@^2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + + is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= + + is-regex@^1.1.0, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + + is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + + is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + + is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + + is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + + is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + + isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + + isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + + isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + + isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + + js-beautify@^1.8.8: + version "1.13.0" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.13.0.tgz#a056d5d3acfd4918549aae3ab039f9f3c51eebb2" + integrity sha512-/Tbp1OVzZjbwzwJQFIlYLm9eWQ+3aYbBXLSaqb1mEJzhcQAfrqMMQYtjb6io+U6KpD0ID4F+Id3/xcjH3l/sqA== + dependencies: + config-chain "^1.1.12" + editorconfig "^0.15.3" + glob "^7.1.3" + mkdirp "^1.0.4" + nopt "^5.0.0" + + js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + + js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + + json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + + json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + + json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + + json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + + jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + + jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + + jsqr@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.3.1.tgz#515a766e58b00c80142f3a2dc4b8751100ceedcf" + integrity sha512-zCTP6Qd/WwjrpuHFkJuXc5opRdKprUr7eI7+JCCtcetThJt45qptu82MWQ+eET+FtDrMo7+BYjo3iD0XIq1L9Q== + + jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + + jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + + keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + + latest-version@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + + levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + + load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + + load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + + locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + + locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + + locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + + lodash-es@^4.17.11: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== + + lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + + lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + + lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + + lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + + lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + + lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + + lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + + lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.5: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + + long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + + loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + + lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + + lru-cache@^4.1.3, lru-cache@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + + lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + + lru-queue@0.1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + + lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= + + make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + + make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + + map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + + media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + + memoizee@^0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" + integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg== + dependencies: + d "1" + es5-ext "^0.10.45" + es6-weak-map "^2.0.2" + event-emitter "^0.3.5" + is-promise "^2.1" + lru-queue "0.1" + next-tick "1" + timers-ext "^0.1.5" + + meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + + merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + + merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + + methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + + micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + + mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + + mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + + mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + + mime@^2.0.3, mime@^2.4.5: + version "2.4.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" + integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== + + mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + + minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + + minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + + mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + + mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + + mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + + moment-timezone@^0.5.21: + version "0.5.31" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05" + integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA== + dependencies: + moment ">= 2.9.0" + + "moment@>= 2.9.0", moment@^2.24.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.28.0.tgz#cdfe73ce01327cee6537b0fafac2e0f21a237d75" + integrity sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw== + + ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + + ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + + ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + + multer@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.2.tgz#2f1f4d12dbaeeba74cb37e623f234bf4d3d2057a" + integrity sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg== + dependencies: + append-field "^1.0.0" + busboy "^0.2.11" + concat-stream "^1.5.2" + mkdirp "^0.5.1" + object-assign "^4.1.1" + on-finished "^2.3.0" + type-is "^1.6.4" + xtend "^4.0.0" + + mysql2@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.1.0.tgz#55ecfd4353114c148cc4c253192dbbfd000e6642" + integrity sha512-9kGVyi930rG2KaHrz3sHwtc6K+GY9d8wWk1XRSYxQiunvGcn4DwuZxOwmK11ftuhhwrYDwGx9Ta4VBwznJn36A== + dependencies: + cardinal "^2.1.1" + denque "^1.4.1" + generate-function "^2.3.1" + iconv-lite "^0.5.0" + long "^4.0.0" + lru-cache "^5.1.1" + named-placeholders "^1.1.2" + seq-queue "^0.0.5" + sqlstring "^2.3.1" + + named-placeholders@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.2.tgz#ceb1fbff50b6b33492b5cf214ccf5e39cef3d0e8" + integrity sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA== + dependencies: + lru-cache "^4.1.3" + + natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + + negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + + next-tick@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + + next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + + nodemon@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" + integrity sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ== + dependencies: + chokidar "^3.2.2" + debug "^3.2.6" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.7" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.2" + update-notifier "^4.0.0" + + nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + + nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + + normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + + normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + + normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + + object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + + object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= + + object-inspect@^1.7.0, object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + + object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + + object.assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" + + object.entries@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" + integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + has "^1.0.3" + + object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + + on-finished@^2.3.0, on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + + once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + + optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + + p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + + p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + + p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + + p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + + p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + + p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + + p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + + p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + + package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + + parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + + parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + + parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + dependencies: + better-assert "~1.0.0" + + parseuri@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + dependencies: + better-assert "~1.0.0" + + parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + + path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + + path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + + path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + + path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + + path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + + path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + + path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + + path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + + path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + + path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + + pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + + picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + + pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + + pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + + pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + + pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + + pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + + prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + + prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + + process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + + progress@^2.0.0, progress@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + + property-expr@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" + integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== + + proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + + proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + + proxy-from-env@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + + pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + + pstree.remy@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + + pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + + punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + + pupa@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" + integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== + dependencies: + escape-goat "^2.0.0" + + puppeteer@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.2.1.tgz#7f0564f0a5384f352a38c8cc42af875cd87f4ea6" + integrity sha512-PZoZG7u+T6N1GFWBQmGVG162Ak5MAy8nYSVpeeQrwJK2oYUlDWpHEJPcd/zopyuEMTv7DiztS1blgny1txR2qw== + dependencies: + debug "^4.1.0" + devtools-protocol "0.0.781568" + extract-zip "^2.0.0" + https-proxy-agent "^4.0.0" + mime "^2.0.3" + pkg-dir "^4.2.0" + progress "^2.0.1" + proxy-from-env "^1.0.0" + rimraf "^3.0.2" + tar-fs "^2.0.0" + unbzip2-stream "^1.3.3" + ws "^7.2.3" + + qrcode-terminal@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" + integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== + + qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + + range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + + raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + + rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + + read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + + read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + + read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + + read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + + readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + + readable-stream@^2.2.2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + + readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + + readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + + redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + + redeyed@~2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" + integrity sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs= + dependencies: + esprima "~4.0.0" + + regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + + regexpp@^3.0.0, regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + + registry-auth-token@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" + integrity sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w== + dependencies: + rc "^1.2.8" + + registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + + repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + + require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + + require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + + resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + + resolve@^1.0.0, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.5.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + + responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + + retry-as-promised@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-3.2.0.tgz#769f63d536bec4783549db0777cb56dadd9d8543" + integrity sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg== + dependencies: + any-promise "^1.3.0" + + reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + + rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + + rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + + rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + + run-parallel@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + + safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + + "safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + + semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + + "semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + + semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + + semver@^7.2.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + + send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + + seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4= + + sequelize-cli@5: + version "5.5.1" + resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-5.5.1.tgz#0b9c2fc04d082cc8ae0a8fe270b96bb606152bab" + integrity sha512-ZM4kUZvY3y14y+Rq3cYxGH7YDJz11jWHcN2p2x7rhAIemouu4CEXr5ebw30lzTBtyXV4j2kTO+nUjZOqzG7k+Q== + dependencies: + bluebird "^3.5.3" + cli-color "^1.4.0" + fs-extra "^7.0.1" + js-beautify "^1.8.8" + lodash "^4.17.5" + resolve "^1.5.0" + umzug "^2.1.0" + yargs "^13.1.0" + + sequelize-pool@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-2.3.0.tgz#64f1fe8744228172c474f530604b6133be64993d" + integrity sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA== + + sequelize@5: + version "5.22.3" + resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.22.3.tgz#7e7a92ddd355d883c9eb11cdb106d874d0d2636f" + integrity sha512-+nxf4TzdrB+PRmoWhR05TP9ukLAurK7qtKcIFv5Vhxm5Z9v+d2PcTT6Ea3YAoIQVkZ47QlT9XWAIUevMT/3l8Q== + dependencies: + bluebird "^3.5.0" + cls-bluebird "^2.1.0" + debug "^4.1.1" + dottie "^2.0.0" + inflection "1.12.0" + lodash "^4.17.15" + moment "^2.24.0" + moment-timezone "^0.5.21" + retry-as-promised "^3.2.0" + semver "^6.3.0" + sequelize-pool "^2.3.0" + toposort-class "^1.0.1" + uuid "^3.3.3" + validator "^10.11.0" + wkx "^0.4.8" + + serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + + set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + + setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + + shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + + shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + + shimmer@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + + sigmund@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + + signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + + slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + + slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + + socket.io-adapter@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" + integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== + + socket.io-client@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4" + integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~4.1.0" + engine.io-client "~3.4.0" + has-binary2 "~1.0.2" + has-cors "1.1.0" + indexof "0.0.1" + object-component "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + socket.io-parser "~3.3.0" + to-array "0.1.4" + + socket.io-parser@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" + integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + isarray "2.0.1" + + socket.io-parser@~3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.1.tgz#b06af838302975837eab2dc980037da24054d64a" + integrity sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A== + dependencies: + component-emitter "1.2.1" + debug "~4.1.0" + isarray "2.0.1" + + socket.io@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.3.0.tgz#cd762ed6a4faeca59bc1f3e243c0969311eb73fb" + integrity sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg== + dependencies: + debug "~4.1.0" + engine.io "~3.4.0" + has-binary2 "~1.0.2" + socket.io-adapter "~1.1.0" + socket.io-client "2.3.0" + socket.io-parser "~3.4.0" + + source-map-support@^0.5.12, source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + + source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + + spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + + spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + + spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + + spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + + sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + + sqlstring@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514" + integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg== + + "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + + streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + + string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + + string-width@^4.0.0, string-width@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + + string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + + string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + + string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + + string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + + string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + + strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + + strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + + strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + + strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + + strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + + strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + + supports-color@^5.3.0, supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + + supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + + synchronous-promise@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.13.tgz#9d8c165ddee69c5a6542862b405bc50095926702" + integrity sha512-R9N6uDkVsghHePKh1TEqbnLddO2IY25OcsksyFp/qBe7XYd0PVbKEWxhcdMhpLzE1I6skj5l4aEZ3CRxcbArlA== + + table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + + tar-fs@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5" + integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + + tar-stream@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.3.tgz#1e2022559221b7866161660f118255e20fa79e41" + integrity sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA== + dependencies: + bl "^4.0.1" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + + term-size@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" + integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== + + text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + + through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + + timers-ext@^0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + + to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + + to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + + to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + + toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + + toposort-class@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" + integrity sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg= + + toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= + + touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + + tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + + trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + + ts-node-dev@^1.0.0-pre.62: + version "1.0.0-pre.62" + resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.62.tgz#835644c43669b659a880379b9d06df86cef665ad" + integrity sha512-hfsEuCqUZOVnZ86l7A3icxD1nFt1HEmLVbx4YOHCkrbSHPBNWcw+IczAPZo3zz7YiOm9vs0xG6OENNrkgm89tQ== + dependencies: + chokidar "^3.4.0" + dateformat "~1.0.4-1.2.3" + dynamic-dedupe "^0.3.0" + minimist "^1.2.5" + mkdirp "^1.0.4" + resolve "^1.0.0" + rimraf "^2.6.1" + source-map-support "^0.5.12" + tree-kill "^1.2.2" + ts-node "^8.10.2" + tsconfig "^7.0.0" + + ts-node@^8.10.2: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + + tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + + tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== + dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" + strip-bom "^3.0.0" + strip-json-comments "^2.0.0" + + tslib@^1.8.1, tslib@^1.9.3: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + + tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + + type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + + type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + + type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + + type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + + type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + + typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + + typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + + typescript@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== + + umzug@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/umzug/-/umzug-2.3.0.tgz#0ef42b62df54e216b05dcaf627830a6a8b84a184" + integrity sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw== + dependencies: + bluebird "^3.7.2" + + unbzip2-stream@^1.3.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + + undefsafe@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" + integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== + dependencies: + debug "^2.2.0" + + unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + + universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + + unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + + update-notifier@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.1.tgz#895fc8562bbe666179500f9f2cebac4f26323746" + integrity sha512-9y+Kds0+LoLG6yN802wVXoIfxYEwh3FlZwzMwpCZp62S2i1/Jzeqb9Eeeju3NSHccGGasfGlK5/vEHbAifYRDg== + dependencies: + boxen "^4.2.0" + chalk "^3.0.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.3.1" + is-npm "^4.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.0.0" + pupa "^2.0.1" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + + uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + + url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + + util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + + utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + + uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + + v8-compile-cache@^2.0.3: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + + validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + + validator@^10.11.0: + version "10.11.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-10.11.0.tgz#003108ea6e9a9874d31ccc9e5006856ccd76b228" + integrity sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw== + + vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + + whatsapp-web.js@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/whatsapp-web.js/-/whatsapp-web.js-1.8.2.tgz#b9059ca7cb70d6297c3cba954ccf1ce0c004d52e" + integrity sha512-vLfq8QxUp/hi/V7hhvLaZJzGOpXvJW6tTOrM5QKNflebCYZXJZNEOaJqkSjgcrqSsKPJHKW7gmkO3mx20pNmYA== + dependencies: + "@pedroslopez/moduleraid" "^4.1.0" + jsqr "^1.3.1" + mime "^2.4.5" + puppeteer "^5.2.1" + + which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + + which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + + widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + + wkx@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.8.tgz#a092cf088d112683fdc7182fd31493b2c5820003" + integrity sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ== + dependencies: + "@types/node" "*" + + word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + + wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + + wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + + write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + + write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + + ws@^7.1.2, ws@^7.2.3: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + + ws@~6.1.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" + integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== + dependencies: + async-limiter "~1.0.0" + + xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + + xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= + + xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + + y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + + yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + + yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + + yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + + yargs@^13.1.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + + yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + + yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= + + yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + + yup@^0.29.3: + version "0.29.3" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.29.3.tgz#69a30fd3f1c19f5d9e31b1cf1c2b851ce8045fea" + integrity sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ== + dependencies: + "@babel/runtime" "^7.10.5" + fn-name "~3.0.0" + lodash "^4.17.15" + lodash-es "^4.17.11" + property-expr "^2.0.2" + synchronous-promise "^2.0.13" + toposort "^2.0.2" diff --git a/backend/yarn.lock b/backend/yarn.lock index 10c94e0..d0a00b2 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -46,6 +46,27 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + "@pedroslopez/moduleraid@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@pedroslopez/moduleraid/-/moduleraid-4.1.0.tgz#468f7195fddc9f367e672ace9269f0698cf4c404" @@ -177,6 +198,16 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/json-schema@^7.0.3": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + "@types/mime@*": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" @@ -229,6 +260,76 @@ dependencies: "@types/node" "*" +"@typescript-eslint/eslint-plugin@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.1.0.tgz#7d309f60815ff35e9627ad85e41928d7b7fd443f" + integrity sha512-U+nRJx8XDUqJxYF0FCXbpmD9nWt/xHDDG0zsw1vrVYAmEAuD/r49iowfurjSL2uTA2JsgtpsyG7mjO7PHf2dYw== + dependencies: + "@typescript-eslint/experimental-utils" "4.1.0" + "@typescript-eslint/scope-manager" "4.1.0" + debug "^4.1.1" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/experimental-utils@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.1.0.tgz#263d7225645c09a411c8735eeffd417f50f49026" + integrity sha512-paEYLA37iqRIDPeQwAmoYSiZ3PiHsaAc3igFeBTeqRHgPnHjHLJ9OGdmP6nwAkF65p2QzEsEBtpjNUBWByNWzA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.1.0" + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/typescript-estree" "4.1.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/parser@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.1.0.tgz#9b0409411725f14cd7faa81a664e5051225961db" + integrity sha512-hM/WNCQTzDHgS0Ke3cR9zPndL3OTKr9OoN9CL3UqulsAjYDrglSwIIgswSmHBcSbOzLmgaMARwrQEbIumIglvQ== + dependencies: + "@typescript-eslint/scope-manager" "4.1.0" + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/typescript-estree" "4.1.0" + debug "^4.1.1" + +"@typescript-eslint/scope-manager@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.1.0.tgz#9e389745ee9cfe12252ed1e9958808abd6b3a683" + integrity sha512-HD1/u8vFNnxwiHqlWKC/Pigdn0Mvxi84Y6GzbZ5f5sbLrFKu0al02573Er+D63Sw67IffVUXR0uR8rpdfdk+vA== + dependencies: + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/visitor-keys" "4.1.0" + +"@typescript-eslint/types@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.1.0.tgz#edbd3fec346f34e13ce7aa176b03b497a32c496a" + integrity sha512-rkBqWsO7m01XckP9R2YHVN8mySOKKY2cophGM8K5uDK89ArCgahItQYdbg/3n8xMxzu2elss+an1TphlUpDuJw== + +"@typescript-eslint/typescript-estree@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.1.0.tgz#394046ead25164494218c0e3d6b960695ea967f6" + integrity sha512-r6et57qqKAWU173nWyw31x7OfgmKfMEcjJl9vlJEzS+kf9uKNRr4AVTRXfTCwebr7bdiVEkfRY5xGnpPaNPe4Q== + dependencies: + "@typescript-eslint/types" "4.1.0" + "@typescript-eslint/visitor-keys" "4.1.0" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/visitor-keys@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.1.0.tgz#b2d528c9484e7eda1aa4f86ccf0432fb16e4d545" + integrity sha512-+taO0IZGCtCEsuNTTF2Q/5o8+fHrlml8i9YsZt2AiDCdYEJzYlsmRY991l/6f3jNXFyAWepdQj7n8Na6URiDRQ== + dependencies: + "@typescript-eslint/types" "4.1.0" + eslint-visitor-keys "^2.0.0" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -366,6 +467,28 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array-includes@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flat@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" @@ -480,7 +603,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@~3.0.2: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -747,6 +870,16 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" +confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -834,7 +967,7 @@ dateformat@~1.0.4-1.2.3: get-stdin "^4.0.1" meow "^3.3.0" -debug@2.6.9, debug@^2.2.0: +debug@2.6.9, debug@^2.2.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -889,6 +1022,13 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + denque@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" @@ -922,6 +1062,21 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -1056,6 +1211,50 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.0: + version "1.18.0-next.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc" + integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" @@ -1107,7 +1306,76 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-scope@^5.1.0: +eslint-config-airbnb-base@^14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz#fe89c24b3f9dc8008c9c0d0d88c28f95ed65e9c4" + integrity sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q== + dependencies: + confusing-browser-globals "^1.0.9" + object.assign "^4.1.0" + object.entries "^1.1.2" + +eslint-config-prettier@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + dependencies: + get-stdin "^6.0.0" + +eslint-import-resolver-node@^0.3.3: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-import-resolver-typescript@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.3.0.tgz#0870988098bc6c6419c87705e6b42bee89425445" + integrity sha512-MHSXvmj5e0SGOOBhBbt7C+fWj1bJbtSYFAD85Xeg8nvUtuooTod2HQb8bfhE9f5QyyNxEfgzqOYFCvmdDIcCuw== + dependencies: + debug "^4.1.1" + glob "^7.1.6" + is-glob "^4.0.1" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + +eslint-module-utils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.21.2: + version "2.22.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" + integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg== + dependencies: + array-includes "^3.1.1" + array.prototype.flat "^1.2.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.3" + eslint-module-utils "^2.6.0" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.1" + read-pkg-up "^2.0.0" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + +eslint-plugin-prettier@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2" + integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@^5.0.0, eslint-scope@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -1115,7 +1383,7 @@ eslint-scope@^5.1.0: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: +eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -1127,6 +1395,11 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + eslint@^7.9.0: version "7.9.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.9.0.tgz#522aeccc5c3a19017cf0cb46ebfd660a79acf337" @@ -1290,6 +1563,23 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^3.1.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -1300,6 +1590,13 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastq@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" + integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== + dependencies: + reusify "^1.0.4" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -1342,6 +1639,13 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -1410,6 +1714,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -1432,6 +1741,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -1446,14 +1760,14 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -glob-parent@^5.0.0, glob-parent@~5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" -glob@^7.1.3: +glob@^7.1.3, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -1479,6 +1793,18 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -1523,11 +1849,23 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -1605,6 +1943,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" @@ -1685,6 +2028,11 @@ is-bluebird@^1.0.2: resolved "https://registry.yarnpkg.com/is-bluebird/-/is-bluebird-1.0.2.tgz#096439060f4aa411abee19143a84d6a55346d6e2" integrity sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI= +is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.1.tgz#4d1e21a4f437509d25ce55f8184350771421c96d" + integrity sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -1692,6 +2040,11 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1727,6 +2080,11 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" @@ -1757,6 +2115,25 @@ is-property@^1.0.2: resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= +is-regex@^1.1.0, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -1782,7 +2159,7 @@ isarray@2.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= -isarray@~1.0.0: +isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -1831,6 +2208,13 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1909,6 +2293,24 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -2076,11 +2478,24 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + mime-db@1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" @@ -2280,6 +2695,45 @@ object-component@0.0.3: resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= +object-inspect@^1.7.0, object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.entries@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" + integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + has "^1.0.3" + +object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -2311,6 +2765,13 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -2318,6 +2779,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -2332,6 +2800,11 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -2426,12 +2899,24 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== @@ -2453,6 +2938,13 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -2470,6 +2962,18 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6" + integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -2594,6 +3098,14 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -2603,6 +3115,15 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + readable-stream@1.1.x: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -2662,7 +3183,7 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== -regexpp@^3.1.0: +regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -2703,7 +3224,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.0.0, resolve@^1.10.0, resolve@^1.5.0: +resolve@^1.0.0, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.5.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -2724,6 +3245,11 @@ retry-as-promised@^3.2.0: dependencies: any-promise "^1.3.0" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -2745,6 +3271,11 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +run-parallel@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -2777,7 +3308,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1: +semver@^7.2.1, semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== @@ -2893,6 +3424,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -3034,6 +3570,22 @@ string-width@^4.0.0, string-width@^4.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -3246,6 +3798,16 @@ ts-node@^8.10.2: source-map-support "^0.5.17" yn "3.1.1" +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tsconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" @@ -3256,11 +3818,18 @@ tsconfig@^7.0.0: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@^1.9.3: +tslib@^1.8.1, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From 32f7e48362fe825c86dcf81781d25f1df8884a9b Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 14 Sep 2020 10:42:50 -0300 Subject: [PATCH 03/54] update esling config --- backend/.eslintrc.json | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json index 00b6080..2ebe3c9 100644 --- a/backend/.eslintrc.json +++ b/backend/.eslintrc.json @@ -16,6 +16,7 @@ }, "plugins": ["@typescript-eslint", "prettier"], "rules": { + "no-console": "off", "prettier/prettier": "error", "import/extensions": [ "error", From f18bab145f8fea523e54c61a8487fa0316884e5e Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 14 Sep 2020 12:42:20 -0300 Subject: [PATCH 04/54] started migration of user domain to ts --- backend/package.json | 6 + backend/src/@types/express.d.ts | 5 + backend/src/config/database.js | 16 -- backend/src/config/database.ts | 17 ++ backend/src/controllers/OldUserController.js | 181 ++++++++++++++++++ backend/src/controllers/UserController.js | 181 ------------------ backend/src/controllers/UserController.ts | 69 +++++++ backend/src/database/index.js | 36 ---- backend/src/database/index.ts | 53 +++++ backend/src/errors/AppError.ts | 11 ++ backend/src/helpers/CheckSettingsHelper.ts | 18 ++ backend/src/models/User.js | 32 ---- backend/src/models/User.ts | 82 ++++++++ backend/src/{models => modelsOld}/Contact.js | 0 .../ContactCustomField.js | 0 backend/src/{models => modelsOld}/Message.js | 0 backend/src/{models => modelsOld}/Setting.js | 0 backend/src/{models => modelsOld}/Ticket.js | 0 backend/src/modelsOld/User.js | 32 ++++ backend/src/{models => modelsOld}/Whatsapp.js | 0 backend/src/routes/userRoutes.ts | 10 +- backend/src/server.ts | 19 +- backend/src/services/CreateUserService.ts | 53 +++++ backend/src/services/FindUserService.ts | 21 ++ backend/src/services/ListUsersService.ts | 54 ++++++ backend/src/services/UpdateUserService.ts | 61 ++++++ backend/tsconfig.json | 120 ++++++------ backend/yarn.lock | 44 +++++ 28 files changed, 781 insertions(+), 340 deletions(-) create mode 100644 backend/src/@types/express.d.ts delete mode 100644 backend/src/config/database.js create mode 100644 backend/src/config/database.ts create mode 100644 backend/src/controllers/OldUserController.js delete mode 100644 backend/src/controllers/UserController.js create mode 100644 backend/src/controllers/UserController.ts delete mode 100644 backend/src/database/index.js create mode 100644 backend/src/database/index.ts create mode 100644 backend/src/errors/AppError.ts create mode 100644 backend/src/helpers/CheckSettingsHelper.ts delete mode 100644 backend/src/models/User.js create mode 100644 backend/src/models/User.ts rename backend/src/{models => modelsOld}/Contact.js (100%) rename backend/src/{models => modelsOld}/ContactCustomField.js (100%) rename backend/src/{models => modelsOld}/Message.js (100%) rename backend/src/{models => modelsOld}/Setting.js (100%) rename backend/src/{models => modelsOld}/Ticket.js (100%) create mode 100644 backend/src/modelsOld/User.js rename backend/src/{models => modelsOld}/Whatsapp.js (100%) create mode 100644 backend/src/services/CreateUserService.ts create mode 100644 backend/src/services/FindUserService.ts create mode 100644 backend/src/services/ListUsersService.ts create mode 100644 backend/src/services/UpdateUserService.ts diff --git a/backend/package.json b/backend/package.json index 3a192fe..142717d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -21,16 +21,22 @@ "multer": "^1.4.2", "mysql2": "^2.1.0", "qrcode-terminal": "^0.12.0", + "reflect-metadata": "^0.1.13", "sequelize": "5", "sequelize-cli": "5", + "sequelize-typescript": "^1.1.0", "socket.io": "^2.3.0", "whatsapp-web.js": "^1.8.2", "yup": "^0.29.3" }, "devDependencies": { + "@types/bluebird": "^3.5.32", "@types/cors": "^2.8.7", "@types/express": "^4.17.8", "@types/multer": "^1.4.4", + "@types/node": "^14.10.1", + "@types/validator": "^13.1.0", + "@types/yup": "^0.29.7", "@typescript-eslint/eslint-plugin": "^4.1.0", "@typescript-eslint/parser": "^4.1.0", "eslint": "^7.9.0", diff --git a/backend/src/@types/express.d.ts b/backend/src/@types/express.d.ts new file mode 100644 index 0000000..204f2e0 --- /dev/null +++ b/backend/src/@types/express.d.ts @@ -0,0 +1,5 @@ +declare namespace Express { + export interface Request { + user: { id: string; profile: string }; + } +} diff --git a/backend/src/config/database.js b/backend/src/config/database.js deleted file mode 100644 index 5fae881..0000000 --- a/backend/src/config/database.js +++ /dev/null @@ -1,16 +0,0 @@ -require("dotenv/config"); - -module.exports = { - define: { - charset: "utf8mb4", - collate: "utf8mb4_bin", - }, - dialect: "mysql", - timezone: "-03:00", - host: process.env.DB_HOST, - database: process.env.DB_NAME, - username: process.env.DB_USER, - password: process.env.DB_PASS, - logging: false, - seederStorage: "sequelize", -}; diff --git a/backend/src/config/database.ts b/backend/src/config/database.ts new file mode 100644 index 0000000..33ccc78 --- /dev/null +++ b/backend/src/config/database.ts @@ -0,0 +1,17 @@ +import "dotenv/config"; + +const dbConfig = { + define: { + charset: "utf8mb4", + collate: "utf8mb4_bin" + }, + dialect: "mysql", + timezone: "-03:00", + host: process.env.DB_HOST, + database: process.env.DB_NAME, + username: process.env.DB_USER, + password: process.env.DB_PASS, + logging: false +}; + +export default dbConfig; diff --git a/backend/src/controllers/OldUserController.js b/backend/src/controllers/OldUserController.js new file mode 100644 index 0000000..13f8186 --- /dev/null +++ b/backend/src/controllers/OldUserController.js @@ -0,0 +1,181 @@ +// const Sequelize = require("sequelize"); +// const Yup = require("yup"); +// const { Op } = require("sequelize"); + +// const User = require("../models/User"); +// const Setting = require("../models/Setting"); + +// const { getIO } = require("../libs/socket"); + +// exports.index = async (req, res) => { +// if (req.user.profile !== "admin") { +// return res +// .status(403) +// .json({ error: "Only administrators can access this route." }); +// } + +// const { searchParam = "", pageNumber = 1 } = req.query; + +// const whereCondition = { +// [Op.or]: [ +// { +// name: Sequelize.where( +// Sequelize.fn("LOWER", Sequelize.col("name")), +// "LIKE", +// "%" + searchParam.toLowerCase() + "%" +// ), +// }, +// { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } }, +// ], +// }; + +// let limit = 20; +// let offset = limit * (pageNumber - 1); + +// const { count, rows: users } = await User.findAndCountAll({ +// attributes: ["name", "id", "email", "profile"], +// where: whereCondition, +// limit, +// offset, +// order: [["createdAt", "DESC"]], +// }); + +// const hasMore = count > offset + users.length; + +// return res.status(200).json({ users, count, hasMore }); +// }; + +export default async (req, res, next) => { + console.log(req.url); + const schema = Yup.object().shape({ + name: Yup.string().required().min(2), + email: Yup.string() + .email() + .required() + .test( + "Check-email", + "An user with this email already exists", + async value => { + const userFound = await User.findOne({ where: { email: value } }); + return !Boolean(userFound); + } + ), + password: Yup.string().required().min(5) + }); + + if (req.url === "/signup") { + const { value: userCreation } = await Setting.findByPk("userCreation"); + + if (userCreation === "disabled") { + return res + .status(403) + .json({ error: "User creation is disabled by administrator." }); + } + } else if (req.user.profile !== "admin") { + return res + .status(403) + .json({ error: "Only administrators can create users." }); + } + + try { + await schema.validate(req.body); + } catch (err) { + return res.status(400).json({ error: err.message }); + } + + const io = getIO(); + + const { name, id, email, profile } = await User.create(req.body); + + io.emit("user", { + action: "create", + user: { name, id, email, profile } + }); + + return res.status(201).json({ message: "User created!", userId: id }); +}; + +// exports.show = async (req, res) => { +// const { userId } = req.params; + +// const user = await User.findByPk(userId, { +// attributes: ["id", "name", "email", "profile"], +// }); + +// if (!user) { +// res.status(400).json({ error: "No user found with this id." }); +// } + +// return res.status(200).json(user); +// }; + +// exports.update = async (req, res) => { +// const schema = Yup.object().shape({ +// name: Yup.string().min(2), +// email: Yup.string().email(), +// password: Yup.string(), +// }); + +// if (req.user.profile !== "admin") { +// return res +// .status(403) +// .json({ error: "Only administrators can edit users." }); +// } + +// await schema.validate(req.body); + +// const io = getIO(); +// const { userId } = req.params; + +// const user = await User.findByPk(userId, { +// attributes: ["name", "id", "email", "profile"], +// }); + +// if (!user) { +// res.status(404).json({ error: "No user found with this id." }); +// } + +// if (user.profile === "admin" && req.body.profile === "user") { +// const adminUsers = await User.count({ where: { profile: "admin" } }); +// if (adminUsers <= 1) { +// return res +// .status(403) +// .json({ error: "There must be at leat one admin user." }); +// } +// } + +// await user.update(req.body); + +// io.emit("user", { +// action: "update", +// user: user, +// }); + +// return res.status(200).json(user); +// }; + +// exports.delete = async (req, res) => { +// const io = getIO(); +// const { userId } = req.params; + +// const user = await User.findByPk(userId); + +// if (!user) { +// res.status(400).json({ error: "No user found with this id." }); +// } + +// if (req.user.profile !== "admin") { +// return res +// .status(403) +// .json({ error: "Only administrators can edit users." }); +// } + +// await user.destroy(); + +// io.emit("user", { +// action: "delete", +// userId: userId, +// }); + +// return res.status(200).json({ message: "User deleted" }); +// }; diff --git a/backend/src/controllers/UserController.js b/backend/src/controllers/UserController.js deleted file mode 100644 index 6dc0e0f..0000000 --- a/backend/src/controllers/UserController.js +++ /dev/null @@ -1,181 +0,0 @@ -const Sequelize = require("sequelize"); -const Yup = require("yup"); -const { Op } = require("sequelize"); - -const User = require("../models/User"); -const Setting = require("../models/Setting"); - -const { getIO } = require("../libs/socket"); - -exports.index = async (req, res) => { - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can access this route." }); - } - - const { searchParam = "", pageNumber = 1 } = req.query; - - const whereCondition = { - [Op.or]: [ - { - name: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ), - }, - { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } }, - ], - }; - - let limit = 20; - let offset = limit * (pageNumber - 1); - - const { count, rows: users } = await User.findAndCountAll({ - attributes: ["name", "id", "email", "profile"], - where: whereCondition, - limit, - offset, - order: [["createdAt", "DESC"]], - }); - - const hasMore = count > offset + users.length; - - return res.status(200).json({ users, count, hasMore }); -}; - -exports.store = async (req, res, next) => { - console.log(req.url); - const schema = Yup.object().shape({ - name: Yup.string().required().min(2), - email: Yup.string() - .email() - .required() - .test( - "Check-email", - "An user with this email already exists", - async value => { - const userFound = await User.findOne({ where: { email: value } }); - return !Boolean(userFound); - } - ), - password: Yup.string().required().min(5), - }); - - if (req.url === "/signup") { - const { value: userCreation } = await Setting.findByPk("userCreation"); - - if (userCreation === "disabled") { - return res - .status(403) - .json({ error: "User creation is disabled by administrator." }); - } - } else if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can create users." }); - } - - try { - await schema.validate(req.body); - } catch (err) { - return res.status(400).json({ error: err.message }); - } - - const io = getIO(); - - const { name, id, email, profile } = await User.create(req.body); - - io.emit("user", { - action: "create", - user: { name, id, email, profile }, - }); - - return res.status(201).json({ message: "User created!", userId: id }); -}; - -exports.show = async (req, res) => { - const { userId } = req.params; - - const user = await User.findByPk(userId, { - attributes: ["id", "name", "email", "profile"], - }); - - if (!user) { - res.status(400).json({ error: "No user found with this id." }); - } - - return res.status(200).json(user); -}; - -exports.update = async (req, res) => { - const schema = Yup.object().shape({ - name: Yup.string().min(2), - email: Yup.string().email(), - password: Yup.string(), - }); - - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can edit users." }); - } - - await schema.validate(req.body); - - const io = getIO(); - const { userId } = req.params; - - const user = await User.findByPk(userId, { - attributes: ["name", "id", "email", "profile"], - }); - - if (!user) { - res.status(404).json({ error: "No user found with this id." }); - } - - if (user.profile === "admin" && req.body.profile === "user") { - const adminUsers = await User.count({ where: { profile: "admin" } }); - if (adminUsers <= 1) { - return res - .status(403) - .json({ error: "There must be at leat one admin user." }); - } - } - - await user.update(req.body); - - io.emit("user", { - action: "update", - user: user, - }); - - return res.status(200).json(user); -}; - -exports.delete = async (req, res) => { - const io = getIO(); - const { userId } = req.params; - - const user = await User.findByPk(userId); - - if (!user) { - res.status(400).json({ error: "No user found with this id." }); - } - - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can edit users." }); - } - - await user.destroy(); - - io.emit("user", { - action: "delete", - userId: userId, - }); - - return res.status(200).json({ message: "User deleted" }); -}; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts new file mode 100644 index 0000000..46d81dd --- /dev/null +++ b/backend/src/controllers/UserController.ts @@ -0,0 +1,69 @@ +import { Request, Response } from "express"; + +// import CheckSettingsHelper from "../helpers/CheckSettingsHelper"; +import AppError from "../errors/AppError"; + +import CreateUserService from "../services/CreateUserService"; +// import UpdateUserService from "../services/UpdateUserService"; +// import ListUsersService from "../services/ListUsersService"; +// import FindUserService from "../services/FindUserService"; + +export const index = async (req: Request, res: Response): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("Only administrators can access this route.", 403); // should be handled better. + } + const { searchParam, pageNumber } = req.query as any; + + const { users, count, hasMore } = await ListUsersService({ + searchParam, + pageNumber + }); + + return res.json({ users, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const { email, password, name, profile } = req.body; + + // if ( + // req.url === "/signup" && + // (await CheckSettingsHelper("userCreation")) === "disabled" + // ) { + // throw new AppError("User creation is disabled by administrator.", 403); + // } else if (req.user.profile !== "admin") { + // throw new AppError("Only administrators can create users.", 403); + // } + + const user = await CreateUserService({ + email, + password, + name, + profile + }); + + return res.status(200).json(user); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { userId } = req.params; + + const user = await FindUserService(userId); + + return res.status(200).json(user); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("Only administrators can edit users.", 403); + } + + const { userId } = req.params; + const userData = req.body; + + const user = await UpdateUserService({ userData, userId }); + + return res.status(200).json(user); +}; diff --git a/backend/src/database/index.js b/backend/src/database/index.js deleted file mode 100644 index fe9956c..0000000 --- a/backend/src/database/index.js +++ /dev/null @@ -1,36 +0,0 @@ -const Sequelize = require("sequelize"); -const dbConfig = require("../config/database"); - -const User = require("../models/User"); -const Contact = require("../models/Contact"); -const Ticket = require("../models/Ticket"); -const Message = require("../models/Message"); -const Whatsapp = require("../models/Whatsapp"); -const ContactCustomField = require("../models/ContactCustomField"); -const Setting = require("../models/Setting"); - -const models = [ - User, - Contact, - Ticket, - Message, - Whatsapp, - ContactCustomField, - Setting, -]; - -class Database { - constructor() { - this.init(); - } - - init() { - this.sequelize = new Sequelize(dbConfig); - - models - .map(model => model.init(this.sequelize)) - .map(model => model.associate && model.associate(this.sequelize.models)); - } -} - -module.exports = new Database(); diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts new file mode 100644 index 0000000..c1aa9aa --- /dev/null +++ b/backend/src/database/index.ts @@ -0,0 +1,53 @@ +import { Sequelize } from "sequelize-typescript"; +import { resolve } from "path"; +// import dbConfig from "../config/database"; +import "dotenv/config"; + +// import User from "../models/User"; +// const Contact = require("../models/Contact"); +// const Ticket = require("../models/Ticket"); +// const Message = require("../models/Message"); +// const Whatsapp = require("../models/Whatsapp"); +// const ContactCustomField = require("../models/ContactCustomField"); +// const Setting = require("../models/Setting"); + +const sequelize = new Sequelize({ + define: { + charset: "utf8mb4", + collate: "utf8mb4_bin" + }, + dialect: "mysql", + timezone: "-03:00", + host: process.env.DB_HOST, + database: process.env.DB_NAME, + username: process.env.DB_USER, + password: process.env.DB_PASS, + models: [resolve(__dirname, "..", "models")], + logging: false +}); + +// const models = [ +// User +// // Contact, +// // Ticket, +// // Message, +// // Whatsapp, +// // ContactCustomField, +// // Setting, +// ]; + +// class Database { +// constructor() { +// this.init(); +// } + +// init() { +// this.sequelize = new Sequelize(dbConfig); + +// models +// .map(model => model.init(this.sequelize)) +// .map(model => model.associate && model.associate(this.sequelize.models)); +// } +// } + +export default sequelize; diff --git a/backend/src/errors/AppError.ts b/backend/src/errors/AppError.ts new file mode 100644 index 0000000..28e6c22 --- /dev/null +++ b/backend/src/errors/AppError.ts @@ -0,0 +1,11 @@ +class AppError { + public readonly message: string; + public readonly statusCode: number; + + constructor(message: string, statusCode = 400) { + this.message = message; + this.statusCode = statusCode; + } +} + +export default AppError; diff --git a/backend/src/helpers/CheckSettingsHelper.ts b/backend/src/helpers/CheckSettingsHelper.ts new file mode 100644 index 0000000..39e8a95 --- /dev/null +++ b/backend/src/helpers/CheckSettingsHelper.ts @@ -0,0 +1,18 @@ +import AppError from "../errors/AppError"; +import Setting from "../models/Setting"; + +const CheckSettings = async (key: string): Promise => { + const settingsRepository = getRepository(Setting); + + const setting = await settingsRepository.findOne({ + where: { key } + }); + + if (!setting) { + throw new AppError("No setting found with this id.", 404); + } + + return setting.value; +}; + +export default CheckSettings; diff --git a/backend/src/models/User.js b/backend/src/models/User.js deleted file mode 100644 index 375f129..0000000 --- a/backend/src/models/User.js +++ /dev/null @@ -1,32 +0,0 @@ -const Sequelize = require("sequelize"); -const bcrypt = require("bcryptjs"); - -class User extends Sequelize.Model { - static init(sequelize) { - super.init( - { - name: { type: Sequelize.STRING }, - password: { type: Sequelize.VIRTUAL }, - profile: { type: Sequelize.STRING, defaultValue: "admin" }, - passwordHash: { type: Sequelize.STRING }, - email: { type: Sequelize.STRING }, - }, - { - sequelize, - } - ); - - this.addHook("beforeSave", async user => { - if (user.password) { - user.passwordHash = await bcrypt.hash(user.password, 8); - } - }); - return this; - } - - checkPassword(password) { - return bcrypt.compare(password, this.passwordHash); - } -} - -module.exports = User; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts new file mode 100644 index 0000000..76b71b3 --- /dev/null +++ b/backend/src/models/User.ts @@ -0,0 +1,82 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType +} from "sequelize-typescript"; + +@Table +class User extends Model { + @Column({ + defaultValue: DataType.UUIDV4, + primaryKey: true, + type: DataType.UUID + }) + id: string; + + @Column + name: string; + + @Column + email: string; + + @Column + passwordHash: string; + + @Column({ + defaultValue: "admin" + }) + profile: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + // @BeforeUpdate + // @BeforeInsert + // hashPassword = async () => { + // if (this.passwordHash) { + // this.passwordHash = await hash(this.passwordHash, 8); + // } + // }; + + // checkPassword = async (password: string) => { + // return await compare(password, this.passwordHash); + // }; +} + +export default User; + +// const bcrypt = require("bcryptjs"); +// @Table +// class User extends Model { +// static init(sequelize) { +// super.init( +// { +// name: { type: Sequelize.STRING }, +// password: { type: Sequelize.VIRTUAL }, +// profile: { type: Sequelize.STRING, defaultValue: "admin" }, +// passwordHash: { type: Sequelize.STRING }, +// email: { type: Sequelize.STRING } +// }, +// { +// sequelize +// } +// ); + +// this.addHook("beforeSave", async user => { +// if (user.password) { +// user.passwordHash = await bcrypt.hash(user.password, 8); +// } +// }); +// return this; +// } + +// checkPassword(password) { +// return bcrypt.compare(password, this.passwordHash); +// } +// } diff --git a/backend/src/models/Contact.js b/backend/src/modelsOld/Contact.js similarity index 100% rename from backend/src/models/Contact.js rename to backend/src/modelsOld/Contact.js diff --git a/backend/src/models/ContactCustomField.js b/backend/src/modelsOld/ContactCustomField.js similarity index 100% rename from backend/src/models/ContactCustomField.js rename to backend/src/modelsOld/ContactCustomField.js diff --git a/backend/src/models/Message.js b/backend/src/modelsOld/Message.js similarity index 100% rename from backend/src/models/Message.js rename to backend/src/modelsOld/Message.js diff --git a/backend/src/models/Setting.js b/backend/src/modelsOld/Setting.js similarity index 100% rename from backend/src/models/Setting.js rename to backend/src/modelsOld/Setting.js diff --git a/backend/src/models/Ticket.js b/backend/src/modelsOld/Ticket.js similarity index 100% rename from backend/src/models/Ticket.js rename to backend/src/modelsOld/Ticket.js diff --git a/backend/src/modelsOld/User.js b/backend/src/modelsOld/User.js new file mode 100644 index 0000000..6617b6c --- /dev/null +++ b/backend/src/modelsOld/User.js @@ -0,0 +1,32 @@ +const Sequelize = require("sequelize"); +const bcrypt = require("bcryptjs"); + +class User extends Sequelize.Model { + static init(sequelize) { + super.init( + { + name: { type: Sequelize.STRING }, + password: { type: Sequelize.VIRTUAL }, + profile: { type: Sequelize.STRING, defaultValue: "admin" }, + passwordHash: { type: Sequelize.STRING }, + email: { type: Sequelize.STRING } + }, + { + sequelize + } + ); + + this.addHook("beforeSave", async user => { + if (user.password) { + user.passwordHash = await bcrypt.hash(user.password, 8); + } + }); + return this; + } + + checkPassword(password) { + return bcrypt.compare(password, this.passwordHash); + } +} + +module.exports = User; diff --git a/backend/src/models/Whatsapp.js b/backend/src/modelsOld/Whatsapp.js similarity index 100% rename from backend/src/models/Whatsapp.js rename to backend/src/modelsOld/Whatsapp.js diff --git a/backend/src/routes/userRoutes.ts b/backend/src/routes/userRoutes.ts index cc77043..e9526a0 100644 --- a/backend/src/routes/userRoutes.ts +++ b/backend/src/routes/userRoutes.ts @@ -1,7 +1,7 @@ import { Router } from "express"; // const isAuth = require("../../middleware/is-auth"); -// const UserController = require("../../controllers/UserController"); +import * as UserController from "../controllers/UserController"; const userRoutes = Router(); @@ -9,12 +9,12 @@ userRoutes.get("/users", (req, res) => res.json({ meessage: "lets do some prettier shit here" }) ); -// routes.post("/users", isAuth, UserController.store); +userRoutes.post("/users", UserController.store); -// routes.put("/users/:userId", isAuth, UserController.update); +// userRoutes.put("/users/:userId", isAuth, UserController.update); -// routes.get("/users/:userId", isAuth, UserController.show); +// userRoutes.get("/users/:userId", isAuth, UserController.show); -// routes.delete("/users/:userId", isAuth, UserController.delete); +// userRoutes.delete("/users/:userId", isAuth, UserController.delete); export default userRoutes; diff --git a/backend/src/server.ts b/backend/src/server.ts index bf1b2d1..8dea37a 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,12 +1,14 @@ +import "dotenv/config"; import "express-async-errors"; import express from "express"; +import cors from "cors"; import routes from "./routes"; + // import path from "path"; -// import cors from "cors"; // import multer from "multer"; // import Sentry from "@sentry/node"; -// require("./database"); +import "./database"; // const { initWbot } = require("./libs/wbot"); // const wbotMessageListener = require("./services/wbotMessageListener"); @@ -17,14 +19,13 @@ import routes from "./routes"; const app = express(); +app.use(cors()); +app.use(express.json()); app.use(routes); -app.listen(8080, () => { - console.log("stated"); -}); -// const server = app.listen(process.env.PORT, () => { -// console.log(`Server started on port: ${process.env.PORT}`); -// }); +const server = app.listen(process.env.PORT, () => { + console.log(`Server started on port: ${process.env.PORT}`); +}); // Sentry.init({ dsn: process.env.SENTRY_DSN }); @@ -38,8 +39,6 @@ app.listen(8080, () => { // }); // app.use(Sentry.Handlers.requestHandler()); -// app.use(cors()); -// app.use(express.json()); // app.use(multer({ storage: fileStorage }).single("media")); // app.use("/public", express.static(path.join(__dirname, "..", "public"))); // app.use(Router); diff --git a/backend/src/services/CreateUserService.ts b/backend/src/services/CreateUserService.ts new file mode 100644 index 0000000..edeeaa7 --- /dev/null +++ b/backend/src/services/CreateUserService.ts @@ -0,0 +1,53 @@ +import * as Yup from "yup"; + +import AppError from "../errors/AppError"; +import User from "../models/User"; + +interface Request { + email: string; + password: string; + name: string; + profile?: string; +} + +const CreateUserService = async ({ + email, + password, + name, + profile = "admin" +}: Request): Promise => { + // const schema = Yup.object().shape({ + // name: Yup.string().required().min(2), + // email: Yup.string() + // .email() + // .required() + // .test( + // "Check-email", + // "An user with this email already exists.", + // async value => { + // const emailExists = await User.findOne({ + // where: { email: value } + // }); + // return !Boolean(emailExists); + // } + // ), + // password: Yup.string().required().min(5) + // }); + + // try { + // await schema.validate({ email, password, name }); + // } catch (err) { + // throw new AppError(err.message); + // } + + const user = User.create({ + email, + passwordHash: password, + name, + profile + }); + + return user; +}; + +export default CreateUserService; diff --git a/backend/src/services/FindUserService.ts b/backend/src/services/FindUserService.ts new file mode 100644 index 0000000..17ab763 --- /dev/null +++ b/backend/src/services/FindUserService.ts @@ -0,0 +1,21 @@ +import { getRepository, Raw } from "typeorm"; + +import User from "../models/User"; +import AppError from "../errors/AppError"; + +const FindUserService = async (id: string): Promise => { + const usersRepository = getRepository(User); + + const user = await usersRepository.findOne({ + where: { id }, + select: ["name", "id", "email", "profile"], + }); + + if (!user) { + throw new AppError("No user found with this ID.", 404); + } + + return user; +}; + +export default FindUserService; diff --git a/backend/src/services/ListUsersService.ts b/backend/src/services/ListUsersService.ts new file mode 100644 index 0000000..5381760 --- /dev/null +++ b/backend/src/services/ListUsersService.ts @@ -0,0 +1,54 @@ +import { getRepository, Raw } from "typeorm"; + +import User from "../models/User"; + +interface Request { + searchParam?: string; + pageNumber?: number; +} + +interface Response { + users: User[]; + count: number; + hasMore: boolean; +} + +const ListUsersService = async ({ + searchParam = "", + pageNumber = 1, +}: Request): Promise => { + const usersRepository = getRepository(User); + + const whereCondition = [ + { + name: Raw( + alias => `LOWER(${alias}) Like '%${searchParam.toLowerCase()}%'` + ), + }, + { + email: Raw( + alias => `LOWER(${alias}) Like '%${searchParam.toLowerCase()}%'` + ), + }, + ]; + const take = 20; + const skip = take * (pageNumber - 1); + + const [users, count] = await usersRepository.findAndCount({ + where: whereCondition, + select: ["name", "id", "email", "profile"], + skip, + take, + order: { createdAt: "DESC" }, + }); + + const hasMore = count > skip + users.length; + + return { + users, + count, + hasMore, + }; +}; + +export default ListUsersService; diff --git a/backend/src/services/UpdateUserService.ts b/backend/src/services/UpdateUserService.ts new file mode 100644 index 0000000..39cfe68 --- /dev/null +++ b/backend/src/services/UpdateUserService.ts @@ -0,0 +1,61 @@ +import { getRepository } from "typeorm"; +import * as Yup from "yup"; + +import AppError from "../errors/AppError"; +import User from "../models/User"; + +interface UserData { + email?: string; + password?: string; + name?: string; + profile?: string; +} + +interface Request { + userData: UserData; + userId: string; +} + +const UpdateUserService = async ({ + userData, + userId, +}: Request): Promise => { + const usersRepository = getRepository(User); + + const schema = Yup.object().shape({ + name: Yup.string().min(2), + email: Yup.string().email(), + password: Yup.string(), + }); + + const { email, password, name } = userData; + + try { + await schema.validate({ email, password, name }); + } catch (err) { + throw new AppError(err.message); + } + + const user = await usersRepository.findOne({ + where: { id: userId }, + select: ["name", "id", "email", "profile"], + }); + + if (!user) { + throw new AppError("No user found with this ID.", 404); + } + + const teste = await usersRepository.update(userId, { + email, + passwordHash: password, + name, + }); + + console.log(teste); + + delete user.passwordHash; + + return user; +}; + +export default UpdateUserService; diff --git a/backend/tsconfig.json b/backend/tsconfig.json index e121133..dcfe622 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -1,69 +1,69 @@ { - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist" /* Redirect output structure to the directory. */, - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - "strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */, - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */, + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ - "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, - "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + /* Experimental Options */ + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, - /* Advanced Options */ - "skipLibCheck": true /* Skip type checking of declaration files. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } } diff --git a/backend/yarn.lock b/backend/yarn.lock index d0a00b2..d498fb3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -152,6 +152,11 @@ dependencies: defer-to-connect "^1.0.1" +"@types/bluebird@^3.5.32": + version "3.5.32" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.32.tgz#381e7b59e39f010d20bbf7e044e48f5caf1ab620" + integrity sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g== + "@types/body-parser@*": version "1.19.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" @@ -225,6 +230,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a" integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ== +"@types/node@^14.10.1": + version "14.10.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.10.1.tgz#cc323bad8e8a533d4822f45ce4e5326f36e42177" + integrity sha512-aYNbO+FZ/3KGeQCEkNhHFRIzBOUgc7QvcVNKXbfnhDkSfwUv91JsQQa10rDgKSTSLkXZ1UIyPe4FJJNVgw1xWQ== + "@types/qs@*": version "6.9.4" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" @@ -253,6 +263,11 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/validator@^13.1.0": + version "13.1.0" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.0.tgz#3d776127dbce7dd31fc06f86d3428b072e631eba" + integrity sha512-gHUHI6pJaANIO2r6WcbT7+WMgbL9GZooR4tWpuBOETpDIqFNxwaJluE+6rj6VGYe8k6OkfhbHz2Fkm8kl06Igw== + "@types/yauzl@^2.9.1": version "2.9.1" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" @@ -260,6 +275,11 @@ dependencies: "@types/node" "*" +"@types/yup@^0.29.7": + version "0.29.7" + resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.7.tgz#80c5e427a3b152e583ac2859767ccf59db0d3a16" + integrity sha512-x3Zeh8/qLZ6fG4S1EztI1S1mLj6N1pSUV1PAj/9finZba48d3Maxtyz4WYNUY0NE76u1KSukfNLkjcRlb+O00g== + "@typescript-eslint/eslint-plugin@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.1.0.tgz#7d309f60815ff35e9627ad85e41928d7b7fd443f" @@ -1767,6 +1787,18 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.3, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -3178,6 +3210,11 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" +reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -3356,6 +3393,13 @@ sequelize-pool@^2.3.0: resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-2.3.0.tgz#64f1fe8744228172c474f530604b6133be64993d" integrity sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA== +sequelize-typescript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/sequelize-typescript/-/sequelize-typescript-1.1.0.tgz#d5c2945e7fbfe55a934917b27d84589858d79123" + integrity sha512-FAPEQPeAhIaFQNLAcf9Q2IWcqWhNcvn5OZZ7BzGB0CJMtImIsGg4E/EAb7huMmPaPwDArxJUWGqk1KurphTNRA== + dependencies: + glob "7.1.2" + sequelize@5: version "5.22.3" resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.22.3.tgz#7e7a92ddd355d883c9eb11cdb106d874d0d2636f" From 60b12cbc9f3c512a27038acf66e60c668607a567 Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 14 Sep 2020 16:28:00 -0300 Subject: [PATCH 05/54] changed migrations to typescript --- backend/.gitignore | 5 +- backend/.sequelizerc | 8 +- backend/package.json | 102 ++++----- backend/src/config/database.ts | 6 +- backend/src/database/index.ts | 56 ++--- .../migrations/20200717133438-create-users.js | 39 ---- .../migrations/20200717133438-create-users.ts | 39 ++++ .../20200717144403-create-contacts.js | 38 ---- .../20200717144403-create-contacts.ts | 38 ++++ .../20200717145643-create-tickets.js | 46 ----- .../20200717145643-create-tickets.ts | 46 +++++ .../20200717151645-create-messages.js | 58 ------ .../20200717151645-create-messages.ts | 58 ++++++ .../20200717170223-create-whatsapps.js | 41 ---- .../20200717170223-create-whatsapps.ts | 41 ++++ ...723200315-create-contacts-custom-fields.js | 41 ---- ...723200315-create-contacts-custom-fields.ts | 41 ++++ ...00723202116-add-email-field-to-contacts.js | 15 -- ...00723202116-add-email-field-to-contacts.ts | 15 ++ ...7-remove-user-association-from-messages.js | 16 -- ...7-remove-user-association-from-messages.ts | 16 ++ .../20200730153545-add-fromMe-to-messages.js | 15 -- .../20200730153545-add-fromMe-to-messages.ts | 15 ++ ...6-change-ticket-lastMessage-column-type.js | 15 -- ...6-change-ticket-lastMessage-column-type.ts | 15 ++ ...00901235509-add-profile-column-to-users.js | 15 -- ...00901235509-add-profile-column-to-users.ts | 15 ++ .../20200903215941-create-settings.js | 29 --- .../20200903215941-create-settings.ts | 29 +++ .../20200904220257-add-name-to-whatsapp.js | 15 -- .../20200904220257-add-name-to-whatsapp.ts | 15 ++ ...2228-add-name-default-field-to-whatsapp.js | 15 -- ...2228-add-name-default-field-to-whatsapp.ts | 15 ++ ...906155658-add-whatsapp-field-to-tickets.js | 16 -- ...906155658-add-whatsapp-field-to-tickets.ts | 16 ++ .../20200904070004-create-default-settings.js | 22 -- .../20200904070004-create-default-settings.ts | 22 ++ backend/yarn-error.log | 193 +++++++++++++----- 38 files changed, 651 insertions(+), 591 deletions(-) delete mode 100644 backend/src/database/migrations/20200717133438-create-users.js create mode 100644 backend/src/database/migrations/20200717133438-create-users.ts delete mode 100644 backend/src/database/migrations/20200717144403-create-contacts.js create mode 100644 backend/src/database/migrations/20200717144403-create-contacts.ts delete mode 100644 backend/src/database/migrations/20200717145643-create-tickets.js create mode 100644 backend/src/database/migrations/20200717145643-create-tickets.ts delete mode 100644 backend/src/database/migrations/20200717151645-create-messages.js create mode 100644 backend/src/database/migrations/20200717151645-create-messages.ts delete mode 100644 backend/src/database/migrations/20200717170223-create-whatsapps.js create mode 100644 backend/src/database/migrations/20200717170223-create-whatsapps.ts delete mode 100644 backend/src/database/migrations/20200723200315-create-contacts-custom-fields.js create mode 100644 backend/src/database/migrations/20200723200315-create-contacts-custom-fields.ts delete mode 100644 backend/src/database/migrations/20200723202116-add-email-field-to-contacts.js create mode 100644 backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts delete mode 100644 backend/src/database/migrations/20200730153237-remove-user-association-from-messages.js create mode 100644 backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts delete mode 100644 backend/src/database/migrations/20200730153545-add-fromMe-to-messages.js create mode 100644 backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts delete mode 100644 backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.js create mode 100644 backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts delete mode 100644 backend/src/database/migrations/20200901235509-add-profile-column-to-users.js create mode 100644 backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts delete mode 100644 backend/src/database/migrations/20200903215941-create-settings.js create mode 100644 backend/src/database/migrations/20200903215941-create-settings.ts delete mode 100644 backend/src/database/migrations/20200904220257-add-name-to-whatsapp.js create mode 100644 backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts delete mode 100644 backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.js create mode 100644 backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts delete mode 100644 backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.js create mode 100644 backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts delete mode 100644 backend/src/database/seeds/20200904070004-create-default-settings.js create mode 100644 backend/src/database/seeds/20200904070004-create-default-settings.ts diff --git a/backend/.gitignore b/backend/.gitignore index dd6817b..53b9b1c 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,9 +1,10 @@ node_modules public/* -!public/.gitkeep +dist +!public/.gitkeep .env package-lock.json yarn.lock -/src/config/sentry.js \ No newline at end of file +/src/config/sentry.js diff --git a/backend/.sequelizerc b/backend/.sequelizerc index e5241e2..264f851 100644 --- a/backend/.sequelizerc +++ b/backend/.sequelizerc @@ -1,8 +1,8 @@ const { resolve } = require("path"); module.exports = { - config: resolve(__dirname, "src", "config", "database.js"), - "modules-path": resolve(__dirname, "src", "models"), - "migrations-path": resolve(__dirname, "src", "database", "migrations"), - "seeders-path": resolve(__dirname, "src", "database", "seeds"), + "config": resolve(__dirname, "dist", "config", "database.js"), + "modules-path": resolve(__dirname, "dist", "models"), + "migrations-path": resolve(__dirname, "dist", "database", "migrations"), + "seeders-path": resolve(__dirname, "dist", "database", "seeds") }; diff --git a/backend/package.json b/backend/package.json index 142717d..e4c5684 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,53 +1,53 @@ { - "name": "backend", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build": "tsc", - "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" - }, - "author": "", - "license": "MIT", - "dependencies": { - "@sentry/node": "5.22.3", - "bcryptjs": "^2.4.3", - "cors": "^2.8.5", - "date-fns": "^2.16.1", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "express-async-errors": "^3.1.1", - "jsonwebtoken": "^8.5.1", - "multer": "^1.4.2", - "mysql2": "^2.1.0", - "qrcode-terminal": "^0.12.0", - "reflect-metadata": "^0.1.13", - "sequelize": "5", - "sequelize-cli": "5", - "sequelize-typescript": "^1.1.0", - "socket.io": "^2.3.0", - "whatsapp-web.js": "^1.8.2", - "yup": "^0.29.3" - }, - "devDependencies": { - "@types/bluebird": "^3.5.32", - "@types/cors": "^2.8.7", - "@types/express": "^4.17.8", - "@types/multer": "^1.4.4", - "@types/node": "^14.10.1", - "@types/validator": "^13.1.0", - "@types/yup": "^0.29.7", - "@typescript-eslint/eslint-plugin": "^4.1.0", - "@typescript-eslint/parser": "^4.1.0", - "eslint": "^7.9.0", - "eslint-config-airbnb-base": "^14.2.0", - "eslint-config-prettier": "^6.11.0", - "eslint-import-resolver-typescript": "^2.3.0", - "eslint-plugin-import": "^2.21.2", - "eslint-plugin-prettier": "^3.1.4", - "nodemon": "^2.0.4", - "prettier": "^2.1.1", - "ts-node-dev": "^1.0.0-pre.62", - "typescript": "^4.0.2" - } + "name": "backend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" + }, + "author": "", + "license": "MIT", + "dependencies": { + "@sentry/node": "5.22.3", + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "date-fns": "^2.16.1", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "express-async-errors": "^3.1.1", + "jsonwebtoken": "^8.5.1", + "multer": "^1.4.2", + "mysql2": "^2.1.0", + "qrcode-terminal": "^0.12.0", + "reflect-metadata": "^0.1.13", + "sequelize": "5", + "sequelize-cli": "5", + "sequelize-typescript": "^1.1.0", + "socket.io": "^2.3.0", + "whatsapp-web.js": "^1.8.2", + "yup": "^0.29.3" + }, + "devDependencies": { + "@types/bluebird": "^3.5.32", + "@types/cors": "^2.8.7", + "@types/express": "^4.17.8", + "@types/multer": "^1.4.4", + "@types/node": "^14.10.1", + "@types/validator": "^13.1.0", + "@types/yup": "^0.29.7", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "eslint": "^7.9.0", + "eslint-config-airbnb-base": "^14.2.0", + "eslint-config-prettier": "^6.11.0", + "eslint-import-resolver-typescript": "^2.3.0", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-prettier": "^3.1.4", + "nodemon": "^2.0.4", + "prettier": "^2.1.1", + "ts-node-dev": "^1.0.0-pre.62", + "typescript": "^4.0.2" + } } diff --git a/backend/src/config/database.ts b/backend/src/config/database.ts index 33ccc78..3484e67 100644 --- a/backend/src/config/database.ts +++ b/backend/src/config/database.ts @@ -1,6 +1,6 @@ -import "dotenv/config"; +require("dotenv/config"); -const dbConfig = { +module.exports = { define: { charset: "utf8mb4", collate: "utf8mb4_bin" @@ -13,5 +13,3 @@ const dbConfig = { password: process.env.DB_PASS, logging: false }; - -export default dbConfig; diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts index c1aa9aa..eb4b24a 100644 --- a/backend/src/database/index.ts +++ b/backend/src/database/index.ts @@ -1,9 +1,10 @@ import { Sequelize } from "sequelize-typescript"; -import { resolve } from "path"; -// import dbConfig from "../config/database"; -import "dotenv/config"; +import User from "../models/User"; + +// eslint-disable-next-line +const dbConfig = require("../config/database"); +// import dbConfig from "../config/database"; -// import User from "../models/User"; // const Contact = require("../models/Contact"); // const Ticket = require("../models/Ticket"); // const Message = require("../models/Message"); @@ -11,43 +12,18 @@ import "dotenv/config"; // const ContactCustomField = require("../models/ContactCustomField"); // const Setting = require("../models/Setting"); -const sequelize = new Sequelize({ - define: { - charset: "utf8mb4", - collate: "utf8mb4_bin" - }, - dialect: "mysql", - timezone: "-03:00", - host: process.env.DB_HOST, - database: process.env.DB_NAME, - username: process.env.DB_USER, - password: process.env.DB_PASS, - models: [resolve(__dirname, "..", "models")], - logging: false -}); +const sequelize = new Sequelize(dbConfig); -// const models = [ -// User -// // Contact, -// // Ticket, -// // Message, -// // Whatsapp, -// // ContactCustomField, -// // Setting, -// ]; +const models = [ + User + // Contact, + // Ticket, + // Message, + // Whatsapp, + // ContactCustomField, + // Setting, +]; -// class Database { -// constructor() { -// this.init(); -// } - -// init() { -// this.sequelize = new Sequelize(dbConfig); - -// models -// .map(model => model.init(this.sequelize)) -// .map(model => model.associate && model.associate(this.sequelize.models)); -// } -// } +sequelize.addModels(models); export default sequelize; diff --git a/backend/src/database/migrations/20200717133438-create-users.js b/backend/src/database/migrations/20200717133438-create-users.js deleted file mode 100644 index 7c046a9..0000000 --- a/backend/src/database/migrations/20200717133438-create-users.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable("Users", { - id: { - type: Sequelize.INTEGER, - autoIncrement: true, - primaryKey: true, - allowNull: false, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - email: { - type: Sequelize.STRING, - allowNull: false, - unique: true, - }, - passwordHash: { - type: Sequelize.STRING, - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: queryInterface => { - return queryInterface.dropTable("Users"); - }, -}; diff --git a/backend/src/database/migrations/20200717133438-create-users.ts b/backend/src/database/migrations/20200717133438-create-users.ts new file mode 100644 index 0000000..17e9ee9 --- /dev/null +++ b/backend/src/database/migrations/20200717133438-create-users.ts @@ -0,0 +1,39 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Users", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + passwordHash: { + type: DataTypes.STRING, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Users"); + } +}; diff --git a/backend/src/database/migrations/20200717144403-create-contacts.js b/backend/src/database/migrations/20200717144403-create-contacts.js deleted file mode 100644 index 7ad2b4f..0000000 --- a/backend/src/database/migrations/20200717144403-create-contacts.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable("Contacts", { - id: { - type: Sequelize.INTEGER, - autoIncrement: true, - primaryKey: true, - allowNull: false, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - number: { - type: Sequelize.STRING, - allowNull: false, - unique: true, - }, - profilePicUrl: { - type: Sequelize.STRING, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: queryInterface => { - return queryInterface.dropTable("Contacts"); - }, -}; diff --git a/backend/src/database/migrations/20200717144403-create-contacts.ts b/backend/src/database/migrations/20200717144403-create-contacts.ts new file mode 100644 index 0000000..9224f66 --- /dev/null +++ b/backend/src/database/migrations/20200717144403-create-contacts.ts @@ -0,0 +1,38 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Contacts", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + number: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + profilePicUrl: { + type: DataTypes.STRING + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Contacts"); + } +}; diff --git a/backend/src/database/migrations/20200717145643-create-tickets.js b/backend/src/database/migrations/20200717145643-create-tickets.js deleted file mode 100644 index 81648e3..0000000 --- a/backend/src/database/migrations/20200717145643-create-tickets.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable("Tickets", { - id: { - type: Sequelize.INTEGER, - autoIncrement: true, - primaryKey: true, - allowNull: false, - }, - status: { - type: Sequelize.STRING, - defaultValue: "pending", - allowNull: false, - }, - lastMessage: { - type: Sequelize.STRING, - }, - contactId: { - type: Sequelize.INTEGER, - references: { model: "Contacts", key: "id" }, - onUpdate: "CASCADE", - onDelete: "CASCADE", - }, - userId: { - type: Sequelize.INTEGER, - references: { model: "Users", key: "id" }, - onUpdate: "CASCADE", - onDelete: "SET NULL", - }, - createdAt: { - type: Sequelize.DATE(6), - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE(6), - allowNull: false, - }, - }); - }, - - down: queryInterface => { - return queryInterface.dropTable("Tickets"); - }, -}; diff --git a/backend/src/database/migrations/20200717145643-create-tickets.ts b/backend/src/database/migrations/20200717145643-create-tickets.ts new file mode 100644 index 0000000..d5016ee --- /dev/null +++ b/backend/src/database/migrations/20200717145643-create-tickets.ts @@ -0,0 +1,46 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Tickets", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + status: { + type: DataTypes.STRING, + defaultValue: "pending", + allowNull: false + }, + lastMessage: { + type: DataTypes.STRING + }, + contactId: { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE" + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }, + createdAt: { + type: DataTypes.DATE(6), + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE(6), + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Tickets"); + } +}; diff --git a/backend/src/database/migrations/20200717151645-create-messages.js b/backend/src/database/migrations/20200717151645-create-messages.js deleted file mode 100644 index e410e41..0000000 --- a/backend/src/database/migrations/20200717151645-create-messages.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable("Messages", { - id: { - type: Sequelize.STRING, - primaryKey: true, - allowNull: false, - }, - body: { - type: Sequelize.TEXT, - allowNull: false, - }, - ack: { - type: Sequelize.INTEGER, - allowNull: false, - defaultValue: 0, - }, - read: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false, - }, - mediaType: { - type: Sequelize.STRING, - }, - mediaUrl: { - type: Sequelize.STRING, - }, - userId: { - type: Sequelize.INTEGER, - references: { model: "Users", key: "id" }, - onUpdate: "CASCADE", - onDelete: "SET NULL", - }, - ticketId: { - type: Sequelize.INTEGER, - references: { model: "Tickets", key: "id" }, - onUpdate: "CASCADE", - onDelete: "CASCADE", - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE(6), - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE(6), - allowNull: false, - }, - }); - }, - - down: queryInterface => { - return queryInterface.dropTable("Messages"); - }, -}; diff --git a/backend/src/database/migrations/20200717151645-create-messages.ts b/backend/src/database/migrations/20200717151645-create-messages.ts new file mode 100644 index 0000000..052dfc1 --- /dev/null +++ b/backend/src/database/migrations/20200717151645-create-messages.ts @@ -0,0 +1,58 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Messages", { + id: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false + }, + body: { + type: DataTypes.TEXT, + allowNull: false + }, + ack: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }, + read: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + mediaType: { + type: DataTypes.STRING + }, + mediaUrl: { + type: DataTypes.STRING + }, + userId: { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }, + ticketId: { + type: DataTypes.INTEGER, + references: { model: "Tickets", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE(6), + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE(6), + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Messages"); + } +}; diff --git a/backend/src/database/migrations/20200717170223-create-whatsapps.js b/backend/src/database/migrations/20200717170223-create-whatsapps.js deleted file mode 100644 index d60e90e..0000000 --- a/backend/src/database/migrations/20200717170223-create-whatsapps.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable("Whatsapps", { - id: { - type: Sequelize.INTEGER, - autoIncrement: true, - primaryKey: true, - allowNull: false, - }, - session: { - type: Sequelize.TEXT, - }, - qrcode: { - type: Sequelize.TEXT, - }, - status: { - type: Sequelize.STRING, - }, - battery: { - type: Sequelize.STRING, - }, - plugged: { - type: Sequelize.BOOLEAN, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: queryInterface => { - return queryInterface.dropTable("Whatsapps"); - }, -}; diff --git a/backend/src/database/migrations/20200717170223-create-whatsapps.ts b/backend/src/database/migrations/20200717170223-create-whatsapps.ts new file mode 100644 index 0000000..0686bc1 --- /dev/null +++ b/backend/src/database/migrations/20200717170223-create-whatsapps.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Whatsapps", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + session: { + type: DataTypes.TEXT + }, + qrcode: { + type: DataTypes.TEXT + }, + status: { + type: DataTypes.STRING + }, + battery: { + type: DataTypes.STRING + }, + plugged: { + type: DataTypes.BOOLEAN + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Whatsapps"); + } +}; diff --git a/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.js b/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.js deleted file mode 100644 index d990ad3..0000000 --- a/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable("ContactCustomFields", { - id: { - type: Sequelize.INTEGER, - autoIncrement: true, - primaryKey: true, - allowNull: false, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - value: { - type: Sequelize.STRING, - allowNull: false, - }, - contactId: { - type: Sequelize.INTEGER, - references: { model: "Contacts", key: "id" }, - onUpdate: "CASCADE", - onDelete: "CASCADE", - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: queryInterface => { - return queryInterface.dropTable("ContactCustomFields"); - }, -}; diff --git a/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.ts b/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.ts new file mode 100644 index 0000000..c6cc7f7 --- /dev/null +++ b/backend/src/database/migrations/20200723200315-create-contacts-custom-fields.ts @@ -0,0 +1,41 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("ContactCustomFields", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + value: { + type: DataTypes.STRING, + allowNull: false + }, + contactId: { + type: DataTypes.INTEGER, + references: { model: "Contacts", key: "id" }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("ContactCustomFields"); + } +}; diff --git a/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.js b/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.js deleted file mode 100644 index a8ce059..0000000 --- a/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn("Contacts", "email", { - type: Sequelize.STRING, - allowNull: false, - defaultValue: "", - }); - }, - - down: queryInterface => { - return queryInterface.removeColumn("Contacts", "email"); - }, -}; diff --git a/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts b/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts new file mode 100644 index 0000000..cbf086d --- /dev/null +++ b/backend/src/database/migrations/20200723202116-add-email-field-to-contacts.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Contacts", "email", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Contacts", "email"); + } +}; diff --git a/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.js b/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.js deleted file mode 100644 index 2978eb5..0000000 --- a/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -module.exports = { - up: queryInterface => { - return queryInterface.removeColumn("Messages", "userId"); - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.addColumn("Messages", "userId", { - type: Sequelize.INTEGER, - references: { model: "Users", key: "id" }, - onUpdate: "CASCADE", - onDelete: "SET NULL", - }); - }, -}; diff --git a/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts b/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts new file mode 100644 index 0000000..765619f --- /dev/null +++ b/backend/src/database/migrations/20200730153237-remove-user-association-from-messages.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "userId"); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "userId", { + type: DataTypes.INTEGER, + references: { model: "Users", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + } +}; diff --git a/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.js b/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.js deleted file mode 100644 index 9dbf4c4..0000000 --- a/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn("Messages", "fromMe", { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false, - }); - }, - - down: queryInterface => { - return queryInterface.removeColumn("Messages", "fromMe"); - }, -}; diff --git a/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts b/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts new file mode 100644 index 0000000..4bdcebe --- /dev/null +++ b/backend/src/database/migrations/20200730153545-add-fromMe-to-messages.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Messages", "fromMe", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Messages", "fromMe"); + } +}; diff --git a/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.js b/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.js deleted file mode 100644 index 23eea6d..0000000 --- a/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.changeColumn("Tickets", "lastMessage", { - type: Sequelize.TEXT, - }); - }, - - down: (queryInterface, Sequelize) => { - return queryInterface.changeColumn("Tickets", "lastMessage", { - type: Sequelize.STRING, - }); - }, -}; diff --git a/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts b/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts new file mode 100644 index 0000000..e4248e8 --- /dev/null +++ b/backend/src/database/migrations/20200813114236-change-ticket-lastMessage-column-type.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("Tickets", "lastMessage", { + type: DataTypes.TEXT + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.changeColumn("Tickets", "lastMessage", { + type: DataTypes.STRING + }); + } +}; diff --git a/backend/src/database/migrations/20200901235509-add-profile-column-to-users.js b/backend/src/database/migrations/20200901235509-add-profile-column-to-users.js deleted file mode 100644 index cb3e93b..0000000 --- a/backend/src/database/migrations/20200901235509-add-profile-column-to-users.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn("Users", "profile", { - type: Sequelize.STRING, - allowNull: false, - defaultValue: "admin", - }); - }, - - down: queryInterface => { - return queryInterface.removeColumn("Users", "profile"); - }, -}; diff --git a/backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts b/backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts new file mode 100644 index 0000000..b1d866d --- /dev/null +++ b/backend/src/database/migrations/20200901235509-add-profile-column-to-users.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "profile", { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "admin" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "profile"); + } +}; diff --git a/backend/src/database/migrations/20200903215941-create-settings.js b/backend/src/database/migrations/20200903215941-create-settings.js deleted file mode 100644 index ad82017..0000000 --- a/backend/src/database/migrations/20200903215941-create-settings.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable("Settings", { - key: { - type: Sequelize.STRING, - primaryKey: true, - allowNull: false, - }, - value: { - type: Sequelize.TEXT, - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: queryInterface => { - return queryInterface.dropTable("Settings"); - }, -}; diff --git a/backend/src/database/migrations/20200903215941-create-settings.ts b/backend/src/database/migrations/20200903215941-create-settings.ts new file mode 100644 index 0000000..b8724fc --- /dev/null +++ b/backend/src/database/migrations/20200903215941-create-settings.ts @@ -0,0 +1,29 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("Settings", { + key: { + type: DataTypes.STRING, + primaryKey: true, + allowNull: false + }, + value: { + type: DataTypes.TEXT, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("Settings"); + } +}; diff --git a/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.js b/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.js deleted file mode 100644 index b2fefc2..0000000 --- a/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn("Whatsapps", "name", { - type: Sequelize.STRING, - allowNull: false, - unique: true, - }); - }, - - down: queryInterface => { - return queryInterface.removeColumn("Whatsapps", "name"); - }, -}; diff --git a/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts b/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts new file mode 100644 index 0000000..3d15507 --- /dev/null +++ b/backend/src/database/migrations/20200904220257-add-name-to-whatsapp.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "name", { + type: DataTypes.STRING, + allowNull: false, + unique: true + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "name"); + } +}; diff --git a/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.js b/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.js deleted file mode 100644 index 57e03fc..0000000 --- a/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn("Whatsapps", "default", { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false, - }); - }, - - down: queryInterface => { - return queryInterface.removeColumn("Whatsapps", "default"); - }, -}; diff --git a/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts b/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts new file mode 100644 index 0000000..7ec4a50 --- /dev/null +++ b/backend/src/database/migrations/20200906122228-add-name-default-field-to-whatsapp.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Whatsapps", "default", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Whatsapps", "default"); + } +}; diff --git a/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.js b/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.js deleted file mode 100644 index 28f7c0c..0000000 --- a/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.addColumn("Tickets", "whatsappId", { - type: Sequelize.INTEGER, - references: { model: "Whatsapps", key: "id" }, - onUpdate: "CASCADE", - onDelete: "SET NULL", - }); - }, - - down: queryInterface => { - return queryInterface.removeColumn("Tickets", "whatsappId"); - }, -}; diff --git a/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts b/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts new file mode 100644 index 0000000..5ed102d --- /dev/null +++ b/backend/src/database/migrations/20200906155658-add-whatsapp-field-to-tickets.ts @@ -0,0 +1,16 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Tickets", "whatsappId", { + type: DataTypes.INTEGER, + references: { model: "Whatsapps", key: "id" }, + onUpdate: "CASCADE", + onDelete: "SET NULL" + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Tickets", "whatsappId"); + } +}; diff --git a/backend/src/database/seeds/20200904070004-create-default-settings.js b/backend/src/database/seeds/20200904070004-create-default-settings.js deleted file mode 100644 index 73dfccb..0000000 --- a/backend/src/database/seeds/20200904070004-create-default-settings.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; - -module.exports = { - up: queryInterface => { - return queryInterface.bulkInsert( - "Settings", - [ - { - key: "userCreation", - value: "enabled", - createdAt: new Date(), - updatedAt: new Date(), - }, - ], - {} - ); - }, - - down: queryInterface => { - return queryInterface.bulkDelete("Settings", null, {}); - }, -}; diff --git a/backend/src/database/seeds/20200904070004-create-default-settings.ts b/backend/src/database/seeds/20200904070004-create-default-settings.ts new file mode 100644 index 0000000..802be29 --- /dev/null +++ b/backend/src/database/seeds/20200904070004-create-default-settings.ts @@ -0,0 +1,22 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.bulkInsert( + "Settings", + [ + { + key: "userCreation", + value: "enabled", + createdAt: new Date(), + updatedAt: new Date() + } + ], + {} + ); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.bulkDelete("Settings", {}); + } +}; diff --git a/backend/yarn-error.log b/backend/yarn-error.log index 60a9bbf..5674f65 100644 --- a/backend/yarn-error.log +++ b/backend/yarn-error.log @@ -1,5 +1,5 @@ Arguments: - /usr/bin/node /usr/share/yarn/bin/yarn.js add -D prettier eslint-config-prettier esling-plugin-prettier + /usr/bin/node /usr/share/yarn/bin/yarn.js sequelize db:migrate PATH: /home/canove/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/bin:/home/canove/.zinit/polaris/sbin:/home/canove/.zinit/polaris/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/ProgramData/chocolatey/bin:/mnt/c/Program Files/Microsoft VS Code/bin:/mnt/c/Program Files (x86)/GNU/GnuPG/pub:/mnt/c/Users/cassio/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/cassio/AppData/Local/GitHubDesktop/bin @@ -14,62 +14,67 @@ Platform: linux x64 Trace: - Error: https://registry.yarnpkg.com/esling-plugin-prettier: Not found - at Request.params.callback [as _callback] (/usr/share/yarn/lib/cli.js:66987:18) - at Request.self.callback (/usr/share/yarn/lib/cli.js:140748:22) - at Request.emit (events.js:315:20) - at Request. (/usr/share/yarn/lib/cli.js:141720:10) - at Request.emit (events.js:315:20) - at IncomingMessage. (/usr/share/yarn/lib/cli.js:141642:12) - at Object.onceWrapper (events.js:421:28) - at IncomingMessage.emit (events.js:327:22) - at endReadableNT (_stream_readable.js:1221:12) - at processTicksAndRejections (internal/process/task_queues.js:84:21) + SyntaxError: /home/canove/code_learn/econowhats/backend/package.json: Unexpected token / in JSON at position 222 + at JSON.parse () + at /usr/share/yarn/lib/cli.js:1625:59 + at Generator.next () + at step (/usr/share/yarn/lib/cli.js:310:30) + at /usr/share/yarn/lib/cli.js:321:13 npm manifest: { - "name": "backend", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build": "tsc", - "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" - }, - "author": "", - "license": "MIT", - "dependencies": { - "@sentry/node": "5.22.3", - "bcryptjs": "^2.4.3", - "cors": "^2.8.5", - "date-fns": "^2.16.1", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "express-async-errors": "^3.1.1", - "jsonwebtoken": "^8.5.1", - "multer": "^1.4.2", - "mysql2": "^2.1.0", - "qrcode-terminal": "^0.12.0", - "sequelize": "5", - "sequelize-cli": "5", - "socket.io": "^2.3.0", - "whatsapp-web.js": "^1.8.2", - "yup": "^0.29.3" - }, - "devDependencies": { - "@types/cors": "^2.8.7", - "@types/express": "^4.17.8", - "@types/multer": "^1.4.4", - "@typescript-eslint/eslint-plugin": "^4.1.0", - "@typescript-eslint/parser": "^4.1.0", - "eslint": "^7.9.0", - "eslint-config-airbnb-base": "^14.2.0", - "eslint-import-resolver-typescript": "^2.3.0", - "eslint-plugin-import": "^2.21.2", - "nodemon": "^2.0.4", - "ts-node-dev": "^1.0.0-pre.62", - "typescript": "^4.0.2" - } + "name": "backend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" + // "sequelize": "ts-node-dev ./node_modules/.bin/sequelize" + }, + "author": "", + "license": "MIT", + "dependencies": { + "@sentry/node": "5.22.3", + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "date-fns": "^2.16.1", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "express-async-errors": "^3.1.1", + "jsonwebtoken": "^8.5.1", + "multer": "^1.4.2", + "mysql2": "^2.1.0", + "qrcode-terminal": "^0.12.0", + "reflect-metadata": "^0.1.13", + "sequelize": "5", + "sequelize-cli": "5", + "sequelize-typescript": "^1.1.0", + "socket.io": "^2.3.0", + "whatsapp-web.js": "^1.8.2", + "yup": "^0.29.3" + }, + "devDependencies": { + "@types/bluebird": "^3.5.32", + "@types/cors": "^2.8.7", + "@types/express": "^4.17.8", + "@types/multer": "^1.4.4", + "@types/node": "^14.10.1", + "@types/validator": "^13.1.0", + "@types/yup": "^0.29.7", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "eslint": "^7.9.0", + "eslint-config-airbnb-base": "^14.2.0", + "eslint-config-prettier": "^6.11.0", + "eslint-import-resolver-typescript": "^2.3.0", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-prettier": "^3.1.4", + "nodemon": "^2.0.4", + "prettier": "^2.1.1", + "ts-node-dev": "^1.0.0-pre.62", + "typescript": "^4.0.2" + } } yarn manifest: @@ -230,6 +235,11 @@ Lockfile: dependencies: defer-to-connect "^1.0.1" + "@types/bluebird@^3.5.32": + version "3.5.32" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.32.tgz#381e7b59e39f010d20bbf7e044e48f5caf1ab620" + integrity sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g== + "@types/body-parser@*": version "1.19.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" @@ -303,6 +313,11 @@ Lockfile: resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a" integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ== + "@types/node@^14.10.1": + version "14.10.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.10.1.tgz#cc323bad8e8a533d4822f45ce4e5326f36e42177" + integrity sha512-aYNbO+FZ/3KGeQCEkNhHFRIzBOUgc7QvcVNKXbfnhDkSfwUv91JsQQa10rDgKSTSLkXZ1UIyPe4FJJNVgw1xWQ== + "@types/qs@*": version "6.9.4" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" @@ -331,6 +346,11 @@ Lockfile: resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + "@types/validator@^13.1.0": + version "13.1.0" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.1.0.tgz#3d776127dbce7dd31fc06f86d3428b072e631eba" + integrity sha512-gHUHI6pJaANIO2r6WcbT7+WMgbL9GZooR4tWpuBOETpDIqFNxwaJluE+6rj6VGYe8k6OkfhbHz2Fkm8kl06Igw== + "@types/yauzl@^2.9.1": version "2.9.1" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" @@ -338,6 +358,11 @@ Lockfile: dependencies: "@types/node" "*" + "@types/yup@^0.29.7": + version "0.29.7" + resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.7.tgz#80c5e427a3b152e583ac2859767ccf59db0d3a16" + integrity sha512-x3Zeh8/qLZ6fG4S1EztI1S1mLj6N1pSUV1PAj/9finZba48d3Maxtyz4WYNUY0NE76u1KSukfNLkjcRlb+O00g== + "@typescript-eslint/eslint-plugin@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.1.0.tgz#7d309f60815ff35e9627ad85e41928d7b7fd443f" @@ -1393,6 +1418,13 @@ Lockfile: object.assign "^4.1.0" object.entries "^1.1.2" + eslint-config-prettier@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + dependencies: + get-stdin "^6.0.0" + eslint-import-resolver-node@^0.3.3: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -1439,6 +1471,13 @@ Lockfile: resolve "^1.17.0" tsconfig-paths "^3.9.0" + eslint-plugin-prettier@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2" + integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-scope@^5.0.0, eslint-scope@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -1627,6 +1666,11 @@ Lockfile: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.1.1: version "3.2.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" @@ -1800,6 +1844,11 @@ Lockfile: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -1821,6 +1870,18 @@ Lockfile: dependencies: is-glob "^4.0.1" + glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.3, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -3016,6 +3077,18 @@ Lockfile: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + + prettier@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6" + integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -3220,6 +3293,11 @@ Lockfile: dependencies: esprima "~4.0.0" + reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -3398,6 +3476,13 @@ Lockfile: resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-2.3.0.tgz#64f1fe8744228172c474f530604b6133be64993d" integrity sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA== + sequelize-typescript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/sequelize-typescript/-/sequelize-typescript-1.1.0.tgz#d5c2945e7fbfe55a934917b27d84589858d79123" + integrity sha512-FAPEQPeAhIaFQNLAcf9Q2IWcqWhNcvn5OZZ7BzGB0CJMtImIsGg4E/EAb7huMmPaPwDArxJUWGqk1KurphTNRA== + dependencies: + glob "7.1.2" + sequelize@5: version "5.22.3" resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.22.3.tgz#7e7a92ddd355d883c9eb11cdb106d874d0d2636f" From eba3553a2d873285a9daac674116fcedca3ecca3 Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 14 Sep 2020 16:47:43 -0300 Subject: [PATCH 06/54] change user model to typescript --- backend/.eslintrc.json | 1 + backend/package.json | 1 + backend/src/models/User.ts | 66 +++++++---------------- backend/src/services/CreateUserService.ts | 2 +- backend/yarn.lock | 5 ++ 5 files changed, 27 insertions(+), 48 deletions(-) diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json index 2ebe3c9..17e8e9c 100644 --- a/backend/.eslintrc.json +++ b/backend/.eslintrc.json @@ -17,6 +17,7 @@ "plugins": ["@typescript-eslint", "prettier"], "rules": { "no-console": "off", + "no-param-reassign": "off", "prettier/prettier": "error", "import/extensions": [ "error", diff --git a/backend/package.json b/backend/package.json index e4c5684..46fec36 100644 --- a/backend/package.json +++ b/backend/package.json @@ -30,6 +30,7 @@ "yup": "^0.29.3" }, "devDependencies": { + "@types/bcryptjs": "^2.4.2", "@types/bluebird": "^3.5.32", "@types/cors": "^2.8.7", "@types/express": "^4.17.8", diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 76b71b3..ad08c66 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -4,24 +4,23 @@ import { CreatedAt, UpdatedAt, Model, - DataType + DataType, + BeforeCreate, + BeforeUpdate } from "sequelize-typescript"; +import { hash, compare } from "bcryptjs"; @Table class User extends Model { - @Column({ - defaultValue: DataType.UUIDV4, - primaryKey: true, - type: DataType.UUID - }) - id: string; - @Column name: string; @Column email: string; + @Column(DataType.VIRTUAL) + password: string; + @Column passwordHash: string; @@ -36,47 +35,20 @@ class User extends Model { @UpdatedAt updatedAt: Date; - // @BeforeUpdate - // @BeforeInsert - // hashPassword = async () => { - // if (this.passwordHash) { - // this.passwordHash = await hash(this.passwordHash, 8); - // } - // }; + @BeforeUpdate + @BeforeCreate + static hashPassword = async (instance: User): Promise => { + if (instance.password) { + instance.passwordHash = await hash(instance.password, 8); + } + }; - // checkPassword = async (password: string) => { - // return await compare(password, this.passwordHash); + // static checkPassword = async ( // maybe not work like this. + // instance: User, + // password: string + // ): Promise => { + // return compare(password, instance.passwordHash); // }; } export default User; - -// const bcrypt = require("bcryptjs"); -// @Table -// class User extends Model { -// static init(sequelize) { -// super.init( -// { -// name: { type: Sequelize.STRING }, -// password: { type: Sequelize.VIRTUAL }, -// profile: { type: Sequelize.STRING, defaultValue: "admin" }, -// passwordHash: { type: Sequelize.STRING }, -// email: { type: Sequelize.STRING } -// }, -// { -// sequelize -// } -// ); - -// this.addHook("beforeSave", async user => { -// if (user.password) { -// user.passwordHash = await bcrypt.hash(user.password, 8); -// } -// }); -// return this; -// } - -// checkPassword(password) { -// return bcrypt.compare(password, this.passwordHash); -// } -// } diff --git a/backend/src/services/CreateUserService.ts b/backend/src/services/CreateUserService.ts index edeeaa7..65363bf 100644 --- a/backend/src/services/CreateUserService.ts +++ b/backend/src/services/CreateUserService.ts @@ -42,7 +42,7 @@ const CreateUserService = async ({ const user = User.create({ email, - passwordHash: password, + password, name, profile }); diff --git a/backend/yarn.lock b/backend/yarn.lock index d498fb3..f19d8b9 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -152,6 +152,11 @@ dependencies: defer-to-connect "^1.0.1" +"@types/bcryptjs@^2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.2.tgz#e3530eac9dd136bfdfb0e43df2c4c5ce1f77dfae" + integrity sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ== + "@types/bluebird@^3.5.32": version "3.5.32" resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.32.tgz#381e7b59e39f010d20bbf7e044e48f5caf1ab620" From 7f33e33078ec3b9d973ebc3dae78cb52142976b3 Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 14 Sep 2020 18:54:36 -0300 Subject: [PATCH 07/54] finished user store in typscript --- backend/package.json | 1 + backend/src/config/auth.js | 4 -- backend/src/config/auth.ts | 4 ++ backend/src/controllers/SessionController.ts | 16 +++++ backend/src/controllers/UserController.ts | 71 ++++++++++--------- .../ContactController.js | 0 .../ImportPhoneContactsController.js | 0 .../MessageController.js | 0 .../OldSessionController.js} | 0 .../OldSettingController.js} | 0 .../OldUserController.js | 0 .../TicketController.js | 0 .../WhatsAppController.js | 0 .../WhatsAppSessionController.js | 0 backend/src/database/index.ts | 6 +- backend/src/errors/AppError.ts | 1 + backend/src/helpers/CheckSettingsHelper.ts | 6 +- backend/src/middleware/is-auth.js | 36 ---------- backend/src/middleware/isAuth.ts | 35 +++++++++ backend/src/models/Setting.ts | 26 +++++++ backend/src/models/User.ts | 14 ++-- backend/src/routes/authRoutes.ts | 11 +++ backend/src/routes/index.ts | 4 +- backend/src/routes/userRoutes.ts | 4 +- backend/src/server.ts | 14 +++- backend/src/services/AuthUserSerice.ts | 48 +++++++++++++ backend/src/services/CreateUserService.ts | 65 ++++++++++------- backend/src/services/FindUserService.ts | 20 +++--- backend/yarn.lock | 7 ++ 29 files changed, 260 insertions(+), 133 deletions(-) delete mode 100644 backend/src/config/auth.js create mode 100644 backend/src/config/auth.ts create mode 100644 backend/src/controllers/SessionController.ts rename backend/src/{controllers => controllersOld}/ContactController.js (100%) rename backend/src/{controllers => controllersOld}/ImportPhoneContactsController.js (100%) rename backend/src/{controllers => controllersOld}/MessageController.js (100%) rename backend/src/{controllers/SessionController.js => controllersOld/OldSessionController.js} (100%) rename backend/src/{controllers/SettingController.js => controllersOld/OldSettingController.js} (100%) rename backend/src/{controllers => controllersOld}/OldUserController.js (100%) rename backend/src/{controllers => controllersOld}/TicketController.js (100%) rename backend/src/{controllers => controllersOld}/WhatsAppController.js (100%) rename backend/src/{controllers => controllersOld}/WhatsAppSessionController.js (100%) delete mode 100644 backend/src/middleware/is-auth.js create mode 100644 backend/src/middleware/isAuth.ts create mode 100644 backend/src/models/Setting.ts create mode 100644 backend/src/routes/authRoutes.ts create mode 100644 backend/src/services/AuthUserSerice.ts diff --git a/backend/package.json b/backend/package.json index 46fec36..739d214 100644 --- a/backend/package.json +++ b/backend/package.json @@ -34,6 +34,7 @@ "@types/bluebird": "^3.5.32", "@types/cors": "^2.8.7", "@types/express": "^4.17.8", + "@types/jsonwebtoken": "^8.5.0", "@types/multer": "^1.4.4", "@types/node": "^14.10.1", "@types/validator": "^13.1.0", diff --git a/backend/src/config/auth.js b/backend/src/config/auth.js deleted file mode 100644 index 77c2494..0000000 --- a/backend/src/config/auth.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - secret: "mysecret", - expiresIn: "7d", -}; diff --git a/backend/src/config/auth.ts b/backend/src/config/auth.ts new file mode 100644 index 0000000..a0f3b9e --- /dev/null +++ b/backend/src/config/auth.ts @@ -0,0 +1,4 @@ +export default { + secret: "mysecret", + expiresIn: "7d" +}; diff --git a/backend/src/controllers/SessionController.ts b/backend/src/controllers/SessionController.ts new file mode 100644 index 0000000..73b3646 --- /dev/null +++ b/backend/src/controllers/SessionController.ts @@ -0,0 +1,16 @@ +import { Request, Response } from "express"; + +import AuthUserService from "../services/AuthUserSerice"; + +const store = async (req: Request, res: Response): Promise => { + const { email, password } = req.body; + + const { user, token } = await AuthUserService({ email, password }); + + return res.status(200).json({ + user, + token + }); +}; + +export default store; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 46d81dd..f0682c5 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; -// import CheckSettingsHelper from "../helpers/CheckSettingsHelper"; +import CheckSettingsHelper from "../helpers/CheckSettingsHelper"; import AppError from "../errors/AppError"; import CreateUserService from "../services/CreateUserService"; @@ -9,30 +9,31 @@ import CreateUserService from "../services/CreateUserService"; // import FindUserService from "../services/FindUserService"; export const index = async (req: Request, res: Response): Promise => { - if (req.user.profile !== "admin") { - throw new AppError("Only administrators can access this route.", 403); // should be handled better. - } - const { searchParam, pageNumber } = req.query as any; + // if (req.user.profile !== "admin") { + // throw new AppError("Only administrators can access this route.", 403); // should be handled better. + // } + // const { searchParam, pageNumber } = req.query as any; - const { users, count, hasMore } = await ListUsersService({ - searchParam, - pageNumber - }); + // const { users, count, hasMore } = await ListUsersService({ + // searchParam, + // pageNumber + // }); - return res.json({ users, count, hasMore }); + // return res.json({ users, count, hasMore }); + return res.json({ ok: "ok" }); }; export const store = async (req: Request, res: Response): Promise => { const { email, password, name, profile } = req.body; - // if ( - // req.url === "/signup" && - // (await CheckSettingsHelper("userCreation")) === "disabled" - // ) { - // throw new AppError("User creation is disabled by administrator.", 403); - // } else if (req.user.profile !== "admin") { - // throw new AppError("Only administrators can create users.", 403); - // } + if ( + req.url === "/signup" && + (await CheckSettingsHelper("userCreation")) === "disabled" + ) { + throw new AppError("User creation is disabled by administrator.", 403); + } else if (req.url !== "/signup" && req.user.profile !== "admin") { + throw new AppError("Only administrators can create users.", 403); + } const user = await CreateUserService({ email, @@ -44,26 +45,26 @@ export const store = async (req: Request, res: Response): Promise => { return res.status(200).json(user); }; -export const show = async (req: Request, res: Response): Promise => { - const { userId } = req.params; +// export const show = async (req: Request, res: Response): Promise => { +// const { userId } = req.params; - const user = await FindUserService(userId); +// const user = await FindUserService(userId); - return res.status(200).json(user); -}; +// return res.status(200).json(user); +// }; -export const update = async ( - req: Request, - res: Response -): Promise => { - if (req.user.profile !== "admin") { - throw new AppError("Only administrators can edit users.", 403); - } +// export const update = async ( +// req: Request, +// res: Response +// ): Promise => { +// if (req.user.profile !== "admin") { +// throw new AppError("Only administrators can edit users.", 403); +// } - const { userId } = req.params; - const userData = req.body; +// const { userId } = req.params; +// const userData = req.body; - const user = await UpdateUserService({ userData, userId }); +// const user = await UpdateUserService({ userData, userId }); - return res.status(200).json(user); -}; +// return res.status(200).json(user); +// }; diff --git a/backend/src/controllers/ContactController.js b/backend/src/controllersOld/ContactController.js similarity index 100% rename from backend/src/controllers/ContactController.js rename to backend/src/controllersOld/ContactController.js diff --git a/backend/src/controllers/ImportPhoneContactsController.js b/backend/src/controllersOld/ImportPhoneContactsController.js similarity index 100% rename from backend/src/controllers/ImportPhoneContactsController.js rename to backend/src/controllersOld/ImportPhoneContactsController.js diff --git a/backend/src/controllers/MessageController.js b/backend/src/controllersOld/MessageController.js similarity index 100% rename from backend/src/controllers/MessageController.js rename to backend/src/controllersOld/MessageController.js diff --git a/backend/src/controllers/SessionController.js b/backend/src/controllersOld/OldSessionController.js similarity index 100% rename from backend/src/controllers/SessionController.js rename to backend/src/controllersOld/OldSessionController.js diff --git a/backend/src/controllers/SettingController.js b/backend/src/controllersOld/OldSettingController.js similarity index 100% rename from backend/src/controllers/SettingController.js rename to backend/src/controllersOld/OldSettingController.js diff --git a/backend/src/controllers/OldUserController.js b/backend/src/controllersOld/OldUserController.js similarity index 100% rename from backend/src/controllers/OldUserController.js rename to backend/src/controllersOld/OldUserController.js diff --git a/backend/src/controllers/TicketController.js b/backend/src/controllersOld/TicketController.js similarity index 100% rename from backend/src/controllers/TicketController.js rename to backend/src/controllersOld/TicketController.js diff --git a/backend/src/controllers/WhatsAppController.js b/backend/src/controllersOld/WhatsAppController.js similarity index 100% rename from backend/src/controllers/WhatsAppController.js rename to backend/src/controllersOld/WhatsAppController.js diff --git a/backend/src/controllers/WhatsAppSessionController.js b/backend/src/controllersOld/WhatsAppSessionController.js similarity index 100% rename from backend/src/controllers/WhatsAppSessionController.js rename to backend/src/controllersOld/WhatsAppSessionController.js diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts index eb4b24a..60eb3dd 100644 --- a/backend/src/database/index.ts +++ b/backend/src/database/index.ts @@ -1,5 +1,6 @@ import { Sequelize } from "sequelize-typescript"; import User from "../models/User"; +import Setting from "../models/Setting"; // eslint-disable-next-line const dbConfig = require("../config/database"); @@ -10,18 +11,17 @@ const dbConfig = require("../config/database"); // const Message = require("../models/Message"); // const Whatsapp = require("../models/Whatsapp"); // const ContactCustomField = require("../models/ContactCustomField"); -// const Setting = require("../models/Setting"); const sequelize = new Sequelize(dbConfig); const models = [ - User + User, // Contact, // Ticket, // Message, // Whatsapp, // ContactCustomField, - // Setting, + Setting ]; sequelize.addModels(models); diff --git a/backend/src/errors/AppError.ts b/backend/src/errors/AppError.ts index 28e6c22..a8b1209 100644 --- a/backend/src/errors/AppError.ts +++ b/backend/src/errors/AppError.ts @@ -1,5 +1,6 @@ class AppError { public readonly message: string; + public readonly statusCode: number; constructor(message: string, statusCode = 400) { diff --git a/backend/src/helpers/CheckSettingsHelper.ts b/backend/src/helpers/CheckSettingsHelper.ts index 39e8a95..c398ea0 100644 --- a/backend/src/helpers/CheckSettingsHelper.ts +++ b/backend/src/helpers/CheckSettingsHelper.ts @@ -1,10 +1,8 @@ -import AppError from "../errors/AppError"; import Setting from "../models/Setting"; +import AppError from "../errors/AppError"; const CheckSettings = async (key: string): Promise => { - const settingsRepository = getRepository(Setting); - - const setting = await settingsRepository.findOne({ + const setting = await Setting.findOne({ where: { key } }); diff --git a/backend/src/middleware/is-auth.js b/backend/src/middleware/is-auth.js deleted file mode 100644 index 596d737..0000000 --- a/backend/src/middleware/is-auth.js +++ /dev/null @@ -1,36 +0,0 @@ -const jwt = require("jsonwebtoken"); -const util = require("util"); - -const User = require("../models/User"); -const authConfig = require("../config/auth"); - -module.exports = async (req, res, next) => { - const authHeader = req.headers.authorization; - - if (!authHeader) { - return res.status(401).json({ error: "Token not provided" }); - } - - const [, token] = authHeader.split(" "); - - try { - const decoded = await util.promisify(jwt.verify)(token, authConfig.secret); - - const user = await User.findByPk(decoded.userId, { - attributes: ["id", "name", "profile", "email"], - }); - - if (!user) { - return res - .status(401) - .json({ error: "The token corresponding user does not exists." }); - } - - req.user = user; - - return next(); - } catch (err) { - console.log(err); - return res.status(401).json({ error: "Invalid Token" }); - } -}; diff --git a/backend/src/middleware/isAuth.ts b/backend/src/middleware/isAuth.ts new file mode 100644 index 0000000..cc9d0be --- /dev/null +++ b/backend/src/middleware/isAuth.ts @@ -0,0 +1,35 @@ +import { verify } from "jsonwebtoken"; +import { Request, Response, NextFunction } from "express"; + +import AppError from "../errors/AppError"; +import authConfig from "../config/auth"; + +interface TokenPayload { + id: string; + username: string; + profile: string; + iat: number; + exp: number; +} + +const isAuth = (req: Request, res: Response, next: NextFunction): void => { + const authHeader = req.headers.authorization; + + if (!authHeader) { + throw new AppError("Token not provided.", 403); + } + + const [, token] = authHeader.split(" "); + + const decoded = verify(token, authConfig.secret); + const { id, profile } = decoded as TokenPayload; + + req.user = { + id, + profile + }; + + return next(); +}; + +export default isAuth; diff --git a/backend/src/models/Setting.ts b/backend/src/models/Setting.ts new file mode 100644 index 0000000..b58e57a --- /dev/null +++ b/backend/src/models/Setting.ts @@ -0,0 +1,26 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey +} from "sequelize-typescript"; + +@Table +class Setting extends Model { + @PrimaryKey + @Column + key: string; + + @Column + value: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default Setting; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index ad08c66..b61a6bf 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -15,7 +15,7 @@ class User extends Model { @Column name: string; - @Column + @Column(DataType.STRING) email: string; @Column(DataType.VIRTUAL) @@ -43,12 +43,12 @@ class User extends Model { } }; - // static checkPassword = async ( // maybe not work like this. - // instance: User, - // password: string - // ): Promise => { - // return compare(password, instance.passwordHash); - // }; + public checkPassword = async ( + // maybe not work like this. + password: string + ): Promise => { + return compare(password, this.getDataValue("passwordHash")); + }; } export default User; diff --git a/backend/src/routes/authRoutes.ts b/backend/src/routes/authRoutes.ts new file mode 100644 index 0000000..6f3b7da --- /dev/null +++ b/backend/src/routes/authRoutes.ts @@ -0,0 +1,11 @@ +import { Router } from "express"; +import SessionController from "../controllers/SessionController"; +import * as UserController from "../controllers/UserController"; + +const authRoutes = Router(); + +authRoutes.post("/signup", UserController.store); + +authRoutes.post("/login", SessionController); + +export default authRoutes; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 1dffd0a..2d7b26b 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -1,8 +1,8 @@ import { Router } from "express"; import userRoutes from "./userRoutes"; +import authRoutes from "./authRoutes"; -// const AuthRoutes = require("./routes/auth"); // const TicketsRoutes = require("./routes/tickets"); // const MessagesRoutes = require("./routes/messages"); // const ContactsRoutes = require("./routes/contacts"); @@ -13,7 +13,7 @@ import userRoutes from "./userRoutes"; const routes = Router(); routes.use(userRoutes); -// routes.use("/auth", AuthRoutes); +routes.use("/auth", authRoutes); // routes.use(TicketsRoutes); // routes.use(MessagesRoutes); // routes.use(ContactsRoutes); diff --git a/backend/src/routes/userRoutes.ts b/backend/src/routes/userRoutes.ts index e9526a0..a165db5 100644 --- a/backend/src/routes/userRoutes.ts +++ b/backend/src/routes/userRoutes.ts @@ -1,6 +1,6 @@ import { Router } from "express"; -// const isAuth = require("../../middleware/is-auth"); +import isAuth from "../middleware/isAuth"; import * as UserController from "../controllers/UserController"; const userRoutes = Router(); @@ -9,7 +9,7 @@ userRoutes.get("/users", (req, res) => res.json({ meessage: "lets do some prettier shit here" }) ); -userRoutes.post("/users", UserController.store); +userRoutes.post("/users", isAuth, UserController.store); // userRoutes.put("/users/:userId", isAuth, UserController.update); diff --git a/backend/src/server.ts b/backend/src/server.ts index 8dea37a..4914f24 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,7 +1,8 @@ import "dotenv/config"; import "express-async-errors"; -import express from "express"; +import express, { Request, Response, NextFunction } from "express"; import cors from "cors"; +import AppError from "./errors/AppError"; import routes from "./routes"; @@ -15,14 +16,21 @@ import "./database"; // const wbotMonitor = require("./services/wbotMonitor"); // const Whatsapp = require("./models/Whatsapp"); -// const Router = require("./router"); - const app = express(); app.use(cors()); app.use(express.json()); app.use(routes); +app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => { + if (err instanceof AppError) { + return res.status(err.statusCode).json({ error: err.message }); + } + + console.error(err); + return res.status(500).json({ error: "Internal server error" }); +}); + const server = app.listen(process.env.PORT, () => { console.log(`Server started on port: ${process.env.PORT}`); }); diff --git a/backend/src/services/AuthUserSerice.ts b/backend/src/services/AuthUserSerice.ts new file mode 100644 index 0000000..7b12496 --- /dev/null +++ b/backend/src/services/AuthUserSerice.ts @@ -0,0 +1,48 @@ +import { sign } from "jsonwebtoken"; + +import User from "../models/User"; +import AppError from "../errors/AppError"; +import authConfig from "../config/auth"; + +interface Request { + email: string; + password: string; +} + +interface Response { + user: User; + token: string; +} + +const AuthUserService = async ({ + email, + password +}: Request): Promise => { + const user = await User.findOne({ + where: { email } + }); + + if (!user) { + throw new AppError("Incorrect user/password combination.", 401); + } + + if (!(await user.checkPassword(password))) { + throw new AppError("Incorrect user/password combination.", 401); + } + + const { secret, expiresIn } = authConfig; + const token = sign( + { usarname: user.name, profile: user.profile, id: user.id }, + secret, + { + expiresIn + } + ); + + return { + user, + token + }; +}; + +export default AuthUserService; diff --git a/backend/src/services/CreateUserService.ts b/backend/src/services/CreateUserService.ts index 65363bf..6c06673 100644 --- a/backend/src/services/CreateUserService.ts +++ b/backend/src/services/CreateUserService.ts @@ -10,44 +10,59 @@ interface Request { profile?: string; } +interface Response { + email: string; + name: string; + id: number; + profile: string; +} + const CreateUserService = async ({ email, password, name, profile = "admin" -}: Request): Promise => { - // const schema = Yup.object().shape({ - // name: Yup.string().required().min(2), - // email: Yup.string() - // .email() - // .required() - // .test( - // "Check-email", - // "An user with this email already exists.", - // async value => { - // const emailExists = await User.findOne({ - // where: { email: value } - // }); - // return !Boolean(emailExists); - // } - // ), - // password: Yup.string().required().min(5) - // }); +}: Request): Promise => { + const schema = Yup.object().shape({ + name: Yup.string().required().min(2), + email: Yup.string() + .email() + .required() + .test( + "Check-email", + "An user with this email already exists.", + async value => { + if (value) { + const emailExists = await User.findOne({ + where: { email: value } + }); + return !emailExists; + } + return false; + } + ), + password: Yup.string().required().min(5) + }); - // try { - // await schema.validate({ email, password, name }); - // } catch (err) { - // throw new AppError(err.message); - // } + try { + await schema.validate({ email, password, name }); + } catch (err) { + throw new AppError(err.message); + } - const user = User.create({ + const user = await User.create({ email, password, name, profile }); - return user; + return { + id: user.id, + name: user.name, + email: user.email, + profile: user.profile + }; }; export default CreateUserService; diff --git a/backend/src/services/FindUserService.ts b/backend/src/services/FindUserService.ts index 17ab763..2a5c350 100644 --- a/backend/src/services/FindUserService.ts +++ b/backend/src/services/FindUserService.ts @@ -1,21 +1,17 @@ -import { getRepository, Raw } from "typeorm"; - import User from "../models/User"; import AppError from "../errors/AppError"; const FindUserService = async (id: string): Promise => { - const usersRepository = getRepository(User); + const user = await User.findOne({ + where: { id }, + attributes: ["name", "id", "email", "profile"] + }); - const user = await usersRepository.findOne({ - where: { id }, - select: ["name", "id", "email", "profile"], - }); + if (!user) { + throw new AppError("No user found with this ID.", 404); + } - if (!user) { - throw new AppError("No user found with this ID.", 404); - } - - return user; + return user; }; export default FindUserService; diff --git a/backend/yarn.lock b/backend/yarn.lock index f19d8b9..b3bac1c 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -218,6 +218,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/jsonwebtoken@^8.5.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5" + integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== + dependencies: + "@types/node" "*" + "@types/mime@*": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" From b3cf16bbd3f1340fb8c131688cb51ed74993f92a Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 14 Sep 2020 19:24:38 -0300 Subject: [PATCH 08/54] feat: add user remove route --- backend/src/controllers/UserController.ts | 75 ++++++++++++-------- backend/src/models/User.ts | 7 +- backend/src/routes/userRoutes.ts | 10 ++- backend/src/services/CreateUserService.ts | 4 +- backend/src/services/DeleteUserService.ts | 16 +++++ backend/src/services/ListUsersService.ts | 73 ++++++++++--------- backend/src/services/UpdateUserService.ts | 85 ++++++++++++----------- 7 files changed, 155 insertions(+), 115 deletions(-) create mode 100644 backend/src/services/DeleteUserService.ts diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index f0682c5..f4ff0b0 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -4,23 +4,23 @@ import CheckSettingsHelper from "../helpers/CheckSettingsHelper"; import AppError from "../errors/AppError"; import CreateUserService from "../services/CreateUserService"; -// import UpdateUserService from "../services/UpdateUserService"; -// import ListUsersService from "../services/ListUsersService"; -// import FindUserService from "../services/FindUserService"; +import ListUsersService from "../services/ListUsersService"; +import UpdateUserService from "../services/UpdateUserService"; +import FindUserService from "../services/FindUserService"; +import DeleteUserService from "../services/DeleteUserService"; export const index = async (req: Request, res: Response): Promise => { - // if (req.user.profile !== "admin") { - // throw new AppError("Only administrators can access this route.", 403); // should be handled better. - // } - // const { searchParam, pageNumber } = req.query as any; + if (req.user.profile !== "admin") { + throw new AppError("Only administrators can access this route.", 403); // should be handled better. + } + const { searchParam, pageNumber } = req.query as any; - // const { users, count, hasMore } = await ListUsersService({ - // searchParam, - // pageNumber - // }); + const { users, count, hasMore } = await ListUsersService({ + searchParam, + pageNumber + }); - // return res.json({ users, count, hasMore }); - return res.json({ ok: "ok" }); + return res.json({ users, count, hasMore }); }; export const store = async (req: Request, res: Response): Promise => { @@ -45,26 +45,41 @@ export const store = async (req: Request, res: Response): Promise => { return res.status(200).json(user); }; -// export const show = async (req: Request, res: Response): Promise => { -// const { userId } = req.params; +export const show = async (req: Request, res: Response): Promise => { + const { userId } = req.params; -// const user = await FindUserService(userId); + const user = await FindUserService(userId); -// return res.status(200).json(user); -// }; + return res.status(200).json(user); +}; -// export const update = async ( -// req: Request, -// res: Response -// ): Promise => { -// if (req.user.profile !== "admin") { -// throw new AppError("Only administrators can edit users.", 403); -// } +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("Only administrators can edit users.", 403); + } -// const { userId } = req.params; -// const userData = req.body; + const { userId } = req.params; + const userData = req.body; -// const user = await UpdateUserService({ userData, userId }); + const user = await UpdateUserService({ userData, userId }); -// return res.status(200).json(user); -// }; + return res.status(200).json(user); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { userId } = req.params; + + if (req.user.profile !== "admin") { + throw new AppError("Only administrators can delete users.", 403); + } + + await DeleteUserService(userId); + + return res.status(200).json({ message: "User deleted" }); +}; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index b61a6bf..80fabc0 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -6,12 +6,17 @@ import { Model, DataType, BeforeCreate, - BeforeUpdate + BeforeUpdate, + PrimaryKey } from "sequelize-typescript"; import { hash, compare } from "bcryptjs"; @Table class User extends Model { + @PrimaryKey + @Column + id: number; + @Column name: string; diff --git a/backend/src/routes/userRoutes.ts b/backend/src/routes/userRoutes.ts index a165db5..ad02251 100644 --- a/backend/src/routes/userRoutes.ts +++ b/backend/src/routes/userRoutes.ts @@ -5,16 +5,14 @@ import * as UserController from "../controllers/UserController"; const userRoutes = Router(); -userRoutes.get("/users", (req, res) => - res.json({ meessage: "lets do some prettier shit here" }) -); +userRoutes.get("/users", isAuth, UserController.index); userRoutes.post("/users", isAuth, UserController.store); -// userRoutes.put("/users/:userId", isAuth, UserController.update); +userRoutes.put("/users/:userId", isAuth, UserController.update); -// userRoutes.get("/users/:userId", isAuth, UserController.show); +userRoutes.get("/users/:userId", isAuth, UserController.show); -// userRoutes.delete("/users/:userId", isAuth, UserController.delete); +userRoutes.delete("/users/:userId", isAuth, UserController.remove); export default userRoutes; diff --git a/backend/src/services/CreateUserService.ts b/backend/src/services/CreateUserService.ts index 6c06673..cab10d3 100644 --- a/backend/src/services/CreateUserService.ts +++ b/backend/src/services/CreateUserService.ts @@ -57,12 +57,14 @@ const CreateUserService = async ({ profile }); - return { + const serializedUser = { id: user.id, name: user.name, email: user.email, profile: user.profile }; + + return serializedUser; }; export default CreateUserService; diff --git a/backend/src/services/DeleteUserService.ts b/backend/src/services/DeleteUserService.ts new file mode 100644 index 0000000..ce63dcb --- /dev/null +++ b/backend/src/services/DeleteUserService.ts @@ -0,0 +1,16 @@ +import User from "../models/User"; +import AppError from "../errors/AppError"; + +const DeleteUserService = async (id: string): Promise => { + const user = await User.findOne({ + where: { id } + }); + + if (!user) { + throw new AppError("No user found with this ID.", 404); + } + + await user.destroy(); +}; + +export default DeleteUserService; diff --git a/backend/src/services/ListUsersService.ts b/backend/src/services/ListUsersService.ts index 5381760..83aed24 100644 --- a/backend/src/services/ListUsersService.ts +++ b/backend/src/services/ListUsersService.ts @@ -1,54 +1,51 @@ -import { getRepository, Raw } from "typeorm"; - +import { Sequelize, Op } from "sequelize"; import User from "../models/User"; interface Request { - searchParam?: string; - pageNumber?: number; + searchParam?: string; + pageNumber?: number; } interface Response { - users: User[]; - count: number; - hasMore: boolean; + users: User[]; + count: number; + hasMore: boolean; } const ListUsersService = async ({ - searchParam = "", - pageNumber = 1, + searchParam = "", + pageNumber = 1 }: Request): Promise => { - const usersRepository = getRepository(User); + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + }, + { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } } + ] + }; + const limit = 20; + const offset = limit * (pageNumber - 1); - const whereCondition = [ - { - name: Raw( - alias => `LOWER(${alias}) Like '%${searchParam.toLowerCase()}%'` - ), - }, - { - email: Raw( - alias => `LOWER(${alias}) Like '%${searchParam.toLowerCase()}%'` - ), - }, - ]; - const take = 20; - const skip = take * (pageNumber - 1); + const { count, rows: users } = await User.findAndCountAll({ + where: whereCondition, + attributes: ["name", "id", "email", "profile"], + limit, + offset, + order: [["createdAt", "DESC"]] + }); - const [users, count] = await usersRepository.findAndCount({ - where: whereCondition, - select: ["name", "id", "email", "profile"], - skip, - take, - order: { createdAt: "DESC" }, - }); + const hasMore = count > limit + users.length; - const hasMore = count > skip + users.length; - - return { - users, - count, - hasMore, - }; + return { + users, + count, + hasMore + }; }; export default ListUsersService; diff --git a/backend/src/services/UpdateUserService.ts b/backend/src/services/UpdateUserService.ts index 39cfe68..0fccf9d 100644 --- a/backend/src/services/UpdateUserService.ts +++ b/backend/src/services/UpdateUserService.ts @@ -1,61 +1,68 @@ -import { getRepository } from "typeorm"; import * as Yup from "yup"; import AppError from "../errors/AppError"; import User from "../models/User"; interface UserData { - email?: string; - password?: string; - name?: string; - profile?: string; + email?: string; + password?: string; + name?: string; + profile?: string; } interface Request { - userData: UserData; - userId: string; + userData: UserData; + userId: string; +} + +interface Response { + id: number; + name: string; + email: string; + profile: string; } const UpdateUserService = async ({ - userData, - userId, -}: Request): Promise => { - const usersRepository = getRepository(User); + userData, + userId +}: Request): Promise => { + const schema = Yup.object().shape({ + name: Yup.string().min(2), + email: Yup.string().email(), + password: Yup.string() + }); - const schema = Yup.object().shape({ - name: Yup.string().min(2), - email: Yup.string().email(), - password: Yup.string(), - }); + const { email, password, name } = userData; - const { email, password, name } = userData; + try { + await schema.validate({ email, password, name }); + } catch (err) { + throw new AppError(err.message); + } - try { - await schema.validate({ email, password, name }); - } catch (err) { - throw new AppError(err.message); - } + const user = await User.findOne({ + where: { id: userId }, + attributes: ["name", "id", "email", "profile"] + }); - const user = await usersRepository.findOne({ - where: { id: userId }, - select: ["name", "id", "email", "profile"], - }); + if (!user) { + throw new AppError("No user found with this ID.", 404); + } - if (!user) { - throw new AppError("No user found with this ID.", 404); - } + await user.update({ + email, + password, + name + }); - const teste = await usersRepository.update(userId, { - email, - passwordHash: password, - name, - }); + const serializedUser = { + id: user.id, + name: user.name, + email: user.email, + profile: user.profile + }; - console.log(teste); - - delete user.passwordHash; - - return user; + return serializedUser; }; export default UpdateUserService; From 149cbef98e78bd3051f077926ff45be94c5b2cfe Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 15 Sep 2020 12:59:20 -0300 Subject: [PATCH 09/54] updated scripts --- backend/.eslintrc.json | 4 ++++ backend/package.json | 1 + backend/src/server.ts | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json index 17e8e9c..7aba60d 100644 --- a/backend/.eslintrc.json +++ b/backend/.eslintrc.json @@ -16,6 +16,10 @@ }, "plugins": ["@typescript-eslint", "prettier"], "rules": { + "@typescript-eslint/no-unused-vars": [ + "error", + { "argsIgnorePattern": "_" } + ], "no-console": "off", "no-param-reassign": "off", "prettier/prettier": "error", diff --git a/backend/package.json b/backend/package.json index 739d214..6719105 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "build": "tsc", + "start": "nodemon dist/server.js", "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" }, "author": "", diff --git a/backend/src/server.ts b/backend/src/server.ts index 4914f24..a8d6c21 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -5,11 +5,11 @@ import cors from "cors"; import AppError from "./errors/AppError"; import routes from "./routes"; +import "./database"; // import path from "path"; // import multer from "multer"; // import Sentry from "@sentry/node"; -import "./database"; // const { initWbot } = require("./libs/wbot"); // const wbotMessageListener = require("./services/wbotMessageListener"); @@ -22,7 +22,7 @@ app.use(cors()); app.use(express.json()); app.use(routes); -app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => { +app.use(async (err: Error, req: Request, res: Response, _: NextFunction) => { if (err instanceof AppError) { return res.status(err.statusCode).json({ error: err.message }); } From 14d90a2dd4267fccfae16d7c1b35e879f5a7b65c Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 15 Sep 2020 17:16:29 -0300 Subject: [PATCH 10/54] migrated setting routes to typescript --- backend/src/controllers/SessionController.ts | 2 +- backend/src/controllers/SettingController.ts | 41 +++++++++++++++++++ backend/src/controllers/UserController.ts | 10 ++--- ...sionController.js => SessionController.js} | 0 ...tingController.js => SettingController.js} | 0 ...OldUserController.js => UserController.js} | 0 backend/src/routes/index.ts | 4 +- backend/src/routes/routes/settings.js | 14 ------- backend/src/routes/settingRoutes.ts | 15 +++++++ .../SettingServices/ListSettingsService.ts | 9 ++++ .../SettingServices/UpdateSettingService.ts | 26 ++++++++++++ .../{ => UserServices}/AuthUserSerice.ts | 6 +-- .../{ => UserServices}/CreateUserService.ts | 4 +- .../{ => UserServices}/DeleteUserService.ts | 4 +- .../{ => UserServices}/FindUserService.ts | 4 +- .../{ => UserServices}/ListUsersService.ts | 2 +- .../{ => UserServices}/UpdateUserService.ts | 4 +- 17 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 backend/src/controllers/SettingController.ts rename backend/src/controllersOld/{OldSessionController.js => SessionController.js} (100%) rename backend/src/controllersOld/{OldSettingController.js => SettingController.js} (100%) rename backend/src/controllersOld/{OldUserController.js => UserController.js} (100%) delete mode 100644 backend/src/routes/routes/settings.js create mode 100644 backend/src/routes/settingRoutes.ts create mode 100644 backend/src/services/SettingServices/ListSettingsService.ts create mode 100644 backend/src/services/SettingServices/UpdateSettingService.ts rename backend/src/services/{ => UserServices}/AuthUserSerice.ts (85%) rename backend/src/services/{ => UserServices}/CreateUserService.ts (93%) rename backend/src/services/{ => UserServices}/DeleteUserService.ts (76%) rename backend/src/services/{ => UserServices}/FindUserService.ts (79%) rename backend/src/services/{ => UserServices}/ListUsersService.ts (96%) rename backend/src/services/{ => UserServices}/UpdateUserService.ts (93%) diff --git a/backend/src/controllers/SessionController.ts b/backend/src/controllers/SessionController.ts index 73b3646..3732b96 100644 --- a/backend/src/controllers/SessionController.ts +++ b/backend/src/controllers/SessionController.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; -import AuthUserService from "../services/AuthUserSerice"; +import AuthUserService from "../services/UserServices/AuthUserSerice"; const store = async (req: Request, res: Response): Promise => { const { email, password } = req.body; diff --git a/backend/src/controllers/SettingController.ts b/backend/src/controllers/SettingController.ts new file mode 100644 index 0000000..1d7411d --- /dev/null +++ b/backend/src/controllers/SettingController.ts @@ -0,0 +1,41 @@ +import { Request, Response } from "express"; + +import AppError from "../errors/AppError"; + +import UpdateSettingService from "../services/SettingServices/UpdateSettingService"; +import ListSettingsService from "../services/SettingServices/ListSettingsService"; +// const { getIO } = require("../libs/socket"); + +export const index = async (req: Request, res: Response): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("Only administrators can access resource.", 403); + } + + const settings = await ListSettingsService(); + + return res.status(200).json(settings); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + if (req.user.profile !== "admin") { + throw new AppError("Only administrators can access this route.", 403); + } + const { settingKey: key } = req.params; + const { value } = req.body; + + const setting = await UpdateSettingService({ + key, + value + }); + + // const io = getIO(); + // io.emit("settings", { + // action: "update", + // setting + // }); + + return res.status(200).json(setting); +}; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index f4ff0b0..11f77db 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -3,11 +3,11 @@ import { Request, Response } from "express"; import CheckSettingsHelper from "../helpers/CheckSettingsHelper"; import AppError from "../errors/AppError"; -import CreateUserService from "../services/CreateUserService"; -import ListUsersService from "../services/ListUsersService"; -import UpdateUserService from "../services/UpdateUserService"; -import FindUserService from "../services/FindUserService"; -import DeleteUserService from "../services/DeleteUserService"; +import CreateUserService from "../services/UserServices/CreateUserService"; +import ListUsersService from "../services/UserServices/ListUsersService"; +import UpdateUserService from "../services/UserServices/UpdateUserService"; +import FindUserService from "../services/UserServices/FindUserService"; +import DeleteUserService from "../services/UserServices/DeleteUserService"; export const index = async (req: Request, res: Response): Promise => { if (req.user.profile !== "admin") { diff --git a/backend/src/controllersOld/OldSessionController.js b/backend/src/controllersOld/SessionController.js similarity index 100% rename from backend/src/controllersOld/OldSessionController.js rename to backend/src/controllersOld/SessionController.js diff --git a/backend/src/controllersOld/OldSettingController.js b/backend/src/controllersOld/SettingController.js similarity index 100% rename from backend/src/controllersOld/OldSettingController.js rename to backend/src/controllersOld/SettingController.js diff --git a/backend/src/controllersOld/OldUserController.js b/backend/src/controllersOld/UserController.js similarity index 100% rename from backend/src/controllersOld/OldUserController.js rename to backend/src/controllersOld/UserController.js diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 2d7b26b..42b4fdc 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -2,22 +2,22 @@ import { Router } from "express"; import userRoutes from "./userRoutes"; import authRoutes from "./authRoutes"; +import settingRoutes from "./settingRoutes"; // const TicketsRoutes = require("./routes/tickets"); // const MessagesRoutes = require("./routes/messages"); // const ContactsRoutes = require("./routes/contacts"); // const WhatsRoutes = require("./routes/whatsapp"); // const UsersRoutes = require("./routes/users"); -// const SettingsRoutes = require("./routes/settings"); const routes = Router(); routes.use(userRoutes); routes.use("/auth", authRoutes); +routes.use(settingRoutes); // routes.use(TicketsRoutes); // routes.use(MessagesRoutes); // routes.use(ContactsRoutes); // routes.use(WhatsRoutes); -// routes.use(SettingsRoutes); export default routes; diff --git a/backend/src/routes/routes/settings.js b/backend/src/routes/routes/settings.js deleted file mode 100644 index fc0fe65..0000000 --- a/backend/src/routes/routes/settings.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require("express"); -const isAuth = require("../../middleware/is-auth"); - -const SettingController = require("../../controllers/SettingController"); - -const routes = express.Router(); - -routes.get("/settings", isAuth, SettingController.index); - -// routes.get("/settings/:settingKey", isAuth, SettingsController.show); - -routes.put("/settings/:settingKey", isAuth, SettingController.update); - -module.exports = routes; diff --git a/backend/src/routes/settingRoutes.ts b/backend/src/routes/settingRoutes.ts new file mode 100644 index 0000000..625864a --- /dev/null +++ b/backend/src/routes/settingRoutes.ts @@ -0,0 +1,15 @@ +import { Router } from "express"; +import isAuth from "../middleware/isAuth"; + +import * as SettingController from "../controllers/SettingController"; + +const settingRoutes = Router(); + +settingRoutes.get("/settings", isAuth, SettingController.index); + +// routes.get("/settings/:settingKey", isAuth, SettingsController.show); + +// change setting key to key in future +settingRoutes.put("/settings/:settingKey", isAuth, SettingController.update); + +export default settingRoutes; diff --git a/backend/src/services/SettingServices/ListSettingsService.ts b/backend/src/services/SettingServices/ListSettingsService.ts new file mode 100644 index 0000000..c3e2a19 --- /dev/null +++ b/backend/src/services/SettingServices/ListSettingsService.ts @@ -0,0 +1,9 @@ +import Setting from "../../models/Setting"; + +const ListSettingsService = async (): Promise => { + const settings = await Setting.findAll(); + + return settings; +}; + +export default ListSettingsService; diff --git a/backend/src/services/SettingServices/UpdateSettingService.ts b/backend/src/services/SettingServices/UpdateSettingService.ts new file mode 100644 index 0000000..12f9a48 --- /dev/null +++ b/backend/src/services/SettingServices/UpdateSettingService.ts @@ -0,0 +1,26 @@ +import AppError from "../../errors/AppError"; +import Setting from "../../models/Setting"; + +interface Request { + key: string; + value: string; +} + +const UpdateSettingService = async ({ + key, + value +}: Request): Promise => { + const setting = await Setting.findOne({ + where: { key } + }); + + if (!setting) { + throw new AppError("No setting found with this ID.", 404); + } + + await setting.update({ value }); + + return setting; +}; + +export default UpdateSettingService; diff --git a/backend/src/services/AuthUserSerice.ts b/backend/src/services/UserServices/AuthUserSerice.ts similarity index 85% rename from backend/src/services/AuthUserSerice.ts rename to backend/src/services/UserServices/AuthUserSerice.ts index 7b12496..a6e36d3 100644 --- a/backend/src/services/AuthUserSerice.ts +++ b/backend/src/services/UserServices/AuthUserSerice.ts @@ -1,8 +1,8 @@ import { sign } from "jsonwebtoken"; -import User from "../models/User"; -import AppError from "../errors/AppError"; -import authConfig from "../config/auth"; +import User from "../../models/User"; +import AppError from "../../errors/AppError"; +import authConfig from "../../config/auth"; interface Request { email: string; diff --git a/backend/src/services/CreateUserService.ts b/backend/src/services/UserServices/CreateUserService.ts similarity index 93% rename from backend/src/services/CreateUserService.ts rename to backend/src/services/UserServices/CreateUserService.ts index cab10d3..531b935 100644 --- a/backend/src/services/CreateUserService.ts +++ b/backend/src/services/UserServices/CreateUserService.ts @@ -1,7 +1,7 @@ import * as Yup from "yup"; -import AppError from "../errors/AppError"; -import User from "../models/User"; +import AppError from "../../errors/AppError"; +import User from "../../models/User"; interface Request { email: string; diff --git a/backend/src/services/DeleteUserService.ts b/backend/src/services/UserServices/DeleteUserService.ts similarity index 76% rename from backend/src/services/DeleteUserService.ts rename to backend/src/services/UserServices/DeleteUserService.ts index ce63dcb..f076730 100644 --- a/backend/src/services/DeleteUserService.ts +++ b/backend/src/services/UserServices/DeleteUserService.ts @@ -1,5 +1,5 @@ -import User from "../models/User"; -import AppError from "../errors/AppError"; +import User from "../../models/User"; +import AppError from "../../errors/AppError"; const DeleteUserService = async (id: string): Promise => { const user = await User.findOne({ diff --git a/backend/src/services/FindUserService.ts b/backend/src/services/UserServices/FindUserService.ts similarity index 79% rename from backend/src/services/FindUserService.ts rename to backend/src/services/UserServices/FindUserService.ts index 2a5c350..868bf15 100644 --- a/backend/src/services/FindUserService.ts +++ b/backend/src/services/UserServices/FindUserService.ts @@ -1,5 +1,5 @@ -import User from "../models/User"; -import AppError from "../errors/AppError"; +import User from "../../models/User"; +import AppError from "../../errors/AppError"; const FindUserService = async (id: string): Promise => { const user = await User.findOne({ diff --git a/backend/src/services/ListUsersService.ts b/backend/src/services/UserServices/ListUsersService.ts similarity index 96% rename from backend/src/services/ListUsersService.ts rename to backend/src/services/UserServices/ListUsersService.ts index 83aed24..4430724 100644 --- a/backend/src/services/ListUsersService.ts +++ b/backend/src/services/UserServices/ListUsersService.ts @@ -1,5 +1,5 @@ import { Sequelize, Op } from "sequelize"; -import User from "../models/User"; +import User from "../../models/User"; interface Request { searchParam?: string; diff --git a/backend/src/services/UpdateUserService.ts b/backend/src/services/UserServices/UpdateUserService.ts similarity index 93% rename from backend/src/services/UpdateUserService.ts rename to backend/src/services/UserServices/UpdateUserService.ts index 0fccf9d..438b85b 100644 --- a/backend/src/services/UpdateUserService.ts +++ b/backend/src/services/UserServices/UpdateUserService.ts @@ -1,7 +1,7 @@ import * as Yup from "yup"; -import AppError from "../errors/AppError"; -import User from "../models/User"; +import AppError from "../../errors/AppError"; +import User from "../../models/User"; interface UserData { email?: string; From b04c0b878e1c817fc4b59f318871e63113a52cd3 Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 15 Sep 2020 19:28:06 -0300 Subject: [PATCH 11/54] changed contacts routes to typescript --- backend/src/controllers/ContactController.ts | 174 ++++++++++++++++++ backend/src/controllers/UserController.ts | 11 +- backend/src/database/index.ts | 8 +- backend/src/models/Contact.ts | 45 +++++ backend/src/models/Setting.ts | 5 +- backend/src/models/Ticket.ts | 36 ++++ backend/src/models/User.ts | 17 +- backend/src/modelsOld/Contact.js | 58 +++--- backend/src/modelsOld/Ticket.js | 78 ++++---- backend/src/routes/authRoutes.ts | 7 +- backend/src/routes/contactRoutes.ts | 21 +++ backend/src/routes/index.ts | 4 +- backend/src/routes/routes/auth.js | 16 -- backend/src/routes/routes/contacts.js | 21 --- .../ContactServices/CreateContactService.ts | 32 ++++ .../ContactServices/DeleteContactService.ts | 16 ++ .../ContactServices/ListContactsService.ts | 50 +++++ .../ContactServices/ShowContactService.ts | 17 ++ .../ContactServices/UpdateContactService.ts | 39 ++++ .../services/UserServices/ListUsersService.ts | 10 +- ...{FindUserService.ts => ShowUserService.ts} | 4 +- 21 files changed, 536 insertions(+), 133 deletions(-) create mode 100644 backend/src/controllers/ContactController.ts create mode 100644 backend/src/models/Contact.ts create mode 100644 backend/src/models/Ticket.ts create mode 100644 backend/src/routes/contactRoutes.ts delete mode 100644 backend/src/routes/routes/auth.js delete mode 100644 backend/src/routes/routes/contacts.js create mode 100644 backend/src/services/ContactServices/CreateContactService.ts create mode 100644 backend/src/services/ContactServices/DeleteContactService.ts create mode 100644 backend/src/services/ContactServices/ListContactsService.ts create mode 100644 backend/src/services/ContactServices/ShowContactService.ts create mode 100644 backend/src/services/ContactServices/UpdateContactService.ts rename backend/src/services/UserServices/{FindUserService.ts => ShowUserService.ts} (76%) diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts new file mode 100644 index 0000000..a2081ad --- /dev/null +++ b/backend/src/controllers/ContactController.ts @@ -0,0 +1,174 @@ +import { Request, Response } from "express"; + +import ListContactsService from "../services/ContactServices/ListContactsService"; +import CreateContactService from "../services/ContactServices/CreateContactService"; +import ShowContactService from "../services/ContactServices/ShowContactService"; +import UpdateContactService from "../services/ContactServices/UpdateContactService"; +import DeleteContactService from "../services/ContactServices/DeleteContactService"; + +// const Sequelize = require("sequelize"); +// const { Op } = require("sequelize"); + +// const Contact = require("../models/Contact"); +// const Whatsapp = require("../models/Whatsapp"); +// const ContactCustomField = require("../models/ContactCustomField"); + +// const { getIO } = require("../libs/socket"); +// const { getWbot } = require("../libs/wbot"); + +type RequestQuery = { + searchParam: string; + pageNumber: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as RequestQuery; + + const { contacts, count, hasMore } = await ListContactsService({ + searchParam, + pageNumber + }); + + return res.json({ contacts, count, hasMore }); +}; + +interface ContactData { + name: string; + number: string; + email?: string; +} + +export const store = async (req: Request, res: Response): Promise => { + const { name, number, email }: ContactData = req.body; + + const contact = await CreateContactService({ name, number, email }); + + // const defaultWhatsapp = await Whatsapp.findOne({ + // where: { default: true } + // }); + + // if (!defaultWhatsapp) { + // return res + // .status(404) + // .json({ error: "No default WhatsApp found. Check Connection page." }); + // } + + // const wbot = getWbot(defaultWhatsapp); + // const io = getIO(); + + // try { + // const isValidNumber = await wbot.isRegisteredUser( + // `${newContact.number}@c.us` + // ); + // if (!isValidNumber) { + // return res + // .status(400) + // .json({ error: "The suplied number is not a valid Whatsapp number" }); + // } + // } catch (err) { + // console.log(err); + // return res.status(500).json({ + // error: "Could not check whatsapp contact. Check connection page." + // }); + // } + + // const profilePicUrl = await wbot.getProfilePicUrl( + // `${newContact.number}@c.us` + // ); + + // const contact = await Contact.create( + // { ...newContact, profilePicUrl }, + // { + // include: "extraInfo" + // } + // ); + + // io.emit("contact", { + // action: "create", + // contact: contact + // }); + + return res.status(200).json(contact); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { contactId } = req.params; + + const contact = await ShowContactService(contactId); + + return res.status(200).json(contact); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const contactData = req.body; + + const { contactId } = req.params; + + const contact = await UpdateContactService({ contactData, contactId }); + + // const io = getIO(); + // const contact = await Contact.findByPk(contactId, { + // include: "extraInfo" + // }); + + // if (!contact) { + // return res.status(404).json({ error: "No contact found with this ID" }); + // } + + // if (updatedContact.extraInfo) { + // await Promise.all( + // updatedContact.extraInfo.map(async info => { + // await ContactCustomField.upsert({ ...info, contactId: contact.id }); + // }) + // ); + + // await Promise.all( + // contact.extraInfo.map(async oldInfo => { + // let stillExists = updatedContact.extraInfo.findIndex( + // info => info.id === oldInfo.id + // ); + + // if (stillExists === -1) { + // await ContactCustomField.destroy({ where: { id: oldInfo.id } }); + // } + // }) + // ); + // } + + // await contact.update(updatedContact); + + // io.emit("contact", { + // action: "update", + // contact: contact + // }); + + return res.status(200).json(contact); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + // const io = getIO(); + const { contactId } = req.params; + + await DeleteContactService(contactId); + + // const contact = await Contact.findByPk(contactId); + + // if (!contact) { + // return res.status(404).json({ error: "No contact found with this ID" }); + // } + + // await contact.destroy(); + + // io.emit("contact", { + // action: "delete", + // contactId: contactId + // }); + + return res.status(200).json({ message: "Contact deleted" }); +}; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 11f77db..062222c 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -6,14 +6,19 @@ import AppError from "../errors/AppError"; import CreateUserService from "../services/UserServices/CreateUserService"; import ListUsersService from "../services/UserServices/ListUsersService"; import UpdateUserService from "../services/UserServices/UpdateUserService"; -import FindUserService from "../services/UserServices/FindUserService"; +import ShowUserService from "../services/UserServices/ShowUserService"; import DeleteUserService from "../services/UserServices/DeleteUserService"; +type RequestQuery = { + searchParam: string; + pageNumber: string; +}; + export const index = async (req: Request, res: Response): Promise => { if (req.user.profile !== "admin") { throw new AppError("Only administrators can access this route.", 403); // should be handled better. } - const { searchParam, pageNumber } = req.query as any; + const { searchParam, pageNumber } = req.query as RequestQuery; const { users, count, hasMore } = await ListUsersService({ searchParam, @@ -48,7 +53,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { userId } = req.params; - const user = await FindUserService(userId); + const user = await ShowUserService(userId); return res.status(200).json(user); }; diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts index 60eb3dd..da0628b 100644 --- a/backend/src/database/index.ts +++ b/backend/src/database/index.ts @@ -1,13 +1,13 @@ import { Sequelize } from "sequelize-typescript"; import User from "../models/User"; import Setting from "../models/Setting"; +import Contact from "../models/Contact"; +import Ticket from "../models/Ticket"; // eslint-disable-next-line const dbConfig = require("../config/database"); // import dbConfig from "../config/database"; -// const Contact = require("../models/Contact"); -// const Ticket = require("../models/Ticket"); // const Message = require("../models/Message"); // const Whatsapp = require("../models/Whatsapp"); // const ContactCustomField = require("../models/ContactCustomField"); @@ -16,8 +16,8 @@ const sequelize = new Sequelize(dbConfig); const models = [ User, - // Contact, - // Ticket, + Contact, + Ticket, // Message, // Whatsapp, // ContactCustomField, diff --git a/backend/src/models/Contact.ts b/backend/src/models/Contact.ts new file mode 100644 index 0000000..99ec142 --- /dev/null +++ b/backend/src/models/Contact.ts @@ -0,0 +1,45 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + PrimaryKey, + AutoIncrement, + AllowNull, + Unique, + Default +} from "sequelize-typescript"; + +@Table +class Contact extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column(DataType.STRING) + name: string; + + @AllowNull(false) + @Unique + @Column + number: string; + + @AllowNull(false) + @Default("") + @Column + email: string; + + @Column + profilePicUrl: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default Contact; diff --git a/backend/src/models/Setting.ts b/backend/src/models/Setting.ts index b58e57a..13ba0e1 100644 --- a/backend/src/models/Setting.ts +++ b/backend/src/models/Setting.ts @@ -4,16 +4,17 @@ import { CreatedAt, UpdatedAt, Model, + DataType, PrimaryKey } from "sequelize-typescript"; @Table class Setting extends Model { @PrimaryKey - @Column + @Column(DataType.STRING) key: string; - @Column + @Column(DataType.STRING) value: string; @CreatedAt diff --git a/backend/src/models/Ticket.ts b/backend/src/models/Ticket.ts new file mode 100644 index 0000000..1e8ce3e --- /dev/null +++ b/backend/src/models/Ticket.ts @@ -0,0 +1,36 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + PrimaryKey +} from "sequelize-typescript"; + +@Table +class Ticket extends Model { + @PrimaryKey + @Column(DataType.NUMBER) + id: number; + + @Column({ defaultValue: "pending" }) + status: string; + + // @Column({ allowNull: false, unique: true }) + // userId: string; + + @Column(DataType.VIRTUAL) + unreadMessages: string; + + @Column(DataType.STRING) + lastMessage: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default Ticket; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 80fabc0..73981b6 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -7,20 +7,23 @@ import { DataType, BeforeCreate, BeforeUpdate, - PrimaryKey + PrimaryKey, + AutoIncrement, + Default } from "sequelize-typescript"; import { hash, compare } from "bcryptjs"; @Table class User extends Model { @PrimaryKey + @AutoIncrement @Column id: number; @Column name: string; - @Column(DataType.STRING) + @Column email: string; @Column(DataType.VIRTUAL) @@ -29,9 +32,8 @@ class User extends Model { @Column passwordHash: string; - @Column({ - defaultValue: "admin" - }) + @Default("admin") + @Column profile: string; @CreatedAt @@ -48,10 +50,7 @@ class User extends Model { } }; - public checkPassword = async ( - // maybe not work like this. - password: string - ): Promise => { + public checkPassword = async (password: string): Promise => { return compare(password, this.getDataValue("passwordHash")); }; } diff --git a/backend/src/modelsOld/Contact.js b/backend/src/modelsOld/Contact.js index 43ab6e5..6187477 100644 --- a/backend/src/modelsOld/Contact.js +++ b/backend/src/modelsOld/Contact.js @@ -1,29 +1,29 @@ -const Sequelize = require("sequelize"); - -class Contact extends Sequelize.Model { - static init(sequelize) { - super.init( - { - name: { type: Sequelize.STRING }, - number: { type: Sequelize.STRING, allowNull: false, unique: true }, - email: { type: Sequelize.STRING, allowNull: false, defaultValue: "" }, - profilePicUrl: { type: Sequelize.STRING }, - }, - { - sequelize, - } - ); - - return this; - } - - static associate(models) { - this.hasMany(models.Ticket, { foreignKey: "contactId", as: "contact" }); - this.hasMany(models.ContactCustomField, { - foreignKey: "contactId", - as: "extraInfo", - }); - } -} - -module.exports = Contact; +const Sequelize = require("sequelize"); + +class Contact extends Sequelize.Model { + static init(sequelize) { + super.init( + { + name: { type: Sequelize.STRING }, + number: { type: Sequelize.STRING, allowNull: false, unique: true }, + email: { type: Sequelize.STRING, allowNull: false, defaultValue: "" }, + profilePicUrl: { type: Sequelize.STRING } + }, + { + sequelize + } + ); + + return this; + } + + static associate(models) { + this.hasMany(models.Ticket, { foreignKey: "contactId", as: "contact" }); + this.hasMany(models.ContactCustomField, { + foreignKey: "contactId", + as: "extraInfo" + }); + } +} + +module.exports = Contact; diff --git a/backend/src/modelsOld/Ticket.js b/backend/src/modelsOld/Ticket.js index 771eba5..bcff844 100644 --- a/backend/src/modelsOld/Ticket.js +++ b/backend/src/modelsOld/Ticket.js @@ -2,49 +2,49 @@ const Sequelize = require("sequelize"); const Message = require("./Message"); class Ticket extends Sequelize.Model { - static init(sequelize) { - super.init( - { - status: { type: Sequelize.STRING, defaultValue: "pending" }, - userId: { type: Sequelize.INTEGER, defaultValue: null }, - unreadMessages: { type: Sequelize.VIRTUAL }, - lastMessage: { type: Sequelize.STRING }, - }, - { - sequelize, - } - ); + static init(sequelize) { + super.init( + { + status: { type: Sequelize.STRING, defaultValue: "pending" }, + userId: { type: Sequelize.INTEGER, defaultValue: null }, + unreadMessages: { type: Sequelize.VIRTUAL }, + lastMessage: { type: Sequelize.STRING } + }, + { + sequelize + } + ); - this.addHook("afterFind", async result => { - if (result && result.length > 0) { - await Promise.all( - result.map(async ticket => { - ticket.unreadMessages = await Message.count({ - where: { ticketId: ticket.id, read: false }, - }); - }) - ); - } - }); + this.addHook("afterFind", async result => { + if (result && result.length > 0) { + await Promise.all( + result.map(async ticket => { + ticket.unreadMessages = await Message.count({ + where: { ticketId: ticket.id, read: false } + }); + }) + ); + } + }); - this.addHook("beforeUpdate", async ticket => { - ticket.unreadMessages = await Message.count({ - where: { ticketId: ticket.id, read: false }, - }); - }); + this.addHook("beforeUpdate", async ticket => { + ticket.unreadMessages = await Message.count({ + where: { ticketId: ticket.id, read: false } + }); + }); - return this; - } + return this; + } - static associate(models) { - this.belongsTo(models.Contact, { foreignKey: "contactId", as: "contact" }); - this.belongsTo(models.User, { foreignKey: "userId", as: "user" }); - this.belongsTo(models.Whatsapp, { - foreignKey: "whatsappId", - as: "whatsapp", - }); - this.hasMany(models.Message, { foreignKey: "ticketId", as: "messages" }); - } + static associate(models) { + this.belongsTo(models.Contact, { foreignKey: "contactId", as: "contact" }); + this.belongsTo(models.User, { foreignKey: "userId", as: "user" }); + this.belongsTo(models.Whatsapp, { + foreignKey: "whatsappId", + as: "whatsapp" + }); + this.hasMany(models.Message, { foreignKey: "ticketId", as: "messages" }); + } } module.exports = Ticket; diff --git a/backend/src/routes/authRoutes.ts b/backend/src/routes/authRoutes.ts index 6f3b7da..b9f0f4a 100644 --- a/backend/src/routes/authRoutes.ts +++ b/backend/src/routes/authRoutes.ts @@ -1,5 +1,6 @@ -import { Router } from "express"; +import { Router, Request, Response } from "express"; import SessionController from "../controllers/SessionController"; +import isAuth from "../middleware/isAuth"; import * as UserController from "../controllers/UserController"; const authRoutes = Router(); @@ -8,4 +9,8 @@ authRoutes.post("/signup", UserController.store); authRoutes.post("/login", SessionController); +authRoutes.get("/check", isAuth, (req: Request, res: Response) => { + res.status(200).json({ authenticated: true }); +}); + export default authRoutes; diff --git a/backend/src/routes/contactRoutes.ts b/backend/src/routes/contactRoutes.ts new file mode 100644 index 0000000..b7a0222 --- /dev/null +++ b/backend/src/routes/contactRoutes.ts @@ -0,0 +1,21 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as ContactController from "../controllers/ContactController"; +// import ImportPhoneContactsController from "../controllers/ImportPhoneContactsController"; + +const contactRoutes = express.Router(); + +// contactRoutes.post("/contacts/import", isAuth, ImportPhoneContactsController.store); + +contactRoutes.get("/contacts", isAuth, ContactController.index); + +contactRoutes.get("/contacts/:contactId", isAuth, ContactController.show); + +contactRoutes.post("/contacts", isAuth, ContactController.store); + +contactRoutes.put("/contacts/:contactId", isAuth, ContactController.update); + +contactRoutes.delete("/contacts/:contactId", isAuth, ContactController.remove); + +export default contactRoutes; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 42b4fdc..bbd3d46 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -3,10 +3,10 @@ import { Router } from "express"; import userRoutes from "./userRoutes"; import authRoutes from "./authRoutes"; import settingRoutes from "./settingRoutes"; +import contactRoutes from "./contactRoutes"; // const TicketsRoutes = require("./routes/tickets"); // const MessagesRoutes = require("./routes/messages"); -// const ContactsRoutes = require("./routes/contacts"); // const WhatsRoutes = require("./routes/whatsapp"); // const UsersRoutes = require("./routes/users"); @@ -15,9 +15,9 @@ const routes = Router(); routes.use(userRoutes); routes.use("/auth", authRoutes); routes.use(settingRoutes); +routes.use(contactRoutes); // routes.use(TicketsRoutes); // routes.use(MessagesRoutes); -// routes.use(ContactsRoutes); // routes.use(WhatsRoutes); export default routes; diff --git a/backend/src/routes/routes/auth.js b/backend/src/routes/routes/auth.js deleted file mode 100644 index 930d1e6..0000000 --- a/backend/src/routes/routes/auth.js +++ /dev/null @@ -1,16 +0,0 @@ -const express = require("express"); -const SessionController = require("../../controllers/SessionController"); -const UserController = require("../../controllers/UserController"); -const isAuth = require("../../middleware/is-auth"); - -const routes = express.Router(); - -routes.post("/signup", UserController.store); - -routes.post("/login", SessionController.store); - -routes.get("/check", isAuth, (req, res) => { - res.status(200).json({ authenticated: true }); -}); - -module.exports = routes; diff --git a/backend/src/routes/routes/contacts.js b/backend/src/routes/routes/contacts.js deleted file mode 100644 index 1d9bfa2..0000000 --- a/backend/src/routes/routes/contacts.js +++ /dev/null @@ -1,21 +0,0 @@ -const express = require("express"); -const isAuth = require("../../middleware/is-auth"); - -const ContactController = require("../../controllers/ContactController"); -const ImportPhoneContactsController = require("../../controllers/ImportPhoneContactsController"); - -const routes = express.Router(); - -routes.post("/contacts/import", isAuth, ImportPhoneContactsController.store); - -routes.get("/contacts", isAuth, ContactController.index); - -routes.get("/contacts/:contactId", isAuth, ContactController.show); - -routes.post("/contacts", isAuth, ContactController.store); - -routes.put("/contacts/:contactId", isAuth, ContactController.update); - -routes.delete("/contacts/:contactId", isAuth, ContactController.delete); - -module.exports = routes; diff --git a/backend/src/services/ContactServices/CreateContactService.ts b/backend/src/services/ContactServices/CreateContactService.ts new file mode 100644 index 0000000..1d11ca5 --- /dev/null +++ b/backend/src/services/ContactServices/CreateContactService.ts @@ -0,0 +1,32 @@ +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; + +interface Request { + name: string; + number: string; + email?: string; +} + +const CreateContactService = async ({ + name, + number, + email +}: Request): Promise => { + const numberExists = await Contact.findOne({ + where: { number } + }); + + if (numberExists) { + throw new AppError("A contact with this number already exists."); + } + + const contact = await Contact.create({ + name, + number, + email + }); + + return contact; +}; + +export default CreateContactService; diff --git a/backend/src/services/ContactServices/DeleteContactService.ts b/backend/src/services/ContactServices/DeleteContactService.ts new file mode 100644 index 0000000..478e117 --- /dev/null +++ b/backend/src/services/ContactServices/DeleteContactService.ts @@ -0,0 +1,16 @@ +import Contact from "../../models/Contact"; +import AppError from "../../errors/AppError"; + +const DeleteContactService = async (id: string): Promise => { + const contact = await Contact.findOne({ + where: { id } + }); + + if (!contact) { + throw new AppError("No contact found with this ID.", 404); + } + + await contact.destroy(); +}; + +export default DeleteContactService; diff --git a/backend/src/services/ContactServices/ListContactsService.ts b/backend/src/services/ContactServices/ListContactsService.ts new file mode 100644 index 0000000..52c7f8a --- /dev/null +++ b/backend/src/services/ContactServices/ListContactsService.ts @@ -0,0 +1,50 @@ +import { Sequelize, Op } from "sequelize"; +import Contact from "../../models/Contact"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + contacts: Contact[]; + count: number; + hasMore: boolean; +} + +const ListContactsService = async ({ + searchParam = "", + pageNumber = "1" +}: Request): Promise => { + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + }, + { number: { [Op.like]: `%${searchParam}%` } } + ] + }; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: contacts } = await Contact.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + contacts.length; + + return { + contacts, + count, + hasMore + }; +}; + +export default ListContactsService; diff --git a/backend/src/services/ContactServices/ShowContactService.ts b/backend/src/services/ContactServices/ShowContactService.ts new file mode 100644 index 0000000..6905683 --- /dev/null +++ b/backend/src/services/ContactServices/ShowContactService.ts @@ -0,0 +1,17 @@ +import Contact from "../../models/Contact"; +import AppError from "../../errors/AppError"; + +const ShowContactService = async (id: string): Promise => { + const user = await Contact.findOne({ + where: { id }, + attributes: ["id", "name", "number", "email"] + }); + + if (!user) { + throw new AppError("No contact found with this ID.", 404); + } + + return user; +}; + +export default ShowContactService; diff --git a/backend/src/services/ContactServices/UpdateContactService.ts b/backend/src/services/ContactServices/UpdateContactService.ts new file mode 100644 index 0000000..5a001cd --- /dev/null +++ b/backend/src/services/ContactServices/UpdateContactService.ts @@ -0,0 +1,39 @@ +import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; + +interface ContactData { + email?: string; + number?: string; + name?: string; +} + +interface Request { + contactData: ContactData; + contactId: string; +} + +const UpdateContactService = async ({ + contactData, + contactId +}: Request): Promise => { + const { email, name, number } = contactData; + + const contact = await Contact.findOne({ + where: { id: contactId }, + attributes: ["id", "name", "number", "email"] + }); + + if (!contact) { + throw new AppError("No contact found with this ID.", 404); + } + + await contact.update({ + name, + number, + email + }); + + return contact; +}; + +export default UpdateContactService; diff --git a/backend/src/services/UserServices/ListUsersService.ts b/backend/src/services/UserServices/ListUsersService.ts index 4430724..a603b1f 100644 --- a/backend/src/services/UserServices/ListUsersService.ts +++ b/backend/src/services/UserServices/ListUsersService.ts @@ -3,7 +3,7 @@ import User from "../../models/User"; interface Request { searchParam?: string; - pageNumber?: number; + pageNumber?: string; } interface Response { @@ -14,7 +14,7 @@ interface Response { const ListUsersService = async ({ searchParam = "", - pageNumber = 1 + pageNumber = "1" }: Request): Promise => { const whereCondition = { [Op.or]: [ @@ -28,8 +28,8 @@ const ListUsersService = async ({ { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } } ] }; - const limit = 20; - const offset = limit * (pageNumber - 1); + const limit = 10; + const offset = limit * (+pageNumber - 1); const { count, rows: users } = await User.findAndCountAll({ where: whereCondition, @@ -39,7 +39,7 @@ const ListUsersService = async ({ order: [["createdAt", "DESC"]] }); - const hasMore = count > limit + users.length; + const hasMore = count > offset + users.length; return { users, diff --git a/backend/src/services/UserServices/FindUserService.ts b/backend/src/services/UserServices/ShowUserService.ts similarity index 76% rename from backend/src/services/UserServices/FindUserService.ts rename to backend/src/services/UserServices/ShowUserService.ts index 868bf15..06f65eb 100644 --- a/backend/src/services/UserServices/FindUserService.ts +++ b/backend/src/services/UserServices/ShowUserService.ts @@ -1,7 +1,7 @@ import User from "../../models/User"; import AppError from "../../errors/AppError"; -const FindUserService = async (id: string): Promise => { +const ShowUserService = async (id: string): Promise => { const user = await User.findOne({ where: { id }, attributes: ["name", "id", "email", "profile"] @@ -14,4 +14,4 @@ const FindUserService = async (id: string): Promise => { return user; }; -export default FindUserService; +export default ShowUserService; From 99fa2cea61b140eb127e007664c01d59b8550c0f Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 08:15:47 -0300 Subject: [PATCH 12/54] changed all models to typescript --- backend/.eslintrc.json | 1 + backend/src/controllers/SessionController.ts | 6 +- backend/src/controllers/TicketController.ts | 174 ++++++++++++++++++ .../src/controllersOld/SessionController.js | 64 +++---- backend/src/database/index.ts | 13 +- backend/src/models/Contact.ts | 14 +- backend/src/models/ContactCustomField.ts | 41 +++++ backend/src/models/Message.ts | 60 ++++++ backend/src/models/Setting.ts | 5 +- backend/src/models/Ticket.ts | 43 ++++- backend/src/models/User.ts | 7 +- backend/src/models/Whatsapp.ts | 53 ++++++ backend/src/modelsOld/ContactCustomField.js | 36 ++-- backend/src/modelsOld/Message.js | 70 +++---- backend/src/modelsOld/Whatsapp.js | 64 +++---- backend/src/routes/authRoutes.ts | 4 +- backend/src/routes/index.ts | 4 +- backend/src/routes/routes/tickets.js | 16 -- backend/src/routes/ticketRoutes.ts | 16 ++ .../TicketServices/CreateTicketService.ts | 24 +++ .../TicketServices/ListTicketsService.ts | 74 ++++++++ .../services/UserServices/ListUsersService.ts | 2 +- 22 files changed, 629 insertions(+), 162 deletions(-) create mode 100644 backend/src/controllers/TicketController.ts create mode 100644 backend/src/models/ContactCustomField.ts create mode 100644 backend/src/models/Message.ts create mode 100644 backend/src/models/Whatsapp.ts delete mode 100644 backend/src/routes/routes/tickets.js create mode 100644 backend/src/routes/ticketRoutes.ts create mode 100644 backend/src/services/TicketServices/CreateTicketService.ts create mode 100644 backend/src/services/TicketServices/ListTicketsService.ts diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json index 7aba60d..5f24a5b 100644 --- a/backend/.eslintrc.json +++ b/backend/.eslintrc.json @@ -20,6 +20,7 @@ "error", { "argsIgnorePattern": "_" } ], + "import/prefer-default-export": "off", "no-console": "off", "no-param-reassign": "off", "prettier/prettier": "error", diff --git a/backend/src/controllers/SessionController.ts b/backend/src/controllers/SessionController.ts index 3732b96..2f2e216 100644 --- a/backend/src/controllers/SessionController.ts +++ b/backend/src/controllers/SessionController.ts @@ -2,15 +2,13 @@ import { Request, Response } from "express"; import AuthUserService from "../services/UserServices/AuthUserSerice"; -const store = async (req: Request, res: Response): Promise => { +export const store = async (req: Request, res: Response): Promise => { const { email, password } = req.body; - const { user, token } = await AuthUserService({ email, password }); + const { token, user } = await AuthUserService({ email, password }); return res.status(200).json({ user, token }); }; - -export default store; diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts new file mode 100644 index 0000000..00303c5 --- /dev/null +++ b/backend/src/controllers/TicketController.ts @@ -0,0 +1,174 @@ +import { Request, Response } from "express"; +import ListTicketsService from "../services/TicketServices/ListTicketsService"; +// const Sequelize = require("sequelize"); +// const { startOfDay, endOfDay, parseISO } = require("date-fns"); + +// const Ticket = require("../models/Ticket"); +// const Contact = require("../models/Contact"); +// const Message = require("../models/Message"); +// const Whatsapp = require("../models/Whatsapp"); + +// const { getIO } = require("../libs/socket"); + +import Whatsapp from "../models/Whatsapp"; +import Ticket from "../models/Ticket"; +import Whatsapp from "../models/Whatsapp"; + +type RequestQuery = { + searchParam: string; + pageNumber: string; + status: string; + date: string; + showAll: string; +}; + +export const index = async (req: Request, res: Response): Promise => { + const { + pageNumber, + status, + date, + searchParam, + showAll + } = req.query as RequestQuery; + + const userId = req.user.id; + + // let includeCondition = [ + // { + // model: Contact, + // as: "contact", + // attributes: ["name", "number", "profilePicUrl"] + // } + // ]; + + // if (searchParam) { + // includeCondition = [ + // ...includeCondition, + // { + // model: Message, + // as: "messages", + // attributes: ["id", "body"], + // where: { + // body: Sequelize.where( + // Sequelize.fn("LOWER", Sequelize.col("body")), + // "LIKE", + // "%" + searchParam.toLowerCase() + "%" + // ) + // }, + // required: false, + // duplicating: false + // } + // ]; + + // whereCondition = { + // [Sequelize.Op.or]: [ + // { + // "$contact.name$": Sequelize.where( + // Sequelize.fn("LOWER", Sequelize.col("name")), + // "LIKE", + // "%" + searchParam.toLowerCase() + "%" + // ) + // }, + // { "$contact.number$": { [Sequelize.Op.like]: `%${searchParam}%` } }, + // { + // "$message.body$": Sequelize.where( + // Sequelize.fn("LOWER", Sequelize.col("body")), + // "LIKE", + // "%" + searchParam.toLowerCase() + "%" + // ) + // } + // ] + // }; + // } + + const { tickets, count, hasMore } = await ListTicketsService({ + searchParam, + pageNumber, + status, + date, + showAll, + userId + }); + + return res.status(200).json({ tickets, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + // const io = getIO(); + + const defaultWhatsapp = await Whatsapp.findOne({ + where: { default: true } + }); + + const ticketData = req.body; + + if (!defaultWhatsapp) { + return res + .status(404) + .json({ error: "No default WhatsApp found. Check Connection page." }); + } + + const ticket: Ticket = await defaultWhatsapp.$create("ticket", req.body); + const contact = await ticket.$get("contact"); + + const wapp = await ticket.$get("whatsapp"); + + const tickets = await wapp?.$get("tickets"); + + // const serializaedTicket = { ...ticket.dataValues, contact: contact }; + + // io.to("notification").emit("ticket", { + // action: "create", + // ticket: serializaedTicket + // }); + + return res.status(200).json({ ticket, contact, wapp, tickets }); +}; + +// export const update = (req: Request, res: Response): Promise => { +// const io = getIO(); +// const { ticketId } = req.params; + +// const ticket = await Ticket.findByPk(ticketId, { +// include: [ +// { +// model: Contact, +// as: "contact", +// attributes: ["name", "number", "profilePicUrl"] +// } +// ] +// }); + +// if (!ticket) { +// return res.status(404).json({ error: "No ticket found with this ID" }); +// } + +// await ticket.update(req.body); + +// io.to("notification").emit("ticket", { +// action: "updateStatus", +// ticket: ticket +// }); + +// return res.status(200).json(ticket); +// }; + +// export const remove = (req: Request, res: Response): Promise => { +// const io = getIO(); +// const { ticketId } = req.params; + +// const ticket = await Ticket.findByPk(ticketId); + +// if (!ticket) { +// return res.status(400).json({ error: "No ticket found with this ID" }); +// } + +// await ticket.destroy(); + +// io.to("notification").emit("ticket", { +// action: "delete", +// ticketId: ticket.id +// }); + +// return res.status(200).json({ message: "ticket deleted" }); +// }; diff --git a/backend/src/controllersOld/SessionController.js b/backend/src/controllersOld/SessionController.js index e9afcb0..fdf1977 100644 --- a/backend/src/controllersOld/SessionController.js +++ b/backend/src/controllersOld/SessionController.js @@ -1,32 +1,32 @@ -const jwt = require("jsonwebtoken"); -const authConfig = require("../config/auth"); - -const User = require("../models/User"); - -exports.store = async (req, res, next) => { - const { email, password } = req.body; - - const user = await User.findOne({ where: { email: email } }); - if (!user) { - return res.status(404).json({ error: "No user found with this email" }); - } - - if (!(await user.checkPassword(password))) { - return res.status(401).json({ error: "Password does not match" }); - } - - const token = jwt.sign( - { email: user.email, userId: user.id }, - authConfig.secret, - { - expiresIn: authConfig.expiresIn, - } - ); - - return res.status(200).json({ - token: token, - username: user.name, - profile: user.profile, - userId: user.id, - }); -}; +const jwt = require("jsonwebtoken"); +const authConfig = require("../config/auth"); + +const User = require("../models/User"); + +exports.store = async (req, res, next) => { + const { email, password } = req.body; + + const user = await User.findOne({ where: { email: email } }); + if (!user) { + return res.status(404).json({ error: "No user found with this email" }); + } + + if (!(await user.checkPassword(password))) { + return res.status(401).json({ error: "Password does not match" }); + } + + const token = jwt.sign( + { email: user.email, userId: user.id }, + authConfig.secret, + { + expiresIn: authConfig.expiresIn + } + ); + + return res.status(200).json({ + token: token, + username: user.name, + profile: user.profile, + userId: user.id + }); +}; diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts index da0628b..9fb351d 100644 --- a/backend/src/database/index.ts +++ b/backend/src/database/index.ts @@ -3,24 +3,23 @@ import User from "../models/User"; import Setting from "../models/Setting"; import Contact from "../models/Contact"; import Ticket from "../models/Ticket"; +import Whatsapp from "../models/Whatsapp"; +import ContactCustomField from "../models/ContactCustomField"; +import Message from "../models/Message"; // eslint-disable-next-line const dbConfig = require("../config/database"); // import dbConfig from "../config/database"; -// const Message = require("../models/Message"); -// const Whatsapp = require("../models/Whatsapp"); -// const ContactCustomField = require("../models/ContactCustomField"); - const sequelize = new Sequelize(dbConfig); const models = [ User, Contact, Ticket, - // Message, - // Whatsapp, - // ContactCustomField, + Message, + Whatsapp, + ContactCustomField, Setting ]; diff --git a/backend/src/models/Contact.ts b/backend/src/models/Contact.ts index 99ec142..44302f5 100644 --- a/backend/src/models/Contact.ts +++ b/backend/src/models/Contact.ts @@ -4,13 +4,15 @@ import { CreatedAt, UpdatedAt, Model, - DataType, PrimaryKey, AutoIncrement, AllowNull, Unique, - Default + Default, + HasMany } from "sequelize-typescript"; +import ContactCustomField from "./ContactCustomField"; +import Ticket from "./Ticket"; @Table class Contact extends Model { @@ -19,7 +21,7 @@ class Contact extends Model { @Column id: number; - @Column(DataType.STRING) + @Column name: string; @AllowNull(false) @@ -40,6 +42,12 @@ class Contact extends Model { @UpdatedAt updatedAt: Date; + + @HasMany(() => Ticket) + tickets: Ticket[]; + + @HasMany(() => ContactCustomField) + extraInfo: ContactCustomField[]; } export default Contact; diff --git a/backend/src/models/ContactCustomField.ts b/backend/src/models/ContactCustomField.ts new file mode 100644 index 0000000..f4a9ebe --- /dev/null +++ b/backend/src/models/ContactCustomField.ts @@ -0,0 +1,41 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement, + ForeignKey, + BelongsTo +} from "sequelize-typescript"; +import Contact from "./Contact"; + +@Table +class ContactCustomField extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column + name: string; + + @Column + value: string; + + @ForeignKey(() => Contact) + @Column + contactId: number; + + @BelongsTo(() => Contact) + contact: Contact; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default ContactCustomField; diff --git a/backend/src/models/Message.ts b/backend/src/models/Message.ts new file mode 100644 index 0000000..83c8cfe --- /dev/null +++ b/backend/src/models/Message.ts @@ -0,0 +1,60 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + PrimaryKey, + AutoIncrement, + Default, + BelongsTo, + ForeignKey +} from "sequelize-typescript"; +import Ticket from "./Ticket"; + +@Table +class Message extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Default(0) + @Column + ack: number; + + @Default(false) + @Column + read: boolean; + + @Default(false) + @Column + fromMe: boolean; + + @Column(DataType.TEXT) + body: string; + + @Column + mediaUrl: string; + + @Column + mediaType: string; + + @CreatedAt + @Column(DataType.DATE(6)) + createdAt: Date; + + @UpdatedAt + @Column(DataType.DATE(6)) + updatedAt: Date; + + @ForeignKey(() => Ticket) + @Column + ticketId: number; + + @BelongsTo(() => Ticket) + ticket: Ticket; +} + +export default Message; diff --git a/backend/src/models/Setting.ts b/backend/src/models/Setting.ts index 13ba0e1..b58e57a 100644 --- a/backend/src/models/Setting.ts +++ b/backend/src/models/Setting.ts @@ -4,17 +4,16 @@ import { CreatedAt, UpdatedAt, Model, - DataType, PrimaryKey } from "sequelize-typescript"; @Table class Setting extends Model { @PrimaryKey - @Column(DataType.STRING) + @Column key: string; - @Column(DataType.STRING) + @Column value: string; @CreatedAt diff --git a/backend/src/models/Ticket.ts b/backend/src/models/Ticket.ts index 1e8ce3e..5b77299 100644 --- a/backend/src/models/Ticket.ts +++ b/backend/src/models/Ticket.ts @@ -5,25 +5,32 @@ import { UpdatedAt, Model, DataType, - PrimaryKey + PrimaryKey, + ForeignKey, + BelongsTo, + HasMany, + AutoIncrement } from "sequelize-typescript"; +import Contact from "./Contact"; +import Message from "./Message"; +import User from "./User"; +import Whatsapp from "./Whatsapp"; + @Table class Ticket extends Model { @PrimaryKey - @Column(DataType.NUMBER) + @AutoIncrement + @Column id: number; @Column({ defaultValue: "pending" }) status: string; - // @Column({ allowNull: false, unique: true }) - // userId: string; - @Column(DataType.VIRTUAL) unreadMessages: string; - @Column(DataType.STRING) + @Column lastMessage: string; @CreatedAt @@ -31,6 +38,30 @@ class Ticket extends Model { @UpdatedAt updatedAt: Date; + + @ForeignKey(() => User) + @Column + userId: number; + + @BelongsTo(() => User) + user: User; + + @ForeignKey(() => Contact) + @Column + contactId: number; + + @BelongsTo(() => Contact) + contact: Contact; + + @ForeignKey(() => Whatsapp) + @Column + whatsappId: number; + + @BelongsTo(() => Whatsapp) + whatsapp: Whatsapp; + + @HasMany(() => Message) + messages: Message[]; } export default Ticket; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 73981b6..e01407b 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -9,9 +9,11 @@ import { BeforeUpdate, PrimaryKey, AutoIncrement, - Default + Default, + HasMany } from "sequelize-typescript"; import { hash, compare } from "bcryptjs"; +import Ticket from "./Ticket"; @Table class User extends Model { @@ -42,6 +44,9 @@ class User extends Model { @UpdatedAt updatedAt: Date; + @HasMany(() => Ticket) + tickets: Ticket[]; + @BeforeUpdate @BeforeCreate static hashPassword = async (instance: User): Promise => { diff --git a/backend/src/models/Whatsapp.ts b/backend/src/models/Whatsapp.ts new file mode 100644 index 0000000..7fe0402 --- /dev/null +++ b/backend/src/models/Whatsapp.ts @@ -0,0 +1,53 @@ +import { + Table, + Column, + CreatedAt, + UpdatedAt, + Model, + DataType, + PrimaryKey, + AutoIncrement, + Default, + AllowNull, + HasMany +} from "sequelize-typescript"; +import Ticket from "./Ticket"; + +@Table +class Whatsapp extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column(DataType.TEXT) + session: string; + + @Column(DataType.TEXT) + qrcode: string; + + @Column + status: string; + + @Column + battery: string; + + @Column + plugged: boolean; + + @Default(false) + @AllowNull + @Column + default: boolean; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; + + @HasMany(() => Ticket) + tickets: Ticket[]; +} + +export default Whatsapp; diff --git a/backend/src/modelsOld/ContactCustomField.js b/backend/src/modelsOld/ContactCustomField.js index 24c6a74..3819cbb 100644 --- a/backend/src/modelsOld/ContactCustomField.js +++ b/backend/src/modelsOld/ContactCustomField.js @@ -1,26 +1,26 @@ const Sequelize = require("sequelize"); class ContactCustomField extends Sequelize.Model { - static init(sequelize) { - super.init( - { - name: { type: Sequelize.STRING }, - value: { type: Sequelize.STRING }, - }, - { - sequelize, - } - ); + static init(sequelize) { + super.init( + { + name: { type: Sequelize.STRING }, + value: { type: Sequelize.STRING } + }, + { + sequelize + } + ); - return this; - } + return this; + } - static associate(models) { - this.belongsTo(models.Contact, { - foreignKey: "contactId", - as: "extraInfo", - }); - } + static associate(models) { + this.belongsTo(models.Contact, { + foreignKey: "contactId", + as: "extraInfo" + }); + } } module.exports = ContactCustomField; diff --git a/backend/src/modelsOld/Message.js b/backend/src/modelsOld/Message.js index 6192485..bf4fbb5 100644 --- a/backend/src/modelsOld/Message.js +++ b/backend/src/modelsOld/Message.js @@ -1,35 +1,35 @@ -const Sequelize = require("sequelize"); - -class Message extends Sequelize.Model { - static init(sequelize) { - super.init( - { - ack: { type: Sequelize.INTEGER, defaultValue: 0 }, - read: { type: Sequelize.BOOLEAN, defaultValue: false }, - fromMe: { type: Sequelize.BOOLEAN, defaultValue: false }, - body: { type: Sequelize.TEXT }, - mediaUrl: { type: Sequelize.STRING }, - mediaType: { type: Sequelize.STRING }, - createdAt: { - type: Sequelize.DATE(6), - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE(6), - allowNull: false, - }, - }, - { - sequelize, - } - ); - - return this; - } - - static associate(models) { - this.belongsTo(models.Ticket, { foreignKey: "ticketId", as: "messages" }); - } -} - -module.exports = Message; +const Sequelize = require("sequelize"); + +class Message extends Sequelize.Model { + static init(sequelize) { + super.init( + { + ack: { type: Sequelize.INTEGER, defaultValue: 0 }, + read: { type: Sequelize.BOOLEAN, defaultValue: false }, + fromMe: { type: Sequelize.BOOLEAN, defaultValue: false }, + body: { type: Sequelize.TEXT }, + mediaUrl: { type: Sequelize.STRING }, + mediaType: { type: Sequelize.STRING }, + createdAt: { + type: Sequelize.DATE(6), + allowNull: false + }, + updatedAt: { + type: Sequelize.DATE(6), + allowNull: false + } + }, + { + sequelize + } + ); + + return this; + } + + static associate(models) { + this.belongsTo(models.Ticket, { foreignKey: "ticketId", as: "messages" }); + } +} + +module.exports = Message; diff --git a/backend/src/modelsOld/Whatsapp.js b/backend/src/modelsOld/Whatsapp.js index 7d77f17..bf63e0c 100644 --- a/backend/src/modelsOld/Whatsapp.js +++ b/backend/src/modelsOld/Whatsapp.js @@ -1,32 +1,32 @@ -const Sequelize = require("sequelize"); - -class Whatsapp extends Sequelize.Model { - static init(sequelize) { - super.init( - { - session: { type: Sequelize.TEXT }, - qrcode: { type: Sequelize.TEXT }, - name: { type: Sequelize.STRING, unique: true, allowNull: false }, - status: { type: Sequelize.STRING }, - battery: { type: Sequelize.STRING }, - plugged: { type: Sequelize.BOOLEAN }, - default: { - type: Sequelize.BOOLEAN, - defaultValue: false, - allowNull: false, - }, - }, - { - sequelize, - } - ); - - return this; - } - - static associate(models) { - this.hasMany(models.Ticket, { foreignKey: "whatsappId", as: "tickets" }); - } -} - -module.exports = Whatsapp; +const Sequelize = require("sequelize"); + +class Whatsapp extends Sequelize.Model { + static init(sequelize) { + super.init( + { + session: { type: Sequelize.TEXT }, + qrcode: { type: Sequelize.TEXT }, + name: { type: Sequelize.STRING, unique: true, allowNull: false }, + status: { type: Sequelize.STRING }, + battery: { type: Sequelize.STRING }, + plugged: { type: Sequelize.BOOLEAN }, + default: { + type: Sequelize.BOOLEAN, + defaultValue: false, + allowNull: false + } + }, + { + sequelize + } + ); + + return this; + } + + static associate(models) { + this.hasMany(models.Ticket, { foreignKey: "whatsappId", as: "tickets" }); + } +} + +module.exports = Whatsapp; diff --git a/backend/src/routes/authRoutes.ts b/backend/src/routes/authRoutes.ts index b9f0f4a..b97ab50 100644 --- a/backend/src/routes/authRoutes.ts +++ b/backend/src/routes/authRoutes.ts @@ -1,5 +1,5 @@ import { Router, Request, Response } from "express"; -import SessionController from "../controllers/SessionController"; +import * as SessionController from "../controllers/SessionController"; import isAuth from "../middleware/isAuth"; import * as UserController from "../controllers/UserController"; @@ -7,7 +7,7 @@ const authRoutes = Router(); authRoutes.post("/signup", UserController.store); -authRoutes.post("/login", SessionController); +authRoutes.post("/login", SessionController.store); authRoutes.get("/check", isAuth, (req: Request, res: Response) => { res.status(200).json({ authenticated: true }); diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index bbd3d46..5fe928a 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -4,8 +4,8 @@ import userRoutes from "./userRoutes"; import authRoutes from "./authRoutes"; import settingRoutes from "./settingRoutes"; import contactRoutes from "./contactRoutes"; +import ticketRoutes from "./ticketRoutes"; -// const TicketsRoutes = require("./routes/tickets"); // const MessagesRoutes = require("./routes/messages"); // const WhatsRoutes = require("./routes/whatsapp"); // const UsersRoutes = require("./routes/users"); @@ -16,7 +16,7 @@ routes.use(userRoutes); routes.use("/auth", authRoutes); routes.use(settingRoutes); routes.use(contactRoutes); -// routes.use(TicketsRoutes); +routes.use(ticketRoutes); // routes.use(MessagesRoutes); // routes.use(WhatsRoutes); diff --git a/backend/src/routes/routes/tickets.js b/backend/src/routes/routes/tickets.js deleted file mode 100644 index 201e3e8..0000000 --- a/backend/src/routes/routes/tickets.js +++ /dev/null @@ -1,16 +0,0 @@ -const express = require("express"); -const isAuth = require("../../middleware/is-auth"); - -const TicketController = require("../../controllers/TicketController"); - -const routes = express.Router(); - -routes.get("/tickets", isAuth, TicketController.index); - -routes.post("/tickets", isAuth, TicketController.store); - -routes.put("/tickets/:ticketId", isAuth, TicketController.update); - -routes.delete("/tickets/:ticketId", isAuth, TicketController.delete); - -module.exports = routes; diff --git a/backend/src/routes/ticketRoutes.ts b/backend/src/routes/ticketRoutes.ts new file mode 100644 index 0000000..4cb6897 --- /dev/null +++ b/backend/src/routes/ticketRoutes.ts @@ -0,0 +1,16 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as TicketController from "../controllers/TicketController"; + +const ticketRoutes = express.Router(); + +ticketRoutes.get("/tickets", isAuth, TicketController.index); + +ticketRoutes.post("/tickets", isAuth, TicketController.store); + +// ticketRoutes.put("/tickets/:ticketId", isAuth, TicketController.update); + +// ticketRoutes.delete("/tickets/:ticketId", isAuth, TicketController.remove); + +export default ticketRoutes; diff --git a/backend/src/services/TicketServices/CreateTicketService.ts b/backend/src/services/TicketServices/CreateTicketService.ts new file mode 100644 index 0000000..d334989 --- /dev/null +++ b/backend/src/services/TicketServices/CreateTicketService.ts @@ -0,0 +1,24 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import User from "../../models/User"; + +interface Request { + email: string; + password: string; + name: string; + profile?: string; +} + +interface Response { + email: string; + name: string; + id: number; + profile: string; +} + +const CreateTicketService = async (): Promise => { + return true; +}; + +export default CreateTicketService; diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts new file mode 100644 index 0000000..5fecc22 --- /dev/null +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -0,0 +1,74 @@ +import { Op } from "sequelize"; +import { startOfDay, endOfDay, parseISO } from "date-fns"; + +import Ticket from "../../models/Ticket"; + +interface Request { + searchParam?: string; + pageNumber?: string; + status?: string; + date?: string; + showAll?: string; + userId: string; +} + +interface Response { + tickets: Ticket[]; + count: number; + hasMore: boolean; +} + +const ListTicketsService = async ({ + searchParam = "", + pageNumber = "1", + status, + date, + showAll, + userId +}: Request): Promise => { + let whereCondition = {}; + + if (showAll === "true") { + whereCondition = {}; + } else { + whereCondition = { userId }; + } + + if (status) { + whereCondition = { + ...whereCondition, + status + }; + } + + if (date) { + whereCondition = { + ...whereCondition, + createdAt: { + [Op.between]: [startOfDay(parseISO(date)), endOfDay(parseISO(date))] + } + }; + } + + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: tickets } = await Ticket.findAndCountAll({ + where: whereCondition, + // distinct: true, + // include: includeCondition, + limit, + offset, + order: [["updatedAt", "DESC"]] + }); + + const hasMore = count > offset + tickets.length; + + return { + tickets, + count, + hasMore + }; +}; + +export default ListTicketsService; diff --git a/backend/src/services/UserServices/ListUsersService.ts b/backend/src/services/UserServices/ListUsersService.ts index a603b1f..233b5c3 100644 --- a/backend/src/services/UserServices/ListUsersService.ts +++ b/backend/src/services/UserServices/ListUsersService.ts @@ -28,7 +28,7 @@ const ListUsersService = async ({ { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } } ] }; - const limit = 10; + const limit = 20; const offset = limit * (+pageNumber - 1); const { count, rows: users } = await User.findAndCountAll({ From 0ea4dbea51e06338d3a3867948eac4bf13c1a988 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 09:40:24 -0300 Subject: [PATCH 13/54] all routes changed to typescript --- backend/src/controllers/TicketController.ts | 5 +- backend/src/controllers/WhatsAppController.ts | 136 +++++++++ .../src/controllersOld/TicketController.js | 270 +++++++++--------- .../src/controllersOld/WhatsAppController.js | 194 ++++++------- backend/src/routes/index.ts | 10 +- backend/src/routes/messageRoutes.ts | 12 + backend/src/routes/routes/messages.js | 12 - backend/src/routes/routes/whatsapp.js | 18 -- backend/src/routes/whatsappRoutes.ts | 22 ++ .../WhatsappService/CreateWhatsAppService.ts | 47 +++ .../WhatsappService/ListWhatsAppsService.ts | 13 + 11 files changed, 469 insertions(+), 270 deletions(-) create mode 100644 backend/src/controllers/WhatsAppController.ts create mode 100644 backend/src/routes/messageRoutes.ts delete mode 100644 backend/src/routes/routes/messages.js delete mode 100644 backend/src/routes/routes/whatsapp.js create mode 100644 backend/src/routes/whatsappRoutes.ts create mode 100644 backend/src/services/WhatsappService/CreateWhatsAppService.ts create mode 100644 backend/src/services/WhatsappService/ListWhatsAppsService.ts diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 00303c5..6b43b3b 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -109,7 +109,8 @@ export const store = async (req: Request, res: Response): Promise => { } const ticket: Ticket = await defaultWhatsapp.$create("ticket", req.body); - const contact = await ticket.$get("contact"); + + await ticket.$get("contact"); const wapp = await ticket.$get("whatsapp"); @@ -122,7 +123,7 @@ export const store = async (req: Request, res: Response): Promise => { // ticket: serializaedTicket // }); - return res.status(200).json({ ticket, contact, wapp, tickets }); + return res.status(200).json({ ticket }); }; // export const update = (req: Request, res: Response): Promise => { diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts new file mode 100644 index 0000000..00c6137 --- /dev/null +++ b/backend/src/controllers/WhatsAppController.ts @@ -0,0 +1,136 @@ +import { Request, Response } from "express"; +import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; +// import Yup from "yup"; +// import Whatsapp from "../models/Whatsapp"; +// import { getIO } from "../libs/socket"; +// import { getWbot, initWbot, removeWbot } from "../libs/wbot"; +// import wbotMessageListener from "../services/wbotMessageListener"; +// import wbotMonitor from "../services/wbotMonitor"; + +import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService"; + +export const index = async (req: Request, res: Response): Promise => { + const whatsapps = await ListWhatsAppsService(); + + return res.status(200).json(whatsapps); +}; + +interface WhatsappData { + name: string; + status: string; +} + +export const store = async (req: Request, res: Response): Promise => { + // const io = getIO(); + + console.log("aqui"); + + const { name, status }: WhatsappData = req.body; + + const whatsapp = await CreateWhatsAppService({ name, status }); + + // if (!whatsapp) { + // return res.status(400).json({ error: "Cannot create whatsapp session." }); + // } + + // initWbot(whatsapp) + // .then(() => { + // wbotMessageListener(whatsapp); + // wbotMonitor(whatsapp); + // }) + // .catch(err => console.log(err)); + + // io.emit("whatsapp", { + // action: "update", + // whatsapp: whatsapp + // }); + + return res.status(200).json(whatsapp); +}; + +// export const show = async (req: Request, res: Response): Promise => { +// const { whatsappId } = req.params; +// const whatsapp = await Whatsapp.findByPk(whatsappId); + +// if (!whatsapp) { +// return res.status(200).json({ message: "Session not found" }); +// } + +// return res.status(200).json(whatsapp); +// }; + +// export const update = async ( +// req: Request, +// res: Response +// ): Promise => { +// const { whatsappId } = req.params; + +// const schema = Yup.object().shape({ +// name: Yup.string().required().min(2), +// default: Yup.boolean() +// .required() +// .test( +// "Check-default", +// "Only one default whatsapp is permited", +// async value => { +// if (value === true) { +// const whatsappFound = await Whatsapp.findOne({ +// where: { default: true } +// }); +// if (whatsappFound) { +// return !(whatsappFound.id !== +whatsappId); +// } else { +// return true; +// } +// } else return true; +// } +// ) +// }); + +// try { +// await schema.validate(req.body); +// } catch (err) { +// return res.status(400).json({ error: err.message }); +// } + +// const io = getIO(); + +// const whatsapp = await Whatsapp.findByPk(whatsappId); + +// if (!whatsapp) { +// return res.status(404).json({ message: "Whatsapp not found" }); +// } + +// await whatsapp.update(req.body); + +// io.emit("whatsapp", { +// action: "update", +// whatsapp: whatsapp +// }); + +// return res.status(200).json({ message: "Whatsapp updated" }); +// }; + +// export const remove = async ( +// req: Request, +// res: Response +// ): Promise => { +// const io = getIO(); +// const { whatsappId } = req.params; + +// const whatsapp = await Whatsapp.findByPk(whatsappId); + +// if (!whatsapp) { +// return res.status(404).json({ message: "Whatsapp not found" }); +// } + +// await whatsapp.destroy(); +// removeWbot(whatsapp.id); + +// io.emit("whatsapp", { +// action: "delete", +// whatsappId: whatsapp.id +// }); + +// return res.status(200).json({ message: "Whatsapp deleted." }); +// }; diff --git a/backend/src/controllersOld/TicketController.js b/backend/src/controllersOld/TicketController.js index 7fe06ae..fcd379c 100644 --- a/backend/src/controllersOld/TicketController.js +++ b/backend/src/controllersOld/TicketController.js @@ -9,177 +9,177 @@ const Whatsapp = require("../models/Whatsapp"); const { getIO } = require("../libs/socket"); exports.index = async (req, res) => { - const { - pageNumber = 1, - status = "", - date = "", - searchParam = "", - showAll, - } = req.query; + const { + pageNumber = 1, + status = "", + date = "", + searchParam = "", + showAll + } = req.query; - const userId = req.user.id; + const userId = req.user.id; - const limit = 20; - const offset = limit * (pageNumber - 1); + const limit = 20; + const offset = limit * (pageNumber - 1); - let includeCondition = [ - { - model: Contact, - as: "contact", - attributes: ["name", "number", "profilePicUrl"], - }, - ]; + let includeCondition = [ + { + model: Contact, + as: "contact", + attributes: ["name", "number", "profilePicUrl"] + } + ]; - let whereCondition = { userId: userId }; + let whereCondition = { userId: userId }; - if (showAll === "true") { - whereCondition = {}; - } + if (showAll === "true") { + whereCondition = {}; + } - if (status) { - whereCondition = { - ...whereCondition, - status: status, - }; - } + if (status) { + whereCondition = { + ...whereCondition, + status: status + }; + } - if (searchParam) { - includeCondition = [ - ...includeCondition, - { - model: Message, - as: "messages", - attributes: ["id", "body"], - where: { - body: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("body")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ), - }, - required: false, - duplicating: false, - }, - ]; + if (searchParam) { + includeCondition = [ + ...includeCondition, + { + model: Message, + as: "messages", + attributes: ["id", "body"], + where: { + body: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("body")), + "LIKE", + "%" + searchParam.toLowerCase() + "%" + ) + }, + required: false, + duplicating: false + } + ]; - whereCondition = { - [Sequelize.Op.or]: [ - { - "$contact.name$": Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ), - }, - { "$contact.number$": { [Sequelize.Op.like]: `%${searchParam}%` } }, - { - "$message.body$": Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("body")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ), - }, - ], - }; - } + whereCondition = { + [Sequelize.Op.or]: [ + { + "$contact.name$": Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + "%" + searchParam.toLowerCase() + "%" + ) + }, + { "$contact.number$": { [Sequelize.Op.like]: `%${searchParam}%` } }, + { + "$message.body$": Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("body")), + "LIKE", + "%" + searchParam.toLowerCase() + "%" + ) + } + ] + }; + } - if (date) { - whereCondition = { - ...whereCondition, - createdAt: { - [Sequelize.Op.between]: [ - startOfDay(parseISO(date)), - endOfDay(parseISO(date)), - ], - }, - }; - } + if (date) { + whereCondition = { + ...whereCondition, + createdAt: { + [Sequelize.Op.between]: [ + startOfDay(parseISO(date)), + endOfDay(parseISO(date)) + ] + } + }; + } - const { count, rows: tickets } = await Ticket.findAndCountAll({ - where: whereCondition, - distinct: true, - include: includeCondition, - limit, - offset, - order: [["updatedAt", "DESC"]], - }); + const { count, rows: tickets } = await Ticket.findAndCountAll({ + where: whereCondition, + distinct: true, + include: includeCondition, + limit, + offset, + order: [["updatedAt", "DESC"]] + }); - const hasMore = count > offset + tickets.length; + const hasMore = count > offset + tickets.length; - return res.status(200).json({ count, tickets, hasMore }); + return res.status(200).json({ count, tickets, hasMore }); }; exports.store = async (req, res) => { - const io = getIO(); + const io = getIO(); - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true }, - }); + const defaultWhatsapp = await Whatsapp.findOne({ + where: { default: true } + }); - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } + if (!defaultWhatsapp) { + return res + .status(404) + .json({ error: "No default WhatsApp found. Check Connection page." }); + } - const ticket = await defaultWhatsapp.createTicket(req.body); + const ticket = await defaultWhatsapp.createTicket(req.body); - const contact = await ticket.getContact(); + const contact = await ticket.getContact(); - const serializaedTicket = { ...ticket.dataValues, contact: contact }; + const serializaedTicket = { ...ticket.dataValues, contact: contact }; - io.to("notification").emit("ticket", { - action: "create", - ticket: serializaedTicket, - }); + io.to("notification").emit("ticket", { + action: "create", + ticket: serializaedTicket + }); - return res.status(200).json(ticket); + return res.status(200).json(ticket); }; exports.update = async (req, res) => { - const io = getIO(); - const { ticketId } = req.params; + const io = getIO(); + const { ticketId } = req.params; - const ticket = await Ticket.findByPk(ticketId, { - include: [ - { - model: Contact, - as: "contact", - attributes: ["name", "number", "profilePicUrl"], - }, - ], - }); + const ticket = await Ticket.findByPk(ticketId, { + include: [ + { + model: Contact, + as: "contact", + attributes: ["name", "number", "profilePicUrl"] + } + ] + }); - if (!ticket) { - return res.status(404).json({ error: "No ticket found with this ID" }); - } + if (!ticket) { + return res.status(404).json({ error: "No ticket found with this ID" }); + } - await ticket.update(req.body); + await ticket.update(req.body); - io.to("notification").emit("ticket", { - action: "updateStatus", - ticket: ticket, - }); + io.to("notification").emit("ticket", { + action: "updateStatus", + ticket: ticket + }); - return res.status(200).json(ticket); + return res.status(200).json(ticket); }; exports.delete = async (req, res) => { - const io = getIO(); - const { ticketId } = req.params; + const io = getIO(); + const { ticketId } = req.params; - const ticket = await Ticket.findByPk(ticketId); + const ticket = await Ticket.findByPk(ticketId); - if (!ticket) { - return res.status(400).json({ error: "No ticket found with this ID" }); - } + if (!ticket) { + return res.status(400).json({ error: "No ticket found with this ID" }); + } - await ticket.destroy(); + await ticket.destroy(); - io.to("notification").emit("ticket", { - action: "delete", - ticketId: ticket.id, - }); + io.to("notification").emit("ticket", { + action: "delete", + ticketId: ticket.id + }); - return res.status(200).json({ message: "ticket deleted" }); + return res.status(200).json({ message: "ticket deleted" }); }; diff --git a/backend/src/controllersOld/WhatsAppController.js b/backend/src/controllersOld/WhatsAppController.js index 30fc5b3..6abf373 100644 --- a/backend/src/controllersOld/WhatsAppController.js +++ b/backend/src/controllersOld/WhatsAppController.js @@ -6,136 +6,136 @@ const wbotMessageListener = require("../services/wbotMessageListener"); const wbotMonitor = require("../services/wbotMonitor"); exports.index = async (req, res) => { - const whatsapps = await Whatsapp.findAll(); + const whatsapps = await Whatsapp.findAll(); - return res.status(200).json(whatsapps); + return res.status(200).json(whatsapps); }; exports.store = async (req, res) => { - const schema = Yup.object().shape({ - name: Yup.string().required().min(2), - default: Yup.boolean() - .required() - .test( - "Check-default", - "Only one default whatsapp is permited", - async value => { - if (value === true) { - const whatsappFound = await Whatsapp.findOne({ - where: { default: true }, - }); - return !Boolean(whatsappFound); - } else return true; - } - ), - }); + const schema = Yup.object().shape({ + name: Yup.string().required().min(2), + default: Yup.boolean() + .required() + .test( + "Check-default", + "Only one default whatsapp is permited", + async value => { + if (value === true) { + const whatsappFound = await Whatsapp.findOne({ + where: { default: true } + }); + return !Boolean(whatsappFound); + } else return true; + } + ) + }); - try { - await schema.validate(req.body); - } catch (err) { - return res.status(400).json({ error: err.message }); - } + try { + await schema.validate(req.body); + } catch (err) { + return res.status(400).json({ error: err.message }); + } - const io = getIO(); + const io = getIO(); - const whatsapp = await Whatsapp.create(req.body); + const whatsapp = await Whatsapp.create(req.body); - if (!whatsapp) { - return res.status(400).json({ error: "Cannot create whatsapp session." }); - } + if (!whatsapp) { + return res.status(400).json({ error: "Cannot create whatsapp session." }); + } - initWbot(whatsapp) - .then(() => { - wbotMessageListener(whatsapp); - wbotMonitor(whatsapp); - }) - .catch(err => console.log(err)); + initWbot(whatsapp) + .then(() => { + wbotMessageListener(whatsapp); + wbotMonitor(whatsapp); + }) + .catch(err => console.log(err)); - io.emit("whatsapp", { - action: "update", - whatsapp: whatsapp, - }); + io.emit("whatsapp", { + action: "update", + whatsapp: whatsapp + }); - return res.status(200).json(whatsapp); + return res.status(200).json(whatsapp); }; exports.show = async (req, res) => { - const { whatsappId } = req.params; - const whatsapp = await Whatsapp.findByPk(whatsappId); + const { whatsappId } = req.params; + const whatsapp = await Whatsapp.findByPk(whatsappId); - if (!whatsapp) { - return res.status(200).json({ message: "Session not found" }); - } + if (!whatsapp) { + return res.status(200).json({ message: "Session not found" }); + } - return res.status(200).json(whatsapp); + return res.status(200).json(whatsapp); }; exports.update = async (req, res) => { - const { whatsappId } = req.params; + const { whatsappId } = req.params; - const schema = Yup.object().shape({ - name: Yup.string().required().min(2), - default: Yup.boolean() - .required() - .test( - "Check-default", - "Only one default whatsapp is permited", - async value => { - if (value === true) { - const whatsappFound = await Whatsapp.findOne({ - where: { default: true }, - }); - if (whatsappFound) { - return !(whatsappFound.id !== +whatsappId); - } else { - return true; - } - } else return true; - } - ), - }); + const schema = Yup.object().shape({ + name: Yup.string().required().min(2), + default: Yup.boolean() + .required() + .test( + "Check-default", + "Only one default whatsapp is permited", + async value => { + if (value === true) { + const whatsappFound = await Whatsapp.findOne({ + where: { default: true } + }); + if (whatsappFound) { + return !(whatsappFound.id !== +whatsappId); + } else { + return true; + } + } else return true; + } + ) + }); - try { - await schema.validate(req.body); - } catch (err) { - return res.status(400).json({ error: err.message }); - } + try { + await schema.validate(req.body); + } catch (err) { + return res.status(400).json({ error: err.message }); + } - const io = getIO(); + const io = getIO(); - const whatsapp = await Whatsapp.findByPk(whatsappId); + const whatsapp = await Whatsapp.findByPk(whatsappId); - if (!whatsapp) { - return res.status(404).json({ message: "Whatsapp not found" }); - } + if (!whatsapp) { + return res.status(404).json({ message: "Whatsapp not found" }); + } - await whatsapp.update(req.body); + await whatsapp.update(req.body); - io.emit("whatsapp", { - action: "update", - whatsapp: whatsapp, - }); + io.emit("whatsapp", { + action: "update", + whatsapp: whatsapp + }); - return res.status(200).json({ message: "Whatsapp updated" }); + return res.status(200).json({ message: "Whatsapp updated" }); }; exports.delete = async (req, res) => { - const io = getIO(); - const { whatsappId } = req.params; + const io = getIO(); + const { whatsappId } = req.params; - const whatsapp = await Whatsapp.findByPk(whatsappId); + const whatsapp = await Whatsapp.findByPk(whatsappId); - if (!whatsapp) { - return res.status(404).json({ message: "Whatsapp not found" }); - } + if (!whatsapp) { + return res.status(404).json({ message: "Whatsapp not found" }); + } - await whatsapp.destroy(); - removeWbot(whatsapp.id); + await whatsapp.destroy(); + removeWbot(whatsapp.id); - io.emit("whatsapp", { - action: "delete", - whatsappId: whatsapp.id, - }); + io.emit("whatsapp", { + action: "delete", + whatsappId: whatsapp.id + }); - return res.status(200).json({ message: "Whatsapp deleted." }); + return res.status(200).json({ message: "Whatsapp deleted." }); }; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 5fe928a..744300e 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -5,10 +5,8 @@ import authRoutes from "./authRoutes"; import settingRoutes from "./settingRoutes"; import contactRoutes from "./contactRoutes"; import ticketRoutes from "./ticketRoutes"; - -// const MessagesRoutes = require("./routes/messages"); -// const WhatsRoutes = require("./routes/whatsapp"); -// const UsersRoutes = require("./routes/users"); +import whatsappRoutes from "./whatsappRoutes"; +import messageRoutes from "./messageRoutes"; const routes = Router(); @@ -17,7 +15,7 @@ routes.use("/auth", authRoutes); routes.use(settingRoutes); routes.use(contactRoutes); routes.use(ticketRoutes); -// routes.use(MessagesRoutes); -// routes.use(WhatsRoutes); +routes.use(whatsappRoutes); +routes.use(messageRoutes); export default routes; diff --git a/backend/src/routes/messageRoutes.ts b/backend/src/routes/messageRoutes.ts new file mode 100644 index 0000000..59d81db --- /dev/null +++ b/backend/src/routes/messageRoutes.ts @@ -0,0 +1,12 @@ +import { Router } from "express"; +// import isAuth from "../middleware/isAuth"; + +// import { index, store } from "../controllers/MessageController"; + +const messageRoutes = Router(); + +// messageRoutes.get("/messages/:ticketId", isAuth, index); + +// messageRoutes.post("/messages/:ticketId", isAuth, store); + +export default messageRoutes; diff --git a/backend/src/routes/routes/messages.js b/backend/src/routes/routes/messages.js deleted file mode 100644 index e48298b..0000000 --- a/backend/src/routes/routes/messages.js +++ /dev/null @@ -1,12 +0,0 @@ -const express = require("express"); -const isAuth = require("../../middleware/is-auth"); - -const MessageController = require("../../controllers/MessageController"); - -const routes = express.Router(); - -routes.get("/messages/:ticketId", isAuth, MessageController.index); - -routes.post("/messages/:ticketId", isAuth, MessageController.store); - -module.exports = routes; diff --git a/backend/src/routes/routes/whatsapp.js b/backend/src/routes/routes/whatsapp.js deleted file mode 100644 index 66d0318..0000000 --- a/backend/src/routes/routes/whatsapp.js +++ /dev/null @@ -1,18 +0,0 @@ -const express = require("express"); -const isAuth = require("../../middleware/is-auth"); - -const WhatsAppController = require("../../controllers/WhatsAppController"); - -const routes = express.Router(); - -routes.get("/whatsapp/", isAuth, WhatsAppController.index); - -routes.post("/whatsapp/", isAuth, WhatsAppController.store); - -routes.get("/whatsapp/:whatsappId", isAuth, WhatsAppController.show); - -routes.put("/whatsapp/:whatsappId", isAuth, WhatsAppController.update); - -routes.delete("/whatsapp/:whatsappId", isAuth, WhatsAppController.delete); - -module.exports = routes; diff --git a/backend/src/routes/whatsappRoutes.ts b/backend/src/routes/whatsappRoutes.ts new file mode 100644 index 0000000..de7fa28 --- /dev/null +++ b/backend/src/routes/whatsappRoutes.ts @@ -0,0 +1,22 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as WhatsAppController from "../controllers/WhatsAppController"; + +const whatsappRoutes = express.Router(); + +whatsappRoutes.get("/whatsapp/", isAuth, WhatsAppController.index); + +whatsappRoutes.post("/whatsapp/", isAuth, WhatsAppController.store); + +// whatsappRoutes.get("/whatsapp/:whatsappId", isAuth, WhatsAppController.show); + +// whatsappRoutes.put("/whatsapp/:whatsappId", isAuth, WhatsAppController.update); + +// whatsappRoutes.remove( +// "/whatsapp/:whatsappId", +// isAuth, +// WhatsAppController.delete +// ); + +export default whatsappRoutes; diff --git a/backend/src/services/WhatsappService/CreateWhatsAppService.ts b/backend/src/services/WhatsappService/CreateWhatsAppService.ts new file mode 100644 index 0000000..423f436 --- /dev/null +++ b/backend/src/services/WhatsappService/CreateWhatsAppService.ts @@ -0,0 +1,47 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Whatsapp from "../../models/Whatsapp"; + +interface Request { + name: string; + status?: string; +} + +const CreateWhatsAppService = async ({ + name, + status = "INITIALIZING" +}: Request): Promise => { + // const schema = Yup.object().shape({ + // name: Yup.string().required().min(2), + // default: Yup.boolean() + // .required() + // .test( + // "Check-default", + // "Only one default whatsapp is permited", + // async value => { + // if (value === true) { + // const whatsappFound = await Whatsapp.findOne({ + // where: { default: true } + // }); + // return !Boolean(whatsappFound); + // } else return true; + // } + // ) + // }); + + // try { + // await schema.validate({ name, status }); + // } catch (err) { + // throw new AppError(err.message); + // } + + const whatsapp = await Whatsapp.create({ + name, + status + }); + + return whatsapp; +}; + +export default CreateWhatsAppService; diff --git a/backend/src/services/WhatsappService/ListWhatsAppsService.ts b/backend/src/services/WhatsappService/ListWhatsAppsService.ts new file mode 100644 index 0000000..2c2b884 --- /dev/null +++ b/backend/src/services/WhatsappService/ListWhatsAppsService.ts @@ -0,0 +1,13 @@ +import Whatsapp from "../../models/Whatsapp"; + +interface Response { + whatsapps: Whatsapp[]; +} + +const ListWhatsAppsService = async (): Promise => { + const whatsapps = await Whatsapp.findAll(); + + return { whatsapps }; +}; + +export default ListWhatsAppsService; From c77eacf3419e48be7ce733e85415148daf4751a6 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 09:48:25 -0300 Subject: [PATCH 14/54] changed whatsapp default column name --- ...9124112-update-default-column-name-on-whatsappp.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts diff --git a/backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts b/backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts new file mode 100644 index 0000000..4821129 --- /dev/null +++ b/backend/src/database/migrations/20200919124112-update-default-column-name-on-whatsappp.ts @@ -0,0 +1,11 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.renameColumn("Whatsapps", "default", "isDefault"); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.renameColumn("Whatsapps", "isDefault", "default"); + } +}; From eab7719583a0eeb883d6349a82c63335970634f9 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 09:52:39 -0300 Subject: [PATCH 15/54] changed whatsapp model --- backend/src/controllers/WhatsAppController.ts | 5 +- backend/src/models/Whatsapp.ts | 2 +- .../WhatsappService/CreateWhatsAppService.ts | 52 ++++++++++--------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 00c6137..3262f74 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -18,6 +18,7 @@ export const index = async (req: Request, res: Response): Promise => { interface WhatsappData { name: string; status: string; + isDefault?: boolean; } export const store = async (req: Request, res: Response): Promise => { @@ -25,9 +26,9 @@ export const store = async (req: Request, res: Response): Promise => { console.log("aqui"); - const { name, status }: WhatsappData = req.body; + const { name, status, isDefault }: WhatsappData = req.body; - const whatsapp = await CreateWhatsAppService({ name, status }); + const whatsapp = await CreateWhatsAppService({ name, status, isDefault }); // if (!whatsapp) { // return res.status(400).json({ error: "Cannot create whatsapp session." }); diff --git a/backend/src/models/Whatsapp.ts b/backend/src/models/Whatsapp.ts index 7fe0402..1ad2896 100644 --- a/backend/src/models/Whatsapp.ts +++ b/backend/src/models/Whatsapp.ts @@ -38,7 +38,7 @@ class Whatsapp extends Model { @Default(false) @AllowNull @Column - default: boolean; + isDefault: boolean; @CreatedAt createdAt: Date; diff --git a/backend/src/services/WhatsappService/CreateWhatsAppService.ts b/backend/src/services/WhatsappService/CreateWhatsAppService.ts index 423f436..5127253 100644 --- a/backend/src/services/WhatsappService/CreateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/CreateWhatsAppService.ts @@ -6,39 +6,43 @@ import Whatsapp from "../../models/Whatsapp"; interface Request { name: string; status?: string; + isDefault?: boolean; } const CreateWhatsAppService = async ({ name, - status = "INITIALIZING" + status = "INITIALIZING", + isDefault = false }: Request): Promise => { - // const schema = Yup.object().shape({ - // name: Yup.string().required().min(2), - // default: Yup.boolean() - // .required() - // .test( - // "Check-default", - // "Only one default whatsapp is permited", - // async value => { - // if (value === true) { - // const whatsappFound = await Whatsapp.findOne({ - // where: { default: true } - // }); - // return !Boolean(whatsappFound); - // } else return true; - // } - // ) - // }); + const schema = Yup.object().shape({ + name: Yup.string().required().min(2), + isDefault: Yup.boolean() + .required() + .test( + "Check-default", + "Only one default whatsapp is permited", + async value => { + if (value === true) { + const whatsappFound = await Whatsapp.findOne({ + where: { isDefault: true } + }); + return !whatsappFound; + } + return true; + } + ) + }); - // try { - // await schema.validate({ name, status }); - // } catch (err) { - // throw new AppError(err.message); - // } + try { + await schema.validate({ name, status, isDefault }); + } catch (err) { + throw new AppError(err.message); + } const whatsapp = await Whatsapp.create({ name, - status + status, + isDefault }); return whatsapp; From d3e0d1230f8524fe28a2cefbc295d1be696a9862 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 09:54:08 -0300 Subject: [PATCH 16/54] updated whatsapp model --- backend/src/models/Whatsapp.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/models/Whatsapp.ts b/backend/src/models/Whatsapp.ts index 1ad2896..6a590d3 100644 --- a/backend/src/models/Whatsapp.ts +++ b/backend/src/models/Whatsapp.ts @@ -20,6 +20,9 @@ class Whatsapp extends Model { @Column id: number; + @Column(DataType.TEXT) + name: string; + @Column(DataType.TEXT) session: string; From 872e237b48f3c1bdaebe521da46647f1382675b1 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 09:59:10 -0300 Subject: [PATCH 17/54] add validation to whatsapp name --- backend/src/controllers/WhatsAppController.ts | 4 +--- backend/src/models/Whatsapp.ts | 5 ++++- .../WhatsappService/CreateWhatsAppService.ts | 19 +++++++++++++++++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 3262f74..dc1f9ef 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -17,15 +17,13 @@ export const index = async (req: Request, res: Response): Promise => { interface WhatsappData { name: string; - status: string; + status?: string; isDefault?: boolean; } export const store = async (req: Request, res: Response): Promise => { // const io = getIO(); - console.log("aqui"); - const { name, status, isDefault }: WhatsappData = req.body; const whatsapp = await CreateWhatsAppService({ name, status, isDefault }); diff --git a/backend/src/models/Whatsapp.ts b/backend/src/models/Whatsapp.ts index 6a590d3..6ba684e 100644 --- a/backend/src/models/Whatsapp.ts +++ b/backend/src/models/Whatsapp.ts @@ -9,7 +9,8 @@ import { AutoIncrement, Default, AllowNull, - HasMany + HasMany, + Unique } from "sequelize-typescript"; import Ticket from "./Ticket"; @@ -20,6 +21,8 @@ class Whatsapp extends Model { @Column id: number; + @AllowNull + @Unique @Column(DataType.TEXT) name: string; diff --git a/backend/src/services/WhatsappService/CreateWhatsAppService.ts b/backend/src/services/WhatsappService/CreateWhatsAppService.ts index 5127253..fbc3fa5 100644 --- a/backend/src/services/WhatsappService/CreateWhatsAppService.ts +++ b/backend/src/services/WhatsappService/CreateWhatsAppService.ts @@ -15,12 +15,27 @@ const CreateWhatsAppService = async ({ isDefault = false }: Request): Promise => { const schema = Yup.object().shape({ - name: Yup.string().required().min(2), + name: Yup.string() + .required() + .min(2) + .test( + "Check-name", + "This whatsapp name is already used.", + async value => { + if (value) { + const whatsappFound = await Whatsapp.findOne({ + where: { name: value } + }); + return !whatsappFound; + } + return true; + } + ), isDefault: Yup.boolean() .required() .test( "Check-default", - "Only one default whatsapp is permited", + "Only one default whatsapp is permitted", async value => { if (value === true) { const whatsappFound = await Whatsapp.findOne({ From 5dda3aabafe2214f51d5529bf39dba8cd5b101c4 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 10:18:31 -0300 Subject: [PATCH 18/54] changed whatsapp controllers to typescript --- backend/src/controllers/WhatsAppController.ts | 118 +++++++----------- backend/src/routes/whatsappRoutes.ts | 14 +-- .../WhatsappService/DeleteWhatsApprService.ts | 16 +++ .../WhatsappService/ShowWhatsAppService.ts | 18 +++ .../WhatsappService/UpdateWhatsAppService.ts | 66 ++++++++++ 5 files changed, 150 insertions(+), 82 deletions(-) create mode 100644 backend/src/services/WhatsappService/DeleteWhatsApprService.ts create mode 100644 backend/src/services/WhatsappService/ShowWhatsAppService.ts create mode 100644 backend/src/services/WhatsappService/UpdateWhatsAppService.ts diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index dc1f9ef..bc7627c 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -1,5 +1,10 @@ import { Request, Response } from "express"; + import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; +import DeleteWhatsApprService from "../services/WhatsappService/DeleteWhatsApprService"; +import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService"; +import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; +import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService"; // import Yup from "yup"; // import Whatsapp from "../models/Whatsapp"; // import { getIO } from "../libs/socket"; @@ -7,8 +12,6 @@ import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppSer // import wbotMessageListener from "../services/wbotMessageListener"; // import wbotMonitor from "../services/wbotMonitor"; -import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService"; - export const index = async (req: Request, res: Response): Promise => { const whatsapps = await ListWhatsAppsService(); @@ -47,89 +50,54 @@ export const store = async (req: Request, res: Response): Promise => { return res.status(200).json(whatsapp); }; -// export const show = async (req: Request, res: Response): Promise => { -// const { whatsappId } = req.params; -// const whatsapp = await Whatsapp.findByPk(whatsappId); +export const show = async (req: Request, res: Response): Promise => { + const { whatsappId } = req.params; -// if (!whatsapp) { -// return res.status(200).json({ message: "Session not found" }); -// } + const whatsapp = await ShowWhatsAppService(whatsappId); -// return res.status(200).json(whatsapp); -// }; + return res.status(200).json(whatsapp); +}; -// export const update = async ( -// req: Request, -// res: Response -// ): Promise => { -// const { whatsappId } = req.params; +export const update = async ( + req: Request, + res: Response +): Promise => { + const { whatsappId } = req.params; + const whatsappData = req.body; -// const schema = Yup.object().shape({ -// name: Yup.string().required().min(2), -// default: Yup.boolean() -// .required() -// .test( -// "Check-default", -// "Only one default whatsapp is permited", -// async value => { -// if (value === true) { -// const whatsappFound = await Whatsapp.findOne({ -// where: { default: true } -// }); -// if (whatsappFound) { -// return !(whatsappFound.id !== +whatsappId); -// } else { -// return true; -// } -// } else return true; -// } -// ) -// }); + const whatsapp = await UpdateWhatsAppService({ whatsappData, whatsappId }); + // const io = getIO(); -// try { -// await schema.validate(req.body); -// } catch (err) { -// return res.status(400).json({ error: err.message }); -// } + // const whatsapp = await Whatsapp.findByPk(whatsappId); -// const io = getIO(); + // if (!whatsapp) { + // return res.status(404).json({ message: "Whatsapp not found" }); + // } -// const whatsapp = await Whatsapp.findByPk(whatsappId); + // await whatsapp.update(req.body); -// if (!whatsapp) { -// return res.status(404).json({ message: "Whatsapp not found" }); -// } + // io.emit("whatsapp", { + // action: "update", + // whatsapp: whatsapp + // }); -// await whatsapp.update(req.body); + return res.status(200).json(whatsapp); +}; -// io.emit("whatsapp", { -// action: "update", -// whatsapp: whatsapp -// }); +export const remove = async ( + req: Request, + res: Response +): Promise => { + // const io = getIO(); + const { whatsappId } = req.params; -// return res.status(200).json({ message: "Whatsapp updated" }); -// }; + await DeleteWhatsApprService(whatsappId); + // removeWbot(whatsapp.id); -// export const remove = async ( -// req: Request, -// res: Response -// ): Promise => { -// const io = getIO(); -// const { whatsappId } = req.params; + // io.emit("whatsapp", { + // action: "delete", + // whatsappId: whatsapp.id + // }); -// const whatsapp = await Whatsapp.findByPk(whatsappId); - -// if (!whatsapp) { -// return res.status(404).json({ message: "Whatsapp not found" }); -// } - -// await whatsapp.destroy(); -// removeWbot(whatsapp.id); - -// io.emit("whatsapp", { -// action: "delete", -// whatsappId: whatsapp.id -// }); - -// return res.status(200).json({ message: "Whatsapp deleted." }); -// }; + return res.status(200).json({ message: "Whatsapp deleted." }); +}; diff --git a/backend/src/routes/whatsappRoutes.ts b/backend/src/routes/whatsappRoutes.ts index de7fa28..dc187a7 100644 --- a/backend/src/routes/whatsappRoutes.ts +++ b/backend/src/routes/whatsappRoutes.ts @@ -9,14 +9,14 @@ whatsappRoutes.get("/whatsapp/", isAuth, WhatsAppController.index); whatsappRoutes.post("/whatsapp/", isAuth, WhatsAppController.store); -// whatsappRoutes.get("/whatsapp/:whatsappId", isAuth, WhatsAppController.show); +whatsappRoutes.get("/whatsapp/:whatsappId", isAuth, WhatsAppController.show); -// whatsappRoutes.put("/whatsapp/:whatsappId", isAuth, WhatsAppController.update); +whatsappRoutes.put("/whatsapp/:whatsappId", isAuth, WhatsAppController.update); -// whatsappRoutes.remove( -// "/whatsapp/:whatsappId", -// isAuth, -// WhatsAppController.delete -// ); +whatsappRoutes.delete( + "/whatsapp/:whatsappId", + isAuth, + WhatsAppController.remove +); export default whatsappRoutes; diff --git a/backend/src/services/WhatsappService/DeleteWhatsApprService.ts b/backend/src/services/WhatsappService/DeleteWhatsApprService.ts new file mode 100644 index 0000000..4f597c3 --- /dev/null +++ b/backend/src/services/WhatsappService/DeleteWhatsApprService.ts @@ -0,0 +1,16 @@ +import Whatsapp from "../../models/Whatsapp"; +import AppError from "../../errors/AppError"; + +const DeleteWhatsApprService = async (id: string): Promise => { + const whatsapp = await Whatsapp.findOne({ + where: { id } + }); + + if (!whatsapp) { + throw new AppError("No whatsapp found with this ID.", 404); + } + + await whatsapp.destroy(); +}; + +export default DeleteWhatsApprService; diff --git a/backend/src/services/WhatsappService/ShowWhatsAppService.ts b/backend/src/services/WhatsappService/ShowWhatsAppService.ts new file mode 100644 index 0000000..00050fb --- /dev/null +++ b/backend/src/services/WhatsappService/ShowWhatsAppService.ts @@ -0,0 +1,18 @@ +import Whatsapp from "../../models/Whatsapp"; +import AppError from "../../errors/AppError"; + +const ShowWhatsAppService = async ( + id: string +): Promise => { + const whatsapp = await Whatsapp.findOne({ + where: { id } + }); + + if (!whatsapp) { + throw new AppError("No whatsapp found with this ID.", 404); + } + + return whatsapp; +}; + +export default ShowWhatsAppService; diff --git a/backend/src/services/WhatsappService/UpdateWhatsAppService.ts b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts new file mode 100644 index 0000000..d818e21 --- /dev/null +++ b/backend/src/services/WhatsappService/UpdateWhatsAppService.ts @@ -0,0 +1,66 @@ +import * as Yup from "yup"; + +import AppError from "../../errors/AppError"; +import Whatsapp from "../../models/Whatsapp"; + +interface WhatsappData { + name?: string; + status?: string; + isDefault?: boolean; +} + +interface Request { + whatsappData: WhatsappData; + whatsappId: string; +} + +const UpdateWhatsAppService = async ({ + whatsappData, + whatsappId +}: Request): Promise => { + const schema = Yup.object().shape({ + name: Yup.string().min(2), + default: Yup.boolean().test( + "Check-default", + "Only one default whatsapp is permited", + async value => { + if (value === true) { + const whatsappFound = await Whatsapp.findOne({ + where: { default: true } + }); + if (whatsappFound) { + return !(whatsappFound.id !== +whatsappId); + } + return true; + } + return true; + } + ) + }); + + const { name, status, isDefault } = whatsappData; + + try { + await schema.validate({ name, status, isDefault }); + } catch (err) { + throw new AppError(err.message); + } + + const whatsapp = await Whatsapp.findOne({ + where: { id: whatsappId } + }); + + if (!whatsapp) { + throw new AppError("No whatsapp found with this ID.", 404); + } + + await whatsapp.update({ + name, + status, + isDefault + }); + + return whatsapp; +}; + +export default UpdateWhatsAppService; From 1d0bbda00daa86340ec0ef0c4273bae9b773475e Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 21:00:49 -0300 Subject: [PATCH 19/54] changed ticket creation and list to typescript --- .../ImportPhoneContactsController.ts | 38 ++++ backend/src/controllers/MessageController.ts | 203 ++++++++++++++++++ backend/src/controllers/TicketController.ts | 34 ++- backend/src/controllers/UserController.ts | 2 +- backend/src/controllers/WhatsAppController.ts | 4 +- .../controllers/WhatsAppSessionController.ts | 32 +++ ...heckSettingsHelper.ts => CheckSettings.ts} | 0 backend/src/helpers/GetDefaultWhatsapp.ts | 17 ++ backend/src/routes/routes/whatsappsessions.js | 20 -- backend/src/routes/whatsappSessionRoutes.ts | 20 ++ .../ContactServices/ListContactsService.ts | 11 + .../TicketServices/CreateTicketService.ts | 40 ++-- .../TicketServices/FindTicketService.ts | 28 +++ .../TicketServices/ListTicketsService.ts | 57 ++++- .../WhatsappService/FindWhatsAppService.ts | 30 +++ .../WhatsappService/ShowWhatsAppService.ts | 18 -- 16 files changed, 473 insertions(+), 81 deletions(-) create mode 100644 backend/src/controllers/ImportPhoneContactsController.ts create mode 100644 backend/src/controllers/MessageController.ts create mode 100644 backend/src/controllers/WhatsAppSessionController.ts rename backend/src/helpers/{CheckSettingsHelper.ts => CheckSettings.ts} (100%) create mode 100644 backend/src/helpers/GetDefaultWhatsapp.ts delete mode 100644 backend/src/routes/routes/whatsappsessions.js create mode 100644 backend/src/routes/whatsappSessionRoutes.ts create mode 100644 backend/src/services/TicketServices/FindTicketService.ts create mode 100644 backend/src/services/WhatsappService/FindWhatsAppService.ts delete mode 100644 backend/src/services/WhatsappService/ShowWhatsAppService.ts diff --git a/backend/src/controllers/ImportPhoneContactsController.ts b/backend/src/controllers/ImportPhoneContactsController.ts new file mode 100644 index 0000000..34f193a --- /dev/null +++ b/backend/src/controllers/ImportPhoneContactsController.ts @@ -0,0 +1,38 @@ +// const Contact = require("../models/Contact"); +// const Whatsapp = require("../models/Whatsapp"); +// const { getIO } = require("../libs/socket"); +// const { getWbot, initWbot } = require("../libs/wbot"); + +// exports.store = async (req, res, next) => { +// const defaultWhatsapp = await Whatsapp.findOne({ +// where: { default: true } +// }); + +// if (!defaultWhatsapp) { +// return res +// .status(404) +// .json({ error: "No default WhatsApp found. Check Connection page." }); +// } + +// const io = getIO(); +// const wbot = getWbot(defaultWhatsapp); + +// let phoneContacts; + +// try { +// phoneContacts = await wbot.getContacts(); +// } catch (err) { +// console.log(err); +// return res.status(500).json({ +// error: "Could not check whatsapp contact. Check connection page." +// }); +// } + +// await Promise.all( +// phoneContacts.map(async ({ number, name }) => { +// await Contact.create({ number, name }); +// }) +// ); + +// return res.status(200).json({ message: "contacts imported" }); +// }; diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts new file mode 100644 index 0000000..30c0975 --- /dev/null +++ b/backend/src/controllers/MessageController.ts @@ -0,0 +1,203 @@ +// const Message = require("../models/Message"); +// const Contact = require("../models/Contact"); +// const User = require("../models/User"); +// const Whatsapp = require("../models/Whatsapp"); + +// const Ticket = require("../models/Ticket"); +// const { getIO } = require("../libs/socket"); +// const { getWbot } = require("../libs/wbot"); +// const Sequelize = require("sequelize"); + +// const { MessageMedia } = require("whatsapp-web.js"); + +// const setMessagesAsRead = async ticket => { +// const io = getIO(); +// const wbot = getWbot(ticket.whatsappId); + +// await Message.update( +// { read: true }, +// { +// where: { +// ticketId: ticket.id, +// read: false, +// }, +// } +// ); + +// try { +// await wbot.sendSeen(`${ticket.contact.number}@c.us`); +// } catch (err) { +// console.log( +// "Could not mark messages as read. Maybe whatsapp session disconnected?" +// ); +// } + +// io.to("notification").emit("ticket", { +// action: "updateUnread", +// ticketId: ticket.id, +// }); +// }; + +// exports.index = async (req, res, next) => { +// const { ticketId } = req.params; +// const { searchParam = "", pageNumber = 1 } = req.query; + +// const whereCondition = { +// body: Sequelize.where( +// Sequelize.fn("LOWER", Sequelize.col("body")), +// "LIKE", +// "%" + searchParam.toLowerCase() + "%" +// ), +// }; + +// const limit = 20; +// const offset = limit * (pageNumber - 1); + +// const ticket = await Ticket.findByPk(ticketId, { +// include: [ +// { +// model: Contact, +// as: "contact", +// include: "extraInfo", +// attributes: ["id", "name", "number", "profilePicUrl"], +// }, +// { +// model: User, +// as: "user", +// }, +// ], +// }); + +// if (!ticket) { +// return res.status(404).json({ error: "No ticket found with this ID" }); +// } + +// await setMessagesAsRead(ticket); + +// const ticketMessages = await ticket.getMessages({ +// where: whereCondition, +// limit, +// offset, +// order: [["createdAt", "DESC"]], +// }); + +// const count = await ticket.countMessages(); + +// const serializedMessages = ticketMessages.map(message => { +// return { +// ...message.dataValues, +// mediaUrl: `${ +// message.mediaUrl +// ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` +// : "" +// }`, +// }; +// }); + +// const hasMore = count > offset + ticketMessages.length; + +// return res.json({ +// messages: serializedMessages.reverse(), +// ticket, +// count, +// hasMore, +// }); +// }; + +// exports.store = async (req, res, next) => { +// const io = getIO(); + +// const { ticketId } = req.params; +// const message = req.body; +// const media = req.file; +// let sentMessage; + +// const ticket = await Ticket.findByPk(ticketId, { +// include: [ +// { +// model: Contact, +// as: "contact", +// attributes: ["number", "name", "profilePicUrl"], +// }, +// ], +// }); + +// if (!ticket) { +// return res.status(404).json({ error: "No ticket found with this ID" }); +// } + +// if (!ticket.whatsappId) { +// const defaultWhatsapp = await Whatsapp.findOne({ +// where: { default: true }, +// }); + +// if (!defaultWhatsapp) { +// return res +// .status(404) +// .json({ error: "No default WhatsApp found. Check Connection page." }); +// } + +// await ticket.setWhatsapp(defaultWhatsapp); +// } + +// const wbot = getWbot(ticket.whatsappId); + +// try { +// if (media) { +// const newMedia = MessageMedia.fromFilePath(req.file.path); + +// message.mediaUrl = req.file.filename; +// if (newMedia.mimetype) { +// message.mediaType = newMedia.mimetype.split("/")[0]; +// } else { +// message.mediaType = "other"; +// } + +// sentMessage = await wbot.sendMessage( +// `${ticket.contact.number}@c.us`, +// newMedia +// ); + +// await ticket.update({ lastMessage: message.mediaUrl }); +// } else { +// sentMessage = await wbot.sendMessage( +// `${ticket.contact.number}@c.us`, +// message.body +// ); +// await ticket.update({ lastMessage: message.body }); +// } +// } catch (err) { +// console.log( +// "Could not create whatsapp message. Is session details valid? " +// ); +// } + +// if (sentMessage) { +// message.id = sentMessage.id.id; +// const newMessage = await ticket.createMessage(message); + +// const serialziedMessage = { +// ...newMessage.dataValues, +// mediaUrl: `${ +// message.mediaUrl +// ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` +// : "" +// }`, +// }; + +// io.to(ticketId).to("notification").emit("appMessage", { +// action: "create", +// message: serialziedMessage, +// ticket: ticket, +// contact: ticket.contact, +// }); + +// await setMessagesAsRead(ticket); + +// return res.status(200).json({ newMessage, ticket }); +// } + +// return res +// .status(500) +// .json({ error: "Cannot sent whatsapp message. Check connection page." }); +// }; diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 6b43b3b..8186fe0 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -1,4 +1,7 @@ import { Request, Response } from "express"; +import GetDefaultWhatsapp from "../helpers/GetDefaultWhatsapp"; +import Ticket from "../models/Ticket"; +import CreateTicketService from "../services/TicketServices/CreateTicketService"; import ListTicketsService from "../services/TicketServices/ListTicketsService"; // const Sequelize = require("sequelize"); // const { startOfDay, endOfDay, parseISO } = require("date-fns"); @@ -10,9 +13,7 @@ import ListTicketsService from "../services/TicketServices/ListTicketsService"; // const { getIO } = require("../libs/socket"); -import Whatsapp from "../models/Whatsapp"; -import Ticket from "../models/Ticket"; -import Whatsapp from "../models/Whatsapp"; +// import FindWhatsAppService from "../services/WhatsappService/FindWhatsAppService"; type RequestQuery = { searchParam: string; @@ -93,28 +94,17 @@ export const index = async (req: Request, res: Response): Promise => { return res.status(200).json({ tickets, count, hasMore }); }; +interface TicketData { + contactId: number; + status: string; +} + export const store = async (req: Request, res: Response): Promise => { // const io = getIO(); - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true } - }); + const { contactId, status }: TicketData = req.body; - const ticketData = req.body; - - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } - - const ticket: Ticket = await defaultWhatsapp.$create("ticket", req.body); - - await ticket.$get("contact"); - - const wapp = await ticket.$get("whatsapp"); - - const tickets = await wapp?.$get("tickets"); + const ticket = await CreateTicketService({ contactId, status }); // const serializaedTicket = { ...ticket.dataValues, contact: contact }; @@ -123,7 +113,7 @@ export const store = async (req: Request, res: Response): Promise => { // ticket: serializaedTicket // }); - return res.status(200).json({ ticket }); + return res.status(200).json(ticket); }; // export const update = (req: Request, res: Response): Promise => { diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 062222c..07dbe64 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; -import CheckSettingsHelper from "../helpers/CheckSettingsHelper"; +import CheckSettingsHelper from "../helpers/CheckSettings"; import AppError from "../errors/AppError"; import CreateUserService from "../services/UserServices/CreateUserService"; diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index bc7627c..5cf4e17 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -3,7 +3,7 @@ import { Request, Response } from "express"; import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; import DeleteWhatsApprService from "../services/WhatsappService/DeleteWhatsApprService"; import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService"; -import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; +import FindWhatsAppService from "../services/WhatsappService/FindWhatsAppService"; import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService"; // import Yup from "yup"; // import Whatsapp from "../models/Whatsapp"; @@ -53,7 +53,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { whatsappId } = req.params; - const whatsapp = await ShowWhatsAppService(whatsappId); + const whatsapp = await FindWhatsAppService(whatsappId); return res.status(200).json(whatsapp); }; diff --git a/backend/src/controllers/WhatsAppSessionController.ts b/backend/src/controllers/WhatsAppSessionController.ts new file mode 100644 index 0000000..38f5416 --- /dev/null +++ b/backend/src/controllers/WhatsAppSessionController.ts @@ -0,0 +1,32 @@ +// const Whatsapp = require("../models/Whatsapp"); +// const { getIO } = require("../libs/socket"); +// const { getWbot, initWbot, removeWbot } = require("../libs/wbot"); +// const wbotMessageListener = require("../services/wbotMessageListener"); +// const wbotMonitor = require("../services/wbotMonitor"); + +// exports.show = async (req, res) => { +// const { whatsappId } = req.params; +// const dbSession = await Whatsapp.findByPk(whatsappId); + +// if (!dbSession) { +// return res.status(200).json({ message: "Session not found" }); +// } + +// return res.status(200).json(dbSession); +// }; + +// exports.delete = async (req, res) => { +// const { whatsappId } = req.params; + +// const dbSession = await Whatsapp.findByPk(whatsappId); + +// if (!dbSession) { +// return res.status(404).json({ message: "Session not found" }); +// } + +// const wbot = getWbot(dbSession.id); + +// wbot.logout(); + +// return res.status(200).json({ message: "Session disconnected." }); +// }; diff --git a/backend/src/helpers/CheckSettingsHelper.ts b/backend/src/helpers/CheckSettings.ts similarity index 100% rename from backend/src/helpers/CheckSettingsHelper.ts rename to backend/src/helpers/CheckSettings.ts diff --git a/backend/src/helpers/GetDefaultWhatsapp.ts b/backend/src/helpers/GetDefaultWhatsapp.ts new file mode 100644 index 0000000..8c37e6b --- /dev/null +++ b/backend/src/helpers/GetDefaultWhatsapp.ts @@ -0,0 +1,17 @@ +import AppError from "../errors/AppError"; +import Whatsapp from "../models/Whatsapp"; +import FindWhatsAppService from "../services/WhatsappService/FindWhatsAppService"; + +const GetDefaultWhatsapp = async (): Promise => { + const defaultWhatsapp = await FindWhatsAppService({ + where: { isDefault: true } + }); + + if (!defaultWhatsapp) { + throw new AppError("No default WhatsApp found. Check Connection page."); + } + + return defaultWhatsapp; +}; + +export default GetDefaultWhatsapp; diff --git a/backend/src/routes/routes/whatsappsessions.js b/backend/src/routes/routes/whatsappsessions.js deleted file mode 100644 index 2eb87f9..0000000 --- a/backend/src/routes/routes/whatsappsessions.js +++ /dev/null @@ -1,20 +0,0 @@ -const express = require("express"); -const isAuth = require("../../middleware/is-auth"); - -const WhatsAppSessionController = require("../../controllers/WhatsAppSessionController"); - -const routes = express.Router(); - -routes.get( - "/whatsappsession/:whatsappId", - isAuth, - WhatsAppSessionController.show -); - -routes.delete( - "/whatsappsession/:whatsappId", - isAuth, - WhatsAppSessionController.delete -); - -module.exports = routes; diff --git a/backend/src/routes/whatsappSessionRoutes.ts b/backend/src/routes/whatsappSessionRoutes.ts new file mode 100644 index 0000000..ead10c2 --- /dev/null +++ b/backend/src/routes/whatsappSessionRoutes.ts @@ -0,0 +1,20 @@ +// const express = require("express"); +// const isAuth = require("../../middleware/is-auth"); + +// const WhatsAppSessionController = require("../../controllers/WhatsAppSessionController"); + +// const routes = express.Router(); + +// routes.get( +// "/whatsappsession/:whatsappId", +// isAuth, +// WhatsAppSessionController.show +// ); + +// routes.delete( +// "/whatsappsession/:whatsappId", +// isAuth, +// WhatsAppSessionController.delete +// ); + +// module.exports = routes; diff --git a/backend/src/services/ContactServices/ListContactsService.ts b/backend/src/services/ContactServices/ListContactsService.ts index 52c7f8a..f0ab9c9 100644 --- a/backend/src/services/ContactServices/ListContactsService.ts +++ b/backend/src/services/ContactServices/ListContactsService.ts @@ -16,6 +16,16 @@ const ListContactsService = async ({ searchParam = "", pageNumber = "1" }: Request): Promise => { + let includeCondition = [ + { + model: Contact, + as: "contact", + attributes: ["name", "number", "profilePicUrl"] + } + ]; + + // let whereCondition = {}; + const whereCondition = { [Op.or]: [ { @@ -33,6 +43,7 @@ const ListContactsService = async ({ const { count, rows: contacts } = await Contact.findAndCountAll({ where: whereCondition, + include: includeCondition, limit, offset, order: [["createdAt", "DESC"]] diff --git a/backend/src/services/TicketServices/CreateTicketService.ts b/backend/src/services/TicketServices/CreateTicketService.ts index d334989..2df4650 100644 --- a/backend/src/services/TicketServices/CreateTicketService.ts +++ b/backend/src/services/TicketServices/CreateTicketService.ts @@ -1,24 +1,34 @@ -import * as Yup from "yup"; - import AppError from "../../errors/AppError"; -import User from "../../models/User"; +import GetDefaultWhatsapp from "../../helpers/GetDefaultWhatsapp"; +import Ticket from "../../models/Ticket"; interface Request { - email: string; - password: string; - name: string; - profile?: string; + contactId: number; + status?: string; } -interface Response { - email: string; - name: string; - id: number; - profile: string; -} +const CreateTicketService = async ({ + contactId, + status +}: Request): Promise => { + const defaultWhatsapp = await GetDefaultWhatsapp(); -const CreateTicketService = async (): Promise => { - return true; + if (!defaultWhatsapp) { + throw new AppError("No default WhatsApp found. Check Connection page."); + } + + const { id } = await defaultWhatsapp.$create("ticket", { + contactId, + status + }); + + const ticket = await Ticket.findByPk(id, { include: ["contact"] }); + + if (!ticket) { + throw new AppError("Error, ticket not created."); + } + + return ticket; }; export default CreateTicketService; diff --git a/backend/src/services/TicketServices/FindTicketService.ts b/backend/src/services/TicketServices/FindTicketService.ts new file mode 100644 index 0000000..4846f61 --- /dev/null +++ b/backend/src/services/TicketServices/FindTicketService.ts @@ -0,0 +1,28 @@ +import Whatsapp from "../../models/Whatsapp"; +import AppError from "../../errors/AppError"; + +interface WhereParams { + id?: number; + name?: string; + isDefault?: boolean; +} + +interface Request { + where?: WhereParams; +} + +const FindTicketService = async ({ where }: Request): Promise => { + const whereCondition = { ...where }; + + const whatsapp = await Whatsapp.findOne({ + where: whereCondition + }); + + if (!whatsapp) { + throw new AppError("No whatsapp found with this conditions.", 404); + } + + return whatsapp; +}; + +export default FindTicketService; diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts index 5fecc22..5b5d402 100644 --- a/backend/src/services/TicketServices/ListTicketsService.ts +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -1,7 +1,9 @@ -import { Op } from "sequelize"; +import { Op, fn, where, col } from "sequelize"; import { startOfDay, endOfDay, parseISO } from "date-fns"; import Ticket from "../../models/Ticket"; +import Contact from "../../models/Contact"; +import Message from "../../models/Message"; interface Request { searchParam?: string; @@ -27,6 +29,15 @@ const ListTicketsService = async ({ userId }: Request): Promise => { let whereCondition = {}; + let includeCondition = []; + + includeCondition = [ + { + model: Contact, + as: "contact", + attributes: ["name", "number", "profilePicUrl"] + } + ]; if (showAll === "true") { whereCondition = {}; @@ -41,6 +52,46 @@ const ListTicketsService = async ({ }; } + if (searchParam) { + includeCondition = [ + ...includeCondition, + { + model: Message, + as: "messages", + attributes: ["id", "body"], + where: { + body: where( + fn("LOWER", col("body")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + }, + required: false, + duplicating: false + } + ]; + + whereCondition = { + [Op.or]: [ + { + "$contact.name$": where( + fn("LOWER", col("name")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + }, + { "$contact.number$": { [Op.like]: `%${searchParam}%` } }, + { + "$message.body$": where( + fn("LOWER", col("body")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ) + } + ] + }; + } + if (date) { whereCondition = { ...whereCondition, @@ -55,8 +106,8 @@ const ListTicketsService = async ({ const { count, rows: tickets } = await Ticket.findAndCountAll({ where: whereCondition, - // distinct: true, - // include: includeCondition, + include: includeCondition, + distinct: true, limit, offset, order: [["updatedAt", "DESC"]] diff --git a/backend/src/services/WhatsappService/FindWhatsAppService.ts b/backend/src/services/WhatsappService/FindWhatsAppService.ts new file mode 100644 index 0000000..1f0d893 --- /dev/null +++ b/backend/src/services/WhatsappService/FindWhatsAppService.ts @@ -0,0 +1,30 @@ +import Whatsapp from "../../models/Whatsapp"; +import AppError from "../../errors/AppError"; + +interface WhereParams { + id?: number; + name?: string; + isDefault?: boolean; +} + +interface Request { + where?: WhereParams; +} + +const FindWhatsAppService = async ({ + where +}: Request): Promise => { + const whereCondition = { ...where }; + + const whatsapp = await Whatsapp.findOne({ + where: whereCondition + }); + + if (!whatsapp) { + throw new AppError("No whatsapp found with this conditions.", 404); + } + + return whatsapp; +}; + +export default FindWhatsAppService; diff --git a/backend/src/services/WhatsappService/ShowWhatsAppService.ts b/backend/src/services/WhatsappService/ShowWhatsAppService.ts deleted file mode 100644 index 00050fb..0000000 --- a/backend/src/services/WhatsappService/ShowWhatsAppService.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Whatsapp from "../../models/Whatsapp"; -import AppError from "../../errors/AppError"; - -const ShowWhatsAppService = async ( - id: string -): Promise => { - const whatsapp = await Whatsapp.findOne({ - where: { id } - }); - - if (!whatsapp) { - throw new AppError("No whatsapp found with this ID.", 404); - } - - return whatsapp; -}; - -export default ShowWhatsAppService; From f221fef32de8f57ea21926391db745eb378572a7 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 21:10:12 -0300 Subject: [PATCH 20/54] changed ticket update route to typescript --- backend/src/controllers/TicketController.ts | 56 ++++++++++--------- backend/src/routes/ticketRoutes.ts | 2 +- .../TicketServices/UpdateTicketService.ts | 36 ++++++++++++ 3 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 backend/src/services/TicketServices/UpdateTicketService.ts diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 8186fe0..dfaca0b 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -3,6 +3,7 @@ import GetDefaultWhatsapp from "../helpers/GetDefaultWhatsapp"; import Ticket from "../models/Ticket"; import CreateTicketService from "../services/TicketServices/CreateTicketService"; import ListTicketsService from "../services/TicketServices/ListTicketsService"; +import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; // const Sequelize = require("sequelize"); // const { startOfDay, endOfDay, parseISO } = require("date-fns"); @@ -100,14 +101,11 @@ interface TicketData { } export const store = async (req: Request, res: Response): Promise => { - // const io = getIO(); - const { contactId, status }: TicketData = req.body; const ticket = await CreateTicketService({ contactId, status }); - // const serializaedTicket = { ...ticket.dataValues, contact: contact }; - + // const io = getIO(); // io.to("notification").emit("ticket", { // action: "create", // ticket: serializaedTicket @@ -116,33 +114,39 @@ export const store = async (req: Request, res: Response): Promise => { return res.status(200).json(ticket); }; -// export const update = (req: Request, res: Response): Promise => { -// const io = getIO(); -// const { ticketId } = req.params; +export const update = async ( + req: Request, + res: Response +): Promise => { + // const io = getIO(); + const { ticketId } = req.params; + const ticketData: TicketData = req.body; -// const ticket = await Ticket.findByPk(ticketId, { -// include: [ -// { -// model: Contact, -// as: "contact", -// attributes: ["name", "number", "profilePicUrl"] -// } -// ] -// }); + const ticket = await UpdateTicketService({ ticketData, ticketId }); -// if (!ticket) { -// return res.status(404).json({ error: "No ticket found with this ID" }); -// } + // const ticket = await Ticket.findByPk(ticketId, { + // include: [ + // { + // model: Contact, + // as: "contact", + // attributes: ["name", "number", "profilePicUrl"] + // } + // ] + // }); -// await ticket.update(req.body); + // if (!ticket) { + // return res.status(404).json({ error: "No ticket found with this ID" }); + // } -// io.to("notification").emit("ticket", { -// action: "updateStatus", -// ticket: ticket -// }); + // await ticket.update(req.body); -// return res.status(200).json(ticket); -// }; + // io.to("notification").emit("ticket", { + // action: "updateStatus", + // ticket: ticket + // }); + + return res.status(200).json(ticket); +}; // export const remove = (req: Request, res: Response): Promise => { // const io = getIO(); diff --git a/backend/src/routes/ticketRoutes.ts b/backend/src/routes/ticketRoutes.ts index 4cb6897..3a17fda 100644 --- a/backend/src/routes/ticketRoutes.ts +++ b/backend/src/routes/ticketRoutes.ts @@ -9,7 +9,7 @@ ticketRoutes.get("/tickets", isAuth, TicketController.index); ticketRoutes.post("/tickets", isAuth, TicketController.store); -// ticketRoutes.put("/tickets/:ticketId", isAuth, TicketController.update); +ticketRoutes.put("/tickets/:ticketId", isAuth, TicketController.update); // ticketRoutes.delete("/tickets/:ticketId", isAuth, TicketController.remove); diff --git a/backend/src/services/TicketServices/UpdateTicketService.ts b/backend/src/services/TicketServices/UpdateTicketService.ts new file mode 100644 index 0000000..342f0c5 --- /dev/null +++ b/backend/src/services/TicketServices/UpdateTicketService.ts @@ -0,0 +1,36 @@ +import AppError from "../../errors/AppError"; +import Ticket from "../../models/Ticket"; + +interface TicketData { + status?: string; + userId?: number; +} + +interface Request { + ticketData: TicketData; + ticketId: string; +} + +const UpdateTicketService = async ({ + ticketData, + ticketId +}: Request): Promise => { + const { status, userId } = ticketData; + + const ticket = await Ticket.findOne({ + where: { id: ticketId } + }); + + if (!ticket) { + throw new AppError("No ticket found with this ID.", 404); + } + + await ticket.update({ + status, + userId + }); + + return ticket; +}; + +export default UpdateTicketService; From 2bf5caadce00bca0505108789420629a5ed6e9a9 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 21:14:25 -0300 Subject: [PATCH 21/54] change delete ticket route to typescript --- backend/src/controllers/TicketController.ts | 48 ++++++------------- backend/src/controllers/WhatsAppController.ts | 4 +- backend/src/routes/ticketRoutes.ts | 2 +- .../TicketServices/DeleteTicketService.ts | 16 +++++++ .../TicketServices/UpdateTicketService.ts | 10 +++- ...pprService.ts => DeleteWhatsAppService.ts} | 0 6 files changed, 43 insertions(+), 37 deletions(-) create mode 100644 backend/src/services/TicketServices/DeleteTicketService.ts rename backend/src/services/WhatsappService/{DeleteWhatsApprService.ts => DeleteWhatsAppService.ts} (100%) diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index dfaca0b..bad73ae 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import GetDefaultWhatsapp from "../helpers/GetDefaultWhatsapp"; import Ticket from "../models/Ticket"; import CreateTicketService from "../services/TicketServices/CreateTicketService"; +import DeleteTicketService from "../services/TicketServices/DeleteTicketService"; import ListTicketsService from "../services/TicketServices/ListTicketsService"; import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; // const Sequelize = require("sequelize"); @@ -118,28 +119,12 @@ export const update = async ( req: Request, res: Response ): Promise => { - // const io = getIO(); const { ticketId } = req.params; const ticketData: TicketData = req.body; const ticket = await UpdateTicketService({ ticketData, ticketId }); - // const ticket = await Ticket.findByPk(ticketId, { - // include: [ - // { - // model: Contact, - // as: "contact", - // attributes: ["name", "number", "profilePicUrl"] - // } - // ] - // }); - - // if (!ticket) { - // return res.status(404).json({ error: "No ticket found with this ID" }); - // } - - // await ticket.update(req.body); - + // const io = getIO(); // io.to("notification").emit("ticket", { // action: "updateStatus", // ticket: ticket @@ -148,22 +133,19 @@ export const update = async ( return res.status(200).json(ticket); }; -// export const remove = (req: Request, res: Response): Promise => { -// const io = getIO(); -// const { ticketId } = req.params; +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { ticketId } = req.params; -// const ticket = await Ticket.findByPk(ticketId); + await DeleteTicketService(ticketId); -// if (!ticket) { -// return res.status(400).json({ error: "No ticket found with this ID" }); -// } + // const io = getIO(); + // io.to("notification").emit("ticket", { + // action: "delete", + // ticketId: ticket.id + // }); -// await ticket.destroy(); - -// io.to("notification").emit("ticket", { -// action: "delete", -// ticketId: ticket.id -// }); - -// return res.status(200).json({ message: "ticket deleted" }); -// }; + return res.status(200).json({ message: "ticket deleted" }); +}; diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 5cf4e17..9dca11f 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -1,7 +1,7 @@ import { Request, Response } from "express"; import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; -import DeleteWhatsApprService from "../services/WhatsappService/DeleteWhatsApprService"; +import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService"; import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService"; import FindWhatsAppService from "../services/WhatsappService/FindWhatsAppService"; import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService"; @@ -91,7 +91,7 @@ export const remove = async ( // const io = getIO(); const { whatsappId } = req.params; - await DeleteWhatsApprService(whatsappId); + await DeleteWhatsAppService(whatsappId); // removeWbot(whatsapp.id); // io.emit("whatsapp", { diff --git a/backend/src/routes/ticketRoutes.ts b/backend/src/routes/ticketRoutes.ts index 3a17fda..1627be2 100644 --- a/backend/src/routes/ticketRoutes.ts +++ b/backend/src/routes/ticketRoutes.ts @@ -11,6 +11,6 @@ ticketRoutes.post("/tickets", isAuth, TicketController.store); ticketRoutes.put("/tickets/:ticketId", isAuth, TicketController.update); -// ticketRoutes.delete("/tickets/:ticketId", isAuth, TicketController.remove); +ticketRoutes.delete("/tickets/:ticketId", isAuth, TicketController.remove); export default ticketRoutes; diff --git a/backend/src/services/TicketServices/DeleteTicketService.ts b/backend/src/services/TicketServices/DeleteTicketService.ts new file mode 100644 index 0000000..9e1f579 --- /dev/null +++ b/backend/src/services/TicketServices/DeleteTicketService.ts @@ -0,0 +1,16 @@ +import Ticket from "../../models/Ticket"; +import AppError from "../../errors/AppError"; + +const DeleteTicketService = async (id: string): Promise => { + const ticket = await Ticket.findOne({ + where: { id } + }); + + if (!ticket) { + throw new AppError("No ticket found with this ID.", 404); + } + + await ticket.destroy(); +}; + +export default DeleteTicketService; diff --git a/backend/src/services/TicketServices/UpdateTicketService.ts b/backend/src/services/TicketServices/UpdateTicketService.ts index 342f0c5..303a91d 100644 --- a/backend/src/services/TicketServices/UpdateTicketService.ts +++ b/backend/src/services/TicketServices/UpdateTicketService.ts @@ -1,4 +1,5 @@ import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; import Ticket from "../../models/Ticket"; interface TicketData { @@ -18,7 +19,14 @@ const UpdateTicketService = async ({ const { status, userId } = ticketData; const ticket = await Ticket.findOne({ - where: { id: ticketId } + where: { id: ticketId }, + include: [ + { + model: Contact, + as: "contact", + attributes: ["name", "number", "profilePicUrl"] + } + ] }); if (!ticket) { diff --git a/backend/src/services/WhatsappService/DeleteWhatsApprService.ts b/backend/src/services/WhatsappService/DeleteWhatsAppService.ts similarity index 100% rename from backend/src/services/WhatsappService/DeleteWhatsApprService.ts rename to backend/src/services/WhatsappService/DeleteWhatsAppService.ts From 5b54c4df414ed2246e79bc71b67980436fe74ccd Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 21:16:07 -0300 Subject: [PATCH 22/54] change service names --- backend/src/controllers/ContactController.ts | 4 ++-- backend/src/controllers/UserController.ts | 4 ++-- .../{ShowContactService.ts => FindContactService.ts} | 4 ++-- .../UserServices/{ShowUserService.ts => FindUserService.ts} | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename backend/src/services/ContactServices/{ShowContactService.ts => FindContactService.ts} (75%) rename backend/src/services/UserServices/{ShowUserService.ts => FindUserService.ts} (76%) diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts index a2081ad..f3995b0 100644 --- a/backend/src/controllers/ContactController.ts +++ b/backend/src/controllers/ContactController.ts @@ -2,7 +2,7 @@ import { Request, Response } from "express"; import ListContactsService from "../services/ContactServices/ListContactsService"; import CreateContactService from "../services/ContactServices/CreateContactService"; -import ShowContactService from "../services/ContactServices/ShowContactService"; +import FindContactService from "../services/ContactServices/FindContactService"; import UpdateContactService from "../services/ContactServices/UpdateContactService"; import DeleteContactService from "../services/ContactServices/DeleteContactService"; @@ -94,7 +94,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { contactId } = req.params; - const contact = await ShowContactService(contactId); + const contact = await FindContactService(contactId); return res.status(200).json(contact); }; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 07dbe64..14c0401 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -6,7 +6,7 @@ import AppError from "../errors/AppError"; import CreateUserService from "../services/UserServices/CreateUserService"; import ListUsersService from "../services/UserServices/ListUsersService"; import UpdateUserService from "../services/UserServices/UpdateUserService"; -import ShowUserService from "../services/UserServices/ShowUserService"; +import FindUserService from "../services/UserServices/FindUserService"; import DeleteUserService from "../services/UserServices/DeleteUserService"; type RequestQuery = { @@ -53,7 +53,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { userId } = req.params; - const user = await ShowUserService(userId); + const user = await FindUserService(userId); return res.status(200).json(user); }; diff --git a/backend/src/services/ContactServices/ShowContactService.ts b/backend/src/services/ContactServices/FindContactService.ts similarity index 75% rename from backend/src/services/ContactServices/ShowContactService.ts rename to backend/src/services/ContactServices/FindContactService.ts index 6905683..c381ca1 100644 --- a/backend/src/services/ContactServices/ShowContactService.ts +++ b/backend/src/services/ContactServices/FindContactService.ts @@ -1,7 +1,7 @@ import Contact from "../../models/Contact"; import AppError from "../../errors/AppError"; -const ShowContactService = async (id: string): Promise => { +const FindContactService = async (id: string): Promise => { const user = await Contact.findOne({ where: { id }, attributes: ["id", "name", "number", "email"] @@ -14,4 +14,4 @@ const ShowContactService = async (id: string): Promise => { return user; }; -export default ShowContactService; +export default FindContactService; diff --git a/backend/src/services/UserServices/ShowUserService.ts b/backend/src/services/UserServices/FindUserService.ts similarity index 76% rename from backend/src/services/UserServices/ShowUserService.ts rename to backend/src/services/UserServices/FindUserService.ts index 06f65eb..868bf15 100644 --- a/backend/src/services/UserServices/ShowUserService.ts +++ b/backend/src/services/UserServices/FindUserService.ts @@ -1,7 +1,7 @@ import User from "../../models/User"; import AppError from "../../errors/AppError"; -const ShowUserService = async (id: string): Promise => { +const FindUserService = async (id: string): Promise => { const user = await User.findOne({ where: { id }, attributes: ["name", "id", "email", "profile"] @@ -14,4 +14,4 @@ const ShowUserService = async (id: string): Promise => { return user; }; -export default ShowUserService; +export default FindUserService; From 70c391e8ef9b5914487198b6e9c7dd9b113ddf3a Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 21:41:28 -0300 Subject: [PATCH 23/54] finished contact migration to typescript --- backend/src/controllers/ContactController.ts | 67 +--- .../src/controllersOld/ContactController.js | 340 +++++++++--------- .../ContactServices/CreateContactService.ts | 25 +- .../ContactServices/UpdateContactService.ts | 33 +- 4 files changed, 235 insertions(+), 230 deletions(-) diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts index f3995b0..0254db2 100644 --- a/backend/src/controllers/ContactController.ts +++ b/backend/src/controllers/ContactController.ts @@ -6,23 +6,16 @@ import FindContactService from "../services/ContactServices/FindContactService"; import UpdateContactService from "../services/ContactServices/UpdateContactService"; import DeleteContactService from "../services/ContactServices/DeleteContactService"; -// const Sequelize = require("sequelize"); -// const { Op } = require("sequelize"); - -// const Contact = require("../models/Contact"); -// const Whatsapp = require("../models/Whatsapp"); -// const ContactCustomField = require("../models/ContactCustomField"); - // const { getIO } = require("../libs/socket"); // const { getWbot } = require("../libs/wbot"); -type RequestQuery = { +type IndexQuery = { searchParam: string; pageNumber: string; }; export const index = async (req: Request, res: Response): Promise => { - const { searchParam, pageNumber } = req.query as RequestQuery; + const { searchParam, pageNumber } = req.query as IndexQuery; const { contacts, count, hasMore } = await ListContactsService({ searchParam, @@ -32,16 +25,26 @@ export const index = async (req: Request, res: Response): Promise => { return res.json({ contacts, count, hasMore }); }; +interface ExtraInfo { + name: string; + value: string; +} interface ContactData { name: string; number: string; email?: string; + extraInfo?: ExtraInfo[]; } export const store = async (req: Request, res: Response): Promise => { - const { name, number, email }: ContactData = req.body; + const { name, number, email, extraInfo }: ContactData = req.body; - const contact = await CreateContactService({ name, number, email }); + const contact = await CreateContactService({ + name, + number, + email, + extraInfo + }); // const defaultWhatsapp = await Whatsapp.findOne({ // where: { default: true } @@ -103,43 +106,13 @@ export const update = async ( req: Request, res: Response ): Promise => { - const contactData = req.body; + const contactData: ContactData = req.body; const { contactId } = req.params; const contact = await UpdateContactService({ contactData, contactId }); // const io = getIO(); - // const contact = await Contact.findByPk(contactId, { - // include: "extraInfo" - // }); - - // if (!contact) { - // return res.status(404).json({ error: "No contact found with this ID" }); - // } - - // if (updatedContact.extraInfo) { - // await Promise.all( - // updatedContact.extraInfo.map(async info => { - // await ContactCustomField.upsert({ ...info, contactId: contact.id }); - // }) - // ); - - // await Promise.all( - // contact.extraInfo.map(async oldInfo => { - // let stillExists = updatedContact.extraInfo.findIndex( - // info => info.id === oldInfo.id - // ); - - // if (stillExists === -1) { - // await ContactCustomField.destroy({ where: { id: oldInfo.id } }); - // } - // }) - // ); - // } - - // await contact.update(updatedContact); - // io.emit("contact", { // action: "update", // contact: contact @@ -152,19 +125,11 @@ export const remove = async ( req: Request, res: Response ): Promise => { - // const io = getIO(); const { contactId } = req.params; await DeleteContactService(contactId); - // const contact = await Contact.findByPk(contactId); - - // if (!contact) { - // return res.status(404).json({ error: "No contact found with this ID" }); - // } - - // await contact.destroy(); - + // const io = getIO(); // io.emit("contact", { // action: "delete", // contactId: contactId diff --git a/backend/src/controllersOld/ContactController.js b/backend/src/controllersOld/ContactController.js index 268d5a3..49a352a 100644 --- a/backend/src/controllersOld/ContactController.js +++ b/backend/src/controllersOld/ContactController.js @@ -1,170 +1,170 @@ -const Sequelize = require("sequelize"); -const { Op } = require("sequelize"); - -const Contact = require("../models/Contact"); -const Whatsapp = require("../models/Whatsapp"); -const ContactCustomField = require("../models/ContactCustomField"); - -const { getIO } = require("../libs/socket"); -const { getWbot } = require("../libs/wbot"); - -exports.index = async (req, res) => { - const { searchParam = "", pageNumber = 1 } = req.query; - - const whereCondition = { - [Op.or]: [ - { - name: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ), - }, - { number: { [Op.like]: `%${searchParam}%` } }, - ], - }; - - let limit = 20; - let offset = limit * (pageNumber - 1); - - const { count, rows: contacts } = await Contact.findAndCountAll({ - where: whereCondition, - limit, - offset, - order: [["createdAt", "DESC"]], - }); - - const hasMore = count > offset + contacts.length; - - return res.json({ contacts, count, hasMore }); -}; - -exports.store = async (req, res) => { - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true }, - }); - - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } - - const wbot = getWbot(defaultWhatsapp); - const io = getIO(); - const newContact = req.body; - - try { - const isValidNumber = await wbot.isRegisteredUser( - `${newContact.number}@c.us` - ); - if (!isValidNumber) { - return res - .status(400) - .json({ error: "The suplied number is not a valid Whatsapp number" }); - } - } catch (err) { - console.log(err); - return res.status(500).json({ - error: "Could not check whatsapp contact. Check connection page.", - }); - } - - const profilePicUrl = await wbot.getProfilePicUrl( - `${newContact.number}@c.us` - ); - - const contact = await Contact.create( - { ...newContact, profilePicUrl }, - { - include: "extraInfo", - } - ); - - io.emit("contact", { - action: "create", - contact: contact, - }); - - return res.status(200).json(contact); -}; - -exports.show = async (req, res) => { - const { contactId } = req.params; - - const contact = await Contact.findByPk(contactId, { - include: "extraInfo", - attributes: ["id", "name", "number", "email"], - }); - - if (!contact) { - return res.status(404).json({ error: "No contact found with this id." }); - } - - return res.status(200).json(contact); -}; - -exports.update = async (req, res) => { - const io = getIO(); - - const updatedContact = req.body; - - const { contactId } = req.params; - - const contact = await Contact.findByPk(contactId, { - include: "extraInfo", - }); - - if (!contact) { - return res.status(404).json({ error: "No contact found with this ID" }); - } - - if (updatedContact.extraInfo) { - await Promise.all( - updatedContact.extraInfo.map(async info => { - await ContactCustomField.upsert({ ...info, contactId: contact.id }); - }) - ); - - await Promise.all( - contact.extraInfo.map(async oldInfo => { - let stillExists = updatedContact.extraInfo.findIndex( - info => info.id === oldInfo.id - ); - - if (stillExists === -1) { - await ContactCustomField.destroy({ where: { id: oldInfo.id } }); - } - }) - ); - } - - await contact.update(updatedContact); - - io.emit("contact", { - action: "update", - contact: contact, - }); - - return res.status(200).json(contact); -}; - -exports.delete = async (req, res) => { - const io = getIO(); - const { contactId } = req.params; - - const contact = await Contact.findByPk(contactId); - - if (!contact) { - return res.status(404).json({ error: "No contact found with this ID" }); - } - - await contact.destroy(); - - io.emit("contact", { - action: "delete", - contactId: contactId, - }); - - return res.status(200).json({ message: "Contact deleted" }); -}; +const Sequelize = require("sequelize"); +const { Op } = require("sequelize"); + +const Contact = require("../models/Contact"); +const Whatsapp = require("../models/Whatsapp"); +const ContactCustomField = require("../models/ContactCustomField"); + +const { getIO } = require("../libs/socket"); +const { getWbot } = require("../libs/wbot"); + +exports.index = async (req, res) => { + const { searchParam = "", pageNumber = 1 } = req.query; + + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + "%" + searchParam.toLowerCase() + "%" + ) + }, + { number: { [Op.like]: `%${searchParam}%` } } + ] + }; + + let limit = 20; + let offset = limit * (pageNumber - 1); + + const { count, rows: contacts } = await Contact.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + contacts.length; + + return res.json({ contacts, count, hasMore }); +}; + +exports.store = async (req, res) => { + const defaultWhatsapp = await Whatsapp.findOne({ + where: { default: true } + }); + + if (!defaultWhatsapp) { + return res + .status(404) + .json({ error: "No default WhatsApp found. Check Connection page." }); + } + + const wbot = getWbot(defaultWhatsapp); + const io = getIO(); + const newContact = req.body; + + try { + const isValidNumber = await wbot.isRegisteredUser( + `${newContact.number}@c.us` + ); + if (!isValidNumber) { + return res + .status(400) + .json({ error: "The suplied number is not a valid Whatsapp number" }); + } + } catch (err) { + console.log(err); + return res.status(500).json({ + error: "Could not check whatsapp contact. Check connection page." + }); + } + + const profilePicUrl = await wbot.getProfilePicUrl( + `${newContact.number}@c.us` + ); + + const contact = await Contact.create( + { ...newContact, profilePicUrl }, + { + include: "extraInfo" + } + ); + + io.emit("contact", { + action: "create", + contact: contact + }); + + return res.status(200).json(contact); +}; + +exports.show = async (req, res) => { + const { contactId } = req.params; + + const contact = await Contact.findByPk(contactId, { + include: "extraInfo", + attributes: ["id", "name", "number", "email"] + }); + + if (!contact) { + return res.status(404).json({ error: "No contact found with this id." }); + } + + return res.status(200).json(contact); +}; + +exports.update = async (req, res) => { + const io = getIO(); + + const updatedContact = req.body; + + const { contactId } = req.params; + + const contact = await Contact.findByPk(contactId, { + include: "extraInfo" + }); + + if (!contact) { + return res.status(404).json({ error: "No contact found with this ID" }); + } + + if (updatedContact.extraInfo) { + await Promise.all( + updatedContact.extraInfo.map(async info => { + await ContactCustomField.upsert({ ...info, contactId: contact.id }); + }) + ); + + await Promise.all( + contact.extraInfo.map(async oldInfo => { + let stillExists = updatedContact.extraInfo.findIndex( + info => info.id === oldInfo.id + ); + + if (stillExists === -1) { + await ContactCustomField.destroy({ where: { id: oldInfo.id } }); + } + }) + ); + } + + await contact.update(updatedContact); + + io.emit("contact", { + action: "update", + contact: contact + }); + + return res.status(200).json(contact); +}; + +exports.delete = async (req, res) => { + const io = getIO(); + const { contactId } = req.params; + + const contact = await Contact.findByPk(contactId); + + if (!contact) { + return res.status(404).json({ error: "No contact found with this ID" }); + } + + await contact.destroy(); + + io.emit("contact", { + action: "delete", + contactId: contactId + }); + + return res.status(200).json({ message: "Contact deleted" }); +}; diff --git a/backend/src/services/ContactServices/CreateContactService.ts b/backend/src/services/ContactServices/CreateContactService.ts index 1d11ca5..3c821aa 100644 --- a/backend/src/services/ContactServices/CreateContactService.ts +++ b/backend/src/services/ContactServices/CreateContactService.ts @@ -1,16 +1,23 @@ import AppError from "../../errors/AppError"; import Contact from "../../models/Contact"; +interface ExtraInfo { + name: string; + value: string; +} + interface Request { name: string; number: string; email?: string; + extraInfo?: ExtraInfo[]; } const CreateContactService = async ({ name, number, - email + email, + extraInfo }: Request): Promise => { const numberExists = await Contact.findOne({ where: { number } @@ -20,11 +27,17 @@ const CreateContactService = async ({ throw new AppError("A contact with this number already exists."); } - const contact = await Contact.create({ - name, - number, - email - }); + const contact = await Contact.create( + { + name, + number, + email, + extraInfo + }, + { + include: ["extraInfo"] + } + ); return contact; }; diff --git a/backend/src/services/ContactServices/UpdateContactService.ts b/backend/src/services/ContactServices/UpdateContactService.ts index 5a001cd..18d5bc2 100644 --- a/backend/src/services/ContactServices/UpdateContactService.ts +++ b/backend/src/services/ContactServices/UpdateContactService.ts @@ -1,10 +1,17 @@ import AppError from "../../errors/AppError"; import Contact from "../../models/Contact"; +import ContactCustomField from "../../models/ContactCustomField"; +interface ExtraInfo { + id?: number; + name: string; + value: string; +} interface ContactData { email?: string; number?: string; name?: string; + extraInfo?: ExtraInfo[]; } interface Request { @@ -16,21 +23,41 @@ const UpdateContactService = async ({ contactData, contactId }: Request): Promise => { - const { email, name, number } = contactData; + const { email, name, number, extraInfo } = contactData; const contact = await Contact.findOne({ where: { id: contactId }, - attributes: ["id", "name", "number", "email"] + attributes: ["id", "name", "number", "email"], + include: ["extraInfo"] }); if (!contact) { throw new AppError("No contact found with this ID.", 404); } + if (extraInfo) { + await Promise.all( + extraInfo.map(async info => { + await ContactCustomField.upsert({ ...info, contactId: contact.id }); + }) + ); + + await Promise.all( + contact.extraInfo.map(async oldInfo => { + const stillExists = extraInfo.findIndex(info => info.id === oldInfo.id); + + if (stillExists === -1) { + await ContactCustomField.destroy({ where: { id: oldInfo.id } }); + } + }) + ); + } + await contact.update({ name, number, - email + email, + extraInfo }); return contact; From 9368ef25126fa30ed2da1182a408c3b766b89300 Mon Sep 17 00:00:00 2001 From: canove Date: Sat, 19 Sep 2020 22:49:27 -0300 Subject: [PATCH 24/54] started messages migration to ts --- backend/src/controllers/MessageController.ts | 353 +++++++++--------- backend/src/controllers/TicketController.ts | 25 +- backend/src/controllers/UserController.ts | 4 +- backend/src/controllers/WhatsAppController.ts | 2 +- backend/src/routes/messageRoutes.ts | 8 +- .../ContactServices/ListContactsService.ts | 11 - .../MessageServices/CreateMessageService.ts | 24 ++ .../MessageServices/ListMessagesService.ts | 60 +++ .../TicketServices/FindTicketService.ts | 35 +- .../TicketServices/ListTicketsService.ts | 3 +- 10 files changed, 295 insertions(+), 230 deletions(-) create mode 100644 backend/src/services/MessageServices/CreateMessageService.ts create mode 100644 backend/src/services/MessageServices/ListMessagesService.ts diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts index 30c0975..c492e13 100644 --- a/backend/src/controllers/MessageController.ts +++ b/backend/src/controllers/MessageController.ts @@ -1,203 +1,188 @@ -// const Message = require("../models/Message"); -// const Contact = require("../models/Contact"); -// const User = require("../models/User"); -// const Whatsapp = require("../models/Whatsapp"); +import { Request, Response } from "express"; +import CreateMessageService from "../services/MessageServices/CreateMessageService"; +import ListMessagesService from "../services/MessageServices/ListMessagesService"; +// import Sequelize from "sequelize"; +// import { MessageMedia } from "whatsapp-web.js"; +// import Message from "../models/Message"; +// import Contact from "../models/Contact"; +// import User from "../models/User"; +// import Whatsapp from "../models/Whatsapp"; -// const Ticket = require("../models/Ticket"); -// const { getIO } = require("../libs/socket"); -// const { getWbot } = require("../libs/wbot"); -// const Sequelize = require("sequelize"); - -// const { MessageMedia } = require("whatsapp-web.js"); +// import Ticket from "../models/Ticket"; +// import { getIO } from "../libs/socket"; +// import { getWbot } from "../libs/wbot"; // const setMessagesAsRead = async ticket => { -// const io = getIO(); -// const wbot = getWbot(ticket.whatsappId); +// const io = getIO(); +// const wbot = getWbot(ticket.whatsappId); -// await Message.update( -// { read: true }, -// { -// where: { -// ticketId: ticket.id, -// read: false, -// }, -// } -// ); +// await Message.update( +// { read: true }, +// { +// where: { +// ticketId: ticket.id, +// read: false +// } +// } +// ); -// try { -// await wbot.sendSeen(`${ticket.contact.number}@c.us`); -// } catch (err) { -// console.log( -// "Could not mark messages as read. Maybe whatsapp session disconnected?" -// ); -// } +// try { +// await wbot.sendSeen(`${ticket.contact.number}@c.us`); +// } catch (err) { +// console.log( +// "Could not mark messages as read. Maybe whatsapp session disconnected?" +// ); +// } -// io.to("notification").emit("ticket", { -// action: "updateUnread", -// ticketId: ticket.id, -// }); +// io.to("notification").emit("ticket", { +// action: "updateUnread", +// ticketId: ticket.id +// }); // }; -// exports.index = async (req, res, next) => { -// const { ticketId } = req.params; -// const { searchParam = "", pageNumber = 1 } = req.query; +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; -// const whereCondition = { -// body: Sequelize.where( -// Sequelize.fn("LOWER", Sequelize.col("body")), -// "LIKE", -// "%" + searchParam.toLowerCase() + "%" -// ), -// }; +export const index = async (req: Request, res: Response): Promise => { + const { ticketId } = req.params; + const { searchParam, pageNumber } = req.query as IndexQuery; -// const limit = 20; -// const offset = limit * (pageNumber - 1); + const { count, messages, ticket, hasMore } = await ListMessagesService({ + searchParam, + pageNumber, + ticketId + }); -// const ticket = await Ticket.findByPk(ticketId, { -// include: [ -// { -// model: Contact, -// as: "contact", -// include: "extraInfo", -// attributes: ["id", "name", "number", "profilePicUrl"], -// }, -// { -// model: User, -// as: "user", -// }, -// ], -// }); + // const ticketMessages = await ticket.getMessages({ + // where: whereCondition, + // limit, + // offset, + // order: [["createdAt", "DESC"]] + // }); -// if (!ticket) { -// return res.status(404).json({ error: "No ticket found with this ID" }); -// } + // const count = await ticket.countMessages(); -// await setMessagesAsRead(ticket); + // const serializedMessages = ticketMessages.map(message => { + // return { + // ...message.dataValues, + // mediaUrl: `${ + // message.mediaUrl + // ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` + // : "" + // }` + // }; + // }); -// const ticketMessages = await ticket.getMessages({ -// where: whereCondition, -// limit, -// offset, -// order: [["createdAt", "DESC"]], -// }); + // const hasMore = count > offset + ticketMessages.length; -// const count = await ticket.countMessages(); + // return res.json({ + // messages: serializedMessages.reverse(), + // ticket, + // count, + // hasMore + // }); + return res.json({ count, messages, ticket, hasMore }); +}; -// const serializedMessages = ticketMessages.map(message => { -// return { -// ...message.dataValues, -// mediaUrl: `${ -// message.mediaUrl -// ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` -// : "" -// }`, -// }; -// }); +export const store = async (req: Request, res: Response): Promise => { + // const io = getIO(); -// const hasMore = count > offset + ticketMessages.length; + const { ticketId } = req.params; + const messageData = req.body; -// return res.json({ -// messages: serializedMessages.reverse(), -// ticket, -// count, -// hasMore, -// }); -// }; - -// exports.store = async (req, res, next) => { -// const io = getIO(); - -// const { ticketId } = req.params; -// const message = req.body; -// const media = req.file; -// let sentMessage; - -// const ticket = await Ticket.findByPk(ticketId, { -// include: [ -// { -// model: Contact, -// as: "contact", -// attributes: ["number", "name", "profilePicUrl"], -// }, -// ], -// }); - -// if (!ticket) { -// return res.status(404).json({ error: "No ticket found with this ID" }); -// } - -// if (!ticket.whatsappId) { -// const defaultWhatsapp = await Whatsapp.findOne({ -// where: { default: true }, -// }); - -// if (!defaultWhatsapp) { -// return res -// .status(404) -// .json({ error: "No default WhatsApp found. Check Connection page." }); -// } - -// await ticket.setWhatsapp(defaultWhatsapp); -// } - -// const wbot = getWbot(ticket.whatsappId); - -// try { -// if (media) { -// const newMedia = MessageMedia.fromFilePath(req.file.path); - -// message.mediaUrl = req.file.filename; -// if (newMedia.mimetype) { -// message.mediaType = newMedia.mimetype.split("/")[0]; -// } else { -// message.mediaType = "other"; -// } - -// sentMessage = await wbot.sendMessage( -// `${ticket.contact.number}@c.us`, -// newMedia -// ); - -// await ticket.update({ lastMessage: message.mediaUrl }); -// } else { -// sentMessage = await wbot.sendMessage( -// `${ticket.contact.number}@c.us`, -// message.body -// ); -// await ticket.update({ lastMessage: message.body }); -// } -// } catch (err) { -// console.log( -// "Could not create whatsapp message. Is session details valid? " -// ); -// } - -// if (sentMessage) { -// message.id = sentMessage.id.id; -// const newMessage = await ticket.createMessage(message); - -// const serialziedMessage = { -// ...newMessage.dataValues, -// mediaUrl: `${ -// message.mediaUrl -// ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` -// : "" -// }`, -// }; - -// io.to(ticketId).to("notification").emit("appMessage", { -// action: "create", -// message: serialziedMessage, -// ticket: ticket, -// contact: ticket.contact, -// }); - -// await setMessagesAsRead(ticket); - -// return res.status(200).json({ newMessage, ticket }); -// } - -// return res -// .status(500) -// .json({ error: "Cannot sent whatsapp message. Check connection page." }); + const message = await CreateMessageService({ messageData, ticketId }); + + // const media = req.file; + // let sentMessage; + + // const ticket = await Ticket.findByPk(ticketId, { + // include: [ + // { + // model: Contact, + // as: "contact", + // attributes: ["number", "name", "profilePicUrl"] + // } + // ] + // }); + + // if (!ticket) { + // return res.status(404).json({ error: "No ticket found with this ID" }); + // } + + // if (!ticket.whatsappId) { + // const defaultWhatsapp = await Whatsapp.findOne({ + // where: { default: true } + // }); + + // if (!defaultWhatsapp) { + // return res + // .status(404) + // .json({ error: "No default WhatsApp found. Check Connection page." }); + // } + + // await ticket.setWhatsapp(defaultWhatsapp); + // } + + // const wbot = getWbot(ticket.whatsappId); + + // try { + // if (media) { + // const newMedia = MessageMedia.fromFilePath(req.file.path); + + // message.mediaUrl = req.file.filename; + // if (newMedia.mimetype) { + // message.mediaType = newMedia.mimetype.split("/")[0]; + // } else { + // message.mediaType = "other"; + // } + + // sentMessage = await wbot.sendMessage( + // `${ticket.contact.number}@c.us`, + // newMedia + // ); + + // await ticket.update({ lastMessage: message.mediaUrl }); + // } else { + // sentMessage = await wbot.sendMessage( + // `${ticket.contact.number}@c.us`, + // message.body + // ); + // await ticket.update({ lastMessage: message.body }); + // } + // } catch (err) { + // console.log( + // "Could not create whatsapp message. Is session details valid? " + // ); + // } + + // if (sentMessage) { + // message.id = sentMessage.id.id; + // const newMessage = await ticket.createMessage(message); + + // const serialziedMessage = { + // ...newMessage.dataValues, + // mediaUrl: `${ + // message.mediaUrl + // ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` + // : "" + // }` + // }; + + // io.to(ticketId).to("notification").emit("appMessage", { + // action: "create", + // message: serialziedMessage, + // ticket, + // contact: ticket.contact + // }); + + // await setMessagesAsRead(ticket); + + return res.status(200).json(message); +}; + +// return res +// .status(500) +// .json({ error: "Cannot sent whatsapp message. Check connection page." }); // }; diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index bad73ae..7684d47 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -1,23 +1,12 @@ import { Request, Response } from "express"; -import GetDefaultWhatsapp from "../helpers/GetDefaultWhatsapp"; -import Ticket from "../models/Ticket"; import CreateTicketService from "../services/TicketServices/CreateTicketService"; import DeleteTicketService from "../services/TicketServices/DeleteTicketService"; import ListTicketsService from "../services/TicketServices/ListTicketsService"; import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; -// const Sequelize = require("sequelize"); -// const { startOfDay, endOfDay, parseISO } = require("date-fns"); - -// const Ticket = require("../models/Ticket"); -// const Contact = require("../models/Contact"); -// const Message = require("../models/Message"); -// const Whatsapp = require("../models/Whatsapp"); // const { getIO } = require("../libs/socket"); -// import FindWhatsAppService from "../services/WhatsappService/FindWhatsAppService"; - -type RequestQuery = { +type IndexQuery = { searchParam: string; pageNumber: string; status: string; @@ -25,6 +14,11 @@ type RequestQuery = { showAll: string; }; +interface TicketData { + contactId: number; + status: string; +} + export const index = async (req: Request, res: Response): Promise => { const { pageNumber, @@ -32,7 +26,7 @@ export const index = async (req: Request, res: Response): Promise => { date, searchParam, showAll - } = req.query as RequestQuery; + } = req.query as IndexQuery; const userId = req.user.id; @@ -96,11 +90,6 @@ export const index = async (req: Request, res: Response): Promise => { return res.status(200).json({ tickets, count, hasMore }); }; -interface TicketData { - contactId: number; - status: string; -} - export const store = async (req: Request, res: Response): Promise => { const { contactId, status }: TicketData = req.body; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 14c0401..521a6eb 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -9,7 +9,7 @@ import UpdateUserService from "../services/UserServices/UpdateUserService"; import FindUserService from "../services/UserServices/FindUserService"; import DeleteUserService from "../services/UserServices/DeleteUserService"; -type RequestQuery = { +type IndexQuery = { searchParam: string; pageNumber: string; }; @@ -18,7 +18,7 @@ export const index = async (req: Request, res: Response): Promise => { if (req.user.profile !== "admin") { throw new AppError("Only administrators can access this route.", 403); // should be handled better. } - const { searchParam, pageNumber } = req.query as RequestQuery; + const { searchParam, pageNumber } = req.query as IndexQuery; const { users, count, hasMore } = await ListUsersService({ searchParam, diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 9dca11f..38d1c8f 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -53,7 +53,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { whatsappId } = req.params; - const whatsapp = await FindWhatsAppService(whatsappId); + const whatsapp = await FindWhatsAppService({ where: { id: +whatsappId } }); return res.status(200).json(whatsapp); }; diff --git a/backend/src/routes/messageRoutes.ts b/backend/src/routes/messageRoutes.ts index 59d81db..808ac6d 100644 --- a/backend/src/routes/messageRoutes.ts +++ b/backend/src/routes/messageRoutes.ts @@ -1,12 +1,12 @@ import { Router } from "express"; -// import isAuth from "../middleware/isAuth"; +import isAuth from "../middleware/isAuth"; -// import { index, store } from "../controllers/MessageController"; +import * as MessageController from "../controllers/MessageController"; const messageRoutes = Router(); -// messageRoutes.get("/messages/:ticketId", isAuth, index); +messageRoutes.get("/messages/:ticketId", isAuth, MessageController.index); -// messageRoutes.post("/messages/:ticketId", isAuth, store); +messageRoutes.post("/messages/:ticketId", isAuth, MessageController.store); export default messageRoutes; diff --git a/backend/src/services/ContactServices/ListContactsService.ts b/backend/src/services/ContactServices/ListContactsService.ts index f0ab9c9..52c7f8a 100644 --- a/backend/src/services/ContactServices/ListContactsService.ts +++ b/backend/src/services/ContactServices/ListContactsService.ts @@ -16,16 +16,6 @@ const ListContactsService = async ({ searchParam = "", pageNumber = "1" }: Request): Promise => { - let includeCondition = [ - { - model: Contact, - as: "contact", - attributes: ["name", "number", "profilePicUrl"] - } - ]; - - // let whereCondition = {}; - const whereCondition = { [Op.or]: [ { @@ -43,7 +33,6 @@ const ListContactsService = async ({ const { count, rows: contacts } = await Contact.findAndCountAll({ where: whereCondition, - include: includeCondition, limit, offset, order: [["createdAt", "DESC"]] diff --git a/backend/src/services/MessageServices/CreateMessageService.ts b/backend/src/services/MessageServices/CreateMessageService.ts new file mode 100644 index 0000000..4a6c59e --- /dev/null +++ b/backend/src/services/MessageServices/CreateMessageService.ts @@ -0,0 +1,24 @@ +import Message from "../../models/Message"; +import FindTicketService from "../TicketServices/FindTicketService"; + +interface Request { + ticketId: string; + messageData: Message; +} + +const CreateMessageService = async ({ + messageData, + ticketId +}: Request): Promise => { + const ticket = await FindTicketService({ where: { id: +ticketId } }); + + if (!ticket) { + throw new Error("No ticket found with this ID"); + } + + const message = Message.create({ ...messageData, ticketId }); + + return message; +}; + +export default CreateMessageService; diff --git a/backend/src/services/MessageServices/ListMessagesService.ts b/backend/src/services/MessageServices/ListMessagesService.ts new file mode 100644 index 0000000..a4bc52c --- /dev/null +++ b/backend/src/services/MessageServices/ListMessagesService.ts @@ -0,0 +1,60 @@ +import { where, fn, col } from "sequelize"; +import Message from "../../models/Message"; +import Ticket from "../../models/Ticket"; +import FindTicketService from "../TicketServices/FindTicketService"; + +interface Request { + ticketId: string; + searchParam?: string; + pageNumber?: string; +} + +interface Response { + messages: Message[]; + ticket: Ticket; + count: number; + hasMore: boolean; +} + +const ListMessagesService = async ({ + searchParam = "", + pageNumber = "1", + ticketId +}: Request): Promise => { + const whereCondition = { + body: where( + fn("LOWER", col("body")), + "LIKE", + `%${searchParam.toLowerCase()}%` + ), + ticketId + }; + + const ticket = await FindTicketService({ where: { id: +ticketId } }); + + if (!ticket) { + throw new Error("No ticket found with this ID"); + } + + // await setMessagesAsRead(ticket); + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: messages } = await Message.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]] + }); + + const hasMore = count > offset + messages.length; + + return { + messages: messages.reverse(), + ticket, + count, + hasMore + }; +}; + +export default ListMessagesService; diff --git a/backend/src/services/TicketServices/FindTicketService.ts b/backend/src/services/TicketServices/FindTicketService.ts index 4846f61..6490ebd 100644 --- a/backend/src/services/TicketServices/FindTicketService.ts +++ b/backend/src/services/TicketServices/FindTicketService.ts @@ -1,28 +1,45 @@ -import Whatsapp from "../../models/Whatsapp"; +import Ticket from "../../models/Ticket"; import AppError from "../../errors/AppError"; +import Contact from "../../models/Contact"; +import User from "../../models/User"; interface WhereParams { id?: number; - name?: string; - isDefault?: boolean; + status?: string; + userId?: number; + contactId?: number; + whatsappId?: number; } interface Request { where?: WhereParams; } -const FindTicketService = async ({ where }: Request): Promise => { +const FindTicketService = async ({ where }: Request): Promise => { const whereCondition = { ...where }; - const whatsapp = await Whatsapp.findOne({ - where: whereCondition + const ticket = await Ticket.findOne({ + where: whereCondition, + include: [ + { + model: Contact, + as: "contact", + attributes: ["id", "name", "number", "profilePicUrl"], + include: ["extraInfo"] + }, + { + model: User, + as: "user", + attributes: ["id", "name"] + } + ] }); - if (!whatsapp) { - throw new AppError("No whatsapp found with this conditions.", 404); + if (!ticket) { + throw new AppError("No ticket found with this conditions.", 404); } - return whatsapp; + return ticket; }; export default FindTicketService; diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts index 5b5d402..ece7f91 100644 --- a/backend/src/services/TicketServices/ListTicketsService.ts +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -35,7 +35,8 @@ const ListTicketsService = async ({ { model: Contact, as: "contact", - attributes: ["name", "number", "profilePicUrl"] + attributes: ["id", "name", "number", "profilePicUrl"], + include: ["extraInfo"] } ]; From 04750df1729cbf2a0d69c447103558e5f583a443 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 09:18:17 -0300 Subject: [PATCH 25/54] add upload config --- backend/src/config/upload.ts | 16 ++++++++++++++++ backend/src/server.ts | 30 +++++++++++------------------- 2 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 backend/src/config/upload.ts diff --git a/backend/src/config/upload.ts b/backend/src/config/upload.ts new file mode 100644 index 0000000..24fb8f8 --- /dev/null +++ b/backend/src/config/upload.ts @@ -0,0 +1,16 @@ +import path from "path"; +import multer from "multer"; + +const publicFolder = path.resolve(__dirname, "..", "..", "public"); +export default { + directory: publicFolder, + + storage: multer.diskStorage({ + destination: publicFolder, + filename(req, file, cb) { + const fileName = new Date().getTime() + path.extname(file.originalname); + + return cb(null, fileName); + } + }) +}; diff --git a/backend/src/server.ts b/backend/src/server.ts index a8d6c21..21fbd79 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,25 +1,33 @@ +import "reflect-metadata"; import "dotenv/config"; import "express-async-errors"; import express, { Request, Response, NextFunction } from "express"; import cors from "cors"; -import AppError from "./errors/AppError"; +import multer from "multer"; +import Sentry from "@sentry/node"; +import uploadConfig from "./config/upload"; +import AppError from "./errors/AppError"; import routes from "./routes"; import "./database"; // import path from "path"; -// import multer from "multer"; -// import Sentry from "@sentry/node"; // const { initWbot } = require("./libs/wbot"); // const wbotMessageListener = require("./services/wbotMessageListener"); // const wbotMonitor = require("./services/wbotMonitor"); // const Whatsapp = require("./models/Whatsapp"); +Sentry.init({ dsn: process.env.SENTRY_DSN }); + +const upload = multer(uploadConfig); const app = express(); app.use(cors()); app.use(express.json()); +app.use(Sentry.Handlers.requestHandler()); +app.use(upload.single("media")); +app.use("/public", express.static(uploadConfig.directory)); app.use(routes); app.use(async (err: Error, req: Request, res: Response, _: NextFunction) => { @@ -35,22 +43,6 @@ const server = app.listen(process.env.PORT, () => { console.log(`Server started on port: ${process.env.PORT}`); }); -// Sentry.init({ dsn: process.env.SENTRY_DSN }); - -// const fileStorage = multer.diskStorage({ -// destination: (req, file, cb) => { -// cb(null, path.resolve(__dirname, "..", "public")); -// }, -// filename: (req, file, cb) => { -// cb(null, new Date().getTime() + path.extname(file.originalname)); -// }, -// }); - -// app.use(Sentry.Handlers.requestHandler()); -// app.use(multer({ storage: fileStorage }).single("media")); -// app.use("/public", express.static(path.join(__dirname, "..", "public"))); -// app.use(Router); - // const io = require("./libs/socket").init(server); // io.on("connection", socket => { // console.log("Client Connected"); From b7484afd63d6eacf8643fbd32cc717fbda4933bb Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 10:45:49 -0300 Subject: [PATCH 26/54] migrated socket IO initialization to typescript --- backend/package.json | 1 + backend/src/libs/socket.js | 14 -------------- backend/src/libs/socket.ts | 15 +++++++++++++++ backend/src/server.ts | 33 +++++++++++++++++---------------- backend/yarn.lock | 15 +++++++++++++++ 5 files changed, 48 insertions(+), 30 deletions(-) delete mode 100644 backend/src/libs/socket.js create mode 100644 backend/src/libs/socket.ts diff --git a/backend/package.json b/backend/package.json index 6719105..4cecba0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -38,6 +38,7 @@ "@types/jsonwebtoken": "^8.5.0", "@types/multer": "^1.4.4", "@types/node": "^14.10.1", + "@types/socket.io": "^2.1.11", "@types/validator": "^13.1.0", "@types/yup": "^0.29.7", "@typescript-eslint/eslint-plugin": "^4.1.0", diff --git a/backend/src/libs/socket.js b/backend/src/libs/socket.js deleted file mode 100644 index 5e3b8c7..0000000 --- a/backend/src/libs/socket.js +++ /dev/null @@ -1,14 +0,0 @@ -let io; - -module.exports = { - init: httpServer => { - io = require("socket.io")(httpServer); - return io; - }, - getIO: () => { - if (!io) { - throw new Error("Socket IO not initialized"); - } - return io; - }, -}; diff --git a/backend/src/libs/socket.ts b/backend/src/libs/socket.ts new file mode 100644 index 0000000..ec82552 --- /dev/null +++ b/backend/src/libs/socket.ts @@ -0,0 +1,15 @@ +import socketIo, { Server as SocketIO } from "socket.io"; +import { Server } from "http"; + +let io: SocketIO; + +export const initIO = (httpServer: Server): SocketIO => { + io = socketIo(httpServer); + return io; +}; +export const getIO = (): SocketIO => { + if (!io) { + throw new Error("Socket IO not initialized"); + } + return io; +}; diff --git a/backend/src/server.ts b/backend/src/server.ts index 21fbd79..93519f2 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -4,11 +4,12 @@ import "express-async-errors"; import express, { Request, Response, NextFunction } from "express"; import cors from "cors"; import multer from "multer"; -import Sentry from "@sentry/node"; +import * as Sentry from "@sentry/node"; import uploadConfig from "./config/upload"; import AppError from "./errors/AppError"; import routes from "./routes"; +import { initIO } from "./libs/socket"; import "./database"; // import path from "path"; @@ -43,23 +44,23 @@ const server = app.listen(process.env.PORT, () => { console.log(`Server started on port: ${process.env.PORT}`); }); -// const io = require("./libs/socket").init(server); -// io.on("connection", socket => { -// console.log("Client Connected"); -// socket.on("joinChatBox", ticketId => { -// console.log("A client joined a ticket channel"); -// socket.join(ticketId); -// }); +const io = initIO(server); +io.on("connection", socket => { + console.log("Client Connected"); + socket.on("joinChatBox", ticketId => { + console.log("A client joined a ticket channel"); + socket.join(ticketId); + }); -// socket.on("joinNotification", () => { -// console.log("A client joined notification channel"); -// socket.join("notification"); -// }); + socket.on("joinNotification", () => { + console.log("A client joined notification channel"); + socket.join("notification"); + }); -// socket.on("disconnect", () => { -// console.log("Client disconnected"); -// }); -// }); + socket.on("disconnect", () => { + console.log("Client disconnected"); + }); +}); // const startWhatsAppSessions = async () => { // const whatsapps = await Whatsapp.findAll(); diff --git a/backend/yarn.lock b/backend/yarn.lock index b3bac1c..012bfb5 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -189,6 +189,13 @@ dependencies: "@types/express" "*" +"@types/engine.io@*": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/engine.io/-/engine.io-3.1.4.tgz#3d9472711d179daa7c95c051e50ad411e18a9bdc" + integrity sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w== + dependencies: + "@types/node" "*" + "@types/express-serve-static-core@*": version "4.17.12" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz#9a487da757425e4f267e7d1c5720226af7f89591" @@ -265,6 +272,14 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" +"@types/socket.io@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-2.1.11.tgz#e0d6759880e5f9818d5297a3328b36641bae996b" + integrity sha512-bVprmqPhJMLb9ZCm8g0Xy8kwBFRbnanOWSxzWkDkkIwxTvud5tKMfAJymXX6LQbizUKCS1yima7JM4BeLqjNqA== + dependencies: + "@types/engine.io" "*" + "@types/node" "*" + "@types/strip-bom@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" From aa6f1165148a4a893e987c15dfc54c98d0553c4e Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 10:52:05 -0300 Subject: [PATCH 27/54] use appError on socket io --- backend/src/libs/socket.ts | 3 +- backend/src/libs/wbot.js | 220 ++++++++++++++++++------------------- 2 files changed, 112 insertions(+), 111 deletions(-) diff --git a/backend/src/libs/socket.ts b/backend/src/libs/socket.ts index ec82552..cf5bcbf 100644 --- a/backend/src/libs/socket.ts +++ b/backend/src/libs/socket.ts @@ -1,5 +1,6 @@ import socketIo, { Server as SocketIO } from "socket.io"; import { Server } from "http"; +import AppError from "../errors/AppError"; let io: SocketIO; @@ -9,7 +10,7 @@ export const initIO = (httpServer: Server): SocketIO => { }; export const getIO = (): SocketIO => { if (!io) { - throw new Error("Socket IO not initialized"); + throw new AppError("Socket IO not initialized"); } return io; }; diff --git a/backend/src/libs/wbot.js b/backend/src/libs/wbot.js index 86ee3a0..6ba443d 100644 --- a/backend/src/libs/wbot.js +++ b/backend/src/libs/wbot.js @@ -1,110 +1,110 @@ -const qrCode = require("qrcode-terminal"); -const { Client } = require("whatsapp-web.js"); -const Whatsapp = require("../models/Whatsapp"); -const { getIO } = require("../libs/socket"); - -let sessions = []; - -module.exports = { - initWbot: async whatsapp => { - try { - const io = getIO(); - const sessionName = whatsapp.name; - let sessionCfg; - - if (whatsapp && whatsapp.session) { - sessionCfg = JSON.parse(whatsapp.session); - } - - const sessionIndex = sessions.findIndex(s => s.id === whatsapp.id); - if (sessionIndex !== -1) { - sessions[sessionIndex].destroy(); - sessions.splice(sessionIndex, 1); - } - - const wbot = new Client({ - session: sessionCfg, - restartOnAuthFail: true, - }); - wbot.initialize(); - - wbot.on("qr", async qr => { - console.log("Session:", sessionName); - - qrCode.generate(qr, { small: true }); - - await whatsapp.update({ qrcode: qr, status: "qrcode" }); - - io.emit("whatsappSession", { - action: "update", - session: whatsapp, - }); - }); - - wbot.on("authenticated", async session => { - console.log("Session:", sessionName, "AUTHENTICATED"); - - await whatsapp.update({ - session: JSON.stringify(session), - status: "authenticated", - }); - - io.emit("whatsappSession", { - action: "update", - session: whatsapp, - }); - }); - - wbot.on("auth_failure", async msg => { - console.error("Session:", sessionName, "AUTHENTICATION FAILURE", msg); - - await whatsapp.update({ session: "" }); - }); - - wbot.on("ready", async () => { - console.log("Session:", sessionName, "READY"); - - await whatsapp.update({ - status: "CONNECTED", - qrcode: "", - }); - - io.emit("whatsappSession", { - action: "update", - session: whatsapp, - }); - - wbot.sendPresenceAvailable(); - }); - - wbot.id = whatsapp.id; - sessions.push(wbot); - } catch (err) { - console.log(err); - } - - return null; - }, - - getWbot: whatsappId => { - const sessionIndex = sessions.findIndex(s => s.id === whatsappId); - - if (sessionIndex === -1) { - console.log("This Wbot session is not initialized"); - return null; - } - return sessions[sessionIndex]; - }, - - removeWbot: whatsappId => { - try { - const sessionIndex = sessions.findIndex(s => s.id === whatsappId); - if (sessionIndex !== -1) { - sessions[sessionIndex].destroy(); - sessions.splice(sessionIndex, 1); - } - } catch (err) { - console.log(err); - } - }, -}; +const qrCode = require("qrcode-terminal"); +const { Client } = require("whatsapp-web.js"); +const Whatsapp = require("../models/Whatsapp"); +const { getIO } = require("../libs/socket"); + +let sessions = []; + +module.exports = { + initWbot: async whatsapp => { + try { + const io = getIO(); + const sessionName = whatsapp.name; + let sessionCfg; + + if (whatsapp && whatsapp.session) { + sessionCfg = JSON.parse(whatsapp.session); + } + + const sessionIndex = sessions.findIndex(s => s.id === whatsapp.id); + if (sessionIndex !== -1) { + sessions[sessionIndex].destroy(); + sessions.splice(sessionIndex, 1); + } + + const wbot = new Client({ + session: sessionCfg, + restartOnAuthFail: true + }); + wbot.initialize(); + + wbot.on("qr", async qr => { + console.log("Session:", sessionName); + + qrCode.generate(qr, { small: true }); + + await whatsapp.update({ qrcode: qr, status: "qrcode" }); + + io.emit("whatsappSession", { + action: "update", + session: whatsapp + }); + }); + + wbot.on("authenticated", async session => { + console.log("Session:", sessionName, "AUTHENTICATED"); + + await whatsapp.update({ + session: JSON.stringify(session), + status: "authenticated" + }); + + io.emit("whatsappSession", { + action: "update", + session: whatsapp + }); + }); + + wbot.on("auth_failure", async msg => { + console.error("Session:", sessionName, "AUTHENTICATION FAILURE", msg); + + await whatsapp.update({ session: "" }); + }); + + wbot.on("ready", async () => { + console.log("Session:", sessionName, "READY"); + + await whatsapp.update({ + status: "CONNECTED", + qrcode: "" + }); + + io.emit("whatsappSession", { + action: "update", + session: whatsapp + }); + + wbot.sendPresenceAvailable(); + }); + + wbot.id = whatsapp.id; + sessions.push(wbot); + } catch (err) { + console.log(err); + } + + return null; + }, + + getWbot: whatsappId => { + const sessionIndex = sessions.findIndex(s => s.id === whatsappId); + + if (sessionIndex === -1) { + console.log("This Wbot session is not initialized"); + return null; + } + return sessions[sessionIndex]; + }, + + removeWbot: whatsappId => { + try { + const sessionIndex = sessions.findIndex(s => s.id === whatsappId); + if (sessionIndex !== -1) { + sessions[sessionIndex].destroy(); + sessions.splice(sessionIndex, 1); + } + } catch (err) { + console.log(err); + } + } +}; From 0f0d9470c5714f69af3fa47570b09729e356246e Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 11:12:32 -0300 Subject: [PATCH 28/54] change 'default' to 'isDefault' whatsapp field on frontend --- backend/src/controllers/WhatsAppController.ts | 16 ++++--- .../WhatsappService/FindWhatsAppService.ts | 22 ++------- .../WhatsappService/ListWhatsAppsService.ts | 8 +--- .../src/components/WhatsAppModal/index.js | 2 +- frontend/src/pages/Connections/index.js | 46 +++++++++---------- 5 files changed, 39 insertions(+), 55 deletions(-) diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 38d1c8f..ff24f29 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -12,18 +12,18 @@ import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppSer // import wbotMessageListener from "../services/wbotMessageListener"; // import wbotMonitor from "../services/wbotMonitor"; -export const index = async (req: Request, res: Response): Promise => { - const whatsapps = await ListWhatsAppsService(); - - return res.status(200).json(whatsapps); -}; - interface WhatsappData { name: string; status?: string; isDefault?: boolean; } +export const index = async (req: Request, res: Response): Promise => { + const whatsapps = await ListWhatsAppsService(); + + return res.status(200).json(whatsapps); +}; + export const store = async (req: Request, res: Response): Promise => { // const io = getIO(); @@ -53,7 +53,9 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { whatsappId } = req.params; - const whatsapp = await FindWhatsAppService({ where: { id: +whatsappId } }); + console.log(whatsappId); + + const whatsapp = await FindWhatsAppService(whatsappId); return res.status(200).json(whatsapp); }; diff --git a/backend/src/services/WhatsappService/FindWhatsAppService.ts b/backend/src/services/WhatsappService/FindWhatsAppService.ts index 1f0d893..741b9a8 100644 --- a/backend/src/services/WhatsappService/FindWhatsAppService.ts +++ b/backend/src/services/WhatsappService/FindWhatsAppService.ts @@ -1,24 +1,10 @@ import Whatsapp from "../../models/Whatsapp"; import AppError from "../../errors/AppError"; -interface WhereParams { - id?: number; - name?: string; - isDefault?: boolean; -} - -interface Request { - where?: WhereParams; -} - -const FindWhatsAppService = async ({ - where -}: Request): Promise => { - const whereCondition = { ...where }; - - const whatsapp = await Whatsapp.findOne({ - where: whereCondition - }); +const FindWhatsAppService = async ( + id: string | number +): Promise => { + const whatsapp = await Whatsapp.findByPk(id); if (!whatsapp) { throw new AppError("No whatsapp found with this conditions.", 404); diff --git a/backend/src/services/WhatsappService/ListWhatsAppsService.ts b/backend/src/services/WhatsappService/ListWhatsAppsService.ts index 2c2b884..2213530 100644 --- a/backend/src/services/WhatsappService/ListWhatsAppsService.ts +++ b/backend/src/services/WhatsappService/ListWhatsAppsService.ts @@ -1,13 +1,9 @@ import Whatsapp from "../../models/Whatsapp"; -interface Response { - whatsapps: Whatsapp[]; -} - -const ListWhatsAppsService = async (): Promise => { +const ListWhatsAppsService = async (): Promise => { const whatsapps = await Whatsapp.findAll(); - return { whatsapps }; + return whatsapps; }; export default ListWhatsAppsService; diff --git a/frontend/src/components/WhatsAppModal/index.js b/frontend/src/components/WhatsAppModal/index.js index 538f867..5bf4f28 100644 --- a/frontend/src/components/WhatsAppModal/index.js +++ b/frontend/src/components/WhatsAppModal/index.js @@ -60,7 +60,7 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => { const classes = useStyles(); const initialState = { name: "", - default: false, + isDefault: false, }; const [whatsApp, setWhatsApp] = useState(initialState); diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index 7961ad9..ce7a406 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -111,31 +111,31 @@ const Connections = () => { fetchSession(); }, []); - useEffect(() => { - const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + // useEffect(() => { + // const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - socket.on("whatsapp", data => { - if (data.action === "update") { - dispatch({ type: "UPDATE_WHATSAPPS", payload: data.whatsapp }); - } - }); + // socket.on("whatsapp", data => { + // if (data.action === "update") { + // dispatch({ type: "UPDATE_WHATSAPPS", payload: data.whatsapp }); + // } + // }); - socket.on("whatsapp", data => { - if (data.action === "delete") { - dispatch({ type: "DELETE_WHATSAPPS", payload: data.whatsappId }); - } - }); + // socket.on("whatsapp", data => { + // if (data.action === "delete") { + // dispatch({ type: "DELETE_WHATSAPPS", payload: data.whatsappId }); + // } + // }); - socket.on("whatsappSession", data => { - if (data.action === "update") { - dispatch({ type: "UPDATE_SESSION", payload: data.session }); - } - }); + // socket.on("whatsappSession", data => { + // if (data.action === "update") { + // dispatch({ type: "UPDATE_SESSION", payload: data.session }); + // } + // }); - return () => { - socket.disconnect(); - }; - }, []); + // return () => { + // socket.disconnect(); + // }; + // }, []); // const handleDisconnectSession = async whatsAppId => { // try { @@ -199,13 +199,13 @@ const Connections = () => { > Are you sure? It cannot be reverted. - + /> */} Date: Sun, 20 Sep 2020 11:13:46 -0300 Subject: [PATCH 29/54] fix default whatsapp in connection page --- frontend/src/components/WhatsAppModal/index.js | 6 +++--- frontend/src/pages/Connections/index.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/WhatsAppModal/index.js b/frontend/src/components/WhatsAppModal/index.js index 5bf4f28..0c58f2a 100644 --- a/frontend/src/components/WhatsAppModal/index.js +++ b/frontend/src/components/WhatsAppModal/index.js @@ -86,7 +86,7 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => { if (whatsAppId) { await api.put(`/whatsapp/${whatsAppId}`, { name: values.name, - default: values.default, + isDefault: values.isDefault, }); } else { await api.post("/whatsapp", values); @@ -140,8 +140,8 @@ const WhatsAppModal = ({ open, onClose, whatsAppId }) => { } label="Default" diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index ce7a406..cb36419 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -262,7 +262,7 @@ const Connections = () => { {format(parseISO(whatsApp.updatedAt), "dd/MM/yy HH:mm")} - {whatsApp.default && ( + {whatsApp.isDefault && ( )} From 4fda0b18c67a0c1d0374ef974881d279bf7173cb Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 11:16:07 -0300 Subject: [PATCH 30/54] fix: qrcode modal throwing error when no whatsappId --- frontend/src/components/QrcodeModal/index.js | 1 + frontend/src/pages/Connections/index.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/QrcodeModal/index.js b/frontend/src/components/QrcodeModal/index.js index d6821ca..fe6e541 100644 --- a/frontend/src/components/QrcodeModal/index.js +++ b/frontend/src/components/QrcodeModal/index.js @@ -12,6 +12,7 @@ const QrcodeModal = ({ open, onClose, whatsAppId }) => { useEffect(() => { const fetchSession = async () => { + if (!whatsAppId) return; try { const { data } = await api.get(`/whatsapp/${whatsAppId}`); setQrCode(data.qrcode); diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index cb36419..3df1a65 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -199,13 +199,13 @@ const Connections = () => { > Are you sure? It cannot be reverted. - {/* */} + /> Date: Sun, 20 Sep 2020 11:27:58 -0300 Subject: [PATCH 31/54] changed find to show services --- backend/src/controllers/ContactController.ts | 4 ++-- backend/src/controllers/UserController.ts | 4 ++-- backend/src/controllers/WhatsAppController.ts | 6 ++---- ...faultWhatsapp.ts => GetDefaultWhatsApp.ts} | 7 +++---- .../ContactServices/FindContactService.ts | 17 --------------- .../ContactServices/ShowContactService.ts | 14 +++++++++++++ .../MessageServices/CreateMessageService.ts | 4 ++-- .../MessageServices/ListMessagesService.ts | 4 ++-- .../TicketServices/CreateTicketService.ts | 4 ++-- ...dTicketService.ts => ShowTicketService.ts} | 21 +++---------------- ...{FindUserService.ts => ShowUserService.ts} | 9 ++++---- ...tsAppService.ts => ShowWhatsAppService.ts} | 4 ++-- frontend/src/components/QrcodeModal/index.js | 1 + 13 files changed, 40 insertions(+), 59 deletions(-) rename backend/src/helpers/{GetDefaultWhatsapp.ts => GetDefaultWhatsApp.ts} (53%) delete mode 100644 backend/src/services/ContactServices/FindContactService.ts create mode 100644 backend/src/services/ContactServices/ShowContactService.ts rename backend/src/services/TicketServices/{FindTicketService.ts => ShowTicketService.ts} (59%) rename backend/src/services/UserServices/{FindUserService.ts => ShowUserService.ts} (59%) rename backend/src/services/WhatsappService/{FindWhatsAppService.ts => ShowWhatsAppService.ts} (81%) diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts index 0254db2..987cc6b 100644 --- a/backend/src/controllers/ContactController.ts +++ b/backend/src/controllers/ContactController.ts @@ -2,7 +2,7 @@ import { Request, Response } from "express"; import ListContactsService from "../services/ContactServices/ListContactsService"; import CreateContactService from "../services/ContactServices/CreateContactService"; -import FindContactService from "../services/ContactServices/FindContactService"; +import ShowContactService from "../services/ContactServices/ShowContactService"; import UpdateContactService from "../services/ContactServices/UpdateContactService"; import DeleteContactService from "../services/ContactServices/DeleteContactService"; @@ -97,7 +97,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { contactId } = req.params; - const contact = await FindContactService(contactId); + const contact = await ShowContactService(contactId); return res.status(200).json(contact); }; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 521a6eb..5adf7b0 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -6,7 +6,7 @@ import AppError from "../errors/AppError"; import CreateUserService from "../services/UserServices/CreateUserService"; import ListUsersService from "../services/UserServices/ListUsersService"; import UpdateUserService from "../services/UserServices/UpdateUserService"; -import FindUserService from "../services/UserServices/FindUserService"; +import ShowUserService from "../services/UserServices/ShowUserService"; import DeleteUserService from "../services/UserServices/DeleteUserService"; type IndexQuery = { @@ -53,7 +53,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { userId } = req.params; - const user = await FindUserService(userId); + const user = await ShowUserService(userId); return res.status(200).json(user); }; diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index ff24f29..26cd7c3 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -3,7 +3,7 @@ import { Request, Response } from "express"; import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService"; import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService"; -import FindWhatsAppService from "../services/WhatsappService/FindWhatsAppService"; +import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService"; // import Yup from "yup"; // import Whatsapp from "../models/Whatsapp"; @@ -53,9 +53,7 @@ export const store = async (req: Request, res: Response): Promise => { export const show = async (req: Request, res: Response): Promise => { const { whatsappId } = req.params; - console.log(whatsappId); - - const whatsapp = await FindWhatsAppService(whatsappId); + const whatsapp = await ShowWhatsAppService(whatsappId); return res.status(200).json(whatsapp); }; diff --git a/backend/src/helpers/GetDefaultWhatsapp.ts b/backend/src/helpers/GetDefaultWhatsApp.ts similarity index 53% rename from backend/src/helpers/GetDefaultWhatsapp.ts rename to backend/src/helpers/GetDefaultWhatsApp.ts index 8c37e6b..cffddde 100644 --- a/backend/src/helpers/GetDefaultWhatsapp.ts +++ b/backend/src/helpers/GetDefaultWhatsApp.ts @@ -1,9 +1,8 @@ import AppError from "../errors/AppError"; import Whatsapp from "../models/Whatsapp"; -import FindWhatsAppService from "../services/WhatsappService/FindWhatsAppService"; -const GetDefaultWhatsapp = async (): Promise => { - const defaultWhatsapp = await FindWhatsAppService({ +const GetDefaultWhatsApp = async (): Promise => { + const defaultWhatsapp = await Whatsapp.findOne({ where: { isDefault: true } }); @@ -14,4 +13,4 @@ const GetDefaultWhatsapp = async (): Promise => { return defaultWhatsapp; }; -export default GetDefaultWhatsapp; +export default GetDefaultWhatsApp; diff --git a/backend/src/services/ContactServices/FindContactService.ts b/backend/src/services/ContactServices/FindContactService.ts deleted file mode 100644 index c381ca1..0000000 --- a/backend/src/services/ContactServices/FindContactService.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Contact from "../../models/Contact"; -import AppError from "../../errors/AppError"; - -const FindContactService = async (id: string): Promise => { - const user = await Contact.findOne({ - where: { id }, - attributes: ["id", "name", "number", "email"] - }); - - if (!user) { - throw new AppError("No contact found with this ID.", 404); - } - - return user; -}; - -export default FindContactService; diff --git a/backend/src/services/ContactServices/ShowContactService.ts b/backend/src/services/ContactServices/ShowContactService.ts new file mode 100644 index 0000000..2852dba --- /dev/null +++ b/backend/src/services/ContactServices/ShowContactService.ts @@ -0,0 +1,14 @@ +import Contact from "../../models/Contact"; +import AppError from "../../errors/AppError"; + +const ShowContactService = async (id: string | number): Promise => { + const user = await Contact.findByPk(id); + + if (!user) { + throw new AppError("No contact found with this ID.", 404); + } + + return user; +}; + +export default ShowContactService; diff --git a/backend/src/services/MessageServices/CreateMessageService.ts b/backend/src/services/MessageServices/CreateMessageService.ts index 4a6c59e..8957672 100644 --- a/backend/src/services/MessageServices/CreateMessageService.ts +++ b/backend/src/services/MessageServices/CreateMessageService.ts @@ -1,5 +1,5 @@ import Message from "../../models/Message"; -import FindTicketService from "../TicketServices/FindTicketService"; +import ShowTicketService from "../TicketServices/ShowTicketService"; interface Request { ticketId: string; @@ -10,7 +10,7 @@ const CreateMessageService = async ({ messageData, ticketId }: Request): Promise => { - const ticket = await FindTicketService({ where: { id: +ticketId } }); + const ticket = await ShowTicketService(ticketId); if (!ticket) { throw new Error("No ticket found with this ID"); diff --git a/backend/src/services/MessageServices/ListMessagesService.ts b/backend/src/services/MessageServices/ListMessagesService.ts index a4bc52c..7656472 100644 --- a/backend/src/services/MessageServices/ListMessagesService.ts +++ b/backend/src/services/MessageServices/ListMessagesService.ts @@ -1,7 +1,7 @@ import { where, fn, col } from "sequelize"; import Message from "../../models/Message"; import Ticket from "../../models/Ticket"; -import FindTicketService from "../TicketServices/FindTicketService"; +import ShowTicketService from "../TicketServices/ShowTicketService"; interface Request { ticketId: string; @@ -30,7 +30,7 @@ const ListMessagesService = async ({ ticketId }; - const ticket = await FindTicketService({ where: { id: +ticketId } }); + const ticket = await ShowTicketService(ticketId); if (!ticket) { throw new Error("No ticket found with this ID"); diff --git a/backend/src/services/TicketServices/CreateTicketService.ts b/backend/src/services/TicketServices/CreateTicketService.ts index 2df4650..c52e6d6 100644 --- a/backend/src/services/TicketServices/CreateTicketService.ts +++ b/backend/src/services/TicketServices/CreateTicketService.ts @@ -1,5 +1,5 @@ import AppError from "../../errors/AppError"; -import GetDefaultWhatsapp from "../../helpers/GetDefaultWhatsapp"; +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; import Ticket from "../../models/Ticket"; interface Request { @@ -11,7 +11,7 @@ const CreateTicketService = async ({ contactId, status }: Request): Promise => { - const defaultWhatsapp = await GetDefaultWhatsapp(); + const defaultWhatsapp = await GetDefaultWhatsApp(); if (!defaultWhatsapp) { throw new AppError("No default WhatsApp found. Check Connection page."); diff --git a/backend/src/services/TicketServices/FindTicketService.ts b/backend/src/services/TicketServices/ShowTicketService.ts similarity index 59% rename from backend/src/services/TicketServices/FindTicketService.ts rename to backend/src/services/TicketServices/ShowTicketService.ts index 6490ebd..4103685 100644 --- a/backend/src/services/TicketServices/FindTicketService.ts +++ b/backend/src/services/TicketServices/ShowTicketService.ts @@ -3,23 +3,8 @@ import AppError from "../../errors/AppError"; import Contact from "../../models/Contact"; import User from "../../models/User"; -interface WhereParams { - id?: number; - status?: string; - userId?: number; - contactId?: number; - whatsappId?: number; -} - -interface Request { - where?: WhereParams; -} - -const FindTicketService = async ({ where }: Request): Promise => { - const whereCondition = { ...where }; - - const ticket = await Ticket.findOne({ - where: whereCondition, +const ShowTicketService = async (id: string | number): Promise => { + const ticket = await Ticket.findByPk(id, { include: [ { model: Contact, @@ -42,4 +27,4 @@ const FindTicketService = async ({ where }: Request): Promise => { return ticket; }; -export default FindTicketService; +export default ShowTicketService; diff --git a/backend/src/services/UserServices/FindUserService.ts b/backend/src/services/UserServices/ShowUserService.ts similarity index 59% rename from backend/src/services/UserServices/FindUserService.ts rename to backend/src/services/UserServices/ShowUserService.ts index 868bf15..4581e98 100644 --- a/backend/src/services/UserServices/FindUserService.ts +++ b/backend/src/services/UserServices/ShowUserService.ts @@ -1,9 +1,10 @@ import User from "../../models/User"; import AppError from "../../errors/AppError"; -const FindUserService = async (id: string): Promise => { - const user = await User.findOne({ - where: { id }, +const ShowUserService = async ( + id: string | number +): Promise => { + const user = await User.findByPk(id, { attributes: ["name", "id", "email", "profile"] }); @@ -14,4 +15,4 @@ const FindUserService = async (id: string): Promise => { return user; }; -export default FindUserService; +export default ShowUserService; diff --git a/backend/src/services/WhatsappService/FindWhatsAppService.ts b/backend/src/services/WhatsappService/ShowWhatsAppService.ts similarity index 81% rename from backend/src/services/WhatsappService/FindWhatsAppService.ts rename to backend/src/services/WhatsappService/ShowWhatsAppService.ts index 741b9a8..b6afadf 100644 --- a/backend/src/services/WhatsappService/FindWhatsAppService.ts +++ b/backend/src/services/WhatsappService/ShowWhatsAppService.ts @@ -1,7 +1,7 @@ import Whatsapp from "../../models/Whatsapp"; import AppError from "../../errors/AppError"; -const FindWhatsAppService = async ( +const ShowWhatsAppService = async ( id: string | number ): Promise => { const whatsapp = await Whatsapp.findByPk(id); @@ -13,4 +13,4 @@ const FindWhatsAppService = async ( return whatsapp; }; -export default FindWhatsAppService; +export default ShowWhatsAppService; diff --git a/frontend/src/components/QrcodeModal/index.js b/frontend/src/components/QrcodeModal/index.js index fe6e541..b85af40 100644 --- a/frontend/src/components/QrcodeModal/index.js +++ b/frontend/src/components/QrcodeModal/index.js @@ -13,6 +13,7 @@ const QrcodeModal = ({ open, onClose, whatsAppId }) => { useEffect(() => { const fetchSession = async () => { if (!whatsAppId) return; + try { const { data } = await api.get(`/whatsapp/${whatsAppId}`); setQrCode(data.qrcode); From a879bb95caa0dbebe849c4ce4795b37f8e69d747 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 11:40:18 -0300 Subject: [PATCH 32/54] fix: no extra info in contacts --- backend/src/services/ContactServices/ShowContactService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/services/ContactServices/ShowContactService.ts b/backend/src/services/ContactServices/ShowContactService.ts index 2852dba..a301cc2 100644 --- a/backend/src/services/ContactServices/ShowContactService.ts +++ b/backend/src/services/ContactServices/ShowContactService.ts @@ -2,7 +2,7 @@ import Contact from "../../models/Contact"; import AppError from "../../errors/AppError"; const ShowContactService = async (id: string | number): Promise => { - const user = await Contact.findByPk(id); + const user = await Contact.findByPk(id, { include: ["extraInfo"] }); if (!user) { throw new AppError("No contact found with this ID.", 404); From 23d16679b6d7f8ee20ff5a111ed253043af95320 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 11:47:46 -0300 Subject: [PATCH 33/54] enabled socket events on contact controller --- backend/src/controllers/ContactController.ts | 32 +++++++++---------- .../ContactServices/CreateContactService.ts | 6 ++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts index 987cc6b..6242fd4 100644 --- a/backend/src/controllers/ContactController.ts +++ b/backend/src/controllers/ContactController.ts @@ -1,4 +1,5 @@ import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; import ListContactsService from "../services/ContactServices/ListContactsService"; import CreateContactService from "../services/ContactServices/CreateContactService"; @@ -6,7 +7,6 @@ import ShowContactService from "../services/ContactServices/ShowContactService"; import UpdateContactService from "../services/ContactServices/UpdateContactService"; import DeleteContactService from "../services/ContactServices/DeleteContactService"; -// const { getIO } = require("../libs/socket"); // const { getWbot } = require("../libs/wbot"); type IndexQuery = { @@ -57,7 +57,6 @@ export const store = async (req: Request, res: Response): Promise => { // } // const wbot = getWbot(defaultWhatsapp); - // const io = getIO(); // try { // const isValidNumber = await wbot.isRegisteredUser( @@ -86,10 +85,11 @@ export const store = async (req: Request, res: Response): Promise => { // } // ); - // io.emit("contact", { - // action: "create", - // contact: contact - // }); + const io = getIO(); + io.emit("contact", { + action: "create", + contact + }); return res.status(200).json(contact); }; @@ -112,11 +112,11 @@ export const update = async ( const contact = await UpdateContactService({ contactData, contactId }); - // const io = getIO(); - // io.emit("contact", { - // action: "update", - // contact: contact - // }); + const io = getIO(); + io.emit("contact", { + action: "update", + contact + }); return res.status(200).json(contact); }; @@ -129,11 +129,11 @@ export const remove = async ( await DeleteContactService(contactId); - // const io = getIO(); - // io.emit("contact", { - // action: "delete", - // contactId: contactId - // }); + const io = getIO(); + io.emit("contact", { + action: "delete", + contactId + }); return res.status(200).json({ message: "Contact deleted" }); }; diff --git a/backend/src/services/ContactServices/CreateContactService.ts b/backend/src/services/ContactServices/CreateContactService.ts index 3c821aa..ecc8955 100644 --- a/backend/src/services/ContactServices/CreateContactService.ts +++ b/backend/src/services/ContactServices/CreateContactService.ts @@ -16,8 +16,8 @@ interface Request { const CreateContactService = async ({ name, number, - email, - extraInfo + email = "", + extraInfo = [] }: Request): Promise => { const numberExists = await Contact.findOne({ where: { number } @@ -27,6 +27,8 @@ const CreateContactService = async ({ throw new AppError("A contact with this number already exists."); } + console.log("extra", extraInfo); + const contact = await Contact.create( { name, From 02c37276ba68deae35c70b67fd0aa54f2a7c8037 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 11:53:31 -0300 Subject: [PATCH 34/54] enabled socket io in settings controller --- backend/src/controllers/MessageController.ts | 4 +++- backend/src/controllers/SessionController.ts | 6 ++++-- backend/src/controllers/SettingController.ts | 12 ++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts index c492e13..4d22ec7 100644 --- a/backend/src/controllers/MessageController.ts +++ b/backend/src/controllers/MessageController.ts @@ -1,6 +1,9 @@ import { Request, Response } from "express"; +// import { getIO } from "../libs/socket"; + import CreateMessageService from "../services/MessageServices/CreateMessageService"; import ListMessagesService from "../services/MessageServices/ListMessagesService"; + // import Sequelize from "sequelize"; // import { MessageMedia } from "whatsapp-web.js"; // import Message from "../models/Message"; @@ -9,7 +12,6 @@ import ListMessagesService from "../services/MessageServices/ListMessagesService // import Whatsapp from "../models/Whatsapp"; // import Ticket from "../models/Ticket"; -// import { getIO } from "../libs/socket"; // import { getWbot } from "../libs/wbot"; // const setMessagesAsRead = async ticket => { diff --git a/backend/src/controllers/SessionController.ts b/backend/src/controllers/SessionController.ts index 2f2e216..89a44fb 100644 --- a/backend/src/controllers/SessionController.ts +++ b/backend/src/controllers/SessionController.ts @@ -8,7 +8,9 @@ export const store = async (req: Request, res: Response): Promise => { const { token, user } = await AuthUserService({ email, password }); return res.status(200).json({ - user, - token + token, + username: user.name, + profile: user.profile, + userId: user.id }); }; diff --git a/backend/src/controllers/SettingController.ts b/backend/src/controllers/SettingController.ts index 1d7411d..1ecb52e 100644 --- a/backend/src/controllers/SettingController.ts +++ b/backend/src/controllers/SettingController.ts @@ -1,10 +1,10 @@ import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; import AppError from "../errors/AppError"; import UpdateSettingService from "../services/SettingServices/UpdateSettingService"; import ListSettingsService from "../services/SettingServices/ListSettingsService"; -// const { getIO } = require("../libs/socket"); export const index = async (req: Request, res: Response): Promise => { if (req.user.profile !== "admin") { @@ -31,11 +31,11 @@ export const update = async ( value }); - // const io = getIO(); - // io.emit("settings", { - // action: "update", - // setting - // }); + const io = getIO(); + io.emit("settings", { + action: "update", + setting + }); return res.status(200).json(setting); }; From 95c6dcb32f0ae7a4a58c8437d5c7496724ae38c4 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 12:00:30 -0300 Subject: [PATCH 35/54] enabled socket io on tickets controller --- backend/src/controllers/TicketController.ts | 82 ++++--------------- .../TicketServices/UpdateTicketService.ts | 2 +- frontend/src/pages/Connections/index.js | 42 +++++----- 3 files changed, 39 insertions(+), 87 deletions(-) diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 7684d47..0ae36da 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -1,11 +1,11 @@ import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + import CreateTicketService from "../services/TicketServices/CreateTicketService"; import DeleteTicketService from "../services/TicketServices/DeleteTicketService"; import ListTicketsService from "../services/TicketServices/ListTicketsService"; import UpdateTicketService from "../services/TicketServices/UpdateTicketService"; -// const { getIO } = require("../libs/socket"); - type IndexQuery = { searchParam: string; pageNumber: string; @@ -30,54 +30,6 @@ export const index = async (req: Request, res: Response): Promise => { const userId = req.user.id; - // let includeCondition = [ - // { - // model: Contact, - // as: "contact", - // attributes: ["name", "number", "profilePicUrl"] - // } - // ]; - - // if (searchParam) { - // includeCondition = [ - // ...includeCondition, - // { - // model: Message, - // as: "messages", - // attributes: ["id", "body"], - // where: { - // body: Sequelize.where( - // Sequelize.fn("LOWER", Sequelize.col("body")), - // "LIKE", - // "%" + searchParam.toLowerCase() + "%" - // ) - // }, - // required: false, - // duplicating: false - // } - // ]; - - // whereCondition = { - // [Sequelize.Op.or]: [ - // { - // "$contact.name$": Sequelize.where( - // Sequelize.fn("LOWER", Sequelize.col("name")), - // "LIKE", - // "%" + searchParam.toLowerCase() + "%" - // ) - // }, - // { "$contact.number$": { [Sequelize.Op.like]: `%${searchParam}%` } }, - // { - // "$message.body$": Sequelize.where( - // Sequelize.fn("LOWER", Sequelize.col("body")), - // "LIKE", - // "%" + searchParam.toLowerCase() + "%" - // ) - // } - // ] - // }; - // } - const { tickets, count, hasMore } = await ListTicketsService({ searchParam, pageNumber, @@ -95,11 +47,11 @@ export const store = async (req: Request, res: Response): Promise => { const ticket = await CreateTicketService({ contactId, status }); - // const io = getIO(); - // io.to("notification").emit("ticket", { - // action: "create", - // ticket: serializaedTicket - // }); + const io = getIO(); + io.to("notification").emit("ticket", { + action: "create", + ticket + }); return res.status(200).json(ticket); }; @@ -113,11 +65,11 @@ export const update = async ( const ticket = await UpdateTicketService({ ticketData, ticketId }); - // const io = getIO(); - // io.to("notification").emit("ticket", { - // action: "updateStatus", - // ticket: ticket - // }); + const io = getIO(); + io.to("notification").emit("ticket", { + action: "updateStatus", + ticket + }); return res.status(200).json(ticket); }; @@ -130,11 +82,11 @@ export const remove = async ( await DeleteTicketService(ticketId); - // const io = getIO(); - // io.to("notification").emit("ticket", { - // action: "delete", - // ticketId: ticket.id - // }); + const io = getIO(); + io.to("notification").emit("ticket", { + action: "delete", + ticketId: +ticketId + }); return res.status(200).json({ message: "ticket deleted" }); }; diff --git a/backend/src/services/TicketServices/UpdateTicketService.ts b/backend/src/services/TicketServices/UpdateTicketService.ts index 303a91d..f7b893d 100644 --- a/backend/src/services/TicketServices/UpdateTicketService.ts +++ b/backend/src/services/TicketServices/UpdateTicketService.ts @@ -24,7 +24,7 @@ const UpdateTicketService = async ({ { model: Contact, as: "contact", - attributes: ["name", "number", "profilePicUrl"] + attributes: ["id", "name", "number", "profilePicUrl"] } ] }); diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index 3df1a65..800b478 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -111,31 +111,31 @@ const Connections = () => { fetchSession(); }, []); - // useEffect(() => { - // const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + useEffect(() => { + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - // socket.on("whatsapp", data => { - // if (data.action === "update") { - // dispatch({ type: "UPDATE_WHATSAPPS", payload: data.whatsapp }); - // } - // }); + socket.on("whatsapp", data => { + if (data.action === "update") { + dispatch({ type: "UPDATE_WHATSAPPS", payload: data.whatsapp }); + } + }); - // socket.on("whatsapp", data => { - // if (data.action === "delete") { - // dispatch({ type: "DELETE_WHATSAPPS", payload: data.whatsappId }); - // } - // }); + socket.on("whatsapp", data => { + if (data.action === "delete") { + dispatch({ type: "DELETE_WHATSAPPS", payload: data.whatsappId }); + } + }); - // socket.on("whatsappSession", data => { - // if (data.action === "update") { - // dispatch({ type: "UPDATE_SESSION", payload: data.session }); - // } - // }); + socket.on("whatsappSession", data => { + if (data.action === "update") { + dispatch({ type: "UPDATE_SESSION", payload: data.session }); + } + }); - // return () => { - // socket.disconnect(); - // }; - // }, []); + return () => { + socket.disconnect(); + }; + }, []); // const handleDisconnectSession = async whatsAppId => { // try { From 256262f353bd3306adc287625c7f119fa13e74a6 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 12:04:21 -0300 Subject: [PATCH 36/54] enabled socketio on users controller --- backend/src/controllers/UserController.ts | 19 ++ backend/src/controllersOld/UserController.js | 202 +++++++++---------- 2 files changed, 120 insertions(+), 101 deletions(-) diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 5adf7b0..d0a5503 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -1,4 +1,5 @@ import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; import CheckSettingsHelper from "../helpers/CheckSettings"; import AppError from "../errors/AppError"; @@ -47,6 +48,12 @@ export const store = async (req: Request, res: Response): Promise => { profile }); + const io = getIO(); + io.emit("user", { + action: "create", + user + }); + return res.status(200).json(user); }; @@ -71,6 +78,12 @@ export const update = async ( const user = await UpdateUserService({ userData, userId }); + const io = getIO(); + io.emit("user", { + action: "update", + user + }); + return res.status(200).json(user); }; @@ -86,5 +99,11 @@ export const remove = async ( await DeleteUserService(userId); + const io = getIO(); + io.emit("user", { + action: "delete", + userId + }); + return res.status(200).json({ message: "User deleted" }); }; diff --git a/backend/src/controllersOld/UserController.js b/backend/src/controllersOld/UserController.js index 13f8186..06fd1a1 100644 --- a/backend/src/controllersOld/UserController.js +++ b/backend/src/controllersOld/UserController.js @@ -1,49 +1,49 @@ -// const Sequelize = require("sequelize"); -// const Yup = require("yup"); -// const { Op } = require("sequelize"); +const Sequelize = require("sequelize"); +const Yup = require("yup"); +const { Op } = require("sequelize"); -// const User = require("../models/User"); -// const Setting = require("../models/Setting"); +const User = require("../models/User"); +const Setting = require("../models/Setting"); -// const { getIO } = require("../libs/socket"); +const { getIO } = require("../libs/socket"); -// exports.index = async (req, res) => { -// if (req.user.profile !== "admin") { -// return res -// .status(403) -// .json({ error: "Only administrators can access this route." }); -// } +exports.index = async (req, res) => { + if (req.user.profile !== "admin") { + return res + .status(403) + .json({ error: "Only administrators can access this route." }); + } -// const { searchParam = "", pageNumber = 1 } = req.query; + const { searchParam = "", pageNumber = 1 } = req.query; -// const whereCondition = { -// [Op.or]: [ -// { -// name: Sequelize.where( -// Sequelize.fn("LOWER", Sequelize.col("name")), -// "LIKE", -// "%" + searchParam.toLowerCase() + "%" -// ), -// }, -// { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } }, -// ], -// }; + const whereCondition = { + [Op.or]: [ + { + name: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("name")), + "LIKE", + "%" + searchParam.toLowerCase() + "%" + ) + }, + { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } } + ] + }; -// let limit = 20; -// let offset = limit * (pageNumber - 1); + let limit = 20; + let offset = limit * (pageNumber - 1); -// const { count, rows: users } = await User.findAndCountAll({ -// attributes: ["name", "id", "email", "profile"], -// where: whereCondition, -// limit, -// offset, -// order: [["createdAt", "DESC"]], -// }); + const { count, rows: users } = await User.findAndCountAll({ + attributes: ["name", "id", "email", "profile"], + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]] + }); -// const hasMore = count > offset + users.length; + const hasMore = count > offset + users.length; -// return res.status(200).json({ users, count, hasMore }); -// }; + return res.status(200).json({ users, count, hasMore }); +}; export default async (req, res, next) => { console.log(req.url); @@ -95,87 +95,87 @@ export default async (req, res, next) => { return res.status(201).json({ message: "User created!", userId: id }); }; -// exports.show = async (req, res) => { -// const { userId } = req.params; +exports.show = async (req, res) => { + const { userId } = req.params; -// const user = await User.findByPk(userId, { -// attributes: ["id", "name", "email", "profile"], -// }); + const user = await User.findByPk(userId, { + attributes: ["id", "name", "email", "profile"] + }); -// if (!user) { -// res.status(400).json({ error: "No user found with this id." }); -// } + if (!user) { + res.status(400).json({ error: "No user found with this id." }); + } -// return res.status(200).json(user); -// }; + return res.status(200).json(user); +}; -// exports.update = async (req, res) => { -// const schema = Yup.object().shape({ -// name: Yup.string().min(2), -// email: Yup.string().email(), -// password: Yup.string(), -// }); +exports.update = async (req, res) => { + const schema = Yup.object().shape({ + name: Yup.string().min(2), + email: Yup.string().email(), + password: Yup.string() + }); -// if (req.user.profile !== "admin") { -// return res -// .status(403) -// .json({ error: "Only administrators can edit users." }); -// } + if (req.user.profile !== "admin") { + return res + .status(403) + .json({ error: "Only administrators can edit users." }); + } -// await schema.validate(req.body); + await schema.validate(req.body); -// const io = getIO(); -// const { userId } = req.params; + const io = getIO(); + const { userId } = req.params; -// const user = await User.findByPk(userId, { -// attributes: ["name", "id", "email", "profile"], -// }); + const user = await User.findByPk(userId, { + attributes: ["name", "id", "email", "profile"] + }); -// if (!user) { -// res.status(404).json({ error: "No user found with this id." }); -// } + if (!user) { + res.status(404).json({ error: "No user found with this id." }); + } -// if (user.profile === "admin" && req.body.profile === "user") { -// const adminUsers = await User.count({ where: { profile: "admin" } }); -// if (adminUsers <= 1) { -// return res -// .status(403) -// .json({ error: "There must be at leat one admin user." }); -// } -// } + if (user.profile === "admin" && req.body.profile === "user") { + const adminUsers = await User.count({ where: { profile: "admin" } }); + if (adminUsers <= 1) { + return res + .status(403) + .json({ error: "There must be at leat one admin user." }); + } + } -// await user.update(req.body); + await user.update(req.body); -// io.emit("user", { -// action: "update", -// user: user, -// }); + io.emit("user", { + action: "update", + user: user + }); -// return res.status(200).json(user); -// }; + return res.status(200).json(user); +}; -// exports.delete = async (req, res) => { -// const io = getIO(); -// const { userId } = req.params; +exports.delete = async (req, res) => { + const io = getIO(); + const { userId } = req.params; -// const user = await User.findByPk(userId); + const user = await User.findByPk(userId); -// if (!user) { -// res.status(400).json({ error: "No user found with this id." }); -// } + if (!user) { + res.status(400).json({ error: "No user found with this id." }); + } -// if (req.user.profile !== "admin") { -// return res -// .status(403) -// .json({ error: "Only administrators can edit users." }); -// } + if (req.user.profile !== "admin") { + return res + .status(403) + .json({ error: "Only administrators can edit users." }); + } -// await user.destroy(); + await user.destroy(); -// io.emit("user", { -// action: "delete", -// userId: userId, -// }); + io.emit("user", { + action: "delete", + userId: userId + }); -// return res.status(200).json({ message: "User deleted" }); -// }; + return res.status(200).json({ message: "User deleted" }); +}; From 8b99690c4af57df385a41bf0dc9e9935aabcea98 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 12:06:45 -0300 Subject: [PATCH 37/54] enable socket io in all controllers --- backend/src/controllers/WhatsAppController.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 26cd7c3..bc8a414 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -1,4 +1,5 @@ import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService"; @@ -25,8 +26,6 @@ export const index = async (req: Request, res: Response): Promise => { }; export const store = async (req: Request, res: Response): Promise => { - // const io = getIO(); - const { name, status, isDefault }: WhatsappData = req.body; const whatsapp = await CreateWhatsAppService({ name, status, isDefault }); @@ -42,10 +41,11 @@ export const store = async (req: Request, res: Response): Promise => { // }) // .catch(err => console.log(err)); - // io.emit("whatsapp", { - // action: "update", - // whatsapp: whatsapp - // }); + const io = getIO(); + io.emit("whatsapp", { + action: "update", + whatsapp + }); return res.status(200).json(whatsapp); }; @@ -66,7 +66,6 @@ export const update = async ( const whatsappData = req.body; const whatsapp = await UpdateWhatsAppService({ whatsappData, whatsappId }); - // const io = getIO(); // const whatsapp = await Whatsapp.findByPk(whatsappId); @@ -76,10 +75,11 @@ export const update = async ( // await whatsapp.update(req.body); - // io.emit("whatsapp", { - // action: "update", - // whatsapp: whatsapp - // }); + const io = getIO(); + io.emit("whatsapp", { + action: "update", + whatsapp + }); return res.status(200).json(whatsapp); }; @@ -88,16 +88,16 @@ export const remove = async ( req: Request, res: Response ): Promise => { - // const io = getIO(); const { whatsappId } = req.params; await DeleteWhatsAppService(whatsappId); // removeWbot(whatsapp.id); - // io.emit("whatsapp", { - // action: "delete", - // whatsappId: whatsapp.id - // }); + const io = getIO(); + io.emit("whatsapp", { + action: "delete", + whatsappId: +whatsappId + }); return res.status(200).json({ message: "Whatsapp deleted." }); }; From cb3ba0925213990c0698f782dcce216d75729ae3 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 14:06:20 -0300 Subject: [PATCH 38/54] migrated wbot initialization to typescript --- backend/src/@types/WAWebJS.d.ts | 5 + backend/src/@types/express.d.ts | 6 +- backend/src/@types/qrcode-terminal.d.ts | 1 + backend/src/libs/wbot.js | 110 ---------- backend/src/libs/wbot.ts | 107 +++++++++ backend/src/server.ts | 34 +-- .../WbotServices/wbotMessageListener.js | 206 ++++++++++++++++++ .../{ => WbotServices}/wbotMonitor.js | 0 backend/src/services/wbotMessageListener.js | 206 ------------------ backend/yarn-error.log | 53 ++++- 10 files changed, 383 insertions(+), 345 deletions(-) create mode 100644 backend/src/@types/WAWebJS.d.ts create mode 100644 backend/src/@types/qrcode-terminal.d.ts delete mode 100644 backend/src/libs/wbot.js create mode 100644 backend/src/libs/wbot.ts create mode 100644 backend/src/services/WbotServices/wbotMessageListener.js rename backend/src/services/{ => WbotServices}/wbotMonitor.js (100%) delete mode 100644 backend/src/services/wbotMessageListener.js diff --git a/backend/src/@types/WAWebJS.d.ts b/backend/src/@types/WAWebJS.d.ts new file mode 100644 index 0000000..0d17ab2 --- /dev/null +++ b/backend/src/@types/WAWebJS.d.ts @@ -0,0 +1,5 @@ +declare namespace WAWebJS { + export interface ClientInfo { + meuId: number; + } +} diff --git a/backend/src/@types/express.d.ts b/backend/src/@types/express.d.ts index 204f2e0..3315ef3 100644 --- a/backend/src/@types/express.d.ts +++ b/backend/src/@types/express.d.ts @@ -1,5 +1,5 @@ declare namespace Express { - export interface Request { - user: { id: string; profile: string }; - } + export interface Request { + user: { id: string; profile: string }; + } } diff --git a/backend/src/@types/qrcode-terminal.d.ts b/backend/src/@types/qrcode-terminal.d.ts new file mode 100644 index 0000000..3b59fed --- /dev/null +++ b/backend/src/@types/qrcode-terminal.d.ts @@ -0,0 +1 @@ +declare module "qrcode-terminal"; diff --git a/backend/src/libs/wbot.js b/backend/src/libs/wbot.js deleted file mode 100644 index 6ba443d..0000000 --- a/backend/src/libs/wbot.js +++ /dev/null @@ -1,110 +0,0 @@ -const qrCode = require("qrcode-terminal"); -const { Client } = require("whatsapp-web.js"); -const Whatsapp = require("../models/Whatsapp"); -const { getIO } = require("../libs/socket"); - -let sessions = []; - -module.exports = { - initWbot: async whatsapp => { - try { - const io = getIO(); - const sessionName = whatsapp.name; - let sessionCfg; - - if (whatsapp && whatsapp.session) { - sessionCfg = JSON.parse(whatsapp.session); - } - - const sessionIndex = sessions.findIndex(s => s.id === whatsapp.id); - if (sessionIndex !== -1) { - sessions[sessionIndex].destroy(); - sessions.splice(sessionIndex, 1); - } - - const wbot = new Client({ - session: sessionCfg, - restartOnAuthFail: true - }); - wbot.initialize(); - - wbot.on("qr", async qr => { - console.log("Session:", sessionName); - - qrCode.generate(qr, { small: true }); - - await whatsapp.update({ qrcode: qr, status: "qrcode" }); - - io.emit("whatsappSession", { - action: "update", - session: whatsapp - }); - }); - - wbot.on("authenticated", async session => { - console.log("Session:", sessionName, "AUTHENTICATED"); - - await whatsapp.update({ - session: JSON.stringify(session), - status: "authenticated" - }); - - io.emit("whatsappSession", { - action: "update", - session: whatsapp - }); - }); - - wbot.on("auth_failure", async msg => { - console.error("Session:", sessionName, "AUTHENTICATION FAILURE", msg); - - await whatsapp.update({ session: "" }); - }); - - wbot.on("ready", async () => { - console.log("Session:", sessionName, "READY"); - - await whatsapp.update({ - status: "CONNECTED", - qrcode: "" - }); - - io.emit("whatsappSession", { - action: "update", - session: whatsapp - }); - - wbot.sendPresenceAvailable(); - }); - - wbot.id = whatsapp.id; - sessions.push(wbot); - } catch (err) { - console.log(err); - } - - return null; - }, - - getWbot: whatsappId => { - const sessionIndex = sessions.findIndex(s => s.id === whatsappId); - - if (sessionIndex === -1) { - console.log("This Wbot session is not initialized"); - return null; - } - return sessions[sessionIndex]; - }, - - removeWbot: whatsappId => { - try { - const sessionIndex = sessions.findIndex(s => s.id === whatsappId); - if (sessionIndex !== -1) { - sessions[sessionIndex].destroy(); - sessions.splice(sessionIndex, 1); - } - } catch (err) { - console.log(err); - } - } -}; diff --git a/backend/src/libs/wbot.ts b/backend/src/libs/wbot.ts new file mode 100644 index 0000000..4cbf5dd --- /dev/null +++ b/backend/src/libs/wbot.ts @@ -0,0 +1,107 @@ +import qrCode from "qrcode-terminal"; +import { Client } from "whatsapp-web.js"; +import { getIO } from "./socket"; +import Whatsapp from "../models/Whatsapp"; + +const sessions: Client[] = []; + +export const initWbot = async (whatsapp: Whatsapp): Promise => { + console.log("starting"); + try { + const io = getIO(); + const sessionName = whatsapp.name; + let sessionCfg; + + if (whatsapp && whatsapp.session) { + sessionCfg = JSON.parse(whatsapp.session); + } + + const sessionIndex = sessions.findIndex(s => s.id === whatsapp.id); + if (sessionIndex !== -1) { + sessions[sessionIndex].destroy(); + sessions.splice(sessionIndex, 1); + } + + const wbot = new Client({ + session: sessionCfg, + restartOnAuthFail: true + }); + wbot.initialize(); + + wbot.on("qr", async qr => { + console.log("Session:", sessionName); + + qrCode.generate(qr, { small: true }); + + await whatsapp.update({ qrcode: qr, status: "qrcode" }); + + io.emit("whatsappSession", { + action: "update", + session: whatsapp + }); + }); + + wbot.on("authenticated", async session => { + console.log("Session:", sessionName, "AUTHENTICATED"); + + await whatsapp.update({ + session: JSON.stringify(session), + status: "authenticated" + }); + + io.emit("whatsappSession", { + action: "update", + session: whatsapp + }); + }); + + wbot.on("auth_failure", async msg => { + console.error("Session:", sessionName, "AUTHENTICATION FAILURE", msg); + + await whatsapp.update({ session: "" }); + }); + + wbot.on("ready", async () => { + console.log("Session:", sessionName, "READY"); + + await whatsapp.update({ + status: "CONNECTED", + qrcode: "" + }); + + io.emit("whatsappSession", { + action: "update", + session: whatsapp + }); + + wbot.sendPresenceAvailable(); + }); + + wbot.id = whatsapp.id; + sessions.push(wbot); + } catch (err) { + console.log(err); + } +}; + +export const getWbot = (whatsappId: number): Client | null => { + const sessionIndex = sessions.findIndex(s => s.id === whatsappId); + + if (sessionIndex === -1) { + console.log("This Wbot session is not initialized"); + return null; + } + return sessions[sessionIndex]; +}; + +export const removeWbot = (whatsappId: number): void => { + try { + const sessionIndex = sessions.findIndex(s => s.id === whatsappId); + if (sessionIndex !== -1) { + sessions[sessionIndex].destroy(); + sessions.splice(sessionIndex, 1); + } + } catch (err) { + console.log(err); + } +}; diff --git a/backend/src/server.ts b/backend/src/server.ts index 93519f2..e16dcfd 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -11,13 +11,13 @@ import AppError from "./errors/AppError"; import routes from "./routes"; import { initIO } from "./libs/socket"; import "./database"; +import { initWbot } from "./libs/wbot"; // import path from "path"; -// const { initWbot } = require("./libs/wbot"); // const wbotMessageListener = require("./services/wbotMessageListener"); // const wbotMonitor = require("./services/wbotMonitor"); -// const Whatsapp = require("./models/Whatsapp"); +import Whatsapp from "./models/Whatsapp"; Sentry.init({ dsn: process.env.SENTRY_DSN }); @@ -62,21 +62,21 @@ io.on("connection", socket => { }); }); -// const startWhatsAppSessions = async () => { -// const whatsapps = await Whatsapp.findAll(); - -// if (whatsapps.length > 0) { -// whatsapps.forEach(whatsapp => { -// initWbot(whatsapp) -// .then(() => { -// wbotMessageListener(whatsapp); -// wbotMonitor(whatsapp); -// }) -// .catch(err => console.log(err)); -// }); -// } -// }; -// startWhatsAppSessions(); +const startWhatsAppSessions = async () => { + const whatsapps = await Whatsapp.findAll(); + if (whatsapps.length > 0) { + whatsapps.forEach(whatsapp => { + initWbot(whatsapp) + .then(() => { + console.log("initialized!!"); + // wbotMessageListener(whatsapp); + // wbotMonitor(whatsapp); + }) + .catch(err => console.log(err)); + }); + } +}; +startWhatsAppSessions(); // app.use(Sentry.Handlers.errorHandler()); diff --git a/backend/src/services/WbotServices/wbotMessageListener.js b/backend/src/services/WbotServices/wbotMessageListener.js new file mode 100644 index 0000000..d8e6755 --- /dev/null +++ b/backend/src/services/WbotServices/wbotMessageListener.js @@ -0,0 +1,206 @@ +const path = require("path"); +const fs = require("fs"); +const { Op } = require("sequelize"); +const { subHours } = require("date-fns"); +const Sentry = require("@sentry/node"); + +const Contact = require("../models/Contact"); +const Ticket = require("../models/Ticket"); +const Message = require("../models/Message"); +const Whatsapp = require("../models/Whatsapp"); + +const { getIO } = require("../libs/socket"); +const { getWbot, initWbot } = require("../libs/wbot"); + +const verifyContact = async (msgContact, profilePicUrl) => { + let contact = await Contact.findOne({ + where: { number: msgContact.number } + }); + + if (contact) { + await contact.update({ profilePicUrl: profilePicUrl }); + } else { + contact = await Contact.create({ + name: msgContact.pushname || msgContact.number.toString(), + number: msgContact.number, + profilePicUrl: profilePicUrl + }); + } + + return contact; +}; + +const verifyTicket = async (contact, whatsappId) => { + let ticket = await Ticket.findOne({ + where: { + status: { + [Op.or]: ["open", "pending"] + }, + contactId: contact.id + } + }); + + if (!ticket) { + ticket = await Ticket.findOne({ + where: { + createdAt: { [Op.between]: [subHours(new Date(), 2), new Date()] }, + contactId: contact.id + }, + order: [["createdAt", "DESC"]] + }); + + if (ticket) { + await ticket.update({ status: "pending", userId: null }); + } + } + + if (!ticket) { + ticket = await Ticket.create({ + contactId: contact.id, + status: "pending", + whatsappId + }); + } + + return ticket; +}; + +const handlMedia = async (msg, ticket) => { + const media = await msg.downloadMedia(); + let newMessage; + console.log("criando midia"); + if (media) { + if (!media.filename) { + let ext = media.mimetype.split("/")[1].split(";")[0]; + media.filename = `${new Date().getTime()}.${ext}`; + } + + fs.writeFile( + path.join(__dirname, "..", "..", "public", media.filename), + media.data, + "base64", + err => { + console.log(err); + } + ); + + newMessage = await ticket.createMessage({ + id: msg.id.id, + body: msg.body || media.filename, + fromMe: msg.fromMe, + mediaUrl: media.filename, + mediaType: media.mimetype.split("/")[0] + }); + await ticket.update({ lastMessage: msg.body || media.filename }); + } + + return newMessage; +}; + +const handleMessage = async (msg, ticket, contact) => { + const io = getIO(); + let newMessage; + + if (msg.hasMedia) { + newMessage = await handlMedia(msg, ticket); + } else { + newMessage = await ticket.createMessage({ + id: msg.id.id, + body: msg.body, + fromMe: msg.fromMe + }); + await ticket.update({ lastMessage: msg.body }); + } + + const serializedMessage = { + ...newMessage.dataValues, + mediaUrl: `${ + newMessage.mediaUrl + ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${newMessage.mediaUrl}` + : "" + }` + }; + + const serializaedTicket = { + ...ticket.dataValues, + contact: contact + }; + + io.to(ticket.id).to("notification").emit("appMessage", { + action: "create", + message: serializedMessage, + ticket: serializaedTicket, + contact: contact + }); +}; + +const wbotMessageListener = whatsapp => { + const whatsappId = whatsapp.id; + const wbot = getWbot(whatsappId); + const io = getIO(); + + wbot.on("message_create", async msg => { + // console.log(msg); + + if ( + msg.from === "status@broadcast" || + msg.type === "location" || + msg.type === "call_log" || + msg.author != null // Ignore Group Messages + ) { + return; + } + + try { + let msgContact; + + if (msg.fromMe) { + msgContact = await wbot.getContactById(msg.to); + } else { + msgContact = await msg.getContact(); + } + + const profilePicUrl = await msgContact.getProfilePicUrl(); + const contact = await verifyContact(msgContact, profilePicUrl); + const ticket = await verifyTicket(contact, whatsappId); + + //return if message was already created by messageController + if (msg.fromMe) { + const alreadyExists = await Message.findOne({ + where: { id: msg.id.id } + }); + + if (alreadyExists) { + return; + } + } + + await handleMessage(msg, ticket, contact); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } + }); + + wbot.on("message_ack", async (msg, ack) => { + try { + const messageToUpdate = await Message.findOne({ + where: { id: msg.id.id } + }); + if (!messageToUpdate) { + return; + } + await messageToUpdate.update({ ack: ack }); + + io.to(messageToUpdate.ticketId).emit("appMessage", { + action: "update", + message: messageToUpdate + }); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } + }); +}; + +module.exports = wbotMessageListener; diff --git a/backend/src/services/wbotMonitor.js b/backend/src/services/WbotServices/wbotMonitor.js similarity index 100% rename from backend/src/services/wbotMonitor.js rename to backend/src/services/WbotServices/wbotMonitor.js diff --git a/backend/src/services/wbotMessageListener.js b/backend/src/services/wbotMessageListener.js deleted file mode 100644 index aed420d..0000000 --- a/backend/src/services/wbotMessageListener.js +++ /dev/null @@ -1,206 +0,0 @@ -const path = require("path"); -const fs = require("fs"); -const { Op } = require("sequelize"); -const { subHours } = require("date-fns"); -const Sentry = require("@sentry/node"); - -const Contact = require("../models/Contact"); -const Ticket = require("../models/Ticket"); -const Message = require("../models/Message"); -const Whatsapp = require("../models/Whatsapp"); - -const { getIO } = require("../libs/socket"); -const { getWbot, initWbot } = require("../libs/wbot"); - -const verifyContact = async (msgContact, profilePicUrl) => { - let contact = await Contact.findOne({ - where: { number: msgContact.number }, - }); - - if (contact) { - await contact.update({ profilePicUrl: profilePicUrl }); - } else { - contact = await Contact.create({ - name: msgContact.pushname || msgContact.number.toString(), - number: msgContact.number, - profilePicUrl: profilePicUrl, - }); - } - - return contact; -}; - -const verifyTicket = async (contact, whatsappId) => { - let ticket = await Ticket.findOne({ - where: { - status: { - [Op.or]: ["open", "pending"], - }, - contactId: contact.id, - }, - }); - - if (!ticket) { - ticket = await Ticket.findOne({ - where: { - createdAt: { [Op.between]: [subHours(new Date(), 2), new Date()] }, - contactId: contact.id, - }, - order: [["createdAt", "DESC"]], - }); - - if (ticket) { - await ticket.update({ status: "pending", userId: null }); - } - } - - if (!ticket) { - ticket = await Ticket.create({ - contactId: contact.id, - status: "pending", - whatsappId, - }); - } - - return ticket; -}; - -const handlMedia = async (msg, ticket) => { - const media = await msg.downloadMedia(); - let newMessage; - console.log("criando midia"); - if (media) { - if (!media.filename) { - let ext = media.mimetype.split("/")[1].split(";")[0]; - media.filename = `${new Date().getTime()}.${ext}`; - } - - fs.writeFile( - path.join(__dirname, "..", "..", "public", media.filename), - media.data, - "base64", - err => { - console.log(err); - } - ); - - newMessage = await ticket.createMessage({ - id: msg.id.id, - body: msg.body || media.filename, - fromMe: msg.fromMe, - mediaUrl: media.filename, - mediaType: media.mimetype.split("/")[0], - }); - await ticket.update({ lastMessage: msg.body || media.filename }); - } - - return newMessage; -}; - -const handleMessage = async (msg, ticket, contact) => { - const io = getIO(); - let newMessage; - - if (msg.hasMedia) { - newMessage = await handlMedia(msg, ticket); - } else { - newMessage = await ticket.createMessage({ - id: msg.id.id, - body: msg.body, - fromMe: msg.fromMe, - }); - await ticket.update({ lastMessage: msg.body }); - } - - const serializedMessage = { - ...newMessage.dataValues, - mediaUrl: `${ - newMessage.mediaUrl - ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${newMessage.mediaUrl}` - : "" - }`, - }; - - const serializaedTicket = { - ...ticket.dataValues, - contact: contact, - }; - - io.to(ticket.id).to("notification").emit("appMessage", { - action: "create", - message: serializedMessage, - ticket: serializaedTicket, - contact: contact, - }); -}; - -const wbotMessageListener = whatsapp => { - const whatsappId = whatsapp.id; - const wbot = getWbot(whatsappId); - const io = getIO(); - - wbot.on("message_create", async msg => { - // console.log(msg); - - if ( - msg.from === "status@broadcast" || - msg.type === "location" || - msg.type === "call_log" || - msg.author != null // Ignore Group Messages - ) { - return; - } - - try { - let msgContact; - - if (msg.fromMe) { - msgContact = await wbot.getContactById(msg.to); - } else { - msgContact = await msg.getContact(); - } - - const profilePicUrl = await msgContact.getProfilePicUrl(); - const contact = await verifyContact(msgContact, profilePicUrl); - const ticket = await verifyTicket(contact, whatsappId); - - //return if message was already created by messageController - if (msg.fromMe) { - const alreadyExists = await Message.findOne({ - where: { id: msg.id.id }, - }); - - if (alreadyExists) { - return; - } - } - - await handleMessage(msg, ticket, contact); - } catch (err) { - Sentry.captureException(err); - console.log(err); - } - }); - - wbot.on("message_ack", async (msg, ack) => { - try { - const messageToUpdate = await Message.findOne({ - where: { id: msg.id.id }, - }); - if (!messageToUpdate) { - return; - } - await messageToUpdate.update({ ack: ack }); - - io.to(messageToUpdate.ticketId).emit("appMessage", { - action: "update", - message: messageToUpdate, - }); - } catch (err) { - Sentry.captureException(err); - console.log(err); - } - }); -}; - -module.exports = wbotMessageListener; diff --git a/backend/yarn-error.log b/backend/yarn-error.log index 5674f65..ff7a8d9 100644 --- a/backend/yarn-error.log +++ b/backend/yarn-error.log @@ -1,8 +1,8 @@ Arguments: - /usr/bin/node /usr/share/yarn/bin/yarn.js sequelize db:migrate + /usr/bin/node /usr/share/yarn/bin/yarn.js add -D @types/qrcode-terminal PATH: - /home/canove/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/bin:/home/canove/.zinit/polaris/sbin:/home/canove/.zinit/polaris/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/ProgramData/chocolatey/bin:/mnt/c/Program Files/Microsoft VS Code/bin:/mnt/c/Program Files (x86)/GNU/GnuPG/pub:/mnt/c/Users/cassio/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/cassio/AppData/Local/GitHubDesktop/bin + /home/canove/.vscode-server/bin/58bb7b2331731bf72587010e943852e13e6fd3cf/bin:/home/canove/.zinit/polaris/sbin:/home/canove/.zinit/polaris/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/ProgramData/chocolatey/bin:/mnt/c/Program Files/Microsoft VS Code/bin:/mnt/c/Program Files (x86)/GNU/GnuPG/pub:/mnt/c/Users/cassio/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/cassio/AppData/Local/GitHubDesktop/bin Yarn version: 1.22.4 @@ -14,12 +14,17 @@ Platform: linux x64 Trace: - SyntaxError: /home/canove/code_learn/econowhats/backend/package.json: Unexpected token / in JSON at position 222 - at JSON.parse () - at /usr/share/yarn/lib/cli.js:1625:59 - at Generator.next () - at step (/usr/share/yarn/lib/cli.js:310:30) - at /usr/share/yarn/lib/cli.js:321:13 + Error: https://registry.yarnpkg.com/@types%2fqrcode-terminal: Not found + at Request.params.callback [as _callback] (/usr/share/yarn/lib/cli.js:66987:18) + at Request.self.callback (/usr/share/yarn/lib/cli.js:140748:22) + at Request.emit (events.js:315:20) + at Request. (/usr/share/yarn/lib/cli.js:141720:10) + at Request.emit (events.js:315:20) + at IncomingMessage. (/usr/share/yarn/lib/cli.js:141642:12) + at Object.onceWrapper (events.js:421:28) + at IncomingMessage.emit (events.js:327:22) + at endReadableNT (_stream_readable.js:1221:12) + at processTicksAndRejections (internal/process/task_queues.js:84:21) npm manifest: { @@ -29,8 +34,8 @@ npm manifest: "main": "index.js", "scripts": { "build": "tsc", + "start": "nodemon dist/server.js", "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" - // "sequelize": "ts-node-dev ./node_modules/.bin/sequelize" }, "author": "", "license": "MIT", @@ -55,11 +60,14 @@ npm manifest: "yup": "^0.29.3" }, "devDependencies": { + "@types/bcryptjs": "^2.4.2", "@types/bluebird": "^3.5.32", "@types/cors": "^2.8.7", "@types/express": "^4.17.8", + "@types/jsonwebtoken": "^8.5.0", "@types/multer": "^1.4.4", "@types/node": "^14.10.1", + "@types/socket.io": "^2.1.11", "@types/validator": "^13.1.0", "@types/yup": "^0.29.7", "@typescript-eslint/eslint-plugin": "^4.1.0", @@ -235,6 +243,11 @@ Lockfile: dependencies: defer-to-connect "^1.0.1" + "@types/bcryptjs@^2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.2.tgz#e3530eac9dd136bfdfb0e43df2c4c5ce1f77dfae" + integrity sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ== + "@types/bluebird@^3.5.32": version "3.5.32" resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.32.tgz#381e7b59e39f010d20bbf7e044e48f5caf1ab620" @@ -267,6 +280,13 @@ Lockfile: dependencies: "@types/express" "*" + "@types/engine.io@*": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/engine.io/-/engine.io-3.1.4.tgz#3d9472711d179daa7c95c051e50ad411e18a9bdc" + integrity sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w== + dependencies: + "@types/node" "*" + "@types/express-serve-static-core@*": version "4.17.12" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz#9a487da757425e4f267e7d1c5720226af7f89591" @@ -296,6 +316,13 @@ Lockfile: resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + "@types/jsonwebtoken@^8.5.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5" + integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== + dependencies: + "@types/node" "*" + "@types/mime@*": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" @@ -336,6 +363,14 @@ Lockfile: "@types/express-serve-static-core" "*" "@types/mime" "*" + "@types/socket.io@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-2.1.11.tgz#e0d6759880e5f9818d5297a3328b36641bae996b" + integrity sha512-bVprmqPhJMLb9ZCm8g0Xy8kwBFRbnanOWSxzWkDkkIwxTvud5tKMfAJymXX6LQbizUKCS1yima7JM4BeLqjNqA== + dependencies: + "@types/engine.io" "*" + "@types/node" "*" + "@types/strip-bom@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" From 9bd4c30d3ce6ee89db4d145173403da241805931 Mon Sep 17 00:00:00 2001 From: canove Date: Sun, 20 Sep 2020 18:03:57 -0300 Subject: [PATCH 39/54] added id to whatsbot session types --- backend/src/@types/WAWebJS.d.ts | 5 ----- backend/src/libs/wbot.ts | 8 ++++++-- 2 files changed, 6 insertions(+), 7 deletions(-) delete mode 100644 backend/src/@types/WAWebJS.d.ts diff --git a/backend/src/@types/WAWebJS.d.ts b/backend/src/@types/WAWebJS.d.ts deleted file mode 100644 index 0d17ab2..0000000 --- a/backend/src/@types/WAWebJS.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare namespace WAWebJS { - export interface ClientInfo { - meuId: number; - } -} diff --git a/backend/src/libs/wbot.ts b/backend/src/libs/wbot.ts index 4cbf5dd..68ef54b 100644 --- a/backend/src/libs/wbot.ts +++ b/backend/src/libs/wbot.ts @@ -3,7 +3,11 @@ import { Client } from "whatsapp-web.js"; import { getIO } from "./socket"; import Whatsapp from "../models/Whatsapp"; -const sessions: Client[] = []; +interface Session extends Client { + id?: number; +} + +const sessions: Session[] = []; export const initWbot = async (whatsapp: Whatsapp): Promise => { console.log("starting"); @@ -84,7 +88,7 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { } }; -export const getWbot = (whatsappId: number): Client | null => { +export const getWbot = (whatsappId: number): Session | null => { const sessionIndex = sessions.findIndex(s => s.id === whatsappId); if (sessionIndex === -1) { From dd9b61c2b907611db3073c03c26508fd9f8a748a Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 21 Sep 2020 11:48:53 -0300 Subject: [PATCH 40/54] added unread messages count to tickets --- backend/src/models/Ticket.ts | 26 ++++- ...sageListener.js => wbotMessageListener.ts} | 103 ++++++++++-------- 2 files changed, 80 insertions(+), 49 deletions(-) rename backend/src/services/WbotServices/{wbotMessageListener.js => wbotMessageListener.ts} (66%) diff --git a/backend/src/models/Ticket.ts b/backend/src/models/Ticket.ts index 5b77299..094df8f 100644 --- a/backend/src/models/Ticket.ts +++ b/backend/src/models/Ticket.ts @@ -9,7 +9,9 @@ import { ForeignKey, BelongsTo, HasMany, - AutoIncrement + AutoIncrement, + AfterFind, + BeforeUpdate } from "sequelize-typescript"; import Contact from "./Contact"; @@ -28,7 +30,7 @@ class Ticket extends Model { status: string; @Column(DataType.VIRTUAL) - unreadMessages: string; + unreadMessages: number; @Column lastMessage: string; @@ -62,6 +64,26 @@ class Ticket extends Model { @HasMany(() => Message) messages: Message[]; + + @AfterFind + static async countTicketsUnreadMessages(tickets: Ticket[]): Promise { + if (tickets && tickets.length > 0) { + await Promise.all( + tickets.map(async ticket => { + ticket.unreadMessages = await Message.count({ + where: { ticketId: ticket.id, read: false } + }); + }) + ); + } + } + + @BeforeUpdate + static async countTicketUnreadMessags(ticket: Ticket): Promise { + ticket.unreadMessages = await Message.count({ + where: { ticketId: ticket.id, read: false } + }); + } } export default Ticket; diff --git a/backend/src/services/WbotServices/wbotMessageListener.js b/backend/src/services/WbotServices/wbotMessageListener.ts similarity index 66% rename from backend/src/services/WbotServices/wbotMessageListener.js rename to backend/src/services/WbotServices/wbotMessageListener.ts index d8e6755..6578ba2 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.js +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -1,16 +1,18 @@ -const path = require("path"); -const fs = require("fs"); -const { Op } = require("sequelize"); -const { subHours } = require("date-fns"); -const Sentry = require("@sentry/node"); +import path from "path"; +import fs from "fs"; +import { Op } from "sequelize"; +import { subHours } from "date-fns"; +import Sentry from "@sentry/node"; -const Contact = require("../models/Contact"); -const Ticket = require("../models/Ticket"); -const Message = require("../models/Message"); -const Whatsapp = require("../models/Whatsapp"); +import { Client, Message } from "whatsapp-web.js"; -const { getIO } = require("../libs/socket"); -const { getWbot, initWbot } = require("../libs/wbot"); +import Contact from "../../models/Contact"; +import Ticket from "../../models/Ticket"; +import Message from "../../models/Message"; +import Whatsapp from "../../models/Whatsapp"; + +import { getIO } from "../../libs/socket"; +import { getWbot } from "../../libs/wbot"; const verifyContact = async (msgContact, profilePicUrl) => { let contact = await Contact.findOne({ @@ -134,53 +136,60 @@ const handleMessage = async (msg, ticket, contact) => { }); }; -const wbotMessageListener = whatsapp => { +const wbotMessageListener = (whatsapp: Whatsapp): void => { const whatsappId = whatsapp.id; const wbot = getWbot(whatsappId); const io = getIO(); - wbot.on("message_create", async msg => { - // console.log(msg); + if (!wbot) { + console.log("Wbot not initialized"); + return; + } - if ( - msg.from === "status@broadcast" || - msg.type === "location" || - msg.type === "call_log" || - msg.author != null // Ignore Group Messages - ) { - return; - } + wbot.on( + "message_create", + async (msg): Promise => { + // console.log(msg); - try { - let msgContact; - - if (msg.fromMe) { - msgContact = await wbot.getContactById(msg.to); - } else { - msgContact = await msg.getContact(); + if ( + msg.from === "status@broadcast" || + msg.type === "location" || + msg.author !== null // Ignore Group Messages + ) { + return; } - const profilePicUrl = await msgContact.getProfilePicUrl(); - const contact = await verifyContact(msgContact, profilePicUrl); - const ticket = await verifyTicket(contact, whatsappId); + try { + let msgContact; - //return if message was already created by messageController - if (msg.fromMe) { - const alreadyExists = await Message.findOne({ - where: { id: msg.id.id } - }); - - if (alreadyExists) { - return; + if (msg.fromMe) { + msgContact = await wbot.getContactById(msg.to); + } else { + msgContact = await msg.getContact(); } - } - await handleMessage(msg, ticket, contact); - } catch (err) { - Sentry.captureException(err); - console.log(err); + const profilePicUrl = await msgContact.getProfilePicUrl(); + const contact = await verifyContact(msgContact, profilePicUrl); + const ticket = await verifyTicket(contact, whatsappId); + + //return if message was already created by messageController + if (msg.fromMe) { + const alreadyExists = await Message.findOne({ + where: { id: msg.id.id } + }); + + if (alreadyExists) { + return; + } + } + + await handleMessage(msg, ticket, contact); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } } - }); + ); wbot.on("message_ack", async (msg, ack) => { try { @@ -203,4 +212,4 @@ const wbotMessageListener = whatsapp => { }); }; -module.exports = wbotMessageListener; +export default wbotMessageListener; From 391cd5495cc4b7b61a1f529dbabb9a9a75c920d5 Mon Sep 17 00:00:00 2001 From: canove Date: Mon, 21 Sep 2020 18:49:38 -0300 Subject: [PATCH 41/54] start converting wbot functions to typescript --- backend/src/models/Message.ts | 14 +- backend/src/server.ts | 4 +- .../TicketServices/CreateTicketService.ts | 2 +- .../WbotServices/wbotMessageListener.ts | 207 +++++++++--------- 4 files changed, 121 insertions(+), 106 deletions(-) diff --git a/backend/src/models/Message.ts b/backend/src/models/Message.ts index 83c8cfe..b187fb4 100644 --- a/backend/src/models/Message.ts +++ b/backend/src/models/Message.ts @@ -16,9 +16,8 @@ import Ticket from "./Ticket"; @Table class Message extends Model { @PrimaryKey - @AutoIncrement @Column - id: number; + id: string; @Default(0) @Column @@ -35,8 +34,15 @@ class Message extends Model { @Column(DataType.TEXT) body: string; - @Column - mediaUrl: string; + @Column(DataType.STRING) + get mediaUrl(): string | null { + if (this.getDataValue("mediaUrl")) { + return `${process.env.BACKEND_URL}:${ + process.env.PROXY_PORT + }/public/${this.getDataValue("mediaUrl")}`; + } + return null; + } @Column mediaType: string; diff --git a/backend/src/server.ts b/backend/src/server.ts index e16dcfd..ec6b241 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -18,6 +18,7 @@ import { initWbot } from "./libs/wbot"; // const wbotMessageListener = require("./services/wbotMessageListener"); // const wbotMonitor = require("./services/wbotMonitor"); import Whatsapp from "./models/Whatsapp"; +import wbotMessageListener from "./services/WbotServices/wbotMessageListener"; Sentry.init({ dsn: process.env.SENTRY_DSN }); @@ -68,8 +69,7 @@ const startWhatsAppSessions = async () => { whatsapps.forEach(whatsapp => { initWbot(whatsapp) .then(() => { - console.log("initialized!!"); - // wbotMessageListener(whatsapp); + wbotMessageListener(whatsapp); // wbotMonitor(whatsapp); }) .catch(err => console.log(err)); diff --git a/backend/src/services/TicketServices/CreateTicketService.ts b/backend/src/services/TicketServices/CreateTicketService.ts index c52e6d6..6dfefff 100644 --- a/backend/src/services/TicketServices/CreateTicketService.ts +++ b/backend/src/services/TicketServices/CreateTicketService.ts @@ -17,7 +17,7 @@ const CreateTicketService = async ({ throw new AppError("No default WhatsApp found. Check Connection page."); } - const { id } = await defaultWhatsapp.$create("ticket", { + const { id }: Ticket = await defaultWhatsapp.$create("ticket", { contactId, status }); diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 6578ba2..2ab9216 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -4,7 +4,10 @@ import { Op } from "sequelize"; import { subHours } from "date-fns"; import Sentry from "@sentry/node"; -import { Client, Message } from "whatsapp-web.js"; +import { + Contact as WbotContact, + Message as WbotMessage +} from "whatsapp-web.js"; import Contact from "../../models/Contact"; import Ticket from "../../models/Ticket"; @@ -13,42 +16,54 @@ import Whatsapp from "../../models/Whatsapp"; import { getIO } from "../../libs/socket"; import { getWbot } from "../../libs/wbot"; +import AppError from "../../errors/AppError"; +import ShowTicketService from "../TicketServices/ShowTicketService"; -const verifyContact = async (msgContact, profilePicUrl) => { +const verifyContact = async ( + msgContact: WbotContact, + profilePicUrl: string +): Promise => { let contact = await Contact.findOne({ where: { number: msgContact.number } }); if (contact) { - await contact.update({ profilePicUrl: profilePicUrl }); + await contact.update({ profilePicUrl }); } else { contact = await Contact.create({ name: msgContact.pushname || msgContact.number.toString(), number: msgContact.number, - profilePicUrl: profilePicUrl + profilePicUrl }); } return contact; }; -const verifyTicket = async (contact, whatsappId) => { +const verifyTicket = async ( + contact: Contact, + whatsappId: number +): Promise => { let ticket = await Ticket.findOne({ where: { status: { [Op.or]: ["open", "pending"] }, contactId: contact.id - } + }, + include: ["contact"] }); if (!ticket) { ticket = await Ticket.findOne({ where: { - createdAt: { [Op.between]: [subHours(new Date(), 2), new Date()] }, + createdAt: { + [Op.between]: [+subHours(new Date(), 2), +new Date()] + }, contactId: contact.id }, - order: [["createdAt", "DESC"]] + order: [["createdAt", "DESC"]], + include: ["contact"] }); if (ticket) { @@ -57,56 +72,65 @@ const verifyTicket = async (contact, whatsappId) => { } if (!ticket) { - ticket = await Ticket.create({ + const { id } = await Ticket.create({ contactId: contact.id, status: "pending", whatsappId }); + + ticket = await ShowTicketService(id); } return ticket; }; -const handlMedia = async (msg, ticket) => { +const handlMedia = async ( + msg: WbotMessage, + ticket: Ticket +): Promise => { const media = await msg.downloadMedia(); - let newMessage; - console.log("criando midia"); - if (media) { - if (!media.filename) { - let ext = media.mimetype.split("/")[1].split(";")[0]; - media.filename = `${new Date().getTime()}.${ext}`; - } - fs.writeFile( - path.join(__dirname, "..", "..", "public", media.filename), - media.data, - "base64", - err => { - console.log(err); - } - ); - - newMessage = await ticket.createMessage({ - id: msg.id.id, - body: msg.body || media.filename, - fromMe: msg.fromMe, - mediaUrl: media.filename, - mediaType: media.mimetype.split("/")[0] - }); - await ticket.update({ lastMessage: msg.body || media.filename }); + if (!media) { + throw new AppError("Cannot download media from whatsapp."); } + if (!media.filename) { + const ext = media.mimetype.split("/")[1].split(";")[0]; + media.filename = `${new Date().getTime()}.${ext}`; + } + + fs.writeFile( + path.join(__dirname, "..", "..", "..", "public", media.filename), + media.data, + "base64", + err => { + console.log(err); + } + ); + + const newMessage: Message = await ticket.$create("message", { + id: msg.id.id, + body: msg.body || media.filename, + fromMe: msg.fromMe, + mediaUrl: media.filename, + mediaType: media.mimetype.split("/")[0] + }); + await ticket.update({ lastMessage: msg.body || media.filename }); return newMessage; }; -const handleMessage = async (msg, ticket, contact) => { +const handleMessage = async ( + msg: WbotMessage, + ticket: Ticket, + contact: Contact +) => { const io = getIO(); - let newMessage; + let newMessage: Message; if (msg.hasMedia) { newMessage = await handlMedia(msg, ticket); } else { - newMessage = await ticket.createMessage({ + newMessage = await ticket.$create("message", { id: msg.id.id, body: msg.body, fromMe: msg.fromMe @@ -114,25 +138,11 @@ const handleMessage = async (msg, ticket, contact) => { await ticket.update({ lastMessage: msg.body }); } - const serializedMessage = { - ...newMessage.dataValues, - mediaUrl: `${ - newMessage.mediaUrl - ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${newMessage.mediaUrl}` - : "" - }` - }; - - const serializaedTicket = { - ...ticket.dataValues, - contact: contact - }; - - io.to(ticket.id).to("notification").emit("appMessage", { + io.to(ticket.id.toString()).to("notification").emit("appMessage", { action: "create", - message: serializedMessage, - ticket: serializaedTicket, - contact: contact + message: newMessage, + ticket, + contact }); }; @@ -146,50 +156,49 @@ const wbotMessageListener = (whatsapp: Whatsapp): void => { return; } - wbot.on( - "message_create", - async (msg): Promise => { - // console.log(msg); + wbot.on("message_create", async msg => { + // console.log(msg); - if ( - msg.from === "status@broadcast" || - msg.type === "location" || - msg.author !== null // Ignore Group Messages - ) { - return; - } - - try { - let msgContact; - - if (msg.fromMe) { - msgContact = await wbot.getContactById(msg.to); - } else { - msgContact = await msg.getContact(); - } - - const profilePicUrl = await msgContact.getProfilePicUrl(); - const contact = await verifyContact(msgContact, profilePicUrl); - const ticket = await verifyTicket(contact, whatsappId); - - //return if message was already created by messageController - if (msg.fromMe) { - const alreadyExists = await Message.findOne({ - where: { id: msg.id.id } - }); - - if (alreadyExists) { - return; - } - } - - await handleMessage(msg, ticket, contact); - } catch (err) { - Sentry.captureException(err); - console.log(err); - } + if ( + msg.from === "status@broadcast" || + msg.type === "location" || + msg.type === "call_log" || + msg.author // Ignore Group Messages + ) { + return; } - ); + + try { + let msgContact: WbotContact; + + if (msg.fromMe) { + msgContact = await wbot.getContactById(msg.to); + } else { + msgContact = await msg.getContact(); + } + + const profilePicUrl = await msgContact.getProfilePicUrl(); + const contact = await verifyContact(msgContact, profilePicUrl); + const ticket = await verifyTicket(contact, whatsappId); + + // return if message was already created by messageController + + if (msg.fromMe) { + const alreadyExists = await Message.findOne({ + where: { id: msg.id.id } + }); + + if (alreadyExists) { + return; + } + } + + await handleMessage(msg, ticket, contact); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } + }); wbot.on("message_ack", async (msg, ack) => { try { @@ -199,9 +208,9 @@ const wbotMessageListener = (whatsapp: Whatsapp): void => { if (!messageToUpdate) { return; } - await messageToUpdate.update({ ack: ack }); + await messageToUpdate.update({ ack }); - io.to(messageToUpdate.ticketId).emit("appMessage", { + io.to(messageToUpdate.ticketId.toString()).emit("appMessage", { action: "update", message: messageToUpdate }); From 48dbf7e859f26dcdc0ab81a643609bd707d2323e Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 09:15:18 -0300 Subject: [PATCH 42/54] migrated receive message logic to typescript --- backend/src/controllers/ContactController.ts | 54 +-- backend/src/controllers/MessageController.ts | 186 ++------ .../src/controllersOld/MessageController.js | 406 +++++++++--------- backend/src/helpers/GetTicketWbot.ts | 22 + .../src/helpers/SetTicketMessagesAsRead.ts | 34 ++ backend/src/libs/wbot.ts | 9 +- .../ContactServices/CreateContactService.ts | 1 + .../MessageServices/CreateMessageService.ts | 17 +- .../TicketServices/ShowTicketService.ts | 2 +- .../WbotServices/CheckIsValidContact.ts | 23 + .../services/WbotServices/GetProfilePicUrl.ts | 14 + .../WbotServices/SendWhatsAppMedia.ts | 29 ++ .../WbotServices/SendWhatsAppMessage.ts | 25 ++ .../WbotServices/wbotMessageListener.ts | 4 +- 14 files changed, 423 insertions(+), 403 deletions(-) create mode 100644 backend/src/helpers/GetTicketWbot.ts create mode 100644 backend/src/helpers/SetTicketMessagesAsRead.ts create mode 100644 backend/src/services/WbotServices/CheckIsValidContact.ts create mode 100644 backend/src/services/WbotServices/GetProfilePicUrl.ts create mode 100644 backend/src/services/WbotServices/SendWhatsAppMedia.ts create mode 100644 backend/src/services/WbotServices/SendWhatsAppMessage.ts diff --git a/backend/src/controllers/ContactController.ts b/backend/src/controllers/ContactController.ts index 6242fd4..3b50675 100644 --- a/backend/src/controllers/ContactController.ts +++ b/backend/src/controllers/ContactController.ts @@ -7,7 +7,8 @@ import ShowContactService from "../services/ContactServices/ShowContactService"; import UpdateContactService from "../services/ContactServices/UpdateContactService"; import DeleteContactService from "../services/ContactServices/DeleteContactService"; -// const { getWbot } = require("../libs/wbot"); +import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact"; +import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl"; type IndexQuery = { searchParam: string; @@ -37,54 +38,17 @@ interface ContactData { } export const store = async (req: Request, res: Response): Promise => { - const { name, number, email, extraInfo }: ContactData = req.body; + const newContact: ContactData = req.body; + + await CheckIsValidContact(newContact.number); + + const profilePicUrl = await GetProfilePicUrl(newContact.number); const contact = await CreateContactService({ - name, - number, - email, - extraInfo + ...newContact, + profilePicUrl }); - // const defaultWhatsapp = await Whatsapp.findOne({ - // where: { default: true } - // }); - - // if (!defaultWhatsapp) { - // return res - // .status(404) - // .json({ error: "No default WhatsApp found. Check Connection page." }); - // } - - // const wbot = getWbot(defaultWhatsapp); - - // try { - // const isValidNumber = await wbot.isRegisteredUser( - // `${newContact.number}@c.us` - // ); - // if (!isValidNumber) { - // return res - // .status(400) - // .json({ error: "The suplied number is not a valid Whatsapp number" }); - // } - // } catch (err) { - // console.log(err); - // return res.status(500).json({ - // error: "Could not check whatsapp contact. Check connection page." - // }); - // } - - // const profilePicUrl = await wbot.getProfilePicUrl( - // `${newContact.number}@c.us` - // ); - - // const contact = await Contact.create( - // { ...newContact, profilePicUrl }, - // { - // include: "extraInfo" - // } - // ); - const io = getIO(); io.emit("contact", { action: "create", diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts index 4d22ec7..a574cac 100644 --- a/backend/src/controllers/MessageController.ts +++ b/backend/src/controllers/MessageController.ts @@ -1,8 +1,15 @@ import { Request, Response } from "express"; // import { getIO } from "../libs/socket"; +import { Message as WbotMessage } from "whatsapp-web.js"; +import SetTicketMessagesAsRead from "../helpers/SetTicketMessagesAsRead"; +import { getIO } from "../libs/socket"; + import CreateMessageService from "../services/MessageServices/CreateMessageService"; import ListMessagesService from "../services/MessageServices/ListMessagesService"; +import ShowTicketService from "../services/TicketServices/ShowTicketService"; +import SendWhatsAppMedia from "../services/WbotServices/SendWhatsAppMedia"; +import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage"; // import Sequelize from "sequelize"; // import { MessageMedia } from "whatsapp-web.js"; @@ -14,39 +21,17 @@ import ListMessagesService from "../services/MessageServices/ListMessagesService // import Ticket from "../models/Ticket"; // import { getWbot } from "../libs/wbot"; -// const setMessagesAsRead = async ticket => { -// const io = getIO(); -// const wbot = getWbot(ticket.whatsappId); - -// await Message.update( -// { read: true }, -// { -// where: { -// ticketId: ticket.id, -// read: false -// } -// } -// ); - -// try { -// await wbot.sendSeen(`${ticket.contact.number}@c.us`); -// } catch (err) { -// console.log( -// "Could not mark messages as read. Maybe whatsapp session disconnected?" -// ); -// } - -// io.to("notification").emit("ticket", { -// action: "updateUnread", -// ticketId: ticket.id -// }); -// }; - type IndexQuery = { searchParam: string; pageNumber: string; }; +type MessageData = { + body: string; + fromMe: boolean; + read: boolean; +}; + export const index = async (req: Request, res: Response): Promise => { const { ticketId } = req.params; const { searchParam, pageNumber } = req.query as IndexQuery; @@ -57,134 +42,49 @@ export const index = async (req: Request, res: Response): Promise => { ticketId }); - // const ticketMessages = await ticket.getMessages({ - // where: whereCondition, - // limit, - // offset, - // order: [["createdAt", "DESC"]] - // }); + await SetTicketMessagesAsRead(ticket); - // const count = await ticket.countMessages(); - - // const serializedMessages = ticketMessages.map(message => { - // return { - // ...message.dataValues, - // mediaUrl: `${ - // message.mediaUrl - // ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` - // : "" - // }` - // }; - // }); - - // const hasMore = count > offset + ticketMessages.length; - - // return res.json({ - // messages: serializedMessages.reverse(), - // ticket, - // count, - // hasMore - // }); return res.json({ count, messages, ticket, hasMore }); }; export const store = async (req: Request, res: Response): Promise => { - // const io = getIO(); - const { ticketId } = req.params; - const messageData = req.body; + const { body, fromMe, read }: MessageData = req.body; + const media = req.file; - const message = await CreateMessageService({ messageData, ticketId }); + const ticket = await ShowTicketService(ticketId); - // const media = req.file; - // let sentMessage; + let sentMessage: WbotMessage; - // const ticket = await Ticket.findByPk(ticketId, { - // include: [ - // { - // model: Contact, - // as: "contact", - // attributes: ["number", "name", "profilePicUrl"] - // } - // ] - // }); + if (media) { + sentMessage = await SendWhatsAppMedia({ media, ticket }); + } else { + sentMessage = await SendWhatsAppMessage({ body, ticket }); + } - // if (!ticket) { - // return res.status(404).json({ error: "No ticket found with this ID" }); - // } + const newMessage = { + id: sentMessage.id.id, + body, + fromMe, + read, + mediaType: sentMessage.type, + mediaUrl: media?.filename + }; - // if (!ticket.whatsappId) { - // const defaultWhatsapp = await Whatsapp.findOne({ - // where: { default: true } - // }); + const message = await CreateMessageService({ + messageData: newMessage, + ticketId: ticket.id + }); - // if (!defaultWhatsapp) { - // return res - // .status(404) - // .json({ error: "No default WhatsApp found. Check Connection page." }); - // } + const io = getIO(); + io.to(ticketId).to("notification").emit("appMessage", { + action: "create", + message, + ticket, + contact: ticket.contact + }); - // await ticket.setWhatsapp(defaultWhatsapp); - // } - - // const wbot = getWbot(ticket.whatsappId); - - // try { - // if (media) { - // const newMedia = MessageMedia.fromFilePath(req.file.path); - - // message.mediaUrl = req.file.filename; - // if (newMedia.mimetype) { - // message.mediaType = newMedia.mimetype.split("/")[0]; - // } else { - // message.mediaType = "other"; - // } - - // sentMessage = await wbot.sendMessage( - // `${ticket.contact.number}@c.us`, - // newMedia - // ); - - // await ticket.update({ lastMessage: message.mediaUrl }); - // } else { - // sentMessage = await wbot.sendMessage( - // `${ticket.contact.number}@c.us`, - // message.body - // ); - // await ticket.update({ lastMessage: message.body }); - // } - // } catch (err) { - // console.log( - // "Could not create whatsapp message. Is session details valid? " - // ); - // } - - // if (sentMessage) { - // message.id = sentMessage.id.id; - // const newMessage = await ticket.createMessage(message); - - // const serialziedMessage = { - // ...newMessage.dataValues, - // mediaUrl: `${ - // message.mediaUrl - // ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` - // : "" - // }` - // }; - - // io.to(ticketId).to("notification").emit("appMessage", { - // action: "create", - // message: serialziedMessage, - // ticket, - // contact: ticket.contact - // }); - - // await setMessagesAsRead(ticket); + await SetTicketMessagesAsRead(ticket); return res.status(200).json(message); }; - -// return res -// .status(500) -// .json({ error: "Cannot sent whatsapp message. Check connection page." }); -// }; diff --git a/backend/src/controllersOld/MessageController.js b/backend/src/controllersOld/MessageController.js index 33a04b7..a34cd3f 100644 --- a/backend/src/controllersOld/MessageController.js +++ b/backend/src/controllersOld/MessageController.js @@ -1,203 +1,203 @@ -const Message = require("../models/Message"); -const Contact = require("../models/Contact"); -const User = require("../models/User"); -const Whatsapp = require("../models/Whatsapp"); - -const Ticket = require("../models/Ticket"); -const { getIO } = require("../libs/socket"); -const { getWbot } = require("../libs/wbot"); -const Sequelize = require("sequelize"); - -const { MessageMedia } = require("whatsapp-web.js"); - -const setMessagesAsRead = async ticket => { - const io = getIO(); - const wbot = getWbot(ticket.whatsappId); - - await Message.update( - { read: true }, - { - where: { - ticketId: ticket.id, - read: false, - }, - } - ); - - try { - await wbot.sendSeen(`${ticket.contact.number}@c.us`); - } catch (err) { - console.log( - "Could not mark messages as read. Maybe whatsapp session disconnected?" - ); - } - - io.to("notification").emit("ticket", { - action: "updateUnread", - ticketId: ticket.id, - }); -}; - -exports.index = async (req, res, next) => { - const { ticketId } = req.params; - const { searchParam = "", pageNumber = 1 } = req.query; - - const whereCondition = { - body: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("body")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ), - }; - - const limit = 20; - const offset = limit * (pageNumber - 1); - - const ticket = await Ticket.findByPk(ticketId, { - include: [ - { - model: Contact, - as: "contact", - include: "extraInfo", - attributes: ["id", "name", "number", "profilePicUrl"], - }, - { - model: User, - as: "user", - }, - ], - }); - - if (!ticket) { - return res.status(404).json({ error: "No ticket found with this ID" }); - } - - await setMessagesAsRead(ticket); - - const ticketMessages = await ticket.getMessages({ - where: whereCondition, - limit, - offset, - order: [["createdAt", "DESC"]], - }); - - const count = await ticket.countMessages(); - - const serializedMessages = ticketMessages.map(message => { - return { - ...message.dataValues, - mediaUrl: `${ - message.mediaUrl - ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` - : "" - }`, - }; - }); - - const hasMore = count > offset + ticketMessages.length; - - return res.json({ - messages: serializedMessages.reverse(), - ticket, - count, - hasMore, - }); -}; - -exports.store = async (req, res, next) => { - const io = getIO(); - - const { ticketId } = req.params; - const message = req.body; - const media = req.file; - let sentMessage; - - const ticket = await Ticket.findByPk(ticketId, { - include: [ - { - model: Contact, - as: "contact", - attributes: ["number", "name", "profilePicUrl"], - }, - ], - }); - - if (!ticket) { - return res.status(404).json({ error: "No ticket found with this ID" }); - } - - if (!ticket.whatsappId) { - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true }, - }); - - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } - - await ticket.setWhatsapp(defaultWhatsapp); - } - - const wbot = getWbot(ticket.whatsappId); - - try { - if (media) { - const newMedia = MessageMedia.fromFilePath(req.file.path); - - message.mediaUrl = req.file.filename; - if (newMedia.mimetype) { - message.mediaType = newMedia.mimetype.split("/")[0]; - } else { - message.mediaType = "other"; - } - - sentMessage = await wbot.sendMessage( - `${ticket.contact.number}@c.us`, - newMedia - ); - - await ticket.update({ lastMessage: message.mediaUrl }); - } else { - sentMessage = await wbot.sendMessage( - `${ticket.contact.number}@c.us`, - message.body - ); - await ticket.update({ lastMessage: message.body }); - } - } catch (err) { - console.log( - "Could not create whatsapp message. Is session details valid? " - ); - } - - if (sentMessage) { - message.id = sentMessage.id.id; - const newMessage = await ticket.createMessage(message); - - const serialziedMessage = { - ...newMessage.dataValues, - mediaUrl: `${ - message.mediaUrl - ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` - : "" - }`, - }; - - io.to(ticketId).to("notification").emit("appMessage", { - action: "create", - message: serialziedMessage, - ticket: ticket, - contact: ticket.contact, - }); - - await setMessagesAsRead(ticket); - - return res.status(200).json({ newMessage, ticket }); - } - - return res - .status(500) - .json({ error: "Cannot sent whatsapp message. Check connection page." }); -}; +const Message = require("../models/Message"); +const Contact = require("../models/Contact"); +const User = require("../models/User"); +const Whatsapp = require("../models/Whatsapp"); + +const Ticket = require("../models/Ticket"); +const { getIO } = require("../libs/socket"); +const { getWbot } = require("../libs/wbot"); +const Sequelize = require("sequelize"); + +const { MessageMedia } = require("whatsapp-web.js"); + +const setMessagesAsRead = async ticket => { + const io = getIO(); + const wbot = getWbot(ticket.whatsappId); + + await Message.update( + { read: true }, + { + where: { + ticketId: ticket.id, + read: false + } + } + ); + + try { + await wbot.sendSeen(`${ticket.contact.number}@c.us`); + } catch (err) { + console.log( + "Could not mark messages as read. Maybe whatsapp session disconnected?" + ); + } + + io.to("notification").emit("ticket", { + action: "updateUnread", + ticketId: ticket.id + }); +}; + +exports.index = async (req, res, next) => { + const { ticketId } = req.params; + const { searchParam = "", pageNumber = 1 } = req.query; + + const whereCondition = { + body: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("body")), + "LIKE", + "%" + searchParam.toLowerCase() + "%" + ) + }; + + const limit = 20; + const offset = limit * (pageNumber - 1); + + const ticket = await Ticket.findByPk(ticketId, { + include: [ + { + model: Contact, + as: "contact", + include: "extraInfo", + attributes: ["id", "name", "number", "profilePicUrl"] + }, + { + model: User, + as: "user" + } + ] + }); + + if (!ticket) { + return res.status(404).json({ error: "No ticket found with this ID" }); + } + + await setMessagesAsRead(ticket); + + const ticketMessages = await ticket.getMessages({ + where: whereCondition, + limit, + offset, + order: [["createdAt", "DESC"]] + }); + + const count = await ticket.countMessages(); + + const serializedMessages = ticketMessages.map(message => { + return { + ...message.dataValues, + mediaUrl: `${ + message.mediaUrl + ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` + : "" + }` + }; + }); + + const hasMore = count > offset + ticketMessages.length; + + return res.json({ + messages: serializedMessages.reverse(), + ticket, + count, + hasMore + }); +}; + +exports.store = async (req, res, next) => { + const io = getIO(); + + const { ticketId } = req.params; + const message = req.body; + const media = req.file; + let sentMessage; + + const ticket = await Ticket.findByPk(ticketId, { + include: [ + { + model: Contact, + as: "contact", + attributes: ["number", "name", "profilePicUrl"] + } + ] + }); + + if (!ticket) { + return res.status(404).json({ error: "No ticket found with this ID" }); + } + + if (!ticket.whatsappId) { + const defaultWhatsapp = await Whatsapp.findOne({ + where: { default: true } + }); + + if (!defaultWhatsapp) { + return res + .status(404) + .json({ error: "No default WhatsApp found. Check Connection page." }); + } + + await ticket.setWhatsapp(defaultWhatsapp); + } + + const wbot = getWbot(ticket.whatsappId); + + try { + if (media) { + const newMedia = MessageMedia.fromFilePath(req.file.path); + + message.mediaUrl = req.file.filename; + if (newMedia.mimetype) { + message.mediaType = newMedia.mimetype.split("/")[0]; + } else { + message.mediaType = "other"; + } + + sentMessage = await wbot.sendMessage( + `${ticket.contact.number}@c.us`, + newMedia + ); + + await ticket.update({ lastMessage: message.mediaUrl }); + } else { + sentMessage = await wbot.sendMessage( + `${ticket.contact.number}@c.us`, + message.body + ); + await ticket.update({ lastMessage: message.body }); + } + } catch (err) { + console.log( + "Could not create whatsapp message. Is session details valid? " + ); + } + + if (sentMessage) { + message.id = sentMessage.id.id; + const newMessage = await ticket.createMessage(message); + + const serialziedMessage = { + ...newMessage.dataValues, + mediaUrl: `${ + message.mediaUrl + ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` + : "" + }` + }; + + io.to(ticketId).to("notification").emit("appMessage", { + action: "create", + message: serialziedMessage, + ticket: ticket, + contact: ticket.contact + }); + + await setMessagesAsRead(ticket); + + return res.status(200).json({ newMessage, ticket }); + } + + return res + .status(500) + .json({ error: "Cannot sent whatsapp message. Check connection page." }); +}; diff --git a/backend/src/helpers/GetTicketWbot.ts b/backend/src/helpers/GetTicketWbot.ts new file mode 100644 index 0000000..50f6422 --- /dev/null +++ b/backend/src/helpers/GetTicketWbot.ts @@ -0,0 +1,22 @@ +import { Client as Session } from "whatsapp-web.js"; +import { getWbot } from "../libs/wbot"; +import AppError from "../errors/AppError"; +import GetDefaultWhatsApp from "./GetDefaultWhatsApp"; +import Ticket from "../models/Ticket"; + +const GetTicketWbot = async (ticket: Ticket): Promise => { + if (!ticket.whatsappId) { + const defaultWhatsapp = await GetDefaultWhatsApp(); + + if (!defaultWhatsapp) { + throw new AppError("No default WhatsApp found. Check Connection page."); + } + await ticket.$set("whatsapp", defaultWhatsapp); + } + + const wbot = getWbot(ticket.whatsappId); + + return wbot; +}; + +export default GetTicketWbot; diff --git a/backend/src/helpers/SetTicketMessagesAsRead.ts b/backend/src/helpers/SetTicketMessagesAsRead.ts new file mode 100644 index 0000000..b78cdd1 --- /dev/null +++ b/backend/src/helpers/SetTicketMessagesAsRead.ts @@ -0,0 +1,34 @@ +import { getIO } from "../libs/socket"; +import { getWbot } from "../libs/wbot"; +import Message from "../models/Message"; +import Ticket from "../models/Ticket"; + +const SetTicketMessagesAsRead = async (ticket: Ticket): Promise => { + const wbot = getWbot(ticket.whatsappId); + + await Message.update( + { read: true }, + { + where: { + ticketId: ticket.id, + read: false + } + } + ); + + try { + await wbot.sendSeen(`${ticket.contact.number}@c.us`); + } catch (err) { + console.log( + "Could not mark messages as read. Maybe whatsapp session disconnected?" + ); + } + + const io = getIO(); + io.to("notification").emit("ticket", { + action: "updateUnread", + ticketId: ticket.id + }); +}; + +export default SetTicketMessagesAsRead; diff --git a/backend/src/libs/wbot.ts b/backend/src/libs/wbot.ts index 68ef54b..49c0224 100644 --- a/backend/src/libs/wbot.ts +++ b/backend/src/libs/wbot.ts @@ -2,6 +2,7 @@ import qrCode from "qrcode-terminal"; import { Client } from "whatsapp-web.js"; import { getIO } from "./socket"; import Whatsapp from "../models/Whatsapp"; +import AppError from "../errors/AppError"; interface Session extends Client { id?: number; @@ -10,7 +11,6 @@ interface Session extends Client { const sessions: Session[] = []; export const initWbot = async (whatsapp: Whatsapp): Promise => { - console.log("starting"); try { const io = getIO(); const sessionName = whatsapp.name; @@ -26,7 +26,7 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { sessions.splice(sessionIndex, 1); } - const wbot = new Client({ + const wbot: Session = new Client({ session: sessionCfg, restartOnAuthFail: true }); @@ -88,12 +88,11 @@ export const initWbot = async (whatsapp: Whatsapp): Promise => { } }; -export const getWbot = (whatsappId: number): Session | null => { +export const getWbot = (whatsappId: number): Session => { const sessionIndex = sessions.findIndex(s => s.id === whatsappId); if (sessionIndex === -1) { - console.log("This Wbot session is not initialized"); - return null; + throw new AppError("This WhatsApp session is not initialized."); } return sessions[sessionIndex]; }; diff --git a/backend/src/services/ContactServices/CreateContactService.ts b/backend/src/services/ContactServices/CreateContactService.ts index ecc8955..e1d258d 100644 --- a/backend/src/services/ContactServices/CreateContactService.ts +++ b/backend/src/services/ContactServices/CreateContactService.ts @@ -10,6 +10,7 @@ interface Request { name: string; number: string; email?: string; + profilePicUrl?: string; extraInfo?: ExtraInfo[]; } diff --git a/backend/src/services/MessageServices/CreateMessageService.ts b/backend/src/services/MessageServices/CreateMessageService.ts index 8957672..700e07a 100644 --- a/backend/src/services/MessageServices/CreateMessageService.ts +++ b/backend/src/services/MessageServices/CreateMessageService.ts @@ -1,9 +1,18 @@ +import AppError from "../../errors/AppError"; import Message from "../../models/Message"; import ShowTicketService from "../TicketServices/ShowTicketService"; +interface MessageData { + id: string; + body: string; + fromMe: boolean; + read: boolean; + mediaType: string; + mediaUrl?: string; +} interface Request { - ticketId: string; - messageData: Message; + ticketId: string | number; + messageData: MessageData; } const CreateMessageService = async ({ @@ -13,10 +22,10 @@ const CreateMessageService = async ({ const ticket = await ShowTicketService(ticketId); if (!ticket) { - throw new Error("No ticket found with this ID"); + throw new AppError("No ticket found with this ID"); } - const message = Message.create({ ...messageData, ticketId }); + const message: Message = await ticket.$create("message", messageData); return message; }; diff --git a/backend/src/services/TicketServices/ShowTicketService.ts b/backend/src/services/TicketServices/ShowTicketService.ts index 4103685..9379aff 100644 --- a/backend/src/services/TicketServices/ShowTicketService.ts +++ b/backend/src/services/TicketServices/ShowTicketService.ts @@ -21,7 +21,7 @@ const ShowTicketService = async (id: string | number): Promise => { }); if (!ticket) { - throw new AppError("No ticket found with this conditions.", 404); + throw new AppError("No ticket found with this ID"); } return ticket; diff --git a/backend/src/services/WbotServices/CheckIsValidContact.ts b/backend/src/services/WbotServices/CheckIsValidContact.ts new file mode 100644 index 0000000..dc9edd6 --- /dev/null +++ b/backend/src/services/WbotServices/CheckIsValidContact.ts @@ -0,0 +1,23 @@ +import AppError from "../../errors/AppError"; +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import { getWbot } from "../../libs/wbot"; + +const CheckIsValidContact = async (number: string): Promise => { + const defaultWhatsapp = await GetDefaultWhatsApp(); + + const wbot = getWbot(defaultWhatsapp.id); + + try { + const isValidNumber = await wbot.isRegisteredUser(`${number}@c.us`); + if (!isValidNumber) { + throw new AppError("The suplied number is not a valid Whatsapp number"); + } + } catch (err) { + console.log(err); + throw new AppError( + "Could not valid WhatsApp contact. Check connections page" + ); + } +}; + +export default CheckIsValidContact; diff --git a/backend/src/services/WbotServices/GetProfilePicUrl.ts b/backend/src/services/WbotServices/GetProfilePicUrl.ts new file mode 100644 index 0000000..6e5829a --- /dev/null +++ b/backend/src/services/WbotServices/GetProfilePicUrl.ts @@ -0,0 +1,14 @@ +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import { getWbot } from "../../libs/wbot"; + +const GetProfilePicUrl = async (number: string): Promise => { + const defaultWhatsapp = await GetDefaultWhatsApp(); + + const wbot = getWbot(defaultWhatsapp.id); + + const profilePicUrl = await wbot.getProfilePicUrl(`${number}@c.us`); + + return profilePicUrl; +}; + +export default GetProfilePicUrl; diff --git a/backend/src/services/WbotServices/SendWhatsAppMedia.ts b/backend/src/services/WbotServices/SendWhatsAppMedia.ts new file mode 100644 index 0000000..4495e4d --- /dev/null +++ b/backend/src/services/WbotServices/SendWhatsAppMedia.ts @@ -0,0 +1,29 @@ +import { MessageMedia, Message as WbotMessage } from "whatsapp-web.js"; +import GetTicketWbot from "../../helpers/GetTicketWbot"; +import Ticket from "../../models/Ticket"; + +interface Request { + media: Express.Multer.File; + ticket: Ticket; +} + +const SendWhatsAppMedia = async ({ + media, + ticket +}: Request): Promise => { + const wbot = await GetTicketWbot(ticket); + + const newMedia = MessageMedia.fromFilePath(media.path); + + const mediaUrl = media.filename; + + const sentMessage = await wbot.sendMessage( + `${ticket.contact.number}@c.us`, + newMedia + ); + + await ticket.update({ lastMessage: mediaUrl }); + return sentMessage; +}; + +export default SendWhatsAppMedia; diff --git a/backend/src/services/WbotServices/SendWhatsAppMessage.ts b/backend/src/services/WbotServices/SendWhatsAppMessage.ts new file mode 100644 index 0000000..403b915 --- /dev/null +++ b/backend/src/services/WbotServices/SendWhatsAppMessage.ts @@ -0,0 +1,25 @@ +import { Message as WbotMessage } from "whatsapp-web.js"; +import GetTicketWbot from "../../helpers/GetTicketWbot"; +import Ticket from "../../models/Ticket"; + +interface Request { + body: string; + ticket: Ticket; +} + +const SendWhatsAppMessage = async ({ + body, + ticket +}: Request): Promise => { + const wbot = await GetTicketWbot(ticket); + + const sentMessage = await wbot.sendMessage( + `${ticket.contact.number}@c.us`, + body + ); + + await ticket.update({ lastMessage: body }); + return sentMessage; +}; + +export default SendWhatsAppMessage; diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 2ab9216..d1dc5ed 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -124,7 +124,6 @@ const handleMessage = async ( ticket: Ticket, contact: Contact ) => { - const io = getIO(); let newMessage: Message; if (msg.hasMedia) { @@ -138,6 +137,7 @@ const handleMessage = async ( await ticket.update({ lastMessage: msg.body }); } + const io = getIO(); io.to(ticket.id.toString()).to("notification").emit("appMessage", { action: "create", message: newMessage, @@ -157,7 +157,7 @@ const wbotMessageListener = (whatsapp: Whatsapp): void => { } wbot.on("message_create", async msg => { - // console.log(msg); + console.log(msg.type); if ( msg.from === "status@broadcast" || From 6bc3b216c86cc04dbbca69a3ba5acefd09f394c0 Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 10:18:38 -0300 Subject: [PATCH 43/54] code cleanup --- backend/src/controllers/MessageController.ts | 11 - backend/src/controllers/WhatsAppController.ts | 14 +- .../src/controllersOld/ContactController.js | 170 --------------- .../ImportPhoneContactsController.js | 38 ---- .../src/controllersOld/MessageController.js | 203 ------------------ .../src/controllersOld/SessionController.js | 32 --- .../src/controllersOld/SettingController.js | 39 ---- .../src/controllersOld/TicketController.js | 185 ---------------- backend/src/controllersOld/UserController.js | 181 ---------------- .../src/controllersOld/WhatsAppController.js | 141 ------------ .../WhatsAppSessionController.js | 32 --- .../src/helpers/SetTicketMessagesAsRead.ts | 5 +- backend/src/modelsOld/Contact.js | 29 --- backend/src/modelsOld/ContactCustomField.js | 26 --- backend/src/modelsOld/Message.js | 35 --- backend/src/modelsOld/Setting.js | 24 --- backend/src/modelsOld/Ticket.js | 50 ----- backend/src/modelsOld/User.js | 32 --- backend/src/modelsOld/Whatsapp.js | 32 --- backend/src/server.ts | 5 +- .../WbotServices/SendWhatsAppMedia.ts | 4 +- 21 files changed, 12 insertions(+), 1276 deletions(-) delete mode 100644 backend/src/controllersOld/ContactController.js delete mode 100644 backend/src/controllersOld/ImportPhoneContactsController.js delete mode 100644 backend/src/controllersOld/MessageController.js delete mode 100644 backend/src/controllersOld/SessionController.js delete mode 100644 backend/src/controllersOld/SettingController.js delete mode 100644 backend/src/controllersOld/TicketController.js delete mode 100644 backend/src/controllersOld/UserController.js delete mode 100644 backend/src/controllersOld/WhatsAppController.js delete mode 100644 backend/src/controllersOld/WhatsAppSessionController.js delete mode 100644 backend/src/modelsOld/Contact.js delete mode 100644 backend/src/modelsOld/ContactCustomField.js delete mode 100644 backend/src/modelsOld/Message.js delete mode 100644 backend/src/modelsOld/Setting.js delete mode 100644 backend/src/modelsOld/Ticket.js delete mode 100644 backend/src/modelsOld/User.js delete mode 100644 backend/src/modelsOld/Whatsapp.js diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts index a574cac..4c33aef 100644 --- a/backend/src/controllers/MessageController.ts +++ b/backend/src/controllers/MessageController.ts @@ -1,5 +1,4 @@ import { Request, Response } from "express"; -// import { getIO } from "../libs/socket"; import { Message as WbotMessage } from "whatsapp-web.js"; import SetTicketMessagesAsRead from "../helpers/SetTicketMessagesAsRead"; @@ -11,16 +10,6 @@ import ShowTicketService from "../services/TicketServices/ShowTicketService"; import SendWhatsAppMedia from "../services/WbotServices/SendWhatsAppMedia"; import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage"; -// import Sequelize from "sequelize"; -// import { MessageMedia } from "whatsapp-web.js"; -// import Message from "../models/Message"; -// import Contact from "../models/Contact"; -// import User from "../models/User"; -// import Whatsapp from "../models/Whatsapp"; - -// import Ticket from "../models/Ticket"; -// import { getWbot } from "../libs/wbot"; - type IndexQuery = { searchParam: string; pageNumber: string; diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index bc8a414..f0726f4 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -1,5 +1,7 @@ import { Request, Response } from "express"; import { getIO } from "../libs/socket"; +import { initWbot } from "../libs/wbot"; +import wbotMessageListener from "../services/WbotServices/wbotMessageListener"; import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService"; @@ -34,12 +36,12 @@ export const store = async (req: Request, res: Response): Promise => { // return res.status(400).json({ error: "Cannot create whatsapp session." }); // } - // initWbot(whatsapp) - // .then(() => { - // wbotMessageListener(whatsapp); - // wbotMonitor(whatsapp); - // }) - // .catch(err => console.log(err)); + initWbot(whatsapp) + .then(() => { + wbotMessageListener(whatsapp); + // wbotMonitor(whatsapp); + }) + .catch(err => console.log(err)); const io = getIO(); io.emit("whatsapp", { diff --git a/backend/src/controllersOld/ContactController.js b/backend/src/controllersOld/ContactController.js deleted file mode 100644 index 49a352a..0000000 --- a/backend/src/controllersOld/ContactController.js +++ /dev/null @@ -1,170 +0,0 @@ -const Sequelize = require("sequelize"); -const { Op } = require("sequelize"); - -const Contact = require("../models/Contact"); -const Whatsapp = require("../models/Whatsapp"); -const ContactCustomField = require("../models/ContactCustomField"); - -const { getIO } = require("../libs/socket"); -const { getWbot } = require("../libs/wbot"); - -exports.index = async (req, res) => { - const { searchParam = "", pageNumber = 1 } = req.query; - - const whereCondition = { - [Op.or]: [ - { - name: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ) - }, - { number: { [Op.like]: `%${searchParam}%` } } - ] - }; - - let limit = 20; - let offset = limit * (pageNumber - 1); - - const { count, rows: contacts } = await Contact.findAndCountAll({ - where: whereCondition, - limit, - offset, - order: [["createdAt", "DESC"]] - }); - - const hasMore = count > offset + contacts.length; - - return res.json({ contacts, count, hasMore }); -}; - -exports.store = async (req, res) => { - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true } - }); - - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } - - const wbot = getWbot(defaultWhatsapp); - const io = getIO(); - const newContact = req.body; - - try { - const isValidNumber = await wbot.isRegisteredUser( - `${newContact.number}@c.us` - ); - if (!isValidNumber) { - return res - .status(400) - .json({ error: "The suplied number is not a valid Whatsapp number" }); - } - } catch (err) { - console.log(err); - return res.status(500).json({ - error: "Could not check whatsapp contact. Check connection page." - }); - } - - const profilePicUrl = await wbot.getProfilePicUrl( - `${newContact.number}@c.us` - ); - - const contact = await Contact.create( - { ...newContact, profilePicUrl }, - { - include: "extraInfo" - } - ); - - io.emit("contact", { - action: "create", - contact: contact - }); - - return res.status(200).json(contact); -}; - -exports.show = async (req, res) => { - const { contactId } = req.params; - - const contact = await Contact.findByPk(contactId, { - include: "extraInfo", - attributes: ["id", "name", "number", "email"] - }); - - if (!contact) { - return res.status(404).json({ error: "No contact found with this id." }); - } - - return res.status(200).json(contact); -}; - -exports.update = async (req, res) => { - const io = getIO(); - - const updatedContact = req.body; - - const { contactId } = req.params; - - const contact = await Contact.findByPk(contactId, { - include: "extraInfo" - }); - - if (!contact) { - return res.status(404).json({ error: "No contact found with this ID" }); - } - - if (updatedContact.extraInfo) { - await Promise.all( - updatedContact.extraInfo.map(async info => { - await ContactCustomField.upsert({ ...info, contactId: contact.id }); - }) - ); - - await Promise.all( - contact.extraInfo.map(async oldInfo => { - let stillExists = updatedContact.extraInfo.findIndex( - info => info.id === oldInfo.id - ); - - if (stillExists === -1) { - await ContactCustomField.destroy({ where: { id: oldInfo.id } }); - } - }) - ); - } - - await contact.update(updatedContact); - - io.emit("contact", { - action: "update", - contact: contact - }); - - return res.status(200).json(contact); -}; - -exports.delete = async (req, res) => { - const io = getIO(); - const { contactId } = req.params; - - const contact = await Contact.findByPk(contactId); - - if (!contact) { - return res.status(404).json({ error: "No contact found with this ID" }); - } - - await contact.destroy(); - - io.emit("contact", { - action: "delete", - contactId: contactId - }); - - return res.status(200).json({ message: "Contact deleted" }); -}; diff --git a/backend/src/controllersOld/ImportPhoneContactsController.js b/backend/src/controllersOld/ImportPhoneContactsController.js deleted file mode 100644 index b24ee0d..0000000 --- a/backend/src/controllersOld/ImportPhoneContactsController.js +++ /dev/null @@ -1,38 +0,0 @@ -const Contact = require("../models/Contact"); -const Whatsapp = require("../models/Whatsapp"); -const { getIO } = require("../libs/socket"); -const { getWbot, initWbot } = require("../libs/wbot"); - -exports.store = async (req, res, next) => { - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true }, - }); - - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } - - const io = getIO(); - const wbot = getWbot(defaultWhatsapp); - - let phoneContacts; - - try { - phoneContacts = await wbot.getContacts(); - } catch (err) { - console.log(err); - return res.status(500).json({ - error: "Could not check whatsapp contact. Check connection page.", - }); - } - - await Promise.all( - phoneContacts.map(async ({ number, name }) => { - await Contact.create({ number, name }); - }) - ); - - return res.status(200).json({ message: "contacts imported" }); -}; diff --git a/backend/src/controllersOld/MessageController.js b/backend/src/controllersOld/MessageController.js deleted file mode 100644 index a34cd3f..0000000 --- a/backend/src/controllersOld/MessageController.js +++ /dev/null @@ -1,203 +0,0 @@ -const Message = require("../models/Message"); -const Contact = require("../models/Contact"); -const User = require("../models/User"); -const Whatsapp = require("../models/Whatsapp"); - -const Ticket = require("../models/Ticket"); -const { getIO } = require("../libs/socket"); -const { getWbot } = require("../libs/wbot"); -const Sequelize = require("sequelize"); - -const { MessageMedia } = require("whatsapp-web.js"); - -const setMessagesAsRead = async ticket => { - const io = getIO(); - const wbot = getWbot(ticket.whatsappId); - - await Message.update( - { read: true }, - { - where: { - ticketId: ticket.id, - read: false - } - } - ); - - try { - await wbot.sendSeen(`${ticket.contact.number}@c.us`); - } catch (err) { - console.log( - "Could not mark messages as read. Maybe whatsapp session disconnected?" - ); - } - - io.to("notification").emit("ticket", { - action: "updateUnread", - ticketId: ticket.id - }); -}; - -exports.index = async (req, res, next) => { - const { ticketId } = req.params; - const { searchParam = "", pageNumber = 1 } = req.query; - - const whereCondition = { - body: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("body")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ) - }; - - const limit = 20; - const offset = limit * (pageNumber - 1); - - const ticket = await Ticket.findByPk(ticketId, { - include: [ - { - model: Contact, - as: "contact", - include: "extraInfo", - attributes: ["id", "name", "number", "profilePicUrl"] - }, - { - model: User, - as: "user" - } - ] - }); - - if (!ticket) { - return res.status(404).json({ error: "No ticket found with this ID" }); - } - - await setMessagesAsRead(ticket); - - const ticketMessages = await ticket.getMessages({ - where: whereCondition, - limit, - offset, - order: [["createdAt", "DESC"]] - }); - - const count = await ticket.countMessages(); - - const serializedMessages = ticketMessages.map(message => { - return { - ...message.dataValues, - mediaUrl: `${ - message.mediaUrl - ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` - : "" - }` - }; - }); - - const hasMore = count > offset + ticketMessages.length; - - return res.json({ - messages: serializedMessages.reverse(), - ticket, - count, - hasMore - }); -}; - -exports.store = async (req, res, next) => { - const io = getIO(); - - const { ticketId } = req.params; - const message = req.body; - const media = req.file; - let sentMessage; - - const ticket = await Ticket.findByPk(ticketId, { - include: [ - { - model: Contact, - as: "contact", - attributes: ["number", "name", "profilePicUrl"] - } - ] - }); - - if (!ticket) { - return res.status(404).json({ error: "No ticket found with this ID" }); - } - - if (!ticket.whatsappId) { - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true } - }); - - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } - - await ticket.setWhatsapp(defaultWhatsapp); - } - - const wbot = getWbot(ticket.whatsappId); - - try { - if (media) { - const newMedia = MessageMedia.fromFilePath(req.file.path); - - message.mediaUrl = req.file.filename; - if (newMedia.mimetype) { - message.mediaType = newMedia.mimetype.split("/")[0]; - } else { - message.mediaType = "other"; - } - - sentMessage = await wbot.sendMessage( - `${ticket.contact.number}@c.us`, - newMedia - ); - - await ticket.update({ lastMessage: message.mediaUrl }); - } else { - sentMessage = await wbot.sendMessage( - `${ticket.contact.number}@c.us`, - message.body - ); - await ticket.update({ lastMessage: message.body }); - } - } catch (err) { - console.log( - "Could not create whatsapp message. Is session details valid? " - ); - } - - if (sentMessage) { - message.id = sentMessage.id.id; - const newMessage = await ticket.createMessage(message); - - const serialziedMessage = { - ...newMessage.dataValues, - mediaUrl: `${ - message.mediaUrl - ? `${process.env.BACKEND_URL}:${process.env.PROXY_PORT}/public/${message.mediaUrl}` - : "" - }` - }; - - io.to(ticketId).to("notification").emit("appMessage", { - action: "create", - message: serialziedMessage, - ticket: ticket, - contact: ticket.contact - }); - - await setMessagesAsRead(ticket); - - return res.status(200).json({ newMessage, ticket }); - } - - return res - .status(500) - .json({ error: "Cannot sent whatsapp message. Check connection page." }); -}; diff --git a/backend/src/controllersOld/SessionController.js b/backend/src/controllersOld/SessionController.js deleted file mode 100644 index fdf1977..0000000 --- a/backend/src/controllersOld/SessionController.js +++ /dev/null @@ -1,32 +0,0 @@ -const jwt = require("jsonwebtoken"); -const authConfig = require("../config/auth"); - -const User = require("../models/User"); - -exports.store = async (req, res, next) => { - const { email, password } = req.body; - - const user = await User.findOne({ where: { email: email } }); - if (!user) { - return res.status(404).json({ error: "No user found with this email" }); - } - - if (!(await user.checkPassword(password))) { - return res.status(401).json({ error: "Password does not match" }); - } - - const token = jwt.sign( - { email: user.email, userId: user.id }, - authConfig.secret, - { - expiresIn: authConfig.expiresIn - } - ); - - return res.status(200).json({ - token: token, - username: user.name, - profile: user.profile, - userId: user.id - }); -}; diff --git a/backend/src/controllersOld/SettingController.js b/backend/src/controllersOld/SettingController.js deleted file mode 100644 index 045a0ee..0000000 --- a/backend/src/controllersOld/SettingController.js +++ /dev/null @@ -1,39 +0,0 @@ -const Setting = require("../models/Setting"); -const { getIO } = require("../libs/socket"); - -exports.index = async (req, res) => { - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can access this route." }); - } - - const settings = await Setting.findAll(); - - return res.status(200).json(settings); -}; - -exports.update = async (req, res) => { - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can access this route." }); - } - - const io = getIO(); - const { settingKey } = req.params; - const setting = await Setting.findByPk(settingKey); - - if (!setting) { - return res.status(404).json({ error: "No setting found with this ID" }); - } - - await setting.update(req.body); - - io.emit("settings", { - action: "update", - setting, - }); - - return res.status(200).json(setting); -}; diff --git a/backend/src/controllersOld/TicketController.js b/backend/src/controllersOld/TicketController.js deleted file mode 100644 index fcd379c..0000000 --- a/backend/src/controllersOld/TicketController.js +++ /dev/null @@ -1,185 +0,0 @@ -const Sequelize = require("sequelize"); -const { startOfDay, endOfDay, parseISO } = require("date-fns"); - -const Ticket = require("../models/Ticket"); -const Contact = require("../models/Contact"); -const Message = require("../models/Message"); -const Whatsapp = require("../models/Whatsapp"); - -const { getIO } = require("../libs/socket"); - -exports.index = async (req, res) => { - const { - pageNumber = 1, - status = "", - date = "", - searchParam = "", - showAll - } = req.query; - - const userId = req.user.id; - - const limit = 20; - const offset = limit * (pageNumber - 1); - - let includeCondition = [ - { - model: Contact, - as: "contact", - attributes: ["name", "number", "profilePicUrl"] - } - ]; - - let whereCondition = { userId: userId }; - - if (showAll === "true") { - whereCondition = {}; - } - - if (status) { - whereCondition = { - ...whereCondition, - status: status - }; - } - - if (searchParam) { - includeCondition = [ - ...includeCondition, - { - model: Message, - as: "messages", - attributes: ["id", "body"], - where: { - body: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("body")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ) - }, - required: false, - duplicating: false - } - ]; - - whereCondition = { - [Sequelize.Op.or]: [ - { - "$contact.name$": Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ) - }, - { "$contact.number$": { [Sequelize.Op.like]: `%${searchParam}%` } }, - { - "$message.body$": Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("body")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ) - } - ] - }; - } - - if (date) { - whereCondition = { - ...whereCondition, - createdAt: { - [Sequelize.Op.between]: [ - startOfDay(parseISO(date)), - endOfDay(parseISO(date)) - ] - } - }; - } - - const { count, rows: tickets } = await Ticket.findAndCountAll({ - where: whereCondition, - distinct: true, - include: includeCondition, - limit, - offset, - order: [["updatedAt", "DESC"]] - }); - - const hasMore = count > offset + tickets.length; - - return res.status(200).json({ count, tickets, hasMore }); -}; - -exports.store = async (req, res) => { - const io = getIO(); - - const defaultWhatsapp = await Whatsapp.findOne({ - where: { default: true } - }); - - if (!defaultWhatsapp) { - return res - .status(404) - .json({ error: "No default WhatsApp found. Check Connection page." }); - } - - const ticket = await defaultWhatsapp.createTicket(req.body); - - const contact = await ticket.getContact(); - - const serializaedTicket = { ...ticket.dataValues, contact: contact }; - - io.to("notification").emit("ticket", { - action: "create", - ticket: serializaedTicket - }); - - return res.status(200).json(ticket); -}; - -exports.update = async (req, res) => { - const io = getIO(); - const { ticketId } = req.params; - - const ticket = await Ticket.findByPk(ticketId, { - include: [ - { - model: Contact, - as: "contact", - attributes: ["name", "number", "profilePicUrl"] - } - ] - }); - - if (!ticket) { - return res.status(404).json({ error: "No ticket found with this ID" }); - } - - await ticket.update(req.body); - - io.to("notification").emit("ticket", { - action: "updateStatus", - ticket: ticket - }); - - return res.status(200).json(ticket); -}; - -exports.delete = async (req, res) => { - const io = getIO(); - const { ticketId } = req.params; - - const ticket = await Ticket.findByPk(ticketId); - - if (!ticket) { - return res.status(400).json({ error: "No ticket found with this ID" }); - } - - await ticket.destroy(); - - io.to("notification").emit("ticket", { - action: "delete", - ticketId: ticket.id - }); - - return res.status(200).json({ message: "ticket deleted" }); -}; diff --git a/backend/src/controllersOld/UserController.js b/backend/src/controllersOld/UserController.js deleted file mode 100644 index 06fd1a1..0000000 --- a/backend/src/controllersOld/UserController.js +++ /dev/null @@ -1,181 +0,0 @@ -const Sequelize = require("sequelize"); -const Yup = require("yup"); -const { Op } = require("sequelize"); - -const User = require("../models/User"); -const Setting = require("../models/Setting"); - -const { getIO } = require("../libs/socket"); - -exports.index = async (req, res) => { - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can access this route." }); - } - - const { searchParam = "", pageNumber = 1 } = req.query; - - const whereCondition = { - [Op.or]: [ - { - name: Sequelize.where( - Sequelize.fn("LOWER", Sequelize.col("name")), - "LIKE", - "%" + searchParam.toLowerCase() + "%" - ) - }, - { email: { [Op.like]: `%${searchParam.toLowerCase()}%` } } - ] - }; - - let limit = 20; - let offset = limit * (pageNumber - 1); - - const { count, rows: users } = await User.findAndCountAll({ - attributes: ["name", "id", "email", "profile"], - where: whereCondition, - limit, - offset, - order: [["createdAt", "DESC"]] - }); - - const hasMore = count > offset + users.length; - - return res.status(200).json({ users, count, hasMore }); -}; - -export default async (req, res, next) => { - console.log(req.url); - const schema = Yup.object().shape({ - name: Yup.string().required().min(2), - email: Yup.string() - .email() - .required() - .test( - "Check-email", - "An user with this email already exists", - async value => { - const userFound = await User.findOne({ where: { email: value } }); - return !Boolean(userFound); - } - ), - password: Yup.string().required().min(5) - }); - - if (req.url === "/signup") { - const { value: userCreation } = await Setting.findByPk("userCreation"); - - if (userCreation === "disabled") { - return res - .status(403) - .json({ error: "User creation is disabled by administrator." }); - } - } else if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can create users." }); - } - - try { - await schema.validate(req.body); - } catch (err) { - return res.status(400).json({ error: err.message }); - } - - const io = getIO(); - - const { name, id, email, profile } = await User.create(req.body); - - io.emit("user", { - action: "create", - user: { name, id, email, profile } - }); - - return res.status(201).json({ message: "User created!", userId: id }); -}; - -exports.show = async (req, res) => { - const { userId } = req.params; - - const user = await User.findByPk(userId, { - attributes: ["id", "name", "email", "profile"] - }); - - if (!user) { - res.status(400).json({ error: "No user found with this id." }); - } - - return res.status(200).json(user); -}; - -exports.update = async (req, res) => { - const schema = Yup.object().shape({ - name: Yup.string().min(2), - email: Yup.string().email(), - password: Yup.string() - }); - - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can edit users." }); - } - - await schema.validate(req.body); - - const io = getIO(); - const { userId } = req.params; - - const user = await User.findByPk(userId, { - attributes: ["name", "id", "email", "profile"] - }); - - if (!user) { - res.status(404).json({ error: "No user found with this id." }); - } - - if (user.profile === "admin" && req.body.profile === "user") { - const adminUsers = await User.count({ where: { profile: "admin" } }); - if (adminUsers <= 1) { - return res - .status(403) - .json({ error: "There must be at leat one admin user." }); - } - } - - await user.update(req.body); - - io.emit("user", { - action: "update", - user: user - }); - - return res.status(200).json(user); -}; - -exports.delete = async (req, res) => { - const io = getIO(); - const { userId } = req.params; - - const user = await User.findByPk(userId); - - if (!user) { - res.status(400).json({ error: "No user found with this id." }); - } - - if (req.user.profile !== "admin") { - return res - .status(403) - .json({ error: "Only administrators can edit users." }); - } - - await user.destroy(); - - io.emit("user", { - action: "delete", - userId: userId - }); - - return res.status(200).json({ message: "User deleted" }); -}; diff --git a/backend/src/controllersOld/WhatsAppController.js b/backend/src/controllersOld/WhatsAppController.js deleted file mode 100644 index 6abf373..0000000 --- a/backend/src/controllersOld/WhatsAppController.js +++ /dev/null @@ -1,141 +0,0 @@ -const Yup = require("yup"); -const Whatsapp = require("../models/Whatsapp"); -const { getIO } = require("../libs/socket"); -const { getWbot, initWbot, removeWbot } = require("../libs/wbot"); -const wbotMessageListener = require("../services/wbotMessageListener"); -const wbotMonitor = require("../services/wbotMonitor"); - -exports.index = async (req, res) => { - const whatsapps = await Whatsapp.findAll(); - - return res.status(200).json(whatsapps); -}; - -exports.store = async (req, res) => { - const schema = Yup.object().shape({ - name: Yup.string().required().min(2), - default: Yup.boolean() - .required() - .test( - "Check-default", - "Only one default whatsapp is permited", - async value => { - if (value === true) { - const whatsappFound = await Whatsapp.findOne({ - where: { default: true } - }); - return !Boolean(whatsappFound); - } else return true; - } - ) - }); - - try { - await schema.validate(req.body); - } catch (err) { - return res.status(400).json({ error: err.message }); - } - - const io = getIO(); - - const whatsapp = await Whatsapp.create(req.body); - - if (!whatsapp) { - return res.status(400).json({ error: "Cannot create whatsapp session." }); - } - - initWbot(whatsapp) - .then(() => { - wbotMessageListener(whatsapp); - wbotMonitor(whatsapp); - }) - .catch(err => console.log(err)); - - io.emit("whatsapp", { - action: "update", - whatsapp: whatsapp - }); - - return res.status(200).json(whatsapp); -}; - -exports.show = async (req, res) => { - const { whatsappId } = req.params; - const whatsapp = await Whatsapp.findByPk(whatsappId); - - if (!whatsapp) { - return res.status(200).json({ message: "Session not found" }); - } - - return res.status(200).json(whatsapp); -}; - -exports.update = async (req, res) => { - const { whatsappId } = req.params; - - const schema = Yup.object().shape({ - name: Yup.string().required().min(2), - default: Yup.boolean() - .required() - .test( - "Check-default", - "Only one default whatsapp is permited", - async value => { - if (value === true) { - const whatsappFound = await Whatsapp.findOne({ - where: { default: true } - }); - if (whatsappFound) { - return !(whatsappFound.id !== +whatsappId); - } else { - return true; - } - } else return true; - } - ) - }); - - try { - await schema.validate(req.body); - } catch (err) { - return res.status(400).json({ error: err.message }); - } - - const io = getIO(); - - const whatsapp = await Whatsapp.findByPk(whatsappId); - - if (!whatsapp) { - return res.status(404).json({ message: "Whatsapp not found" }); - } - - await whatsapp.update(req.body); - - io.emit("whatsapp", { - action: "update", - whatsapp: whatsapp - }); - - return res.status(200).json({ message: "Whatsapp updated" }); -}; - -exports.delete = async (req, res) => { - const io = getIO(); - const { whatsappId } = req.params; - - const whatsapp = await Whatsapp.findByPk(whatsappId); - - if (!whatsapp) { - return res.status(404).json({ message: "Whatsapp not found" }); - } - - await whatsapp.destroy(); - removeWbot(whatsapp.id); - - io.emit("whatsapp", { - action: "delete", - whatsappId: whatsapp.id - }); - - return res.status(200).json({ message: "Whatsapp deleted." }); -}; diff --git a/backend/src/controllersOld/WhatsAppSessionController.js b/backend/src/controllersOld/WhatsAppSessionController.js deleted file mode 100644 index 0bee528..0000000 --- a/backend/src/controllersOld/WhatsAppSessionController.js +++ /dev/null @@ -1,32 +0,0 @@ -// const Whatsapp = require("../models/Whatsapp"); -// const { getIO } = require("../libs/socket"); -// const { getWbot, initWbot, removeWbot } = require("../libs/wbot"); -// const wbotMessageListener = require("../services/wbotMessageListener"); -// const wbotMonitor = require("../services/wbotMonitor"); - -// exports.show = async (req, res) => { -// const { whatsappId } = req.params; -// const dbSession = await Whatsapp.findByPk(whatsappId); - -// if (!dbSession) { -// return res.status(200).json({ message: "Session not found" }); -// } - -// return res.status(200).json(dbSession); -// }; - -// exports.delete = async (req, res) => { -// const { whatsappId } = req.params; - -// const dbSession = await Whatsapp.findByPk(whatsappId); - -// if (!dbSession) { -// return res.status(404).json({ message: "Session not found" }); -// } - -// const wbot = getWbot(dbSession.id); - -// wbot.logout(); - -// return res.status(200).json({ message: "Session disconnected." }); -// }; diff --git a/backend/src/helpers/SetTicketMessagesAsRead.ts b/backend/src/helpers/SetTicketMessagesAsRead.ts index b78cdd1..c6bbff9 100644 --- a/backend/src/helpers/SetTicketMessagesAsRead.ts +++ b/backend/src/helpers/SetTicketMessagesAsRead.ts @@ -1,11 +1,9 @@ import { getIO } from "../libs/socket"; -import { getWbot } from "../libs/wbot"; import Message from "../models/Message"; import Ticket from "../models/Ticket"; +import GetTicketWbot from "./GetTicketWbot"; const SetTicketMessagesAsRead = async (ticket: Ticket): Promise => { - const wbot = getWbot(ticket.whatsappId); - await Message.update( { read: true }, { @@ -17,6 +15,7 @@ const SetTicketMessagesAsRead = async (ticket: Ticket): Promise => { ); try { + const wbot = await GetTicketWbot(ticket); await wbot.sendSeen(`${ticket.contact.number}@c.us`); } catch (err) { console.log( diff --git a/backend/src/modelsOld/Contact.js b/backend/src/modelsOld/Contact.js deleted file mode 100644 index 6187477..0000000 --- a/backend/src/modelsOld/Contact.js +++ /dev/null @@ -1,29 +0,0 @@ -const Sequelize = require("sequelize"); - -class Contact extends Sequelize.Model { - static init(sequelize) { - super.init( - { - name: { type: Sequelize.STRING }, - number: { type: Sequelize.STRING, allowNull: false, unique: true }, - email: { type: Sequelize.STRING, allowNull: false, defaultValue: "" }, - profilePicUrl: { type: Sequelize.STRING } - }, - { - sequelize - } - ); - - return this; - } - - static associate(models) { - this.hasMany(models.Ticket, { foreignKey: "contactId", as: "contact" }); - this.hasMany(models.ContactCustomField, { - foreignKey: "contactId", - as: "extraInfo" - }); - } -} - -module.exports = Contact; diff --git a/backend/src/modelsOld/ContactCustomField.js b/backend/src/modelsOld/ContactCustomField.js deleted file mode 100644 index 3819cbb..0000000 --- a/backend/src/modelsOld/ContactCustomField.js +++ /dev/null @@ -1,26 +0,0 @@ -const Sequelize = require("sequelize"); - -class ContactCustomField extends Sequelize.Model { - static init(sequelize) { - super.init( - { - name: { type: Sequelize.STRING }, - value: { type: Sequelize.STRING } - }, - { - sequelize - } - ); - - return this; - } - - static associate(models) { - this.belongsTo(models.Contact, { - foreignKey: "contactId", - as: "extraInfo" - }); - } -} - -module.exports = ContactCustomField; diff --git a/backend/src/modelsOld/Message.js b/backend/src/modelsOld/Message.js deleted file mode 100644 index bf4fbb5..0000000 --- a/backend/src/modelsOld/Message.js +++ /dev/null @@ -1,35 +0,0 @@ -const Sequelize = require("sequelize"); - -class Message extends Sequelize.Model { - static init(sequelize) { - super.init( - { - ack: { type: Sequelize.INTEGER, defaultValue: 0 }, - read: { type: Sequelize.BOOLEAN, defaultValue: false }, - fromMe: { type: Sequelize.BOOLEAN, defaultValue: false }, - body: { type: Sequelize.TEXT }, - mediaUrl: { type: Sequelize.STRING }, - mediaType: { type: Sequelize.STRING }, - createdAt: { - type: Sequelize.DATE(6), - allowNull: false - }, - updatedAt: { - type: Sequelize.DATE(6), - allowNull: false - } - }, - { - sequelize - } - ); - - return this; - } - - static associate(models) { - this.belongsTo(models.Ticket, { foreignKey: "ticketId", as: "messages" }); - } -} - -module.exports = Message; diff --git a/backend/src/modelsOld/Setting.js b/backend/src/modelsOld/Setting.js deleted file mode 100644 index fe4168a..0000000 --- a/backend/src/modelsOld/Setting.js +++ /dev/null @@ -1,24 +0,0 @@ -const Sequelize = require("sequelize"); - -class Setting extends Sequelize.Model { - static init(sequelize) { - super.init( - { - key: { - type: Sequelize.STRING, - primaryKey: true, - allowNull: false, - unique: true, - }, - value: { type: Sequelize.TEXT, allowNull: false }, - }, - { - sequelize, - } - ); - - return this; - } -} - -module.exports = Setting; diff --git a/backend/src/modelsOld/Ticket.js b/backend/src/modelsOld/Ticket.js deleted file mode 100644 index bcff844..0000000 --- a/backend/src/modelsOld/Ticket.js +++ /dev/null @@ -1,50 +0,0 @@ -const Sequelize = require("sequelize"); -const Message = require("./Message"); - -class Ticket extends Sequelize.Model { - static init(sequelize) { - super.init( - { - status: { type: Sequelize.STRING, defaultValue: "pending" }, - userId: { type: Sequelize.INTEGER, defaultValue: null }, - unreadMessages: { type: Sequelize.VIRTUAL }, - lastMessage: { type: Sequelize.STRING } - }, - { - sequelize - } - ); - - this.addHook("afterFind", async result => { - if (result && result.length > 0) { - await Promise.all( - result.map(async ticket => { - ticket.unreadMessages = await Message.count({ - where: { ticketId: ticket.id, read: false } - }); - }) - ); - } - }); - - this.addHook("beforeUpdate", async ticket => { - ticket.unreadMessages = await Message.count({ - where: { ticketId: ticket.id, read: false } - }); - }); - - return this; - } - - static associate(models) { - this.belongsTo(models.Contact, { foreignKey: "contactId", as: "contact" }); - this.belongsTo(models.User, { foreignKey: "userId", as: "user" }); - this.belongsTo(models.Whatsapp, { - foreignKey: "whatsappId", - as: "whatsapp" - }); - this.hasMany(models.Message, { foreignKey: "ticketId", as: "messages" }); - } -} - -module.exports = Ticket; diff --git a/backend/src/modelsOld/User.js b/backend/src/modelsOld/User.js deleted file mode 100644 index 6617b6c..0000000 --- a/backend/src/modelsOld/User.js +++ /dev/null @@ -1,32 +0,0 @@ -const Sequelize = require("sequelize"); -const bcrypt = require("bcryptjs"); - -class User extends Sequelize.Model { - static init(sequelize) { - super.init( - { - name: { type: Sequelize.STRING }, - password: { type: Sequelize.VIRTUAL }, - profile: { type: Sequelize.STRING, defaultValue: "admin" }, - passwordHash: { type: Sequelize.STRING }, - email: { type: Sequelize.STRING } - }, - { - sequelize - } - ); - - this.addHook("beforeSave", async user => { - if (user.password) { - user.passwordHash = await bcrypt.hash(user.password, 8); - } - }); - return this; - } - - checkPassword(password) { - return bcrypt.compare(password, this.passwordHash); - } -} - -module.exports = User; diff --git a/backend/src/modelsOld/Whatsapp.js b/backend/src/modelsOld/Whatsapp.js deleted file mode 100644 index bf63e0c..0000000 --- a/backend/src/modelsOld/Whatsapp.js +++ /dev/null @@ -1,32 +0,0 @@ -const Sequelize = require("sequelize"); - -class Whatsapp extends Sequelize.Model { - static init(sequelize) { - super.init( - { - session: { type: Sequelize.TEXT }, - qrcode: { type: Sequelize.TEXT }, - name: { type: Sequelize.STRING, unique: true, allowNull: false }, - status: { type: Sequelize.STRING }, - battery: { type: Sequelize.STRING }, - plugged: { type: Sequelize.BOOLEAN }, - default: { - type: Sequelize.BOOLEAN, - defaultValue: false, - allowNull: false - } - }, - { - sequelize - } - ); - - return this; - } - - static associate(models) { - this.hasMany(models.Ticket, { foreignKey: "whatsappId", as: "tickets" }); - } -} - -module.exports = Whatsapp; diff --git a/backend/src/server.ts b/backend/src/server.ts index ec6b241..c040e55 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -6,16 +6,13 @@ import cors from "cors"; import multer from "multer"; import * as Sentry from "@sentry/node"; +import "./database"; import uploadConfig from "./config/upload"; import AppError from "./errors/AppError"; import routes from "./routes"; import { initIO } from "./libs/socket"; -import "./database"; import { initWbot } from "./libs/wbot"; -// import path from "path"; - -// const wbotMessageListener = require("./services/wbotMessageListener"); // const wbotMonitor = require("./services/wbotMonitor"); import Whatsapp from "./models/Whatsapp"; import wbotMessageListener from "./services/WbotServices/wbotMessageListener"; diff --git a/backend/src/services/WbotServices/SendWhatsAppMedia.ts b/backend/src/services/WbotServices/SendWhatsAppMedia.ts index 4495e4d..424200f 100644 --- a/backend/src/services/WbotServices/SendWhatsAppMedia.ts +++ b/backend/src/services/WbotServices/SendWhatsAppMedia.ts @@ -15,14 +15,12 @@ const SendWhatsAppMedia = async ({ const newMedia = MessageMedia.fromFilePath(media.path); - const mediaUrl = media.filename; - const sentMessage = await wbot.sendMessage( `${ticket.contact.number}@c.us`, newMedia ); - await ticket.update({ lastMessage: mediaUrl }); + await ticket.update({ lastMessage: media.filename }); return sentMessage; }; From 9bab61c7dc7aeca90c4deffc5bb6aeea89efdb82 Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 10:39:59 -0300 Subject: [PATCH 44/54] enabled sentry --- backend/package.json | 1 + backend/src/server.ts | 26 ++++++++------------------ 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4cecba0..95b0891 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "build": "tsc", + "watch": "tsc -w", "start": "nodemon dist/server.js", "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts" }, diff --git a/backend/src/server.ts b/backend/src/server.ts index c040e55..37c88c3 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -29,15 +29,6 @@ app.use(upload.single("media")); app.use("/public", express.static(uploadConfig.directory)); app.use(routes); -app.use(async (err: Error, req: Request, res: Response, _: NextFunction) => { - if (err instanceof AppError) { - return res.status(err.statusCode).json({ error: err.message }); - } - - console.error(err); - return res.status(500).json({ error: "Internal server error" }); -}); - const server = app.listen(process.env.PORT, () => { console.log(`Server started on port: ${process.env.PORT}`); }); @@ -75,14 +66,13 @@ const startWhatsAppSessions = async () => { }; startWhatsAppSessions(); -// app.use(Sentry.Handlers.errorHandler()); +app.use(Sentry.Handlers.errorHandler()); -// app.use(async (err, req, res, next) => { -// if (process.env.NODE_ENV === "DEVELOPMENT") { -// const errors = await new Youch(err, req).toJSON(); -// console.log(err); -// return res.status(500).json(errors); -// } +app.use(async (err: Error, req: Request, res: Response, _: NextFunction) => { + if (err instanceof AppError) { + return res.status(err.statusCode).json({ error: err.message }); + } -// return res.status(500).json({ error: "Internal server error" }); -// }); + console.error(err); + return res.status(500).json({ error: "Internal server error" }); +}); From 0d8b4cd60c9a21ed24cf48bb551489209454ab14 Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 11:09:52 -0300 Subject: [PATCH 45/54] migrated import contacts controller to typescript --- .../ImportPhoneContactsController.ts | 42 ++------- backend/src/controllers/WhatsAppController.ts | 3 +- backend/src/routes/contactRoutes.ts | 8 +- backend/src/server.ts | 3 +- .../WbotServices/ImportContactsService.ts | 28 ++++++ .../src/services/WbotServices/wbotMonitor.js | 86 ------------------ .../src/services/WbotServices/wbotMonitor.ts | 87 +++++++++++++++++++ 7 files changed, 131 insertions(+), 126 deletions(-) create mode 100644 backend/src/services/WbotServices/ImportContactsService.ts delete mode 100644 backend/src/services/WbotServices/wbotMonitor.js create mode 100644 backend/src/services/WbotServices/wbotMonitor.ts diff --git a/backend/src/controllers/ImportPhoneContactsController.ts b/backend/src/controllers/ImportPhoneContactsController.ts index 34f193a..01f1cfc 100644 --- a/backend/src/controllers/ImportPhoneContactsController.ts +++ b/backend/src/controllers/ImportPhoneContactsController.ts @@ -1,38 +1,8 @@ -// const Contact = require("../models/Contact"); -// const Whatsapp = require("../models/Whatsapp"); -// const { getIO } = require("../libs/socket"); -// const { getWbot, initWbot } = require("../libs/wbot"); +import { Request, Response } from "express"; +import ImportContactsService from "../services/WbotServices/ImportContactsService"; -// exports.store = async (req, res, next) => { -// const defaultWhatsapp = await Whatsapp.findOne({ -// where: { default: true } -// }); +export const store = async (req: Request, res: Response): Promise => { + await ImportContactsService(); -// if (!defaultWhatsapp) { -// return res -// .status(404) -// .json({ error: "No default WhatsApp found. Check Connection page." }); -// } - -// const io = getIO(); -// const wbot = getWbot(defaultWhatsapp); - -// let phoneContacts; - -// try { -// phoneContacts = await wbot.getContacts(); -// } catch (err) { -// console.log(err); -// return res.status(500).json({ -// error: "Could not check whatsapp contact. Check connection page." -// }); -// } - -// await Promise.all( -// phoneContacts.map(async ({ number, name }) => { -// await Contact.create({ number, name }); -// }) -// ); - -// return res.status(200).json({ message: "contacts imported" }); -// }; + return res.status(200).json({ message: "contacts imported" }); +}; diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index f0726f4..9e32ee6 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import { getIO } from "../libs/socket"; import { initWbot } from "../libs/wbot"; import wbotMessageListener from "../services/WbotServices/wbotMessageListener"; +import wbotMonitor from "../services/WbotServices/wbotMonitor"; import CreateWhatsAppService from "../services/WhatsappService/CreateWhatsAppService"; import DeleteWhatsAppService from "../services/WhatsappService/DeleteWhatsAppService"; @@ -39,7 +40,7 @@ export const store = async (req: Request, res: Response): Promise => { initWbot(whatsapp) .then(() => { wbotMessageListener(whatsapp); - // wbotMonitor(whatsapp); + wbotMonitor(whatsapp); }) .catch(err => console.log(err)); diff --git a/backend/src/routes/contactRoutes.ts b/backend/src/routes/contactRoutes.ts index b7a0222..5bbc100 100644 --- a/backend/src/routes/contactRoutes.ts +++ b/backend/src/routes/contactRoutes.ts @@ -2,11 +2,15 @@ import express from "express"; import isAuth from "../middleware/isAuth"; import * as ContactController from "../controllers/ContactController"; -// import ImportPhoneContactsController from "../controllers/ImportPhoneContactsController"; +import * as ImportPhoneContactsController from "../controllers/ImportPhoneContactsController"; const contactRoutes = express.Router(); -// contactRoutes.post("/contacts/import", isAuth, ImportPhoneContactsController.store); +contactRoutes.post( + "/contacts/import", + isAuth, + ImportPhoneContactsController.store +); contactRoutes.get("/contacts", isAuth, ContactController.index); diff --git a/backend/src/server.ts b/backend/src/server.ts index 37c88c3..6beb9a4 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -16,6 +16,7 @@ import { initWbot } from "./libs/wbot"; // const wbotMonitor = require("./services/wbotMonitor"); import Whatsapp from "./models/Whatsapp"; import wbotMessageListener from "./services/WbotServices/wbotMessageListener"; +import wbotMonitor from "./services/WbotServices/wbotMonitor"; Sentry.init({ dsn: process.env.SENTRY_DSN }); @@ -58,7 +59,7 @@ const startWhatsAppSessions = async () => { initWbot(whatsapp) .then(() => { wbotMessageListener(whatsapp); - // wbotMonitor(whatsapp); + wbotMonitor(whatsapp); }) .catch(err => console.log(err)); }); diff --git a/backend/src/services/WbotServices/ImportContactsService.ts b/backend/src/services/WbotServices/ImportContactsService.ts new file mode 100644 index 0000000..297893f --- /dev/null +++ b/backend/src/services/WbotServices/ImportContactsService.ts @@ -0,0 +1,28 @@ +import AppError from "../../errors/AppError"; +import GetDefaultWhatsApp from "../../helpers/GetDefaultWhatsApp"; +import { getWbot } from "../../libs/wbot"; +import Contact from "../../models/Contact"; + +const ImportContactsService = async (): Promise => { + const defaultWhatsapp = await GetDefaultWhatsApp(); + + const wbot = getWbot(defaultWhatsapp.id); + + let phoneContacts; + + try { + phoneContacts = await wbot.getContacts(); + } catch (err) { + throw new AppError( + "Could not check whatsapp contact. Check connection page." + ); + } + + await Promise.all( + phoneContacts.map(async ({ number, name }) => { + await Contact.create({ number, name }); + }) + ); +}; + +export default ImportContactsService; diff --git a/backend/src/services/WbotServices/wbotMonitor.js b/backend/src/services/WbotServices/wbotMonitor.js deleted file mode 100644 index 696b44d..0000000 --- a/backend/src/services/WbotServices/wbotMonitor.js +++ /dev/null @@ -1,86 +0,0 @@ -const Sentry = require("@sentry/node"); - -const wbotMessageListener = require("./wbotMessageListener"); - -const { getIO } = require("../libs/socket"); -const { getWbot, initWbot } = require("../libs/wbot"); - -const wbotMonitor = whatsapp => { - const io = getIO(); - const sessionName = whatsapp.name; - const wbot = getWbot(whatsapp.id); - - try { - wbot.on("change_state", async newState => { - console.log("Monitor session:", sessionName, newState); - try { - await whatsapp.update({ status: newState }); - } catch (err) { - Sentry.captureException(err); - console.log(err); - } - - io.emit("session", { - action: "update", - session: whatsapp, - }); - }); - - wbot.on("change_battery", async batteryInfo => { - const { battery, plugged } = batteryInfo; - console.log( - `Battery session: ${sessionName} ${battery}% - Charging? ${plugged}` - ); - - try { - await whatsapp.update({ battery, plugged }); - } catch (err) { - Sentry.captureException(err); - console.log(err); - } - - io.emit("session", { - action: "update", - session: whatsapp, - }); - }); - - wbot.on("disconnected", async reason => { - console.log("Disconnected session:", sessionName, reason); - try { - await whatsapp.update({ status: "disconnected" }); - } catch (err) { - Sentry.captureException(err); - console.log(err); - } - - io.emit("session", { - action: "update", - session: whatsapp, - }); - - setTimeout( - () => - initWbot(whatsapp) - .then(() => { - wbotMessageListener(whatsapp); - wbotMonitor(whatsapp); - }) - .catch(err => { - Sentry.captureException(err); - console.log(err); - }), - 2000 - ); - }); - - // setInterval(() => { - // wbot.resetState(); - // }, 20000); - } catch (err) { - Sentry.captureException(err); - console.log(err); - } -}; - -module.exports = wbotMonitor; diff --git a/backend/src/services/WbotServices/wbotMonitor.ts b/backend/src/services/WbotServices/wbotMonitor.ts new file mode 100644 index 0000000..5f1995e --- /dev/null +++ b/backend/src/services/WbotServices/wbotMonitor.ts @@ -0,0 +1,87 @@ +import * as Sentry from "@sentry/node"; + +import wbotMessageListener from "./wbotMessageListener"; + +import { getIO } from "../../libs/socket"; +import { getWbot, initWbot } from "../../libs/wbot"; +import Whatsapp from "../../models/Whatsapp"; + +const wbotMonitor = (whatsapp: Whatsapp): void => { + const io = getIO(); + const sessionName = whatsapp.name; + const wbot = getWbot(whatsapp.id); + + try { + wbot.on("change_state", async newState => { + console.log("Monitor session:", sessionName, newState); + try { + await whatsapp.update({ status: newState }); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } + + io.emit("session", { + action: "update", + session: whatsapp + }); + }); + + wbot.on("change_battery", async batteryInfo => { + const { battery, plugged } = batteryInfo; + console.log( + `Battery session: ${sessionName} ${battery}% - Charging? ${plugged}` + ); + + try { + await whatsapp.update({ battery, plugged }); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } + + io.emit("session", { + action: "update", + session: whatsapp + }); + }); + + wbot.on("disconnected", async reason => { + console.log("Disconnected session:", sessionName, reason); + try { + await whatsapp.update({ status: "disconnected" }); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } + + io.emit("session", { + action: "update", + session: whatsapp + }); + + setTimeout( + () => + initWbot(whatsapp) + .then(() => { + wbotMessageListener(whatsapp); + wbotMonitor(whatsapp); + }) + .catch(err => { + Sentry.captureException(err); + console.log(err); + }), + 2000 + ); + }); + + // setInterval(() => { + // wbot.resetState(); + // }, 20000); + } catch (err) { + Sentry.captureException(err); + console.log(err); + } +}; + +export default wbotMonitor; From 5c7a759efabe54c1b67004bb5be15bd6c8bd2336 Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 16:33:13 -0300 Subject: [PATCH 46/54] fix: handling wwebjs erros without blocking routes --- backend/src/controllers/WhatsAppController.ts | 16 +------- backend/src/libs/socket.ts | 16 ++++++++ backend/src/server.ts | 41 ++----------------- .../WbotServices/ImportContactsService.ts | 17 ++++---- .../WbotServices/SendWhatsAppMedia.ts | 24 +++++++---- .../WbotServices/SendWhatsAppMessage.ts | 22 ++++++---- .../WbotServices/StartWhatsAppSessions.ts | 18 ++++++++ 7 files changed, 80 insertions(+), 74 deletions(-) create mode 100644 backend/src/services/WbotServices/StartWhatsAppSessions.ts diff --git a/backend/src/controllers/WhatsAppController.ts b/backend/src/controllers/WhatsAppController.ts index 9e32ee6..e147fa1 100644 --- a/backend/src/controllers/WhatsAppController.ts +++ b/backend/src/controllers/WhatsAppController.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; import { getIO } from "../libs/socket"; -import { initWbot } from "../libs/wbot"; +import { initWbot, removeWbot } from "../libs/wbot"; import wbotMessageListener from "../services/WbotServices/wbotMessageListener"; import wbotMonitor from "../services/WbotServices/wbotMonitor"; @@ -33,10 +33,6 @@ export const store = async (req: Request, res: Response): Promise => { const whatsapp = await CreateWhatsAppService({ name, status, isDefault }); - // if (!whatsapp) { - // return res.status(400).json({ error: "Cannot create whatsapp session." }); - // } - initWbot(whatsapp) .then(() => { wbotMessageListener(whatsapp); @@ -70,14 +66,6 @@ export const update = async ( const whatsapp = await UpdateWhatsAppService({ whatsappData, whatsappId }); - // const whatsapp = await Whatsapp.findByPk(whatsappId); - - // if (!whatsapp) { - // return res.status(404).json({ message: "Whatsapp not found" }); - // } - - // await whatsapp.update(req.body); - const io = getIO(); io.emit("whatsapp", { action: "update", @@ -94,7 +82,7 @@ export const remove = async ( const { whatsappId } = req.params; await DeleteWhatsAppService(whatsappId); - // removeWbot(whatsapp.id); + removeWbot(+whatsappId); const io = getIO(); io.emit("whatsapp", { diff --git a/backend/src/libs/socket.ts b/backend/src/libs/socket.ts index cf5bcbf..cdeacc1 100644 --- a/backend/src/libs/socket.ts +++ b/backend/src/libs/socket.ts @@ -6,6 +6,22 @@ let io: SocketIO; export const initIO = (httpServer: Server): SocketIO => { io = socketIo(httpServer); + io.on("connection", socket => { + console.log("Client Connected"); + socket.on("joinChatBox", ticketId => { + console.log("A client joined a ticket channel"); + socket.join(ticketId); + }); + + socket.on("joinNotification", () => { + console.log("A client joined notification channel"); + socket.join("notification"); + }); + + socket.on("disconnect", () => { + console.log("Client disconnected"); + }); + }); return io; }; export const getIO = (): SocketIO => { diff --git a/backend/src/server.ts b/backend/src/server.ts index 6beb9a4..b023c28 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -11,12 +11,7 @@ import uploadConfig from "./config/upload"; import AppError from "./errors/AppError"; import routes from "./routes"; import { initIO } from "./libs/socket"; -import { initWbot } from "./libs/wbot"; - -// const wbotMonitor = require("./services/wbotMonitor"); -import Whatsapp from "./models/Whatsapp"; -import wbotMessageListener from "./services/WbotServices/wbotMessageListener"; -import wbotMonitor from "./services/WbotServices/wbotMonitor"; +import { StartWhatsAppSessions } from "./services/WbotServices/StartWhatsAppSessions"; Sentry.init({ dsn: process.env.SENTRY_DSN }); @@ -34,38 +29,8 @@ const server = app.listen(process.env.PORT, () => { console.log(`Server started on port: ${process.env.PORT}`); }); -const io = initIO(server); -io.on("connection", socket => { - console.log("Client Connected"); - socket.on("joinChatBox", ticketId => { - console.log("A client joined a ticket channel"); - socket.join(ticketId); - }); - - socket.on("joinNotification", () => { - console.log("A client joined notification channel"); - socket.join("notification"); - }); - - socket.on("disconnect", () => { - console.log("Client disconnected"); - }); -}); - -const startWhatsAppSessions = async () => { - const whatsapps = await Whatsapp.findAll(); - if (whatsapps.length > 0) { - whatsapps.forEach(whatsapp => { - initWbot(whatsapp) - .then(() => { - wbotMessageListener(whatsapp); - wbotMonitor(whatsapp); - }) - .catch(err => console.log(err)); - }); - } -}; -startWhatsAppSessions(); +initIO(server); +StartWhatsAppSessions(); app.use(Sentry.Handlers.errorHandler()); diff --git a/backend/src/services/WbotServices/ImportContactsService.ts b/backend/src/services/WbotServices/ImportContactsService.ts index 297893f..a45a249 100644 --- a/backend/src/services/WbotServices/ImportContactsService.ts +++ b/backend/src/services/WbotServices/ImportContactsService.ts @@ -13,16 +13,19 @@ const ImportContactsService = async (): Promise => { try { phoneContacts = await wbot.getContacts(); } catch (err) { - throw new AppError( - "Could not check whatsapp contact. Check connection page." + console.log( + "Could not get whatsapp contacts from phone. Check connection page.", + err ); } - await Promise.all( - phoneContacts.map(async ({ number, name }) => { - await Contact.create({ number, name }); - }) - ); + if (phoneContacts) { + await Promise.all( + phoneContacts.map(async ({ number, name }) => { + await Contact.create({ number, name }); + }) + ); + } }; export default ImportContactsService; diff --git a/backend/src/services/WbotServices/SendWhatsAppMedia.ts b/backend/src/services/WbotServices/SendWhatsAppMedia.ts index 424200f..8afade7 100644 --- a/backend/src/services/WbotServices/SendWhatsAppMedia.ts +++ b/backend/src/services/WbotServices/SendWhatsAppMedia.ts @@ -1,4 +1,5 @@ import { MessageMedia, Message as WbotMessage } from "whatsapp-web.js"; +import AppError from "../../errors/AppError"; import GetTicketWbot from "../../helpers/GetTicketWbot"; import Ticket from "../../models/Ticket"; @@ -11,17 +12,24 @@ const SendWhatsAppMedia = async ({ media, ticket }: Request): Promise => { - const wbot = await GetTicketWbot(ticket); + try { + const wbot = await GetTicketWbot(ticket); - const newMedia = MessageMedia.fromFilePath(media.path); + const newMedia = MessageMedia.fromFilePath(media.path); - const sentMessage = await wbot.sendMessage( - `${ticket.contact.number}@c.us`, - newMedia - ); + const sentMessage = await wbot.sendMessage( + `${ticket.contact.number}@c.us`, + newMedia + ); - await ticket.update({ lastMessage: media.filename }); - return sentMessage; + await ticket.update({ lastMessage: media.filename }); + return sentMessage; + } catch (err) { + console.log(err); + throw new AppError( + "Could not send whatsapp message. Check connections page." + ); + } }; export default SendWhatsAppMedia; diff --git a/backend/src/services/WbotServices/SendWhatsAppMessage.ts b/backend/src/services/WbotServices/SendWhatsAppMessage.ts index 403b915..049b1f0 100644 --- a/backend/src/services/WbotServices/SendWhatsAppMessage.ts +++ b/backend/src/services/WbotServices/SendWhatsAppMessage.ts @@ -1,4 +1,5 @@ import { Message as WbotMessage } from "whatsapp-web.js"; +import AppError from "../../errors/AppError"; import GetTicketWbot from "../../helpers/GetTicketWbot"; import Ticket from "../../models/Ticket"; @@ -11,15 +12,22 @@ const SendWhatsAppMessage = async ({ body, ticket }: Request): Promise => { - const wbot = await GetTicketWbot(ticket); + try { + const wbot = await GetTicketWbot(ticket); - const sentMessage = await wbot.sendMessage( - `${ticket.contact.number}@c.us`, - body - ); + const sentMessage = await wbot.sendMessage( + `${ticket.contact.number}@c.us`, + body + ); - await ticket.update({ lastMessage: body }); - return sentMessage; + await ticket.update({ lastMessage: body }); + return sentMessage; + } catch (err) { + console.log(err); + throw new AppError( + "Could not send whatsapp message. Check connections page." + ); + } }; export default SendWhatsAppMessage; diff --git a/backend/src/services/WbotServices/StartWhatsAppSessions.ts b/backend/src/services/WbotServices/StartWhatsAppSessions.ts new file mode 100644 index 0000000..82dc542 --- /dev/null +++ b/backend/src/services/WbotServices/StartWhatsAppSessions.ts @@ -0,0 +1,18 @@ +import { initWbot } from "../../libs/wbot"; +import Whatsapp from "../../models/Whatsapp"; +import wbotMessageListener from "./wbotMessageListener"; +import wbotMonitor from "./wbotMonitor"; + +export const StartWhatsAppSessions = async (): Promise => { + const whatsapps = await Whatsapp.findAll(); + if (whatsapps.length > 0) { + whatsapps.forEach(whatsapp => { + initWbot(whatsapp) + .then(() => { + wbotMessageListener(whatsapp); + wbotMonitor(whatsapp); + }) + .catch(err => console.log(err)); + }); + } +}; From ee9fe6c60d5f5ed61f53f72b8d0af51764feb837 Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 17:55:22 -0300 Subject: [PATCH 47/54] fix: check message types on wbotMessageListener --- .../WbotServices/wbotMessageListener.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index d1dc5ed..fb5a7e8 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -146,6 +146,21 @@ const handleMessage = async ( }); }; +const isValidMsg = (msg: WbotMessage): boolean => { + if (msg.from === "status@broadcast" || msg.author) return false; + if ( + msg.type === "chat" || + msg.type === "audio" || + msg.type === "ptt" || + msg.type === "video" || + msg.type === "image" || + msg.type === "document" || + msg.type === "vcard" + ) + return true; + return false; +}; + const wbotMessageListener = (whatsapp: Whatsapp): void => { const whatsappId = whatsapp.id; const wbot = getWbot(whatsappId); @@ -159,12 +174,7 @@ const wbotMessageListener = (whatsapp: Whatsapp): void => { wbot.on("message_create", async msg => { console.log(msg.type); - if ( - msg.from === "status@broadcast" || - msg.type === "location" || - msg.type === "call_log" || - msg.author // Ignore Group Messages - ) { + if (!isValidMsg(msg)) { return; } From c0e42d71c08177cf8b53a6a5e351f3a3182300ed Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 21:18:06 -0300 Subject: [PATCH 48/54] code cleanup --- backend/src/services/WbotServices/wbotMessageListener.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index fb5a7e8..3e267e6 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -147,7 +147,7 @@ const handleMessage = async ( }; const isValidMsg = (msg: WbotMessage): boolean => { - if (msg.from === "status@broadcast" || msg.author) return false; + if (msg.author) return false; if ( msg.type === "chat" || msg.type === "audio" || From e6641bde39771572fd4767931dd7cd679dc4aafd Mon Sep 17 00:00:00 2001 From: canove Date: Tue, 22 Sep 2020 21:20:29 -0300 Subject: [PATCH 49/54] code cleanup --- backend/src/services/WbotServices/wbotMessageListener.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 3e267e6..af352f4 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -166,11 +166,6 @@ const wbotMessageListener = (whatsapp: Whatsapp): void => { const wbot = getWbot(whatsappId); const io = getIO(); - if (!wbot) { - console.log("Wbot not initialized"); - return; - } - wbot.on("message_create", async msg => { console.log(msg.type); From 34a99ade2de9b880f47a7e3cd994d27ced426a68 Mon Sep 17 00:00:00 2001 From: canove Date: Thu, 24 Sep 2020 13:49:38 -0300 Subject: [PATCH 50/54] improvement: error handling on contact creation --- backend/src/libs/wbot.ts | 4 +++- backend/src/services/ContactServices/CreateContactService.ts | 2 -- backend/src/services/WbotServices/CheckIsValidContact.ts | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/src/libs/wbot.ts b/backend/src/libs/wbot.ts index 49c0224..cf6626b 100644 --- a/backend/src/libs/wbot.ts +++ b/backend/src/libs/wbot.ts @@ -92,7 +92,9 @@ export const getWbot = (whatsappId: number): Session => { const sessionIndex = sessions.findIndex(s => s.id === whatsappId); if (sessionIndex === -1) { - throw new AppError("This WhatsApp session is not initialized."); + throw new AppError( + "This WhatsApp session is not initialized. Check connections page." + ); } return sessions[sessionIndex]; }; diff --git a/backend/src/services/ContactServices/CreateContactService.ts b/backend/src/services/ContactServices/CreateContactService.ts index e1d258d..66fbfc1 100644 --- a/backend/src/services/ContactServices/CreateContactService.ts +++ b/backend/src/services/ContactServices/CreateContactService.ts @@ -28,8 +28,6 @@ const CreateContactService = async ({ throw new AppError("A contact with this number already exists."); } - console.log("extra", extraInfo); - const contact = await Contact.create( { name, diff --git a/backend/src/services/WbotServices/CheckIsValidContact.ts b/backend/src/services/WbotServices/CheckIsValidContact.ts index dc9edd6..9ec0c27 100644 --- a/backend/src/services/WbotServices/CheckIsValidContact.ts +++ b/backend/src/services/WbotServices/CheckIsValidContact.ts @@ -10,10 +10,13 @@ const CheckIsValidContact = async (number: string): Promise => { try { const isValidNumber = await wbot.isRegisteredUser(`${number}@c.us`); if (!isValidNumber) { - throw new AppError("The suplied number is not a valid Whatsapp number"); + throw new AppError("invalidNumber"); } } catch (err) { console.log(err); + if (err.message === "invalidNumber") { + throw new AppError("This is not a valid whatsapp number."); + } throw new AppError( "Could not valid WhatsApp contact. Check connections page" ); From f6b5ed389a4d08238a1797527a403259b3c24ad5 Mon Sep 17 00:00:00 2001 From: canove Date: Thu, 24 Sep 2020 14:08:57 -0300 Subject: [PATCH 51/54] improvement: better token error handling --- backend/src/middleware/isAuth.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/src/middleware/isAuth.ts b/backend/src/middleware/isAuth.ts index cc9d0be..1aa6218 100644 --- a/backend/src/middleware/isAuth.ts +++ b/backend/src/middleware/isAuth.ts @@ -21,13 +21,17 @@ const isAuth = (req: Request, res: Response, next: NextFunction): void => { const [, token] = authHeader.split(" "); - const decoded = verify(token, authConfig.secret); - const { id, profile } = decoded as TokenPayload; + try { + const decoded = verify(token, authConfig.secret); + const { id, profile } = decoded as TokenPayload; - req.user = { - id, - profile - }; + req.user = { + id, + profile + }; + } catch (err) { + throw new AppError("Invalid token.", 403); + } return next(); }; From e1db12d545dc585fb12ff82ecbd2c4078229b6fd Mon Sep 17 00:00:00 2001 From: canove Date: Thu, 24 Sep 2020 14:15:06 -0300 Subject: [PATCH 52/54] improvement: added types to ListTicketService --- backend/src/services/TicketServices/ListTicketsService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts index ece7f91..1368b06 100644 --- a/backend/src/services/TicketServices/ListTicketsService.ts +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -1,4 +1,4 @@ -import { Op, fn, where, col } from "sequelize"; +import { Op, fn, where, col, Filterable, Includeable } from "sequelize"; import { startOfDay, endOfDay, parseISO } from "date-fns"; import Ticket from "../../models/Ticket"; @@ -28,8 +28,8 @@ const ListTicketsService = async ({ showAll, userId }: Request): Promise => { - let whereCondition = {}; - let includeCondition = []; + let whereCondition: Filterable["where"]; + let includeCondition: Includeable[]; includeCondition = [ { @@ -97,7 +97,7 @@ const ListTicketsService = async ({ whereCondition = { ...whereCondition, createdAt: { - [Op.between]: [startOfDay(parseISO(date)), endOfDay(parseISO(date))] + [Op.between]: [+startOfDay(parseISO(date)), +endOfDay(parseISO(date))] } }; } From 0729f252950d919e69606eba44812912cfe4c643 Mon Sep 17 00:00:00 2001 From: canove Date: Thu, 24 Sep 2020 14:27:11 -0300 Subject: [PATCH 53/54] improvement: change code execution order in some services --- .../MessageServices/ListMessagesService.ts | 12 ++++++------ .../services/UserServices/UpdateUserService.ts | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/backend/src/services/MessageServices/ListMessagesService.ts b/backend/src/services/MessageServices/ListMessagesService.ts index 7656472..5791420 100644 --- a/backend/src/services/MessageServices/ListMessagesService.ts +++ b/backend/src/services/MessageServices/ListMessagesService.ts @@ -21,6 +21,12 @@ const ListMessagesService = async ({ pageNumber = "1", ticketId }: Request): Promise => { + const ticket = await ShowTicketService(ticketId); + + if (!ticket) { + throw new Error("No ticket found with this ID"); + } + const whereCondition = { body: where( fn("LOWER", col("body")), @@ -30,12 +36,6 @@ const ListMessagesService = async ({ ticketId }; - const ticket = await ShowTicketService(ticketId); - - if (!ticket) { - throw new Error("No ticket found with this ID"); - } - // await setMessagesAsRead(ticket); const limit = 20; const offset = limit * (+pageNumber - 1); diff --git a/backend/src/services/UserServices/UpdateUserService.ts b/backend/src/services/UserServices/UpdateUserService.ts index 438b85b..883ea2e 100644 --- a/backend/src/services/UserServices/UpdateUserService.ts +++ b/backend/src/services/UserServices/UpdateUserService.ts @@ -26,6 +26,15 @@ const UpdateUserService = async ({ userData, userId }: Request): Promise => { + const user = await User.findOne({ + where: { id: userId }, + attributes: ["name", "id", "email", "profile"] + }); + + if (!user) { + throw new AppError("No user found with this ID.", 404); + } + const schema = Yup.object().shape({ name: Yup.string().min(2), email: Yup.string().email(), @@ -40,15 +49,6 @@ const UpdateUserService = async ({ throw new AppError(err.message); } - const user = await User.findOne({ - where: { id: userId }, - attributes: ["name", "id", "email", "profile"] - }); - - if (!user) { - throw new AppError("No user found with this ID.", 404); - } - await user.update({ email, password, From d44b81b05d822dc0ee7e335ec44f7c7b9e06d909 Mon Sep 17 00:00:00 2001 From: canove Date: Thu, 24 Sep 2020 14:48:59 -0300 Subject: [PATCH 54/54] update readme with build instructions for new typescript backend --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2cbe357..27c87b1 100644 --- a/README.md +++ b/README.md @@ -64,25 +64,71 @@ Install puppeteer dependencies: sudo apt-get install -y libgbm-dev wget unzip fontconfig locales gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils ``` -- Clone this repo -- On backend folder: - - Copy .env.example to .env and fill it with database details. - - Install dependecies: `npm install` (Only in the first time) - - Create database tables: `npx sequelize db:migrate` (Only in the first time) - - Fill database with initial values: `npx sequelize db:seed:all` - - Start backend: `npm start` -- In another terminal, on frontend folder: - - Copy .env.example to .env and fill it with backend URL (normally localhost:port) - - Install dependecies: `npm install` (Only in the first time) - - Start frontend: `npm start` +Clone this repo + +```bash +git clone https://github.com/canove/whaticket/ whaticket +``` + +Go to backend folder and create .env file: + +```bash +cp .env.example .env +nano .env +``` + +Fill `.env` file with environment variables: + +```bash +NODE_ENV=DEVELOPMENT #it helps on debugging +BACKEND_URL=http://localhost +PROXY_PORT=8080 +PORT=8080 + +DB_HOST= #DB host IP, usually localhost +DB_USER= +DB_PASS= +DB_NAME= +``` + +Install backend dependencies, build app, run migrations and seeds: + +```bash +npm install +npm build +npx sequelize db:migrate +npx sequelize db:seed:all +``` + +Start backend: + +```bash +npm start +``` + +Open a second terminal, go to frontend folder and create .env file: + +```bash +nano .env +REACT_APP_BACKEND_URL = http://localhost:8080/ # Your previous configured backend app URL. +``` + +Start frontend app: + +```bash +npm start +``` + - Go to http://your_server_ip:3000/signup - Create an user and login with it. -- On the sidebard, go to _Connections_ and create your first WhatsApp connection. +- On the sidebard, go to _Connections_ page and create your first WhatsApp connection. - Wait for QR CODE button to appear, click it and read qr code. - Done. Every message received by your synced WhatsApp number will appear in Tickets List. ## Basic production deployment (Ubuntu 18.04 VPS) +All instructions below assumes you are NOT running as root, since it will give error in puppeteer. See + You'll need two subdomains forwarding to yours VPS ip to follow these instructions. We'll use `myapp.mydomain.com` to frontend and `api.mydomain.com` to backend in the following example. We'll also use an dedicated user with sudo privileges no deploy it (not root). Update all system packages: @@ -152,11 +198,12 @@ Install puppeteer dependencies: sudo apt-get install -y libgbm-dev wget unzip fontconfig locales gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils ``` -Install backend dependencies, run migrations and seeds: +Install backend dependencies, build app, run migrations and seeds: ```bash cd whaticket/backend npm install +npm build npx sequelize db:migrate npx sequelize db:seed:all ```