UNPKG

w-comor-hapi

Version:

A http communicator in nodejs and browser. Mapping functions from nodejs to other end points, like a simple RPC.

409 lines (318 loc) 11.3 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>WComorHapiServer.mjs - Documentation</title> <script src="scripts/prettify/prettify.js"></script> <script src="scripts/prettify/lang-css.js"></script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc.css"> <script src="scripts/nav.js" defer></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <input type="checkbox" id="nav-trigger" class="nav-trigger" /> <label for="nav-trigger" class="navicon-button x"> <div class="navicon"></div> </label> <label for="nav-trigger" class="overlay"></label> <nav > <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WComorHapiClient">WComorHapiClient</a></li><li><a href="global.html#WComorHapiServer">WComorHapiServer</a></li></ul> </nav> <div id="main"> <h1 class="page-title">WComorHapiServer.mjs</h1> <section> <article> <pre class="prettyprint source linenums"><code>import Hapi from '@hapi/hapi' //import Inert from '@hapi/inert' //提供靜態檔案 import cloneDeep from 'lodash-es/cloneDeep.js' import keys from 'lodash-es/keys.js' import get from 'lodash-es/get.js' import uniqBy from 'lodash-es/uniqBy.js' import isEqual from 'lodash-es/isEqual.js' import each from 'lodash-es/each.js' import genPm from 'wsemi/src/genPm.mjs' import genID from 'wsemi/src/genID.mjs' import haskey from 'wsemi/src/haskey.mjs' import isfun from 'wsemi/src/isfun.mjs' import arrHas from 'wsemi/src/arrHas.mjs' /** * 建立http API伺服器 * * @param {Object} opt 輸入設定參數物件 * @param {Object} [opt.serverHapi={}] 輸入hapi伺服器物件,若提供,本服務將自動加入api至route。使用外部hapi伺服器時,需開啟跨域功能,或是使用nginx反向代理轉入api請求 * @param {Integer} [opt.port=8080] 輸入http API伺服器所在port,預設8080 * @param {String} [opt.apiName='api'] 輸入http API伺服器網址的api名稱,預設'api' * @param {Function} opt.authenticate 輸入使用者身份認證函數,供伺服器端驗證之用,函數會傳入使用者端連線之token參數,回傳為Promise,resolve(true)為驗證通過,resolve(false)為驗證不通過 * @param {Object} [opt.funcs={}] 輸入伺服器端供使用者端呼叫之函數物件,各key為函數名稱,對應value為函數本體。各函數之輸入需為單一物件,而各函數回傳皆為Promise,可通過resolve與reject回傳結果,預設{} * @param {Array} [opt.routes=[]] 輸入伺服器額外掛載routes陣列,預設[] * @example * * import WComorHapiServer from 'w-comor-hapi/dist/w-comor-hapi-server.umd.js' * * function random(min, max) { * return Math.floor(Math.random() * max) + min * } * * let opt = { * port: 8080, * apiName: 'api', * authenticate: function(token) { * //使用token驗證使用者身份 * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(true) * }, 1000) * }) * }, * filterFuncs: function(token, funcs) { * //使用token驗證使用者身份與過濾可用funcs * return new Promise(function(resolve, reject) { * funcs = funcs.filter(function(v) { * return v.indexOf('Hide') &lt; 0 * }) * resolve(funcs) * }) * }, * onClientChange: function(conns, opt) { * console.log(`Server[port:${opt.port}] now conns: ${conns.length}`) * }, * funcs: { * 'group.plus': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 * p2) * }, random(100, 3000)) * }) * }, * 'group.div': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 / p2) * }, random(100, 3000)) * }) * }, * 'add': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 + p2) * }, random(100, 3000)) * }) * }, * 'addHide': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 + p2) * }, random(100, 3000)) * }) * }, * 'minu': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 - p2) * }, random(100, 3000)) * }) * }, * }, * routes: [ * { * method: 'GET', * path: '/code', * handler: function (req, res) { * //console.log(req) * return 'get code query: ' + JSON.stringify(req.query) * //http://localhost:8080/code?a=1&amp;bb=23.45 * // => get code query: {"a":"1","bb":"23.45"} * } * }, * ], * } * * new WComorHapiServer(opt) * */ function WComorHapiServer(opt) { let conns = [] let clients = [] //cloneDeep opt = cloneDeep(opt) //default if (!opt.port) { opt.port = 8080 } if (!opt.apiName) { opt.apiName = 'api' } if (!opt.routes) { opt.routes = [] } //funcs let funcs = [] if (haskey(opt, 'funcs')) { funcs = keys(opt['funcs']) } //authenticate function authenticate(token) { let pm = genPm() if (isfun(opt.authenticate)) { opt.authenticate(token) .then(function(vd) { pm.resolve(vd) }) } else { pm.resolve(true) } return pm } //execFunction async function execFunction(data) { //console.log(`Server[port:${opt.port}][api:${opt.apiName}]: `, data) //token let token = get(data, 'token', '') //vd let vd = await authenticate(token) //check if (vd) { //func let func = get(data, 'func', '') //input let input = get(data, 'input') //getFuncs if (func === 'getFuncs') { if (isfun(opt.filterFuncs)) { funcs = await opt.filterFuncs(token, funcs) } //add output data['output'] = { sys: 'sys', funcs: funcs } } //call else if (arrHas(funcs, func)) { //call func in opt.funcs let output = await opt['funcs'][func](input) //add output data['output'] = output } else { //return no func //add output data['output'] = { err: `can not find: ${func}` } } } else { //return no authenticate //add output data['output'] = { err: `can not authenticate token: ${token}` } } //delete input, 因input可能很大故回傳數據不包含原input delete data['input'] //return data return JSON.stringify(data) } //changeConns function changeConns(conns) { if (isfun(opt.onClientChange)) { let clients_new = uniqBy(conns, 'clientId') if (!isEqual(clients, clients_new)) { clients = clients_new opt.onClientChange(clients, opt) } } } //api let api = { path: '/' + opt.apiName, method: 'POST', options: { payload: { maxBytes: 1000 * 1024 * 1024, //1g timeout: 3 * 60 * 1000, //3分鐘, 注意payload timeout必須小於socket timeout multipart: true, //hapi 19之後修改multipart預設值為false }, timeout: { socket: 5 * 60 * 1000, //5分鐘 }, }, handler: function (req, res) { let pm = genPm() //connId let connId = genID() //clientId let clientId = get(req.payload, 'clientId') //client let client = { headers: req.headers, info: req.info, } //push conns.push({ clientId: clientId, connId: connId, data: client, }) changeConns(conns) //data let data = req.payload //execFunction execFunction(data) .then(function(r) { pm.resolve(r) }) .catch(function(r) { pm.reject(r) }) .finally(function() { //remove conns = conns.filter(function(c) { return c.connId !== connId }) changeConns(conns) }) return pm }, } async function startServer() { //server let server = Hapi.server({ port: opt.port, //host: 'localhost', routes: { cors: true }, }) //register inert //await server.register(Inert) //route api server.route(api) //add routes each(opt.routes, function(v) { server.route(v) }) //start await server.start() console.log(`Server running at: ${server.info.uri}`) } if (opt.serverHapi) { opt.serverHapi.route(api) } else { startServer() } } export default WComorHapiServer </code></pre> </article> </section> </div> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on Sun Mar 24 2024 22:16:24 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme. </footer> <script>prettyPrint();</script> <script src="scripts/polyfill.js"></script> <script src="scripts/linenumber.js"></script> </body> </html>