diff --git a/. jshintrc b/. jshintrc new file mode 100644 index 00000000..1efdc213 --- /dev/null +++ b/. jshintrc @@ -0,0 +1,4 @@ +{ + "esversion": 6, + "browser": false +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 67045665..d3bb4579 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,6 @@ dist # TernJS port file .tern-port + +#ngrock file +.ngrock \ No newline at end of file diff --git a/README.md b/README.md index 91555583..f1c14d66 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,6 @@ FDK Extension Helper Library const redis = new Redis(); let extensionHandler = { - setup: async function(data) { - console.log("called setup callback"); - }, - install: async function(data) { console.log("called install callback"); }, @@ -37,22 +33,17 @@ FDK Extension Helper Library let baseUrl = "https://test.extension.com"; - let FDKClient = setupFdk({ - name: "Transformer", + let fdkClient = setupFdk({ + api_key: "", + api_secret: "", base_url: baseUrl, - logo : { - small: `${baseUrl}\icon.png`, - large: `${baseUrl}\icon.png`, - }, scopes: ["company/products"], - prefix_path: "/callback", callbacks: extensionHandler, - contact_email: "xyz@gmail.com", - developed_by_name: "Fynd", - webhooks: [], - storage: new RedisStorage(redis) + storage: new RedisStorage(redis), + access_mode: "offline", + cluster: "https://api.fyndx0.de" // this is optional by default it points to prod. }); - app.use(FDKClient.fdkHandler); + app.use(fdkClient.fdkHandler); app.listen(8080); ``` diff --git a/examples/test.js b/examples/test.js index db3714c0..db353f40 100644 --- a/examples/test.js +++ b/examples/test.js @@ -4,7 +4,7 @@ const cookieParser = require('cookie-parser'); const { setupFdk } = require("../express"); -const { MemoryStorage, RedisStorage } = require("./../express/storage"); +const { RedisStorage } = require("./../express/storage"); const extensionHandler = require("./extension.handler"); const Redis = require("ioredis"); @@ -23,29 +23,25 @@ console.log(baseUrl); const redis = new Redis(); -let FDKExtension = setupFdk({ - name: "Transformer", +let fdkExtension = setupFdk({ + api_key: "60b85632a11966719e7d9aed", + api_secret: "aE7lauU_LxM1tJD", base_url: baseUrl, - logo: { - small: `${baseUrl}/icon.png`, - large: `${baseUrl}/icon.png` - }, - scopes: ["company/products"], + scopes: ["company/product"], callbacks: extensionHandler, - contact_email: "xyz@gmail.com", - developed_by_name: "Fynd", storage: new RedisStorage(redis), - access_mode: "offline" + access_mode: "offline", + cluster: "https://api.fyndx0.de" // this is optional by default it points to prod. }); -app.use(FDKExtension.fdkHandler); +app.use(fdkExtension.fdkHandler); app.use('/_healthz', (req, res, next) => { res.json({ "ok": "ok" }); }); -FDKExtension.apiRoutes.get("/test/routes", async (req, res, next) => { +fdkExtension.apiRoutes.get("/test/routes", async (req, res, next) => { try { let data = await req.platformClient.lead.getTickets(); res.json(data); @@ -56,7 +52,7 @@ FDKExtension.apiRoutes.get("/test/routes", async (req, res, next) => { }); -FDKExtension.applicationProxyRoutes.get("/1234", async (req, res, next) => { +fdkExtension.applicationProxyRoutes.get("/1234", async (req, res, next) => { try { let data = await req.platformClient.lead.getTickets(); res.json(data); @@ -67,37 +63,24 @@ FDKExtension.applicationProxyRoutes.get("/1234", async (req, res, next) => { }); -app.use(FDKExtension.applicationProxyRoutes); -app.use(FDKExtension.apiRoutes); - -// FDKExtension.apiRoutes.post("/:application_id", async (req, res, next) => { -// try { -// const { application_id } = req.params; -// const { platformClient } = req; -// const platformApiUrl = `https://api.fyndx0.de/service/platform/partners/v1.0/company/${req.fdkSession.company_id}/application/${application_id}/proxy/${platformClient.config.apiKey}`; -// response = await APIClient.execute(platformClient.config, 'post', platformApiUrl, {}, { -// attached_path: "chatbot-test", -// proxy_url: `https://chatbots.extensions.fyndx0.de/proxy/application` -// }); -// return res.json(response); -// } -// catch(err) { -// next(err); -// } -// }); - - // sample webhook endpoint const webhookRouter = express.Router({ mergeParams: true }); webhookRouter.get("/webhook", async (req, res, next) => { - // fetch company id from query params - let companyId = req.query.companyId; - let cluster = "https://api.fyndx0.de"; // either take it from some env variables like "https://api.fyndx0.de" - let client = await FDKExtension.getPlatformClient(cluster, companyId); - res.json({"success": true}); + try { + // fetch company id from query params + let companyId = req.query.companyId; + let cluster = "https://api.fyndx0.de"; // either take it from some env variables like "https://api.fyndx0.de" + let client = await fdkExtension.getPlatformClient(companyId); + res.json({"success": true}); + } catch (err) { + console.error(err); + res.status(404).json({"success": false}); + } }); app.use(webhookRouter); +app.use(fdkExtension.applicationProxyRoutes); +app.use(fdkExtension.apiRoutes); app.use("*", async (req, res, next) => { res.json({"success": true}); diff --git a/express/api_routes.js b/express/api_routes.js index abb0acf5..55acf630 100644 --- a/express/api_routes.js +++ b/express/api_routes.js @@ -2,7 +2,6 @@ const { extension } = require('./extension'); const express = require('express'); const { sessionMiddleware } = require('./middleware/session_middleware'); -const FdkHelper = require("./fdk_helper"); const { PlatformClient, ApplicationConfig, ApplicationClient } = require("fdk-client-javascript"); @@ -32,11 +31,7 @@ function setupProxyRoutes() { apiRoutes.use(sessionMiddleware(true), async (req, res, next) => { try { - let fdkHelper = FdkHelper.getInstance(req.fdkSession.cluster, extension); - let platformConfig = await fdkHelper.getPlatformConfigInstance(req.fdkSession.company_id); - platformConfig.oauthClient.setToken(req.fdkSession); - await platformConfig.oauthClient.renewAccessToken(); - const client = new PlatformClient(platformConfig); + const client = await extension.getPlatformClient(req.fdkSession.company_id, req.fdkSession); req.platformClient = client; req.extension = extension; next(); @@ -46,11 +41,12 @@ function setupProxyRoutes() { }); return { - apiRoutes, - applicationProxyRoutes + platformApiRoutes: apiRoutes, + apiRoutes: apiRoutes, // this is deprecated use platformApiRoutes + applicationProxyRoutes: applicationProxyRoutes }; } module.exports = { - setupProxyRoutes + setupProxyRoutes: setupProxyRoutes }; \ No newline at end of file diff --git a/express/extension.js b/express/extension.js index 992ed9cb..d2281e5b 100644 --- a/express/extension.js +++ b/express/extension.js @@ -2,39 +2,37 @@ const validator = require('validator'); const {FdkInvalidExtensionJson} = require("./error_code"); const urljoin = require('url-join'); +const { PlatformConfig, PlatformClient, ApplicationConfig, ApplicationClient } = require("fdk-client-javascript"); class Extension { constructor() { + this.api_key = null; + this.api_secret = null; this.storage = null; - this.name = null; - this.prefix_path = null; this.base_url = null; - this.logo = { - small: null, // Replace with dummy logo url - large: null - }, this.callbacks = null; - this.developed_by_name = null; - this.contact_email = null; this.access_mode = null; + this.cluster = "https://api.fynd.com"; } initialize(data) { this.storage = data.storage; - this.name = data.name; - this.prefix_path = data.prefix_path || "/callback"; - + + if(!data.api_key){ + throw new FdkInvalidExtensionJson("Invalid api_key"); + } + this.api_key = data.api_key; + + if(!data.api_secret){ + throw new FdkInvalidExtensionJson("Invalid api_secret"); + } + this.api_secret = data.api_secret; + if(!validator.isURL(data.base_url)) { throw new FdkInvalidExtensionJson("Invalid base_url"); } this.base_url = data.base_url; - - if(!data.logo.small || !data.logo.large) { - throw new FdkInvalidExtensionJson("Missing logo"); - } - - this.logo = data.logo; this.scopes = this.verifyScopes(data.scopes); if(!data.callbacks || ( data.callbacks && (!data.callbacks.setup || !data.callbacks.auth || !data.callbacks.install || !data.callbacks.uninstall))) { @@ -42,17 +40,16 @@ class Extension { } this.callbacks = data.callbacks; - this.developed_by_name = data.developed_by_name; - - if(data.contact_email && validator.isEmail(data.contact_email)) { - this.contact_email = data.contact_email; - } else { - this.contact_email = null; - } this.access_mode = data.access_mode || "offline"; + + if(data.cluster) { + if(!validator.isURL(data.cluster)) { + throw new FdkInvalidExtensionJson("Invalid cluster"); + } + this.cluster = data.cluster; + } } - verifyScopes(scopes) { if(!scopes || scopes.length <= 0 ) { throw new FdkInvalidExtensionJson("Invalid scopes in extension.json"); @@ -61,47 +58,38 @@ class Extension { } getAuthCallback() { - return urljoin(this.base_url, this.prefix_path, "/auth"); - } - - toJson() { - return { - name: this.name, - base_url: this.base_url, - logo: this.logo, - scopes: this.scopes, - developed_by_name: this.developed_by_name, - contact_email: this.contact_email, - callbacks: { - setup: urljoin(this.base_url, this.prefix_path, "/setup"), - auth: urljoin(this.base_url, this.prefix_path, "/auth"), - install: urljoin(this.base_url, this.prefix_path, "/install"), - uninstall: urljoin(this.base_url, this.prefix_path, "/uninstall") - }, - webhooks: [{ - - }] - }; + return urljoin(this.base_url, "/fp/auth"); } isOnlineAccessMode() { return this.access_mode === 'online'; } - async register() { - // TODO: add code for triggering register extension call explicitly + getPlatformConfig(companyId) { + let platformConfig = new PlatformConfig({ + companyId: parseInt(companyId), + domain: this.cluster, + apiKey: this.api_key, + apiSecret: this.api_secret + }); + return platformConfig; } -} - -let extension = new Extension(); - -function initExtension(data) { - extension.initialize(data); - return extension; + async getPlatformClient(companyId, session) { + let platformConfig = this.getPlatformConfig(companyId); + platformConfig.oauthClient.setToken(session); + if(session.access_token_validity) { + let ac_nr_expired = ((session.access_token_validity - new Date().getTime())/ 1000) <= 120; + if(ac_nr_expired) { + await platformConfig.oauthClient.renewAccessToken(); + } + } + return new PlatformClient(platformConfig); + } } +const extension = new Extension(); + module.exports = { - initExtension, extension }; \ No newline at end of file diff --git a/express/fdk_helper.js b/express/fdk_helper.js deleted file mode 100644 index 2098b8df..00000000 --- a/express/fdk_helper.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; -const { FdkClusterMetaMissingEror } = require("./error_code"); -const { PlatformConfig, PlatformClient, ApplicationConfig, ApplicationClient } = require("fdk-client-javascript"); - -const __helperMap = {}; - - -class FdkHelper { - constructor(cluster, extension) { - this.cluster = cluster; - this.clusterMeta = null; - this.extension = extension; - this.storage = this.extension.storage; - this.callbacks = this.extension.callbacks; - this.domain = null; - } - - static getInstance(cluster, extension, clusterMeta) { - if(__helperMap[cluster]) { - return __helperMap[cluster]; - } - let fdkHlper = new FdkHelper(cluster, extension, clusterMeta); - __helperMap[cluster] = fdkHlper; - return fdkHlper; - } - - async setClusterMeta(data) { - this.clusterMeta = data; - this.domain = new URL(this.clusterMeta.cluster).origin; - await this.storage.set(this.cluster, JSON.stringify(data)); - } - - - async loadClusterMeta() { - if(this.clusterMeta) { - return this.clusterMeta; - } - let clusterMeta = await this.storage.get(this.cluster); - - if(!clusterMeta) { - throw new FdkClusterMetaMissingEror('cluster meta missing'); - } - - clusterMeta = JSON.parse(clusterMeta); - this.clusterMeta = clusterMeta; - this.domain = new URL(this.clusterMeta.cluster).origin; - return this.clusterMeta; - } - - async getPlatformConfigInstance(companyId) { - await this.loadClusterMeta(); - let platformConfig = new PlatformConfig({ - companyId: parseInt(companyId), - domain: this.domain, - apiKey: this.clusterMeta.client_id, - apiSecret: this.clusterMeta.secret[0] - }); - - return platformConfig; - } - -} - -module.exports = FdkHelper; \ No newline at end of file diff --git a/express/index.js b/express/index.js index 04d728b8..9cef68e9 100644 --- a/express/index.js +++ b/express/index.js @@ -1,43 +1,37 @@ 'use strict'; -const {initExtension} = require('./extension'); +const {extension} = require('./extension'); const setupRoutes = require("./routes"); const { setupProxyRoutes } = require("./api_routes"); -const FdkHelper = require("./fdk_helper"); const Session = require("./session/session"); const SessionStorage = require("./session/session_storage"); const { PlatformConfig, PlatformClient, ApplicationConfig, ApplicationClient } = require("fdk-client-javascript"); function setupFdk(data) { - data.prefix = data.prefix ? data.prefix : "/callback"; - let extension = initExtension(data); + extension.initialize(data); let router = setupRoutes(extension); let { apiRoutes, applicationProxyRoutes } = setupProxyRoutes(); - async function getPlatformClient(cluster, companyId) { - let fdkHelper = FdkHelper.getInstance(cluster, extension); - let platformConfig = await fdkHelper.getPlatformConfigInstance(companyId); + async function getPlatformClient(companyId) { let client = null; if(!extension.isOnlineAccessMode()) { let sid = Session.generateSessionId(false, { - cluster: cluster, + cluster: extension.cluster, companyId: companyId }); let session = await SessionStorage.getSession(sid); - platformConfig.oauthClient.setToken(session); - await platformConfig.oauthClient.renewAccessToken(); - client = new PlatformClient(platformConfig); + client = await extension.getPlatformClient(companyId, session); } return client; } - async function getApplicationClient(cluster, applicationId, applicationToken) { + async function getApplicationClient(applicationId, applicationToken) { let applicationConfig = new ApplicationConfig({ applicationID: applicationId, applicationToken: applicationToken, - domain: cluster + domain: extension.cluster }); let applicationClient = new ApplicationClient(applicationConfig); return applicationClient; diff --git a/express/routes.js b/express/routes.js index 7db37183..214fa269 100644 --- a/express/routes.js +++ b/express/routes.js @@ -1,86 +1,60 @@ 'use strict'; const express = require('express'); const { v4 : uuidv4} = require("uuid"); -const FdkHelper = require("./fdk_helper"); const Session = require("./session/session"); const SessionStorage = require("./session/session_storage"); const { FdkSessionNotFoundError, FdkInvalidOAuthError } = require("./error_code"); const { SESSION_COOKIE_NAME } = require('./constants'); const { sessionMiddleware } = require('./middleware/session_middleware'); -const { extension } = require('./extension'); const FdkRoutes = express.Router(); const { PlatformConfig, PlatformClient, ApplicationConfig, ApplicationClient } = require("fdk-client-javascript"); +const sha256 = require('crypto-js/sha256'); -// FdkRoutes.use(async (req, res, next) => { -// req.query.cluster = "https://api.fyndx0.de"; -// next(); -// }); function setupRoutes(ext) { let storage = ext.storage; let callbacks = ext.callbacks; - FdkRoutes.get(`/extension.json`, async (req, res) => { - let data = ext.toJson(); - res.json(data); - }); - - FdkRoutes.post(`${ext.prefix_path}/setup`, async (req, res, next) => { - /** - * { - "client_id": "5f0dd8939d35180069468a70", - "secret": ["jjdlajdkjaksjdlkaslkdkalsdj"], - "cluster": "https://api.fynd.com" - } - */ + // setup call is deperected + FdkRoutes.post("/fp/setup", async (req, res, next) => { try { - let cluster = req.body.cluster; - let fpHelper = await FdkHelper.getInstance(cluster, ext); - await fpHelper.setClusterMeta(req.body); - await callbacks.setup(req.body); - res.json({sucess: true}); + res.json({sucess: true, meessage: "This is deprected call"}); } catch (error) { console.error(error); res.status(500, { message: error.message}); } }); - FdkRoutes.get(`${ext.prefix_path}/install`, async (req, res, next) => { - // ?company_id=1&cluster=https://api.fynd.com&client_id=123313112122 + FdkRoutes.get("/fp/install", async (req, res, next) => { + // ?company_id=1&client_id=123313112122 try { - let cluster = req.query.cluster || req.query.cluster_url; let companyId = parseInt(req.query.company_id); - - if(!cluster) { - res.status(400).json({"message": "cluster not found"}); - } - - let fdkHelper = FdkHelper.getInstance(cluster, ext); - let platformConfig = await fdkHelper.getPlatformConfigInstance(companyId); - + let platformConfig = ext.getPlatformConfig(companyId); let session; if(ext.isOnlineAccessMode()) { session = new Session(Session.generateSessionId(true)); } else { let sid = Session.generateSessionId(false, { - cluster: cluster, + cluster: ext.cluster, companyId: companyId }); session = await SessionStorage.getSession(sid); if(!session) { session = new Session(sid); + } else if(session.extension_id !== ext.api_key) { + session = new Session(sid); } } let sessionExpires = new Date(Date.now() + 900000); if(session.isNew) { - session.cluster = cluster; session.company_id = companyId; session.scope = ext.scopes; session.expires = sessionExpires; - session.access_mode = extension.access_mode; + session.access_mode = ext.access_mode; + session.extension_id = ext.api_key; } else { if(session.expires) { session.expires = new Date(session.expires); @@ -88,7 +62,7 @@ function setupRoutes(ext) { } req.fdkSession = session; - req.extension = extension; + req.extension = ext; res.cookie(SESSION_COOKIE_NAME, session.id, { secure: true, @@ -108,19 +82,6 @@ function setupRoutes(ext) { state: session.state, access_mode: ext.access_mode }); - - // if(!session.access_token) { - // session.state = uuidv4(); - // // start authorization flow - // redirectUrl = platformConfig.oauthClient.startAuthorization({ - // scope: session.scope, - // redirectUri: ext.getAuthCallback(), - // state: session.state, - // access_mode: ext.access_mode - // }); - // } else { - // redirectUrl = await ext.callbacks.install(req); - // } await SessionStorage.saveSession(session); res.redirect(redirectUrl); } catch (error) { @@ -128,8 +89,8 @@ function setupRoutes(ext) { } }); - FdkRoutes.get(`${ext.prefix_path}/auth`, sessionMiddleware(false), async (req, res, next) => { - // ?code=ddjfhdsjfsfh&client_id=jsfnsajfhkasf&company_id=1&cluster=https://api.fynd.com&state=jashoh + FdkRoutes.get("/fp/auth", sessionMiddleware(false), async (req, res, next) => { + // ?code=ddjfhdsjfsfh&client_id=jsfnsajfhkasf&company_id=1&state=jashoh try { if(!req.fdkSession) { throw new FdkSessionNotFoundError("Can not complete oauth process as session not found"); @@ -139,14 +100,13 @@ function setupRoutes(ext) { throw new FdkInvalidOAuthError("Invalid oauth call"); } - let fdkHelper = FdkHelper.getInstance(req.fdkSession.cluster, ext); - let platformConfig = await fdkHelper.getPlatformConfigInstance(req.fdkSession.company_id); + let platformConfig = ext.getPlatformConfig(req.fdkSession.company_id); await platformConfig.oauthClient.verifyCallback(req.query); let token = platformConfig.oauthClient.raw_token; let sessionExpires = new Date(Date.now() + token.expires_in * 1000); - if(extension.isOnlineAccessMode()) { + if(ext.isOnlineAccessMode()) { req.fdkSession.expires = sessionExpires; } else { req.fdkSession.expires = null; @@ -154,6 +114,7 @@ function setupRoutes(ext) { req.fdkSession.access_token = token.access_token; req.fdkSession.expires_in = token.expires_in; + req.fdkSession.access_token_validity = sessionExpires.getTime(); req.fdkSession.current_user = token.current_user; req.fdkSession.refresh_token = token.refresh_token; await SessionStorage.saveSession(req.fdkSession); @@ -166,7 +127,7 @@ function setupRoutes(ext) { sameSite: "None" }); - req.extension = extension; + req.extension = ext; let redirectUrl = await ext.callbacks.auth(req); res.redirect(redirectUrl); } catch (error) { @@ -174,22 +135,16 @@ function setupRoutes(ext) { } }); - FdkRoutes.post(`${ext.prefix_path}/uninstall`, async (req, res, next) => { + FdkRoutes.post("/fp/uninstall", async (req, res, next) => { try { - - let {client_id, company_id, cluster} = req.body; - - let fdkHelper = FdkHelper.getInstance(cluster, ext); - let platformConfig = await fdkHelper.getPlatformConfigInstance(company_id); + let {client_id, company_id} = req.body; if(!ext.isOnlineAccessMode()) { let sid = Session.generateSessionId(false, { - cluster: cluster, - companyId: companyId + cluster: ext.cluster, + companyId: company_id }); let session = await SessionStorage.getSession(sid); - platformConfig.oauthClient.setToken(session); - await platformConfig.oauthClient.renewAccessToken(); - const client = new PlatformClient(platformConfig); + const client = await ext.getPlatformClient(company_id, session); req.platformClient = client; } req.extension = ext; diff --git a/express/session/session.js b/express/session/session.js index 9080004a..36df2d5f 100644 --- a/express/session/session.js +++ b/express/session/session.js @@ -5,17 +5,18 @@ const { v4 : uuidv4, v4} = require("uuid"); class Session { constructor(id, isNew = true) { this.id = id; - this.cluster = null; this.company_id = null; this.state = null; this.scope = null; this.expires = null; this.expires_in = null; + this.access_token_validity = null; this.access_mode = 'online'; this.access_token = null; this.current_user = null; this.refresh_token = null; this.isNew = isNew; + this.extension_id = null; } static cloneSession(id, session, isNew=true) { @@ -34,8 +35,9 @@ class Session { access_token: this.access_token, current_user: this.current_user, refresh_token: this.refresh_token, - cluster: this.cluster, - expires_in: this.expires_in + expires_in: this.expires_in, + extension_id: this.extension_id, + access_token_validity: this.access_token_validity }; } diff --git a/package-lock.json b/package-lock.json index 76777bf8..101be186 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,8 @@ { - "requires": true, + "name": "fdk-extension-javascript", + "version": "1.0.0", "lockfileVersion": 1, + "requires": true, "dependencies": { "@types/caseless": { "version": "0.12.2", @@ -108,6 +110,12 @@ "follow-redirects": "^1.10.0" } }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -145,6 +153,16 @@ "type-is": "~1.6.17" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", @@ -172,6 +190,16 @@ "traverse": ">=0.3.0 <0.4" } }, + "cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "^7.1.1" + } + }, "cluster-key-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", @@ -187,6 +215,21 @@ "delayed-stream": "~1.0.0" } }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -245,6 +288,12 @@ "assert-plus": "^1.0.0" } }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -293,6 +342,55 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -315,6 +413,12 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -327,6 +431,12 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -439,6 +549,12 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -448,6 +564,20 @@ "assert-plus": "^1.0.0" } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "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" + } + }, "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", @@ -470,6 +600,19 @@ "har-schema": "^2.0.0" } }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + } + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -503,6 +646,16 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -574,6 +727,22 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jshint": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz", + "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==", + "dev": true, + "requires": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.19", + "minimatch": "~3.0.2", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x" + } + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -661,6 +830,15 @@ "mime-db": "1.46.0" } }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "mkpath": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz", @@ -725,6 +903,15 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, "p-map": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", @@ -737,6 +924,12 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -972,6 +1165,12 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -1013,6 +1212,12 @@ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -1132,6 +1337,12 @@ "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true } } } diff --git a/package.json b/package.json index a912606f..cfa4a072 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "crypto-js": "^4.0.0", "express": "^4.17.1", "ioredis": "^4.24.2", + "jshint": "^2.12.0", "ngrok": "^3.4.0", "querystring": "^0.2.1" },