diff --git a/.gitignore b/.gitignore index 5f6c423e..ae19d5bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ dist node_modules -tmp \ No newline at end of file +tmp +tests +db/ \ No newline at end of file diff --git a/bin/authenticators/http.js b/bin/authenticators/http.js new file mode 100644 index 00000000..2f0bdd8e --- /dev/null +++ b/bin/authenticators/http.js @@ -0,0 +1,53 @@ +const authCallback = process.env.YWEBSOCKET_HTTP_AUTH_CALLBACK || 'http://localhost/auth/' +const authCallbackParam = process.env.YWEBSOCKET_HTTP_AUTH_CALLBACK_GET_PARAM || 'code' +const querystring = require('querystring') + +module.exports = { + authenticate: function (request) { + return new Promise(function (resolve, reject) { + const docName = request.url.substring(1) + const query = querystring.stringify({ + [authCallbackParam]: docName + }) + let authRequester + if (authCallback.indexOf('https:') === 0) { + authRequester = require('https') + } else { + authRequester = require('http') + } + const authCallbackWithRoomCode = authCallback + '?' + query + console.log(authCallbackWithRoomCode) + const authRequest = authRequester.request( + authCallbackWithRoomCode, + { + method: 'GET', + headers: { + Cookie: request.headers.cookie || '' + } + }, + response => { + if (response.statusCode < 200 || response.statusCode >= 300) { + return reject(new Error('statusCode=' + response.statusCode)) + } + response.setEncoding('utf8') + let rawData = '' + response.on('data', chunk => { + console.log(chunk) + rawData += chunk + }) + response.on('end', () => { + const data = JSON.parse(rawData) + console.log(rawData) + if (data.status === 'ok') { + resolve(true) + } + }) + } + ) + authRequest.on('error', function (error) { + reject(error) + }) + authRequest.end() + }) + } +} diff --git a/bin/collaborate-editing-server.js b/bin/collaborate-editing-server.js index cbddd159..292a677f 100644 --- a/bin/collaborate-editing-server.js +++ b/bin/collaborate-editing-server.js @@ -8,6 +8,7 @@ const setupWSConnection = require('./utils.js').setupWSConnection const host = process.env.YWEBSOCKET_HOST || 'localhost' const port = process.env.YWEBSOCKET_PORT || 1234 +const authenticator = process.env.YWEBSOCKET_AUTHENTICATOR || null const server = http.createServer((request, response) => { response.writeHead(200, { 'Content-Type': 'application/json' }) @@ -22,7 +23,14 @@ server.on('upgrade', (request, socket, head) => { * @param {any} ws */ const handleAuth = ws => { - wss.emit('connection', ws, request) + if (authenticator) { + const authenticate = require('./authenticators/' + authenticator).authenticate + authenticate(request).then(() => { + wss.emit('connection', ws, request) + }) + } else { + wss.emit('connection', ws, request) + } } wss.handleUpgrade(request, socket, head, handleAuth) }) diff --git a/bin/connections/mysql.js b/bin/connections/mysql.js new file mode 100644 index 00000000..db8f92e1 --- /dev/null +++ b/bin/connections/mysql.js @@ -0,0 +1,23 @@ +const mysql = require('mysql2') + +const MYSQL_HOST = process.env.MYSQL_HOST || 'localhost' +const MYSQL_PORT = process.env.MYSQL_PORT || 3306 +const MYSQL_USER = process.env.MYSQL_USER || 'root' +const MYSQL_PASSWORD = process.env.MYSQL_PASSWORD || '' +const MYSQL_DATABASE = process.env.MYSQL_DATABASE || 'database' + +const mysqlConnection = mysql.createConnection({ + host: MYSQL_HOST, + user: MYSQL_USER, + database: MYSQL_DATABASE, + password: MYSQL_PASSWORD, + port: MYSQL_PORT +}) + +mysqlConnection.connect(function (error) { + if (error) throw error +}) + +module.exports = { + mysqlConnection: mysqlConnection +} diff --git a/bin/note-synchronizer-server.js b/bin/note-synchronizer-server.js index 2a9e1609..ae06cf76 100644 --- a/bin/note-synchronizer-server.js +++ b/bin/note-synchronizer-server.js @@ -1,5 +1,3 @@ -const redisPersistenceBridge = require('./redis-persistence-bridge') - const http = require('http') const host = process.env.YPERSISTENCE_SYNCHRONIZER_HOST || 'localhost' @@ -16,7 +14,7 @@ const server = http.createServer((request, response) => { request.on('end', function () { const post = JSON.parse(body) for (const sharedObject of post.sharedObjects) { - redisPersistenceBridge.saveQuillDeltaToRedis(post.documentName, sharedObject.name, sharedObject.delta) + // redisPersistenceBridge.saveQuillDeltaToRedis(post.documentName, sharedObject.name, sharedObject.delta) } }) } diff --git a/bin/transfer-data-from-mysql-to-redis.js b/bin/transfer-data-from-mysql-to-redis.js index 283d3ddf..aac50b97 100644 --- a/bin/transfer-data-from-mysql-to-redis.js +++ b/bin/transfer-data-from-mysql-to-redis.js @@ -1,29 +1,11 @@ #!/usr/bin/env node const redisPersistenceBridge = require('./redis-persistence-bridge') +const mysqlConnection = require('../connections/mysql').mysqlConnection -const mysql = require('mysql2') - -const MYSQL_HOST = process.env.MYSQL_HOST || 'localhost' -const MYSQL_USER = process.env.MYSQL_USER || 'root' -const MYSQL_PASSWORD = process.env.MYSQL_PASSWORD || '' -const MYSQL_DATABASE = process.env.MYSQL_DATABASE || 'database' const MYSQL_TABLE = process.env.MYSQL_TABLE || 'table' const MYSQL_CONTENT_FIELDS = process.env.MYSQL_CONTENT_FIELDS || 'content' const MYSQL_KEY_FIELD = process.env.MYSQL_KEY_FIELD || 'id' -const MYSQL_PORT = process.env.MYSQL_PORT || 3306 - -const mysqlConnection = mysql.createConnection({ - host: MYSQL_HOST, - user: MYSQL_USER, - database: MYSQL_DATABASE, - password: MYSQL_PASSWORD, - port: MYSQL_PORT -}) - -mysqlConnection.connect(function (error) { - if (error) throw error -}) mysqlConnection.query( `SELECT ${MYSQL_KEY_FIELD}, ${MYSQL_CONTENT_FIELDS} FROM ${MYSQL_TABLE}`, diff --git a/bin/utils.js b/bin/utils.js index 411b62f4..3f19b1ee 100644 --- a/bin/utils.js +++ b/bin/utils.js @@ -22,36 +22,25 @@ const wsReadyStateClosed = 3 // eslint-disable-line // disable gc when using snapshots! const gcEnabled = process.env.GC !== 'false' && process.env.GC !== '0' -const persistenceStorage = process.env.YPERSISTENCE_STORAGE || 'level-db' -const persistenceHost = process.env.YPERSISTENCE_REDIS_HOST || 'localhost' -const persistencePort = process.env.YPERSISTENCE_REDIS_PORT || 6379 -const persistencePath = process.env.YPERSISTENCE_LEVELDB_PATH || 'db' - +const persistenceDir = process.env.YPERSISTENCE_LEVELDB_PATH /** * @type {{bindState: function(string,WSSharedDoc):void, writeState:function(string,WSSharedDoc):Promise, provider: any}|null} */ let persistence = null -if (typeof persistencePath === 'string') { +if (typeof persistenceDir === 'string') { + console.info('Persisting documents to "' + persistenceDir + '"') // @ts-ignore - let persistenceDB - if (persistenceStorage === 'redis') { - console.info('Persisting documents to "' + persistenceHost + '"') - const RedisPersistence = require('y-redis').RedisPersistence - persistenceDB = new RedisPersistence({ redisOpts: { host: persistenceHost, port: persistencePort } }) - } else { - console.info('Persisting documents to "' + persistencePath + '"') - const LeveldbPersistence = require('y-leveldb').LeveldbPersistence - persistenceDB = new LeveldbPersistence(persistencePath) - } + const LeveldbPersistence = require('y-leveldb').LeveldbPersistence + const ldb = new LeveldbPersistence(persistenceDir) persistence = { - provider: persistenceDB, + provider: ldb, bindState: async (docName, ydoc) => { - const persistedYdoc = await persistenceDB.getYDoc(docName) + const persistedYdoc = await ldb.getYDoc(docName) const newUpdates = Y.encodeStateAsUpdate(ydoc) - persistenceDB.storeUpdate(docName, newUpdates) + ldb.storeUpdate(docName, newUpdates) Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc)) ydoc.on('update', update => { - persistenceDB.storeUpdate(docName, update) + ldb.storeUpdate(docName, update) }) }, writeState: async (docName, ydoc) => {}