From 5b0e8dd1522900b07551d4062a7e5e8b6a846f10 Mon Sep 17 00:00:00 2001 From: AlexMcArrow Date: Tue, 2 Nov 2021 11:19:11 +0500 Subject: [PATCH 1/4] New structure --- config.json._ | 75 ++++- ext/dns.js | 41 --- index.js | 13 + package.json | 9 +- server.js | 549 --------------------------------- src/ext/_all.js | 3 + src/ext/dns.js | 41 +++ src/modules/_all.js | 3 + src/modules/caller.js | 29 ++ src/modules/caller/_all.js | 5 + src/modules/caller/httpget.js | 69 +++++ src/modules/caller/httppost.js | 69 +++++ src/modules/caller/shell.js | 29 ++ src/server.js | 35 +++ webpack.config.js | 6 +- 15 files changed, 366 insertions(+), 610 deletions(-) delete mode 100644 ext/dns.js create mode 100644 index.js delete mode 100644 server.js create mode 100644 src/ext/_all.js create mode 100644 src/ext/dns.js create mode 100644 src/modules/_all.js create mode 100644 src/modules/caller.js create mode 100644 src/modules/caller/_all.js create mode 100644 src/modules/caller/httpget.js create mode 100644 src/modules/caller/httppost.js create mode 100644 src/modules/caller/shell.js create mode 100644 src/server.js diff --git a/config.json._ b/config.json._ index 0bbd28f..cb6ca0c 100644 --- a/config.json._ +++ b/config.json._ @@ -1,17 +1,72 @@ { "server": { - "port": 20744, - "logs": false + "port": 20744 }, "metric": { - "check": { - "interval": 15, - "run": "/usr/bin/sh /home/user/check.sh", - "notice": 20, - "warning": 35, - "error": 45, - "history": 10, - "call": "/usr/bin/sh /home/user/send.sh --metric=$1 --level=$2 --time=$3" + "test": { + "active": true, + "metric": { + "interval": 1000, + "driver": "dns", + "path": { + "rec": "google.com", + "type": "A", + "dns": "8.8.8.8" + } + }, + "agr": { + "lvl": { + "active": true, + "notice": 60, + "warning": 75, + "error": 100 + }, + "history": { + "active": true, + "max": 10 + }, + "avg": { + "active": true, + "zones": { + "10": { + "active": true, + "interval": 10000, + "src": "@live" + }, + "30": { + "active": true, + "interval": 30000, + "src": "@zones.10" + }, + "60": { + "active": true, + "interval": 60000, + "src": "@zones.10" + } + } + } + }, + "call": { + "caller": "shell", + "path": "echo $1 $2 $3 >> call.log" + }, + "log": { + "logger": "local" + } + } + }, + "caller": { + "shell": { + "active": true, + "driver": "shell", + "path": "echo $1 $2 $3 >> $1.call.log" + } + }, + "logger": { + "local": { + "active": true, + "driver": "file", + "path": "./logs/$1.log" } } } diff --git a/ext/dns.js b/ext/dns.js deleted file mode 100644 index b768139..0000000 --- a/ext/dns.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * DNS Lookup speed tester - * - * use: node dns.js domain.tld A 8.8.8.8 - * where: - * - domain.tld - checked DNS-record - * - A - type of DNS-record - * - 8.8.8.8 - used DNS-server - */ - -var dns = require('dns'); -const { hrtime } = require('process'); - -const NS_PER_SEC = 1e9; -const NS_TO_MS = 1e6; - -/** - * DNS lookup speed check - * @param {string} host Checked DNS-record - * @param {string} rrtype [A,AAAA,ANY,CAA,CNAME,MX,NAPTR,NS,PTR,SOA,SRV,TXT] see: https://nodejs.org/api/dns.html#dnsresolvehostname-rrtype-callback - * @param {string} dnshost Used DNS-server - * @param {function} cb (string err, int time) Callback function - */ -function DNSLookupSpeed(host, rrtype, dnshost, cb) { - dns.setServers([dnshost]); - var start = hrtime(); - dns.resolve(host, rrtype, function(err) { - var diff = hrtime(start); - cb(err != null ? err.code : 'OK', Math.round((diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS)); - }); -} - -const run = process.argv.slice(2); - -DNSLookupSpeed(run[0], run[1], run[2], (err, end) => { - if (err == 'OK') { - console.log(end); - } else { - console.log(1000); - } -}); \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..5548653 --- /dev/null +++ b/index.js @@ -0,0 +1,13 @@ +/** + * Metric From Future + * Metric Server for calculation timing of script execution time + * + * @author AlexMcArrow + * @version 0.3.0 + * @package metricfromfuture + */ + +const server = require('./src/server') + +var MFF = new server() +MFF.start() \ No newline at end of file diff --git a/package.json b/package.json index dbd9fa0..e3fca34 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,17 @@ { "name": "@alexmcarrow/metricfromfuture", - "version": "0.2.18", + "version": "0.3.0", "description": "Metric Server with realtime calculation", - "main": "./server.js", + "main": "./index.js", "scripts": { - "test": "node server.js", - "start": "node ./dist/mff.js", + "dev": "node index.js", "build": "webpack --config webpack.config.js" }, "keywords": [ "metric", "server", "realtime", - "rpc" + "json-rpc" ], "repository": { "type": "git", diff --git a/server.js b/server.js deleted file mode 100644 index 84ec92f..0000000 --- a/server.js +++ /dev/null @@ -1,549 +0,0 @@ -/** - * Metric From Future - * Metric Server for calculation timing of script execution time - * - * @author AlexMcArrow - * @version 0.2.18 - * @package metricfromfuture - */ - -const fs = require('fs'); -const http = require('http'); -const url = require('url'); -const path = require('path'); -const child_process = require('child_process'); -const crypto = require('crypto'); - -/** - * Helper class with frequently used functions - */ -class Helper { - - /** - * Generate random ID - * @returns string - */ - id() { - return crypto.randomBytes(16).toString('hex'); - } - - /** - * Return current Unix timestamp - * @returns string - */ - ts() { - return Date.now(); - } - - /** - * Return current date-time - * @returns datetime - */ - dt() { - return new Date().toISOString(); - } -} - -/** - * Collector for store actual (by logic) value - */ -class Collector { - - /** - * Constructor - * @param {int} history History max count - */ - constructor(history = 10) { - var that = this; - that.Helper = new Helper; - that._s = 0; - that._l = 0; - that._t = that.Helper.ts(); - that._dt = that.Helper.dt(); - that._history = {}; - that._max_history = parseInt(history); - } - - /** - * Save complete value - * @param {int} v complete Value - */ - save(v) { - var that = this; - that._history[that._t] = { - v: that._s, - t: that._t, - dt: that._dt - }; - that._s = v; - that._l = that._s; - that._t = that.Helper.ts(); - that._dt = that.Helper.dt(); - if (Object.keys(that._history).length > that._max_history) { - delete that._history[Object.keys(that._history)[0]]; - } - } - - /** - * Store current value - * @param {int} v current value - */ - live(v) { - var that = this; - if (v > that._s) { - that._l = parseInt(v); - that._t = that.Helper.ts(); - that._dt = that.Helper.dt(); - } else { - if (v > that._l) { - that._l = parseInt(v); - that._t = that.Helper.ts(); - that._dt = that.Helper.dt(); - } - } - } - - /** - * Return actual value, timestame, datetime - * @returns object - */ - now() { - var that = this; - return { v: that._l, t: that._t, dt: that._dt, hist: that._history }; - } -} - -/** - * Collector pool - */ -class CollectorPool { - - /** - * Constructor - */ - constructor() { - var that = this; - that.Helper = new Helper; - that._pool = {}; - } - - /** - * Create new Metric with params - * @param {bool} uselogs Using file-logs flag - * @param {string} metric Metric name - * @param {int} period Period of metric run in millisecond - * @param {object} runner Runner - * @param {int} notice Time of Notice range - * @param {int} warn Time of Warning range - * @param {int} error Time of Error range - * @param {int} history History max count - * @param {bool|string} call Call string - * @returns - */ - create(uselogs, metric, period, runner, notice = 10, warn = 30, error = 1000, history = 10, call = false) { - var that = this; - metric = that._metric(metric); - period = parseInt(period); - that._pool[metric] = { - id: metric, - p: period, - r: runner, - _tn: notice, - _tw: warn, - _te: error, - _c: new Collector(history), - _r: {}, - _t: null, - _log: new Logs(uselogs, path_logs, metric), - _call: new Caller(call, metric) - }; - return true; - } - - /** - * Run all metrics in pool - * @returns bool - */ - startALL() { - var that = this; - for (const k in that._pool) { - if (that._pool.hasOwnProperty(k)) { - const e = that._pool[k]; - that._initRunner(e); - e._t = setInterval(() => { - that._initRunner(e); - }, e.p); - } - } - return true; - } - - /** - * Stop all metrics in pool - * @returns bool - */ - stopALL() { - var that = this; - for (const k in that._pool) { - if (that._pool.hasOwnProperty(k)) { - const e = that._pool[k]; - clearInterval(e._t); - } - } - return true; - } - - /** - * Initilize runner for metric by config - * @param {object} e Metric config - */ - _initRunner(e) { - var that = this; - var t = that.Helper.id(); - e._r[t] = new Runner(t, e.r, (tid, r) => { - e._c.save(r); - delete e._r[tid]; - if (r >= e._te) { - that._log(e.id, 'error', r); - } else if (r >= e._tw) { - that._log(e.id, 'warning', r); - } else if (r >= e._tn) { - that._log(e.id, 'notice', r); - } - }, (l) => { - e._c.live(l); - }); - } - - /** - * Get Metric actual value - * @param {string} metric Metric name - * @returns object - */ - get(metric) { - var that = this; - metric = that._metric(metric); - if (that._pool.hasOwnProperty(metric)) { - return that._pool[metric]._c.now(); - } - return {}; - } - - /** - * Get all Metrics actual values - * @returns object - */ - getALL() { - var that = this; - var out = {}; - for (const k in that._pool) { - if (that._pool.hasOwnProperty(k)) { - const e = that._pool[k]; - out[e.id] = e._c.now(); - } - } - return out; - } - - /** - * Get all Metrics actual values & runner lists - * @returns object - */ - getSYS() { - var that = this; - var out = {}; - for (const k in that._pool) { - if (that._pool.hasOwnProperty(k)) { - const e = that._pool[k]; - const r = Object.keys(e._r); - const rl = {}; - for (const x in r) { - rl[r[x]] = e._r[r[x]].tc; - } - out[e.id] = { - ...e._c.now(), - r: rl - }; - } - } - return out; - } - - /** - * Return clear metric name - * @param {string} metric Metric name - * @returns string - */ - _metric(metric) { - return metric.trim().toLowerCase(); - } - - /** - * Logging and Calling - * @param {string} metric Metric name - * @param {string} level Log level - * @param {mixed} val logging value - */ - _log(metric, level, val) { - var that = this; - that._pool[metric]._log.write(that.Helper.ts() + '|' + that.Helper.dt() + '|' + metric + '|' + level + '|' + val + '\n'); - that._pool[metric]._call.ring(level, val); - } -} - -/** - * External script runner class - */ -class Runner { - - /** - * - * @param {string} id Runner ID - * @param {string} runner Runnable script - * @param {func} cbret Callback function for return value - * @param {func} cblive Callback function for publish "live" value - */ - constructor(id, runner, cbret, cblive) { - var that = this; - that._id = id; - that._runner = runner; - that._cbret = cbret; - that._cblive = cblive; - that.work(); - that.tc = -1; - that.t = setInterval(() => { - that.tc++; - that._cblive(that.tc); - }, 1000); - } - - /** - * Worker function - */ - work() { - var that = this; - var workerProcess = child_process.exec(that._runner); - workerProcess.stdout.on('data', function(data) { - clearInterval(that.t); - that._cbret(that._id, parseInt(data.toString())); - }); - workerProcess.stderr.on('data', function(data) { - clearInterval(that.t); - console.error('Error in worker: ', data); - that._cbret(that._id, 1000); - }); - workerProcess.on('close', function(code) { - clearInterval(that.t); - workerProcess = null; - if (code > 0) { - that._cbret(that._id, 1000); - } - }); - workerProcess.on('exit', function(code) { - clearInterval(that.t); - workerProcess = null; - if (code > 0) { - that._cbret(that._id, 1000); - } - }); - } -} - -/** - * Logger - */ -class Logs { - - /** - * Constructor - * @param {bool} use Using log write - * @param {string} path Path for log-file - * @param {string} metric Metric name - */ - constructor(use, path, metric) { - var that = this; - that._use = use; - that._path = path; - that._metric = metric; - that._log = null; - if (that._use && !fs.existsSync(path.join(path_logs))) { - fs.mkdirSync(path.join(path_logs)); - } - } - - /** - * Write into log - * @param {string} line - */ - write(line) { - var that = this; - if (that._use) { - if (that._log.write !== 'function') { - that._log = fs.createWriteStream(path.join(that._path, that._metric + '.log'), { flags: 'a' }); - } - that._log.write(line); - } - } -} - -/** - * Caller - */ -class Caller { - - /** - * Constructor - * @param {bool|string} call Script for calling - * @param {string} metric Metric name - */ - constructor(call, metric) { - var that = this; - that._call = call; - that._metric = metric; - that._caller = null; - } - - /** - * Ringing script for calling - * @param {string} level Log level - * @param {mixed} val logging value - */ - ring(level, val) { - var that = this; - if (that._call !== false) { - var line = that._call; - line = line.replace(/\$1/g, that._metric); - line = line.replace(/\$2/g, level); - line = line.replace(/\$3/g, val); - child_process.exec(line); - } - } -} - -/** - * Metric From Future main class - */ -class MFF { - - /** - * Constructor - */ - constructor() { - var that = this; - console.log('MFF init'); - that.http = null; - - that.config = { - server: { - port: 20744, - logs: true - }, - metric: {} - }; - /// catch errors - process.on('uncaughtException', function(err) { - console.error('Caught exception: ', err); - }); - /// init pool - that.pool = new CollectorPool(); - /// check config - if (fs.existsSync(path_config)) { - try { - /// load config - that.config = JSON.parse(fs.readFileSync(path_config)); - /// create listeners - for (const m in that.config.metric) { - if (that.config.metric.hasOwnProperty(m)) { - const mdata = that.config.metric[m]; - that.pool.create(that.config.server.logs, m, mdata.interval, mdata.run, mdata.notice, mdata.warning, mdata.error, mdata.history, mdata.call); - } - } - } catch (error) { - throw new Error(error.toString()); - } - } - /// run RPC - that.bootstrap(); - /// start listeners - that.pool.startALL(); - /// catch SIGs - process.on('SIGTERM', () => { - console.info('CATCH SIGTERM'); - that.shutdown(); - }); - process.on('SIGINT', () => { - console.info('CATCH SIGINT'); - that.shutdown(); - }); - } - - /** - * Boostrap function - */ - bootstrap() { - var that = this; - const requestListener = function(req, res) { - var parts = url.parse(req.url, true); - var query = parts.query; - var r = { - c: 1, - d: {} - }; - if (query.get !== undefined) { - switch (query.get) { - case 'all': - case '*': - r.d = that.pool.getALL(); - break; - case '_sys': - r.d = that.pool.getSYS(); - break; - default: - r.d = that.pool.get(query.get); - break; - } - } - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); - res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify(r)); - } - that.http = http.createServer(requestListener); - that.http.listen(parseInt(that.config.server.port), () => { - console.log('MFF run at :' + that.config.server.port); - }); - } - - /** - * Shutdown function - */ - shutdown() { - var that = this; - console.log('MFF stoping'); - that.pool.stopALL(); - that.http.close(() => { - console.log('MFF shutdown'); - process.exit(0); - }); - } -} - -// Check ENV -if (process.env.NODE_ENV !== 'production') { - process.env.PATH_LOG = 'logs'; - process.env.PATH_CONFIG = 'config.json'; -} - -// read path_logs & path_config from ENV -const path_logs = process.env.PATH_LOG; -const path_config = process.env.PATH_CONFIG; - -// Check path_logs & path_config => run MFF -if (path_logs !== undefined && path_config !== undefined) { - new MFF; -} else { - // ENV not set => exit(1) - console.error('ENV not set', 'PATH_CONFIG=', process.env.PATH_CONFIG, 'PATH_LOG=', process.env.PATH_LOG); - process.exit(1); -} \ No newline at end of file diff --git a/src/ext/_all.js b/src/ext/_all.js new file mode 100644 index 0000000..3c86faf --- /dev/null +++ b/src/ext/_all.js @@ -0,0 +1,3 @@ +module.exports = { + dns: require('./dns') +} \ No newline at end of file diff --git a/src/ext/dns.js b/src/ext/dns.js new file mode 100644 index 0000000..5d952a2 --- /dev/null +++ b/src/ext/dns.js @@ -0,0 +1,41 @@ +// /** +// * DNS Lookup speed tester +// * +// * use: node dns.js domain.tld A 8.8.8.8 +// * where: +// * - domain.tld - checked DNS-record +// * - A - type of DNS-record +// * - 8.8.8.8 - used DNS-server +// */ + +// var dns = require('dns'); +// const { hrtime } = require('process'); + +// const NS_PER_SEC = 1e9; +// const NS_TO_MS = 1e6; + +// /** +// * DNS lookup speed check +// * @param {string} host Checked DNS-record +// * @param {string} rrtype [A,AAAA,ANY,CAA,CNAME,MX,NAPTR,NS,PTR,SOA,SRV,TXT] see: https://nodejs.org/api/dns.html#dnsresolvehostname-rrtype-callback +// * @param {string} dnshost Used DNS-server +// * @param {function} cb (string err, int time) Callback function +// */ +// function DNSLookupSpeed(host, rrtype, dnshost, cb) { +// dns.setServers([dnshost]); +// var start = hrtime(); +// dns.resolve(host, rrtype, function(err) { +// var diff = hrtime(start); +// cb(err != null ? err.code : 'OK', Math.round((diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS)); +// }); +// } + +// const run = process.argv.slice(2); + +// DNSLookupSpeed(run[0], run[1], run[2], (err, end) => { +// if (err == 'OK') { +// console.log(end); +// } else { +// console.log(1000); +// } +// }); \ No newline at end of file diff --git a/src/modules/_all.js b/src/modules/_all.js new file mode 100644 index 0000000..4dc4e69 --- /dev/null +++ b/src/modules/_all.js @@ -0,0 +1,3 @@ +module.exports = { + caller: require('./caller') +} diff --git a/src/modules/caller.js b/src/modules/caller.js new file mode 100644 index 0000000..af70b03 --- /dev/null +++ b/src/modules/caller.js @@ -0,0 +1,29 @@ +const caller_drivers = require('./caller/_all') + +class CALLER { + + /** + * Constructor + * @param {string} caller driver + */ + constructor(driver, driverconf) { + if (driver in caller_drivers) { + this.$driver = new caller_drivers[driver](driverconf) + return true + } + return false + } + + /** + * Ring caller + * @param {string} metric + * @param {string} level + * @param {float} value + */ + ring(metric, level, value) { + this.$driver.ring(metric, level, value) + } + +} + +module.exports = CALLER \ No newline at end of file diff --git a/src/modules/caller/_all.js b/src/modules/caller/_all.js new file mode 100644 index 0000000..e76c911 --- /dev/null +++ b/src/modules/caller/_all.js @@ -0,0 +1,5 @@ +module.exports = { + shell: require('./shell'), + httpget: require('./httpget'), + httppost: require('./httppost'), +} \ No newline at end of file diff --git a/src/modules/caller/httpget.js b/src/modules/caller/httpget.js new file mode 100644 index 0000000..cfb2b4b --- /dev/null +++ b/src/modules/caller/httpget.js @@ -0,0 +1,69 @@ +const http = require('http') +const https = require('https') + +class CALLER_HTTPGET { + + /** + * Constructor + * @param {path} shell path with regexp + */ + constructor(path) { + this.$path = new URL(path) + } + + /** + * Make HTTP-GET request + * @param {string} metric + * @param {string} level + * @param {float} value + * @returns boolean + */ + ring(metric, level, value) { + var request + var agentOptions + var agent + + var line = this.$path.href + line = line.replace(/\$1/g, metric) + line = line.replace(/\$2/g, level) + line = line.replace(/\$3/g, value) + + agentOptions = { + host: this.$path.hostname, + path: '/', + rejectUnauthorized: false + } + + switch (this.$path.protocol) { + case 'https:': + agent = new https.Agent(agentOptions) + agent.options.port = this.$path.port || 443 + request = https.request + break + case 'http:': + agent = new http.Agent(agentOptions) + agent.options.port = this.$path.port || 80 + request = http.request + break + + default: + return false + break + } + + request({ + url: line, + method: "GET", + agent: agent + }).on('error', (e) => { + //TODO: Logging internal error + console.log('CALLER_HTTPGET', e); + }).end() + + + return true + } + +} + +module.exports = CALLER_HTTPGET diff --git a/src/modules/caller/httppost.js b/src/modules/caller/httppost.js new file mode 100644 index 0000000..a4ab590 --- /dev/null +++ b/src/modules/caller/httppost.js @@ -0,0 +1,69 @@ +const http = require('http') +const https = require('https') + +class CALLER_HTTPPOST { + + /** + * Constructor + * @param {path} shell path with regexp + */ + constructor(path) { + this.$path = new URL(path) + } + + /** + * Make HTTP-POST request + * @param {string} metric + * @param {string} level + * @param {float} value + */ + ring(metric, level, value) { + var request + var agentOptions + var agent + + agentOptions = { + host: this.$path.hostname, + path: '/', + rejectUnauthorized: false + } + + switch (this.$path.protocol) { + case 'https:': + agent = new https.Agent(agentOptions) + agent.options.port = this.$path.port || 443 + request = https.request + break + case 'http:': + agent = new http.Agent(agentOptions) + agent.options.port = this.$path.port || 80 + request = http.request + break + + default: + return false + break + } + + var payload = JSON.stringify({ metric, level, value }) + + var req = request({ + url: this.$path.pathname, + method: "POST", + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(payload) + }, + agent: agent + }).on('error', (e) => { + //TODO: Logging internal error + console.log('CALLER_HTTPPOST', e); + }) + req.write(payload) + req.end() + return true + } + +} + +module.exports = CALLER_HTTPPOST diff --git a/src/modules/caller/shell.js b/src/modules/caller/shell.js new file mode 100644 index 0000000..4892280 --- /dev/null +++ b/src/modules/caller/shell.js @@ -0,0 +1,29 @@ +const child_process = require('child_process') + +class CALLER_SHELL { + + /** + * Constructor + * @param {path} shell path with regexp + */ + constructor(path) { + this.$path = path + } + + /** + * Run shell + * @param {string} metric + * @param {string} level + * @param {float} value + */ + ring(metric, level, value) { + var line = this.$path + line = line.replace(/\$1/g, metric) + line = line.replace(/\$2/g, level) + line = line.replace(/\$3/g, value) + child_process.exec(line) + } + +} + +module.exports = CALLER_SHELL \ No newline at end of file diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..d243fb3 --- /dev/null +++ b/src/server.js @@ -0,0 +1,35 @@ +/** + * Metric From Future + * Metric Server for calculation timing of script execution time + * + * @author AlexMcArrow + * @version 0.3.0 + * @package metricfromfuture + */ + +const modules = require('./modules/_all') +const exts = require('./ext/_all') + +class MFF { + + /** + * Constructor + */ + constructor() { + + console.log('MFF init') + // var hget = new modules.caller('httpget', 'http://127.0.0.1:8080/api/v2/ping') + // hget.ring('test', 'log', 0) + } + + start() { + console.log('MFF start') + this.stop() + } + + stop() { + console.log('MFF stop') + } +} + +module.exports = MFF diff --git a/webpack.config.js b/webpack.config.js index 99e43b2..dbda646 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,11 +8,7 @@ module.exports = { target: 'node', mode: 'production', entry: { - mff: './server.js', - ...glob.sync('./ext/**.js').reduce(function(obj, el) { - obj['ext/' + path.parse(el).name] = el; - return obj - }, {}) + mff: './index.js' }, resolve: { extensions: [".ts", ".js"] From de7275fee4c2ee5752c885dba7846206df428dd4 Mon Sep 17 00:00:00 2001 From: AlexMcArrow Date: Wed, 3 Nov 2021 13:15:07 +0500 Subject: [PATCH 2/4] Server: init, start, stop, $$pipe; RPC(json): listen using $$pipe --- config.json._ | 55 ++---- index.js | 4 +- src/modules/_all.js | 5 +- src/modules/rpc.js | 36 ++++ src/modules/rpc/_all.js | 3 + src/modules/rpc/json.js | 421 ++++++++++++++++++++++++++++++++++++++++ src/server.js | 196 ++++++++++++++++++- 7 files changed, 674 insertions(+), 46 deletions(-) create mode 100644 src/modules/rpc.js create mode 100644 src/modules/rpc/_all.js create mode 100644 src/modules/rpc/json.js diff --git a/config.json._ b/config.json._ index cb6ca0c..55358ef 100644 --- a/config.json._ +++ b/config.json._ @@ -1,6 +1,25 @@ { - "server": { - "port": 20744 + "rpc": { + "front": { + "active": true, + "driver": "json", + "dsn": { + "type": "front", + "host": "127.0.0.1", + "port": 20744, + "path": "/" + } + }, + "back": { + "active": true, + "driver": "json", + "dsn": { + "type": "back", + "host": "127.0.0.1", + "port": 20799, + "path": "/" + } + } }, "metric": { "test": { @@ -14,38 +33,6 @@ "dns": "8.8.8.8" } }, - "agr": { - "lvl": { - "active": true, - "notice": 60, - "warning": 75, - "error": 100 - }, - "history": { - "active": true, - "max": 10 - }, - "avg": { - "active": true, - "zones": { - "10": { - "active": true, - "interval": 10000, - "src": "@live" - }, - "30": { - "active": true, - "interval": 30000, - "src": "@zones.10" - }, - "60": { - "active": true, - "interval": 60000, - "src": "@zones.10" - } - } - } - }, "call": { "caller": "shell", "path": "echo $1 $2 $3 >> call.log" diff --git a/index.js b/index.js index 5548653..708cbb3 100644 --- a/index.js +++ b/index.js @@ -10,4 +10,6 @@ const server = require('./src/server') var MFF = new server() -MFF.start() \ No newline at end of file +MFF.start(process.env.PATH_CONFIG || './config.json') + +/** End of index */ \ No newline at end of file diff --git a/src/modules/_all.js b/src/modules/_all.js index 4dc4e69..6f6d2a3 100644 --- a/src/modules/_all.js +++ b/src/modules/_all.js @@ -1,3 +1,4 @@ module.exports = { - caller: require('./caller') -} + rpc: require('./rpc'), + caller: require('./caller'), +} \ No newline at end of file diff --git a/src/modules/rpc.js b/src/modules/rpc.js new file mode 100644 index 0000000..a9e05ee --- /dev/null +++ b/src/modules/rpc.js @@ -0,0 +1,36 @@ +const rpc_drivers = require('./rpc/_all') + +class RPC { + + /** + * Constructor + * @param {string} rpc driver + */ + constructor(driver, driverconf, server) { + if (driver in rpc_drivers) { + var drv = new rpc_drivers[driver](driverconf, server) + if (drv !== undefined) { + this.$driver = drv + return true + } + } + return false + } + + /** + * Start rpc listener + */ + start() { + this.$driver.start() + } + + /** + * Stop rpc listener + */ + stop() { + this.$driver.stop() + } + +} + +module.exports = RPC \ No newline at end of file diff --git a/src/modules/rpc/_all.js b/src/modules/rpc/_all.js new file mode 100644 index 0000000..2103e3c --- /dev/null +++ b/src/modules/rpc/_all.js @@ -0,0 +1,3 @@ +module.exports = { + json: require('./json'), +} \ No newline at end of file diff --git a/src/modules/rpc/json.js b/src/modules/rpc/json.js new file mode 100644 index 0000000..8fcbc49 --- /dev/null +++ b/src/modules/rpc/json.js @@ -0,0 +1,421 @@ +const http = require('http') +const assert = require('assert') + +const PARSE_ERROR = -32700 +const INVALID_REQUEST = -32600 +const METHOD_NOT_FOUND = -32601 +const INVALID_PARAMS = -32602 +const SERVER_ERROR = -32000 +const SERVER_ERROR_MAX = -32099 +const INVALID_ACCEPT_HEADER_MSG = 'Accept header must include application/json' +const INVALID_CONTENT_LENGTH_MSG = 'Invalid Content-Length header' +const callbackNames = ['onRequest', 'onRequestError', 'onResult', 'onServerError'] + +class RPC_JSON { + + /** + * Constructor + * @param {Object} config for listen + */ + constructor(config, pipe) { + this.$active = config.active + this.$dsn = config.dsn + this.$pipe = pipe + this.$rpc = null + } + + /** + * Start listen + */ + start() { + console.log('RPC', 'JSON', 'start', this.$dsn) + if (this.$active) { + this.$rpc = new RpcServer({ + path: this.$dsn.path, + onRequest: (request) => { + console.log('RPC', 'JSON', 'onRequest', JSON.stringify(request)) + }, + onRequestError: (err, id) => { + console.error('RPC', 'JSON', 'onRequestError', 'request ' + id + ' threw an error: ' + err) + }, + onResult: (result, id) => { + console.log('RPC', 'JSON', 'onResult', result) + }, + onServerError: (err) => { + console.error('RPC', 'JSON', 'onServerError', err) + }, + }) + if (this.$pipe[this.$dsn.type] !== undefined) { + for (const m in this.$pipe[this.$dsn.type]) { + if (Object.hasOwnProperty.call(this.$pipe[this.$dsn.type], m)) { + this.$rpc.setMethod(m, this.$pipe[this.$dsn.type][m]) + } + } + } + var that = this + this.$rpc.listen(parseInt(this.$dsn.port), this.$dsn.host).then(() => { + console.log('RPC', 'JSON', 'start at', 'http://' + that.$dsn.host + ':' + that.$dsn.port + that.$dsn.path) + }) + } else { + console.log('RPC', 'JSON', 'is disabled') + } + } + + /** + * Stop listen + */ + stop() { + console.log('RPC', 'JSON', 'stop') + } + +} + +/** + * Class representing a HTTP JSON-RPC server + * @see https://github.com/sangaman/http-jsonrpc-server + */ +class RpcServer { + /** + * @param {Object} options - Optional parameters for the server + * @param {Object} options.methods - A map of method names to functions. Method functions are + * passed one parameter which will either be an Object or a string array + * @param options.context - Context to be used as `this` for method functions. + * @param {string} options.path - The path for the server + * @param {function} options.onRequest - Callback for when requests are received, it is + * passed an Object representing the request + * @param {function} options.onRequestError - Callback for when requested methods throw errors, + * it is passed an error and request id + * @param {function} options.onResult - Callback for when requests are successfully returned a + * result. It is passed the response object and request id + * @param {function} options.onServerError - Callback for server errors, it is passed an + * {@link https://nodejs.org/api/errors.html#errors_class_error Error} + * @param {string} options.username - Username for authentication. If provided, Basic + * Authentication will be enabled and required for all requests. + * @param {string} options.password - Password for authentication, ignored unless a username is + * also specified. + * @param {string} options.realm - Realm for authentication, ignored unless a username is also + * specified, defaults to `Restricted` if not specified. + */ + constructor(options) { + this.methods = {} + this.path = '/' + + if (options) { + this.applyOptions(options) + } + + this.server = http.createServer(reqHandler.bind(this)) + if (this.onServerError) { + this.server.on('error', this.onServerError) + } + } + + applyOptions(options) { + if (options.methods) { + assert(typeof options.methods === 'object' && !Array.isArray(options.methods), 'methods must be an object') + const keys = Object.keys(options.methods) + for (let n = 0; n < keys.length; n += 1) { + const key = keys[n] + assert(typeof options.methods[key] === 'function', 'methods may only contain functions') + } + this.methods = options.methods + if (options.context) { + for (let n = 0; n < keys.length; n += 1) { + const key = keys[n] + this.methods[key] = this.methods[key].bind(options.context) + } + } + } + + if (options.path) { + assert(typeof options.path === 'string', 'path must be a string') + assert(options.path.startsWith('/'), 'path must start with a "/" slash') + assert(/^[A-Za-z0-9\-./\]@$&()*+,;=`_:~?#!']+$/.test(options.path), 'path contains invalid characters') + this.path = options.path + } + + if (options.username) { + // Basic Authentication is enabled + assert(typeof options.username === 'string', 'username must be a string') + let stringToEncode = `${options.username}:` + if (options.password) { + assert(typeof options.password === 'string', 'password must be a string') + stringToEncode += options.password + } + this.authorization = Buffer.from(stringToEncode).toString('base64') + this.realm = options.realm || 'Restricted' + } + + callbackNames.forEach((callbackName) => { + if (options[callbackName]) { + assert(typeof options[callbackName] === 'function', `${callbackName} must be a function`) + this[callbackName] = options[callbackName] + } + }) + } + + static get PARSE_ERROR() { + return PARSE_ERROR + } + + static get INVALID_REQUEST() { + return INVALID_REQUEST + } + + static get METHOD_NOT_FOUND() { + return METHOD_NOT_FOUND + } + + static get INVALID_PARAMS() { + return INVALID_PARAMS + } + + static get SERVER_ERROR() { + return SERVER_ERROR + } + + static get SERVER_ERROR_MAX() { + return SERVER_ERROR_MAX + } + + /** + * Sets a method. + * @param {string} name - The name of the method + * @param {function} method - The function to be called for this method. Method functions are + * passed one parameter which will either be an Object or a string array. + */ + setMethod(name, method) { + assert(typeof method === 'function', 'method is not a function') + this.methods[name] = method + } + + /** + * Begins listening on a given port and host. + * @param {number} port - The port number to listen on - an arbitrary available port is used if + * no port is specified + * @param {string} host - The host name or ip address to listen on - the unspecified IP address + * (`0.0.0.0` or `(::)`) is used if no host is specified + * @returns {Promise} A promise that resolves to the assigned port once the server is + * listening. On error the promise will be rejected with an {@link https://nodejs.org/api/errors.html#errors_class_error Error}. + */ + listen(port, host) { + return new Promise((resolve, reject) => { + const errHandler = (err) => { + reject(err) + } + + this.server.listen(port, host, () => { + resolve(this.server.address().port) + this.server.removeListener('error', errHandler) + }).once('error', errHandler) + }) + } + + /** + * Stops listening on all ports. + * @returns {Promise} A promise that resolves once the server stops listening. On error the + * promise will be rejected with an {@link https://nodejs.org/api/errors.html#errors_class_error Error}. + */ + close() { + return new Promise((resolve, reject) => { + this.server.close(() => { + resolve() + this.server.removeAllListeners() + }).once('error', (err) => { + reject(err) + }) + }) + } + + async processRequest(request) { + if (this.onRequest) { + this.onRequest(request) + } + let response = { + jsonrpc: '2.0', + } + + if (request.id) { + if (request.id !== null && typeof request.id !== 'number' && typeof request.id !== 'string') { + response.error = { + code: INVALID_REQUEST, + message: 'Invalid id', + } + return response + } + response.id = request.id + } + + if (request.jsonrpc !== '2.0') { + response.error = { + code: INVALID_REQUEST, + message: 'Invalid jsonrpc value', + } + } else if (!request.id) { + // if we have no id, treat this as a notification and return nothing + response = undefined + } else if (!request.method || typeof request.method !== 'string' || request.method.startsWith('rpc.') || !(request.method in this.methods)) { + response.error = { + code: METHOD_NOT_FOUND, + } + } else if (request.params && typeof request.params !== 'object') { + response.error = { + code: INVALID_PARAMS, + } + } else { + // we have passed all up front error checks, call the method + try { + response.result = await Promise.resolve(this.methods[request.method](request.params)) + if (!response.id) { + response = undefined // don't return a response if id is null + } + } catch (err) { + if (this.onRequestError) { + this.onRequestError(err, request.id) + } + const message = err.message || err + response.error = { message } + if (err.code && err.code <= SERVER_ERROR && err.code >= SERVER_ERROR_MAX) { + response.error.code = err.code + } else { + response.error.code = SERVER_ERROR + } + } + } + + if (this.onResult && response.result) { + this.onResult(response.result, request.id) + } + return response + } +} + +function sendResponse(res, response) { + if (response) { + const responseStr = JSON.stringify(response) + res.setHeader('Content-Type', 'application/json') + res.setHeader('Content-Length', Buffer.byteLength(responseStr)) + res.write(responseStr) + } else { + // Respond 204 for notifications with no response + res.setHeader('Content-Length', 0) + res.statusCode = 204 + } + res.end() +} + +function sendError(res, statusCode, message) { + res.statusCode = statusCode + if (message) { + const formattedMessage = `{"error":"${message}"}` + res.setHeader('Content-Type', 'application/json') + res.setHeader('Content-Length', Buffer.byteLength(formattedMessage)) + res.write(formattedMessage) + } + res.end() +} + +function checkRequest(req, path) { + let err + if (req.url !== path) { + err = { statusCode: 404 } + } else if (req.method !== 'POST') { + err = { statusCode: 405 } + } else if (!req.headers['content-type'] || (req.headers['content-type'] !== 'application/json' && + !req.headers['content-type'].startsWith('application/json;'))) { + err = { statusCode: 415 } + } else if (!req.headers.accept || (req.headers.accept !== 'application/json' && + !req.headers.accept.split(',').some((value) => { + const trimmedValue = value.trim() + return trimmedValue === 'application/json' || trimmedValue.startsWith('application/json;') + }))) { + err = { statusCode: 400, message: INVALID_ACCEPT_HEADER_MSG } + } else { + const reqContentLength = parseInt(req.headers['content-length'], 10) + if (Number.isNaN(reqContentLength) || reqContentLength < 0) { + err = { statusCode: 400, message: INVALID_CONTENT_LENGTH_MSG } + } + } + return err +} + +function reqHandler(req, res) { + res.setHeader('Connection', 'close') + const reqErr = checkRequest(req, this.path) + if (reqErr) { + sendError(res, reqErr.statusCode, reqErr.message) + return + } + + if (this.authorization) { + const { authorization } = req.headers + if (!authorization || !authorization.startsWith('Basic ') || authorization.substring(6) !== this.authorization) { + res.setHeader('WWW-Authenticate', `Basic realm="${this.realm}"`) + sendError(res, 401) + return + } + } + + const body = [] + req.on('data', (chunk) => { + body.push(chunk) + }).on('end', () => { + const bodyStr = Buffer.concat(body).toString() + const reqContentLength = parseInt(req.headers['content-length'], 10) + + if (Buffer.byteLength(bodyStr) !== reqContentLength) { + sendError(res, 400, INVALID_CONTENT_LENGTH_MSG) + return + } + + res.setHeader('Content-Type', 'application/json') + + let request + try { + request = JSON.parse(bodyStr) + } catch (err) { + const response = { + id: null, + jsonrpc: '2.0', + error: { + code: PARSE_ERROR, + message: err.message, + }, + } + sendResponse(res, response) + return + } + + if (Array.isArray(request)) { + if (request.length === 0) { + sendResponse(res) + } else { + const requestPromises = [] + for (let n = 0; n < request.length; n += 1) { + requestPromises.push(this.processRequest(request[n])) + } + Promise.all(requestPromises).then((responses) => { + // Remove undefined values from responses array. + // These represent notifications that don't require responses. + let prunedResponses = [] + for (let n = 0; n < responses.length; n += 1) { + if (responses[n]) { + prunedResponses.push(responses[n]) + } + } + if (prunedResponses.length === 0) { + // If all the requests were notifications, there should be no response + prunedResponses = undefined + } + sendResponse(res, prunedResponses) + }) + } + } else { + this.processRequest(request).then((response) => { + sendResponse(res, response) + }) + } + }) +} + +module.exports = RPC_JSON \ No newline at end of file diff --git a/src/server.js b/src/server.js index d243fb3..d2e0baf 100644 --- a/src/server.js +++ b/src/server.js @@ -7,29 +7,207 @@ * @package metricfromfuture */ +const fs = require('fs') + const modules = require('./modules/_all') -const exts = require('./ext/_all') class MFF { /** * Constructor */ - constructor() { + constructor(config_path = './config.json') { + console.log('MFF', 'init defs') + + this.$config = { + rpc: {}, + metric: {}, + caller: {}, + logger: {} + } + + this.$rpc = {} + this.$metric = {} + this.$caller = {} + this.$logger = {} + + console.log('MFF', 'read config from', config_path) + this.$readConfig(config_path) + + console.log('MFF', 'init:') + this.$initLogger() + this.$initRPC() + this.$initCaller() + this.$initMetric() + console.log('MFF', 'init complete') + } + + $readConfig(path) { + if (fs.existsSync(path)) { + try { + this.$config = JSON.parse(fs.readFileSync(path)) + } catch (error) { + throw new Error(error.toString()) + } + } + } - console.log('MFF init') - // var hget = new modules.caller('httpget', 'http://127.0.0.1:8080/api/v2/ping') - // hget.ring('test', 'log', 0) + $initRPC() { + console.log('MFF', 'init', 'RPCs') + for (const rpcidx in this.$config.rpc) { + if (Object.hasOwnProperty.call(this.$config.rpc, rpcidx)) { + const element = this.$config.rpc[rpcidx] + var drv = new modules.rpc(element.driver, element, this.$$pipe(this)) + if (drv.$driver !== undefined) { + this.$rpc[rpcidx] = drv + } + } + } + console.log('MFF', 'init', 'RPCs', Object.keys(this.$rpc)) + } + + $initMetric() { + console.log('MFF', 'init', 'Metrics') + //TODO: Metric init loop + } + + $initCaller() { + console.log('MFF', 'init', 'Callers') + //TODO: Caller init loop + } + + $initLogger() { + console.log('MFF', 'init', 'Loggers') + //TODO: Logger init loop } start() { - console.log('MFF start') - this.stop() + console.log('MFF', 'start') + + this.$startLogger() + this.$startRPC() + this.$startCaller() + this.$startMetric() + } + + $startRPC() { + console.log('MFF', 'start', 'RPCs') + for (const rpcidx in this.$rpc) { + if (Object.hasOwnProperty.call(this.$rpc, rpcidx)) { + const rpc = this.$rpc[rpcidx] + rpc.start() + } + } + } + + $startMetric() { + console.log('MFF', 'start', 'Metrics') + //TODO: Metric start loop + } + $startCaller() { + console.log('MFF', 'start', 'Callers') + //TODO: Caller start loop + } + $startLogger() { + console.log('MFF', 'start', 'Loggers') + //TODO: Logger start loop } stop() { - console.log('MFF stop') + console.log('MFF', 'stop') + + this.$stopMetric() + this.$stopCaller() + this.$stopRPC() + this.$stopLogger() + process.exit(0) + } + + $stopRPC() { + console.log('MFF', 'stop', 'RPCs') + for (const rpcidx in this.$rpc) { + if (Object.hasOwnProperty.call(this.$rpc, rpcidx)) { + const rpc = this.$rpc[rpcidx] + rpc.stop() + } + } + } + + $stopMetric() { + console.log('MFF', 'stop', 'Metrics') + } + $stopCaller() { + console.log('MFF', 'stop', 'Callers') + } + $stopLogger() { + console.log('MFF', 'stop', 'Loggers') + } + + /** + * Proxy-pipe controled functions + * @param {this} that + * @returns functions + */ + $$pipe(that) { + return { + /** + * Used by internal + */ + ///TODO: Work with internal config + config: { + read(a) { + return that.$config.rpc + ///TODO: dummy + }, + write(a) { + return that.$config.rpc + ///TODO: dummy + }, + save() { + return 'saved' + ///TODO: dummy + } + }, + /** + * Used by RPC + */ + ///TODO: work with front-data (metric) + front: { + ping() { + return 'pong' + }, + list() { + ///TODO: work with Metric(read list) + return [] + }, + get(params) { + if (params.item !== undefined) { + ///TODO: work with Metric(read by name) + return { + item: params.item + } + } + return false + } + }, + /** + * Used by RPC + */ + ///TODO: work with back-methods like: stoping server, etc + back: { + ping() { + return 'pong' + }, + stop() { + /** Stop after 250ms for return responce */ + setTimeout(() => { + that.stop() + }, 250) + return true + } + } + } } } -module.exports = MFF +module.exports = MFF \ No newline at end of file From dc73d16bf0f5c38c7d0a496e5e664f70baa609c8 Mon Sep 17 00:00:00 2001 From: AlexMcArrow Date: Wed, 3 Nov 2021 19:40:51 +0500 Subject: [PATCH 3/4] add HTTP JSONRPC CLI-client --- cli/index.js | 170 ++++++++++++++++++++++++++++++++++++++++++++++ webpack.config.js | 3 +- 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 cli/index.js diff --git a/cli/index.js b/cli/index.js new file mode 100644 index 0000000..d1d3fbd --- /dev/null +++ b/cli/index.js @@ -0,0 +1,170 @@ +/** + * MFF HTTP-JSON-RPC CLI-client for administrative operation + */ + +const { match } = require('assert'); +var http = require('http') + +var JSONRPCClient = function(port, host, path = null) { + this.port = port; + this.host = host; + this.path = path; + + this.call = function(method, params, callback) { + var requestJSON = JSON.stringify({ + 'jsonrpc': '2.0', + 'id': (new Date().getTime()), + 'method': method, + 'params': params, + 'cl': 'mffcli' + }); + var headers = { + 'host': host, + 'Content-Length': requestJSON.length, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }; + + if (this.path === null) { + this.path = '/'; + } + + var options = { + host: host, + port: port, + path: this.path, + headers: headers, + method: 'POST' + } + + var buffer = ''; + + var req = http.request(options, function(res) { + res.on('data', function(chunk) { + buffer = buffer + chunk; + }); + + res.on('end', function() { + var decoded = {} + try { + decoded = JSON.parse(buffer); + } catch (error) { + callback(error, null); + return + } + + if (decoded.hasOwnProperty('result')) { + callback(null, decoded.result); + } else { + callback(decoded.error, null); + } + }); + + res.on('error', function(err) { + callback(err, null); + }); + }); + + req.on('error', function(err) { + console.error('ClientError:', err.code); + }); + + req.write(requestJSON); + req.end(); + }; +} + +const ARGUMENT_SEPARATION_REGEX = /([^=\s]+)=?\s*(.*)/; + +function Parse(argv) { + // Removing node/bin and called script name + argv = argv.slice(2); + + const parsedArgs = {}; + let argName, argValue; + + argv.forEach(function(arg) { + // Separate argument for a key/value return + arg = arg.match(ARGUMENT_SEPARATION_REGEX); + arg.splice(0, 1); + + // Retrieve the argument name + argName = arg[0]; + + // Remove "--" or "-" + if (argName.indexOf('-') === 0) { + argName = argName.slice(argName.slice(0, 2).lastIndexOf('-') + 1); + } + + // Parse argument value or set it to `true` if empty + argValue = + arg[1] !== '' ? + parseFloat(arg[1]).toString() === arg[1] ? + +arg[1] : + arg[1] : + true; + + parsedArgs[argName] = argValue; + }); + + return parsedArgs; +} + +/** Read args from cmd */ +var args = Parse(process.argv); +/** Set from args or use defs */ +var host = args.host || '127.0.0.1' +var port = args.port || 20799 +var path = args.path || '/' +var method = args.method || 'ping' +var params = {} + +for (const key in args) { + if (Object.hasOwnProperty.call(args, key)) { + const value = args[key]; + switch (key) { + case 'host': + case 'port': + case 'path': + case 'method': + break; + + default: + if (value === true) { + method = key + } else if (key.match(/^p\.(.*)$/)) { + params[key.replace(/^p\./, '')] = value + } + break; + } + } +} + +if (Object.keys(args).length == 0) { + console.log('') + console.log(' MFF HTTP-JSON-RPC CLI-client') + console.log('') + console.log('use: node cli.js method [args,...]') + console.log(' node cli.js ping --port=20799') + console.log(' node cli.js ping --p.alpha=5 --p.beta=7 => params: {"alpha":5,"beta":7}') + console.log('') + console.log('\targ\t\treq\tdescription \tdefault') + console.log('') + console.log('\t--host\t\t\tserver host \t"127.0.0.1"') + console.log('\t--port\t\t\tserver port \t20799 (back-RPC)') + console.log('\t--path\t\t\tserver path \t"/"') + console.log('\t--p.[key]\t\tset params key \t"{}"') + console.log('\t--method\t*\texecuted method') + console.log('') + return +} + +/** Run client */ +var client = new JSONRPCClient(port, host, path) +client.call(method, params, function(error, result) { + if (error) { + console.error('ServerError:', error); + } + console.log('ServerResult:', result); +}); +/** End of CLI */ \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index dbda646..d313da1 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,7 +8,8 @@ module.exports = { target: 'node', mode: 'production', entry: { - mff: './index.js' + mff: './index.js', + cli: './cli/index.js' }, resolve: { extensions: [".ts", ".js"] From c74941c1d7136767f6c791582796aee676d2a770 Mon Sep 17 00:00:00 2001 From: AlexMcArrow Date: Wed, 3 Nov 2021 19:42:13 +0500 Subject: [PATCH 4/4] clear CLI: unused require --- cli/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/index.js b/cli/index.js index d1d3fbd..b697717 100644 --- a/cli/index.js +++ b/cli/index.js @@ -2,7 +2,6 @@ * MFF HTTP-JSON-RPC CLI-client for administrative operation */ -const { match } = require('assert'); var http = require('http') var JSONRPCClient = function(port, host, path = null) {