diff --git a/.gitignore b/.gitignore index adda588..34d79e9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ node_modules scripts/* tmp config.yaml +sessions +sessions-journal diff --git a/app.js b/app.js index af39593..a4c080f 100644 --- a/app.js +++ b/app.js @@ -5,6 +5,7 @@ const createError = require('http-errors'), logger = require('morgan'), compileSass = require('express-compile-sass'), session = require('express-session'), + SQLiteStore = require('connect-sqlite3')(session), fsx = require('fs-extra'), yaml = require('js-yaml'), graphqlHTTP = require('express-graphql'), @@ -39,6 +40,7 @@ app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(session({ + store: new SQLiteStore, secret: settings.sessions.secret, resave: false, saveUninitialized: true, diff --git a/package-lock.json b/package-lock.json index e1f03ba..2621680 100644 --- a/package-lock.json +++ b/package-lock.json @@ -474,6 +474,11 @@ "upath": "^1.1.1" } }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -567,6 +572,14 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "connect-sqlite3": { + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/connect-sqlite3/-/connect-sqlite3-0.9.11.tgz", + "integrity": "sha1-TlQVXcLq3ypZmDhbLzXoPdkkCy0=", + "requires": { + "sqlite3": "^4.0.0" + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -675,6 +688,11 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -737,6 +755,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", @@ -1131,6 +1154,14 @@ "universalify": "^0.1.0" } }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1832,6 +1863,14 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, "in-publish": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", @@ -1859,6 +1898,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, "inline-source-map-comment": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/inline-source-map-comment/-/inline-source-map-comment-1.0.5.tgz", @@ -2341,6 +2385,30 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", @@ -2422,6 +2490,31 @@ } } }, + "needle": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.1.tgz", + "integrity": "sha512-CaLXV3W8Vnbps8ZANqDGz7j4x7Yj1LW4TWF/TQuDfj7Cfx4nAPTvw98qgTevtto1oHDrh3pQkaODbqupXlsWTg==", + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -2453,6 +2546,53 @@ } } }, + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "dependencies": { + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } + }, "node-sass": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", @@ -2525,6 +2665,20 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" + }, + "npm-packlist": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -2896,6 +3050,17 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -3127,6 +3292,11 @@ } } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "scss-tokenizer": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", @@ -3371,6 +3541,16 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "sqlite3": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.8.tgz", + "integrity": "sha512-kgwHu4j10KhpCHtx//dejd/tVQot7jc3sw+Sn0vMuKOw0X00Ckyg9VceKgzPyGmmz+zEoYue9tOLriWTvYy0ww==", + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.11.0", + "request": "^2.87.0" + } + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -3461,6 +3641,11 @@ "get-stdin": "^4.0.1" } }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, "sum-up": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz", diff --git a/package.json b/package.json index a17f1e6..176f489 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "start": "node ./bin/www" }, "dependencies": { + "connect-sqlite3": "^0.9.11", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", diff --git a/public/javascripts/bingo-web.js b/public/javascripts/bingo-web.js index c89e5ac..59179f4 100644 --- a/public/javascripts/bingo-web.js +++ b/public/javascripts/bingo-web.js @@ -76,8 +76,9 @@ async function createFollowup() { */ async function submitUsername() { let unameInput = document.querySelector('#username-input'); - let username = unameInput.value; - let response = await postGraphqlQuery(` + let username = unameInput.value.replace(/^\s+|\s+$/g, ''); + if (username.length > 1 && username !== 'anonymous') { + let response = await postGraphqlQuery(` mutation($username:String!) { bingo { setUsername(input: {username: $username}) { @@ -86,16 +87,19 @@ async function submitUsername() { } } }`, { - username: username - },`/graphql?game=${getGameParam()}`); - if (response.status === 200) { - unameInput.value = ''; - unameInput.placeholder = response.data.username; - document.querySelector('#username-form').remove(); - document.querySelector('.greyover').remove(); + username: username + },`/graphql?game=${getGameParam()}`); + if (response.status === 200) { + unameInput.value = ''; + unameInput.placeholder = response.data.username; + document.querySelector('#username-form').remove(); + document.querySelector('.greyover').remove(); + } else { + showError(`Failed to submit username. HTTP Error: ${response.status}`); + console.error(response); + } } else { - showError(`Failed to submit username. HTTP Error: ${response.status}`); - console.error(response); + showError('You need to provide a username (minimum 2 characters)!'); } } @@ -306,12 +310,10 @@ function addChatMessage(messageObject) { if (messageObject.type === "USER") { msgSpan.innerHTML = ` ${messageObject.username}: - ${messageObject.htmlContent} - `; + ${messageObject.htmlContent}`; } else { msgSpan.innerHTML = ` - ${messageObject.htmlContent} - `; + ${messageObject.htmlContent}`; } let chatContent = document.querySelector('#chat-content'); chatContent.appendChild(msgSpan); @@ -334,6 +336,8 @@ window.addEventListener("unhandledrejection", function(promiseRejectionEvent) { }); window.onload = () => { + if (document.querySelector('#chat-container')) + refresh(); if (window && !document.querySelector('#bingoform')) { refrInterval = setInterval(refresh, 1000); // global variable to clear } diff --git a/public/stylesheets/sass/bingo/style.sass b/public/stylesheets/sass/bingo/style.sass index ab28468..c5b0c92 100644 --- a/public/stylesheets/sass/bingo/style.sass +++ b/public/stylesheets/sass/bingo/style.sass @@ -23,14 +23,17 @@ textarea grid-template-columns: 0 100% !important grid-template-rows: 10% 80% 10% !important - #players-container div - display: none + #players-container #chat-container + display: none !important padding: 0 + #username-form width: calc(100% - 2rem) !important left: 0 !important + #hide-player-container-button display: none + .popup width: calc(100% - 2rem) !important left: 0 !important @@ -173,6 +176,7 @@ textarea height: 100% border: 1px solid $inactive margin: 0 0.5rem + word-break: break-word #chat-content height: calc(100% - 3.5rem) diff --git a/routes/bingo.js b/routes/bingo.js index 3daa8b1..b14b677 100644 --- a/routes/bingo.js +++ b/routes/bingo.js @@ -33,6 +33,9 @@ class BingoSession { addUser(user) { let id = user.id; this.users[id] = user; + if (user.username !== 'anonymous') { + this.chatMessages.push(new BingoChatMessage(`**${user.username}** joined.`, "INFO")); + } } /** @@ -57,7 +60,7 @@ class BingoSession { this.followup = followup.id; bingoSessions[followup.id] = followup; followup.chatMessages = this.chatMessages; - followup.chatMessages.push(new BingoChatMessage('--- Rematch ---', "INFO")); + followup.chatMessages.push(new BingoChatMessage('**Rematch**', "INFO")); return followup; } @@ -84,7 +87,7 @@ class BingoSession { */ sendToggleInfo(base64Word, bingoUser) { let word = Buffer.from(base64Word, 'base64').toString(); - let toggleMessage = new BingoChatMessage(`${bingoUser.username} toggled phrase "${word}"`, "INFO"); + let toggleMessage = new BingoChatMessage(`**${bingoUser.username}** toggled phrase "${word}".`, "INFO"); this.chatMessages.push(toggleMessage); } } @@ -343,6 +346,7 @@ router.graphqlResolver = (req, res) => { }); let size = input.size; if (words.length > 0 && size < 10 && size > 0) { + words = words.slice(0, 10000); // only allow up to 10000 words in the bingo let game = new BingoSession(words, size); bingoSessions[game.id] = game; @@ -404,7 +408,7 @@ router.graphqlResolver = (req, res) => { } }, sendChatMessage: ({input}) => { - input.message = replaceTagSigns(input.message); + input.message = replaceTagSigns(input.message).substring(0, 250); if (bingoSession && input.message) { let userMessage = new BingoChatMessage(input.message, 'USER', bingoUser.username); bingoSession.chatMessages.push(userMessage); diff --git a/views/bingo/bingo-game.pug b/views/bingo/bingo-game.pug index 78654b6..1f9974a 100644 --- a/views/bingo/bingo-game.pug +++ b/views/bingo/bingo-game.pug @@ -4,7 +4,7 @@ block content if username === 'anonymous' div(class='greyover') div(id='username-form', onkeypress='submitOnEnter(event, submitUsername)') - input(type='text', id='username-input', placeholder=username) + input(type='text', id='username-input', placeholder=username, maxlength="30") span Maximum is 30 characters. button(onclick='submitUsername()') Set Username div(id='content-container') @@ -15,7 +15,7 @@ block content span(class='player-name-span')= player.username div(id='chat-container') div(id='chat-content') - input(id='chat-input' type='text', placeholder='chat', onkeypress='submitOnEnter(event, sendChatMessage)') + input(id='chat-input' type='text', placeholder='chat', onkeypress='submitOnEnter(event, sendChatMessage)' maxlength="250") div(id='words-container') each val in grid div(class='bingo-word-row') diff --git a/views/bingo/bingo-submit.pug b/views/bingo/bingo-submit.pug index e351819..db8c533 100644 --- a/views/bingo/bingo-submit.pug +++ b/views/bingo/bingo-submit.pug @@ -10,4 +10,4 @@ block content div(class='stretchDiv') button(onclick='submitBingoWords()') Submit span(id='word-count') Please provide at least 9 phrases: - textarea(id='bingo-textarea', placeholder='Bingo Words') + textarea(id='bingo-textarea', placeholder='Bingo Words (max 10,000)', maxlength=1000000)