From 4ee0cd22806a57fe7501c5dbc9638d633e129f33 Mon Sep 17 00:00:00 2001 From: Nathaniel Tampus Date: Sun, 19 Mar 2023 21:14:06 +0800 Subject: [PATCH 1/6] update deps --- client/package.json | 8 ++++---- package.json | 2 +- server/package.json | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/package.json b/client/package.json index 65d1b62..f4e3fd8 100644 --- a/client/package.json +++ b/client/package.json @@ -9,11 +9,11 @@ "lint": "next lint" }, "dependencies": { - "@tabler/icons-react": "^2.10.0", - "chess.js": "^1.0.0-beta.3", + "@tabler/icons-react": "^2.11.0", + "chess.js": "^1.0.0-beta.4", "next": "13.2.4", "react": "18.2.0", - "react-chessboard": "^2.1.1", + "react-chessboard": "^2.1.2", "react-dom": "18.2.0", "socket.io-client": "^4.6.1" }, @@ -26,7 +26,7 @@ "daisyui": "^2.51.4", "postcss": "^8.4.21", "tailwindcss": "^3.2.7", - "typescript": "4.9.5" + "typescript": "5.0.2" }, "optionalDependencies": { "bufferutil": "^4.0.7", diff --git a/package.json b/package.json index 1c0b9cc..7cd7109 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "eslint-config-next": "13.2.4", "eslint-config-prettier": "^8.7.0", "prettier": "^2.8.4", - "prettier-plugin-tailwindcss": "^0.2.4" + "prettier-plugin-tailwindcss": "^0.2.5" }, "engines": { "node": ">=18" diff --git a/server/package.json b/server/package.json index e762dc5..11f1cf0 100644 --- a/server/package.json +++ b/server/package.json @@ -9,7 +9,7 @@ "dev": "node --loader ts-node/esm --watch src/server.ts" }, "dependencies": { - "chess.js": "^1.0.0-beta.3", + "chess.js": "^1.0.0-beta.4", "connect-pg-simple": "^8.0.0", "cors": "^2.8.5", "dotenv": "^16.0.3", @@ -28,7 +28,7 @@ "@types/node": "^18.15.3", "@types/pg": "^8.6.6", "ts-node": "^10.9.1", - "typescript": "^4.9.5" + "typescript": "^5.0.2" }, "engines": { "node": ">=18" From 802a03fa1144cf7fc60ffbda3445b2a45008d8ef Mon Sep 17 00:00:00 2001 From: Nathaniel Tampus Date: Sun, 19 Mar 2023 21:18:16 +0800 Subject: [PATCH 2/6] use .after FEN for move navigation --- client/src/components/game/GamePage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/game/GamePage.tsx b/client/src/components/game/GamePage.tsx index 846ac61..31b41e0 100644 --- a/client/src/components/game/GamePage.tsx +++ b/client/src/components/game/GamePage.tsx @@ -430,7 +430,7 @@ export default function GamePage({ initialLobby }: { initialLobby: Game }) { function navigateMove(index: number | null | "prev") { const history = lobby.actualGame.history({ verbose: true }); - if (index === null || index >= history.length - 1 || !history.length) { + if (index === null || (index !== "prev" && index >= history.length - 1) || !history.length) { // last move setNavIndex(null); setNavFen(null); @@ -446,7 +446,7 @@ export default function GamePage({ initialLobby }: { initialLobby: Game }) { chessboardRef.current?.clearPremoves(false); setNavIndex(index); - setNavFen(history[index + 1].fen); + setNavFen(history[index].after); } function getNavMoveSquares() { From b6df2c0df1f5ad3394b0c90f405e4ecf21bad03d Mon Sep 17 00:00:00 2001 From: Nathaniel Tampus Date: Sun, 19 Mar 2023 20:51:29 +0800 Subject: [PATCH 3/6] sync user info in games, adjust game timeout --- server/src/controllers/games.controller.ts | 5 +-- server/src/socket/game.socket.ts | 41 ++++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/server/src/controllers/games.controller.ts b/server/src/controllers/games.controller.ts index 8e10a7f..ed41a83 100644 --- a/server/src/controllers/games.controller.ts +++ b/server/src/controllers/games.controller.ts @@ -34,13 +34,14 @@ export const getActiveGame = async (req: Request, res: Response) => { export const createGame = async (req: Request, res: Response) => { try { - if (!req.session.user) { + if (!req.session.user?.id) { console.log("unauthorized createGame"); res.status(401).end(); return; } const user: User = { - ...req.session.user, + id: req.session.user.id, + name: req.session.user.name, connected: false }; const unlisted: boolean = req.body.unlisted ?? false; diff --git a/server/src/socket/game.socket.ts b/server/src/socket/game.socket.ts index 6ad6643..b3eed82 100644 --- a/server/src/socket/game.socket.ts +++ b/server/src/socket/game.socket.ts @@ -10,13 +10,29 @@ export async function joinLobby(this: Socket, gameCode: string) { return; } + if (game.host && game.host?.id === this.request.session.user.id) { + game.host.connected = true; + if (game.host.name !== this.request.session.user.name) { + game.host.name = this.request.session.user.name; + } + } if (game.white && game.white?.id === this.request.session.user.id) { game.white.connected = true; + if (game.white.name !== this.request.session.user.name) { + game.white.name = this.request.session.user.name; + } } else if (game.black && game.black?.id === this.request.session.user.id) { game.black.connected = true; + if (game.black.name !== this.request.session.user.name) { + game.black.name = this.request.session.user.name; + } } else { if (game.observers === undefined) game.observers = []; - game.observers?.push(this.request.session.user); + const user = { + id: this.request.session.user.id, + name: this.request.session.user.name + }; + game.observers?.push(user); } if (this.rooms.size >= 2) { @@ -61,10 +77,15 @@ export async function leaveLobby(this: Socket, reason?: DisconnectReason, code?: if (sockets.length <= 0 || (reason === undefined && sockets.length <= 1)) { if (game.timeout) clearTimeout(game.timeout); + + let timeout = 1000 * 60; // 1 minute + if (game.pgn) { + timeout *= 20; // 20 minutes if game has started + } game.timeout = Number( setTimeout(() => { activeGames.splice(activeGames.indexOf(game), 1); - }, 1000 * 60 * 10) // 10 minutes + }, timeout) ); } else { this.to(game.code as string).emit("receivedLatestGame", game); @@ -137,16 +158,24 @@ export async function joinAsPlayer(this: Socket) { if (!game) return; const user = game.observers?.find((o) => o.id === this.request.session.user.id); if (!game.white) { - game.white = this.request.session.user; - game.white.connected = true; + const sessionUser = { + id: this.request.session.user.id, + name: this.request.session.user.name, + connected: true + }; + game.white = sessionUser; if (user) game.observers?.splice(game.observers?.indexOf(user), 1); io.to(game.code as string).emit("userJoinedAsPlayer", { name: this.request.session.user.name, side: "white" }); } else if (!game.black) { - game.black = this.request.session.user; - game.black.connected = true; + const sessionUser = { + id: this.request.session.user.id, + name: this.request.session.user.name, + connected: true + }; + game.black = sessionUser; if (user) game.observers?.splice(game.observers?.indexOf(user), 1); io.to(game.code as string).emit("userJoinedAsPlayer", { name: this.request.session.user.name, From 9ae628c592f0c035fa2daa776f75be23bd826866 Mon Sep 17 00:00:00 2001 From: Nathaniel Tampus Date: Sun, 19 Mar 2023 21:23:15 +0800 Subject: [PATCH 4/6] update game data on when updating guest names --- server/src/controllers/auth.controller.ts | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index 2e2b067..ca417e7 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -2,6 +2,9 @@ import type { Request, Response } from "express"; import type { User } from "@chessu/types"; import xss from "xss"; +import { activeGames } from "../db/models/game.model.js"; +import { io } from "../server.js"; + export const getCurrentSession = async (req: Request, res: Response) => { try { if (req.session.user) { @@ -36,6 +39,29 @@ export const guestSession = async (req: Request, res: Response) => { } else if (typeof req.session.user.id === "string" && req.session.user.name !== name) { // update guest name req.session.user.name = name; + + const game = activeGames.find( + (g) => + g.white?.id === req.session.user.id || + g.black?.id === req.session.user.id || + g.observers?.find((o) => o.id === req.session.user.id) + ); + if (game) { + if (game.host?.id === req.session.user.id) { + game.host.name = name; + } + if (game.white?.id === req.session.user.id) { + game.white.name = name; + } else if (game.black?.id === req.session.user.id) { + game.black.name = name; + } else { + const observer = game.observers?.find((o) => o.id === req.session.user.id); + if (observer) { + observer.name = name; + } + } + io.to(game.code as string).emit("receivedLatestGame", game); + } } req.session.save(() => { res.status(201).json(req.session.user); From dc2f218115e28ed86aea709a8f24e5ebcfb2267a Mon Sep 17 00:00:00 2001 From: Nathaniel Tampus Date: Sun, 19 Mar 2023 21:24:41 +0800 Subject: [PATCH 5/6] disallow underscores for names --- client/src/components/auth/AuthModal.tsx | 4 ++-- server/src/controllers/auth.controller.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/auth/AuthModal.tsx b/client/src/components/auth/AuthModal.tsx index a164caa..45fa774 100644 --- a/client/src/components/auth/AuthModal.tsx +++ b/client/src/components/auth/AuthModal.tsx @@ -62,8 +62,8 @@ export default function AuthModal() { Name { try { const name = xss(req.body.name); - const pattern = /^[A-Za-z0-9_]+$/; + const pattern = /^[A-Za-z0-9]+$/; if (!pattern.test(name)) { res.status(400).end(); From 76c1e82b9020abce3ce9f2a2c99c5e76f7d74db6 Mon Sep 17 00:00:00 2001 From: Nathaniel Tampus Date: Sun, 19 Mar 2023 20:43:16 +0800 Subject: [PATCH 6/6] update readme --- README.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5761a2e..6a46a80 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# chessu [![Deployment status](https://img.shields.io/github/deployments/nizewn/chessu/Production?label=deployment)](https://ches.su) [![MIT License](https://img.shields.io/github/license/nizewn/chessu?color=blue)](https://github.com/nizewn/chessu/blob/main/LICENSE) +# chessu [![Deployment status](https://img.shields.io/github/deployments/nizewn/chessu/Production?label=deployment)](https://ches.su) -> ❗ This project is still in the early stages of development and should be considered unstable. Expect bugs and weird behavior. +> ❗ This project is still in the early stages of development. Expect bugs and incomplete features. Yet another Chess web app. Live demo at [ches.su](https://ches.su). @@ -19,7 +19,9 @@ This project is structured as a monorepo using npm workspaces, separated into th - `server` - Node/Express.js application for the back-end, deployed to [server.ches.su](https://server.ches.su). - `types` - Shared type definitions for the client and server. -### Scripts +For separate deployments, you may exclude the `client` or `server` directory. However, you should include the `types` folder as it contains shared type definitions that are required by both packages. + +
Scripts

```sh # install all dependencies, including eslint and prettier for development @@ -27,8 +29,9 @@ npm install # concurrently run frontend and backend development servers npm run dev # -w client/server to run only one +``` - +```sh # for separate production deployments npm install -w client npm install -w server @@ -40,9 +43,9 @@ npm start -w client npm start -w server ``` -For separate deployments, you may exclude the `client` or `server` directory. However, you should include the `types` folder as it contains shared type definitions that are required by both packages. +

-### Environment variables +
Environment variables

You may create a `.env` file in each package directory to set their environment variables. @@ -65,3 +68,13 @@ PGUSER=exampleuser PGPASSWORD=examplepassword PGDATABASE=chessu ``` + +

+ +## Contributing + +Pull requests are welcome. For feature changes or suggestions, please open an issue first for discussion. + +## License + +[MIT](https://github.com/nizewn/chessu/blob/main/LICENSE)