w-serv-hapi
v1.0.72
Published
A wrapper hapi for data control and synchronization between nodejs and browser.
Maintainers
Readme
w-serv-hapi
A wrapper hapi for data control and synchronization between nodejs and browser.
Documentation
To view documentation or get support, visit docs.
Parts
w-serv-hapi includes 2 parts:
w-serv-hapi-server: for nodejs serverw-serv-hapi-client: for nodejs and browser client
Installation
Using npm(ES6 module):
npm i w-serv-hapiExample for w-serv-hapi-server:
Link: [dev source code]
import fs from 'fs'
import _ from 'lodash-es'
// import WOrm from 'w-orm-mongodb/src/WOrmMongodb.mjs' //自行選擇引用ORM
import WOrm from 'w-orm-lowdb/src/WOrmLowdb.mjs' //自行選擇引用ORM
import WServHapiServer from './src/WServHapiServer.mjs'
async function run() {
//預先刪除w-orm-lowdb資料庫
try {
fs.unlinkSync('./db.json')
}
catch (err) {}
//optWOrm
let optWOrm = {
// url: 'mongodb://username:[email protected]:27017',
// db: 'servhapi',
url: './db.json',
db: 'servhapi',
cl: '',
}
//tableNamesExec, tableNamesSync
let tableNamesExec = ['tabA', 'tabB']
let tableNamesSync = ['tabA']
//kpOrm
let kpOrm = {}
for (let k in tableNamesExec) {
let v = tableNamesExec[k]
let opt = { ...optWOrm, cl: v }
let wo = new WOrm(opt)
kpOrm[v] = wo
}
async function saveData(cl, r) {
//w
let w = kpOrm[cl] //一定要由woItems操作, 否則傳woItems進去WServHapiServer會無法收到change事件
//save
await w.save(r) //autoInsert: false
.then(function(msg) {
console.log('save then', cl, msg)
})
.catch(function(msg) {
console.log('save catch', cl, msg)
})
}
let r
r = [
{
id: 'id-tabA-peter',
name: 'peter',
value: 123,
},
{
id: 'id-tabA-rosemary',
name: 'rosemary',
value: 123.456,
},
{
id: 'id-tabA-kettle',
name: 'kettle',
value: 456,
},
]
await saveData('tabA', r)
r = [
{
id: 'id-tabB-peter',
name: 'peter',
value: 123,
},
{
id: 'id-tabB-rosemary',
name: 'rosemary',
value: 123.456,
},
]
await saveData('tabB', r)
let ntv = 0
let genValue = () => {
ntv++
return `value-${ntv}`
}
let n = 0
let tn = setInterval(() => {
n++
console.log('update tabA', n)
r = {
id: 'id-tabA-peter',
name: 'peter',
value: genValue(),
}
saveData('tabA', r)
if (n >= 5) {
clearInterval(tn)
}
}, 3000)
let procCommon = async (userId, tableName, methodName, input) => {
// console.log('procCommon call', tableName, methodName, input)
let r = await kpOrm[tableName][methodName](input)
// console.log('procCommon result', r)
return r
}
let uploadFile = async (userId, { name, u8a }) => {
console.log('uploadFile', userId, name, _.size(u8a))
// fs.writeFileSync(name, Buffer.from(u8a))
console.log('uploadFile writeFileSync finish')
return 'finish'
}
let add = async (userId, { pa, pb }) => {
// console.log('add', userId, pa, pb)
return `result: pa+pb=${pa + pb}`
}
let ntg = 0
let genTag = () => {
ntg++
return `tag-${ntg}`
}
//WServHapiServer
let wsrv = new WServHapiServer({
port: 8080,
apis: [],
verifyConn: ({ apiType, authorization, headers, query }) => {
console.log('verifyConn', `apiType[${apiType}]`, `authorization[${authorization}]`)
return true
},
getUserIdByToken: async (token) => { //可使用async或sync函數
return 'id-for-admin'
},
useDbOrm: true,
kpOrm,
operOrm: procCommon, //procCommon的輸入為: userId, tableName, methodName, input
tableNamesExec,
methodsExec: ['select', 'insert', 'save', 'del'],
tableNamesSync,
kpFunExt: { //接收參數第1個為userId, 之後才是前端給予參數
uploadFile,
add,
//...
},
// fnTableTags: 'tableTags-serv-hapi.json',
genTag,
})
wsrv.on('error', (err) => {
console.log(err)
})
}
run()
// save then tabA [
// { n: 1, nInserted: 1, ok: 1 },
// { n: 1, nInserted: 1, ok: 1 },
// { n: 1, nInserted: 1, ok: 1 }
// ]
// save then tabB [ { n: 1, nInserted: 1, ok: 1 }, { n: 1, nInserted: 1, ok: 1 } ]
// Server running at: http://DESKTOP-6R7USAO:8080
// Server[port:8080]: now clients: 1
// uploadFile id-for-admin zdata.b1 3
// uploadFile writeFileSync finish
// update tabA 1
// save then tabA [ { n: 1, nModified: 1, ok: 1 } ]
// update tabA 2
// save then tabA [ { n: 1, nModified: 1, ok: 1 } ]
// update tabA 3
// save then tabA [ { n: 1, nModified: 1, ok: 1 } ]
// update tabA 4
// save then tabA [ { n: 1, nModified: 1, ok: 1 } ]
// update tabA 5
// save then tabA [ { n: 1, nModified: 1, ok: 1 } ]Example for w-serv-hapi-client:
Link: [dev source code]
// import fs from 'fs'
import FormData from 'form-data'
import WServHapiClient from './src/WServHapiClient.mjs'
async function client() {
//WServHapiClient
let instWServHapiClient = new WServHapiClient({
FormData,
url: 'http://localhost:8080',
useWaitToken: false,
getToken: () => {
return 'token-for-test' //Vue.prototype.$store.state.userToken
},
getServerMethods: (r) => {
console.log('getServerMethods', r)
//Vue.prototype.$fapi = r
//select tabA
r.tabA.select(({ prog, p, m }) => {
console.log('select tabA', prog, p, m)
})
.then((res) => {
console.log('r.tabA.select then', res)
})
.catch((err) => {
console.log('r.tabA.select catch', err)
})
//select tabB
r.tabB.select(({ prog, p, m }) => {
console.log('select tabB', prog, p, m)
})
.then((res) => {
console.log('r.tabB.select then', res)
})
.catch((err) => {
console.log('r.tabB.select catch', err)
})
//uploadFile
r.uploadFile({
name: 'zdata.b1',
u8a: new Uint8Array([66, 97, 115]),
}, ({ prog, p, m }) => {
console.log('uploadFile', prog, p, m)
})
//add
r.add({
pa: 1,
pb: 2.5,
}, ({ prog, p, m }) => {
console.log('add', prog, p, m)
})
.then((res) => {
console.log('add then', res)
})
.catch((err) => {
console.log('add catch', err)
})
},
recvData: (r) => {
console.log('recvData', r)
//Vue.prototype.$store.commit(Vue.prototype.$store.types.UpdateTableData, r)
},
getRefreshState: (r) => {
console.log('getRefreshState', 'needToRefresh', r.needToRefresh)
},
getRefreshTable: (r) => {
console.log('getRefreshTable', 'tableName', r.tableName, 'timeTag', r.timeTag)
},
getBeforeUpdateTableTags: (r) => {
console.log('getBeforeUpdateTableTags', 'needToRefresh', JSON.stringify(r.oldTableTags) !== JSON.stringify(r.newTableTags))
},
getAfterUpdateTableTags: (r) => {
console.log('getAfterUpdateTableTags', 'needToRefresh', JSON.stringify(r.oldTableTags) !== JSON.stringify(r.newTableTags))
},
})
instWServHapiClient.on('error', (err) => {
console.log(err)
})
}
client()
// getServerMethods {
// tabA: {
// select: [AsyncFunction: f],
// insert: [AsyncFunction: f],
// save: [AsyncFunction: f],
// del: [AsyncFunction: f]
// },
// tabB: {
// select: [AsyncFunction: f],
// insert: [AsyncFunction: f],
// save: [AsyncFunction: f],
// del: [AsyncFunction: f]
// },
// uploadFile: [AsyncFunction: f],
// add: [AsyncFunction: f]
// }
// select tabA 100 98 upload
// select tabB 100 98 upload
// add 100 107 upload
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-1
// select tabA 100 246 download
// r.tabA.select then [
// { id: 'id-tabA-peter', name: 'peter', value: 123 },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// add 100 93 download
// add then result: pa+pb=3.5
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-1',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 123 },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// select tabB 100 194 download
// r.tabB.select then [
// { id: 'id-tabB-peter', name: 'peter', value: 123 },
// { id: 'id-tabB-rosemary', name: 'rosemary', value: 123.456 }
// ]
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-2
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-2',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-1' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-3
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-3',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-2' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-4
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-4',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-3' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-5
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-5',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-4' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-6
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-6',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-5' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh falseIn a browser(UMD module):
Add script for w-serv-hapi-client.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/w-serv-hapi-client.umd.js"></script>Example for w-serv-hapi-client:
Link: [dev source code]
let $fapi = null
async function client() {
//WServHapiClient
let WServHapiClient = window['w-serv-hapi-client']
let wshc = new WServHapiClient({
// FormData,
url: 'http://localhost:8080',
useWaitToken: false,
getToken: () => {
return '' //Vue.prototype.$store.state.userToken
},
getServerMethods: (r) => {
console.log('getServerMethods', r)
//save
//Vue.prototype.$fapi = r
$fapi = r
//select tabA
r.tabA.select(({ prog, p, m }) => {
console.log('select tabA', prog, p, m)
})
.then((res) => {
console.log('r.tabA.select then', res)
})
.catch((err) => {
console.log('r.tabA.select catch', err)
})
//select tabB
r.tabB.select(({ prog, p, m }) => {
console.log('select tabB', prog, p, m)
})
.then((res) => {
console.log('r.tabB.select then', res)
})
.catch((err) => {
console.log('r.tabB.select catch', err)
})
//uploadFile
r.uploadFile({
name: 'zdata.b1',
u8a: new Uint8Array([66, 97, 115]),
// u8a: new Uint8Array(fs.readFileSync('../_data/500mb.7z')), //最多500mb, 因測試使用w-converhp, 其依賴新版@hapi/pez無法處理1g檔案, 會出現: Invalid string length
}, ({ prog, p, m }) => {
console.log('uploadFile', prog, p, m)
})
//add
r.add({
pa: 1,
pb: 2.5,
}, ({ prog, p, m }) => {
console.log('add', prog, p, m)
})
.then((res) => {
console.log('add then', res)
})
.catch((err) => {
console.log('add catch', err)
})
},
recvData: (r) => {
console.log('recvData', r)
//Vue.prototype.$store.commit(Vue.prototype.$store.types.UpdateTableData, r)
},
getRefreshState: (r) => {
console.log('getRefreshState', 'needToRefresh', r.needToRefresh)
},
getRefreshTable: (r) => {
console.log('getRefreshTable', 'tableName', r.tableName, 'timeTag', r.timeTag)
},
getBeforeUpdateTableTags: (r) => {
console.log('getBeforeUpdateTableTags', 'needToRefresh', JSON.stringify(r.oldTableTags) !== JSON.stringify(r.newTableTags))
},
getAfterUpdateTableTags: (r) => {
console.log('getAfterUpdateTableTags', 'needToRefresh', JSON.stringify(r.oldTableTags) !== JSON.stringify(r.newTableTags))
},
})
return wshc
}
client()
.catch((err) => {
console.log(err)
})
// getServerMethods {
// tabA: {
// select: [AsyncFunction: f],
// insert: [AsyncFunction: f],
// save: [AsyncFunction: f],
// del: [AsyncFunction: f]
// },
// tabB: {
// select: [AsyncFunction: f],
// insert: [AsyncFunction: f],
// save: [AsyncFunction: f],
// del: [AsyncFunction: f]
// },
// uploadFile: [AsyncFunction: f],
// add: [AsyncFunction: f]
// }
// select tabA 100 98 upload
// select tabB 100 98 upload
// add 100 107 upload
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-1
// select tabA 100 246 download
// r.tabA.select then [
// { id: 'id-tabA-peter', name: 'peter', value: 123 },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// add 100 93 download
// add then result: pa+pb=3.5
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-1',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 123 },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// select tabB 100 194 download
// r.tabB.select then [
// { id: 'id-tabB-peter', name: 'peter', value: 123 },
// { id: 'id-tabB-rosemary', name: 'rosemary', value: 123.456 }
// ]
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-2
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-2',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-1' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-3
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-3',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-2' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-4
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-4',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-3' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-5
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-5',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-4' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false
// getBeforeUpdateTableTags needToRefresh true
// getRefreshState needToRefresh true
// getRefreshTable tableName tabA timeTag tag-6
// recvData {
// tableName: 'tabA',
// timeTag: 'tag-6',
// data: [
// { id: 'id-tabA-peter', name: 'peter', value: 'value-5' },
// { id: 'id-tabA-rosemary', name: 'rosemary', value: 123.456 },
// { id: 'id-tabA-kettle', name: 'kettle', value: 456 }
// ]
// }
// getAfterUpdateTableTags needToRefresh false