first commit

This commit is contained in:
pakerpale
2020-08-28 02:08:23 +09:00
commit b8899d1d4e
24 changed files with 3865 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
package-lock.json

1
README.md Normal file
View File

@@ -0,0 +1 @@
# cx-amazon-crawler

1823
category.html Executable file

File diff suppressed because one or more lines are too long

60
config/Config.js Normal file
View File

@@ -0,0 +1,60 @@
const commandLineArgs = require('command-line-args'),
logger = require("logops");
class Config {
constructor() {
this.options = {}
this.requires = ['env']
this.init()
process.env.TZ = 'Europe/Berlin'
}
init() {
try {
this.options = commandLineArgs([
{ name: 'env', alias: 'e', type: String, defaultValue: ['production'] },
{ name: 'host', type: String },
{ name: 'port', type: Number },
{ name: 'database', alias: 'd', type: String },
{ name: 'username', alias: 'u', type: String },
{ name: 'password', alias: 'p', type: String },
{ name: 'redis', alias: 'r', type: String }
]);
this.validate()
} catch (e) {
logger.debug('Command line arguments interpret failed :', e.message)
logger.debug('expected arguments : ', JSON.stringify(this.requires))
process.exit(1)
}
}
validate() {
let options = {}
for (let key in this.options) {
if (this.options[key]) {
options[key] = this.options[key]
delete this.requires[this.requires.indexOf(key)]
}
}
if (options.env == 'development') {
logger.setLevel("DEBUG")
logger.format = logger.formatters.dev;
}else {
logger.setLevel("WARN")
}
console.log(options)
this.requires = this.requires.filter(function (el) {
return el != null;
})
if (this.requires.length) {
logger.debug('Process terminated invalid required arguments: ', JSON.stringify(requires))
process.exit(1)
}
}
}
let config = new Config;
module.exports = config.options

39
config/config.json Normal file
View File

@@ -0,0 +1,39 @@
{
"development": {
"username": "root",
"password": null,
"database": "iblogis_legacy",
"host": "localhost",
"dialect": "mysql",
"pool": {
"max": 5,
"min": 0,
"acquire": 30000,
"idle": 10000,
"charset": "utf8"
},
"timezone": "+02:00"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "iblogis",
"password": "dkrnffk73**",
"database": "iblogis",
"host": "87.106.21.246",
"dialect": "mysql",
"pool": {
"max": 5,
"min": 0,
"acquire": 30000,
"idle": 10000,
"charset": "utf8"
},
"timezone": "+02:00"
}
}

60
index.js Executable file
View File

@@ -0,0 +1,60 @@
const express = require("express")
, expressLogging = require("express-logging")
, logger = require("logops")
, HttpStatus = require('http-status-codes')
, hostname = require('os').hostname()
, port = 22002
, controllers = {};
/**
* Express web server starting
*
*/
logger.getContext = function getContext() {
return {
// hostname: hostname,
// pid: process.pid,
port: port,
app: 'Headless'
};
}
var app = express();
app.use(express.json());
app.use(expressLogging(logger));
app.listen(port, () => {
logger.info({ app: 'Headless browser' }, 'Startup');
});
/**
* Request handlers
*
*/
app.all('*', (req, res, next) => {
let urlSegments = req.url.split('/');
req.params.service = urlSegments[1]
req.params.what = urlSegments.length > 2 ? urlSegments[2] : ''
req.params.args = urlSegments.length > 3 ? urlSegments.slice(3) : []
if (!controllers.hasOwnProperty(req.params.service)) {
let controller = req.params.service.charAt(0).toUpperCase() + req.params.service.slice(1) + 'Controller';
try {
controllers[req.params.service] = require('./src/controller/' + controller)
logger.info('%s has been loaded', controller)
} catch (err) {
res.json({code: HttpStatus.NO_CONTENT, message: HttpStatus.getStatusText(HttpStatus.NO_CONTENT)})
}
}
if (controllers.hasOwnProperty(req.params.service)) {
controllers[req.params.service][req.params.service](req, result => {
res.json({
code: HttpStatus.OK,
message: HttpStatus.getStatusText(HttpStatus.OK),
data: result
})
})
}
})

914
package-lock.json generated Executable file
View File

@@ -0,0 +1,914 @@
{
"name": "cx-amazon-crawler",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/node": {
"version": "14.6.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz",
"integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA=="
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"ansicolors": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
"integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="
},
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
},
"array-back": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
"integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q=="
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"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"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"cardinal": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz",
"integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=",
"requires": {
"ansicolors": "~0.3.2",
"redeyed": "~2.1.0"
}
},
"cheerio": {
"version": "1.0.0-rc.3",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
"integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==",
"requires": {
"css-select": "~1.2.0",
"dom-serializer": "~0.1.1",
"entities": "~1.1.1",
"htmlparser2": "^3.9.1",
"lodash": "^4.15.0",
"parse5": "^3.0.1"
}
},
"cls-bluebird": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz",
"integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=",
"requires": {
"is-bluebird": "^1.0.2",
"shimmer": "^1.1.0"
}
},
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
},
"command-line-args": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz",
"integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==",
"requires": {
"array-back": "^3.0.1",
"find-replace": "^3.0.0",
"lodash.camelcase": "^4.3.0",
"typical": "^4.0.0"
}
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"requires": {
"boolbase": "~1.0.0",
"css-what": "2.1",
"domutils": "1.5.1",
"nth-check": "~1.0.1"
}
},
"css-what": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"denque": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"dom-serializer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
"integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
"requires": {
"domelementtype": "^1.3.0",
"entities": "^1.1.1"
}
},
"domelementtype": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
},
"domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
"requires": {
"domelementtype": "1"
}
},
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"requires": {
"dom-serializer": "0",
"domelementtype": "1"
}
},
"dottie": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
"integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg=="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"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"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"express-logging": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/express-logging/-/express-logging-1.1.1.tgz",
"integrity": "sha1-YoOWGMurW7NhDxocFIU1L+nSbCo=",
"requires": {
"on-headers": "^1.0.0"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"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"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"find-replace": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
"integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
"requires": {
"array-back": "^3.0.1"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
"requires": {
"is-property": "^1.0.2"
}
},
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
"requires": {
"domelementtype": "^1.3.1",
"domhandler": "^2.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^3.1.1"
}
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"dependencies": {
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}
}
},
"http-status-codes": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.4.0.tgz",
"integrity": "sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ=="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inflection": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
"integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"is-bluebird": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz",
"integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI="
},
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
},
"logops": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/logops/-/logops-2.1.1.tgz",
"integrity": "sha512-4VKuufobS4yzCsU3iyKJSudYhZfzg1aHO6j2aTdYf1K1kWz6+Mhfi91BqD+RXtokLpSfocc9vOkzbsun5U4OiQ==",
"requires": {
"colors": "^1.1.2",
"lodash": "^4.17.15",
"safe-json-stringify": "^1.2.0",
"serr": "^1.0.1"
}
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"requires": {
"yallist": "^3.0.2"
}
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"moment": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
},
"moment-timezone": {
"version": "0.5.31",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz",
"integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==",
"requires": {
"moment": ">= 2.9.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"mysql2": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.1.0.tgz",
"integrity": "sha512-9kGVyi930rG2KaHrz3sHwtc6K+GY9d8wWk1XRSYxQiunvGcn4DwuZxOwmK11ftuhhwrYDwGx9Ta4VBwznJn36A==",
"requires": {
"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"
},
"dependencies": {
"iconv-lite": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
}
}
},
"named-placeholders": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz",
"integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
"requires": {
"lru-cache": "^4.1.3"
},
"dependencies": {
"lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}
}
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"nth-check": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"requires": {
"boolbase": "~1.0.0"
}
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"parse5": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
"integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
"requires": {
"@types/node": "*"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"redeyed": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz",
"integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=",
"requires": {
"esprima": "~4.0.0"
}
},
"retry-as-promised": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz",
"integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==",
"requires": {
"any-promise": "^1.3.0"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safe-json-stringify": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
"integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"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"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
},
"dependencies": {
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"seq-queue": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
"integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
},
"sequelize": {
"version": "5.22.3",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.22.3.tgz",
"integrity": "sha512-+nxf4TzdrB+PRmoWhR05TP9ukLAurK7qtKcIFv5Vhxm5Z9v+d2PcTT6Ea3YAoIQVkZ47QlT9XWAIUevMT/3l8Q==",
"requires": {
"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"
}
},
"sequelize-pool": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-2.3.0.tgz",
"integrity": "sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA=="
},
"serr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/serr/-/serr-1.0.1.tgz",
"integrity": "sha1-dKW55/rdW1X4qF5+crwApBm25II=",
"requires": {
"lodash": "^4.0.0"
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"shimmer": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
"integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
},
"sqlstring": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
"integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"toposort-class": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz",
"integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"typical": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
"integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw=="
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
},
"validator": {
"version": "10.11.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
"integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"wkx": {
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.8.tgz",
"integrity": "sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ==",
"requires": {
"@types/node": "*"
}
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
}
}
}

23
package.json Executable file
View File

@@ -0,0 +1,23 @@
{
"name": "cx-amazon-crawler",
"version": "1.0.0",
"description": "C-Xtra headless browser for crawling service",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"cheerio": "^1.0.0-rc.3",
"command-line-args": "^5.1.1",
"express": "^4.17.1",
"express-logging": "^1.1.1",
"http-status-codes": "^1.4.0",
"logops": "^2.1.1",
"mysql2": "^2.1.0",
"node-fetch": "^2.6.0",
"sequelize": "^5.22.3"
}
}

51
src/Amazon.js Executable file
View File

@@ -0,0 +1,51 @@
const Product = require('./Product')
, parser = require('./Parser')
, client = require('./Client');
const amazon = {
us: {
data: {
url: 'https://www.amazon.com',
categories: {},
subcategories: {}
},
utils: {
fakeTitle: (length, calls, leadingTxt) => {
let result = '';
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
let lengthRand = length //Math.ceil(Math.random(3, length))
return --calls ? amazon.us.utils.fakeTitle(lengthRand, calls, leadingTxt + result + '-') : leadingTxt + result;
}
},
task: {
categories: () => {
return client.request({ amazon: amazon, call: 'categories' }).then(parser.categories);
},
subCategories: (category) => {
return client.request({ amazon: amazon, category: category, call: 'subCategories' }).then(parser.subCategories);
},
productLink: (link) => {
return client.request({ amazon: amazon, link: link, call: 'productLinks' }).then(parser.productLinks);
},
productDetail: (asin) => {
let product = new Product()
return product.fetch(amazon, asin)
},
offerListing: (asin) => {
return client.request({ amazon: amazon, asin:asin, call: 'offerListing' }).then(parser.offerListing);
}
}
}
}
module.exports = amazon;

53
src/Client.js Executable file
View File

@@ -0,0 +1,53 @@
const fetch = require("node-fetch");
const Client = {
headers: {
'authority' : 'www.amazon.com',
'method' : 'GET',
'path' : '',
'scheme' : 'https',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'accept-encoding' : 'gzip, deflate, br',
'accept-language' : 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'cache-control' : 'max-age=0',
'sec-fetch-mode' : 'navigate',
'sec-fetch-site' : 'none',
'sec-fetch-user' : '?1',
'upgrade-insecure-requests' : '1',
'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
cookie: 'lc-main=ko_KR;i18n-prefs=KRW'
},
request: async (args) => {
let url = Client[args.call](args)
Client.headers['path'] = url.replace(args.amazon.us.data.url, '')
return fetch(url, { headers: Client.headers }).then(res => res.textConverted());
},
offerListing: (args) => {
let url = args.amazon.us.data.url + '/dp/offer-listing/' + args.asin + '/ref=dp_olp_new_mbc?ie=UTF8&condition=new';
console.log('- request product offer listing page url : ' + url);
return url;
},
productDetail: (args) => {
let url = args.amazon.us.data.url + '/' + args.amazon.us.utils.fakeTitle(7, 4, '') + '/dp/' + args.asin;
console.log('- request product detail page url : ' + url);
return url;
},
categories: (args) => {
let url = args.amazon.us.data.url + '/gp/site-directory';
console.log('- request category page url : ' + url)
return url;
},
subCategories: (args) => {
let url = args.amazon.us.data.url + args.category.link;
console.log('- request sub category page url : ' + url)
return url;
},
productLinks: (args) => {
let url = args.amazon.us.data.url + args.link;
console.log('- request product links page url : ' + url)
return url;
}
}
module.exports = Client;

54
src/Parser.js Executable file
View File

@@ -0,0 +1,54 @@
const cheerio = require("cheerio");
const Parser = {
categories: body => {
let data = []
const $ = cheerio.load(body)
$(".a-container a").map((k, v) => {
data.push({
link: $(v).attr('href'),
title: $(v).text()
});
})
console.log('- fetching ' + data.length + ' category links completed')
return data
},
subCategories: body => {
let data = []
const $ = cheerio.load(body)
$(".left_nav.browseBox li a").map((k, v) => {
data.push({
link: $(v).attr('href'),
title: $(v).text()
});
})
return data;
},
productLinks: body => {
let data = []
const $ = cheerio.load(body)
$("#mainResults a.a-link-normal").map((k, v) => {
let title = $(v).find('h2').text().trim();
if (title) {
data.push({
link: $(v).attr('href'),
title: title
});
}
})
// console.log(data)
amazon.us.data.subcategories = data;
data.forEach((v, k) => {
console.log(v.link)
})
},
offerListing: body => {
const $ = cheerio.load(body)
let stock = $('#olpOfferList .a-row.olpOffer').length;
let price = $('#olpOfferList div.a-column.a-span2.olpPriceColumn').last().find('span').text().trim();
return {stock: stock, price: price}
}
}
module.exports = Parser

118
src/Product.js Executable file
View File

@@ -0,0 +1,118 @@
const cheerio = require("cheerio")
, client = require('./Client');
class Product {
constructor() {
this.data = {}
this.amazon = null
}
async fetch(amazon, asin) {
this.amazon = amazon;
let args = [{ amazon: this.amazon, asin: asin, call: 'productDetail', resolver: 'parseHtmlDetail' },
{ amazon: this.amazon, asin: asin, call: 'offerListing', resolver: 'parseHtmlOfferListing' }]
let fetchPromises = args.map(async args => {
return await client.request(args)
})
for (let k in fetchPromises) {
await fetchPromises[k].then(body => this[args[k].resolver](body))
}
this.data.asin = asin
return this.data
}
parseHtmlDetail(body) {
let $ = cheerio.load(body)
this.data.title = $('#productTitle').text().trim()
let merchant = $('#merchant-info a');
if (!merchant.length) {
merchant = $('#merchant-info');
}
this.data.seller = {
title: merchant.text().replace('에서 판매하고 배송합니다.', '').replace('주문 처리', '').trim(),
link: merchant.attr('href')
}
this.data.img = $('#landingImage').attr('src')
this.data.description = this.description(this.amazon, $)
this.data.category = this.category(this.amazon, $)
this.data.price = this.price(this.amazon, $)
this.data.colors = this.colors(this.amazon, $)
this.data.colorsASIN = this.colorsASIN(this.amazon, $)
}
description(amazon, $) {
return {
content: $('#aplus').html(),
spec: $('#descriptionAndDetails').html(),
videos: function () {
if ($('#vse-rel-videos-carousel ol li a').length) {
return {
link: amazon.us.data.url + $('#vse-rel-videos-carousel ol li a').eq(0).attr('href'),
title: $('#vse-rel-videos-carousel ol li a div.vse-video-content .vse-video-title-text').eq(0).text().trim()
}
} else return {}
}()
}
}
parseHtmlOfferListing(body) {
let $ = cheerio.load(body)
let stock = $('#olpOfferList .a-row.olpOffer').length;
let price = $('#olpOfferList div.a-column.a-span2.olpPriceColumn').last().find('span').text().trim();
// let seller = $('#olpOfferList .olpSellerName').last().find('span a').text().trim();
// let sellerLink = $('#olpOfferList .olpSellerName').last().find('span a').attr('href');
this.data.stock = stock;
this.data.price = price;
// this.data.seller = { title: seller, link: sellerLink }
}
category(amazon, $) {
let categoryPath = []
$('#wayfinding-breadcrumbs_feature_div li a').map((k, n) => {
categoryPath.push({ title: $(n).text().trim(), link: amazon.us.data.url + $(n).attr('href') })
})
return categoryPath
}
price(amazon, $) {
let price;
price = $('#olp-upd-new > span > a').eq(0).text().trim()
if (price) {
if (price.split('$').length == 2) {
return '$' + price.split('$')[1]
}
}
price = $('#priceblock_ourprice').text().trim()
if (price) return price
}
colors(amazon, $) {
let colors = []
$('li[id^=color_name_]').map((k, n) => {
colors.push({
img: $(n).find('img').attr('src'),
title: $(n).find('img').attr('alt'),
})
})
return colors
}
colorsASIN(amazon, $) {
let colorsASIN = []
$('li[id^=color_name_]').map((k, n) => {
let asin = $(n).attr('data-defaultasin');
if (!asin) {
asin = $(n).attr('data-dp-url').split('/')[2];
}
colorsASIN.push(asin)
})
return colorsASIN
}
}
module.exports = Product;

View File

@@ -0,0 +1,117 @@
const logger = require('logops')
, db = require("../model");
class PurchaseComponent {
getPurchaseData(data, now) {
return {
OrgCode: data.orgCode,
PurchaseNo: data.purchaseNo,
Status: data.status,
CreatedAt: now
}
}
getPurchaseItemData(itemData, purchase, now) {
return itemData.map(item => {
return {
PurchaseId: purchase.Id,
ItemLink: item.itemLink,
ItemName: item.itemName,
ItemBrand: item.itemBrand,
ItemUnitPrice: item.itemUnitPrice,
ItemTax: item.itemTax,
ItemCount: item.itemCount,
Status: item.status,
CreatedAt: now
}
})
}
getSearchPurchaseItemClause(params) {
let clause = { where: {}, raw: true }
if (params.purchaseId) {
clause.where.PurchaseId = Array.isArray(params.purchaseId) ? { [db.Sequelize.Op.in]: params.purchaseId } : params.purchaseId
}
if (params.id) {
clause.where.Id = Array.isArray(params.id) ? { [db.Sequelize.Op.in]: params.id } : params.id
}
if (params.sdate && params.edate) {
clause.where[params.daterangeby ? params.daterangeby : 'CreatedAt'] = { [db.Sequelize.Op.between]: [params.sdate, params.edate] }
}
if (params.limit) {
clause.limit = parseInt(params.limit)
}
if (params.offset) {
clause.offset = parseInt(params.offset)
}
if (params.column) {
clause.attributes = Array.isArray(params.column) ? params.column : [params.column]
}
if (params.sort) {
clause.order = [
[params.sort, params.order ? params.order.toUpperCase() : 'ASC']
]
}
return clause
}
findPurchaseById(purchaseId) {
let purchases = {}
return db.purchase.findAll({ where: { Id: { [db.Sequelize.Op.in]: purchaseId } } }).then(rows => {
rows.forEach(p => {
purchases[p.Id] = p
})
return purchases
})
}
findPurchaseItemOptionByItemId(purchaseItemId) {
let itemOptions = {}
let clause = { where: { ItemId: { [db.Sequelize.Op.in]: purchaseItemId } } }
return db.purchaseItemOption.findAll(clause).then(options => {
options.forEach(opt => {
if (itemOptions.hasOwnProperty(opt.ItemId) == false) {
itemOptions[opt.ItemId] = []
}
itemOptions[opt.ItemId].push([opt.OptName, opt.OptValue])
})
return itemOptions
});
}
getSearchTotalPurchaseItem(params) {
let clause = this.getSearchPurchaseItemClause(params)
delete clause.limit;
return db.purchaseItem.count(clause).then(cnt => cnt)
}
findPurchaseByOrgCodeAndPurchaseNo(params) {
let clause = { where: {} }
if (params.sword) {
clause.where.PurchaseNo = params.sword
}
if (params.orgCode) {
clause.where.OrgCode = params.orgCode
}
return db.purchase.findAll(clause).then(purchases => {
return purchases.map(p => p.Id)
})
}
replaceItemOption(itemId, itemOptions) {
return db.purchaseItemOption.destroy({ where: { ItemId: itemId } }).then(a => {
let options = itemOptions.map(opt => {
if(opt.optValue) {
return { ItemId: itemId, OptName: opt.optName, OptValue: opt.optValue }
}
})
return db.purchaseItemOption.bulkCreate(options).then(inserted => inserted.length > 0)
})
}
}
module.exports = new PurchaseComponent

View File

@@ -0,0 +1,20 @@
const service = require("../service/CrawlService")
, logger = require('logops');
class CrawlController {
getMethodName(req) {
return req.method.toLowerCase()
+ req.params.what.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('')
}
crawl(req, callback) {
let fn = this.getMethodName(req);
let params = Object.assign(req.body, req.query)
params = Object.assign(params, req.params)
logger.info('Invoking method TestService.%s', fn)
service[fn](params, callback)
}
}
module.exports = new CrawlController

View File

@@ -0,0 +1,20 @@
const purchase = require("../service/PurchaseService")
, logger = require('logops');
class PurchaseController {
getMethodName(req) {
return req.method.toLowerCase()
+ req.params.what.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('')
}
purchase(req, callback) {
let fn = this.getMethodName(req);
let params = Object.assign(req.body, req.query)
params = Object.assign(params, req.params)
logger.info('Invoking method PurchaseService.%s', fn)
purchase[fn](params, callback)
}
}
module.exports = new PurchaseController

85
src/model/IoBoundModel.js Normal file
View File

@@ -0,0 +1,85 @@
/* jshint indent: 2 */
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ioBound', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
primaryKey: true,
field: 'id'
},
IoType: {
type: DataTypes.STRING(2),
allowNull: false,
field: 'type'
},
ItemId: {
type: DataTypes.STRING(20),
allowNull: false,
field: 'pcode'
},
Barcode: {
type: DataTypes.STRING(30),
allowNull: false,
field: 'ean'
},
SerialNo: {
type: DataTypes.STRING(30),
allowNull: false,
field: 'serialno'
},
ArtNo: {
type: DataTypes.STRING(30),
allowNull: false,
comment: 'Article Number/PZN',
field: 'artno'
},
Qty: {
type: DataTypes.INTEGER(9).UNSIGNED,
allowNull: false,
comment: '실재고',
field: 'qty'
},
OrgCode: {
type: DataTypes.STRING(10),
allowNull: false,
field: 'c_code'
},
ShippingCode: {
type: DataTypes.STRING(30),
allowNull: false,
field: 'wcode'
},
BoxCode: {
type: DataTypes.STRING(30),
allowNull: false,
field: 'bcode'
},
CreatedBy: {
type: DataTypes.STRING(30),
allowNull: false,
field: 'reg_user'
},
CreatedAt: {
type: DataTypes.DATE,
allowNull: true,
field: 'regtime'
},
UpdatedBy: {
type: DataTypes.STRING(10),
allowNull: false,
field: 'update_user'
},
UpdatedAt: {
type: DataTypes.DATE,
allowNull: false,
field: 'update_time'
}
}, {
sequelize,
tableName: 'wms_in_out_bound',
freezeTableName: true,
timestamps: false
});
};

27
src/model/IoTypeModel.js Normal file
View File

@@ -0,0 +1,27 @@
/* jshint indent: 2 */
module.exports = function (sequelize, DataTypes) {
return sequelize.define('ioType', {
code: {
type: DataTypes.STRING(2),
allowNull: false,
primaryKey: true,
field: 'code'
},
Name: {
type: DataTypes.STRING(50),
allowNull: true,
field: 'name'
},
NameKr: {
type: DataTypes.STRING(50),
allowNull: true,
field: 'name_kr'
}
}, {
sequelize,
tableName: 'wms_in_out_type',
freezeTableName: true,
timestamps: false
});
};

38
src/model/Purchase.js Normal file
View File

@@ -0,0 +1,38 @@
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('purchase', {
Id: {
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'seq'
},
OrgCode: {
type: DataTypes.STRING(10),
allowNull: true,
field: 'company_code'
},
PurchaseNo: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
field: 'purchase_no'
},
Status: {
type: DataTypes.ENUM('발주요청'),
allowNull: false,
field: 'status'
},
CreatedAt: {
type: DataTypes.DATE,
allowNull: false,
field: 'created_at'
}
}, {
tableName: 'ibl_purchase',
freezeTableName: true,
timestamps: false
});
};

62
src/model/PurchaseItem.js Normal file
View File

@@ -0,0 +1,62 @@
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('purchaseItem', {
Id: {
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'seq'
},
PurchaseId: {
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
field: 'purchase_seq'
},
ItemLink: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'item_link'
},
ItemName: {
type: DataTypes.STRING(50),
allowNull: false,
field: 'item_name'
},
ItemBrand: {
type: DataTypes.STRING(30),
allowNull: false,
field: 'item_brand'
},
ItemUnitPrice: {
type: DataTypes.DECIMAL,
allowNull: false,
field: 'item_unit_price'
},
ItemTax: {
type: DataTypes.ENUM('0','7','19','5','16'),
allowNull: false,
field: 'item_tax'
},
ItemCount: {
type: DataTypes.INTEGER(4),
allowNull: false,
field: 'item_count'
},
Status: {
type: DataTypes.ENUM('발주요청'),
allowNull: false,
field: 'status'
},
CreatedAt: {
type: DataTypes.DATE,
allowNull: false,
field: 'created_at'
}
}, {
tableName: 'ibl_purchase_item',
freezeTableName: true,
timestamps: false
});
};

View File

@@ -0,0 +1,23 @@
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('purchaseItemOption', {
ItemId: {
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
primaryKey: true
},
OptName: {
type: DataTypes.STRING(100),
allowNull: false
},
OptValue: {
type: DataTypes.STRING(100),
allowNull: false
}
}, {
tableName: 'ibp_purchase_itemopt',
freezeTableName: true,
timestamps: false
});
};

42
src/model/index.js Normal file
View File

@@ -0,0 +1,42 @@
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const args = require("../../config/Config")
const env = args.env || 'development';
const config = require('../../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(args.database, config.username, config.password, config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
sequelize.authenticate().then(() => {
console.log('Connection has been established successfully.');
}).catch(err => {
console.error('Unable to connect to the database:', err);
process.exit(1)
});
fs.readdirSync(__dirname).filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
}).forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;

View File

@@ -0,0 +1,38 @@
const logger = require("logops")
, HttpStatus = require('http-status-codes')
, cheerio = require('cheerio')
, puppeteer = require('puppeteer');
class CrawlService {
constructor() {
this.browser = null;
this.page = null;
}
async request(url) {
if (this.page == null) {
this.browser = await puppeteer.launch({ headless: true });
this.page = await this.browser.newPage();
}
await this.page.goto(url)
return this.page.evaluate(() => document.documentElement.outerHTML)
}
get(params, callback) {
this.request('https://www.amazon.de/').then(html => {
const $ = cheerio.load(html)
let selector = 'div.a-section.a-spacing-none.feed-carousel.first-carousel ul li a'
let links = $(selector).map((i, el) => {
return $(el).attr('href')
}).get()
callback(links)
})
}
}
module.exports = new CrawlService

View File

@@ -0,0 +1,103 @@
const db = require("../model")
, logger = require("logops")
, restClient = require('../component/RestClientComponent')
, HttpStatus = require('http-status-codes')
, purchaseComponent = require('../component/PurchaseComponent');
class PurchaseService {
response(res, callback) {
if (!res.code) res.code = HttpStatus.OK
if (!res.message) res.message = HttpStatus.getStatusText(res.code)
if (res.code) {
callback(res)
return;
}
callback({ code: HttpStatus.NOT_FOUND, message: HttpStatus.getStatusText(HttpStatus.NOT_FOUND) })
}
post(params, callback) {
db.purchase.findAll({
where: { PurchaseNo: params.itemData[0].purchaseNo }
}).then(result => {
if (result.length) {
logger.error('Purchase No. Duplicated %s', JSON.stringify(params))
callback({ code: HttpStatus.CONFLICT, message: HttpStatus.getStatusText(HttpStatus.CONFLICT) })
} else {
let now = new Date();
db.purchase.create(purchaseComponent.getPurchaseData(params.itemData[0], now)).then(purchase => {
db.purchaseItem.bulkCreate(purchaseComponent.getPurchaseItemData(params.itemData, purchase, now)).then(createdItems => {
let itemOptions = []
createdItems.map((created, k) => {
params.itemData[k].itemOptions.map(opt => {
if (opt.optValue) {
itemOptions.push({
ItemId: created.Id,
OptName: opt.optName,
OptValue: opt.optValue
})
}
})
})
db.purchaseItemOption.bulkCreate(itemOptions).then(result => {
callback(result.length ? { inserted: createdItems.map(item => item.Id) } : { inserted: [] })
})
})
})
}
})
}
put(params, callback) {
let itemOptions = params.ItemOptions;
delete params.ItemOptions
Promise.all([
db.purchaseItem.update(params, { where: { Id: params.Id } }).then(updated => updated > 0),
purchaseComponent.replaceItemOption(params.Id, itemOptions)
]).then(results => {
logger.info('Update purchase promise results - %s', JSON.stringify(results))
let code = results.filter(result => result == true).length ? HttpStatus.OK : HttpStatus.NOT_MODIFIED
callback({ code: code, message: HttpStatus.getStatusText(code) })
})
}
async getSearch(params, callback) {
if (params.sword || params.orgCode) {
params.purchaseId = await purchaseComponent.findPurchaseByOrgCodeAndPurchaseNo(params);
}
let clause = purchaseComponent.getSearchPurchaseItemClause(params)
if (Object.keys(clause.where).length) {
db.purchaseItem.findAll(clause).then(rows => {
let responseBody = { code: HttpStatus.NOT_FOUND, message: HttpStatus.getStatusText(HttpStatus.NOT_FOUND) }
if (rows.length) {
Promise.all([
purchaseComponent.getSearchTotalPurchaseItem(params),
purchaseComponent.findPurchaseById(rows.map(row => row.PurchaseId)),
purchaseComponent.findPurchaseItemOptionByItemId(rows.map(row => row.Id))
]).then(results => {
let total = results[0]
let purchases = results[1];
let itemOptions = results[2];
rows.forEach((row, k) => {
rows[k].PurchaseNo = purchases[row.PurchaseId].PurchaseNo
rows[k].OrgCode = purchases[row.PurchaseId].OrgCode
rows[k].ItemOptions = itemOptions[row.Id]
})
responseBody = { code: HttpStatus.OK, message: HttpStatus.getStatusText(HttpStatus.OK), data: { total: total, rows: rows } }
this.response(responseBody, callback)
})
} else {
this.response(responseBody, callback)
}
});
} else {
logger.debug('Clause has no where statement - %s', JSON.stringify(clause))
this.response({
code: HttpStatus.BAD_REQUEST,
message: HttpStatus.getStatusText(HttpStatus.BAD_REQUEST)
}, callback)
}
}
}
module.exports = new PurchaseService

View File

@@ -0,0 +1,92 @@
const fetch = require("node-fetch")
, logger = require("logops");
class RestClientService {
constructor() {
this.token = null
this.emptyResponse = { code: 200, data: null }
this.localhost = 'http://localhost:31000/api/v1/'
this.clientId = 'F8Nbz9gGUvLgdn8T'
this.clientSecret = 'Fesmx7hHCCMx7Cby7FhV2fAtf2NYEFzP5fCKuwEEdpf8hpnsnnQn9qYhNHt6SsDD'
this.setToken()
}
setToken() {
this.token = null
return fetch(this.localhost + 'oauth/token?client_id=' + this.clientId + '&grant_type=client_credentials&client_secret=' + this.clientSecret, {
method: 'GET',
headers: {
'content-type': 'application/json'
}
}).then(res => res.json()).then(res => {
logger.debug(res, 'Tracking service got app token successfully')
this.token = res.access_token
return this.token
})
}
getHeader(additionalheaders) {
let headers = {
"Authorization": "Bearer " + this.token,
"Content-Type": "application/json"
}
return additionalheaders ? Object.assign(headers, additionalheaders) : headers
}
getResponse(data) {
if (data.code == 406) {
logger.info(data, 'Token expired and requesting new token')
this.emptyResponse.tokenPromise = this.setToken();
return this.emptyResponse
}
return data;
}
getProductBrandByItemId(params) {
return fetch(this.localhost + 'catalog/brand', {
method: 'OPTIONS',
headers: this.getHeader(),
body: JSON.stringify(params)
}).then(res => res.json()).then(data => this.getResponse(data))
}
getProductByBrand(params) {
return fetch(this.localhost + 'catalog/brand/' + params.brand, {
method: 'GET',
headers: this.getHeader()
}).then(res => res.json()).then(data => this.getResponse(data))
}
getParcel(params) {
if (this.token == null) return this.emptyResponse
let defaultParams = {
"request": "get", "column": ["ShippingCode"],
"status": [2], "limit": 1, "offset": 0, "groupby": ["ShippingCode"], "order": [['CreatedAt', 'DESC']],
'daterangeby': 'ExportedAt',
'daterange': {
sdate: (function () {
let d = new Date();
d.setMonth(d.getMonth() - 1);
return d.toLocaleDateString();
})(),
edate: (function () {
let d = new Date();
d.setDate(d.getDate() + 2);
return d.toLocaleDateString();
})()
}
}
Object.assign(defaultParams, params)
logger.debug(defaultParams, 'Request parcel service params')
return fetch(this.localhost + 'parcel', {
method: 'POST',
headers: this.getHeader(),
body: JSON.stringify(defaultParams)
}).then(res => res.json()).then(data => this.getResponse(data))
}
}
module.exports = new RestClientService;