var app = require('http').createServer(handler) , sockets = require('./sockets.js') , log = require("./log.js").log , path = require('path') , url = require('url') , fs = require("fs") , crypto = require("crypto") , serveStatic = require("serve-static") , createSVG = require("./createSVG.js") , templating = require("./templating.js") , config = require("./configuration.js"); var MIN_NODE_VERSION = 10.0; if (parseFloat(process.versions.node) < MIN_NODE_VERSION) { console.warn( "!!! You are using node " + process.version + ", wbo requires at least " + MIN_NODE_VERSION + " !!!"); } var io = sockets.start(app); app.listen(config.PORT); log("server started", { port: config.PORT }); var CSP = "default-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss:"; var fileserver = serveStatic(config.WEBROOT, { maxAge: 2 * 3600 * 1000, setHeaders: function (res) { res.setHeader("X-UA-Compatible", "IE=Edge"); res.setHeader("Content-Security-Policy", CSP); } }); var errorPage = fs.readFileSync(path.join(config.WEBROOT, "error.html")); function serveError(request, response) { return function (err) { log("error", { "error": err, "url": request.url }); response.writeHead(err ? 500 : 404, { "Content-Length": errorPage.length }); response.end(errorPage); } } function logRequest(request) { log('connection', { ip: request.connection.remoteAddress, original_ip: request.headers['x-forwarded-for'] || request.headers['forwarded'], user_agent: request.headers['user-agent'], referer: request.headers['referer'], language: request.headers['accept-language'], url: request.url, }); } function handler(request, response) { try { handleRequest(request, response); } catch (err) { console.trace(err); response.writeHead(500, { 'Content-Type': 'text/plain' }); response.end(err.toString()); } } const boardTemplate = new templating.BoardTemplate(path.join(config.WEBROOT, 'board.html')); const indexTemplate = new templating.Template(path.join(config.WEBROOT, 'index.html')); function handleRequest(request, response) { var parsedUrl = url.parse(request.url, true); var parts = parsedUrl.pathname.split('/'); if (parts[0] === '') parts.shift(); if (parts[0] === "boards") { // "boards" refers to the root directory if (parts.length === 1 && parsedUrl.query.board) { // '/boards?board=...' This allows html forms to point to boards var headers = { Location: 'boards/' + encodeURIComponent(parsedUrl.query.board) }; response.writeHead(301, headers); response.end(); } else if (parts.length === 2 && request.url.indexOf('.') === -1) { // If there is no dot and no directory, parts[1] is the board name boardTemplate.serve(request, response); } else { // Else, it's a resource request.url = "/" + parts.slice(1).join('/'); fileserver(request, response, serveError(request, response)); } } else if (parts[0] === "download") { var boardName = encodeURIComponent(parts[1]), history_file = path.join(config.HISTORY_DIR, "board-" + boardName + ".json"); if (parts.length > 2 && /^[0-9A-Za-z.\-]+$/.test(parts[2])) { history_file += '.' + parts[2] + '.bak'; } log("download", { "file": history_file }); fs.readFile(history_file, function (err, data) { if (err) return serveError(request, response)(err); response.writeHead(200, { "Content-Type": "application/json", "Content-Disposition": 'attachment; filename="' + boardName + '.wbo"', "Content-Length": data.length, }); response.end(data); }); } else if (parts[0] === "preview") { var boardName = encodeURIComponent(parts[1]), history_file = path.join(config.HISTORY_DIR, "board-" + boardName + ".json"); createSVG.renderBoard(history_file, function (err, svg) { if (err) { log(err); response.writeHead(404, { 'Content-Type': 'application/json' }); return response.end(JSON.stringify(err)); } response.writeHead(200, { "Content-Type": "image/svg+xml", "Content-Security-Policy": CSP, 'Content-Length': Buffer.byteLength(svg), }); response.end(svg); }); } else if (parts[0] === "random") { var name = crypto.randomBytes(32).toString('base64').replace(/[^\w]/g, '-'); response.writeHead(307, { 'Location': '/boards/' + name }); response.end(name); } else if (parts[0] === "") { // Index page logRequest(request); indexTemplate.serve(request, response); } else { fileserver(request, response, serveError(request, response)); } } module.exports = app;