summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--public/common/client.js10
-rw-r--r--server.js141
-rw-r--r--views/forum_reply.ejs3
-rw-r--r--views/games.ejs4
-rw-r--r--views/info.ejs21
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;
}
diff --git a/server.js b/server.js
index f7f0286..02411db 100644
--- a/server.js
+++ b/server.js
@@ -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.
-
-<% } %>