From fddc0c9bdf2e0d6fe0deb48f7df060ace2e53352 Mon Sep 17 00:00:00 2001 From: ryleu Date: Mon, 20 Jun 2022 12:10:44 -0400 Subject: [PATCH] release 1.0.1 - fix crash when loading boards - allow saving boards when disconnected - TODO: attempt downloading board to check if disconnected - TODO: implement websocket reconnection --- main.js | 327 ++++++++++++++++++++++-------------------- site/board/index.html | 6 +- site/board/index.js | 34 ++++- 3 files changed, 209 insertions(+), 158 deletions(-) diff --git a/main.js b/main.js index 890c758..a43d7cf 100755 --- a/main.js +++ b/main.js @@ -47,173 +47,194 @@ let sessions = {} const server = https.createServer(auth, (req, res) => { - try{ - // Initial 200 status code - res.statusCode = 200; - - // If this is an api request, use different handling - if (req.url.match(apiRe)) { - let data = ""; - req.on("data", chunk => { - data += chunk; - }); - req.on("end", () => { - let url = req.url.split("?"); - let resource = url[0]; - - if (resource[resource.length - 1] !== "/") { - resource += "/"; - } - - let args = {}; - (url[1] ? url[1].split("&") : []).forEach((rawArg) => { - let arg = rawArg.split("="); - args[arg[0]] = arg[1]; + try { + // Initial 200 status code + res.statusCode = 200; + + // If this is an api request, use different handling + if (req.url.match(apiRe)) { + let data = ""; + req.on("data", chunk => { + data += chunk; }); + req.on("end", () => { + let url = req.url.split("?"); + let resource = url[0]; - switch (resource) { - case "/api/board/": - res.setHeader("Allow", "GET, PUT"); - let id = args.id; - if (!args.id || !sessions[id]) { - res.statusCode = 403; - res.end("There is no session with that ID."); - break; - } + if (resource[resource.length - 1] !== "/") { + resource += "/"; + } - switch (req.method) { - case "GET": - res.setHeader("Content-Type", "application/json"); - res.end(JSON.stringify(sessions[args.id].board)); + let args = {}; + (url[1] ? url[1].split("&") : []).forEach((rawArg) => { + let arg = rawArg.split("="); + args[arg[0]] = arg[1]; + }); + + switch (resource) { + case "/api/board/": + res.setHeader("Allow", "GET, PUT"); + let id = args.id; + if (!args.id || !sessions[id]) { + res.statusCode = 403; + res.end("There is no session with that ID."); break; - case "PUT": - if (req.headers["content-type"] !== "application/json") { - res.statusCode = 400; - res.end(noJson + "\nPlease use application/json"); + } + + switch (req.method) { + case "GET": + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(sessions[args.id].board)); break; - } - let json; - try { - json = JSON.parse(data); - } catch (e) { - res.statusCode = 400; - res.end(noJson + "\nYour JSON isn't JSON."); + case "PUT": + if (req.headers["content-type"] !== "application/json") { + res.statusCode = 400; + res.end(noJson + "\nPlease use application/json"); + break; + } + let json; + try { + json = JSON.parse(data); + } catch (e) { + res.statusCode = 400; + res.end(noJson + "\nYour JSON isn't JSON."); + break; + } + + if (json.dimensions === undefined || + json.pieces === undefined || + json.lines === undefined) { + res.statusCode = 400; + res.end(noJson + "\nYour JSON is dog poo."); + break; + } + + const pieceKeys = Object.keys(json.pieces); + + if (json.pieceCount === undefined) { + let highest = 0; + + for (let i = 0; i < pieceKeys.length; i++) { + const piece = json.pieces[pieceKeys[i]]; + const pieceNumber = piece.id.split("-")[0] - 0; + highest = Math.max(highest, pieceNumber); + } + } + + for (let i = 0; i < pieceKeys.length; i++) { + if (json.pieces.pos === undefined) { + const pos = json.pieces[pieceKeys[i]]._pos; + json.pieces[pieceKeys[i]].pos = [pos.x, pos.y]; + delete json.pieces[pieceKeys[i]]._pos; + } + } + + sessions[args.id].board = json; + + res.end("Successfully applied new board."); + + sessions[args.id].sockets.forEach(s => s.send(`&B`)); break; - } - - if (json.dimensions === undefined || - json.pieces === undefined || - json.lines === undefined || - json.pieceCount === undefined) { - res.statusCode = 400; - res.end(noJson + "\nYour JSON is dog poo."); + default: + res.statusCode = 405; + res.end(); break; - } - - sessions[args.id].board = json; - - res.end("Successfully applied new JSON."); + } + break; + case "/api/new/": + res.setHeader("Allow", "POST"); + switch (req.method) { + case "POST": + let invite = genInviteCode(); + sessions[invite] = { + board: defaultBoard(), + sockets: [], + hasBeenLoaded: false + }; + res.setHeader("Content-Type", "application/json"); + res.end(`{"invite": "${invite}"}`); + break; + default: + res.statusCode = 405; + res.end(); + break; + } + break; + default: + res.setHeader("Content-Type", "text/plain"); + res.statusCode = 404 + res.end(); + break; + } + }); - sessions[args.id].sockets.forEach(s => s.send(`&B`)); - break; - default: - res.statusCode = 405; - res.end(); - break; - } - break; - case "/api/new/": - res.setHeader("Allow", "POST"); - switch (req.method) { - case "POST": - let invite = genInviteCode(); - sessions[invite] = { - board: defaultBoard(), - sockets: [], - hasBeenLoaded: false - }; - res.setHeader("Content-Type", "application/json"); - res.end(`{"invite": "${invite}"}`); - break; - default: - res.statusCode = 405; - res.end(); - break; - } - break; - default: - res.setHeader("Content-Type", "text/plain"); - res.statusCode = 404 - res.end(); - break; - } - }); - } + return; + } - // Start building the file request path - let path = "site" + req.url.split("?")[0]; - if (path[path.length - 1] === "/") { - path += "index.html"; - } + // Start building the file request path + let path = "site" + req.url.split("?")[0]; + if (path[path.length - 1] === "/") { + path += "index.html"; + } - // If the path has funky spooky stuff in it, yeet it to the abyss - if (path.match(evilReqRe)) { - res.statusCode = 418; - res.setHeader("Content-Type", "text/plain"); - res.end(insults[path.length % insults.length]); - return; - } + // If the path has funky spooky stuff in it, yeet it to the abyss + if (path.match(evilReqRe)) { + res.statusCode = 418; + res.setHeader("Content-Type", "text/plain"); + res.end(insults[path.length % insults.length]); + return; + } - // If the file has an extension (it always should), use it - let extension = path.match(extRe); - if (extension) { - extension = extension[0]; - } else { - extension = null; - } + // If the file has an extension (it always should), use it + let extension = path.match(extRe); + if (extension) { + extension = extension[0]; + } else { + extension = null; + } - // Set the correct content type based on the extension - switch (extension) { - case ".html": - res.setHeader("Content-Type", "text/html"); - break; - case ".css": - res.setHeader("Content-Type", "text/css"); - break; - case ".js": - res.setHeader("Content-Type", "text/javascript"); - break; - case ".svg": - res.setHeader("Cache-Control", "private"); - res.setHeader("Content-Type", "image/svg+xml"); - break; - default: - res.setHeader("Content-Type", "text/plain"); - break; - } + // Set the correct content type based on the extension + switch (extension) { + case ".html": + res.setHeader("Content-Type", "text/html"); + break; + case ".css": + res.setHeader("Content-Type", "text/css"); + break; + case ".js": + res.setHeader("Content-Type", "text/javascript"); + break; + case ".svg": + res.setHeader("Cache-Control", "private"); + res.setHeader("Content-Type", "image/svg+xml"); + break; + default: + res.setHeader("Content-Type", "text/plain"); + break; + } - // Read the file requested - fs.readFile(path, (err, data) => { - if (err) { - if (err.errno === -2) { - res.statusCode = 404; - res.end("not found"); - } else { - try { - console.error(err); - res.statusCode = 500; - res.end("failed to read file"); - } catch (err2) { - console.error(err, err2); + // Read the file requested + fs.readFile(path, (err, data) => { + if (err) { + if (err.errno === -2) { + res.statusCode = 404; + res.end("not found"); + } else { + try { + console.error(err); + res.statusCode = 500; + res.end("failed to read file"); + } catch (err2) { + console.error(err, err2); + } } + } else { + res.end(data); } - } else { - res.end(data); - } - }); -} catch (e) { - console.log(e); -} + }); + } catch (e) { + console.log(e); + } }).listen(config.port, (err) => { console.log(`Listening on 0.0.0.0:${config.port}`) const uid = parseInt(process.env.SUDO_UID); @@ -347,7 +368,7 @@ wss.on('connection', function(socket) { }); }); function parsePos(posStr) { - var pos = posStr.split(","); + const pos = posStr.split(","); return [pos[0] - 0, pos[1] - 0]; } diff --git a/site/board/index.html b/site/board/index.html index 117534e..438956c 100644 --- a/site/board/index.html +++ b/site/board/index.html @@ -133,9 +133,9 @@
- - Download Board - +
diff --git a/site/board/index.js b/site/board/index.js index f16fe16..2e9397f 100644 --- a/site/board/index.js +++ b/site/board/index.js @@ -52,6 +52,32 @@ ws.onopen = () => { document.getElementById("board-holder").style.display = "inherit"; document.getElementById("invite-code").innerHTML = `Invite: ${args.id}`; document.getElementById("config-menu-download").href = `/api/board/?id=${args.id}`; + let downloadButton = document.getElementById("config-menu-download"); + downloadButton.addEventListener("click", () => { + if (ws.readyState === ws.OPEN) { + const link = document.createElement("a"); + link.download = "board.json"; + link.href = `/api/board/?id=${args.id}`; + link.click(); + } else { + let boardString = JSON.stringify(board); + navigator.clipboard.writeText(boardString).then(() => { + const boardElement = document.getElementById("board"); + boardElement.innerHTML = ""; + + const label = document.createElement("h1"); + label.className = "warning"; + label.textContent = "Board copied to clipboard because the site was unreachable."; + + boardElement.appendChild(label); + + const labelText = document.createElement("p"); + labelText.textContent = boardString; + + boardElement.appendChild(labelText); + }); + } + }); wsReady = true; @@ -269,6 +295,10 @@ ws.onmessage = (msg) => { } }; +ws.onclose = () => { + document.getElementById("invite-code").innerHTML = "DISCONNECTED"; +} + fetch("/api/board/?id=" + args.id).then(response => response.json()).then((json) => { board.dimensions = json.dimensions; board.fill = json.fill; @@ -333,7 +363,6 @@ function renderBoard(x, y) { // get the alternate fill color (if it exists) let altFill = board.fill[`${j+1}_${i+1}`]; - console.log(altFill, i, j); if (altFill !== undefined) { square.style.backgroundColor = altFill.color; } @@ -598,7 +627,7 @@ class Piece { this.element.style.width = getScale() + "px"; this.element.style.height = getScale() + "px"; - this.pos = pos; + this.pos = new Pos(pos[0], pos[1]); } set pos(newPos) { @@ -614,6 +643,7 @@ class Piece { try { elementGrid[newPos.y - 1].appendChild(this.element); } catch (e) { + console.error(newPos); console.error(e); ws.send(`&D;${this.id}`); }