diff options
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | public/common/client.js | 10 | ||||
-rw-r--r-- | server.js | 141 | ||||
-rw-r--r-- | views/forum_reply.ejs | 3 | ||||
-rw-r--r-- | views/games.ejs | 4 | ||||
-rw-r--r-- | views/info.ejs | 21 |
6 files changed, 94 insertions, 87 deletions
diff --git a/package.json b/package.json index 2306457..603974a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "better-sqlite3": "^7.4.4", "connect": "^3.7.0", "connect-flash": "^0.1.1", - "cookie-parser": "^1.4.5", "dotenv": "^10.0.0", "ejs": "^3.1.5", "express": "^4.17.1", @@ -14,7 +13,6 @@ "nodemailer": "^6.7.0", "passport": "^0.5.0", "passport-local": "^1.0.0", - "passport.socketio": "^3.7.0", "socket.io": "^4.3.1" } } diff --git a/public/common/client.js b/public/common/client.js index 5df0413..1e31f79 100644 --- a/public/common/client.js +++ b/public/common/client.js @@ -188,7 +188,7 @@ function init_client(roles) { load_chat(game_id); - console.log("JOINING game", game_id, "role", role); + console.log("JOINING", title + "/" + game_id + "/" + role); socket = io({ transports: ['websocket'], @@ -220,7 +220,7 @@ function init_client(roles) { }); socket.on('presence', (presence) => { - console.log("PRESENCE", presence); + console.log("PRESENCE", JSON.stringify(presence)); for (let i = 0; i < roles.length; ++i) { let elt = document.querySelector(ROLE_SEL[i]); if (roles[i] in presence) @@ -231,7 +231,7 @@ function init_client(roles) { }); socket.on('state', (new_game, new_game_over) => { - console.log("STATE"); + console.log("STATE", !!new_game.actions, new_game_over); game = new_game; game_over = new_game_over; on_update_log(); @@ -426,14 +426,14 @@ function send_action(verb, noun) { if (noun !== undefined) { if (game.actions && game.actions[verb] && game.actions[verb].includes(noun)) { game.actions = null; - console.log("SEND ACTION", verb, noun); + console.log("ACTION", verb, JSON.stringify(noun)); socket.emit('action', verb, noun); return true; } } else { if (game.actions && game.actions[verb]) { game.actions = null; - console.log("SEND ACTION", verb); + console.log("ACTION", verb); socket.emit('action', verb); return true; } @@ -8,7 +8,6 @@ const express = require('express'); const express_session = require('express-session'); const passport = require('passport'); const passport_local = require('passport-local'); -const passport_socket = require('passport.socketio'); const body_parser = require('body-parser'); const connect_flash = require('connect-flash'); const crypto = require('crypto'); @@ -31,11 +30,24 @@ db.pragma("journal_mode = WAL"); db.pragma("synchronous = NORMAL"); db.pragma("foreign_keys = ON"); +let session = express_session({ + secret: SESSION_SECRET, + resave: false, + rolling: true, + saveUninitialized: false, + store: session_store, + cookie: { + maxAge: 7 * 24 * 60 * 60 * 1000, + sameSite: 'lax', + } +}); + let app = express(); let http_port = process.env.HTTP_PORT || 8080; let http_server = http.createServer(app); let http_io = socket_io(http_server); +http_server.keepAliveTimeout = 0; http_server.listen(http_port, '0.0.0.0', () => console.log('listening HTTP on *:' + http_port)); let io = http_io; @@ -47,6 +59,7 @@ if (https_port) { }, app); let https_io = socket_io(https_server); https_server.listen(https_port, '0.0.0.0', () => console.log('listening HTTPS on *:' + https_port)); + http_server.keepAliveTimeout=0; io = { use: function (fn) { http_io.use(fn); https_io.use(fn); }, on: function (ev,fn) { http_io.on(ev,fn); https_io.on(ev,fn); }, @@ -65,36 +78,27 @@ if (process.env.MAIL_HOST && process.env.MAIL_PORT) { console.log("Mail notifications disabled."); } -app.disable('x-powered-by'); +app.set('x-powered-by', false); +app.set('etag', false); app.set('view engine', 'ejs'); + app.use(body_parser.urlencoded({extended:false})); -app.use(express_session({ - secret: SESSION_SECRET, - resave: false, - rolling: true, - saveUninitialized: false, - store: session_store, - cookie: { - maxAge: 7 * 24 * 60 * 60 * 1000, - sameSite: 'lax', - } -})); +app.use(session); app.use(connect_flash()); -io.use(passport_socket.authorize({ - key: 'connect.sid', - secret: SESSION_SECRET, - store: session_store, -})); +const socket_wrap = middleware => (socket, next) => middleware(socket.request, {}, next); +io.use(socket_wrap(session)); +io.use(socket_wrap(passport.initialize())); +io.use(socket_wrap(passport.session())); -const is_immutable = /\.(svg|png|jpg|jpeg|woff2)$/; +const is_immutable = /\.(svg|png|jpg|jpeg|woff2|webp|ico)$/; function setHeaders(res, path) { if (is_immutable.test(path)) res.set("Cache-Control", "public, max-age=86400, immutable"); } -app.use(express.static('public', { setHeaders: setHeaders })); +app.use(express.static('public', { setHeaders: setHeaders, lastModified:false })); /* * MISC FUNCTIONS @@ -106,7 +110,7 @@ function SQL(s) { function LOG(req, ...msg) { let name; - if (req.isAuthenticated()) + if (req.user) name = `"${req.user.name}" <${req.user.mail}>`; else name = "guest"; @@ -115,10 +119,10 @@ function LOG(req, ...msg) { } function SLOG(socket, ...msg) { - let name = `"${socket.request.user.name}" <${socket.request.user.mail}>`; + let name = socket.request.user ? `"${socket.request.user.name}" <${socket.request.user.mail}>` : "guest"; let time = new Date().toISOString().substring(0,19).replace("T", " "); console.log(time, socket.request.connection.remoteAddress, name, - socket.id, socket.title_id, socket.game_id, socket.role, ...msg); + socket.title_id + "/" + socket.game_id + "/" + socket.role, ...msg); } function human_date(time) { @@ -299,7 +303,7 @@ function must_not_be_logged_in(req, res, next) { function must_be_logged_in(req, res, next) { if (SQL_BLACKLIST_IP.get(req.connection.remoteAddress) === 1) return res.redirect('/banned'); - if (!req.isAuthenticated()) { + if (!req.user) { req.session.redirect = req.originalUrl; return res.redirect('/login'); } @@ -310,7 +314,7 @@ function must_be_logged_in(req, res, next) { function may_be_logged_in(req, res, next) { if (SQL_BLACKLIST_IP.get(req.connection.remoteAddress) === 1) return res.redirect('/banned'); - if (req.isAuthenticated()) + if (req.user) touch_user(req); return next(); } @@ -335,14 +339,14 @@ app.get('/logout', function (req, res) { }); app.get('/login', function (req, res) { - if (req.isAuthenticated()) + if (req.user) return res.redirect('/'); LOG(req, "GET /login"); res.render('login.ejs', { user: req.user, flash: req.flash('message') }); }); app.get('/signup', function (req, res) { - if (req.isAuthenticated()) + if (req.user) return res.redirect('/'); LOG(req, "GET /signup"); res.render('signup.ejs', { user: req.user, flash: req.flash('message') }); @@ -973,6 +977,11 @@ function annotate_games(games, user_id) { }).join(", "); game.is_active = is_active(game, players, user_id); game.is_shared = is_shared(game, players, user_id); + game.is_yours = false; + if (user_id > 0) + for (let i = 0; i < players.length; ++i) + if (players[i].user_id === user_id) + game.is_yours = 1; game.ctime = human_date(game.ctime); game.mtime = human_date(game.mtime); } @@ -982,7 +991,7 @@ app.get('/games', may_be_logged_in, function (req, res) { LOG(req, "GET /join"); let open_games = QUERY_LIST_GAMES.all(0); let active_games = QUERY_LIST_GAMES.all(1); - if (req.isAuthenticated()) { + if (req.user) { annotate_games(open_games, req.user.user_id); annotate_games(active_games, req.user.user_id); } else { @@ -1021,31 +1030,20 @@ app.get('/info/:title_id', may_be_logged_in, function (req, res) { let title = TITLES[title_id]; if (!title) return res.status(404).send("Invalid title."); - if (req.isAuthenticated()) { - let open_games = QUERY_LIST_GAMES_OF_TITLE.all(title_id, 0, 1000); - let active_games = QUERY_LIST_GAMES_OF_TITLE.all(title_id, 1, 1000); - let finished_games = QUERY_LIST_GAMES_OF_TITLE.all(title_id, 2, 50); - annotate_games(open_games, req.user.user_id); - annotate_games(active_games, req.user.user_id); - annotate_games(finished_games, req.user.user_id); - res.set("Cache-Control", "no-store"); - res.render('info.ejs', { - user: req.user, - title: title, - open_games: open_games, - active_games: active_games, - finished_games: finished_games, - }); - } else { - res.set("Cache-Control", "no-store"); - res.render('info.ejs', { - user: req.user, - title: title, - open_games: [], - active_games: [], - finished_games: [], - }); - } + let open_games = QUERY_LIST_GAMES_OF_TITLE.all(title_id, 0, 1000); + let active_games = QUERY_LIST_GAMES_OF_TITLE.all(title_id, 1, 1000); + let finished_games = QUERY_LIST_GAMES_OF_TITLE.all(title_id, 2, 50); + res.set("Cache-Control", "no-store"); + annotate_games(open_games, req.user ? req.user.user_id : 0); + annotate_games(active_games, req.user ? req.user.user_id : 0); + annotate_games(finished_games, req.user ? req.user.user_id : 0); + res.render('info.ejs', { + user: req.user, + title: title, + open_games: open_games, + active_games: active_games, + finished_games: finished_games, + }); }); app.get('/create/:title_id', must_be_logged_in, function (req, res) { @@ -1305,7 +1303,7 @@ app.get('/start/:game_id', must_be_logged_in, function (req, res) { res.send("SUCCESS"); }); -app.get('/play/:game_id/:role', must_be_logged_in, function (req, res) { +app.get('/play/:game_id/:role', may_be_logged_in, function (req, res) { LOG(req, "GET /play/" + req.params.game_id + "/" + req.params.role); let game_id = req.params.game_id | 0; let role = req.params.role; @@ -1315,10 +1313,10 @@ app.get('/play/:game_id/:role', must_be_logged_in, function (req, res) { res.redirect('/'+title+'/play.html?game='+game_id+'&role='+role); }); -app.get('/play/:game_id', must_be_logged_in, function (req, res) { +app.get('/play/:game_id', may_be_logged_in, function (req, res) { LOG(req, "GET /play/" + req.params.game_id); let game_id = req.params.game_id | 0; - let user_id = req.user.user_id | 0; + let user_id = req.user ? req.user.user_id : 0; let title = SQL_SELECT_GAME_TITLE.get(game_id); if (!title) return res.redirect('/join/'+game_id); @@ -1517,7 +1515,7 @@ function put_replay(game_id, role, action, args) { } function on_action(socket, action, arg) { - SLOG(socket, "--> ACTION", action, arg); + SLOG(socket, "ACTION", action, arg); try { let state = get_game_state(socket.game_id); let old_active = state.active; @@ -1531,7 +1529,7 @@ function on_action(socket, action, arg) { } function on_resign(socket) { - SLOG(socket, "--> RESIGN"); + SLOG(socket, "RESIGN"); try { let state = get_game_state(socket.game_id); let old_active = state.active; @@ -1547,7 +1545,8 @@ function on_resign(socket) { function on_getchat(socket, seen) { try { let chat = SQL_SELECT_GAME_CHAT.all(socket.game_id, seen); - SLOG(socket, "<-- CHAT", seen, chat.length); + if (chat.length > 0) + SLOG(socket, "GETCHAT", seen, chat.length); for (let i = 0; i < chat.length; ++i) socket.emit('chat', chat[i]); } catch (err) { @@ -1561,7 +1560,7 @@ function on_chat(socket, message) { try { let chat = SQL_INSERT_GAME_CHAT.get(socket.game_id, socket.user_id, message); chat[2] = socket.user_name; - SLOG(socket, "--> CHAT", JSON.stringify(chat)); + SLOG(socket, "CHAT", JSON.stringify(chat)); for (let other of clients[socket.game_id]) if (other.role !== "Observer") other.emit('chat', chat); @@ -1572,7 +1571,7 @@ function on_chat(socket, message) { } function on_debug(socket) { - SLOG(socket, "<-- DEBUG"); + SLOG(socket, "DEBUG"); try { let game_state = SQL_SELECT_GAME_STATE.get(socket.game_id); if (!game_state) @@ -1585,7 +1584,7 @@ function on_debug(socket) { } function on_save(socket) { - SLOG(socket, "<-- SAVE"); + SLOG(socket, "SAVE"); try { let game_state = SQL_SELECT_GAME_STATE.get(socket.game_id); if (!game_state) @@ -1598,11 +1597,14 @@ function on_save(socket) { } function on_restore(socket, state_text) { - SLOG(socket, '--> RESTORE', state_text); + SLOG(socket, 'RESTORE'); try { let state = JSON.parse(state_text); - SQL_UPDATE_GAME_RESULT.run(1, null, game_id); - SQL_UPDATE_GAME_STATE.run(game_id, state_text, state.active); + state.seed = random_seed(); // reseed! + state_text = JSON.stringify(state); + SQL_UPDATE_GAME_RESULT.run(1, null, socket.game_id); + SQL_UPDATE_GAME_STATE.run(socket.game_id, state_text, state.active); + put_replay(socket.game_id, null, 'restore', state_text); for (let other of clients[socket.game_id]) send_state(other, state); } catch (err) { @@ -1620,10 +1622,15 @@ function broadcast_presence(game_id) { } io.on('connection', (socket) => { + if (!socket.request.user) { + socket.user_id = 0; + socket.user_name = "guest"; + } else { + socket.user_id = socket.request.user.user_id | 0; + socket.user_name = socket.request.user.name; + } socket.title_id = socket.handshake.query.title || "unknown"; socket.game_id = socket.handshake.query.game | 0; - socket.user_id = socket.request.user.user_id | 0; - socket.user_name = socket.request.user.name; socket.role = socket.handshake.query.role; socket.log_length = 0; socket.rules = RULES[socket.title_id]; diff --git a/views/forum_reply.ejs b/views/forum_reply.ejs index 85bafad..3e87ddd 100644 --- a/views/forum_reply.ejs +++ b/views/forum_reply.ejs @@ -15,8 +15,7 @@ table .time { border-left: none; font-weight: normal; } <p> Reply: <br> -<textarea name="body" rows="15" cols="80" maxlength="32000" required autofocus - placeholder="Say something nice."></textarea> +<textarea name="body" rows="15" cols="80" maxlength="32000" required autofocus></textarea> <p> <button type="submit">Submit</button> </form> diff --git a/views/games.ejs b/views/games.ejs index acb94b6..3b3f485 100644 --- a/views/games.ejs +++ b/views/games.ejs @@ -31,7 +31,11 @@ <td class="description"><%= row.description %> <td class="time"><%= row.mtime %> <td class="role <%= row.is_active ? "is_active" : "" %>"><%= row.active %> +<% if (row.is_yours) { %> <td class="command"><a href="/join/<%= row.game_id %>">Enter</a> +<% } else { %> +<td class="command"><a href="/play/<%- row.game_id %>/Observer">View</a> +<% } %> <% }); } else { %> <tr><td colspan="8">No active games. <% } %> diff --git a/views/info.ejs b/views/info.ejs index f97b04e..7c98e21 100644 --- a/views/info.ejs +++ b/views/info.ejs @@ -5,8 +5,6 @@ Read more about the game on <a href="https://boardgamegeek.com/boardgame/<%= title.bgg %>">boardgamegeek.com</a>. -<% if (user) { %> - <h2>Open Games</h2> <table class="game"> <tr><th>ID<th>Scenario<th>Players<th>Description<th>Created<th> @@ -39,7 +37,11 @@ Read more about the game on <td class="description"><%= row.description %> <td class="time"><%= row.mtime %> <td class="<%= row.is_active ? "role is_active" : "role" %>"><%= row.active %> -<td class="command"><a href="/join/<%= row.game_id %>">Enter</a> +<% if (row.is_yours) { %> +<td class="command"><a href="/join/<%- row.game_id %>">Enter</a> +<% } else { %> +<td class="command"><a href="/play/<%- row.game_id %>/Observer">View</a> +<% } %> <% }); %> </table> <% } %> @@ -56,14 +58,11 @@ Read more about the game on <td class="description"><%= row.description %> <td class="time"><%= row.mtime %> <td class="result"><%= row.result %> -<td class="command"><a href="/join/<%= row.game_id %>">View</a> +<% if (row.is_yours) { %> +<td class="command"><a href="/join/<%= row.game_id %>">Enter</a> +<% } else { %> +<td class="command"><a href="/play/<%- row.game_id %>/Observer">View</a> +<% } %> <% }); %> </table> <% } %> - -<% } else { %> - -<p> -Login to create or join a game. - -<% } %> |