From 3a1ef01a5462daf041a4dabbf7a4db8b5bdb5ff3 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sun, 28 Nov 2021 15:53:47 +0100 Subject: [PATCH] Registration, Login, Resume, Table Creation, Dark Style --- Public/game.js | 337 ++++++++++++++++++++-- Public/schafkopf.html | 31 +- Public/style.css | 168 ++++++++++- Sources/App/Model/Crypto+Extensions.swift | 14 + Sources/App/Model/Database.swift | 167 +++++------ Sources/App/Model/PlayerManagement.swift | 106 +++++++ Sources/App/Model/TableManagement.swift | 75 +++++ Sources/App/routes.swift | 231 +++++++++++---- 8 files changed, 940 insertions(+), 189 deletions(-) create mode 100644 Sources/App/Model/Crypto+Extensions.swift create mode 100644 Sources/App/Model/PlayerManagement.swift create mode 100644 Sources/App/Model/TableManagement.swift diff --git a/Public/game.js b/Public/game.js index b248d59..3eae3fc 100644 --- a/Public/game.js +++ b/Public/game.js @@ -1,15 +1,270 @@ + +// The web socket to connect to the server +var socket = null; + +function closeSocketIfNeeded() { + if (socket) { + socket.close() + socket = null + } +} + function hideLoginWindow() { document.getElementById("signup-window").style.display = "none" } +function showLoginWindow() { + document.getElementById("signup-window").style.display = "table" +} + +function setPlayerName(name) { + document.getElementById("player-name").innerHTML = name +} + +function getPlayerName() { + return document.getElementById("player-name").innerHTML +} + +function getLoginName() { + return document.getElementById("user-name").value +} + +function clearLoginName() { + document.getElementById("user-name").value = "" +} + +function getLoginPassword() { + return document.getElementById("user-pwd").value +} + +function clearLoginPassword() { + document.getElementById("user-pwd").value = "" +} + +function getSessionToken() { + return localStorage.getItem('token') +} + +function setSessionToken(token) { + localStorage.setItem('token', token) +} + +function deleteSessionToken() { + localStorage.removeItem('token') +} + +function setLoginError(text) { + document.getElementById("login-error").innerHTML = text +} + +function getTableName() { + return document.getElementById("table-name-field").value +} + +function clearTableName() { + return document.getElementById("table-name-field").value = "" +} + +function getTableVisibility() { + return document.getElementById("table-public-checkbox").checked +} + +function setTableListContent(content) { + document.getElementById("table-list").innerHTML = content +} + +function showBlankLoginScreen(text) { + closeSocketIfNeeded() + clearLoginPassword() + clearLoginName() + deleteSessionToken() + showLoginWindow() + setLoginError(text) +} + async function registerUser() { - let username = document.getElementById("user-name").value - let password = document.getElementById("user-pwd").value - errorField = document.getElementById("login-error") + console.log("Registration started") + performGetSessionTokenRequest("register") +} - console.log("Registration started"); +async function deletePlayerAccount() { + const name = getPlayerName() + const password = getLoginPassword() - fetch("/create/user/" + username + "/" + password, { method: 'POST' }) + fetch("/player/delete/" + username, { method: 'POST', body: password }) + .then(function(response) { + if (response.status == 200) { // Success + return + } + if (response.status == 400) { // Bad request + throw Error("The request had an error") + } + if (response.status == 401) { // Invalid session token + throw Error("Please log in again") + } + if (response.status == 403) { // Forbidden + throw Error("The password or name is incorrect") + } + if (response.status == 424) { // Failed dependency + throw Error("The request couldn't be completed") + } + throw Error("Unexpected registration response: " + response.statusText) + }).then(function() { + showBlankLoginScreen("") + console.log("Player deleted") + }).catch(function(error) { + closeSocketIfNeeded() + deleteSessionToken() + alert(error.message) + console.log(error) + }) +} + +async function loginUser() { + console.log("Login started"); + performGetSessionTokenRequest("login") +} + +async function logoutUser() { + const token = getSessionToken() + if (token) { + console.log("Logging out player") + performLogoutRequest(token) + } else { + console.log("No player to log out") + showBlankLoginScreen("") + } +} + +async function performLogoutRequest(token) { + fetch("/player/logout", { method: 'POST', body: token }) + .then(function(response) { + if (response.status == 200) { // Success + return + } + if (response.status == 400) { // Bad request + throw Error("The request had an error") + } + throw Error("Unexpected logout response: " + response.statusText) + }).then(function() { + showBlankLoginScreen("") + console.log("Player logged out") + }).catch(function(error) { + showBlankLoginScreen(error.message) + console.log(error) + }) +} + +function convertServerResponse(response) { + if (response.status == 200) { // Success + return response.text() + } + if (response.status == 400) { // Bad request + throw Error("The request was malformed") + } + if (response.status == 403) { // Forbidden + throw Error("Invalid username or password") + } + if (response.status == 406) { // notAcceptable + throw Error("The password or name is too long") + } + if (response.status == 409) { // Conflict + throw Error("A user with the same name is already registered") + } + if (response.status == 424) { // Failed dependency + throw Error("The request couldn't be completed") + } + throw Error("Unexpected response: " + response.statusText) +} + +async function performGetSessionTokenRequest(type) { + const username = getLoginName() + const password = getLoginPassword() + + console.log("Performing request " + type); + fetch("/player/" + type + "/" + username, { method: 'POST', body: password }) + .then(convertServerResponse) + .then(function(token) { + setSessionToken(token) + setPlayerName(username) + hideLoginWindow() + setLoginError("") + console.log(type + " successful") + performGetPublicTablesList(token) + openSocket(token) + }).catch(function(error) { + setLoginError(error.message) + console.log(error) + return + }) +} + +async function loadExistingSession() { + const token = getSessionToken() + if (token) { + console.log("Resuming session with token " + token) + resumeSession(token) + } else { + console.log("No session to resume") + showLoginWindow() + } +} + +async function resumeSession(token) { + fetch("/player/resume", { method: 'POST', body: token }) + .then(convertServerResponse) + .then(function(name) { + setPlayerName(name) + hideLoginWindow() + console.log("Session resumed") + performGetPublicTablesList(token) + openSocket(token) + }).catch(function(error) { + deleteSessionToken() + setLoginError(error.message) + showLoginWindow() + console.log(error) + return + }) +} + +async function openSocket(token) { + socket = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/session/start") + + socket.onopen = function(e) { + socket.send(token); + }; + + socket.onmessage = function(event) { + // TODO: Handle server data + }; + + socket.onclose = function(event) { + if (event.wasClean) { + + } else { + // e.g. server process killed or network down + // event.code is usually 1006 in this case + } + }; + + socket.onerror = function(error) { + // error.message + }; +} + +function createTable() { + const tableName = getTableName() + const isVisible = getTableVisibility() + const token = getSessionToken() + if (token) { + performCreateTableRequest(token, tableName, isVisible) + } +} + +async function performCreateTableRequest(token, name, visibility) { + const vis = visibility ? "public" : "private"; + fetch("/table/create/" + vis + "/" + name, { method: 'POST', body: token }) .then(function(response) { if (response.status == 200) { // Success return response.text() @@ -17,36 +272,70 @@ async function registerUser() { if (response.status == 400) { // Bad request throw Error("The request had an error") } - if (response.status == 409) { // Conflict - throw Error("A user with the same name is already registered") + if (response.status == 401) { // Token invalid + showBlankLoginScreen("") + return "" } - throw Error("Unexpected response: " + response.statusText) - }).then(function(text) { - localStorage.setItem('token', text) - hideLoginWindow() - console.log("Registered") + throw Error("Unexpected registration response: " + response.statusText) + }) + .then(function(tableId) { + if (tableId == "") { + return; + } + clearTableName() + console.log("Created table " + tableId) }).catch(function(error) { - errorField.innerHTML = error.message + showBlankLoginScreen(error.message) console.log(error) return }) } -function loadExistingSession() { - console.log("Checking to resume session"); - const token = localStorage.getItem('token'); +function refreshTables() { + const token = getSessionToken() if (token) { - console.log("Resuming session with token " + token); - resumeSession(token); + performGetPublicTablesList(token) + } else { + showBlankLoginScreen() } } -function resumeSession(token) { - - localStorage.removeItem('token'); - hideLoginWindow() +async function performGetPublicTablesList(token) { + fetch("/tables/public", { method: 'POST', body: token }) + .then(convertServerResponse) + .then(function(text) { + const decoded = atob(text) + const json = JSON.parse(decoded); + const html = processTableList(json) + setTableListContent(html) + }).catch(function(error) { + showBlankLoginScreen(error.message) + console.log(error) + return + }) } -function loginUser() { - +function processTableList(tables) { + var html = "" + for (let i = 0, len = tables.length, text = ""; i < len; i++) { + tableInfo = tables[i] + html += "
" + + "
" + tableInfo.name + + "
Players: " + tableInfo.players.join(", ") + "
" + } + return html } + +function joinTable(tableId) { + const token = getSessionToken() + if (token) { + performJoinTableRequest(tableId, token) + } else { + showBlankLoginScreen() + } +} + +async function performJoinTableRequest(tableId, token) { + +} \ No newline at end of file diff --git a/Public/schafkopf.html b/Public/schafkopf.html index 905e2df..94ea18b 100644 --- a/Public/schafkopf.html +++ b/Public/schafkopf.html @@ -7,17 +7,38 @@ -
-