first commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
package-lock.json
|
||||
.env
|
||||
storage/html/*
|
||||
!storage/html/.gitignore
|
||||
68
config/index.js
Normal file
68
config/index.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const commandLineArgs = require('command-line-args')
|
||||
, logger = require("logops");
|
||||
|
||||
class Config {
|
||||
|
||||
constructor() {
|
||||
this.options = {}
|
||||
// this.requires = ['env', 'gateway', 'quix24', 'mongo']
|
||||
this.requires = ['env']
|
||||
this.init()
|
||||
this.validate()
|
||||
this.finalize()
|
||||
}
|
||||
|
||||
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: 'mongo', alias: 'm', type: String },
|
||||
{ name: 'redis', alias: 'r', type: String },
|
||||
{ name: 'level', alias: 'l', type: String },
|
||||
{ name: 'gateway', alias: 'g', type: String },
|
||||
{ name: 'quix24', alias: 'q', type: String }
|
||||
]);
|
||||
} catch (e) {
|
||||
logger.debug('Command line arguments interpret failed :', e.message)
|
||||
logger.debug('expected arguments : ', JSON.stringify(this.requires))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
finalize() {
|
||||
if (this.options.env == 'development') {
|
||||
this.options.level = this.options.level ? this.options.level : "DEBUG"
|
||||
logger.formatters.dev.omit = ['pid', 'port', 'hostname', 'app'];
|
||||
// logger.format = logger.formatters.dev;
|
||||
} else {
|
||||
this.options.level = this.options.level ? this.options.level : "WARN"
|
||||
}
|
||||
logger.formatters.json.omit = ['pid', 'port', 'hostname', 'app'];
|
||||
logger.setLevel(this.options.level)
|
||||
logger.debug(this.options, 'Environment setting options')
|
||||
}
|
||||
|
||||
validate() {
|
||||
for (let key in this.options) {
|
||||
if (this.options[key]) {
|
||||
delete this.requires[this.requires.indexOf(key)]
|
||||
}
|
||||
}
|
||||
this.requires = this.requires.filter(function (el) {
|
||||
return el != null;
|
||||
})
|
||||
if (this.requires.length) {
|
||||
logger.debug('Process terminated invalid required arguments: ', JSON.stringify(this.requires))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let config = new Config;
|
||||
module.exports = config.options
|
||||
56
index.js
Normal file
56
index.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const config = require('./config')
|
||||
, express = require("express")
|
||||
, expressLogging = require("express-logging")
|
||||
, logger = require("logops")
|
||||
, { StatusCodes, getStatusText } = require('http-status-codes')
|
||||
, hostname = require('os').hostname()
|
||||
, port = 32108
|
||||
, controllers = require('./src/controller');
|
||||
|
||||
|
||||
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
|
||||
|
||||
/**
|
||||
* Express web server starting
|
||||
*
|
||||
*/
|
||||
logger.getContext = function getContext() {
|
||||
return {
|
||||
// hostname: hostname,
|
||||
// pid: process.pid,
|
||||
// port: port,
|
||||
app: 'External'
|
||||
};
|
||||
}
|
||||
var app = express();
|
||||
app.use(express.json());
|
||||
app.use(expressLogging(logger));
|
||||
app.use((req, res, next) => {
|
||||
// logger.debug(Object.assign(req.body, req.query, req.params), 'Request params')
|
||||
next()
|
||||
})
|
||||
app.listen(port, () => {
|
||||
logger.info('Service started')
|
||||
});
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
controllers[req.params.service][req.params.service](req, result => {
|
||||
let body = { code: StatusCodes.OK, message: getStatusText(StatusCodes.OK), data: result }
|
||||
if (result.code) body = result
|
||||
res.json(body)
|
||||
})
|
||||
} else {
|
||||
res.json({ code: StatusCodes.NOT_FOUND, message: getStatusText(StatusCodes.NOT_FOUND) })
|
||||
}
|
||||
})
|
||||
|
||||
28
package.json
Normal file
28
package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "spd-app-cache",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://spiduler@github.com/spiduler/spd-app-cache.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/spiduler/spd-app-cache/issues"
|
||||
},
|
||||
"homepage": "https://github.com/spiduler/spd-app-cache#readme",
|
||||
"dependencies": {
|
||||
"command-line-args": "^5.1.1",
|
||||
"express": "^4.17.1",
|
||||
"express-logging": "^1.1.1",
|
||||
"http-status-codes": "^2.1.4",
|
||||
"logops": "^2.1.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
"redis": "^3.0.2"
|
||||
}
|
||||
}
|
||||
52
src/component/RedisClientComp.js
Normal file
52
src/component/RedisClientComp.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const HttpStatus = require('http-status-codes')
|
||||
, redis = require('redis')
|
||||
, logger = require("logops")
|
||||
, { promisify } = require("util")
|
||||
, config = require("../../config");
|
||||
|
||||
class RedisClientComp {
|
||||
constructor() {
|
||||
this.client = redis.createClient({ auth_pass: 'ZFF8JN6jMHzzUw7F', host: config.redis.split(':')[0], port: config.redis.split(':')[1] });
|
||||
this.client.on("error", function (error) {
|
||||
logger.error(error.message);
|
||||
if (error.message.includes('connection')) {
|
||||
process.exit(1)
|
||||
}
|
||||
});
|
||||
this.client.getAsync = promisify(this.client.get).bind(this.client)
|
||||
}
|
||||
|
||||
set(key, value, done) {
|
||||
this.client.set(key, JSON.stringify(value), function (err) {
|
||||
// This will either result in an error (flush parameter is set to true)
|
||||
// or will silently fail and this callback will not be called at all (flush set to false)
|
||||
if (err) {
|
||||
logger.error({ message: err.message }, 'Set data on redis error occured')
|
||||
} else {
|
||||
logger.info({ key: key, value: value }, 'Redis set successfullly')
|
||||
}
|
||||
done(err)
|
||||
});
|
||||
}
|
||||
|
||||
get(key, callback) {
|
||||
if (Array.isArray(key)) {
|
||||
let mget = ['mget'].concat(key)
|
||||
logger.debug(mget, 'Mutiple get operation keys')
|
||||
this.client.multi([mget]).exec((multiExecError, results) => {
|
||||
results = results[0].filter((v, k) => v != null).map((v, k) => JSON.parse(v))
|
||||
callback(results)
|
||||
});
|
||||
} else {
|
||||
this.client.get(key, function (err, value) {
|
||||
if (err) {
|
||||
logger.error(err, 'Redis get operation failed with key : ' + key)
|
||||
} else {
|
||||
callback(value)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new RedisClientComp
|
||||
53
src/component/RestClientComp.js
Normal file
53
src/component/RestClientComp.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const fetch = require("node-fetch")
|
||||
, logger = require("logops")
|
||||
, TokenExpiredException = require('../exception/TokenExpiredException')
|
||||
, config = require('../../config');
|
||||
|
||||
|
||||
class RestClientComp {
|
||||
|
||||
constructor() {
|
||||
this.token = null
|
||||
this.gateway = 'http://' + config.quix24 + '/api/v1/'
|
||||
this.clientId = 'F8Nbz9gGUvLgdn8T'
|
||||
this.clientSecret = 'Fesmx7hHCCMx7Cby7FhV2fAtf2NYEFzP5fCKuwEEdpf8hpnsnnQn9qYhNHt6SsDD'
|
||||
}
|
||||
|
||||
auth() {
|
||||
let url = this.gateway + 'oauth/token?client_id=' + this.clientId + '&grant_type=client_credentials&client_secret=' + this.clientSecret
|
||||
return fetch(url, { method: 'GET', headers: { 'content-type': 'application/json' } }).then(res => res.json()).then(res => {
|
||||
logger.debug(res, 'Token from %s', this.gateway)
|
||||
this.token = res.access_token
|
||||
return res.access_token
|
||||
})
|
||||
}
|
||||
|
||||
headers(additionalheaders) {
|
||||
let headers = {
|
||||
"Authorization": "Bearer " + this.token,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
return additionalheaders ? Object.assign(headers, additionalheaders) : headers
|
||||
}
|
||||
|
||||
response(res) {
|
||||
if (res.code == 406) {
|
||||
throw new TokenExpiredException("Token expired " + this.gateway)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async request(fetchable) {
|
||||
if(!this.token) await this.auth()
|
||||
return fetchable().catch(error => {
|
||||
logger.error({message: error.message}, 'Fetchable failed with error')
|
||||
if (error.status == 406) {
|
||||
logger.info('Requesting token %s', this.gateway)
|
||||
return this.auth().then(() => fetchable())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = RestClientComp;
|
||||
52
src/controller/CatalogController.js
Normal file
52
src/controller/CatalogController.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const logger = require("logops")
|
||||
, catalogService = require('../service/CatalogService');
|
||||
|
||||
class CatalogController {
|
||||
getMethodName(req) {
|
||||
return req.method.toLowerCase()
|
||||
+ req.params.what.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('')
|
||||
}
|
||||
|
||||
catalog(req, callback) {
|
||||
let fn = this.getMethodName(req);
|
||||
this[fn](req.body, callback)
|
||||
}
|
||||
|
||||
postFind(params, callback) {
|
||||
catalogService.find(params).then(result => {
|
||||
callback(result)
|
||||
})
|
||||
}
|
||||
|
||||
postSearch(params, callback) {
|
||||
catalogService.get(params).then(result => {
|
||||
callback(result)
|
||||
})
|
||||
}
|
||||
|
||||
putUpsert(params, callback) {
|
||||
catalogService.post(params).then(result => {
|
||||
callback(result)
|
||||
})
|
||||
}
|
||||
|
||||
put(params, callback) {
|
||||
catalogService.put(params).then(result => {
|
||||
callback(result)
|
||||
})
|
||||
}
|
||||
|
||||
postImageSearch(params, callback) {
|
||||
catalogService.getImage(params).then(result => {
|
||||
callback(result)
|
||||
})
|
||||
}
|
||||
|
||||
putImageUpsert(params, callback) {
|
||||
catalogService.postImage(params).then(result => {
|
||||
callback(result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new CatalogController;
|
||||
15
src/controller/index.js
Normal file
15
src/controller/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs')
|
||||
, path = require('path')
|
||||
, basename = path.basename(__filename)
|
||||
, controllers = {};
|
||||
|
||||
fs.readdirSync(__dirname).filter(file => {
|
||||
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
|
||||
}).forEach(file => {
|
||||
const controller = require('./' + file)
|
||||
controllers[file.slice(0, -3).toLowerCase().replace('controller', '')] = controller;
|
||||
});
|
||||
|
||||
module.exports = controllers;
|
||||
16
src/exception/TokenExpiredException.js
Normal file
16
src/exception/TokenExpiredException.js
Normal file
@@ -0,0 +1,16 @@
|
||||
class TokenExpiredException extends Error {
|
||||
constructor (message) {
|
||||
super(message)
|
||||
|
||||
// assign the error class name in your custom error (as a shortcut)
|
||||
this.name = this.constructor.name
|
||||
|
||||
// capturing the stack trace keeps the reference to your error class
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
|
||||
// you may also assign additional properties to your error
|
||||
this.status = 406
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TokenExpiredException
|
||||
81
src/service/CatalogService.js
Normal file
81
src/service/CatalogService.js
Normal file
@@ -0,0 +1,81 @@
|
||||
const logger = require("logops")
|
||||
, fetch = require('node-fetch')
|
||||
, config = require('../../config')
|
||||
, RestClient = require("../component/RestClientComp")
|
||||
|
||||
|
||||
class CatalogService extends RestClient {
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
post(params) {
|
||||
return this.request(() => fetch(this.gateway + 'catalog', {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify(params)
|
||||
}).then(res => res.json()).then(res => this.response(res))).then(res => res.data).catch(err => {
|
||||
logger.error(err.message, 'Catalog save failed')
|
||||
return { created: false }
|
||||
})
|
||||
}
|
||||
|
||||
put(params) {
|
||||
return this.request(() => fetch(this.gateway + 'catalog', {
|
||||
method: 'PUT',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify(params)
|
||||
}).then(res => res.json()).then(res => this.response(res))).then(res => res.data).catch(err => {
|
||||
logger.error(err.message, 'Catalog update failed')
|
||||
return { created: false }
|
||||
})
|
||||
}
|
||||
|
||||
get(params) {
|
||||
return this.request(() => fetch(this.gateway + 'catalog/search', {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify(params)
|
||||
}).then(res => res.json()).then(res => this.response(res))).then(res => res.data).catch(err => {
|
||||
logger.error(err.message, 'Catalog request failed')
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
find(params) {
|
||||
return this.request(() => fetch(this.gateway + 'catalog/find', {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify(params)
|
||||
}).then(res => res.json()).then(res => this.response(res))).then(res => res.data).catch(err => {
|
||||
logger.error(err.message, 'Catalog request failed')
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
postImage(params) {
|
||||
return this.request(() => fetch(this.gateway + 'catalog/image', {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify(params)
|
||||
}).then(res => res.json()).then(res => this.response(res))).then(res => res.data).catch(err => {
|
||||
logger.error(err.message, 'Catalog save failed')
|
||||
return { created: false }
|
||||
})
|
||||
}
|
||||
|
||||
getImage(params) {
|
||||
return this.request(() => fetch(this.gateway + 'catalog/image/search', {
|
||||
method: 'POST',
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify(params)
|
||||
}).then(res => res.json()).then(res => this.response(res))).then(res => res.data).catch(err => {
|
||||
logger.error(err.message, 'Catalog request failed')
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = new CatalogService;
|
||||
Reference in New Issue
Block a user