diff --git a/s_whiteboard.js b/scripts/s_whiteboard.js similarity index 100% rename from s_whiteboard.js rename to scripts/s_whiteboard.js diff --git a/scripts/server-backend.js b/scripts/server-backend.js index c112bcf..0bd87b0 100644 --- a/scripts/server-backend.js +++ b/scripts/server-backend.js @@ -1,293 +1,277 @@ -var PORT = 8080; //Set port for the app -var accessToken = ""; //Can be set here or as start parameter (node server.js --accesstoken=MYTOKEN) -var disableSmallestScreen = false; //Can be set to true if you dont want to show (node server.js --disablesmallestscreen=true) -var webdav = false; //Can be set to true if you want to allow webdav save (node server.js --webdav=true) +const { getArgs } = require("./utils"); +const path = require("path"); -var fs = require("fs-extra"); -var express = require('express'); -var formidable = require('formidable'); //form upload processing - -const createDOMPurify = require('dompurify'); //Prevent xss -const { JSDOM } = require('jsdom'); -const window = (new JSDOM('')).window; -const DOMPurify = createDOMPurify(window); - -const { createClient } = require("webdav"); - -var s_whiteboard = require("./s_whiteboard.js"); - -var app = express(); -app.use(express.static(__dirname + '/public')); -var server = require('http').Server(app); -server.listen(PORT); -var io = require('socket.io')(server); -console.log("Webserver & socketserver running on port:" + PORT); - -if (process.env.accesstoken) { - accessToken = process.env.accesstoken; -} -if (process.env.disablesmallestscreen) { - disablesmallestscreen = true; -} -if (process.env.webdav) { - webdav = true; -} - -var startArgs = getArgs(); -if (startArgs["accesstoken"]) { - accessToken = startArgs["accesstoken"]; -} -if (startArgs["disablesmallestscreen"]) { - disableSmallestScreen = true; -} -if (startArgs["webdav"]) { - webdav = true; -} - -if (accessToken !== "") { - console.log("AccessToken set to: " + accessToken); -} -if (disableSmallestScreen) { - console.log("Disabled showing smallest screen resolution!"); -} -if (webdav) { - console.log("Webdav save is enabled!"); -} - -app.get('/loadwhiteboard', function (req, res) { - var wid = req["query"]["wid"]; - var at = req["query"]["at"]; //accesstoken - if (accessToken === "" || accessToken == at) { - var ret = s_whiteboard.loadStoredData(wid); - res.send(ret); - res.end(); - } else { - res.status(401); //Unauthorized - res.end(); +function startBackendServer(port) { + var accessToken = ""; //Can be set here or as start parameter (node server.js --accesstoken=MYTOKEN) + var disableSmallestScreen = false; //Can be set to true if you dont want to show (node server.js --disablesmallestscreen=true) + var webdav = false; //Can be set to true if you want to allow webdav save (node server.js --webdav=true) + + var fs = require("fs-extra"); + var express = require('express'); + var formidable = require('formidable'); //form upload processing + + const createDOMPurify = require('dompurify'); //Prevent xss + const { JSDOM } = require('jsdom'); + const window = (new JSDOM('')).window; + const DOMPurify = createDOMPurify(window); + + const { createClient } = require("webdav"); + + var s_whiteboard = require("./s_whiteboard.js"); + + var app = express(); + app.use(express.static(path.join(__dirname, '..', 'public'))); + var server = require('http').Server(app); + server.listen(port); + var io = require('socket.io')(server, {path: "/ws-api", }); + console.log("Webserver & socketserver running on port:" + port); + if (process.env.accesstoken) { + accessToken = process.env.accesstoken; } -}); - -app.post('/upload', function (req, res) { //File upload - var form = new formidable.IncomingForm(); //Receive form - var formData = { - files: {}, - fields: {} + if (process.env.disablesmallestscreen) { + disablesmallestscreen = true; } - - form.on('file', function (name, file) { - formData["files"][file.name] = file; - }); - - form.on('field', function (name, value) { - formData["fields"][name] = value; - }); - - form.on('error', function (err) { - console.log('File uplaod Error!'); - }); - - form.on('end', function () { - if (accessToken === "" || accessToken == formData["fields"]["at"]) { - progressUploadFormData(formData, function (err) { - if (err) { - if (err == "403") { - res.status(403); - } else { - res.status(500); - } - res.end(); - } else { - res.send("done"); - } - }); + if (process.env.webdav) { + webdav = true; + } + + var startArgs = getArgs(); + if (startArgs["accesstoken"]) { + accessToken = startArgs["accesstoken"]; + } + if (startArgs["disablesmallestscreen"]) { + disableSmallestScreen = true; + } + if (startArgs["webdav"]) { + webdav = true; + } + + if (accessToken !== "") { + console.log("AccessToken set to: " + accessToken); + } + if (disableSmallestScreen) { + console.log("Disabled showing smallest screen resolution!"); + } + if (webdav) { + console.log("Webdav save is enabled!"); + } + + app.get('/api/loadwhiteboard', function (req, res) { + var wid = req["query"]["wid"]; + var at = req["query"]["at"]; //accesstoken + if (accessToken === "" || accessToken == at) { + var ret = s_whiteboard.loadStoredData(wid); + res.send(ret); + res.end(); } else { res.status(401); //Unauthorized res.end(); } - //End file upload }); - form.parse(req); -}); - -function progressUploadFormData(formData, callback) { - console.log("Progress new Form Data"); - var fields = escapeAllContentStrings(formData.fields); - var files = formData.files; - var whiteboardId = fields["whiteboardId"]; - - var name = fields["name"] || ""; - var date = fields["date"] || (+new Date()); - var filename = whiteboardId + "_" + date + ".png"; - var webdavaccess = fields["webdavaccess"] || false; - try { - webdavaccess = JSON.parse(webdavaccess); - } catch (e) { - webdavaccess = false; - } - fs.ensureDir("./public/uploads", function (err) { - if (err) { - console.log("Could not create upload folder!", err); - return; + + app.post('/api/upload', function (req, res) { //File upload + var form = new formidable.IncomingForm(); //Receive form + var formData = { + files: {}, + fields: {} } - var imagedata = fields["imagedata"]; - if (imagedata && imagedata != "") { //Save from base64 data - imagedata = imagedata.replace(/^data:image\/png;base64,/, "").replace(/^data:image\/jpeg;base64,/, ""); - console.log(filename, "uploaded"); - fs.writeFile('./public/uploads/' + filename, imagedata, 'base64', function (err) { - if (err) { - console.log("error", err); - callback(err); - } else { - if (webdavaccess) { //Save image to webdav - if (webdav) { - saveImageToWebdav('./public/uploads/' + filename, filename, webdavaccess, function (err) { - if (err) { - console.log("error", err); - callback(err); - } else { - callback(); - } - }) + + form.on('file', function (name, file) { + formData["files"][file.name] = file; + }); + + form.on('field', function (name, value) { + formData["fields"][name] = value; + }); + + form.on('error', function (err) { + console.log('File uplaod Error!'); + }); + + form.on('end', function () { + if (accessToken === "" || accessToken == formData["fields"]["at"]) { + progressUploadFormData(formData, function (err) { + if (err) { + if (err == "403") { + res.status(403); } else { - callback("Webdav is not enabled on the server!"); + res.status(500); } + res.end(); } else { - callback(); + res.send("done"); } + }); + } else { + res.status(401); //Unauthorized + res.end(); + } + //End file upload + }); + form.parse(req); + }); + + function progressUploadFormData(formData, callback) { + console.log("Progress new Form Data"); + var fields = escapeAllContentStrings(formData.fields); + var files = formData.files; + var whiteboardId = fields["whiteboardId"]; + + var name = fields["name"] || ""; + var date = fields["date"] || (+new Date()); + var filename = whiteboardId + "_" + date + ".png"; + var webdavaccess = fields["webdavaccess"] || false; + try { + webdavaccess = JSON.parse(webdavaccess); + } catch (e) { + webdavaccess = false; + } + fs.ensureDir("./public/uploads", function (err) { + if (err) { + console.log("Could not create upload folder!", err); + return; + } + var imagedata = fields["imagedata"]; + if (imagedata && imagedata != "") { //Save from base64 data + imagedata = imagedata.replace(/^data:image\/png;base64,/, "").replace(/^data:image\/jpeg;base64,/, ""); + console.log(filename, "uploaded"); + fs.writeFile('./public/uploads/' + filename, imagedata, 'base64', function (err) { + if (err) { + console.log("error", err); + callback(err); + } else { + if (webdavaccess) { //Save image to webdav + if (webdav) { + saveImageToWebdav('./public/uploads/' + filename, filename, webdavaccess, function (err) { + if (err) { + console.log("error", err); + callback(err); + } else { + callback(); + } + }) + } else { + callback("Webdav is not enabled on the server!"); + } + } else { + callback(); + } + } + }); + } else { + callback("no imagedata!"); + console.log("No image Data found for this upload!", name); + } + }); + } + + function saveImageToWebdav(imagepath, filename, webdavaccess, callback) { + if (webdavaccess) { + var webdavserver = webdavaccess["webdavserver"] || ""; + var webdavpath = webdavaccess["webdavpath"] || "/"; + var webdavusername = webdavaccess["webdavusername"] || ""; + var webdavpassword = webdavaccess["webdavpassword"] || ""; + + const client = createClient( + webdavserver, + { + username: webdavusername, + password: webdavpassword } + ) + client.getDirectoryContents(webdavpath).then((items) => { + var cloudpath = webdavpath+ '' + filename; + console.log("webdav saving to:", cloudpath); + fs.createReadStream(imagepath).pipe(client.createWriteStream(cloudpath)); + callback(); + }).catch((error) => { + callback("403"); + console.log("Could not connect to webdav!") }); } else { - callback("no imagedata!"); - console.log("No image Data found for this upload!", name); + callback("Error: no access data!") } - }); -} - -function saveImageToWebdav(imagepath, filename, webdavaccess, callback) { - if (webdavaccess) { - var webdavserver = webdavaccess["webdavserver"] || ""; - var webdavpath = webdavaccess["webdavpath"] || "/"; - var webdavusername = webdavaccess["webdavusername"] || ""; - var webdavpassword = webdavaccess["webdavpassword"] || ""; - - const client = createClient( - webdavserver, - { - username: webdavusername, - password: webdavpassword + } + + var smallestScreenResolutions = {}; + io.on('connection', function (socket) { + var whiteboardId = null; + + socket.on('disconnect', function () { + if (smallestScreenResolutions && smallestScreenResolutions[whiteboardId] && socket && socket.id) { + delete smallestScreenResolutions[whiteboardId][socket.id]; } - ) - client.getDirectoryContents(webdavpath).then((items) => { - var cloudpath = webdavpath+ '' + filename; - console.log("webdav saving to:", cloudpath); - fs.createReadStream(imagepath).pipe(client.createWriteStream(cloudpath)); - callback(); - }).catch((error) => { - callback("403"); - console.log("Could not connect to webdav!") + socket.broadcast.emit('refreshUserBadges', null); //Removes old user Badges + sendSmallestScreenResolution(); }); - } else { - callback("Error: no access data!") - } -} - -var smallestScreenResolutions = {}; -io.on('connection', function (socket) { - var whiteboardId = null; - - socket.on('disconnect', function () { - if (smallestScreenResolutions && smallestScreenResolutions[whiteboardId] && socket && socket.id) { - delete smallestScreenResolutions[whiteboardId][socket.id]; - } - socket.broadcast.emit('refreshUserBadges', null); //Removes old user Badges - sendSmallestScreenResolution(); - }); - - socket.on('drawToWhiteboard', function (content) { - content = escapeAllContentStrings(content); - if (accessToken === "" || accessToken == content["at"]) { - socket.broadcast.to(whiteboardId).emit('drawToWhiteboard', content); //Send to all users in the room (not own socket) - s_whiteboard.handleEventsAndData(content); //save whiteboardchanges on the server - } else { - socket.emit('wrongAccessToken', true); - } - }); - - socket.on('joinWhiteboard', function (content) { - content = escapeAllContentStrings(content); - if (accessToken === "" || accessToken == content["at"]) { - whiteboardId = content["wid"]; - socket.join(whiteboardId); //Joins room name=wid - smallestScreenResolutions[whiteboardId] = smallestScreenResolutions[whiteboardId] ? smallestScreenResolutions[whiteboardId] : {}; - smallestScreenResolutions[whiteboardId][socket.id] = content["windowWidthHeight"] || { w: 10000, h: 10000 }; - sendSmallestScreenResolution(); - } else { - socket.emit('wrongAccessToken', true); - } - }); - - socket.on('updateScreenResolution', function (content) { - content = escapeAllContentStrings(content); - if (accessToken === "" || accessToken == content["at"]) { - smallestScreenResolutions[whiteboardId][socket.id] = content["windowWidthHeight"] || { w: 10000, h: 10000 }; - sendSmallestScreenResolution(); - } - }); - - function sendSmallestScreenResolution() { - if (disableSmallestScreen) { - return; - } - var smallestWidth = 10000; - var smallestHeight = 10000; - for (var i in smallestScreenResolutions[whiteboardId]) { - smallestWidth = smallestWidth > smallestScreenResolutions[whiteboardId][i]["w"] ? smallestScreenResolutions[whiteboardId][i]["w"] : smallestWidth; - smallestHeight = smallestHeight > smallestScreenResolutions[whiteboardId][i]["h"] ? smallestScreenResolutions[whiteboardId][i]["h"] : smallestHeight; - } - io.to(whiteboardId).emit('updateSmallestScreenResolution', { w: smallestWidth, h: smallestHeight }); - } -}); - -//Prevent cross site scripting (xss) -function escapeAllContentStrings(content, cnt) { - if (!cnt) - cnt = 0; - - if (typeof (content) === "string") { - return DOMPurify.sanitize(content); - } - for (var i in content) { - if (typeof (content[i]) === "string") { - content[i] = DOMPurify.sanitize(content[i]); - } if (typeof (content[i]) === "object" && cnt < 10) { - content[i] = escapeAllContentStrings(content[i], ++cnt); - } - } - return content; -} - -function getArgs() { - const args = {} - process.argv - .slice(2, process.argv.length) - .forEach(arg => { - // long arg - if (arg.slice(0, 2) === '--') { - const longArg = arg.split('=') - args[longArg[0].slice(2, longArg[0].length)] = longArg[1] + + socket.on('drawToWhiteboard', function (content) { + content = escapeAllContentStrings(content); + if (accessToken === "" || accessToken == content["at"]) { + socket.broadcast.to(whiteboardId).emit('drawToWhiteboard', content); //Send to all users in the room (not own socket) + s_whiteboard.handleEventsAndData(content); //save whiteboardchanges on the server + } else { + socket.emit('wrongAccessToken', true); } - // flags - else if (arg[0] === '-') { - const flags = arg.slice(1, arg.length).split('') - flags.forEach(flag => { - args[flag] = true - }) + }); + + socket.on('joinWhiteboard', function (content) { + content = escapeAllContentStrings(content); + if (accessToken === "" || accessToken == content["at"]) { + whiteboardId = content["wid"]; + socket.join(whiteboardId); //Joins room name=wid + smallestScreenResolutions[whiteboardId] = smallestScreenResolutions[whiteboardId] ? smallestScreenResolutions[whiteboardId] : {}; + smallestScreenResolutions[whiteboardId][socket.id] = content["windowWidthHeight"] || { w: 10000, h: 10000 }; + sendSmallestScreenResolution(); + } else { + socket.emit('wrongAccessToken', true); } - }) - return args + }); + + socket.on('updateScreenResolution', function (content) { + content = escapeAllContentStrings(content); + if (accessToken === "" || accessToken == content["at"]) { + smallestScreenResolutions[whiteboardId][socket.id] = content["windowWidthHeight"] || { w: 10000, h: 10000 }; + sendSmallestScreenResolution(); + } + }); + + function sendSmallestScreenResolution() { + if (disableSmallestScreen) { + return; + } + var smallestWidth = 10000; + var smallestHeight = 10000; + for (var i in smallestScreenResolutions[whiteboardId]) { + smallestWidth = smallestWidth > smallestScreenResolutions[whiteboardId][i]["w"] ? smallestScreenResolutions[whiteboardId][i]["w"] : smallestWidth; + smallestHeight = smallestHeight > smallestScreenResolutions[whiteboardId][i]["h"] ? smallestScreenResolutions[whiteboardId][i]["h"] : smallestHeight; + } + io.to(whiteboardId).emit('updateSmallestScreenResolution', { w: smallestWidth, h: smallestHeight }); + } + }); + + //Prevent cross site scripting (xss) + function escapeAllContentStrings(content, cnt) { + if (!cnt) + cnt = 0; + + if (typeof (content) === "string") { + return DOMPurify.sanitize(content); + } + for (var i in content) { + if (typeof (content[i]) === "string") { + content[i] = DOMPurify.sanitize(content[i]); + } if (typeof (content[i]) === "object" && cnt < 10) { + content[i] = escapeAllContentStrings(content[i], ++cnt); + } + } + return content; + } + + process.on('unhandledRejection', error => { + // Will print "unhandledRejection err is not defined" + console.log('unhandledRejection', error.message); + }) } -process.on('unhandledRejection', error => { - // Will print "unhandledRejection err is not defined" - console.log('unhandledRejection', error.message); -}) \ No newline at end of file +module.exports = startBackendServer; \ No newline at end of file diff --git a/scripts/server-frontend-dev.js b/scripts/server-frontend-dev.js new file mode 100644 index 0000000..a60319f --- /dev/null +++ b/scripts/server-frontend-dev.js @@ -0,0 +1,32 @@ +const webpack = require("webpack"); +const WebpackDevServer = require("webpack-dev-server"); +const config = require("../config/webpack.dev"); + +const devServerConfig = { + hot: true, + inline: true, + stats: { + children: false, + maxModules: 0 + }, + proxy: { + '/api': 'http://localhost:3000', + '/ws-api': { + target: 'ws://localhost:3000', + ws: true, + } + } +} + +function startFrontendDevServer(port) { + new WebpackDevServer(webpack(config), devServerConfig) + .listen(port, (err) => { + if (err) { + console.log(err); + } + + console.log("Listening on port " + port); + }); +} + +module.exports = startFrontendDevServer; diff --git a/scripts/server.js b/scripts/server.js new file mode 100644 index 0000000..06478c6 --- /dev/null +++ b/scripts/server.js @@ -0,0 +1,25 @@ +const { getArgs } = require("./utils"); +const startFrontendDevServer = require("./server-frontend-dev"); +const startBackendServer = require("./server-backend"); + +const SERVER_MODES = { + PRODUCTION: 1, + DEVELOPMENT: 2 +} + +const args = getArgs(); + +if ( typeof args.mode === "undefined" || (args.mode !== "production" && args.mode !== "development")) { + throw new Error("--mode=development or --mode=production is expected") +} + +const server_mode = args.mode === "production" ? SERVER_MODES.PRODUCTION : SERVER_MODES.DEVELOPMENT; + +if (server_mode === SERVER_MODES.DEVELOPMENT){ + console.info("Starting server in development mode."); + startFrontendDevServer(8080); + startBackendServer(3000); +} else { + console.info("Starting server in production mode."); + startBackendServer(8080); +} diff --git a/scripts/utils.js b/scripts/utils.js new file mode 100644 index 0000000..dad47b3 --- /dev/null +++ b/scripts/utils.js @@ -0,0 +1,22 @@ +function getArgs() { + const args = {} + process.argv + .slice(2, process.argv.length) + .forEach(arg => { + // long arg + if (arg.slice(0, 2) === '--') { + const longArg = arg.split('=') + args[longArg[0].slice(2, longArg[0].length)] = longArg[1] + } + // flags + else if (arg[0] === '-') { + const flags = arg.slice(1, arg.length).split('') + flags.forEach(flag => { + args[flag] = true + }) + } + }) + return args +} + +module.exports.getArgs = getArgs; \ No newline at end of file