Merge branch 'Mansehej-master'

This commit is contained in:
Kevin Jahns 2020-08-08 18:45:32 +02:00
commit fcb9b10d73
5 changed files with 114 additions and 0 deletions

View File

@ -40,6 +40,23 @@ See [LevelDB Persistence](https://github.com/yjs/y-leveldb) for more info.
PORT=1234 YPERSISTENCE=./dbDir node ./node_modules/y-websocket/bin/server.js PORT=1234 YPERSISTENCE=./dbDir node ./node_modules/y-websocket/bin/server.js
``` ```
**Websocket Server with HTTP callback**
Send a debounced callback to an HTTP server (`POST`) on document update.
Can take the following ENV variables:
* `CALLBACK_URL` : Callback server URL
* `CALLBACK_DEBOUNCE_WAIT` : Debounce time between callbacks (in ms). Defaults to 2000 ms
* `CALLBACK_DEBOUNCE_MAXWAIT` : Maximum time to wait before callback. Defaults to 10 seconds
* `CALLBACK_TIMEOUT` : Timeout for the HTTP call. Defaults to 5 seconds
* `CALLBACK_OBJECTS` : JSON of shared objects to get data (`'{"SHARED_OBJECT_NAME":"SHARED_OBJECT_TYPE}'`)
```sh
CALLBACK_URL=http://localhost:3000/ CALLBACK_OBJECTS='{"prosemirror":"XmlFragment"}' npm start
```
This sends a debounced callback to `localhost:3000` 2 seconds after receiving an update (default `DEBOUNCE_WAIT`) with the data of an XmlFragment named `"prosemirror"` in the body.
### Scaling ### Scaling
These are mere suggestions how you could scale your server environment. These are mere suggestions how you could scale your server environment.

76
bin/callback.js Normal file
View File

@ -0,0 +1,76 @@
const http = require('http')
const CALLBACK_URL = process.env.CALLBACK_URL ? new URL(process.env.CALLBACK_URL) : null
const CALLBACK_TIMEOUT = process.env.CALLBACK_TIMEOUT || 5000
const CALLBACK_OBJECTS = process.env.CALLBACK_OBJECTS ? JSON.parse(process.env.CALLBACK_OBJECTS) : {}
exports.isCallbackSet = !!CALLBACK_URL
/**
* @param {Uint8Array} update
* @param {any} origin
* @param {WSSharedDoc} doc
*/
exports.callbackHandler = (update, origin, doc) => {
const room = doc.name
const dataToSend = {
room: room,
data: {}
}
const sharedObjectList = Object.keys(CALLBACK_OBJECTS)
sharedObjectList.forEach(sharedObjectName => {
const sharedObjectType = CALLBACK_OBJECTS[sharedObjectName]
dataToSend.data[sharedObjectName] = {
type: sharedObjectType,
content: getContent(sharedObjectName, sharedObjectType, doc).toJSON()
}
})
callbackRequest(CALLBACK_URL, CALLBACK_TIMEOUT, dataToSend)
}
/**
* @param {URL} url
* @param {number} timeout
* @param {Object} data
*/
const callbackRequest = (url, timeout, data) => {
data = JSON.stringify(data)
const options = {
hostname: url.hostname,
port: url.port,
path: url.pathname,
timeout: timeout,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
var req = http.request(options)
req.on('timeout', () => {
console.warn('Callback request timed out.')
req.abort()
})
req.on('error', (e) => {
console.error('Callback request error.', e)
req.abort()
})
req.write(data)
req.end()
}
/**
* @param {string} objName
* @param {string} objType
* @param {WSSharedDoc} doc
*/
const getContent = (objName, objType, doc) => {
switch (objType) {
case 'Array': return doc.getArray(objName)
case 'Map': return doc.getMap(objName)
case 'Text': return doc.getText(objName)
case 'XmlFragment': return doc.getXmlFragment(objName)
case 'XmlElement': return doc.getXmlElement(objName)
default : return {}
}
}

View File

@ -7,6 +7,14 @@ const decoding = require('lib0/dist/decoding.cjs')
const mutex = require('lib0/dist/mutex.cjs') const mutex = require('lib0/dist/mutex.cjs')
const map = require('lib0/dist/map.cjs') const map = require('lib0/dist/map.cjs')
const debounce = require('lodash.debounce')
const callbackHandler = require('./callback.js').callbackHandler
const isCallbackSet = require('./callback.js').isCallbackSet
const CALLBACK_DEBOUNCE_WAIT = process.env.CALLBACK_DEBOUNCE_WAIT || 2000
const CALLBACK_DEBOUNCE_MAXWAIT = process.env.CALLBACK_DEBOUNCE_MAXWAIT || 10000
const wsReadyStateConnecting = 0 const wsReadyStateConnecting = 0
const wsReadyStateOpen = 1 const wsReadyStateOpen = 1
const wsReadyStateClosing = 2 // eslint-disable-line const wsReadyStateClosing = 2 // eslint-disable-line
@ -110,6 +118,13 @@ class WSSharedDoc extends Y.Doc {
} }
this.awareness.on('update', awarenessChangeHandler) this.awareness.on('update', awarenessChangeHandler)
this.on('update', updateHandler) this.on('update', updateHandler)
if (isCallbackSet) {
this.on('update', debounce(
callbackHandler,
CALLBACK_DEBOUNCE_WAIT,
{ maxWait: CALLBACK_DEBOUNCE_MAXWAIT }
))
}
} }
} }

5
package-lock.json generated
View File

@ -1192,6 +1192,11 @@
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true "dev": true
}, },
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"loose-envify": { "loose-envify": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",

View File

@ -47,6 +47,7 @@
"dependencies": { "dependencies": {
"lib0": "^0.2.31", "lib0": "^0.2.31",
"y-leveldb": "^0.1.0", "y-leveldb": "^0.1.0",
"lodash.debounce": "^4.0.8",
"y-protocols": "^1.0.0" "y-protocols": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {