UNPKG

w-comor-socketio

Version:

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

359 lines (287 loc) 10.2 kB
import Hapi from '@hapi/hapi' import Inert from '@hapi/inert' //提供靜態檔案 import { Server } from 'socket.io' import keys from 'lodash-es/keys.js' import get from 'lodash-es/get.js' import genPm from 'wsemi/src/genPm.mjs' import haskey from 'wsemi/src/haskey.mjs' import j2o from 'wsemi/src/j2o.mjs' import isfun from 'wsemi/src/isfun.mjs' import ispint from 'wsemi/src/ispint.mjs' import isestr from 'wsemi/src/isestr.mjs' import cint from 'wsemi/src/cint.mjs' import arrHas from 'wsemi/src/arrHas.mjs' let SocketIO = Server /** * 建立socket.io伺服器 * * @param {Object} opt 輸入設定參數物件 * @param {Object} [opt.serverHapi={}] 輸入hapi伺服器物件,若提供,本服務將自動加入api至route。使用外部hapi伺服器時,需開啟跨域功能,或是使用nginx反向代理轉入api請求 * @param {Integer} [opt.port=8080] 輸入hapi與socket.io伺服器所在port,為hapi與socket.io共用,預設8080 * @param {String} [opt.pathPolling=undefined] 輸入socket.io伺服器無法使用WebSocket連線自動降級成為輪詢(polling)所在子目錄字串,預設undefined,代表使用'/socket.io' * @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 WComorSocketioServer from 'w-comor-socketio/dist/w-comor-socketio-server.umd.js' * * function random(min, max) { * return Math.floor(Math.random() * max) + min * } * * let opt = { * port: 8080, * 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') < 0 * }) * resolve(funcs) * }) * }, * onClientChange: function(clients, opt) { * console.log(`Server[port:${opt.port}] now clients: ${clients.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)) * }) * }, * }, * } * * new WComorSocketioServer(opt) * */ function WComorSocketioServer(opt) { //port let port = get(opt, 'port') if (!ispint(port)) { port = 8080 } port = cint(port) //pathPolling let pathPolling = get(opt, 'pathPolling') //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 } //server let server if (opt.serverHapi) { //use serverHapi server = opt.serverHapi } else { //create server server = Hapi.server({ port: opt.port, //host: 'localhost', routes: { cors: true }, }) } //io let io = null let ioOpt = {} if (isestr(pathPolling)) { ioOpt = { path: pathPolling } } try { io = new SocketIO(server.listener, ioOpt) } catch (err) { console.log(`create SocketIO catch:`, err) return null } //connect let clients = [] io.on('connect', function(client) { //console.log('connect', client.id) //push clients.push(client.id) if (isfun(opt.onClientChange)) { opt.onClientChange(clients, opt) } //execFunction async function execFunction(data) { //console.log('execFunction', client.id, 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 } } //call else if (arrHas(funcs, func)) { //call func in opt.funcs let output = await opt['funcs'][func](input) //add output data['output'] = output } else { //add output data['output'] = { err: `can not find: ${func}` } } } else { //add output data['output'] = { err: `can not authenticate token: ${token}` } } //delete input, 因input可能很大故回傳數據不包含原input delete data['input'] //send try { client.send(JSON.stringify(data), function(err) { if (err) { console.log(`Server: send output error:`, err) } }) } catch (err) { console.log(`Server: send output catch:`, err) } } //message client.on('message', function(msg) { //console.log('message', client.id, msg) //data let data = j2o(msg) //execFunction execFunction(data) }) //disconnect, close client.on('disconnect', function(msg) { //console.log('disconnect', client.id, msg) //刪除client clients = clients.filter(function(id) { return id !== client.id }) if (isfun(opt.onClientChange)) { opt.onClientChange(clients, opt) } }) }) async function createServer() { if (!opt.serverHapi) { //register inert await server.register(Inert) //api let api = [ // { // method: 'GET', // path: '/{file*}', // handler: { // directory: { // path: './' // }, // }, // }, //預設僅提供測試之3檔案 { method: 'GET', path: '/web.html', handler: { file: { path: './web.html' }, }, }, { method: 'GET', path: '/dist/w-comor-socketio-client.umd.js', handler: { file: { path: './dist/w-comor-socketio-client.umd.js' }, }, }, { method: 'GET', path: '/dist/w-comor-socketio-client.umd.js.map', handler: { file: { path: './dist/w-comor-socketio-client.umd.js.map' }, }, }, ] //route server.route(api) } //start await server.start() console.log(`Server running at: ${server.info.uri}`) } createServer() } export default WComorSocketioServer