From 3f97b240aae8d716b2776033e60bc33cacc94907 Mon Sep 17 00:00:00 2001 From: MaciejkaG Date: Mon, 18 Mar 2024 18:43:13 +0100 Subject: [PATCH] Major changes - Improved e-mail display on some of the major clients that do not handle flexboxes properly (Gmail) - Added ratelimiting (with Redis store) - Sessions are now stored in Redis - Added some serious login problem display - Improved match saving into the MySQL database - Login system enhancements - Minor design improvements - Bug fixes - User security improvements --- index.js | 75 ++++++++++++--- package-lock.json | 136 ++++++++++++++++++++++++++++ package.json | 5 + utils/auth.js | 54 +++++------ utils/mail/auth-code-firsttime.html | 22 +++-- utils/mail/auth-code.html | 22 +++-- views/error.handlebars | 39 ++++++++ views/index.handlebars | 2 +- 8 files changed, 303 insertions(+), 52 deletions(-) create mode 100644 views/error.handlebars diff --git a/index.js b/index.js index 143a7f0..ee8fea8 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,9 @@ import { engine } from 'express-handlebars'; import { createClient } from 'redis'; import * as bships from './utils/battleships.js'; import { MailAuth } from './utils/auth.js'; +import { rateLimit } from 'express-rate-limit'; +import { RedisStore as LimiterRedisStore } from 'rate-limit-redis'; +import SessionRedisStore from 'connect-redis'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -31,6 +34,17 @@ const redis = createClient(); redis.on('error', err => console.log('Redis Client Error', err)); await redis.connect(); +const limiter = rateLimit({ + windowMs: 40 * 1000, + limit: 100, + standardHeaders: 'draft-7', + legacyHeaders: false, + store: new LimiterRedisStore({ + sendCommand: (...args) => redis.sendCommand(args), + }), +}); +app.use(limiter); + const GInfo = new bships.GameInfo(redis, io); const auth = new MailAuth(redis, { host: process.env.mail_host, @@ -45,7 +59,14 @@ const auth = new MailAuth(redis, { }); app.set('trust proxy', 1); + +let sessionStore = new SessionRedisStore({ + client: redis, + prefix: "statkiSession:", +}); + const sessionMiddleware = session({ + store: sessionStore, secret: uuidv4(), resave: true, saveUninitialized: true, @@ -117,17 +138,29 @@ app.post('/api/login', (req, res) => { res.redirect('/'); } else if (login == 0 && req.body.email != null && validateEmail(req.body.email)) { auth.startVerification(req.body.email).then(result => { - if (result.status) { + if (result.status === 1) { req.session.userId = result.uid; req.session.loggedIn = 1; res.redirect('/auth'); + } else if (result.status === -1) { + res.render("error", { + helpers: { + error: "Nie udało się zalogować", + fallback: "/login" + } + }); } else { res.sendStatus(500); } }); } else { - res.sendStatus(403); + res.render("error", { + helpers: { + error: "Niepoprawny adres e-mail", + fallback: "/login" + } + }); } }); @@ -141,10 +174,20 @@ app.post('/api/auth', async (req, res) => { req.session.loggedIn = 2; res.redirect('/'); } else { - res.sendStatus(401); + res.render("error", { + helpers: { + error: "Niepoprawny kod logowania", + fallback: "/auth" + } + }); } } else { - res.sendStatus(403); + res.render("error", { + helpers: { + error: "Niepoprawny kod logowania", + fallback: "/login" + } + }); } }); @@ -212,10 +255,18 @@ io.on('connection', async (socket) => { status: "bad_id" }); } else { + let opp = io.sockets.sockets.get(io.sockets.adapter.rooms.get(msg).values().next().value); + + if (opp.request.session.userId == session.userId) { + callback({ + status: "cantJoinYourself", + }); + return; + } if (socket.rooms.size === 1) { io.to(msg).emit("joined", session.nickname); // Wyślij hostowi powiadomienie o dołączającym graczu // Zmienna opp zawiera socket hosta - let opp = io.sockets.sockets.get(io.sockets.adapter.rooms.get(msg).values().next().value); + // let opp = io.sockets.sockets.get(io.sockets.adapter.rooms.get(msg).values().next().value); let oppNickname = opp.request.session.nickname; socket.join(msg); // Dołącz gracza do grupy @@ -229,6 +280,7 @@ io.on('connection', async (socket) => { redis.json.set(`game:${gameId}`, '$', { hostId: opp.request.session.id, state: "pregame", + startTs: (new Date()).getTime() / 1000, boards: [ { // typ 2 to trójmasztowiec pozycja i obrót na planszy które pola zostały trafione ships: [], // zawiera np. {type: 2, posX: 3, posY: 4, rot: 2, hits: [false, false, true]} @@ -416,8 +468,9 @@ io.on('connection', async (socket) => { hostSocket.emit("game finished", !enemyIdx ? 1 : 0, guestNickname); guestSocket.emit("game finished", !enemyIdx ? 1 : 0, hostNickname); - const stats = await GInfo.getStats(socket); - auth.saveMatch(playerGame.id, "pvp", hostSocket.request.session.userId, guestSocket.request.session.userId, stats, !enemyIdx ? 1 : 0); + // const stats = await GInfo.getStats(socket); + const playerGame = await GInfo.getPlayerGameData(socket); + auth.saveMatch(playerGame.id, (new Date).getTime() / 1000 - playerGame.data.startTs, "pvp", hostSocket.request.session.userId, guestSocket.request.session.userId, playerGame.data.boards, !enemyIdx ? 1 : 0); GInfo.resetTimer(playerGame.id); endGame(playerGame.id, !enemyIdx ? 1 : 0); @@ -464,10 +517,10 @@ function resetUserGame(req) { }); } -function endGame(gameId, winnerIdx = -1) { - const boards = redis.json.get(`game:${gameId}`, { keys: [".boards"] }); - const hostUid = redis.json.get(`game:${gameId}`, { keys: [".hostUserId"] }); - const guestUid = redis.json.get(`game:${gameId}`, { keys: [".hostUserId"] }); +function endGame(gameId) { + // const boards = redis.json.get(`game:${gameId}`, { keys: [".boards"] }); + // const hostUid = redis.json.get(`game:${gameId}`, { keys: [".hostUserId"] }); + // const guestUid = redis.json.get(`game:${gameId}`, { keys: [".hostUserId"] }); let iterator = roomMemberIterator(gameId); if (iterator != null) { diff --git a/package-lock.json b/package-lock.json index be9bf17..a9a65ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "connect-redis": "^7.1.1", "dotenv": "^16.4.5", "express": "^4.18.3", "express-handlebars": "^7.1.2", + "express-rate-limit": "^7.2.0", "express-session": "^1.18.0", + "mysql": "^2.18.1", + "nodemailer": "^6.9.12", + "rate-limit-redis": "^4.2.0", "redis": "^4.6.13", "socket.io": "^4.7.4", "uuid": "^9.0.1", @@ -175,6 +180,14 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "engines": { + "node": "*" + } + }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -269,6 +282,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/connect-redis": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-7.1.1.tgz", + "integrity": "sha512-M+z7alnCJiuzKa8/1qAYdGUXHYfDnLolOGAUjOioB07pP39qxjG+X9ibsud7qUBc4jMV5Mcy3ugGv8eFcgamJQ==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "express-session": ">=1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -301,6 +325,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -523,6 +552,20 @@ "node": ">=v16" } }, + "node_modules/express-rate-limit": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.2.0.tgz", + "integrity": "sha512-T7nul1t4TNyfZMJ7pKRKkdeVJWa2CqB8NA1P8BwYaoDI5QSBZARv5oMS43J7b7I5P+4asjVXjb7ONuwDKucahg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "4 || 5 || ^5.0.0-beta.1" + } + }, "node_modules/express-session": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", @@ -831,6 +874,11 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -947,6 +995,25 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "dependencies": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mysql/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -960,6 +1027,14 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/nodemailer": { + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz", + "integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1031,6 +1106,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1073,6 +1153,17 @@ "node": ">= 0.6" } }, + "node_modules/rate-limit-redis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-4.2.0.tgz", + "integrity": "sha512-wV450NQyKC24NmPosJb2131RoczLdfIJdKCReNwtVpm5998U8SgKrAZrIHaN/NfQgqOHaan8Uq++B4sa5REwjA==", + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "express-rate-limit": ">= 6" + } + }, "node_modules/raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", @@ -1087,6 +1178,25 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/redis": { "version": "4.6.13", "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.13.tgz", @@ -1292,6 +1402,14 @@ "node": ">=0.10.0" } }, + "node_modules/sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1300,6 +1418,19 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -1444,6 +1575,11 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/package.json b/package.json index aabf2ba..07b37f0 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,15 @@ }, "homepage": "https://github.com/MaciejkaG/statki-backend#readme", "dependencies": { + "connect-redis": "^7.1.1", "dotenv": "^16.4.5", "express": "^4.18.3", "express-handlebars": "^7.1.2", + "express-rate-limit": "^7.2.0", "express-session": "^1.18.0", + "mysql": "^2.18.1", + "nodemailer": "^6.9.12", + "rate-limit-redis": "^4.2.0", "redis": "^4.6.13", "socket.io": "^4.7.4", "uuid": "^9.0.1", diff --git a/utils/auth.js b/utils/auth.js index 223a715..6d67a41 100644 --- a/utils/auth.js +++ b/utils/auth.js @@ -5,8 +5,8 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import mysql from 'mysql'; -import { createClient } from 'redis'; import readline from "node:readline"; + const rl = readline.createInterface({ input: process.stdin, output: process.stdout @@ -27,7 +27,7 @@ export class MailAuth { port: options.port ? options.port : "465", secure: options.secure ? options.secure : true, auth: { - user: options.user, + user: options.user ? options.user : "root", pass: options.pass, }, }); @@ -36,8 +36,8 @@ export class MailAuth { } async timer(tId, time, callback) { - await this.redis.set(`timer:${tId}`, new Date().getTime() / 1000); - let localLastUpdate = await this.redis.get(`timer:${tId}`); + await this.redis.set(`loginTimer:${tId}`, new Date().getTime() / 1000); + let localLastUpdate = await this.redis.get(`loginTimer:${tId}`); let timeout = setTimeout(callback, time * 1000); @@ -47,7 +47,7 @@ export class MailAuth { return; } - let lastUpdate = await this.redis.get(`timer:${tId}`); + let lastUpdate = await this.redis.get(`loginTimer:${tId}`); if (localLastUpdate != lastUpdate) { clearTimeout(timeout); clearInterval(interval); @@ -57,31 +57,34 @@ export class MailAuth { } async resetTimer(tId) { - let lastUpdate = await this.redis.get(`timer:${tId}`); - await this.redis.set(`timer:${tId}`, -lastUpdate); + let lastUpdate = await this.redis.get(`loginTimer:${tId}`); + await this.redis.set(`loginTimer:${tId}`, -lastUpdate); } startVerification(email) { return new Promise((resolve, reject) => { const conn = mysql.createConnection(this.mysqlOptions); conn.query(`SELECT user_id, nickname FROM accounts WHERE email = ${conn.escape(email)}`, async (error, response) => { - if (error) reject(error); + if (error) { reject(error); return; } + if (response.length !== 0 && await this.redis.get(`loginTimer:${response[0].user_id}`)) { + resolve({ status: -1 }); + return; + } + if (response.length === 0 || response[0].nickname == null) { - conn.query(`DELETE FROM accounts WHERE email = ${conn.escape(email)};`, (error, response) => { if (error) reject(error) }); - conn.query(`INSERT INTO accounts(email) VALUES (${conn.escape(email)});`, (error, response) => { if (error) reject(error) }); + conn.query(`INSERT INTO accounts(email) VALUES (${conn.escape(email)});`, (error) => { if (error) reject(error) }); conn.query(`SELECT user_id, nickname FROM accounts WHERE email = ${conn.escape(email)}`, async (error, response) => { if (error) reject(error); const row = response[0]; const html = fs.readFileSync(path.join(__dirname, 'mail/auth-code-firsttime.html'), 'utf8'); let authCode = genCode(); - let tId = uuid4(); - await this.redis.json.set(`code_auth:${authCode}`, "$", { uid: row.user_id, tid: tId }); + await this.redis.json.set(`codeAuth:${authCode}`, "$", { uid: row.user_id, tid: tId }); - await this.timer(tId, 600, async () => { - await this.redis.json.del(`code_auth:${authCode}`); + await this.timer(row.user_id, 600, async () => { + await this.redis.json.del(`codeAuth:${authCode}`); }); authCode = authCode.slice(0, 4) + " " + authCode.slice(4); @@ -107,12 +110,11 @@ export class MailAuth { const html = fs.readFileSync(path.join(__dirname, 'mail/auth-code.html'), 'utf8'); let authCode = genCode(); - let tId = uuid4(); - await this.redis.json.set(`code_auth:${authCode}`, "$", { uid: row.user_id, tid: tId }); + await this.redis.set(`codeAuth:${authCode}`, row.user_id); - await this.timer(tId, 600, async () => { - await this.redis.json.del(`code_auth:${authCode}`); + await this.timer(row.user_id, 600, async () => { + await this.redis.json.del(`codeAuth:${authCode}`); }); authCode = authCode.slice(0, 4) + " " + authCode.slice(4); @@ -129,14 +131,14 @@ export class MailAuth { }); } - saveMatch(matchId, type, hostId, guestId, stats, winnerIdx) { + saveMatch(matchId, duration, type, hostId, guestId, boards, winnerIdx) { return new Promise((resolve, reject) => { const conn = mysql.createConnection(this.mysqlOptions); - conn.query(`INSERT INTO matches(match_id, match_type, host_id, guest_id) VALUES (${conn.escape(matchId)}, ${conn.escape(type)}, ${conn.escape(hostId)}, ${conn.escape(guestId)})`, async (error, response) => { + conn.query(`INSERT INTO matches(match_id, match_type, host_id, guest_id, duration) VALUES (${conn.escape(matchId)}, ${conn.escape(type)}, ${conn.escape(hostId)}, ${conn.escape(guestId)}, ${conn.escape(duration)})`, async (error) => { if (error) reject(error); - conn.query(`INSERT INTO statistics(match_id, user_id, shots, hits, placed_ships, sunk_ships, sunk_ships_by, won) VALUES (${conn.escape(matchId)}, ${conn.escape(hostId)}, ${conn.escape(stats[0].shots)}, ${conn.escape(stats[0].hits)}, ${conn.escape(stats[0].placedShips)}, ${conn.escape(stats[0].sunkShips)}, ${conn.escape(stats[0].sunkShipsBy)}, ${conn.escape(winnerIdx == 0)}), (${conn.escape(matchId)}, ${conn.escape(guestId)}, ${conn.escape(stats[1].shots)}, ${conn.escape(stats[1].hits)}, ${conn.escape(stats[1].placedShips)}, ${conn.escape(stats[1].sunkShips)}, ${conn.escape(stats[1].sunkShipsBy)}, ${conn.escape(winnerIdx == 1)})`).then((error) => { + else conn.query(`INSERT INTO statistics(match_id, user_id, board, won) VALUES (${conn.escape(matchId)}, ${conn.escape(hostId)}, ${conn.escape(JSON.stringify(boards[0]))}, ${conn.escape(winnerIdx == 0 ? 1 : 0)}), (${conn.escape(matchId)}, ${conn.escape(guestId)}, ${conn.escape(JSON.stringify(boards[1]))}, ${conn.escape(winnerIdx == 1 ? 1 : 0)})`, async (error, response) => { if (error) reject(error); - resolve(); + else resolve(); }); }); }); @@ -144,10 +146,10 @@ export class MailAuth { async finishVerification(uid, authCode) { authCode = authCode.replace(/\s+/g, ""); - let redisRes = await this.redis.json.get(`code_auth:${authCode}`); - if (redisRes != null && redisRes.uid === uid) { - this.resetTimer(redisRes.tid); - await this.redis.del(`code_auth:${authCode}`); + const rUid = await this.redis.get(`codeAuth:${authCode}`); + if (rUid != null && rUid === uid) { + this.resetTimer(rUid); + await this.redis.del(`codeAuth:${authCode}`); return true; } else { return false; diff --git a/utils/mail/auth-code-firsttime.html b/utils/mail/auth-code-firsttime.html index b2003f9..1657d08 100644 --- a/utils/mail/auth-code-firsttime.html +++ b/utils/mail/auth-code-firsttime.html @@ -1,22 +1,30 @@ + - + + -
+

Cześć!

-

Ktoś próbował utworzyć konto w Statkach za pomocą tego e-maila, jeśli to nie byłeś ty - zignoruj tę wiadomość.
Poniżej znajduje się kod autoryzacyjny, pamiętaj by nie udostępniać go nikomu.

-
+

Ktoś próbował utworzyć konto w Statkach przy użyciu Twojego adresu e-mail. Jeśli to nie byłeś ty, zignoruj tą + wiadomość.
Poniżej znajduje się kod autoryzacyjny, pamiętaj by nie udostępniać go nikomu.

+
{{ CODE }}

Powyższy kod wygasa po 10 minutach

Copyright © 2024 MCJK | statki.maciejka.xyz
Ta wiadomość została wysłana automatycznie, nie odpowiadaj na nią.

+ style="text-decoration: none;color:white;">Copyright © 2024 MCJK | statki.maciejka.xyz
Ta wiadomość została + wysłana automatycznie, nie odpowiadaj na nią.

+ \ No newline at end of file diff --git a/utils/mail/auth-code.html b/utils/mail/auth-code.html index a136bda..a7d9494 100644 --- a/utils/mail/auth-code.html +++ b/utils/mail/auth-code.html @@ -1,22 +1,30 @@ + - + + -
+

Hej, {{ NICKNAME }}!

-

Ktoś próbował się zalogować na twoje konto w Statkach, jeśli to nie byłeś ty - zignoruj tę wiadomość.
Poniżej znajduje się kod autoryzacyjny, pamiętaj by nie udostępniać go nikomu.

-
+

Ktoś próbował się zalogować na twoje konto w Statkach, jeśli to nie byłeś ty - zignoruj tę + wiadomość.
Poniżej znajduje się kod autoryzacyjny, pamiętaj by nie udostępniać go nikomu.

+
{{ CODE }}

Powyższy kod wygasa po 10 minutach

Copyright © 2024 MCJK | statki.maciejka.xyz
Ta wiadomość została wysłana automatycznie, nie odpowiadaj na nią.

+ style="text-decoration: none;color:white;">Copyright © 2024 MCJK | statki.maciejka.xyz
Ta wiadomość została + wysłana automatycznie, nie odpowiadaj na nią.

+ \ No newline at end of file diff --git a/views/error.handlebars b/views/error.handlebars new file mode 100644 index 0000000..390de1d --- /dev/null +++ b/views/error.handlebars @@ -0,0 +1,39 @@ + +
+
+

Statki

+

Błąd

+
+
+

{{ error }}

+ +
+
+
+
\ No newline at end of file diff --git a/views/index.handlebars b/views/index.handlebars index d28fbe2..fe8dac6 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -52,7 +52,7 @@
- +